Compare commits

...

1203 Commits

Author SHA1 Message Date
Erik
813fa922e2 Include issue tracker link in custom integration analytics data 2026-04-30 10:24:44 +02:00
Erik Montnemery
4b28928702 Remove scripts from DATA_SCRIPTS on unload (#169415) 2026-04-29 18:09:49 +02:00
A. Gideonse
859ce55c96 Bump indevolt-api to 1.6.5 (#169406) 2026-04-29 16:57:53 +01:00
MohamedBarrak3
9a9f19cb9e Fix Schlage add_code service failing when code is passed as integer (#168399)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-29 17:42:16 +02:00
Heikki Henriksen
d8b1bfb268 prusalink: populate serial number and firmware version in device info (#169309)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 17:38:32 +02:00
Joakim Plate
f5363db26f Move finish watering to sensor (#169476)
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 17:34:38 +02:00
Erik Montnemery
3be1aa5441 Include errors in script trace when continue_on_error is set (#168676) 2026-04-29 17:30:47 +02:00
Paul Bottein
7dbffb7375 Update frontend to 20260429.0 (#169475) 2026-04-29 17:21:40 +02:00
A. Gideonse
07c4025d47 Add indevolt binary sensor platform (#169375)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-29 16:19:17 +01:00
Franck Nijhof
3e3e425aa5 Bump Fumis integration to platinum quality scale (#169443) 2026-04-29 17:14:50 +02:00
Erik Montnemery
162a4fc385 Use automation behavior selector in triggers and conditions (#169438) 2026-04-29 17:10:50 +02:00
Manu
ef6fd92079 Add notify entities to Mobile app integration (#168510)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-29 17:06:13 +02:00
Erik Montnemery
4ad71a070a Improve timer icons (#169474) 2026-04-29 17:02:22 +02:00
Erik Montnemery
f33ad12f5e Correct entity_id change for automations (#169470) 2026-04-29 16:30:02 +02:00
Erik Montnemery
da7fbb0dd6 Correct entity_id change for scripts (#169472) 2026-04-29 16:29:25 +02:00
Abílio Costa
81137345a3 Extract triggers/conditions/services for non-primary entities (#169441) 2026-04-29 15:28:09 +01:00
Erik Montnemery
d3e77d4195 Add timer triggers (#169450) 2026-04-29 16:27:52 +02:00
renovate[bot]
ce977e90a5 Update cryptography to 47.0.0 (#169465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 16:22:16 +02:00
Martin Hjelmare
2871b87344 Revert "Include indirect automation references in device view (#167719)" (#169471) 2026-04-29 15:15:50 +01:00
renovate[bot]
d82ce1e22d Update ruff (#169473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 16:11:22 +02:00
Guido Schmitz
b8bb2e0090 Use uptime sensor class in devolo Home Network (#169469) 2026-04-29 16:10:39 +02:00
Erik Montnemery
1b81cfe3ca Make it always optional to specify trigger and condition options (#169467) 2026-04-29 15:06:05 +01:00
renovate[bot]
0a3f0d90c3 Update url-normalize to 3.0.0 (#169466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 15:48:05 +02:00
renovate[bot]
84d566a02c Update pyOpenSSL to 26.1.0 (#169464)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 15:47:52 +02:00
renovate[bot]
0e0d54e4b6 Update uv to 0.11.8 (#169463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 15:47:39 +02:00
epenet
5b05061def Fix plex sensor test broken by Python 3.14.3 asyncio changes (#169448) 2026-04-29 15:22:34 +02:00
renovate[bot]
e0bf76769a Update ruff (#169461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 15:17:04 +02:00
renovate[bot]
63868bc169 Migrate Renovate config (#169462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 15:16:26 +02:00
Bram Kragten
b8b7169371 Add automation behavior selector (#166484)
Co-authored-by: Erik <erik@montnemery.com>
2026-04-29 15:10:47 +02:00
Maciej Bieniek
1cc778954f Use new UPTIME sensor class in Brother (#169457) 2026-04-29 14:54:55 +02:00
Maciej Bieniek
3ba3ecdef3 Use new UPTIME sensor class in NAM (#169458) 2026-04-29 14:54:14 +02:00
Ronald van der Meer
5c57fc6e14 Fix Duco HTTPS polling performance by lowering SCAN_INTERVAL to 10 seconds (#169453) 2026-04-29 14:25:04 +02:00
epenet
2da440043a Fix Sonos group regroup race when entity is not yet registered (#169445) 2026-04-29 14:13:59 +02:00
epenet
4f34725e53 Fix flaky portainer test_device_registry (#169456)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:11:07 +02:00
epenet
d03bec2f44 Fix race in devolo Home Network device tracker device lookup (#169454)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:08:00 +02:00
epenet
57c37fc10c Fix race in Ping device tracker device lookup (#169432)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:02:37 +02:00
Simone Chemelli
fd98594143 Use defaults for device class UPTIME in Fritz (#169149) 2026-04-29 12:34:25 +01:00
Robert Svensson
894547abed Add Axis doorbell event platform (#169422) 2026-04-29 12:29:58 +01:00
Luis Miranda
b48060674c Add OMIE integration (#150399)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: abmantis <amfcalt@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-29 12:10:22 +01:00
vturekhanov
6f2aa7852a Fix availability state for bridged Matter devices (#165078)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-29 13:05:15 +02:00
Abílio Costa
9d53645468 Remove LLM test instruction (#169442) 2026-04-29 12:58:47 +02:00
Simone Chemelli
a3f1c067f7 Fix host connections for Fritz (#169434) 2026-04-29 12:57:05 +02:00
Tomer
cef97973d0 Victron GX device_tracker optional attributes (#168646)
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 12:56:13 +02:00
TheJulianJES
7bb297a3fc Bump ZHA to 1.3.0 (#169433) 2026-04-29 12:51:18 +02:00
Maciej Bieniek
7e2b8e1a48 Bump aioshelly to 13.24.2 (#169440) 2026-04-29 12:50:44 +02:00
Franck Nijhof
013c5e7f7c Add diagnostics to Fumis integration (#169437) 2026-04-29 12:38:09 +02:00
Abílio Costa
7cb1d5b8ab Allow targeting non-primary entities in conditions (#169291) 2026-04-29 12:25:26 +02:00
Paulus Schoutsen
57d9e8ea6f Filter history API responses by per-entity read permissions (#169236)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:16:49 +02:00
Andrew Ng
32743fcf8d Fix Acaia battery sensor going unavailable on first-session disconnect (#169420)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-04-29 12:12:44 +02:00
Simone Chemelli
f4637db26d Add routine management to Alexa Devices (#166291) 2026-04-29 11:45:03 +02:00
Erik Montnemery
b4bfe6b80b Rename timer last_action to last_transition (#169430) 2026-04-29 11:35:36 +02:00
Andrej Walilko
278f25ec6e Redact sensitive api creds before logging message in websocket api (#169326)
Co-authored-by: Andrej Walilko <awalilko@liquidweb.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-29 11:15:05 +02:00
Robert Resch
39d3bc3e53 Bump deebot-client to 18.2.0 (#169003) 2026-04-29 11:13:14 +02:00
Yabing Yi
bb41a2df9f Fix logbook spam by including image domain in ALWAYS_CONTINUOUS_DOMAINS (#169240)
Co-authored-by: Claude Code <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-29 10:58:13 +02:00
Petar Petrov
284242b90e Copy unit_of_measurement onto energy inverted power sensor (#169427) 2026-04-29 10:56:08 +02:00
Erik Montnemery
a95c216983 Unload scripts created by websocket command execute_script (#169368)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-04-29 10:24:24 +02:00
Simone Chemelli
d41a3ae0cd Use defaults for device class UPTIME in Shelly (#169148) 2026-04-29 10:12:18 +02:00
J. Nick Koston
0dfbe3ef84 Expose async_clear_advertisement_history in the bluetooth API (#169191) 2026-04-29 10:11:27 +02:00
Franck Nijhof
71fc725d75 Extract state template functions into a state Jinja2 extension (#169034) 2026-04-29 10:03:38 +02:00
Shay Levy
d41c9aee52 Bump aioshelly to 13.24.1 (#169426)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-04-29 10:53:38 +03:00
epenet
8091f511b8 Reject manifest dependencies on core integrations in hassfest (#169425) 2026-04-29 09:52:46 +02:00
Franck Nijhof
a7baedc22b Add error and alert sensors to Fumis integration (#169307) 2026-04-29 09:51:22 +02:00
Franck Nijhof
05bfb3a52e Add number platform to Fumis integration (#169100) 2026-04-29 09:39:15 +02:00
Robert Resch
2a5b95ba4d Require hass in Template (#169292)
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 09:26:32 +02:00
Steve Easley
3dd972cc7a Fix jvcprojector entities going unavailable on transient command errors (#168985) 2026-04-29 09:21:53 +02:00
Marc Mueller
acd9dd218a Protect CI cache save against cancellation (#168310) 2026-04-29 09:20:37 +02:00
J. Diego Rodríguez Royo
6552cf8f7a Keep options values when chaging or starting program on Home Connect (#168575) 2026-04-29 09:19:41 +02:00
Artur Pragacz
e4e4785225 Clean up entity_service_call tests (#169170)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-29 09:17:45 +02:00
G Johansson
d531ce8d1d Use async_on_create_entry in bayesian (#169218)
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 09:14:58 +02:00
Stefan Agner
0224928655 Bump python-otbr-api to 2.10.0 (#169370)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-04-29 09:10:26 +02:00
Erik Montnemery
05121b89c6 Unload scripts and conditions created by automations (#169362) 2026-04-29 09:03:34 +02:00
Tomer
326895f0a1 Victron GX: Platinum quality scale (#169070) 2026-04-29 09:02:39 +02:00
Brett Adams
45121eddf1 Use new console pages for vehicles and energy sites in Teslemetry (#168865) 2026-04-29 09:00:39 +02:00
Konrad Strack
5e4f8f8bff Fix missing hue.activate_scene actions (#168859) 2026-04-29 08:59:04 +02:00
Klaas Schoute
b9bbe36af0 Remove name field from Forecast.solar config flow (#169165) 2026-04-29 08:58:22 +02:00
epenet
b56cdb9106 Fix flaky unifi device_tracker entity race on setup (#169359) 2026-04-29 08:55:10 +02:00
epenet
e975496145 Fix flaky test_tasks_logged_that_block_stage_2 with Python 3.14.3 (#169424)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:54:21 +02:00
Simone Chemelli
cdeb550b87 Use new UPTIME sensor class for Synology DSM (#169090) 2026-04-29 08:52:53 +02:00
Erik Montnemery
62082bdf14 Use modern condition API in script helper (#169355) 2026-04-29 08:51:57 +02:00
ibrahim amous
891efeb9cb Use enumerate instead of range(len()) in Duco fan speed list (#169392) 2026-04-29 08:51:26 +02:00
Daniel Hjelseth Høyer
dc8abff6b9 Improve data updating for Tibber (#168065)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-29 08:42:47 +02:00
epenet
aa7474839b Fix watts coordinator interrupting fast polling on hub update (#169365)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:39:58 +02:00
Robert Svensson
06a96712f6 Bump axis to v69 (#169408) 2026-04-29 08:27:43 +02:00
Kurt Chrisford
97be8f485a Add DRY HVAC mode support to Actron Air based on hardware capabilities (#169132) 2026-04-29 08:26:03 +02:00
Florent Thoumie
a9c23ff445 iaqualink: add reconfigure flow (#169412) 2026-04-29 08:24:57 +02:00
Brett Adams
cd92cb1258 Filter out "Unknown" part_name from Teslemetry energy device model (#169413)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:23:52 +02:00
Erik Montnemery
c3f01b3a23 Unload scripts created by wake_on_lan switch (#169367) 2026-04-29 08:22:00 +02:00
Erik Montnemery
4b232be04a Unload scripts created by intent_script (#169363) 2026-04-29 08:21:26 +02:00
Robert Svensson
cd5e21d3ac Allow Axis websocket event usage if supported (#169409) 2026-04-29 08:19:19 +02:00
Abílio Costa
84d5085f3b Add path-specific custom instructions to copilot gen script (#169402) 2026-04-29 08:19:10 +02:00
Erik Montnemery
44e94a82f1 Add last_action state attribute to timers (#168282) 2026-04-29 08:16:19 +02:00
Christian Lackas
fe0da5c34f Bump PyViCare to 2.60.1 (#169401) 2026-04-29 08:14:39 +02:00
epenet
c0200084ec Fix flaky test_alexa_config_expose_entity_prefs with Python 3.14.3 (#169421)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:13:14 +02:00
Simone Chemelli
ef63ab5def Use new UPTIME sensor class for Vodafone Station (#169077)
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 08:12:05 +02:00
Michael
3683607820 Deprecate firmware update button in FRITZ!Box Tools (#168117) 2026-04-29 08:10:29 +02:00
epenet
4c70fef2da Fix flaky mcp_server tests with Python 3.14.3 (#169385) 2026-04-28 22:13:06 -07:00
Nicolas Mowen
d956af095e Add ability to filter GetLiveContext tool (#168457)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-28 22:08:29 -07:00
Brett Adams
ea34fe4107 Bump Tesla Fleet API to 1.4.7 (#169411) 2026-04-29 01:25:04 +02:00
oxidworks
e1c81c9b9e Reword country_not_configured repair description (#168357) 2026-04-29 00:55:48 +02:00
Mike Degatano
4ea0e6b240 Require admin for supervisor event publishing and addon options info (#169325)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 18:51:16 -04:00
TheJulianJES
0ae5a19602 Handle ZHA dynamic entity add/remove events (#169341) 2026-04-28 23:48:57 +02:00
Raphael Hehl
80c7e47c42 Migrate UniFi Network discovery from SSDP to unifi_discovery (#168122)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-28 23:17:36 +02:00
Paul Bottein
dfe4085189 Add fan platform to Novy Cooker Hood (#169380) 2026-04-28 16:51:46 -04:00
Paul Bottein
65a12b48e7 Add Novy Cooker Hood integration (#169194) 2026-04-28 16:24:19 -04:00
Joost Lekkerkerker
cd639b829c Add battery mode select to Zinvolt (#169397) 2026-04-28 21:16:23 +02:00
Paulus Schoutsen
ea5b633574 Bump rf-protocols to 2.2.0 (#169400)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-28 21:11:02 +02:00
Paulus Schoutsen
2f2413c979 Enforce per-entity permissions in calendar HTTP and WS APIs (#169235)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-28 15:06:49 -04:00
Ludovic BOUÉ
799bcb0f88 Fix Matter electrical sensors wrongly categorized as diagnostic (#169208)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-04-28 20:56:26 +02:00
Erik Montnemery
d3cf5d9aab Add duration support to cover conditions (#169346) 2026-04-28 20:50:41 +02:00
puddly
d2fddf129d Include matching integrations in scanned ports WS API (#169387) 2026-04-28 14:50:31 -04:00
AlCalzone
d19c2506bf Discover Fibaro FGMS001 v2.8 as a motion sensor for Z-Wave (#169276) 2026-04-28 20:45:12 +02:00
Øyvind Matheson Wergeland
8fd3d0bb44 Fix nobo_hub KeyError when a zone or component is removed (#169378) 2026-04-28 20:30:53 +02:00
Ludovic BOUÉ
d62f136c58 Add child lock entity for Eve Matter devices (#169391) 2026-04-28 19:55:37 +02:00
Ludovic BOUÉ
86e8b9df9b Add temporary mute button for Heiman smoke detector (#169311)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-04-28 18:19:22 +01:00
epenet
aa5e942528 Fix flaky gardena_bluetooth test_timeout_manufacturer_data (#169389) 2026-04-28 19:15:43 +02:00
epenet
6636e67af6 Fix flaky cloud TTS and picotts streaming tests (#169376)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 18:38:03 +02:00
Raphael Hehl
30f310fc24 Add UniFi Protect relay output switches via public API (#169201)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-28 18:34:58 +02:00
Paulus Schoutsen
de4e1c444e Restrict homematic.set_install_mode service to admins (#169203)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-04-28 17:00:13 +01:00
Raphael Hehl
eaf72106f8 Add siren platform to UniFi Protect integration (#169216)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 17:50:02 +02:00
A. Gideonse
b90a074fb4 Bump indevolt-api to 1.6.4 (#169377) 2026-04-28 16:12:09 +01:00
epenet
3aea7f0695 Fix flaky stream test_stream_retries (#169372)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:41:21 +02:00
epenet
ba8b1b2daf Fix flaky google calendar tests (#169371)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-28 15:21:38 +02:00
Erik Montnemery
5ff1c15df3 Unload scripts created by script entities (#169364) 2026-04-28 15:02:09 +02:00
Zoltán Farkasdi
0280d921e5 netatmo: add battery sensor for doortags (#168202) 2026-04-28 14:22:26 +02:00
Petro31
955e8362e4 Add template number device_class (#168438) 2026-04-28 14:12:18 +02:00
Ariel Ebersberger
1fc0b620c0 Fix flaky template test (#169361) 2026-04-28 14:09:07 +02:00
puddly
ab08153d62 Expose more port metadata when listing serial ports (#169336) 2026-04-28 07:57:13 -04:00
Robert Resch
b47b7fa58c User camera unique id in go2rtc if available (#168603)
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 13:39:55 +02:00
Kurt Chrisford
c50676dee9 Bump actron-neo-api requirement to version 0.5.6 (#169357) 2026-04-28 13:17:10 +02:00
Erik Montnemery
96bd991bb8 Use modern condition API in condition helper tests (#169353) 2026-04-28 13:04:21 +02:00
epenet
7e2a7b9393 Fix shelly device_trigger tests (#169305)
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 13:50:02 +03:00
Erik Montnemery
eb2217cfa6 Use modern condition API in condition tests (#169354) 2026-04-28 12:04:29 +02:00
Matrix
b2269b3dba Bump yolink-api to 0.6.5 (#169350) 2026-04-28 11:59:23 +02:00
Abílio Costa
eb85d7cd98 Allow targeting non-primary entities in triggers (#168857) 2026-04-28 11:49:20 +02:00
Robert Resch
6663717d59 Require local_only to be a boolean on webhook (#169296)
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 11:40:49 +02:00
Mike Degatano
33e5a96a57 Require admin on APIs to create/delete config entries from Supervisor discovery (#169340) 2026-04-28 11:38:39 +02:00
Mike Degatano
7cb4d5ca9c Require admin for addon panel register and delete (#169329) 2026-04-28 11:38:29 +02:00
epenet
7dacd0080b Fix otbr flaky config flow tests in Python 3.14.3 (#169348)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:22:15 +02:00
Franck Nijhof
7f44fe031c Update gotailwind to v0.4.0 (#169316) 2026-04-28 10:54:05 +02:00
epenet
9656aaa6bd Fix Withings via_device race causing flaky test_devices (#169347)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:52:59 +02:00
Leonardo Rivera
bf4b865e83 Fix OneDrive upload service to report all missing files at once (#169221) 2026-04-28 10:31:08 +02:00
Erik Montnemery
73dcc2f5a8 Add missing call to ConditionChecker.async_setup in async_from_config (#169055) 2026-04-28 10:30:11 +02:00
Ronald van der Meer
fa0cf37e2c Fix Duco diagnostics crash on connection error (#169322) 2026-04-28 10:25:18 +02:00
epenet
fa6c6ee4fc Fix bang olufsen flaky tests in Python 3.14.3 (#169345)
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 09:23:31 +02:00
Erik Montnemery
4eb000d863 Add state tracking to EntityConditionBase (#169030) 2026-04-28 09:14:46 +02:00
Artur Pragacz
d3809dd4cb Improve error handling for vacuum clean_area (#168177) 2026-04-28 09:14:17 +02:00
karwosts
2f3a6243f7 Restore state_class to derivative sensor (#163557) 2026-04-28 09:09:21 +02:00
epenet
f36799d139 Refactor timezone handling in mqtt datetime (#169342)
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 09:08:49 +02:00
Erik Montnemery
8d5f83e5f1 Add explicit test of conditions.async_conditions_from_config (#169042) 2026-04-28 08:24:43 +02:00
A. Gideonse
308cb686d2 Refactor indevolt integration to remove magic number constants (#169337)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-28 07:51:04 +02:00
frantzju
63d4f4d03d Bump freebox-api to 1.3.1 (#169335) 2026-04-28 01:08:57 +02:00
A. Gideonse
d8a4b36381 Bump indevolt-api to 1.6.3 (#169338) 2026-04-28 00:22:31 +02:00
Florent Thoumie
c048af2e4e Update to iaqualink 0.7.0 (#169330) 2026-04-27 22:27:38 +01:00
Manu
1a25864890 Bump aiontfy to 0.8.5 (#169327) 2026-04-27 23:13:14 +02:00
Thomas55555
d1922189aa Bump aioautomower to 2.7.4 (#169331) 2026-04-27 23:02:15 +02:00
Miko Stern
7594ead857 Add departure_delay sensor to Israel Rail (#169035) 2026-04-27 20:19:14 +01:00
puddly
8ce14877a4 Register a stub transport for ESPHome serial proxies via USB (#169308) 2026-04-27 15:12:07 -04:00
A. Gideonse
2fb0de3cdb Add actions to Indevolt integration (#163578)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-27 19:46:28 +01:00
Stefan Agner
3e77a4bfb2 Remove unused X-Hass constants (#169324) 2026-04-27 20:35:52 +02:00
puddly
4b9dd68fe7 Remove unnecessary title assertion from Yellow integration tests (#169320) 2026-04-27 20:22:33 +02:00
Stefan Agner
f21ed9054b Remove hassio onboarding bypass for backup endpoints (#169299)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:37:06 -04:00
puddly
53e4d6c8fc Migrate ZHA to SerialPortSelector (#169099) 2026-04-27 13:03:54 -04:00
MohamedBarrak3
6a67c0faf7 Fix grammatical error in synology_dsm missing_data error message (#169306) 2026-04-27 18:17:25 +02:00
epenet
d590f4f0b5 Fix more Shelly tests for Python 3.14.3 (#169304) 2026-04-27 18:16:43 +02:00
epenet
45a6134209 Wait for background tasks in shelly tests (#169301) 2026-04-27 18:40:49 +03:00
Paulus Schoutsen
6893d2b13d Migrate remaining top-level async_register_command handlers to decorators (#169227)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-27 17:29:24 +02:00
Paulus Schoutsen
370babf542 Require admin for diagnostics download and application credentials list (#169234) 2026-04-27 17:24:28 +02:00
epenet
6c89ecb98b Improve subscription handling in shelly tests (#169293)
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 18:17:22 +03:00
Simone Chemelli
cc1eaa72a6 Bump aiocomelit to 2.0.3 (#169300) 2026-04-27 17:12:00 +02:00
Paulus Schoutsen
21a3c5b0ed Require admin for logger.set_level and logger.set_default_level services (#169232)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:03:03 +02:00
Paulus Schoutsen
080eb6af84 Require admin for backup.create and backup.create_automatic services (#169228)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-27 17:01:30 +02:00
Paulus Schoutsen
663538c492 Require admin for cloud preference and support package endpoints (#169205) 2026-04-27 16:59:24 +02:00
Paulus Schoutsen
d119bbe4ef Require admin for configurator.configure service (#169230)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:56:47 +02:00
Ronald van der Meer
0eb204508c Bump python-duco-client to 0.3.9 (#169174) 2026-04-27 15:58:03 +02:00
Peter Grauvogel
ad836b48b0 Fix Green Planet Energy timestamp sensors (#166153)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-04-27 15:41:31 +02:00
epenet
9cc9f240e7 Raise ConfigEntryError on decora_wifi authentication error (#169285) 2026-04-27 15:19:29 +02:00
epenet
91d5c080de Add missing mock in izone tests (#169287) 2026-04-27 15:18:12 +02:00
epenet
9390bf3414 Fix incorrect CONF_ID in twinkly tests (#169284) 2026-04-27 15:16:35 +02:00
Matthias Alphart
a5b65766db Fix flaky KNX test (#169288) 2026-04-27 15:11:57 +02:00
epenet
9321ff504c Fix missing mock in switcher_kis config flow test (#169286)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-27 16:07:58 +03:00
Robert Resch
8a22e84db0 Verify local_only webhook on MockRequest (#169271)
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 14:49:48 +02:00
Paulus Schoutsen
642206699d Require admin for management WebSocket commands (#169209)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 08:34:15 -04:00
epenet
5d98f467fb Migrate esphome to use HassKey (#168873)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 14:16:29 +02:00
Paulus Schoutsen
54727a6f20 Require admin access for hassio Supervisor services (#169226)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 08:06:40 -04:00
Michael
ed371bc644 Deprecate yaml import in Shopping List (#169084) 2026-04-27 13:04:11 +01:00
Manu
3cc6cc9519 Set integration type helper in OTP integration (#169012) 2026-04-27 12:52:02 +02:00
Denis Shulyaka
2053e61a80 Refactor anthropic stream processing (#168980) 2026-04-27 12:51:48 +02:00
Allen Porter
3673a80a37 Fix Google Generative AI token usage statistics tracing (#169222) 2026-04-27 12:48:32 +02:00
wollew
0cc531e333 Remove unused line of code in Velux integration (#168987) 2026-04-27 12:43:50 +02:00
Øyvind Matheson Wergeland
12280dbe63 Add sensor platform tests for nobo_hub (#169008) 2026-04-27 12:43:09 +02:00
Maciej Bieniek
84a5ba26d3 Remove unused name string from AccuWeather strings (#169195) 2026-04-27 12:38:28 +02:00
epenet
6ec4466ad7 Remove deprecated action from google generative AI (#169255) 2026-04-27 12:36:51 +02:00
epenet
cb62562f5b Migrate waze_travel_time to use HassKey for shared semaphore (#169264)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 12:36:37 +02:00
Franck Nijhof
a381a3a741 Update vehicle to 3.0.0 (#169127) 2026-04-27 12:24:48 +02:00
G Johansson
6902504087 Bump holidays lib to 0.95 (#169199) 2026-04-27 11:59:27 +02:00
Manu
64f2fa42fc Add integration type service to HTML5 Push notifications (#169015) 2026-04-27 11:56:37 +02:00
Franck Nijhof
64c9a76fc8 Add binary sensor platform to Fumis integration (#169032)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-27 11:53:25 +02:00
Franck Nijhof
e9fc6b3e74 Merge branch 'master' into dev 2026-04-27 09:50:23 +00:00
epenet
605eea6274 Migrate yeelight to use HassKey for shared custom effects (#169268)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 11:31:35 +02:00
Paulus Schoutsen
86af61d7b5 Generalize radio_frequency test fixtures in components conftest (#169098)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-27 11:25:25 +02:00
epenet
45978f41cd Use runtime_data in xiaomi_aqara (#169262) 2026-04-27 11:18:32 +02:00
epenet
4d4e45854f Use HassKey in wemo (#169261) 2026-04-27 11:09:39 +02:00
epenet
43fa4f2646 Use runtime_data in yeelight (#169260) 2026-04-27 10:52:32 +02:00
epenet
5cedb0b726 Apply Tuya device quirks on device registration (#168897)
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 10:51:40 +02:00
epenet
e3de695b99 Fix openai_conversation tests broken by Python 3.14.3 asyncio changes (#169259) 2026-04-27 10:27:01 +02:00
epenet
e684490219 Fix google_generative_ai_conversation tests broken by Python 3.14.3 asyncio changes (#169258) 2026-04-27 10:20:49 +02:00
Paulus Schoutsen
70947c612c Require admin for fritz.set_guest_wifi_password service (#169233)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 08:55:58 +02:00
epenet
07c144841f Cleanup deprecated SEND_PIN service in blink (#169253) 2026-04-27 08:50:51 +02:00
G Johansson
392c46c028 Remove unique id in dnsip (#169211)
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 08:47:24 +02:00
Paulus Schoutsen
5af3b361c8 Enforce admin policy for assist_satellite ask_question service (#169231)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 06:41:22 +02:00
Paulus Schoutsen
040c960ced Require admin for deCONZ services (#169207)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:33:42 -04:00
Paulus Schoutsen
f2787115d0 Restrict file.read_file service to admin users (#169204) 2026-04-26 20:33:11 -04:00
Paulus Schoutsen
2dd1632fc3 Restrict Insteon link management services to admins (#169206)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:32:44 -04:00
Manu
ed1aefc643 Bump pyloadapi to 2.1.0 (#169215) 2026-04-26 22:11:21 +02:00
Maciej Bieniek
f1bbe4204b Use the carbon monoxide device class in the Airly integration (#169193)
Co-authored-by: Copilot <copilot@github.com>
2026-04-26 17:53:01 +02:00
Luke Lashley
929379799c Add cleaning route for Q7 vacuums (#169177) 2026-04-26 08:42:15 -07:00
Daniel Feinberg
d20d1df382 Add reset buttons for dock strainer and maintenance brush consumables in Roborock (#167395)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-26 08:41:18 -07:00
Raphael Hehl
b5a1b592e9 Bump uiprotect to 10.4.1 (#169192)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-04-26 09:44:41 -05:00
Raphael Hehl
6384e6b38d Add alarm control panel platform to UniFi Protect (#169158)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-26 08:45:08 -05:00
Klaas Schoute
cd98577eb7 Update easyEnergy integration to v3.0.0 (#169162) 2026-04-26 14:23:59 +02:00
Kurt Chrisford
fa9a336725 Bump actron-neo-api to 0.5.5 (#169176) 2026-04-26 14:17:24 +02:00
mithomas
aa0199b442 Add LG Netcast service to send remote control commands (#168649) 2026-04-26 14:14:16 +02:00
Richard Kroegel
a506be4be0 Remove TARGET_TEMPERATURE_RANGE from eurotronic climate (#169182) 2026-04-26 14:09:11 +02:00
Maciej Bieniek
e78a79a29e Remove name from Airly config flow (#169145)
Co-authored-by: Copilot <copilot@github.com>
2026-04-26 13:38:50 +02:00
Paul Bottein
eed4acc745 Add radio_frequency platform to Broadlink (#169128)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-26 11:40:47 +02:00
Tom Matheussen
f1fcca2c75 Bump satel_integra to 1.2.2 (#169180) 2026-04-26 11:19:05 +02:00
javicalle
8673694f6f Try to fix RFLink tests broken by Python 3.14.3 asyncio changes (#169157) 2026-04-26 10:23:56 +02:00
Gustav Åkerström
e8e9914ef5 Template vacuum segments (#167805)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-26 03:33:41 +02:00
Jan Bouwhuis
77c7225750 Fix None is not and allowed Unit of Measurement during MQTT Device setup via the UI (#169173) 2026-04-26 00:47:34 +02:00
Jan Bouwhuis
595f041143 Add MQTT datetime platform (#169091) 2026-04-26 00:45:11 +02:00
Franck Nijhof
2c4f598c06 Add button platform to Fumis integration (#169095) 2026-04-26 00:35:48 +02:00
Michael
1bf77e095d Migrate refoss to use entry.runtime_data (#169105) 2026-04-26 00:18:35 +02:00
Andres Ruiz
d832abc5fc Add climate entity to Waterfurnace (#168729) 2026-04-26 00:17:12 +02:00
Jordi
e7dae028ba Bump aioaquacell to 1.0.0 (#169166) 2026-04-26 00:15:01 +02:00
EnjoyingM
e19d0e75c3 Wolflink: Fixing Codeowner (#169171) 2026-04-26 00:14:11 +02:00
Samuel Xiao
306fc529f2 Switchbot_BLE: bump PySwitchbot to 2.2.0 (#169119) 2026-04-26 00:01:14 +02:00
Marc Mueller
c1894eda83 Detect .start entry point files in hassfest check (#169135) 2026-04-25 23:55:47 +02:00
mayerwin
e9ca9254df Preserve sub-meter GPS accuracy in mobile_app webhooks (#169144)
Co-authored-by: mayerwin <2272127+mayerwin@users.noreply.github.com>
2026-04-25 23:51:56 +02:00
Ronald van der Meer
9ccc2e7473 Add temperature sensor to Duco integration (#169021) 2026-04-25 23:49:17 +02:00
Raphael Hehl
d1bdd6eeeb Upgrade UniFi Network integration quality scale to Silver (#168736)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-25 23:44:37 +02:00
Franck Nijhof
8e3070afe1 Add reconfiguration flow to PVOutput (#169123) 2026-04-25 17:19:11 -04:00
Maciej Bieniek
c48502afda Remove name from AccuWeather config flow (#169142)
Co-authored-by: Copilot <copilot@github.com>
2026-04-25 23:12:14 +02:00
Øyvind Matheson Wergeland
77df31fa83 Add climate platform tests for nobo_hub (#169010) 2026-04-25 23:11:44 +02:00
Denis Shulyaka
f06cd25f4a Add GPT-5.5 support (#169112) 2026-04-25 23:11:19 +02:00
Matthias Alphart
19ebb1da2a Update knx-frontend to 2026.4.25.155016: Add notes to UI expose (#169154) 2026-04-25 23:05:44 +02:00
Christian Lackas
f225d8162b homematicip_cloud: migrate entity unique IDs to stable format (#166580)
Co-authored-by: Christian Lackas <9592452+lackas@users.noreply.github.com>
2026-04-25 23:01:00 +02:00
Mika
759ac2eacd Add battery storage data sensors to SolarEdge integration (#161722)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: it-rec <19797875+it-rec@users.noreply.github.com>
2026-04-25 23:00:49 +02:00
Raphael Hehl
b474a42844 unifiprotect: bump uiprotect to 10.4.0 (#169146)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-25 12:39:40 -05:00
shbatm
db76773727 Standardize ISY994 sensor units and device classes (#169017)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-25 05:44:41 -05:00
Franck Nijhof
48b650c486 Modernize RDW config flow tests (#169129) 2026-04-25 11:03:15 +02:00
Franck Nijhof
9e1c02262e Set parallel updates for Hydrawise platforms (#169101)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-25 10:47:33 +02:00
Andrew Jackson
5a79dd9d99 Bump aiomealie to 1.2.4 (#169125) 2026-04-25 10:42:16 +02:00
Franck Nijhof
c3f66f9e90 Set parallel updates to 0 for Forecast.Solar (#169126) 2026-04-25 10:27:57 +02:00
Joakim Plate
77fd120cd5 Protect update coordinator callbacks (#169122) 2026-04-25 10:15:33 +02:00
Joakim Plate
1978c9772a Filter unknown values from arcam enum (#169124) 2026-04-25 10:14:10 +02:00
Franck Nijhof
6862b808ae Update fumis to v0.4.0 (#169097) 2026-04-25 09:30:44 +02:00
Manu
757deb3a1c Add reconfiguration flow to Notifications for Android TV / Fire TV (#169111) 2026-04-25 09:29:38 +02:00
Franck Nijhof
54e3c3fc9b Extract common entity base class for RDW (#169118) 2026-04-25 09:26:28 +02:00
Michael
e509c9b78a Migrate onvif to use entry.runtime_data (#169106) 2026-04-25 09:23:48 +02:00
A. Gideonse
2eb9f69d1e Bump indevolt-api to 1.4.3 (#169103) 2026-04-25 03:05:49 +02:00
Franck Nijhof
2278423758 Upgrade Elgato quality scale to platinum (#169102) 2026-04-24 23:10:46 +01:00
Franck Nijhof
4625176606 Upgrade Twente Milieu quality scale to platinum (#169104) 2026-04-24 23:29:52 +02:00
Tom Wilkie
e7aa672133 Register MAC address connections on Synology DSM hub device (#169085)
Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
2026-04-24 22:28:22 +02:00
Miko Stern
99185bf9a4 Bump israel-rail-api to 0.1.5 (#169094) 2026-04-24 21:11:46 +02:00
Franck Nijhof
b5e66bbcd0 2026.4.4 (#169092) 2026-04-24 20:37:21 +02:00
Abílio Costa
2deb364ab0 Reinforce Python 3.14 exceptions Agent instructions (#169089) 2026-04-24 20:21:07 +02:00
Raphael Hehl
822b97d096 Upgrade unifi_access quality scale to platinum (#168204)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-24 20:20:39 +02:00
Simone Chemelli
1e0dc86eea Use new UPTIME sensor class for Shelly (#169088) 2026-04-24 20:20:00 +02:00
A. Gideonse
ca70abe240 Refactor button platform to use indevolt-api 1.4.2 (#169063)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-24 19:11:01 +01:00
Franck Nijhof
f479b0ad6a Bump version to 2026.4.4 2026-04-24 18:08:48 +00:00
Bram Kragten
458b5fe8bf Update frontend to 20260325.8 (#169076) 2026-04-24 18:07:57 +00:00
Robert Resch
9621307cb0 Validate local_only user for signed requests (#169066) 2026-04-24 18:06:48 +00:00
Maciej Bieniek
4507f9a8d8 Bump aiotractive to 1.0.3 (#169059) 2026-04-24 18:06:47 +00:00
Maciej Bieniek
19dd68b7fc Slow down Tractive API polling to avoid 429 too many requests (#169057)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 18:06:45 +00:00
Daniel Hjelseth Høyer
245b9ed4c0 Update Tibber library, 0.37.2 (#169027) 2026-04-24 18:06:44 +00:00
Robert Resch
838feef660 Validate local_only user property during ws auth phase (#168812) 2026-04-24 18:06:42 +00:00
Jan Bouwhuis
ca4d36db1a Cancel and await idle_start future if the task was canceled after an IMAP connection was lost (#168662)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-04-24 18:06:41 +00:00
Joakim Plate
39b690b22c Correct state/device class for water in gardena (#168637) 2026-04-24 18:06:39 +00:00
Allen Porter
ed560f0ba7 Add Roborock fan speed validation and error handling (#168623) 2026-04-24 18:06:38 +00:00
Nils Ove Erstad
0db50acb89 Fix MQTT JSON light restoring None color_mode on startup (#168608)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2026-04-24 18:06:36 +00:00
Ariel Ebersberger
446d89aee2 Disable rflink tests broken by Python 3.14.3 asyncio changes (#169074)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 19:06:23 +01:00
Ariel Ebersberger
6fe1862d15 Disable dsmr tests broken by Python 3.14.3 asyncio changes (#169064)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 19:06:14 +01:00
Ariel Ebersberger
4f5d0a7305 Disable plex tests broken by Python 3.14.3 asyncio changes (#169069)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 19:06:11 +01:00
Simone Chemelli
f84bf99105 Bump aioamazondevices to 13.4.3 (#168536) 2026-04-24 18:04:30 +00:00
Khole
7fad242ad0 Hive - Bump pyhive-integration to 1.0.9 (#168489) 2026-04-24 18:04:29 +00:00
MohamedBarrak3
fcd6f78f35 Fix case-sensitive MIME type check in Google Generative AI TTS (#168458) 2026-04-24 18:04:27 +00:00
Raj Laud
056ff957e8 Fix Victron BLE false reauth on unrecognised advertisement mode bytes (#168209)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 18:04:26 +00:00
albaintor
9cf95404cf Fixed Kodi Media Browsing (#165819) 2026-04-24 18:04:24 +00:00
Ariel Ebersberger
4d8acfa61c Disable knx tests broken by Python 3.14.3 asyncio changes (#169079)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 18:54:21 +01:00
Maciej Bieniek
9369a5dc93 Slow down Tractive API polling to avoid 429 too many requests (#169057)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 19:51:07 +02:00
WardZhou
8b2afb4e66 Add a dynamic sensitivity slider for Matter sensors (#167710)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2026-04-24 18:50:23 +01:00
Bram Kragten
a53d3ea9eb Update frontend to 20260325.8 (#169076) 2026-04-24 19:50:11 +02:00
Maciej Bieniek
e422c08d4e Support media player for Shelly Wall Display (#168494)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 19:49:31 +02:00
Michael
599fe252ef Fix feedreader tests broken by Python 3.14.3 asyncio changes (#169080) 2026-04-24 18:49:10 +02:00
Erik Montnemery
aad93fd577 Add tests asserting condition features (#168881) 2026-04-24 18:41:39 +02:00
Stefan Agner
a19aebed16 Keep add-on update entity in progress across post-install refresh (#168756)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:09:25 +02:00
mnaggatz
c9d8257465 Return None for Velux cover position when unknown (#168566) 2026-04-24 17:42:30 +02:00
shbatm
5ee6a2181f Fix Flume sensor units and device classes (#169013) 2026-04-24 10:27:37 -05:00
Simone Chemelli
ec18e0c6d4 Add uptime device class to the sensor platform (#164266)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 16:18:17 +01:00
Paulus Schoutsen
c4426b9476 Add radio_frequency platform to ESPHome (#168448)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 10:48:06 -04:00
Paulus Schoutsen
c4fd458d03 Add Honeywell String Lights integration (#168450) 2026-04-24 10:43:56 -04:00
Robert Resch
dd71d6cd50 Validate local_only user for signed requests (#169066) 2026-04-24 16:27:15 +02:00
Erik Montnemery
7d494f687e Adjust compound conditions (#169054)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-24 16:21:13 +02:00
Paulus Schoutsen
45adc3d477 Bump rf-protocols to 2.1.0 (#169062) 2026-04-24 09:53:41 -04:00
Tomer
59766bb249 Victron GX: quality scale adjustments (#168988)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 15:45:31 +02:00
Martin
d849b12bc7 Add distance device class to Ecowitt lightning distance sensors (#168995) 2026-04-24 15:41:51 +02:00
Manu
8cd2d397d1 Add data descriptions to config flow in OTP integration (#168989) 2026-04-24 15:41:18 +02:00
Jan Bouwhuis
8580a6436d Add MQTT date platform (#168998) 2026-04-24 15:36:21 +02:00
A. Gideonse
7b3b1e34fa Bump indevolt-api to 1.4.2 (#169061) 2026-04-24 15:24:45 +02:00
Maciej Bieniek
bb9520856f Bump aiotractive to 1.0.3 (#169059) 2026-04-24 15:14:17 +02:00
Paulus Schoutsen
032dce20b1 Bump aioesphomeapi to 44.21.0 (#169056) 2026-04-24 08:14:12 -05:00
Erik Montnemery
a92277b7fa Add method Script.unload (#169036) 2026-04-24 15:12:17 +02:00
Willem-Jan van Rootselaar
10d78d280a Add multiple heating system circuit support to BSBlan (#165992)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 15:12:09 +02:00
Renat Sibgatulin
cf1faf3a20 Refactor AirQ config flow tests (#169053) 2026-04-24 15:11:29 +02:00
Ariel Ebersberger
ccd82e6b8b Disable sonos tests broken by Python 3.14.3 asyncio changes (#169046)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 14:59:58 +02:00
Erik Montnemery
db01b8e421 Migrate async_conditions_from_config to ConditionChecker (#169033) 2026-04-24 14:28:10 +02:00
A. Gideonse
bf36c3d193 Bump indevolt-api to 1.4.0 (#169050) 2026-04-24 13:20:19 +02:00
Paulus Schoutsen
dd2a90a31f Add radio_frequency entity integration (#168447)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-04-24 06:37:28 -04:00
Mattie
eb42804871 Add binary sensor platform to Qube heat pump (#166611)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-24 12:30:49 +02:00
Erik Montnemery
6b5bbede52 Update websocket_api.handle_test_condition to use modern condition API (#169029) 2026-04-24 12:25:39 +02:00
Marc Mueller
28c3ca37b9 Refactor pylint plugins to use match statements (#168894) 2026-04-24 12:25:16 +02:00
Franck Nijhof
76376d6b26 Add pylint plugin to detect IP-based unique IDs in config entries (#168822)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-04-24 10:59:59 +02:00
Raphael Hehl
dbb750a583 Add AV1 support for HLS fallback (#161492)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-24 10:07:26 +02:00
Trendafil Gechev
aec8d00c95 Add WLED segment freeze support (#168424) 2026-04-24 10:06:49 +02:00
A. Gideonse
39fbd2ccbd Bump indevolt-api to 1.3.1 (#168986) 2026-04-24 09:58:59 +02:00
Denis Shulyaka
1942f12e55 Refactor Anthropic model args (#169014) 2026-04-24 09:58:16 +02:00
Manu
eb825796f9 Remove name from config flow of Notifications for Android TV /Fire TV (#169024) 2026-04-24 09:47:03 +02:00
Maciej Bieniek
ac6e425748 Add tilt and rotation binary sensors for Shelly Cury (#169002)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 09:45:13 +02:00
Øyvind Matheson Wergeland
cf092c63c0 Declare PARALLEL_UPDATES = 0 for nobo_hub platforms (#169011) 2026-04-24 09:44:54 +02:00
Daniel Hjelseth Høyer
4d8f3dfaf7 Update Tibber library, 0.37.2 (#169027) 2026-04-24 09:44:43 +02:00
Erik Montnemery
ed7f2b1810 Migrate compound conditions to ConditionChecker (#169028) 2026-04-24 09:44:28 +02:00
Raphael Hehl
3ff2b4424f Bump uiprotect to 10.3.1 (#169031) 2026-04-24 09:44:08 +02:00
Franck Nijhof
f8e6137d28 Update fumis to v0.3.0 (#168984) 2026-04-24 09:24:05 +02:00
Abílio Costa
6a57382eff Allow extracting non-primary entities in websocket command (#168860)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-24 09:13:51 +02:00
Erik Montnemery
cebe4aa685 Refactor condition API (#168815)
Co-authored-by: Artur Pragacz <artur@pragacz.com>
2026-04-24 07:46:34 +02:00
Ronald van der Meer
32b9a21294 Bump python-duco-client to 0.3.6 (#169020) 2026-04-24 05:18:55 +02:00
Tomer
7de684d47b Victron GX: Add reconfiguration flow (#168997)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
2026-04-23 23:35:30 +02:00
Samuel Xiao
5a9bb972d0 Add sensor description for Lock state in Switchbot Cloud (#168607)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-23 23:34:47 +02:00
Øyvind Matheson Wergeland
e1a73fbeed Add select platform tests for nobo_hub (#168738)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:21:26 +02:00
Tom Matheussen
20a88eb21e Add entity availability to Satel Integra (#168476)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-23 23:20:00 +02:00
Raphael Hehl
0bb678cacf Bump uiprotect to 10.3.0 (#168992)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-23 22:18:18 +01:00
Harvey
0e817c5c90 Bump HueBLE to 2.2.2 (#167677) 2026-04-23 22:48:44 +02:00
fender4645
e5cd1e2830 Tessie: log warning instead of raising UpdateFailed for missing energy history (#168068)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-23 22:47:15 +02:00
kostavelikov
b4c8452a5a Add open (unlatch) support to Homee locks (#168532) 2026-04-23 22:32:51 +02:00
Tomer
86ffb9eccb Victron GX: Add exception translations (#168762)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 22:30:47 +02:00
epenet
7bf3e75bc8 Fix invalid notification/event handling in Tuya tests (#168854)
Co-authored-by: Copilot <copilot@github.com>
2026-04-23 22:14:20 +02:00
Tomer
5394c764b4 Victron GX: Add strict typing (#168907)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 22:11:33 +02:00
Tomer
1cd34e8477 Victron GX stale devices (#168706)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-23 22:06:13 +02:00
Willem-Jan van Rootselaar
0122b2811a Bump python-bsblan to 5.2.0 (#168892) 2026-04-23 22:05:58 +02:00
Artur Pragacz
3f2bc45686 Migrate to entity services in monoprice (#168972) 2026-04-23 22:05:20 +02:00
Franck Nijhof
4612a72cd2 Add reconfiguration flow to Fumis integration (#168759)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 21:53:40 +02:00
Michael
8448ace289 Migrate shopping_list to use entry.runtime_data (#168911) 2026-04-23 21:30:11 +02:00
Ariel Ebersberger
19fd6e2036 Fix b&o race conditions for Python 3.14.3 (#168885) 2026-04-23 21:26:54 +02:00
c0ffeeca7
94ca503f71 Media browser: apply sentence-style capitalization (#168971) 2026-04-23 21:24:25 +02:00
Artur Pragacz
fbf30e64a0 Migrate to entity services in amcrest (#168974) 2026-04-23 21:23:43 +02:00
Jan Bouwhuis
49022b69b0 Add MQTT time platform (#168898) 2026-04-23 21:12:28 +02:00
Mick Vleeshouwer
13105bd0b7 Migrate cover platform to entity descriptions in Overkiz (#141330)
Co-authored-by: Copilot <copilot@github.com>
2026-04-23 19:58:17 +02:00
epenet
438c1e9c3d Remove leftover hass.data[DOMAIN] usage from insteon (#168880)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-23 15:22:44 +02:00
Christophe Gagnier
b0ecc2f36a Add reconfigure flow to TechnoVE integration (#168466)
Co-authored-by: Moustachauve <2206577+Moustachauve@users.noreply.github.com>
2026-04-23 15:09:35 +02:00
Ronald van der Meer
19f19e00f6 Add UCRH sensor support and warn on unknown node types in Duco (#168758) 2026-04-23 15:02:36 +02:00
Raphael Hehl
95ec39ac1a unifi_access: add direction attribute to access events (#168853)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: home-assistant[bot] <78085893+home-assistant[bot]@users.noreply.github.com>
2026-04-23 14:44:57 +02:00
Matthias Alphart
c6b4594e7a Update knx-frontend to 2026.4.22.141111 (#168837) 2026-04-23 14:44:24 +02:00
Joost Lekkerkerker
cf0b5c6e51 Migrate GitHub to subentries (#160564)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-04-23 14:23:47 +02:00
epenet
187fcd10b3 Add async_panel_exists helper to frontend and use it across integrations (#168884) 2026-04-23 14:14:45 +03:00
TheJulianJES
ed1cba02ae Migrate Matter integration to use runtime_data (#168862) 2026-04-23 13:03:08 +02:00
epenet
b213eb23c8 Reduce context switching in Tuya initialisation (#168830)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-23 12:48:15 +02:00
renovate[bot]
30d362dc8e Update uv to 0.11.7 (#168864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 10:43:51 +02:00
Erik Montnemery
67c818c7a8 Add comment to trigger base class (#168882) 2026-04-23 10:42:07 +02:00
epenet
5927f50bd2 Use runtime_data in Huawei LTE (#168876)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:48:45 +02:00
epenet
66d7afa442 Migrate flux_led to use HassKey for FLUX_LED_DISCOVERY (#168872)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 08:56:20 +02:00
epenet
51fcdaff7a Migrate slimproto to use runtime_data (#168869)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 08:55:37 +02:00
Raphael Hehl
67baec27cf unifi_access: add missing WebSocket handlers for remote_view and device_update events (#168850)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-23 08:50:09 +02:00
epenet
d45941d648 Migrate kraken to use runtime_data (#168870)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 08:24:56 +02:00
Raphael Hehl
a338d04441 unifi_access: bump py-unifi-access to 1.3.0 (#168851)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-23 08:24:41 +02:00
epenet
69eca62446 Clean up leftover hass.data[DOMAIN] usage in keenetic_ndms2 (#168871)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 08:20:48 +02:00
Franck Nijhof
507b5f1bbf Add pylint plugin to detect polling interval fields in config flows (#168849) 2026-04-22 23:41:43 +02:00
A. Gideonse
ee8a15b368 Fix incorrect sensor definition for Indevolt Gen-1 devices (#168835)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-22 22:03:13 +02:00
Erik Montnemery
7f92d88606 Replace climate-control device with thermostat in climate translations (#161419) 2026-04-22 21:02:54 +02:00
epenet
cc1c5e788f Revert Tuya camera quirk changes (#168820) 2026-04-22 20:54:49 +02:00
epenet
1159946391 Bump tuya-device-handlers to 0.0.18 (#168821) 2026-04-22 20:53:37 +02:00
Erik Montnemery
46208c034e Add tests asserting air_quality condition features (#168731) 2026-04-22 20:42:42 +02:00
puddly
abdd132bdc Register optimized ESPHome serial proxy transport with serialx (#168817) 2026-04-22 13:16:56 -04:00
Denis Shulyaka
1b71ef2a60 Add gpt-image-2 model support for OpenAI (#168826) 2026-04-22 18:13:04 +01:00
Abílio Costa
f0445a792d Add dummy Claude skill instruction for testing (#168829) 2026-04-22 18:35:24 +02:00
Abílio Costa
24e3842319 Rename Claude's integration skill (#168825) 2026-04-22 17:04:49 +01:00
epenet
54aae2c7de Ensure Tuya (stale) device is removed before adding new (#168721) 2026-04-22 16:58:00 +01:00
epenet
ea3e8cf9b0 Add tests for Tuya dynamic add/remove device (#168824) 2026-04-22 16:13:56 +01:00
Abílio Costa
a16f6f965e Improve claude gh pr review summary + business logic lib note (#168819) 2026-04-22 16:05:28 +01:00
Manu
d772320f06 Record notifications sent via ntfy.publish action in ntfy integration (#166352) 2026-04-22 17:01:31 +02:00
Michael Hansen
8a74b41db5 Add audio processing settings to speech-to-text entities (#167246) 2026-04-22 08:43:21 -05:00
Raphael Hehl
fddc6aaf38 Add entity translations to UniFi integration (#168739)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-22 15:40:35 +02:00
Franck Nijhof
fab59d7a13 Add pylint plugin to enforce entry.runtime_data over hass.data[DOMAIN] (#168760) 2026-04-22 15:31:58 +02:00
Robert Resch
1345356bdc Validate local_only user property during ws auth phase (#168812) 2026-04-22 14:07:47 +02:00
Shay Levy
be07fed774 Remove unused hass.data[DOMAIN] in LG webOS TV (#168813) 2026-04-22 13:58:44 +02:00
Erwin Douna
d17f6a1509 Firefly III consistency with access token (#168565) 2026-04-22 11:12:40 +02:00
Thijs W.
f3932f2342 Improve exception handling for frontier_silicon (#168635)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-04-22 10:58:09 +02:00
Mick Vleeshouwer
598be31daf Improve test structure for Overkiz (#168728) 2026-04-22 10:10:18 +02:00
epenet
9b2a81614f Simplify Tuya runtime_data (#168718)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-22 10:02:24 +02:00
Øyvind Matheson Wergeland
f53c89d3bc Translate override_type options in nobo_hub (#168752) 2026-04-22 09:59:51 +02:00
dependabot[bot]
ac6991072f Bump github/codeql-action from 4.35.1 to 4.35.2 (#168754)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 09:53:11 +02:00
Jan Bouwhuis
018e8e06fa Cancel and await idle_start future if the task was canceled after an IMAP connection was lost (#168662)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-04-22 09:43:22 +02:00
Ronald van der Meer
0ffc9694a7 Bump python-duco-client to 0.3.4 (#168757) 2026-04-22 09:41:21 +02:00
Marc Mueller
8d8b30a41e Update mypy to 1.20.2 (#168741) 2026-04-22 09:38:08 +02:00
Tomer
9b7f61d862 Victron GX: Diagnostics (#168700)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-22 09:36:49 +02:00
epenet
368f2f44be Use HassKey in zeroconf (#168707)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 09:26:13 +02:00
LG-ThinQ-Integration
ad6a910244 Bump thinqconnect to 1.0.12 (#168753)
Co-authored-by: YunseonPark-LGE <yunseon.park@lge.com>
2026-04-22 09:21:15 +02:00
Leonardo Rivera
840b44039d Fix OneDrive upload service to support multiple files (#168512) 2026-04-22 09:11:27 +02:00
Ronald van der Meer
1943675a64 Add DHCP discovery to Duco integration (#168730) 2026-04-22 08:32:05 +02:00
Linkplay2020
161e05b075 Updata wiim to 0.1.2 (#168671)
Co-authored-by: Tao Jiang <tao.jiang@linkplay.com>
2026-04-22 08:07:17 +02:00
Paulus Schoutsen
f2d5ca3582 Rename SerialSelector to SerialPortSelector (#168744)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-22 07:47:28 +02:00
Florent Thoumie
551af8caef Rename iAqualink to iAquaLink (#168743) 2026-04-22 07:26:48 +02:00
Johan Henkens
201c575316 Bump aioesphomeapi to 44.18.0 (#168749) 2026-04-22 06:12:32 +02:00
tronikos
703860ee6e Add support for away mode in ESPHome water heater (#167951)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-22 05:37:47 +02:00
puddly
cb021f0b6b Allow integrations to contribute serial port scanning helpers (#168660)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2026-04-21 21:15:57 -04:00
Øyvind Matheson Wergeland
50dbff31b0 Fix nobo_hub override type description (#168740) 2026-04-21 23:30:06 +02:00
MohamedBarrak3
800299077e Fix case-sensitive MIME type check in Google Generative AI TTS (#168458) 2026-04-21 23:26:31 +02:00
Andrew Jackson
f40b269752 Version checking of Transmission (#168429)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-21 23:26:14 +02:00
David
f2105c07de Expose Lutron Caseta shade battery status on covers (#165180) 2026-04-21 23:25:45 +02:00
Erwin Douna
d23dbfb214 Add volumes to Portainer (#167326) 2026-04-21 23:23:27 +02:00
Erwin Douna
de6586684a Add recreate container button to Portainer (#167163) 2026-04-21 23:21:45 +02:00
Avi Miller
9a08b941bb Limit LIFX bulb changes to the values that are actually changing (#168618) 2026-04-21 23:08:04 +02:00
Øyvind Matheson Wergeland
51b9f004e9 Introduce NoboBaseEntity in nobo_hub (#168724)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-21 23:03:45 +02:00
epenet
fe443f4ce9 Use runtime_data in wyoming integration (#168619) 2026-04-21 22:50:06 +02:00
Thijs W.
b0ba7ec6ec Frontier silicon: use correct command to restart stopped stream (#168633)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-21 22:36:44 +02:00
Florent Thoumie
156901c290 iaqualink: Add basic DHCP discovery for iAquaLink devices (#168256)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-21 22:34:37 +02:00
Franck Nijhof
b6271e59fa Add sensor platform to Fumis integration (#168680)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-21 22:24:13 +02:00
Franck Nijhof
17cd0aa474 Add DHCP discovery to Fumis integration (#168735) 2026-04-21 22:20:51 +02:00
Stefan Agner
79f12f658a Improve Supervisor update entity progress and data refresh (#168712)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:01:01 +02:00
Simone Chemelli
e13b63342e Disable DNS queries in tests (#165603)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-21 20:42:30 +02:00
Erik Montnemery
3500f0a195 Revert "Add Broadlink infrared emitter support to native infrared platform" (#168717) 2026-04-21 18:19:22 +02:00
Øyvind Matheson Wergeland
4a93dcb936 Add data descriptions for nobo_hub config and options flows (#168723)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 18:02:54 +02:00
Ronald van der Meer
27ddb5b6a4 Claim platinum quality scale for Duco integration (#168719) 2026-04-21 17:30:58 +02:00
Raphael Hehl
0ff38cdc7f Fix/unifi access uah door and thumbnail (#168708) 2026-04-21 17:04:49 +02:00
Mick Vleeshouwer
1a8adea358 Add sensor entity tests to Overkiz (#168701) 2026-04-21 16:53:14 +02:00
Ariel Ebersberger
2a85046584 Fix shelly tests - bluetooth config flow (#166850) 2026-04-21 16:46:33 +02:00
Florent Thoumie
fc85d35d4c Add initial quality scale assessment to iaqualink, set to bronze (#167738)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-21 16:39:25 +02:00
Raphael Hehl
608b92be40 unifi: implement action-exceptions quality scale rule (#168559)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-21 16:25:41 +02:00
renovate[bot]
af01b41e52 Update infrared-protocols to 2.0.0 (#168667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-04-21 15:13:58 +01:00
MohamedBarrak3
f257d54d1e Bump mcstatus to 13.1.0 (#168716) 2026-04-21 16:09:14 +02:00
Denis Shulyaka
7c7c075df4 Filter Anthropic schema (#168542) 2026-04-21 09:55:00 -04:00
Denis Shulyaka
5a487d452d Remove retired Claude Haiku 3 model (#168657) 2026-04-21 09:53:56 -04:00
arsenicks
a4138fa4cd Sonos - Add support for TV Autoplay and Ungroup on Autoplay (#167956)
Co-authored-by: Gustav Åkerström <23389010+gustavakerstrom@users.noreply.github.com>
2026-04-21 15:28:39 +02:00
epenet
a6b4609313 Combine AWS hass.data entries into a single dataclass (#168711) 2026-04-21 15:24:14 +02:00
Aaron Ten Clay
95e9405cd0 Preserve Fahrenheit precision in google_assistant temperature range (#168672) 2026-04-21 15:22:21 +02:00
bkobus-bbx
d990ec1b65 Bump blebox_uniapi to v2.5.1 (#168713) 2026-04-21 15:21:24 +02:00
epenet
52d7dcbcc8 Drop redundant BackupManager annotation in aws_s3/google_drive diagnostics (#168714)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 15:18:57 +02:00
Ronald van der Meer
8e1346fd1f Add dynamic device discovery and stale device removal to Duco integration (#168675) 2026-04-21 15:18:27 +02:00
epenet
a2485960d8 Move Tuya listener classes to separate module (#168636) 2026-04-21 15:15:14 +02:00
epenet
a06ffe6379 Use runtime_data in abode integration (#168709) 2026-04-21 15:05:49 +02:00
Martin Claesson
966e8aeca4 Add Kiosker binary sensor platform (#168507)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-21 14:52:15 +02:00
Abílio Costa
d7f666a661 Implement doorbell.rang trigger (#168388) 2026-04-21 14:43:34 +02:00
Thomas Rupprecht
671b3e01ad Allow requesting spaceapi without authentication and with cors headers (#160797) 2026-04-21 14:31:07 +02:00
Erwin Douna
a85c82ae24 Add dynamic update interval to Tado (#160723)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-04-21 14:28:41 +02:00
Denis Shulyaka
d9af83a03f Fix telegram_bot.send_message_draft action description (#168212)
Co-authored-by: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-21 14:54:08 +03:00
Erik Montnemery
c489980551 Add duration to more conditions (#168383) 2026-04-21 13:41:53 +02:00
epenet
06400ab688 Use runtime_data in zamg (#168699) 2026-04-21 13:06:14 +02:00
epenet
9d7d56c5bf Use runtime_data in Yardian (#168697) 2026-04-21 13:05:09 +02:00
epenet
b1fcc0ebde Use runtime_data in youtube (#168696) 2026-04-21 13:04:49 +02:00
epenet
12af4bd0f4 Use runtime_data in yolink (#168693) 2026-04-21 13:04:19 +02:00
Retha Runolfsson
6bb083ee61 Bump pySwitchbot to 2.1.0 (#168692) 2026-04-21 13:03:47 +02:00
Denis Shulyaka
a6f9246c2f Add myself as a codeowner for OpenAI integration (#168705) 2026-04-21 13:01:45 +02:00
epenet
3222472f10 Use runtime_data in youless (#168694)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-21 12:44:18 +02:00
epenet
e620426002 Use runtime_data in yamaha_musiccast (#168691) 2026-04-21 11:33:02 +02:00
Mike Degatano
6e61a60eba refactor(hassio): store aiohasupervisor models directly in hass.data using typed HassKey (#168400)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 11:24:07 +02:00
epenet
6942066930 Use runtime_data in wiffi integration (#168687) 2026-04-21 10:58:47 +02:00
Marc Mueller
7c1fd1a237 Update aiousbwatcher to 1.1.2 (#168688) 2026-04-21 10:56:00 +02:00
epenet
3fd77b0d7a Use runtime_data in wilight integration (#168686) 2026-04-21 10:47:53 +02:00
Allen Porter
f73f1df5a2 Add Roborock fan speed validation and error handling (#168623) 2026-04-21 10:47:32 +02:00
Florent Thoumie
fb89d94957 Add missing data_description strings to iaqualink (#168670) 2026-04-21 10:30:15 +02:00
epenet
a9c3854d69 Use runtime_data in whois (#168684) 2026-04-21 10:28:45 +02:00
renovate[bot]
ef1a5ea2df Update zizmor (#168666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 10:14:26 +02:00
Raphael Hehl
514d5e570a Bump py-unifi-access to version 1.2.0 (#168679) 2026-04-21 10:13:31 +02:00
epenet
9de658b918 Use runtime_data in WeatherKit (#168682) 2026-04-21 09:43:54 +02:00
Franck Nijhof
ac4e746977 Add reauthentication flow to Fumis integration (#168645) 2026-04-21 09:32:13 +02:00
Mick Vleeshouwer
e10f59c936 Add additional cover fixtures to Overkiz (#168661) 2026-04-21 08:57:28 +02:00
Andres Ruiz
fb171809ec Update waterfurnace to 1.7.1 (#168665) 2026-04-21 08:56:45 +02:00
epenet
137122ebb5 Use runtime_data in weatherflow integration (#168622) 2026-04-21 08:55:50 +02:00
epenet
502dc5075d Use runtime_data in weatherflow_cloud integration (#168624)
Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
2026-04-21 08:55:29 +02:00
Marc Mueller
42232cfe3f Fix esphome test ResourceWarning (#168181) 2026-04-21 08:55:05 +02:00
epenet
0ae1236acb Use runtime_data in ws66i integration (#168628) 2026-04-21 08:54:49 +02:00
Ariel Ebersberger
63f84af4ff Fix tplink tests for Python 3.14.3 (#168361) 2026-04-21 08:54:21 +02:00
Ronald van der Meer
89fe56c599 Add reconfiguration flow to Duco integration (#168652) 2026-04-21 07:46:50 +02:00
Rene Nulsch
2fb1ed443a Validate directory_path and file_name in telegram_bot.download_file (#168656) 2026-04-21 07:46:43 +02:00
Glenn Vandeuren (aka Iondependent)
ea8f82e9ba Bump nhc to 0.8.0 (#168651)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com>
2026-04-20 22:09:19 +01:00
puddly
31dc02c3ee Bump universal-silabs-flasher to 1.1.0 (#168647) 2026-04-20 23:02:53 +02:00
Nils Ove Erstad
70ec6fa654 Fix MQTT JSON light restoring None color_mode on startup (#168608)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2026-04-20 21:59:03 +02:00
puddly
c2946404ea Bump ZHA to 1.2.1 (#168644) 2026-04-20 15:42:04 -04:00
Abílio Costa
f715bcd7c1 Change Claude gh review agent back to skill (#168642) 2026-04-20 20:59:20 +02:00
Manu
0c0e61e133 Remove hunterjm from Xbox integration codeowners (#167024) 2026-04-20 20:58:43 +02:00
Tomer
305761e7de Victron GX: device_tracker platfrom (#168462)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-20 20:54:58 +02:00
puddly
3b81f09765 Bump serialx to 1.4.1 (#168640) 2026-04-20 20:53:51 +02:00
epenet
a2cc7d0fca Use runtime_data in watttime integration (#168630) 2026-04-20 20:46:41 +02:00
Ronald van der Meer
038b56e5eb Claim Silver quality scale for Duco integration (#168620) 2026-04-20 19:45:57 +01:00
Franck Nijhof
0edcb8d60f Set parallel updates for PVOutput sensor platform (#168643) 2026-04-20 20:45:11 +02:00
Stefan Agner
cc8000ed89 Remove hassio-main panel registration (#168626)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 19:42:10 +02:00
Mick Vleeshouwer
a92dcaaf5f Add first cover entity tests to Overkiz (#165670)
Co-authored-by: Copilot <copilot@github.com>
2026-04-20 18:30:26 +01:00
Joakim Plate
e889541d2e Correct state/device class for water in gardena (#168637) 2026-04-20 19:02:29 +02:00
Michael
85e9d3c6a8 Migrate Z-Wave.Me to use runtime_data (#168562) 2026-04-20 18:29:46 +02:00
Robert Resch
fe9db39684 Add docker syntax to all Docker files (#168350) 2026-04-20 17:31:04 +02:00
Assaf Akrabi
253d3e1758 Migrate lib to aiorussound for Russound RNET (#168484) 2026-04-20 17:21:45 +02:00
Raphael Hehl
dcb5f0d533 Improve UniFi config flow quality scale: config-flow and config-flow-test-coverage (#168477)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-20 17:16:51 +02:00
epenet
d5e4be317c Use runtime_data in wolflink integration (#168625) 2026-04-20 17:09:49 +02:00
albaintor
0ebf4d86f5 Fixed Kodi Media Browsing (#165819) 2026-04-20 17:09:03 +02:00
Klaas Schoute
1a86913239 Merge config flows for powerfox integration (#164019) 2026-04-20 17:07:45 +02:00
Max R
f2c010aaaf feat(citybikes): add number of ebikes attribute (#166229) 2026-04-20 17:02:24 +02:00
Erik Montnemery
74de32377e Improve async_get_system_info tests (#168586)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-20 16:47:40 +02:00
Franck Nijhof
901925ad54 Add Fumis pellet stove integration (#168515) 2026-04-20 16:25:12 +02:00
Øyvind Matheson Wergeland
defbfe17a3 Fix nobo_hub via_device warning (#168595) 2026-04-20 16:25:05 +02:00
Merlin Schumacher
9795f55af3 Remove reference to deprecated state STANDBY from universal media player (#160930)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-04-20 16:10:34 +02:00
johanzander
967c5d2092 Fix KeyError in Growatt server login response handling (#168482)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 15:41:38 +02:00
Paulus Schoutsen
cdecff9380 Use dedicated power commands for LG infrared (#168488)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-04-20 15:36:00 +02:00
Robert Resch
59ceb7c58c Revert "Update PyTurboJPEG to 2.2.0" (#168617) 2026-04-20 15:28:17 +02:00
Kurt Chrisford
d66b9f4316 Bump actron-neo-api to 0.5.3 (#167732) 2026-04-20 14:58:43 +02:00
Christophe Gagnier
40477ff87b Bump python-technove to 2.1.1 (#168403)
Co-authored-by: Moustachauve <2206577+Moustachauve@users.noreply.github.com>
2026-04-20 14:52:07 +02:00
epenet
d96b626497 Use runtime_data in vallox integration (#168604)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 14:41:53 +02:00
epenet
0c294b342c Use runtime_data in verisure integration (#168605)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 14:39:53 +02:00
Marc Mueller
1f64ca4a8d Update pydantic to 2.13.2 (#168601)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-04-20 14:36:07 +02:00
epenet
79ae0e6c49 Use runtime_data in venstar integration (#168613) 2026-04-20 14:32:13 +02:00
epenet
dc0052552a Use runtime_data in vera integration (#168614) 2026-04-20 14:31:22 +02:00
epenet
77f4baa79e Use runtime_data in volumio integration (#168616) 2026-04-20 14:30:34 +02:00
Matthias Alphart
52377b958b Update knx-frontend to 2026.4.19.175239 (#168568) 2026-04-20 14:28:46 +02:00
Denis Shulyaka
09105693c7 Filter OpenAI schema (#168543) 2026-04-20 14:28:13 +02:00
epenet
db838f67d7 Move vallox service registration to services.py (#168612)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:26:57 +02:00
renovate[bot]
720fd6d802 Update ruff (#168240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edenhaus <26537646+edenhaus@users.noreply.github.com>
2026-04-20 14:25:43 +02:00
epenet
b43d6a70da Fix cookie file suppression in verisure (#168609) 2026-04-20 13:41:41 +02:00
Erik Montnemery
b5caabcbae Fix quantum_gateway tests (#168610) 2026-04-20 13:37:37 +02:00
Raphael Hehl
9bb46494d3 unifi: implement parallel-updates quality scale rule (#168563) 2026-04-20 13:26:17 +02:00
Erik Montnemery
ca066b94c5 Deprecate legacy device tracker (#168387) 2026-04-20 13:20:36 +02:00
Erik Montnemery
8de6fa63cd Allow passing a set of event types to logbook.async_subscribe_events (#168163) 2026-04-20 13:19:31 +02:00
Erik Montnemery
866f41791a Deprecate support for local installation of dependencies (#168164) 2026-04-20 13:19:13 +02:00
epenet
5b3d2f823f Always load all platforms in sfr_box (#168594)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 13:05:51 +02:00
Ariel Ebersberger
e1d38fa237 Fix flaky airtouch5 test for Python 3.14.3 (#168366) 2026-04-20 12:51:18 +02:00
epenet
8eef269ce3 Use runtime_data in upb integration (#168600)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 12:50:22 +02:00
David Bonnes
8afee640ef Remove device ids from extra_state_attrs of Evohome's Button entities (#168517)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 12:17:09 +02:00
Maciej Bieniek
10fd51b34d Add ice phenomena sensor to IMGW-PIB integration (#168548) 2026-04-20 12:07:26 +02:00
Copilot
a1cde0308a Clarify Copilot review guidance for validated entity action inputs (#168449)
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-04-20 11:56:21 +02:00
Erik Montnemery
5c14025e70 Sort keys in dict returned by async_get_system_info (#168585) 2026-04-20 11:31:03 +02:00
epenet
7e5762dcee Use runtime_data in ukraine_alarm integration (#168597)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:09:10 +02:00
Franck Nijhof
c425b69373 Set parallel updates for Tailscale platforms (#168596) 2026-04-20 11:09:06 +02:00
Thijs W.
f73ee29ffb Add seek support to frontier_silicon (#168483) 2026-04-20 11:05:42 +02:00
Erik Montnemery
db9c5a6df4 Adjust repair text about unsupported installation method (#168156)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-20 11:03:48 +02:00
J. Nick Koston
00d16864e3 Bump habluetooth to 6.1.0 (#168576) 2026-04-20 04:01:16 -05:00
Erik Montnemery
2fb22e5654 Use hass_tmp_config_dir fixture in device_tracker tests (#168582) 2026-04-20 10:58:23 +02:00
epenet
65e09c3213 Use runtime_data in toon integration (#168591)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 10:51:13 +02:00
SeifEddineMezned
86eece57c8 Fix grammar and clarity in strings.json (#168577) 2026-04-20 10:47:37 +02:00
trevorvey
e449e28ff5 Updated H590 input source mapping (#168523) 2026-04-20 10:41:49 +02:00
TheJulianJES
6e5b72ea87 Bump matter-python-client to 0.6.0 (#168312) 2026-04-20 10:39:38 +02:00
dependabot[bot]
450aa6d73b Bump actions/cache from 5.0.4 to 5.0.5 (#168583)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 10:39:15 +02:00
dependabot[bot]
953fda87c8 Bump j178/prek-action from 2.0.1 to 2.0.2 (#168584)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 10:39:01 +02:00
epenet
4b38b79ac5 Use runtime_data in todoist integration (#168590)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 10:37:58 +02:00
epenet
7acc412902 Use runtime_data in thethingsnetwork integration (#168589)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 10:28:36 +02:00
Nick Berardi
bf2364e4cb Add First Alert app selection to Lyric auth (#168427)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-04-20 10:20:34 +02:00
Amit Finkelstein
2a6fba3990 Bump hdate to 1.2.1 (#168538) 2026-04-20 10:10:21 +02:00
epenet
6a8220a9df Use runtime_data in tami4 integration (#168587)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 09:56:18 +02:00
Øyvind Matheson Wergeland
b005fb236f Improve nobo_hub config entry setup (#168550)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2026-04-20 09:08:49 +02:00
renovate[bot]
528f7625f4 Update zizmor (#168581) 2026-04-20 08:34:51 +02:00
Franck Nijhof
0358696028 Update tailscale to 0.7.0 (#168544)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 08:31:26 +02:00
Franck Nijhof
ca4b4de20e Migrate Tailscale to use runtime_data (#168556) 2026-04-20 08:30:53 +02:00
Ronald van der Meer
34530810db Enable strict typing for Duco integration (#168572) 2026-04-20 08:30:25 +02:00
Michael
c201275fef Migrate Zeversolar to use runtime_data (#168574) 2026-04-20 08:29:45 +02:00
Tomer
ef2fa67c36 Victron GX: dedupe strings.json (#168460) 2026-04-20 08:26:43 +02:00
Fabian Munkes
0af4dfb7fd Add initial support for PlayerOptions: Select entities to Music Assistant (#167974)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-04-20 05:14:21 +02:00
Thomas55555
894b3bd6a4 Add suggested uom to mop_drying_remaining_time in roborock (#168516) 2026-04-20 03:13:14 +02:00
Franck Nijhof
a317bf9ed1 Remove leftover YAML import code from PVOutput config flow (#168560) 2026-04-19 22:29:34 +02:00
Ronald van der Meer
18a4440668 Handle rate limit error separately in Duco fan platform (#168558) 2026-04-19 22:26:15 +02:00
Franck Nijhof
fc45201f93 Update pvo to v3.0.0 (#168561) 2026-04-19 21:53:11 +02:00
Franck Nijhof
829d3da432 Update peblar to v0.5.1 (#168386)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 21:43:26 +02:00
Franck Nijhof
94cc1e6aed Migrate Rituals Perfume Genie to use runtime_data (#168564) 2026-04-19 21:40:50 +02:00
Yuval Weiss
746846fa74 Add Broadlink infrared emitter support to native infrared platform (#168385) 2026-04-19 14:54:59 -04:00
J. Nick Koston
59986f2a13 Revert "Bump habluetooth to 6.0.0" (#168552) 2026-04-19 20:14:46 +02:00
Franck Nijhof
025a5d31ae Mark reconfiguration-flow as exempt for Twente Milieu (#168040)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 14:22:02 +02:00
Raphael Hehl
77bd066a71 Improve UniFi quality scale: has-entity-name (#168490)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-19 14:13:11 +02:00
Erwin Douna
4ac4ead186 Firefly III update installation instructions (#168529) 2026-04-19 14:10:52 +02:00
Maciej Bieniek
a532c72459 Bump imgw_pib to 2.1.1 (#168534) 2026-04-19 14:09:27 +02:00
Maciej Bieniek
4a0152b4d7 Bump aioshelly to 13.24.0 (#168533) 2026-04-19 14:09:01 +02:00
Ronald van der Meer
75e9608631 Add zeroconf discovery to Duco integration (#168439) 2026-04-19 14:08:55 +02:00
Simone Chemelli
5301f1d49e Bump aioamazondevices to 13.4.3 (#168536) 2026-04-19 14:06:04 +02:00
Ronald van der Meer
8f75131829 Bump python-duco-client to 0.3.2 (#168528)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-04-19 11:35:04 +02:00
Artem Draft
7ba4b92fa8 Long polling Bravia TV in standby mode (#167364) 2026-04-19 10:30:47 +02:00
David Knowles
d9b4d633d2 Bump pydrawise to 2026.4.0 (#168500) 2026-04-19 08:51:46 +02:00
Denis Shulyaka
93b236ff94 Remove Temperature parameter from Anthropic integration (#168504) 2026-04-19 08:51:02 +02:00
superdingo101
2ec1b12a94 Fix local_calendar recurring event creation failing with UNTIL when HA timezone is non-UTC (#167735) 2026-04-18 17:42:57 -07:00
Paulus Schoutsen
0c2dd5b02f Include uid, recurrence_id, and rrule in calendar event subscription data (#168318)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
Co-authored-by: allenporter <6026418+allenporter@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2026-04-19 00:30:01 +02:00
Alistair Francis
5b255d476a husqvarna_automower_ble: Wait for product data (#168426)
Signed-off-by: Alistair Francis <alistair@alistair23.me>
2026-04-18 21:18:05 +02:00
Denis Shulyaka
476a04dcb2 Add Claude Opus 4.7 support (#168496) 2026-04-18 16:58:31 +02:00
Denis Shulyaka
2fde105979 Bump anthropic to 0.96.0 (#168487) 2026-04-18 14:47:11 +02:00
David Bonnes
8adc9600f2 Refactor how Evohome allocates names and IDs (#168485)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 14:46:48 +02:00
Khole
e7d26d8a60 Hive - Bump pyhive-integration to 1.0.9 (#168489) 2026-04-18 14:39:31 +02:00
Denis Shulyaka
9fa2430e5f Get deprecated Anthropic models from SDK (#168464)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2026-04-18 13:19:59 +03:00
Maikel Punie
ba0fc4c8be Bump velbusaio to 2026.4.1 (#168473) 2026-04-18 09:17:01 +02:00
Denis Shulyaka
771b1cac6f Use model info from API for Anthropic (#168459) 2026-04-18 07:47:51 +02:00
Joakim Plate
1091a089b4 Allow removing devices that are no longer available in fjaraskupan (#167937) 2026-04-18 00:43:45 +02:00
Tomer
f4649f7fb5 Bump victron-mqtt to 2026.4.17 (#168435) 2026-04-18 00:39:57 +02:00
Denis Shulyaka
67f11f686f Use model info from API for Maximum tokens in Anthropic config flow (#167941) 2026-04-18 00:20:43 +02:00
Steve Easley
6144180f55 Remove inactive codeowner from jvcprojector (#167451) 2026-04-18 00:20:01 +02:00
Martin Claesson
d11d88bb76 Add Kiosker integration (#164543)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-18 00:03:02 +02:00
Ian Foster
9e123b429c Add MAC filter option to ruckus integration (#164706) 2026-04-18 00:00:44 +02:00
Raphael Hehl
07db7f0024 Add new sensor entities for MELCloud Air-to-Water (ATW/Ecodan) heat pump devices (#168105)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-17 23:55:37 +02:00
Michael Rademaker
e0535fb1b2 Add EARN-E P1 Meter integration (#164412)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-17 23:53:00 +02:00
Raj Laud
0da7c0c15d Fix Victron BLE false reauth on unrecognised advertisement mode bytes (#168209)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-17 23:49:16 +02:00
Emmanuel Sciara
375a9aa575 Add teleinfo integration for French electricity meters (#167554)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-17 23:39:19 +02:00
Raphael Hehl
1dc03c84a8 Use DoorbellEventType.RING in unifiprotect (#168317)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-17 23:24:21 +02:00
Thijs W.
42d47f7d62 Add repeat and shuffle support to frontier_silicon (#168433) 2026-04-17 23:10:01 +02:00
G Johansson
a55827c01a Add reconfigure to Scrape subentry flows (#168428) 2026-04-17 23:09:06 +02:00
G Johansson
32632cc114 Automatically start subentry flow when creating a scrape config entry (#168437) 2026-04-17 23:07:42 +02:00
Anis Kadri
26d937c36c Add UniFi Access lock rule service (#167949)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-17 23:07:24 +02:00
Tom Matheussen
ff595b627d Handle specific connection errors for Satel Integra (#168442) 2026-04-17 23:03:30 +02:00
rrooggiieerr
36ba9a1a59 Remove _enable_turn_on_off_backwards_compatibility teslemetry (#168333) 2026-04-17 22:29:07 +02:00
Franck Nijhof
1fec38ef28 2026.4.3 (#168451) 2026-04-17 22:08:45 +02:00
Franck Nijhof
9c4b6951ef Ran hassfest manually 2026-04-17 19:45:38 +00:00
Franck Nijhof
7d2f303035 Bump version to 2026.4.3 2026-04-17 19:35:02 +00:00
Åke Strandberg
c61c09fba3 Add cleaning codes for MIele steam oven combo (#168418) 2026-04-17 19:33:51 +00:00
Tom Matheussen
83c807d01c Update satel-integra to 1.2.1 (#168416) 2026-04-17 19:33:50 +00:00
Jan Bouwhuis
1b0386ddfc Fix disabled discovered MQTT entities cleaned up (#168382) 2026-04-17 19:33:48 +00:00
Jamin
0af6a85049 Fix VOIP blocking call in event loop (#168331) 2026-04-17 19:27:38 +00:00
Maciej Bieniek
7bd0bc9c8a Bump imgw-pib to 2.1.0 (#168319) 2026-04-17 19:27:36 +00:00
Christopher Fenner
b200930fd4 Bump PyViCare to v2.59.0 (#168254) 2026-04-17 19:27:35 +00:00
johanzander
5c046a3750 Fix unit of measurement for SPH power sensors in growatt_server (#168251)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:27:34 +00:00
renovate[bot]
c1a013d718 Update uv to 0.11.6 (#168237)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 19:26:34 +00:00
renovate[bot]
f1f6cdae2a Update Pillow to 12.2.0 (#168234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 19:25:04 +00:00
Daniel Hjelseth Høyer
f98de4618a Bump pyTibber to 0.37.1 (#168208) 2026-04-17 19:25:02 +00:00
Daniel Hjelseth Høyer
6b2033b060 Handle Tibber async_get_client failing (#168207)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-17 19:25:01 +00:00
Arie Catsman
d9dc2bbae4 Bump pyenphase from 2.4.6 to 2.4.8 (#168190) 2026-04-17 19:24:59 +00:00
Marcel van der Veldt
82a1884085 Fix Wyoming satellite memory leak on disconnect (#168152) 2026-04-17 19:24:58 +00:00
Nathan Spencer
c46f6721bc Bump pylitterbot to 2025.3.2 (#168146) 2026-04-17 19:24:57 +00:00
Simone Chemelli
ed3ff38d30 Bump aioamazondevices to 13.4.1 (#168121) 2026-04-17 19:24:55 +00:00
Simone Chemelli
fd3e12a85f Bump aioamazondevices to 13.4.0 (#167984) 2026-04-17 19:24:54 +00:00
Retha Runolfsson
1e0a0b70f4 Fix Switchbot Keypad Vision doorbell detection (#168098)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-17 19:21:51 +00:00
AlCalzone
2598dde7aa Update Z-Wave cover moving state based on current position and cover capabilities (#168096) 2026-04-17 19:21:49 +00:00
Tom Matheussen
9af7fe22bd Bump satel-integra to 1.1.1 (#168091) 2026-04-17 19:21:48 +00:00
Tom Matheussen
546eef2eee Bump satel_integra to 1.1.0 (#167353) 2026-04-17 19:21:46 +00:00
Stefan Agner
32a8344554 Catch HomeAssistantError in ZHA migration retry loops (#168420) 2026-04-17 21:06:12 +02:00
David Bonnes
9bc81130ad Fix wording in deprecation repair strings in Evohome (#168436) 2026-04-17 19:52:23 +02:00
Retha Runolfsson
d65b7ce2f3 Bump PySwitchbot to 2.0.1 (#168090) 2026-04-17 17:29:23 +00:00
gerculanum
b09671a409 Fix missing kWh unit for dlq ADD_ELE energy sensor (#168026)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 17:29:22 +00:00
J. Nick Koston
412771465d Fix ESPHome cold/warm white color temperature read-back (#167972) 2026-04-17 17:29:21 +00:00
Michael
3a72bc23b9 Don't create cpu temperature sensor when not supported in FRITZ!Box Tools (#167905) 2026-04-17 17:29:20 +00:00
Colin
b3d7ba5ce5 Fix openevse charging_current and charging_power units (#167863) 2026-04-17 17:29:18 +00:00
Aidan Timson
4e7b6838eb Fix device_class removal in template binary sensors (#167775) 2026-04-17 17:29:17 +00:00
Kevin O'Brien
437f5ef66c Fix Proxmox VE storage usage percentage crash on missing used_fraction (#167136)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 17:29:15 +00:00
Jan Bouwhuis
008bebab05 Fix disabled discovered MQTT entities cleaned up (#168382) 2026-04-17 19:21:05 +02:00
Tomer
c20e344682 Victron GX time platform (#167916)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-17 16:35:21 +02:00
Leo Periou
7f6af18e30 Add Myneomitis climate entity (#163937)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 16:21:18 +02:00
David Bonnes
18df6e4c60 Isolate Evohome's dispatcher framework to its controller class (#168395) 2026-04-17 16:20:36 +02:00
Thijs W.
a6868ccf8b Use play_caps to determine currently available features (#168421)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 16:11:09 +02:00
Retha Runolfsson
c6a5e49c8f Add pm25 sensor for switchbot air purifier us model (#167175)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 16:07:07 +02:00
David Bonnes
679ebd5751 Deprecate evohome.set_system_mode calls without an entity_id (#166727) 2026-04-17 16:03:06 +02:00
Ronald van der Meer
e8a39e03b5 Add Wi-Fi signal strength diagnostic sensor to Duco (#168290) 2026-04-17 15:48:30 +02:00
cdheiser
3196bc6c44 Lutron Keypad LEDs as Select entities (#165876) 2026-04-17 15:47:05 +02:00
Tomer
482d0dbcd2 Victron GX number platform support (#167709)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 15:25:23 +02:00
Noah Husby
bfc18aaed4 Add USB support to Russound RIO (#167943) 2026-04-17 15:22:28 +02:00
Åke Strandberg
dab2e32236 Add cleaning codes for MIele steam oven combo (#168418) 2026-04-17 13:10:03 +02:00
Thijs W.
0824142b9c Update afsapi to v1.0.0 (#168414) 2026-04-17 12:57:06 +02:00
Manu
02c6af8be2 Add test for dynamic notify entities creation and removal in PlayStation Network integration (#167109) 2026-04-17 11:40:30 +02:00
dependabot[bot]
8ffc0de765 Bump actions/github-script from 8.0.0 to 9.0.0 (#168339)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-17 11:09:34 +02:00
Tom Matheussen
e6c3995f24 Update satel-integra to 1.2.1 (#168416) 2026-04-17 11:08:32 +02:00
Brooke Hatton
f32f7ae6ec Add Maintenance dashboard (#168392) 2026-04-17 10:49:47 +02:00
dependabot[bot]
d1eb55c028 Bump actions/upload-artifact from 7.0.0 to 7.0.1 (#168408)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-17 10:17:10 +02:00
renovate[bot]
d5b86c18a5 Update infrared-protocols to 1.3.0 (#168401)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 09:12:22 +02:00
David Bonnes
31d212425a Add a missing test of set_zone_override service to Evohome (#168053) 2026-04-17 09:08:22 +02:00
Franck Nijhof
5e2f46fb9e Update wled to v0.22.0 (#168390)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 08:46:39 +02:00
Erik Montnemery
1e6c832c9a Add tests asserting trigger features (#168407) 2026-04-17 08:37:47 +02:00
Erik Montnemery
b28f04a503 Remove device tracker and person automations (#168406) 2026-04-17 08:18:27 +02:00
Robert Resch
67458786a3 Use the python version from .pyton-version file for hassfest image (#168368) 2026-04-17 08:02:49 +02:00
Erik Montnemery
dfa911b2b3 Add tests asserting air_quality trigger features (#168377) 2026-04-16 23:52:16 +02:00
Maciej Bieniek
6da92a8be9 Add release_url for Shelly Wall Display devices (#168381)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-16 23:17:24 +02:00
Emily Love Watson
d5faf88c88 Add total disk size to glances as an entity (#168131) 2026-04-16 22:38:36 +02:00
Emily Love Watson
ad20b9798b Bump glances-api version (#168389) 2026-04-16 22:25:54 +02:00
Franck Nijhof
7c0ba4d250 Migrate Twente Milieu sensor unique IDs to snake_case and domainless (#168384) 2026-04-16 21:12:44 +02:00
Retha Runolfsson
6277ef5c21 Create a battery range sensor for switchbot presence sensor (#159096)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-16 18:52:10 +02:00
snek
b75263e486 Add heat/cool dmsr device support (#168279) 2026-04-16 18:41:22 +02:00
Paulus Schoutsen
2087906758 Add Denon rs232 integration (#166923)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-16 12:23:56 -04:00
Artur Pragacz
395d741324 Implement batched service call (#168175) 2026-04-16 18:10:43 +02:00
Richard Kroegel
2bcde89f5a Add sensor platform to eurotronic_cometblue (#168118) 2026-04-16 18:03:03 +02:00
Marc Mueller
74c62c34da Fix shelly test RuntimeWarnings (#168380) 2026-04-16 18:33:30 +03:00
Jan Bouwhuis
810672ea78 Improve scope discovery abbreviation checking for MQTT config options (#168302)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-16 17:15:18 +02:00
sanpo
afe3280aee Fix DLNA local file playback for Sony TA-AN1000 by returning content type for HEAD requests (#165807) 2026-04-16 17:14:24 +02:00
AlCalzone
fc573a0cf6 Fix Z-Wave connection string for encrypted ESPHome proxies (#168370) 2026-04-16 16:52:09 +02:00
Erik Montnemery
7b8978c7e5 Add duration to state based entity conditions (#168348) 2026-04-16 16:12:59 +02:00
renovate[bot]
d99d041e49 Update uv to 0.11.6 (#168237)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 16:10:29 +02:00
Ariel Ebersberger
cd15261d1c Fix helper tests for Python 3.14.3 (#168355) 2026-04-16 15:56:50 +02:00
Raphael Hehl
5def2456f0 Unifi access doorbell event type (#168316)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-16 15:54:14 +02:00
David Bonnes
87742dbf4e Deprecate Evohome reset services and corresponding climate preset (#167975) 2026-04-16 15:51:48 +02:00
Artur Pragacz
f5fef37210 Remove bind_hass usage (#168369) 2026-04-16 15:38:59 +02:00
Colin
fa85d0d6c2 Fix openevse charging_current and charging_power units (#167863) 2026-04-16 15:29:32 +02:00
Raphael Hehl
0fa5927fc8 Add quality scale tracking for UniFi Network integration (#168125)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-16 15:00:03 +02:00
Joost Lekkerkerker
5335367493 Check if serialx is pinned (#168358) 2026-04-16 14:50:00 +02:00
Robert Resch
1f6e078d1d Extract dynamically package version at build time in hassfest image (#168347) 2026-04-16 14:40:13 +02:00
Marc Mueller
71d857b5e1 Update pydantic to 2.13.1 (#168311) 2026-04-16 14:34:30 +02:00
Barry vd. Heuvel
0de75a013b Add weheat standby electricity usage (#168363) 2026-04-16 14:33:36 +02:00
Robert Resch
f87ec0a7b8 Just copy explicit files in the Dockerfile (#168197) 2026-04-16 14:30:54 +02:00
Ariel Ebersberger
6d1bd15256 Fix synology_dsm test for Python 3.14.3 (#168359) 2026-04-16 13:23:09 +02:00
Jürgen
9fe9064884 Fix sonos availability (#161024)
Co-authored-by: Pete Sage <76050312+PeteRager@users.noreply.github.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-04-16 12:14:19 +01:00
Jamin
f9f57b00bb Fix VOIP blocking call in event loop (#168331) 2026-04-16 12:14:58 +02:00
johanzander
2b65b06003 Fix unit of measurement for SPH power sensors in growatt_server (#168251)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 12:14:13 +02:00
Leo Periou
206c498027 Bump pyaxencoapi to 1.0.7 (#168286) 2026-04-16 12:10:24 +02:00
renovate[bot]
0ac62b241e Update home-assistant-bluetooth to 2.0.0 (#168353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 12:06:34 +02:00
renovate[bot]
4ba123a1a8 Update PyTurboJPEG to 2.2.0 (#168354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 12:02:56 +02:00
Maciej Bieniek
8b8b39c1b7 Bump imgw-pib to 2.1.0 (#168319) 2026-04-16 11:27:44 +02:00
renovate[bot]
5b70e5f829 Update lru-dict to 1.4.1 (#168336)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 11:25:00 +02:00
Erik Montnemery
4f8e7125d4 Add state based condition tests (#168349) 2026-04-16 11:22:14 +02:00
renovate[bot]
baf5e32c59 Update xmltodict to 1.0.4 (#168330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 10:49:35 +02:00
renovate[bot]
0f0ceaace2 Update PyJWT to 2.12.1 (#168239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2026-04-16 10:44:41 +02:00
Andres Ruiz
5ecae7066b Bump waterfurance to 1.6.5 (#168328) 2026-04-16 10:09:25 +02:00
Ronald van der Meer
ac9bf9b7cb Bump python-duco-client to 0.3.1 (#168341) 2026-04-16 10:08:41 +02:00
renovate[bot]
d4a98c3336 Update audioop-lts to 0.2.2 (#168326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 10:07:45 +02:00
dependabot[bot]
f0aae350b5 Bump docker/build-push-action from 7.0.0 to 7.1.0 (#168338)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 10:06:09 +02:00
Paulus Schoutsen
69332ed822 Add SerialSelector (#168263) 2026-04-16 10:45:37 +03:00
Erik Montnemery
32db17fab9 Add duration to more triggers (#168337) 2026-04-16 08:46:58 +02:00
renovate[bot]
84e8cff2ea Update infrared-protocols to 1.2.0 (#168335)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 08:31:56 +02:00
Ariel Ebersberger
cfe390e4f6 Migrate demo image_processing to async (#168315) 2026-04-16 08:17:00 +02:00
Erik Montnemery
a9becca321 Add duration to state based entity triggers (#167740) 2026-04-16 07:38:50 +02:00
renovate[bot]
0043a307f0 Update PyTurboJPEG to 1.8.3 (#168329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 05:49:04 +02:00
renovate[bot]
dfb1819800 Update fnv-hash-fast to 2.0.2 (#168327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-15 17:04:50 -10:00
puddly
12018cf9f4 Migrate remaining Core integrations from pyserial to serialx (#168325) 2026-04-15 22:39:32 -04:00
Franck Nijhof
70368c622e Extend Renovate allowlist with common packages (#168295) 2026-04-15 23:42:32 +02:00
Franck Nijhof
743aef05be Update twentemilieu to 3.0.0 (#168313) 2026-04-15 22:39:42 +02:00
Ariel Ebersberger
49e5b03c08 Migrate hdmi_cec to async (#168306) 2026-04-15 21:51:07 +02:00
Jan Bouwhuis
6bc3fcef36 Fix minor issues in MQTT tests (#168303) 2026-04-15 21:34:44 +02:00
puddly
e3e87185c5 Switch USB integration to list serial ports with serialx (#167615) 2026-04-15 19:22:45 +02:00
epenet
6d83b73cbb Simplify raise-pull-request agent push step (#167739)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:10:31 +01:00
Ariel Ebersberger
533871babb Optimize add_job to skip double-deferral for @callback targets (#168198) 2026-04-15 18:50:33 +02:00
Erik Montnemery
1dc93a80c4 Improve type annotations and remove unused code in mobile_app (#168298) 2026-04-15 18:09:10 +02:00
Erik Montnemery
f8a94c6f22 Fix climate trigger labs flag test (#168299) 2026-04-15 17:53:26 +02:00
Erik Montnemery
b127d13587 Add additional media_player triggers (#156927)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2026-04-15 17:34:36 +02:00
renovate[bot]
1895f8ebce Update attrs to 26.1.0 (#168276)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-04-15 17:22:33 +02:00
renovate[bot]
b6916954dc Update respx to 0.23.1 (#168272)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-15 17:10:28 +02:00
renovate[bot]
23181f5275 Update pytest-github-actions-annotate-failures to 0.4.0 (#168269)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-15 16:59:51 +02:00
Robert Resch
607a10d1e1 Use pip to install dynamically extracted version from requirements.txt (#168246) 2026-04-15 16:34:01 +02:00
Ariel Ebersberger
ecb814adb0 Add test coverage for add_job and fix docstring (#168291) 2026-04-15 16:17:01 +02:00
G Johansson
67df556e84 Add async_on_create_entry method to create config entries (#155016)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-15 15:57:32 +02:00
AlCalzone
4d472418c5 Ensure extra_fields in Z-Wave automation config are strings (#168281) 2026-04-15 15:12:18 +02:00
renovate[bot]
cf6441561c Update voluptuous-openapi to 0.3.0 (#168275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-15 15:06:24 +02:00
Erik Montnemery
6d8d447355 Revert "Add last_non_buffering_state media_player state attribute (#166941)" (#168285) 2026-04-15 14:41:02 +02:00
Erik Montnemery
ab5ae33290 Exclude unavailable and unknown in trigger first and last checks (#168224)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-15 14:20:49 +02:00
renovate[bot]
c0bf9a2bd2 Update pytest-sugar to 1.1.1 (#168270)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-15 13:07:21 +02:00
Norbert Rittel
d862b999ae Capitalize "REST" abbreviation in scrape error messages (#168280) 2026-04-15 11:36:39 +02:00
Erik Montnemery
d6be6e8810 Improve timer tests (#168277) 2026-04-15 11:21:59 +02:00
Daniel Hjelseth Høyer
f397f4c908 Handle Tibber async_get_client failing (#168207)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-15 10:50:29 +02:00
G Johansson
d58e7862c0 Scrape sub config entry (#141389) 2026-04-15 09:59:12 +02:00
Erik Montnemery
84f57f9859 Deduplicate toggle entity condition tests (#168195) 2026-04-15 08:19:09 +02:00
Erik Montnemery
c6169ec8eb Add update conditions (#167751) 2026-04-15 08:03:51 +02:00
renovate[bot]
c47cecf350 Update SQLAlchemy to 2.0.49 (#168260) 2026-04-15 07:20:58 +02:00
renovate[bot]
e31f611901 Update pytest-cov to 7.1.0 (#168267) 2026-04-15 07:20:10 +02:00
renovate[bot]
bc36b1dda2 Update coverage to 7.13.5 (#168238) 2026-04-15 07:19:39 +02:00
renovate[bot]
b3967130f0 Update orjson to 3.11.8 (#168259) 2026-04-15 06:40:43 +02:00
renovate[bot]
2960db3d8e Update codespell (#168235) 2026-04-15 06:34:50 +02:00
Christopher Fenner
a74cf69607 Bump PyViCare to v2.59.0 (#168254) 2026-04-15 01:42:16 +02:00
Noah Husby
d3e346c728 Bump aiorussound to 5.0.1 (#168255) 2026-04-15 01:03:10 +02:00
renovate[bot]
efb93c928e Update pylint-per-file-ignores to 3.2.1 (#168243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edenhaus <26537646+edenhaus@users.noreply.github.com>
2026-04-15 00:41:54 +02:00
Paulus Schoutsen
b7894407d2 Git ignore Claude worktrees (#168247) 2026-04-15 00:17:38 +02:00
Ian Foster
0b7da89e6e Modernize reauth flow in ruckus_unleashed (#168013) 2026-04-15 00:07:20 +02:00
Leon Grave
c765077442 Fresh-r integration: Get Quality Scale to Platinum (#167148)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 00:07:01 +02:00
Franck Nijhof
efbfeb7c30 Set parallel updates for Rituals Perfume Genie platforms (#168042) 2026-04-14 23:55:19 +02:00
Leon Grave
5670a12805 Add FreshrEntity base class (#168023)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 23:55:03 +02:00
Joakim Plate
d88fe45393 Wait for complete set of product data before accepting gardena device (#166481)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-14 23:32:30 +02:00
Leon Grave
b231742049 Add test for LoginError reauth in FreshrReadingsCoordinator (#168022)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-14 23:06:46 +02:00
Fredrik Mårtensson
99dc368c79 Add feeder meal plan actions to tuya (#161488)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-14 23:02:31 +02:00
cdheiser
a21a0a6577 Refactor Lutron setup logic (#167993) 2026-04-14 23:01:49 +02:00
Ian Foster
513fff12ac Improve setup exception handling in ruckus_unleashed (#168014) 2026-04-14 22:48:45 +02:00
David Bonnes
4474ad0450 Add native DHW service to Evohome (#167359)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-14 22:40:28 +02:00
Ronald van der Meer
a5d640acdb Add diagnostics to Duco integration (#168231) 2026-04-14 22:07:16 +02:00
renovate[bot]
da66632798 Update syrupy to 5.1.0 (#168241)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 21:49:20 +02:00
renovate[bot]
f5998856b4 Update yamllint (#168242)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 21:49:02 +02:00
renovate[bot]
d5441ff99e Update freezegun to 1.5.5 (#168236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 21:41:48 +02:00
renovate[bot]
3848d4e8a6 Update Pillow to 12.2.0 (#168234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 21:41:14 +02:00
Paulus Schoutsen
599c548264 Bump serialx to 1.2.2 (#168229) 2026-04-14 21:21:26 +02:00
Franck Nijhof
b18602cd18 Disable Renovate vulnerability alerts flow (#168233) 2026-04-14 21:11:07 +02:00
Stefan Agner
a45e2d74ec Split hassio data coordinator and add dedicated stats coordinator (#167080)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-14 20:51:13 +02:00
Franck Nijhof
a952636c28 Refine Renovate config with built-in manager and review follow-ups (#168225) 2026-04-14 20:27:59 +02:00
Daniel Hjelseth Høyer
ccd1d9f8ea Bump pyTibber to 0.37.1 (#168208) 2026-04-14 19:41:05 +02:00
Franck Nijhof
a4d4fe3722 Add Renovate config for allow-listed Python dependency updates (#168192) 2026-04-14 18:56:51 +02:00
Denis Shulyaka
98b41d25f3 Add send_message_draft action to telegram_bot (#165682)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-14 17:57:10 +02:00
Franck Nijhof
d8c8f82c7e Translate coordinator exceptions for PVOutput (#168076)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-14 17:55:15 +02:00
Florent Thoumie
8695d32b32 iaqualink: enable _attr_has_entity_name (#167810)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-14 17:45:44 +02:00
Marcel van der Veldt
073d22d046 Fix Wyoming satellite memory leak on disconnect (#168152) 2026-04-14 10:37:36 -05:00
Raphael Hehl
939412717f Add binary sensor platform for MELCloud ATW devices (#168128)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-14 17:28:56 +02:00
Marc Mueller
8217d3683a Fix mqtt test ResourceWarnings (#168182) 2026-04-14 17:24:50 +02:00
Ronald van der Meer
fa9185b755 Add sensor platform to Duco integration (#167920) 2026-04-14 17:21:46 +02:00
Erik Montnemery
f2f59eb8b7 Add todo conditions (#167752) 2026-04-14 17:15:56 +02:00
Erik Montnemery
16edfc9624 Add remote conditions (#167750) 2026-04-14 16:34:42 +02:00
Niracler
177d244b91 Add diagnostics platform to Sunricher DALI integration (#168074) 2026-04-14 16:33:54 +02:00
Andres Ruiz
dd8a79bd0e Add energy backfill support for waterfurnace (#167955)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-14 16:33:03 +02:00
Retha Runolfsson
3c46ecb93a Fix Switchbot Keypad Vision doorbell detection (#168098)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-14 16:32:03 +02:00
Raphael Hehl
66b2d4477b Fix unifi_discovery deepcopy crash on Python 3.14 (#168153) 2026-04-14 16:31:04 +02:00
Robert Resch
2ba66fb722 Use runtime_data in plaato integration (#167900)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:26:10 +02:00
Arjan
9fab53d083 MeteoFrance - Add wind gusts for hourly forecast (re) (#168166) 2026-04-14 16:17:52 +02:00
Shay Levy
41c3db9ebd Revert "Replace "custom" with "community" in analytics_insights" (#168160) 2026-04-14 16:09:20 +02:00
Arie Catsman
3d0d048d1f Bump pyenphase from 2.4.6 to 2.4.8 (#168190) 2026-04-14 16:07:15 +02:00
Marc Mueller
c57a666921 Fix matrix ResourceWarning (#168186) 2026-04-14 16:05:32 +02:00
Marc Mueller
3cd67cea53 Fix test fixture tests ResourceWarning (#168183) 2026-04-14 16:05:29 +02:00
Kurt Chrisford
e05622f8d0 Mark entity-translations and icon-translations as done for Actron Air (#167150) 2026-04-14 15:24:41 +02:00
Marc Mueller
c17d3584cb Fix backup test ResourceWarnings (#168180) 2026-04-14 15:18:34 +02:00
Marc Mueller
3f3b3db913 Fix go2rtc ResourceWarnings (#168184) 2026-04-14 15:17:34 +02:00
Marc Mueller
44a0e964ef Fix homekit ResourceWarnings (#168185) 2026-04-14 15:17:11 +02:00
Marc Mueller
d6e56b41b1 Fix mcp_server ResourceWarnings (#168187) 2026-04-14 15:16:49 +02:00
Marc Mueller
4191bbf504 Fix octoprint ResourceWarnings (#168188) 2026-04-14 15:16:45 +02:00
Artur Pragacz
041fed4b48 Fix missing async_request_call in single-entity service call path (#168171) 2026-04-14 14:35:28 +02:00
Shay Levy
6311e6feec Revert "Replace 'custom component' with 'community integration' in bmw_connected_drive" (#168159) 2026-04-14 14:03:29 +02:00
Jan Čermák
582a0a5ae3 Add MariaDB 11.4 to CI tests (#168111) 2026-04-14 13:39:55 +02:00
Christopher Fenner
1a3f75c6fc Add additional codeowner to ViCare integration (#168169) 2026-04-14 13:38:29 +02:00
Shay Levy
21301e43a9 Revert "Update "custom component" to "community integration" in Shelly" (#168162) 2026-04-14 14:25:50 +03:00
Christian Lackas
cbe7823fd5 Bump homematicip to 2.8.0 (#168168) 2026-04-14 13:10:01 +02:00
Raphael Hehl
7a5951b72d Add discovery support to unifi_access via unifi_discovery (#168085) 2026-04-14 13:00:06 +02:00
Shay Levy
42771ed0a7 Revert "Replace "custom" with "community" in homeassistant" (#168161) 2026-04-14 12:58:33 +02:00
Aidan Timson
ded34b4430 Fix device_class removal in template binary sensors (#167775) 2026-04-14 11:40:13 +02:00
Franck Nijhof
68d6a3e6bd Move template state infrastructure into a dedicated states module (#167996) 2026-04-14 10:12:26 +02:00
Erik Montnemery
20ea635e39 Deduplicate installation method repair tests (#168157) 2026-04-14 10:11:17 +02:00
Erik Montnemery
c65dc842a5 Fix generic_thermostat context handling (#168080) 2026-04-14 08:03:56 +02:00
Erik Montnemery
27d8c7b93e Improve logbook parent context handling (#167036)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-04-14 07:48:33 +02:00
Nathan Spencer
eae9db4aac Bump pylitterbot to 2025.3.2 (#168146) 2026-04-14 02:58:10 +02:00
Shai Ungar
aa70023d89 Bump pyseventeentrack to 1.1.3 (#168135)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 00:57:16 +01:00
Marc Mueller
f3eb9f1bbc Update asyncinotify to 4.4.4 (#168141) 2026-04-14 00:39:36 +01:00
G Johansson
ed9c2616bb Bump pynordpool to 0.4.0 (#168130) 2026-04-13 23:00:55 +02:00
Marc Mueller
6a66d0a9a2 Update pytest to 9.0.3 (#168132) 2026-04-13 22:52:31 +02:00
AlCalzone
6d2a567572 Update Z-Wave cover moving state based on current position and cover capabilities (#168096) 2026-04-13 21:33:01 +02:00
Richard Kroegel
30a554a242 Add button platform to eurotronic_cometblue (#168120)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-13 21:11:20 +02:00
Franck Nijhof
888ec5e965 Set parallel updates to 0 for Apple TV binary sensor (#168116) 2026-04-13 21:05:29 +02:00
potelux
6396744f19 Remove redundant _attr_media_image_remotely_accessible from Jellyfin (#168112)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-13 20:56:47 +02:00
G Johansson
308aa7b868 Add missing return after reloading in telegram_bot (#168114) 2026-04-13 20:53:06 +02:00
Simone Chemelli
a74c3d41b9 Bump aioamazondevices to 13.4.1 (#168121) 2026-04-13 20:41:49 +02:00
Franck Nijhof
e6d9f80c9e Translate coordinator exceptions for RDW (#168044) 2026-04-13 20:13:25 +02:00
Franck Nijhof
8789afe21f Translate coordinator exceptions for Sensor.Community (#168048) 2026-04-13 20:13:12 +02:00
Erik Montnemery
1fbf437c49 Adjust logbook timestamp handling (#168079) 2026-04-13 17:22:48 +02:00
Andrew Jackson
f8e5165ec7 Add quote approval policy to Mastodon post service (#168092)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-13 17:18:34 +02:00
gerculanum
d1fcc7564e Fix missing kWh unit for dlq ADD_ELE energy sensor (#168026)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 16:04:03 +02:00
Marc Mueller
667002ddfa Update pydantic pin to 2.13.0 (#168103) 2026-04-13 14:53:27 +02:00
Marc Mueller
10780adb6e Update mypy to 1.20.1 (#168100) 2026-04-13 14:41:23 +02:00
Franck Nijhof
431736c5d8 Set parallel updates to 0 for Met.no (#168094) 2026-04-13 13:28:04 +02:00
Franck Nijhof
6a3051718a Add reconfiguration flow to Elgato (#168036) 2026-04-13 13:06:33 +02:00
Retha Runolfsson
95c3624b01 Bump PySwitchbot to 2.0.1 (#168090) 2026-04-13 12:43:14 +02:00
Tom Matheussen
f53b629dfd Bump satel-integra to 1.1.1 (#168091) 2026-04-13 12:41:56 +02:00
Giga77
d901541f48 Add hacf/reviewers as codeowners to Freebox (#168050) 2026-04-13 12:13:14 +02:00
Giga77
cdcf810506 Remove hacf-fr from Epic Games Store (#168038) 2026-04-13 12:02:47 +02:00
Giga77
274146cbb2 Remove hacf-fr from Synology DSM (#168039) 2026-04-13 11:55:10 +02:00
Giga77
b8cdd8dccc Remove hacf-fr (#168054) 2026-04-13 11:53:43 +02:00
Raphael Hehl
5abaa2ae72 Bump python-melcloud to 0.1.3 (#168086) 2026-04-13 11:34:05 +02:00
Simone Chemelli
4a511a3e53 Bump aioamazondevices to 13.4.0 (#167984) 2026-04-13 11:27:12 +02:00
Andrew Jackson
81a657ab2c Bump mastodon.py to 2.2.1 (#168084) 2026-04-13 11:11:30 +02:00
Giga77
e9a79ee0e5 Replace hacf-fr by hacf-fr reviewers team (#168056) 2026-04-13 11:06:40 +02:00
Fabian Neundorf
ffd439abc5 Add support for KM7576 in Miele integration (#168069) 2026-04-13 10:30:33 +02:00
Niracler
982a2b8af7 Bump PySrDaliGateway to 0.20.4 (#168078) 2026-04-13 10:28:14 +02:00
Raphael Hehl
ef589f9b46 Add unifi_discovery integration, migrate unifiprotect discovery (#168030)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-13 09:50:39 +02:00
Denis Shulyaka
81f8319af4 Fix llm tool results mutation (#167485)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 09:33:37 +02:00
Richard Kroegel
a061e47bec Improve eurotronic_cometblue tests (#168046) 2026-04-13 07:16:22 +02:00
Franck Nijhof
e5c49b6455 Set parallel updates to 0 for Sensor.Community (#168063) 2026-04-13 06:11:16 +02:00
Christian Lackas
5c51820869 Add Heatbox3 to ViCare unsupported devices list (#168067) 2026-04-13 05:49:12 +02:00
Franck Nijhof
eb64589115 Translate coordinator exceptions for Tailwind (#168027) 2026-04-12 18:45:37 +02:00
Franck Nijhof
4ebf0bf0b6 Fix untranslated button error in Tailwind (#168031) 2026-04-12 12:20:12 +02:00
Franck Nijhof
f521838bf1 Add reconfiguration flow to Tailwind (#168033) 2026-04-12 11:50:52 +02:00
Franck Nijhof
efb0162c6f Set parallel updates for Tailwind platforms (#168025) 2026-04-12 11:13:13 +02:00
Franck Nijhof
ba62b6cbda Handle connection errors in Peblar zeroconf confirm step (#167998) 2026-04-12 10:11:13 +02:00
Franck Nijhof
4e13731838 Extract entity template functions into an entity Jinja2 extension (#167992) 2026-04-12 10:00:53 +02:00
Franck Nijhof
4f255c23dd Translate coordinator exceptions for Twente Milieu (#168005) 2026-04-11 23:05:55 +02:00
Franck Nijhof
af69e9b5de Translate exceptions raised by Elgato (#168004) 2026-04-11 23:05:47 +02:00
Franck Nijhof
df734655f6 Remove unused service constants from Twente Milieu (#168000) 2026-04-11 22:27:04 +02:00
Franck Nijhof
4926ea9ef0 Set parallel updates to 0 for RDW platforms (#168003) 2026-04-11 22:26:36 +02:00
Franck Nijhof
322dc2adeb Add DHCP discovery for known Elgato devices (#168002) 2026-04-11 22:26:22 +02:00
Franck Nijhof
2e648aca8b Mark exception-translations rule as done for Peblar (#167997) 2026-04-11 21:56:24 +02:00
Franck Nijhof
dac2777729 Mark entity-translations rule as done for Twente Milieu (#168001) 2026-04-11 21:56:01 +02:00
Franck Nijhof
f886b03e14 2026.4.2 (#167939) 2026-04-11 20:41:23 +02:00
Willem-Jan van Rootselaar
190ee49e3a Bump python-bsblan to version 5.1.4 (#167987) 2026-04-11 18:00:37 +00:00
Erwin Douna
f7c5a51f46 Portainer fix fetching swarm stacks (#167979) 2026-04-11 17:56:13 +00:00
tronikos
e4e9c22016 Bump opower to 0.18.1 (#167967) 2026-04-11 17:56:12 +00:00
Norbert Rittel
f2df848e3f Fix spelling of "Shut down" button label in proxmoxve (#167059) 2026-04-11 17:56:10 +00:00
Willem-Jan van Rootselaar
1e1e37637f Bump python-bsblan to version 5.1.4 (#167987) 2026-04-11 18:45:56 +02:00
Joakim Plate
d695250507 Fix gardena entity categories and percentage values (#167986) 2026-04-11 18:44:03 +02:00
Richard Kroegel
ab7b257785 Add eurotronic cometblue integration (#165626)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-11 16:35:04 +02:00
J. Nick Koston
3b1fa609f7 Bump aioesphomeapi to 44.13.3 (#167966) 2026-04-11 16:28:29 +02:00
Kevin Stillhammer
822fae227a Add base_coords for OptionsFlow and action call in waze_travel_time (#166642) 2026-04-11 16:28:02 +02:00
J. Nick Koston
2fa0bdb2dc Fix ESPHome cold/warm white color temperature read-back (#167972) 2026-04-11 16:24:50 +02:00
Andres Ruiz
8a43d1a12c Add remote start/stop button for supported Subaru vehicles (#167100)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-11 16:22:19 +02:00
Erwin Douna
483265a707 Portainer fix fetching swarm stacks (#167979) 2026-04-11 16:21:16 +02:00
Raphael Hehl
84f5cd8a12 Bump uiprotect to 10.2.6 (#167978)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-11 14:32:01 +01:00
J. Diego Rodríguez Royo
e23da7a5f0 Bump aiohomeconnect to 0.36.0 (#167973) 2026-04-11 13:55:29 +02:00
Florent Thoumie
fe1e12a298 Improve iaqualink reauthentication flow (#167931)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-11 13:06:49 +02:00
tronikos
938eacd777 Bump opower to 0.18.1 (#167967) 2026-04-11 13:04:25 +02:00
David Bonnes
ba7a959727 Remove unused constant from Evohome's const.py (#167969) 2026-04-11 12:23:33 +02:00
Fabian Munkes
966eadad69 Follow up to adding support for sound modes to Music Assistant (#167929) 2026-04-11 11:07:52 +02:00
Fabian Munkes
f34ed8f8ba Follow-up to player options: switch entities in Music Assistant (#167964) 2026-04-11 10:46:20 +02:00
Brett Adams
ac4b253a2f Add LoginRequired exception handling to Teslemetry coordinators (#167959) 2026-04-11 10:37:37 +02:00
Fabian Munkes
640fea89e0 Follow-up to player options: number entities in Music Assistant (#167963) 2026-04-11 10:33:27 +02:00
Fabian Munkes
fdf1b6536a Follow-up to player options: text entities in Music Assistant (#167962) 2026-04-11 10:27:43 +02:00
Raphael Hehl
974047664c Bump unifi-discovery to version 1.4.0 (#167958)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-10 22:26:15 -10:00
Franck Nijhof
cdce98faaf Update cryptography to 46.0.7 (#167960) 2026-04-11 08:12:40 +00:00
Martin Hjelmare
fde103cdfd Fix tibber price sensor first state update (#167938)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-11 08:02:37 +00:00
Daniel Hjelseth Høyer
fcd6c6e335 Improve Tibber price coordinator (#166175)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-11 08:02:36 +00:00
Franck Nijhof
03d6f5a756 Update cryptography to 46.0.7 (#167960) 2026-04-11 10:00:37 +02:00
epenet
9f1c396407 Unlink tomorrowio coordinator from config entry (#167901) 2026-04-11 09:49:54 +02:00
J. Nick Koston
054b8ad534 Bump aioesphomeapi to 44.13.2 (#167952) 2026-04-10 16:48:34 -10:00
J. Nick Koston
b93cdc64f3 Bump bleak-esphome to 3.7.3 (#167953) 2026-04-10 16:27:52 -10:00
Fabian Munkes
59248e5414 Bump music-assistant-client to 1.3.5 (#167947) 2026-04-11 01:07:18 +02:00
Michael
a5b830cc34 Don't create cpu temperature sensor when not supported in FRITZ!Box Tools (#167905) 2026-04-11 00:04:23 +02:00
James
299562d6ee Set integer display precision for Yardian duration sensors (#165896)
Co-authored-by: barneyonline <barneyonline@users.noreply.github.com>
2026-04-10 23:58:57 +02:00
Denis Shulyaka
47cc31067c Check if model exists in Anthropic config flow (#167844) 2026-04-11 00:06:17 +03:00
Martin Hjelmare
f050407bfa Fix tibber price sensor first state update (#167938)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-10 22:50:36 +02:00
Franck Nijhof
8f2cec26e3 Merge branch 'master' into rc 2026-04-10 22:45:55 +02:00
Tomeamis
a202742fc6 Z-Wave.me: Make Light support the Transition feature (#167840) 2026-04-10 22:45:36 +02:00
Franck Nijhof
05463cde99 Bump version to 2026.4.2 2026-04-10 20:41:40 +00:00
On Freund
a948799a6e Bump pyrisco to 0.6.8 (#167924) 2026-04-10 20:41:20 +00:00
Bram Kragten
624fab064a Update frontend to 20260325.7 (#167922) 2026-04-10 20:41:19 +00:00
Nathan Spencer
a331cb7199 Bump pylitterbot to 2025.2.1 (#167921) 2026-04-10 20:41:17 +00:00
Thomas D
7d6eaf40a6 Fix light on action for qbus integration (#167917) 2026-04-10 20:41:16 +00:00
panosmz
1ae9e7c87d Bump oasatelematics to 0.4 (#167911) 2026-04-10 20:41:14 +00:00
Thomas D
6bcfc32d48 Bump qbusmqttapi to 1.4.3 (#167909) 2026-04-10 20:41:13 +00:00
Joost Lekkerkerker
0b5f85bdb9 Bump zinvolt to 0.4.3 (#167908) 2026-04-10 20:41:12 +00:00
Maikel Punie
d153eee822 Bump velbusaio to 2026.4.0 (#167868) 2026-04-10 20:41:10 +00:00
TheJulianJES
afcc2113ce Bump ZHA to 1.1.2 (#167849) 2026-04-10 20:40:12 +00:00
J. Diego Rodríguez Royo
ae5bd63993 Fix service.yaml values for Home Connect (#167847) 2026-04-10 20:38:38 +00:00
Simone Chemelli
78107c478d Fix stale devices removal for Alexa devices (#167837) 2026-04-10 20:38:36 +00:00
Joost Lekkerkerker
84490ef0bb Support Chess.com accounts with no name (#167824) 2026-04-10 20:38:34 +00:00
Raj Laud
887e14638b Fix Victron BLE storage errors caused by non-serializable value_fn callable in sensor entity description (#167819)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 20:38:32 +00:00
Raj Laud
818bde1d5e Fix Victron BLE false reauth triggered by unknown enum bitmask combinations (#167809)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 20:38:31 +00:00
Benjamin Hudgens
83da18b761 Revert "Fix Ring snapshots" - #164337 (#167790) 2026-04-10 20:38:29 +00:00
Maciej Bieniek
bd904caea1 Bump aiotractive to 1.0.2 (#167783) 2026-04-10 20:38:28 +00:00
Michael
500f030eaa Set proper state for the internet_access switches in FRITZ!Box Tools (#167767) 2026-04-10 20:38:26 +00:00
wollew
ce755f5f8f Bump pyvlx to 0.2.33 (#167764) 2026-04-10 20:38:25 +00:00
Nick Haghiri
fb766d164b Improve error logging for Backblaze B2 upload failures (#167721) 2026-04-10 20:38:23 +00:00
Tom
394670e33f Fix ProxmoxVE migration causing reauthentication (#167624) 2026-04-10 20:38:21 +00:00
G Johansson
f79285f9ab Bump holidays to 0.94 (#167604) 2026-04-10 20:38:20 +00:00
Erik Montnemery
a422611ada Fix securetar size calculation when encrypting backup (#167602) 2026-04-10 20:38:18 +00:00
Erik Montnemery
4c34dcd560 Bump securetar to 2026.4.0 (#167600) 2026-04-10 20:38:17 +00:00
Maciej Bieniek
1aca993c12 Fix Tractive switch availability (#167599) 2026-04-10 20:38:15 +00:00
Leo Periou
a8cc099b66 fix EWS deviceType problem (#167597) 2026-04-10 20:38:13 +00:00
Artur Pragacz
c56d67c02f Set up condition and trigger helpers in check config script (#167589)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 20:38:11 +00:00
Jan Čermák
0ce98cfb34 Remove homeassistant/actions/helpers/info from builder workflow (#167573) 2026-04-10 20:35:03 +00:00
Jan Bouwhuis
4a13ab9aff Bump incomfort-client to v0.7.0 (#167546) 2026-04-10 20:28:23 +00:00
Fabian Neundorf
dc65646d8b Bump python-picnic-api2 to 1.3.4 (#167539)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-10 20:28:22 +00:00
Nelson Osacky
39fbdad775 Add missing Miele dishwasher program ID 201 (#167536)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 20:28:20 +00:00
Jordan Harvey
b4f6a43a14 Bump pynintendoparental to 2.3.4 (#167510) 2026-04-10 20:28:19 +00:00
Nick Haghiri
e5ff7a9944 Handle BadRequest exception in Backblaze B2 config flow and setup (#167482) 2026-04-10 20:28:18 +00:00
Nick Haghiri
ca9945f750 Bump b2sdk to 2.10.4 (#167481) 2026-04-10 20:28:16 +00:00
Andrea Turri
b028e2a6ae Miele - fix core temperature reading (#167476) 2026-04-10 20:28:15 +00:00
Allen Porter
6f4aca495b Update roborock services to raise ServiceNotSupported for new devices that don't yet support it (#167470)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 20:28:14 +00:00
Jamie Magee
a892b5364d Fix nzbget positional argument mismatch in NZBGetAPI calls (#167456) 2026-04-10 20:28:12 +00:00
Steve Easley
f57e682a98 Bump jvcprojector dependency to pyjvcprojector 2.0.5 (#167450) 2026-04-10 20:28:11 +00:00
Nils Ove Erstad
3493517b6d Fix missing color_mode initialization in MQTT JSON light schema (#167429) 2026-04-10 20:28:09 +00:00
Jordan Harvey
b5842b8484 Fix handling of missing period statistics in Anglian Water coordinator (#167427)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 20:28:08 +00:00
Willem-Jan van Rootselaar
3333b8d019 Fix setup without dhw (#167423) 2026-04-10 20:28:07 +00:00
Simone Chemelli
745860553c Bump aiocomelit to 2.0.2 (#167414) 2026-04-10 20:26:02 +00:00
Shai Ungar
7188a09a59 Use dedicated session for seventeentrack to preserve login cookies (#167394)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 20:26:01 +00:00
Robert Svensson
96a9b89412 Bump axis to v68 to improve MQTT event resiliance (#167373) 2026-04-10 20:25:59 +00:00
Tom
586d7ab526 Improve ProxmoxVE permissions handling (#167370) 2026-04-10 20:25:58 +00:00
Joost Lekkerkerker
5f2fe4ffd4 Bump aiohue to 4.8.1 (#167369) 2026-04-10 20:25:56 +00:00
Simone Chemelli
040192c103 Align and cleanup tests data for Fritz (#167363) 2026-04-10 20:25:55 +00:00
Jordan Harvey
e85430105e Bump cryptography to 46.0.6 (#167330)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-04-10 20:22:31 +00:00
cdheiser
5c7c0a6e83 Bump pylutron to 0.4.1 (#167324) 2026-04-10 20:21:53 +00:00
007hacky007
c7bd673d01 Bump afsapi to 0.3.1 (#167321) 2026-04-10 20:21:51 +00:00
MarkGodwin
6d3a93df81 Update to tplink-omada-client 1.5.7 (#167313) 2026-04-10 20:21:50 +00:00
Raj Laud
6a934b5fe3 Fix victron ble reauth flow title (#167307)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 20:21:48 +00:00
Daniel Hjelseth Høyer
d644348dc8 Bump pyTibber to 0.37.0 (#167283) 2026-04-10 20:21:47 +00:00
Ludovic BOUÉ
dbfde9266c Add Hisense AC (0x138C/0x0101) to Matter dry and fan mode device lists (#167282)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-04-10 20:21:45 +00:00
Simone Chemelli
ed0b68ec4a Allow force alarm actions for Comelit (#167202) 2026-04-10 20:21:44 +00:00
Patrick
c32d523f63 Bump starlink-grpc-core to 1.2.5 (#167195) 2026-04-10 20:21:42 +00:00
Marco Sousa
98a4e27e35 Bump aiopvpc to 4.3.1 (#167189) 2026-04-10 20:21:41 +00:00
Alex Merkel
fb1365e9a4 [LG Soundbar] Fix incorrect state for some models (#167094) 2026-04-10 20:21:39 +00:00
Ronald van der Meer
63a0b5d2ff Bump python-duco-client to 0.3.0 (#167936) 2026-04-10 22:19:39 +02:00
Raman Gupta
53ed4b2c77 Refactor Vizio tests: shared fixtures, snapshot_platform, reduced parametrize (#167935)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:19:25 +02:00
Willem-Jan van Rootselaar
850b034a5f Include port in BSB-LAN configuration URL when non-default (#166480) 2026-04-10 20:19:09 +00:00
Samuel Xiao
b880876e0e Switchbot Cloud: Enable Webhook for Bot (#165647) 2026-04-10 20:13:56 +00:00
Jeef
ab601e5717 Prevent the intellifire client from polling independently of its coordinator (#165341)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 20:13:54 +00:00
Matt Philips
7eda592c72 Improve handling of disconnected meters with Rainforest Automation Eagle-200 (#161185)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-10 20:13:53 +00:00
Tomer
2f91c6b050 Promote victron_gx integration to silver quality scale (#167789)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 20:24:54 +02:00
Raj Laud
d17cb0e096 Fix Victron BLE storage errors caused by non-serializable value_fn callable in sensor entity description (#167819)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 20:22:55 +02:00
mettolen
c9ee533916 Update Liebherr to platinum (#167836) 2026-04-10 20:17:04 +02:00
Raman Gupta
e88022c2cc Add remote platform to Vizio integration (#165820)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 20:13:11 +02:00
Nick Haghiri
d633ac8120 Improve error logging for Backblaze B2 upload failures (#167721) 2026-04-10 20:12:24 +02:00
potelux
fb90237ae3 Proxy Jellyfin artwork through HA so thumbnails work over HTTPS (#167238)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 20:08:09 +02:00
Artur Pragacz
4658f4246d Allow frontend-handled issues to omit description in strings (#167928) 2026-04-10 19:59:05 +02:00
tronikos
99e4c87f5e Add reauthentication and reconfiguration flows in Google Weather to reach platinum (#166106) 2026-04-10 19:55:33 +02:00
Abílio Costa
7690d9570c Narrow log check on ring event test (#167927) 2026-04-10 18:26:31 +01:00
On Freund
00560abd9c Bump pyrisco to 0.6.8 (#167924) 2026-04-10 18:47:51 +02:00
Bram Kragten
b6d4fca477 Update frontend to 20260325.7 (#167922) 2026-04-10 18:46:06 +02:00
Nathan Spencer
44e51c1103 Bump pylitterbot to 2025.2.1 (#167921) 2026-04-10 18:21:45 +02:00
Erik Montnemery
3ad2c5e574 Fix config validation in trigger and condition tests (#167683) 2026-04-10 18:20:04 +02:00
Marcello
212c9b1a94 Bump fluss-api to 0.2.4 (#167680)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-04-10 18:03:49 +02:00
epenet
b670172867 Bump tuya-device-handlers to 0.0.17 (#167904) 2026-04-10 18:01:28 +02:00
David Bonnes
23bcde09b0 Add Buttons to natively reset the mode of Evohome entities (#167550) 2026-04-10 18:00:30 +02:00
Tom Matheussen
62717fd3f5 Add support for encrypted connection to Satel Integra (#167372) 2026-04-10 17:57:26 +02:00
Simone Chemelli
86b72501ad Add faulty/anomaly binary sensors to Comelit (#167201) 2026-04-10 17:51:49 +02:00
Stef Coene
59827967e6 Velbus reconfigure fix (#167471)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:50:56 +02:00
Thomas D
fe5d45ed57 Fix light on action for qbus integration (#167917) 2026-04-10 17:24:00 +02:00
Florent Thoumie
cf87e9ab72 iaqualink: move custom update logic to DataUpdateCoordinator (#167816)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 17:15:11 +02:00
Alex Merkel
64907ad7e2 [LG Soundbar] Fix incorrect state for some models (#167094) 2026-04-10 17:10:18 +02:00
Noah Husby
9e111b2418 Bump aiorussound to 5.0.0 (#167914) 2026-04-10 17:07:07 +02:00
Joost Lekkerkerker
97d64ab37c Bump zinvolt to 0.4.3 (#167908) 2026-04-10 17:02:51 +02:00
Tomer
547830b450 Victron GX switch platform (#167859)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 17:00:39 +02:00
Andrew Brainwood
f2f605b425 Add Preset button support for Bond cover devices (#167881)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-04-10 17:00:25 +02:00
Thomas D
781b5e1c0e Bump qbusmqttapi to 1.4.3 (#167909) 2026-04-10 16:57:05 +02:00
panosmz
68a7cbb620 Bump oasatelematics to 0.4 (#167911) 2026-04-10 16:48:11 +02:00
epenet
a6a716571d Use runtime_data in tesla_wall_connector (#167893)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:38:04 +02:00
epenet
ba09a54a37 Use runtime_data in tradfri integration (#167896)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:19:02 +02:00
puddly
7125796aac Temporarily stop the Z2M app when installing firmwares (#163958)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 14:16:47 +02:00
Robert Resch
ce9875806d Use runtime_data in launch_library integration (#167887)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-04-10 13:47:37 +02:00
Pierre Hauweele
7cf422361b Make the scaffold script ask for the integration type (#167725) 2026-04-10 12:49:40 +02:00
Robert Resch
9a97f1e8d2 Use runtime_data in soma integration (#167890)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:49:39 +02:00
Robert Resch
777f78f74d Use runtime_data in litejet integration (#167888) 2026-04-10 12:35:31 +02:00
Joost Lekkerkerker
10c922b21f Support Chess.com accounts with no name (#167824) 2026-04-10 12:34:05 +02:00
epenet
aa293ba2f4 Add ability to load custom Tuya quirks (#166952) 2026-04-10 12:31:36 +02:00
Tomer
5edcfdf621 Mark docs-examples and docs-known-limitations as done for victron_gx (#167866)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 12:13:06 +02:00
Andre v d Walt
244ed14019 smartthings: add Samsung OCF AC purify switch (#167705) 2026-04-10 12:12:36 +02:00
Robert Resch
109ec0705c Use runtime_data in vilfo integration (#167886)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:54:32 +02:00
epenet
6f7fa85d18 Use runtime_data in system_bridge integration (#167880)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:53:53 +02:00
epenet
8d2564f00f Use runtime_data in soundtouch integration (#167869)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:52:47 +02:00
epenet
f7096e3744 Use runtime_data in srp_energy integration (#167870)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:51:29 +02:00
epenet
d7f28a09bb Use runtime_data in sleepiq integration (#167865)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:50:53 +02:00
epenet
a54ea071f8 Use runtime_data in Slack (#167864)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 11:50:19 +02:00
epenet
1597b740da Use runtime_data in skybell integration (#167862)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:48:48 +02:00
epenet
3758d606c9 Use runtime_data in simplisafe integration (#167858)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:47:58 +02:00
epenet
a79988aca7 Use runtime_data in sia integration (#167857)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:46:43 +02:00
epenet
837cd7d89d Use runtime_data in sanix integration (#167856)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:46:12 +02:00
Retha Runolfsson
038bb6c15d Add child lock and wireless charging switches for air purifier (#167140)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 11:29:02 +02:00
Paul Bottein
6ccede7f30 Add fabric index fields to Matter lock user and credential responses (#167875) 2026-04-10 11:18:10 +02:00
Abílio Costa
fb541d8835 Replace ding with new ring event in Ring integration doorbell (#167728) 2026-04-10 11:04:52 +02:00
epenet
39a2c08d4e Use runtime_data in switchbot_cloud integration (#167879)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 10:59:20 +02:00
epenet
ea642980f2 Use runtime_data in switchbee (#167878)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 10:45:04 +02:00
Renaud Allard
4c8ea3669c Load lovelace resource collection eagerly during setup (#165773) 2026-04-10 04:38:17 -04:00
epenet
14f24226ae Use runtime_data in streamlabswater (#167874)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 10:31:55 +02:00
epenet
3a9f805f10 Use runtime_data in surepetcare integration (#167877)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 10:31:42 +02:00
Maikel Punie
191dd42a92 Bump velbusaio to 2026.4.0 (#167868) 2026-04-10 09:59:53 +02:00
Daniel Hjelseth Høyer
35ffffb159 Improve Tibber price coordinator (#166175)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 09:57:32 +02:00
J. Nick Koston
4494f9ff6b Bump aioesphomeapi to 44.13.1 (#167855) 2026-04-10 09:57:28 +02:00
dependabot[bot]
09e6b6533a Bump dawidd6/action-download-artifact from 19 to 20 (#167861)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 09:56:29 +02:00
dependabot[bot]
c42e37dd7d Bump docker/login-action from 4.0.0 to 4.1.0 (#167860)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 09:55:57 +02:00
Simone Chemelli
853b6a80d2 Fix stale devices removal for Alexa devices (#167837) 2026-04-10 09:53:51 +02:00
TheJulianJES
eaa1fc591a Bump ZHA to 1.1.2 (#167849) 2026-04-10 09:52:19 +02:00
peteS-UK
3f388e88e0 Add support for deletion of stale devices for Squeezebox (#159848)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-04-10 09:47:20 +02:00
epenet
44eea221b7 Use runtime_data in Snooz (#167867)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:43:06 +02:00
Brendan McShane
6a3937b96b Add HomeKit AirPlay Enable (Ecobee) (#159564)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-10 09:33:22 +02:00
norkudev
1c5e020344 Include indirect automation references in device view (#167719) 2026-04-10 01:56:37 +02:00
Brett Adams
6ac7952f26 Tessie: use Vehicle methods for button commands (#167193)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:39:28 +02:00
J. Diego Rodríguez Royo
7f0d94da9f Fix service.yaml values for Home Connect (#167847) 2026-04-10 00:34:50 +02:00
epenet
8f383bccd9 Set assumed state on Renault number entity (#167644) 2026-04-10 00:20:47 +02:00
Fabian Munkes
8c50cb2ab1 Add initial support for PlayerOptions: Switch entities to Music Assistant (#167829)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-04-10 00:14:37 +02:00
David Bonnes
b0888b051c Improve services.yaml in Evohome to improve UI/UX (#167788)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-10 00:12:30 +02:00
Fabian Munkes
0764e3e239 Add support for sound modes to Music Assistant. (#167838) 2026-04-10 00:11:06 +02:00
Robin Thoni
cf4d8f0974 Add VoIP sensors to sfr_box (#166609)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-04-10 00:03:49 +02:00
Ronald van der Meer
e7e4c495fd Add Duco integration (#167220) 2026-04-09 23:54:31 +02:00
Matthias Alphart
8f6ae15a6a KNX: Configure entity expose from config panel UI (#167692) 2026-04-09 23:46:50 +02:00
David Bishop
910dcb4d68 Govee light local availability test cleanup (#167702)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:45:37 +02:00
G Johansson
86b5efaf2c Don't use async_update_reload_and_abort with update listeners in tele… (#167696) 2026-04-09 23:44:42 +02:00
Noah Husby
5f8483ba07 Add party mode to Russound RIO (#167342) 2026-04-09 23:40:15 +02:00
g4bri3lDev
496c9551b3 Add event platform for OpenDisplay (#167393) 2026-04-09 23:37:25 +02:00
puddly
2d45f9978e List serial ports via USB integration helpers (Q-Z) (#167701) 2026-04-09 17:24:36 -04:00
Petro31
caa1a8880f Allow trigger based template entities to skip option validation (#167708) 2026-04-09 23:23:37 +02:00
Jeef
1e78666b90 Prevent the intellifire client from polling independently of its coordinator (#165341)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-09 23:22:33 +02:00
Fabian Neundorf
53738c0168 Add 2fa support in picnic integration (#167636) 2026-04-09 23:09:35 +02:00
J. Diego Rodríguez Royo
ca96c751e1 Add delayed start as an operation state that flags as program running at Home Connect (#167549) 2026-04-09 23:03:03 +02:00
johanzander
2a0a386e6d Update Growatt quality scale: mark docs rules done and exempt discovery (#166075)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:59:50 +02:00
Michael
79dfa61e8b Add favorite collection to immich media source (#167841) 2026-04-09 22:51:32 +02:00
Kurt Chrisford
431387b76d Fix Actron Air quality scale rule statuses (#167149) 2026-04-09 22:47:59 +02:00
Tomer
f4a2f37fa6 Victron GX select platform (#167675)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-09 22:45:47 +02:00
Fabian Munkes
ec54a121c1 Add initial support for PlayerOptions: Text entities to Music Assistant (#167832)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-04-09 22:10:20 +02:00
Petro31
f5d5ee71f5 Update template lock tests to use new framework (#164621) 2026-04-09 22:01:02 +02:00
Michael
7e1f4d27e8 Bump aioimmich to 0.14.0 (#167833) 2026-04-09 22:00:39 +02:00
mettolen
4700c79ace Implement reconfiguration flow for Huum integration (#167711) 2026-04-09 21:57:18 +02:00
Artur Pragacz
b6ea61f953 Fix run_then_background in service intent handler (#167817) 2026-04-09 21:37:16 +02:00
Tomer
1eab08f986 Victron GX binary_sensor platform (#167527)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-09 21:32:16 +02:00
Erwin Douna
f491ec8b44 Generate translations optimization (#166483)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-09 21:30:17 +02:00
Jamie Magee
e639e983dc Use offline command for non-UTF-8 stdout test (#167466) 2026-04-09 21:25:59 +02:00
David Bonnes
a983cb7ccd Tidy up Evohome code, and improve docstrings (#167827) 2026-04-09 21:18:01 +02:00
Petro31
89ddfff66f Update template switch tests to use new framework (#167826) 2026-04-09 21:16:21 +02:00
Petro31
77c8eab698 Update template update tests to use new framework (#167828) 2026-04-09 21:11:04 +02:00
Petro31
9ac730fb58 Update template vacuum tests to use new framework (#167830) 2026-04-09 21:07:02 +02:00
Raj Laud
6cc05e6a28 Fix Victron BLE false reauth triggered by unknown enum bitmask combinations (#167809)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 20:53:26 +02:00
Denis Shulyaka
75a4b088bc Entity translation for Anthropic integration (#166725)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-09 20:41:04 +02:00
Petro31
9056e0b64f Update template cover tests to use new framework (#167686) 2026-04-09 20:39:12 +02:00
Petro31
3a1002457b Update template number tests to use new framework (#167823) 2026-04-09 20:34:45 +02:00
Petro31
97fe710187 Update template select tests to use new framework (#167825) 2026-04-09 20:34:02 +02:00
Benjamin Hudgens
09585a7e1c Revert "Fix Ring snapshots" - #164337 (#167790) 2026-04-09 20:21:14 +02:00
puddly
6d55c076e4 List serial ports via USB integration helpers (A-P) (#167695) 2026-04-09 19:56:59 +02:00
Samuel Xiao
8b37cc8719 Switchbot Cloud: Enable Webhook for Bot (#165647) 2026-04-09 19:43:13 +02:00
Simone Chemelli
6510b3d1d1 Add configuration URL to Comelit (#167813) 2026-04-09 19:36:08 +02:00
David Bonnes
e5a83106d7 Change default icon of Evohome's WaterHeater entities (#167818) 2026-04-09 19:14:32 +02:00
Florent Thoumie
de973e8900 iaqualink: don't return False in async_setup_entry (#167812) 2026-04-09 17:59:52 +02:00
Petro31
fefc5a950f Update template binary sensor tests to use new framework (#167704) 2026-04-09 17:56:55 +02:00
Petro31
b3e7ae0fdd Update template alarm control panel tests to use new framework (#167799) 2026-04-09 17:56:24 +02:00
Petro31
7bad7fc4f6 Update template button tests to use new framework (#167806) 2026-04-09 17:53:11 +02:00
Petro31
36944525e1 Update template event tests to use new framework (#167808) 2026-04-09 17:52:51 +02:00
Barry vd. Heuvel
f3d25a04f8 Bump weheat to 2026.4.8 (#167807) 2026-04-09 17:50:18 +02:00
MoonDevLT
f2c20fedeb Add zeroconf discovery to Lunatone integration (#167582)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-09 17:31:01 +02:00
mcisk
93e9575547 Add reauthentication flow to Autoskope integration (#167688) 2026-04-09 17:30:53 +02:00
Brett Adams
681f8bedb4 Handle boolean charging state in Tessie sensor (#165172) 2026-04-09 17:26:53 +02:00
Lamarqe
566ff6d1d5 Add frequency unit conversion (#167537) 2026-04-09 17:16:02 +02:00
epenet
5bec3d1b41 Disable pilight integration (#167760) 2026-04-09 16:44:18 +02:00
MoonDevLT
050d929d8a Bump lunatone-rest-api-client to 0.9.1 (#167804) 2026-04-09 16:28:35 +02:00
Barry vd. Heuvel
15045f55d5 Make Weheat energy output TOTAL instead of TOTAL_INCREASING (#167761) 2026-04-09 15:42:57 +02:00
epenet
b2fb6c0a68 Use runtime_data in seventeentrack integration (#167737)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:27:15 +02:00
Tomer
66e35cef06 Bump victron-mqtt to 2026.4.3 (#167787) 2026-04-09 15:26:37 +02:00
Denis Shulyaka
9ea527520a Bump anthropic to 0.92.0 (#167793) 2026-04-09 15:18:21 +02:00
Retha Runolfsson
872120821c Fix SwitchBot encrypted device method selection not resetting on back (#167749)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-04-09 15:15:49 +02:00
wibbit
998f24649d geniushub: add water heater platform tests (#167763)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-09 15:11:41 +02:00
TimL
cc21c99e55 Fix "IR emitter" sentence case in SMLIGHT string (#167684) 2026-04-09 15:00:09 +02:00
MoonDevLT
4efb6b9b56 Add color modes to Lunatone light entity (#167574) 2026-04-09 14:22:44 +02:00
Marc Mueller
a9f0cd203c Update pytest warnings filter (#167703) 2026-04-09 14:20:59 +02:00
Maciej Bieniek
eb31499e78 Bump aiotractive to 1.0.2 (#167783) 2026-04-09 14:19:46 +02:00
TimL
d292aa2e90 Add missing exception string from smlight IR platform (#167784) 2026-04-09 13:50:30 +02:00
epenet
87f44a67be Use runtime_data in sharkiq integration (#167741)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:45:44 +02:00
epenet
efb0e80577 Use runtime_data in smart_meter_texas integration (#167743)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:44:34 +02:00
epenet
11c34c7ddf Use runtime_data in snapcast integration (#167744)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:40:11 +02:00
epenet
4b820a0204 Use runtime_data in somfy_mylink integration (#167745)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:39:19 +02:00
epenet
0c98f01b07 Use runtime_data in starline integration (#167746)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:37:59 +02:00
epenet
aa50822a82 Use runtime_data in Subaru integration (#167747)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:36:45 +02:00
epenet
f634525798 Use runtime_data in syncthing integration (#167748)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:34:42 +02:00
Denis Shulyaka
047500af42 Anthropic pretty device model name (#167772)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-09 13:33:02 +02:00
MoonDevLT
db589f7318 Bump lunatone-rest-api-client to 0.9.0 (#167762) 2026-04-09 13:20:13 +02:00
TimL
3ea15f2743 Refactor Ultima fixtures to reduce duplication (#167731) 2026-04-09 13:20:01 +02:00
Maciej Bieniek
8e430d9f26 Bump brother to 6.1.0 (#167768) 2026-04-09 13:19:05 +02:00
Michael
075b47b5f9 Set proper state for the internet_access switches in FRITZ!Box Tools (#167767) 2026-04-09 12:46:34 +02:00
wollew
949c907407 Bump pyvlx to 0.2.33 (#167764) 2026-04-09 11:55:32 +02:00
Franck Nijhof
326799209c Extract config entry template functions into a config entry Jinja2 extension (#167360)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-09 11:49:56 +02:00
Simone Chemelli
65bc7c9ea7 Allow force alarm actions for Comelit (#167202) 2026-04-09 11:16:05 +02:00
dependabot[bot]
86d443f8c6 Bump pypa/gh-action-pypi-publish from 1.13.0 to 1.14.0 (#167648)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 10:29:18 +02:00
epenet
19ae7e722e Bump pybotvac to 0.0.29 (#167758) 2026-04-09 09:51:03 +02:00
Abílio Costa
57568fdc2c Add standard event type for doorbell event entities (#167630) 2026-04-09 00:02:05 +01:00
Oluwatobi Mustapha
4c8a660b2d Redact Z-Wave add-on options sensitive error details (#167239)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-08 21:17:26 +02:00
puddly
b0511519a1 Expose async serial port scanning helper in USB integration (#167706) 2026-04-08 14:29:27 -04:00
Marc Mueller
038b583888 Update types packages (#167700) 2026-04-08 19:20:57 +02:00
Raphael Hehl
018c130988 Update UniFi Access quality scale: mark documentation rules as done (#166898)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-08 16:18:19 +02:00
David Bishop
462e9965d7 Mark Govee local devices unavailable when they stop responding (#167566)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:18:55 +01:00
Franck Nijhof
ea4d85f96c Extract arithmetic template filters into the math Jinja2 extension (#167309)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-08 14:27:22 +02:00
Petro31
1a4d518ef2 Update template fan tests to use new framework (#167625) 2026-04-08 13:51:15 +02:00
TimL
a48a770ca4 Add Infrared platform to SMLIGHT (#167568) 2026-04-08 12:35:48 +01:00
Tom
e4aeee9d85 Fix ProxmoxVE migration causing reauthentication (#167624) 2026-04-08 13:22:25 +02:00
Raphael Hehl
726edf3a3b Unifi access protect api key hint (#167404)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-08 13:21:54 +02:00
epenet
b98aa0ad91 Use runtime_data in rdw integration (#167654)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:18:26 +02:00
David
f82b8cb7c7 Bump pylutron-caseta to 0.28.0 (#167642) 2026-04-08 13:17:45 +02:00
epenet
d6342d51cc Use runtime_data in radiotherm (#167650)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:16:21 +02:00
epenet
1eead15c24 Use runtime_data in Rabbit Air (#167649)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:15:46 +02:00
epenet
2e6137325c Use runtime_data in ridwell integration (#167658)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:14:23 +02:00
Kurt Chrisford
8d3d4a1b5c Add diagnostics to Actron Air (#167145) 2026-04-08 13:12:56 +02:00
Tomer
3e5132bf85 Victron GX reauthentication-flow (#167614)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-04-08 12:58:16 +02:00
epenet
65e4b26006 Use suggested uom in Renault charging power sensor (#167646) 2026-04-08 12:32:26 +02:00
epenet
13f1a42d69 Use runtime_data in roon integration (#167660)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:16:32 +02:00
epenet
5be48affcf Use runtime_data in rova integration (#167661)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:15:59 +02:00
epenet
8994f501f1 Use runtime_data in rympro integration (#167663)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:15:34 +02:00
epenet
7f49ecffd3 Use runtime_data in romy integration (#167665)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:14:29 +02:00
epenet
a560967861 Use runtime_data in roomba integration (#167667)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:13:44 +02:00
epenet
82202ee1c2 Use runtime_data in ruckus_unleashed integration (#167662)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:13:26 +02:00
Franck Nijhof
b697b3a54e Extract version template function into a version Jinja2 extension (#167172)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-08 12:10:22 +02:00
Kurt Chrisford
6cf5bbe2f5 Bump actronneoapi to 0.5.0 (#167669) 2026-04-08 12:06:48 +02:00
epenet
c0c61533e6 Use runtime_data in risco integration (#167659)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:05:04 +02:00
epenet
15e434431d Use runtime_data in renson integration (#167664)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:58:10 +02:00
epenet
0452bb91c7 Cleanup unused renault base entity method (#167643) 2026-04-08 11:57:55 +02:00
epenet
5620fc9e96 Use runtime_data in recollect_waste integration (#167655)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:57:44 +02:00
epenet
1a5ef199da Remove duplicated FlussConfigEntry type aliases (#167676)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:48:02 +02:00
Joost Lekkerkerker
e98eec113e Add DHCP discovery to MyStrom (#167084)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-08 11:36:20 +02:00
Mattheinrichs
c74d4047d8 Add diagnostics support to tplink_omada (#166802)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-04-08 10:37:01 +02:00
epenet
f5ae250720 Improve type hints in ipma system_health (#167670) 2026-04-08 10:30:27 +02:00
epenet
bea4eea871 Use runtime_data in rainforest_eagle integration (#167652)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:42:10 +02:00
Maciej Bieniek
f4f202a8a1 Fix Tractive switch availability (#167599) 2026-04-08 07:44:45 +02:00
Erwin Douna
c30ccf3750 Bump pyportainer 1.0.38 (#167627) 2026-04-08 05:36:10 +02:00
Raphael Hehl
0b8390cf21 Bump py-unifi-access to 1.1.5 (#167633)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-04-08 00:47:58 +02:00
Joakim Plate
1a048a7845 Move logging for loading/unloading config entry to integration logger (#167415) 2026-04-07 23:43:11 +02:00
Erik Montnemery
08097c67eb Bump securetar to 2026.4.1 (#167617) 2026-04-07 20:19:51 +01:00
Oliver Verity
550e53d192 Add support for storing OpenAI conversation responses (#165723) 2026-04-07 20:19:25 +01:00
Fabian Munkes
09ee76c265 Add initial support for PlayerOptions: Number entities to Music Assistant (#162669)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-04-07 20:51:26 +02:00
Norbert Rittel
f7b2f5e8f1 Improve Remote action naming consistency (#167382) 2026-04-07 18:48:05 +02:00
G Johansson
a1414717ad Bump holidays to 0.94 (#167604) 2026-04-07 18:41:28 +02:00
Erik Montnemery
2f0889ac02 Fix securetar size calculation when encrypting backup (#167602) 2026-04-07 18:40:06 +02:00
Joakim Plate
323b3a4d96 Add contour and position names to gardena (#167512) 2026-04-07 18:29:05 +02:00
Erwin Douna
8aa0e9f6c3 Refactor to active_containers (#167529) 2026-04-07 18:27:43 +02:00
Erik Montnemery
906475249c Bump securetar to 2026.4.0 (#167600) 2026-04-07 16:00:06 +02:00
Oliver Verity
354b5860bb Add read-only MCP Assist context snapshot resource (#167396) 2026-04-07 06:57:55 -07:00
Ludovic BOUÉ
74957969f7 Add select entities for Roborock q10 s5+ (#166142)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-04-07 06:55:43 -07:00
Leo Periou
b52ce22ee7 fix EWS deviceType problem (#167597) 2026-04-07 15:49:21 +02:00
Jan Čermák
920ffdb9b5 Remove homeassistant/actions/helpers/info from builder workflow (#167573) 2026-04-07 14:52:05 +02:00
Artur Pragacz
4a454dff02 Set up condition and trigger helpers in check config script (#167589)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-07 14:50:37 +02:00
Stefan S
481eb66bc5 Add unit 'µA' for the units of electric current (#166786)
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-04-07 14:48:21 +02:00
Retha Runolfsson
b76627a442 Add light sensor button to switchbot air purifier (#167134)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 14:13:35 +02:00
Denis Shulyaka
1aa214fb61 Enable minimal thinking budget by default for Anthropic integration (#167593) 2026-04-07 13:36:49 +02:00
markhannon
6e30de3a1c Bump zcc-helper to 3.8 (#167555) 2026-04-07 13:31:51 +02:00
Franck Nijhof
b981ece163 Pin actions/helpers/info to fix release build (#167327) 2026-04-03 20:58:54 +00:00
Franck Nijhof
7ea931fdc8 2026.4.1 (#167310) 2026-04-03 22:10:54 +02:00
Franck Nijhof
f3038a20af Bump version to 2026.4.1 2026-04-03 16:05:08 +00:00
Pete Sage
de234c7190 Sonos alarm switch entities may not be created when speaker offline initially (#167303) 2026-04-03 16:01:17 +00:00
Pete Sage
399681984f Bump soco to 0.30.15 (#167299) 2026-04-03 16:01:16 +00:00
Joost Lekkerkerker
5ca14ca7d7 Bump Zinvolt to 0.4.1 (#167296) 2026-04-03 16:01:15 +00:00
Joost Lekkerkerker
ac53cfa85a Make sure we take all Zinvolt battery units in account (#167294) 2026-04-03 16:01:13 +00:00
Ludovic BOUÉ
02f1a9c3a9 Fix Matter water heater off mode (#167286)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-04-03 16:01:12 +00:00
Bram Kragten
f93fdceac9 Update frontend to 20260325.6 (#167285) 2026-04-03 16:01:11 +00:00
Ludovic BOUÉ
711a89f7b8 Fix to allow Matter Fan percent setting to be null when FanMode is Auto (#167279)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-04-03 16:01:09 +00:00
Norbert Rittel
19e58c554e Improve Assist satellite action naming consistency (#167278) 2026-04-03 16:01:08 +00:00
Joost Lekkerkerker
feb6c2bfe6 Bump zinvolt to 0.4.0 (#167276) 2026-04-03 16:01:07 +00:00
Norbert Rittel
6bb91422ff Improve Media player action naming consistency (#167274)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-03 16:01:06 +00:00
Andrew Jackson
3bd699285b Remove Transmission port forward sensor (#167269) 2026-04-03 16:00:17 +00:00
dotlambda
6d10305197 Bump psutil to 7.2.2 (#167263) 2026-04-03 15:57:57 +00:00
Joakim Plate
42a9c8488d Update arcam to 1.8.3 (#167249) 2026-04-03 15:57:56 +00:00
Norbert Rittel
c6c273559e Improve Recorder action naming consistency (#167244) 2026-04-03 15:57:55 +00:00
Pete Sage
f7394ce302 Fix Sonos reporting wrong state when media title is whitespace (#167223) 2026-04-03 15:57:53 +00:00
G Johansson
175dec6f1a Bump holiday library to 0.93 (#167217) 2026-04-03 15:57:52 +00:00
G Johansson
d137761cb5 Fix SMHI (#167212) 2026-04-03 15:57:50 +00:00
Simone Chemelli
8055cbc58d Migrate image unique_id for Fritz (#167209) 2026-04-03 15:57:49 +00:00
Joost Lekkerkerker
c9dff27590 Remove not implemented supported feature from Wiim (#167205) 2026-04-03 15:57:48 +00:00
Mike Degatano
c913a858b6 Wrap hassio import in is_hassio check in get_system_info helper (#167111) 2026-04-03 15:57:46 +00:00
Joost Lekkerkerker
4ed33a804e Bump pySmartThings to 3.7.3 (#167075) 2026-04-03 15:57:45 +00:00
Kevin O'Brien
8bf5674826 Fix Proxmox VE backup status sensor false positive due to case mismatch (#167069)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 15:57:43 +00:00
Manu
b8a0b0083b Fix websocket calling async_release_notes in update component although unavailable (#167067) 2026-04-03 15:57:42 +00:00
Bram Kragten
a57c101b5e Fix select condition state selector (#167064) 2026-04-03 15:57:41 +00:00
Brett Adams
957b8c1c52 Fix Tesla Fleet OAuth scope refresh during reauth (#166920) 2026-04-03 15:57:40 +00:00
Brett Adams
bb002d051b Fix Tesla Fleet charge current scope handling (#166919) 2026-04-03 15:57:38 +00:00
LTek
2b2fd4ac92 Fix Ring snapshots (#164337)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-04-03 15:57:37 +00:00
Jan Bouwhuis
f4c270629b Fix tuya energy sensor units (#160392) 2026-04-03 15:57:35 +00:00
3033 changed files with 186091 additions and 31920 deletions

View File

@@ -186,15 +186,11 @@ If `CHANGE_TYPE` IS "Breaking change" or "Deprecation", keep the `## Breaking ch
## Step 10: Push Branch and Create PR
```bash
# Get branch name and GitHub username
BRANCH=$(git branch --show-current)
PUSH_REMOTE=$(git config "branch.$BRANCH.remote" 2>/dev/null || git remote | head -1)
GITHUB_USER=$(gh api user --jq .login 2>/dev/null || git remote get-url "$PUSH_REMOTE" | sed -E 's#.*[:/]([^/]+)/([^/]+)(\.git)?$#\1#')
Push the branch with upstream tracking, and create a PR against `home-assistant/core` with the generated title and body:
```bash
# Create PR (gh pr create pushes the branch automatically)
gh pr create --repo home-assistant/core --base dev \
--head "$GITHUB_USER:$BRANCH" \
--draft \
--title "TITLE_HERE" \
--body "$(cat <<'EOF'

View File

@@ -1,7 +1,6 @@
---
name: github-pr-reviewer
description: Reviews GitHub pull requests and provides feedback comments.
disallowedTools: Write, Edit
description: Reviews GitHub pull requests and provides feedback comments. This is the top skill to use for reviewing Pull Requests from GitHub.
---
# Review GitHub Pull Request
@@ -28,12 +27,13 @@ disallowedTools: Write, Edit
- No need to highlight things that are already good.
## Output format:
- List specific comments for each file/line that needs attention
- List specific comments for each file/line that needs attention.
- In the end, summarize with an overall assessment (approve, request changes, or comment) and bullet point list of changes suggested, if any.
- Example output:
```
Overall assessment: request changes.
- [CRITICAL] Memory leak in homeassistant/components/sensor/my_sensor.py:143
- [PROBLEM] Inefficient algorithm in homeassistant/helpers/data_processing.py:87
- [SUGGESTION] Improve variable naming in homeassistant/helpers/config_validation.py:45
- [CRITICAL] sensor.py:143 - Memory leak
- [PROBLEM] data_processing.py:87 - Inefficient algorithm
- [SUGGESTION] test_init.py:45 - Improve x variable name
```
- Make sure to include the file and line number when possible in the bullet points.

View File

@@ -1,5 +1,5 @@
---
name: Home Assistant Integration knowledge
name: ha-integration-knowledge
description: Everything you need to know to build, test and review Home Assistant Integrations. If you're looking at an integration, you must use this as your primary reference.
---
@@ -12,6 +12,9 @@ description: Everything you need to know to build, test and review Home Assistan
- When looking for examples, prefer integrations with the platinum or gold quality scale level first.
- Polling intervals are NOT user-configurable. Never add scan_interval, update_interval, or polling frequency options to config flows or config entries.
- Do NOT allow users to set config entry names in config flows. Names are automatically generated or can be customized later in UI. Exception: helper integrations may allow custom names.
- For entity actions and entity services, avoid requesting redundant defensive checks for fields already enforced by Home Assistant validation schemas and entity filters; only request extra guards when values bypass validation or are transformed unsafely.
- When validation guarantees a key is present, prefer direct dictionary indexing (`data["key"]`) over `.get("key")` so invalid assumptions fail fast.
- Integrations should be thin wrappers. Protocol parsing, device state machines, or other domain logic belong in a separate PyPI library, not in the integration itself. If unsure, ask before inlining.
The following platforms have extra guidelines:
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection

View File

@@ -36,6 +36,7 @@ base_platforms: &base_platforms
- homeassistant/components/image_processing/**
- homeassistant/components/infrared/**
- homeassistant/components/lawn_mower/**
- homeassistant/components/radio_frequency/**
- homeassistant/components/light/**
- homeassistant/components/lock/**
- homeassistant/components/media_player/**

View File

@@ -5,7 +5,7 @@
# Copilot code review instructions
- Start review comments with a short, one-sentence summary of the suggested fix.
- Do not add comments about code style, formatting or linting issues.
- Do not comment on code style, formatting or linting issues.
# GitHub Copilot & Claude Code Instructions
@@ -21,7 +21,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Python Syntax Notes
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses.
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses. Never flag this as an issue since Home Assistant officially supports Python 3.14.
## Testing
@@ -32,7 +32,5 @@ Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) ov
Integrations with Platinum or Gold level in the Integration Quality Scale reflect a high standard of code quality and maintainability. When looking for examples of something, these are good places to start. The level is indicated in the manifest.json of the integration.
# Skills
- Home Assistant Integration knowledge: .claude/skills/integrations/SKILL.md
When reviewing entity actions, do not suggest extra defensive checks for input fields that are already validated by Home Assistant's service/action schemas and entity selection filters. Suggest additional guards only when data bypasses those validators or is transformed into a less-safe form.
When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")` so contract violations are surfaced instead of silently masked.

View File

@@ -0,0 +1,46 @@
---
applyTo: "homeassistant/components/**, tests/components/**"
excludeAgent: "cloud-agent"
---
<!-- Automatically generated by gen_copilot_instructions.py, do not edit -->
## File Locations
- **Integration code**: `./homeassistant/components/<integration_domain>/`
- **Integration tests**: `./tests/components/<integration_domain>/`
## General guidelines
- When looking for examples, prefer integrations with the platinum or gold quality scale level first.
- Polling intervals are NOT user-configurable. Never add scan_interval, update_interval, or polling frequency options to config flows or config entries.
- Do NOT allow users to set config entry names in config flows. Names are automatically generated or can be customized later in UI. Exception: helper integrations may allow custom names.
- For entity actions and entity services, avoid requesting redundant defensive checks for fields already enforced by Home Assistant validation schemas and entity filters; only request extra guards when values bypass validation or are transformed unsafely.
- When validation guarantees a key is present, prefer direct dictionary indexing (`data["key"]`) over `.get("key")` so invalid assumptions fail fast.
- Integrations should be thin wrappers. Protocol parsing, device state machines, or other domain logic belong in a separate PyPI library, not in the integration itself. If unsure, ask before inlining.
The following platforms have extra guidelines:
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection
- **Repairs**: [`platform-repairs.md`](platform-repairs.md) for user-actionable repair issues
## Integration Quality Scale
- When validating the quality scale rules, check them at https://developers.home-assistant.io/docs/core/integration-quality-scale/rules
- When implementing or reviewing an integration, always consider the quality scale rules, since they promote best practices.
Template scale file: `./script/scaffold/templates/integration/integration/quality_scale.yaml`
### How Rules Apply
1. **Check `manifest.json`**: Look for `"quality_scale"` key to determine integration level
2. **Bronze Rules**: Always required for any integration with quality scale
3. **Higher Tier Rules**: Only apply if integration targets that tier or higher
4. **Rule Status**: Check `quality_scale.yaml` in integration folder for:
- `done`: Rule implemented
- `exempt`: Rule doesn't apply (with reason in comment)
- `todo`: Rule needs implementation
## Testing Requirements
- Tests should avoid interacting or mocking internal integration details. For more info, see https://developers.home-assistant.io/docs/development_testing/#writing-tests-for-integrations

217
.github/renovate.json vendored Normal file
View File

@@ -0,0 +1,217 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"enabledManagers": [
"pep621",
"pip_requirements",
"pre-commit",
"custom.regex",
"homeassistant-manifest"
],
"pre-commit": {
"enabled": true
},
"pip_requirements": {
"managerFilePatterns": [
"/(^|/)requirements[\\w_-]*\\.txt$/",
"/(^|/)homeassistant/package_constraints\\.txt$/"
]
},
"homeassistant-manifest": {
"managerFilePatterns": [
"/^homeassistant/components/[^/]+/manifest\\.json$/"
]
},
"customManagers": [
{
"customType": "regex",
"description": "Update ruff required-version in pyproject.toml",
"managerFilePatterns": ["/^pyproject\\.toml$/"],
"matchStrings": ["required-version = \">=(?<currentValue>[\\d.]+)\""],
"depNameTemplate": "ruff",
"datasourceTemplate": "pypi"
}
],
"minimumReleaseAge": "7 days",
"prConcurrentLimit": 10,
"prHourlyLimit": 2,
"schedule": ["before 6am"],
"semanticCommits": "disabled",
"commitMessageAction": "Update",
"commitMessageTopic": "{{depName}}",
"commitMessageExtra": "to {{newVersion}}",
"automerge": false,
"vulnerabilityAlerts": {
"enabled": false
},
"packageRules": [
{
"description": "Deny all by default — allowlist below re-enables specific packages",
"matchPackageNames": ["*"],
"enabled": false
},
{
"description": "Core runtime dependencies (allowlisted)",
"matchPackageNames": [
"aiohttp",
"aiohttp-fast-zlib",
"aiohttp_cors",
"aiohttp-asyncmdnsresolver",
"yarl",
"httpx",
"requests",
"urllib3",
"certifi",
"orjson",
"PyYAML",
"Jinja2",
"cryptography",
"pyOpenSSL",
"PyJWT",
"SQLAlchemy",
"Pillow",
"attrs",
"uv",
"voluptuous",
"voluptuous-serialize",
"voluptuous-openapi",
"zeroconf"
],
"enabled": true,
"labels": ["dependency", "core"]
},
{
"description": "Common Python utilities (allowlisted)",
"matchPackageNames": [
"astral",
"atomicwrites-homeassistant",
"audioop-lts",
"awesomeversion",
"bcrypt",
"ciso8601",
"cronsim",
"defusedxml",
"fnv-hash-fast",
"getmac",
"ical",
"ifaddr",
"lru-dict",
"mutagen",
"propcache",
"pyserial",
"python-slugify",
"PyTurboJPEG",
"securetar",
"standard-aifc",
"standard-telnetlib",
"ulid-transform",
"url-normalize",
"xmltodict"
],
"enabled": true,
"labels": ["dependency"]
},
{
"description": "Home Assistant ecosystem packages (core-maintained, no cooldown)",
"matchPackageNames": [
"hassil",
"home-assistant-bluetooth",
"home-assistant-frontend",
"home-assistant-intents",
"infrared-protocols"
],
"enabled": true,
"minimumReleaseAge": null,
"labels": ["dependency", "core"]
},
{
"description": "Test dependencies (allowlisted)",
"matchPackageNames": [
"pytest",
"pytest-asyncio",
"pytest-aiohttp",
"pytest-cov",
"pytest-freezer",
"pytest-github-actions-annotate-failures",
"pytest-socket",
"pytest-sugar",
"pytest-timeout",
"pytest-unordered",
"pytest-picked",
"pytest-xdist",
"pylint",
"pylint-per-file-ignores",
"astroid",
"coverage",
"freezegun",
"syrupy",
"respx",
"requests-mock",
"ruff",
"codespell",
"yamllint",
"zizmor"
],
"enabled": true,
"labels": ["dependency"]
},
{
"description": "For types-* stubs, only allow patch updates. Major/minor bumps track the upstream runtime package version and must be manually coordinated with the corresponding pin.",
"matchPackageNames": ["/^types-/"],
"matchUpdateTypes": ["patch"],
"enabled": true,
"labels": ["dependency"]
},
{
"description": "Pre-commit hook repos (allowlisted, matched by owner/repo)",
"matchPackageNames": [
"astral-sh/ruff-pre-commit",
"codespell-project/codespell",
"adrienverge/yamllint",
"zizmorcore/zizmor-pre-commit"
],
"enabled": true,
"labels": ["dependency"]
},
{
"description": "Group ruff pre-commit hook with its PyPI twin into one PR",
"matchPackageNames": ["astral-sh/ruff-pre-commit", "ruff"],
"groupName": "ruff",
"groupSlug": "ruff"
},
{
"description": "Group codespell pre-commit hook with its PyPI twin into one PR",
"matchPackageNames": ["codespell-project/codespell", "codespell"],
"groupName": "codespell",
"groupSlug": "codespell"
},
{
"description": "Group yamllint pre-commit hook with its PyPI twin into one PR",
"matchPackageNames": ["adrienverge/yamllint", "yamllint"],
"groupName": "yamllint",
"groupSlug": "yamllint"
},
{
"description": "Group zizmor pre-commit hook with its PyPI twin into one PR",
"matchPackageNames": ["zizmorcore/zizmor-pre-commit", "zizmor"],
"groupName": "zizmor",
"groupSlug": "zizmor"
},
{
"description": "Group pylint with astroid (their versions are linked and must move together)",
"matchPackageNames": ["pylint", "astroid"],
"groupName": "pylint",
"groupSlug": "pylint"
}
]
}

View File

@@ -47,10 +47,6 @@ jobs:
with:
python-version-file: ".python-version"
- name: Get information
id: info
uses: home-assistant/actions/helpers/info@5752577ea7cc5aefb064b0b21432f18fe4d6ba90 # zizmor: ignore[unpinned-uses]
- name: Get version
id: version
uses: home-assistant/actions/helpers/version@master # zizmor: ignore[unpinned-uses]
@@ -80,7 +76,7 @@ jobs:
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
- name: Upload translations
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: translations
path: translations.tar.gz
@@ -112,7 +108,7 @@ jobs:
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@8a338493df3d275e4a7a63bcff3b8fe97e51a927 # v19
uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/frontend
@@ -123,7 +119,7 @@ jobs:
- name: Download nightly wheels of intents
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@8a338493df3d275e4a7a63bcff3b8fe97e51a927 # v19
uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: OHF-Voice/intents-package
@@ -348,13 +344,13 @@ jobs:
- name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant'
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -503,7 +499,7 @@ jobs:
python -m build
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
skip-existing: true
@@ -527,14 +523,14 @@ jobs:
persist-credentials: false
- name: Login to GitHub Container Registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -547,7 +543,7 @@ jobs:
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile

View File

@@ -50,9 +50,11 @@ env:
# - 10.10.3 is the latest (as of 6 Feb 2023)
# 10.11 is the latest long-term-support
# - 10.11.2 is the version currently shipped with Synology (as of 11 Oct 2023)
# 11.4 is an LTS with support until May 2029
# - 11.4.9 is used in Alpine 3.23 (used in latest HA base images as of 11 Apr 2026)
# mysql 8.0.32 does not always behave the same as MariaDB
# and some queries that work on MariaDB do not work on MySQL
MARIADB_VERSIONS: "['mariadb:10.3.32','mariadb:10.6.10','mariadb:10.10.3','mariadb:10.11.2','mysql:8.0.32']"
MARIADB_VERSIONS: "['mariadb:10.3.32','mariadb:10.6.10','mariadb:10.10.3','mariadb:10.11.2','mariadb:11.4.9','mysql:8.0.32']"
# 12 is the oldest supported version
# - 12.14 is the latest (as of 9 Feb 2023)
# 15 is the latest version
@@ -280,7 +282,7 @@ jobs:
echo "::add-matcher::.github/workflows/matchers/check-executables-have-shebangs.json"
echo "::add-matcher::.github/workflows/matchers/codespell.json"
- name: Run prek
uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1
uses: j178/prek-action@cbc2f23eb5539cf20d82d1aabd0d0ecbcc56f4e3 # v2.0.2
env:
PREK_SKIP: no-commit-to-branch,mypy,pylint,gen_requirements_all,hassfest,hassfest-metadata,hassfest-mypy-config,zizmor
RUFF_OUTPUT_FORMAT: github
@@ -301,7 +303,7 @@ jobs:
with:
persist-credentials: false
- name: Run zizmor
uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1
uses: j178/prek-action@cbc2f23eb5539cf20d82d1aabd0d0ecbcc56f4e3 # v2.0.2
with:
extra-args: --all-files zizmor
@@ -364,7 +366,7 @@ jobs:
echo "key=uv-${UV_CACHE_VERSION}-${uv_version}-${HA_SHORT_VERSION}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
key: >-
@@ -372,7 +374,8 @@ jobs:
needs.info.outputs.python_cache_key }}
- name: Restore uv wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
id: cache-uv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ env.UV_CACHE_DIR }}
key: >-
@@ -384,7 +387,7 @@ jobs:
env.HA_SHORT_VERSION }}-
- name: Check if apt cache exists
id: cache-apt-check
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
lookup-only: ${{ steps.cache-venv.outputs.cache-hit == 'true' }}
path: |
@@ -396,6 +399,7 @@ jobs:
if: |
steps.cache-venv.outputs.cache-hit != 'true'
|| steps.cache-apt-check.outputs.cache-hit != 'true'
id: install-os-deps
timeout-minutes: 10
env:
APT_CACHE_HIT: ${{ steps.cache-apt-check.outputs.cache-hit }}
@@ -429,8 +433,11 @@ jobs:
sudo chmod -R 755 ${APT_CACHE_BASE}
fi
- name: Save apt cache
if: steps.cache-apt-check.outputs.cache-hit != 'true'
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
if: |
always()
&& steps.cache-apt-check.outputs.cache-hit != 'true'
&& steps.install-os-deps.outcome == 'success'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -439,6 +446,7 @@ jobs:
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true'
id: create-venv
run: |
python -m venv venv
. venv/bin/activate
@@ -456,7 +464,7 @@ jobs:
python --version
uv pip freeze >> pip_freeze.txt
- name: Upload pip_freeze artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pip-freeze-${{ matrix.python-version }}
path: pip_freeze.txt
@@ -469,6 +477,26 @@ jobs:
- name: Check dirty
run: |
./script/check_dirty
- name: Save uv wheel cache
if: |
(success() && steps.cache-venv.outputs.cache-hit != 'true')
|| (always()
&& steps.create-venv.outcome == 'success'
&& steps.cache-uv.outputs.cache-matched-key == '')
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ env.UV_CACHE_DIR }}
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
steps.generate-uv-key.outputs.key }}
- name: Save base Python virtual environment
if: always() && steps.create-venv.outcome == 'success'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
hassfest:
name: Check hassfest
@@ -484,7 +512,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Restore apt cache
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -515,7 +543,7 @@ jobs:
check-latest: true
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -552,7 +580,7 @@ jobs:
check-latest: true
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -643,7 +671,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -657,7 +685,7 @@ jobs:
. venv/bin/activate
python -m script.licenses extract --output-file=licenses-${PYTHON_VERSION}.json
- name: Upload licenses
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
path: licenses-${{ matrix.python-version }}.json
@@ -694,7 +722,7 @@ jobs:
check-latest: true
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -747,7 +775,7 @@ jobs:
check-latest: true
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -804,7 +832,7 @@ jobs:
echo "key=mypy-${MYPY_CACHE_VERSION}-${mypy_version}-${HA_SHORT_VERSION}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -812,7 +840,7 @@ jobs:
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Restore mypy cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: .mypy_cache
key: >-
@@ -854,7 +882,7 @@ jobs:
- base
steps:
- name: Restore apt cache
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -887,7 +915,7 @@ jobs:
check-latest: true
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -901,7 +929,7 @@ jobs:
. venv/bin/activate
python -m script.split_tests ${TEST_GROUP_COUNT} tests
- name: Upload pytest_buckets
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pytest_buckets
path: pytest_buckets.txt
@@ -930,7 +958,7 @@ jobs:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -964,7 +992,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -1020,14 +1048,14 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${TEST_GROUP}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1040,7 +1068,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results-full-${{ matrix.python-version }}-${{ matrix.group }}
path: junit.xml
@@ -1062,7 +1090,9 @@ jobs:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping -uroot -ppassword" --health-interval=5s --health-timeout=2s --health-retries=3
options: >-
--health-cmd="if command -v mariadb-admin >/dev/null; then mariadb-admin ping -uroot -ppassword; else mysqladmin ping -uroot -ppassword; fi"
--health-interval=5s --health-timeout=2s --health-retries=3
needs:
- info
- base
@@ -1080,7 +1110,7 @@ jobs:
mariadb-group: ${{ fromJson(needs.info.outputs.mariadb_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -1115,7 +1145,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -1177,7 +1207,7 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${mariadb}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1185,7 +1215,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1199,7 +1229,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results-mariadb-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1238,7 +1268,7 @@ jobs:
postgresql-group: ${{ fromJson(needs.info.outputs.postgresql_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -1275,7 +1305,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -1338,7 +1368,7 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${postgresql}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1346,7 +1376,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1360,7 +1390,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results-postgres-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1421,7 +1451,7 @@ jobs:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
@@ -1455,7 +1485,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
fail-on-cache-miss: true
@@ -1514,14 +1544,14 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${TEST_GROUP}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1534,7 +1564,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results-partial-${{ matrix.python-version }}-${{ matrix.group }}
path: junit.xml

View File

@@ -28,11 +28,11 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
category: "/language:python"

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Check if integration label was added and extract details
id: extract
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
// Debug: Log the event payload
@@ -118,7 +118,7 @@ jobs:
- name: Fetch similar issues
id: fetch_similar
if: steps.extract.outputs.should_continue == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
INTEGRATION_LABELS: ${{ steps.extract.outputs.integration_labels }}
CURRENT_NUMBER: ${{ steps.extract.outputs.current_number }}
@@ -285,7 +285,7 @@ jobs:
- name: Post duplicate detection results
id: post_results
if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
AI_RESPONSE: ${{ steps.ai_detection.outputs.response }}
SIMILAR_ISSUES: ${{ steps.fetch_similar.outputs.similar_issues }}

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Check issue language
id: detect_language
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
@@ -95,7 +95,7 @@ jobs:
- name: Process non-English issues
if: steps.detect_language.outputs.should_continue == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
AI_RESPONSE: ${{ steps.ai_language_detection.outputs.response }}
ISSUE_NUMBER: ${{ steps.detect_language.outputs.issue_number }}

View File

@@ -22,7 +22,7 @@ jobs:
|| github.event.issue.type.name == 'Opportunity'
steps:
- name: Add no-stale label
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
await github.rest.issues.addLabels({
@@ -42,7 +42,7 @@ jobs:
if: github.event.issue.type.name == 'Task'
steps:
- name: Check if user is authorized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const issueAuthor = context.payload.issue.user.login;

View File

@@ -74,7 +74,7 @@ jobs:
) > .env_file
- name: Upload env_file
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: env_file
path: ./.env_file
@@ -82,7 +82,7 @@ jobs:
overwrite: true
- name: Upload requirements_diff
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: requirements_diff
path: ./requirements_diff.txt
@@ -94,7 +94,7 @@ jobs:
python -m script.gen_requirements_all ci
- name: Upload requirements_all_wheels
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: requirements_all_wheels
path: ./requirements_all_wheels_*.txt

1
.gitignore vendored
View File

@@ -142,5 +142,6 @@ pytest_buckets.txt
# AI tooling
.claude/settings.local.json
.claude/worktrees/
.serena/

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.1
rev: v0.15.12
hooks:
- id: ruff-check
args:
@@ -8,7 +8,7 @@ repos:
- id: ruff-format
files: ^((homeassistant|pylint|script|tests)/.+)?[^/]+\.(py|pyi)$
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
rev: v2.4.2
hooks:
- id: codespell
args:
@@ -18,7 +18,7 @@ repos:
exclude_types: [csv, json, html]
exclude: ^tests/fixtures/|homeassistant/generated/|tests/components/.*/snapshots/
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.23.1
rev: v1.24.1
hooks:
- id: zizmor
args:
@@ -36,7 +36,7 @@ repos:
- --branch=master
- --branch=rc
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
rev: v1.38.0
hooks:
- id: yamllint
- repo: https://github.com/rbubley/mirrors-prettier

View File

@@ -46,6 +46,7 @@ homeassistant.components.accuweather.*
homeassistant.components.acer_projector.*
homeassistant.components.acmeda.*
homeassistant.components.actiontec.*
homeassistant.components.actron_air.*
homeassistant.components.adax.*
homeassistant.components.adguard.*
homeassistant.components.aftership.*
@@ -178,6 +179,7 @@ homeassistant.components.dropbox.*
homeassistant.components.droplet.*
homeassistant.components.dsmr.*
homeassistant.components.duckdns.*
homeassistant.components.duco.*
homeassistant.components.dunehd.*
homeassistant.components.duotecno.*
homeassistant.components.easyenergy.*
@@ -222,6 +224,7 @@ homeassistant.components.fronius.*
homeassistant.components.frontend.*
homeassistant.components.fujitsu_fglair.*
homeassistant.components.fully_kiosk.*
homeassistant.components.fumis.*
homeassistant.components.fyta.*
homeassistant.components.generic_hygrostat.*
homeassistant.components.generic_thermostat.*
@@ -332,6 +335,7 @@ homeassistant.components.letpot.*
homeassistant.components.lg_infrared.*
homeassistant.components.libre_hardware_monitor.*
homeassistant.components.lidarr.*
homeassistant.components.liebherr.*
homeassistant.components.lifx.*
homeassistant.components.light.*
homeassistant.components.linkplay.*
@@ -551,6 +555,7 @@ homeassistant.components.tcp.*
homeassistant.components.technove.*
homeassistant.components.tedee.*
homeassistant.components.telegram_bot.*
homeassistant.components.teleinfo.*
homeassistant.components.teslemetry.*
homeassistant.components.text.*
homeassistant.components.thethingsnetwork.*
@@ -594,6 +599,7 @@ homeassistant.components.vallox.*
homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.velux.*
homeassistant.components.victron_gx.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*

View File

@@ -12,7 +12,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Python Syntax Notes
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses.
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses. Never flag this as an issue since Home Assistant officially supports Python 3.14.
## Testing
@@ -22,3 +22,6 @@ Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) ov
## Good practices
Integrations with Platinum or Gold level in the Integration Quality Scale reflect a high standard of code quality and maintainability. When looking for examples of something, these are good places to start. The level is indicated in the manifest.json of the integration.
When reviewing entity actions, do not suggest extra defensive checks for input fields that are already validated by Home Assistant's service/action schemas and entity selection filters. Suggest additional guards only when data bypasses those validators or is transformed into a less-safe form.
When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")` so contract violations are surfaced instead of silently masked.

64
CODEOWNERS generated
View File

@@ -362,6 +362,8 @@ CLAUDE.md @home-assistant/core
/tests/components/deluge/ @tkdrob
/homeassistant/components/demo/ @home-assistant/core
/tests/components/demo/ @home-assistant/core
/homeassistant/components/denon_rs232/ @balloob
/tests/components/denon_rs232/ @balloob
/homeassistant/components/denonavr/ @ol-iver @starkillerOG
/tests/components/denonavr/ @ol-iver @starkillerOG
/homeassistant/components/derivative/ @afaucogney @karwosts
@@ -398,6 +400,8 @@ CLAUDE.md @home-assistant/core
/tests/components/dnsip/ @gjohansson-ST
/homeassistant/components/door/ @home-assistant/core
/tests/components/door/ @home-assistant/core
/homeassistant/components/doorbell/ @home-assistant/core
/tests/components/doorbell/ @home-assistant/core
/homeassistant/components/doorbird/ @oblogic7 @bdraco @flacjacket
/tests/components/doorbird/ @oblogic7 @bdraco @flacjacket
/homeassistant/components/dormakaba_dkey/ @emontnemery
@@ -418,6 +422,8 @@ CLAUDE.md @home-assistant/core
/tests/components/dsmr_reader/ @sorted-bits @glodenox @erwindouna
/homeassistant/components/duckdns/ @tr4nt0r
/tests/components/duckdns/ @tr4nt0r
/homeassistant/components/duco/ @ronaldvdmeer
/tests/components/duco/ @ronaldvdmeer
/homeassistant/components/duotecno/ @cereal2nd
/tests/components/duotecno/ @cereal2nd
/homeassistant/components/dwd_weather_warnings/ @runningman84 @stephan192
@@ -426,6 +432,8 @@ CLAUDE.md @home-assistant/core
/tests/components/dynalite/ @ziv1234
/homeassistant/components/eafm/ @Jc2k
/tests/components/eafm/ @Jc2k
/homeassistant/components/earn_e_p1/ @Miggets7
/tests/components/earn_e_p1/ @Miggets7
/homeassistant/components/easyenergy/ @klaasnicolaas
/tests/components/easyenergy/ @klaasnicolaas
/homeassistant/components/ecoforest/ @pjanuario
@@ -487,8 +495,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/environment_canada/ @gwww @michaeldavie
/tests/components/environment_canada/ @gwww @michaeldavie
/homeassistant/components/ephember/ @ttroy50 @roberty99
/homeassistant/components/epic_games_store/ @hacf-fr @Quentame
/tests/components/epic_games_store/ @hacf-fr @Quentame
/homeassistant/components/epic_games_store/ @Quentame
/tests/components/epic_games_store/ @Quentame
/homeassistant/components/epion/ @lhgravendeel
/tests/components/epion/ @lhgravendeel
/homeassistant/components/epson/ @pszafer
@@ -503,6 +511,8 @@ CLAUDE.md @home-assistant/core
/tests/components/essent/ @jaapp
/homeassistant/components/eufylife_ble/ @bdr99
/tests/components/eufylife_ble/ @bdr99
/homeassistant/components/eurotronic_cometblue/ @rikroe
/tests/components/eurotronic_cometblue/ @rikroe
/homeassistant/components/event/ @home-assistant/core
/tests/components/event/ @home-assistant/core
/homeassistant/components/evohome/ @zxdavb
@@ -562,8 +572,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/fortios/ @kimfrellsen
/homeassistant/components/foscam/ @Foscam-wangzhengyu
/tests/components/foscam/ @Foscam-wangzhengyu
/homeassistant/components/freebox/ @hacf-fr @Quentame
/tests/components/freebox/ @hacf-fr @Quentame
/homeassistant/components/freebox/ @hacf-fr/reviewers @Quentame
/tests/components/freebox/ @hacf-fr/reviewers @Quentame
/homeassistant/components/freedompro/ @stefano055415
/tests/components/freedompro/ @stefano055415
/homeassistant/components/freshr/ @SierraNL
@@ -584,6 +594,8 @@ CLAUDE.md @home-assistant/core
/tests/components/fujitsu_fglair/ @crevetor
/homeassistant/components/fully_kiosk/ @cgarwood
/tests/components/fully_kiosk/ @cgarwood
/homeassistant/components/fumis/ @frenck
/tests/components/fumis/ @frenck
/homeassistant/components/fyta/ @dontinelli
/tests/components/fyta/ @dontinelli
/homeassistant/components/garage_door/ @home-assistant/core
@@ -746,6 +758,8 @@ CLAUDE.md @home-assistant/core
/tests/components/homewizard/ @DCSBL
/homeassistant/components/honeywell/ @rdfurman @mkmer
/tests/components/honeywell/ @rdfurman @mkmer
/homeassistant/components/honeywell_string_lights/ @balloob
/tests/components/honeywell_string_lights/ @balloob
/homeassistant/components/hr_energy_qube/ @MattieGit
/tests/components/hr_energy_qube/ @MattieGit
/homeassistant/components/html5/ @alexyao2015 @tr4nt0r
@@ -894,8 +908,8 @@ CLAUDE.md @home-assistant/core
/tests/components/jewish_calendar/ @tsvi
/homeassistant/components/justnimbus/ @kvanzuijlen
/tests/components/justnimbus/ @kvanzuijlen
/homeassistant/components/jvc_projector/ @SteveEasley @msavazzi
/tests/components/jvc_projector/ @SteveEasley @msavazzi
/homeassistant/components/jvc_projector/ @SteveEasley
/tests/components/jvc_projector/ @SteveEasley
/homeassistant/components/kaiterra/ @Michsior14
/homeassistant/components/kaleidescape/ @SteveEasley
/tests/components/kaleidescape/ @SteveEasley
@@ -908,6 +922,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/keyboard_remote/ @bendavid @lanrat
/homeassistant/components/keymitt_ble/ @spycle
/tests/components/keymitt_ble/ @spycle
/homeassistant/components/kiosker/ @Claeysson
/tests/components/kiosker/ @Claeysson
/homeassistant/components/kitchen_sink/ @home-assistant/core
/tests/components/kitchen_sink/ @home-assistant/core
/homeassistant/components/kmtronic/ @dgomes
@@ -1053,8 +1069,8 @@ CLAUDE.md @home-assistant/core
/tests/components/met/ @danielhiversen
/homeassistant/components/met_eireann/ @DylanGore
/tests/components/met_eireann/ @DylanGore
/homeassistant/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
/tests/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
/homeassistant/components/meteo_france/ @hacf-fr/reviewers @oncleben31 @Quentame
/tests/components/meteo_france/ @hacf-fr/reviewers @oncleben31 @Quentame
/homeassistant/components/meteo_lt/ @xE1H
/tests/components/meteo_lt/ @xE1H
/homeassistant/components/meteoalarm/ @rolfberkenbosch
@@ -1146,8 +1162,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/netatmo/ @cgtobi
/tests/components/netatmo/ @cgtobi
/homeassistant/components/netdata/ @fabaff
/homeassistant/components/netgear/ @hacf-fr @Quentame @starkillerOG
/tests/components/netgear/ @hacf-fr @Quentame @starkillerOG
/homeassistant/components/netgear/ @Quentame @starkillerOG
/tests/components/netgear/ @Quentame @starkillerOG
/homeassistant/components/netgear_lte/ @tkdrob
/tests/components/netgear_lte/ @tkdrob
/homeassistant/components/network/ @home-assistant/core
@@ -1187,6 +1203,8 @@ CLAUDE.md @home-assistant/core
/tests/components/notify_events/ @matrozov @papajojo
/homeassistant/components/notion/ @bachya
/tests/components/notion/ @bachya
/homeassistant/components/novy_cooker_hood/ @piitaya
/tests/components/novy_cooker_hood/ @piitaya
/homeassistant/components/nrgkick/ @andijakl
/tests/components/nrgkick/ @andijakl
/homeassistant/components/nsw_fuel_station/ @nickw444
@@ -1223,6 +1241,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/ollama/ @synesthesiam
/tests/components/ollama/ @synesthesiam
/homeassistant/components/ombi/ @larssont
/homeassistant/components/omie/ @luuuis
/tests/components/omie/ @luuuis
/homeassistant/components/onboarding/ @home-assistant/core
/tests/components/onboarding/ @home-assistant/core
/homeassistant/components/ondilo_ico/ @JeromeHXP
@@ -1241,6 +1261,8 @@ CLAUDE.md @home-assistant/core
/tests/components/open_meteo/ @frenck
/homeassistant/components/open_router/ @joostlek @ab3lson
/tests/components/open_router/ @joostlek @ab3lson
/homeassistant/components/openai_conversation/ @Shulyaka
/tests/components/openai_conversation/ @Shulyaka
/homeassistant/components/opendisplay/ @g4bri3lDev
/tests/components/opendisplay/ @g4bri3lDev
/homeassistant/components/openerz/ @misialq
@@ -1399,6 +1421,8 @@ CLAUDE.md @home-assistant/core
/tests/components/radarr/ @tkdrob
/homeassistant/components/radio_browser/ @frenck
/tests/components/radio_browser/ @frenck
/homeassistant/components/radio_frequency/ @home-assistant/core
/tests/components/radio_frequency/ @home-assistant/core
/homeassistant/components/radiotherm/ @vinnyfuria
/tests/components/radiotherm/ @vinnyfuria
/homeassistant/components/rainbird/ @konikvranik @allenporter
@@ -1690,8 +1714,8 @@ CLAUDE.md @home-assistant/core
/tests/components/syncthing/ @zhulik
/homeassistant/components/syncthru/ @nielstron
/tests/components/syncthru/ @nielstron
/homeassistant/components/synology_dsm/ @hacf-fr @Quentame @mib1185
/tests/components/synology_dsm/ @hacf-fr @Quentame @mib1185
/homeassistant/components/synology_dsm/ @Quentame @mib1185
/tests/components/synology_dsm/ @Quentame @mib1185
/homeassistant/components/synology_srm/ @aerialls
/homeassistant/components/system_bridge/ @timmo001
/tests/components/system_bridge/ @timmo001
@@ -1722,6 +1746,8 @@ CLAUDE.md @home-assistant/core
/tests/components/tedee/ @patrickhilker @zweckj
/homeassistant/components/telegram_bot/ @hanwg
/tests/components/telegram_bot/ @hanwg
/homeassistant/components/teleinfo/ @esciara
/tests/components/teleinfo/ @esciara
/homeassistant/components/tellduslive/ @fredrike
/tests/components/tellduslive/ @fredrike
/homeassistant/components/teltonika/ @karlbeecken
@@ -1824,6 +1850,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/unifi_access/ @imhotep @RaHehl
/tests/components/unifi_access/ @imhotep @RaHehl
/homeassistant/components/unifi_direct/ @tofuSCHNITZEL
/homeassistant/components/unifi_discovery/ @RaHehl
/tests/components/unifi_discovery/ @RaHehl
/homeassistant/components/unifiled/ @florisvdk
/homeassistant/components/unifiprotect/ @RaHehl
/tests/components/unifiprotect/ @RaHehl
@@ -1871,8 +1899,8 @@ CLAUDE.md @home-assistant/core
/tests/components/version/ @ludeeus
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak @sapuseven
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak @sapuseven
/homeassistant/components/vicare/ @CFenner
/tests/components/vicare/ @CFenner
/homeassistant/components/vicare/ @CFenner @lackas
/tests/components/vicare/ @CFenner @lackas
/homeassistant/components/victron_ble/ @rajlaud
/tests/components/victron_ble/ @rajlaud
/homeassistant/components/victron_gx/ @tomer-w
@@ -1963,8 +1991,8 @@ CLAUDE.md @home-assistant/core
/tests/components/wled/ @frenck @mik-laj
/homeassistant/components/wmspro/ @mback2k
/tests/components/wmspro/ @mback2k
/homeassistant/components/wolflink/ @adamkrol93 @mtielen
/tests/components/wolflink/ @adamkrol93 @mtielen
/homeassistant/components/wolflink/ @adamkrol93 @EnjoyingM
/tests/components/wolflink/ @adamkrol93 @EnjoyingM
/homeassistant/components/workday/ @fabaff @gjohansson-ST
/tests/components/workday/ @fabaff @gjohansson-ST
/homeassistant/components/worldclock/ @fabaff
@@ -1975,8 +2003,8 @@ CLAUDE.md @home-assistant/core
/tests/components/wsdot/ @ucodery
/homeassistant/components/wyoming/ @synesthesiam
/tests/components/wyoming/ @synesthesiam
/homeassistant/components/xbox/ @hunterjm @tr4nt0r
/tests/components/xbox/ @hunterjm @tr4nt0r
/homeassistant/components/xbox/ @tr4nt0r
/tests/components/xbox/ @tr4nt0r
/homeassistant/components/xiaomi_aqara/ @danielhiversen @syssi
/tests/components/xiaomi_aqara/ @danielhiversen @syssi
/homeassistant/components/xiaomi_ble/ @Jc2k @Ernst79

20
Dockerfile generated
View File

@@ -1,3 +1,4 @@
# syntax=docker/dockerfile@sha256:2780b5c3bab67f1f76c781860de469442999ed1a0d7992a5efdf2cffc0e3d769
# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
@@ -19,25 +20,22 @@ ENV \
UV_SYSTEM_PYTHON=true \
UV_NO_CACHE=true
WORKDIR /usr/src
# Home Assistant S6-Overlay
COPY rootfs /
# Add go2rtc binary
COPY --from=ghcr.io/alexxit/go2rtc@sha256:675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae /usr/local/bin/go2rtc /bin/go2rtc
## Setup Home Assistant Core dependencies
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
RUN \
# Verify go2rtc can be executed
go2rtc --version \
# Install uv
&& pip3 install uv==0.11.1
WORKDIR /usr/src
## Setup Home Assistant Core dependencies
COPY requirements.txt homeassistant/
COPY homeassistant/package_constraints.txt homeassistant/homeassistant/
RUN \
uv pip install \
# Install uv at the version pinned in the requirements file
&& pip3 install --no-cache-dir "uv==$(awk -F'==' '/^uv==/{print $2}' homeassistant/requirements.txt)" \
&& uv pip install \
--no-build \
-r homeassistant/requirements.txt
@@ -51,7 +49,7 @@ RUN \
-r homeassistant/requirements_all.txt
## Setup Home Assistant Core
COPY . homeassistant/
COPY --parents LICENSE* README* homeassistant/ pyproject.toml homeassistant/
RUN \
uv pip install \
-e ./homeassistant \

View File

@@ -1,3 +1,4 @@
# syntax=docker/dockerfile@sha256:2780b5c3bab67f1f76c781860de469442999ed1a0d7992a5efdf2cffc0e3d769
FROM mcr.microsoft.com/vscode/devcontainers/base:debian
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

View File

@@ -7,23 +7,31 @@ to speed up the process.
from __future__ import annotations
from collections.abc import Container, Iterable, Sequence
from datetime import timedelta
from functools import lru_cache, partial
from typing import Any
from functools import lru_cache
from typing import Any, override
from jwt import DecodeError, PyJWS, PyJWT
from jwt import DecodeError, PyJWK, PyJWS, PyJWT
from jwt.algorithms import AllowedPublicKeys
from jwt.types import Options
from homeassistant.util.json import json_loads
JWT_TOKEN_CACHE_SIZE = 16
MAX_TOKEN_SIZE = 8192
_VERIFY_KEYS = ("signature", "exp", "nbf", "iat", "aud", "iss", "sub", "jti")
_VERIFY_OPTIONS: dict[str, Any] = {f"verify_{key}": True for key in _VERIFY_KEYS} | {
"require": []
}
_NO_VERIFY_OPTIONS = {f"verify_{key}": False for key in _VERIFY_KEYS}
_NO_VERIFY_OPTIONS = Options(
verify_signature=False,
verify_exp=False,
verify_nbf=False,
verify_iat=False,
verify_aud=False,
verify_iss=False,
verify_sub=False,
verify_jti=False,
require=[],
)
class _PyJWSWithLoadCache(PyJWS):
@@ -38,9 +46,6 @@ class _PyJWSWithLoadCache(PyJWS):
return super()._load(jwt)
_jws = _PyJWSWithLoadCache()
@lru_cache(maxsize=JWT_TOKEN_CACHE_SIZE)
def _decode_payload(json_payload: str) -> dict[str, Any]:
"""Decode the payload from a JWS dictionary."""
@@ -56,21 +61,12 @@ def _decode_payload(json_payload: str) -> dict[str, Any]:
class _PyJWTWithVerify(PyJWT):
"""PyJWT with a fast decode implementation."""
def decode_payload(
self, jwt: str, key: str, options: dict[str, Any], algorithms: list[str]
) -> dict[str, Any]:
"""Decode a JWT's payload."""
if len(jwt) > MAX_TOKEN_SIZE:
# Avoid caching impossible tokens
raise DecodeError("Token too large")
return _decode_payload(
_jws.decode_complete(
jwt=jwt,
key=key,
algorithms=algorithms,
options=options,
)["payload"]
)
def __init__(self) -> None:
"""Initialize the PyJWT instance."""
# We require exp and iat claims to be present
super().__init__(Options(require=["exp", "iat"]))
# Override the _jws instance with our cached version
self._jws = _PyJWSWithLoadCache()
def verify_and_decode(
self,
@@ -79,37 +75,70 @@ class _PyJWTWithVerify(PyJWT):
algorithms: list[str],
issuer: str | None = None,
leeway: float | timedelta = 0,
options: dict[str, Any] | None = None,
options: Options | None = None,
) -> dict[str, Any]:
"""Verify a JWT's signature and claims."""
merged_options = {**_VERIFY_OPTIONS, **(options or {})}
payload = self.decode_payload(
return self.decode(
jwt=jwt,
key=key,
options=merged_options,
algorithms=algorithms,
)
# These should never be missing since we verify them
# but this is an additional safeguard to make sure
# nothing slips through.
assert "exp" in payload, "exp claim is required"
assert "iat" in payload, "iat claim is required"
self._validate_claims(
payload=payload,
options=merged_options,
issuer=issuer,
leeway=leeway,
options=options,
)
return payload
@override
def decode(
self,
jwt: str | bytes,
key: AllowedPublicKeys | PyJWK | str | bytes = "",
algorithms: Sequence[str] | None = None,
options: Options | None = None,
verify: bool | None = None,
detached_payload: bytes | None = None,
audience: str | Iterable[str] | None = None,
subject: str | None = None,
issuer: str | Container[str] | None = None,
leeway: float | timedelta = 0,
**kwargs: Any,
) -> dict[str, Any]:
"""Decode a JWT, verifying the signature and claims."""
if len(jwt) > MAX_TOKEN_SIZE:
# Avoid caching impossible tokens
raise DecodeError("Token too large")
return super().decode(
jwt=jwt,
key=key,
algorithms=algorithms,
options=options,
verify=verify,
detached_payload=detached_payload,
audience=audience,
subject=subject,
issuer=issuer,
leeway=leeway,
**kwargs,
)
@override
def _decode_payload(self, decoded: dict[str, Any]) -> dict[str, Any]:
return _decode_payload(decoded["payload"])
_jwt = _PyJWTWithVerify()
verify_and_decode = _jwt.verify_and_decode
unverified_hs256_token_decode = lru_cache(maxsize=JWT_TOKEN_CACHE_SIZE)(
partial(
_jwt.decode_payload, key="", algorithms=["HS256"], options=_NO_VERIFY_OPTIONS
@lru_cache(maxsize=JWT_TOKEN_CACHE_SIZE)
def unverified_hs256_token_decode(jwt: str) -> dict[str, Any]:
"""Decode a JWT without verifying the signature."""
return _jwt.decode(
jwt=jwt,
key="",
algorithms=["HS256"],
options=_NO_VERIFY_OPTIONS,
)
)
__all__ = [
"unverified_hs256_token_decode",

View File

@@ -2,7 +2,8 @@
from __future__ import annotations
from collections.abc import Callable
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING
import voluptuous as vol
@@ -13,6 +14,9 @@ from .models import PermissionLookup
from .types import PolicyType
from .util import test_all
if TYPE_CHECKING:
from ..models import User
POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
__all__ = [
@@ -22,10 +26,21 @@ __all__ = [
"PermissionLookup",
"PolicyPermissions",
"PolicyType",
"filter_entity_ids_by_permission",
"merge_policies",
]
def filter_entity_ids_by_permission(
user: User, entity_ids: Iterable[str], key: str
) -> list[str]:
"""Filter entity IDs to those the user can access for the given policy key."""
if user.is_admin or user.permissions.access_all_entities(key):
return list(entity_ids)
check_entity = user.permissions.check_entity
return [entity_id for entity_id in entity_ids if check_entity(entity_id, key)]
class AbstractPermissions:
"""Default permissions class."""

View File

@@ -1,5 +1,5 @@
{
"domain": "denon",
"name": "Denon",
"integrations": ["denon", "denonavr", "heos"]
"integrations": ["denon", "denonavr", "denon_rs232", "heos"]
}

View File

@@ -1,5 +1,5 @@
{
"domain": "honeywell",
"name": "Honeywell",
"integrations": ["lyric", "evohome", "honeywell"]
"integrations": ["lyric", "evohome", "honeywell", "honeywell_string_lights"]
}

View File

@@ -6,6 +6,7 @@
"unifi",
"unifi_access",
"unifi_direct",
"unifi_discovery",
"unifiled",
"unifiprotect"
]

View File

@@ -30,7 +30,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import CONF_POLLING, DOMAIN, DOMAIN_DATA, LOGGER
from .const import CONF_POLLING, DOMAIN, LOGGER
from .services import async_setup_services
ATTR_DEVICE_NAME = "device_name"
@@ -67,13 +67,16 @@ class AbodeSystem:
logout_listener: CALLBACK_TYPE | None = None
type AbodeConfigEntry = ConfigEntry[AbodeSystem]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Abode component."""
async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> bool:
"""Set up Abode integration from a config entry."""
username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
@@ -99,50 +102,54 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except (AbodeException, ConnectTimeout, HTTPError) as ex:
raise ConfigEntryNotReady(f"Unable to connect to Abode: {ex}") from ex
hass.data[DOMAIN_DATA] = AbodeSystem(abode, polling)
entry.runtime_data = AbodeSystem(abode, polling)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
await setup_hass_events(hass)
await hass.async_add_executor_job(setup_abode_events, hass)
await setup_hass_events(hass, entry)
await hass.async_add_executor_job(setup_abode_events, hass, entry)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
def _shutdown_client(abode: Abode) -> None:
"""Shutdown client."""
abode.events.stop()
abode.logout()
async def async_unload_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.events.stop)
await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.logout)
await hass.async_add_executor_job(_shutdown_client, entry.runtime_data.abode)
if logout_listener := hass.data[DOMAIN_DATA].logout_listener:
if logout_listener := entry.runtime_data.logout_listener:
logout_listener()
hass.data.pop(DOMAIN_DATA)
return unload_ok
async def setup_hass_events(hass: HomeAssistant) -> None:
async def setup_hass_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None:
"""Home Assistant start and stop callbacks."""
def logout(event: Event) -> None:
"""Logout of Abode."""
if not hass.data[DOMAIN_DATA].polling:
hass.data[DOMAIN_DATA].abode.events.stop()
if not entry.runtime_data.polling:
entry.runtime_data.abode.events.stop()
hass.data[DOMAIN_DATA].abode.logout()
entry.runtime_data.abode.logout()
LOGGER.info("Logged out of Abode")
if not hass.data[DOMAIN_DATA].polling:
await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.events.start)
if not entry.runtime_data.polling:
await hass.async_add_executor_job(entry.runtime_data.abode.events.start)
hass.data[DOMAIN_DATA].logout_listener = hass.bus.async_listen_once(
entry.runtime_data.logout_listener = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, logout
)
def setup_abode_events(hass: HomeAssistant) -> None:
def setup_abode_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None:
"""Event callbacks."""
def event_callback(event: str, event_json: dict[str, str]) -> None:
@@ -179,6 +186,6 @@ def setup_abode_events(hass: HomeAssistant) -> None:
]
for event in events:
hass.data[DOMAIN_DATA].abode.events.add_event_callback(
entry.runtime_data.abode.events.add_event_callback(
event, partial(event_callback, event)
)

View File

@@ -9,21 +9,20 @@ from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntityFeature,
AlarmControlPanelState,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN_DATA
from . import AbodeConfigEntry
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode alarm control panel device."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
async_add_entities(
[AbodeAlarm(data, await hass.async_add_executor_job(data.abode.get_alarm))]
)

View File

@@ -10,22 +10,21 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.enum import try_parse_enum
from .const import DOMAIN_DATA
from . import AbodeConfigEntry
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode binary sensor devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
device_types = [
"connectivity",

View File

@@ -12,14 +12,13 @@ import requests
from requests.models import Response
from homeassistant.components.camera import Camera
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import Throttle
from . import AbodeSystem
from .const import DOMAIN_DATA, LOGGER
from . import AbodeConfigEntry, AbodeSystem
from .const import LOGGER
from .entity import AbodeDevice
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
@@ -27,11 +26,11 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode camera devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
async_add_entities(
AbodeCamera(data, device, timeline.CAPTURE_IMAGE)

View File

@@ -3,17 +3,10 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from homeassistant.util.hass_dict import HassKey
if TYPE_CHECKING:
from . import AbodeSystem
LOGGER = logging.getLogger(__package__)
DOMAIN = "abode"
DOMAIN_DATA: HassKey[AbodeSystem] = HassKey(DOMAIN)
ATTRIBUTION = "Data provided by goabode.com"
CONF_POLLING = "polling"

View File

@@ -5,21 +5,20 @@ from typing import Any
from jaraco.abode.devices.cover import Cover
from homeassistant.components.cover import CoverEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN_DATA
from . import AbodeConfigEntry
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode cover devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
async_add_entities(
AbodeCover(data, device)

View File

@@ -7,7 +7,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from . import AbodeSystem
from .const import ATTRIBUTION, DOMAIN, DOMAIN_DATA
from .const import ATTRIBUTION, DOMAIN
class AbodeEntity(Entity):
@@ -29,7 +29,7 @@ class AbodeEntity(Entity):
self._update_connection_status,
)
self.hass.data[DOMAIN_DATA].entity_ids.add(self.entity_id)
self._data.entity_ids.add(self.entity_id)
async def async_will_remove_from_hass(self) -> None:
"""Unsubscribe from Abode connection status updates."""

View File

@@ -16,21 +16,20 @@ from homeassistant.components.light import (
ColorMode,
LightEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN_DATA
from . import AbodeConfigEntry
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode light devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
async_add_entities(
AbodeLight(data, device)

View File

@@ -5,21 +5,20 @@ from typing import Any
from jaraco.abode.devices.lock import Lock
from homeassistant.components.lock import LockEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN_DATA
from . import AbodeConfigEntry
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode lock devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
async_add_entities(
AbodeLock(data, device)

View File

@@ -14,13 +14,11 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeSystem
from .const import DOMAIN_DATA
from . import AbodeConfigEntry, AbodeSystem
from .entity import AbodeDevice
ABODE_TEMPERATURE_UNIT_HA_UNIT = {
@@ -66,11 +64,11 @@ SENSOR_TYPES: tuple[AbodeSensorDescription, ...] = (
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode sensor devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
async_add_entities(
AbodeSensor(data, device, description)

View File

@@ -2,15 +2,21 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from jaraco.abode.exceptions import Exception as AbodeException
import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.dispatcher import dispatcher_send
from .const import DOMAIN, DOMAIN_DATA, LOGGER
from .const import DOMAIN, LOGGER
if TYPE_CHECKING:
from . import AbodeConfigEntry, AbodeSystem
ATTR_SETTING = "setting"
ATTR_VALUE = "value"
@@ -25,13 +31,21 @@ CAPTURE_IMAGE_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids})
AUTOMATION_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids})
def _get_abode_system(hass: HomeAssistant) -> AbodeSystem:
"""Return the Abode system for the loaded config entry."""
entries: list[AbodeConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN)
if not entries:
raise ServiceValidationError("Abode integration is not loaded")
return entries[0].runtime_data
def _change_setting(call: ServiceCall) -> None:
"""Change an Abode system setting."""
setting = call.data[ATTR_SETTING]
value = call.data[ATTR_VALUE]
try:
call.hass.data[DOMAIN_DATA].abode.set_setting(setting, value)
_get_abode_system(call.hass).abode.set_setting(setting, value)
except AbodeException as ex:
LOGGER.warning(ex)
@@ -42,7 +56,7 @@ def _capture_image(call: ServiceCall) -> None:
target_entities = [
entity_id
for entity_id in call.hass.data[DOMAIN_DATA].entity_ids
for entity_id in _get_abode_system(call.hass).entity_ids
if entity_id in entity_ids
]
@@ -57,7 +71,7 @@ def _trigger_automation(call: ServiceCall) -> None:
target_entities = [
entity_id
for entity_id in call.hass.data[DOMAIN_DATA].entity_ids
for entity_id in _get_abode_system(call.hass).entity_ids
if entity_id in entity_ids
]

View File

@@ -7,12 +7,11 @@ from typing import Any, cast
from jaraco.abode.devices.switch import Switch
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN_DATA
from . import AbodeConfigEntry
from .entity import AbodeAutomation, AbodeDevice
DEVICE_TYPES = ["switch", "valve"]
@@ -20,11 +19,11 @@ DEVICE_TYPES = ["switch", "valve"]
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: AbodeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode switch devices."""
data = hass.data[DOMAIN_DATA]
data = entry.runtime_data
entities: list[SwitchEntity] = [
AbodeSwitch(data, device)

View File

@@ -143,4 +143,4 @@ class AcaiaRestoreSensor(AcaiaEntity, RestoreSensor):
@property
def available(self) -> bool:
"""Return True if entity is available."""
return super().available or self._restored_data is not None
return super().available or self.native_value is not None

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from asyncio import timeout
from collections.abc import Mapping
from typing import Any
from typing import TYPE_CHECKING, Any
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientError
@@ -12,7 +12,7 @@ from aiohttp.client_exceptions import ClientConnectorError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -55,8 +55,11 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
)
self._abort_if_unique_id_configured()
if TYPE_CHECKING:
assert accuweather.location_name is not None
return self.async_create_entry(
title=user_input[CONF_NAME], data=user_input
title=accuweather.location_name, data=user_input
)
return self.async_show_form(
@@ -70,9 +73,6 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(
CONF_NAME, default=self.hass.config.location_name
): str,
}
),
errors=errors,

View File

@@ -64,7 +64,7 @@ class AccuWeatherObservationDataUpdateCoordinator(
"""Initialize."""
self.accuweather = accuweather
self.location_key = accuweather.location_key
name = config_entry.data[CONF_NAME]
name = config_entry.data.get(CONF_NAME) or config_entry.title
if TYPE_CHECKING:
assert self.location_key is not None
@@ -122,7 +122,7 @@ class AccuWeatherForecastDataUpdateCoordinator(
self.accuweather = accuweather
self.location_key = accuweather.location_key
self._fetch_method = fetch_method
name = config_entry.data[CONF_NAME]
name = config_entry.data.get(CONF_NAME) or config_entry.title
if TYPE_CHECKING:
assert self.location_key is not None

View File

@@ -25,8 +25,7 @@
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]",
"name": "[%key:common::config_flow::data::name%]"
"longitude": "[%key:common::config_flow::data::longitude%]"
},
"data_description": {
"api_key": "API key generated in the AccuWeather APIs portal."

View File

@@ -6,10 +6,11 @@ from typing import Final
from homeassistant.const import STATE_OFF, STATE_ON
CONF_READ_TIMEOUT: Final = "timeout"
CONF_WRITE_TIMEOUT: Final = "write_timeout"
DEFAULT_NAME: Final = "Acer Projector"
DEFAULT_TIMEOUT: Final = 1
DEFAULT_READ_TIMEOUT: Final = 1
DEFAULT_WRITE_TIMEOUT: Final = 1
ECO_MODE: Final = "ECO Mode"

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/acer_projector",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["pyserial==3.5"]
"requirements": ["serialx==1.4.1"]
}

View File

@@ -6,7 +6,7 @@ import logging
import re
from typing import Any
import serial
from serialx import Serial, SerialException
import voluptuous as vol
from homeassistant.components.switch import (
@@ -16,21 +16,22 @@ from homeassistant.components.switch import (
from homeassistant.const import (
CONF_FILENAME,
CONF_NAME,
CONF_TIMEOUT,
STATE_OFF,
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
CMD_DICT,
CONF_READ_TIMEOUT,
CONF_WRITE_TIMEOUT,
DEFAULT_NAME,
DEFAULT_TIMEOUT,
DEFAULT_READ_TIMEOUT,
DEFAULT_WRITE_TIMEOUT,
ECO_MODE,
ICON,
@@ -45,7 +46,7 @@ PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_FILENAME): cv.isdevice,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_READ_TIMEOUT, default=DEFAULT_READ_TIMEOUT): cv.positive_int,
vol.Optional(
CONF_WRITE_TIMEOUT, default=DEFAULT_WRITE_TIMEOUT
): cv.positive_int,
@@ -62,10 +63,10 @@ def setup_platform(
"""Connect with serial port and return Acer Projector."""
serial_port = config[CONF_FILENAME]
name = config[CONF_NAME]
timeout = config[CONF_TIMEOUT]
read_timeout = config[CONF_READ_TIMEOUT]
write_timeout = config[CONF_WRITE_TIMEOUT]
add_entities([AcerSwitch(serial_port, name, timeout, write_timeout)], True)
add_entities([AcerSwitch(serial_port, name, read_timeout, write_timeout)], True)
class AcerSwitch(SwitchEntity):
@@ -77,14 +78,14 @@ class AcerSwitch(SwitchEntity):
self,
serial_port: str,
name: str,
timeout: int,
read_timeout: int,
write_timeout: int,
) -> None:
"""Init of the Acer projector."""
self.serial = serial.Serial(
port=serial_port, timeout=timeout, write_timeout=write_timeout
)
self._serial_port = serial_port
self._read_timeout = read_timeout
self._write_timeout = write_timeout
self._attr_name = name
self._attributes = {
LAMP_HOURS: STATE_UNKNOWN,
@@ -94,22 +95,26 @@ class AcerSwitch(SwitchEntity):
def _write_read(self, msg: str) -> str:
"""Write to the projector and read the return."""
ret = ""
# Sometimes the projector won't answer for no reason or the projector
# was disconnected during runtime.
# This way the projector can be reconnected and will still work
try:
if not self.serial.is_open:
self.serial.open()
self.serial.write(msg.encode("utf-8"))
# Size is an experience value there is no real limit.
# AFAIK there is no limit and no end character so we will usually
# need to wait for timeout
ret = self.serial.read_until(size=20).decode("utf-8")
except serial.SerialException:
_LOGGER.error("Problem communicating with %s", self._serial_port)
self.serial.close()
return ret
with Serial.from_url(
self._serial_port,
read_timeout=self._read_timeout,
write_timeout=self._write_timeout,
) as serial:
serial.write(msg.encode("utf-8"))
# Size is an experience value there is no real limit.
# AFAIK there is no limit and no end character so we will usually
# need to wait for timeout
return serial.read_until(size=20).decode("utf-8")
except (OSError, SerialException, TimeoutError) as exc:
raise HomeAssistantError(
f"Problem communicating with {self._serial_port}"
) from exc
def _write_read_format(self, msg: str) -> str:
"""Write msg, obtain answer and format output."""

View File

@@ -1,11 +1,7 @@
"""The Actron Air integration."""
from actron_neo_api import (
ActronAirACSystem,
ActronAirAPI,
ActronAirAPIError,
ActronAirAuthError,
)
from actron_neo_api import ActronAirAPI, ActronAirAPIError, ActronAirAuthError
from actron_neo_api.models.system import ActronAirSystemInfo
from homeassistant.const import CONF_API_TOKEN, Platform
from homeassistant.core import HomeAssistant
@@ -25,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
"""Set up Actron Air integration from a config entry."""
api = ActronAirAPI(refresh_token=entry.data[CONF_API_TOKEN])
systems: list[ActronAirACSystem] = []
systems: list[ActronAirSystemInfo] = []
try:
systems = await api.get_ac_systems()
@@ -44,9 +40,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
for system in systems:
coordinator = ActronAirSystemCoordinator(hass, entry, api, system)
_LOGGER.debug("Setting up coordinator for system: %s", system["serial"])
_LOGGER.debug("Setting up coordinator for system: %s", system.serial)
await coordinator.async_config_entry_first_refresh()
system_coordinators[system["serial"]] = coordinator
system_coordinators[system.serial] = coordinator
entry.runtime_data = ActronAirRuntimeData(
api=api,

View File

@@ -15,10 +15,12 @@ from homeassistant.components.climate import (
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
from .entity import ActronAirAcEntity, ActronAirZoneEntity, handle_actron_api_errors
from .entity import ActronAirAcEntity, ActronAirZoneEntity, actron_air_command
PARALLEL_UPDATES = 0
@@ -36,6 +38,7 @@ HVAC_MODE_MAPPING_ACTRONAIR_TO_HA = {
"HEAT": HVACMode.HEAT,
"FAN": HVACMode.FAN_ONLY,
"AUTO": HVACMode.AUTO,
"DRY": HVACMode.DRY,
"OFF": HVACMode.OFF,
}
HVAC_MODE_MAPPING_HA_TO_ACTRONAIR = {
@@ -77,7 +80,6 @@ class ActronAirClimateEntity(ClimateEntity):
)
_attr_name = None
_attr_fan_modes = list(FAN_MODE_MAPPING_ACTRONAIR_TO_HA.values())
_attr_hvac_modes = list(HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.values())
class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
@@ -91,6 +93,17 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
super().__init__(coordinator)
self._attr_unique_id = self._serial_number
@property
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of supported HVAC modes."""
modes = [
HVAC_MODE_MAPPING_ACTRONAIR_TO_HA[mode]
for mode in self._status.user_aircon_settings.supported_modes
if mode in HVAC_MODE_MAPPING_ACTRONAIR_TO_HA
]
modes.append(HVACMode.OFF)
return modes
@property
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
@@ -136,23 +149,27 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
"""Return the target temperature."""
return self._status.user_aircon_settings.temperature_setpoint_cool_c
@handle_actron_api_errors
@actron_air_command
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set a new fan mode."""
api_fan_mode = FAN_MODE_MAPPING_HA_TO_ACTRONAIR.get(fan_mode)
api_fan_mode = FAN_MODE_MAPPING_HA_TO_ACTRONAIR[fan_mode]
await self._status.user_aircon_settings.set_fan_mode(api_fan_mode)
@handle_actron_api_errors
@actron_air_command
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
ac_mode = HVAC_MODE_MAPPING_HA_TO_ACTRONAIR.get(hvac_mode)
ac_mode = HVAC_MODE_MAPPING_HA_TO_ACTRONAIR[hvac_mode]
await self._status.ac_system.set_system_mode(ac_mode)
@handle_actron_api_errors
@actron_air_command
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
await self._status.user_aircon_settings.set_temperature(temperature=temp)
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="temperature_missing",
)
await self._status.user_aircon_settings.set_temperature(temperature=temperature)
class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
@@ -173,6 +190,18 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
super().__init__(coordinator, zone)
self._attr_unique_id: str = self._zone_identifier
@property
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of supported HVAC modes."""
status = self.coordinator.data
modes = [
HVAC_MODE_MAPPING_ACTRONAIR_TO_HA[mode]
for mode in status.user_aircon_settings.supported_modes
if mode in HVAC_MODE_MAPPING_ACTRONAIR_TO_HA
]
modes.append(HVACMode.OFF)
return modes
@property
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
@@ -212,13 +241,18 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
"""Return the target temperature."""
return self._zone.temperature_setpoint_cool_c
@handle_actron_api_errors
@actron_air_command
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
is_enabled = hvac_mode != HVACMode.OFF
await self._zone.enable(is_enabled)
@handle_actron_api_errors
@actron_air_command
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the temperature."""
await self._zone.set_temperature(temperature=kwargs.get(ATTR_TEMPERATURE))
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="temperature_missing",
)
await self._zone.set_temperature(temperature=temperature)

View File

@@ -23,7 +23,7 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
self._user_code: str = ""
self._verification_uri: str = ""
self._expires_minutes: str = "30"
self.login_task: asyncio.Task | None = None
self.login_task: asyncio.Task[None] | None = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -38,10 +38,10 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.error("OAuth2 flow failed: %s", err)
return self.async_abort(reason="oauth2_error")
self._device_code = device_code_response["device_code"]
self._user_code = device_code_response["user_code"]
self._verification_uri = device_code_response["verification_uri_complete"]
self._expires_minutes = str(device_code_response["expires_in"] // 60)
self._device_code = device_code_response.device_code
self._user_code = device_code_response.user_code
self._verification_uri = device_code_response.verification_uri_complete
self._expires_minutes = str(device_code_response.expires_in // 60)
async def _wait_for_authorization() -> None:
"""Wait for the user to authorize the device."""
@@ -94,7 +94,7 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.error("Error getting user info: %s", err)
return self.async_abort(reason="oauth2_error")
unique_id = str(user_data["id"])
unique_id = user_data.sub
await self.async_set_unique_id(unique_id)
# Check if this is a reauth flow
@@ -107,7 +107,7 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_data["email"],
title=user_data.email,
data={CONF_API_TOKEN: self._api.refresh_token_value},
)

View File

@@ -6,12 +6,12 @@ from dataclasses import dataclass
from datetime import timedelta
from actron_neo_api import (
ActronAirACSystem,
ActronAirAPI,
ActronAirAPIError,
ActronAirAuthError,
ActronAirStatus,
)
from actron_neo_api.models.system import ActronAirSystemInfo
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -38,7 +38,7 @@ class ActronAirRuntimeData:
type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData]
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirStatus]):
"""System coordinator for Actron Air integration."""
def __init__(
@@ -46,7 +46,7 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
hass: HomeAssistant,
entry: ActronAirConfigEntry,
api: ActronAirAPI,
system: ActronAirACSystem,
system: ActronAirSystemInfo,
) -> None:
"""Initialize the coordinator."""
super().__init__(
@@ -57,7 +57,7 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
config_entry=entry,
)
self.system = system
self.serial_number = system["serial"]
self.serial_number = system.serial
self.api = api
self.status = self.api.state_manager.get_status(self.serial_number)
self.last_seen = dt_util.utcnow()
@@ -78,7 +78,14 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
translation_placeholders={"error": repr(err)},
) from err
self.status = self.api.state_manager.get_status(self.serial_number)
status = self.api.state_manager.get_status(self.serial_number)
if status is None:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_error",
translation_placeholders={"error": "Status not available"},
)
self.status = status
self.last_seen = dt_util.utcnow()
return self.status

View File

@@ -0,0 +1,35 @@
"""Diagnostics support for Actron Air."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_API_TOKEN
from homeassistant.core import HomeAssistant
from .coordinator import ActronAirConfigEntry
TO_REDACT = {CONF_API_TOKEN, "master_serial", "serial_number", "serial"}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant,
entry: ActronAirConfigEntry,
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinators: dict[int, Any] = {}
for idx, coordinator in enumerate(entry.runtime_data.system_coordinators.values()):
coordinators[idx] = {
"system": async_redact_data(
coordinator.system.model_dump(mode="json"), TO_REDACT
),
"status": async_redact_data(
coordinator.data.model_dump(mode="json", exclude={"last_known_state"}),
TO_REDACT,
),
}
return {
"entry_data": async_redact_data(entry.data, TO_REDACT),
"coordinators": coordinators,
}

View File

@@ -14,13 +14,17 @@ from .const import DOMAIN
from .coordinator import ActronAirSystemCoordinator
def handle_actron_api_errors[_EntityT: ActronAirEntity, **_P](
def actron_air_command[_EntityT: ActronAirEntity, **_P](
func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]],
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
"""Decorate Actron Air API calls to handle ActronAirAPIError exceptions."""
"""Decorator for Actron Air API calls.
Handles ActronAirAPIError exceptions, and requests a coordinator update
to update the status of the devices as soon as possible.
"""
@wraps(func)
async def wrapper(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None:
async def wrapper(self: _EntityT, /, *args: _P.args, **kwargs: _P.kwargs) -> None:
"""Wrap API calls with exception handling."""
try:
await func(self, *args, **kwargs)
@@ -30,6 +34,7 @@ def handle_actron_api_errors[_EntityT: ActronAirEntity, **_P](
translation_key="api_error",
translation_placeholders={"error": str(err)},
) from err
self.coordinator.async_set_updated_data(self.coordinator.data)
return wrapper

View File

@@ -13,5 +13,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"quality_scale": "silver",
"requirements": ["actron-neo-api==0.4.1"]
"requirements": ["actron-neo-api==0.5.6"]
}

View File

@@ -41,7 +41,7 @@ rules:
# Gold
devices: done
diagnostics: todo
diagnostics: done
discovery-update-info:
status: exempt
comment: This integration uses DHCP discovery, however is cloud polling. Therefore there is no information to update.
@@ -54,18 +54,12 @@ rules:
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: todo
entity-category:
status: exempt
comment: This integration does not use entity categories.
entity-device-class:
status: exempt
comment: This integration does not use entity device classes.
entity-disabled-by-default:
status: exempt
comment: Not required for this integration at this stage.
entity-translations: todo
entity-category: done
entity-device-class: todo
entity-disabled-by-default: todo
entity-translations: done
exception-translations: done
icon-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues:
status: exempt
@@ -75,4 +69,4 @@ rules:
# Platinum
async-dependency: done
inject-websession: todo
strict-typing: todo
strict-typing: done

View File

@@ -58,6 +58,9 @@
"setup_connection_error": {
"message": "Failed to connect to the Actron Air API"
},
"temperature_missing": {
"message": "Provide a temperature value when adjusting the climate entity."
},
"update_error": {
"message": "An error occurred while retrieving data from the Actron Air API: {error}"
}

View File

@@ -10,7 +10,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
from .entity import ActronAirAcEntity, handle_actron_api_errors
from .entity import ActronAirAcEntity, actron_air_command
PARALLEL_UPDATES = 0
@@ -105,12 +105,12 @@ class ActronAirSwitch(ActronAirAcEntity, SwitchEntity):
"""Return true if the switch is on."""
return self.entity_description.is_on_fn(self.coordinator)
@handle_actron_api_errors
@actron_air_command
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self.entity_description.set_fn(self.coordinator, True)
@handle_actron_api_errors
@actron_air_command
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self.entity_description.set_fn(self.coordinator, False)

View File

@@ -25,7 +25,7 @@ async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
hass.data[DATA_MEDIA_SOURCE] = source = local_source.LocalSource(
hass,
DOMAIN,
"AI Generated Images",
"AI generated images",
{IMAGE_DIR: str(media_dir)},
f"/{DOMAIN}",
)

View File

@@ -36,7 +36,9 @@ def _make_detected_condition(
) -> type[Condition]:
"""Create a detected condition for a binary sensor device class."""
return make_entity_state_condition(
{BINARY_SENSOR_DOMAIN: DomainSpec(device_class=device_class)}, STATE_ON
{BINARY_SENSOR_DOMAIN: DomainSpec(device_class=device_class)},
STATE_ON,
support_duration=True,
)
@@ -45,7 +47,9 @@ def _make_cleared_condition(
) -> type[Condition]:
"""Create a cleared condition for a binary sensor device class."""
return make_entity_state_condition(
{BINARY_SENSOR_DOMAIN: DomainSpec(device_class=device_class)}, STATE_OFF
{BINARY_SENSOR_DOMAIN: DomainSpec(device_class=device_class)},
STATE_OFF,
support_duration=True,
)

View File

@@ -4,11 +4,8 @@
required: true
default: any
selector:
select:
translation_key: condition_behavior
options:
- all
- any
automation_behavior:
mode: condition
# --- Unit lists for multi-unit pollutants ---
@@ -249,6 +246,11 @@
.condition_binary_common: &condition_binary_common
fields:
behavior: *condition_behavior
for:
required: true
default: 00:00:00
selector:
duration:
is_gas_detected:
<<: *condition_binary_common

View File

@@ -1,8 +1,10 @@
{
"common": {
"condition_behavior_name": "Condition passes if",
"condition_for_name": "For at least",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_for_name": "For at least",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
@@ -23,6 +25,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::condition_for_name%]"
}
},
"name": "Carbon monoxide cleared"
@@ -32,6 +37,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::condition_for_name%]"
}
},
"name": "Carbon monoxide detected"
@@ -53,6 +61,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::condition_for_name%]"
}
},
"name": "Gas cleared"
@@ -62,6 +73,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::condition_for_name%]"
}
},
"name": "Gas detected"
@@ -167,6 +181,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::condition_for_name%]"
}
},
"name": "Smoke cleared"
@@ -176,6 +193,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::condition_for_name%]"
}
},
"name": "Smoke detected"
@@ -217,21 +237,6 @@
"name": "Volatile organic compounds value"
}
},
"selector": {
"condition_behavior": {
"options": {
"all": "All",
"any": "Any"
}
},
"trigger_behavior": {
"options": {
"any": "Any",
"first": "First",
"last": "Last"
}
}
},
"title": "Air Quality",
"triggers": {
"co2_changed": {
@@ -249,6 +254,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -269,6 +277,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
}
},
"name": "Carbon monoxide cleared"
@@ -279,6 +290,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -290,6 +304,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
}
},
"name": "Carbon monoxide detected"
@@ -299,6 +316,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
}
},
"name": "Gas cleared"
@@ -308,6 +328,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
}
},
"name": "Gas detected"
@@ -327,6 +350,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -348,6 +374,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -369,6 +398,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -390,6 +422,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -411,6 +446,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -432,6 +470,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -453,6 +494,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -474,6 +518,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -485,6 +532,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
}
},
"name": "Smoke cleared"
@@ -494,6 +544,9 @@
"fields": {
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
}
},
"name": "Smoke detected"
@@ -513,6 +566,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -534,6 +590,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
@@ -555,6 +614,9 @@
"behavior": {
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::air_quality::common::trigger_for_name%]"
},
"threshold": {
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}

View File

@@ -3,12 +3,13 @@
required: true
default: any
selector:
select:
translation_key: trigger_behavior
options:
- first
- last
- any
automation_behavior:
mode: trigger
for: &trigger_for
required: true
default: 00:00:00
selector:
duration:
# --- Unit lists for multi-unit pollutants ---
@@ -163,6 +164,7 @@
# Binary sensor detected/cleared trigger fields
.trigger_binary_fields: &trigger_binary_fields
behavior: *trigger_behavior
for: *trigger_for
# --- Binary sensor targets ---
@@ -294,6 +296,7 @@ co_crossed_threshold:
target: *target_co_sensor
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -320,6 +323,7 @@ co2_crossed_threshold:
target: *target_co2
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -344,6 +348,7 @@ pm1_crossed_threshold:
target: *target_pm1
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -368,6 +373,7 @@ pm25_crossed_threshold:
target: *target_pm25
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -392,6 +398,7 @@ pm4_crossed_threshold:
target: *target_pm4
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -416,6 +423,7 @@ pm10_crossed_threshold:
target: *target_pm10
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -442,6 +450,7 @@ ozone_crossed_threshold:
target: *target_ozone
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -470,6 +479,7 @@ voc_crossed_threshold:
target: *target_voc
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -498,6 +508,7 @@ voc_ratio_crossed_threshold:
target: *target_voc_ratio
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -526,6 +537,7 @@ no_crossed_threshold:
target: *target_no
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -554,6 +566,7 @@ no2_crossed_threshold:
target: *target_no2
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -580,6 +593,7 @@ n2o_crossed_threshold:
target: *target_n2o
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:
@@ -606,6 +620,7 @@ so2_crossed_threshold:
target: *target_so2
fields:
behavior: *trigger_behavior
for: *trigger_for
threshold:
required: true
selector:

View File

@@ -12,11 +12,11 @@ from airly.exceptions import AirlyError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_USE_NEAREST, DOMAIN, NO_AIRLY_SENSORS
from .const import CONF_USE_NEAREST, DEFAULT_NAME, DOMAIN, NO_AIRLY_SENSORS
DESCRIPTION_PLACEHOLDERS = {
"developer_registration_url": "https://developer.airly.eu/register",
@@ -45,16 +45,16 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
try:
location_point_valid = await check_location(
websession,
user_input["api_key"],
user_input["latitude"],
user_input["longitude"],
user_input[CONF_API_KEY],
user_input[CONF_LATITUDE],
user_input[CONF_LONGITUDE],
)
if not location_point_valid:
location_nearest_valid = await check_location(
websession,
user_input["api_key"],
user_input["latitude"],
user_input["longitude"],
user_input[CONF_API_KEY],
user_input[CONF_LATITUDE],
user_input[CONF_LONGITUDE],
use_nearest=True,
)
except AirlyError as err:
@@ -68,7 +68,7 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="wrong_location")
use_nearest = True
return self.async_create_entry(
title=user_input[CONF_NAME],
title=DEFAULT_NAME,
data={**user_input, CONF_USE_NEAREST: use_nearest},
)
@@ -83,9 +83,6 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(
CONF_NAME, default=self.hass.config.location_name
): str,
}
),
errors=errors,

View File

@@ -37,3 +37,5 @@ MAX_UPDATE_INTERVAL: Final = 90
MIN_UPDATE_INTERVAL: Final = 5
NO_AIRLY_SENSORS: Final = "There are no Airly sensors in this area yet."
URL = "https://airly.org/map/#{latitude},{longitude}"
DEFAULT_NAME: Final = "Airly"

View File

@@ -127,7 +127,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
),
AirlySensorEntityDescription(
key=ATTR_API_CO,
translation_key="co",
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
@@ -178,7 +178,7 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Airly sensor entities based on a config entry."""
name = entry.data[CONF_NAME]
name = entry.data.get(CONF_NAME) or entry.title
coordinator = entry.runtime_data

View File

@@ -13,8 +13,7 @@
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]",
"name": "[%key:common::config_flow::data::name%]"
"longitude": "[%key:common::config_flow::data::longitude%]"
},
"description": "To generate API key go to {developer_registration_url}"
}
@@ -24,9 +23,6 @@
"sensor": {
"caqi": {
"name": "Common air quality index"
},
"co": {
"name": "[%key:component::sensor::entity_component::carbon_monoxide::name%]"
}
}
},

View File

@@ -36,6 +36,8 @@ class AirTouch5ConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception")
errors = {"base": "cannot_connect"}
else:
# Uses the host/IP value from CONF_HOST as unique ID, which is no longer allowed
# pylint: disable-next=hass-unique-id-ip-based
await self.async_set_unique_id(user_input[CONF_HOST])
self._abort_if_unique_id_configured()
return self.async_create_entry(

View File

@@ -4,6 +4,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.condition import (
ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL_FOR,
Condition,
EntityStateConditionBase,
make_entity_state_condition,
@@ -25,6 +26,7 @@ class EntityStateRequiredFeaturesCondition(EntityStateConditionBase):
"""State condition."""
_required_features: int
_schema = ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL_FOR
def entity_filter(self, entities: set[str]) -> set[str]:
"""Filter entities of this domain with the required features."""
@@ -82,9 +84,11 @@ CONDITIONS: dict[str, type[Condition]] = {
AlarmControlPanelState.ARMED_VACATION,
AlarmControlPanelEntityFeature.ARM_VACATION,
),
"is_disarmed": make_entity_state_condition(DOMAIN, AlarmControlPanelState.DISARMED),
"is_disarmed": make_entity_state_condition(
DOMAIN, AlarmControlPanelState.DISARMED, support_duration=True
),
"is_triggered": make_entity_state_condition(
DOMAIN, AlarmControlPanelState.TRIGGERED
DOMAIN, AlarmControlPanelState.TRIGGERED, support_duration=True
),
}

View File

@@ -1,22 +1,29 @@
.condition_common: &condition_common
target:
target: &condition_common_target
entity:
domain: alarm_control_panel
fields: &condition_common_fields
behavior:
behavior: &condition_common_behavior
required: true
default: any
selector:
select:
translation_key: condition_behavior
options:
- all
- any
automation_behavior:
mode: condition
.condition_common_for: &condition_common_for
target: *condition_common_target
fields: &condition_common_for_fields
behavior: *condition_common_behavior
for:
required: true
default: 00:00:00
selector:
duration:
is_armed: *condition_common
is_armed_away:
fields: *condition_common_fields
fields: *condition_common_for_fields
target:
entity:
domain: alarm_control_panel
@@ -24,7 +31,7 @@ is_armed_away:
- alarm_control_panel.AlarmControlPanelEntityFeature.ARM_AWAY
is_armed_home:
fields: *condition_common_fields
fields: *condition_common_for_fields
target:
entity:
domain: alarm_control_panel
@@ -32,7 +39,7 @@ is_armed_home:
- alarm_control_panel.AlarmControlPanelEntityFeature.ARM_HOME
is_armed_night:
fields: *condition_common_fields
fields: *condition_common_for_fields
target:
entity:
domain: alarm_control_panel
@@ -40,13 +47,13 @@ is_armed_night:
- alarm_control_panel.AlarmControlPanelEntityFeature.ARM_NIGHT
is_armed_vacation:
fields: *condition_common_fields
fields: *condition_common_for_fields
target:
entity:
domain: alarm_control_panel
supported_features:
- alarm_control_panel.AlarmControlPanelEntityFeature.ARM_VACATION
is_disarmed: *condition_common
is_disarmed: *condition_common_for
is_triggered: *condition_common
is_triggered: *condition_common_for

View File

@@ -1,7 +1,9 @@
{
"common": {
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
"condition_for_name": "For at least",
"trigger_behavior_name": "Trigger when",
"trigger_for_name": "For at least"
},
"conditions": {
"is_armed": {
@@ -18,6 +20,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::condition_for_name%]"
}
},
"name": "Alarm is armed away"
@@ -27,6 +32,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::condition_for_name%]"
}
},
"name": "Alarm is armed home"
@@ -36,6 +44,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::condition_for_name%]"
}
},
"name": "Alarm is armed night"
@@ -45,6 +56,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::condition_for_name%]"
}
},
"name": "Alarm is armed vacation"
@@ -54,6 +68,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::condition_for_name%]"
}
},
"name": "Alarm is disarmed"
@@ -63,6 +80,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::condition_for_name%]"
}
},
"name": "Alarm is triggered"
@@ -140,21 +160,6 @@
"message": "Arming requires a code but none was given for {entity_id}."
}
},
"selector": {
"condition_behavior": {
"options": {
"all": "All",
"any": "Any"
}
},
"trigger_behavior": {
"options": {
"any": "Any",
"first": "First",
"last": "Last"
}
}
},
"services": {
"alarm_arm_away": {
"description": "Arms an alarm in the away mode.",
@@ -234,6 +239,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm armed"
@@ -243,6 +251,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm armed away"
@@ -252,6 +263,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm armed home"
@@ -261,6 +275,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm armed night"
@@ -270,6 +287,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm armed vacation"
@@ -279,6 +299,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm disarmed"
@@ -288,6 +311,9 @@
"fields": {
"behavior": {
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
}
},
"name": "Alarm triggered"

View File

@@ -7,12 +7,13 @@
required: true
default: any
selector:
select:
options:
- first
- last
- any
translation_key: trigger_behavior
automation_behavior:
mode: trigger
for:
required: true
default: 00:00:00
selector:
duration:
armed: *trigger_common

View File

@@ -11,6 +11,7 @@ from .services import async_setup_services
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.NOTIFY,
Platform.SENSOR,
Platform.SWITCH,

View File

@@ -0,0 +1,55 @@
"""Support for buttons."""
from homeassistant.components.button import ButtonEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import slugify
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .entity import AmazonServiceEntity
# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up button entities for Alexa Devices."""
coordinator = entry.runtime_data
known_routines: set[str] = set()
def _check_routines() -> None:
current_routines = set(coordinator.api.routines)
new_routines = current_routines - known_routines
if new_routines:
known_routines.update(new_routines)
async_add_entities(
AmazonRoutineButton(coordinator, routine) for routine in new_routines
)
_check_routines()
entry.async_on_unload(coordinator.async_add_listener(_check_routines))
class AmazonRoutineButton(AmazonServiceEntity, ButtonEntity):
"""Button entity for Alexa routine."""
_attr_has_entity_name = True
def __init__(self, coordinator: AmazonDevicesCoordinator, routine: str) -> None:
"""Initialize the routine button entity."""
self._coordinator = coordinator
self._routine = routine
super().__init__(
coordinator,
EntityDescription(key=slugify(routine), name=routine),
)
async def async_press(self) -> None:
"""Handle button press action."""
await self._coordinator.api.call_routine(self._routine)

View File

@@ -12,12 +12,13 @@ from aioamazondevices.structures import AmazonDevice
from aiohttp import ClientSession
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import slugify
from .const import _LOGGER, CONF_LOGIN_DATA, DOMAIN
@@ -54,7 +55,23 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
entry.data[CONF_PASSWORD],
entry.data[CONF_LOGIN_DATA],
)
self.previous_devices: set[str] = set()
device_registry = dr.async_get(hass)
self.previous_devices: set[str] = {
identifier
for device in device_registry.devices.get_devices_for_config_entry_id(
entry.entry_id
)
if device.entry_type != dr.DeviceEntryType.SERVICE
for identifier_domain, identifier in device.identifiers
if identifier_domain == DOMAIN
}
self.previous_routines: set[str] = {
routine.unique_id
for routine in er.async_entries_for_config_entry(
er.async_get(hass), entry.entry_id
)
if routine.domain == Platform.BUTTON
}
async def _async_update_data(self) -> dict[str, AmazonDevice]:
"""Update device data."""
@@ -83,8 +100,13 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
current_devices = set(data.keys())
if stale_devices := self.previous_devices - current_devices:
await self._async_remove_device_stale(stale_devices)
self.previous_devices = current_devices
current_routines = {slugify(routine) for routine in self.api.routines}
if stale_routines := self.previous_routines - current_routines:
await self._async_remove_routine_stale(stale_routines)
self.previous_routines = current_routines
return data
async def _async_remove_device_stale(
@@ -107,3 +129,23 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
device_id=device.id,
remove_config_entry_id=self.config_entry.entry_id,
)
async def _async_remove_routine_stale(
self,
stale_routines: set[str],
) -> None:
"""Remove stale routine."""
entity_registry = er.async_get(self.hass)
for routine in stale_routines:
_LOGGER.debug(
"Detected change in routines: routine %s removed",
routine,
)
entity_id = entity_registry.async_get_entity_id(
Platform.BUTTON,
DOMAIN,
f"{slugify(self.config_entry.unique_id)}-{slugify(routine)}",
)
if entity_id:
entity_registry.async_remove(entity_id)

View File

@@ -2,9 +2,10 @@
from aioamazondevices.structures import AmazonDevice
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import slugify
from .const import DOMAIN
from .coordinator import AmazonDevicesCoordinator
@@ -50,3 +51,32 @@ class AmazonEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
and self._serial_num in self.coordinator.data
and self.device.online
)
class AmazonServiceEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
"""Defines Alexa Devices entity for service device."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AmazonDevicesCoordinator,
description: EntityDescription,
) -> None:
"""Initialize the service entity."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, service_device_id(coordinator))},
manufacturer="Amazon",
entry_type=DeviceEntryType.SERVICE,
)
self.entity_description = description
self._attr_unique_id = (
f"{slugify(coordinator.config_entry.unique_id)}-{description.key}"
)
def service_device_id(coordinator: AmazonDevicesCoordinator) -> str:
"""Return service device id."""
return slugify(f"{coordinator.config_entry.unique_id}_service_device")

View File

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

View File

@@ -39,7 +39,6 @@ from homeassistant.helpers.typing import ConfigType
from .binary_sensor import BINARY_SENSOR_KEYS, BINARY_SENSORS, check_binary_sensors
from .camera import STREAM_SOURCE_LIST
from .const import (
CAMERAS,
COMM_RETRIES,
COMM_TIMEOUT,
DATA_AMCREST,
@@ -359,7 +358,7 @@ def _start_event_monitor(
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Amcrest IP Camera component."""
hass.data.setdefault(DATA_AMCREST, {DEVICES: {}, CAMERAS: []})
hass.data.setdefault(DATA_AMCREST, {DEVICES: {}})
for device in config[DOMAIN]:
name: str = device[CONF_NAME]

View File

@@ -12,13 +12,11 @@ import aiohttp
from aiohttp import web
from amcrest import AmcrestError
from haffmpeg.camera import CameraMjpeg
import voluptuous as vol
from homeassistant.components.camera import Camera, CameraEntityFeature
from homeassistant.components.ffmpeg import FFmpegManager, get_ffmpeg_manager
from homeassistant.const import ATTR_ENTITY_ID, CONF_NAME, STATE_OFF, STATE_ON
from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import (
async_aiohttp_proxy_stream,
async_aiohttp_proxy_web,
@@ -29,11 +27,13 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
ATTR_COLOR_BW,
CAMERA_WEB_SESSION_TIMEOUT,
CAMERAS,
CBW,
COMM_TIMEOUT,
DATA_AMCREST,
DEVICES,
MOV,
RESOLUTION_TO_STREAM,
SERVICE_UPDATE,
SNAPSHOT_TIMEOUT,
@@ -49,65 +49,11 @@ SCAN_INTERVAL = timedelta(seconds=15)
STREAM_SOURCE_LIST = ["snapshot", "mjpeg", "rtsp"]
_ATTR_PTZ_TT = "travel_time"
_ATTR_PTZ_MOV = "movement"
_MOV = [
"zoom_out",
"zoom_in",
"right",
"left",
"up",
"down",
"right_down",
"right_up",
"left_down",
"left_up",
]
_ZOOM_ACTIONS = ["ZoomWide", "ZoomTele"]
_MOVE_1_ACTIONS = ["Right", "Left", "Up", "Down"]
_MOVE_2_ACTIONS = ["RightDown", "RightUp", "LeftDown", "LeftUp"]
_ACTION = _ZOOM_ACTIONS + _MOVE_1_ACTIONS + _MOVE_2_ACTIONS
_DEFAULT_TT = 0.2
_ATTR_PRESET = "preset"
_ATTR_COLOR_BW = "color_bw"
_CBW_COLOR = "color"
_CBW_AUTO = "auto"
_CBW_BW = "bw"
_CBW = [_CBW_COLOR, _CBW_AUTO, _CBW_BW]
_SRV_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids})
_SRV_GOTO_SCHEMA = _SRV_SCHEMA.extend(
{vol.Required(_ATTR_PRESET): vol.All(vol.Coerce(int), vol.Range(min=1))}
)
_SRV_CBW_SCHEMA = _SRV_SCHEMA.extend({vol.Required(_ATTR_COLOR_BW): vol.In(_CBW)})
_SRV_PTZ_SCHEMA = _SRV_SCHEMA.extend(
{
vol.Required(_ATTR_PTZ_MOV): vol.In(_MOV),
vol.Optional(_ATTR_PTZ_TT, default=_DEFAULT_TT): cv.small_float,
}
)
CAMERA_SERVICES = {
"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),
),
}
_BOOL_TO_STATE = {True: STATE_ON, False: STATE_OFF}
@@ -275,7 +221,7 @@ class AmcrestCam(Camera):
self._motion_recording_enabled
)
if self._color_bw is not None:
attr[_ATTR_COLOR_BW] = self._color_bw
attr[ATTR_COLOR_BW] = self._color_bw
return attr
@property
@@ -322,15 +268,7 @@ class AmcrestCam(Camera):
self.async_schedule_update_ha_state(True)
async def async_added_to_hass(self) -> None:
"""Subscribe to signals and add camera to list."""
self._unsub_dispatcher.extend(
async_dispatcher_connect(
self.hass,
service_signal(service, self.entity_id),
getattr(self, callback_name),
)
for service, (_, callback_name, _) in CAMERA_SERVICES.items()
)
"""Subscribe to signals."""
self._unsub_dispatcher.append(
async_dispatcher_connect(
self.hass,
@@ -338,11 +276,9 @@ class AmcrestCam(Camera):
self.async_on_demand_update,
)
)
self.hass.data[DATA_AMCREST][CAMERAS].append(self.entity_id)
async def async_will_remove_from_hass(self) -> None:
"""Remove camera from list and disconnect from signals."""
self.hass.data[DATA_AMCREST][CAMERAS].remove(self.entity_id)
"""Disconnect from signals."""
for unsub_dispatcher in self._unsub_dispatcher:
unsub_dispatcher()
@@ -456,7 +392,7 @@ class AmcrestCam(Camera):
async def async_ptz_control(self, movement: str, travel_time: float) -> None:
"""Move or zoom camera in specified direction."""
code = _ACTION[_MOV.index(movement)]
code = _ACTION[MOV.index(movement)]
kwargs = {"code": code, "arg1": 0, "arg2": 0, "arg3": 0}
if code in _MOVE_1_ACTIONS:
@@ -613,10 +549,10 @@ class AmcrestCam(Camera):
)
async def _async_get_color_mode(self) -> str:
return _CBW[await self._api.async_day_night_color]
return CBW[await self._api.async_day_night_color]
async def _async_set_color_mode(self, cbw: str) -> None:
await self._api.async_set_day_night_color(_CBW.index(cbw), channel=0)
await self._api.async_set_day_night_color(CBW.index(cbw), channel=0)
async def _async_set_color_bw(self, cbw: str) -> None:
"""Set camera color mode."""

View File

@@ -2,7 +2,6 @@
DOMAIN = "amcrest"
DATA_AMCREST = DOMAIN
CAMERAS = "cameras"
DEVICES = "devices"
BINARY_SENSOR_SCAN_INTERVAL_SECS = 5
@@ -17,3 +16,18 @@ SERVICE_UPDATE = "update"
RESOLUTION_LIST = {"high": 0, "low": 1}
RESOLUTION_TO_STREAM = {0: "Main", 1: "Extra"}
ATTR_COLOR_BW = "color_bw"
CBW = ["color", "auto", "bw"]
MOV = [
"zoom_out",
"zoom_in",
"right",
"left",
"up",
"down",
"right_down",
"right_up",
"left_down",
"left_up",
]

View File

@@ -1,62 +1,67 @@
"""Support for Amcrest IP cameras."""
"""Services for Amcrest IP cameras."""
from __future__ import annotations
from homeassistant.auth.models import User
from homeassistant.auth.permissions.const import POLICY_CONTROL
from homeassistant.const import ATTR_ENTITY_ID, ENTITY_MATCH_ALL, ENTITY_MATCH_NONE
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import Unauthorized, UnknownUser
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.service import async_extract_entity_ids
import voluptuous as vol
from .camera import CAMERA_SERVICES
from .const import CAMERAS, DATA_AMCREST, DOMAIN
from .helpers import service_signal
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, service
from .const import ATTR_COLOR_BW, CBW, DOMAIN, MOV
_ATTR_PRESET = "preset"
_ATTR_PTZ_MOV = "movement"
_ATTR_PTZ_TT = "travel_time"
_DEFAULT_TT = 0.2
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up the Amcrest IP Camera services."""
for service_name, func in (
("enable_recording", "async_enable_recording"),
("disable_recording", "async_disable_recording"),
("enable_audio", "async_enable_audio"),
("disable_audio", "async_disable_audio"),
("enable_motion_recording", "async_enable_motion_recording"),
("disable_motion_recording", "async_disable_motion_recording"),
("start_tour", "async_start_tour"),
("stop_tour", "async_stop_tour"),
):
service.async_register_platform_entity_service(
hass,
DOMAIN,
service_name,
entity_domain=CAMERA_DOMAIN,
schema=None,
func=func,
)
def have_permission(user: User | None, entity_id: str) -> bool:
return not user or user.permissions.check_entity(entity_id, POLICY_CONTROL)
async def async_extract_from_service(call: ServiceCall) -> list[str]:
if call.context.user_id:
user = await hass.auth.async_get_user(call.context.user_id)
if user is None:
raise UnknownUser(context=call.context)
else:
user = None
if call.data.get(ATTR_ENTITY_ID) == ENTITY_MATCH_ALL:
# Return all entity_ids user has permission to control.
return [
entity_id
for entity_id in hass.data[DATA_AMCREST][CAMERAS]
if have_permission(user, entity_id)
]
if call.data.get(ATTR_ENTITY_ID) == ENTITY_MATCH_NONE:
return []
call_ids = await async_extract_entity_ids(call)
entity_ids = []
for entity_id in hass.data[DATA_AMCREST][CAMERAS]:
if entity_id not in call_ids:
continue
if not have_permission(user, entity_id):
raise Unauthorized(
context=call.context, entity_id=entity_id, permission=POLICY_CONTROL
)
entity_ids.append(entity_id)
return entity_ids
async def async_service_handler(call: ServiceCall) -> None:
args = [call.data[arg] for arg in CAMERA_SERVICES[call.service][2]]
for entity_id in await async_extract_from_service(call):
async_dispatcher_send(hass, service_signal(call.service, entity_id), *args)
for service, params in CAMERA_SERVICES.items():
hass.services.async_register(DOMAIN, service, async_service_handler, params[0])
service.async_register_platform_entity_service(
hass,
DOMAIN,
"goto_preset",
entity_domain=CAMERA_DOMAIN,
schema={vol.Required(_ATTR_PRESET): vol.All(vol.Coerce(int), vol.Range(min=1))},
func="async_goto_preset",
)
service.async_register_platform_entity_service(
hass,
DOMAIN,
"set_color_bw",
entity_domain=CAMERA_DOMAIN,
schema={vol.Required(ATTR_COLOR_BW): vol.In(CBW)},
func="async_set_color_bw",
)
service.async_register_platform_entity_service(
hass,
DOMAIN,
"ptz_control",
entity_domain=CAMERA_DOMAIN,
schema={
vol.Required(_ATTR_PTZ_MOV): vol.In(MOV),
vol.Optional(_ATTR_PTZ_TT, default=_DEFAULT_TT): cv.small_float,
},
func="async_ptz_control",
)

View File

@@ -76,6 +76,7 @@ from .const import (
ATTR_HEALTHY,
ATTR_INTEGRATION_COUNT,
ATTR_INTEGRATIONS,
ATTR_ISSUE_TRACKER,
ATTR_OPERATING_SYSTEM,
ATTR_PROTECTED,
ATTR_RECORDER,
@@ -414,6 +415,7 @@ class Analytics:
custom_integrations.append(
{
ATTR_DOMAIN: integration.domain,
ATTR_ISSUE_TRACKER: integration.issue_tracker,
ATTR_VERSION: integration.version,
}
)

View File

@@ -36,6 +36,7 @@ ATTR_HEALTHY = "healthy"
ATTR_INSTALLATION_TYPE = "installation_type"
ATTR_INTEGRATION_COUNT = "integration_count"
ATTR_INTEGRATIONS = "integrations"
ATTR_ISSUE_TRACKER = "issue_tracker"
ATTR_ONBOARDED = "onboarded"
ATTR_OPERATING_SYSTEM = "operating_system"
ATTR_PREFERENCES = "preferences"

View File

@@ -11,12 +11,12 @@
"user": {
"data": {
"tracked_apps": "Apps",
"tracked_custom_integrations": "Community integrations",
"tracked_custom_integrations": "Custom integrations",
"tracked_integrations": "Integrations"
},
"data_description": {
"tracked_apps": "Select the apps you want to track",
"tracked_custom_integrations": "Select the community integrations you want to track",
"tracked_custom_integrations": "Select the custom integrations you want to track",
"tracked_integrations": "Select the integrations you want to track"
}
}
@@ -31,7 +31,7 @@
"unit_of_measurement": "[%key:component::analytics_insights::entity::sensor::apps::unit_of_measurement%]"
},
"custom_integrations": {
"name": "{custom_integration_domain} (community)",
"name": "{custom_integration_domain} (custom)",
"unit_of_measurement": "[%key:component::analytics_insights::entity::sensor::apps::unit_of_measurement%]"
},
"total_active_installations": {

View File

@@ -2,6 +2,8 @@
from __future__ import annotations
from anthropic.resources.messages.messages import DEPRECATED_MODELS
from homeassistant.config_entries import ConfigSubentry
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
@@ -13,13 +15,7 @@ from homeassistant.helpers import (
)
from homeassistant.helpers.typing import ConfigType
from .const import (
CONF_CHAT_MODEL,
DEFAULT_CONVERSATION_NAME,
DEPRECATED_MODELS,
DOMAIN,
LOGGER,
)
from .const import CONF_CHAT_MODEL, DEFAULT_CONVERSATION_NAME, DOMAIN, LOGGER
from .coordinator import AnthropicConfigEntry, AnthropicCoordinator
PLATFORMS = (Platform.AI_TASK, Platform.CONVERSATION)
@@ -37,15 +33,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: AnthropicConfigEntry) ->
coordinator = AnthropicCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
LOGGER.debug("Available models: %s", coordinator.data)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(async_update_options))
for subentry in entry.subentries.values():
if (model := subentry.data.get(CONF_CHAT_MODEL)) and model.startswith(
tuple(DEPRECATED_MODELS)
):
if (model := subentry.data.get(CONF_CHAT_MODEL)) and model in DEPRECATED_MODELS:
ir.async_create_issue(
hass,
DOMAIN,
@@ -235,6 +230,19 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AnthropicConfigEntry)
)
hass.config_entries.async_update_entry(entry, minor_version=3)
if entry.version == 2 and entry.minor_version == 3:
# Remove Temperature parameter
CONF_TEMPERATURE = "temperature"
for subentry in entry.subentries.values():
data = subentry.data.copy()
if CONF_TEMPERATURE not in data:
continue
data.pop(CONF_TEMPERATURE, None)
hass.config_entries.async_update_subentry(entry, subentry, data=data)
hass.config_entries.async_update_entry(entry, minor_version=4)
LOGGER.debug(
"Migration to version %s:%s successful", entry.version, entry.minor_version
)

View File

@@ -5,7 +5,6 @@ from __future__ import annotations
from collections.abc import Mapping
import json
import logging
import re
from typing import TYPE_CHECKING, Any, cast
import anthropic
@@ -30,6 +29,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import llm
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.selector import (
NumberSelector,
@@ -43,14 +43,12 @@ 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_PROMPT_CACHING,
CONF_RECOMMENDED,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
@@ -65,12 +63,11 @@ from .const import (
DEFAULT_AI_TASK_NAME,
DEFAULT_CONVERSATION_NAME,
DOMAIN,
NON_ADAPTIVE_THINKING_MODELS,
NON_THINKING_MODELS,
MIN_THINKING_BUDGET,
TOOL_SEARCH_UNSUPPORTED_MODELS,
WEB_SEARCH_UNSUPPORTED_MODELS,
PromptCaching,
)
from .coordinator import model_alias
if TYPE_CHECKING:
from . import AnthropicConfigEntry
@@ -105,39 +102,11 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
await client.models.list(timeout=10.0)
async def get_model_list(client: anthropic.AsyncAnthropic) -> list[SelectOptionDict]:
"""Get list of available models."""
try:
models = (await client.models.list()).data
except anthropic.AnthropicError:
models = []
_LOGGER.debug("Available models: %s", models)
model_options: list[SelectOptionDict] = []
short_form = re.compile(r"[^\d]-\d$")
for model_info in models:
# Resolve alias from versioned model name:
model_alias = (
model_info.id[:-9]
if model_info.id != "claude-3-haiku-20240307"
and model_info.id[-2:-1] != "-"
else model_info.id
)
if short_form.search(model_alias):
model_alias += "-0"
model_options.append(
SelectOptionDict(
label=model_info.display_name,
value=model_alias,
)
)
return model_options
class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Anthropic."""
VERSION = 2
MINOR_VERSION = 3
MINOR_VERSION = 4
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -229,6 +198,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
"""Flow for managing conversation subentries."""
options: dict[str, Any]
model_info: anthropic.types.ModelInfo
@property
def _is_new(self) -> bool:
@@ -342,24 +312,15 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
) -> SubentryFlowResult:
"""Manage advanced options."""
errors: dict[str, str] = {}
description_placeholders: dict[str, str] = {}
step_schema: VolDictType = {
vol.Optional(
CONF_CHAT_MODEL,
default=DEFAULT[CONF_CHAT_MODEL],
): SelectSelector(
SelectSelectorConfig(
options=await self._get_model_list(), custom_value=True
)
SelectSelectorConfig(options=self._get_model_list(), custom_value=True)
),
vol.Optional(
CONF_MAX_TOKENS,
default=DEFAULT[CONF_MAX_TOKENS],
): int,
vol.Optional(
CONF_TEMPERATURE,
default=DEFAULT[CONF_TEMPERATURE],
): NumberSelector(NumberSelectorConfig(min=0, max=1, step=0.05)),
vol.Optional(
CONF_PROMPT_CACHING,
default=DEFAULT[CONF_PROMPT_CACHING],
@@ -375,6 +336,25 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
if user_input is not None:
self.options.update(user_input)
coordinator = self._get_entry().runtime_data
self.model_info, status = coordinator.get_model_info(
self.options[CONF_CHAT_MODEL]
)
if not status:
# Couldn't find the model in the cached list, try to fetch it directly
client = coordinator.client
try:
self.model_info = await client.models.retrieve(
self.options[CONF_CHAT_MODEL], timeout=10.0
)
except anthropic.NotFoundError:
errors[CONF_CHAT_MODEL] = "model_not_found"
except anthropic.AnthropicError as err:
errors[CONF_CHAT_MODEL] = "api_error"
description_placeholders["message"] = (
err.message if isinstance(err, anthropic.APIError) else str(err)
)
if not errors:
return await self.async_step_model()
@@ -384,6 +364,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
vol.Schema(step_schema), self.options
),
errors=errors,
description_placeholders=description_placeholders,
)
async def async_step_model(
@@ -392,30 +373,59 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
"""Manage model-specific options."""
errors: dict[str, str] = {}
step_schema: VolDictType = {}
step_schema: VolDictType = {
vol.Optional(
CONF_MAX_TOKENS,
default=DEFAULT[CONF_MAX_TOKENS],
): vol.All(
NumberSelector(
NumberSelectorConfig(min=0, max=self.model_info.max_tokens)
),
vol.Coerce(int),
)
if self.model_info.max_tokens
else cv.positive_int,
}
model = self.options[CONF_CHAT_MODEL]
if not model.startswith(tuple(NON_THINKING_MODELS)) and model.startswith(
tuple(NON_ADAPTIVE_THINKING_MODELS)
if (
self.model_info.capabilities
and self.model_info.capabilities.thinking.supported
and not self.model_info.capabilities.thinking.types.adaptive.supported
):
step_schema[
vol.Optional(
CONF_THINKING_BUDGET, default=DEFAULT[CONF_THINKING_BUDGET]
)
] = vol.All(
NumberSelector(
NumberSelectorConfig(
min=0,
max=self.options.get(CONF_MAX_TOKENS, DEFAULT[CONF_MAX_TOKENS]),
)
),
vol.Coerce(int),
] = (
vol.All(
NumberSelector(
NumberSelectorConfig(min=0, max=self.model_info.max_tokens)
),
vol.Coerce(int),
)
if self.model_info.max_tokens
else cv.positive_int
)
else:
self.options.pop(CONF_THINKING_BUDGET, None)
if not model.startswith(tuple(NON_ADAPTIVE_THINKING_MODELS)):
if (
self.model_info.capabilities
and (effort_capability := self.model_info.capabilities.effort).supported
):
effort_options: list[str] = []
if self.model_info.capabilities.thinking.types.adaptive.supported:
effort_options.append("none")
if effort_capability.low.supported:
effort_options.append("low")
if effort_capability.medium.supported:
effort_options.append("medium")
if effort_capability.high.supported:
effort_options.append("high")
if effort_capability.xhigh and effort_capability.xhigh.supported:
effort_options.append("xhigh")
if effort_capability.max.supported:
effort_options.append("max")
step_schema[
vol.Optional(
CONF_THINKING_EFFORT,
@@ -423,7 +433,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
)
] = SelectSelector(
SelectSelectorConfig(
options=["none", "low", "medium", "high", "max"],
options=effort_options,
translation_key=CONF_THINKING_EFFORT,
mode=SelectSelectorMode.DROPDOWN,
)
@@ -431,43 +441,34 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
else:
self.options.pop(CONF_THINKING_EFFORT, None)
if not model.startswith(tuple(CODE_EXECUTION_UNSUPPORTED_MODELS)):
step_schema[
step_schema.update(
{
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(
{
vol.Optional(
CONF_WEB_SEARCH,
default=DEFAULT[CONF_WEB_SEARCH],
): bool,
vol.Optional(
CONF_WEB_SEARCH_MAX_USES,
default=DEFAULT[CONF_WEB_SEARCH_MAX_USES],
): int,
vol.Optional(
CONF_WEB_SEARCH_USER_LOCATION,
default=DEFAULT[CONF_WEB_SEARCH_USER_LOCATION],
): bool,
}
)
else:
self.options.pop(CONF_WEB_SEARCH, None)
self.options.pop(CONF_WEB_SEARCH_MAX_USES, None)
self.options.pop(CONF_WEB_SEARCH_USER_LOCATION, None)
): bool,
vol.Optional(
CONF_WEB_SEARCH,
default=DEFAULT[CONF_WEB_SEARCH],
): bool,
vol.Optional(
CONF_WEB_SEARCH_MAX_USES,
default=DEFAULT[CONF_WEB_SEARCH_MAX_USES],
): int,
vol.Optional(
CONF_WEB_SEARCH_USER_LOCATION,
default=DEFAULT[CONF_WEB_SEARCH_USER_LOCATION],
): bool,
}
)
self.options.pop(CONF_WEB_SEARCH_CITY, None)
self.options.pop(CONF_WEB_SEARCH_REGION, None)
self.options.pop(CONF_WEB_SEARCH_COUNTRY, None)
self.options.pop(CONF_WEB_SEARCH_TIMEZONE, None)
model = self.options[CONF_CHAT_MODEL]
if not model.startswith(tuple(TOOL_SEARCH_UNSUPPORTED_MODELS)):
step_schema[
vol.Optional(
@@ -479,9 +480,19 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
self.options.pop(CONF_TOOL_SEARCH, None)
if not step_schema:
user_input = {}
# Currently our schema is always present, but if one day it becomes empty,
# then the below line is needed to skip this step
user_input = {} # pragma: no cover
if user_input is not None:
if (
CONF_THINKING_BUDGET in user_input
and user_input[CONF_THINKING_BUDGET] >= MIN_THINKING_BUDGET
and user_input[CONF_THINKING_BUDGET]
>= user_input.get(CONF_MAX_TOKENS, DEFAULT[CONF_MAX_TOKENS])
):
errors[CONF_THINKING_BUDGET] = "thinking_budget_too_large"
if user_input.get(CONF_WEB_SEARCH, DEFAULT[CONF_WEB_SEARCH]) and not errors:
if user_input.get(
CONF_WEB_SEARCH_USER_LOCATION,
@@ -513,13 +524,16 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
last_step=True,
)
async def _get_model_list(self) -> list[SelectOptionDict]:
def _get_model_list(self) -> list[SelectOptionDict]:
"""Get list of available models."""
client = anthropic.AsyncAnthropic(
api_key=self._get_entry().data[CONF_API_KEY],
http_client=get_async_client(self.hass),
)
return await get_model_list(client)
coordinator = self._get_entry().runtime_data
return [
SelectOptionDict(
label=model_info.display_name,
value=model_alias(model_info.id),
)
for model_info in coordinator.data or []
]
async def _get_location_data(self) -> dict[str, str]:
"""Get approximate location data of the user."""

View File

@@ -15,7 +15,6 @@ CONF_CHAT_MODEL = "chat_model"
CONF_CODE_EXECUTION = "code_execution"
CONF_MAX_TOKENS = "max_tokens"
CONF_PROMPT_CACHING = "prompt_caching"
CONF_TEMPERATURE = "temperature"
CONF_THINKING_BUDGET = "thinking_budget"
CONF_THINKING_EFFORT = "thinking_effort"
CONF_TOOL_SEARCH = "tool_search"
@@ -36,13 +35,14 @@ class PromptCaching(StrEnum):
AUTOMATIC = "automatic"
MIN_THINKING_BUDGET = 1024
DEFAULT = {
CONF_CHAT_MODEL: "claude-haiku-4-5",
CONF_CODE_EXECUTION: False,
CONF_MAX_TOKENS: 3000,
CONF_PROMPT_CACHING: PromptCaching.PROMPT.value,
CONF_TEMPERATURE: 1.0,
CONF_THINKING_BUDGET: 0,
CONF_THINKING_BUDGET: MIN_THINKING_BUDGET,
CONF_THINKING_EFFORT: "low",
CONF_TOOL_SEARCH: False,
CONF_WEB_SEARCH: False,
@@ -50,56 +50,6 @@ DEFAULT = {
CONF_WEB_SEARCH_MAX_USES: 5,
}
MIN_THINKING_BUDGET = 1024
NON_THINKING_MODELS = [
"claude-3-haiku",
]
NON_ADAPTIVE_THINKING_MODELS = [
"claude-opus-4-5",
"claude-sonnet-4-5",
"claude-haiku-4-5",
"claude-opus-4-1",
"claude-opus-4-0",
"claude-opus-4-20250514",
"claude-sonnet-4-0",
"claude-sonnet-4-20250514",
"claude-3-haiku",
]
UNSUPPORTED_STRUCTURED_OUTPUT_MODELS = [
"claude-opus-4-1",
"claude-opus-4-0",
"claude-opus-4-20250514",
"claude-sonnet-4-0",
"claude-sonnet-4-20250514",
"claude-3-haiku",
]
WEB_SEARCH_UNSUPPORTED_MODELS = [
"claude-3-haiku",
]
CODE_EXECUTION_UNSUPPORTED_MODELS = [
"claude-3-haiku",
]
PROGRAMMATIC_TOOL_CALLING_UNSUPPORTED_MODELS = [
"claude-haiku-4-5",
"claude-opus-4-1",
"claude-opus-4-0",
"claude-opus-4-20250514",
"claude-sonnet-4-0",
"claude-sonnet-4-20250514",
"claude-3-haiku",
]
TOOL_SEARCH_UNSUPPORTED_MODELS = [
"claude-3",
"claude-haiku",
]
DEPRECATED_MODELS = [
"claude-3",
]

View File

@@ -2,7 +2,8 @@
from __future__ import annotations
from datetime import timedelta
import datetime
import re
import anthropic
@@ -15,13 +16,26 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN, LOGGER
UPDATE_INTERVAL_CONNECTED = timedelta(hours=12)
UPDATE_INTERVAL_DISCONNECTED = timedelta(minutes=1)
UPDATE_INTERVAL_CONNECTED = datetime.timedelta(hours=12)
UPDATE_INTERVAL_DISCONNECTED = datetime.timedelta(minutes=1)
type AnthropicConfigEntry = ConfigEntry[AnthropicCoordinator]
class AnthropicCoordinator(DataUpdateCoordinator[None]):
_model_short_form = re.compile(r"[^\d]-\d$")
@callback
def model_alias(model_id: str) -> str:
"""Resolve alias from versioned model name."""
if model_id[-2:-1] != "-" and not model_id.endswith("-preview"):
model_id = model_id[:-9]
if _model_short_form.search(model_id):
return model_id + "-0"
return model_id
class AnthropicCoordinator(DataUpdateCoordinator[list[anthropic.types.ModelInfo]]):
"""DataUpdateCoordinator which uses different intervals after successful and unsuccessful updates."""
client: anthropic.AsyncAnthropic
@@ -42,16 +56,16 @@ class AnthropicCoordinator(DataUpdateCoordinator[None]):
)
@callback
def async_set_updated_data(self, data: None) -> None:
def async_set_updated_data(self, data: list[anthropic.types.ModelInfo]) -> None:
"""Manually update data, notify listeners and update refresh interval."""
self.update_interval = UPDATE_INTERVAL_CONNECTED
super().async_set_updated_data(data)
async def async_update_data(self) -> None:
async def async_update_data(self) -> list[anthropic.types.ModelInfo]:
"""Fetch data from the API."""
try:
self.update_interval = UPDATE_INTERVAL_DISCONNECTED
await self.client.models.list(timeout=10.0)
result = await self.client.models.list(timeout=10.0)
self.update_interval = UPDATE_INTERVAL_CONNECTED
except anthropic.APITimeoutError as err:
raise TimeoutError(err.message or str(err)) from err
@@ -67,6 +81,7 @@ class AnthropicCoordinator(DataUpdateCoordinator[None]):
translation_key="api_error",
translation_placeholders={"message": err.message},
) from err
return result.data
def mark_connection_error(self) -> None:
"""Mark the connection as having an error and reschedule background check."""
@@ -76,3 +91,23 @@ class AnthropicCoordinator(DataUpdateCoordinator[None]):
self.async_update_listeners()
if self._listeners and not self.hass.is_stopping:
self._schedule_refresh()
@callback
def get_model_info(self, model_id: str) -> tuple[anthropic.types.ModelInfo, bool]:
"""Get model info for a given model ID."""
# First try: exact name match
for model in self.data or []:
if model.id == model_id:
return model, True
# Second try: match by alias
alias = model_alias(model_id)
for model in self.data or []:
if model_alias(model.id) == alias:
return model, True
# Model not found, return safe defaults
return anthropic.types.ModelInfo(
type="model",
id=model_id,
created_at=datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC),
display_name=alias,
), False

View File

@@ -1,7 +1,8 @@
"""Base entity for Anthropic."""
import base64
from collections.abc import AsyncGenerator, Callable, Iterable
from collections import deque
from collections.abc import AsyncIterator, Callable, Iterable
from dataclasses import dataclass, field
from datetime import UTC, datetime
import json
@@ -20,17 +21,22 @@ from anthropic.types import (
CitationWebSearchResultLocationParam,
CodeExecutionTool20250825Param,
CodeExecutionToolResultBlock,
CodeExecutionToolResultBlockContent,
CodeExecutionToolResultBlockParamContentParam,
Container,
ContentBlock,
ContentBlockParam,
DocumentBlockParam,
ImageBlockParam,
InputJSONDelta,
JSONOutputFormatParam,
Message,
MessageDeltaUsage,
MessageParam,
MessageStreamEvent,
ModelInfo,
OutputConfigParam,
RawContentBlockDelta,
RawContentBlockDeltaEvent,
RawContentBlockStartEvent,
RawContentBlockStopEvent,
@@ -67,18 +73,30 @@ from anthropic.types import (
WebSearchTool20250305Param,
WebSearchTool20260209Param,
WebSearchToolResultBlock,
WebSearchToolResultBlockContent,
WebSearchToolResultBlockParamContentParam,
)
from anthropic.types.bash_code_execution_tool_result_block import (
Content as BashCodeExecutionToolResultBlockContent,
)
from anthropic.types.bash_code_execution_tool_result_block_param import (
Content as BashCodeExecutionToolResultBlockParamContentParam,
)
from anthropic.types.message_create_params import MessageCreateParamsStreaming
from anthropic.types.raw_message_delta_event import Delta
from anthropic.types.text_editor_code_execution_tool_result_block import (
Content as TextEditorCodeExecutionToolResultBlockContent,
)
from anthropic.types.text_editor_code_execution_tool_result_block_param import (
Content as TextEditorCodeExecutionToolResultBlockParamContentParam,
)
from anthropic.types.tool_search_tool_result_block import (
Content as ToolSearchToolResultBlockContent,
)
from anthropic.types.tool_search_tool_result_block_param import (
Content as ToolSearchToolResultBlockParamContentParam,
)
from anthropic.types.tool_use_block import Caller
import voluptuous as vol
from voluptuous_openapi import convert
@@ -90,14 +108,13 @@ from homeassistant.helpers import device_registry as dr, llm
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import slugify
from homeassistant.util.json import JsonObjectType
from homeassistant.util.json import JsonArrayType, JsonObjectType
from .const import (
CONF_CHAT_MODEL,
CONF_CODE_EXECUTION,
CONF_MAX_TOKENS,
CONF_PROMPT_CACHING,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
@@ -112,10 +129,6 @@ from .const import (
DOMAIN,
LOGGER,
MIN_THINKING_BUDGET,
NON_ADAPTIVE_THINKING_MODELS,
NON_THINKING_MODELS,
PROGRAMMATIC_TOOL_CALLING_UNSUPPORTED_MODELS,
UNSUPPORTED_STRUCTURED_OUTPUT_MODELS,
PromptCaching,
)
from .coordinator import AnthropicConfigEntry, AnthropicCoordinator
@@ -128,10 +141,14 @@ def _format_tool(
tool: llm.Tool, custom_serializer: Callable[[Any], Any] | None
) -> ToolParam:
"""Format tool specification."""
unsupported_keys = {"oneOf", "anyOf", "allOf"}
schema = convert(tool.parameters, custom_serializer=custom_serializer)
schema = {k: v for k, v in schema.items() if k not in unsupported_keys}
return ToolParam(
name=tool.name,
description=tool.description or "",
input_schema=convert(tool.parameters, custom_serializer=custom_serializer),
input_schema=schema,
)
@@ -445,13 +462,7 @@ def _convert_content( # noqa: C901
return messages, container_id
async def _transform_stream( # noqa: C901 - This is complex, but better to have it in one place
chat_log: conversation.ChatLog,
stream: AsyncStream[MessageStreamEvent],
output_tool: str | None = None,
) -> AsyncGenerator[
conversation.AssistantContentDeltaDict | conversation.ToolResultContentDeltaDict
]:
class AnthropicDeltaStream:
"""Transform the response stream into HA format.
A typical stream of responses might look something like the following:
@@ -481,201 +492,376 @@ 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 or not hasattr(stream, "__aiter__"):
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="unexpected_stream_object"
def __init__(
self,
chat_log: conversation.ChatLog,
stream: AsyncStream[MessageStreamEvent],
output_tool: str | None = None,
) -> None:
"""Initialize the delta stream."""
self._chat_log: conversation.ChatLog = chat_log
self._stream: AsyncStream[MessageStreamEvent] = stream
self._output_tool: str | None = output_tool
self._buffer: deque[
conversation.AssistantContentDeltaDict
| conversation.ToolResultContentDeltaDict
] = deque()
self._stream_iterator: AsyncIterator[MessageStreamEvent] | None = None
self._current_tool_block: ToolUseBlockParam | ServerToolUseBlockParam | None = (
None
)
self._current_tool_args: str = ""
self._content_details = ContentDetails()
self._content_details.add_citation_detail()
self._input_usage: Usage | None = None
self._first_block: bool = True
current_tool_block: ToolUseBlockParam | ServerToolUseBlockParam | None = None
current_tool_args: str
content_details = ContentDetails()
content_details.add_citation_detail()
input_usage: Usage | None = None
first_block: bool = True
def __aiter__(
self,
) -> AsyncIterator[
conversation.AssistantContentDeltaDict | conversation.ToolResultContentDeltaDict
]:
"""Initialize the stream and return the async iterator."""
if self._stream is None or not hasattr(self._stream, "__aiter__"):
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="unexpected_stream_object"
)
if self._stream_iterator is None:
self._stream_iterator = self._stream.__aiter__()
return self
async for response in stream:
LOGGER.debug("Received response: %s", response)
async def __anext__(
self,
) -> (
conversation.AssistantContentDeltaDict | conversation.ToolResultContentDeltaDict
):
"""Get the next item from the stream."""
while True:
if self._buffer:
return self._buffer.popleft()
if isinstance(response, RawMessageStartEvent):
input_usage = response.message.usage
first_block = True
elif isinstance(response, RawContentBlockStartEvent):
if isinstance(response.content_block, ToolUseBlock):
current_tool_block = ToolUseBlockParam(
type="tool_use",
id=response.content_block.id,
name=response.content_block.name,
input=response.content_block.input or {},
)
current_tool_args = ""
if response.content_block.name == output_tool:
if first_block or content_details.has_content():
if content_details:
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {"role": "assistant"}
first_block = False
elif isinstance(response.content_block, TextBlock):
if ( # Do not start a new assistant content just for citations, concatenate consecutive blocks with citations instead.
first_block
or (
not content_details.has_citations()
and response.content_block.citations is None
and content_details.has_content()
)
):
if content_details:
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
yield {"role": "assistant"}
first_block = False
content_details.add_citation_detail()
if response.content_block.text:
content_details.citation_details[-1].length += len(
response.content_block.text
)
yield {"content": response.content_block.text}
elif isinstance(response.content_block, ThinkingBlock):
if first_block or content_details.thinking_signature:
if content_details:
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {"role": "assistant"}
first_block = False
elif isinstance(response.content_block, RedactedThinkingBlock):
LOGGER.debug(
"Some of Claudes internal reasoning has been automatically "
"encrypted for safety reasons. This doesnt affect the quality of "
"responses"
)
if first_block or content_details.redacted_thinking:
if content_details:
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {"role": "assistant"}
first_block = False
content_details.redacted_thinking = response.content_block.data
elif isinstance(response.content_block, ServerToolUseBlock):
current_tool_block = ServerToolUseBlockParam(
type="server_tool_use",
id=response.content_block.id,
name=response.content_block.name,
input=response.content_block.input or {},
)
current_tool_args = ""
elif isinstance(
response.content_block,
(
WebSearchToolResultBlock,
CodeExecutionToolResultBlock,
BashCodeExecutionToolResultBlock,
TextEditorCodeExecutionToolResultBlock,
ToolSearchToolResultBlock,
),
):
if content_details:
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {
"role": "tool_result",
"tool_call_id": response.content_block.tool_use_id,
"tool_name": response.content_block.type.removesuffix(
"_tool_result"
),
"tool_result": {
"content": cast(
JsonObjectType, response.content_block.to_dict()["content"]
)
}
if isinstance(response.content_block.content, list)
else cast(JsonObjectType, response.content_block.content.to_dict()),
response = await self._stream_iterator.__anext__() # type: ignore[union-attr]
LOGGER.debug("Received response: %s", response)
self.on_message_stream_event(response)
def on_message_stream_event(self, event: MessageStreamEvent) -> None:
"""Handle MessageStreamEvent."""
if isinstance(event, RawMessageStartEvent):
self.on_message_start_event(event.message)
return
if isinstance(event, RawContentBlockStartEvent):
self.on_content_block_start_event(event.content_block, event.index)
return
if isinstance(event, RawContentBlockDeltaEvent):
self.on_content_block_delta_event(event.delta)
return
if isinstance(event, RawContentBlockStopEvent):
self.on_content_block_stop_event(event.index)
return
if isinstance(event, RawMessageDeltaEvent):
self.on_message_delta_event(event.delta, event.usage)
return
if isinstance(event, RawMessageStopEvent):
self.on_message_stop_event()
return
LOGGER.debug("Unhandled event type: %s", event.type) # type: ignore[unreachable] # pragma: no cover - All types are handled but we want to verify that
def on_message_start_event(self, message: Message) -> None:
"""Handle RawMessageStartEvent."""
self._input_usage = message.usage
self._first_block = True
def on_content_block_start_event(
self, content_block: ContentBlock, index: int
) -> None:
"""Handle RawContentBlockStartEvent."""
if isinstance(content_block, ToolUseBlock):
self.on_tool_use_block(
content_block.id,
content_block.input,
content_block.name,
content_block.caller,
)
return
if isinstance(content_block, TextBlock):
self.on_text_block(content_block.text, content_block.citations)
return
if isinstance(content_block, ThinkingBlock):
self.on_thinking_block(content_block.thinking, content_block.signature)
return
if isinstance(content_block, RedactedThinkingBlock):
self.on_redacted_thinking_block(content_block.data)
return
if isinstance(content_block, ServerToolUseBlock):
self.on_server_tool_use_block(
content_block.id,
content_block.name,
content_block.input,
content_block.caller,
)
return
if isinstance(
content_block,
(
WebSearchToolResultBlock,
CodeExecutionToolResultBlock,
BashCodeExecutionToolResultBlock,
TextEditorCodeExecutionToolResultBlock,
ToolSearchToolResultBlock,
),
):
self.on_server_tool_result_block(
content_block.tool_use_id,
content_block.type,
content_block.content,
content_block.caller if hasattr(content_block, "caller") else None,
)
return
LOGGER.debug("Unhandled content block type: %s", content_block.type)
def on_tool_use_block(
self, id: str, input: dict[str, Any], name: str, caller: Caller | None
) -> None:
"""Handle ToolUseBlock."""
self._current_tool_block = ToolUseBlockParam(
type="tool_use",
id=id,
name=name,
input=input,
)
self._current_tool_args = ""
if name == self._output_tool:
if self._first_block or self._content_details.has_content():
if self._content_details:
self._content_details.delete_empty()
self._buffer.append({"native": self._content_details})
self._content_details = ContentDetails()
self._content_details.add_citation_detail()
self._buffer.append({"role": "assistant"})
self._first_block = False
def on_text_block(self, text: str, citations: list[TextCitation] | None) -> None:
"""Handle TextBlock."""
if ( # Do not start a new assistant content just for citations, concatenate consecutive blocks with citations instead.
self._first_block
or (
not self._content_details.has_citations()
and citations is None
and self._content_details.has_content()
)
):
if self._content_details:
self._content_details.delete_empty()
self._buffer.append({"native": self._content_details})
self._content_details = ContentDetails()
self._buffer.append({"role": "assistant"})
self._first_block = False
self._content_details.add_citation_detail()
if text:
self._content_details.citation_details[-1].length += len(text)
self._buffer.append({"content": text})
def on_thinking_block(self, thinking: str, signature: str) -> None:
"""Handle ThinkingBlock."""
if self._first_block or self._content_details.thinking_signature:
if self._content_details:
self._content_details.delete_empty()
self._buffer.append({"native": self._content_details})
self._content_details = ContentDetails()
self._content_details.add_citation_detail()
self._buffer.append({"role": "assistant"})
self._first_block = False
def on_redacted_thinking_block(self, data: str) -> None:
"""Handle RedactedThinkingBlock."""
LOGGER.debug(
"Some of Claudes internal reasoning has been automatically "
"encrypted for safety reasons. This doesnt affect the quality of "
"responses"
)
if self._first_block or self._content_details.redacted_thinking:
if self._content_details:
self._content_details.delete_empty()
self._buffer.append({"native": self._content_details})
self._content_details = ContentDetails()
self._content_details.add_citation_detail()
self._buffer.append({"role": "assistant"})
self._first_block = False
self._content_details.redacted_thinking = data
def on_server_tool_use_block(
self,
id: str,
name: Literal[
"web_search",
"web_fetch",
"code_execution",
"bash_code_execution",
"text_editor_code_execution",
"tool_search_tool_regex",
"tool_search_tool_bm25",
],
input: dict[str, Any],
caller: Caller | None,
) -> None:
"""Handle ServerToolUseBlock."""
self._current_tool_block = ServerToolUseBlockParam(
type="server_tool_use",
id=id,
name=name,
input=input,
)
self._current_tool_args = ""
def on_server_tool_result_block(
self,
tool_use_id: str,
tool_name: Literal[
"web_search_tool_result",
"code_execution_tool_result",
"bash_code_execution_tool_result",
"text_editor_code_execution_tool_result",
"tool_search_tool_result",
],
content: WebSearchToolResultBlockContent
| CodeExecutionToolResultBlockContent
| BashCodeExecutionToolResultBlockContent
| TextEditorCodeExecutionToolResultBlockContent
| ToolSearchToolResultBlockContent,
caller: Caller | None,
) -> None:
"""Handle various server tool result blocks."""
if self._content_details:
self._content_details.delete_empty()
self._buffer.append({"native": self._content_details})
self._content_details = ContentDetails()
self._content_details.add_citation_detail()
self._buffer.append(
{
"role": "tool_result",
"tool_call_id": tool_use_id,
"tool_name": tool_name.removesuffix("_tool_result"),
"tool_result": {
"content": cast(JsonArrayType, [x.to_dict() for x in content])
}
first_block = True
elif isinstance(response, RawContentBlockDeltaEvent):
if isinstance(response.delta, InputJSONDelta):
if (
current_tool_block is not None
and current_tool_block["name"] == output_tool
):
content_details.citation_details[-1].length += len(
response.delta.partial_json
)
yield {"content": response.delta.partial_json}
else:
current_tool_args += response.delta.partial_json
elif isinstance(response.delta, TextDelta):
if response.delta.text:
content_details.citation_details[-1].length += len(
response.delta.text
)
yield {"content": response.delta.text}
elif isinstance(response.delta, ThinkingDelta):
if response.delta.thinking:
yield {"thinking_content": response.delta.thinking}
elif isinstance(response.delta, SignatureDelta):
content_details.thinking_signature = response.delta.signature
elif isinstance(response.delta, CitationsDelta):
content_details.add_citation(response.delta.citation)
elif isinstance(response, RawContentBlockStopEvent):
if current_tool_block is not None:
if current_tool_block["name"] == output_tool:
current_tool_block = None
continue
tool_args = json.loads(current_tool_args) if current_tool_args else {}
current_tool_block["input"] |= tool_args
yield {
if isinstance(content, list)
else cast(JsonObjectType, content.to_dict()),
}
)
self._first_block = True
def on_content_block_delta_event(self, delta: RawContentBlockDelta) -> None:
"""Handle RawContentBlockDeltaEvent."""
if isinstance(delta, InputJSONDelta):
self.on_input_json_delta(delta.partial_json)
return
if isinstance(delta, TextDelta):
self.on_text_delta(delta.text)
return
if isinstance(delta, ThinkingDelta):
self.on_thinking_delta(delta.thinking)
return
if isinstance(delta, SignatureDelta):
self.on_signature_delta(delta.signature)
return
if isinstance(delta, CitationsDelta):
self.on_citations_delta(delta.citation)
return
LOGGER.debug("Unhandled content delta type: %s", delta.type) # type: ignore[unreachable] # pragma: no cover - All types are handled but we want to verify that
def on_input_json_delta(self, partial_json: str) -> None:
"""Handle InputJSONDelta."""
if (
self._current_tool_block is not None
and self._current_tool_block["name"] == self._output_tool
):
self._content_details.citation_details[-1].length += len(partial_json)
self._buffer.append({"content": partial_json})
else:
self._current_tool_args += partial_json
def on_text_delta(self, text: str) -> None:
"""Handle TextDelta."""
if text:
self._content_details.citation_details[-1].length += len(text)
self._buffer.append({"content": text})
def on_thinking_delta(self, thinking: str) -> None:
"""Handle ThinkingDelta."""
if thinking:
self._buffer.append({"thinking_content": thinking})
def on_signature_delta(self, signature: str) -> None:
"""Handle SignatureDelta."""
self._content_details.thinking_signature = signature
def on_citations_delta(self, citation: TextCitation) -> None:
"""Handle CitationsDelta."""
self._content_details.add_citation(citation)
def on_content_block_stop_event(self, index: int) -> None:
"""Handle RawContentBlockStopEvent."""
if self._current_tool_block is not None:
if self._current_tool_block["name"] == self._output_tool:
self._current_tool_block = None
return
tool_args = (
json.loads(self._current_tool_args) if self._current_tool_args else {}
)
self._current_tool_block["input"] |= tool_args
self._buffer.append(
{
"tool_calls": [
llm.ToolInput(
id=current_tool_block["id"],
tool_name=current_tool_block["name"],
tool_args=current_tool_block["input"],
external=current_tool_block["type"] == "server_tool_use",
id=self._current_tool_block["id"],
tool_name=self._current_tool_block["name"],
tool_args=self._current_tool_block["input"],
external=self._current_tool_block["type"]
== "server_tool_use",
)
]
}
current_tool_block = None
elif isinstance(response, RawMessageDeltaEvent):
if (usage := response.usage) is not None:
chat_log.async_trace(_create_token_stats(input_usage, usage))
content_details.container = response.delta.container
if response.delta.stop_reason == "refusal":
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="api_refusal"
)
elif isinstance(response, RawMessageStopEvent):
if content_details:
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
)
self._current_tool_block = None
def on_message_delta_event(self, delta: Delta, usage: MessageDeltaUsage) -> None:
"""Handle RawMessageDeltaEvent."""
self._chat_log.async_trace(self._create_token_stats(self._input_usage, usage))
self._content_details.container = delta.container
if delta.stop_reason == "refusal":
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="api_refusal"
)
def _create_token_stats(
input_usage: Usage | None, response_usage: MessageDeltaUsage
) -> dict[str, Any]:
"""Create token stats for conversation agent tracing."""
input_tokens = 0
cached_input_tokens = 0
if input_usage:
input_tokens = input_usage.input_tokens
cached_input_tokens = input_usage.cache_creation_input_tokens or 0
output_tokens = response_usage.output_tokens
return {
"stats": {
"input_tokens": input_tokens,
"cached_input_tokens": cached_input_tokens,
"output_tokens": output_tokens,
def on_message_stop_event(self) -> None:
"""Handle RawMessageStopEvent."""
if self._content_details:
self._content_details.delete_empty()
self._buffer.append({"native": self._content_details})
self._content_details = ContentDetails()
self._content_details.add_citation_detail()
def _create_token_stats(
self, input_usage: Usage | None, response_usage: MessageDeltaUsage
) -> dict[str, Any]:
"""Create token stats for conversation agent tracing."""
input_tokens = 0
cached_input_tokens = 0
if input_usage:
input_tokens = input_usage.input_tokens
cached_input_tokens = input_usage.cache_creation_input_tokens or 0
output_tokens = response_usage.output_tokens
return {
"stats": {
"input_tokens": input_tokens,
"cached_input_tokens": cached_input_tokens,
"output_tokens": output_tokens,
}
}
}
class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
@@ -689,24 +875,28 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
super().__init__(entry.runtime_data)
self.entry = entry
self.subentry = subentry
coordinator = entry.runtime_data
self.model_info, _ = coordinator.get_model_info(
subentry.data.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL])
)
self._attr_unique_id = subentry.subentry_id
self._attr_device_info = dr.DeviceInfo(
identifiers={(DOMAIN, subentry.subentry_id)},
name=subentry.title,
manufacturer="Anthropic",
model=subentry.data.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL]),
model=self.model_info.display_name,
model_id=self.model_info.id,
entry_type=dr.DeviceEntryType.SERVICE,
)
async def _async_handle_chat_log( # noqa: C901
async def _get_model_args( # noqa: C901
self,
chat_log: conversation.ChatLog,
structure_name: str | None = None,
structure: vol.Schema | None = None,
max_iterations: int = MAX_TOOL_ITERATIONS,
) -> None:
"""Generate an answer for the chat log."""
options = self.subentry.data
) -> tuple[MessageCreateParamsStreaming, str | None]:
"""Get the model arguments."""
options: dict[str, Any] = DEFAULT | self.subentry.data
preloaded_tools = [
"HassTurnOn",
@@ -724,21 +914,18 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
messages, container_id = _convert_content(chat_log.content[1:])
model = options.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL])
model = options[CONF_CHAT_MODEL]
model_args = MessageCreateParamsStreaming(
model=model,
messages=messages,
max_tokens=options.get(CONF_MAX_TOKENS, DEFAULT[CONF_MAX_TOKENS]),
max_tokens=options[CONF_MAX_TOKENS],
system=system.content,
stream=True,
container=container_id,
)
if (
options.get(CONF_PROMPT_CACHING, DEFAULT[CONF_PROMPT_CACHING])
== PromptCaching.PROMPT
):
if options[CONF_PROMPT_CACHING] == PromptCaching.PROMPT:
model_args["system"] = [
{
"type": "text",
@@ -746,39 +933,40 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
"cache_control": {"type": "ephemeral"},
}
]
elif (
options.get(CONF_PROMPT_CACHING, DEFAULT[CONF_PROMPT_CACHING])
== PromptCaching.AUTOMATIC
):
elif options[CONF_PROMPT_CACHING] == PromptCaching.AUTOMATIC:
model_args["cache_control"] = {"type": "ephemeral"}
if not model.startswith(tuple(NON_ADAPTIVE_THINKING_MODELS)):
thinking_effort = options.get(
CONF_THINKING_EFFORT, DEFAULT[CONF_THINKING_EFFORT]
)
if (
self.model_info.capabilities
and self.model_info.capabilities.thinking.types.adaptive.supported
):
thinking_effort = options[CONF_THINKING_EFFORT]
if thinking_effort != "none":
model_args["thinking"] = ThinkingConfigAdaptiveParam(type="adaptive")
model_args["thinking"] = ThinkingConfigAdaptiveParam(
type="adaptive", display="summarized"
)
model_args["output_config"] = OutputConfigParam(effort=thinking_effort)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
)
else:
thinking_budget = options.get(
CONF_THINKING_BUDGET, DEFAULT[CONF_THINKING_BUDGET]
)
thinking_budget = options[CONF_THINKING_BUDGET]
if (
not model.startswith(tuple(NON_THINKING_MODELS))
self.model_info.capabilities
and self.model_info.capabilities.thinking.types.enabled.supported
and thinking_budget >= MIN_THINKING_BUDGET
):
model_args["thinking"] = ThinkingConfigEnabledParam(
type="enabled", budget_tokens=thinking_budget
type="enabled", display="summarized", budget_tokens=thinking_budget
)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
if (
self.model_info.capabilities
and self.model_info.capabilities.effort.supported
):
model_args["output_config"] = OutputConfigParam(
effort=options[CONF_THINKING_EFFORT]
)
tools: list[ToolUnionParam] = []
@@ -788,11 +976,13 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
for tool in chat_log.llm_api.tools
]
if options.get(CONF_CODE_EXECUTION):
if options[CONF_CODE_EXECUTION]:
# The `web_search_20260209` tool automatically enables `code_execution_20260120` tool
if model.startswith(
tuple(PROGRAMMATIC_TOOL_CALLING_UNSUPPORTED_MODELS)
) or not options.get(CONF_WEB_SEARCH):
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options[CONF_WEB_SEARCH]
):
tools.append(
CodeExecutionTool20250825Param(
name="code_execution",
@@ -800,24 +990,26 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
),
)
if options.get(CONF_WEB_SEARCH):
if model.startswith(
tuple(PROGRAMMATIC_TOOL_CALLING_UNSUPPORTED_MODELS)
) or not options.get(CONF_CODE_EXECUTION):
if options[CONF_WEB_SEARCH]:
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options[CONF_CODE_EXECUTION]
):
web_search: WebSearchTool20250305Param | WebSearchTool20260209Param = (
WebSearchTool20250305Param(
name="web_search",
type="web_search_20250305",
max_uses=options.get(CONF_WEB_SEARCH_MAX_USES),
max_uses=options[CONF_WEB_SEARCH_MAX_USES],
)
)
else:
web_search = WebSearchTool20260209Param(
name="web_search",
type="web_search_20260209",
max_uses=options.get(CONF_WEB_SEARCH_MAX_USES),
max_uses=options[CONF_WEB_SEARCH_MAX_USES],
)
if options.get(CONF_WEB_SEARCH_USER_LOCATION):
if options[CONF_WEB_SEARCH_USER_LOCATION]:
web_search["user_location"] = {
"type": "approximate",
"city": options.get(CONF_WEB_SEARCH_CITY, ""),
@@ -841,12 +1033,17 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
]
last_message["content"].extend( # type: ignore[union-attr]
await async_prepare_files_for_prompt(
self.hass, [(a.path, a.mime_type) for a in last_content.attachments]
self.hass,
self.model_info,
[(a.path, a.mime_type) for a in last_content.attachments],
)
)
if structure and structure_name:
if not model.startswith(tuple(UNSUPPORTED_STRUCTURED_OUTPUT_MODELS)):
if (
self.model_info.capabilities
and self.model_info.capabilities.structured_outputs.supported
):
# Native structured output for those models who support it.
structure_name = None
model_args.setdefault("output_config", OutputConfigParam())[
@@ -913,10 +1110,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
preloaded_tools.append(structure_name)
if tools:
if (
options.get(CONF_TOOL_SEARCH, DEFAULT[CONF_TOOL_SEARCH])
and len(tools) > len(preloaded_tools) + 1
):
if options[CONF_TOOL_SEARCH] and len(tools) > len(preloaded_tools) + 1:
for tool in tools:
if not tool["name"].endswith(tuple(preloaded_tools)):
tool["defer_loading"] = True
@@ -929,6 +1123,19 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
model_args["tools"] = tools
return model_args, structure_name
async def _async_handle_chat_log(
self,
chat_log: conversation.ChatLog,
structure_name: str | None = None,
structure: vol.Schema | None = None,
max_iterations: int = MAX_TOOL_ITERATIONS,
) -> None:
"""Generate an answer for the chat log."""
model_args, structure_name = await self._get_model_args(
chat_log, structure_name, structure
)
coordinator = self.entry.runtime_data
client = coordinator.client
@@ -942,7 +1149,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
content
async for content in chat_log.async_add_delta_content_stream(
self.entity_id,
_transform_stream(
AnthropicDeltaStream(
chat_log,
stream,
output_tool=structure_name or None,
@@ -950,7 +1157,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
)
]
)
messages.extend(new_messages)
cast(list[MessageParam], model_args["messages"]).extend(new_messages)
except anthropic.AuthenticationError as err:
# Trigger coordinator to confirm the auth failure and trigger the reauth flow.
await coordinator.async_request_refresh()
@@ -969,7 +1176,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
) from err
except anthropic.AnthropicError as err:
# Non-connection error, mark connection as healthy
coordinator.async_set_updated_data(None)
coordinator.async_set_updated_data(coordinator.data)
LOGGER.error("Error while talking to Anthropic: %s", err)
raise HomeAssistantError(
translation_domain=DOMAIN,
@@ -982,12 +1189,12 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
) from err
if not chat_log.unresponded_tool_results:
coordinator.async_set_updated_data(None)
coordinator.async_set_updated_data(coordinator.data)
break
async def async_prepare_files_for_prompt(
hass: HomeAssistant, files: list[tuple[Path, str | None]]
hass: HomeAssistant, model_info: ModelInfo, files: list[tuple[Path, str | None]]
) -> Iterable[ImageBlockParam | DocumentBlockParam]:
"""Append files to a prompt.
@@ -1008,13 +1215,26 @@ async def async_prepare_files_for_prompt(
if mime_type is None:
mime_type = guess_file_type(file_path)[0]
if not mime_type or not mime_type.startswith(("image/", "application/pdf")):
if (
not mime_type
or not mime_type.startswith(("image/", "application/pdf"))
or not model_info.capabilities
or (
mime_type.startswith("image/")
and not model_info.capabilities.image_input.supported
)
or (
mime_type.startswith("application/pdf")
and not model_info.capabilities.pdf_input.supported
)
):
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="wrong_file_type",
translation_placeholders={
"file_path": file_path.as_posix(),
"mime_type": mime_type or "unknown",
"model": model_info.display_name,
},
)
if mime_type == "image/jpg":

View File

@@ -9,5 +9,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"quality_scale": "silver",
"requirements": ["anthropic==0.83.0"]
"requirements": ["anthropic==0.96.0"]
}

View File

@@ -81,7 +81,10 @@ rules:
status: exempt
comment: |
No entities disabled by default.
entity-translations: todo
entity-translations:
status: exempt
comment: |
Entities explicitly set `_attr_name` to `None`, so entity name translations are not used.
exception-translations: done
icon-translations: done
reconfiguration-flow: done

View File

@@ -5,6 +5,8 @@ from __future__ import annotations
from collections.abc import Iterator
from typing import TYPE_CHECKING
import anthropic
from anthropic.resources.messages.messages import DEPRECATED_MODELS
import voluptuous as vol
from homeassistant import data_entry_flow
@@ -18,8 +20,8 @@ from homeassistant.helpers.selector import (
SelectSelectorConfig,
)
from .config_flow import get_model_list
from .const import CONF_CHAT_MODEL, DEPRECATED_MODELS, DOMAIN
from .const import CONF_CHAT_MODEL, DOMAIN
from .coordinator import model_alias
if TYPE_CHECKING:
from . import AnthropicConfigEntry
@@ -61,8 +63,8 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
client = entry.runtime_data.client
model_list = [
model_option
for model_option in await get_model_list(client)
if not model_option["value"].startswith(tuple(DEPRECATED_MODELS))
for model_option in await self.get_model_list(client)
if model_option["value"] not in DEPRECATED_MODELS
]
self._model_list_cache[entry.entry_id] = model_list
@@ -104,9 +106,26 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
"model": model,
"subentry_name": subentry.title,
"subentry_type": self._format_subentry_type(subentry.subentry_type),
"retirement_date": DEPRECATED_MODELS[model],
},
)
async def get_model_list(
self, client: anthropic.AsyncAnthropic
) -> list[SelectOptionDict]:
"""Get list of available models."""
try:
models = (await client.models.list(timeout=10.0)).data
except anthropic.AnthropicError:
models = []
return [
SelectOptionDict(
label=model_info.display_name,
value=model_alias(model_info.id),
)
for model_info in models
]
def _iter_deprecated_subentries(self) -> Iterator[tuple[str, str]]:
"""Yield entry/subentry pairs that use deprecated models."""
for entry in self.hass.config_entries.async_entries(DOMAIN):
@@ -114,7 +133,7 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
continue
for subentry in entry.subentries.values():
model = subentry.data.get(CONF_CHAT_MODEL)
if model and model.startswith(tuple(DEPRECATED_MODELS)):
if model and model in DEPRECATED_MODELS:
yield entry.entry_id, subentry.subentry_id
async def _async_next_target(
@@ -141,7 +160,7 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
continue
model = subentry.data.get(CONF_CHAT_MODEL)
if not model or not model.startswith(tuple(DEPRECATED_MODELS)):
if not model or model not in DEPRECATED_MODELS:
continue
self._current_entry_id = entry_id

View File

@@ -38,6 +38,11 @@
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"entry_type": "AI task",
"error": {
"api_error": "[%key:component::anthropic::config_subentries::conversation::error::api_error%]",
"model_not_found": "[%key:component::anthropic::config_subentries::conversation::error::model_not_found%]",
"thinking_budget_too_large": "[%key:component::anthropic::config_subentries::conversation::error::thinking_budget_too_large%]"
},
"initiate_flow": {
"reconfigure": "Reconfigure AI task",
"user": "Add AI task"
@@ -46,13 +51,11 @@
"advanced": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"max_tokens": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::max_tokens%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::prompt_caching%]",
"temperature": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::temperature%]"
},
"data_description": {
"chat_model": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::chat_model%]",
"max_tokens": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::max_tokens%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::prompt_caching%]",
"temperature": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::temperature%]"
},
@@ -72,6 +75,7 @@
"model": {
"data": {
"code_execution": "[%key:component::anthropic::config_subentries::conversation::step::model::data::code_execution%]",
"max_tokens": "[%key:component::anthropic::config_subentries::conversation::step::model::data::max_tokens%]",
"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%]",
"tool_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data::tool_search%]",
@@ -81,6 +85,7 @@
},
"data_description": {
"code_execution": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::code_execution%]",
"max_tokens": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::max_tokens%]",
"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%]",
"tool_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::tool_search%]",
@@ -98,6 +103,11 @@
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"entry_type": "Conversation agent",
"error": {
"api_error": "Unable to get model info: {message}",
"model_not_found": "Model not found",
"thinking_budget_too_large": "Thinking budget must be less than the Maximum tokens."
},
"initiate_flow": {
"reconfigure": "Reconfigure conversation agent",
"user": "Add conversation agent"
@@ -106,13 +116,11 @@
"advanced": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"max_tokens": "Maximum tokens to return in response",
"prompt_caching": "Caching strategy",
"temperature": "Temperature"
},
"data_description": {
"chat_model": "The model to serve the responses.",
"max_tokens": "Limit the number of response tokens.",
"prompt_caching": "Optimize your API cost and response times based on your usage.",
"temperature": "Control the randomness of the response, trading off between creativity and coherence."
},
@@ -136,6 +144,7 @@
"model": {
"data": {
"code_execution": "Code execution",
"max_tokens": "Maximum tokens to return in response",
"thinking_budget": "Thinking budget",
"thinking_effort": "Thinking effort",
"tool_search": "Enable tool search tool",
@@ -145,6 +154,7 @@
},
"data_description": {
"code_execution": "Allow the model to execute code in a secure sandbox environment, enabling it to analyze data and perform complex calculations.",
"max_tokens": "Limit the number of response tokens.",
"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",
"tool_search": "Enable dynamic tool discovery instead of preloading all tools into the context",
@@ -195,7 +205,7 @@
"message": "`{file_path}` does not exist."
},
"wrong_file_type": {
"message": "Only images and PDF are supported by the Anthropic API, `{file_path}` ({mime_type}) is not an image file or PDF."
"message": "The {model} model does not support {mime_type} file types (for `{file_path}`)."
}
},
"issues": {
@@ -209,7 +219,7 @@
"data_description": {
"chat_model": "Select the new model to use."
},
"description": "You are updating {subentry_name} ({subentry_type}) in {entry_name}. The current model {model} is deprecated. Select a supported model to continue.",
"description": "You are updating {subentry_name} ({subentry_type}) in {entry_name}. The current model {model} is deprecated and will reach end-of-life on {retirement_date}. Select a supported model to continue.",
"title": "Update model"
}
}
@@ -231,7 +241,8 @@
"low": "[%key:common::state::low%]",
"max": "Max",
"medium": "[%key:common::state::medium%]",
"none": "None"
"none": "None",
"xhigh": "X-High"
}
}
}

View File

@@ -14,6 +14,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import SIGNAL_CONNECTED, AppleTvConfigEntry
from .entity import AppleTVEntity
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -155,7 +155,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DATA_COMPONENT] = storage_collection
collection.DictStorageCollectionWebsocket(
storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
storage_collection,
DOMAIN,
DOMAIN,
CREATE_FIELDS,
UPDATE_FIELDS,
admin_only=True,
).async_setup(hass)
websocket_api.async_register_command(hass, handle_integration_list)
@@ -341,6 +346,7 @@ async def handle_integration_list(
vol.Required("config_entry_id"): str,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def handle_config_entry(
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]

View File

@@ -28,7 +28,7 @@ class AquacellEntity(CoordinatorEntity[AquacellCoordinator]):
self._attr_unique_id = f"{softener_key}-{entity_key}"
self._attr_device_info = DeviceInfo(
name=self.softener.name,
hw_version=self.softener.fwVersion,
hw_version=self.softener.diagnostics.fw_version,
identifiers={(DOMAIN, str(softener_key))},
manufacturer=self.softener.brand,
model=self.softener.ssn,

View File

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

View File

@@ -38,39 +38,39 @@ SENSORS: tuple[SoftenerSensorEntityDescription, ...] = (
translation_key="salt_left_side_percentage",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda softener: softener.salt.leftPercent,
value_fn=lambda softener: softener.salt.left_percent,
),
SoftenerSensorEntityDescription(
key="salt_right_side_percentage",
translation_key="salt_right_side_percentage",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda softener: softener.salt.rightPercent,
value_fn=lambda softener: softener.salt.right_percent,
),
SoftenerSensorEntityDescription(
key="salt_left_side_time_remaining",
translation_key="salt_left_side_time_remaining",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.DAYS,
value_fn=lambda softener: softener.salt.leftDays,
value_fn=lambda softener: softener.salt.left_days,
),
SoftenerSensorEntityDescription(
key="salt_right_side_time_remaining",
translation_key="salt_right_side_time_remaining",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.DAYS,
value_fn=lambda softener: softener.salt.rightDays,
value_fn=lambda softener: softener.salt.right_days,
),
SoftenerSensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda softener: softener.battery,
value_fn=lambda softener: softener.diagnostics.battery,
),
SoftenerSensorEntityDescription(
key="wi_fi_strength",
translation_key="wi_fi_strength",
value_fn=lambda softener: softener.wifiLevel,
value_fn=lambda softener: softener.diagnostics.wifi_level,
device_class=SensorDeviceClass.ENUM,
options=[
"high",
@@ -82,7 +82,7 @@ SENSORS: tuple[SoftenerSensorEntityDescription, ...] = (
key="last_update",
translation_key="last_update",
device_class=SensorDeviceClass.TIMESTAMP,
value_fn=lambda softener: softener.lastUpdate,
value_fn=lambda softener: softener.diagnostics.last_update,
),
)

View File

@@ -4,8 +4,9 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from arcam.fmj import IncomingVideoAspectRatio, IncomingVideoColorspace
from arcam.fmj import IncomingVideoAspectRatio, IncomingVideoColorspace, IntOrTypeEnum
from arcam.fmj.state import IncomingAudioConfig, IncomingAudioFormat, State
from homeassistant.components.sensor import (
@@ -21,6 +22,25 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import ArcamFmjConfigEntry
from .entity import ArcamFmjEntity
_LOGGER = logging.getLogger(__name__)
def _enum_options(value: type[IntOrTypeEnum]) -> list[str]:
return [
member.name.lower() for member in value if not member.name.startswith("CODE_")
]
def _enum_value(value: IntOrTypeEnum | None) -> str | None:
if value is None:
return None
if value.name.startswith("CODE_"):
_LOGGER.debug("Undefined enum value %s ignored", value)
return None
return value.name.lower()
@dataclass(frozen=True, kw_only=True)
class ArcamFmjSensorEntityDescription(SensorEntityDescription):
@@ -75,9 +95,9 @@ SENSORS: tuple[ArcamFmjSensorEntityDescription, ...] = (
translation_key="incoming_video_aspect_ratio",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingVideoAspectRatio],
options=_enum_options(IncomingVideoAspectRatio),
value_fn=lambda state: (
vp.aspect_ratio.name.lower()
_enum_value(vp.aspect_ratio)
if (vp := state.get_incoming_video_parameters()) is not None
else None
),
@@ -87,11 +107,10 @@ SENSORS: tuple[ArcamFmjSensorEntityDescription, ...] = (
translation_key="incoming_video_colorspace",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingVideoColorspace],
options=_enum_options(IncomingVideoColorspace),
value_fn=lambda state: (
vp.colorspace.name.lower()
_enum_value(vp.colorspace)
if (vp := state.get_incoming_video_parameters()) is not None
and vp.colorspace is not None
else None
),
),
@@ -100,24 +119,16 @@ SENSORS: tuple[ArcamFmjSensorEntityDescription, ...] = (
translation_key="incoming_audio_format",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingAudioFormat],
value_fn=lambda state: (
result.name.lower()
if (result := state.get_incoming_audio_format()[0]) is not None
else None
),
options=_enum_options(IncomingAudioFormat),
value_fn=lambda state: _enum_value(state.get_incoming_audio_format()[0]),
),
ArcamFmjSensorEntityDescription(
key="incoming_audio_config",
translation_key="incoming_audio_config",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingAudioConfig],
value_fn=lambda state: (
result.name.lower()
if (result := state.get_incoming_audio_format()[1]) is not None
else None
),
options=_enum_options(IncomingAudioConfig),
value_fn=lambda state: _enum_value(state.get_incoming_audio_format()[1]),
),
ArcamFmjSensorEntityDescription(
key="incoming_audio_sample_rate",

View File

@@ -945,7 +945,10 @@ class PipelineRun:
try:
# Transcribe audio stream
stt_vad: VoiceCommandSegmenter | None = None
if self.audio_settings.is_vad_enabled:
if (
self.audio_settings.is_vad_enabled
and self.stt_provider.audio_processing.requires_external_vad
):
stt_vad = VoiceCommandSegmenter(
silence_seconds=self.audio_settings.silence_seconds
)

View File

@@ -13,11 +13,12 @@ from hassil.util import (
)
import voluptuous as vol
from homeassistant.auth.permissions.const import CAT_ENTITIES, POLICY_CONTROL
from homeassistant.components.http import StaticPathConfig
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, Unauthorized, UnknownUser
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
@@ -103,6 +104,22 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def handle_ask_question(call: ServiceCall) -> dict[str, Any]:
"""Handle a Show View service call."""
satellite_entity_id: str = call.data[ATTR_ENTITY_ID]
if call.context.user_id:
user = await hass.auth.async_get_user(call.context.user_id)
if user is None:
raise UnknownUser(
context=call.context,
permission=POLICY_CONTROL,
user_id=call.context.user_id,
)
if not user.permissions.check_entity(satellite_entity_id, POLICY_CONTROL):
raise Unauthorized(
context=call.context,
permission=POLICY_CONTROL,
user_id=call.context.user_id,
perm_category=CAT_ENTITIES,
)
satellite_entity: AssistSatelliteEntity | None = component.get_entity(
satellite_entity_id
)

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