Compare commits

...

424 Commits

Author SHA1 Message Date
J. Nick Koston 8d6baf9878 Apply suggestions from code review
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-05-19 21:19:29 -05:00
J. Nick Koston f4d4687626 fix bad venv 2026-05-16 22:03:26 -07:00
J. Nick Koston 14d6ea14f3 Bump bluetooth-data-tools to 1.29.5
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.28.4...v1.29.5
2026-05-16 22:02:03 -07:00
J. Nick Koston 5dbe8d54e8 Bump bluetooth-data-tools to 1.29.5
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.28.4...v1.29.5
2026-05-16 22:01:37 -07:00
J. Nick Koston d64e596e99 Bump bluetooth-data-tools to 1.29.5
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.28.4...v1.29.5
2026-05-16 22:01:16 -07:00
J. Nick Koston 5383e9c0a7 Bump bluetooth-data-tools to 1.29.5
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.28.4...v1.29.5
2026-05-16 22:00:59 -07:00
TomFilsell b96f904d15 cert_expiry: Fix error attribute returning string "None" for valid certificates (#170878)
Co-authored-by: FIls0010 <a1867444@adelaide.edu.au>
2026-05-16 20:55:04 -04:00
Markus Adrario 0d16fa1e65 bump pyHomee to 1.4.0 (#170934) 2026-05-16 20:53:12 -04:00
wollew 27816fcb0c Bump pyvlx to 0.2.34 (#170919) 2026-05-16 20:52:45 -04:00
Simone Chemelli 4f0faf43c6 Bump aioamazondevices to 13.7.0 (#170935)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-16 20:51:40 -04:00
Paulus Schoutsen c28f5d3eed Add Marantz IR Remote integration (#169626)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:07:54 -04:00
puddly 7b589d6ce8 Disable USB discovery for teleinfo (#170933) 2026-05-16 18:59:46 -04:00
Michael b5556e17b2 Add exception translations to FRITZ!SmartHome (#170445)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-16 23:03:12 +02:00
Michael 407d29396a Fix swallowed exceptions in adguard action handlers (#170918) 2026-05-16 22:38:40 +02:00
Manu 7eaa132189 Return response only if requested in mastodon.update_profile action (#170921) 2026-05-16 21:50:40 +02:00
Franck Nijhof 87b151a436 Fix line length violations in tests/components d-f (#170881) 2026-05-16 21:17:51 +02:00
Paulus Schoutsen 19cbb3e5c9 Fix flaky kraken sensor test by waiting for background tasks (#170916)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:14:06 +02:00
Maciej Bieniek 675bbd704c Add Shelly occupancy binary sensor (#170894) 2026-05-16 12:25:02 -04:00
Abílio Costa 30a51e643f Make infrared test messages strict again (#170903) 2026-05-16 12:24:27 -04:00
Franck Nijhof 6ae50fffe1 Populate uid and recurrence_id in CalDAV calendar events (#170910) 2026-05-16 12:22:54 -04:00
Abílio Costa e60704ccec Remove rf-protocols requirement from individual integrations (#170912) 2026-05-16 12:22:11 -04:00
Mick Vleeshouwer ad9a7c08ab Fix is_closed state for SlidingDiscreteGateWithPedestrianPosition covers in Overkiz (#170913) 2026-05-16 17:58:22 +02:00
Franck Nijhof 198cb331ed Fix flaky plex update test (#170911) 2026-05-16 17:50:48 +02:00
Daniil Karpenko fc8949d4a2 Add tilt controls for UpDownSheerScreen in Overkiz (#170563)
Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
2026-05-16 17:27:12 +02:00
iluebbe ed74360485 Bump rf-protocols to 3.2.0 (#170909)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:18:42 +01:00
Michael 9109cb5bfb Fix swallowed exceptions in synology_dsm action handlers (#170879) 2026-05-16 17:10:53 +02:00
Franck Nijhof 5c29580969 Fix line length violations in tests/components g (#170882) 2026-05-16 17:06:36 +02:00
Simone Chemelli 3813843c8c Bump aioamazondevices to 13.6.0 (#170904) 2026-05-16 17:04:34 +02:00
Abílio Costa 5ac7f898dd Re-add clarification comment to lg infrared (#170902) 2026-05-16 16:25:03 +02:00
renovate[bot] 85e7c12535 Update uv to 0.11.12 (#170851) 2026-05-16 11:18:04 +02:00
Franck Nijhof 5472a537ed Replace unused mock_setup_entry arguments with @pytest.mark.usefixtures (#170760)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-16 11:02:36 +02:00
A. Gideonse a1e22c22d3 Add cycle count / transformer temp to Indevolt (#170794) 2026-05-16 10:30:29 +02:00
J. Nick Koston a69e0ca7c5 Bump aioesphomeapi to 45.0.2 (#170869) 2026-05-16 09:19:26 +02:00
Mark Cracknell fb129255b5 Add Tuya fixture for Madimack Elite V3 (#168527) 2026-05-16 08:45:08 +02:00
renovate[bot] f521e35e68 Update propcache to 0.5.2 (#170855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-05-16 08:38:56 +02:00
Simone Chemelli e7eb508277 Cleanup and simplify sensor code in Alexa Devices (#170793) 2026-05-16 08:11:42 +02:00
Daniel Jolly 816544b5e9 Fixed todo item capitalization (#170871) 2026-05-16 08:09:47 +02:00
Abílio Costa e46ad54f51 Add helper base classes for infrared consumers (#170854)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: abmantis <974569+abmantis@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 23:45:12 -04:00
J. Nick Koston 2aa95b1280 Bump aiodns to 4.0.3 (#170865)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-15 23:39:55 -04:00
renovate[bot] 542afb5081 Update infrared-protocols to 5.4.0 (#170852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-15 23:31:44 -04:00
J. Nick Koston 32dde39a77 Bump aioesphomeapi to 45.0.1 (#170842) 2026-05-15 23:23:05 -04:00
Paulus Schoutsen 81280f9cca Fix flaky input_datetime tests at date boundaries (#170861)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 20:14:16 -07:00
Paulus Schoutsen bbd6aa83b1 Fix flaky test_add_event_date_in_x in Google Calendar (#170863)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 20:14:09 -07:00
Paulus Schoutsen cbcf3f3eb4 Pin aiofile to 3.9.0 to fix test collection crash (#170864)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 22:53:41 -04:00
Vincent van Adrighem 362bcf722c Fix KeyError in CalDAV when supported components are not reported (#170468) 2026-05-15 21:38:37 -04:00
Erwin Douna 54da9649dc Tado optimize executor job usage (#170836) 2026-05-15 22:21:10 +02:00
Franck Nijhof 31f98c12af Fix line length violations in tests/components b (#170825)
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-15 15:51:13 -04:00
Franck Nijhof 5f811e856f Add pylint checker for redundant EntityDescription defaults (#170810)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 21:49:17 +02:00
Glenn Waters 4e7bfe2e48 UPB integration: bump library version to 0.7.1 (#170833) 2026-05-15 21:28:44 +02:00
Christian Lackas 5e383eeb2e Fix homematicip_cloud config entry setup crash after migration to 2026.5.0 (#170156) 2026-05-15 21:19:04 +02:00
Franck Nijhof 84019955ce Add pylint checker for sequential async_add_executor_job calls (#170789)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 20:31:46 +02:00
Franck Nijhof 7da2014ff2 Add pylint checker for redundant @pytest.mark.usefixtures decorator (#170787) 2026-05-15 20:29:33 +02:00
Simone Chemelli df541bd30e Guard migration when downgrading from a future version for Alexa devices (#170811) 2026-05-15 20:29:06 +02:00
Simone Chemelli 905fd28112 Guard migration when downgrading from a future version for SamsungTV (#170823) 2026-05-15 20:28:41 +02:00
Paulus Schoutsen eee9449e6b Fix flaky telegram_bot test timeout in CI (#170837) 2026-05-15 20:28:05 +02:00
Paulus Schoutsen bdd6b34055 Fix flaky test_template_timeout race condition (#170832)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 20:25:00 +02:00
Paulus Schoutsen 17e206d749 Update PR template instructions in AGENTS.md (#170834)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <195327+balloob@users.noreply.github.com>
2026-05-15 14:02:36 -04:00
Marc Mueller 9a49696fca Minor typing improvements (#170784) 2026-05-15 19:50:38 +02:00
Maciej Bieniek 77fed7ae7b Raise HomeAssistantError for Tractive action errors (#170798) 2026-05-15 19:48:43 +02:00
Simone Chemelli 9feb450de0 Fix swallowed exceptions in action handlers for SamsungTV (#170805)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:40:01 +02:00
Franck Nijhof a9207988f5 Fix line length violations in tests/components a (#170806) 2026-05-15 13:07:01 -04:00
Simone Chemelli 63b199c865 Guard migration when downgrading from a future version for Vodafone Station (#170830) 2026-05-15 19:05:16 +02:00
Lukas 92285c37a2 Add Samsung Infrared integration (#170449)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 12:59:22 -04:00
Paulus Schoutsen 73e0624964 Disable polling for LG Infrared integration (#170808)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 18:40:08 +02:00
TheJulianJES 057db984ed Fix fractional setpoints in Matter climate not rounded (#170442) 2026-05-15 18:04:07 +02:00
Maciej Bieniek 0fa1561d7a Add river vegetation phenomena sensors for IMGW-PIB (#170770) 2026-05-15 17:25:47 +02:00
Paulus Schoutsen cffcaa9e78 Add script/setup instruction to AGENTS.md (#170800)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 17:06:44 +02:00
Andre Lengwenus cb9fd1abc0 Bump lcn-frontend to 0.2.9 (#170131)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-15 10:16:07 -04:00
Manu bff3f1c062 Automatically start flow for first topic subentry in ntfy integration (#170145) 2026-05-15 10:15:06 -04:00
Paulus Schoutsen 6068e78216 Fix flaky imgw_pib diagnostics test (#170795)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-15 15:49:33 +02:00
Jan Bouwhuis c80c48ab0a Move MQTT reload service to async_setup (#170781) 2026-05-15 08:55:24 -04:00
karwosts be52471cbb Fix incorrect energy name typing (#170792) 2026-05-15 08:52:04 -04:00
Franck Nijhof fc7a6b48e2 Use PEP 695 type parameter syntax for generic classes (#170502)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-15 11:53:00 +02:00
A. Gideonse 75a5fcbee7 Add MOS temperature sensors to Indevolt (#170775) 2026-05-15 11:43:55 +02:00
Maciej Bieniek e61835df17 Fix availability of IMGW-PIB sensors (#170774) 2026-05-15 11:23:45 +02:00
Patrick Große 6062d12f34 Fix availability state for bridged Matter composed devices (#169983) 2026-05-15 11:20:45 +02:00
Tom Matheussen 3425bd22ba Set parallel updates for Satel Integra platforms (#170776) 2026-05-15 11:18:57 +02:00
dependabot[bot] 6d2cc5d6cf Bump actions/dependency-review-action from 4.9.0 to 5.0.0 (#170768) 2026-05-15 10:09:08 +02:00
Crocmagnon d61dc0b67f data grand lyon: fix binary sensor icons (#170708) 2026-05-15 07:15:36 +02:00
Joakim Plate 20d7aa5523 Avoid swallowing exceptions in arcam_fmj services (#170681)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-14 20:41:18 -04:00
Raphael Hehl 6d10249a93 Fix swallowed exception in unifi_access image thumbnail fetch (#170707) 2026-05-14 20:40:53 -04:00
Michael Hansen d10dc679e7 Add second audio channel for voice (#169875) 2026-05-14 20:39:18 -04:00
Ronald van der Meer 2753309946 Bump python-duco-connectivity to 0.4.0 (#170661) 2026-05-14 20:32:24 -04:00
Michael Hansen dc3e3efc66 Bump wyoming to 1.9.0 (#170682) 2026-05-14 20:30:01 -04:00
Paul Bottein e3eb165f9b Bump Novy Cooker Hood quality scale to Gold (#170706) 2026-05-14 20:29:34 -04:00
Paulus Schoutsen dec693ca14 Add PR template guidance to AGENTS.md (#170755) 2026-05-14 17:55:04 -04:00
Dougal Matthews 3d42a2508c Fix unused return value in Prometheus metric name sanitizer (#170723) 2026-05-14 17:45:18 -04:00
Dougal Matthews 0d93b9b9f4 Fix binary_sensor metric description in Prometheus exporter (#170724) 2026-05-14 17:44:26 -04:00
Dougal Matthews c89612eda5 Fix water_heater current temperature metric description in Prometheus exporter (#170725) 2026-05-14 17:28:44 -04:00
A. Gideonse b1e0ce48ae Bump indevolt-api to 1.8.0 (#170731) 2026-05-14 23:27:18 +02:00
Paulus Schoutsen 7f04d47ff3 Update testing instructions in AGENTS.md (#170657)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-14 23:24:29 +02:00
Franck Nijhof b9575ee881 Fix line length violations in components i-l (#170704)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-14 23:24:13 +02:00
Franck Nijhof dac2c91ae5 Add pylint checker for service registration in async_setup_entry (#170730) 2026-05-14 17:22:39 -04:00
Franck Nijhof ef29183160 Fix line length violations in components p-r (#170718)
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-14 23:16:47 +02:00
Franck Nijhof 4ba0dc5cc2 Enable ruff RUF057 rule and remove unnecessary round() calls (#170576)
Co-authored-by: Joakim Plate <elupus@ecce.se>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-14 17:03:56 -04:00
Franck Nijhof daffc8c2ce Fix line length violations in components s (#170722)
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-14 17:01:09 -04:00
Franck Nijhof 0ed8d24b54 Fix line length violations in components n-o (#170711)
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-14 16:48:36 -04:00
Paulus Schoutsen cd7a8c68f9 Fix flaky MaryTTS test by awaiting background tasks (#170727)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 23:45:19 +03:00
Paulus Schoutsen cddad5b790 Fix flaky TTS tests by awaiting background tasks in retrieve_media (#170728)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-14 16:36:36 -04:00
Franck Nijhof eae809abd1 Fix line length violations in core and helpers (#170534)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-14 16:31:01 -04:00
Franck Nijhof bb964ccd95 Fix line length violations in components c-e (#170540)
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-14 16:30:32 -04:00
Franck Nijhof 2ec51ef113 Fix line length violations in components f-g (#170542)
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-14 16:29:56 -04:00
Franck Nijhof e5682d97a6 Fix line length violations in components h (#170688)
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-14 16:25:18 -04:00
Franck Nijhof 5d8087441c Fix line length violations in components m (#170705) 2026-05-14 16:23:59 -04:00
Franck Nijhof 211cccfab8 Enable the ruff rule (unnecessary-empty-iterable-within-deque-call) and simplify 7 initializations (#170684) 2026-05-14 16:09:53 -04:00
Franck Nijhof e0ac404f96 Add pylint checker for branching in test functions (#170541) 2026-05-14 16:09:16 -04:00
Franck Nijhof 7cdb6d27e7 Migrate reauthentication-flow quality scale check from hassfest to pylint (#170715) 2026-05-14 16:08:27 -04:00
Paulus Schoutsen 60a2e811c0 Fix flaky logger tests due to test-ordering dependency (#170461)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-14 22:06:51 +02:00
Franck Nijhof bc0899ba10 Migrate config-entry-unloading quality scale check from hassfest to pylint (#170720) 2026-05-14 16:06:12 -04:00
Franck Nijhof 8304f35734 Migrate diagnostics quality scale check from hassfest to pylint (#170717) 2026-05-14 16:03:23 -04:00
Franck Nijhof 2eb0701792 Migrate parallel-updates quality scale check from hassfest to pylint (#170533)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-14 21:02:03 +02:00
Abílio Costa 972240f994 Add receiver event entity to LG Infrared (#170529)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: abmantis <974569+abmantis@users.noreply.github.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-14 14:59:59 -04:00
Franck Nijhof 017f85243a Add pylint checker for swallowed exceptions in action handlers (#170652)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-14 20:59:17 +02:00
Bram Kragten 02666f8762 Update frontend to 20260429.4 (#170567) 2026-05-14 14:55:42 -04:00
Paulus Schoutsen e8ea01980e Generalize the infrared and radio_frequency strings (#170462)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-14 17:55:48 +01:00
Simone Chemelli 62a79389ed Reinit API on stale session for Vodafone Station (#170190) 2026-05-14 17:20:00 +01:00
Thomas D 0614fde503 Bump qbusmqttapi to v1.5.0 for Qbus integration (#170655) 2026-05-14 17:35:23 +02:00
Paulus Schoutsen 90d9314c36 Remove unused future annotations import (#170659) 2026-05-14 11:28:21 -04:00
Paulus Schoutsen ed250370ab Bump infrared-protocols to 5.3.0 (#170656)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-14 11:13:24 -04:00
Retha Runolfsson b5580db910 Add LED control settings for switchbot air purifier (#167144)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-14 17:07:09 +02:00
Tom Matheussen a1b138acd2 Add zone temperature sensors to Satel Integra (#169060)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-14 17:03:12 +02:00
G Johansson 7b4d2bdd6d Fix possible timezone issue in Trafikverket Ferry (#170592)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-14 11:01:42 -04:00
Andrew Jackson 7f08212efd Fix default dates on get_mealplan service (#170596)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-14 11:01:12 -04:00
Michael Hansen e66b24b0fc Remove hassil fuzzy matcher (#170653) 2026-05-14 11:00:02 -04:00
G Johansson febfd409d3 Fix possible timezone issue in calendar (#170594)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-14 10:57:08 -04:00
Crocmagnon 5f3a549851 data grand lyon: refactor entities around base classes (#170598) 2026-05-14 16:56:57 +02:00
G Johansson da1246c3cf Fix possible timezone issue in input_datetime helper (#170597)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-14 16:41:53 +02:00
Manu 5b082b5377 Add support for API key authentication to pyLoad integration (#169219) 2026-05-14 16:38:35 +02:00
Allen Porter a1de03d63e Update Fitbit sensor test snapshots to use EntityRegistryEntry and StateSnapshot structures (#169213) 2026-05-14 16:37:51 +02:00
LG-ThinQ-Integration 4696e428cf Fix ValueError for non-numeric value in LG ThinQ (#166300)
Co-authored-by: YunseonPark-LGE <yunseon.park@lge.com>
2026-05-14 16:37:33 +02:00
Michael 0cca80e57d Migrate mullvad to use entry.runtime_data (#170583) 2026-05-14 10:18:23 -04:00
G Johansson eb45c35952 Fix possible timezone issue in Trafikverket Train (#170595)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joostlek <7083755+joostlek@users.noreply.github.com>
2026-05-14 10:17:19 -04:00
Franck Nijhof b62b8a0f3f Enable ruff DTZ011 rule to detect date.today() usage (#170593) 2026-05-14 15:44:08 +02:00
Onero-testdev 4d7737f4d3 Add support for SwitchBot Permanent Outdoor Light (#170572)
Co-authored-by: Fan Kai <fankai@onero.com>
2026-05-14 15:11:50 +02:00
bkobus-bbx 500f4ea796 Fix blebox light temperature scaling (#170573) 2026-05-14 15:10:42 +02:00
Øyvind Matheson Wergeland 846300df28 Enable DHCP rediscovery for registered nobo_hub devices (#170562) 2026-05-14 15:09:22 +02:00
Jens Timmerman ec918e0460 Added guntamatic heater integration (#167419)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-14 14:54:37 +02:00
Michael 885bb41416 Add data descriptions for all configuration fields in FRITZ!SmartHome (#170219) 2026-05-14 14:45:44 +02:00
Crocmagnon 3747fa3b8b data grand lyon: update data-grand-lyon-ha to v0.7.0 (#170518) 2026-05-14 14:40:21 +02:00
Øyvind Matheson Wergeland 98f5c818e1 Update pynobo to 1.9.0 (#170559) 2026-05-14 14:35:34 +02:00
Abílio Costa 3514d9d3ca Change codeowner of LG Infrared (#170578) 2026-05-14 13:46:39 +02:00
Crocmagnon 8ba113769d data grand lyon: add binary sensor for station status (#170522) 2026-05-14 13:01:24 +02:00
dependabot[bot] eef03f5def Bump github/codeql-action from 4.35.3 to 4.35.4 (#170555)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-14 12:36:33 +02:00
Simon Lamon c924bba41e Fix hassfest by removing knx from forbidden package file exceptions (#170561) 2026-05-14 10:20:56 +02:00
Tomer 1369fe2451 Victron GX: button integration (#169568)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-14 09:48:44 +02:00
renovate[bot] fec97f383d Update orjson to 3.11.9 (#170545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 07:54:53 +02:00
renovate[bot] ec3c61a877 Update uv to 0.11.11 (#170546)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 07:53:25 +02:00
Simone Chemelli 993be3c0c8 Fix non unique_id for Comelit (#169756)
Co-authored-by: Copilot <copilot@github.com>
2026-05-14 07:51:29 +02:00
Franck Nijhof 1e5f618ff9 Fix line length violations in components a-b (#170538) 2026-05-14 07:17:37 +02:00
Abílio Costa efaf22270d Add power toggle button to LG Infrared (#170544) 2026-05-14 07:16:51 +02:00
Ryan Peay f9c6da812d Bump thermopro-ble to 1.1.4 (#170543) 2026-05-13 17:46:00 -07:00
Franck Nijhof 286e5f246f Add pylint checker for unused test fixture arguments (#170537) 2026-05-13 20:14:12 -04:00
ollo69 ef33cd58fd Fix exlusive schema option in asuswrt (#170539) 2026-05-14 00:44:13 +02:00
G Johansson e4c44f873a Add missing optional category strings in workday (#170505)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-14 00:36:36 +02:00
Dan Raper a1d8cf5cdf Add solar boost switch to Ohme (#170531)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-05-13 23:19:33 +01:00
Dan Raper 53bb8c7012 Bump ohme to 1.9.1 (#170530) 2026-05-13 23:15:16 +01:00
ollo69 6cb1e05784 Collapse advanced options on auswrt config flow (#170532) 2026-05-13 22:56:54 +01:00
Franck Nijhof b3eb97adad Enable ruff RUF046 rule and remove unnecessary int() casts (#170514)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 23:50:44 +02:00
Paulus Schoutsen 0b0618e5c8 Move infrared entity classes to entity module (#170525)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 23:40:05 +02:00
Paulus Schoutsen 5c588dd5d3 Move radio_frequency entity classes to entity module (#170526)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 23:39:47 +02:00
Franck Nijhof 555d838290 Enable ruff B010 rule and replace setattr with direct attribute assignmet (#170527) 2026-05-13 23:39:32 +02:00
Paulus Schoutsen d7568a8f16 Move valve entity classes to entity module (#170524)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 23:39:05 +02:00
Franck Nijhof 1bf3bfc082 Enable ruff RUF051 rule and simplify dict key removal (#170523) 2026-05-13 23:18:52 +02:00
Franck Nijhof 1e8a9ded70 Refactor pylint plugins into pylint_home_assistant package (#170521) 2026-05-13 17:12:19 -04:00
Mike Degatano eaf73d6526 App services in Supervisor integration validate syntax not installed status (#170519) 2026-05-13 22:34:34 +02:00
Paulus Schoutsen 150e241601 Filter unrelated sqlalchemy errors in zwave_js integration fixture (#170303)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 22:18:07 +02:00
Petar Petrov 02928ccadd Apply unit_of_measurement to energy combined power sensor (#170404) 2026-05-13 22:00:58 +02:00
puddly 57615b8c66 CI fix: set up the SkyConnect integration in ZHA repair tests (#170520) 2026-05-13 21:47:03 +02:00
Mike Degatano 66bac035c3 Allow set stop handler to revert to default (#170515) 2026-05-13 15:26:19 -04:00
Jeef b2ed78b317 Add WeatherFlow Cloud station sensors (#169561)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-13 21:19:21 +02:00
Robert Svensson 3d65d42d47 Remove old unique id converter for UniFi switch entities (#170517) 2026-05-13 21:15:24 +02:00
Robert Svensson 74af1fd5d4 Remove old unique id converter for UniFi device tracker entities (#170516) 2026-05-13 21:03:57 +02:00
Franck Nijhof 793018e0d6 Enable ruff S301 rule and remove pickle usage in tests (#170513) 2026-05-13 20:43:22 +02:00
Franck Nijhof a4865a69b9 Enable ruff S107 rule to detect hardcoded passwords in function defaults (#170511) 2026-05-13 20:26:12 +02:00
Franck Nijhof a07034c13b Improve unifi options flow UX and remove advanced mode dependency (#170501) 2026-05-13 20:17:53 +02:00
puddly ccc454f8f9 Migrate ZBT-1 and ZBT-2 to use serial number for unique_id (#169879)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-13 20:10:15 +02:00
Paulus Schoutsen 31b348f3cd Fix flaky test_history_stream_live_chained_events race condition (#170509)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 20:03:06 +02:00
Michael Keck c5e14bd1a4 Increase WebDAV client timeout from 10 to 30 seconds (#170476) 2026-05-13 19:57:15 +02:00
Mika e8a36f7128 Improve test coverage for the SolarEdge integration (#169178)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-13 19:57:01 +02:00
Crocmagnon 4e01805270 data grand lyon: add bike sharing data (#170345) 2026-05-13 19:53:20 +02:00
Niracler f0eb151cdd Bump PySrDaliGateway to 0.21.0 (#170473) 2026-05-13 19:52:24 +02:00
Abílio Costa 3527556e58 Bump infrared-protocols to 5.2.0 (#170510) 2026-05-13 18:50:40 +01:00
A. Gideonse a4bfbd3dde Add MAC address to Indevolt device info (#170472)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-13 19:48:58 +02:00
Øyvind Matheson Wergeland ff971ce20b Bring nobo_hub to Bronze quality scale (#168638)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 19:39:23 +02:00
Jeef f82bc56dfa Add initial quality scale file to Monarch Money (#166026) 2026-05-13 19:31:06 +02:00
Franck Nijhof 0d0a6d4c91 Remove advanced mode dependency from androidtv config flow (#170477) 2026-05-13 19:28:14 +02:00
Abílio Costa b9105db16c Add infrared receiver entity (#169110)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: abmantis <974569+abmantis@users.noreply.github.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-13 18:21:46 +01:00
Maciej Bieniek ab0f791851 Bump imgw_pib to 2.2.0 (#170479) 2026-05-13 19:17:50 +02:00
Franck Nijhof 3ddd37242c Remove advanced mode dependency from sonarr config flow (#170487) 2026-05-13 19:12:18 +02:00
Franck Nijhof 8a5cb61613 Remove advanced mode dependency from nzbget config flow (#170488) 2026-05-13 19:09:50 +02:00
Franck Nijhof 8a77661c14 Remove advanced mode dependency from onvif options flow (#170489) 2026-05-13 19:00:40 +02:00
Paulus Schoutsen e021119fba Fix flaky sensibo test_device snapshot ordering (#170499)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 18:59:56 +02:00
Heikki Henriksen 237fef8220 prusalink: add quality_scale.yaml as a tracked roadmap (#170204)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 18:59:51 +02:00
Paulus Schoutsen ee674cfa3f Fix flaky openrgb tests by waiting for background tasks (#170498)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 18:58:25 +02:00
Franck Nijhof 7fc4a5cf66 Remove advanced mode dependency from risco options flow (#170493)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-13 18:57:00 +02:00
Franck Nijhof 5f84cdf4b0 Remove advanced mode dependency from coolmaster config flow (#170478) 2026-05-13 18:56:03 +02:00
Noah Husby da9deb1c75 Add volume limit to Cambridge Audio (#163949) 2026-05-13 18:45:38 +02:00
Manu 100d729ab6 Add missing translations for backup failure reason (#170437) 2026-05-13 18:42:35 +02:00
mhuiskes d16da986d8 Add @mhuiskes as codeowner for zeversolar (#170506) 2026-05-13 18:40:31 +02:00
Raj Laud 868769efff Use direct dict access instead of .get() in victron_ble config flow tests (#165972)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 18:24:15 +02:00
Erik Montnemery 97a7a5623b Improve test test_validate_python (#170475)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-13 18:22:09 +02:00
Franck Nijhof 0c44e77ae4 Remove advanced mode dependency from motioneye options flow (#170492)
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-05-13 18:19:32 +02:00
Franck Nijhof 24dadf3668 Enable additional flake8-bugbear ruff rules (#170494)
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-13 18:17:23 +02:00
Mike Degatano a19c2ee9ef Improve async_setup and async_setup_entry logic separation in hassio (#169586)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 17:52:39 +02:00
Ian C. 6f7b571dae Fix stale values being returned after a 502 error (#168553)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-05-13 17:48:03 +02:00
G Johansson 5753da7dff Fix ObjectSelector when using other selectors (#170453) 2026-05-13 16:42:47 +02:00
karwosts 97f3b48bf8 Make gas & water sources nameable (#170447) 2026-05-13 16:16:49 +02:00
Franck Nijhof f2e6cd297d Improve plex config flow UX and remove advanced mode dependency (#170485) 2026-05-13 16:15:40 +02:00
Erik Montnemery 011ebec001 Adjust condition API (#170486) 2026-05-13 16:02:45 +02:00
Jan Bouwhuis 2e738e22d2 Fix MQTT settings in device subentry device settings are not recalled when reconfiguring the device (#170484) 2026-05-13 14:57:01 +02:00
Franck Nijhof b843047d9a Add README for custom pylint plugins (#170465)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-05-13 14:54:00 +02:00
Max R ebf1195dc6 Expand testing guidance for agents (#166495)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-13 14:26:13 +02:00
renovate[bot] ab1f92309f Update uv to 0.11.10 (#170460)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-13 14:00:57 +02:00
Stefan Agner d3b2be7e86 Deprecate legacy "homeassistant" entry in hassio backup/restore folders (#170317)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:35:06 +02:00
Franck Nijhof a2131c0d45 Add pylint plugin to detect name fields in config flow schemas (#168875)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 12:30:30 +02:00
Markus Tuominen b179d71658 Add Ouman EH-800 heating controller integration (#169733) 2026-05-13 12:59:49 +03:00
Michael 070ef8f0b0 Remove advanced mode from FRITZ!Box Tools (#167815) 2026-05-13 11:50:07 +02:00
Rob Bierbooms aaeb55b132 Fix influxdb reconfigure for v1 configuration (#170448) 2026-05-13 10:46:51 +02:00
Erik Montnemery 1f5cb05f50 Add websocket command subscribe_condition (#170385) 2026-05-13 10:18:40 +02:00
dependabot[bot] cee87ed1f5 Bump sigstore/cosign-installer from 4.1.1 to 4.1.2 (#170466) 2026-05-13 09:20:19 +02:00
Marc Hörsken e2ae9c1b95 Bump pywmspro to 0.3.4 (#170454) 2026-05-13 08:55:04 +02:00
Jeef 8b257cdd6c Fix WeatherFlow Cloud empty observations (#170440) 2026-05-13 07:53:23 +02:00
tronikos f756392b6a Add nest.set_fan_timer service action (#170367) 2026-05-12 22:41:03 -07:00
Abílio Costa 894ee88033 Add agent instructions to prefer usefixtures (#170458) 2026-05-12 22:01:26 -04:00
J. Nick Koston d5d56e6e23 Bump aioharmony to 1.0.3 (#170459) 2026-05-12 18:38:57 -05:00
James Nimmo a19a1ec6e8 Bump pyintesishome to 1.8.7 (#170382) 2026-05-12 23:29:32 +02:00
mhuiskes b98015dc76 Add hardware and software version to Zeversolar device info (#170407) 2026-05-12 22:30:15 +02:00
zhangluofeng 4112b2af07 Add Xthings Cloud (#167885)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-12 22:07:39 +02:00
Ariel Ebersberger 944c0d7ed2 Drop Advanced mode dependency in generic camera config flow (#170427) 2026-05-12 19:11:09 +01:00
perceival a471f7059f risco: improve local reconnect/unload robustness (#165924)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 19:39:08 +02:00
J. Nick Koston cd1d4244ae Bump aioesphomeapi to 44.24.1 (#170428) 2026-05-12 12:17:06 -05:00
Lukas 573409dcbf bump pooldose api to 0.9.1 (#170434) 2026-05-12 19:16:07 +02:00
Øyvind Matheson Wergeland 6ec70734c1 Drop _spec_hub helper in nobo_hub init tests (#170147) 2026-05-12 19:05:33 +02:00
Åke Strandberg adf6213c9f Avoid stack traces on certain transient miele API errors (#170429) 2026-05-12 18:26:58 +02:00
Jan Bouwhuis e925672bb6 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-12 18:13:54 +02:00
Jan Bouwhuis 15c5e257f5 Allow MQTT discovery to happen at QoS 0, 1 or 2 (#170178) 2026-05-12 18:02:40 +02:00
Richard Kroegel 8396964023 Update device class for eurotronic number (#170356) 2026-05-12 17:00:57 +02:00
Thomas Bouron 09a08011d6 Add support for Inverter Pool Heat Pump (InverGo) (#169606)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-12 16:26:08 +02:00
Åke Strandberg 891e0aebb0 Bump pymiele version to 0.6.2 (#170419) 2026-05-12 16:25:21 +02:00
David ca9a7f6051 Add fault codes to Tuya Pro Breeze OmniDr Dehumidifier (#170411)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-12 15:41:29 +02:00
Erik Montnemery 24dc206462 Initiate translation attributes on class level of HomeAssistantError (#170413) 2026-05-12 15:10:07 +02:00
Mattie 60e4f924a0 Bump python-qube-heatpump to 1.10.0 (#170405) 2026-05-12 14:55:46 +02:00
Manu 339703ca04 Add initial quality scale to HTML5 integration (#167046)
Co-authored-by: Markus Tuominen <3738613+Markus98@users.noreply.github.com>
2026-05-12 15:13:28 +03:00
Erik Montnemery 362cba91fb Make ConditionError a non dataclass (#170391) 2026-05-12 13:55:48 +02:00
Raman Gupta a215b82bd9 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-12 12:32:58 +02:00
knsj 3393598d91 Bump ihcsdk to v. 2.8.12 (#170366) 2026-05-12 12:30:51 +02:00
Erik Montnemery 676df1d2b2 Fix cv.CONDITION_SCHEMA (#170395) 2026-05-12 11:39:43 +02:00
Artur Pragacz 36cc629faf Validate device info string fields in the registry (#170021) 2026-05-12 11:01:04 +02:00
Marc Mueller 99b1e7c229 Enable parallel type checking for mypy (#170381) 2026-05-12 10:34:47 +02:00
renovate[bot] cfdb00bf36 Update pyOpenSSL to 26.2.0 (#170371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 10:17:56 +02:00
puddly 9b8c81cba1 Bump serialx to 1.7.3 (#170368) 2026-05-12 07:52:05 +02:00
Petar Petrov 095cf07f43 Add battery state of charge to energy preferences (#169550)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-12 08:37:25 +03:00
Marc Mueller b275791a71 Update mypy to 2.1.0 (#170352) 2026-05-12 05:08:39 +02:00
Lukas e7dccd3ad3 Bump infrared-protocols to 5.1.0 (#170365) 2026-05-11 22:36:56 -04:00
Franck Nijhof adab0d6486 Clean up template engine after extension modularization (#170346) 2026-05-11 18:41:54 -04:00
Robert Svensson aad964889f Bump axis to v71 (#170347) 2026-05-11 23:10:20 +02:00
Jan-Philipp Benecke 9200658526 Enhance WebDAV metadata download with concurrency (#170223) 2026-05-11 21:33:22 +02:00
Christian Lackas 68f10249a5 Add target temperature sensor for ViCare RadiatorActuator devices (#170102) 2026-05-11 21:32:47 +02:00
Andreas Schneider b5ee78aeac Bump pyzbar to 0.1.9 (#170076) 2026-05-11 21:32:19 +02:00
Christian Lackas 86a967ee7b homematicip_cloud: fix HmIP-FLC lock state polarity (#170159) 2026-05-11 21:29:00 +02:00
theobld-ww eeca75b937 Watts: add timer mode service (#169846)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 21:18:13 +02:00
Franck Nijhof ce6b6601fa Set parallel updates for Ecowitt platforms (#170349) 2026-05-11 21:00:15 +02:00
Sören 4641c829ca Add config flow to Avea (#168070)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-11 18:42:24 +02:00
Joost Lekkerkerker 56fbd096e2 Cleanup Eurotronic number platform (#170337) 2026-05-11 18:30:58 +02:00
Richard Kroegel c071c08f86 Add number platform to eurotronic_cometblue (#168119)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 17:42:56 +02:00
TheJulianJES e47c152222 Add ZWaveNodeBaseEntity for Z-Wave node-level entities (#170124) 2026-05-11 17:34:30 +02:00
Martin Claesson 8232415fd5 Add Kiosker switch platform (#168858) 2026-05-11 16:31:14 +02:00
A. Gideonse dcc95328ec Complete exception translations for Indevolt (#170291)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 10:26:26 -04:00
Karl Beecken 85faab5d5d Bump teltasync to 0.3.0, fix discovery for older devices (#169660)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 15:21:09 +01:00
MoonDevLT bacb8a8fea Update discovery description wording (#170325) 2026-05-11 15:57:40 +02:00
Maciej Bieniek c9926915ff Fix Shelly media player availability (#170319) 2026-05-11 15:57:37 +02:00
Joshua Leaper 0772034d9d Add quality scale file to Ness Alarm (#163425)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-11 15:46:30 +02:00
r2xj 8cfdc52762 Only use SmartThings switch for light if it should (#166424)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-11 15:16:18 +02:00
Brett Adams 738b9936d9 Add quality scale to Tesla Fleet integration (#160475)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-11 15:10:21 +02:00
lucsansag b3bb5c9abc Google assistant temperature setting active thermostat mode (#166448)
Co-authored-by: Lucas Sanchez Sagrado <lucas.sansag@educa.jcyl.es>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-11 15:08:00 +02:00
Jan Bouwhuis 3149da12a4 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-11 14:53:32 +02:00
Tomeamis e2805e4489 Z-Wave.me: Allow updating entities (#167839) 2026-05-11 14:49:40 +02:00
noifen 14a8ef6e48 Allow setting hvac_mode in generic_thermostat.set_temperature (#168062)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-11 14:41:53 +02:00
Peter 015fc5809a Add countdown number for Tuya sfkzq single-valve timers (#170318) 2026-05-11 14:14:55 +02:00
Simone Chemelli 2e4f4040c7 Bump aiovodafone to 3.2.0 (#170322) 2026-05-11 14:14:41 +02:00
TomFilsell 095de73a53 Fix coordinator data mutation in YouTube diagnostics (#170300)
Co-authored-by: FIls0010 <a1867444@adelaide.edu.au>
2026-05-11 08:03:33 -04:00
Nikolai Rahimi 7dca14e78a Add Mitsubishi brand (#169924)
Co-authored-by: Nikolai Rahimi <nikolairahimi@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:02:10 -04:00
nayfield 0a974cbc7a Add cover support to control4 (#169417)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 13:37:49 +02:00
Øyvind Matheson Wergeland 2e37a0bba6 Fix nobo_hub NoboProfileSelector class-level mutable defaults (#170119) 2026-05-11 13:35:38 +02:00
HoffmanEl 7e2ec795d6 Add quality scale for airnow integration (#169709)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 13:28:52 +02:00
theobld-ww 7ba7700d5e Watts: add HVAC action + preset mode (#169546)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-11 13:27:01 +02:00
Greg Haines 261ca2dd9a Add new CentriConnect component (#166933)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-11 13:23:50 +02:00
Jan Bouwhuis 284478f620 Add Message Expire Interval option to MQTT publish service (#169317)
Co-authored-by: Copilot <copilot@github.com>
2026-05-11 13:01:06 +02:00
Paulus Schoutsen 62ac3f9834 Update rf-protocols to 3.0.0 (#170301)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:46:26 +02:00
Marcos A L M Macedo 3bf57ae9cd Add Tuya DLQ fixture (#169585) 2026-05-11 12:28:11 +02:00
Kamil Breguła ed0abfb238 Add more entities for Tuya camera (#169966)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2026-05-11 12:25:42 +02:00
Peter 0789eb0db6 Add tuya water timer data points (#170314) 2026-05-11 12:24:14 +02:00
Marc Mueller 980d43accc Add backoff to forbidden packages (#170242)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 12:14:37 +02:00
Stefan Agner 6d8b010245 Fix hassio.backup_partial AttributeError when folders are specified (#170312)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:10:37 +02:00
Jordan Harvey dc9eba372a Add player specific sensor to nintendo_parental_controls (#155786)
Co-authored-by: Joshua Peisach (ItzSwirlz) <itzswirlz2020@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-05-11 12:00:17 +02:00
Simone Chemelli 20827b66d9 Update IQS to platinum for UptimeRobot (#170260) 2026-05-11 11:26:30 +02:00
A. Gideonse a43ab34302 Bump indevolt api to 1.7.2 (#170310) 2026-05-11 10:37:48 +02:00
Craig Dean b14e863877 Bump renault-api to 0.5.8 (#170309) 2026-05-11 10:36:47 +02:00
Paulus Schoutsen af41b704d5 Add shared test mocks for the Infrared integration (#170296)
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-11 09:46:09 +02:00
Crocmagnon d5f2cd8b17 data grand lyon: implement reconfiguration flow (#170210) 2026-05-11 09:17:31 +02:00
Paulus Schoutsen f96afda959 Fix spelling of RS-232 in Denon RS-232 integration (#170298) 2026-05-11 07:30:16 +02:00
Paulus Schoutsen 94bf13c6bb Fix flaky diagnostics test for Novy Cooker Hood (#170299) 2026-05-11 07:29:51 +02:00
iluvdata b7dca79743 Add RepairsFlowResult TypeAlias (#170263) 2026-05-10 23:42:47 -04:00
Mick Vleeshouwer df84d7a32d Fix local API incorrectly marking devices as unavailable in Overkiz (#170118)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2026-05-10 23:40:13 -04:00
Christian Lackas c217acd7ab Skip offline devices instead of aborting ViCare diagnostics (#170106) 2026-05-10 23:31:10 -04:00
Abílio Costa f008f1501f Fix deprecated_class to work with inheritance (#170137) 2026-05-10 23:28:19 -04:00
Michael 739a5780b7 Add diagnostics platform to Home Assistant Connect ZBT-2 (#170158) 2026-05-10 23:27:13 -04:00
Willem-Jan van Rootselaar 0ef221611a Handle empty BSB-LAN heating circuits (#170249) 2026-05-10 23:25:55 -04:00
Crocmagnon 59e04c2169 data grand lyon: use test fixture instead of manual patching (#170294) 2026-05-10 22:08:06 -04:00
renovate[bot] 5b0bf09fdc Update infrared-protocols to 4.0.0 (#170297)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-10 22:07:13 -04:00
puddly c07d176467 Set serial port description from description, not product (#170160)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-10 22:06:04 -04:00
Crocmagnon c39f0127ca data grand lyon: implement diagnostics (#170184) 2026-05-10 21:09:48 -04:00
Heikki Henriksen cdf5d39f57 prusalink: add continue-job button for ATTENTION state (#170193)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:56:22 -04:00
Sören 90b6aa4d91 Use standby for HDMI-CEC turn off (#170206) 2026-05-10 20:53:17 -04:00
Florent Thoumie f8ebc6c1e2 iaqualink: set system specific polling interval (#170279) 2026-05-10 20:07:13 -04:00
A. Gideonse e4b4503c10 Apply strict typing to Indevolt integration (#170288) 2026-05-10 20:06:22 -04:00
A. Gideonse 7db1c855c1 Align Indevolt quality scale with documentation (#170289) 2026-05-10 20:06:01 -04:00
A. Gideonse aa45f90a87 Complete icon translations for Indevolt (#170292) 2026-05-10 20:00:20 -04:00
Sören Beye cd945a42e6 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-10 22:17:52 +02:00
Ronald van der Meer afc97268de Migrate Duco to python-duco-connectivity and remove temperature sensors (#170237) 2026-05-10 13:09:46 -04:00
Heikki Henriksen 497faeb103 Use VERSION from PrusaLinkConfigFlow in prusalink (#170200)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:51:02 +01:00
Luka Matijević 84625678d3 Bump qbittorrent-api to 2026.5.1 (#170181) 2026-05-10 17:44:46 +01:00
Perry Naseck 95daee9f07 Update upb-lib to 0.7.0 (#170051) 2026-05-10 17:42:35 +01:00
Simone Chemelli ff1552e317 Bump aioamazondevices to 13.5.0 (#170258) 2026-05-10 17:40:36 +01:00
Pascal Brunot ff6b69c929 Bump serialx to 1.7.2 (#170272) 2026-05-10 17:39:04 +01:00
Maciej Bieniek 2a74d5a81c Bump imgw-pib to 2.1.2 (#170274) 2026-05-10 17:37:48 +01:00
Michael 52237247ae Migrate Sensirion BLE to config entry runtime data (#170227)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-05-10 17:46:12 +02:00
Michael 62d958682a Migrate Thermo Beacon to config entry runtime data (#170226) 2026-05-10 16:46:13 +02:00
Joris Pelgröm b2dad41d35 Bump letpot to 0.7.0 (#169868) 2026-05-10 11:39:17 +02:00
Karl Beecken 83c5dbb111 Add more Gen1 sensors to indevolt (#170191) 2026-05-10 11:37:46 +02:00
Klaas Schoute cf73ef8a20 Update easyEnergy exception and icon translations (#170218) 2026-05-10 08:19:18 +02:00
Simone Chemelli 6555db12b1 Fix entities refresh for UptimeRobot (#170217) 2026-05-10 08:14:03 +02:00
Jan-Philipp Benecke 20b81e9c74 Bump aiotankerkoenig to 0.5.3 (#170224) 2026-05-10 08:12:27 +02:00
Yogev Kriger 51d004a5bb Require admin for webhook list websocket command (#170216) 2026-05-09 21:23:42 +02:00
Michael 9c9b626ade Use has_entity_name for all entities in FRITZ!SmartHome integration (#170199) 2026-05-09 19:01:51 +02:00
Klaas Schoute e0d3eb0fe3 Add config flow connection check to easyEnergy integration (#170207) 2026-05-09 17:18:27 +02:00
Øyvind Matheson Wergeland 5f5df558c6 Drop redundant HVAC mode validation in nobo_hub (#170140) 2026-05-09 16:28:46 +02:00
Klaas Schoute fbc5884ce8 Update easyEnergy integration to v3.0.1 (#170201) 2026-05-09 15:59:35 +02:00
Crocmagnon e72346c222 data grand lyon: exception translations (#170188) 2026-05-09 15:44:56 +02:00
Jan Bouwhuis 266f7b8dbe Fix overkiz snapshots (#170196) 2026-05-09 14:49:19 +02:00
Crocmagnon 3ae4811e99 data grand lyon: icon translations (#170189) 2026-05-09 14:28:15 +02:00
Crocmagnon 526ed271ae data grand lyon: mark repair-issues as exempt (#170194) 2026-05-09 14:26:32 +02:00
renovate[bot] 6c823cd970 Update infrared-protocols to 3.5.0 (#170169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 13:05:54 +02:00
Paul Bottein fb4b36b7f0 Add reconfigure flow to Novy Cooker Hood (#169410) 2026-05-09 10:27:27 +02:00
Paul Bottein 86898f9111 Add diagnostics to Novy Cooker Hood (#169891) 2026-05-09 08:38:58 +02:00
Abílio Costa 27969c34a5 Stop using make_command in LG Infrared (#170149) 2026-05-09 00:24:51 +03:00
Mick Vleeshouwer 74fabca890 Add button entity tests to Overkiz (#170122) 2026-05-09 00:24:37 +03:00
Crocmagnon af6fcae8b6 data grand lyon: update quality scale in manifest (#170109) 2026-05-08 23:04:47 +02:00
Thijs W. 818b420cb5 Update afsapi to 1.0.1 (#170073) 2026-05-08 23:02:43 +02:00
Øyvind Matheson Wergeland ef2a065784 Use suggested_display_precision in nobo_hub temperature sensor (#170138) 2026-05-08 21:03:25 +02:00
Mick Vleeshouwer 15943a737a Fix is_closed state for DynamicGate covers in Overkiz (#170130) 2026-05-08 20:42:50 +02:00
Heikki Henriksen 1647c0bf84 Bump pyprusalink to 2.2.0 (#170105)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 16:26:52 +02:00
bkobus-bbx 42aefd67dd Bump blebox_uniapi to v2.5.3 (#170115) 2026-05-08 16:21:54 +02:00
Mick Vleeshouwer c281c51fc9 Set is_closed state to None when a cover state returns "unknown" in Overkiz (#170081) 2026-05-08 16:20:10 +02:00
Robert Svensson fa09c6d29a Fix websocket certificate verification Bump axis to v70 (#170038) 2026-05-08 16:18:24 +02:00
Ronald van der Meer 9f7ddcca22 Add system health platform for Duco integration (#169517) 2026-05-08 13:25:28 +02:00
Willem-Jan van Rootselaar e488c7f3a5 Bump python-bsblan to 5.2.1 (#170100) 2026-05-08 13:17:47 +02:00
wollew bb924e79b1 Speed up Velux setup by avoiding disconnect from gateway (#167932)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-08 12:57:48 +02:00
Øyvind Matheson Wergeland 39d60faa42 Add DHCP discovery to nobo_hub (#169595)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:57:05 +02:00
Mick Vleeshouwer 378a26f778 Add number entity tests to Overkiz (#170098) 2026-05-08 12:23:07 +02:00
Mick Vleeshouwer 5c12d59ab7 Fix tilt support for UpDownVenetianBlind (rts:VenetianBlindRTSComponent) in Overkiz (#170047) 2026-05-08 12:22:23 +02:00
TheJulianJES c9e44d2d51 Bump ZHA to 1.3.1 (#170095) 2026-05-08 12:18:19 +02:00
Heikki Henriksen c195ddd8f2 prusalink: extract PrusaLinkEntityDescription base class (#170092)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:11:22 +02:00
Mick Vleeshouwer 4e388e1435 Fix cover controls for UpDownBioclimaticPergola in Overkiz (#170058) 2026-05-08 12:09:04 +02:00
Heikki Henriksen 191143d12d prusalink: expose printer location as suggested_area (#170099)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:08:09 +02:00
Mick Vleeshouwer bb6087cf87 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 11:57:37 +02:00
Rob Treacy 70e18fc196 Fix WiZ Light config flow timeout by properly closing UDP connections (#168456) 2026-05-08 10:35:13 +02:00
TheJulianJES 526ddc4770 Fix Z-Wave discovery crash with unknown node firmware version (#170090) 2026-05-08 10:21:39 +02:00
Mick Vleeshouwer 5f6bd9b6a7 Fix sensors getting wrong unit from MeasuredValueType attribute in Overkiz (#170088) 2026-05-08 10:15:20 +02:00
Maciej Bieniek 9b525bf1cb Use SensorDeviceClass.UPTIME in System Monitor (#170084) 2026-05-08 10:13:15 +02:00
Maciej Bieniek 3bc2c0d097 Use SensorDeviceClass.UPTIME in Unifi (#170087) 2026-05-08 10:12:57 +02:00
Mattie b5bdff7068 Add water_heater platform to Qube heat pump (#169851) 2026-05-08 10:12:26 +02:00
Erwin Douna 7103b07638 Portainer refactor tests to use enums from pyportainer (#170044) 2026-05-08 09:05:36 +02:00
Mick Vleeshouwer d52c281826 Fix is_closed state for DynamicGarageDoor in Overkiz (#170052) 2026-05-08 09:04:49 +02:00
Crocmagnon 9fca2f284b data grand lyon: implement reauth (#170059) 2026-05-08 09:04:16 +02:00
dependabot[bot] f1986d5fc3 Bump github/codeql-action from 4.35.2 to 4.35.3 (#170077) 2026-05-08 08:47:31 +02:00
Thomas Bouron ce9c83e33c Add fixture for Tuya pool heating pump (#170064) 2026-05-08 07:33:59 +02:00
renovate[bot] aa98fce92e Update infrared-protocols to 3.2.0 (#170070)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08 06:11:27 +02:00
Robert Resch b01e56582a Bump deebot-client to 18.3.0 (#170066) 2026-05-08 01:43:54 +02:00
Mick Vleeshouwer 9be078475d Bump pyOverkiz to 1.20.3 (#170060) 2026-05-08 01:23:43 +02:00
Ronald van der Meer 9174ae4e00 Bump python-duco-client to 0.5.0 (#170065) 2026-05-08 01:10:50 +02:00
th3spis d4aa1b53f2 Added wfsens as a occupancy source in wiz (#166799)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-08 00:06:23 +02:00
mayerwin ba29f210c2 Translate switchbot_cloud library errors to HomeAssistantError (#169715)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-08 00:01:49 +02:00
Joost Lekkerkerker 845572927c Fix CI (#170061) 2026-05-07 22:51:44 +01:00
MoonDevLT 9cd7ac2722 Add sensor entity to lunatone integration (#167873)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-07 23:34:48 +02:00
Muhammad Ihsan a7fd763570 Add Cielo Home integration (#158511)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Owais Amin <141307092+owais-cielo@users.noreply.github.com>
Co-authored-by: Owais Amin <owais@cielowigle.com>
Co-authored-by: Maria Nadeem <maria@cielowigle.com>
2026-05-07 23:12:19 +02:00
theobld-ww 65491372c2 Bump visionpluspython 1.0.2 to 1.1.0 (#169842)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-07 22:41:16 +02:00
Mattie de96ee44e5 Add switch platform to Qube heat pump (#169407) 2026-05-07 22:40:37 +02:00
Crocmagnon 6edcf5722e Add Data Grand Lyon integration (#167946)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 22:12:57 +02:00
Jeef e6acebb322 Fix IntelliFire setup recovery (#169739) 2026-05-07 21:55:17 +02:00
Christian Lackas 277daf2dba vicare: migrate to OAuth2 with application credentials (#165621)
Co-authored-by: home-assistant[bot] <78085893+home-assistant[bot]@users.noreply.github.com>
2026-05-07 21:23:46 +02:00
Paulus Schoutsen 1b935314f8 Represent ThinQ hoods as fans instead of number entities (#159601)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-07 21:17:01 +02:00
G Johansson cad5c9e8fa Remove advanced mode from dnsip (#170040) 2026-05-07 21:03:15 +02:00
Midori Kochiya f7201f1910 Bump xiaomi-ble to 1.11.0 (#170018) 2026-05-07 19:34:12 +01:00
Glenn Waters c406e1aeed ElkM1 integration: Add time entity for settings (#170035) 2026-05-07 20:17:42 +02:00
G Johansson 946a3bcf11 Add missing areas in Nord Pool services (#169752) 2026-05-07 20:10:02 +02:00
Erwin Douna 2c8d9c7207 Add disk space coordinator for Portainer (#165855) 2026-05-07 20:05:29 +02:00
Michael db25f1911e Proper handling of malformed data during FRITZ!Box Tools setup (#170030) 2026-05-07 19:59:06 +02:00
Yevhenii Vaskivskyi 7e2fa90773 Remove Advanced mode from asuswrt (#170029) 2026-05-07 19:54:12 +02:00
Felipe Santos ef83ccc423 Allow selecting input source on SmartThings TVs (#160034)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-05-07 18:53:38 +01:00
Ronald van der Meer 046b48df43 Bump python-duco-client to 0.4.2 (#170027) 2026-05-07 19:51:54 +02:00
epenet 66cd719f85 Fix KeyError in hydrawise (#169853) 2026-05-07 18:50:29 +01:00
renovate[bot] b0c2e57649 Update infrared-protocols to 3.1.0 (#169968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: abmantis <amfcalt@gmail.com>
2026-05-07 18:26:33 +01:00
Glenn Waters cb92fa27ba Add number entity to ElkM1 integration (#169861) 2026-05-07 17:39:30 +02:00
Erik Montnemery c3f8f6f310 Use modern API in condition tests (#170002) 2026-05-07 17:33:00 +02:00
Tomasz Dylewski a82205fed7 Added PAJ GPS integration (#165070)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-07 17:04:19 +02:00
epenet 776fd69e39 Use SensorDeviceClass.ENUM in Tuya sensors (#169987) 2026-05-07 17:02:31 +02:00
Christian Lackas 2863b59be4 Bump homematicip to 2.11.0 (#170005) 2026-05-07 16:58:13 +02:00
epenet 676e9c7f29 Migrate Cast to use runtime_data (#168856)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:57:29 +02:00
Petro31 05c3c058d6 Remove legacy alarm control panel template entities (#169608)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-07 15:54:59 +02:00
Petro31 fd93f24208 Remove legacy binary sensor template entities (#169610) 2026-05-07 15:52:43 +02:00
Petro31 544b21f014 Remove legacy cover template entities (#169611) 2026-05-07 15:51:41 +02:00
Petro31 8d30abab9e Remove legacy fan template entities (#169613) 2026-05-07 15:51:08 +02:00
Petro31 ee19c11565 Remove legacy lock template entities (#169725) 2026-05-07 15:50:22 +02:00
Heikki Henriksen c26eb2374d prusalink: add X/Y axis, location, and min extrusion temp sensors (#169312)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 15:39:07 +02:00
Kamil Breguła 59bc46a9d2 Fix Tuya siren entity naming to avoid incorrect main entity assignment (#170008)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-07 15:24:37 +02:00
Petro31 ab668ac576 Remove legacy sensor template entities (#169728) 2026-05-07 15:22:22 +02:00
Petro31 c4836600c4 Remove legacy vacuum template entities (#169732) 2026-05-07 15:18:45 +02:00
Petro31 f4e0349825 Remove legacy light template entities (#169615) 2026-05-07 15:00:39 +02:00
Petro31 4d578b6c98 Remove legacy switch template entities (#169730) 2026-05-07 14:58:27 +02:00
chiro79 741779efd7 Remove name field from pvpc_hourly_pricing config flow #168955 (#169998) 2026-05-07 14:34:31 +02:00
Erik Montnemery eb1babedfd Improve condition docstrings (#170000)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-07 14:33:37 +02:00
Aidan Timson de0d24e91c Add default icon translations for lg_infrared (#170004) 2026-05-07 14:21:11 +02:00
Jan Bouwhuis 0de23f2636 Remove not used None defaults on MQTT publish API (#169936) 2026-05-07 13:53:29 +02:00
2990 changed files with 80515 additions and 16703 deletions
+12 -2
View File
@@ -16,9 +16,15 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- **Do NOT amend, squash, or rebase commits that have already been pushed to the PR branch after the PR is opened** - Reviewers need to follow the commit history, as well as see what changed since their last review
## Pull Requests
- When opening a pull request, use the repository's PR template (`.github/PULL_REQUEST_TEMPLATE.md`). NEVER REMOVE ANYTHING from the template.
- Do not remove checkboxes that are not checked — leave all unchecked checkboxes in place so reviewers can see which options were not selected.
## Development Commands
.vscode/tasks.json contains useful commands used for development.
- 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.
## Python Syntax Notes
@@ -28,10 +34,14 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Testing
- Use `uv run pytest` to run tests
- After modifying `strings.json` for an integration, regenerate the English translation file before running tests: `.venv/bin/python3 -m script.translations develop --integration <integration_name>`. Tests load translations from the generated `translations/en.json`, not directly from `strings.json`.
- When writing or modifying tests, ensure all test function parameters have type annotations.
- Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) over `Any`.
- Prefer `@pytest.mark.usefixtures` over arguments, if the argument is not going to be used.
- Avoid using conditions/branching in tests. Instead, either split tests or adjust the test parametrization to cover all cases without branching.
- If multiple tests share most of their code, use `pytest.mark.parametrize` to merge them into a single parameterized test instead of duplicating the body.
- If multiple tests share most of their code, use `pytest.mark.parametrize` to merge them into a single parameterized test instead of duplicating the body. Use `pytest.param` with an `id` parameter to name the test cases clearly.
- We use Syrupy for snapshot testing. Leverage `.ambr` snapshots instead of repetitive and exhaustive generation of test data within Python code itself.
## Good practices
+1 -1
View File
@@ -338,7 +338,7 @@ jobs:
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
with:
cosign-release: "v2.5.3"
+3 -3
View File
@@ -632,7 +632,7 @@ jobs:
with:
persist-credentials: false
- name: Dependency review
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
license-check: false # We use our own license audit checks
@@ -853,7 +853,7 @@ jobs:
run: |
. venv/bin/activate
python --version
mypy homeassistant pylint
mypy --num-workers=4 homeassistant pylint
- name: Run mypy (partially)
if: needs.info.outputs.test_full_suite == 'false'
shell: bash
@@ -862,7 +862,7 @@ jobs:
run: |
. venv/bin/activate
python --version
mypy $(printf "homeassistant/components/%s " ${INTEGRATIONS_GLOB})
mypy --num-workers=4 $(printf "homeassistant/components/%s " ${INTEGRATIONS_GLOB})
prepare-pytest-full:
name: Split tests for full run
+2 -2
View File
@@ -28,11 +28,11 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
category: "/language:python"
+8
View File
@@ -139,6 +139,7 @@ homeassistant.components.cambridge_audio.*
homeassistant.components.camera.*
homeassistant.components.canary.*
homeassistant.components.casper_glow.*
homeassistant.components.centriconnect.*
homeassistant.components.cert_expiry.*
homeassistant.components.clickatell.*
homeassistant.components.clicksend.*
@@ -155,6 +156,7 @@ homeassistant.components.counter.*
homeassistant.components.cover.*
homeassistant.components.cpuspeed.*
homeassistant.components.crownstone.*
homeassistant.components.data_grand_lyon.*
homeassistant.components.date.*
homeassistant.components.datetime.*
homeassistant.components.deako.*
@@ -248,6 +250,7 @@ homeassistant.components.gpsd.*
homeassistant.components.greeneye_monitor.*
homeassistant.components.group.*
homeassistant.components.guardian.*
homeassistant.components.guntamatic.*
homeassistant.components.habitica.*
homeassistant.components.hardkernel.*
homeassistant.components.hardware.*
@@ -295,6 +298,7 @@ homeassistant.components.imap.*
homeassistant.components.imgw_pib.*
homeassistant.components.immich.*
homeassistant.components.incomfort.*
homeassistant.components.indevolt.*
homeassistant.components.inels.*
homeassistant.components.infrared.*
homeassistant.components.input_button.*
@@ -354,6 +358,7 @@ homeassistant.components.lunatone.*
homeassistant.components.lutron.*
homeassistant.components.madvr.*
homeassistant.components.manual.*
homeassistant.components.marantz_infrared.*
homeassistant.components.mastodon.*
homeassistant.components.matrix.*
homeassistant.components.matter.*
@@ -420,9 +425,11 @@ homeassistant.components.opower.*
homeassistant.components.oralb.*
homeassistant.components.otbr.*
homeassistant.components.otp.*
homeassistant.components.ouman_eh_800.*
homeassistant.components.overkiz.*
homeassistant.components.overseerr.*
homeassistant.components.p1_monitor.*
homeassistant.components.paj_gps.*
homeassistant.components.panel_custom.*
homeassistant.components.paperless_ngx.*
homeassistant.components.peblar.*
@@ -485,6 +492,7 @@ homeassistant.components.rss_feed_template.*
homeassistant.components.russound_rio.*
homeassistant.components.ruuvi_gateway.*
homeassistant.components.ruuvitag_ble.*
homeassistant.components.samsung_infrared.*
homeassistant.components.samsungtv.*
homeassistant.components.saunum.*
homeassistant.components.scene.*
+12 -2
View File
@@ -6,9 +6,15 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- **Do NOT amend, squash, or rebase commits that have already been pushed to the PR branch after the PR is opened** - Reviewers need to follow the commit history, as well as see what changed since their last review
## Pull Requests
- When opening a pull request, use the repository's PR template (`.github/PULL_REQUEST_TEMPLATE.md`). NEVER REMOVE ANYTHING from the template.
- Do not remove checkboxes that are not checked — leave all unchecked checkboxes in place so reviewers can see which options were not selected.
## Development Commands
.vscode/tasks.json contains useful commands used for development.
- 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.
## Python Syntax Notes
@@ -18,10 +24,14 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Testing
- Use `uv run pytest` to run tests
- After modifying `strings.json` for an integration, regenerate the English translation file before running tests: `.venv/bin/python3 -m script.translations develop --integration <integration_name>`. Tests load translations from the generated `translations/en.json`, not directly from `strings.json`.
- When writing or modifying tests, ensure all test function parameters have type annotations.
- Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) over `Any`.
- Prefer `@pytest.mark.usefixtures` over arguments, if the argument is not going to be used.
- Avoid using conditions/branching in tests. Instead, either split tests or adjust the test parametrization to cover all cases without branching.
- If multiple tests share most of their code, use `pytest.mark.parametrize` to merge them into a single parameterized test instead of duplicating the body.
- If multiple tests share most of their code, use `pytest.mark.parametrize` to merge them into a single parameterized test instead of duplicating the body. Use `pytest.param` with an `id` parameter to name the test cases clearly.
- We use Syrupy for snapshot testing. Leverage `.ambr` snapshots instead of repetitive and exhaustive generation of test data within Python code itself.
## Good practices
Generated
+23 -4
View File
@@ -196,6 +196,7 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/autoskope/ @mcisk
/tests/components/autoskope/ @mcisk
/homeassistant/components/avea/ @pattyland
/tests/components/avea/ @pattyland
/homeassistant/components/awair/ @ahayworth @ricohageman
/tests/components/awair/ @ahayworth @ricohageman
/homeassistant/components/aws_s3/ @tomasbedrich
@@ -288,12 +289,16 @@ CLAUDE.md @home-assistant/core
/tests/components/cast/ @emontnemery
/homeassistant/components/ccm15/ @ocalvo
/tests/components/ccm15/ @ocalvo
/homeassistant/components/centriconnect/ @gresrun
/tests/components/centriconnect/ @gresrun
/homeassistant/components/cert_expiry/ @jjlawren
/tests/components/cert_expiry/ @jjlawren
/homeassistant/components/chacon_dio/ @cnico
/tests/components/chacon_dio/ @cnico
/homeassistant/components/chess_com/ @joostlek
/tests/components/chess_com/ @joostlek
/homeassistant/components/cielo_home/ @ihsan-cielo @mudasar-cielo
/tests/components/cielo_home/ @ihsan-cielo @mudasar-cielo
/homeassistant/components/cisco_ios/ @fbradyirl
/homeassistant/components/cisco_mobility_express/ @fbradyirl
/homeassistant/components/cisco_webex_teams/ @fbradyirl
@@ -345,6 +350,8 @@ CLAUDE.md @home-assistant/core
/tests/components/cync/ @Kinachi249
/homeassistant/components/daikin/ @fredrike
/tests/components/daikin/ @fredrike
/homeassistant/components/data_grand_lyon/ @Crocmagnon
/tests/components/data_grand_lyon/ @Crocmagnon
/homeassistant/components/date/ @home-assistant/core
/tests/components/date/ @home-assistant/core
/homeassistant/components/datetime/ @home-assistant/core
@@ -688,6 +695,8 @@ CLAUDE.md @home-assistant/core
/tests/components/growatt_server/ @johanzander
/homeassistant/components/guardian/ @bachya
/tests/components/guardian/ @bachya
/homeassistant/components/guntamatic/ @JensTimmerman
/tests/components/guntamatic/ @JensTimmerman
/homeassistant/components/habitica/ @tr4nt0r
/tests/components/habitica/ @tr4nt0r
/homeassistant/components/hanna/ @bestycame
@@ -972,8 +981,8 @@ CLAUDE.md @home-assistant/core
/tests/components/lektrico/ @lektrico
/homeassistant/components/letpot/ @jpelgrom
/tests/components/letpot/ @jpelgrom
/homeassistant/components/lg_infrared/ @home-assistant/core
/tests/components/lg_infrared/ @home-assistant/core
/homeassistant/components/lg_infrared/ @abmantis
/tests/components/lg_infrared/ @abmantis
/homeassistant/components/lg_netcast/ @Drafteed @splinter98
/tests/components/lg_netcast/ @Drafteed @splinter98
/homeassistant/components/lg_thinq/ @LG-ThinQ-Integration
@@ -1036,6 +1045,8 @@ CLAUDE.md @home-assistant/core
/tests/components/lyric/ @timmo001
/homeassistant/components/madvr/ @iloveicedgreentea
/tests/components/madvr/ @iloveicedgreentea
/homeassistant/components/marantz_infrared/ @balloob
/tests/components/marantz_infrared/ @balloob
/homeassistant/components/mastodon/ @fabaff @andrew-codechimp
/tests/components/mastodon/ @fabaff @andrew-codechimp
/homeassistant/components/matrix/ @PaarthShah
@@ -1298,6 +1309,8 @@ CLAUDE.md @home-assistant/core
/tests/components/osoenergy/ @osohotwateriot
/homeassistant/components/otbr/ @home-assistant/core
/tests/components/otbr/ @home-assistant/core
/homeassistant/components/ouman_eh_800/ @Markus98
/tests/components/ouman_eh_800/ @Markus98
/homeassistant/components/ourgroceries/ @OnFreund
/tests/components/ourgroceries/ @OnFreund
/homeassistant/components/overkiz/ @imicknl
@@ -1308,6 +1321,8 @@ CLAUDE.md @home-assistant/core
/tests/components/ovo_energy/ @timmo001
/homeassistant/components/p1_monitor/ @klaasnicolaas
/tests/components/p1_monitor/ @klaasnicolaas
/homeassistant/components/paj_gps/ @skipperro
/tests/components/paj_gps/ @skipperro
/homeassistant/components/palazzetti/ @dotvav
/tests/components/palazzetti/ @dotvav
/homeassistant/components/panel_custom/ @home-assistant/frontend
@@ -1519,6 +1534,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/sabnzbd/ @shaiu @jpbede
/tests/components/sabnzbd/ @shaiu @jpbede
/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/sanix/ @tomaszsluszniak
@@ -2017,6 +2034,8 @@ CLAUDE.md @home-assistant/core
/tests/components/xiaomi_miio/ @rytilahti @syssi @starkillerOG
/homeassistant/components/xiaomi_tv/ @simse
/homeassistant/components/xmpp/ @fabaff @flowolf
/homeassistant/components/xthings_cloud/ @XthingsJacobs
/tests/components/xthings_cloud/ @XthingsJacobs
/homeassistant/components/yale/ @bdraco
/tests/components/yale/ @bdraco
/homeassistant/components/yale_smart_alarm/ @gjohansson-ST
@@ -2047,8 +2066,8 @@ CLAUDE.md @home-assistant/core
/tests/components/zeroconf/ @bdraco
/homeassistant/components/zerproc/ @emlove
/tests/components/zerproc/ @emlove
/homeassistant/components/zeversolar/ @kvanzuijlen
/tests/components/zeversolar/ @kvanzuijlen
/homeassistant/components/zeversolar/ @kvanzuijlen @mhuiskes
/tests/components/zeversolar/ @kvanzuijlen @mhuiskes
/homeassistant/components/zha/ @dmulcahey @adminiuga @puddly @TheJulianJES
/tests/components/zha/ @dmulcahey @adminiuga @puddly @TheJulianJES
/homeassistant/components/zimi/ @markhannon
+6 -4
View File
@@ -73,10 +73,12 @@ async def auth_manager_from_config(
provider_hash[key] = provider
if isinstance(provider, HassAuthProvider):
# Can be removed in 2026.7 with the legacy mode of homeassistant auth provider
# We need to initialize the provider to create the repair if needed as otherwise
# the provider will be initialized on first use, which could be rare as users
# don't frequently change auth settings
# Can be removed in 2026.7 with the legacy mode of
# homeassistant auth provider.
# We need to initialize the provider to create the repair
# if needed as otherwise the provider will be initialized
# on first use, which could be rare as users don't
# frequently change auth settings
await provider.async_initialize()
if module_configs:
@@ -120,9 +120,10 @@ class Data:
if self.normalize_username(username, force_normalize=True) != username:
logging.getLogger(__name__).warning(
(
"Home Assistant auth provider is running in legacy mode "
"because we detected usernames that are normalized (lowercase and without spaces)."
" Please change the username: '%s'."
"Home Assistant auth provider is running in"
" legacy mode because we detected usernames"
" that are normalized (lowercase and without"
" spaces). Please change the username: '%s'."
),
username,
)
@@ -139,7 +140,9 @@ class Data:
severity=ir.IssueSeverity.WARNING,
translation_key="homeassistant_provider_not_normalized_usernames",
translation_placeholders={
"usernames": f'- "{'"\n- "'.join(sorted(not_normalized_usernames))}"'
"usernames": (
f'- "{'"\n- "'.join(sorted(not_normalized_usernames))}"'
)
},
learn_more_url="homeassistant://config/users",
)
+6 -2
View File
@@ -60,7 +60,10 @@ def restore_backup_file_content(config_dir: Path) -> RestoreBackupFileContent |
def _clear_configuration_directory(config_dir: Path, keep: Iterable[str]) -> None:
"""Delete all files and directories in the config directory except entries in the keep list."""
"""Delete all files and directories in the config dir.
Entries in the keep list are preserved.
"""
keep_paths = [config_dir.joinpath(path) for path in keep]
entries_to_remove = sorted(
entry for entry in config_dir.iterdir() if entry not in keep_paths
@@ -101,7 +104,8 @@ def _extract_backup(
)
) > HA_VERSION:
raise ValueError(
f"You need at least Home Assistant version {backup_meta_version} to restore this backup"
f"You need at least Home Assistant version"
f" {backup_meta_version} to restore this backup"
)
with securetar.SecureTarFile(
+10 -5
View File
@@ -17,7 +17,8 @@ from time import monotonic
from typing import TYPE_CHECKING, Any
# Import cryptography early since import openssl is not thread-safe
# _frozen_importlib._DeadlockError: deadlock detected by _ModuleLock('cryptography.hazmat.backends.openssl.backend')
# _frozen_importlib._DeadlockError: deadlock detected by
# _ModuleLock('cryptography.hazmat.backends.openssl.backend')
import cryptography.hazmat.backends.openssl.backend # noqa: F401
import voluptuous as vol
import yarl
@@ -165,10 +166,14 @@ FRONTEND_INTEGRATIONS = {
# visible in frontend
"frontend",
}
# Stage 0 is divided into substages. Each substage has a name, a set of integrations and a timeout.
# The substage containing recorder should have no timeout, as it could cancel a database migration.
# Recorder freezes "recorder" timeout during a migration, but it does not freeze other timeouts.
# If we add timeouts to the frontend substages, we should make sure they don't apply in recovery mode.
# Stage 0 is divided into substages. Each substage has a name,
# a set of integrations and a timeout.
# The substage containing recorder should have no timeout, as it
# could cancel a database migration.
# Recorder freezes "recorder" timeout during a migration, but it
# does not freeze other timeouts.
# If we add timeouts to the frontend substages, we should make sure
# they don't apply in recovery mode.
STAGE_0_INTEGRATIONS = (
# Load logging and http deps as soon as possible
("logging, http deps", LOGGING_AND_HTTP_DEPS_INTEGRATIONS, None),
+5
View File
@@ -0,0 +1,5 @@
{
"domain": "marantz",
"name": "Marantz",
"integrations": ["marantz", "marantz_infrared"]
}
+5
View File
@@ -0,0 +1,5 @@
{
"domain": "mitsubishi",
"name": "Mitsubishi",
"integrations": ["melcloud", "mitsubishi_comfort"]
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"domain": "samsung",
"name": "Samsung",
"integrations": ["familyhub", "samsungtv", "syncthru"]
"integrations": ["familyhub", "samsung_infrared", "samsungtv", "syncthru"]
}
@@ -44,6 +44,7 @@ def _change_setting(call: ServiceCall) -> None:
try:
_get_abode_system(call.hass).abode.set_setting(setting, value)
# pylint: disable-next=home-assistant-action-swallowed-exception
except AbodeException as ex:
LOGGER.warning(ex)
@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/acer_projector",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["serialx==1.7.1"]
"requirements": ["serialx==1.7.3"]
}
@@ -79,6 +79,12 @@
"exceptions": {
"config_entry_not_loaded": {
"message": "Config entry not loaded."
},
"error_while_turn_off": {
"message": "An error occurred while turning off AdGuard Home switch."
},
"error_while_turn_on": {
"message": "An error occurred while turning on AdGuard Home switch."
}
},
"services": {
+12 -5
View File
@@ -9,10 +9,11 @@ from adguardhome import AdGuardHome, AdGuardHomeError
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdGuardConfigEntry, AdGuardData
from .const import DOMAIN, LOGGER
from .const import DOMAIN
from .entity import AdGuardHomeEntity
SCAN_INTERVAL = timedelta(seconds=10)
@@ -116,17 +117,23 @@ class AdGuardHomeSwitch(AdGuardHomeEntity, SwitchEntity):
"""Turn off the switch."""
try:
await self.entity_description.turn_off_fn(self.adguard)()
except AdGuardHomeError:
LOGGER.error("An error occurred while turning off AdGuard Home switch")
except AdGuardHomeError as err:
self._attr_available = False
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="error_while_turn_off",
) from err
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the switch."""
try:
await self.entity_description.turn_on_fn(self.adguard)()
except AdGuardHomeError:
LOGGER.error("An error occurred while turning on AdGuard Home switch")
except AdGuardHomeError as err:
self._attr_available = False
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="error_while_turn_on",
) from err
async def _adguard_update(self) -> None:
"""Update AdGuard Home entity."""
+2 -1
View File
@@ -106,7 +106,8 @@ class AdsLight(AdsEntity, LightEntity):
self._attr_supported_color_modes = filter_supported_color_modes(color_modes)
self._attr_color_mode = next(iter(self._attr_supported_color_modes))
# Set color temperature range (static config values take precedence over defaults)
# Set color temperature range
# (static config values take precedence over defaults)
if ads_var_color_temp_kelvin is not None:
self._attr_min_color_temp_kelvin = (
min_color_temp_kelvin
@@ -171,7 +171,8 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
@property
def target_temperature(self) -> float | None:
"""Return the current target temperature."""
# If the system is in MyZone mode, and a zone is set, return that temperature instead.
# If the system is in MyZone mode, and a zone is set,
# return that temperature instead.
if self._myzone and self.preset_mode == ADVANTAGE_AIR_MYZONE:
return self._myzone["setTemp"]
return self._ac["setTemp"]
@@ -296,7 +297,11 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
@property
def hvac_action(self) -> HVACAction | None:
"""Return the HVAC action, inheriting from master AC if zone is open but idle if air is <= 5%."""
"""Return the HVAC action.
Inherits from master AC if zone is open but idle if air
is <= 5%.
"""
if self._ac["state"] == ADVANTAGE_AIR_STATE_OFF:
return HVACAction.OFF
master_action = HVAC_ACTIONS.get(self._ac["mode"], HVACAction.OFF)
@@ -59,6 +59,8 @@ class AemetConfigFlow(ConfigFlow, domain=DOMAIN):
schema = vol.Schema(
{
vol.Required(CONF_API_KEY): str,
# Name field is no longer allowed in config flow schemas
# pylint: disable-next=home-assistant-config-flow-name-field
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
vol.Optional(
CONF_LATITUDE, default=self.hass.config.latitude
@@ -56,6 +56,7 @@ async def async_setup_entry(
)
async_dispatcher_send(hass, UPDATE_TOPIC)
# pylint: disable-next=home-assistant-service-registered-in-setup-entry
hass.services.async_register(
DOMAIN,
SERVICE_ADD_TRACKING,
@@ -71,6 +72,7 @@ async def async_setup_entry(
)
async_dispatcher_send(hass, UPDATE_TOPIC)
# pylint: disable-next=home-assistant-service-registered-in-setup-entry
hass.services.async_register(
DOMAIN,
SERVICE_REMOVE_TRACKING,
+58 -36
View File
@@ -68,20 +68,24 @@ TRIGGERS: dict[str, type[Trigger]] = {
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CarbonMonoxideConcentrationConverter,
),
"co_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.CO)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CarbonMonoxideConcentrationConverter,
"co_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.CO)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CarbonMonoxideConcentrationConverter,
)
),
"ozone_changed": make_entity_numerical_state_changed_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.OZONE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
OzoneConcentrationConverter,
),
"ozone_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.OZONE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
OzoneConcentrationConverter,
"ozone_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.OZONE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
OzoneConcentrationConverter,
)
),
"voc_changed": make_entity_numerical_state_changed_with_unit_trigger(
{
@@ -92,14 +96,16 @@ TRIGGERS: dict[str, type[Trigger]] = {
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
MassVolumeConcentrationConverter,
),
"voc_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{
SENSOR_DOMAIN: DomainSpec(
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS
)
},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
MassVolumeConcentrationConverter,
"voc_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{
SENSOR_DOMAIN: DomainSpec(
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS
)
},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
MassVolumeConcentrationConverter,
)
),
"voc_ratio_changed": make_entity_numerical_state_changed_with_unit_trigger(
{
@@ -110,44 +116,60 @@ TRIGGERS: dict[str, type[Trigger]] = {
CONCENTRATION_PARTS_PER_BILLION,
UnitlessRatioConverter,
),
"voc_ratio_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{
SENSOR_DOMAIN: DomainSpec(
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS
)
},
CONCENTRATION_PARTS_PER_BILLION,
UnitlessRatioConverter,
"voc_ratio_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{
SENSOR_DOMAIN: DomainSpec(
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS
)
},
CONCENTRATION_PARTS_PER_BILLION,
UnitlessRatioConverter,
)
),
"no_changed": make_entity_numerical_state_changed_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.NITROGEN_MONOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
NitrogenMonoxideConcentrationConverter,
),
"no_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.NITROGEN_MONOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
NitrogenMonoxideConcentrationConverter,
"no_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{
SENSOR_DOMAIN: DomainSpec(
device_class=SensorDeviceClass.NITROGEN_MONOXIDE
)
},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
NitrogenMonoxideConcentrationConverter,
)
),
"no2_changed": make_entity_numerical_state_changed_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.NITROGEN_DIOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
NitrogenDioxideConcentrationConverter,
),
"no2_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.NITROGEN_DIOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
NitrogenDioxideConcentrationConverter,
"no2_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{
SENSOR_DOMAIN: DomainSpec(
device_class=SensorDeviceClass.NITROGEN_DIOXIDE
)
},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
NitrogenDioxideConcentrationConverter,
)
),
"so2_changed": make_entity_numerical_state_changed_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.SULPHUR_DIOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SulphurDioxideConcentrationConverter,
),
"so2_crossed_threshold": make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.SULPHUR_DIOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SulphurDioxideConcentrationConverter,
"so2_crossed_threshold": (
make_entity_numerical_state_crossed_threshold_with_unit_trigger(
{SENSOR_DOMAIN: DomainSpec(device_class=SensorDeviceClass.SULPHUR_DIOXIDE)},
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SulphurDioxideConcentrationConverter,
)
),
# Numerical sensor triggers without unit conversion (single-unit device classes)
"co2_changed": make_entity_numerical_state_changed_trigger(
+2 -1
View File
@@ -184,7 +184,8 @@ async def async_setup_entry(
(
AirlySensor(coordinator, name, description)
for description in SENSOR_TYPES
# When we use the nearest method, we are not sure which sensors are available
# When we use the nearest method, we are not sure
# which sensors are available
if coordinator.data.get(description.key)
),
False,
@@ -0,0 +1,67 @@
rules:
# Bronze
action-setup:
status: exempt
comment: Integration does not register custom actions.
appropriate-polling:
status: done
comment: Reports are polled every 30 minutes so newly published hourly AirNow reports are picked up promptly.
brands: done
common-modules: done
config-flow-test-coverage: todo
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: Integration does not register custom actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: Integration does not subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions: todo
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo
entity-unavailable: todo
integration-owner: todo
log-when-unavailable: todo
parallel-updates: done
reauthentication-flow: todo
test-coverage: todo
# Gold
devices: todo
diagnostics: done
discovery: todo
discovery-update-info: todo
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
dynamic-devices: todo
entity-category: todo
entity-device-class:
status: todo
comment: The ozone sensor can still use the ozone device class.
entity-disabled-by-default: todo
entity-translations: todo
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
stale-devices: todo
repair-issues: todo
# Platinum
async-dependency: done
inject-websession: done
strict-typing: todo
+3 -1
View File
@@ -75,7 +75,9 @@ def aqi_extra_attrs(data: dict[str, Any]) -> dict[str, Any]:
ATTR_DESCR: data[ATTR_API_AQI_DESCRIPTION],
ATTR_LEVEL: data[ATTR_API_AQI_LEVEL],
ATTR_TIME: parser.parse(
f"{data[ATTR_API_REPORT_DATE]} {data[ATTR_API_REPORT_HOUR]}:00 {data[ATTR_API_REPORT_TZ]}",
f"{data[ATTR_API_REPORT_DATE]} "
f"{data[ATTR_API_REPORT_HOUR]}:00 "
f"{data[ATTR_API_REPORT_TZ]}",
tzinfos=US_TZ_OFFSETS,
).isoformat(),
}
@@ -46,6 +46,9 @@
"init": {
"data": {
"radius": "Station radius (miles)"
},
"data_description": {
"radius": "The radius in miles around your location to search for reporting stations."
}
}
}
@@ -83,6 +83,7 @@ class AirobotButton(AirobotEntity, ButtonEntity):
"""Handle the button press."""
try:
await self.entity_description.press_fn(self.coordinator)
# pylint: disable-next=home-assistant-action-swallowed-exception
except AirobotConnectionError, AirobotTimeoutError:
# Connection errors during reboot are expected as device restarts
pass
@@ -2,7 +2,6 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import Generic, TypeVar
from airos.data import AirOSDataBaseClass
@@ -20,13 +19,10 @@ from .entity import AirOSEntity
PARALLEL_UPDATES = 0
AirOSDataModel = TypeVar("AirOSDataModel", bound=AirOSDataBaseClass)
@dataclass(frozen=True, kw_only=True)
class AirOSBinarySensorEntityDescription(
class AirOSBinarySensorEntityDescription[AirOSDataModel: AirOSDataBaseClass](
BinarySensorEntityDescription,
Generic[AirOSDataModel],
):
"""Describe an AirOS binary sensor."""
+3 -4
View File
@@ -3,7 +3,6 @@
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Generic, TypeVar
from airos.data import (
AirOSDataBaseClass,
@@ -41,11 +40,11 @@ WIRELESS_ROLE_OPTIONS = [mode.value for mode in DerivedWirelessRole]
PARALLEL_UPDATES = 0
AirOSDataModel = TypeVar("AirOSDataModel", bound=AirOSDataBaseClass)
@dataclass(frozen=True, kw_only=True)
class AirOSSensorEntityDescription(SensorEntityDescription, Generic[AirOSDataModel]):
class AirOSSensorEntityDescription[AirOSDataModel: AirOSDataBaseClass](
SensorEntityDescription
):
"""Describe an AirOS sensor."""
value_fn: Callable[[AirOSDataModel], StateType]
+2 -1
View File
@@ -54,7 +54,8 @@ class AirQCoordinator(DataUpdateCoordinator):
"""Fetch the data from the device."""
if "name" not in self.device_info:
_LOGGER.debug(
"'name' not found in AirQCoordinator.device_info, fetching from the device"
"'name' not found in AirQCoordinator.device_info,"
" fetching from the device"
)
info = await self.airq.fetch_device_info()
self.device_info.update(
@@ -158,7 +158,8 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
await self._airtouch.SetCoolingModeForAc(
self._ac_number, HA_STATE_TO_AT[hvac_mode]
)
# in case it isn't already, unless the HVAC mode was off, then the ac should be on
# in case it isn't already, unless the HVAC mode was off,
# then the ac should be on
await self.async_turn_on()
self._unit = self._airtouch.GetAcs()[self._ac_number]
_LOGGER.debug("Setting operation mode of %s to %s", self._ac_number, hvac_mode)
@@ -246,7 +247,8 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
@property
def hvac_mode(self) -> HVACMode:
"""Return hvac target hvac state."""
# there are other power states that aren't 'on' but still count as on (eg. 'Turbo')
# there are other power states that aren't 'on' but still
# count as on (eg. 'Turbo')
is_off = self._unit.PowerState == "Off"
if is_off:
return HVACMode.OFF
@@ -178,7 +178,8 @@ class Airtouch5AC(Airtouch5ClimateEntity):
if ability.supports_fan_speed_intelligent_auto:
self._attr_fan_modes.append(FAN_INTELLIGENT_AUTO)
# We can have different setpoints for heat cool, we expose the lowest low and highest high
# We can have different setpoints for heat cool,
# we expose the lowest low and highest high
self._attr_min_temp = min(
ability.min_cool_set_point, ability.min_heat_set_point
)
@@ -290,7 +291,8 @@ class Airtouch5Zone(Airtouch5ClimateEntity):
manufacturer="Polyaire",
model="AirTouch 5",
)
# We can have different setpoints for heat and cool, we expose the lowest low and highest high
# We can have different setpoints for heat and cool,
# we expose the lowest low and highest high
self._attr_min_temp = min(ac.min_cool_set_point, ac.min_heat_set_point)
self._attr_max_temp = max(ac.max_cool_set_point, ac.max_heat_set_point)
@@ -34,8 +34,9 @@ class AirTouch5ConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception")
errors = {"base": "cannot_connect"}
else:
# Uses the host/IP value from CONF_HOST as unique ID, which is no longer allowed
# pylint: disable-next=hass-unique-id-ip-based
# Uses the host/IP value from CONF_HOST as unique ID,
# which is no longer allowed
# pylint: disable-next=home-assistant-unique-id-ip-based
await self.async_set_unique_id(user_input[CONF_HOST])
self._abort_if_unique_id_configured()
return self.async_create_entry(
@@ -75,7 +75,7 @@ def async_get_cloud_api_update_interval(
def async_get_cloud_coordinators_by_api_key(
hass: HomeAssistant, api_key: str
) -> list[AirVisualDataUpdateCoordinator]:
"""Get all AirVisualDataUpdateCoordinator objects related to a particular API key."""
"""Get all coordinators related to a particular API key."""
return [
entry.runtime_data
for entry in hass.config_entries.async_entries(DOMAIN)
@@ -24,7 +24,7 @@ class AsyncConfigFlowAuth(Auth):
class AsyncConfigEntryAuth(Auth):
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""
"""Provide Aladdin Connect Genie auth tied to an OAuth2 config entry."""
def __init__(
self,
@@ -354,8 +354,9 @@ def _validate_zone_input(zone_input: dict[str, Any] | None) -> dict[str, str]:
def _fix_input_types(zone_input: dict[str, Any]) -> dict[str, Any]:
"""Convert necessary keys to int.
Since ConfigFlow inputs of type int cannot default to an empty string, we collect the values below as
strings and then convert them to ints.
Since ConfigFlow inputs of type int cannot default to an empty
string, we collect the values below as strings and then convert
them to ints.
"""
for key in (CONF_ZONE_LOOP, CONF_RELAY_ADDR, CONF_RELAY_CHAN):
+4 -1
View File
@@ -1255,7 +1255,10 @@ async def async_api_set_mode(
service = water_heater.SERVICE_SET_OPERATION_MODE
data[water_heater.ATTR_OPERATION_MODE] = operation_mode
else:
msg = f"Entity '{entity.entity_id}' does not support Operation mode '{operation_mode}'"
msg = (
f"Entity '{entity.entity_id}' does not support"
f" Operation mode '{operation_mode}'"
)
raise AlexaInvalidValueError(msg)
# Cover Position
+2 -1
View File
@@ -224,7 +224,8 @@ def resolve_slot_data(key: str, request: dict[str, Any]) -> dict[str, str]:
resolved_data["id"] = possible_values[0]["id"]
# If there is only one match use the resolved value, otherwise the
# resolution cannot be determined, so use the spoken slot value and empty string as id
# resolution cannot be determined, so use the spoken slot
# value and empty string as id
if len(possible_values) == 1:
resolved_data["value"] = possible_values[0]["name"]
else:
+36 -3
View File
@@ -2,6 +2,7 @@
from asyncio import timeout
from collections.abc import Mapping
from datetime import datetime, timedelta
from http import HTTPStatus
import json
import logging
@@ -11,7 +12,12 @@ from uuid import uuid4
import aiohttp
from homeassistant.components import event
from homeassistant.const import EVENT_STATE_CHANGED, STATE_ON
from homeassistant.const import (
EVENT_STATE_CHANGED,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import (
CALLBACK_TYPE,
Event,
@@ -51,6 +57,25 @@ DEFAULT_TIMEOUT = 10
TO_REDACT = {"correlationToken", "token"}
def valid_doorbell_timestamp(entity_id: str, event_state: str) -> bool:
"""Check if doorbell event timestamp is valid."""
if event_state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False
try:
timestamp = datetime.fromisoformat(event_state)
except ValueError:
_LOGGER.debug(
"Unable to parse ISO timestamp from state for %s. Got %s",
entity_id,
event_state,
)
return False
else:
if (dt_util.utcnow() - timestamp) < timedelta(seconds=30):
return True
return False
class AlexaDirective:
"""An incoming Alexa directive."""
@@ -315,9 +340,17 @@ async def async_enable_proactive_mode(
if should_doorbell:
old_state = data["old_state"]
if new_state.domain == event.DOMAIN or (
if (
new_state.domain == event.DOMAIN
and valid_doorbell_timestamp(new_state.entity_id, new_state.state)
and (old_state is None or old_state.state != STATE_UNAVAILABLE)
and (old_state is None or old_state.state != new_state.state)
) or (
new_state.state == STATE_ON
and (old_state is None or old_state.state != STATE_ON)
and (
old_state is None
or old_state.state not in (STATE_ON, STATE_UNAVAILABLE)
)
):
await async_send_doorbell_event_message(
hass, smart_home_config, alexa_changed_entity
@@ -44,6 +44,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version > 1:
# This means the user has downgraded from a future version
return False
if entry.version == 1 and entry.minor_version < 3:
if CONF_SITE in entry.data:
# Site in data (wrong place), just move to login data
@@ -27,9 +27,6 @@ COUNTRY_DOMAINS = {
"za": "co.za",
}
CATEGORY_SENSORS = "sensors"
CATEGORY_NOTIFICATIONS = "notifications"
# Map service translation keys to Alexa API
INFO_SKILLS_MAPPING = {
"calendar_today": "Alexa.Calendar.PlayToday",
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==13.4.3"]
"requirements": ["aioamazondevices==13.7.0"]
}
@@ -29,8 +29,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .const import CATEGORY_NOTIFICATIONS, CATEGORY_SENSORS
from .coordinator import AmazonConfigEntry
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .entity import AmazonEntity
from .utils import async_remove_unsupported_notification_sensors
@@ -38,30 +37,44 @@ from .utils import async_remove_unsupported_notification_sensors
PARALLEL_UPDATES = 0
type ValueFn = Callable[
[AmazonDevice, str, AmazonDevicesCoordinator], StateType | datetime
]
@dataclass(frozen=True, kw_only=True)
class AmazonSensorEntityDescription(SensorEntityDescription):
"""Amazon Devices sensor entity description."""
class AmazonBaseEntityDescription(SensorEntityDescription):
"""Shared Amazon Devices entity description."""
native_unit_of_measurement_fn: Callable[[AmazonDevice, str], str] | None = None
is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: (
device.online
)
value_fn: ValueFn
@dataclass(frozen=True, kw_only=True)
class AmazonSensorEntityDescription(AmazonBaseEntityDescription):
"""Amazon Devices sensor entity description."""
is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: (
device.online
and (sensor := device.sensors.get(key)) is not None
and sensor.error is False
)
category: str = CATEGORY_SENSORS
value_fn: ValueFn = lambda device, key, _: device.sensors[key].value
@dataclass(frozen=True, kw_only=True)
class AmazonNotificationEntityDescription(SensorEntityDescription):
class AmazonNotificationEntityDescription(AmazonBaseEntityDescription):
"""Amazon Devices notification entity description."""
native_unit_of_measurement_fn: Callable[[AmazonDevice, str], str] | None = None
is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: (
device.online
and (notification := device.notifications.get(key)) is not None
and notification.next_occurrence is not None
)
category: str = CATEGORY_NOTIFICATIONS
value_fn: ValueFn = lambda device, key, _: device.notifications[key].next_occurrence
SENSORS: Final = (
@@ -193,11 +206,11 @@ class AmazonSensorEntity(AmazonEntity, SensorEntity):
@property
def native_value(self) -> StateType | datetime:
"""Return the state of the sensor."""
# Sensors
if self.entity_description.category == CATEGORY_SENSORS:
return self.device.sensors[self.entity_description.key].value
# Notifications
return self.device.notifications[self.entity_description.key].next_occurrence
return self.entity_description.value_fn(
self.device,
self.entity_description.key,
self.coordinator,
)
@property
def available(self) -> bool:
@@ -21,7 +21,8 @@ API_URL = "https://app.amber.com.au/developers"
def generate_site_selector_name(site: Site) -> str:
"""Generate the name to show in the site drop down in the configuration flow."""
# For some reason the generated API key returns this as any, not a string. Thanks pydantic
# For some reason the generated API key returns this as any,
# not a string. Thanks pydantic
nmi = str(site.nmi)
if site.status == SiteStatus.CLOSED:
if site.closed_on is None:
@@ -48,7 +48,7 @@ def is_feed_in(interval: ActualInterval | CurrentInterval | ForecastInterval) ->
class AmberUpdateCoordinator(DataUpdateCoordinator):
"""AmberUpdateCoordinator - In charge of downloading the data for a site, which all the sensors read."""
"""Coordinator in charge of downloading site data for all sensors."""
config_entry: AmberConfigEntry
@@ -14,7 +14,10 @@ DESCRIPTOR_MAP: dict[str, str] = {
def normalize_descriptor(descriptor: PriceDescriptor | None) -> str | None:
"""Return the snake case versions of descriptor names. Returns None if the name is not recognized."""
"""Return the snake case versions of descriptor names.
Returns None if the name is not recognized.
"""
if descriptor in DESCRIPTOR_MAP:
return DESCRIPTOR_MAP[descriptor]
return None
@@ -26,4 +26,5 @@ def get_station_name(station: dict[str, Any]) -> str:
.get(API_STATION_LOCATION)
)
station_type = station.get(API_LAST_DATA, {}).get(API_STATION_TYPE)
return f"{location}{'' if location is None or station_type is None else ' '}{station_type}"
separator = "" if location is None or station_type is None else " "
return f"{location}{separator}{station_type}"
@@ -192,7 +192,8 @@ class AmcrestBinarySensor(BinarySensorEntity):
if self._api.available:
# Send a command to the camera to test if we can still communicate with it.
# Override of Http.async_command() in __init__.py will set self._api.available
# Override of Http.async_command() in __init__.py will
# set self._api.available
# accordingly.
with suppress(AmcrestError):
await self._api.async_current_time
+4 -2
View File
@@ -461,7 +461,8 @@ class AmcrestCam(Camera):
async def _async_set_recording(self, enable: bool) -> None:
rec_mode = {"Automatic": 0, "Manual": 1}
# The property has a str type, but setter has int type, which causes mypy confusion
# The property has a str type, but setter has int type,
# which causes mypy confusion
await self._api.async_set_record_mode(
rec_mode["Manual" if enable else "Automatic"]
)
@@ -479,7 +480,8 @@ class AmcrestCam(Camera):
return await self._api.async_is_motion_detector_on()
async def _async_set_motion_detection(self, enable: bool) -> None:
# The property has a str type, but setter has bool type, which causes mypy confusion
# The property has a str type, but setter has bool type,
# which causes mypy confusion
await self._api.async_set_motion_detection(enable)
async def _async_enable_motion_detection(self, enable: bool) -> None:
+33 -20
View File
@@ -3,6 +3,7 @@
import asyncio
from asyncio import timeout
from collections.abc import Awaitable, Callable, Iterable, Mapping
import contextlib
from dataclasses import asdict as dataclass_asdict, dataclass, field
from datetime import datetime
import random
@@ -297,20 +298,24 @@ class Analytics:
if stored:
self._data = AnalyticsData.from_dict(stored)
if (
self.supervisor
and (supervisor_info := hassio.get_supervisor_info(self._hass)) is not None
):
if not self.onboarded:
# User have not configured analytics, get this setting from the supervisor
if supervisor_info[ATTR_DIAGNOSTICS] and not self.preferences.get(
ATTR_DIAGNOSTICS, False
):
self._data.preferences[ATTR_DIAGNOSTICS] = True
elif not supervisor_info[ATTR_DIAGNOSTICS] and self.preferences.get(
ATTR_DIAGNOSTICS, False
):
self._data.preferences[ATTR_DIAGNOSTICS] = False
if self.supervisor and not self.onboarded:
# This may raise HassioNotReadyError if Supervisor was unreachable
# during setup of the Supervisor integration. That will fail setup
# of this integration. However there is no better option at this time
# since we need to get the diagnostic setting from Supervisor to correctly
# setup this integration and we can't raise ConfigEntryNotReady to
# trigger a retry from async_setup.
supervisor_info = hassio.get_supervisor_info(self._hass)
# User have not configured analytics, get this setting from the supervisor
if supervisor_info[ATTR_DIAGNOSTICS] and not self.preferences.get(
ATTR_DIAGNOSTICS, False
):
self._data.preferences[ATTR_DIAGNOSTICS] = True
elif not supervisor_info[ATTR_DIAGNOSTICS] and self.preferences.get(
ATTR_DIAGNOSTICS, False
):
self._data.preferences[ATTR_DIAGNOSTICS] = False
async def _save(self) -> None:
"""Save data."""
@@ -344,9 +349,14 @@ class Analytics:
await self._save()
if self.supervisor:
# get_supervisor_info was called during setup so we can't get here
# if it raised. The others may raise HassioNotReadyError if only some
# data was successfully fetched from Supervisor
supervisor_info = hassio.get_supervisor_info(hass)
operating_system_info = hassio.get_os_info(hass) or {}
addons_info = hassio.get_addons_info(hass) or {}
with contextlib.suppress(hassio.HassioNotReadyError):
operating_system_info = hassio.get_os_info(hass)
with contextlib.suppress(hassio.HassioNotReadyError):
addons_info = hassio.get_addons_info(hass)
system_info = await async_get_system_info(hass)
integrations = []
@@ -419,7 +429,7 @@ class Analytics:
integrations.append(integration.domain)
if addons_info is not None:
if addons_info:
supervisor_client = hassio.get_supervisor_client(hass)
installed_addons = await asyncio.gather(
*(supervisor_client.addons.addon_info(slug) for slug in addons_info)
@@ -602,7 +612,8 @@ class Analytics:
else:
LOGGER.warning(
"Unexpected status code %s when submitting snapshot analytics to %s",
"Unexpected status code %s when submitting"
" snapshot analytics to %s",
response.status,
url,
)
@@ -804,7 +815,8 @@ async def _async_snapshot_payload(hass: HomeAssistant) -> dict: # noqa: C901
if not isinstance(integration_config, AnalyticsModifications):
LOGGER.error( # type: ignore[unreachable]
"Calling async_modify_analytics for integration '%s' did not return an AnalyticsConfig",
"Calling async_modify_analytics for integration"
" '%s' did not return an AnalyticsConfig",
integration_domain,
)
integration_configs[integration_domain] = AnalyticsModifications(
@@ -818,7 +830,8 @@ async def _async_snapshot_payload(hass: HomeAssistant) -> dict: # noqa: C901
# We need to refer to other devices, for example in `via_device` field.
# We don't however send the original device ids outside of Home Assistant,
# instead we refer to devices by (integration_domain, index_in_integration_device_list).
# instead we refer to devices by
# (integration_domain, index_in_integration_device_list).
device_id_mapping: dict[str, tuple[str, int]] = {}
# Fill out information about devices
@@ -15,6 +15,7 @@ from homeassistant.config_entries import (
)
from homeassistant.const import CONF_DEVICE_CLASS, CONF_HOST, CONF_PORT
from homeassistant.core import callback
from homeassistant.data_entry_flow import SectionConfig, section
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
ObjectSelector,
@@ -32,6 +33,7 @@ from .const import (
CONF_APPS,
CONF_EXCLUDE_UNNAMED_APPS,
CONF_GET_SOURCES,
CONF_MORE_OPTIONS,
CONF_SCREENCAP_INTERVAL,
CONF_STATE_DETECTION_RULES,
CONF_TURN_OFF_COMMAND,
@@ -97,20 +99,22 @@ class AndroidTVFlowHandler(ConfigFlow, domain=DOMAIN):
)
),
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Required(CONF_MORE_OPTIONS): section(
vol.Schema(
{
vol.Optional(CONF_ADBKEY): str,
vol.Optional(CONF_ADB_SERVER_IP): str,
vol.Optional(
CONF_ADB_SERVER_PORT,
default=DEFAULT_ADB_SERVER_PORT,
): cv.port,
}
),
SectionConfig(collapsed=True),
),
},
)
if self.show_advanced_options:
data_schema = data_schema.extend(
{
vol.Optional(CONF_ADBKEY): str,
vol.Optional(CONF_ADB_SERVER_IP): str,
vol.Required(
CONF_ADB_SERVER_PORT, default=DEFAULT_ADB_SERVER_PORT
): cv.port,
}
)
return self.async_show_form(
step_id="user",
data_schema=data_schema,
@@ -155,6 +159,10 @@ class AndroidTVFlowHandler(ConfigFlow, domain=DOMAIN):
error = None
if user_input is not None:
user_input = user_input.copy()
more_options = user_input.pop(CONF_MORE_OPTIONS, {})
user_input.update(more_options)
host = user_input[CONF_HOST]
adb_key = user_input.get(CONF_ADBKEY)
if CONF_ADB_SERVER_IP in user_input:
@@ -3,6 +3,7 @@
DOMAIN = "androidtv"
CONF_ADB_SERVER_IP = "adb_server_ip"
CONF_MORE_OPTIONS = "more_options"
CONF_ADB_SERVER_PORT = "adb_server_port"
CONF_ADBKEY = "adbkey"
CONF_APPS = "apps"
+3 -4
View File
@@ -94,10 +94,9 @@ def adb_decorator[_ADBDeviceT: AndroidTVEntity, **_P, _R](
# it doesn't happen over and over again.
if self.available:
_LOGGER.error(
(
"Unexpected exception executing an ADB command. ADB connection"
" re-establishing attempt in the next update. Error: %s"
),
"Unexpected exception executing an ADB"
" command. ADB connection re-establishing"
" attempt in the next update. Error: %s",
err,
)
@@ -281,7 +281,7 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
@adb_decorator()
async def service_download(self, device_path: str, local_path: str) -> None:
"""Download a file from your Android / Fire TV device to your Home Assistant instance."""
"""Download a file from your Android / Fire TV device."""
if not self.hass.config.is_allowed_path(local_path):
_LOGGER.warning("'%s' is not secure to load data from!", local_path)
return
@@ -290,7 +290,7 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
@adb_decorator()
async def service_upload(self, device_path: str, local_path: str) -> None:
"""Upload a file from your Home Assistant instance to an Android / Fire TV device."""
"""Upload a file to an Android / Fire TV device."""
if not self.hass.config.is_allowed_path(local_path):
_LOGGER.warning("'%s' is not secure to load data from!", local_path)
return
@@ -14,12 +14,19 @@
"step": {
"user": {
"data": {
"adb_server_ip": "IP address of the ADB server (leave empty to not use)",
"adb_server_port": "Port of the ADB server",
"adbkey": "Path to your ADB key file (leave empty to auto generate)",
"device_class": "The type of device",
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"sections": {
"more_options": {
"data": {
"adb_server_ip": "IP address of the ADB server (leave empty to not use)",
"adb_server_port": "Port of the ADB server",
"adbkey": "Path to your ADB key file (leave empty to auto generate)"
},
"name": "More options"
}
}
}
}
@@ -41,8 +41,9 @@ async def async_setup_entry(
# The Android TV is hard reset or the certificate and key files were deleted.
raise ConfigEntryAuthFailed from exc
except (CannotConnect, ConnectionClosed, TimeoutError) as exc:
# The Android TV is network unreachable. Raise exception and let Home Assistant retry
# later. If device gets a new IP address the zeroconf flow will update the config.
# The Android TV is network unreachable. Raise exception and
# let Home Assistant retry later. If device gets a new IP
# address the zeroconf flow will update the config.
raise ConfigEntryNotReady from exc
def reauth_needed() -> None:
@@ -107,7 +107,10 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def _async_start_pair(self) -> ConfigFlowResult:
"""Start pairing with the Android TV. Navigate to the pair flow to enter the PIN shown on screen."""
"""Start pairing with the Android TV.
Navigate to the pair flow to enter the PIN shown on screen.
"""
self.api = create_api(self.hass, self.host, enable_ime=False)
await self.api.async_generate_cert_if_missing()
await self.api.async_start_pairing()
@@ -135,9 +138,10 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
return await self._async_start_pair()
except CannotConnect, ConnectionClosed:
# Device doesn't respond to the specified host. Abort.
# If we are in the user flow we could go back to the user step to allow
# them to enter a new IP address but we cannot do that for the zeroconf
# flow. Simpler to abort for both flows.
# If we are in the user flow we could go back
# to the user step to allow them to enter a
# new IP address but we cannot do that for the
# zeroconf flow. Simpler to abort for both.
return self.async_abort(reason="cannot_connect")
else:
if self.source == SOURCE_REAUTH:
@@ -42,7 +42,7 @@ class AndroidTVRemoteBaseEntity(Entity):
@callback
def _is_available_updated(self, is_available: bool) -> None:
"""Update the state when the device is ready to receive commands or is unavailable."""
"""Update the state when the device is ready or unavailable."""
self._attr_available = is_available
self.async_write_ha_state()
@@ -65,7 +65,8 @@ class AndroidTVRemoteBaseEntity(Entity):
def _send_key_command(self, key_code: str, direction: str = "SHORT") -> None:
"""Send a key press to Android TV.
This does not block; it buffers the data and arranges for it to be sent out asynchronously.
This does not block; it buffers the data and arranges
for it to be sent out asynchronously.
"""
try:
self._api.send_key_command(key_code, direction)
@@ -77,7 +78,8 @@ class AndroidTVRemoteBaseEntity(Entity):
def _send_launch_app_command(self, app_link: str) -> None:
"""Launch an app on Android TV.
This does not block; it buffers the data and arranges for it to be sent out asynchronously.
This does not block; it buffers the data and arranges
for it to be sent out asynchronously.
"""
try:
self._api.send_launch_app_command(app_link)
@@ -95,8 +95,10 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
if not meter.readings or len(meter.readings) == 0:
_LOGGER.debug("No recent usage statistics found, skipping update")
continue
# Anglian Water stats are hourly, the read_at time is the time that the meter took the reading
# We remove 1 hour from this so that the data is shown in the correct hour on the dashboards
# Anglian Water stats are hourly, the read_at time
# is the time that the meter took the reading.
# We remove 1 hour from this so that the data is
# shown in the correct hour on the dashboards
parsed_read_at = dt_util.parse_datetime(meter.readings[0]["read_at"])
if not parsed_read_at:
_LOGGER.debug(
@@ -130,8 +132,9 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
if not stats or not stats.get(usage_statistic_id):
_LOGGER.debug(
"Could not find existing statistics during period lookup for %s, "
"falling back to last stored statistic",
"Could not find existing statistics during"
" period lookup for %s, falling back to"
" last stored statistic",
usage_statistic_id,
)
allow_update_last_stored_hour = True
+2 -1
View File
@@ -43,7 +43,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: AnovaConfigEntry) -> boo
except NoDevicesFound as err:
# Can later setup successfully and spawn a repair.
raise ConfigEntryNotReady(
"No devices were found on the websocket, perhaps you don't have any devices on this account?"
"No devices were found on the websocket, perhaps you"
" don't have any devices on this account?"
) from err
except WebsocketFailure as err:
raise ConfigEntryNotReady("Failed connecting to the websocket.") from err
@@ -546,7 +546,9 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
{
vol.Optional(
CONF_WEB_SEARCH_CITY,
description="Free text input for the city, e.g. `San Francisco`",
description=(
"Free text input for the city, e.g. `San Francisco`"
),
): str,
vol.Optional(
CONF_WEB_SEARCH_REGION,
@@ -34,7 +34,7 @@ def model_alias(model_id: str) -> str:
class AnthropicCoordinator(DataUpdateCoordinator[list[anthropic.types.ModelInfo]]):
"""DataUpdateCoordinator which uses different intervals after successful and unsuccessful updates."""
"""Coordinator using different intervals after success and failure."""
client: anthropic.AsyncAnthropic
+10 -5
View File
@@ -452,7 +452,8 @@ def _convert_content( # noqa: C901
# If there is only one text block, simplify the content to a string
messages[-1]["content"] = messages[-1]["content"][0]["text"]
else:
# Note: We don't pass SystemContent here as it's passed to the API as the prompt
# Note: We don't pass SystemContent here as it's
# passed to the API as the prompt
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unexpected_chat_log_content",
@@ -467,7 +468,8 @@ class AnthropicDeltaStream:
A typical stream of responses might look something like the following:
- RawMessageStartEvent with no content
- RawContentBlockStartEvent with an empty ThinkingBlock (if extended thinking is enabled)
- RawContentBlockStartEvent with an empty ThinkingBlock
(if extended thinking is enabled)
- RawContentBlockDeltaEvent with a ThinkingDelta
- RawContentBlockDeltaEvent with a ThinkingDelta
- RawContentBlockDeltaEvent with a ThinkingDelta
@@ -646,7 +648,8 @@ class AnthropicDeltaStream:
def on_text_block(self, text: str, citations: list[TextCitation] | None) -> None:
"""Handle TextBlock."""
if ( # Do not start a new assistant content just for citations, concatenate consecutive blocks with citations instead.
if ( # Do not start a new assistant content just for
# citations, concatenate consecutive blocks instead.
self._first_block
or (
not self._content_details.has_citations()
@@ -977,7 +980,8 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
]
if options[CONF_CODE_EXECUTION]:
# The `web_search_20260209` tool automatically enables `code_execution_20260120` tool
# The `web_search_20260209` tool automatically enables
# `code_execution_20260120` tool
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
@@ -1159,7 +1163,8 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
)
cast(list[MessageParam], model_args["messages"]).extend(new_messages)
except anthropic.AuthenticationError as err:
# Trigger coordinator to confirm the auth failure and trigger the reauth flow.
# Trigger coordinator to confirm the auth failure
# and trigger the reauth flow.
await coordinator.async_request_refresh()
raise HomeAssistantError(
translation_domain=DOMAIN,
@@ -7,8 +7,7 @@ import anthropic
from anthropic.resources.messages.messages import DEPRECATED_MODELS
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.repairs import RepairsFlow
from homeassistant.components.repairs import RepairsFlow, RepairsFlowResult
from homeassistant.config_entries import ConfigEntryState, ConfigSubentry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
@@ -41,9 +40,7 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
self._current_subentry_id = None
self._model_list_cache = None
async def async_step_init(
self, user_input: dict[str, str]
) -> data_entry_flow.FlowResult:
async def async_step_init(self, user_input: dict[str, str]) -> RepairsFlowResult:
"""Handle the steps of a fix flow."""
if user_input.get(CONF_CHAT_MODEL):
self._async_update_current_subentry(user_input)
+4 -1
View File
@@ -45,7 +45,10 @@ class AOSmithHotWaterPlusSelectEntity(AOSmithStatusEntity, SelectEntity):
@property
def suggested_object_id(self) -> str | None:
"""Override the suggested object id to make '+' get converted to 'plus' in the entity id."""
"""Override the suggested object id.
Makes '+' get converted to 'plus' in the entity id.
"""
return "hot_water_plus_level"
@property
@@ -54,7 +54,8 @@ class OnlineStatus(APCUPSdEntity, BinarySensorEntity):
"""Returns true if the UPS is online."""
# Check if ONLINE bit is set in STATFLAG.
key = self.entity_description.key.upper()
# The daemon could either report just a hex ("0x05000008"), or a hex with a "Status Flag"
# The daemon could either report just a hex
# ("0x05000008"), or a hex with a "Status Flag"
# suffix ("0x05000008 Status Flag") in older versions.
# Here we trim the suffix if it exists to support both.
flag = self.coordinator.data[key].removesuffix(" Status Flag")
+2 -1
View File
@@ -8,7 +8,8 @@ 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
# Mapping of deprecated sensor keys (as reported by apcupsd,
# lower-cased) to their deprecation
# repair issue translation keys.
DEPRECATED_SENSORS: Final = {
"apc": "apc_deprecated",
@@ -27,7 +27,7 @@ type APCUPSdConfigEntry = ConfigEntry[APCUPSdCoordinator]
class APCUPSdData(dict[str, str]):
"""Store data about an APCUPSd and provide a few helper methods for easier accesses."""
"""Store data about an APCUPSd and provide helper methods."""
@property
def name(self) -> str | None:
@@ -45,8 +45,9 @@ class APCUPSdData(dict[str, str]):
def serial_no(self) -> str | None:
"""Return the unique serial number of the UPS, if available."""
sn = self.get("SERIALNO")
# We had user reports that some UPS models simply return "Blank" as serial number, in
# which case we fall back to `None` to indicate that it is actually not available.
# We had user reports that some UPS models simply return
# "Blank" as serial number, in which case we fall back to
# `None` to indicate that it is actually not available.
return None if sn == "Blank" else sn
@@ -85,7 +86,11 @@ class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):
@property
def unique_device_id(self) -> str:
"""Return a unique ID of the device, which is the serial number (if available) or the config entry ID."""
"""Return a unique ID of the device.
Uses the serial number if available, otherwise the
config entry ID.
"""
return self.data.serial_no or self.config_entry.entry_id
@property
+20 -12
View File
@@ -473,13 +473,16 @@ async def async_setup_entry(
entities = []
# "laststest" is a special sensor that only appears when the APC UPS daemon has done a
# periodical (or manual) self test since last daemon restart. It might not be available
# when we set up the integration, and we do not know if it would ever be available. Here we
# add it anyway and mark it as unknown initially.
# "laststest" is a special sensor that only appears when
# the APC UPS daemon has done a periodical (or manual) self
# test since last daemon restart. It might not be available
# when we set up the integration, and we do not know if it
# would ever be available. Here we add it anyway and mark it
# 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.
# 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.
for resource in sorted(available_resources | {LAST_S_TEST}):
if resource not in SENSORS:
_LOGGER.warning("Invalid resource from APCUPSd: %s", resource.upper())
@@ -527,9 +530,11 @@ class APCUPSdSensor(APCUPSdEntity, SensorEntity):
def _update_attrs(self) -> None:
"""Update sensor attributes based on coordinator data."""
key = self.entity_description.key.upper()
# For most sensors the key will always be available for each refresh. However, some sensors
# (e.g., "laststest") will only appear after certain event occurs (e.g., a self test is
# performed) and may disappear again after certain event. So we mark the state as "unknown"
# For most sensors the key will always be available for
# each refresh. However, some sensors (e.g., "laststest")
# will only appear after certain event occurs (e.g., a
# self test is performed) and may disappear again after
# certain event. So we mark the state as "unknown"
# when it becomes unknown after such events.
if key not in self.coordinator.data:
self._attr_native_value = None
@@ -538,7 +543,8 @@ class APCUPSdSensor(APCUPSdEntity, SensorEntity):
data = self.coordinator.data[key]
if self.entity_description.device_class == SensorDeviceClass.TIMESTAMP:
# The date could be "N/A" for certain fields (e.g., XOFFBATT), indicating there is no value yet.
# The date could be "N/A" for certain fields
# (e.g., XOFFBATT), indicating there is no value yet.
if data == "N/A":
self._attr_native_value = None
return
@@ -546,7 +552,8 @@ class APCUPSdSensor(APCUPSdEntity, SensorEntity):
try:
self._attr_native_value = dateutil.parser.parse(data)
except dateutil.parser.ParserError, OverflowError:
# If parsing fails we should mark it as unknown, with a log for further debugging.
# If parsing fails we should mark it as unknown,
# with a log for further debugging.
_LOGGER.warning('Failed to parse date for %s: "%s"', key, data)
self._attr_native_value = None
return
@@ -578,7 +585,8 @@ class APCUPSdSensor(APCUPSdEntity, SensorEntity):
entity_registry = er.async_get(self.hass)
items = [
f"- [{entry.name or entry.original_name or entity_id}]"
f"(/config/{integration}/edit/{entry.unique_id or entity_id.split('.', 1)[-1]})"
f"(/config/{integration}/edit/"
f"{entry.unique_id or entity_id.split('.', 1)[-1]})"
for integration, entities in (
("automation", automations),
("script", scripts),
+2 -1
View File
@@ -406,7 +406,8 @@ class APIDomainServicesView(HomeAssistantView):
is ha.SupportsResponse.NONE
):
return self.json_message(
"Service does not support responses. Remove return_response from request.",
"Service does not support responses."
" Remove return_response from request.",
HTTPStatus.BAD_REQUEST,
)
elif (
@@ -300,8 +300,10 @@ class AppleTVManager(DeviceListener):
config_entry.title,
address,
)
# We no longer multicast scan for the device since as soon as async_step_zeroconf runs,
# it will update the address and reload the config entry when the device is found.
# We no longer multicast scan for the device since as
# soon as async_step_zeroconf runs, it will update the
# address and reload the config entry when the device
# is found.
return None
async def _connect(self, conf: AppleTV, raise_missing_credentials: bool) -> None:
@@ -463,7 +463,8 @@ class AppleTvMediaPlayer(
"""Implement the websocket media browsing helper."""
if media_content_id == "apps" or (
# If we can't stream files or URLs, we can't browse media.
# In that case the `BROWSE_MEDIA` feature was added because of AppList/LaunchApp
# In that case the `BROWSE_MEDIA` feature was added
# because of AppList/LaunchApp
not self._is_feature_available(FeatureName.PlayUrl)
and not self._is_feature_available(FeatureName.StreamFile)
):
@@ -18,10 +18,10 @@ from homeassistant.components.media_player import (
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import EVENT_TURN_ON
from .const import DOMAIN, EVENT_TURN_ON
from .coordinator import ArcamFmjConfigEntry, ArcamFmjCoordinator
from .entity import ArcamFmjEntity
@@ -52,7 +52,7 @@ def convert_exception[**_P, _R](
return await func(*args, **kwargs)
except ConnectionFailed as exception:
raise HomeAssistantError(
f"Connection failed to device during {func}"
translation_domain=DOMAIN, translation_key="connection_failed"
) from exception
return _convert_exception
@@ -96,9 +96,12 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
"""Select a specific source."""
try:
value = SourceCodes[source]
except KeyError:
_LOGGER.error("Unsupported source %s", source)
return
except KeyError as exception:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="unsupported_source",
translation_placeholders={"source": source},
) from exception
await self._state.set_source(value)
self.async_write_ha_state()
@@ -109,8 +112,10 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
try:
await self._state.set_decode_mode(sound_mode)
except (KeyError, ValueError) as exception:
raise HomeAssistantError(
f"Unsupported sound_mode {sound_mode}"
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="unsupported_sound_mode",
translation_placeholders={"sound_mode": sound_mode},
) from exception
self.async_write_ha_state()
@@ -193,8 +198,11 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
preset = int(media_id[7:])
await self._state.set_tuner_preset(preset)
else:
_LOGGER.error("Media %s is not supported", media_id)
return
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="unsupported_media",
translation_placeholders={"media": media_id},
)
@property
def source(self) -> str | None:
@@ -139,5 +139,19 @@
"name": "Incoming video vertical resolution"
}
}
},
"exceptions": {
"connection_failed": {
"message": "Connection failed to the device."
},
"unsupported_media": {
"message": "Unsupported media: {media}."
},
"unsupported_sound_mode": {
"message": "Unsupported sound mode: {sound_mode}."
},
"unsupported_source": {
"message": "Unsupported source: {source}."
}
}
}
@@ -44,7 +44,10 @@ class SpeechToTextError(PipelineError):
class DuplicateWakeUpDetectedError(WakeWordDetectionError):
"""Error when multiple voice assistants wake up at the same time (same wake word)."""
"""Error when multiple voice assistants wake up at the same time.
Happens when multiple assistants detect the same wake word.
"""
def __init__(self, wake_up_phrase: str) -> None:
"""Set error message."""
@@ -589,7 +589,10 @@ class PipelineRun:
"""Data tied to the conversation ID."""
_intent_agent_only = False
"""If request should only be handled by agent, ignoring sentence triggers and local processing."""
"""If request should only be handled by agent.
Ignores sentence triggers and local processing.
"""
_streamed_response_text = False
"""If the conversation agent streamed response text to TTS result."""
@@ -932,6 +935,7 @@ class PipelineRun:
{
"engine": engine,
"metadata": asdict(metadata),
"audio_processing": asdict(self.stt_provider.audio_processing),
},
)
)
@@ -1045,7 +1049,11 @@ class PipelineRun:
if agent_info is None:
raise IntentRecognitionError(
code="intent-agent-not-found",
message=f"Intent recognition engine {self._conversation_data.continue_conversation_agent} asked for follow-up but is no longer found",
message=(
f"Intent recognition engine"
f" {self._conversation_data.continue_conversation_agent}"
" asked for follow-up but is no longer found"
),
)
self._intent_agent_only = True
@@ -1149,14 +1157,17 @@ class PipelineRun:
nonlocal delta_character_count
# Streamed responses are not cached. That's why we only start streaming text after
# we have received enough characters that indicates it will be a long response
# or if we have received text, and then a tool call.
# Streamed responses are not cached. That's why we
# only start streaming text after we have received
# enough characters that indicates it will be a long
# response or if we have received text, and then a
# tool call.
# Tool call after we already received text
start_streaming = delta_character_count > 0 and delta.get("tool_calls")
# Count characters in the content and test if we exceed streaming threshold
# Count characters in the content and test if we
# exceed streaming threshold
if not start_streaming and content:
delta_character_count += len(content)
start_streaming = delta_character_count > STREAM_RESPONSE_CHARS
@@ -1186,7 +1197,8 @@ class PipelineRun:
parts.append(tts_input_stream.get_nowait())
tts_input_stream.put_nowait(
"".join(
# At this point parts is only strings, None indicates end of queue
# At this point parts is only strings,
# None indicates end of queue
cast(list[str], parts)
)
)
@@ -1427,7 +1439,8 @@ class PipelineRun:
code="tts-not-supported",
message=(
f"Text-to-speech engine {engine} "
f"does not support language {self.pipeline.tts_language} or options {tts_options}:"
f"does not support language {self.pipeline.tts_language}"
f" or options {tts_options}:"
f" {err}"
),
) from err
@@ -1541,7 +1554,10 @@ class PipelineRun:
async def process_volume_only(
self, audio_stream: AsyncIterable[bytes]
) -> AsyncGenerator[EnhancedAudioChunk]:
"""Apply volume transformation only (no VAD/audio enhancements) with optional chunking."""
"""Apply volume transformation only with optional chunking.
No VAD/audio enhancements are applied.
"""
timestamp_ms = 0
async for chunk in audio_stream:
if self.audio_settings.volume_multiplier != 1.0:
@@ -1560,7 +1576,11 @@ class PipelineRun:
async def process_enhance_audio(
self, audio_stream: AsyncIterable[bytes]
) -> AsyncGenerator[EnhancedAudioChunk]:
"""Split audio into chunks and apply VAD/noise suppression/auto gain/volume transformation."""
"""Split audio into chunks and apply audio enhancements.
Applies VAD/noise suppression/auto gain/volume
transformation.
"""
assert self.audio_enhancer is not None
timestamp_ms = 0
@@ -1663,7 +1683,7 @@ class PipelineInput:
"""Identifier of the device that is processing the input/output of the pipeline."""
satellite_id: str | None = None
"""Identifier of the satellite that is processing the input/output of the pipeline."""
"""Identifier of the satellite processing the pipeline."""
async def execute(self, validate: bool = False) -> None:
"""Run pipeline."""
@@ -1725,7 +1745,8 @@ class PipelineInput:
sec_since_last_wake_up = time.monotonic() - last_wake_up
if sec_since_last_wake_up < WAKE_WORD_COOLDOWN:
_LOGGER.debug(
"Speech-to-text cancelled to avoid duplicate wake-up for %s",
"Speech-to-text cancelled to avoid"
" duplicate wake-up for %s",
self.wake_word_phrase,
)
raise DuplicateWakeUpDetectedError(self.wake_word_phrase)
@@ -1738,7 +1759,8 @@ class PipelineInput:
stt_input_stream = stt_processed_stream
if stt_audio_buffer:
# Send audio in the buffer first to speech-to-text, then move on to stt_stream.
# Send audio in the buffer first to speech-to-text,
# then move on to stt_stream.
# This is basically an async itertools.chain.
async def buffer_then_audio_stream() -> AsyncGenerator[
EnhancedAudioChunk
@@ -2042,7 +2064,9 @@ class PipelineStorageCollectionWebsocket(
msg["id"],
{
"pipelines": async_get_pipelines(hass),
"preferred_pipeline": self.storage_collection.async_get_preferred_item(),
"preferred_pipeline": (
self.storage_collection.async_get_preferred_item()
),
},
)
@@ -5,8 +5,7 @@ from typing import cast
import voluptuous as vol
from homeassistant.components.assist_satellite import DOMAIN as ASSIST_SATELLITE_DOMAIN
from homeassistant.components.repairs import RepairsFlow
from homeassistant.data_entry_flow import FlowResult
from homeassistant.components.repairs import RepairsFlow, RepairsFlowResult
from homeassistant.helpers import entity_registry as er
REQUIRED_KEYS = ("entity_id", "entity_uuid", "integration_name")
@@ -21,14 +20,14 @@ class AssistInProgressDeprecatedRepairFlow(RepairsFlow):
raise ValueError("Missing data")
self._data = data
async def async_step_init(self, _: None = None) -> FlowResult:
async def async_step_init(self, _: None = None) -> RepairsFlowResult:
"""Handle the first step of a fix flow."""
return await self.async_step_confirm_disable_entity()
async def async_step_confirm_disable_entity(
self,
user_input: dict[str, str] | None = None,
) -> FlowResult:
) -> RepairsFlowResult:
"""Handle the confirm step of a fix flow."""
if user_input is not None:
entity_registry = er.async_get(self.hass)
@@ -204,7 +204,8 @@ class VoiceCommandSegmenter:
) -> bool:
"""Process an audio chunk using an external VAD.
A buffer is required if the VAD requires fixed-sized audio chunks (usually the case).
A buffer is required if the VAD requires fixed-sized audio
chunks (usually the case).
Returns False when voice command is finished.
"""
@@ -293,7 +294,10 @@ def chunk_samples(
bytes_per_chunk: int,
leftover_chunk_buffer: AudioBuffer,
) -> Iterable[bytes]:
"""Yield fixed-sized chunks from samples, keeping leftover bytes from previous call(s)."""
"""Yield fixed-sized chunks from samples.
Keeps leftover bytes from previous call(s).
"""
if (len(leftover_chunk_buffer) + len(samples)) < bytes_per_chunk:
# Extend leftover chunk, but not enough samples to complete it
@@ -470,7 +470,7 @@ async def websocket_device_capture(
# single sample (16 bits) per queue item.
max_queue_items = (
# +1 for None to signal end
int(math.ceil(timeout_seconds * CAPTURE_RATE)) + 1
math.ceil(timeout_seconds * CAPTURE_RATE) + 1
)
audio_queue = DeviceAudioQueue(queue=asyncio.Queue(maxsize=max_queue_items))
@@ -291,7 +291,8 @@ class AssistSatelliteEntity(entity.Entity):
self._is_announcing = True
self._set_state(AssistSatelliteState.RESPONDING)
# Provide our start info to the LLM so it understands context of incoming message
# Provide our start info to the LLM so it understands
# context of incoming message
if extra_system_prompt is not None:
self._extra_system_prompt = extra_system_prompt
else:
@@ -501,7 +502,8 @@ class AssistSatelliteEntity(entity.Entity):
with chat_session.async_get_chat_session(
self.hass, self._conversation_id
) as session:
# Store the conversation ID. If it is no longer valid, get_chat_session will reset it
# Store the conversation ID. If it is no longer valid,
# get_chat_session will reset it
self._conversation_id = session.conversation_id
self._pipeline_task = (
self.platform.config_entry.async_create_background_task(
+18 -15
View File
@@ -23,6 +23,7 @@ from homeassistant.const import (
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import SectionConfig, section
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.schema_config_entry_flow import (
SchemaCommonFlowHandler,
@@ -30,12 +31,12 @@ from homeassistant.helpers.schema_config_entry_flow import (
SchemaOptionsFlowHandler,
)
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
from homeassistant.helpers.typing import VolDictType
from .bridge import AsusWrtBridge
from .const import (
CONF_DNSMASQ,
CONF_INTERFACE,
CONF_MORE_OPTIONS,
CONF_REQUIRE_IP,
CONF_SSH_KEY,
CONF_TRACK_UNKNOWN,
@@ -58,9 +59,6 @@ ALLOWED_PROTOCOL = [
PROTOCOL_TELNET,
]
PASS_KEY = "pass_key"
PASS_KEY_MSG = "Only provide password or SSH key file"
RESULT_CONN_ERROR = "cannot_connect"
RESULT_SUCCESS = "success"
RESULT_UNKNOWN = "unknown"
@@ -142,20 +140,10 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN):
user_input = self._config_data
add_schema: VolDictType
if self.show_advanced_options:
add_schema = {
vol.Exclusive(CONF_PASSWORD, PASS_KEY, PASS_KEY_MSG): str,
vol.Optional(CONF_PORT): cv.port,
vol.Exclusive(CONF_SSH_KEY, PASS_KEY, PASS_KEY_MSG): str,
}
else:
add_schema = {vol.Required(CONF_PASSWORD): str}
schema = {
vol.Required(CONF_HOST, default=user_input.get(CONF_HOST, "")): str,
vol.Required(CONF_USERNAME, default=user_input.get(CONF_USERNAME, "")): str,
**add_schema,
vol.Optional(CONF_PASSWORD): str,
vol.Required(
CONF_PROTOCOL,
default=user_input.get(CONF_PROTOCOL, PROTOCOL_HTTPS),
@@ -164,6 +152,15 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN):
options=ALLOWED_PROTOCOL, translation_key="protocols"
)
),
vol.Required(CONF_MORE_OPTIONS): section(
vol.Schema(
{
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_SSH_KEY): str,
}
),
SectionConfig(collapsed=True),
),
}
return self.async_show_form(
@@ -238,6 +235,10 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN):
if user_input is None:
return self._show_setup_form()
user_input = user_input.copy()
more_options = user_input.pop(CONF_MORE_OPTIONS, {})
user_input.update(more_options)
self._config_data = user_input
pwd: str | None = user_input.get(CONF_PASSWORD)
ssh: str | None = user_input.get(CONF_SSH_KEY)
@@ -247,6 +248,8 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN):
return self._show_setup_form(error="pwd_required")
if not (pwd or ssh):
return self._show_setup_form(error="pwd_or_ssh")
if pwd and ssh:
return self._show_setup_form(error="pwd_and_ssh")
if ssh and not await self.hass.async_add_executor_job(_is_file, ssh):
return self._show_setup_form(error="ssh_not_file")
@@ -4,6 +4,7 @@ DOMAIN = "asuswrt"
CONF_DNSMASQ = "dnsmasq"
CONF_INTERFACE = "interface"
CONF_MORE_OPTIONS = "more_options"
CONF_REQUIRE_IP = "require_ip"
CONF_SSH_KEY = "ssh_key"
CONF_TRACK_UNKNOWN = "track_unknown"
+11 -3
View File
@@ -7,6 +7,7 @@
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_host": "[%key:common::config_flow::error::invalid_host%]",
"pwd_and_ssh": "Please provide either password or SSH key file, not both",
"pwd_or_ssh": "Please provide password or SSH key file",
"pwd_required": "Password is required for selected protocol",
"ssh_not_file": "SSH key file not found",
@@ -23,15 +24,22 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]",
"port": "Port (leave empty for protocol default)",
"protocol": "Communication protocol to use",
"ssh_key": "Path to your SSH key file (instead of password)",
"username": "[%key:common::config_flow::data::username%]"
},
"data_description": {
"host": "The hostname or IP address of your ASUSWRT router."
},
"description": "Set required parameter to connect to your router"
"description": "Set required parameter to connect to your router",
"sections": {
"more_options": {
"data": {
"port": "Port (leave empty for protocol default)",
"ssh_key": "Path to your SSH key file (instead of password)"
},
"name": "More options"
}
}
}
}
},
+1 -1
View File
@@ -75,7 +75,7 @@ class AtagThermostat(AtagEntity, ClimateEntity):
@property
def preset_mode(self) -> str | None:
"""Return the current preset mode, e.g., auto, manual, fireplace, extend, etc."""
"""Return the current preset mode, e.g., auto, manual."""
preset = self.coordinator.atag.climate.preset_mode
return PRESET_INVERTED.get(preset)
@@ -54,7 +54,7 @@ class AtagWaterHeater(AtagEntity, WaterHeaterEntity):
@property
def target_temperature(self) -> float:
"""Return the setpoint if water demand, otherwise return base temp (comfort level)."""
"""Return the setpoint if water demand, otherwise base temp."""
return self.coordinator.atag.dhw.target_temperature
@property
@@ -164,7 +164,7 @@ class AugustDoorbellBinarySensor(AugustDescriptionEntity, BinarySensorEntity):
self.async_write_ha_state()
def _schedule_update_to_recheck_turn_off_sensor(self) -> None:
"""Schedule an update to recheck the sensor to see if it is ready to turn off."""
"""Schedule an update to recheck if sensor is ready to turn off."""
# If the sensor is already off there is nothing to do
if not self.is_on:
return
+4 -1
View File
@@ -129,7 +129,10 @@ class AugustLock(AugustEntity, RestoreEntity, LockEntity):
)
async def async_added_to_hass(self) -> None:
"""Restore ATTR_CHANGED_BY on startup since it is likely no longer in the activity log."""
"""Restore ATTR_CHANGED_BY on startup.
It is likely no longer in the activity log.
"""
await super().async_added_to_hass()
if not (last_state := await self.async_get_last_state()):
+4 -1
View File
@@ -167,7 +167,10 @@ class AugustOperatorSensor(AugustEntity, RestoreSensor):
return attributes
async def async_added_to_hass(self) -> None:
"""Restore ATTR_CHANGED_BY on startup since it is likely no longer in the activity log."""
"""Restore ATTR_CHANGED_BY on startup.
It is likely no longer in the activity log.
"""
await super().async_added_to_hass()
last_state = await self.async_get_last_state()
@@ -20,10 +20,16 @@ async def async_get_config_entry_diagnostics(
"name": coordinator.account_site.system_name,
"health": coordinator.account_site.health,
"solar": {
"power_production": coordinator.data.solar.power_production,
"energy_production_today": coordinator.data.solar.energy_production_today,
"energy_production_month": coordinator.data.solar.energy_production_month,
"energy_production_total": coordinator.data.solar.energy_production_total,
"power_production": (coordinator.data.solar.power_production),
"energy_production_today": (
coordinator.data.solar.energy_production_today
),
"energy_production_month": (
coordinator.data.solar.energy_production_month
),
"energy_production_total": (
coordinator.data.solar.energy_production_total
),
},
"inverters": [
{
@@ -41,9 +47,15 @@ async def async_get_config_entry_diagnostics(
"flow_now": coordinator.data.battery.flow_now,
"net_charged_now": coordinator.data.battery.net_charged_now,
"state_of_charge": coordinator.data.battery.state_of_charge,
"discharged_today": coordinator.data.battery.discharged_today,
"discharged_month": coordinator.data.battery.discharged_month,
"discharged_total": coordinator.data.battery.discharged_total,
"discharged_today": (
coordinator.data.battery.discharged_today
),
"discharged_month": (
coordinator.data.battery.discharged_month
),
"discharged_total": (
coordinator.data.battery.discharged_total
),
"charged_today": coordinator.data.battery.charged_today,
"charged_month": coordinator.data.battery.charged_month,
"charged_total": coordinator.data.battery.charged_total,
+4 -2
View File
@@ -52,7 +52,8 @@ flow for details.
Progress the flow. Most flows will be 1 page, but could optionally add extra
login challenges, like TFA. Once the flow has finished, the returned step will
have type FlowResultType.CREATE_ENTRY and "result" key will contain an authorization code.
have type FlowResultType.CREATE_ENTRY and "result" key will contain
an authorization code.
The authorization code associated with an authorized user by default, it will
associate with an credential if "type" set to "link_user" in
"/auth/login_flow"
@@ -226,7 +227,8 @@ class AuthProvidersView(HomeAssistantView):
remote_address
)
except InvalidAuthError:
# Not a trusted network, so we don't expose that trusted_network authenticator is setup
# Not a trusted network, so we don't expose that
# trusted_network authenticator is setup
continue
providers.append(
@@ -1155,7 +1155,7 @@ async def _async_process_config(
automations: list[BaseAutomationEntity],
automation_configs: list[AutomationEntityConfig],
) -> tuple[set[int], set[int]]:
"""Find matches between a list of automation entities and a list of configurations.
"""Find matches between automation entities and configurations.
An automation or configuration is only allowed to match at most once to handle
the case of multiple automations with identical configuration.
+33 -1
View File
@@ -1 +1,33 @@
"""The avea component."""
"""The Avea integration."""
import avea
from homeassistant.components.bluetooth import async_ble_device_from_address
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ADDRESS, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
type AveaConfigEntry = ConfigEntry[avea.Bulb]
PLATFORMS: list[Platform] = [Platform.LIGHT]
async def async_setup_entry(hass: HomeAssistant, entry: AveaConfigEntry) -> bool:
"""Set up Avea from a config entry."""
ble_device = async_ble_device_from_address(
hass, entry.data[CONF_ADDRESS], connectable=True
)
if not ble_device:
raise ConfigEntryNotReady(
f"Could not find Avea device with address {entry.data[CONF_ADDRESS]}"
)
entry.runtime_data = avea.Bulb(ble_device)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AveaConfigEntry) -> bool:
"""Unload an Avea config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@@ -0,0 +1,211 @@
"""Config flow for Avea."""
from contextlib import suppress
import logging
from typing import Any
import avea
from bleak.exc import BleakError
import voluptuous as vol
from homeassistant.components.bluetooth import (
BluetoothServiceInfoBleak,
async_discovered_service_info,
)
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ADDRESS, CONF_NAME
from .const import AVEA_SERVICE_UUID, DOMAIN, UNKNOWN_NAME
_LOGGER = logging.getLogger(__name__)
def _normalize_name(name: str | None) -> str | None:
"""Return a valid Avea name."""
if not name or name == UNKNOWN_NAME:
return None
return name
def _validate_device(discovery_info: BluetoothServiceInfoBleak) -> str:
"""Validate the device is reachable and return a title for it."""
bulb = avea.Bulb(discovery_info.device)
try:
if not bulb.connect():
raise CannotConnect
try:
name = bulb.get_name()
except BleakError, OSError, RuntimeError:
_LOGGER.debug(
"Failed to get name for Avea device %s",
discovery_info.address,
exc_info=True,
)
name = None
brightness = bulb.get_brightness()
except (BleakError, OSError, RuntimeError) as err:
raise CannotConnect from err
finally:
with suppress(BleakError, OSError, RuntimeError):
bulb.close()
if brightness is None:
raise CannotConnect
return (
_normalize_name(name)
or _normalize_name(discovery_info.name)
or discovery_info.address
)
def _is_avea_discovery(discovery_info: BluetoothServiceInfoBleak) -> bool:
"""Return if the bluetooth discovery matches an Avea bulb."""
return AVEA_SERVICE_UUID in discovery_info.service_uuids
class AveaConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Avea."""
def __init__(self) -> None:
"""Initialize the config flow."""
self._discovery_info: BluetoothServiceInfoBleak | None = None
self._discovered_devices: dict[str, BluetoothServiceInfoBleak] = {}
async def async_step_bluetooth(
self, discovery_info: BluetoothServiceInfoBleak
) -> ConfigFlowResult:
"""Handle the bluetooth discovery step."""
await self.async_set_unique_id(discovery_info.address)
self._abort_if_unique_id_configured()
self._discovery_info = discovery_info
self.context["title_placeholders"] = {
"name": discovery_info.name or discovery_info.address
}
return await self.async_step_bluetooth_confirm()
async def async_step_bluetooth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm the discovered device before creating the entry."""
assert self._discovery_info is not None
errors: dict[str, str] = {}
if user_input is not None:
try:
title = await self.hass.async_add_executor_job(
_validate_device, self._discovery_info
)
except CannotConnect:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected error while validating Avea device")
errors["base"] = "unknown"
else:
return self.async_create_entry(
title=title,
data={CONF_ADDRESS: self._discovery_info.address},
)
self.context["title_placeholders"] = {
"name": self._discovery_info.name or self._discovery_info.address
}
self._set_confirm_only()
return self.async_show_form(
step_id="bluetooth_confirm",
description_placeholders=self.context["title_placeholders"],
errors=errors,
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the user step to pick a discovered device."""
errors: dict[str, str] = {}
if user_input is not None:
address = user_input[CONF_ADDRESS]
discovery_info = self._discovered_devices[address]
await self.async_set_unique_id(address, raise_on_progress=False)
self._abort_if_unique_id_configured()
try:
title = await self.hass.async_add_executor_job(
_validate_device, discovery_info
)
except CannotConnect:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected error while validating Avea device")
errors["base"] = "unknown"
else:
return self.async_create_entry(
title=title,
data={CONF_ADDRESS: address},
)
if discovery := self._discovery_info:
self._discovered_devices[discovery.address] = discovery
else:
current_addresses = self._async_current_ids(include_ignore=False)
for discovery in async_discovered_service_info(self.hass):
if (
discovery.address in current_addresses
or discovery.address in self._discovered_devices
or not _is_avea_discovery(discovery)
):
continue
self._discovered_devices[discovery.address] = discovery
if not self._discovered_devices:
return self.async_abort(reason="no_devices_found")
if self._discovery_info:
disc = self._discovery_info
label = f"{disc.name or disc.address} ({disc.address})"
data_schema = vol.Schema(
{
vol.Required(CONF_ADDRESS, default=disc.address): vol.In(
{disc.address: label}
)
}
)
else:
data_schema = vol.Schema(
{
vol.Required(CONF_ADDRESS): vol.In(
{
service_info.address: (
f"{service_info.name or service_info.address}"
f" ({service_info.address})"
)
for service_info in self._discovered_devices.values()
}
),
}
)
return self.async_show_form(
step_id="user",
data_schema=data_schema,
errors=errors,
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Handle import from YAML."""
address = import_data[CONF_ADDRESS]
await self.async_set_unique_id(address, raise_on_progress=False)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=import_data.get(CONF_NAME, address),
data={CONF_ADDRESS: address},
)
class CannotConnect(Exception):
"""Error to indicate an Avea device cannot be connected to."""
+8
View File
@@ -0,0 +1,8 @@
"""Constants for the Avea integration."""
DOMAIN = "avea"
INTEGRATION_TITLE = "Elgato Avea"
MANUFACTURER = "Elgato"
MODEL = "Avea"
AVEA_SERVICE_UUID = "f815e810-456c-6761-746f-4d756e696368"
UNKNOWN_NAME = "Unknown"
+143 -18
View File
@@ -1,8 +1,11 @@
"""Support for the Elgato Avea lights."""
"""Light platform for Avea."""
from contextlib import suppress
import logging
from typing import Any
import avea
from bleak.exc import BleakError
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
@@ -10,29 +13,154 @@ from homeassistant.components.light import (
ColorMode,
LightEntity,
)
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import CONF_ADDRESS, CONF_NAME
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
)
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
def setup_platform(
_LOGGER = logging.getLogger(__name__)
UPDATE_EXCEPTIONS = (BleakError, OSError, RuntimeError)
BREAKS_IN_HA_VERSION = "2026.12.0"
def _normalize_name(name: str | None) -> str | None:
"""Return a valid Avea name."""
if not name or name == UNKNOWN_NAME:
return None
return name
def _create_deprecated_yaml_issue(hass: HomeAssistant) -> None:
"""Create the deprecated YAML issue for Avea."""
ir.async_create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_yaml_{DOMAIN}",
breaks_in_ha_version=BREAKS_IN_HA_VERSION,
is_fixable=False,
is_persistent=False,
issue_domain=DOMAIN,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_yaml",
translation_placeholders={
"domain": DOMAIN,
"integration_title": INTEGRATION_TITLE,
},
)
def _create_yaml_import_failed_issue(hass: HomeAssistant) -> None:
"""Create a repair issue when the Avea YAML import cannot find bulbs."""
ir.async_create_issue(
hass,
DOMAIN,
"deprecated_yaml_import_issue_no_bulbs",
breaks_in_ha_version=BREAKS_IN_HA_VERSION,
is_fixable=False,
issue_domain=DOMAIN,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_yaml_import_issue_no_bulbs",
translation_placeholders={
"domain": DOMAIN,
"integration_title": INTEGRATION_TITLE,
},
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AveaConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Avea light platform."""
async_add_entities([AveaLight(entry.runtime_data)], update_before_add=True)
def _discover_bulbs_for_import() -> list[dict[str, str]]:
"""Discover and validate Avea bulbs for YAML import."""
discovered_bulbs: list[dict[str, str]] = []
for bulb in avea.discover_avea_bulbs():
address = bulb.addr
try:
name = bulb.get_name()
brightness = bulb.get_brightness()
except UPDATE_EXCEPTIONS as err:
_LOGGER.warning(
"Skipping Avea bulb %s during YAML import due to read failure: %s",
address,
err,
)
continue
finally:
with suppress(*UPDATE_EXCEPTIONS):
bulb.close()
if brightness is None:
_LOGGER.warning(
"Skipping Avea bulb %s during YAML import due to"
" read failure: brightness is None",
address,
)
continue
discovered_bulbs.append(
{
CONF_ADDRESS: address,
CONF_NAME: _normalize_name(name)
or _normalize_name(bulb.name)
or address,
}
)
return discovered_bulbs
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Avea platform."""
"""Import the Avea YAML platform into config entries."""
try:
nearby_bulbs = avea.discover_avea_bulbs()
for bulb in nearby_bulbs:
bulb.get_name()
bulb.get_brightness()
except OSError as err:
raise PlatformNotReady from err
bulbs = await hass.async_add_executor_job(_discover_bulbs_for_import)
except UPDATE_EXCEPTIONS as err:
raise PlatformNotReady("Could not discover Avea bulbs for YAML import") from err
add_entities(AveaLight(bulb) for bulb in nearby_bulbs)
if not bulbs:
_create_yaml_import_failed_issue(hass)
for bulb in bulbs:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=bulb,
)
if (
result.get("type") is FlowResultType.ABORT
and result.get("reason") != "already_configured"
):
_LOGGER.warning(
"Skipping Avea YAML import for bulb %s: %s",
bulb[CONF_ADDRESS],
result.get("reason"),
)
continue
_create_deprecated_yaml_issue(hass)
class AveaLight(LightEntity):
@@ -41,7 +169,7 @@ class AveaLight(LightEntity):
_attr_color_mode = ColorMode.HS
_attr_supported_color_modes = {ColorMode.HS}
def __init__(self, light):
def __init__(self, light: avea.Bulb) -> None:
"""Initialize an AveaLight."""
self._light = light
self._attr_name = light.name
@@ -64,10 +192,7 @@ class AveaLight(LightEntity):
self._light.set_brightness(0)
def update(self) -> None:
"""Fetch new state data for this light.
This is the only method that should fetch new data for Home Assistant.
"""
"""Fetch new state data for this light."""
if (brightness := self._light.get_brightness()) is not None:
self._attr_is_on = brightness != 0
self._attr_brightness = round(255 * (brightness / 4095))
+9 -1
View File
@@ -1,10 +1,18 @@
{
"domain": "avea",
"name": "Elgato Avea",
"bluetooth": [
{
"local_name": "Avea*",
"service_uuid": "f815e810-456c-6761-746f-4d756e696368"
}
],
"codeowners": ["@pattyland"],
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/avea",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["avea"],
"quality_scale": "legacy",
"requirements": ["avea==1.6.1"]
}
@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
},
"user": {
"data": {
"address": "[%key:common::config_flow::data::device%]"
},
"description": "[%key:component::bluetooth::config::step::user::description%]"
}
}
},
"issues": {
"deprecated_yaml": {
"description": "[%key:component::homeassistant::issues::deprecated_yaml::description%]",
"title": "[%key:component::homeassistant::issues::deprecated_yaml::title%]"
},
"deprecated_yaml_import_issue_no_bulbs": {
"description": "Configuring {integration_title} using YAML is deprecated and will be removed in a future release. While importing your YAML configuration, Home Assistant could not discover any Avea bulbs. Make sure the bulbs are powered on, nearby, and reachable over Bluetooth, then restart Home Assistant. If you no longer use the YAML configuration, remove the `{domain}` entry from your `configuration.yaml` file.",
"title": "Avea YAML configuration import failed"
}
}
}
+3 -2
View File
@@ -242,8 +242,9 @@ class S3BackupAgent(BackupAgent):
finally:
view.release()
# Compact the buffer if the consumed offset has grown large enough. This
# avoids unnecessary memory copies when compacting after every part upload.
# Compact the buffer if the consumed offset has grown
# large enough. This avoids unnecessary memory copies
# when compacting after every part upload.
if offset and offset >= MULTIPART_MIN_PART_SIZE_BYTES:
buffer = bytearray(buffer[offset:])
offset = 0
+1 -1
View File
@@ -29,7 +29,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["axis"],
"requirements": ["axis==69"],
"requirements": ["axis==71"],
"ssdp": [
{
"manufacturer": "AXIS"

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