Compare commits

...

871 Commits

Author SHA1 Message Date
J. Nick Koston 51cddb88f5 Merge remote-tracking branch 'upstream/dev' into ci-uv-managed-python
# Conflicts:
#	.github/workflows/ci.yaml
2026-05-26 18:24:40 -05:00
J. Nick Koston 0a7293dbbd Bump qingping-ble to 1.1.4 and update CGPR1 test fixtures (#172292) 2026-05-26 18:21:23 -05:00
J. Nick Koston 057788d531 Add composite action to cache CI apt installs (#171735) 2026-05-27 01:17:31 +02:00
J. Nick Koston 74cb4e2448 Bump aioesphomeapi to 45.3.1 (#172287) 2026-05-26 18:10:36 -05:00
Manu 62aa79a304 Add delete profile/header picture to mastodon.update_profile action (#170930)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-27 01:09:19 +02:00
A. Gideonse da74ae1955 Add Rated Capacity to Indevolt Gen-1 devices (#171107) 2026-05-27 01:09:02 +02:00
Maikel Punie 2a4728463b Fix swallowed exceptions in velbus action handlers (#171111) 2026-05-27 01:08:42 +02:00
Amit Finkelstein 3c5bcad0e9 Update Jewish calendar holiday at candle lighting and Havdalah (#170357) 2026-05-27 01:08:22 +02:00
Adam Katic 2388353bd2 Add diagnostics support for cert_expiry integration (#170767)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-27 01:01:54 +02:00
Erik Montnemery 98823d6816 Use select selector for input of cast uuid allow list and CEC ignore list (#171201) 2026-05-27 00:55:49 +02:00
Thomas D cdd09f2535 Remove redundant async_on_unload calls in Qbus integration (#171214) 2026-05-27 00:55:24 +02:00
Glenn Waters 2c900c59eb ElkM1 integration: add switch_output_turn_on_for action (#170128)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-27 00:51:47 +02:00
Thomas55555 68757996de Add google air quality forecast service (#171142)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-27 00:48:53 +02:00
renovate[bot] 0fa3985b1d Update infrared-protocols to 5.6.1 (#172289) 2026-05-26 23:48:18 +01:00
jameson_uk a2551647b8 Add media_player platform to Alexa Devices (#165825)
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
2026-05-27 00:43:04 +02:00
Michael e19601f991 Remove deprecated APCUPSD sensors (#172280) 2026-05-27 00:17:42 +02:00
Michael bc6060f98b Remove deprecated call_in_progress binary-sensor in VoIP (#172285) 2026-05-26 23:40:50 +02:00
A. Gideonse 0e2190fb25 Add battery cycles to Indevolt (#172286) 2026-05-26 23:40:32 +02:00
Crocmagnon dd75a39e25 data grand lyon: update quality scale (#170311) 2026-05-26 23:39:48 +02:00
Heikki Henriksen 6efb3fffa3 prusalink: extract press_button_and_verify fixture for button tests (#170332)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:29:54 +02:00
epenet 4ef409f3cd Store login_token in renault config-flow (#171707) 2026-05-26 23:29:23 +02:00
Paulus Schoutsen 0842c1cdfc Add LG TV via Serial integration (#170945)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 23:27:01 +02:00
Joost Lekkerkerker 49c045236c Enable N806 (#171388)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
2026-05-26 23:23:39 +02:00
AlCalzone 0b687df9f8 Migrate opensensemap to UI configuration (#171066)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 23:22:57 +02:00
Miko Stern ffcab49087 Improve Israel Rail departure sensor coverage (#171397)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 23:09:39 +02:00
Retha Runolfsson 06c92cd328 Add half lock for switchbot lock ultra (#168750) 2026-05-26 23:07:30 +02:00
HoffmanEl 66d4124439 Add quality scale cert expiry (#170491)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:42:38 +02:00
Copilot 99877d79e3 Replace duplicated ATTR_LOCATION with shared homeassistant.const import in hassio and remove unused ATTR_STATE mapping (#171334)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: agners <34061+agners@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
2026-05-26 22:42:02 +02:00
Thomas D 978171b600 Use reported units for the Qbus integration (#171588) 2026-05-26 22:41:45 +02:00
Jonathan Segev 4bd011702e Add room priority select entity to Lyric integration (#167942) 2026-05-26 22:40:14 +02:00
Crocmagnon 64bc689bcf add ovhcloud_ai_endpoints integration (#171402) 2026-05-26 22:38:18 +02:00
Ronald van der Meer 2f3f91ec82 Require Duco Connectivity API 2.1 for new setups (#170766) 2026-05-26 22:21:39 +02:00
Markus Adrario f6e8394771 Homeegrams (#170932)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:09:42 +02:00
Will Pike 27b0ba1a25 Bump python-ecobee-api to 0.4.0 and handle MFA in ecobee config flow (#172101)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:07:35 +02:00
Samuel Xiao 1070226acf Switchbot Cloud: Debug make_device_data function too complex issue 0521 (#171688)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:06:08 +02:00
Simone Chemelli e8d7df7770 Add history events for Alexa Devices (#170905) 2026-05-26 22:05:20 +02:00
G Johansson 31f87b3a8a Remove name from workday (#169210)
Co-authored-by: Copilot <copilot@github.com>
2026-05-26 22:00:52 +02:00
Heikki Henriksen 81efe6ddbf Bump pyprusalink to 3.0.0 (#170480)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:59:58 +02:00
Tomasz Dylewski af53865b2a Speed sensor in paj_gps (#171755)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 21:50:09 +02:00
Franck Nijhof beeb8aca4c Merge branch 'master' into dev 2026-05-26 19:46:47 +00:00
Marko Todorić b4063aaac9 Refactor SFTP Storage integration to replace duplicate constants (#171730) 2026-05-26 21:45:22 +02:00
Michael Bisbjerg 7087cb2046 Fix Loqed webhook cleanup across setup retries (#162453)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 21:43:12 +02:00
J. Nick Koston 0044c43f3a Fix flaky test_overflow_queue in history websocket tests (#171766) 2026-05-26 21:41:57 +02:00
Raphael Hehl 0bb6113bfd Migrate more UniFi Protect entities to public API (#171785)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-26 21:41:25 +02:00
Florent Thoumie b6fa89c032 iaqualink: complete test coverage, bump to silver (#168268) 2026-05-26 21:40:54 +02:00
Manu 6a18e05bda Make service response optional for Habitica integration (#171818) 2026-05-26 21:37:55 +02:00
Nolan Gilley b312bd010b bump python-join-api to 0.1.1 (#171802) 2026-05-26 21:31:49 +02:00
Karl Beecken 3487eaf8c5 Bump teltasync to 0.3.1, add strict typing (#169665)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 21:31:29 +02:00
Manu 9db7b3d012 Change selector and add translations in System Bridge send_keypress action (#171860) 2026-05-26 21:30:51 +02:00
Ingo Fischer 23ecc311fd Add BLE proxy support to matter integration (#171384)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:26:32 +02:00
Ashton 3355581bbf Add disk_life_time to hassio system health info (#171770)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:16:01 +02:00
starkillerOG 995707160f Implement entity available for battery cameras (#171838) 2026-05-26 21:14:31 +02:00
starkillerOG b3199bac88 Do not wake Reolink battery camera for privacy mode check (#171842) 2026-05-26 21:13:32 +02:00
Christian Lackas 97de25d55a homematicip_cloud: migrate simple binary sensors to entity descriptions (#171825)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 21:12:06 +02:00
yoxcu 16ef7f967e Fix automatic stop calling in continous move in onvif (#163173)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Boris Obmoroshev <bobmoroshev@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-26 20:06:54 +01:00
Christian Lackas 4864a4125e Add per-button event entities for HomematicIP key-press devices (#171065) 2026-05-26 21:06:34 +02:00
Matthew Gibson e2b71cee1f Fix exception translation placeholder mismatches in PTDevices integration (#171750) 2026-05-26 21:06:02 +02:00
Christian Lackas 5bd92d47a9 Map ViCare hvac_action to compressor phase for cooling support (#171945) 2026-05-26 21:03:27 +02:00
Michael 7f0133e2ce Remove deprecated yaml import in vivotek (#172279) 2026-05-26 21:02:49 +02:00
G Johansson ba1ed66f7a Bump holidays to 0.97 (#172088) 2026-05-26 20:20:08 +02:00
Simone Chemelli 2bc91e7a3e Filter unsupported soundbar devices for SamsungTV (#172126) 2026-05-26 20:15:43 +02:00
Mattias Arrelid 1c3a080506 Remove stale ONVIF asyncio.CancelledError workaround for anyio #374 (#172139) 2026-05-26 20:12:33 +02:00
Max Michels 5ecbfea028 Add missing exception translation key in local_file (#172271) 2026-05-26 20:06:38 +02:00
Jan-Philipp Benecke 9b67a24d92 Allow multiple headers in response in REST command (#165613) 2026-05-26 19:53:32 +02:00
Robert Resch 4bd829a6a8 Replace archived tibdex/github-app-token with actions/create-github-app-token (#172269) 2026-05-26 19:40:38 +02:00
Jordan Harvey 6feeba1f4f Add get_color service for RGB extraction from images (#167403)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 19:39:22 +02:00
Paul Bottein 71b849cb58 Add Yoto integration (#171207)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 18:35:28 +02:00
Michael Barrett ba5855f5d2 Added gift members sensor to Ghost integration (#171441) 2026-05-26 18:20:27 +02:00
Alex Romanov 4b04006302 Add test fixture for Tuya smart kettle (dft4ebatvon3ha5s) (#172260)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-26 17:57:31 +02:00
1einmal1 430e03f299 Ignore DS1420 devices in onewire (#172132) 2026-05-26 16:59:41 +02:00
J. Nick Koston 7a2422013c Bump bthome-ble to 3.23.2 and add support for light level, settings revision, and command events (#172216) 2026-05-26 16:58:18 +02:00
Ian c906dc3d0c Fix invalid schema for HassStartTimer in OpenRouter extension (#172153) 2026-05-26 16:51:11 +02:00
Matt f2fa25d449 Fix Netatmo select AttributeError when webhook schedule_id not in cache (#171914)
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
2026-05-26 16:50:17 +02:00
Yardian Support 0426f9beb6 Bump yardian to v133 (#170982)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 16:48:39 +02:00
J. Nick Koston b6f0ca13f9 Bump inkbird-ble to 1.4.4 (#172266) 2026-05-26 16:45:17 +02:00
Michael 83e8f4991c Add SecureOn password support to Wake On Lan (#172167) 2026-05-26 16:43:14 +02:00
Max Michels 3b38208e07 Remove positional message strings when translation_key is set in tesla_fleet (#172267) 2026-05-26 16:39:06 +02:00
Arsène Reymond 1a15f925a0 Add entity_picture_local on universal media player (#164872)
Co-authored-by: Copilot <copilot@github.com>
2026-05-26 16:38:34 +02:00
Vincent Knoop Pathuis 10d944eab7 Migrate landisgyr_heat_meter to ultraheat-api 0.6.0 (serialx) (#172186)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 16:37:32 +02:00
Sören 1f873927aa Add Avea device info (#171624) 2026-05-26 16:27:07 +02:00
Chrystyan A Pulido fe071ff66b Add tests for states_in_range and int_states_in_range (#164548)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 16:19:56 +02:00
Joost Lekkerkerker e4b79d4f3d Don't use async_setup in vesync tests (#172257) 2026-05-26 16:17:37 +02:00
dontinelli f6d4d0289e Fix timeout increase for longtime coordinator for solarlog (#170564)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 16:11:14 +02:00
Mattie 3089f3cc06 Bump python-qube-heatpump to 1.11.0 (#172261) 2026-05-26 16:06:57 +02:00
Max Michels e24f35473c Replace duplicate constants in kiosker with homeassistant.const imports (#172263) 2026-05-26 16:05:07 +02:00
Petro31 1da605230d Move device_tracker entity classes out of device_tracker.config_entry (#171857) 2026-05-26 16:04:34 +02:00
Joost Lekkerkerker fd572d83b7 Use async_setup_component in emulated_kasa (#172256) 2026-05-26 16:03:42 +02:00
bkobus-bbx 305d4429ec Resolve cover device class from blebox unified cover type (#171174)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 16:03:28 +02:00
Simone Chemelli b95a3f5b2d Bump aioamazondevices to 13.8.0 (#172251) 2026-05-26 16:03:07 +02:00
Joost Lekkerkerker 4e986b181b Lower update interval for zinvolt (#171851) 2026-05-26 16:02:37 +02:00
Joost Lekkerkerker 65c074af9a Add Copper water meter sensors to SmartThings (#171848) 2026-05-26 16:01:40 +02:00
Markus Tuominen 58eae0b815 Add climate platform to Ouman EH-800 (#172163) 2026-05-26 16:01:27 +02:00
Markus Tuominen c201c62b3d Add select platform to Ouman EH-800 (#170496) 2026-05-26 15:43:19 +02:00
bkobus-bbx 8b9b21c006 Add update platform to Blebox integration (#172148) 2026-05-26 15:32:32 +02:00
Duco Sebel b9c00dd82b Generate repair when predictive mode is enabled while cloud communication is disabled in HomeWizard (#171850)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-26 15:28:38 +02:00
bkobus-bbx 910b87b847 Add support for 180-degree tilt in BleBox shutter covers (#172237) 2026-05-26 15:19:43 +02:00
Markus Tuominen e37459c16b Add the number platform to the Ouman EH-800 integration (#172134) 2026-05-26 16:16:12 +03:00
Nathan Osman c347afe28d Add video_count sensor to YouTube integration (#171999) 2026-05-26 15:12:50 +02:00
bkobus-bbx c8270fcb91 Add tilt-only mode support for BleBox cover entities (#172235) 2026-05-26 15:10:26 +02:00
Joost Lekkerkerker ed399a6d14 Remove internal test for ps4 (#172258) 2026-05-26 15:07:29 +02:00
Jan Bouwhuis afa01d3d8c Improve docstring and comment in mqtt code (#172246) 2026-05-26 14:36:26 +02:00
Jan Bouwhuis ba03aaa2fa Add subentry support for MQTT date, datetime and time entity platforms (#171396) 2026-05-26 14:26:30 +02:00
Duco Sebel 33f3640f66 Add HomeWizard battery group power sensor (#172248) 2026-05-26 14:25:19 +02:00
Erik Montnemery 46fc47bcdf Add explicit tests of trigger helper extract_xxx functions (#172238) 2026-05-26 14:15:07 +02:00
Markus Tuominen 71ec3c31fa Add valve platform to Ouman EH-800 (#172149) 2026-05-26 12:22:37 +02:00
Duco Sebel 2d54070cab Add HomeWizard battery group target power sensor (#172243) 2026-05-26 11:50:51 +02:00
Duco Sebel 67e4f04f09 Add serial_number to HomeWizard device registry entries (#172233) 2026-05-26 11:25:09 +02:00
Erik Montnemery 78db1e3407 Deprecate the FlowHandler show_advanced_options property (#171754)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-26 10:25:41 +02:00
Erik Montnemery 2368a3614d Remove support for advanced mode from schema config flow (#172117)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-26 09:50:55 +02:00
Erik Montnemery 5053392cf2 Add in_zones property to mobile_app device tracker (#171814) 2026-05-26 08:51:47 +02:00
Max Michels 6ec11460ed Replace duplicate constants in bluetooth with homeassistant.const imports (#172079) 2026-05-26 08:38:23 +02:00
Paul Tarjan 975e30c048 Remove unreachable Hikvision Shelter Alarm binary sensor (#172152) 2026-05-26 08:30:23 +02:00
J. Nick Koston 7655cb0fc6 Fix blocking time_zone validation in config/core/update websocket command (#172227) 2026-05-26 08:28:34 +02:00
Åke Strandberg 7566839e9d Add missing Miele program phase codes (#172144) 2026-05-26 08:28:31 +02:00
Manu 7db5e82f58 Use non-reloading entry update methods in config flow of ntfy integration (#172222) 2026-05-26 08:25:37 +02:00
Manu 7e67c53417 Use non-reloading entry update method in config flow of PlayStation Netwwork integration (#172223) 2026-05-26 08:25:20 +02:00
Manu 89fb856302 Use non-reloading entry update method in config flow of Xbox integration (#172224) 2026-05-26 08:25:07 +02:00
Manu a2fbd2b1ea Migrate EDL21 to use SerialPortSelector (#172220) 2026-05-26 08:23:34 +02:00
Manu 231ed34133 Bump pysml to 0.1.7 (#172217) 2026-05-26 08:23:05 +02:00
Manu 6cff433b2e Remove artificial throttling of push updates in EDL21 integration (#172213) 2026-05-26 08:22:57 +02:00
Joakim Plate eca83fb7b1 Switch to async_setup in coordinator for gardena setup (#172198)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 08:22:35 +02:00
A. Gideonse 2c5adaec5c Bump indevolt-api to 1.8.2 (#172201) 2026-05-26 08:21:56 +02:00
Manu 5d75f1c33b Use non-reloading entry update method in config flow of Habitica integration (#172225) 2026-05-26 08:20:48 +02:00
Manu d628d2314e Fix wrong integration type classification of EDL21 (#172230) 2026-05-26 08:19:46 +02:00
J. Nick Koston a9547ec349 Bump inkbird-ble to 1.4.3 (#172211) 2026-05-25 22:16:07 -05:00
J. Nick Koston 2ec637df84 Bump dbus-fast to 5.0.14 (#172215) 2026-05-25 22:15:54 -05:00
J. Nick Koston 4f50ee5675 Fix ONVIF camera_address using uninitialized inner device attribute (#172219) 2026-05-25 22:15:41 -05:00
J. Nick Koston 0faf96b983 Bump onvif-zeep-async to 4.1.0 (#172212) 2026-05-25 22:15:27 -05:00
Allen Porter c3dacbc601 Bump ical to 13.2.5 (#172214) 2026-05-25 19:23:46 -07:00
renovate[bot] 2659484000 Update uv to 0.11.15 (#172208) 2026-05-25 20:18:52 -05:00
J. Nick Koston 6830ca75f5 Bump aiodiscover to 3.2.4 (#172203) 2026-05-25 20:17:54 -05:00
J. Nick Koston 38b4184dc3 Bump inkbird-ble to 1.4.0 (#172199) 2026-05-25 19:04:41 -05:00
A. Gideonse cfde7975d8 Add main MOS temp to Indevolt (#171476) 2026-05-26 00:42:59 +02:00
J. Nick Koston d7ab696a4c Bump ESPHome stable BLE version to 2026.5.1 (#172196) 2026-05-25 17:00:30 -05:00
J. Nick Koston 7f7dad7f71 Bump bluetooth-adapters to 2.3.0 (#172165) 2026-05-26 00:52:38 +03:00
J. Nick Koston 0ed21dbed7 Bump icmplib to 3.0.4 (#172189) 2026-05-25 23:15:08 +02:00
J. Nick Koston d2b37ee28b Bump aiohttp-asyncmdnsresolver to 0.2.0 (#172188) 2026-05-25 23:03:12 +02:00
J. Nick Koston b82c95e77f Bump ulid-transform to 2.2.9 (#172190) 2026-05-25 15:56:22 -05:00
J. Nick Koston baa61982a1 Bump dbus-fast to 5.0.11 (#172191) 2026-05-25 15:56:09 -05:00
Robert Svensson 8ff6de788d Local helper for Axis serial number (#172172) 2026-05-25 22:24:47 +02:00
J. Nick Koston 640f82642a Bump habluetooth to 6.7.4 (#172162) 2026-05-25 22:15:11 +02:00
Robert Svensson 64ed269f9c Bump to aiounifi v91 (#172175) 2026-05-25 22:04:46 +02:00
Bouwe Westerdijk 2b58ef96eb Refactor set HVAC mode for Plugwise (#172121) 2026-05-25 21:56:44 +02:00
J. Nick Koston 74ca79ac28 Extend INKBIRD active scan duration to cover slower broadcasters (#172171) 2026-05-25 14:55:27 -05:00
Pete Sage afb27bc165 bump soco to 0.31.1 for Sonos (#172168) 2026-05-25 22:53:32 +03:00
J. Nick Koston 0cbf27f44f Restore sensorpro sensor entity data across reloads (#172182) 2026-05-25 14:52:21 -05:00
J. Nick Koston a5ceafa544 Restore tilt_ble sensor entity data across reloads (#172184) 2026-05-25 22:51:56 +03:00
J. Nick Koston cd4d669231 Restore thermobeacon sensor entity data across reloads (#172183) 2026-05-25 22:51:03 +03:00
J. Nick Koston cc411d06b5 Restore victron_ble sensor entity data across reloads (#172185) 2026-05-25 14:50:18 -05:00
J. Nick Koston 1329f12d37 Restore aranet sensor entity data across reloads (#172173) 2026-05-25 22:50:07 +03:00
J. Nick Koston 3899f5347b Restore sensirion_ble sensor entity data across reloads (#172181) 2026-05-25 14:49:59 -05:00
J. Nick Koston cf02cfaa7c Restore rapt_ble sensor entity data across reloads (#172179) 2026-05-25 14:49:43 -05:00
J. Nick Koston e77c16ea1b Restore bluemaestro sensor entity data across reloads (#172174) 2026-05-25 14:49:26 -05:00
J. Nick Koston f1e2f94ee0 Restore moat sensor entity data across reloads (#172177) 2026-05-25 14:49:07 -05:00
J. Nick Koston 3516883b0a Restore ruuvitag_ble sensor entity data across reloads (#172180) 2026-05-25 14:48:15 -05:00
J. Nick Koston c8b70b1a38 Restore kegtron sensor entity data across reloads (#172176) 2026-05-25 14:47:55 -05:00
J. Nick Koston 946625e281 Restore mopeka sensor entity data across reloads (#172178) 2026-05-25 14:47:09 -05:00
J. Nick Koston f4b7840d5c Bump aiodhcpwatcher to 1.2.7 (#172161) 2026-05-25 22:43:14 +03:00
Michael 060f447e4a Fix swallowed exceptions in homeassistant action handlers (#170922)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 21:02:49 +02:00
Joost Lekkerkerker d5bae0a2cf Add pylint rule for checking async_setup calls in tests (#171890) 2026-05-25 20:56:44 +02:00
Abílio Costa f9bef804b1 Add infrared receiver support to ESPHome (#171789)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 19:42:34 +01:00
Erik Montnemery 6de03f4ed6 Add state attribute in_zones to BaseScannerEntity (#171832) 2026-05-25 18:28:31 +02:00
J. Nick Koston e7f3e5637f Bump aioshelly to 13.26.1 (#172160) 2026-05-25 10:31:21 -05:00
Paul Bottein 80cefc74ec Update rf-protocols to 4.0.0 (#172131) 2026-05-25 17:17:53 +02:00
J. Nick Koston 2f33b4b7f9 Bump aioharmony to 1.0.8 (#172116) 2026-05-25 17:16:47 +02:00
J. Nick Koston cf52a7a509 Bump bluetooth-adapters to 2.2.0 (#172120) 2026-05-25 10:10:54 -05:00
Mattias Arrelid f5835f849a Update anyio to 4.13.0 (#172138) 2026-05-25 09:36:53 -05:00
J. Nick Koston ec5210dca8 Bump led-ble to 1.1.11 (#172154) 2026-05-25 09:35:11 -05:00
Michael 422ea1a9b1 Bump wakeonlan to 3.3.0 (#172150) 2026-05-25 16:13:38 +02:00
Artur Pragacz b6f69f6b99 Clean up should_expose in google assistant (#171937) 2026-05-25 13:48:49 +02:00
Tom a2a3819241 Extract ProxmoxVE TOKEN_ID from full token string (#172129) 2026-05-25 12:57:05 +02:00
Erwin Douna 3ce33b0ac6 Proxmox fix duplicate const (#171352) 2026-05-25 12:56:19 +02:00
bkobus-bbx e507a97d8b Bump blebox_uniapi to v2.5.4 (#172130) 2026-05-25 12:44:25 +02:00
Erik Montnemery 5801fdad14 Add property in_zones to TrackerEntity (#171765) 2026-05-25 12:22:45 +02:00
johanzander 2f4abd6a25 growatt_server: implement dynamic-devices and stale-devices Gold rules (#166081)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-25 11:37:25 +02:00
Erik Montnemery 1c045ab715 Add negative test to WS API test test_test_condition (#171427) 2026-05-25 11:26:14 +02:00
Simone Chemelli d4ca541a96 Cleanup tests for Waze Travel Time (#172122) 2026-05-25 11:23:34 +02:00
rlrghb a07a9dc6c8 Add Lichess sensors for Ultra Bullet, Correspondence, and variant perfs (#172098)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:22:51 +02:00
Mick Vleeshouwer 6d60b3a23a Add tests for Overkiz switch platform (#171901) 2026-05-25 11:19:58 +02:00
Will Pike 37bb895b91 Bump python-ecobee-api to 0.4.0 (#172108) 2026-05-25 11:19:45 +02:00
Onero-testdev f87dc917a6 Add support for SwitchBot Weather Station (#170571)
Co-authored-by: Fan Kai <fankai@onero.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-25 11:14:42 +02:00
Marc Hörsken 71a15c188e Bump pywmspro to 0.3.5 to avoid freeze of WMS WebControl pro (#172096) 2026-05-25 11:14:15 +02:00
Simone Chemelli 003ecdb867 Handle connection error in Waze Travel Time (#172086) 2026-05-25 11:13:37 +02:00
A. Gideonse ec7e5e5a75 Switch DHCP discovery to zeroconf for Indevolt (#172093) 2026-05-25 11:13:00 +02:00
Tomer 7587f062e1 Bump victron-mqtt to 2026.5.4 (#170876) 2026-05-25 11:09:53 +02:00
J. Nick Koston 11970144e4 Bump bluetooth-auto-recovery to 1.6.4 (#172114) 2026-05-25 10:50:13 +02:00
Paulus Schoutsen 70750a6d79 Guard AppleTV will only send valid URLs to AirPlay (#172103) 2026-05-25 10:48:17 +02:00
dependabot[bot] a53437315f Bump codecov/codecov-action from 6.0.0 to 6.0.1 (#172124)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 10:41:52 +02:00
Markus Adrario 5c73ad0310 Homee: exception-translations (#171995) 2026-05-25 09:13:38 +02:00
J. Nick Koston 4a04a271ec Bump cached-ipaddress to 1.1.1 (#172110) 2026-05-25 08:40:05 +02:00
J. Nick Koston 52c27bdea5 Bump inkbird-ble to 1.2.3 (#172113) 2026-05-25 08:39:42 +02:00
J. Nick Koston 6fdc52c002 Bump dbus-fast to 5.0.9 (#172118) 2026-05-25 08:39:19 +02:00
J. Nick Koston e560bbc103 Bump aiodhcpwatcher to 1.2.6 (#172105) 2026-05-24 23:10:11 -05:00
J. Nick Koston b8c573685f Trigger active scan when picking an idasen_desk device in the config flow (#172068) 2026-05-24 23:58:11 -04:00
J. Nick Koston 3764b70b90 Bump bleak, habluetooth, and bleak-retry-connector for BlueZ backend fix (#172094) 2026-05-24 23:57:29 -04:00
Paulus Schoutsen 5d2de6f82b Prefer local file access for streaming in AppleTV (#172102) 2026-05-24 22:56:35 -04:00
fdebrus 64d17f44fa Add aquarite integration (#168051)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-25 00:48:48 +02:00
Sebastian Lövdahl 6f67d44cfe Fix swallowed exceptions in vallox action handlers (#170839) 2026-05-25 00:42:13 +02:00
Robert Svensson def3befb0e Use discovered Axis name for config entry title and device name (#171894) 2026-05-25 00:35:56 +02:00
renovate[bot] 05716ae196 Update infrared-protocols to 5.6.0 (#171916) 2026-05-25 00:29:13 +02:00
rlrghb c0a864297f Update aiolichess to 1.3.0 (#172082)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 00:26:04 +02:00
J. Nick Koston 04bb84cd03 Add AUTO bluetooth scanner mode to Shelly (#172008) 2026-05-24 14:53:54 -05:00
J. Nick Koston cb55accc3b Use latest service info for INKBIRD fallback poll recency check (#172041) 2026-05-24 14:43:58 -05:00
Erwin Douna d21c227804 SMA refactor validate input (#171956) 2026-05-24 19:59:31 +02:00
Cyrill Raccaud 1ebccd9fa2 Update cookidoo API requirement to version 0.17.2 (#171793) 2026-05-24 19:57:44 +02:00
rlrghb cfbd0f3217 Add puzzles to Lichess integration (#171987)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-24 19:55:07 +02:00
Kamil Breguła 4afb7c0997 Use explicit translation keys in WLED number entities (#171984)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-24 19:49:48 +02:00
Allen Porter 105caccc51 Add shared rainbird device lock and increase calendar timeout (#172002) 2026-05-24 19:38:07 +02:00
J. Nick Koston 6419551117 Trigger active scan when picking an inkbird device in the config flow (#172048) 2026-05-24 19:30:42 +02:00
J. Nick Koston 585bd6616a Trigger active scan when picking a switchbot device in the config flow (#172046) 2026-05-24 19:30:33 +02:00
Max Michels b8dd97cf21 Replace duplicate constants in google_generative_ai_conversation with homeassistant.const imports (#172050) 2026-05-24 19:29:24 +02:00
J. Nick Koston 68fc4aed78 Trigger active scan when picking a govee_ble device in the config flow (#172051) 2026-05-24 19:29:03 +02:00
Max Michels 7dbb259625 Replace duplicate constants in aws_s3 with homeassistant.const imports (#172055) 2026-05-24 19:27:40 +02:00
J. Nick Koston 057eac7fb6 Trigger active scan when picking a rapt_ble device in the config flow (#172054) 2026-05-24 19:27:07 +02:00
Max Michels 31c9cdf742 Replace duplicate constants in xthings_cloud with homeassistant.const imports (#172076) 2026-05-24 19:25:39 +02:00
J. Nick Koston 3147104132 Trigger active scan when picking a togrill device in the config flow (#172072) 2026-05-24 19:22:52 +02:00
Max Michels d6d0f37b52 Replace duplicate constants in clicksend_tts with homeassistant.const imports (#172058) 2026-05-24 19:21:39 +02:00
G Johansson 75e48745a8 Remove useless test from trafikverket_camera (#172059) 2026-05-24 19:21:29 +02:00
J. Nick Koston 533417778c Trigger active scan when picking a sensirion_ble device in the config flow (#172056) 2026-05-24 19:21:21 +02:00
J. Nick Koston e49fd4ebbd Trigger active scan when picking a victron_ble device in the config flow (#172057) 2026-05-24 19:21:18 +02:00
Max Michels 8412b029b1 Replace duplicate constants in cloudflare_r2 with homeassistant.const imports (#172060) 2026-05-24 19:21:08 +02:00
J. Nick Koston c65de7521f Trigger active scan when picking a tilt_ble device in the config flow (#172053) 2026-05-24 19:20:48 +02:00
J. Nick Koston 752c17917e Trigger active scan when picking a ruuvitag_ble device in the config flow (#172062) 2026-05-24 19:19:58 +02:00
Max Michels f643c7ddc6 Replace duplicate constants in intent_script with homeassistant.const imports (#172066) 2026-05-24 19:19:29 +02:00
J. Nick Koston 6f5d4cf991 Trigger active scan when picking a ld2410_ble device in the config flow (#172061) 2026-05-24 19:18:55 +02:00
Max Michels b52466fed1 Replace duplicate constants in linux_battery with homeassistant.const imports (#172070) 2026-05-24 19:18:42 +02:00
J. Nick Koston 189534e32b Trigger active scan when picking a eufylife_ble device in the config flow (#172067) 2026-05-24 19:18:30 +02:00
J. Nick Koston 684ae23b18 Trigger active scan when picking a thermopro device in the config flow (#172052) 2026-05-24 19:17:35 +02:00
J. Nick Koston f4d2f65602 Trigger active scan when picking a qingping device in the config flow (#172071) 2026-05-24 19:17:05 +02:00
J. Nick Koston 65879ff37b Trigger active scan when picking a xiaomi_ble device in the config flow (#172074) 2026-05-24 19:16:57 +02:00
J. Nick Koston d902104bee Trigger active scan when picking a keymitt_ble device in the config flow (#172075) 2026-05-24 19:16:43 +02:00
J. Nick Koston 7bad27c412 Trigger active scan when picking a snooz device in the config flow (#172073) 2026-05-24 19:16:28 +02:00
Max Michels 74a7102cf6 Replace duplicate constants in altruist with homeassistant.const imports (#172078) 2026-05-24 19:14:36 +02:00
Max Michels e88fb03388 Replace duplicate constants in husqvarna_automower with homeassistant.const imports (#172064) 2026-05-24 19:13:21 +02:00
G Johansson 92ce5ed75a Group sequential executor calls in yale_smart_alarm (#172065) 2026-05-24 18:21:27 +02:00
SeifEddineMezned 466e28eae2 worldclock: Remove name field from config flow (#169000)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-24 18:20:58 +02:00
J. Nick Koston d53a8c7df9 Bump aioshelly to 13.26.0 (#172049) 2026-05-24 17:53:17 +02:00
renovate[bot] 8fe26bdf59 Update fnv-hash-fast to 2.0.3 (#172003) 2026-05-24 10:22:02 -05:00
Max Michels f75d56c096 Remove unused duplicate constants in google_travel_time with homeassistant.const imports (#172045) 2026-05-24 17:01:45 +02:00
Max Michels 7e5942ae51 Remove unused duplicate constants in aurora_abb_powerone with homeassistant.const imports (#172047) 2026-05-24 17:01:22 +02:00
renovate[bot] f9ef9963e6 Update ulid-transform to 2.2.1 (#172004) 2026-05-24 10:00:58 -05:00
J. Nick Koston cea718528f Bump aioesphomeapi to 45.2.2 (#172043) 2026-05-24 16:57:09 +02:00
Max Michels 9c38868bbe Replace duplicate constants in victron_remote_monitoring with homeassistant.const imports (#172044) 2026-05-24 16:51:25 +02:00
J. Nick Koston 62da1c34fb Expose async_request_active_scan via the bluetooth API (#172010) 2026-05-24 10:44:49 -04:00
J. Nick Koston 2d3a3bf4fc Allow bluetooth coordinators to request an active scan cadence (#172015) 2026-05-24 10:43:52 -04:00
J. Nick Koston daa60c6d55 Bump habluetooth to 6.7.2 (#172042) 2026-05-24 09:40:31 -05:00
J. Nick Koston d45730aa02 Bump inkbird-ble to 1.2.2 (#172040) 2026-05-24 16:28:40 +02:00
Ron 05ef944766 Group sequential async_add_executor_job calls in fireservicerota (#171474) 2026-05-24 16:20:10 +02:00
J. Nick Koston a51daf48c7 Bump bluetooth-data-tools to 1.29.18 (#172017) 2026-05-24 15:35:44 +02:00
Simone Chemelli 6a789d5af7 Bump aiovodafone to 3.3.0 (#172036) 2026-05-24 15:07:47 +02:00
Glenn Waters ad4e218b69 UPB integration: bump lib to 0.7.2 (#171975) 2026-05-24 15:06:42 +02:00
Max Michels 55f576c784 Remove unused duplicate constants in tuya with homeassistant.const imports (#171971) 2026-05-24 12:12:11 +02:00
Max Michels c7c3988b11 Replace duplicate constants in color_extractor with homeassistant.const imports (#172032) 2026-05-24 12:10:36 +02:00
Max Michels ac825ca36d Replace duplicate constants in iperf3 with homeassistant.const imports (#171972) 2026-05-24 12:07:17 +02:00
Allen Porter 0b439a25e1 Bump ical to 13.2.4 (#172001) 2026-05-24 12:03:21 +02:00
Maciej Bieniek 0385e81010 Import ATTR_MODEL from homeassistant.const in BraviaTV (#171983) 2026-05-24 12:01:54 +02:00
Max Michels 699fed7a3a Replace duplicate constants in fujitsu_fglair with homeassistant.const imports (#172029) 2026-05-24 12:00:34 +02:00
Max Michels 0eefc8f327 Replace duplicate constants in drop_connect with homeassistant.const imports (#172031) 2026-05-24 11:59:32 +02:00
Max Michels 6888d203eb Replace duplicate constants in fish_audio with homeassistant.const imports (#172030) 2026-05-24 11:58:47 +02:00
Max Michels 359949adc2 Replace duplicate constants in growatt_server with homeassistant.const imports (#172027) 2026-05-24 11:34:00 +02:00
Maciej Bieniek afe7d0cbbf Bump gios to 7.1.0 (#171962) 2026-05-24 10:45:03 +02:00
Max Michels 2f7b3cb7d9 Replace duplicate constants in hegel with homeassistant.const imports (#171974) 2026-05-24 10:43:43 +02:00
Max Michels c49ed549db Replace duplicate constants in energyid with homeassistant.const imports (#171976) 2026-05-24 10:43:16 +02:00
Joakim Sørensen 3016198644 Add devcontainer-lock.json file (#171982) 2026-05-24 10:38:04 +02:00
Max Michels f0c9156cdb Replace duplicate constants in version with homeassistant.const imports (#171970) 2026-05-24 10:32:46 +02:00
Max Michels f091871aa5 Replace duplicate constants in zeroconf with homeassistant.const imports (#171968) 2026-05-24 10:31:54 +02:00
Max Michels d25207180a Replace duplicate constants in elevenlabs with homeassistant.const imports (#171977) 2026-05-24 10:31:28 +02:00
Max Michels 9ce047b9be Replace duplicate constants in tplink_omada with homeassistant.const imports (#171978) 2026-05-24 10:30:58 +02:00
Nick Kuiper 488c04fc5b Remove myself as code owner from blue_current integration (#171998) 2026-05-24 10:29:59 +02:00
Allen Porter 7598fdb8cb Fix exception translation placeholder mismatch in google_photos (#172012) 2026-05-24 10:29:28 +02:00
Penny Wood 49e22072c9 Bump python-izone to 1.2.10 (#172021) 2026-05-24 10:29:04 +02:00
J. Nick Koston c056242390 Bump habluetooth to 6.7.1 (#172000) 2026-05-24 08:52:21 +02:00
J. Nick Koston 9cbb14bbde Bump inkbird-ble to 1.1.2 (#172011) 2026-05-24 08:41:11 +02:00
Allen Porter 6634c4ce78 Replace duplicate constant ATTR_ELEVATION in fitbit (#172018) 2026-05-24 08:40:32 +02:00
Allen Porter ae1355666b Remove positional message strings from roborock exceptions (#172016) 2026-05-23 22:14:12 -07:00
Allen Porter 2d0d202b80 Fix exception translation placeholder mismatch in roborock (#172014) 2026-05-23 22:14:02 -07:00
skye-harris 9fd48344f8 Reorder device location context towards the end of the Assist LLM instructions (#165136) 2026-05-23 20:51:17 -07:00
J. Nick Koston 7b4ed59861 Change default ESPHome bluetooth proxy scanning mode to Auto (#171996) 2026-05-23 18:21:37 -05:00
J. Nick Koston fb8f82542e Use AlarmControlPanelEntityFeature from aioesphomeapi in esphome (#171961) 2026-05-23 19:08:52 -04:00
Robert Svensson af5583ba76 Axis bump to v72 (#171967) 2026-05-23 19:06:47 -04:00
J. Nick Koston 2a943369d5 Change default Bluetooth scanning mode to Auto (#171985) 2026-05-23 17:44:19 -05:00
J. Nick Koston 29425fd0ac Bump bleak-esphome to 3.9.1 (#171994) 2026-05-23 17:25:35 -05:00
Markus Adrario 271111fe75 Homee: Update quality-scale for current state. (#171981) 2026-05-23 22:17:02 +02:00
J. Nick Koston 37e9bdd36f Wire scan_interval and scan_duration into bluetooth.async_register_callback (#171806) 2026-05-23 15:34:31 -04:00
J. Nick Koston e1d1bdd377 Bump aioesphomeapi to 45.2.0 (#171986) 2026-05-23 14:34:01 -05:00
J. Nick Koston b3a60de487 Bump habluetooth to 6.5.0 (#171966) 2026-05-23 14:33:42 -05:00
Michael 0cb7ea5584 Improve switch definitions in FRITZ!Box Tools (#171862) 2026-05-23 21:19:22 +02:00
Max Michels 7bc7694e14 Replace duplicate constants in ios with homeassistant.const imports (#171973) 2026-05-23 19:40:31 +02:00
Max Michels c45c949080 Replace duplicate constants in wiz with homeassistant.const imports (#171969) 2026-05-23 19:01:03 +02:00
SeifEddineMezned ec4f64172b Fix grammar and clarity in homekit_controller/strings.json (#169625) 2026-05-23 17:52:39 +02:00
Max Michels f88b7bcdf6 Replace duplicate constants in olama with homeassistant.const imports (#171949)
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2026-05-23 17:03:44 +02:00
Max Michels 05009871aa Replace duplicate constants in numato with homeassistant.const imports (#171950) 2026-05-23 16:57:46 +02:00
Max Michels 4aa7323af2 Replace duplicate constants in nmbs with homeassistant.const imports (#171951) 2026-05-23 16:57:07 +02:00
Max Michels bcacf3a73c Remove unused duplicate constants in nice_go with homeassistant.const imports (#171952) 2026-05-23 16:56:16 +02:00
Maciej Bieniek 96a6babaef Remove Shelly temperature and humidity sensors with error (#170900) 2026-05-23 14:02:32 +02:00
Max Michels e856271a5a Replace duplicate constants in motioneye with homeassistant.const imports (#171954)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-23 12:05:02 +02:00
Max Michels add023ed74 Replace duplicate constants in openerz with homeassistant.const imports (#171946) 2026-05-23 11:58:51 +02:00
Max Michels 8d456cb24f Replace duplicate constants osoenergy with homeassistant.const imports (#171944) 2026-05-23 11:52:54 +02:00
Max Michels 5ebd95eb34 Replace duplicate constants in netatmo with homeassistant.const imports (#171953) 2026-05-23 11:47:58 +02:00
Max Michels 228d7189c3 Replace duplicate constants in profiler with homeassistant.const imports (#171943) 2026-05-23 11:01:02 +02:00
Max Michels a8e141a48a Replace duplicate constants in rainmachine with homeassistant.const imports (#171942)
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2026-05-23 10:59:53 +02:00
Max Michels d42d52a0f7 Replace duplicate constants in onkyo with homeassistant.const imports (#171947) 2026-05-23 10:57:48 +02:00
Mick Vleeshouwer cee0fe071d Fix tilt-only DynamicPergola covers in Overkiz (#171898) 2026-05-23 10:52:16 +02:00
Josh Gustafson e3593c3076 Arcam reconfig flow (#171767) 2026-05-23 10:24:28 +02:00
Martin Hjelmare 5498de07ff Remove legacy Konnected integration (#171896) 2026-05-23 10:19:35 +02:00
J. Nick Koston ac3f973d7d Bump aioesphomeapi to 45.1.0 (#171935) 2026-05-23 09:47:15 +02:00
J. Nick Koston 6b8a2a4032 Bump bleak-esphome to 3.8.1 (#171936) 2026-05-23 09:46:49 +02:00
Artur Pragacz 74e40af4bb Remove CLOUD_NEVER_EXPOSED_ENTITIES (#171933) 2026-05-23 00:26:45 -04:00
J. Nick Koston 833e15d6f2 Bump habluetooth to 6.4.0 (#171918) 2026-05-23 00:10:50 -04:00
Matt ee56fd1eb0 Fix two HEOS bugs: host set construction and missing error decorator (#171913) 2026-05-22 18:42:43 -05:00
Felipe Santos e6528bae8a Add missing translation for connection failure on OpenRGB (#171892) 2026-05-22 21:59:39 +02:00
Joost Lekkerkerker a17eb65498 Refactor labs websocket API tests to use async_setup_component (#171891) 2026-05-22 21:53:52 +02:00
Joost Lekkerkerker 912a839d66 Don't call migrate entry in generic thermostat tests directly (#171887) 2026-05-22 21:44:10 +02:00
Martin Hjelmare 4306863729 Fix homekit test_reload flaky test (#171878) 2026-05-22 14:33:27 -05:00
Martin Hjelmare ba2f66e751 Remove not needed default force_update in flo (#171854) 2026-05-22 20:15:00 +02:00
Franck Nijhof 0723d8d83f 2026.5.4 (#171859) 2026-05-22 19:26:21 +02:00
Manu 94581d8ab6 Move service registration in System Bridge integration to async_setup (#171761)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-22 18:43:22 +02:00
Ingo Fischer 7d6ec7fc58 Bump matter-python-client to 0.7.1 (#171764)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2026-05-22 17:34:20 +01:00
Jan Bouwhuis f49de3548e Add MQTT message expiry interval option (#171143) 2026-05-22 18:27:22 +02:00
Franck Nijhof 73c9edd3e8 Ran gen_requirements_all 2026-05-22 16:18:20 +00:00
Franck Nijhof 18f30bd97b Bump version to 2026.5.4 2026-05-22 16:06:32 +00:00
Manu eae6e79b61 Fix dead link in System Bridge service action (#171855) 2026-05-22 16:04:27 +00:00
Franck Nijhof 5bb42801d9 Fix Hue device trigger crash for devices removed from bridge (#171844) 2026-05-22 16:04:25 +00:00
Franck Nijhof 98271265d3 Fix OpenHome config flow crash when UDN is a list (#171841) 2026-05-22 16:04:23 +00:00
Franck Nijhof 92d20477bc Register Insteon modem device before platform setup (#171839) 2026-05-22 16:04:21 +00:00
Franck Nijhof 9352a0057e Fix invalid MDI icon references (#171831)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 16:04:19 +00:00
Franck Nijhof 5fb874277a Fix Lutron Caseta battery sensor crash on unsupported devices (#171829) 2026-05-22 16:04:17 +00:00
Franck Nijhof d65f605398 Fix ZBT-2 hardware page crash when entry data is missing VID (#171828) 2026-05-22 16:04:16 +00:00
Simone Chemelli 7e5b448f70 Add missing exception translation keys in alexa_devices (#171749) 2026-05-22 16:04:14 +00:00
Simone Chemelli ef5da5ef36 Fix exception translation placeholder mismatches in comelit (#171748) 2026-05-22 16:04:11 +00:00
epenet 410f00c4ed Bump renault-api to 0.5.10 (#171692) 2026-05-22 15:58:41 +00:00
Kamil Breguła 33c205dc04 Bump wled to 0.23.0 and remove backoff exception (#171622) 2026-05-22 15:58:39 +00:00
dontinelli 267b3e279d Fix update error message key in solarlog (#171611) 2026-05-22 15:58:37 +00:00
Maciej Bieniek 9c1cd8093d Fix media_image_hash and validate the MIME type in the Shelly media player (#171585) 2026-05-22 15:58:35 +00:00
Josef Zweck 201c0c2470 Fix string ref for tedee (#171548) 2026-05-22 15:58:33 +00:00
Franck Nijhof 281d6e0e8b Fix Wyoming satellite crash when TTS is not configured (#171513) 2026-05-22 15:58:31 +00:00
Franck Nijhof 88746534a4 Fix PowerView cover crash when shade position is unavailable (#171471) 2026-05-22 15:58:29 +00:00
Franck Nijhof 135f91c3c5 Fix habitica ignoring zero values for interval and streak (#171468) 2026-05-22 15:58:27 +00:00
Franck Nijhof 49d8dc88d9 Fix SmartThings crash when timestamp attribute is None (#171467) 2026-05-22 15:58:25 +00:00
epenet a7a2c1eb02 Bump renault-api to 0.5.9 (#171428) 2026-05-22 15:58:23 +00:00
J. Nick Koston 6596f956d2 Bump aiodns to 4.0.4 (#171420)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-22 15:57:27 +00:00
TheJulianJES 9d8859833b Fix ZHA blocking minor version downgrades (#171319) 2026-05-22 15:48:41 +00:00
Aidan Timson 65a4c10660 Bump aiolyric to 2.1.1, Update OAuth URL for lyric (#171181) 2026-05-22 15:48:39 +00:00
Åke Strandberg 1737b50558 Add missing Miele Dishwasher codes (#171175) 2026-05-22 15:48:37 +00:00
Luke Lashley 614c7006f6 Bump python-roborock to 5.12.0 (#171112)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-05-22 15:46:06 +00:00
Jonathan Segev 8c901cc405 Bump aiolyric to 2.1.0 (#171007)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-22 15:46:04 +00:00
Franck Nijhof 5d0fdfd38b Apply web search citation stripping for GPT-5.x models in OpenAI conversation (#170956) 2026-05-22 15:46:02 +00:00
Manu 49ab42d3a2 Fix dead link in System Bridge service action (#171855) 2026-05-22 17:00:30 +02:00
Franck Nijhof 383f6142f0 Fix ZBT-2 hardware page crash when entry data is missing VID (#171828) 2026-05-22 16:58:01 +02:00
Kamil Breguła 2f120cf604 Fix rgb_color passed as RGBColor NamedTuple instead of plain tuple to light entity turn_on (#171795)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 16:56:34 +02:00
Franck Nijhof 37288849b3 Register Insteon modem device before platform setup (#171839) 2026-05-22 10:23:47 -04:00
zhangluofeng aa8659f507 Add xthings cloud lock (#171176)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-22 15:54:37 +02:00
DeerMaximum 40c0d79d1d Replaced duplicate constant with homeassistant.const in NINA (#171852) 2026-05-22 15:41:04 +02:00
Franck Nijhof bef8632d78 Fix OpenHome config flow crash when UDN is a list (#171841) 2026-05-22 15:23:42 +02:00
Duco Sebel f00decfaa3 Use uptime device class for HomeWizard uptime sensor (#171830) 2026-05-22 15:23:09 +02:00
Manu 42e7add026 Add selector options translations to System Bridge integration (#171771) 2026-05-22 15:22:22 +02:00
Franck Nijhof 263aa3f16e Fix Hue device trigger crash for devices removed from bridge (#171844) 2026-05-22 15:18:00 +02:00
mhuiskes 03b364dcf0 Refactor zeversolar tests: use fixtures, patch at use site, add unique_id (#171697) 2026-05-22 14:58:56 +02:00
Duco Sebel 3b1aaf39af Bumb python homewizard energy 10.1.0 (#171826) 2026-05-22 14:51:58 +02:00
Franck Nijhof b82ba43fa4 Add pylint checker for invalid MDI icon references (#171824)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 13:45:40 +02:00
starkillerOG d81ef5593c Bump reolink_aio to 0.20.0: Reolink battery camera support (#171836) 2026-05-22 12:59:33 +02:00
Manu 5c5e50f024 Fix platform unloading in System Bridge integration (#171822) 2026-05-22 12:56:03 +02:00
Lex Postma e796d9c467 Update strings.json to align with HomeWizard app (#171740)
Co-authored-by: Duco Sebel <74970928+DCSBL@users.noreply.github.com>
2026-05-22 12:54:58 +02:00
Karl Beecken 342f23526f Remove empty requirements_test_all.txt (#171530 follow-up) (#171834) 2026-05-22 12:38:58 +02:00
Erik Montnemery 814ec697cf Remove advanced mode from hue service actions (#171442) 2026-05-22 11:45:33 +02:00
Erik Montnemery 120f1446d4 Rename advanced section to additional options in telegram_bot service actions (#171460) 2026-05-22 11:44:05 +02:00
Franck Nijhof 170af75b7d Fix Lutron Caseta battery sensor crash on unsupported devices (#171829) 2026-05-22 11:37:05 +02:00
Ariel Ebersberger 5432d29489 Use is/is not for same-enum identity comparisons (tests) (#171689) 2026-05-22 11:32:27 +02:00
Franck Nijhof 8098f4f6bc Fix invalid MDI icon references (#171831)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 11:25:28 +02:00
Simone Chemelli 6a70077687 Fix exception translation placeholder mismatches in comelit (#171748) 2026-05-22 11:17:17 +02:00
Max Michels 5dbb0464ba Replace duplicate constants with homeassistant.const imports (#171815) 2026-05-22 11:10:21 +02:00
dependabot[bot] 1df165ea02 Bump j178/prek-action from 2.0.3 to 2.0.4 (#171812)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 10:39:43 +02:00
Manu 62542eb911 Replace duplicate constants with homeassistant.const imports in xiaomi_miio (#171823) 2026-05-22 10:39:09 +02:00
Max Michels a842cac34c Replace duplicate constants with homeassistant.const imports (#171817) 2026-05-22 10:38:06 +02:00
Simone Chemelli 2460f688e3 Add missing exception translation keys in alexa_devices (#171749) 2026-05-22 10:34:00 +02:00
Simone Chemelli a868ea443c Fix hardcoded exception strings in uptimerobot (#171744) 2026-05-22 10:33:07 +02:00
Franck Nijhof 1d8565483b Apply web search citation stripping for GPT-5.x models in OpenAI conversation (#170956) 2026-05-22 10:31:10 +02:00
dependabot[bot] 1ef3301253 Bump github/codeql-action from 4.35.4 to 4.35.5 (#171813)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 09:47:16 +02:00
Manu 525952f016 Add entity translations to System Bridge integration (#171807) 2026-05-22 09:00:54 +02:00
Shay Levy 3257275c5a Fix LG webOS TV hardcoded exception strings (#171777) 2026-05-22 08:28:19 +02:00
Max Michels cb54fd4921 Replace duplicate constants with homeassistant.const imports (#171809) 2026-05-22 07:57:08 +02:00
Max Michels b391fc61ea Replace duplicate constants with homeassistant.const imports (#171808) 2026-05-22 07:56:29 +02:00
J. Nick Koston fcd4e4939c Bump habluetooth to 6.2.0 (#171800) 2026-05-21 23:08:17 -05:00
J. Nick Koston deb8b5da05 Parallelize pytest --collect-only in split_tests.py (#171772) 2026-05-21 22:58:01 -04:00
g4bri3lDev c7754a6ce9 Bump py-opendisplay to 7.2.3 (#171775) 2026-05-21 22:52:36 -04:00
J. Nick Koston 242724bd50 Bump aiodiscover to 3.2.3 (#171803) 2026-05-21 22:51:54 -04:00
Max Michels 42454563db Replace duplicate constants with homeassistant.const imports (#171790)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-21 22:51:34 -04:00
J. Nick Koston bf03d0c216 Bump dbus-fast to 5.0.3 (#171595) 2026-05-21 21:11:35 -05:00
Max Michels 568107e06b Replace duplicate constants with homeassistant.const imports (#171784) 2026-05-22 01:33:48 +03:00
Jens Timmerman 7da44428b6 Bump guntamatic to v1.9.0 (#171631) 2026-05-21 22:55:29 +01:00
Max Michels 0a27f31949 Replace duplicate constants with homeassistant.const imports (#171781) 2026-05-21 22:53:07 +01:00
Erwin Douna 905b868c82 Add recreate services to Portainer (#167225)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2026-05-21 22:52:07 +01:00
Max Michels 3187289913 Replace duplicate constants with homeassistant.const imports (#171776) 2026-05-22 00:18:54 +03:00
Max Michels 87cecd4a44 Replace duplicate constants with homeassistant.const imports (#171778) 2026-05-22 00:18:23 +03:00
Robert Svensson fed38b0e38 Replace duplicate ATTR_LOCKED constant with homeassistant.const import in deconz (#171779) 2026-05-22 00:17:22 +03:00
Raphael Hehl 6a36d1260b Bump uiprotect to 10.5.0 (#171768)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-21 15:42:31 -05:00
Raphael Hehl 49fc1b413d Bump pydantic to 2.13.4 (#171763)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-21 14:42:06 -05:00
J. Nick Koston 748a9842af Merge branch 'ci-cache-postgres-mariadb-deps' into ci-uv-managed-python 2026-05-21 14:33:44 -05:00
J. Nick Koston 55786dbdfc Use dpkg-architecture to derive multiarch lib path
So the ldconfig workaround also works on non-x86_64 runners.
2026-05-21 14:32:58 -05:00
Abílio Costa bffb0417cc Instruct agents to run prek after doing changes (#171757) 2026-05-21 20:16:26 +01:00
G Johansson 8b8c687fc3 Remove not needed exception handling in dnsip (#171758) 2026-05-21 20:58:32 +02:00
J. Nick Koston e88c03a437 Merge branch 'dev' into ci-cache-postgres-mariadb-deps 2026-05-21 13:37:03 -05:00
Lukas e3dd6b5fc5 Fix hardcoded exception strings in pooldose integration (#171652)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 20:36:17 +02:00
J. Nick Koston dbc0dc1ea6 Install Python eagerly via setup-uv-python composite
setup-uv only sets UV_PYTHON, it does not actually fetch the
interpreter; uv installs it lazily on the first 'uv venv' or
'uv pip install'. When the venv cache hits, no uv command runs, so
the cached venv's bin/python3 symlink points at an interpreter that
was never installed in this job and the next step that activates the
venv aborts with 'broken symlink'.

Extract setup-uv plus an explicit 'uv python install' into the
.github/actions/setup-uv-python composite action so every job that
restores the venv ends up with a real Python at the expected path.
Enable cache-python in the wrapper so subsequent jobs reuse astral's
download instead of refetching it.
2026-05-21 13:16:39 -05:00
J. Nick Koston 31271876bf Pin uv version in every setup-uv call to skip manifest fetch
Mirrors esphome/esphome#16534. Without an explicit version, setup-uv
fetches uv.ndjson from raw.githubusercontent.com on every cache miss,
which periodically times out and fails the job. Expose the uv version
from requirements.txt via the info job and pass it to every setup-uv
call. Also set ignore-nothing-to-cache: true so jobs that do not touch
uv (e.g. gen-copilot-instructions) no longer fail on the post-step
cache save.
2026-05-21 13:08:57 -05:00
J. Nick Koston d5c31332b5 Switch CI to astral-managed Python via setup-uv
Replace actions/setup-python with astral-sh/setup-uv so every job uses
the python-build-standalone interpreter astral ships, which bakes in
the PEP 744 tail call interpreter on 3.14. setup-uv handles both
installing uv and provisioning the requested Python version, so the
venv bootstrap uses 'uv venv' instead of 'python -m venv' and there is
no longer a separate uv install step on cache miss.

Bumps CACHE_VERSION so the old setup-python venv caches are invalidated;
the venv symlinks would otherwise point at the absent hostedtoolcache
interpreter.
2026-05-21 13:05:30 -05:00
J. Nick Koston 3f0c93c26c Merge branch 'dev' into ci-cache-postgres-mariadb-deps 2026-05-21 12:48:19 -05:00
Ariel Ebersberger 94d620438b Use is/is not for same-enum identity comparisons (source) (#171591) 2026-05-21 19:30:55 +02:00
Erik Montnemery 8867b792dc Remove use of advanced mode from the zha integration (#171753) 2026-05-21 19:26:24 +02:00
G Johansson 97967abfeb Fix missing string in smhi (#171756) 2026-05-21 19:25:03 +02:00
mhuiskes af8fea272d Declare Bronze quality scale for Zeversolar integration (#170410) 2026-05-21 19:12:54 +02:00
Simone Chemelli 2db0eed570 Fix hardcoded exception strings in samsungtv (#171745) 2026-05-21 18:59:37 +02:00
Erwin Douna ded1628c20 Downloader add missing data description (#171727) 2026-05-21 18:59:24 +02:00
Petro31 a02e54f332 Update documentation link to point to each domain/platform (#171734)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 18:47:39 +02:00
Erik Montnemery 1858649bc7 Improve tests of trigger variables (#171742) 2026-05-21 17:55:41 +02:00
Leonardo Merza 109e09c3ec Add fan minimum on time number entity to ecobee (#171419)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 17:53:02 +02:00
Manu ad139b259b Add notify entity to System Bridge integration (#171736)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 17:49:25 +02:00
J. Nick Koston 07ed913ba2 Extract apt caching into composite action with alternatives workaround
Wrap awalsh128/cache-apt-pkgs-action in .github/actions/cache-apt-packages
so every job uses the same pattern, and route /usr/lib/x86_64-linux-gnu
subdirectories through ldconfig. The upstream action does not run postinst
on cache restore so update-alternatives symlinks (libblas, liblapack via
ffmpeg) never appear; adding the subdirs to ld.so.conf.d lets the linker
find the real libraries without those symlinks.
2026-05-21 10:45:13 -05:00
Scott Giminiani a1a76874fd Fix name of config flow form field (#171741) 2026-05-21 17:33:53 +02:00
Ariel Ebersberger e7bd56325b Use is for IntentResponseType identity check in conversation (#171699)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arturpragacz <49985303+arturpragacz@users.noreply.github.com>
2026-05-21 17:31:14 +02:00
J. Nick Koston ef2ef0c8ba Bump zeroconf to 0.149.16 (#171737) 2026-05-21 17:28:26 +02:00
Max Michels a8381e923a Replace duplicate constants with homeassistant.const imports (#171675) 2026-05-21 17:13:31 +02:00
Max Michels b7adba559b Replace duplicate constants with homeassistant.const imports (#171677) 2026-05-21 17:13:20 +02:00
Matthias Alphart 5cf6dceb04 Normalize empty string to None in knx config flow (#171693) 2026-05-21 17:13:11 +02:00
J. Nick Koston b7905b163f Run ldconfig after cache-apt-pkgs-action restore
The action restores cached .deb files to disk but skips dpkg-trigger so
/etc/ld.so.cache stays stale and ctypes-based loaders (eg opuslib)
cannot find libopus.so.0. Add an explicit ldconfig step after each
action call.
2026-05-21 10:02:39 -05:00
Paul Bottein 975bcc5431 Reorganize Freebox entity categories (#171480) 2026-05-21 16:55:27 +02:00
Michael Barrett f24a44e81f Update aioghost to 0.4.16 (#171690) 2026-05-21 16:53:45 +02:00
Phil-Rad 43c91843cd Remove unreachable import config flow path from cert_expiry (#171733) 2026-05-21 16:50:49 +02:00
Chris dbce1d328a Bump python-openevse-http to 0.3.4 (#171621) 2026-05-21 16:46:54 +02:00
J. Nick Koston c712b07da3 Switch CI apt caching to awalsh128/cache-apt-pkgs-action 2026-05-21 09:42:20 -05:00
Petro31 d294b04b79 Add EntityComponent to device_tracker (#171507) 2026-05-21 16:10:20 +02:00
Markus Tuominen 8b0e9060b3 Set _attr_has_entity_name on tplink_omada OmadaClientScannerEntity (#171680) 2026-05-21 16:41:37 +03:00
MoonDevLT 39066b6e3a Fix missing exceptions translation key missing_device_info in lunatone (#171569) 2026-05-21 14:59:48 +02:00
Max Michels a23a9b350b Replace duplicate constants with homeassistant.const imports (#171701) 2026-05-21 14:57:58 +02:00
chiro79 fdaa807ca8 Switch to aiopvpc-ng (#171025) 2026-05-21 14:54:23 +02:00
A. Gideonse f290dcc03f Update Indevolt integration quality scale to platinum (#170320) 2026-05-21 14:53:06 +02:00
Markus Tuominen 654408cc76 Set _attr_has_entity_name on sonos SonosFavoritesEntity (#171678) 2026-05-21 13:42:21 +02:00
Max Michels 1f814faad8 Replace duplicate constants with homeassistant.const imports (#171702) 2026-05-21 13:36:14 +02:00
Markus Tuominen 6e00eecfcd Set _attr_has_entity_name on lunatone LunatoneLineBroadcastLight (#171682) 2026-05-21 13:19:42 +02:00
Robert Resch 8c8620c511 Add check requirements yanked and CVE check (#171641)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-21 12:54:15 +02:00
Wendelin cca8825ca5 Add comment optional attribute to automation items (#171091) 2026-05-21 12:52:54 +02:00
Max Michels 92fbcc29a5 Replace duplicate constants with homeassistant.const imports (#171700) 2026-05-21 12:51:19 +02:00
Shay Levy 1c28833f39 Fix LG WebOS TV translation placeholders mismatches (#171696) 2026-05-21 13:33:36 +03:00
Christian Lackas cfdef77222 homematicip_cloud: migrate entity names to has_entity_name (#169273) 2026-05-21 12:29:43 +02:00
epenet 49720475da Bump renault-api to 0.5.10 (#171692) 2026-05-21 12:16:29 +02:00
Markus Tuominen 7967b84cc6 Set _attr_has_entity_name on omie OMIEPriceSensor (#171671) 2026-05-21 12:14:03 +02:00
Markus Tuominen c715557813 Set _attr_has_entity_name on smartthings SmartThingsScene (#171672) 2026-05-21 12:10:06 +02:00
Markus Tuominen 79e5330782 Set _attr_has_entity_name on ekeybionyx EkeyEvent (#171668) 2026-05-21 12:03:01 +02:00
Max Michels 5210ca64b1 Replace duplicate constants with homeassistant.const imports (#171669) 2026-05-21 12:02:12 +02:00
Markus Tuominen 65283e3d77 Set _attr_has_entity_name on fitbit battery sensors (#171670) 2026-05-21 12:01:27 +02:00
mhuiskes 427cb9f8db Remove unnecessary intermediate variables in zeversolar diagnostics (#171691) 2026-05-21 11:55:34 +02:00
Erik Montnemery a09e042d42 Add test of FlowHandler show_advanced_options property (#171681) 2026-05-21 11:47:42 +02:00
Shay Levy 072e9b51a2 Fix Shelly translation placeholders mismatches (#171685) 2026-05-21 11:47:20 +02:00
Erik Montnemery b96342c4f3 Remove use of advanced mode from the knx integration (#171674) 2026-05-21 11:26:22 +02:00
Erik Montnemery 56eae8c808 Fix min value for music_assistant.get_library offset (#171664) 2026-05-21 10:25:08 +02:00
Erik Montnemery 9fbdf86104 Rename advanced options section to additional options in opendisplay service actions (#171452) 2026-05-21 10:18:31 +02:00
Jan Bouwhuis 8ff5da59c4 Fix hardcoded exception strings in incomfort (#171616) 2026-05-21 10:09:04 +02:00
Andres Ruiz 298f4f8ed0 Remove National Grid US virtual integration (#171204) 2026-05-21 09:53:59 +02:00
Willem-Jan van Rootselaar 6fdc0bb90b Fix bsblan set data error translation (#171529) 2026-05-21 09:51:54 +02:00
Mick Vleeshouwer 94c3ad2cb2 Bump pyOverkiz to 1.20.4 (#171626) 2026-05-21 09:50:54 +02:00
Martin Hjelmare d83d44648c Fix Home Connect exception translation placeholder mismatch (#171655) 2026-05-21 09:38:22 +02:00
Erik Montnemery 279b614b7c Remove advanced mode from music_assistant service actions (#171451) 2026-05-21 09:35:48 +02:00
Erik Montnemery 244dfe014a Remove advanced mode from mqtt service actions (#171448)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 09:33:10 +02:00
Markus Tuominen 6b379e50cf Add has-entity-name pylint quality scale checker (#171486) 2026-05-21 10:21:06 +03:00
epenet 1368cd15da Remove myself from samsungtv code-owners (#171654) 2026-05-21 08:54:59 +02:00
Franck Nijhof 8c8cc3acb9 Fix habitica ignoring zero values for interval and streak (#171468) 2026-05-21 08:06:08 +02:00
Franck Nijhof b0634bea35 Fix SmartThings crash when timestamp attribute is None (#171467) 2026-05-21 08:05:42 +02:00
Raphael Hehl 5ae31cad6f Fix unifiprotect exception translations (#171510) (#171619)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 08:04:21 +02:00
Brandon Rothweiler b45aaaa177 Update py-aosmith to 1.0.18 (#171647) 2026-05-21 07:42:07 +02:00
Jan-Philipp Benecke 6560496440 Add missing WebDAV exception translation (#171614) 2026-05-20 20:46:31 -04:00
Erwin Douna 489dda8efb SMA refactor to new pylint (#171630) 2026-05-20 20:45:39 -04:00
Alexey Masolov 30c942d139 Catch requests.Timeout and apply TIMEOUT constant across CalDAV integration (#171632)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 20:45:08 -04:00
On Freund c735e47e23 Bump pyrisco to 0.7.0 (#171644) 2026-05-20 20:42:29 -04:00
Robert Resch 3856405c72 Add aw check requirements async block check (#171642) 2026-05-21 01:28:32 +02:00
Robert Resch 323479ca44 Fix aw check requirements safe output (#171643) 2026-05-21 01:16:25 +02:00
Raphael Hehl c8bfe56975 Fix hardcoded exception strings in unifi_access (#171629)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-21 00:37:08 +02:00
A. Gideonse ab214b64f2 Implement final Indevolt exceptions translations (#171635) 2026-05-21 00:35:01 +02:00
Max Michels fea673d93a Replace duplicate constants with homeassistant.const imports (#171639) 2026-05-21 00:24:05 +02:00
Max Michels 5405151112 Replace duplicate constants with homeassistant.const imports (#171637) 2026-05-21 00:12:23 +02:00
Max Michels b3c210ef24 Replace duplicate constants with homeassistant.const imports (#171638) 2026-05-21 00:11:59 +02:00
Robert Resch 5f5d74cfbd Remove requirements_test_all file (#171530) 2026-05-20 23:54:31 +02:00
Josh Gustafson c188fdcc8b Clean up arcam_fmj config flow (#171161)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:58:10 +02:00
Michael Hansen a3b43fc19b Handle multiple intents in Wyoming conversation (#171615)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-05-20 16:48:56 -04:00
Maciej Bieniek 894a68acb6 Fix media_image_hash and validate the MIME type in the Shelly media player (#171585) 2026-05-20 22:25:30 +02:00
Kamil Breguła 30bc3fc412 Bump wled to 0.23.0 and remove backoff exception (#171622) 2026-05-20 22:16:43 +02:00
Michael 3cc0cc38ab Add missing translation placeholders for SMA exceptions (#171625) 2026-05-20 21:31:44 +02:00
Michael 296caa90c1 Fix exception strings in FRITZ!Box tools (#171603) 2026-05-20 20:55:42 +02:00
A. Gideonse bb4c211fb6 Add DHCP discovery to Indevolt (#169597)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 20:53:23 +02:00
Nick Haghiri d4fa904386 Add invalid_auth exception translation key to backblaze_b2 (#171584)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 20:51:44 +02:00
Erik Montnemery db98f0b434 Remove advanced mode from homeassistant service actions (#171440)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 20:40:36 +02:00
Erwin Douna 7341ac91ee SMA add missing exceptions (#171550) 2026-05-20 20:32:19 +02:00
Jan Bouwhuis b2fb5df0fb Remove positional message strings when translation_key is set in mqtt (#171617) 2026-05-20 20:16:41 +02:00
Manu 265485a7d0 Fix positional message strings in exceptions in Notify for Android TV / Fire TV integration (#171581) 2026-05-20 20:00:30 +02:00
J. Nick Koston bf1b93fb66 Bump aioesphomeapi to 45.0.4 (#171601) 2026-05-20 12:54:08 -05:00
dontinelli be9d4bedfd Fix update error message key in solarlog (#171611) 2026-05-20 19:53:19 +02:00
Franck Nijhof e8ac982e83 Add pylint checker for exception translation validation (#171453)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 19:45:56 +02:00
Abílio Costa 6c8e5a8e98 Add common availability test helper for IR/RF integrations (#171610)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 13:05:37 -04:00
Paul Bottein e40a3e18db Send entity domain in template config flow preview (#171599) 2026-05-20 13:00:23 -04:00
Robert Resch cba05caadd Fix aw generation (#171609) 2026-05-20 12:56:57 -04:00
Denis Shulyaka ef3bc61e2b Remove stale temperature key from anthropic strings (#171612) 2026-05-20 12:56:33 -04:00
shbatm 3eff36eb9d Use CONF_CODE from homeassistant.const in isy994 (#171608)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 18:48:35 +02:00
Michael 8402a4d876 Fix hardcoded exception strings in tankerkoenig (#171607) 2026-05-20 18:24:32 +02:00
shbatm 6159516dc0 Bump pyisy to 3.6.1 and modernize TLS handling for isy994 (#170136)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 18:12:11 +02:00
Åke Strandberg 8fd3dcc7b1 Fix translation placeholder for Miele fan errors (#171592) 2026-05-20 18:07:24 +02:00
Robert Resch b724e52408 Reduce token usage by moving deterministic checks to python (#171466)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 17:59:20 +02:00
Abílio Costa 1654f7b0f7 Move duplicated infrared state tracking to common class (#170906) 2026-05-20 11:53:02 -04:00
Luke Lashley c8b23d52ba Bump python-roborock to 5.12.0 (#171112)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-05-20 17:48:42 +02:00
Aidan Timson 75d2babe65 Fix swallowed exceptions in lyric climate action handlers (#171356)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 17:47:13 +02:00
Jens Timmerman 39ad57acfd Bump guntamatic to v1.8.0 (#171593) 2026-05-20 17:45:47 +02:00
TheJulianJES 162729a176 Fix ZHA blocking minor version downgrades (#171319) 2026-05-20 17:45:16 +02:00
Erik Montnemery 376d94e7e1 Remove advanced mode from scene service actions (#171454) 2026-05-20 17:38:12 +02:00
Chris c3223b29a4 fix: handle and translate OpenEVSE charger exceptions in number entities (#171368) 2026-05-20 17:36:35 +02:00
Erik Montnemery 073ee88a64 Remove advanced mode from motioneye service actions (#171446) 2026-05-20 17:35:52 +02:00
Manu ef8b4f2d7f Fix reminder time calculation to use timezone-aware dt_util in Habitica (#171557) 2026-05-20 17:34:53 +02:00
Josef Zweck 0df063b420 Fix string ref for tedee (#171548) 2026-05-20 17:30:35 +02:00
Manu 79fe415d6c Add exception translations to Notifications for Android TV / Fire TV (#171583) 2026-05-20 16:54:03 +02:00
J. Nick Koston 00e3a909a0 Bump bluetooth-data-tools to 1.29.11 (#170949)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-20 10:52:58 -04:00
Jan-Philipp Benecke a85fb79331 Remove duplicate constant in zha (#171586) 2026-05-20 16:25:02 +02:00
Ronald van der Meer 7f2f268fca Fix Duco VLV nodes not creating CO2 and humidity sensors (#171182) 2026-05-20 16:21:32 +02:00
Paulus Schoutsen 4c31a1737d Split BrowseMediaSource into root and source-specific classes (#170835)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-20 09:59:49 -04:00
Abílio Costa abd8d85225 Add validator subagents to github-pr-reviewer skill (#171370) 2026-05-20 14:48:29 +01:00
Jordan Harvey 626a1a5c87 Remove positional message strings when translation_key is set in nintendo_parental_controls (#171531) 2026-05-20 15:47:43 +02:00
Jarkko Pöyry 1b2e8ccc0f Avoid polling in wled integration (#161183)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-20 15:47:07 +02:00
Mike O'Driscoll 4da2cd465a casper_glow: fix missing translation for exception (#171534) 2026-05-20 15:47:01 +02:00
Thomas55555 e3c31a3482 Allow setting a custom laqi in Google Air Quality (#160681)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-05-20 15:41:45 +02:00
A. Gideonse a0b52e0f58 Bump indevolt-api to 1.8.1 (#171472) 2026-05-20 14:37:01 +01:00
Andrew Jackson 75dd509c7b Add translations to Mastodon exceptions (#171528) 2026-05-20 15:33:01 +02:00
Max Michels 6540ccd52a Replace duplicate constants with homeassistant.const imports in citybikes (#171478) 2026-05-20 15:18:12 +02:00
Ronald van der Meer a35ad41495 Fix untranslated config entry error in Duco (#171514) 2026-05-20 15:17:32 +02:00
Max Michels cedf5a5861 Replace duplicate constants with homeassistant.const imports hddtemp (#171517) 2026-05-20 15:15:55 +02:00
Alexey Masolov 16f4dc74bf Add TIMEOUT constant to CalDAV integration (#171463)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 15:08:42 +02:00
Franck Nijhof c5f22936e4 Use HA timezone for date in saj (#171450) 2026-05-20 14:57:34 +02:00
Jan Čermák aa23b3176c Bump base image to 2026.05.0 with Python 3.14.5, use 3.14.5 in CI (#171482) 2026-05-20 14:43:54 +02:00
Franck Nijhof a144bbab2b Fix Wyoming satellite crash when TTS is not configured (#171513) 2026-05-20 14:30:20 +02:00
Erik Montnemery 6a20b99252 Adjust device_registry.async_setup (#167653) 2026-05-20 14:28:10 +02:00
Franck Nijhof 8a12c06116 Fix PowerView cover crash when shade position is unavailable (#171471) 2026-05-20 13:50:49 +02:00
Alistair Francis 5dc057b36d husqvarna_automower_ble: Gracefully handle unreachable device (#171479)
Signed-off-by: Alistair Francis <alistair@alistair23.me>
2026-05-20 13:48:11 +02:00
Robert Resch 6d6f14a0aa Revert "Bump py-opendisplay to 7.0.0" (#171477) 2026-05-20 13:42:15 +02:00
dependabot[bot] 7bd81aeb9f Bump actions/ai-inference from 2.0.7 to 2.1.0 (#171475)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-20 13:06:59 +02:00
Sören 8dc29b5411 Bump avea to 1.8.0 (#171473) 2026-05-20 11:55:49 +01:00
Sören 1cabcf522e Fix Avea stale brightness restore (#171139) 2026-05-20 12:53:45 +02:00
Erik Montnemery ff8d244839 Remove advanced mode from tts service actions (#171462) 2026-05-20 12:11:30 +02:00
J. Nick Koston d1a5b0dbd3 Bump aiodns to 4.0.4 (#171420)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-20 12:10:03 +02:00
Erik Montnemery 86bab0c0f6 Remove advanced mode from zwave_js service actions (#171465) 2026-05-20 11:57:05 +02:00
J. Nick Koston 7f320a5a41 Bump zeroconf to 0.149.7 (#171054)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-05-20 11:45:24 +02:00
Erik Montnemery 309ce5545e Set breaks_in_ha_version on issue about not running core in venv or container (#171426) 2026-05-20 11:35:55 +02:00
Erik Montnemery 293d7851ba Remove advanced mode from webostv service actions (#171464) 2026-05-20 12:33:21 +03:00
Erik Montnemery eeb9270241 Rename advanced options section to additional options in light service actions (#171444) 2026-05-20 11:30:30 +02:00
Erik Montnemery b841a26aff Remove advanced mode from fan service actions (#171439) 2026-05-20 11:29:41 +02:00
Erik Montnemery 40f7a2f50f Remove advanced mode from climate service actions (#171437) 2026-05-20 11:29:00 +02:00
Willem-Jan van Rootselaar 15b230c4e7 Bump python-bsblan to version 6.0.1 (#171447) 2026-05-20 11:24:24 +02:00
Erik Montnemery 49a14112b7 Remove advanced mode from sharkiq service actions (#171456) 2026-05-20 11:23:23 +02:00
Erik Montnemery e59e631a87 Remove advanced mode from squeezebox service actions (#171457) 2026-05-20 11:22:58 +02:00
Erik Montnemery c1d0c9fb9f Rename advanced options section to additional options in kitchen_sink service actions (#171443) 2026-05-20 10:57:35 +02:00
Andrew Jackson ee7461ed9c Remove duplicate const in time_date (#171438) 2026-05-20 10:43:27 +02:00
AlCalzone 9757f8b574 Add support for Z-Wave credential management (#168360)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-20 10:41:02 +02:00
Thomas Bouron 5ee96a3616 Improve temperature unit handling in Tuya numbers and sensors (#170432) 2026-05-20 09:59:27 +02:00
Tomer 703ac31bd1 Use CONF_MODEL from homeassistant.const in victron_gx (#171434) 2026-05-20 09:56:23 +02:00
Erik Montnemery 52bf0b0ee0 Remove references to the removed toon.update service (#171435) 2026-05-20 09:55:45 +02:00
Robert Resch 29db335930 Remove advanced mode dependency from version config flow (#171215) 2026-05-20 09:50:16 +02:00
Denis Shulyaka 8257107462 Replace duplicate constants with homeassistant.const imports in humidifier (#171354)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-20 09:27:05 +02:00
Jan Bouwhuis c7618949da Remove deprecated advanced flags from MQTT service actions services.yaml (#171430) 2026-05-20 09:24:48 +02:00
Marcos A L M Macedo 592154bd27 Add fixture for Tuya INTELAR IR288 (#171412)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-20 09:11:11 +02:00
epenet 7919330ae0 Bump renault-api to 0.5.9 (#171428) 2026-05-20 09:10:44 +02:00
epenet 6ffe1bab9a Fix duplicate-const in Renault services (#171429) 2026-05-20 08:58:36 +02:00
Michael Heyman 91705ef821 Replace duplicate ATTR_TEMPERATURE constant with homeassistant.const (#171423)
Co-authored-by: Michael Heyman <michaelheyman@users.noreply.github.com>
2026-05-20 08:58:11 +02:00
J. Nick Koston e15797af14 Bump aiodiscover to 3.2.0 (#171401) 2026-05-20 08:43:10 +02:00
Russell VanderMey a966ce4586 Use homeassistant.const CONF_TOKEN for triggercmd (#171411) 2026-05-20 08:42:44 +02:00
Paulus Schoutsen ee2de6641f Use CONF_MODEL from homeassistant.const in marantz_infrared (#171415)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 08:42:11 +02:00
J. Nick Koston 5f85ae6f95 Bump dbus-fast to 5.0.0 (#171421) 2026-05-20 08:40:57 +02:00
Phil-Rad 275e0b3dd1 Add reconfigure flow to cert_expiry (#170888) 2026-05-20 08:39:32 +02:00
Erik Montnemery a9475683e1 Add new device tracker base entity BaseScannerEntity (#171063) 2026-05-20 08:09:28 +02:00
Petro31 d4e1a7075e Clean up legacy template entity code (#170016) 2026-05-20 07:44:32 +02:00
Adam Katic 2a3d75eb2b Add missing speedtestdotnet options flow translation (#171153) 2026-05-20 08:26:37 +03:00
Tsvi Mostovicz 9212d2300c Replace duplicate constants with homeassistant.const imports in jewish calendar (#171403) 2026-05-19 21:18:18 -04:00
J. Nick Koston 6836b27ba6 Bump yarl to 1.24.2 (#171407) 2026-05-19 21:17:58 -04:00
TimL 8656f52d7a Add buzzer action play_rtttl to SMLIGHT (#166665)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 23:24:49 +02:00
Paul Bottein 6a07ca93e9 Migrate Freebox to has_entity_name and key-based unique IDs (#169860) 2026-05-19 23:17:57 +02:00
Jan Bouwhuis 77990c8808 Remove duplicate constants for homeassistant integration (#171363) 2026-05-19 23:00:55 +02:00
Jan Rieger e4227ee1d4 Replace duplicate constant with homeassistant.const import in gpsd (#171355) 2026-05-19 22:59:48 +02:00
Denis Shulyaka 87aca7416f Replace duplicate constants with homeassistant.const imports in generic_hygrostat (#171358) 2026-05-19 22:59:00 +02:00
Glenn Waters 1ec6619a20 ElkM1 integration: Fix duplicate constants (#171364) 2026-05-19 22:57:15 +02:00
Denis Shulyaka 85013282e4 Explicitly set parallel-updates for Anthropic (#171387) 2026-05-19 22:32:40 +02:00
Chris ceab93ab83 refactor: remove redundant CONF_ID constant from OpenEVSE integration (#171366) 2026-05-19 22:14:54 +02:00
Tomas Kislan ac636ce54f Use homeassistant.const CONF_HOST and CONF_PORT in minio (#171395)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-19 22:13:59 +02:00
Jan-Philipp Benecke 3287b01ed1 Group Nuki executor jobs (#171391) 2026-05-19 22:06:46 +02:00
Jens Timmerman 3acc7d08b3 fix translations (#171157) 2026-05-19 22:04:31 +02:00
Jan-Philipp Benecke 16eb5dce63 Fix language handling in jewish_calendar tests (#171383) 2026-05-19 21:47:37 +02:00
Petro31 3fee05db71 Add entity_platform helper function to create issues when platform setup is not supported by integration (#171105)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-19 21:08:03 +02:00
Artur Pragacz f823ef639a Prefix area to entity ID (#170560) 2026-05-19 20:53:26 +02:00
mithomas da4263b95c Fix missing delay and repeat support in LG Netcast remote (#170324)
Co-authored-by: Copilot <copilot@github.com>
2026-05-19 20:49:43 +02:00
Robert Resch 29e2184163 Fix workflow run (#171367) 2026-05-19 20:49:10 +02:00
Robert Resch 816c3ff939 Adjust aw check requirements checks (#171389)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-19 20:48:53 +02:00
karwosts 2348ccc76e Use modern batteries for demo integration (#171376) 2026-05-19 19:23:11 +01:00
Manu 4202686a0d Remove duplicate constants in Mobile App integration (#171379) 2026-05-19 20:17:18 +02:00
dontinelli dd1437f5f2 Remove obsolete local const in slide_local (#171386) 2026-05-19 20:16:09 +02:00
Willem-Jan van Rootselaar 1a1c9d935c Remove duplicate constant in bsblan integration (#171385) 2026-05-19 19:48:17 +02:00
javicalle 4c0e7eb92d Migrate RFLink YAML configuration (ADR0007) (#161822)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 19:44:03 +02:00
Josh Gustafson d288645f0e Declare PARALLEL_UPDATES on arcam_fmj platforms (#171151)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:26:57 +02:00
Jan-Philipp Benecke 66aad8d3c5 Fix solaredge tests (#171378) 2026-05-19 18:38:10 +02:00
peteS-UK 89e15b9eae Remove unused ATTR_TIME from squeezebox const.py (#171374) 2026-05-19 18:29:03 +02:00
Nick Haghiri 489b831a4b Use homeassistant.const CONF_PREFIX in backblaze_b2 (#171365) 2026-05-19 18:24:56 +02:00
Manu f1854e1816 Remove duplicate constant in Notifications for Android TV / Fire TV integration (#171377) 2026-05-19 18:18:36 +02:00
Manu 8931ce561c Remove duplicate constant in ntfy integration (#171375) 2026-05-19 18:08:46 +02:00
Petro31 4d19cec214 Replace duplicate constants with homeassistant.const imports in template (#171349)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 18:08:20 +02:00
Manu e111678c40 Remove duplicate constant from HTML5 integration (#171373) 2026-05-19 18:07:07 +02:00
Jan Bouwhuis 69de70407b Remove duplicate constants for MQTT (#171359) 2026-05-19 18:05:16 +02:00
Andrew Jackson 64d17521a4 Remove duplicate const in Mastodon (#171357) 2026-05-19 16:38:11 +01:00
Erik Montnemery b52476a37e Remove use of advanced mode from the homekit integration (#171200) 2026-05-19 16:27:58 +02:00
Denis Shulyaka 58c906a2d1 Replace duplicate constants with homeassistant.const imports in anthropic (#171316) 2026-05-19 16:17:15 +02:00
Denis Shulyaka 3b2fa3f5b7 Replace duplicate constants with homeassistant.const imports in openai_conversation (#171348)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 16:15:12 +02:00
Paul Bottein 0dae4689cf Use CONF_CODE in Novy Cooker Hood (#171350)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 16:15:08 +02:00
Joost Lekkerkerker cd7fe836b0 Fix CI (#171351) 2026-05-19 16:07:27 +02:00
Robert Resch e3bae0dbda Use multistage workflow to run agentic workflow on forks (#171186)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-19 16:06:14 +02:00
Mika 7cf3cba27b Split SolarEdge power-flow attributes into sensor entities (#170457)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-19 15:42:36 +02:00
Tsvi Mostovicz de70d9ed82 Jewish Calendar: add a calendar entity (#145140)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
2026-05-19 15:40:26 +02:00
Onero-testdev eb0c1700b7 Add support for SwitchBot Lock Vision (Pro) and Lock Pro Wifi (#170470) 2026-05-19 15:36:33 +02:00
Franck Nijhof 6fa5fc77aa Add pylint checker for duplicate homeassistant.const definitions (#170848)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 15:10:13 +02:00
Åke Strandberg c705e8ff56 Cleanup miele API timeouts (#171172) 2026-05-19 14:19:01 +02:00
Jan Bouwhuis ee248b536e Fix subscription ID for restored subscriptions (#171130) 2026-05-19 14:08:36 +02:00
Åke Strandberg 7bfd11cf2e Add missing Miele Dishwasher codes (#171175) 2026-05-19 14:01:35 +02:00
iluvdata 2dae262135 Address future error in RepairFlow for Anthropic (#171156) 2026-05-19 14:53:18 +03:00
Aidan Timson 76a463dd50 Bump aiolyric to 2.1.1, Update OAuth URL for lyric (#171181) 2026-05-19 13:16:47 +02:00
epenet 0d83b1cbe8 Handle temperature unit mismatch in Tuya climate (#171183) 2026-05-19 13:16:02 +02:00
Artur Pragacz ae622a7cd4 Fix zwave_js fixture path resolution (#171196) 2026-05-19 13:11:43 +02:00
Przemko92 3f0af1e5b7 Add Compit switch (#164053) 2026-05-19 13:00:39 +02:00
G Johansson 742e63d02c Fix exception handling in command_line notify service (#170709)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-19 12:49:19 +02:00
nopoz 1042ec2964 Bump pyenvisalink to 4.9 (#171125) 2026-05-19 12:03:20 +02:00
Erik Montnemery f4fdd4d58f Adjust device tracker tests (#171178) 2026-05-19 11:52:12 +02:00
Franck Nijhof c9ed57bc56 2026.5.3 (#171185) 2026-05-19 11:49:12 +02:00
iluvdata 3963555b2f Add RepairsFlowResult pylint check (#171145) 2026-05-19 11:35:25 +02:00
Erwin Douna 4f8885b40d Downloader add proper exceptions (#170771)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-19 11:35:01 +02:00
bkobus-bbx 0e0901993d Fix blebox light temperature scaling (#170573) 2026-05-19 08:47:23 +00:00
Franck Nijhof 54aba11091 Bump version to 2026.5.3 2026-05-19 08:39:50 +00:00
Mick Vleeshouwer dc9116a7a7 Fix tilt and position support for VenetianBlind covers in Overkiz (#170974) 2026-05-19 08:39:38 +00:00
Mick Vleeshouwer 1e90882918 Fix is_closed state and position for DynamicPergola covers in Overkiz (#170983) 2026-05-19 08:37:54 +00:00
puddly e8295e14b1 Fix ZHA config entries using a URI without a port (#171164) 2026-05-19 08:35:43 +00:00
Mick Vleeshouwer 7ebaaf129a Fix controls for UpDownGarageDoor4T and additional 4T covers in Overkiz (#171144) 2026-05-19 08:35:00 +00:00
Michael ee734dede6 Bump aioimmich to 0.14.1 (#171138) 2026-05-19 08:33:58 +00:00
Franck Nijhof ebc582c813 Return media_content_id as string in forked_daapd (#171059) 2026-05-19 08:33:56 +00:00
James Nimmo 311e5a9bd2 Bump pyIntesishome to 1.8.8 (#171041) 2026-05-19 08:33:54 +00:00
Franck Nijhof cd6c3c878b Fix WeatherFlow websocket crash when data payload is None (#171037) 2026-05-19 08:33:52 +00:00
Franck Nijhof 51589ec2ff Add stop command to Overkiz pergola horizontal awning covers (#171034) 2026-05-19 08:33:50 +00:00
Franck Nijhof 8e1a04dc82 Fix Verisure alarm crash when cloud rejects arm/disarm command (#171024) 2026-05-19 08:33:48 +00:00
Mick Vleeshouwer 6b15f9a2ec Add additional overrides to cover entity in Overkiz (#171019) 2026-05-19 08:33:46 +00:00
Franck Nijhof 8d66752556 Fix shorthand template conditions in choose blocks crashing all automations (#171018) 2026-05-19 08:33:44 +00:00
Franck Nijhof 266767e37d Handle Daikin connection errors gracefully in coordinator (#171017) 2026-05-19 08:33:42 +00:00
Franck Nijhof d39775ac34 Fix manual alarm panel crash on restore with invalid state (#171016) 2026-05-19 08:33:40 +00:00
Franck Nijhof a314f7bf64 Fix Control4 climate crash when humidity is 'Undefined' (#171015) 2026-05-19 08:33:38 +00:00
Franck Nijhof 37478d33eb Fix SleepIQ timer units: seconds should be minutes for core climate and foot warmer (#171013) 2026-05-19 08:33:36 +00:00
Franck Nijhof 5a76f3bd19 Fix Growatt mix device IndexError when chart data is empty (#171012) 2026-05-19 08:33:34 +00:00
Franck Nijhof 17e105083e Fix threshold preview crash when hysteresis is not provided (#171009) 2026-05-19 08:33:32 +00:00
Franck Nijhof db8589b2bc Fix time trigger crash when using entity_id dict format without offset (#171006)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-19 08:33:30 +00:00
Franck Nijhof 771b016f33 Fix Netatmo valve KeyError when hvac_action state is unavailable in Overkiz (#171004) 2026-05-19 08:33:28 +00:00
Franck Nijhof 0bc0745e8c Use asyncio.get_running_loop() in emulated_hue UPnP responder (#171000)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:43 +00:00
Franck Nijhof ea084797d3 Load template extensions by class to prevent import deadlock (#170995)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:41 +00:00
Franck Nijhof 2456753caf Prevent Google Assistant entity sync from blocking startup (#170991)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:39 +00:00
Mick Vleeshouwer 070de13c14 Fix controls for OpenCloseGate4T (rts:GateOpenerRTS4TComponent) in Overkiz (#170987) 2026-05-19 08:30:30 +00:00
Mick Vleeshouwer 5e45f37ee6 Fix is_closed state for DiscretePositionableGarageDoor in Overkiz (#170981) 2026-05-19 08:25:10 +00:00
Franck Nijhof 4a96880f51 Reduce GoodWe connect retries to avoid blocking startup (#170964)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:20:26 +00:00
Franck Nijhof 228ac01124 Use correct state_class for utility meters with device classes that don't support total_increasing (#170962)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:20:24 +00:00
Franck Nijhof d366027e6b Fix utility meter next_reset shifting forward on entity rename (#170957)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:20:22 +00:00
puddly 2f35ad2a8a Disable USB discovery for teleinfo (#170933) 2026-05-19 08:20:20 +00:00
Mick Vleeshouwer 95cc9aed64 Fix is_closed state for SlidingDiscreteGateWithPedestrianPosition covers in Overkiz (#170913) 2026-05-19 08:19:03 +00:00
Franck Nijhof 37d6449a49 Populate uid and recurrence_id in CalDAV calendar events (#170910) 2026-05-19 08:14:10 +00:00
Robert Resch 3f49877ff1 Use renovate to update go2rtc (#169508) 2026-05-19 10:10:17 +02:00
Erik Montnemery d2bb31d115 Remove useless input validation from cast options flow (#171171) 2026-05-19 10:09:17 +02:00
J. Nick Koston 249b5435d9 Bump aiodns to 4.0.3 (#170865)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-19 08:07:21 +00:00
bkobus-bbx 3293ebcea5 Fix ValueError when turning on blebox light with brightness set to 0 (#170769)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 08:07:18 +00:00
Daniil Karpenko 47d8adc77c Add tilt controls for UpDownSheerScreen in Overkiz (#170563)
Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
2026-05-19 08:01:00 +00:00
Keith Roehrenbeck 356e6a691b Fix Apple TV keyboard focus binary_sensor missing on cold start (#170360) 2026-05-19 08:00:58 +00:00
Florent Thoumie b26c2f3854 Improve iaqualink 429 handling (#170231) 2026-05-19 08:00:56 +00:00
Luka Matijević 0830988687 Bump qbittorrent-api to 2026.5.1 (#170181) 2026-05-19 08:00:54 +00:00
Denis Shulyaka f499dbf29f Add web fetch tool support for Anthropic (#167405)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-19 09:50:50 +02:00
Keith Roehrenbeck bc0e3dc3be Fix Apple TV keyboard focus binary_sensor missing on cold start (#170360) 2026-05-19 09:38:48 +02:00
Florent Thoumie fb6e6170bf Improve iaqualink 429 handling (#170231) 2026-05-19 09:24:09 +02:00
Mick Vleeshouwer 9e22711874 Fix controls for UpDownGarageDoor4T and additional 4T covers in Overkiz (#171144) 2026-05-19 09:22:31 +02:00
puddly 1982dd9085 Fix ZHA config entries using a URI without a port (#171164) 2026-05-19 09:14:48 +02:00
Adam Katic c32098decd Add quality scale for speedtestdotnet integration (#170782) 2026-05-19 10:06:06 +03:00
Erik Montnemery 2e87750d70 Remove use of advanced mode from the cast integration (#171090) 2026-05-19 08:26:35 +02:00
karwosts 55354770a8 Make energy electric sources nameable (#170658) 2026-05-19 09:22:42 +03:00
epenet d7b63a40db Rename Tuya fixtures (#171169) 2026-05-19 08:22:09 +02:00
Erik Montnemery c80d1ba003 Correct signature of mock class in test_recovery_from_dbus_restart (#171097) 2026-05-19 07:48:12 +02:00
Paulus Schoutsen e675423c3c add /local to no auth sig required urls (#171140)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-05-19 06:55:47 +02:00
Marcos A L M Macedo 11cbf91563 Add total production sensor support for Tuya SPM02 devices (#171166) 2026-05-19 06:51:46 +02:00
yemua 4d5c36a3c1 Enable current/power/voltage sensors by default for Tuya electrical categories (#171098) 2026-05-19 06:45:58 +02:00
Carlos Sánchez López cc335a3bd9 Add number support for Tuya WG2 alarm panel (Duosmart C30) (#165836)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-19 06:38:12 +02:00
Josh Gustafson f764a32564 Use device name in arcam_fmj browse media root (#171160)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:49:22 -04:00
Josh Gustafson aeb7109708 Share arcam_fmj convert_exception decorator from entity module (#171162)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:01:28 -04:00
Josh Gustafson f75c205c08 Annotate parametrized arcam_fmj media_player test signatures (#171163)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:57:24 -04:00
Joost Lekkerkerker e20f4c8f6e Use subentry helper in Satel Integra (#167060)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-19 01:08:53 +02:00
Erik Montnemery 72f6c38e7d Remove use of advanced mode from the tasmota integration (#171093) 2026-05-19 00:43:08 +02:00
Erik Montnemery 40408def0f Don't set _attr_source_type in victron_gx device tracker entity (#171077) 2026-05-19 00:40:05 +02:00
Robert Resch 282737e3c4 Bump gh aw to 0.74.4 (#171137) 2026-05-18 23:02:56 +01:00
Josh Gustafson a1cc735337 Report unknown state in arcam_fmj when power state is unreported (#171149)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 23:01:51 +01:00
Mick Vleeshouwer b6f4551a76 Add light entity tests to Overkiz (#171102) 2026-05-18 23:46:15 +02:00
Phil-Rad f5d2aa9c12 Use runtime_data and validate connection at setup for dnsip (#169745) 2026-05-18 23:24:57 +02:00
Onero-testdev 612dbf2d44 Add SwitchBot Permanent Outdoor Light support (#170463)
Co-authored-by: Fan Kai <fankai@onero.com>
2026-05-18 23:22:56 +02:00
Maciej Bieniek f2691e4feb Change model to model ID in the Tractive DeviceInfo (#171147) 2026-05-18 23:12:24 +02:00
Franck Nijhof 456202325a 2026.5.2 (#170840) 2026-05-15 22:55:45 +02:00
Franck Nijhof 1e47149764 Fix hassfest warning 2026-05-15 20:26:51 +00:00
Franck Nijhof 116b63ca3a Bump version to 2026.5.2 2026-05-15 20:13:00 +00:00
Ronald van der Meer 3096bcf8a9 Bump python-duco-connectivity to 0.4.0 (#170661) 2026-05-15 20:12:26 +00:00
Ronald van der Meer a4027029d0 Migrate Duco to python-duco-connectivity and remove temperature sensors (#170237) 2026-05-15 20:11:35 +00:00
Bram Kragten fffc9d0695 Update frontend to 20260429.4 (#170567) 2026-05-15 20:06:23 +00:00
G Johansson 3ca5cf5add Add missing optional category strings in workday (#170505)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 20:06:21 +00:00
Jan Bouwhuis 087cb77042 Fix MQTT settings in device subentry device settings are not recalled when reconfiguring the device (#170484) 2026-05-15 20:06:19 +00:00
Michael Keck 8bd1c07ec9 Increase WebDAV client timeout from 10 to 30 seconds (#170476) 2026-05-15 20:06:17 +00:00
J. Nick Koston 9ecb59590b Bump aioharmony to 1.0.3 (#170459) 2026-05-15 20:02:46 +00:00
Rob Bierbooms e14eb9fbc5 Fix influxdb reconfigure for v1 configuration (#170448) 2026-05-15 20:01:59 +00:00
TheJulianJES 149c796227 Fix fractional setpoints in Matter climate not rounded (#170442) 2026-05-15 20:01:11 +00:00
J. Nick Koston 3383e5b1e9 Bump aioesphomeapi to 44.24.1 (#170428) 2026-05-15 20:00:24 +00:00
Åke Strandberg 05862c6dc8 Bump pymiele version to 0.6.2 (#170419) 2026-05-15 19:59:37 +00:00
Petar Petrov b35ac41470 Apply unit_of_measurement to energy combined power sensor (#170404) 2026-05-15 19:58:50 +00:00
James Nimmo 20cec56512 Bump pyintesishome to 1.8.7 (#170382) 2026-05-15 19:58:03 +00:00
puddly 74580262b6 Bump serialx to 1.7.3 (#170368) 2026-05-15 19:57:16 +00:00
Pascal Brunot f75cdae602 Bump serialx to 1.7.2 (#170272) 2026-05-15 19:56:59 +00:00
Jan Bouwhuis 8c95f4f7ae Fix duplicate doorbell events when entity becomes unavailable (#170354)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:54:02 +00:00
Robert Svensson c3ec51c471 Bump axis to v71 (#170347) 2026-05-15 19:54:00 +00:00
Raman Gupta 0f80a4bc18 Cancel previous Debouncer timer handle in _schedule_timer (#170339)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:53:58 +00:00
Maciej Bieniek 0761d618f1 Fix Shelly media player availability (#170319) 2026-05-15 19:53:57 +00:00
Stefan Agner 03e3c46faf Fix hassio.backup_partial AttributeError when folders are specified (#170312)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:53:55 +00:00
Craig Dean d1962b0df2 Bump renault-api to 0.5.8 (#170309) 2026-05-15 19:53:53 +00:00
Florent Thoumie 7a38a2303a iaqualink: set system specific polling interval (#170279) 2026-05-15 19:53:51 +00:00
Maciej Bieniek 6f5c2a8614 Bump imgw-pib to 2.1.2 (#170274) 2026-05-15 19:53:49 +00:00
Sören Beye ff36498698 fix: Do not forget segments from state when a new config arrives (#170265)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:53:47 +00:00
Willem-Jan van Rootselaar 23e19ea2e4 Handle empty BSB-LAN heating circuits (#170249) 2026-05-15 19:53:46 +00:00
Ronald van der Meer c33f174041 Bump python-duco-client to 0.5.0 (#170065) 2026-05-15 19:52:32 +00:00
Ronald van der Meer bbe64d74e3 Bump python-duco-client to 0.4.2 (#170027) 2026-05-15 19:52:30 +00:00
Ronald van der Meer ed3a71f2ee Add API version to Duco diagnostics for support triage (#169802) 2026-05-15 19:51:21 +00:00
Ronald van der Meer 46c49daba4 Add system health platform for Duco integration (#169517) 2026-05-15 19:48:52 +00:00
Ronald van der Meer a2f2ded188 Add target flow level and mode end time sensors to Duco integration (#169298) 2026-05-15 19:47:15 +00:00
Simone Chemelli 7be061796d Fix entities refresh for UptimeRobot (#170217) 2026-05-15 19:32:16 +00:00
Jan Bouwhuis 27c7d8de0c Fix MQTT device discovery not using shared QoS and encoding options (#170195)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:32:14 +00:00
Simone Chemelli 07542523b5 Reinit API on stale session for Vodafone Station (#170190) 2026-05-15 19:32:12 +00:00
puddly 18597bb653 Set serial port description from description, not product (#170160)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-15 19:32:10 +00:00
Christian Lackas c4be57a294 homematicip_cloud: fix HmIP-FLC lock state polarity (#170159) 2026-05-15 19:32:08 +00:00
Christian Lackas 7ceaebb086 Fix homematicip_cloud config entry setup crash after migration to 2026.5.0 (#170156) 2026-05-15 19:32:06 +00:00
Mick Vleeshouwer 7c5ef09734 Fix local API incorrectly marking devices as unavailable in Overkiz (#170118)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2026-05-15 19:32:05 +00:00
Thijs W. b4d8ba66fe Update afsapi to 1.0.1 (#170073) 2026-05-15 19:32:02 +00:00
puddly 308221ce67 Migrate ZBT-1 and ZBT-2 to use serial number for unique_id (#169879)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-15 19:30:56 +00:00
Simone Chemelli 1344213335 Fix non unique_id for Comelit (#169756)
Co-authored-by: Copilot <copilot@github.com>
2026-05-15 19:26:54 +00:00
r2xj 7e405e9014 Only use SmartThings switch for light if it should (#166424)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:26:52 +00:00
LG-ThinQ-Integration b0c45132ed Fix ValueError for non-numeric value in LG ThinQ (#166300)
Co-authored-by: YunseonPark-LGE <yunseon.park@lge.com>
2026-05-15 19:26:49 +00:00
Franck Nijhof 7d7738303a 2026.5.1 (#170146) 2026-05-08 22:07:51 +02:00
Franck Nijhof dd0cdc4fc4 Bump version to 2026.5.1 2026-05-08 18:54:08 +00:00
Mick Vleeshouwer 18ea40c46d Fix tilt support for UpDownVenetianBlind (rts:VenetianBlindRTSComponent) in Overkiz (#170047) 2026-05-08 18:53:57 +00:00
Mick Vleeshouwer a23131efc8 Fix is_closed state for DynamicGate covers in Overkiz (#170130) 2026-05-08 18:53:10 +00:00
bkobus-bbx 4940a0abae Bump blebox_uniapi to v2.5.3 (#170115) 2026-05-08 18:53:08 +00:00
Willem-Jan van Rootselaar 5f98d5ae52 Bump python-bsblan to 5.2.1 (#170100) 2026-05-08 18:53:06 +00:00
TheJulianJES ba18cded30 Bump ZHA to 1.3.1 (#170095) 2026-05-08 18:53:04 +00:00
TheJulianJES fb7504e9df Fix Z-Wave discovery crash with unknown node firmware version (#170090) 2026-05-08 18:53:02 +00:00
Mick Vleeshouwer 106f815a1e Fix sensors getting wrong unit from MeasuredValueType attribute in Overkiz (#170088) 2026-05-08 18:53:00 +00:00
Mick Vleeshouwer 167757762b Set is_closed state to None when a cover state returns "unknown" in Overkiz (#170081) 2026-05-08 18:52:58 +00:00
Robert Resch 3a902e1a16 Bump deebot-client to 18.3.0 (#170066) 2026-05-08 18:52:56 +00:00
Mick Vleeshouwer 85c11672d8 Bump pyOverkiz to 1.20.3 (#170060) 2026-05-08 18:52:54 +00:00
Mick Vleeshouwer 89649df20d Fix cover controls for UpDownBioclimaticPergola in Overkiz (#170058) 2026-05-08 18:52:52 +00:00
Mick Vleeshouwer 7b749b95ce Fix tilt controls for TiltOnlyVenetianBlind in Overkiz (#170055)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-08 18:52:50 +00:00
Mick Vleeshouwer cc140be85c Fix is_closed state for DynamicGarageDoor in Overkiz (#170052) 2026-05-08 18:52:47 +00:00
Robert Svensson e1ad765414 Fix websocket certificate verification Bump axis to v70 (#170038) 2026-05-08 18:48:55 +00:00
Michael 44b1fea745 Proper handling of malformed data during FRITZ!Box Tools setup (#170030) 2026-05-08 18:48:54 +00:00
Ronald van der Meer 5dd04363b2 Bump python-duco-client to 0.4.1 (#169991) 2026-05-08 18:48:51 +00:00
Ronald van der Meer 03aa979309 Bump python-duco-client to 0.4.0 (#169776) 2026-05-08 18:48:49 +00:00
Daniel Hjelseth Høyer 6fabbb354b Bump pyTibber to 0.37.5 (#169981)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-05-08 18:45:50 +00:00
Erik Montnemery f644448d0f Add support for options to todo triggers (#169947) 2026-05-08 18:45:48 +00:00
G Johansson 4e61581cd8 Bump holidays to 0.96 (#169939) 2026-05-08 18:45:47 +00:00
puddly 6f87d02b72 Bump serialx to 1.7.1 (#169928) 2026-05-08 18:45:45 +00:00
Joakim Plate 348f6149b4 Update gardena ble to 2.8.1 (#169914) 2026-05-08 18:45:43 +00:00
Stefan Agner a4227ef1bc Fix hassio auth IndexError on Supervisor Unix socket requests (#169911) 2026-05-08 18:45:41 +00:00
Jeef aac49a567f Fix IntelliFire setup recovery (#169739) 2026-05-08 18:45:39 +00:00
Rob Treacy 76b878b136 Fix WiZ Light config flow timeout by properly closing UDP connections (#168456) 2026-05-08 18:45:37 +00:00
th3spis 2d05931683 Added wfsens as a occupancy source in wiz (#166799)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-08 18:45:35 +00:00
Franck Nijhof b10582b0a9 2026.5.0 (#169484) 2026-05-06 17:22:09 +02:00
Franck Nijhof b193d951d7 Bump version to 2026.5.0 2026-05-06 15:01:09 +00:00
Franck Nijhof 4cd0d9dcec Bump version to 2026.5.0b4 2026-05-06 13:27:18 +00:00
Daniel Hjelseth Høyer 32f65b2e11 Bump pyTibber to 0.37.4 (#169907) 2026-05-06 13:27:09 +00:00
Erik Montnemery 8c79d1e44b Remove _get_tracked_value method from EntityConditionBase (#169906) 2026-05-06 13:27:07 +00:00
Erik Montnemery 8d53f7a520 Exclude incompatible humidifier entities from humidifier automations (#169905) 2026-05-06 13:27:05 +00:00
Erik Montnemery cc83ee88fb Exclude incompatible water_heater entities from water_heater automations (#169904) 2026-05-06 13:27:03 +00:00
Erik Montnemery 0c5b02eff3 Exclude incompatible climate entities from climate automations (#169903) 2026-05-06 13:27:02 +00:00
Erik Montnemery 9da9f8fd50 Unload scripts and conditions created by template entities (#169366) 2026-05-06 13:27:00 +00:00
Franck Nijhof d70ffcd3e9 Bump version to 2026.5.0b3 2026-05-06 11:16:10 +00:00
Erik Montnemery 3e26d0dfe3 Exclude incompatible entities from temperature automations (#169901) 2026-05-06 11:15:56 +00:00
Erik Montnemery eab9747b32 Exclude incompatible entities from humidity automations (#169898) 2026-05-06 11:15:54 +00:00
Erik Montnemery 9e955d8294 Add media_player volume condition (#169897) 2026-05-06 11:15:52 +00:00
Bram Kragten f08cd01ff8 Update frontend to 20260429.3 (#169893) 2026-05-06 11:10:49 +00:00
Erik Montnemery eabaf3b0fe Add media_player muted conditions (#169892) 2026-05-06 11:10:47 +00:00
Tom Matheussen 65ca790d15 Bump satel_integra to 1.3.1 (#169889) 2026-05-06 11:10:45 +00:00
Joost Lekkerkerker d177944f7a Fix Zinvolt select options (#169886) 2026-05-06 11:10:43 +00:00
Erik Montnemery 7f186f4430 Add media_player volume triggers (#169885) 2026-05-06 11:10:41 +00:00
Erik Montnemery 4f4f4642a7 Add method _should_include to EntityConditionBase (#169884)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-06 11:10:39 +00:00
Erik Montnemery 12e443cd31 Improve entity trigger tests (#169881) 2026-05-06 11:10:37 +00:00
Erik Montnemery 22a7daabe7 Add method _should_include to EntityTriggerBase (#169837) 2026-05-06 11:10:35 +00:00
Erik Montnemery c139e99abd Improve condition test helper docstrings (#169871) 2026-05-06 11:09:06 +00:00
Erik Montnemery 2bfdb96a3f Improve trigger test helper docstrings (#169869) 2026-05-06 11:09:04 +00:00
puddly 4b24ca924b Bump serialx to 1.7.0 (#169867) 2026-05-06 11:09:02 +00:00
Michael Hansen 1d3d714e4f Bump intents to 2026.5.5 (#169855) 2026-05-06 11:09:00 +00:00
Erik Montnemery ffae6eda8a Validate yaml matches implementation in automation options_supported tests (#169798) 2026-05-06 11:05:41 +00:00
Erik Montnemery 4dd996b728 Add trigger media_player.unmuted (#169797) 2026-05-06 11:05:40 +00:00
Erik Montnemery afad1e8dac Improve mobile_app device tracker tests (#169724) 2026-05-06 11:05:38 +00:00
Manu 8e41933251 Record notification from legacy notify action in Mobile App (#169749) 2026-05-06 11:00:21 +00:00
Erik Montnemery c581eaad53 Add trigger timer.time_remaining (#169763) 2026-05-06 10:58:59 +00:00
Ludovic BOUÉ 3050e79d06 Expose SET_SPEED for all fans via PercentSetting in Matter (#169696)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-05-06 08:55:15 +00:00
Andres Ruiz 0e8ecd1065 Catch additional errors as potentially retryable errors during energy data updates (#169646) 2026-05-06 08:55:13 +00:00
Paulus Schoutsen 94732139f4 Bump version to 2026.5.0b2 2026-05-05 10:29:37 -04:00
Denis Shulyaka c5e08b2409 Return the requested format for OpenAI TTS (#169839)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-05 10:29:30 -04:00
Joost Lekkerkerker c12e1b5f4a Add Zunzunbee Zigbee brand (#169838) 2026-05-05 10:29:29 -04:00
Joost Lekkerkerker 6cfedb55e6 Add Sensereo matter brand (#169836) 2026-05-05 10:29:27 -04:00
Åke Strandberg af4cb9530b Add missing code for miele washing machine (#169795) 2026-05-05 10:29:26 -04:00
Matthias Alphart 58e97e7d5f Update xknxproject to 3.9.0 (#169775) 2026-05-05 10:29:25 -04:00
Daniel Hjelseth Høyer 2945b51617 Bump pyTibber to 0.37.3 (#169762) 2026-05-05 10:29:24 -04:00
Keilin Bickar 9d0e2df627 bump sense-energy to 0.14.1 (#169761) 2026-05-05 10:29:23 -04:00
Steve Syrell 643ae080db Bump Insteon-panel to 0.6.2 (#169757) 2026-05-05 10:29:22 -04:00
G Johansson a7eaa51179 Fix config flow validation in Nord Pool (#169751) 2026-05-05 10:29:21 -04:00
Petro31 e15852ff38 Fix uptime template sensor (#169743) 2026-05-05 10:29:20 -04:00
Diogo Gomes f6dec34136 Bump pytrydan to 1.0.0 (#169742) 2026-05-05 10:29:19 -04:00
Raj Laud 53905fbc49 Bump victron-ble-ha-parser to 0.7.0 (#169736)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 10:29:17 -04:00
Thomas D 8218ff0fe8 Add missing initialization charging power status option to Volvo (#169727) 2026-05-05 10:29:16 -04:00
kernelpanic85 663f7e3e6b Add Celsius and Fahrenheit to Smartthings UNITS mapping (#169686) 2026-05-05 10:29:15 -04:00
Nathan Spencer 4dfa2b8b88 Limit power status binary sensor to non-LR5 devices (#169659) 2026-05-05 10:29:14 -04:00
Nathan Spencer f828b165b1 Bump pylitterbot to 2025.4.0 (#169652) 2026-05-05 10:29:13 -04:00
shbatm c56c506648 Add precipitation device class to WeatherFlow Cloud accumulation sensors (#169638)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 10:29:12 -04:00
Artur Pragacz 8e5bf2a35f Fix async_unload teardown race in scripts (#169562) 2026-05-05 10:29:10 -04:00
Erik Montnemery 4d575e69a4 Improve template reload (#169480) 2026-05-05 10:29:09 -04:00
Christian Lackas 4f78bbccc0 Use all_devices in ViCare diagnostics for completeness (#169429) 2026-05-05 10:29:08 -04:00
Erik Montnemery 2d66ebe54a Add trigger media_player.muted (#156736) 2026-05-05 10:29:07 -04:00
Paulus Schoutsen a3e1209778 Bump version to 2026.5.0b1 2026-05-04 12:44:42 -04:00
Paul Bottein 7c44a0b88d Update frontend to 20260429.2 (#169748) 2026-05-04 12:44:23 -04:00
Manu 126058e0fa Bump bring-api to 1.1.2 (#169729) 2026-05-04 12:44:22 -04:00
Thomas D 28742822cb Ignore location FORBIDDEN response for the Volvo integration (#169713) 2026-05-04 12:44:21 -04:00
karwosts 179d370c2a Use uptime device_class for Uptime sensor (#169692) 2026-05-04 12:44:20 -04:00
Allen Porter 2d8f3691cf Update Nest doorbell event to use standard DoorbellEventType.RING (#169691) 2026-05-04 12:44:19 -04:00
Tom ce4fc9e880 Improve ProxmoxVE config flow preparing bug fixing (#169682)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-04 12:44:18 -04:00
Ronald van der Meer 9e357e7e5a Bump python-duco-client to 0.3.10 (#169677) 2026-05-04 12:44:17 -04:00
OMEGA_RAZER ed35b23e62 Updated prowlpy to 1.1.5 (#169671) 2026-05-04 12:44:17 -04:00
Tom Matheussen 191d2d1f12 Bump satel_integra to 1.3.0 (#169668) 2026-05-04 12:44:16 -04:00
SeifEddineMezned b165d8251f Fix grammar in mqtt/strings.json: "Minimal one" → "At least one" (#169666) 2026-05-04 12:44:15 -04:00
Midori Kochiya 5e8886aeb7 Fix M1S-T500 update error (#169651) 2026-05-04 12:44:14 -04:00
Michael bdb66635f8 Pass None config entry to schluter coordinator (#169621) 2026-05-04 12:44:13 -04:00
Michael 5ba6e348da Fix detection of CPU temperature sensor support on olde FRITZ!Box models (#169620) 2026-05-04 12:44:12 -04:00
Petro31 ed52b0ce80 Change vacuum template config names for clean area (#169599)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-05-04 12:44:11 -04:00
Jan-Philipp Benecke 33ee3d6967 Decrease WebDAV client timeout (#169591) 2026-05-04 12:44:10 -04:00
tronikos f36676c32c Bump opower to 0.18.2 (#169588) 2026-05-04 12:44:09 -04:00
Ronald van der Meer 77beddb1e7 Fix Duco unknown node type not re-evaluated after becoming known (#169579) 2026-05-04 12:42:31 -04:00
SeifEddineMezned 1677e410b3 Fix possessive apostrophe errors in mqtt/strings.json (#169576)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2026-05-04 12:38:37 -04:00
SeifEddineMezned 1be09347cd Fix grammar and clarity in samsungtv/strings.json (#169574) 2026-05-04 12:38:36 -04:00
Simone Chemelli c30ac2c0f3 Bump pyuptimerobot to 25.0.0 (#169572) 2026-05-04 12:37:45 -04:00
Shay Levy 145c7435a5 Bump aioshelly to 13.25.0 (#169569) 2026-05-04 12:36:21 -04:00
Paul Bottein 60f3b3bcc0 Update frontend to 20260429.1 (#169565) 2026-05-04 12:36:20 -04:00
Dan Raper 03e6d3bd30 Bump ohme to 1.9.0 (#169556) 2026-05-04 12:36:19 -04:00
Abílio Costa ee4d150e13 Use the correct schema for triggers/conditions "for" option (#169539) 2026-05-04 12:35:29 -04:00
bkobus-bbx 148603a10e Bump blebox_uniapi to 2.5.2 (#169534) 2026-05-04 12:33:13 -04:00
Erik Montnemery 1dbd933d3c Enable duration support in all entity conditions (#169532)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-04 12:32:30 -04:00
Matthias Alphart f7ee7423fe Update knx-frontend to 2026.4.30.60856 (#169529) 2026-05-04 12:26:31 -04:00
Tomer 6322f1e37a Victron GX: Bug fix: parent device is mapped to the wrong device (#169525)
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 12:26:30 -04:00
Manu 0d8c7fbb9d Fix: Migrate also device entries to subentry in GitHub integration (#169523) 2026-05-04 12:26:29 -04:00
Boris Bolshem 70e30b02a4 Fix KeyError in telegram_bot media group download debug log (#169519) 2026-05-04 12:26:28 -04:00
Simone Chemelli ebd21ea9b2 Fix uptime sensor for Synology DSM (#169512) 2026-05-04 12:26:27 -04:00
Erik Montnemery 9aa092cd34 Correct wake_on_lan entity behavior when entity_id changes (#169486)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-04 12:26:26 -04:00
TheJulianJES b274fe85b7 Re-interview ZHA device on websocket reconfigure (#169483) 2026-05-04 12:26:25 -04:00
Erik Montnemery 777c36998c Remove scripts from DATA_SCRIPTS on unload (#169415) 2026-05-04 12:26:24 -04:00
Kurt Chrisford a3977428f9 Implement current setpoint method in actron air integration (#169358) 2026-05-04 12:26:23 -04:00
Simone Chemelli 2d626c263c Storage problem management for Comelit Serial Bridge (#169297) 2026-05-04 12:26:22 -04:00
Jeef d1461f2e68 Bump weatherflow4py to 1.5.4 (#168994) 2026-05-04 12:26:21 -04:00
bkobus-bbx 3b778d2cc7 fix: incorrect position inversion for blebox gateBox cover (#168893) 2026-05-04 12:26:20 -04:00
Yuval Weiss 67b7d17a2f Add Broadlink infrared emitter support (#168889) 2026-05-04 12:26:18 -04:00
Tomer 1afeadc342 Victron GX: bug fix for missing translation key (#168461)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-04 12:26:17 -04:00
jftkcs f6aa4e2092 Fix reasoning summary handling for OpenAI o-models (#168093)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Denis Shulyaka <Shulyaka@gmail.com>
2026-05-04 12:26:16 -04:00
Khole 3b00c5bb96 Check device registration before completing Hive reauth flow (#168035)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-04 12:26:15 -04:00
Franck Nijhof ef7eed579b Bump version to 2026.5.0b0 2026-04-29 16:40:46 +00:00
Franck Nijhof 568a0085fe Bump version to 2026.5.0 2026-04-29 15:50:10 +00:00
2165 changed files with 116780 additions and 35705 deletions
@@ -18,6 +18,13 @@ description: Reviews GitHub pull requests and provides feedback comments. This i
4. Ensure any existing review comments have been addressed.
5. Generate constructive review comments in the CONSOLE. DO NOT POST TO GITHUB YOURSELF.
## Verification:
- After the review, run parallel subagents for each finding to double check it.
- Spawn up to a maximum of 10 parallel subagents at a time.
- Gather the results from the subagents and summarize them in the final review comments.
## IMPORTANT:
- Just review. DO NOT make any changes
- Be constructive and specific in your comments
+9
View File
@@ -0,0 +1,9 @@
{
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
"integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
}
}
}
+1 -2
View File
@@ -14,13 +14,12 @@ Dockerfile.dev linguist-language=Dockerfile
# Generated files
CODEOWNERS linguist-generated=true
Dockerfile linguist-generated=true
homeassistant/generated/*.py linguist-generated=true
pylint/plugins/pylint_home_assistant/generated/*.py linguist-generated=true
machine/* linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
requirements_all.txt linguist-generated=true
requirements_test_all.txt linguist-generated=true
requirements_test_pre_commit.txt linguist-generated=true
script/hassfest/docker/Dockerfile linguist-generated=true
.github/workflows/*.lock.yml linguist-generated=true
@@ -0,0 +1,52 @@
name: Cache and install APT packages
description: >-
Wraps awalsh128/cache-apt-pkgs-action with the workarounds Home Assistant CI
needs. Removes the conflicting Microsoft apt source before any apt run, and
points the dynamic linker at the host's multiarch lib subdirectories so
shared libraries that rely on update-alternatives or postinst-managed paths
(eg libblas, liblapack pulled in by ffmpeg) stay reachable since the upstream
action does not execute postinst scripts on cache restore.
inputs:
packages:
description: Space-delimited list of apt packages to install.
required: true
version:
description: Cache version. Bump to invalidate the cache.
required: false
default: "1"
execute_install_scripts:
description: >-
Pass-through to awalsh128/cache-apt-pkgs-action. Postinst scripts are not
actually cached by the upstream action, so this is largely a no-op today.
required: false
default: "false"
runs:
using: composite
steps:
- name: Remove conflicting Microsoft apt source
shell: bash
run: sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
- name: Install apt packages via cache
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3
with:
packages: ${{ inputs.packages }}
version: ${{ inputs.version }}
execute_install_scripts: ${{ inputs.execute_install_scripts }}
- name: Refresh dynamic linker cache
shell: bash
run: |
# awalsh128/cache-apt-pkgs-action does not run postinst scripts on
# cache restore, so update-alternatives symlinks (eg the one libblas
# creates at /usr/lib/<multiarch>/libblas.so.3) are never produced.
# Add every /usr/lib/<multiarch> subdirectory that holds shared
# libraries to the ldconfig search path so the dynamic linker still
# finds them. Use dpkg-architecture to derive the host's multiarch
# tuple so this works on non-x86_64 runners too.
multiarch="$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
find "/usr/lib/${multiarch}" -mindepth 2 -maxdepth 2 \
-name '*.so.*' -printf '%h\n' \
| sort -u \
| sudo tee /etc/ld.so.conf.d/zzz-cache-apt-extras.conf > /dev/null
sudo ldconfig
@@ -0,0 +1,42 @@
name: Set up uv and managed Python
description: >-
Pins uv (avoids the raw.githubusercontent.com manifest fetch on cache miss)
and proactively installs the requested Python so cached venvs created with
`uv venv` resolve their interpreter symlinks in jobs that only restore the
venv. setup-uv alone only sets UV_PYTHON, it does not actually fetch the
interpreter until uv first uses it, so jobs that just activate the venv
blow up with broken symlinks on cache hit.
inputs:
python-version:
description: The Python version uv should install and use.
required: true
uv-version:
description: The uv version setup-uv should install.
required: true
outputs:
python-version:
description: The Python version uv reports as installed.
value: ${{ steps.uv.outputs.python-version }}
runs:
using: composite
steps:
- name: Set up uv
id: uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: ${{ inputs.uv-version }}
python-version: ${{ inputs.python-version }}
# Persist astral's managed Python across jobs so 'uv venv' below is
# fast on the second job onwards.
cache-python: true
# Lint-only and codegen jobs touch no Python deps, so the post-step
# cache save would otherwise abort the job.
ignore-nothing-to-cache: true
- name: Install Python interpreter
shell: bash
env:
PYTHON_VERSION: ${{ inputs.python-version }}
run: uv python install "${PYTHON_VERSION}"
+1
View File
@@ -25,6 +25,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
- .vscode/tasks.json contains useful commands used for development.
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
## Python Syntax Notes
+3
View File
@@ -11,3 +11,6 @@ updates:
- github_actions
cooldown:
default-days: 7
ignore:
# Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
- dependency-name: "github/gh-aw-actions/**"
+27
View File
@@ -6,6 +6,7 @@
"pep621",
"pip_requirements",
"pre-commit",
"dockerfile",
"custom.regex",
"homeassistant-manifest"
],
@@ -21,6 +22,10 @@
]
},
"dockerfile": {
"managerFilePatterns": ["/^Dockerfile$/"]
},
"homeassistant-manifest": {
"managerFilePatterns": [
"/^homeassistant/components/[^/]+/manifest\\.json$/"
@@ -35,6 +40,14 @@
"matchStrings": ["required-version = \">=(?<currentValue>[\\d.]+)\""],
"depNameTemplate": "ruff",
"datasourceTemplate": "pypi"
},
{
"customType": "regex",
"description": "Update go2rtc RECOMMENDED_VERSION in const.py alongside the Dockerfile pin",
"managerFilePatterns": ["/^homeassistant/components/go2rtc/const\\.py$/"],
"matchStrings": ["RECOMMENDED_VERSION = \"(?<currentValue>[\\d.]+)\""],
"depNameTemplate": "ghcr.io/alexxit/go2rtc",
"datasourceTemplate": "docker"
}
],
@@ -115,6 +128,7 @@
"standard-aifc",
"standard-telnetlib",
"ulid-transform",
"unidiff",
"url-normalize",
"xmltodict"
],
@@ -184,6 +198,13 @@
"enabled": true,
"labels": ["dependency"]
},
{
"description": "Docker allowlist (ghcr.io exposes no release timestamps so the global cooldown needs to be bypassed)",
"matchPackageNames": ["ghcr.io/alexxit/go2rtc"],
"enabled": true,
"minimumReleaseAge": null,
"labels": ["dependency"]
},
{
"description": "Group ruff pre-commit hook with its PyPI twin into one PR",
"matchPackageNames": ["astral-sh/ruff-pre-commit", "ruff"],
@@ -213,6 +234,12 @@
"matchPackageNames": ["pylint", "astroid"],
"groupName": "pylint",
"groupSlug": "pylint"
},
{
"description": "Group go2rtc Dockerfile pin with const.py RECOMMENDED_VERSION into one PR",
"matchPackageNames": ["ghcr.io/alexxit/go2rtc"],
"groupName": "go2rtc",
"groupSlug": "go2rtc"
}
]
}
+1 -1
View File
@@ -14,7 +14,7 @@ env:
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"
# Base image version from https://github.com/home-assistant/docker
BASE_IMAGE_VERSION: "2026.04.0"
BASE_IMAGE_VERSION: "2026.05.0"
ARCHITECTURES: '["amd64", "aarch64"]'
permissions: {}
@@ -0,0 +1,74 @@
name: Check requirements (deterministic)
# Stage 1 of the Check requirements pipeline.
#
# Runs the deterministic Python checks and uploads the structured
# results as an artifact. Stage 2 (the agentic workflow defined in
# `check-requirements.md`) consumes the artifact on completion.
# yamllint disable-line rule:truthy
on:
# Auto-trigger on PRs that touch tracked requirement files is disabled
# for now while we iterate — testing the workflow_run handoff to the
# agentic stage is hard with an auto-trigger. Re-enable once the chain
# has been validated end-to-end.
# pull_request:
# types: [opened, synchronize, reopened]
# paths:
# - "**/requirements*.txt"
# - "homeassistant/package_constraints.txt"
workflow_dispatch:
inputs:
pull_request_number:
description: "Pull request number to (re-)check"
required: true
type: number
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ inputs.pull_request_number || github.event.pull_request.number }}
cancel-in-progress: true
jobs:
deterministic:
name: Run deterministic requirement checks
runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: read # To fetch the PR diff via gh CLI
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
check-latest: true
- name: Install script dependencies
run: pip install -r script/check_requirements/requirements.txt
- name: Collect PR diff
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ inputs.pull_request_number || github.event.pull_request.number }}
run: |
mkdir -p deterministic
gh pr diff "${PR_NUMBER}" > deterministic/pr.diff
- name: Run deterministic checks
env:
PR_NUMBER: ${{ inputs.pull_request_number || github.event.pull_request.number }}
run: |
python -m script.check_requirements \
--pr-number "${PR_NUMBER}" \
--diff deterministic/pr.diff \
--output deterministic/results.json
- name: Upload deterministic-results artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: check-requirements-deterministic
path: deterministic/results.json
if-no-files-found: error
retention-days: 7
File diff suppressed because it is too large Load Diff
+358 -385
View File
@@ -1,405 +1,378 @@
---
on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- "requirements*.txt"
- "homeassistant/package_constraints.txt"
- "pyproject.toml"
forks: ["*"]
workflow_dispatch:
inputs:
pull_request_number:
description: "Pull request number to (re-)check"
required: true
type: number
roles: all
workflow_run:
workflows: ["Check requirements (deterministic)"]
types: [completed]
permissions:
contents: read
pull-requests: read
actions: read
issues: read
pull-requests: read
network:
allowed:
- python
tools:
web-fetch: {}
github:
toolsets: [default]
toolsets: [default, actions]
min-integrity: unapproved
safe-outputs:
add-comment:
max: 1
target: "${{ needs.extract_pr_number.outputs.pr_number }}"
needs:
- extract_pr_number
jobs:
extract_pr_number:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
outputs:
pr_number: ${{ steps.extract.outputs.pr_number }}
steps:
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Extract PR number from artifact
id: extract
run: |
PR=$(jq -r '.pr_number' /tmp/deterministic/results.json)
echo "pr_number=${PR}" >> "${GITHUB_OUTPUT}"
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: true
steps:
- name: Download deterministic-results artifact
if: github.event.workflow_run.conclusion == 'success'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: check-requirements-deterministic
path: /tmp/gh-aw/deterministic
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
post-steps:
- name: Verify agent produced an add_comment safe-output
if: always() && github.event.workflow_run.conclusion == 'success'
run: |
OUTPUT=/tmp/gh-aw/agent_output.json
if [ ! -f "${OUTPUT}" ]; then
echo "::error::Agent output file ${OUTPUT} is missing; the agent did not run to completion."
exit 1
fi
if ! grep -q '"add_comment"' "${OUTPUT}"; then
echo "::error::Agent did not emit an add_comment safe-output; no review comment was posted to the PR."
echo "Agent output:"
cat "${OUTPUT}"
exit 1
fi
description: >
Checks changed Python package requirements on PRs targeting the core repo
(including PRs opened from forks) and verifies licenses match PyPI metadata, source
repositories are publicly accessible, PyPI releases were uploaded via
automated CI (Trusted Publisher attestation), the package's release pipeline
uses OIDC or equivalent automated credentials (not static tokens), and the PR
description contains the required links.
Resolves the deterministic-stage artifact's NEEDS_AGENT checks for changed
Python package requirements on PRs targeting the core repo, then posts the
final review comment. Triggered by completion of the deterministic workflow.
Reads the uploaded artifact from disk, replaces placeholders for any check
whose status is `needs_agent`, and posts the merged comment using the PR
number recorded inside the artifact itself. Each check kind has a dedicated
instruction section below; if the artifact contains a check kind that does
not have a section here, the agent fails hard rather than guess.
---
# Requirements License and Availability Check
You are a code review assistant for the Home Assistant project. Your job is to
review changes to Python package requirements and verify they meet the project's
standards.
## Context
- Home Assistant uses `requirements_all.txt` (all integration packages),
`requirements.txt` (core packages), `requirements_test.txt` (test
dependencies), and `requirements_test_all.txt` (all test dependencies) to
declare Python dependencies.
- Each integration lists its packages in `homeassistant/components/<name>/manifest.json`
under the `requirements` field.
- Allowed licenses are maintained in `script/licenses.py` under
`OSI_APPROVED_LICENSES_SPDX` (SPDX identifiers) and `OSI_APPROVED_LICENSES`
(classifier strings).
## Step 1 — Identify Changed Packages
Use the GitHub tool to fetch the PR diff. Look for lines that were added (`+`)
or removed (`-`) in **any** of these files:
- `requirements.txt`
- `requirements_all.txt`
- `requirements_test.txt`
- `requirements_test_all.txt`
- `homeassistant/package_constraints.txt`
- `pyproject.toml`
For each changed line that contains a package pin (e.g. `SomePackage==1.2.3`),
classify it as:
- **New package**: the package name appears only in `+` lines, with no
corresponding `-` line for the same package name.
- **Version bump**: the same package name appears in both `+` lines (new
version) and `-` lines (old version), with different version numbers.
Record the **old version** and **new version** for every version bump — you
will need these values in Step 4.
## Step 2 — Check License via PyPI
For each new or bumped package:
1. Fetch `https://pypi.org/pypi/{package_name}/json` (use the exact
package name as it appears on the requirements file).
2. From the JSON response, extract:
- `info.license` — free-text license field
- `info.license_expression` — SPDX expression (if present)
- `info.classifiers` — filter for entries starting with `"License ::"`,
then normalize each match the same way as `script/licenses.py` by
extracting the final ` :: ` segment (for example,
`"License :: OSI Approved :: MIT License"``"MIT License"`).
3. Determine if the license is in the approved list from `script/licenses.py`:
- SPDX identifiers: compare against `OSI_APPROVED_LICENSES_SPDX`
- Normalized classifier strings: compare against `OSI_APPROVED_LICENSES`
4. Flag a package as ❌ if the license is unknown, missing, or not in the
approved list. Flag as ⚠️ if the license information is ambiguous or cannot
be definitively determined.
## Step 2b — Verify PyPI Release Was Uploaded by CI
For each new or bumped package, verify that the release on PyPI was published
automatically by a CI pipeline (via OIDC Trusted Publisher), not uploaded
manually.
1. Fetch the PyPI JSON for the specific version being introduced or bumped:
`https://pypi.org/pypi/{package_name}/{version}/json`
2. Inspect the `urls` array in the response. For each distribution file (wheel
or sdist), note the filename.
3. For each filename, attempt to fetch the PyPI provenance attestation:
`https://pypi.org/integrity/{package_name}/{version}/{filename}/provenance`
- If the response is HTTP 200 and contains a valid attestation object,
inspect `attestation_bundles[*].publisher`. A Trusted Publisher attestation
will have a `kind` identifying the CI system (e.g. `"GitHub Actions"`,
`"GitLab"`) and a `repository` or `project` field matching the source
repository.
- If at least one distribution file has a valid Trusted Publisher attestation,
mark ✅ CI-uploaded.
- If no attestation is found for any file (404 for all), mark ❌ — "Release
has no provenance attestation; it may have been uploaded manually".
- If an attestation exists but the `publisher` does not identify a recognized
CI system or Trusted Publisher, mark ⚠️ — "Attestation present but
publisher cannot be verified as automated CI".
Note: if PyPI returns an error fetching the per-version JSON, fall back to the
latest JSON (`https://pypi.org/pypi/{package_name}/json`) and look up the
specific version in the `releases` dict.
## Step 3 — Identify Repository URL
For each new or bumped package:
1. From the PyPI JSON at `info.project_urls`, find the source repository URL
(keys such as `"Source"`, `"Homepage"`, `"Repository"`, or `"Source Code"`).
2. Record that repository URL for later checks.
3. If no suitable repository URL is present, mark ❌ with a note that the
source repository URL is missing and cannot be verified.
## Step 4 — Check PR Description
Read the PR body from the GitHub API using the PR number from the workflow
context (`pull-request-number`). If that value is absent, use the
`workflow_dispatch` input `pull_request_number`.
Extract all URLs present in the PR body.
### 4a — New packages: repository link required
For **new packages** (brand-new dependency not previously in any requirements
file): the PR description must contain a link that points to the package's
**source repository** as identified in Step 3 (the URL recorded from
`info.project_urls`). A PyPI page link alone is **not** acceptable — the link
must point directly to the source repository (e.g. a GitHub or GitLab URL).
- If a URL in the PR body matches (or is a sub-path of) the source repository
URL identified via PyPI, mark ✅.
- If the PR body contains a source repository URL that does **not** match the
repository URL found in the package's PyPI metadata (`info.project_urls`),
mark ❌ — "PR description links to `<pr_url>` but PyPI reports the source
repository as `<pypi_repo_url>`; please use the correct repository URL."
- If no source repository URL is present in the PR body at all, mark ❌ —
"PR description must link to the source repository at `<repo_url>` (found
via PyPI). A PyPI page link is not sufficient."
### 4b — Version bumps: changelog or diff link required
For **version bumps**: the PR description must contain a link to a changelog,
release notes page, or a diff/comparison URL that references the **correct
versions** being bumped (old → new).
Checks to perform for each bumped package (old version = X, new version = Y):
1. Extract all URLs from the PR body that contain the repository's domain or
path (as identified in Step 3).
2. Verify that at least one such URL includes both the old version string and
new version string in some form — e.g. a GitHub compare URL like
`compare/vX...vY`, a releases URL mentioning version Y, or a
`CHANGELOG.md` anchor referencing Y.
3. If no URL matches, check if the PR body contains any changelog/diff link at
all for this package.
Outcome:
- ✅ — a URL pointing to the correct repo with version references covering the
exact bump (X → Y).
- ⚠️ — a changelog/diff link exists but does not clearly reference the correct
versions or the correct repository; explain what was found and what is
expected.
- ❌ — no changelog or diff link found at all in the PR description for this
package.
### 4c — Diff consistency check
For each **version bump**, verify that the version change recorded in the diff
(Step 1) is internally consistent:
- The `-` line must contain the old version and the `+` line must contain the
new version for the same package name.
- Flag ❌ if the diff shows a downgrade (new version < old version) without an
explanation, or if the version strings cannot be parsed.
## Step 5 — Verify Source Repository is Publicly Accessible
Before inspecting the release pipeline, confirm that the source repository
identified in Step 3 is publicly reachable.
For each new or bumped package:
1. Use the source repository URL recorded in Step 3.
2. If no repository URL was found in `info.project_urls`, mark ❌ — "No source
repository URL found in PyPI metadata; a public source repository is
required."
3. If a repository URL was found, perform a GET request to that URL (using
web-fetch). If the response is HTTP 200 and returns a publicly accessible
page (not a login redirect or error page), mark ✅.
4. If the response is non-200, the URL redirects to a login/authentication page,
or the repository appears private or unavailable, mark ❌ — "Source
repository at `<repo_url>` is not publicly accessible. Home Assistant
requires all dependencies to have publicly available source code." **Do not
proceed with the release pipeline check (Step 6) for this package.**
## Step 6 — Check Release Pipeline Sanity
For each new or bumped package, determine the source repository host from the
URL identified in Step 3, then inspect whether the project's release/publish CI
workflow is sane. The checks differ by hosting provider.
### GitHub repositories (`github.com`)
1. Using the GitHub API, list the workflows in the source repository:
`GET /repos/{owner}/{repo}/actions/workflows`
2. Identify any workflow whose name or filename suggests publishing to PyPI
(e.g., contains "release", "publish", "pypi", or "deploy").
3. Fetch the workflow file content and check the following:
a. **Trigger sanity**: The publish job should be triggered by `push` to tags,
`release: published`, or `workflow_run` on a release job — **not** solely
by `workflow_dispatch` with no additional guards. A `workflow_dispatch`
trigger alongside other triggers is acceptable. Mark ❌ if the only trigger
is manual `workflow_dispatch` with no environment protection rules.
b. **OIDC / Trusted Publisher**: The workflow should use OIDC-based publishing.
Look for `id-token: write` permission and one of:
- `pypa/gh-action-pypi-publish` action
- `actions/attest-build-provenance` action
- Any step that sets `TWINE_PASSWORD` from `secrets.PYPI_TOKEN` directly
(flag ❌ if a long-lived API token is used instead of OIDC).
Mark ✅ if OIDC is used, ⚠️ if the publish method cannot be determined,
❌ if a static secret token is the only credential.
c. **No manual upload bypass**: Verify there is no step that calls
`twine upload` or `pip upload` outside of a properly gated job (e.g., one
that requires an environment approval). Flag ⚠️ if such steps exist.
4. If no publish workflow is found in the repository, mark ⚠️ — "No publish
workflow found; it is unclear how this package is released to PyPI."
### GitLab repositories (`gitlab.com` or self-hosted GitLab)
1. Use the GitLab REST API to list CI/CD pipeline configuration files. First
resolve the project ID via
`GET https://gitlab.com/api/v4/projects/{url-encoded-namespace-and-name}`
and note the `id` field.
2. Fetch the repository's `.gitlab-ci.yml` (and any included files) using
`GET https://gitlab.com/api/v4/projects/{id}/repository/files/.gitlab-ci.yml/raw?ref=HEAD`
(use web-fetch for public repos).
3. Identify any job whose name or `stage` suggests publishing to PyPI
(e.g., "publish", "deploy", "release", "pypi").
4. For each such job, check:
a. **Trigger sanity**: The job should run only on tag pipelines (`only: tags`
or `rules: - if: $CI_COMMIT_TAG`) or on protected branches — **not**
solely on manual triggers (`when: manual`) with no additional protection.
Mark ❌ if the only trigger is manual with no environment or protected-branch
guard.
b. **Automated credentials**: The job should use GitLab's OIDC ID token
(`id_tokens:` block) and `pypa/gh-action-pypi-publish` equivalent, or
reference `secrets.PYPI_TOKEN` / `$PYPI_TOKEN` injected from GitLab CI/CD
protected variables (flag ❌ if the token is hard-coded or unprotected).
Mark ✅ if OIDC or protected CI variables are used, ⚠️ if the method
cannot be determined, ❌ if credentials appear to be insecure.
c. **No manual upload bypass**: Flag ⚠️ if any job calls `twine upload`
without being behind a protected-variable or environment guard.
5. If no publish job is found, mark ⚠️ — "No publish job found in .gitlab-ci.yml;
it is unclear how this package is released to PyPI."
### Other code hosting providers
For repositories hosted on platforms other than GitHub or GitLab (e.g.,
Bitbucket, Codeberg, Gitea, Sourcehut):
1. Use web-fetch to retrieve the repository's root page and look for any
publicly visible CI configuration files (e.g., `.circleci/config.yml`,
`Jenkinsfile`, `azure-pipelines.yml`, `bitbucket-pipelines.yml`,
`.builds/*.yml` for Sourcehut).
2. Apply the same conceptual checks as above:
- Does publishing run on automated triggers (tags/releases), not solely
manual ones?
- Are credentials injected by the CI system (not hard-coded)?
- Is there a `twine upload` or equivalent step that could be run manually?
3. If no CI configuration can be retrieved, mark ⚠️ — "Release pipeline could
not be inspected; hosting provider is not GitHub or GitLab."
## Step 7 — Post a Review Comment
**Always** post a review comment using `add_comment`, regardless of whether
packages pass or fail. Use the following structure:
**Note on deduplication**: The workflow automatically updates any previous
requirements-check comment on the PR in place (preserving its position in the
thread). If no previous comment exists, the newly created comment is kept as-is.
You do not need to search for or update previous comments yourself.
### Comment structure
Begin every comment with the HTML marker `<!-- requirements-check -->` on its
own line (this is used by the workflow to find the previous comment and update
it on the next run).
### 7a — Overall summary line
Begin the comment with a single summary line, before anything else:
- If everything passed: `All requirements checks passed. ✅`
- If there are failures or warnings: `⚠️ Some checks require attention — see the details below.`
### 7b — Summary table
Render a compact table where every check column contains **only the status
icon** (✅, ⚠️, or ❌). No explanatory text belongs inside the table cells —
all detail goes in the per-package sections below.
Use `—` (em dash) when a check was skipped (e.g. Release Pipeline is skipped
when the repository is not publicly accessible).
```
<!-- requirements-check -->
## Requirements Check
| Package | Type | Old→New | License | Repo Public | CI Upload | Release Pipeline | PR Link | Diff Consistent |
|---------|------|---------|---------|-------------|-----------|------------------|---------|-----------------|
| PackageA | bump | 1.2.3→1.3.0 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| PackageB | new | —→4.5.6 | ❌ | ✅ | ❌ | ⚠️ | ❌ | ✅ |
| PackageC | bump | 2.0.0→2.1.0 | ✅ | ❌ | — | — | ⚠️ | ✅ |
```
### 7c — Per-package detail sections
After the table, add one collapsible `<details>` block per package.
- If **all checks passed** for that package, render the block **collapsed**
(no `open` attribute) so the comment stays concise.
- If **any check failed or produced a warning**, render the block **open**
(`<details open>`) so the contributor sees the issues immediately.
Each block must include the full detail for every check: the license found, the
repository URL, whether a provenance attestation was found, the release
pipeline findings, the PR link found (or missing), and whether the diff is
consistent. For failed or warned checks, explain exactly what the contributor
must fix, including the expected source repository URL, expected version range,
etc.
Template (repeat for each package):
```
<details open>
<summary><strong>PackageB 📦 new —→4.5.6</strong></summary>
- **License**: ❌ License is `UNKNOWN` — not in the approved list. Check PyPI metadata and `script/licenses.py`.
- **Repository Public**: ✅ https://github.com/example/packageb is publicly accessible.
- **CI Upload**: ❌ No provenance attestation found for any distribution file. The release may have been uploaded manually.
- **Release Pipeline**: ⚠️ No publish workflow found in the repository; it is unclear how this package is released to PyPI.
- **PR Link**: ❌ PR description must link to the source repository at https://github.com/example/packageb (a PyPI page link is not sufficient).
- **Diff Consistent**: ✅
</details>
```
Collapsed example (all checks passed):
```
<details>
<summary><strong>PackageA 📦 bump 1.2.3→1.3.0</strong></summary>
- **License**: ✅ MIT
- **Repository Public**: ✅ https://github.com/example/packagea
- **CI Upload**: ✅ Trusted Publisher attestation found (GitHub Actions).
- **Release Pipeline**: ✅ OIDC via `pypa/gh-action-pypi-publish`; triggered on `release: published`; `environment: release` gate.
- **PR Link**: ✅ https://github.com/example/packagea/compare/v1.2.3...v1.3.0
- **Diff Consistent**: ✅
</details>
```
# Check requirements (AW)
You are a code review assistant for the Home Assistant project. The
deterministic stage has already evaluated every check it can on its own
and produced an artifact containing the PR number, per-package check
results, and a pre-rendered comment with placeholders. **Your only job is
to read that artifact, resolve any `needs_agent` checks, and post the
final comment.**
## Step 1 — Read the deterministic-stage artifact
The deterministic stage uploaded its results to the runner at
`/tmp/gh-aw/deterministic/results.json`.
The JSON has this shape:
- `pr_number` — the PR being checked. The `add_comment` safe-output is
already targeted at this PR (a pre-job extracts `pr_number` from the
artifact and the workflow wires it into the safe-output config via
`needs.extract_pr_number.outputs.pr_number`), so **you do not need to
set `item_number` yourself** — just emit `add_comment` with the
rendered body.
- `needs_agent``true` iff any package's check needs resolution.
- `packages[]` — one entry per changed package. Each entry has:
- `name`, `old_version` (`null` for a newly added package; otherwise the
previous pin), `new_version`, `repo_url`, `publisher_kind`.
- `checks` — a dict keyed by **check kind** (string). Each value has a
`status` (`pass`, `warn`, `fail`, or `needs_agent`) and `details`.
- `rendered_comment` — the final PR comment body, already rendered. For
every check whose status is `needs_agent` it contains two placeholders
you must replace:
- `{{CHECK_CELL:<pkg-name>:<check-kind>}}` — one cell of the summary
table. Replace with exactly one of `✅`, `⚠️`, `❌`.
- `{{CHECK_DETAIL:<pkg-name>:<check-kind>}}` — the body of one bullet
in the package's `<details>` block. Replace with
`<icon> <one-line explanation>` (the bullet's leading
`- **<label>**:` is already rendered — replace only the placeholder).
You **must not** modify any other content in `rendered_comment`. Do not
re-evaluate checks that already have a deterministic status. Do not add
or remove packages.
## Step 2 — Resolve each `needs_agent` check
For each `package` in `packages`:
For each `(check_kind, result)` in `package.checks` where
`result.status == "needs_agent"`:
1. Look up `## Check kind: <check_kind>` in the **Check instructions**
section below.
2. **If no matching section exists**: emit a single `add_comment` whose
body is:
```
<!-- requirements-check -->
## Check requirements
❌ Internal error: the deterministic artifact contains a check kind
(`<check_kind>` on package `<pkg-name>`) that this workflow has no
instructions for. Update `.github/workflows/check-requirements.md`
to add a matching `## Check kind: <check_kind>` section, or remove
the kind from the deterministic stage.
```
Then stop. **Do not improvise** a verdict for an unknown check kind.
3. Otherwise, follow the instructions in that section. They tell you
which icon (✅/⚠️/❌) and one-line explanation to produce.
## Step 3 — Post the comment
1. Replace every `{{CHECK_CELL:…}}` and `{{CHECK_DETAIL:…}}` placeholder
in `rendered_comment` with the resolved value.
2. Emit the resulting markdown using `add_comment` — set `body` to the
merged `rendered_comment` verbatim (the leading
`<!-- requirements-check -->` marker must be preserved). The PR
target is already set by the workflow; do not pass `item_number`.
If the artifact's top-level `needs_agent` is `false` (no checks need
you), emit `rendered_comment` unchanged.
## Check instructions
### Check kind: `repo_public`
Verify that the package's source repository is publicly reachable.
1. Read `package.repo_url`.
2. Use the `web-fetch` tool to GET that URL.
3. Decide the verdict:
- HTTP 200, returns a public repository page → ✅
`<repo_url> is publicly accessible.`
- HTTP 4xx/5xx, or the response redirects to a login / sign-in page →
❌ `Source repository at <repo_url> is not publicly accessible.
Home Assistant requires all dependencies to have publicly available
source code.`
- Any other inconclusive result → ⚠️ with a one-line description.
If `repo_public` resolves to ❌ for a package, **also** mark that
package's `release_pipeline` and `async_blocking` cells/details as ``
(em dash) and explain `Skipped because the source repository is not
publicly accessible.` — neither check can be performed without a public
repo.
### Check kind: `pr_link`
Verify the PR description contains the right link for the change.
1. Fetch the PR body via the GitHub MCP tool, using the `pr_number`
field from the artifact.
2. Extract all URLs from the body.
3. For a **new package** (`package.old_version` is `null`):
- The PR body must contain a URL that points at `package.repo_url`
(any sub-path of the same `owner/repo` on the same host is
acceptable). A PyPI link is **not** sufficient.
- ✅ if such a URL is present.
- ❌ otherwise:
`PR description must link to the source repository at <repo_url>.
A PyPI page link is not sufficient.`
4. For a **version bump** (`package.old_version` is not `null`):
- The PR body must contain a URL on the same host as
`package.repo_url` that references **both** `package.old_version`
and `package.new_version` (e.g. a GitHub compare URL
`compare/vX...vY`, a release / changelog URL containing both
versions, etc.).
- ✅ if such a URL is present and the versions match the actual bump.
- ❌ otherwise:
`PR description should link to a changelog or compare URL on
<repo_url> that mentions both <old_version> and <new_version>.`
### Check kind: `release_pipeline`
Inspect the upstream project's release / publish CI pipeline.
For each package needing inspection, determine the source repository
host from `package.repo_url`, then apply the corresponding checklist.
#### GitHub repositories (`github.com`)
1. List workflows: `GET /repos/{owner}/{repo}/actions/workflows`.
2. Identify any workflow whose name or filename suggests publishing to
PyPI (`release`, `publish`, `pypi`, or `deploy`).
3. Fetch the workflow file and check:
- **Trigger sanity**: triggered by `push` to tags,
`release: published`, or `workflow_run` on a release job —
**not** solely `workflow_dispatch` with no environment-protection
guard.
- **OIDC / Trusted Publisher**: look for `id-token: write` and one of
`pypa/gh-action-pypi-publish`, `actions/attest-build-provenance`,
or `TWINE_PASSWORD` from a static `secrets.PYPI_TOKEN`.
- **No manual upload bypass**: no ungated `twine upload` or
`pip upload`.
4. Verdict:
- ✅ if OIDC + sane triggers + no bypass.
- ⚠️ if static token but version bump, or details unclear.
- ❌ if static token on a new package, or only-manual triggers with
no environment protection.
#### GitLab repositories (`gitlab.com` or self-hosted GitLab)
1. Resolve the project ID via
`GET https://gitlab.com/api/v4/projects/{url-encoded-namespace-and-name}`.
2. Fetch `.gitlab-ci.yml` via
`GET https://gitlab.com/api/v4/projects/{id}/repository/files/.gitlab-ci.yml/raw?ref=HEAD`.
3. Apply the same conceptual checks: tag-only / protected-branch
triggers, GitLab OIDC `id_tokens` or CI/CD protected `PYPI_TOKEN`, no
ungated `twine upload`. Same verdict rules as GitHub.
#### Other code hosting providers (Bitbucket, Codeberg, Gitea, Sourcehut, …)
1. Use `web-fetch` to retrieve any visible CI configuration
(`.circleci/config.yml`, `Jenkinsfile`, `azure-pipelines.yml`,
`bitbucket-pipelines.yml`, `.builds/*.yml`).
2. Apply the conceptual checks: automated triggers, CI-injected
credentials, no manual `twine upload`.
3. If no CI config can be retrieved: ⚠️ `Release pipeline could not be
inspected; hosting provider is not GitHub or GitLab.`
### Check kind: `async_blocking`
Verify whether the dependency performs blocking I/O inside async code
paths. Home Assistant runs on a single asyncio event loop, so a library
that exposes an `async` surface must not call blocking APIs from inside
its `async def` functions — that stalls the whole loop. A purely sync
library is fine: Home Assistant integrations are expected to wrap such
calls in an executor.
**Two modes — pick by inspecting `package.old_version`:**
- `old_version` is `null` → **new package**: review the *entire current
source tree*. Nothing about this dependency has been vetted before.
- `old_version` is a string → **version bump**: review only the *diff
between `old_version` and `new_version`*. The previous version was
already accepted, so blocking calls that were present in
`old_version` are not regressions; report only what `new_version`
introduces.
#### Step 1 — Decide whether the library exposes an async surface
Use the `github` MCP tool (for `github.com` repos) or `web-fetch`
(other hosts) on `package.repo_url`. Always inspect the tag /
ref matching `new_version` (e.g. `v{new_version}` or `{new_version}`).
- Locate the top-level package directory (usually named after the
import name, often equal or close to `package.name`).
- Check `pyproject.toml` / `setup.py` / `setup.cfg` / `README*` for
async indicators (`Framework :: AsyncIO` trove classifier, `asyncio`
/ `aiohttp` / `httpx` / `anyio` in dependencies, an async usage
example in the README).
- Grep the package source for `async def`. A handful of `async def`
entries in the public modules is enough to treat the library as
having an async surface.
If the library is **sync-only** (no `async def` in its public modules
and no async framework dependency) → ✅
`Sync-only library; Home Assistant integrations must wrap calls in an
executor.` *This verdict is the same in both modes.*
#### Step 2a — Mode: new package (`old_version` is `null`)
Inspect **every `async def` in the public modules** for blocking
patterns. Walk transitively into helpers the async functions call.
#### Step 2b — Mode: version bump (`old_version` is a string)
Fetch the diff between the two tags and review **only changed lines**:
- GitHub: `GET /repos/{owner}/{repo}/compare/{old_tag}...{new_tag}` via
the `github` MCP tool, or
`https://github.com/{owner}/{repo}/compare/{old_tag}...{new_tag}.diff`
via `web-fetch`. Try the common tag formats in order until one
resolves: `v{version}`, `{version}`, `release-{version}`.
- GitLab: `https://gitlab.com/{namespace}/{project}/-/compare/{old_tag}...{new_tag}.diff`.
- Other hosts: use the project's equivalent compare URL via
`web-fetch`.
If neither tag format resolves on the host, fall back to a full review
(Step 2a) and mention in the detail that the diff was unavailable.
When reviewing the diff, only flag blocking patterns that appear in
**added lines** *inside or reachable from* an `async def`. A blocking
call that existed in `old_version` and is unchanged is not a regression
for this bump.
#### Step 3 — Blocking patterns to look for
In both modes, the patterns to flag inside `async def` bodies are:
- Sync HTTP: `requests.`, `urllib.request`, `urllib3.` direct use,
`http.client.`, sync `httpx.Client(` / `httpx.get(` (NOT the
`AsyncClient`), `pycurl`.
- `time.sleep(` (must be `await asyncio.sleep(`).
- Sync sockets: bare `socket.socket` reads/writes, `ssl.wrap_socket`,
blocking `select.select`.
- File I/O: `open(` / `pathlib.Path.read_*` / `.write_*` for
non-trivial sizes (small one-shot reads during import are
acceptable; reads/writes on the request path are not — prefer
`aiofiles` / executor).
- Sync DB drivers used directly: `sqlite3`, `psycopg2`, `pymysql`,
`pymongo` (sync client), `redis.Redis` (sync client).
- `subprocess.run` / `subprocess.call` / `os.system` (must be
`asyncio.create_subprocess_*`).
A call that is clearly dispatched to an executor
(`run_in_executor`, `asyncio.to_thread`, `anyio.to_thread.run_sync`)
does NOT count as blocking.
#### Step 4 — Verdict
- ✅ — no offending blocking pattern in the surface being reviewed
(whole tree for a new package, added lines for a bump). For a bump,
phrase the detail as `No new blocking calls introduced in
{old_version} → {new_version}.`.
- ⚠️ — blocking calls exist only in sync helpers that the async API
does not call, or only on a clearly non-hot path (e.g. one-shot
setup before the event loop is running). Cite at least one
`<file>:<line>` and explain why it is not on the hot path.
- ❌ — a blocking call is reachable from an `async def` that is part
of the public API on the request / polling path (for a bump: the
call was introduced or moved onto the hot path by this version).
Cite the offending `<file>:<line>` as a clickable link on the repo
host so the contributor can jump to it.
## Notes
- Be constructive and helpful. Provide direct links where possible so the
contributor can quickly fix the issue.
- If PyPI returns an error for a package, mention that it could not be found and
suggest the contributor verify the package name.
- For packages that only appear in `homeassistant/package_constraints.txt` or
`pyproject.toml` without being tied to a specific integration, the PR
description link requirement still applies.
- When checking test-only packages (from `requirements_test.txt` or
`requirements_test_all.txt`), apply the same license, repository, and PR
description checks as for production dependencies.
- A package that appears in both a production file and a test file should only
be reported once; use the production file entry as the canonical one.
- This workflow is only triggered when a commit actually changes one of the
tracked requirements files (for `synchronize` events GitHub compares the
before/after SHAs of the push, not the entire PR diff). Members can manually
retrigger the workflow via `workflow_dispatch` with the PR number to re-run
the check after updating the PR description or fixing issues without changing
any requirements files. On a retrigger the existing comment is updated in
place so there is always exactly one requirements-check comment in the PR.
- Be constructive and helpful. Reference the inspected workflow / CI
file by URL where useful so the contributor can fix the issue.
- The dedup of the requirements-check comment is handled by gh-aw's
`add_comment` safe-output via the `<!-- requirements-check -->`
marker on the first line of `rendered_comment`.
- If the deterministic workflow concluded with a non-success status,
this workflow's `if:` guard on `Download deterministic-results
artifact` skipped the download. If you find no file at
`/tmp/gh-aw/deterministic/results.json`, emit nothing — the post-step
verification is also gated and will not complain.
+137 -246
View File
@@ -37,7 +37,7 @@ on:
type: boolean
env:
CACHE_VERSION: 3
CACHE_VERSION: 4
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.6"
ADDITIONAL_PYTHON_VERSIONS: "[]"
@@ -60,9 +60,7 @@ env:
# - 15.2 is the latest (as of 9 Feb 2023)
POSTGRESQL_VERSIONS: "['postgres:12.14','postgres:15.2']"
UV_CACHE_DIR: /tmp/uv-cache
APT_CACHE_BASE: /home/runner/work/apt
APT_CACHE_DIR: /home/runner/work/apt/cache
APT_LIST_CACHE_DIR: /home/runner/work/apt/lists
APT_CACHE_VERSION: 1
SQLALCHEMY_WARN_20: 1
PYTHONASYNCIODEBUG: 1
HASS_CI: 1
@@ -86,12 +84,13 @@ jobs:
core: ${{ steps.core.outputs.changes }}
integrations_glob: ${{ steps.info.outputs.integrations_glob }}
integrations: ${{ steps.integrations.outputs.changes }}
apt_cache_key: ${{ steps.generate_apt_cache_key.outputs.key }}
python_cache_key: ${{ steps.generate_python_cache_key.outputs.key }}
requirements: ${{ steps.core.outputs.requirements }}
mariadb_groups: ${{ steps.info.outputs.mariadb_groups }}
postgresql_groups: ${{ steps.info.outputs.postgresql_groups }}
python_versions: ${{ steps.info.outputs.python_versions }}
default_python: ${{ steps.info.outputs.default_python }}
uv_version: ${{ steps.info.outputs.uv_version }}
test_full_suite: ${{ steps.info.outputs.test_full_suite }}
test_group_count: ${{ steps.info.outputs.test_group_count }}
test_groups: ${{ steps.info.outputs.test_groups }}
@@ -116,10 +115,6 @@ jobs:
# Include HA_SHORT_VERSION to force the immediate creation
# of a new uv cache entry after a version bump.
echo "key=venv-${CACHE_VERSION}-${HA_SHORT_VERSION}-${HASH_REQUIREMENTS_TEST}-${HASH_REQUIREMENTS}-${HASH_REQUIREMENTS_ALL}-${HASH_PACKAGE_CONSTRAINTS}-${HASH_GEN_REQUIREMENTS}" >> $GITHUB_OUTPUT
- name: Generate partial apt restore key
id: generate_apt_cache_key
run: |
echo "key=$(lsb_release -rs)-apt-${CACHE_VERSION}-${HA_SHORT_VERSION}" >> $GITHUB_OUTPUT
- name: Filter for core changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: core
@@ -242,6 +237,11 @@ jobs:
echo "postgresql_groups=${postgresql_groups}" >> $GITHUB_OUTPUT
echo "python_versions: ${all_python_versions}"
echo "python_versions=${all_python_versions}" >> $GITHUB_OUTPUT
echo "default_python: ${default_python}"
echo "default_python=${default_python}" >> $GITHUB_OUTPUT
uv_version=$(grep '^uv==' requirements.txt | cut -d'=' -f3)
echo "uv_version: ${uv_version}"
echo "uv_version=${uv_version}" >> $GITHUB_OUTPUT
echo "test_full_suite: ${test_full_suite}"
echo "test_full_suite=${test_full_suite}" >> $GITHUB_OUTPUT
echo "integrations_glob: ${integrations_glob}"
@@ -281,7 +281,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@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # v2.0.3
uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
env:
PREK_SKIP: no-commit-to-branch,mypy,pylint,gen_requirements_all,hassfest,hassfest-metadata,hassfest-mypy-config,zizmor
RUFF_OUTPUT_FORMAT: github
@@ -302,7 +302,7 @@ jobs:
with:
persist-credentials: false
- name: Run zizmor
uses: j178/prek-action@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # v2.0.3
uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
with:
extra-args: --all-files zizmor
@@ -351,12 +351,12 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
- name: Set up uv and Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -384,80 +384,40 @@ jobs:
path: ${{ env.UV_CACHE_DIR }}
key: ${{ steps.generate-uv-key.outputs.full_key }}
restore-keys: ${{ steps.generate-uv-key.outputs.partial_key }}
- name: Check if apt cache exists
id: cache-apt-check
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
lookup-only: ${{ steps.cache-venv.outputs.cache-hit == 'true' }}
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
if: |
steps.cache-venv.outputs.cache-hit != 'true'
|| steps.cache-apt-check.outputs.cache-hit != 'true'
id: install-os-deps
if: steps.cache-venv.outputs.cache-hit != 'true'
timeout-minutes: 10
env:
APT_CACHE_HIT: ${{ steps.cache-apt-check.outputs.cache-hit }}
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
if [[ "${APT_CACHE_HIT}" != 'true' ]]; then
mkdir -p ${APT_CACHE_DIR}
mkdir -p ${APT_LIST_CACHE_DIR}
fi
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
bluez \
ffmpeg \
libturbojpeg \
libxml2-utils \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libswresample-dev \
libswscale-dev \
libudev-dev
if [[ "${APT_CACHE_HIT}" != 'true' ]]; then
sudo chmod -R 755 ${APT_CACHE_BASE}
fi
- name: Save apt cache
if: |
always()
&& steps.cache-apt-check.outputs.cache-hit != 'true'
&& steps.install-os-deps.outcome == 'success'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: ./.github/actions/cache-apt-packages
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
packages: >-
bluez
ffmpeg
libturbojpeg
libxml2-utils
libavcodec-dev
libavdevice-dev
libavfilter-dev
libavformat-dev
libavutil-dev
libswresample-dev
libswscale-dev
libudev-dev
version: ${{ env.APT_CACHE_VERSION }}
execute_install_scripts: true
- name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true'
id: create-venv
env:
PYTHON_VERSION: ${{ steps.python.outputs.python-version }}
run: |
python -m venv venv
uv venv venv --python "${PYTHON_VERSION}"
. venv/bin/activate
python --version
pip install "$(grep '^uv' < requirements.txt)"
uv pip install -U "pip>=25.2"
uv pip install -r requirements.txt
uv pip install -r requirements_all.txt -r requirements_test.txt
uv pip install -e . --config-settings editable_mode=compat
- name: Dump pip freeze
run: |
python -m venv venv
. venv/bin/activate
python --version
uv pip freeze >> pip_freeze.txt
@@ -506,36 +466,22 @@ jobs:
&& github.event.inputs.mypy-only != 'true'
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Restore apt cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
fail-on-cache-miss: true
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
libturbojpeg
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install additional OS dependencies
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: libturbojpeg
version: ${{ env.APT_CACHE_VERSION }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -569,10 +515,10 @@ jobs:
persist-credentials: false
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -605,10 +551,10 @@ jobs:
persist-credentials: false
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Run gen_copilot_instructions.py
run: |
python -m script.gen_copilot_instructions validate
@@ -660,10 +606,10 @@ jobs:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -711,10 +657,10 @@ jobs:
persist-credentials: false
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -764,10 +710,10 @@ jobs:
persist-credentials: false
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -815,10 +761,10 @@ jobs:
persist-credentials: false
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Generate partial mypy restore key
id: generate-mypy-key
run: |
@@ -876,38 +822,26 @@ jobs:
- info
- base
steps:
- name: Restore apt cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
fail-on-cache-miss: true
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
bluez \
ffmpeg \
libturbojpeg
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install additional OS dependencies
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: >-
bluez
ffmpeg
libturbojpeg
version: ${{ env.APT_CACHE_VERSION }}
execute_install_scripts: true
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
python-version-file: ".python-version"
check-latest: true
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ needs.info.outputs.default_python }}
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -952,39 +886,27 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
fail-on-cache-miss: true
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
bluez \
ffmpeg \
libturbojpeg \
libxml2-utils
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install additional OS dependencies
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: >-
bluez
ffmpeg
libturbojpeg
libxml2-utils
version: ${{ env.APT_CACHE_VERSION }}
execute_install_scripts: true
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -1105,40 +1027,28 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
mariadb-group: ${{ fromJson(needs.info.outputs.mariadb_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
fail-on-cache-miss: true
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
bluez \
ffmpeg \
libturbojpeg \
libmariadb-dev-compat \
libxml2-utils
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install additional OS dependencies
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: >-
bluez
ffmpeg
libturbojpeg
libmariadb-dev-compat
libxml2-utils
version: ${{ env.APT_CACHE_VERSION }}
execute_install_scripts: true
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -1266,42 +1176,35 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
postgresql-group: ${{ fromJson(needs.info.outputs.postgresql_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
fail-on-cache-miss: true
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
bluez \
ffmpeg \
libturbojpeg \
libxml2-utils
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
sudo apt-get -y install \
postgresql-server-dev-14
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install additional OS dependencies
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: >-
bluez
ffmpeg
libturbojpeg
libxml2-utils
version: ${{ env.APT_CACHE_VERSION }}
execute_install_scripts: true
- name: Set up PostgreSQL apt repository
run: sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
- name: Cache PostgreSQL development headers
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: postgresql-server-dev-14
version: ${{ env.APT_CACHE_VERSION }}
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -1421,7 +1324,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
fail_ci_if_error: true
flags: full-suite
@@ -1449,39 +1352,27 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Restore apt cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
${{ env.APT_CACHE_DIR }}
${{ env.APT_LIST_CACHE_DIR }}
fail-on-cache-miss: true
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Install additional OS dependencies
timeout-minutes: 10
run: |
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR}
sudo apt-get -y install \
-o Dir::Cache=${APT_CACHE_DIR} \
-o Dir::State::Lists=${APT_LIST_CACHE_DIR} \
bluez \
ffmpeg \
libturbojpeg \
libxml2-utils
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install additional OS dependencies
timeout-minutes: 10
uses: ./.github/actions/cache-apt-packages
with:
packages: >-
bluez
ffmpeg
libturbojpeg
libxml2-utils
version: ${{ env.APT_CACHE_VERSION }}
execute_install_scripts: true
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: ./.github/actions/setup-uv-python
with:
uv-version: ${{ needs.info.outputs.uv_version }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -1592,7 +1483,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # zizmor: ignore[secrets-outside-env]
@@ -1620,7 +1511,7 @@ jobs:
with:
pattern: test-results-*
- name: Upload test results to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
report_type: test_results
fail_ci_if_error: true
+2 -2
View File
@@ -28,11 +28,11 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
with:
category: "/language:python"
@@ -236,7 +236,7 @@ jobs:
- name: Detect duplicates using AI
id: ai_detection
if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true'
uses: actions/ai-inference@e09e65981758de8b2fdab13c2bfb7c7d5493b0b6 # v2.0.7
uses: actions/ai-inference@17ff458cb182449bbb2e43701fcd98f6af8f6570 # v2.1.0
with:
model: openai/gpt-4o
system-prompt: |
@@ -62,7 +62,7 @@ jobs:
- name: Detect language using AI
id: ai_language_detection
if: steps.detect_language.outputs.should_continue == 'true'
uses: actions/ai-inference@e09e65981758de8b2fdab13c2bfb7c7d5493b0b6 # v2.0.7
uses: actions/ai-inference@17ff458cb182449bbb2e43701fcd98f6af8f6570 # v2.1.0
with:
model: openai/gpt-4o-mini
system-prompt: |
+4 -4
View File
@@ -55,11 +55,11 @@ jobs:
- name: Generate app token
id: token
# Pinned to a specific version of the action for security reasons
# v1.7.0
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
# v3.2.0
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
app_id: ${{ secrets.ISSUE_TRIAGE_APP_ID }} # zizmor: ignore[secrets-outside-env]
private_key: ${{ secrets.ISSUE_TRIAGE_APP_PEM }} # zizmor: ignore[secrets-outside-env]
app-id: ${{ secrets.ISSUE_TRIAGE_APP_ID }} # zizmor: ignore[secrets-outside-env]
private-key: ${{ secrets.ISSUE_TRIAGE_APP_PEM }} # zizmor: ignore[secrets-outside-env]
# The 90 day stale policy for issues
# Used for:
+1 -1
View File
@@ -1 +1 @@
3.14.4
3.14.5
+4
View File
@@ -337,6 +337,7 @@ homeassistant.components.led_ble.*
homeassistant.components.lektrico.*
homeassistant.components.letpot.*
homeassistant.components.lg_infrared.*
homeassistant.components.lg_tv_rs232.*
homeassistant.components.libre_hardware_monitor.*
homeassistant.components.lidarr.*
homeassistant.components.liebherr.*
@@ -428,6 +429,7 @@ homeassistant.components.otp.*
homeassistant.components.ouman_eh_800.*
homeassistant.components.overkiz.*
homeassistant.components.overseerr.*
homeassistant.components.ovhcloud_ai_endpoints.*
homeassistant.components.p1_monitor.*
homeassistant.components.paj_gps.*
homeassistant.components.panel_custom.*
@@ -565,6 +567,7 @@ homeassistant.components.technove.*
homeassistant.components.tedee.*
homeassistant.components.telegram_bot.*
homeassistant.components.teleinfo.*
homeassistant.components.teltonika.*
homeassistant.components.teslemetry.*
homeassistant.components.text.*
homeassistant.components.thethingsnetwork.*
@@ -609,6 +612,7 @@ homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.velux.*
homeassistant.components.victron_gx.*
homeassistant.components.vistapool.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
+3 -3
View File
@@ -132,7 +132,7 @@
"problemMatcher": []
},
{
"label": "Install all Requirements",
"label": "Install all production Requirements",
"type": "shell",
"command": "uv pip install -r requirements_all.txt",
"group": {
@@ -146,9 +146,9 @@
"problemMatcher": []
},
{
"label": "Install all Test Requirements",
"label": "Install all (test & production) Requirements",
"type": "shell",
"command": "uv pip install -r requirements.txt -r requirements_test_all.txt",
"command": "uv pip install -r requirements_all.txt -r requirements_test.txt",
"group": {
"kind": "build",
"isDefault": true
+1
View File
@@ -15,6 +15,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
- .vscode/tasks.json contains useful commands used for development.
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
## Python Syntax Notes
Generated
+16 -8
View File
@@ -236,8 +236,8 @@ CLAUDE.md @home-assistant/core
/tests/components/blebox/ @bbx-a @swistakm @bkobus-bbx
/homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot
/homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/homeassistant/components/blue_current/ @gleeuwen @jtodorova23
/tests/components/blue_current/ @gleeuwen @jtodorova23
/homeassistant/components/bluemaestro/ @bdraco
/tests/components/bluemaestro/ @bdraco
/homeassistant/components/blueprint/ @home-assistant/core
@@ -945,8 +945,6 @@ CLAUDE.md @home-assistant/core
/tests/components/knx/ @Julius2342 @farmio @marvin-w
/homeassistant/components/kodi/ @OnFreund
/tests/components/kodi/ @OnFreund
/homeassistant/components/konnected/ @heythisisnate
/tests/components/konnected/ @heythisisnate
/homeassistant/components/kostal_plenticore/ @stegm
/tests/components/kostal_plenticore/ @stegm
/homeassistant/components/kraken/ @eifinger
@@ -989,6 +987,8 @@ CLAUDE.md @home-assistant/core
/tests/components/lg_netcast/ @Drafteed @splinter98
/homeassistant/components/lg_thinq/ @LG-ThinQ-Integration
/tests/components/lg_thinq/ @LG-ThinQ-Integration
/homeassistant/components/lg_tv_rs232/ @balloob
/tests/components/lg_tv_rs232/ @balloob
/homeassistant/components/libre_hardware_monitor/ @Sab44
/tests/components/libre_hardware_monitor/ @Sab44
/homeassistant/components/lichess/ @aryanhasgithub
@@ -1292,6 +1292,8 @@ CLAUDE.md @home-assistant/core
/tests/components/openhome/ @bazwilliams
/homeassistant/components/openrgb/ @felipecrs
/tests/components/openrgb/ @felipecrs
/homeassistant/components/opensensemap/ @AlCalzone
/tests/components/opensensemap/ @AlCalzone
/homeassistant/components/opensky/ @joostlek
/tests/components/opensky/ @joostlek
/homeassistant/components/opentherm_gw/ @mvn23
@@ -1319,6 +1321,8 @@ CLAUDE.md @home-assistant/core
/tests/components/overkiz/ @imicknl
/homeassistant/components/overseerr/ @joostlek @AmGarera
/tests/components/overseerr/ @joostlek @AmGarera
/homeassistant/components/ovhcloud_ai_endpoints/ @Crocmagnon
/tests/components/ovhcloud_ai_endpoints/ @Crocmagnon
/homeassistant/components/ovo_energy/ @timmo001
/tests/components/ovo_energy/ @timmo001
/homeassistant/components/p1_monitor/ @klaasnicolaas
@@ -1413,8 +1417,8 @@ CLAUDE.md @home-assistant/core
/tests/components/pushover/ @engrbm87
/homeassistant/components/pvoutput/ @frenck
/tests/components/pvoutput/ @frenck
/homeassistant/components/pvpc_hourly_pricing/ @azogue
/tests/components/pvpc_hourly_pricing/ @azogue
/homeassistant/components/pvpc_hourly_pricing/ @azogue @chiro79
/tests/components/pvpc_hourly_pricing/ @azogue @chiro79
/homeassistant/components/pyload/ @tr4nt0r
/tests/components/pyload/ @tr4nt0r
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
@@ -1538,8 +1542,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/saj/ @fredericvl
/homeassistant/components/samsung_infrared/ @lmaertin
/tests/components/samsung_infrared/ @lmaertin
/homeassistant/components/samsungtv/ @chemelli74 @epenet
/tests/components/samsungtv/ @chemelli74 @epenet
/homeassistant/components/samsungtv/ @chemelli74
/tests/components/samsungtv/ @chemelli74
/homeassistant/components/sanix/ @tomaszsluszniak
/tests/components/sanix/ @tomaszsluszniak
/homeassistant/components/satel_integra/ @Tommatheussen
@@ -1932,6 +1936,8 @@ CLAUDE.md @home-assistant/core
/tests/components/victron_remote_monitoring/ @AndyTempel
/homeassistant/components/vilfo/ @ManneW
/tests/components/vilfo/ @ManneW
/homeassistant/components/vistapool/ @fdebrus
/tests/components/vistapool/ @fdebrus
/homeassistant/components/vivotek/ @HarlemSquirrel
/tests/components/vivotek/ @HarlemSquirrel
/homeassistant/components/vizio/ @raman325
@@ -2056,6 +2062,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/yi/ @bachya
/homeassistant/components/yolink/ @matrixd2
/tests/components/yolink/ @matrixd2
/homeassistant/components/yoto/ @cdnninja @piitaya
/tests/components/yoto/ @cdnninja @piitaya
/homeassistant/components/youless/ @gjong
/tests/components/youless/ @gjong
/homeassistant/components/youtube/ @joostlek
+2 -2
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile@sha256:2780b5c3bab67f1f76c781860de469442999ed1a0d7992a5efdf2cffc0e3d769
# Automatically generated by hassfest.
# Partly generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
ARG BUILD_FROM
@@ -26,7 +26,7 @@ WORKDIR /usr/src
COPY rootfs /
# Add go2rtc binary
COPY --from=ghcr.io/alexxit/go2rtc@sha256:675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae /usr/local/bin/go2rtc /bin/go2rtc
COPY --from=ghcr.io/alexxit/go2rtc:1.9.14@sha256:675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae /usr/local/bin/go2rtc /bin/go2rtc
## Setup Home Assistant Core dependencies
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
+1 -1
View File
@@ -134,7 +134,7 @@ class AuthManagerFlowManager(
"""
flow = cast(LoginFlow, flow)
if result["type"] != FlowResultType.CREATE_ENTRY:
if result["type"] is not FlowResultType.CREATE_ENTRY:
return result
# we got final result
+1
View File
@@ -19,6 +19,7 @@ from .hub import AdsHub
DEFAULT_NAME = "ADS select"
# pylint: disable-next=home-assistant-duplicate-const
CONF_OPTIONS = "options"
PLATFORM_SCHEMA = SELECT_PLATFORM_SCHEMA.extend(
@@ -81,8 +81,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> boo
) as err:
raise ConfigEntryAuthFailed from err
except AirOSKeyDataMissingError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryError("key_data_missing") from err
except Exception as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryError("unknown") from err
airos_class: type[AirOS8 | AirOS6] = (
@@ -11,7 +11,7 @@
"service": "mdi:dialpad"
},
"alarm_toggle_chime": {
"service": "mdi:abc"
"service": "mdi:bell-ring"
}
}
}
@@ -39,7 +39,6 @@ from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_SUPPORTED_FEATURES,
ATTR_UNIT_OF_MEASUREMENT,
CLOUD_NEVER_EXPOSED_ENTITIES,
CONF_DESCRIPTION,
CONF_NAME,
UnitOfTemperature,
@@ -373,9 +372,6 @@ def async_get_entities(
"""Return all entities that are supported by Alexa."""
entities: list[AlexaEntity] = []
for state in hass.states.async_all():
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
continue
if state.domain not in ENTITY_ADAPTERS:
continue
@@ -1,9 +1,13 @@
"""Alexa Devices integration."""
import asyncio
import contextlib
from homeassistant.const import CONF_COUNTRY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers import aiohttp_client, config_validation as cv, httpx_client
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.ssl import SSL_ALPN_HTTP11_HTTP2
from .const import _LOGGER, CONF_LOGIN_DATA, CONF_SITE, COUNTRY_DOMAINS, DOMAIN
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
@@ -12,6 +16,8 @@ from .services import async_setup_services
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.EVENT,
Platform.MEDIA_PLAYER,
Platform.NOTIFY,
Platform.SENSOR,
Platform.SWITCH,
@@ -34,6 +40,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
await coordinator.async_config_entry_first_refresh()
await coordinator.sync_history_state()
await coordinator.sync_media_state()
async def _on_http2_reauth_required() -> None:
entry.async_start_reauth(hass)
async def _cancel_http2() -> None:
http2_task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await http2_task
alexa_httpx_client = httpx_client.get_async_client(
hass,
alpn_protocols=SSL_ALPN_HTTP11_HTTP2,
)
http2_task = await coordinator.api.start_http2_processing(
alexa_httpx_client, on_reauth_required=_on_http2_reauth_required
)
entry.async_on_unload(_cancel_http2)
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -8,13 +8,18 @@ from aioamazondevices.exceptions import (
CannotConnect,
CannotRetrieveData,
)
from aioamazondevices.structures import AmazonDevice
from aioamazondevices.structures import (
AmazonDevice,
AmazonMediaState,
AmazonVocalRecord,
AmazonVolumeState,
)
from aiohttp import ClientSession
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
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
@@ -73,6 +78,18 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
if routine.domain == Platform.BUTTON
}
self._vocal_records: dict[str, AmazonVocalRecord] = {}
self.api.on_history_event.append(self.history_state_event_handler)
self.api.on_history_event.freeze()
self._volume_states: dict[str, AmazonVolumeState] = {}
self.api.on_volume_state_event.append(self.volume_state_event_handler)
self.api.on_volume_state_event.freeze()
self._media_states: dict[str, AmazonMediaState] = {}
self.api.on_media_state_event.append(self.media_state_event_handler)
self.api.on_media_state_event.freeze()
async def _async_update_data(self) -> dict[str, AmazonDevice]:
"""Update device data."""
try:
@@ -149,3 +166,66 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
)
if entity_id:
entity_registry.async_remove(entity_id)
async def sync_history_state(self) -> None:
"""Sync history state."""
try:
self._vocal_records = await self.api.sync_history_state()
except CannotAuthenticate as e:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
translation_placeholders={"error": repr(e)},
) from e
except CannotConnect as e:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="cannot_connect_with_error",
translation_placeholders={"error": repr(e)},
) from e
except BaseException as e:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="cannot_retrieve_data_with_error",
translation_placeholders={"error": repr(e)},
) from e
async def history_state_event_handler(
self, vocal_records: dict[str, AmazonVocalRecord]
) -> None:
"""Handle pushed vocal record events."""
self._vocal_records = {**self._vocal_records, **vocal_records}
self.async_update_listeners()
@property
def vocal_records(self) -> dict[str, AmazonVocalRecord]:
"""Vocal records of devices."""
return self._vocal_records
async def sync_media_state(self) -> None:
"""Sync media state."""
await self.api.sync_media_state()
async def media_state_event_handler(
self, media_state: dict[str, AmazonMediaState]
) -> None:
"""Handle pushed media state changed events."""
self._media_states = media_state
self.async_update_listeners()
@property
def media_states(self) -> dict[str, AmazonMediaState]:
"""Media state of devices."""
return self._media_states
async def volume_state_event_handler(
self, volume_states: dict[str, AmazonVolumeState]
) -> None:
"""Handle pushed volume change events."""
self._volume_states = volume_states
self.async_update_listeners()
@property
def volume_states(self) -> dict[str, AmazonVolumeState]:
"""Volumes of devices."""
return self._volume_states
@@ -0,0 +1,86 @@
"""Support for events."""
from typing import Final
from homeassistant.components.event import EventEntity, EventEntityDescription
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import _LOGGER
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .entity import AmazonEntity
# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0
EVENTS: Final = {
EventEntityDescription(
key="voice_event",
translation_key="voice_event",
),
}
EVENT_TYPE = "triggered"
async def async_setup_entry(
hass: HomeAssistant,
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Alexa Devices events based on a config entry."""
coordinator = entry.runtime_data
known_devices: set[str] = set()
def _check_device() -> None:
current_devices = set(coordinator.data)
new_devices = current_devices - known_devices
if new_devices:
known_devices.update(new_devices)
async_add_entities(
AlexaVoiceEvent(coordinator, serial_num, event_desc)
for event_desc in EVENTS
for serial_num in new_devices
)
_check_device()
entry.async_on_unload(coordinator.async_add_listener(_check_device))
class AlexaVoiceEvent(AmazonEntity, EventEntity):
"""Representation of an Alexa voice event."""
_attr_event_types = [EVENT_TYPE]
coordinator: AmazonDevicesCoordinator
_last_seen_timestamp: int | None = None
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if not (
vocal_record := self.coordinator.vocal_records.get(
self.device.serial_number
)
):
_LOGGER.debug(
"No vocal record found for device %s [%s]",
self.device.account_name,
self.device.serial_number,
)
return
if vocal_record.timestamp == self._last_seen_timestamp:
return
self._last_seen_timestamp = vocal_record.timestamp
self._trigger_event(
EVENT_TYPE,
{
"intent": vocal_record.intent,
"voice_command": vocal_record.title,
"voice_reply": vocal_record.sub_title,
},
)
self.async_write_ha_state()
@@ -1,5 +1,10 @@
{
"entity": {
"event": {
"voice_event": {
"default": "mdi:chat-processing"
}
},
"sensor": {
"voc_index": {
"default": "mdi:molecule"
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==13.7.0"]
"requirements": ["aioamazondevices==13.8.0"]
}
@@ -0,0 +1,294 @@
"""Media player platform for Alexa Devices."""
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Final
from aioamazondevices.structures import (
AmazonMediaControls,
AmazonMediaState,
AmazonVolumeState,
)
from homeassistant.components.media_player import (
MediaPlayerDeviceClass,
MediaPlayerEnqueue,
MediaPlayerEntity,
MediaPlayerEntityDescription,
MediaPlayerEntityFeature,
MediaPlayerState,
MediaType,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import _LOGGER
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .entity import AmazonEntity
from .utils import alexa_api_call
PARALLEL_UPDATES = 1
STANDARD_SUPPORTED_FEATURES = (
MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.STOP
| MediaPlayerEntityFeature.PLAY_MEDIA
)
@dataclass(frozen=True, kw_only=True)
class AmazonDevicesMediaPlayerEntityDescription(MediaPlayerEntityDescription):
"""Describes an Alexa Devices media player entity."""
MEDIA_PLAYERS: Final = (
AmazonDevicesMediaPlayerEntityDescription(
key="media",
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Alexa Devices media player entities from a config entry."""
coordinator = entry.runtime_data
known_devices: set[str] = set()
def _check_device() -> None:
"""Add entities for newly discovered devices."""
new_entities: list[AlexaDevicesMediaPlayer] = []
for serial_num, device in coordinator.data.items():
if serial_num in known_devices or not device.media_player_supported:
continue
known_devices.add(serial_num)
new_entities.extend(
AlexaDevicesMediaPlayer(coordinator, serial_num, description)
for description in MEDIA_PLAYERS
)
if new_entities:
async_add_entities(new_entities)
remove_listener = coordinator.async_add_listener(_check_device)
entry.async_on_unload(remove_listener)
_check_device()
class AlexaDevicesMediaPlayer(AmazonEntity, MediaPlayerEntity):
"""Representation of an Alexa device media player."""
entity_description: AmazonDevicesMediaPlayerEntityDescription
_attr_name = None # Uses the device name
_attr_device_class = MediaPlayerDeviceClass.SPEAKER
_attr_volume_step = 0.05
def __init__(
self,
coordinator: AmazonDevicesCoordinator,
serial_num: str,
description: AmazonDevicesMediaPlayerEntityDescription,
) -> None:
"""Initialize."""
self._prev_volume: int | None = None
super().__init__(coordinator, serial_num, description)
@property
def media_state(self) -> AmazonMediaState | None:
"""Return the media state relating to device."""
if not self.coordinator or not self.coordinator.media_states:
return None
return self.coordinator.media_states.get(self._serial_num)
@property
def volume_state(self) -> AmazonVolumeState | None:
"""Volume settings for device."""
if not self.coordinator or not self.coordinator.volume_states:
return None
return self.coordinator.volume_states.get(self._serial_num)
@property
def supported_features(self) -> MediaPlayerEntityFeature:
"""Return dynamically supported features based on current media."""
features = STANDARD_SUPPORTED_FEATURES
if self.media_state is None:
return features
if self.media_state.pause_enabled:
features |= MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.PAUSE
if self.media_state.next_enabled:
features |= MediaPlayerEntityFeature.NEXT_TRACK
if self.media_state.previous_enabled:
features |= MediaPlayerEntityFeature.PREVIOUS_TRACK
return features
@property
def state(self) -> MediaPlayerState | None:
"""Return the current state of the player."""
if not self.media_state:
return MediaPlayerState.IDLE
if self.media_state.player_state == "PLAYING":
return MediaPlayerState.PLAYING
if self.media_state.player_state == "PAUSED":
return MediaPlayerState.PAUSED
return MediaPlayerState.IDLE
@property
def volume_level(self) -> float | None:
"""Return the volume level (0.0 to 1.0)."""
if not self.volume_state or self.volume_state.volume is None:
return None
return self.volume_state.volume / 100
@property
def is_volume_muted(self) -> bool | None:
"""Return True if the volume is muted."""
if not self.volume_state:
return None
return self.volume_state.volume == 0
@property
def media_title(self) -> str | None:
"""Track title."""
if not self.media_state:
return None
return self.media_state.now_playing_title
@property
def media_artist(self) -> str | None:
"""Artist name."""
if not self.media_state:
return None
return self.media_state.now_playing_line1
@property
def media_album_name(self) -> str | None:
"""Album name."""
if not self.media_state:
return None
return self.media_state.now_playing_line2
@property
def media_image_url(self) -> str | None:
"""Album art URL."""
if not self.media_state:
return None
return self.media_state.now_playing_url
@property
def media_duration(self) -> int | None:
"""Duration in seconds."""
if not self.media_state:
return None
return self.media_state.media_length
@property
def media_position(self) -> int | None:
"""Current playback position in seconds."""
if not self.media_state:
return None
return self.media_state.media_position
@property
def media_position_updated_at(self) -> datetime | None:
"""When media_position was last updated — HA uses this to interpolate the progress bar."""
if not self.media_state:
return None
return self.media_state.media_position_updated_at
@property
def media_content_type(self) -> MediaType | None:
"""Content type — tells HA what kind of media is playing."""
if self.state in [MediaPlayerState.PLAYING, MediaPlayerState.PAUSED]:
return MediaType.MUSIC
return None
async def async_play_media(
self,
media_type: MediaType | str,
media_id: str,
enqueue: MediaPlayerEnqueue | None = None,
announce: bool | None = None,
**kwargs: Any,
) -> None:
"""Play a piece of media."""
await self.async_call_alexa_music(media_id, media_type)
@alexa_api_call
async def async_call_alexa_music(
self, search_phrase: str, provider_id: str
) -> None:
"""Call alexa music."""
await self.coordinator.api.call_alexa_music(
self.device, search_phrase, provider_id
)
@alexa_api_call
async def async_set_device_volume(self, volume: int) -> None:
"""Set the device volume."""
_LOGGER.debug(
"Setting volume for %s to %s%%",
self.device.serial_number,
volume,
)
await self.coordinator.api.set_device_volume(self.device, volume)
async def async_set_volume_level(self, volume: float) -> None:
"""Set the volume level (0.0 to 1.0)."""
device_volume = round(volume * 100)
await self.async_set_device_volume(device_volume)
async def async_mute_volume(self, mute: bool) -> None:
"""Mute or un-mute the volume."""
# Whilst you can mute a device by asking it there appears to be
# no way to do this programmatically so set volume to 0
if not self.volume_state or self.volume_state.volume is None:
return
if mute:
self._prev_volume = self.volume_state.volume
target_volume = 0
else:
if self._prev_volume is None:
return
target_volume = self._prev_volume
await self.async_set_volume_level(target_volume / 100)
@alexa_api_call
async def _send_media_command(self, command: AmazonMediaControls) -> None:
_LOGGER.debug(
"Sending media command '%s' to %s", command, self.device.serial_number
)
await self.coordinator.api.send_media_command(self.device, command)
async def async_media_stop(self) -> None:
"""Send stop command."""
await self._send_media_command(AmazonMediaControls.Stop)
async def async_media_pause(self) -> None:
"""Send pause command."""
await self._send_media_command(AmazonMediaControls.Pause)
async def async_media_play(self) -> None:
"""Send play command."""
await self._send_media_command(AmazonMediaControls.Play)
async def async_media_next_track(self) -> None:
"""Send next track command."""
await self._send_media_command(AmazonMediaControls.Next)
async def async_media_previous_track(self) -> None:
"""Send previous track command."""
await self._send_media_command(AmazonMediaControls.Previous)
@@ -58,6 +58,18 @@
}
},
"entity": {
"event": {
"voice_event": {
"name": "Voice event",
"state_attributes": {
"event_type": {
"state": {
"triggered": "Triggered"
}
}
}
}
},
"notify": {
"announce": {
"name": "Announce"
@@ -102,6 +114,9 @@
"entry_not_loaded": {
"message": "Entry not loaded: {entry}"
},
"invalid_auth": {
"message": "Invalid authentication credentials: {error}"
},
"invalid_device_id": {
"message": "Invalid device ID specified: {device_id}"
},
@@ -7,10 +7,11 @@ from altruistclient import AltruistClient, AltruistDeviceModel, AltruistError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_HOST, DOMAIN
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -1,5 +1,3 @@
"""Constants for the Altruist integration."""
DOMAIN = "altruist"
CONF_HOST = "host"
@@ -10,13 +10,12 @@ import logging
from altruistclient import AltruistClient, AltruistError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_HOST
_LOGGER = logging.getLogger(__name__)
UPDATE_INTERVAL = timedelta(seconds=15)
@@ -230,13 +230,13 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AnthropicConfigEntry)
if entry.version == 2 and entry.minor_version == 3:
# Remove Temperature parameter
CONF_TEMPERATURE = "temperature"
temperature_key = "temperature"
for subentry in entry.subentries.values():
data = subentry.data.copy()
if CONF_TEMPERATURE not in data:
if temperature_key not in data:
continue
data.pop(CONF_TEMPERATURE, None)
data.pop(temperature_key, None)
hass.config_entries.async_update_subentry(entry, subentry, data=data)
hass.config_entries.async_update_entry(entry, minor_version=4)
@@ -16,6 +16,8 @@ from .entity import AnthropicBaseLLMEntity
if TYPE_CHECKING:
from . import AnthropicConfigEntry
PARALLEL_UPDATES = 0
_LOGGER = logging.getLogger(__name__)
@@ -24,10 +24,10 @@ from homeassistant.const import (
CONF_API_KEY,
CONF_LLM_HASS_API,
CONF_NAME,
CONF_PROMPT,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import llm
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import config_validation as cv, llm
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.selector import (
NumberSelector,
@@ -44,12 +44,13 @@ from .const import (
CONF_CHAT_MODEL,
CONF_CODE_EXECUTION,
CONF_MAX_TOKENS,
CONF_PROMPT,
CONF_PROMPT_CACHING,
CONF_RECOMMENDED,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
CONF_WEB_FETCH,
CONF_WEB_FETCH_MAX_USES,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
@@ -225,7 +226,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
) -> SubentryFlowResult:
"""Set initial options."""
# abort if entry is not loaded
if self._get_entry().state != ConfigEntryState.LOADED:
if self._get_entry().state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
hass_apis: list[SelectOptionDict] = [
@@ -452,11 +453,19 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
vol.Optional(
CONF_WEB_SEARCH_MAX_USES,
default=DEFAULT[CONF_WEB_SEARCH_MAX_USES],
): int,
): cv.positive_int,
vol.Optional(
CONF_WEB_SEARCH_USER_LOCATION,
default=DEFAULT[CONF_WEB_SEARCH_USER_LOCATION],
): bool,
vol.Optional(
CONF_WEB_FETCH,
default=DEFAULT[CONF_WEB_FETCH],
): bool,
vol.Optional(
CONF_WEB_FETCH_MAX_USES,
default=DEFAULT[CONF_WEB_FETCH_MAX_USES],
): cv.positive_int,
}
)
+4 -1
View File
@@ -10,7 +10,6 @@ DEFAULT_CONVERSATION_NAME = "Claude conversation"
DEFAULT_AI_TASK_NAME = "Claude AI Task"
CONF_RECOMMENDED = "recommended"
CONF_PROMPT = "prompt"
CONF_CHAT_MODEL = "chat_model"
CONF_CODE_EXECUTION = "code_execution"
CONF_MAX_TOKENS = "max_tokens"
@@ -18,6 +17,8 @@ CONF_PROMPT_CACHING = "prompt_caching"
CONF_THINKING_BUDGET = "thinking_budget"
CONF_THINKING_EFFORT = "thinking_effort"
CONF_TOOL_SEARCH = "tool_search"
CONF_WEB_FETCH = "web_fetch"
CONF_WEB_FETCH_MAX_USES = "web_fetch_max_uses"
CONF_WEB_SEARCH = "web_search"
CONF_WEB_SEARCH_USER_LOCATION = "user_location"
CONF_WEB_SEARCH_MAX_USES = "web_search_max_uses"
@@ -45,6 +46,8 @@ DEFAULT = {
CONF_THINKING_BUDGET: MIN_THINKING_BUDGET,
CONF_THINKING_EFFORT: "low",
CONF_TOOL_SEARCH: False,
CONF_WEB_FETCH: False,
CONF_WEB_FETCH_MAX_USES: 5,
CONF_WEB_SEARCH: False,
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_WEB_SEARCH_MAX_USES: 5,
@@ -4,14 +4,16 @@ from typing import Literal
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigSubentry
from homeassistant.const import CONF_LLM_HASS_API, MATCH_ALL
from homeassistant.const import CONF_LLM_HASS_API, CONF_PROMPT, MATCH_ALL
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AnthropicConfigEntry
from .const import CONF_PROMPT, DOMAIN
from .const import DOMAIN
from .entity import AnthropicBaseLLMEntity
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
@@ -5,11 +5,10 @@ from typing import TYPE_CHECKING, Any
from anthropic import __title__, __version__
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_API_KEY
from homeassistant.const import CONF_API_KEY, CONF_PROMPT
from homeassistant.helpers import entity_registry as er
from .const import (
CONF_PROMPT,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
CONF_WEB_SEARCH_REGION,
+55 -17
View File
@@ -17,8 +17,6 @@ from anthropic.types import (
Base64PDFSourceParam,
BashCodeExecutionToolResultBlock,
CitationsDelta,
CitationsWebSearchResultLocation,
CitationWebSearchResultLocationParam,
CodeExecutionTool20250825Param,
CodeExecutionToolResultBlock,
CodeExecutionToolResultBlockContent,
@@ -70,6 +68,9 @@ from anthropic.types import (
ToolUseBlock,
ToolUseBlockParam,
Usage,
WebFetchTool20250910Param,
WebFetchTool20260209Param,
WebFetchToolResultBlock,
WebSearchTool20250305Param,
WebSearchTool20260209Param,
WebSearchToolResultBlock,
@@ -97,6 +98,12 @@ from anthropic.types.tool_search_tool_result_block_param import (
Content as ToolSearchToolResultBlockParamContentParam,
)
from anthropic.types.tool_use_block import Caller
from anthropic.types.web_fetch_tool_result_block import (
Content as WebFetchToolResultBlockContent,
)
from anthropic.types.web_fetch_tool_result_block_param import (
Content as WebFetchToolResultBlockParamContentParam,
)
import voluptuous as vol
from voluptuous_openapi import convert
@@ -118,6 +125,8 @@ from .const import (
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
CONF_WEB_FETCH,
CONF_WEB_FETCH_MAX_USES,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
@@ -208,17 +217,9 @@ class ContentDetails:
"""Add a citation to the current detail."""
if not self.citation_details:
self.citation_details.append(CitationDetails())
citation_param: TextCitationParam | None = None
if isinstance(citation, CitationsWebSearchResultLocation):
citation_param = CitationWebSearchResultLocationParam(
type="web_search_result_location",
title=citation.title,
url=citation.url,
cited_text=citation.cited_text,
encrypted_index=citation.encrypted_index,
)
if citation_param:
self.citation_details[-1].citations.append(citation_param)
self.citation_details[-1].citations.append(
cast(TextCitationParam, citation.to_dict())
)
def delete_empty(self) -> None:
"""Delete empty citation details."""
@@ -289,6 +290,15 @@ def _convert_content( # noqa: C901
content.tool_result,
),
}
elif content.tool_name == "web_fetch":
tool_result_block = {
"type": "web_fetch_tool_result",
"tool_use_id": content.tool_call_id,
"content": cast(
WebFetchToolResultBlockParamContentParam,
content.tool_result,
),
}
else:
tool_result_block = {
"type": "tool_result",
@@ -415,6 +425,7 @@ def _convert_content( # noqa: C901
id=tool_call.id,
name=cast(
Literal[
"web_fetch",
"web_search",
"code_execution",
"bash_code_execution",
@@ -428,6 +439,7 @@ def _convert_content( # noqa: C901
if tool_call.external
and tool_call.tool_name
in [
"web_fetch",
"web_search",
"code_execution",
"bash_code_execution",
@@ -609,6 +621,7 @@ class AnthropicDeltaStream:
if isinstance(
content_block,
(
WebFetchToolResultBlock,
WebSearchToolResultBlock,
CodeExecutionToolResultBlock,
BashCodeExecutionToolResultBlock,
@@ -724,13 +737,15 @@ class AnthropicDeltaStream:
self,
tool_use_id: str,
tool_name: Literal[
"web_fetch_tool_result",
"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
content: WebFetchToolResultBlockContent
| WebSearchToolResultBlockContent
| CodeExecutionToolResultBlockContent
| BashCodeExecutionToolResultBlockContent
| TextEditorCodeExecutionToolResultBlockContent
@@ -907,6 +922,7 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
"GetLiveContext",
"code_execution",
"web_search",
"web_fetch",
]
system = chat_log.content[0]
@@ -980,12 +996,12 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
]
if options[CONF_CODE_EXECUTION]:
# The `web_search_20260209` tool automatically enables
# `code_execution_20260120` tool
# The `web_search_20260209` and `web_fetch_20260209` tools
# automatically enable `code_execution_20260120` tool
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options[CONF_WEB_SEARCH]
or (not options[CONF_WEB_SEARCH] and not options[CONF_WEB_FETCH])
):
tools.append(
CodeExecutionTool20250825Param(
@@ -1023,6 +1039,28 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
}
tools.append(web_search)
if options[CONF_WEB_FETCH]:
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options[CONF_CODE_EXECUTION]
):
tools.append(
WebFetchTool20250910Param(
name="web_fetch",
type="web_fetch_20250910",
max_uses=options[CONF_WEB_FETCH_MAX_USES],
)
)
else:
tools.append(
WebFetchTool20260209Param(
name="web_fetch",
type="web_fetch_20260209",
max_uses=options[CONF_WEB_FETCH_MAX_USES],
)
)
# Handle attachments by adding them to the last user message
last_content = chat_log.content[-1]
if last_content.role == "user" and last_content.attachments:
@@ -38,10 +38,7 @@ rules:
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates:
status: exempt
comment: |
The API does not limit parallel updates.
parallel-updates: done
reauthentication-flow: done
test-coverage: done
# Gold
@@ -40,9 +40,11 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
self._current_subentry_id = None
self._model_list_cache = None
async def async_step_init(self, user_input: dict[str, str]) -> RepairsFlowResult:
async def async_step_init(
self, user_input: dict[str, str] | None
) -> RepairsFlowResult:
"""Handle the steps of a fix flow."""
if user_input.get(CONF_CHAT_MODEL):
if user_input and user_input.get(CONF_CHAT_MODEL):
self._async_update_current_subentry(user_input)
target = await self._async_next_target()
@@ -51,13 +51,11 @@
"advanced": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::prompt_caching%]",
"temperature": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::temperature%]"
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::prompt_caching%]"
},
"data_description": {
"chat_model": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::chat_model%]",
"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%]"
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::prompt_caching%]"
},
"title": "[%key:component::anthropic::config_subentries::conversation::step::advanced::title%]"
},
@@ -80,6 +78,8 @@
"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%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data::user_location%]",
"web_fetch": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_fetch%]",
"web_fetch_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_fetch_max_uses%]",
"web_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search%]",
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search_max_uses%]"
},
@@ -90,6 +90,8 @@
"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%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::user_location%]",
"web_fetch": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_fetch%]",
"web_fetch_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_fetch_max_uses%]",
"web_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_search%]",
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_search_max_uses%]"
},
@@ -116,13 +118,11 @@
"advanced": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"prompt_caching": "Caching strategy",
"temperature": "Temperature"
"prompt_caching": "Caching strategy"
},
"data_description": {
"chat_model": "The model to serve the responses.",
"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."
"prompt_caching": "Optimize your API cost and response times based on your usage."
},
"title": "Advanced settings"
},
@@ -149,6 +149,8 @@
"thinking_effort": "Thinking effort",
"tool_search": "Enable tool search tool",
"user_location": "Include home location",
"web_fetch": "Enable web fetch",
"web_fetch_max_uses": "Maximum web fetches",
"web_search": "Enable web search",
"web_search_max_uses": "Maximum web searches"
},
@@ -159,6 +161,8 @@
"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",
"user_location": "Localize search results based on home location",
"web_fetch": "The web fetch tool allows Claude to retrieve full content from specified web pages and PDF documents to augment Claude's context with live web content",
"web_fetch_max_uses": "Limit the number of web fetches performed per response",
"web_search": "The web search tool gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff",
"web_search_max_uses": "Limit the number of searches performed per response"
},
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/aosmith",
"integration_type": "hub",
"iot_class": "cloud_polling",
"requirements": ["py-aosmith==1.0.17"]
"requirements": ["py-aosmith==1.0.18"]
}
@@ -89,7 +89,7 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
def supported_features(self) -> WaterHeaterEntityFeature:
"""Return the list of supported features."""
supports_vacation_mode = any(
supported_mode.mode == AOSmithOperationMode.VACATION
supported_mode.mode is AOSmithOperationMode.VACATION
for supported_mode in self.device.supported_modes
)
@@ -122,7 +122,7 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
@property
def is_away_mode_on(self) -> bool:
"""Return True if away mode is on."""
return self.device.status.current_mode == AOSmithOperationMode.VACATION
return self.device.status.current_mode is AOSmithOperationMode.VACATION
async def async_set_operation_mode(self, operation_mode: str) -> None:
"""Set new target operation mode."""
-24
View File
@@ -7,27 +7,3 @@ CONNECTION_TIMEOUT: int = 10
# Field name of last self test retrieved from apcupsd.
LAST_S_TEST: Final = "laststest"
# Mapping of deprecated sensor keys (as reported by apcupsd,
# lower-cased) to their deprecation
# repair issue translation keys.
DEPRECATED_SENSORS: Final = {
"apc": "apc_deprecated",
"end apc": "date_deprecated",
"date": "date_deprecated",
"apcmodel": "available_via_device_info",
"model": "available_via_device_info",
"firmware": "available_via_device_info",
"version": "available_via_device_info",
"upsname": "available_via_device_info",
"serialno": "available_via_device_info",
}
AVAILABLE_VIA_DEVICE_ATTR: Final = {
"apcmodel": "model",
"model": "model",
"firmware": "hw_version",
"version": "sw_version",
"upsname": "name",
"serialno": "serial_number",
}
+19 -121
View File
@@ -1,11 +1,10 @@
"""Support for APCUPSd sensors."""
import logging
from typing import Final
import dateutil
from homeassistant.components.automation import automations_with_entity
from homeassistant.components.script import scripts_with_entity
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
@@ -24,11 +23,9 @@ from homeassistant.const import (
UnitOfTime,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
import homeassistant.helpers.issue_registry as ir
from .const import AVAILABLE_VIA_DEVICE_ATTR, DEPRECATED_SENSORS, DOMAIN, LAST_S_TEST
from .const import LAST_S_TEST
from .coordinator import APCUPSdConfigEntry, APCUPSdCoordinator
from .entity import APCUPSdEntity
@@ -36,6 +33,20 @@ PARALLEL_UPDATES = 0
_LOGGER = logging.getLogger(__name__)
# List of useless sensors to ignore, since they are either provided in device
# information, or not useful at all
IGNORED_SENSORS: Final = {
"apc",
"end apc",
"date",
"apcmodel",
"model",
"firmware",
"version",
"upsname",
"serialno",
}
SENSORS: dict[str, SensorEntityDescription] = {
"alarmdel": SensorEntityDescription(
key="alarmdel",
@@ -49,18 +60,6 @@ SENSORS: dict[str, SensorEntityDescription] = {
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
"apc": SensorEntityDescription(
key="apc",
translation_key="apc_status",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"apcmodel": SensorEntityDescription(
key="apcmodel",
translation_key="apc_model",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"badbatts": SensorEntityDescription(
key="badbatts",
translation_key="bad_batteries",
@@ -100,12 +99,6 @@ SENSORS: dict[str, SensorEntityDescription] = {
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.DURATION,
),
"date": SensorEntityDescription(
key="date",
translation_key="date",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"dipsw": SensorEntityDescription(
key="dipsw",
translation_key="dip_switch_settings",
@@ -132,23 +125,11 @@ SENSORS: dict[str, SensorEntityDescription] = {
translation_key="wake_delay",
entity_category=EntityCategory.DIAGNOSTIC,
),
"end apc": SensorEntityDescription(
key="end apc",
translation_key="date_and_time",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"extbatts": SensorEntityDescription(
key="extbatts",
translation_key="external_batteries",
entity_category=EntityCategory.DIAGNOSTIC,
),
"firmware": SensorEntityDescription(
key="firmware",
translation_key="firmware_version",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"hitrans": SensorEntityDescription(
key="hitrans",
translation_key="transfer_high",
@@ -264,12 +245,6 @@ SENSORS: dict[str, SensorEntityDescription] = {
translation_key="min_time",
entity_category=EntityCategory.DIAGNOSTIC,
),
"model": SensorEntityDescription(
key="model",
translation_key="model",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"nombattv": SensorEntityDescription(
key="nombattv",
translation_key="battery_nominal_voltage",
@@ -358,12 +333,6 @@ SENSORS: dict[str, SensorEntityDescription] = {
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"serialno": SensorEntityDescription(
key="serialno",
translation_key="serial_number",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"starttime": SensorEntityDescription(
key="starttime",
translation_key="startup_time",
@@ -404,18 +373,6 @@ SENSORS: dict[str, SensorEntityDescription] = {
translation_key="ups_mode",
entity_category=EntityCategory.DIAGNOSTIC,
),
"upsname": SensorEntityDescription(
key="upsname",
translation_key="ups_name",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"version": SensorEntityDescription(
key="version",
translation_key="version",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"xoffbat": SensorEntityDescription(
key="xoffbat",
translation_key="transfer_from_battery",
@@ -481,9 +438,10 @@ async def async_setup_entry(
# as unknown initially.
#
# We also sort the resources to ensure the order of entities
# created is deterministic since "APCMODEL" and "MODEL"
# resources map to the same "Model" name.
# created is deterministic
for resource in sorted(available_resources | {LAST_S_TEST}):
if resource in IGNORED_SENSORS:
continue
if resource not in SENSORS:
_LOGGER.warning("Invalid resource from APCUPSd: %s", resource.upper())
continue
@@ -561,63 +519,3 @@ class APCUPSdSensor(APCUPSdEntity, SensorEntity):
self._attr_native_value, inferred_unit = infer_unit(data)
if not self.native_unit_of_measurement:
self._attr_native_unit_of_measurement = inferred_unit
async def async_added_to_hass(self) -> None:
"""Handle when entity is added to Home Assistant.
If this is a deprecated sensor entity, create a repair issue to guide
the user to disable it.
"""
await super().async_added_to_hass()
if not self.enabled:
return
reason = DEPRECATED_SENSORS.get(self.entity_description.key)
if not reason:
return
automations = automations_with_entity(self.hass, self.entity_id)
scripts = scripts_with_entity(self.hass, self.entity_id)
if not automations and not scripts:
return
entity_registry = er.async_get(self.hass)
items = [
f"- [{entry.name or entry.original_name or entity_id}]"
f"(/config/{integration}/edit/"
f"{entry.unique_id or entity_id.split('.', 1)[-1]})"
for integration, entities in (
("automation", automations),
("script", scripts),
)
for entity_id in entities
if (entry := entity_registry.async_get(entity_id))
]
placeholders = {
"entity_name": str(self.name or self.entity_id),
"entity_id": self.entity_id,
"items": "\n".join(items),
}
if via_attr := AVAILABLE_VIA_DEVICE_ATTR.get(self.entity_description.key):
placeholders["available_via_device_attr"] = via_attr
if device_entry := self.device_entry:
placeholders["device_id"] = device_entry.id
ir.async_create_issue(
self.hass,
DOMAIN,
f"{reason}_{self.entity_id}",
breaks_in_ha_version="2026.6.0",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
translation_key=reason,
translation_placeholders=placeholders,
)
async def async_will_remove_from_hass(self) -> None:
"""Handle when entity will be removed from Home Assistant."""
await super().async_will_remove_from_hass()
if issue_key := DEPRECATED_SENSORS.get(self.entity_description.key):
ir.async_delete_issue(self.hass, DOMAIN, f"{issue_key}_{self.entity_id}")
@@ -241,19 +241,5 @@
"cannot_connect": {
"message": "Cannot connect to APC UPS Daemon."
}
},
"issues": {
"apc_deprecated": {
"description": "The {entity_name} sensor (`{entity_id}`) is deprecated because it exposes internal details of the APC UPS Daemon response.\n\nIt is still referenced in the following automations or scripts:\n{items}\n\nUpdate those automations or scripts to use supported APC UPS entities instead. Reload the APC UPS Daemon integration afterwards to resolve this issue.",
"title": "{entity_name} sensor is deprecated"
},
"available_via_device_info": {
"description": "The {entity_name} sensor (`{entity_id}`) is deprecated because the same value is available from the device registry via `device_attr(\"{device_id}\", \"{available_via_device_attr}\")`.\n\nIt is still referenced in the following automations or scripts:\n{items}\n\nUpdate those automations or scripts to use the `device_attr` helper instead of this sensor. Reload the APC UPS Daemon integration afterwards to resolve this issue.",
"title": "{entity_name} sensor is deprecated"
},
"date_deprecated": {
"description": "The {entity_name} sensor (`{entity_id}`) is deprecated because the timestamp is already available from other APC UPS sensors via their last updated time.\n\nIt is still referenced in the following automations or scripts:\n{items}\n\nUpdate those automations or scripts to reference any entity's `last_updated` attribute instead (for example, `states.binary_sensor.apcups_online_status.last_updated`). Reload the APC UPS Daemon integration afterwards to resolve this issue.",
"title": "{entity_name} sensor is deprecated"
}
}
}
@@ -369,7 +369,7 @@ class AppleTVManager(DeviceListener):
attrs[ATTR_MODEL] = (
dev_info.raw_model
if dev_info.model == DeviceModel.Unknown and dev_info.raw_model
if dev_info.model is DeviceModel.Unknown and dev_info.raw_model
else model_str(dev_info.model)
)
attrs[ATTR_SW_VERSION] = dev_info.version
@@ -5,7 +5,7 @@ from pyatv.interface import AppleTV, KeyboardListener
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.const import CONF_NAME
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -21,23 +21,33 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Load Apple TV binary sensor based on a config entry."""
# apple_tv config entries always have a unique id
manager = config_entry.runtime_data
cb: CALLBACK_TYPE
added = False
@callback
def setup_entities(atv: AppleTV) -> None:
nonlocal added
if added:
return
if atv.features.in_state(FeatureState.Available, FeatureName.TextFocusState):
assert config_entry.unique_id is not None
name: str = config_entry.data[CONF_NAME]
async_add_entities(
[AppleTVKeyboardFocused(name, config_entry.unique_id, manager)]
)
cb()
added = True
cb = async_dispatcher_connect(
hass, f"{SIGNAL_CONNECTED}_{config_entry.unique_id}", setup_entities
config_entry.async_on_unload(
async_dispatcher_connect(
hass, f"{SIGNAL_CONNECTED}_{config_entry.unique_id}", setup_entities
)
)
config_entry.async_on_unload(cb)
# The manager may have already connected (and dispatched SIGNAL_CONNECTED)
# before this platform was forwarded, in which case the signal above was
# missed; handle that case directly.
if manager.atv is not None:
setup_entities(manager.atv)
class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener):
@@ -53,7 +63,7 @@ class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener
# Listen to keyboard updates
atv.keyboard.listener = self
# Set initial state based on current focus state
self._update_state(atv.keyboard.text_focus_state == KeyboardFocusState.Focused)
self._update_state(atv.keyboard.text_focus_state is KeyboardFocusState.Focused)
@callback
def async_device_disconnected(self) -> None:
@@ -68,7 +78,7 @@ class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener
This is a callback function from pyatv.interface.KeyboardListener.
"""
self._update_state(new_state == KeyboardFocusState.Focused)
self._update_state(new_state is KeyboardFocusState.Focused)
def _update_state(self, new_state: bool) -> None:
"""Update and report."""
@@ -354,7 +354,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
"name": self.atv.name,
"type": (
dev_info.raw_model
if dev_info.model == DeviceModel.Unknown and dev_info.raw_model
if dev_info.model is DeviceModel.Unknown and dev_info.raw_model
else model_str(dev_info.model)
),
}
@@ -441,12 +441,12 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
return await self.async_step_password()
# Figure out, depending on protocol, what kind of pairing is needed
if service.pairing == PairingRequirement.Unsupported:
if service.pairing is PairingRequirement.Unsupported:
_LOGGER.debug("%s does not support pairing", self.protocol)
return await self.async_pair_next_protocol()
if service.pairing == PairingRequirement.Disabled:
if service.pairing is PairingRequirement.Disabled:
return await self.async_step_protocol_disabled()
if service.pairing == PairingRequirement.NotNeeded:
if service.pairing is PairingRequirement.NotNeeded:
_LOGGER.debug("%s does not require pairing", self.protocol)
self.credentials[self.protocol.value] = None
return await self.async_pair_next_protocol()
@@ -457,7 +457,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
pair_args: dict[str, Any] = {}
if self.protocol in {Protocol.AirPlay, Protocol.Companion, Protocol.DMAP}:
pair_args["name"] = "Home Assistant"
if self.protocol == Protocol.DMAP:
if self.protocol is Protocol.DMAP:
pair_args["zeroconf"] = await zeroconf.async_get_instance(self.hass)
# Initiate the pairing process
@@ -24,6 +24,7 @@ from pyatv.interface import (
PushListener,
PushUpdater,
)
from yarl import URL
from homeassistant.components import media_source
from homeassistant.components.media_player import (
@@ -139,7 +140,7 @@ class AppleTvMediaPlayer(
all_features = atv.features.all_features()
for feature_name, support_flag in SUPPORT_FEATURE_MAPPING.items():
feature_info = all_features.get(feature_name)
if feature_info and feature_info.state != FeatureState.Unsupported:
if feature_info and feature_info.state is not FeatureState.Unsupported:
self._attr_supported_features |= support_flag
# No need to schedule state update here as that will happen when the first
@@ -188,14 +189,14 @@ class AppleTvMediaPlayer(
return MediaPlayerState.OFF
if (
self._is_feature_available(FeatureName.PowerState)
and self.atv.power.power_state == PowerState.Off
and self.atv.power.power_state is PowerState.Off
):
return MediaPlayerState.OFF
if self._playing:
state = self._playing.device_state
if state in (DeviceState.Idle, DeviceState.Loading):
return MediaPlayerState.IDLE
if state == DeviceState.Playing:
if state is DeviceState.Playing:
return MediaPlayerState.PLAYING
if state in (DeviceState.Paused, DeviceState.Seeking, DeviceState.Stopped):
return MediaPlayerState.PAUSED
@@ -345,7 +346,10 @@ class AppleTvMediaPlayer(
play_item = await media_source.async_resolve_media(
self.hass, media_id, self.entity_id
)
media_id = async_process_play_media_url(self.hass, play_item.url)
if play_item.path and self._is_feature_available(FeatureName.StreamFile):
media_id = str(play_item.path)
else:
media_id = async_process_play_media_url(self.hass, play_item.url)
media_type = MediaType.MUSIC
if self._is_feature_available(FeatureName.StreamFile) and (
@@ -353,11 +357,16 @@ class AppleTvMediaPlayer(
):
_LOGGER.debug("Streaming %s via RAOP", media_id)
await self.atv.stream.stream_file(media_id)
elif self._is_feature_available(FeatureName.PlayUrl):
elif self._is_feature_available(FeatureName.PlayUrl) and (
(parsed_url := URL(media_id)).is_absolute() and parsed_url.host
):
_LOGGER.debug("Playing %s via AirPlay", media_id)
await self.atv.stream.play_url(media_id)
else:
_LOGGER.error("Media streaming is not possible with current configuration")
_LOGGER.error(
"Media streaming is not possible with current configuration for %s",
media_id,
)
@property
def media_image_hash(self) -> str | None:
@@ -446,7 +455,7 @@ class AppleTvMediaPlayer(
def shuffle(self) -> bool | None:
"""Boolean if shuffle is enabled."""
if self._playing and self._is_feature_available(FeatureName.Shuffle):
return self._playing.shuffle != ShuffleState.Off
return self._playing.shuffle is not ShuffleState.Off
return None
def _is_feature_available(self, feature: FeatureName) -> bool:
@@ -506,7 +515,7 @@ class AppleTvMediaPlayer(
and (self._is_feature_available(FeatureName.TurnOff))
and (
not self._is_feature_available(FeatureName.PowerState)
or self.atv.power.power_state == PowerState.On
or self.atv.power.power_state is PowerState.On
)
):
await self.atv.power.turn_off()
@@ -59,7 +59,7 @@ def _check_keyboard_focus(atv: AppleTVInterface) -> None:
translation_domain=DOMAIN,
translation_key="keyboard_not_available",
) from err
if focus_state != KeyboardFocusState.Focused:
if focus_state is not KeyboardFocusState.Focused:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="keyboard_not_focused",
+5 -1
View File
@@ -193,7 +193,11 @@ async def async_setup_entry(
Aranet4BluetoothSensorEntity, async_add_entities
)
)
entry.async_on_unload(entry.runtime_data.async_register_processor(processor))
entry.async_on_unload(
entry.runtime_data.async_register_processor(
processor, AranetSensorEntityDescription
)
)
class Aranet4BluetoothSensorEntity(
@@ -16,6 +16,9 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import ArcamFmjConfigEntry
from .entity import ArcamFmjEntity
# Read-only, coordinator-driven entities; no per-entity I/O to bound.
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class ArcamFmjBinarySensorEntityDescription(BinarySensorEntityDescription):
@@ -1,9 +1,11 @@
"""Config flow to configure the Arcam FMJ component."""
import socket
from typing import Any
from urllib.parse import urlparse
from arcam.fmj.client import Client, ConnectionFailed
from arcam.fmj import ConnectionFailed
from arcam.fmj.client import Client
from arcam.fmj.utils import get_uniqueid_from_host, get_uniqueid_from_udn
import voluptuous as vol
@@ -14,6 +16,13 @@ from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_UDN, SsdpServiceIn
from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN
STEP_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
)
class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle config flow."""
@@ -29,26 +38,28 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(uuid)
self._abort_if_unique_id_configured({CONF_HOST: host, CONF_PORT: port})
async def _async_check_and_create(self, host: str, port: int) -> ConfigFlowResult:
async def _async_try_connect(self, host: str, port: int) -> dict[str, str]:
"""Verify the device is reachable; return errors keyed by reason."""
client = Client(host, port)
try:
await client.start()
except ConnectionFailed:
return self.async_abort(reason="cannot_connect")
except socket.gaierror:
return {"base": "invalid_host"}
except TimeoutError:
return {"base": "timeout_connect"}
except ConnectionRefusedError:
return {"base": "connection_refused"}
except ConnectionFailed, OSError:
return {"base": "cannot_connect"}
finally:
await client.stop()
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({host})",
data={CONF_HOST: host, CONF_PORT: port},
)
return {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a discovered device."""
errors: dict[str, str] = {}
if user_input is not None:
uuid = await get_uniqueid_from_host(
async_get_clientsession(self.hass), user_input[CONF_HOST]
@@ -58,17 +69,56 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
user_input[CONF_HOST], user_input[CONF_PORT], uuid
)
return await self._async_check_and_create(
errors = await self._async_try_connect(
user_input[CONF_HOST], user_input[CONF_PORT]
)
if not errors:
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({user_input[CONF_HOST]})",
data={
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
},
)
fields = {
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
schema = STEP_DATA_SCHEMA
if user_input is not None:
schema = self.add_suggested_values_to_schema(schema, user_input)
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of an existing entry."""
errors: dict[str, str] = {}
reconfigure_entry = self._get_reconfigure_entry()
if user_input is not None:
uuid = await get_uniqueid_from_host(
async_get_clientsession(self.hass), user_input[CONF_HOST]
)
if uuid:
await self.async_set_unique_id(uuid)
self._abort_if_unique_id_mismatch()
errors = await self._async_try_connect(
user_input[CONF_HOST], user_input[CONF_PORT]
)
if not errors:
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates={
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
},
)
schema = self.add_suggested_values_to_schema(
STEP_DATA_SCHEMA, user_input or reconfigure_entry.data
)
return self.async_show_form(
step_id="user", data_schema=vol.Schema(fields), errors=errors
step_id="reconfigure", data_schema=schema, errors=errors
)
async def async_step_confirm(
@@ -79,7 +129,10 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
self.context["title_placeholders"] = placeholders
if user_input is not None:
return await self._async_check_and_create(self.host, self.port)
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({self.host})",
data={CONF_HOST: self.host, CONF_PORT: self.port},
)
return self.async_show_form(
step_id="confirm", description_placeholders=placeholders
@@ -97,6 +150,9 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
await self._async_set_unique_id_and_update(host, port, uuid)
if await self._async_try_connect(host, port):
return self.async_abort(reason="cannot_connect")
self.host = host
self.port = DEFAULT_PORT
self.port = port
return await self.async_step_confirm()
@@ -53,18 +53,19 @@ class ArcamFmjCoordinator(DataUpdateCoordinator[None]):
self.state = State(client, zone)
self.update_in_progress = False
name = config_entry.title
device_name = config_entry.title
unique_id = config_entry.unique_id or config_entry.entry_id
unique_id_device = unique_id
if zone != 1:
unique_id_device += f"-{zone}"
name += f" Zone {zone}"
device_name += f" Zone {zone}"
self.device_name = device_name
self.device_info = DeviceInfo(
identifiers={(DOMAIN, unique_id_device)},
manufacturer="Arcam",
model="Arcam FMJ AVR",
name=name,
name=device_name,
)
self.zone_unique_id = f"{unique_id}-{zone}"
@@ -1,11 +1,36 @@
"""Base entity for Arcam FMJ integration."""
from collections.abc import Callable, Coroutine
import functools
from typing import Any
from arcam.fmj import ConnectionFailed
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import ArcamFmjCoordinator
def convert_exception[**_P, _R](
func: Callable[_P, Coroutine[Any, Any, _R]],
) -> Callable[_P, Coroutine[Any, Any, _R]]:
"""Convert a connection failure into a translated HomeAssistantError."""
@functools.wraps(func)
async def _convert_exception(*args: _P.args, **kwargs: _P.kwargs) -> _R:
try:
return await func(*args, **kwargs)
except ConnectionFailed as exception:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="connection_failed"
) from exception
return _convert_exception
class ArcamFmjEntity(CoordinatorEntity[ArcamFmjCoordinator]):
"""Base entity for Arcam FMJ."""
@@ -1,11 +1,9 @@
"""Arcam media player."""
from collections.abc import Callable, Coroutine
import functools
import logging
from typing import Any
from arcam.fmj import ConnectionFailed, SourceCodes
from arcam.fmj import SourceCodes
from homeassistant.components.media_player import (
BrowseError,
@@ -18,15 +16,19 @@ from homeassistant.components.media_player import (
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN, EVENT_TURN_ON
from .coordinator import ArcamFmjConfigEntry, ArcamFmjCoordinator
from .entity import ArcamFmjEntity
from .entity import ArcamFmjEntity, convert_exception
_LOGGER = logging.getLogger(__name__)
# arcam-fmj serializes commands on a single TCP writer at the library
# layer; serialize at HA's layer to match the device's contract.
PARALLEL_UPDATES = 1
async def async_setup_entry(
hass: HomeAssistant,
@@ -41,23 +43,6 @@ async def async_setup_entry(
)
def convert_exception[**_P, _R](
func: Callable[_P, Coroutine[Any, Any, _R]],
) -> Callable[_P, Coroutine[Any, Any, _R]]:
"""Return decorator to convert a connection error into a home assistant error."""
@functools.wraps(func)
async def _convert_exception(*args: _P.args, **kwargs: _P.kwargs) -> _R:
try:
return await func(*args, **kwargs)
except ConnectionFailed as exception:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="connection_failed"
) from exception
return _convert_exception
class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
"""Representation of a media device."""
@@ -79,11 +64,17 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
self._attr_supported_features |= MediaPlayerEntityFeature.SELECT_SOUND_MODE
@property
def state(self) -> MediaPlayerState:
"""Return the state of the device."""
if self._state.get_power():
return MediaPlayerState.ON
return MediaPlayerState.OFF
def state(self) -> MediaPlayerState | None:
"""Return the state of the device.
``None`` is returned (surfaced as ``unknown``) when the device has
not yet reported a power state; this is distinct from a real
powered-off state and must not be collapsed to ``OFF``.
"""
power = self._state.get_power()
if power is None:
return None
return MediaPlayerState.ON if power else MediaPlayerState.OFF
@convert_exception
async def async_mute_volume(self, mute: bool) -> None:
@@ -179,7 +170,7 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
]
return BrowseMedia(
title="Arcam FMJ Receiver",
title=self.coordinator.device_name,
media_class=MediaClass.DIRECTORY,
media_content_id="root",
media_content_type="library",
@@ -272,9 +263,9 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
def media_channel(self) -> str | None:
"""Channel currently playing."""
source = self._state.get_source()
if source == SourceCodes.DAB:
if source is SourceCodes.DAB:
value = self._state.get_dab_station()
elif source == SourceCodes.FM:
elif source is SourceCodes.FM:
value = self._state.get_rds_information()
else:
value = None
@@ -283,7 +274,7 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
@property
def media_artist(self) -> str | None:
"""Artist of current playing media, music track only."""
if self._state.get_source() == SourceCodes.DAB:
if self._state.get_source() is SourceCodes.DAB:
value = self._state.get_dls_pdt()
else:
value = None
@@ -22,6 +22,9 @@ from .entity import ArcamFmjEntity
_LOGGER = logging.getLogger(__name__)
# Read-only, coordinator-driven entities; no per-entity I/O to bound.
PARALLEL_UPDATES = 0
def _enum_options(value: type[IntOrTypeEnum]) -> list[str]:
return [
@@ -3,13 +3,28 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unique_id_mismatch": "Please ensure you reconfigure against the same device."
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"connection_refused": "Host refused connection",
"invalid_host": "[%key:common::config_flow::error::invalid_host%]",
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
},
"flow_title": "{host}",
"step": {
"confirm": {
"description": "Do you want to add Arcam FMJ on `{host}` to Home Assistant?"
},
"reconfigure": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"description": "[%key:component::arcam_fmj::config::step::user::description%]"
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
@@ -1355,7 +1355,7 @@ class PipelineRun:
) -> bool:
"""Return true if all targeted entities were in the same area as the device."""
if (
intent_response.response_type != intent.IntentResponseType.ACTION_DONE
intent_response.response_type is not intent.IntentResponseType.ACTION_DONE
or not intent_response.matched_states
):
return False
+14 -14
View File
@@ -49,6 +49,20 @@ SENSORS_TYPE_COUNT = "sensors_count"
_LOGGER = logging.getLogger(__name__)
_ENTITY_MIGRATION_ID = {
"sensor_connected_device": "Devices Connected",
"sensor_rx_bytes": "Download",
"sensor_tx_bytes": "Upload",
"sensor_rx_rates": "Download Speed",
"sensor_tx_rates": "Upload Speed",
"sensor_load_avg1": "Load Avg (1m)",
"sensor_load_avg5": "Load Avg (5m)",
"sensor_load_avg15": "Load Avg (15m)",
"2.4GHz": "2.4GHz Temperature",
"5.0GHz": "5GHz Temperature",
"CPU": "CPU Temperature",
}
class AsusWrtSensorDataHandler:
"""Data handler for AsusWrt sensor."""
@@ -187,20 +201,6 @@ class AsusWrtRouter:
def _migrate_entities_unique_id(self) -> None:
"""Migrate router entities to new unique id format."""
_ENTITY_MIGRATION_ID = {
"sensor_connected_device": "Devices Connected",
"sensor_rx_bytes": "Download",
"sensor_tx_bytes": "Upload",
"sensor_rx_rates": "Download Speed",
"sensor_tx_rates": "Upload Speed",
"sensor_load_avg1": "Load Avg (1m)",
"sensor_load_avg5": "Load Avg (5m)",
"sensor_load_avg15": "Load Avg (15m)",
"2.4GHz": "2.4GHz Temperature",
"5.0GHz": "5GHz Temperature",
"CPU": "CPU Temperature",
}
entity_reg = er.async_get(self.hass)
router_entries = er.async_entries_for_config_entry(
entity_reg, self._entry.entry_id
@@ -9,12 +9,11 @@ import voluptuous as vol
from homeassistant.components import usb
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import ATTR_SERIAL_NUMBER, CONF_ADDRESS, CONF_PORT
from homeassistant.const import ATTR_MODEL, ATTR_SERIAL_NUMBER, CONF_ADDRESS, CONF_PORT
from homeassistant.core import HomeAssistant
from .const import (
ATTR_FIRMWARE,
ATTR_MODEL,
DEFAULT_ADDRESS,
DEFAULT_INTEGRATION_TITLE,
DOMAIN,
@@ -19,6 +19,4 @@ DEVICES = "devices"
MANUFACTURER = "ABB"
ATTR_DEVICE_NAME = "device_name"
ATTR_DEVICE_ID = "device_id"
ATTR_MODEL = "model"
ATTR_FIRMWARE = "firmware"
@@ -13,6 +13,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
ATTR_MODEL,
ATTR_SERIAL_NUMBER,
EntityCategory,
UnitOfElectricCurrent,
@@ -31,7 +32,6 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ATTR_DEVICE_NAME,
ATTR_FIRMWARE,
ATTR_MODEL,
DEFAULT_DEVICE_NAME,
DOMAIN,
MANUFACTURER,
@@ -82,7 +82,7 @@ rules:
comment: |
This integration does not have any entities that should disabled by default.
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues:
+4 -4
View File
@@ -251,12 +251,12 @@ class AuthProvidersView(HomeAssistantView):
def _prepare_result_json(result: AuthFlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY:
return {
key: val for key, val in result.items() if key not in ("result", "data")
}
if result["type"] != data_entry_flow.FlowResultType.FORM:
if result["type"] is not data_entry_flow.FlowResultType.FORM:
return result # type: ignore[return-value]
data = dict(result)
@@ -289,11 +289,11 @@ class LoginFlowBaseView(HomeAssistantView):
result: AuthFlowResult,
) -> web.Response:
"""Convert the flow result to a response."""
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is not data_entry_flow.FlowResultType.CREATE_ENTRY:
# @log_invalid_auth does not work here since it returns HTTP 200.
# We need to manually log failed login attempts.
if (
result["type"] == data_entry_flow.FlowResultType.FORM
result["type"] is data_entry_flow.FlowResultType.FORM
and (errors := result.get("errors"))
and errors.get("base")
in (
@@ -142,9 +142,9 @@ def websocket_depose_mfa(
def _prepare_result_json(result: data_entry_flow.FlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY:
return dict(result)
if result["type"] != data_entry_flow.FlowResultType.FORM:
if result["type"] is not data_entry_flow.FlowResultType.FORM:
return result # type: ignore[return-value]
data = dict(result)
@@ -67,7 +67,7 @@ rules:
comment: |
Only one entity type (device_tracker) is created, making this not applicable.
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow:
status: todo
+46 -6
View File
@@ -1,5 +1,6 @@
"""Light platform for Avea."""
from collections.abc import Callable
from contextlib import suppress
import logging
from typing import Any
@@ -19,6 +20,7 @@ from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@@ -27,7 +29,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import color as color_util
from . import AveaConfigEntry
from .const import DOMAIN, INTEGRATION_TITLE, UNKNOWN_NAME
from .const import DOMAIN, INTEGRATION_TITLE, MODEL, UNKNOWN_NAME
_LOGGER = logging.getLogger(__name__)
UPDATE_EXCEPTIONS = (BleakError, OSError, RuntimeError)
@@ -42,6 +44,13 @@ def _normalize_name(name: str | None) -> str | None:
return name
def _read_device_info_value(read: Callable[[], str | None]) -> str | None:
"""Read a device information value from an Avea bulb."""
with suppress(*UPDATE_EXCEPTIONS):
return _normalize_name(read())
return None
def _ha_brightness_to_avea(brightness: int) -> int:
"""Convert Home Assistant brightness to Avea brightness."""
return round((brightness / 255) * AVEA_MAX_BRIGHTNESS)
@@ -96,7 +105,8 @@ async def async_setup_entry(
) -> None:
"""Set up the Avea light platform."""
async_add_entities(
[AveaLight(entry.runtime_data, entry.title)], update_before_add=True
[AveaLight(entry.runtime_data, entry.data[CONF_ADDRESS])],
update_before_add=True,
)
@@ -180,14 +190,42 @@ class AveaLight(LightEntity):
"""Representation of an Avea."""
_attr_color_mode = ColorMode.HS
_attr_has_entity_name = True
_attr_name = None
_attr_supported_color_modes = {ColorMode.HS}
def __init__(self, light: avea.Bulb, entry_title: str) -> None:
def __init__(self, light: avea.Bulb, address: str) -> None:
"""Initialize an AveaLight."""
self._light = light
self._attr_name = entry_title
self._attr_unique_id = address
self._attr_brightness = light.brightness
self._last_brightness = 255
self._device_info_updated = False
self._attr_device_info = DeviceInfo(
connections={(CONNECTION_BLUETOOTH, address)},
model=MODEL,
)
def _update_device_info(self) -> None:
"""Fetch device information from the Avea bulb."""
device_info = self._attr_device_info
assert device_info is not None
manufacturer = _read_device_info_value(self._light.get_manufacturer_name)
hardware_revision = _read_device_info_value(self._light.get_hardware_revision)
firmware_version = _read_device_info_value(self._light.get_fw_version)
serial_number = _read_device_info_value(self._light.get_serial_number)
if manufacturer:
device_info["manufacturer"] = manufacturer
if hardware_revision:
device_info["hw_version"] = hardware_revision
if firmware_version:
device_info["sw_version"] = firmware_version
if serial_number:
device_info["serial_number"] = serial_number
self._device_info_updated = True
def turn_on(self, **kwargs: Any) -> None:
"""Instruct the light to turn on."""
@@ -205,15 +243,17 @@ class AveaLight(LightEntity):
def turn_off(self, **kwargs: Any) -> None:
"""Instruct the light to turn off."""
if self._attr_brightness:
self._last_brightness = self._attr_brightness
self._light.set_brightness(0)
self._attr_is_on = False
self._attr_brightness = 0
def update(self) -> None:
"""Fetch new state data for this light."""
connected = self._light.connect()
try:
if not self._device_info_updated:
self._update_device_info()
brightness = self._light.get_brightness()
rgb_color = self._light.get_rgb()
finally:
+1 -1
View File
@@ -14,5 +14,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["avea"],
"requirements": ["avea==1.6.1"]
"requirements": ["avea==1.8.0"]
}
@@ -51,6 +51,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
translation_key="invalid_bucket_name",
) from err
except ValueError as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_endpoint_url",
+2 -1
View File
@@ -17,10 +17,11 @@ from homeassistant.components.backup import (
OnProgressCallback,
suggested_filename,
)
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant, callback
from . import S3ConfigEntry
from .const import CONF_BUCKET, CONF_PREFIX, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .const import CONF_BUCKET, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .helpers import async_list_backups_from_s3
_LOGGER = logging.getLogger(__name__)
@@ -8,6 +8,7 @@ from botocore.exceptions import ClientError, ConnectionError, ParamValidationErr
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PREFIX
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
TextSelector,
@@ -20,7 +21,6 @@ from .const import (
CONF_ACCESS_KEY_ID,
CONF_BUCKET,
CONF_ENDPOINT_URL,
CONF_PREFIX,
CONF_SECRET_ACCESS_KEY,
DEFAULT_ENDPOINT_URL,
DESCRIPTION_AWS_S3_DOCS_URL,
-1
View File
@@ -11,7 +11,6 @@ CONF_ACCESS_KEY_ID = "access_key_id"
CONF_SECRET_ACCESS_KEY = "secret_access_key"
CONF_ENDPOINT_URL = "endpoint_url"
CONF_BUCKET = "bucket"
CONF_PREFIX = "prefix"
AWS_DOMAIN = "amazonaws.com"
DEFAULT_ENDPOINT_URL = f"https://s3.eu-central-1.{AWS_DOMAIN}/"
@@ -8,10 +8,11 @@ from aiobotocore.client import AioBaseClient as S3Client
from botocore.exceptions import BotoCoreError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_BUCKET, CONF_PREFIX, DOMAIN
from .const import CONF_BUCKET, DOMAIN
from .helpers import async_list_backups_from_s3
SCAN_INTERVAL = timedelta(hours=6)
@@ -5,15 +5,10 @@ from typing import Any
from homeassistant.components.backup import DATA_MANAGER as BACKUP_DATA_MANAGER
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant
from .const import (
CONF_ACCESS_KEY_ID,
CONF_BUCKET,
CONF_PREFIX,
CONF_SECRET_ACCESS_KEY,
DOMAIN,
)
from .const import CONF_ACCESS_KEY_ID, CONF_BUCKET, CONF_SECRET_ACCESS_KEY, DOMAIN
from .coordinator import S3ConfigEntry
from .helpers import async_list_backups_from_s3
@@ -72,7 +72,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not use icons.
+25 -19
View File
@@ -2,13 +2,12 @@
from collections.abc import Mapping
from ipaddress import ip_address
from typing import Any
from typing import TYPE_CHECKING, Any
from urllib.parse import urlsplit
import voluptuous as vol
from homeassistant.config_entries import (
SOURCE_IGNORE,
SOURCE_REAUTH,
SOURCE_RECONFIGURE,
ConfigEntry,
@@ -50,6 +49,9 @@ from .const import (
from .errors import AuthenticationRequired, CannotConnect
from .hub import AxisHub, get_axis_api
if TYPE_CHECKING:
import axis
AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f", "e8:27:25"}
DEFAULT_PORT = 443
DEFAULT_PROTOCOL = "https"
@@ -94,7 +96,8 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
errors["base"] = "cannot_connect"
else:
serial = api.vapix.serial_number
if (serial := self._get_serial_number(api)) is None:
return self.async_abort(reason="no_serial_number")
config = {
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
CONF_HOST: user_input[CONF_HOST],
@@ -139,25 +142,15 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
async def _create_entry(self, serial: str) -> ConfigFlowResult:
"""Create entry for device.
Generate a name to be used as a prefix for device entities.
Use the discovered device name when available.
"""
model = self.config[CONF_MODEL]
same_model = [
entry.data[CONF_NAME]
for entry in self.hass.config_entries.async_entries(DOMAIN)
if entry.source != SOURCE_IGNORE and entry.data[CONF_MODEL] == model
]
name = model
for idx in range(len(same_model) + 1):
name = f"{model} {idx}"
if name not in same_model:
break
if (title_placeholders := self.context.get("title_placeholders")) is not None:
name = title_placeholders[CONF_NAME]
else:
name = f"{self.config[CONF_MODEL]} - {serial}"
self.config[CONF_NAME] = name
title = f"{model} - {serial}"
return self.async_create_entry(title=title, data=self.config)
return self.async_create_entry(title=name, data=self.config)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
@@ -269,6 +262,19 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self.async_step_user()
@staticmethod
def _get_serial_number(api: axis.AxisDevice) -> str | None:
"""Retrieve the device serial number from the Axis API.
Tries basic_device_info first, then property_handler. Returns None if not found.
"""
vapix = api.vapix
if vapix.basic_device_info.initialized:
return vapix.basic_device_info["0"].serial_number
if vapix.params.property_handler.initialized:
return vapix.params.property_handler["0"].system_serial_number
return None
class AxisOptionsFlowHandler(OptionsFlow):
"""Handle Axis device options."""
@@ -2,8 +2,7 @@
import axis
from axis.errors import Unauthorized
from axis.interfaces.mqtt import mqtt_json_to_event
from axis.models.mqtt import ClientState
from axis.models.mqtt import ClientState, mqtt_json_to_event
from axis.stream_manager import Signal, State
from homeassistant.components import mqtt
+1 -1
View File
@@ -29,7 +29,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["axis"],
"requirements": ["axis==71"],
"requirements": ["axis==72"],
"ssdp": [
{
"manufacturer": "AXIS"
@@ -3,6 +3,7 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"link_local_address": "Link local addresses are not supported",
"no_serial_number": "Could not retrieve a serial number from the device. Please check device connectivity and try again.",
"not_axis_device": "Discovered device not an Axis device",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
@@ -75,11 +75,13 @@ def handle_backup_errors[_R, **P](
err.message,
exc_info=True,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
f"Error during backup operation in {func.__name__}:"
f" Status {err.status_code}, message: {err.message}"
) from err
except ServiceRequestError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
f"Timeout during backup operation in {func.__name__}"
) from err
@@ -90,6 +92,7 @@ def handle_backup_errors[_R, **P](
err,
exc_info=True,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
f"Error during backup operation in {func.__name__}: {err}"
) from err
@@ -118,6 +121,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Download a backup file."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
download_stream = await self._client.download_blob(blob.name)
return download_stream.chunks()
@@ -155,6 +159,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Delete a backup file."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
await self._client.delete_blob(blob.name)
@@ -181,6 +186,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Return a backup."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
return AgentBackup.from_dict(json.loads(blob.metadata["backup_metadata"]))
@@ -89,6 +89,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BackblazeConfigEntry) ->
translation_key="cannot_connect",
) from err
except exception.MissingAccountData as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -20,12 +20,12 @@ from homeassistant.components.backup import (
OnProgressCallback,
suggested_filename,
)
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant, callback
from homeassistant.util.async_iterator import AsyncIteratorReader
from . import BackblazeConfigEntry
from .const import (
CONF_PREFIX,
DATA_BACKUP_AGENT_LISTENERS,
DOMAIN,
METADATA_FILE_SUFFIX,
@@ -8,6 +8,7 @@ from b2sdk.v2 import exception
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PREFIX
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
TextSelector,
@@ -22,7 +23,6 @@ from .const import (
CONF_APPLICATION_KEY,
CONF_BUCKET,
CONF_KEY_ID,
CONF_PREFIX,
DOMAIN,
)
@@ -10,7 +10,6 @@ DOMAIN: Final = "backblaze_b2"
CONF_KEY_ID = "key_id"
CONF_APPLICATION_KEY = "application_key"
CONF_BUCKET = "bucket"
CONF_PREFIX = "prefix"
DATA_BACKUP_AGENT_LISTENERS: HassKey[list[Callable[[], None]]] = HassKey(
f"{DOMAIN}.backup_agent_listeners"
@@ -96,7 +96,7 @@ rules:
entity-translations:
status: exempt
comment: This integration does not have entities.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not use icons.
@@ -67,6 +67,9 @@
"cannot_connect": {
"message": "Cannot connect to endpoint"
},
"invalid_auth": {
"message": "Authentication failed using the provided key ID and application key."
},
"invalid_bucket_name": {
"message": "Bucket does not exist or is not writable by the provided credentials."
},
@@ -32,6 +32,7 @@ PLATFORMS = [
Platform.LIGHT,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,
]
PARALLEL_UPDATES = 0
+31 -3
View File
@@ -3,7 +3,7 @@
from typing import Any
import blebox_uniapi.cover
from blebox_uniapi.cover import BleboxCoverState
from blebox_uniapi.cover import BleboxCoverState, UnifiedCoverType
from homeassistant.components.cover import (
ATTR_POSITION,
@@ -25,6 +25,19 @@ BLEBOX_TO_COVER_DEVICE_CLASSES = {
"shutter": CoverDeviceClass.SHUTTER,
}
UNIFIED_COVER_TYPE_TO_DEVICE_CLASS = {
UnifiedCoverType.AWNING: CoverDeviceClass.AWNING,
UnifiedCoverType.BLIND: CoverDeviceClass.BLIND,
UnifiedCoverType.CURTAIN: CoverDeviceClass.CURTAIN,
UnifiedCoverType.DAMPER: CoverDeviceClass.DAMPER,
UnifiedCoverType.DOOR: CoverDeviceClass.DOOR,
UnifiedCoverType.GARAGE: CoverDeviceClass.GARAGE,
UnifiedCoverType.GATE: CoverDeviceClass.GATE,
UnifiedCoverType.SHADE: CoverDeviceClass.SHADE,
UnifiedCoverType.SHUTTER: CoverDeviceClass.SHUTTER,
UnifiedCoverType.WINDOW: CoverDeviceClass.WINDOW,
}
BLEBOX_TO_HASS_COVER_STATES = {
None: None,
# all blebox covers
@@ -59,7 +72,6 @@ class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
def __init__(self, feature: blebox_uniapi.cover.Cover) -> None:
"""Initialize a BleBox cover feature."""
super().__init__(feature)
self._attr_device_class = BLEBOX_TO_COVER_DEVICE_CLASSES[feature.device_class]
self._attr_supported_features = (
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
)
@@ -76,6 +88,21 @@ class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
| CoverEntityFeature.CLOSE_TILT
)
if feature.tilt_only:
self._attr_supported_features &= ~(
CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.SET_POSITION
| CoverEntityFeature.STOP
)
@property
def device_class(self) -> CoverDeviceClass | None:
"""Return the device class based on cover type when available."""
if (cover_type := self._feature.cover_type) is not None:
return UNIFIED_COVER_TYPE_TO_DEVICE_CLASS[cover_type]
return BLEBOX_TO_COVER_DEVICE_CLASSES[self._feature.device_class]
@property
def current_cover_position(self) -> int | None:
"""Return the current cover position."""
@@ -118,7 +145,8 @@ class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
"""Fully open the cover tilt."""
await self._feature.async_set_tilt_position(0)
position = 50 if self._feature.is_tilt_180 else 0
await self._feature.async_set_tilt_position(position)
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
"""Fully close the cover tilt."""
@@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["blebox_uniapi"],
"requirements": ["blebox-uniapi==2.5.3"],
"requirements": ["blebox-uniapi==2.5.4"],
"zeroconf": ["_bbxsrv._tcp.local."]
}
+141
View File
@@ -0,0 +1,141 @@
"""BleBox update entities implementation."""
from datetime import timedelta
from typing import Any, Final
from blebox_uniapi.error import ConnectionError as BleBoxConnectionError, Error
import blebox_uniapi.update
from homeassistant.components.update import (
UpdateDeviceClass,
UpdateEntity,
UpdateEntityFeature,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.event import async_call_later
from . import BleBoxConfigEntry
from .entity import BleBoxEntity
SCAN_INTERVAL = timedelta(hours=1)
_POLL_INTERVAL_SECONDS: Final = 10
_MAX_POLL_ATTEMPTS: Final = 30
async def async_setup_entry(
hass: HomeAssistant,
config_entry: BleBoxConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up a BleBox update entry."""
entities = [
BleBoxUpdateEntity(feature)
for feature in config_entry.runtime_data.features.get("updates", [])
]
async_add_entities(entities, True)
class BleBoxUpdateEntity(BleBoxEntity[blebox_uniapi.update.Update], UpdateEntity):
"""Representation of BleBox updates."""
_attr_device_class = UpdateDeviceClass.FIRMWARE
_attr_supported_features = (
UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
)
def __init__(self, feature: blebox_uniapi.update.Update) -> None:
"""Initialize the update entity."""
super().__init__(feature)
self._in_progress_old_version: str | None = None
self._poll_cancel: CALLBACK_TYPE | None = None
self._poll_attempts: int = 0
@property
def in_progress(self) -> bool:
"""Return True while the device hasn't yet rebooted to the new firmware."""
return (
self._in_progress_old_version is not None
and self._in_progress_old_version == self._feature.installed_version
)
def _sync_sw_version(self) -> None:
"""Sync installed firmware version to the device registry."""
if self.device_entry:
dr.async_get(self.hass).async_update_device(
self.device_entry.id,
sw_version=self._feature.installed_version,
)
async def async_update(self) -> None:
"""Update state and refresh sw_version in device registry."""
try:
await self._feature.async_update()
except Error as ex:
raise HomeAssistantError(ex) from ex
self._sync_sw_version()
@property
def installed_version(self) -> str | None:
"""Version installed and in use."""
return self._feature.installed_version
@property
def latest_version(self) -> str | None:
"""Latest version available for install."""
return self._feature.latest_version
def _cancel_poll(self) -> None:
if self._poll_cancel is not None:
self._poll_cancel()
self._poll_cancel = None
def _reset_progress(self) -> None:
self._in_progress_old_version = None
self._poll_attempts = 0
self.async_write_ha_state()
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
self._cancel_poll()
self._in_progress_old_version = self._feature.installed_version
self._poll_attempts = 0
self.async_write_ha_state()
try:
await self._feature.async_install()
except Error as ex:
self._reset_progress()
raise HomeAssistantError(ex) from ex
self._poll_cancel = async_call_later(
self.hass, _POLL_INTERVAL_SECONDS, self._poll_until_updated
)
async def async_will_remove_from_hass(self) -> None:
"""Cancel any pending poll timer when the entity is removed."""
self._cancel_poll()
async def _poll_until_updated(self, _now: Any) -> None:
"""Poll device until the installed version changes after OTA reboot."""
self._poll_cancel = None
self._poll_attempts += 1
try:
await self._feature.async_update()
except BleBoxConnectionError:
pass
except Error:
self._reset_progress()
return
else:
self._sync_sw_version()
if self.in_progress and self._poll_attempts < _MAX_POLL_ATTEMPTS:
self._poll_cancel = async_call_later(
self.hass, _POLL_INTERVAL_SECONDS, self._poll_until_updated
)
else:
self._reset_progress()
+2
View File
@@ -169,6 +169,7 @@ class BlinkCamera(CoordinatorEntity[BlinkUpdateCoordinator], Camera):
try:
await self._camera.save_recent_clips(output_dir=file_path)
except OSError as err:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
str(err),
translation_domain=DOMAIN,
@@ -190,6 +191,7 @@ class BlinkCamera(CoordinatorEntity[BlinkUpdateCoordinator], Camera):
try:
await self._camera.video_to_file(filename)
except OSError as err:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
str(err),
translation_domain=DOMAIN,

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