Compare commits

...

935 Commits

Author SHA1 Message Date
epenet 7b2e4dbe16 Ruff 2025-10-17 13:46:52 +00:00
epenet 1f812e9f89 Fix motor reverse mode logic 2025-10-17 14:54:20 +02:00
johanzander b182d5ce87 Add additional unit tests for Growatt Server integration (#154644) 2025-10-17 14:22:16 +02:00
Thomas55555 175365bdea Add integration_type to Husqvarna Automower (#154642) 2025-10-17 14:18:32 +02:00
Bouwe Westerdijk cbe52cbfca Bump plugwise to v1.8.1 (#154679) 2025-10-17 15:13:35 +03:00
Felipe Santos 9251dde2c6 Add OpenRGB reconfiguration flow (#154478) 2025-10-17 12:27:11 +02:00
Andrew Jackson 24d77cc453 Bump aiomealie to 1.0.1 (#154672) 2025-10-17 12:23:55 +03:00
johanzander a1f98abe49 Add CODEOWNERS entry for Growatt Server integration (#154647) 2025-10-17 11:20:11 +03:00
cdnninja d25dde1d11 Bump pyvesync version to 3.1.2 (#154650)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-10-17 10:19:48 +02:00
hanwg 8ec483b38b Fix Telegram bot bug where message is sent to wrong recipient (#154658) 2025-10-17 11:15:41 +03:00
epenet bf14caca69 Fix behavior spelling for public facing strings (#154665) 2025-10-17 11:07:05 +03:00
Ludovic BOUÉ e5fb6b2fb2 Remove duplicated Matter powersource cluster from Mock device fixture files (#154668) 2025-10-17 11:06:01 +03:00
epenet 7dfeb3a3f6 Improve metoffice typing (#154670) 2025-10-17 10:05:27 +02:00
epenet 9d3b1562c4 Remove more components from _IGNORE_ROOT_IMPORT in pylint plugin (#154667) 2025-10-17 09:46:53 +02:00
epenet e14407f066 Remove HomeAssistantRemoteScanner from __all__ in bluetooth (#154669) 2025-10-17 09:31:30 +02:00
epenet 67872e3746 Adjust onewire strings (#154664) 2025-10-17 09:28:37 +02:00
Manu 06bd1a2003 Migrate Xbox to runtime_data (#154652) 2025-10-17 09:25:49 +02:00
dependabot[bot] 37ea360304 Bump sigstore/cosign-installer from 3.10.0 to 4.0.0 (#154661)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-17 09:15:41 +02:00
epenet 25ce57424c Remove more components from _IGNORE_ROOT_IMPORT in pylint plugin (#154660) 2025-10-17 08:35:18 +02:00
Thomas55555 3d46ab549d Add serial number to IPP (#154648) 2025-10-16 23:58:57 +01:00
Thomas55555 567cc9f842 Bump colorlog to 6.10.1 (#154643) 2025-10-16 23:57:24 +01:00
Shay Levy b5457a5abd Fix demo cover set position action (#154641) 2025-10-16 21:21:32 +03:00
Marc Mueller e4b5e35d1d Update Pillow to 12.0.0 (#154637) 2025-10-16 18:25:36 +01:00
Ludovic BOUÉ 12023c33b5 Rename Mock Door Lock with unbolt fixture (#154627) 2025-10-16 13:01:46 -04:00
Jan Čermák a28749937c Allow ignored rapt_ble devices to be set up from the user flow (#154606) 2025-10-16 12:54:24 -04:00
Jan Čermák 3fe37d651f Update Home Assistant base image to 2025.10.1 (#154609)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-16 12:53:25 -04:00
epenet cb3424cdf0 Remove more components from _IGNORE_ROOT_IMPORT in pylint plugin (#154622) 2025-10-16 12:52:51 -04:00
Thomas D a799f7ff91 Add service warning sensor to Volvo integration (#154613) 2025-10-16 18:52:12 +02:00
Louis Pré 34ab725b75 LLM prefix caching optimization using new GetDateTime tool (#152408)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Denis Shulyaka <Shulyaka@gmail.com>
2025-10-16 12:47:12 -04:00
Manu 2dfc7f02ba Bump habiticalib to v0.4.6 (#154566) 2025-10-16 17:15:13 +01:00
Jan Čermák c8919222bd Mock network calls in comfoconnect tests to fix timeouts (#154620) 2025-10-16 11:42:04 -04:00
Ludovic BOUÉ a888264d2f Add Matter fixture for Aqara Smart Lock U200 (#154623) 2025-10-16 16:25:16 +02:00
Joost Lekkerkerker ae84c7e15d Add subentries to WAQI (#148966)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-16 14:11:52 +01:00
epenet 415c8b490b Add device diagnostics to onewire (#154617)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-16 14:56:19 +02:00
Aviad Levy 6038f15406 Add support for Telegram message attachments (#153216) 2025-10-16 14:54:50 +02:00
Justus a8758253c4 Add config flow exceptions to IOMeter (#154604)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-16 14:52:51 +02:00
epenet fa4eb2e820 The 1-wire integration has now reached silver on the quality scale (#154614) 2025-10-16 14:52:11 +02:00
Ludovic BOUÉ 58f35d0614 Add Matter Eve Energy 20ECN4101 fixture (#154608) 2025-10-16 14:07:29 +02:00
epenet f72a91ca29 Remove assist_pipeline from _IGNORE_ROOT_IMPORT in pylint plugin (#154600) 2025-10-16 13:33:19 +02:00
Thomas D 5d99da6e1f The Volvo integration has now reached platinum on the quality scale (#154015)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-16 13:07:54 +02:00
Joost Lekkerkerker 64746eb99c Add new Dryer fixture to SmartThings (#154607) 2025-10-16 12:55:30 +02:00
Maciej Bieniek 70fc6df599 Make Shelly deprecated firmware issue more general (#154539) 2025-10-16 13:50:43 +03:00
epenet 8dc33ece7b Remove sensor from _IGNORE_ROOT_IMPORT in pylint plugin (#154602) 2025-10-16 11:28:29 +01:00
Carlos Gustavo Sarmiento 3d4d8e7f20 Make Speed optional for GoToPreset ONVIF command (#149636)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-16 11:21:02 +01:00
Joakim Sørensen c92d319e12 Bump hass-nabucasa from 1.3.0 to 1.4.0 (#154599) 2025-10-16 11:18:55 +01:00
Christopher Fenner 1bdba7906a Add new sensors for Zigbee based devices in ViCare (#154271) 2025-10-16 11:11:08 +01:00
epenet aa8198d852 Bump epson-projector to 0.6.0 (#154596) 2025-10-16 12:06:30 +02:00
Ashus b7f30ec17f Fix friendly names of zones with mobile_app (#149453)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-16 11:27:59 +02:00
Ludovic BOUÉ 2da1878f60 Add Matter Inovelli VTM30 fixture (#154601) 2025-10-16 11:20:29 +02:00
tstabrawa 872b33a088 Move URL out of Nuheat strings.json (#154580) 2025-10-16 10:14:22 +02:00
epenet e0faa36157 Bump pymonoprice to 0.5 (#146936) 2025-10-16 10:07:52 +02:00
Magnus 14b270a2db Component asuswrt: handle_errors_and_zip._wrapper returns dict[str, str] (#154544) 2025-10-16 09:48:21 +02:00
Magnus 8402bead4f Component asuswrt: import of ConnectionState corrected (#154518) 2025-10-16 09:38:09 +02:00
Erik Montnemery 6bf7a4278e Fix flaky playstation_network test (#154559)
Co-authored-by: Joakim Plate <elupus@ecce.se>
2025-10-16 09:35:53 +02:00
Erik Montnemery 3de62b2b4c Improve mobile_app device_tracker tests (#154584)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-10-16 09:15:35 +02:00
Kinachi249 0d2558c030 Implement cync reauth flow (#154257) 2025-10-16 08:48:31 +02:00
Jordan Harvey 9efbcb2f82 Add model information for probe_plus devices (#154262) 2025-10-16 08:46:29 +02:00
Keith Burzinski f210bb35ed Bump aioesphomeapi to 42.0.0 (#154577) 2025-10-16 00:21:15 -05:00
Grzegorz M 0581ceb771 Add ability for CalDAV to create calendar events (#150030) 2025-10-15 20:07:31 -07:00
J. Diego Rodríguez Royo 7ba2e60af3 Bump aiohomeconnect to version 0.22.0 (#154572) 2025-10-16 00:10:54 +01:00
epenet 75fa0ffd04 Update onewire quality scale (#154515) 2025-10-16 00:51:14 +03:00
epenet 01effb7ca6 Remove hardware from _IGNORE_ROOT_IMPORT in pylint plugin (#154532)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-15 21:59:40 +01:00
Jan Bouwhuis 88d383962c Fix lingering todoist test by fixing its test time (#154511) 2025-10-15 22:17:21 +02:00
Paulus Schoutsen 3c001bd6ed Revert "Expose the entity_id of an entity to LLMs" (#154561)
Co-authored-by: Michael Hansen <mike@rhasspy.org>
2025-10-15 16:07:34 -04:00
Marc Mueller ec5c4843d1 Fix typing issue in fritz (#154497) 2025-10-15 21:27:50 +02:00
Omer Korner e2c281549e Expose the entity_id of an entity to LLMs (#149428) 2025-10-15 21:24:32 +02:00
epenet 051e472537 Import device_tracker classes from component root (#154524) 2025-10-15 20:57:38 +02:00
Marc Mueller 1e5910215d Update pylint to 4.0.1 (#154526) 2025-10-15 20:54:15 +02:00
epenet 645089edba Bump aio-ownet to 0.0.4 (#154520)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-15 20:52:58 +02:00
Marc Mueller 7abe289681 Add support for Python 3.14 (#153939) 2025-10-15 20:50:16 +02:00
Maciej Bieniek 7829c2d03e Align Shelly entity names with device classes (#154492) 2025-10-15 20:47:47 +02:00
Erwin Douna 148a13361f Firefly refactor entities (#153292) 2025-10-15 20:33:38 +02:00
epenet 57dccd1474 Remove zha from _IGNORE_ROOT_IMPORT in pylint plugin (#154534) 2025-10-15 20:12:11 +02:00
Aarni Koskela a3b0132299 Move template-rendering test helpers to separate module (#154366) 2025-10-15 20:11:19 +02:00
epenet fbd8443745 Simplify onewire entity descriptions (#154513) 2025-10-15 20:09:51 +02:00
Manu cd7015c6b7 Add integration type device to IronOS manifest (#154533) 2025-10-15 20:00:46 +02:00
Joakim Plate 1012c7bdf9 Ensure psn wait more than coordinator tick (#154549) 2025-10-15 19:54:53 +02:00
Markus Adrario ca912906f5 Automatically removing stale devices in Homee (#152680)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-15 18:36:27 +01:00
karwosts d0cad43a6c Recalculate derivative unit correctly when source or options change (#147527)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-15 18:17:41 +02:00
Magnus 751540e606 Component asuswrt: Update SENSORS_DEFAULT in tests (#154547) 2025-10-15 17:53:22 +02:00
hanwg 3d2ec712f1 Raise exceptions for Telegram bot when actions fail (#148140) 2025-10-15 17:49:57 +02:00
Noah Husby e3a6c06997 Bump aiorussound to 4.9.0 (#154545) 2025-10-15 16:14:38 +01:00
Jordan Harvey 08b94e29e6 Bump pynintendoparental to 1.1.2 (#154527) 2025-10-15 16:08:06 +01:00
Simone Chemelli 79323189fb Bump aioamazondevices to 6.4.4 (#154538) 2025-10-15 18:02:56 +03:00
Magnus 7508828518 Adding __all__ export to device_tracker (#154525) 2025-10-15 15:11:16 +01:00
Magnus f257e89b2a Adjust import of ATTR_GPS_ACCURACY in device_tracker tests (#154531) 2025-10-15 13:55:10 +02:00
Magnus a2e469eb28 Adjust import of ATTR_GPS_ACCURACY in mobile_app.webhook (#154529) 2025-10-15 13:52:30 +02:00
Magnus 7c80491325 Adjust import of ATTR_GPS_ACCURACY in mobile_app (#154528) 2025-10-15 13:50:49 +02:00
J. Nick Koston adedf2037a Fix improv_ble provisioning futures type (#154530) 2025-10-15 13:46:23 +02:00
G Johansson 188459e3ff Allow use of Selector in ObjectSelector fields (#147929) 2025-10-15 13:25:04 +02:00
Luke Lashley 7324a12ada Add suggested units for Roborock Durations sensors (#153607)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-15 12:05:46 +01:00
Jan Bouwhuis fe07e9c840 Move out MQTT translation strings (#154406)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-15 11:25:21 +01:00
krahabb afeaf2409f Add TEMPERATURE_DELTA device class to Number and Sensor entities (#147358) 2025-10-15 11:49:12 +02:00
Magnus 69f9c0a6cc Typing for WrtDevice (#154514) 2025-10-15 11:08:54 +02:00
epenet 46f52db87c Mark tempres configuration as disabled by default in onewire (#154517) 2025-10-15 11:00:49 +02:00
epenet d877761dbb Cleanup model/model_id in onewire (#154509) 2025-10-15 10:52:57 +02:00
Fabian Weisshaar 95da65f552 System Bridge to set unavailable entry state if host is not reachable (#154177) 2025-10-15 10:43:42 +02:00
Jan Bouwhuis 6ec82d0b21 Fix MQTT siren subentry translation string (#154483) 2025-10-15 10:36:55 +02:00
Foscam-wangzhengyu f6a16f63a4 Bump libpyfoscamcgi to 0.0.8 (#154505) 2025-10-15 10:36:20 +02:00
wollew 9ff2dab468 set integration type for velux to hub (#154510) 2025-10-15 10:35:03 +02:00
epenet 9422703288 Add support for DS2401 (#154506) 2025-10-15 08:47:28 +02:00
Anuj Soni d91eccb209 Move translatable URLs out of strings.json for vera (#154475) 2025-10-15 07:51:52 +02:00
J. Nick Koston 939cbc8644 Bump uiprotect to 7.22.0 (#154494) 2025-10-15 01:07:27 +02:00
J. Nick Koston 0f1d2a77cb Add flow chaining from Improv BLE to integration config flows (#154415)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-14 12:00:55 -10:00
J. Nick Koston 385fc5b3d0 Add next_flow parameter to async_abort for flow chaining (#154416)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-14 11:45:21 -10:00
Felipe Santos 18c63e3b8f Introduce the OpenRGB integration (#153373)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Manu <4445816+tr4nt0r@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-10-14 19:03:58 +02:00
Paul Bottein cf477186aa Set assumed state to group if at least one child has assumed state (#154163) 2025-10-14 18:53:51 +02:00
Jan Bouwhuis 0eef44be91 Fix inconsistent use of StrEnum as index in MQTT subentry flow globals (#154210) 2025-10-14 18:47:20 +02:00
Denis Shulyaka e7ac56c59f Revisit list of OpenAI models for tool support (#154399)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-14 18:37:18 +02:00
Tom 3cc4091f31 Update airOS IQS (completing silver) (#153675) 2025-10-14 18:36:55 +02:00
Anuj Soni 00025c8f42 Move translatable URLs out of strings.json for isy994 (#154464) 2025-10-14 18:31:01 +02:00
Ludovic BOUÉ db48f8cb28 Add Matter Zemismart Roller Motor fixture (#154458)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2025-10-14 18:19:25 +02:00
TheJulianJES 4fdbe82df2 Bump pydantic to 2.12.2 (#154461) 2025-10-14 17:46:41 +02:00
Lennart Coopmans 742f1b2157 PushSafer: Handle empty data section properly (#154109) 2025-10-14 17:29:34 +02:00
Sebastian Schneider 681eb6b594 Add LED control for supported UniFi network devices (#152649) 2025-10-14 17:20:47 +02:00
epenet 1d6c6628f4 Migrate onewire to async library (#154439) 2025-10-14 17:18:25 +02:00
Marc Mueller b6337c07d6 Update intellifire4py to 4.2.1 (#154454) 2025-10-14 16:52:12 +02:00
Jan Bouwhuis 8b6fb05ee4 Add subentry support for MQTT siren device (#154220) 2025-10-14 16:45:48 +02:00
MoonDevLT 28405e2b04 Add model name to Lunatone devices (#154432) 2025-10-14 16:40:48 +02:00
ollo69 31857a03d6 Remove Asuwrt device tracker last_time_reachable extra attribute (#154219) 2025-10-14 16:35:48 +02:00
Kelyan PEGEOT SELME 97a0a4ea17 Add tyre pressure to Renault integration (#154377) 2025-10-14 16:33:43 +02:00
Abílio Costa b494074ee0 Fix device registry arg docstring (#154453) 2025-10-14 15:32:31 +01:00
Manu 6aff1287dd Fix capitalization of RADIUS in Uptime Kuma (#154456) 2025-10-14 17:29:50 +03:00
puddly 655de3dfd2 Use async_schedule_reload instead of async_reload for ZHA (#154397) 2025-10-14 16:26:40 +02:00
cdnninja 11ee7d63be Remove vesync unused extra attributes, refine enums (#153171) 2025-10-14 16:23:29 +02:00
Simone Chemelli 080a7dcfa7 Allow more device types for Vodafone Station (#153990) 2025-10-14 16:18:16 +02:00
Sid 3e20c506f4 Add gallons per hour as volume flow rate unit (#154246)
Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com>
2025-10-14 16:16:48 +02:00
Abílio Costa 2abc197dcd Add extract_from_target websocket command (#150124)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2025-10-14 16:16:00 +02:00
karwosts a3dec46d59 Add derivative tests exhibiting unit issues (#153051) 2025-10-14 15:58:14 +02:00
Samuel Xiao 7a3630e647 Add sensor description for switchbot cloud's device(plug) small changes (#148551)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-14 15:35:11 +02:00
Heindrich Paul 2812d7c712 Add the coordinator pattern to the NS integration (#154149)
Signed-off-by: Heindrich Paul <heindrich.paul@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-14 15:21:09 +02:00
Anuj Soni c0fc7b66f0 Move translatable URLs out of strings.json for huawei lte (#154368)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-14 15:11:47 +02:00
David Recordon c6e334ca60 Skip adding Control4 rooms with no audio/video sources as media player devices (#154348)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-10-14 15:09:04 +02:00
Tom 416f6b922c Add reconfigure flow to airOS (#154447) 2025-10-14 15:05:10 +02:00
DannyS95 d2af875d63 Move igloohome API access URL into constant placeholders (#154430) 2025-10-14 15:01:39 +02:00
Mateusz 1237010b4a auth: add required issuer to OAuth (#152385) 2025-10-14 14:50:38 +02:00
Jan-Philipp Benecke 26fec2fdcc Move Electricity Maps url out of strings.json (#154284) 2025-10-14 14:50:28 +02:00
Oliver Gründel 13e828038d Move developer url out of strings.json for coinbase setup flow (#154339) 2025-10-14 14:50:12 +02:00
Oliver Gründel b517774be0 Move Ecobee authorization URL out of strings.json (#154332) 2025-10-14 14:49:45 +02:00
Andrew Jackson 6e515d4829 Move URL out of Mealie strings.json (#154230) 2025-10-14 14:48:36 +02:00
Manu 7f5128eb15 Add description placeholders to pyLoad config flow (#154254) 2025-10-14 14:48:11 +02:00
Shai Ungar 7ddfcd350b Move URLs out of SABnzbd strings.json (#154333)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-14 14:47:50 +02:00
epenet a92e73ff17 Move URL out of sfr_box strings.json (#154364)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-14 14:47:32 +02:00
Shay Levy ae3d32073c Move URL out of Switcher strings.json (#154240) 2025-10-14 14:47:22 +02:00
Christopher Fenner 38d0299951 Remove URL from ViCare strings.json (#154243) 2025-10-14 14:47:12 +02:00
Stefan Agner 8dba1edbe5 Machine container: Remove codenotary configuration (#153855)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-14 14:39:38 +02:00
Jamin f3c4288026 Use contact header for outgoing call transport (#151847) 2025-10-14 14:36:31 +02:00
peteS-UK 8db6505a97 Set initial integration_hub in manifest for Squeezebox (#154438) 2025-10-14 14:35:12 +02:00
Kamil Breguła 61a9094d5f Update WLED Select Options after update (#154205)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-10-14 14:23:41 +02:00
Joakim Plate d140eb4c76 Protect internal coordinator state (#153685) 2025-10-14 14:14:37 +02:00
Arie Catsman 21f24c2f6a Get Enphase_envoy collar grid status from admin_state_str rather then from grid_state (#153766)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-14 13:10:14 +01:00
Aarni Koskela 85b26479de Shut down core event loop on unrecoverable errors (#144806) 2025-10-14 14:09:29 +02:00
Artur Pragacz bddbf9c73c Simplify current ids callback in config entries (#154082) 2025-10-14 14:04:57 +02:00
Tom 64f48564ff Change device identifier and binary_sensor unique_id for airOS (#153085)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-10-14 14:02:22 +02:00
Yvan13120 06e4922021 Fix state class for Overkiz water consumption (#154164) 2025-10-14 12:49:32 +01:00
G Johansson cdc6c44a49 Fix reconfigure flow in esphome uses create_entry (#154107) 2025-10-14 13:46:53 +02:00
mmstano 106a74c954 Prevent AttributeError in luci device tracker (#148357)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-14 13:39:56 +02:00
Domochip 8464dad8e0 Add milliPascal (mPa) as unit of measurement for Pressure (#153087) 2025-10-14 12:38:14 +01:00
Joakim Plate c3e2f0e19b Always run install of packages with same python as script (#154253) 2025-10-14 13:35:00 +02:00
Erik Montnemery fbf875b5af Deprecate has_mean in favor of mean_type in recorder statistic API (#154093) 2025-10-14 13:34:25 +02:00
epenet fcea5e0da6 Simplify DPType lookup in Tuya (#150117) 2025-10-14 13:23:50 +02:00
nasWebio 81fd9e1c5a Move state conversion from library to nasweb integration code (#153208) 2025-10-14 13:21:19 +02:00
Shay Levy d108d5f106 Use Shelly RPC cover methods from upstream and fix cover status update (#154345)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-14 14:07:37 +03:00
Magnus 487940872e Dependency update py-melissa-climate to 3.0.2 (#154285) 2025-10-14 12:37:37 +02:00
Paul Bottein aaf58075c6 Rename security panel to safety panel (#154435) 2025-10-14 12:08:41 +02:00
Michel van de Wetering a23bed6f4d Add missinglong_press entry for trigger_type in strings.json for Hue (#154437) 2025-10-14 11:23:26 +02:00
Maciej Bieniek 02e05643f1 Add boost switches for Shelly cury component (#154387) 2025-10-14 11:21:55 +02:00
Samuel Xiao 5f9b098c19 Add K11+ vacuum support to Switchbot Cloud (#154363) 2025-10-14 10:52:40 +02:00
Simone Chemelli 143f7df7fd Use aioshelly methods for climate platform (#154384) 2025-10-14 10:44:39 +02:00
Marc Mueller 9a28ee5378 Update pydantic to 2.12.1 (#154424) 2025-10-14 10:37:16 +02:00
J. Nick Koston 82f33fbc39 Bump aioesphomeapi to 41.16.0 (#154427) 2025-10-14 09:00:27 +02:00
Shay Levy 6a632a71b6 Bump aioshelly to 13.14.0 (#154421) 2025-10-14 09:28:26 +03:00
PaulCavill ae8678b2af Bump pyiCloud to 2.1.0 (#154365) 2025-10-13 23:36:40 +01:00
Tucker Kern b52ee6915a Make Snapcast snapshot action async (#153132) 2025-10-13 23:32:33 +01:00
Åke Strandberg b0e1b00598 Set integration_type explicitly in miele manifest (#154375) 2025-10-13 23:29:28 +01:00
Jamin fd902af23b VOIP Integration Type (#154418) 2025-10-13 23:21:27 +01:00
Matthias Alphart 07d6ebef4c Restore KNX sensor entity states (#154318) 2025-10-13 23:18:41 +01:00
tronikos c9b9f05f4b Google Assistant SDK: improve config flow tests (#153794) 2025-10-13 17:28:33 -04:00
J. Nick Koston 90a0262217 Bump aioesphomeapi to 41.15.0 (#154407) 2025-10-13 10:56:24 -10:00
J. Nick Koston 324aa09ebe Update Improv BLE discovery notification when device name changes (#154352)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-10-13 16:14:51 -04:00
Dave T 663431fc80 Allow following of 302 redirects in generic camera (#154308) 2025-10-13 16:11:34 -04:00
J. Nick Koston 610183c11b Fix Improv BLE factory reset rediscovery (#154354) 2025-10-13 16:03:26 -04:00
Simone Chemelli b7718f6f0f Bump aiocomelit to 1.1.2 (#154393) 2025-10-13 16:01:46 -04:00
Jan Bouwhuis 5708f61964 Prepare to move out URL's from MQTT translation strings (#154391) 2025-10-13 21:48:01 +02:00
G Johansson 4fb3c9fed2 Add async_update_and_abort method to config flow (#153146)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-13 21:39:04 +02:00
Marc Mueller 1e5f5f4ad3 Enable pylint consider-math-not-float check (#154338) 2025-10-13 21:14:57 +02:00
TheJulianJES 82c536a4e9 Migrate Matter descriptions to be kw_only (#154398) 2025-10-13 20:18:05 +02:00
Matthias Alphart 97afec1912 Record last_reported for KNX BinarySensor entitiy states (#154392) 2025-10-13 20:12:13 +02:00
G Johansson 0bfdd70730 async_config_entry_first_refresh in update coordinator requires a config entry (#154114) 2025-10-13 19:47:07 +02:00
johanzander 01dee6507b Add 14 additional sensor entities for Growatt TLX/MIN inverters (#153964)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-13 19:17:14 +02:00
Kurt 04f83bc067 Add actron_air climate integration (#134740)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-13 19:10:35 +02:00
Paulus Schoutsen f0756af52d Add Python version file (#154267) 2025-10-13 19:02:49 +02:00
johanzander dd6bc715d8 Add switch platform and grid charge enable for Growatt Server integration (#153960)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-13 17:54:30 +02:00
Jordan Harvey 1452aec47f Add switch platform to Nintendo Parental controls integration (#154179) 2025-10-13 17:52:06 +02:00
Simone Chemelli 6f8439de5b Fix switch platform for Comelit SimpleHome (#154227) 2025-10-13 17:48:50 +02:00
Tom f649717372 Add model_id support to airOS (#154388) 2025-10-13 17:47:53 +02:00
Tom bf273ef407 Add integration_type to airOS (#154390) 2025-10-13 17:47:44 +02:00
J. Nick Koston 94d015e00a Fix Bluetooth discovery for devices with alternating advertisement names (#154347) 2025-10-13 11:44:16 -04:00
Thomas D f185ffddf1 Set model_id on device for Volvo integration (#154385) 2025-10-13 17:29:15 +02:00
Foscam-wangzhengyu 2d0b4dd7e9 New Foscam switch (#152732) 2025-10-13 17:23:27 +02:00
J. Nick Koston eab1205823 Add config flow title placeholder update infrastructure (#154353) 2025-10-13 11:15:28 -04:00
J. Nick Koston a991dcbe6a Add Bluetooth API to clear address from match history (#154355) 2025-10-13 16:56:39 +02:00
Yevhenii Vaskivskyi 6f79a65762 AsusWRT: Pass only online clients to the device list from the API (#154322) 2025-10-13 16:55:28 +02:00
Matthias Alphart ce1fdc6b75 Update xknx to 3.10.0 (#154361) 2025-10-13 16:53:32 +02:00
Tom d7aa0834c7 Bump airOS preparing for model_id matching (#154370) 2025-10-13 16:48:32 +02:00
Tom Matheussen 3151384867 Set integration type for Satel Integra to device (#154372) 2025-10-13 16:47:57 +02:00
Ravaka Razafimanantsoa 8aa5e7de91 Bump momonga to 0.2.0 (#154371) 2025-10-13 16:43:44 +02:00
Erik Montnemery cca5c807ad Store nmap tracker options as lists (#154378) 2025-10-13 16:39:04 +02:00
Krisjanis Lejejs 89433219dd Bump hass-nabucasa from 1.2.0 to 1.3.0 (#154376) 2025-10-13 16:37:48 +02:00
Renat Sibgatulin 694b169c79 Bump aioairq to 0.4.7 (#154386) 2025-10-13 16:37:31 +02:00
puddly f1e0954c61 Automatically setup hardware integrations when firmware info is published by an integration (#154030) 2025-10-13 16:26:01 +02:00
Erik Montnemery 3c3b4ef14a Fix stale docstring in nmap_tracker (#154380) 2025-10-13 15:45:33 +02:00
Ted van den Brink 54ff49115c Implement MAC address exclude list in nmap_tracker (#142724)
Co-authored-by: Erik <erik@montnemery.com>
2025-10-13 15:01:47 +02:00
Åke Strandberg 2512dad843 Set model_id in miele integration (#154367) 2025-10-13 14:31:22 +02:00
Erik Montnemery a3b67d5f28 Add support to sensor statistics for changing unit_class (#154130) 2025-10-13 12:35:10 +01:00
epenet 76a0b2d616 Bump renault-api to 0.4.4 (#154137)
Thanks!
2025-10-13 11:23:33 +02:00
dependabot[bot] 1182082c1f Bump actions/dependency-review-action from 4.8.0 to 4.8.1 (#154356)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 11:12:30 +02:00
wollew e0811558cb update pysqueezebox lib to 0.13.0 (#154358) 2025-10-13 11:05:33 +02:00
Christopher Fenner d389405218 Bump PyViCare to 2.54.0 (#154336) 2025-10-13 11:04:25 +02:00
Magnus 3a71087c9c Bump aioasuswrt to 1.5.1 (#153209) 2025-10-13 10:59:22 +02:00
starkillerOG c7d7cfa7ad Add Reolink IO input binary sensor (#154133) 2025-10-13 10:54:30 +02:00
wittypluck e4ea79866d Add support for μg/m³ for Carbon Monoxide (#153158) 2025-10-13 10:08:55 +02:00
tronikos ddfa6f33d2 Bump opower to 0.15.7 (#154351) 2025-10-13 00:07:20 -07:00
dependabot[bot] 15e99650aa Bump github/codeql-action from 4.30.7 to 4.30.8 (#154357) 2025-10-13 08:32:24 +02:00
Christopher Fenner 58bacbb84e Fix identifier generation for sub devices in ViCare (#154330) 2025-10-13 08:31:03 +02:00
Marc Mueller 82758f7671 Update pyheos to 1.0.6 (#154346) 2025-10-13 01:39:36 +02:00
David Recordon 7739cdc626 Update pyControl4 to v1.5.0 (#154341) 2025-10-12 23:28:08 +02:00
Michael Davie 4ca1ae61aa Environment Canada station selector (#154307)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-12 22:34:16 +02:00
Dave T 3d130a9bdf Simplify generic camera tests (#154313) 2025-10-12 22:06:13 +02:00
Shay Levy 2b38f33d50 Bump aioshelly to 13.13.0 (#154337) 2025-10-12 23:01:22 +03:00
Glenn Vandeuren (aka Iondependent) 19dedb038e Update nhc requirement to version 0.7.0 (#154250) 2025-10-12 21:58:01 +02:00
Dan Schafer 59781422f7 Update Snoo strings.json to include weaning_baseline (#154268) 2025-10-12 21:57:47 +02:00
Thomas55555 083277d1ff Add model_id to Husqvarna Automower (#154335) 2025-10-12 21:45:01 +02:00
Marcus Gustavsson 9b9c55b37b Updated prowlpy to 1.1.1 and changed the usage to do asynchronous calls (#154193) 2025-10-12 21:17:43 +02:00
J. Nick Koston c9d67d596b Fix August integration to handle unavailable OAuth implementation at startup (#154244) 2025-10-12 09:16:22 -10:00
J. Nick Koston 7948b35265 Fix Yale integration to handle unavailable OAuth implementation at startup (#154245) 2025-10-12 09:16:02 -10:00
Ernst Klamer be843970fd bump tilt-ble to 1.0.1 (#154320) 2025-10-12 21:38:27 +03:00
Michael Davie 53b65b2fb4 Bump env-canada to v0.12.1 (#154303)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-12 20:31:02 +02:00
Simone Chemelli ac7be97245 Bump aioamazondevices to 6.4.3 (#154293) 2025-10-12 19:25:53 +02:00
Jan Bouwhuis 09e539bf0e Fix home wiziard total increasing sensors returning 0 (#154264) 2025-10-12 12:51:50 -04:00
J. Nick Koston 6ef1b3bad3 Bump aioesphomeapi to 41.14.0 (#154275) 2025-10-12 12:51:05 -04:00
Bouwe Westerdijk 38e46f7a53 Bump plugwise to v1.8.0 - add initial support for Emma (#154277) 2025-10-12 12:50:46 -04:00
Michael Davie ef60d16659 Fix Environment Canada camera entity initialization (#154302)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-12 12:47:22 -04:00
Marc Mueller bf4f8b48a3 Update pylint to 4.0.0 + astroid to 4.0.1 (#154311) 2025-10-12 12:46:04 -04:00
Denis Shulyaka 3c1496d2bb Add gpt-image-1-mini support (#154316) 2025-10-12 12:44:38 -04:00
Mick Vleeshouwer d457787639 Move URL out of Overkiz Config Flow descriptions (#154315) 2025-10-12 18:23:24 +02:00
Mick Vleeshouwer de4bfd6f05 Bump pyOverkiz to 1.19.0 in Overkiz (#154310) 2025-10-12 18:07:19 +02:00
Shay Levy 34c5748132 Align Shelly async_setup_entry in platforms (#154142)
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
2025-10-12 18:41:54 +03:00
Michael Hansen 5bfd9620db Handle Wyoming config entries with missing info (#154186) 2025-10-12 10:23:09 -05:00
Michael Davie 6f8766e4bd Update config flow strings for Environment Canada (#154242) 2025-10-12 11:49:29 +02:00
Jordan Harvey d3b519846b Bump pyprobeplus to 1.1.0 (#154265) 2025-10-12 10:06:00 +02:00
Joakim Plate 36d952800b Move url like strings to placeholders for nibe (#154249) 2025-10-12 00:11:23 +02:00
Andrew Jackson b832561e53 Move URL out of Mastodon strings.json (#154231) 2025-10-12 00:07:40 +02:00
Manu c59d295bf2 Add description placeholders in Uptime Kuma config flow (#154252)
Signed-off-by: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com>
2025-10-12 00:06:29 +02:00
Oliver Gründel 6e28e3aed1 Move developer registration url out of strings.json file (#154261) 2025-10-12 00:04:59 +02:00
Magnus 6d8944d379 Fix multiple definition of DEFAULT_PORT and DEFAULT_RETAIN constants (#154255) 2025-10-11 23:47:18 +02:00
Joost Lekkerkerker 762fd6d241 Move URL out of Aemet strings.json (#154225) 2025-10-11 20:52:51 +03:00
Simone Chemelli 4c6500e7a4 Bump aioamazondevices to 6.4.1 (#154228) 2025-10-11 18:58:14 +02:00
Jan Bouwhuis cdc224715f Fix inconsistent naming of MQTT test config globals (#154221) 2025-10-11 16:29:13 +02:00
Paul Bottein 648b250fc8 Bump frontend 20251001.4 (#154218) 2025-10-11 09:33:06 -04:00
Ernst Klamer ba61562300 Bump kegtron-ble to 1.0.2 (#154207) 2025-10-11 16:27:25 +03:00
Marc Mueller 8d67182e0e [ci] No longer install setuptools + wheel by default (#154212) 2025-10-11 15:26:04 +02:00
Shay Levy 3ce1ef4c3f Use Entity Description in Shelly light platform (#154102) 2025-10-11 16:15:48 +03:00
Ludovic BOUÉ bde4eb5011 Rename Matter SolarPower fixture to Solar inverter (#154201)
The goal is to facilitate understanding for the introduction of Matter namespaces and tags:
 - https://github.com/home-assistant/core/pull/152754
2025-10-11 12:16:37 +02:00
srirams a58a7065b6 Remove redudant state write in Smart Meter Texas (#154126) 2025-10-11 10:32:10 +02:00
Marc Mueller 0c9b72bf1d Update pylint to 3.3.9 (#154194) 2025-10-11 09:31:36 +02:00
Matthias Alphart 541d94d8c6 Record last_reported for KNX sensor entitiy states (#154169) 2025-10-11 08:21:51 +02:00
Abílio Costa c370c86a4f Use custom string for Oral-B no-devices-found message (#154183) 2025-10-10 22:08:38 +01:00
Paul Bottein bc6accf4ae Add missing entity category and icons for smlight integration (#154131) 2025-10-10 21:30:32 +02:00
G Johansson d40eeee422 Remove deprecated ConfigSource from core (#154112) 2025-10-10 18:23:13 +02:00
Erik Montnemery c9d9730c4a Change domain and name of Nintendo Switch parental controls integration (#153893) 2025-10-10 17:11:10 +02:00
ehendrix23 d3a8f3191b Add Speech-to-Text (stt) to elevenlabs (#147838)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-10-10 17:01:22 +02:00
Thomas D cb3829ddee Add buttons to Volvo integration (#153272)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2025-10-10 16:51:35 +02:00
tronikos 73383e6c26 Add reconfigure flow in Google Assistant SDK (#153802) 2025-10-10 16:24:46 +02:00
Matthias Alphart 217894ee8b Update knx-frontend to 2025.10.9.185845 (#154103) 2025-10-10 16:24:14 +02:00
Thomas D c7321a337e Add device_tracker platform to Volvo integration (#153437) 2025-10-10 16:23:07 +02:00
Denis Shulyaka 517124dfbe Anthropic web search support (#153753) 2025-10-10 16:21:21 +02:00
hanwg f49299b009 Add edit message media feature for Telegram bot (#151034) 2025-10-10 15:50:54 +02:00
Shay Levy 1001da08f6 Fix Shelly RPC cover update when the device is not initialized (#154159) 2025-10-10 16:50:45 +03:00
Lars 0da019404c Remove deprecated extra attributes from fritzbox climate (#154152) 2025-10-10 15:48:22 +02:00
Jan Čermák 9a4280d0de Add attachments support to OpenRouter AI task (#154161) 2025-10-10 15:44:33 +02:00
Lukas c28e105df5 Pooldose update api (#153497)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-10-10 15:43:47 +02:00
Matthias Alphart 68787248f6 Update xknx to 3.9.1 (#154146) 2025-10-10 15:42:38 +02:00
Tom Matheussen 36be6b6187 Add configured number to Satel Integra subentry titles (#154155) 2025-10-10 15:27:23 +02:00
Jordan Harvey 42dea92c51 Add time platform to nintendo_parental integration (#153866)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-10 15:20:59 +02:00
Justus 4b828d4753 IOmeter bump version v0.2.0 (#154150) 2025-10-10 15:15:58 +02:00
Robert Resch 8e79c38f34 Bump deebot-client to 15.1.0 (#154154) 2025-10-10 16:07:45 +03:00
jvmahon c92107b8d4 Inherit MatterEntityDescription in Matter entities (#154083) 2025-10-10 15:04:01 +02:00
epenet b25622f40e Use SI constants in CO unit converter (#153187) 2025-10-10 14:59:20 +02:00
Petro31 e887d5e6ad Fix delay_on and auto_off with multiple triggers (#153839) 2025-10-10 14:21:11 +02:00
TheJulianJES 1f19e40cfe Adjust OTBR config entry name for ZBT-2 (#153940) 2025-10-10 14:19:08 +02:00
Bram Kragten 3d2d2271d3 Update frontend to 20251001.2 (#154143) 2025-10-10 14:08:17 +02:00
Jack Thomasson d1dd5eecd6 use a consistent python version for uv (#154022) 2025-10-10 13:59:45 +02:00
Jan Bouwhuis cdec29ffb7 Add MQTT select subentry support (#153637) 2025-10-10 13:46:21 +02:00
peteS-UK 07f3e00f18 Fix for multiple Lyrion Music Server on a single Home Assistant server for Squeezebox (#154081) 2025-10-10 13:36:46 +02:00
starkillerOG 084d029168 Add Reolink survaillance rule switch entities (#154132)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-10 13:35:07 +02:00
tronikos 17e997ee18 Add module-level statistics to SolarEdge (#152581) 2025-10-10 13:07:39 +02:00
J. Diego Rodríguez Royo 16d4c6c95a Add Spotless series features to Home Connect integration (#153016) 2025-10-10 13:00:17 +02:00
epenet 0205a636ef Filter out invalid Renault vehicles (#154070)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-10 12:52:21 +02:00
hanwg 4707fd2f94 Update quality scale for Telegram bot (#154122) 2025-10-10 12:47:55 +02:00
J. Nick Koston ad3cadab83 Bump propcache to 0.4.1 (#154033) 2025-10-10 11:45:58 +01:00
Jordan Harvey 3fce815415 Add reauthentication to Nintendo Switch Parental controls integration (#154077) 2025-10-10 12:31:46 +02:00
Erik Montnemery ee67619cb1 Add mg/m³ as a valid UOM for sensor/number Carbon Monoxide device class (#154074)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-10 11:31:31 +01:00
TheJulianJES 1a744a2c91 Fix HA hardware configuration message for Thread without HAOS (#153933) 2025-10-10 11:37:43 +02:00
Erik Montnemery 951978e483 Include unit class in units_changed statistics issue (#154069) 2025-10-10 10:50:40 +03:00
Marcus Gustavsson 54d30377d3 Add ConfigFlow to Prowl integration (#133771)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-10-10 09:41:45 +02:00
Shay Levy eb04dda197 Use Entity Description in Shelly BLU TRV button (#154118) 2025-10-10 10:24:08 +03:00
Fabien Kleinbourg 1e192aadfa sharkiq dependency bump to 1.4.2 (#153931) 2025-10-10 08:40:20 +02:00
Erwin Douna 6f680f3d03 Portainer fix offline endpoint (#154101) 2025-10-10 08:14:49 +02:00
starkillerOG f0663dc275 Bump reolink-aio to 0.16.2 (#154117) 2025-10-10 02:20:11 +03:00
Paulus Schoutsen 96bb67bef9 Z-Wave: ESPHome discovery to update all options (#154113) 2025-10-09 17:14:53 -04:00
G Johansson 929d76e236 Add validation for ObjectSelector (#153081) 2025-10-09 21:46:03 +02:00
Artur Pragacz fe1ff083de Improve comments in the core config (#154096) 2025-10-09 21:08:16 +02:00
puddly 90c68f8ad0 Prevent reloading the ZHA integration while adapter firmware is being updated (#152626) 2025-10-09 21:00:02 +02:00
Shay Levy 6b79aa7738 Use Entity Description in Shelly cover platform (#154085) 2025-10-09 21:04:06 +03:00
Joost Lekkerkerker f6fb4c8d5a Add unique id to nederlandse spoorwegen (#154013) 2025-10-09 19:00:47 +02:00
hanwg a6e575ecfa Add diagnostics for Telegram bot (#154016) 2025-10-09 18:20:00 +02:00
Thomas D 85392ae167 Bump dependency for Volvo integration (#154084) 2025-10-09 18:15:22 +02:00
G Johansson 9d124be491 Remove deprecated set state directly in alarmcontrolpanel (#154038) 2025-10-09 18:06:13 +02:00
G Johansson 8bca3931ab Remove deprecated cover state constants (#154037) 2025-10-09 18:05:49 +02:00
Kevin McCormack 0367a01287 Enable strict typing for GitHub integration (#154048) 2025-10-09 17:50:24 +02:00
Manu 86e2c2f361 Add jet lag prevention event support to Sleep as Android integration (#154075) 2025-10-09 17:48:44 +02:00
Daniel De Sousa 335c8e50a2 Add switchbot_cloud climate TURN_OFF, TURN_ON support. (#154017) 2025-10-09 17:47:26 +02:00
eskerda 8152a9e5da Update Citybikes component with third-party library and fields (#151009) 2025-10-09 17:27:31 +02:00
Felipe Santos 250e562caf Fix devcontainer mistakenly using Python 3.14 (#154046) 2025-10-09 17:25:59 +02:00
Maciej Bieniek a3b641e53d Bump brother to version 5.1.1 (#154080) 2025-10-09 17:00:41 +02:00
Shay Levy 135ea4c02e Fix Shelly orphaned entity removal logic (#154031) 2025-10-09 16:21:58 +03:00
Simone Chemelli bc980c1212 Bump aioamazondevices to 6.4.0 (#154071) 2025-10-09 15:20:25 +02:00
Shay Levy 59ca88a7e8 Update Shelly block valve platform to use entity description (#154068) 2025-10-09 12:05:19 +03:00
Erik Montnemery d45114cd11 Improve unit handling in recorder (#153941) 2025-10-09 10:29:42 +02:00
David Rapan 2eba650064 Mark Shelly docs-troubleshooting as done (#154066) 2025-10-09 11:22:32 +03:00
Christopher Fenner de4adb8855 Make sensor names translatable in OpenWeatherMap integration (#153872) 2025-10-09 09:47:25 +02:00
Joost Lekkerkerker 1d86c03b02 Migrate Nederlandse Spoorwegen sensor to timestamp (#154011) 2025-10-09 09:25:11 +02:00
Klaas Schoute 77fb1036cc Bump autarco to v3.2.0 (#154039) 2025-10-09 01:13:57 +03:00
Shay Levy b15b4e4888 Fix Shelly virtual components roles migration (#153987) 2025-10-09 00:06:32 +03:00
puddly dddf6d5f1a Add new ZBT-2 VID:PID pair for discovery (#154036) 2025-10-08 15:59:49 -05:00
Abílio Costa 66fb5f4d95 Simplify firing of trigger actions (#152772)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-08 21:40:20 +01:00
hanwg 42a9d5d4e3 Add webhook tests for Telegram bot (#153998) 2025-10-08 20:58:15 +02:00
Maciej Bieniek 93fa162913 Update IQS for IMGW-PIB integration (#153870) 2025-10-08 20:30:05 +02:00
Maciej Bieniek c432b1c8da Add entities for Shely cury component (#153918) 2025-10-08 20:26:29 +02:00
Artur Pragacz 00955b8e6a Fix empty llm api list in chat log (#153996) 2025-10-08 10:39:56 -05:00
Erik Montnemery 045b9d7f01 Correct homeassistant.helpers.trigger._trigger_action_wrapper (#153983) 2025-10-08 17:33:44 +02:00
Aaron Bach 438c4c7871 Limit SimpliSafe websocket connection attempts during startup (#153853)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-08 16:32:17 +02:00
Thomas D abc360460c Add diagnostics to Volvo integration (#153997) 2025-10-08 16:25:33 +02:00
HarvsG 26437bb253 Adds ConfigFlow for London Underground (#152050)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-08 16:17:34 +02:00
epenet 56d953ac1e Use contants in climate set_temperature (#154008) 2025-10-08 16:15:01 +02:00
Joost Lekkerkerker fe4eb8766d Don't mark ZHA coordinator as via_device with itself (#154004) 2025-10-08 16:05:54 +02:00
Mark Adkins 2d9f14c401 Add 3rd maintainer to sharkiq (#153961) 2025-10-08 15:17:52 +02:00
dependabot[bot] 7b6ccb07fd Bump github/codeql-action from 3.30.6 to 4.30.7 (#153979)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-08 13:42:25 +02:00
Shay Levy 2ba5728060 Enable Shelly binary input sensors by default (#154001) 2025-10-08 14:41:53 +03:00
epenet b5f163cc85 Update Tuya fixture for product ID IAYz2WK1th0cMLmL (#154000) 2025-10-08 13:28:11 +02:00
Marc Mueller 65540a3e0b Update mypy dev to 1.19.0a4 (#153995) 2025-10-08 13:24:54 +02:00
Erwin Douna cbf1b39edb Portainer add sensor platform (#153059)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Manu <4445816+tr4nt0r@users.noreply.github.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-10-08 11:02:20 +02:00
G Johansson 142daf5e49 Call async_track_template_result with template without hass now fails (#153473)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-08 10:14:51 +02:00
Erik Montnemery 8bd0ff7cca Replace has_mean with mean_type in mill external statistics (#153985) 2025-10-08 09:52:07 +02:00
Erik Montnemery ac676e12f6 Remove has_mean from suez_water external statistics (#153986) 2025-10-08 09:51:44 +02:00
Glenn Vandeuren (aka Iondependent) c0ac3292cd FIx brightness always 100% when toggling the light (#153765)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-08 09:48:41 +02:00
Denis Shulyaka 80fd07c128 Add GPT-5 Pro and GPT-5 Codex support (#153936) 2025-10-08 09:48:07 +02:00
Michael Davie 3701d8859a Bump env-canada to 0.11.3 (#153967) 2025-10-08 09:40:55 +02:00
Jesse Hills 6dd26bae88 Bump aioesphomeapi to 41.13.0 (#153974) 2025-10-07 18:28:56 -10:00
Dave T 1a0abe296c Remove deprecated conductivity constants (#153942) 2025-10-07 23:20:36 +01:00
G Johansson de6c61a4ab Bump psutil 7.1.0 (#153954) 2025-10-07 23:16:49 +01:00
Glenn Vandeuren (aka Iondependent) 33c677596e Update nhc to 0.6.1 (#153962) 2025-10-07 23:16:04 +01:00
peetersch e9b4b8e99b Modbus Fix message_wait_milliseconds is no longer applied (#153709) 2025-10-07 23:38:05 +02:00
Maciej Bieniek 0525c04c42 Fix update interval for AccuWeather hourly forecast (#153957) 2025-10-07 23:25:04 +02:00
Shay Levy d57b502551 Migrate Shelly virtual button platfrom unique IDs to include roles (#153865) 2025-10-07 23:01:30 +03:00
G Johansson 9fb708baf4 Bump holidays to 0.82 (#153952) 2025-10-07 23:00:38 +03:00
Josef Zweck abdf24b7a0 Bump pylamarzocco to 2.1.2 (#153950) 2025-10-07 22:07:39 +03:00
TheJulianJES 29bfbd27bb Do not auto-set up ZHA zeroconf discoveries during onboarding (#153914) 2025-10-07 15:02:02 -04:00
starkillerOG 224553f8d9 Reverse Motion Blinds tilt direction (#149777)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-10-07 18:50:39 +01:00
mbo18 7c9f6a061f Add icons for SmartThings climate presets (#153929) 2025-10-07 19:15:15 +02:00
Marc Mueller 8e115d4685 Update pydantic to 2.12.0 (#153937) 2025-10-07 17:50:40 +01:00
Denis Shulyaka 00c189844f Bump openai to 2.2.0 (#153926) 2025-10-07 17:41:52 +01:00
Ståle Storø Hauknes 4587c286bb Add new sensors for Airthings Wave Enhance (#153879) 2025-10-07 17:44:30 +02:00
Artur Pragacz b46097a7fc Move agent functionality from http (#153917) 2025-10-07 14:49:11 +02:00
mbo18 299cb6a2ff Change smart preset name to smart saver (#153916) 2025-10-07 14:11:00 +02:00
Erik Montnemery 1b7b91b328 Remove unused test fixtures from nintendo_parental (#153894) 2025-10-07 14:03:29 +02:00
Maciej Bieniek 01a1480ebd Use aioshelly methods for switches (#153746) 2025-10-07 13:28:58 +02:00
Jordan Harvey 26b8abb118 Bump pynintendoparental to 1.1.1 (#153874) 2025-10-07 13:28:08 +02:00
FMKaiba 53d1bbb530 Add support for gas detector status to SmartThings (#153831)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-10-07 12:56:53 +02:00
Tom Matheussen a3ef55274e Add missing translation string for Satel Integra subentry type (#153905) 2025-10-07 12:18:51 +02:00
Joost Lekkerkerker 2034915457 Add fixture to SmartThings (#153902) 2025-10-07 12:13:12 +02:00
Joost Lekkerkerker 9e46d7964a Update SmartThings comments (#153903) 2025-10-07 11:46:44 +02:00
Maciej Bieniek f9828a227b Bump aioshelly to version 13.12.0 (#153899) 2025-10-07 11:43:56 +02:00
Simone Chemelli 3341fa5f33 Code optimization for Comelit SimpleHome (#153029) 2025-10-07 10:31:44 +01:00
Christopher Fenner e38ae47e76 Add language and location selector to OpenWeatherMap config flow (#153645)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-07 11:06:04 +02:00
Christopher Fenner 934c0e3c4c fix typo in icon assignment of AccuWeather integration (#153890) 2025-10-07 10:15:01 +02:00
Simone Chemelli 994a6ae7ed Fix restore cover state for Comelit SimpleHome (#153887) 2025-10-07 09:06:55 +02:00
Christopher Fenner cdbe93c289 Set display precision for sensors in OpenWeatherMap integration (#153858) 2025-10-07 08:58:18 +02:00
Marc Mueller 56f90e4d96 Update pytest warnings filter (#153881) 2025-10-07 09:55:50 +03:00
TheJulianJES 34977abfec Remove Z-Wave JS voltage sensor overriding suggested precision (#153882) 2025-10-07 08:44:53 +02:00
Marc Mueller 5622103eb1 Fix nintendo_parental RuntimeWarning in tests (#153884) 2025-10-07 08:44:34 +02:00
Artur Pragacz b9a1ab4a44 Clean up core references in conversation (#153880) 2025-10-07 00:46:47 +02:00
David Rapan 18997833c4 Shelly's power sensors naming paradigm standardization (#153822)
Signed-off-by: David Rapan <david@rapan.cz>
2025-10-07 01:32:44 +03:00
David Rapan f99b194afc Shelly's current sensors naming paradigm standardization (#153827)
Signed-off-by: David Rapan <david@rapan.cz>
2025-10-07 01:32:25 +03:00
Dave T 566a347da7 Remove deprecated alarm panel constants (#153876) 2025-10-06 23:03:29 +01:00
Shay Levy 881306f6a4 Migrate Shelly virtual component unique IDs to include roles (#153844) 2025-10-07 00:50:47 +03:00
Marc Mueller f63504af01 Update aiohttp to 3.13.0 (#153875) 2025-10-06 15:47:33 -05:00
derytive d140b82a70 Add plate_count for Miele KM7575 (#153868) 2025-10-06 21:53:09 +02:00
Allen Porter 681211b1a5 Add Model Context Protocol support for OAuth scopes (#153150) 2025-10-06 15:32:42 -04:00
Joost Lekkerkerker 6c8b1f3618 Catch update exception in AirGradient (#153828) 2025-10-06 21:31:55 +02:00
Abílio Costa d341065c34 Replace inner function with lambda in Idasen Desk (#153862) 2025-10-06 21:25:10 +02:00
G Johansson 81b1346080 Handle timeout errors gracefully in Nord Pool services (#153856) 2025-10-06 22:15:38 +03:00
J. Nick Koston 5613be3980 Bump yarl to 1.22.0 (#153860) 2025-10-06 13:43:37 -05:00
Alec fbcf0eb94c Increase connect and configuration time for rfxtrx (#153834)
Increase the allowed time for connection and configuration. Some devices take a long time to respond to configuration changes and this time is counted for both network and configuration of the device.
2025-10-06 20:25:44 +02:00
Felipe Santos 1c7b9cc354 Avoid storing entities list in ONVIF binary_sensor and sensor (#153857) 2025-10-06 19:52:24 +02:00
William Scanlon 75e900606e Update water heater max temperature (#150970) 2025-10-06 19:21:21 +02:00
Norbert Rittel 7c665c53b5 Change translation of box in number to "Input field" for consistency (#153850) 2025-10-06 19:07:48 +02:00
Jordan Harvey f72047eb02 Add new Nintendo Parental Controls integration (#145343)
Co-authored-by: Manu <4445816+tr4nt0r@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-06 18:36:46 +02:00
Marc Mueller ade424c074 Update attrs to 25.4.0 (#153849) 2025-10-06 17:54:19 +02:00
Joost Lekkerkerker 5ad805de3c Add motion presets to SmartThings AC (#153830) 2025-10-06 17:29:33 +02:00
Simone Chemelli ece77cf620 Fix PIN validation for Comelit SimpleHome (#153840) 2025-10-06 17:02:50 +02:00
Simone Chemelli 7eaa559056 Bump aiocomelit to 1.1.1 (#153843) 2025-10-06 16:57:40 +02:00
Allen Porter 08a9377373 Update the MCP Server API endpoint to mcp (#153845) 2025-10-06 16:56:45 +02:00
Jan Bouwhuis a2837e6aee Add MQTT number subentry support (#153358) 2025-10-06 16:50:42 +02:00
Abílio Costa fa03f6194d Remove log file write check (#153842) 2025-10-06 16:49:04 +02:00
Felipe Santos d2851ea1df Deduplicate ONVIF sensor and binary sensor entity names (#153505) 2025-10-06 16:35:48 +02:00
Pavel Tarasov 72f8ac7857 Add BME680 sensor support for Altruist Insight (#153463) 2025-10-06 15:55:37 +02:00
NANI 77a267bc2f Updated VRM client and accounted for missing forecasts (#153464) 2025-10-06 15:37:49 +02:00
Arie Catsman ad238daadc Enphase_envoy to use alternate data source for current transformers (#153621) 2025-10-06 15:33:25 +02:00
Øyvind Matheson Wergeland 42370ba203 Synology DSM: Don't reinitialize API during configuration (#153739) 2025-10-06 15:25:10 +02:00
Christopher Fenner d9691c2a3b Add sensor for hydraulic separator temperature in ViCare integration (#153696) 2025-10-06 15:15:51 +02:00
Michael 66cca981a9 Expose climate current temp as dedicated sensor in FRITZ!SmartHome (#153558) 2025-10-06 15:00:29 +02:00
Ståle Storø Hauknes 9640ebb593 Add support for Wave Enhance and Corentium Home 2 in Airthings BLE integration (#153780) 2025-10-06 14:59:21 +02:00
Joost Lekkerkerker 645f32fd65 Bump pySmartThings to 3.3.1 (#153826) 2025-10-06 14:57:52 +02:00
hanwg cb6e65f972 Refactor Telegram bot entity (#153609)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-06 14:57:46 +02:00
cdnninja 425bdc0ba6 Vesync add oscillation to fan (#153297) 2025-10-06 14:50:11 +02:00
cdnninja c36341e51f vesync correct fan set modes (#153761) 2025-10-06 14:49:05 +02:00
Robert Resch 553d896899 Add Ecovacs active map select entity (#153748)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-06 14:42:01 +02:00
Åke Strandberg ac79b3072e Use customized miele device name if set (#153835) 2025-10-06 14:40:57 +02:00
Andrew Jackson c0aa9bfd4b Update Mealie quality scale to platinum (#153810) 2025-10-06 12:14:05 +02:00
epenet e97100028d Add new test fixture for Tuya cl category (#153800) 2025-10-06 11:51:10 +02:00
tronikos da89617432 Google Assistant SDK: improve init tests (#153795) 2025-10-06 11:49:06 +02:00
dependabot[bot] e6203dffd3 Bump actions/stale from 10.0.0 to 10.1.0 (#153799)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 11:45:39 +02:00
Josef Zweck c13cfe9c37 Re-add AGENTS.md as symlink (#153804) 2025-10-06 11:45:21 +02:00
Ludovic BOUÉ 2447df9341 Add Matter speaker mute toggle (#150104)
Add Matter speaker mute toggle functionality:
- OnOff attribute == True state means volume is on, so HA should show mute switch as off
- OnOff attribute == False means volume is off (muted), so HA should show mute switch as on
2025-10-06 11:09:59 +02:00
Erik Montnemery 1c1fbe0ec1 Log when failing to remove foreign key in recorder EventIDPostMigration (#153812) 2025-10-06 10:58:47 +02:00
Erik Montnemery 4a6d2017fd Fix stale docstring in recorder (#153811) 2025-10-06 10:58:24 +02:00
Simone Chemelli b4997a52df Remove stale entities from Alexa Devices (#153759) 2025-10-06 10:57:21 +02:00
Joris Pelgröm 464dec1dcb Update LetPot integration quality scale to silver (#153783) 2025-10-06 10:28:43 +02:00
tronikos 85506ac78a Google Assistant SDK: use setup_credentials in setup_integration (#153793) 2025-10-06 10:23:44 +02:00
Marc Mueller 6d97355b42 Update raspyrfm-client to 1.2.9 (#153789) 2025-10-06 10:20:15 +02:00
Ludovic BOUÉ f9e75c616a Use TEMPERATURE_SCALING_FACTOR for Matter sensors (#153807) 2025-10-06 10:15:37 +02:00
tronikos a821d02dfb Translate reauthentication error message in Google Assistant SDK (#153797)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-06 01:10:40 -07:00
epenet e05169c7a4 Fix Tuya cover position when only control is available (#153803) 2025-10-06 08:50:38 +02:00
tronikos 1cc3431529 Fix missing google_assistant_sdk.send_text_command (#153735) 2025-10-05 23:46:03 -07:00
Maciej Bieniek 4ba765f265 Add Shelly Wall Display XL to the list of devices without firmware changelog (#153781) 2025-10-06 06:52:15 +02:00
Paulus Schoutsen 50a7af4179 Handle ESPHome discoveries with uninitialized Z-Wave antennas (#153790) 2025-10-05 23:06:10 -04:00
Allen Porter e0a2116e88 Update MCP server to support the newer HTTP protocol (#153779)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2025-10-05 21:16:53 -04:00
Aidan Timson d8e1ed5f4a Fix power device classes for system bridge (#153201) 2025-10-05 23:52:35 +02:00
Denis Shulyaka f1b8e8a963 Ollama thinking content (#150393) 2025-10-05 17:33:45 -04:00
Marc Mueller 9a9fd44c62 Use yaml anchors in ci workflow (#152586) 2025-10-05 23:21:38 +02:00
G Johansson bc3fe7a18e Use automatic reload options flow in min_max (#153143) 2025-10-05 23:17:37 +02:00
G Johansson 19f3559345 Remove previously deprecated template attach function (#153370) 2025-10-05 23:16:57 +02:00
Jan Bouwhuis fad0e23797 Allow to set the manufacturer in a MQTT device subentry setup (#153747) 2025-10-05 23:15:00 +02:00
Erik Montnemery 7f931e4d70 Add device class filter to hydrawise services (#153249) 2025-10-05 23:14:12 +02:00
Erik Montnemery a04835629b Make hassfest fail on services with device filter on targets (#152794) 2025-10-05 23:13:33 +02:00
Andrew Jackson 78cd80746d Bump aiomealie to 1.0.0, update min Mealie instance version to v2. (#153203) 2025-10-05 23:12:05 +02:00
G Johansson 9ac93920d8 Cleanup process_fds addition in systemmonitor (#153568) 2025-10-05 22:41:19 +02:00
G Johansson 1818fce1ae Validating schema outside the event loop will now fail (#153472) 2025-10-05 22:37:14 +02:00
Erik Montnemery f524edc4b9 Add pytest command line option to drop recorder db before test (#153527) 2025-10-05 22:36:24 +02:00
Paulus Schoutsen 19f990ed31 ESPHome to set Z-Wave discovery as next_flow (#153706) 2025-10-05 22:33:12 +02:00
David Rapan 5d83c82b81 Shelly's energy sensors naming paradigm standardization (#153729) 2025-10-05 22:32:39 +02:00
Fredrik Erlandsson d63d154457 Daikin increase timeout (#153722)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-05 22:18:31 +02:00
Josef Zweck 933b15ce36 Revert "AGENTS.md" (#153777) 2025-10-05 22:05:04 +02:00
Denis Shulyaka 6ec7b63ebe Add support for Anthropic Claude Sonnet 4.5 (#153769) 2025-10-05 21:29:24 +02:00
Denis Shulyaka 26bfbc55e9 Bump anthropic to 0.69.0 (#153764) 2025-10-05 20:59:50 +02:00
Simone Chemelli d75ca0f5f3 Bump aioamazondevices to 6.2.9 (#153756) 2025-10-05 20:59:02 +02:00
Sander Jochems fed8f137e9 Upgrade python-melcloud to 0.1.2 (#153742) 2025-10-05 19:49:22 +02:00
Josef Zweck f44d65e023 Migrate tolo to entry.runtime_data (#153744) 2025-10-05 18:43:37 +02:00
Christopher Fenner a270bd76de Add sensors for battery charge amount to ViCare integration (#153631)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-05 18:34:45 +02:00
Christopher Fenner 9209e419ec Change style for critical number entities in ViCare integration (#153634) 2025-10-05 16:36:42 +02:00
Ståle Storø Hauknes 98f8f15e90 Fix crash when setting up Airthings BLE device (#153510) 2025-10-05 09:18:47 -05:00
Denis Shulyaka b2a2868afd AGENTS.md (#153680) 2025-10-05 15:51:46 +02:00
J. Nick Koston 0d4737d360 Bump aiohomekit to 3.2.20 (#153750) 2025-10-05 15:49:55 +02:00
Ståle Storø Hauknes 2b370a0eca Use full serial number when adding an Airthings device (#153499) 2025-10-05 08:23:02 -05:00
Ståle Storø Hauknes 618fe81207 Check if firmware is outdated when adding an Airthings BLE device (#153559) 2025-10-05 14:49:34 +02:00
Maciej Bieniek c0fe4861f9 Align Shelly presencezone entity to the new API/firmware (#153737) 2025-10-05 15:36:57 +03:00
Simone Chemelli dfd33fdab1 Fix sensors availability check for Alexa Devices (#153743) 2025-10-05 14:26:16 +02:00
Josef Zweck cceee05c15 Fix lamarzocco brewing start time sensor availability (#153732) 2025-10-05 13:04:28 +02:00
Manu f560d2a05e Update suggested display precision for ntfy attachment size to 2 (#153741) 2025-10-05 13:03:55 +02:00
Ville Skyttä 3601cff88e Upgrade upcloud-api to 2.9.0 (#153727) 2025-10-05 13:58:35 +03:00
Maciej Bieniek ca5c0a759f Remove Shelly presencezone component from VIRTUAL_COMPONENTS tuple (#153740) 2025-10-05 13:46:42 +03:00
Tom 6f9e6909ce Bump airOS to 0.5.5 using formdata for v6 firmware (#153736) 2025-10-05 12:43:43 +02:00
Manu ccf563437b Bump aiontfy to v0.6.1 (#153738) 2025-10-05 12:34:18 +02:00
Josef Zweck 78e97428fd Add debouncer to acaia (#153725) 2025-10-05 12:31:45 +02:00
Denis Shulyaka 8b4c730993 Gemini: Use default model instead of recommended where applicable (#153676) 2025-10-05 02:59:51 -07:00
Fredrik Erlandsson 0a071a13e2 Version bump pydaikin to 2.17.1 (#153726) 2025-10-05 11:12:10 +02:00
Shay Levy ab80991eac Add Shelly support for climate entities (#153450)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-05 11:53:52 +03:00
Erwin Douna ee7262efb4 Portainer add button platform (#153063) 2025-10-05 10:36:52 +02:00
Fredrik Erlandsson ea5a52cdc8 Version bump pydaikin to 2.17.0 (#153718) 2025-10-05 10:49:14 +03:00
tronikos 31fe0322ab Clarify description for media player entity in Google Assistant SDK (#153715) 2025-10-05 09:47:46 +02:00
tronikos e8e0eabb99 Double max retries in Google Drive (#153717) 2025-10-05 10:35:50 +03:00
tronikos 1629dad1a8 Bump opower to 0.15.6 (#153714) 2025-10-05 09:25:45 +02:00
Shay Levy d9baad530a Shelly code quality and cleanup (#153692) 2025-10-05 09:14:13 +02:00
cdnninja 4a1d00e59a Bump pyvesync to 3.1.0 (#153693) 2025-10-05 09:56:25 +03:00
Daniel Hjelseth Høyer 437e4e027c Bump Mill library (#153683)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2025-10-05 09:55:48 +03:00
J. Nick Koston 3726f7eca9 Bump zeroconf to 0.148.0 (#153704) 2025-10-04 21:57:00 -05:00
Marc Mueller c943cf515c Add zeroconf to hassfest version requirements (#153703) 2025-10-05 02:55:35 +02:00
Christopher Fenner 3b0c2a7e56 Fix ViCare pressure sensors missing unit of measurement (#153691) 2025-10-05 02:14:32 +02:00
Christopher Fenner 6ebaa9cd1d Bump PyViCare to 2.52.0 (#153629) 2025-10-05 02:05:53 +02:00
J. Nick Koston f81c32f6ea Bump aioesphomeapi to 41.12.0 (#153698) 2025-10-04 18:55:36 -05:00
J. Nick Koston c0cd7a1a62 Bump propcache to 0.4.0 (#153694) 2025-10-04 18:03:53 -05:00
J. Nick Koston 7a61c818c6 Bump ulid-transform to 1.5.2 (#153690) 2025-10-04 23:51:59 +02:00
J. Nick Koston 2800625bcf Bump dbus-fast to 2.44.5 (#153686) 2025-10-04 23:04:37 +02:00
J. Nick Koston cfec998221 Bump fnv-hash-fast to 1.6.0 (#153682) 2025-10-04 15:25:11 -05:00
Joakim Plate 7203cffbd7 Schedule update coordinator again if it is active (#153596) 2025-10-04 15:13:41 -05:00
Josef Zweck 23397ef6a9 Smarter calculation of chunk size in onedrive (#153679)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-04 21:52:58 +02:00
Joakim Plate 0e154635ff Limit shelly tests to single platform (#153681) 2025-10-04 22:45:48 +03:00
J. Nick Koston 2e6e518722 Bump cached-ipaddress to 1.0.1 (#153670) 2025-10-04 13:49:13 -05:00
J. Nick Koston e0cded97c7 Bump bleak-esphome to 3.4.0 (#153669) 2025-10-04 20:38:56 +02:00
J. Nick Koston 87a6a029bb Bump habluetooth to 5.7.0 (#153665) 2025-10-04 12:16:51 -05:00
Jan Bouwhuis 1cc3c22d3f Fix MQTT Lock state reset to unknown when a reset payload is received (#153647) 2025-10-04 17:32:18 +02:00
Marc Mueller 2341d1d965 Fix flaky template test (#153624) 2025-10-04 18:28:36 +03:00
J. Nick Koston a0bae9485c Bump bluetooth-data-tools to 1.28.3 (#153653) 2025-10-04 18:27:09 +03:00
J. Nick Koston f281b0fc6b Bump annotatedyaml to 1.0.2 (#153651) 2025-10-04 18:26:55 +03:00
Manu 6f89fe81cc Remove Plum Lightpad integration (#153590) 2025-10-04 16:42:28 +02:00
Hessel 34f6ead7a1 Wallbox fix Rate Limit issue for multiple chargers (#153074) 2025-10-04 16:38:11 +02:00
Kevin McCormack 8985527a87 Bump libpyvivotek to 0.6.1 and add strict typing for Vivotek integration (#153342)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-04 16:34:37 +02:00
Marc Mueller bd87a3aa4d Update PyYAML to 6.0.3 (#153626) 2025-10-04 16:27:22 +02:00
Manu 768a505904 Add translations and icons to OralB integration (#153605) 2025-10-04 16:23:27 +02:00
Marc Mueller d97c1f0fc3 Update grpcio to 1.75.1 (#153643) 2025-10-04 16:21:16 +02:00
Joakim Plate c3fcd34d4c Fix blue current mocking out platform with empty string (#153604)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-04 15:17:56 +02:00
Joakim Plate 44d9eaea95 Correct kraken test issues (#153601) 2025-10-04 12:25:35 +02:00
G Johansson 0f34f5139a Fix sql repair string (#153619) 2025-10-04 10:39:26 +02:00
Ludovic BOUÉ 2afb1a673d Add Matter Thermostat OccupancySensor (#153166)
Co-authored-by: Björn Ebbinghaus <bjoern@ebbinghaus.me>
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2025-10-04 10:31:17 +02:00
G Johansson c2f7f29630 Setup platform services during integration start in sensibo (#153571) 2025-10-04 10:30:01 +02:00
G Johansson b01f5dd24b Raise repairs on platform setup for sql (#153581) 2025-10-04 07:02:36 +02:00
Luke Lashley 0cda0c449f Update the map parser in Roborock vacuum to use coord parser. (#153520) 2025-10-03 19:59:57 -07:00
dollaransh17 40fdf12bc9 Fix string interpolation in local_todo error messages (#153580)
Co-authored-by: dollaransh17 <dollaransh17@users.noreply.github.com>
2025-10-03 19:57:50 -07:00
Luke Lashley 3939a80302 Switch Roborock to v4 of the code login api (#153593) 2025-10-03 19:41:39 -07:00
Luke Lashley d32a102613 Add two new consumable sensors to Roborock (#153606) 2025-10-03 19:38:29 -07:00
Felipe Santos 20949d39c4 Address comments for the add-on switch entity (#153518) 2025-10-04 02:22:59 +02:00
Kevin Stillhammer 310a0c8d13 Use SensorDescription for GoogleTravelTimeSensor (#153585) 2025-10-04 01:29:26 +02:00
Arie Catsman c9e80ac7e9 Extend enphase_envoy test data with new library data fields (#153591) 2025-10-04 01:24:11 +02:00
G Johansson 5df4e9e1cf Bump pynordpool to 0.3.1 (#153599) 2025-10-04 01:23:09 +02:00
Simone Chemelli 4022ee74e8 Bump aioamazondevices to 6.2.8 (#153592) 2025-10-04 01:22:15 +02:00
Erwin Douna 80a4115c44 Portainer follow-up points (#153594) 2025-10-03 22:36:55 +02:00
Manu ce548efd80 Remove IBM Watson IoT Platform integration (#153567) 2025-10-03 21:18:39 +02:00
dependabot[bot] 2edf622b41 Bump github/codeql-action from 3.30.5 to 3.30.6 (#153524)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 21:15:47 +02:00
Manu 66ac9078aa Improve Habitica tests (#153573) 2025-10-03 20:55:38 +02:00
Erwin Douna ba75f18f5a Portainer add switch platform (#153485)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-03 20:52:37 +02:00
Arie Catsman 8ee2ece03e Bump pyenphase to 2.4.0 (#153583) 2025-10-03 21:14:34 +03:00
Michael 7060ab8c44 Remove Vultr integration (#153560) 2025-10-03 21:12:26 +03:00
Paulus Schoutsen 85d8244b8a When discovering a Z-Wave adapter, always configure add-on in config flow (#153575) 2025-10-03 19:16:51 +02:00
Abílio Costa 3f9421ab08 Debounce updates in Idasen Desk (#153503) 2025-10-03 18:39:14 +02:00
Luke Lashley 2f3fbf00b7 Bump python-roborock to 2.50.2 (#153561) 2025-10-03 09:30:30 -07:00
Paulus Schoutsen d595ec8a07 Z-Wave to support migrating from USB to socket with same home ID (#153522) 2025-10-03 10:46:12 -04:00
Ståle Storø Hauknes 4ff5462cc4 Bump Airthings BLE to 1.1.1 (#153529) 2025-10-03 15:50:20 +02:00
Shay Levy 404f95b442 Add Shelly support for valve entities (#153348) 2025-10-03 15:04:35 +03:00
cdnninja 89cf784022 Fix VeSync zero fan speed handling (#153493)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-10-03 11:56:02 +02:00
Copilot 02142f352d Fix awair integration AttributeError when update listener accesses runtime_data (#153521)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2025-10-03 05:49:41 -04:00
Stefan Agner ec3dd7d1e5 Add num open fds sensor to systemmonitor (#152441)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-10-03 10:53:02 +02:00
Ståle Storø Hauknes 7355799030 Fix typo in Airthings BLE config flow (#153512) 2025-10-03 08:20:17 +02:00
Erik Montnemery 982166df3c Remove module recorder.history.modern (#153502) 2025-10-03 01:00:09 +02:00
puddly c7d3512ad2 Bump universal-silabs-flasher to 0.0.35 (#153500) 2025-10-03 00:18:05 +02:00
Aidan Timson ada6f7b3fb Update ovoenergy to 3.0.2 (#153488) 2025-10-02 22:44:28 +01:00
Erik Montnemery 78e16495bd Remove runtime support for recorder DB without States.last_reported_ts (#153495) 2025-10-02 23:15:15 +02:00
starkillerOG 12085e6152 Improve Reolink docstrings (#153498) 2025-10-02 23:10:27 +02:00
starkillerOG 6764463689 Use new Reolink rec_enable flag (#153496) 2025-10-02 23:09:43 +02:00
starkillerOG 7055276665 Allign naming of Reolink host switch entities (#153494) 2025-10-02 23:09:17 +02:00
starkillerOG 71b3ebd15a Cleanup reolink update entity migration (#153492) 2025-10-02 22:49:55 +02:00
starkillerOG b87910e596 Bump reolink-aio to 0.16.1 (#153489) 2025-10-02 22:49:39 +02:00
Erik Montnemery e19bfd670b Bump recorder live schema migration to schema version 48 (#153404) 2025-10-02 22:47:01 +02:00
dollaransh17 7b3c96e80b Remove deprication code for reolink Hub switches (#153483)
Thank you, good work!
2025-10-02 22:37:08 +02:00
Erik Montnemery 01ff3cf9d9 Start recorder data migration after schema migration (#153471) 2025-10-02 22:21:49 +02:00
Erik Montnemery d66da0c10d Respect filtering of WS subscribe_entities when there are unserializalizable states (#153262) 2025-10-02 22:20:45 +02:00
Josef Zweck 3491bb1b40 Fix missing parameter pass in onedrive (#153478) 2025-10-02 22:17:56 +02:00
G Johansson 3bf995eb71 Fix next event in workday calendar (#153465) 2025-10-02 22:17:11 +02:00
Joost Lekkerkerker 2169ce1722 Remove state attributes from Firefly 3 (#153285) 2025-10-02 22:14:51 +02:00
Joost Lekkerkerker 275e9485e9 Fix missing powerconsumptionreport in Smartthings (#153438) 2025-10-02 22:08:48 +02:00
Daniel Hjelseth Høyer 95198ae540 Bump pyTibber to 0.32.2 (#153484) 2025-10-02 21:04:32 +02:00
Aidan Timson aed2d3899d Update OVOEnergy to 3.0.1 (#153476) 2025-10-02 21:04:16 +02:00
Erik Montnemery 4011d62ac7 Improve enable_migrate_event_ids recorder test fixture (#153470)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-10-02 21:03:26 +02:00
Björn Ebbinghaus d2aa0573de Add relative humidity to matter climate entities (#152554)
OK after talking with Marcel.
2025-10-02 20:44:19 +02:00
Ståle Storø Hauknes 571b2e3ab6 Fix Airthings config flow description (#153452) 2025-10-02 20:38:27 +02:00
peteS-UK a7f48360b7 Add PARALLEL_UPDATES to Squeezebox switch platform (#153477) 2025-10-02 20:32:37 +02:00
Erik Montnemery 22f2f8680a Improve recorder migration tests dropping indices (#153456) 2025-10-02 20:16:24 +02:00
Aidan Timson d92004a9e7 Add missing translation for media browser default title (#153430)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-10-02 19:33:57 +02:00
Norbert Rittel 64875894d6 Fix sentence-casing in user-facing strings of slack (#153427) 2025-10-02 19:27:28 +02:00
Ståle Storø Hauknes 3f7a288526 Add data_description field for Airthings BLE (#153442) 2025-10-02 19:25:59 +02:00
Ståle Storø Hauknes a2a067a81c Add serial number to the list of discovered devices (#153448) 2025-10-02 19:25:19 +02:00
Erwin Douna f9f61b8da7 Portainer add configuration URL's (#153466) 2025-10-02 19:22:34 +02:00
Paul Bottein cd69b82fc9 Add light, security and climate panel (#153261) 2025-10-02 13:06:53 -04:00
Shay Levy d20631598e Bump aioshelly 13.11.0 (#153458) 2025-10-02 19:44:24 +03:00
puddly 229ebe16f3 Disable baudrate bootloader reset for ZBT-2 (#153443) 2025-10-02 17:36:10 +01:00
G Johansson a172f67d37 Fix Nord Pool 15 minute interval (#153350) 2025-10-02 18:26:33 +02:00
Joost Lekkerkerker ee4a1de566 Add translation for turbo fan mode in SmartThings (#153445)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-02 18:45:37 +03:00
epenet 7ab99c028c Add new test fixture for Tuya wk category (#153457) 2025-10-02 17:29:14 +02:00
TheJulianJES 0e1d12b1ae Fix Z-Wave RGB light turn on causing rare ZeroDivisionError (#153422) 2025-10-02 17:26:49 +02:00
Artur Pragacz e090ddd761 Move entities to the end of devices in analytics payload (#153449) 2025-10-02 16:36:38 +02:00
Stefan Agner 9721ce6877 Update Home Assistant base image to 2025.10.0 (#153441) 2025-10-02 15:17:59 +02:00
MoonDevLT 8dde94f421 Add Lunatone gateway integration (#149182)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-02 12:55:17 +01:00
dollaransh17 f5f6b22af1 Fix spelling error in logbook tests (#153417)
Co-authored-by: dollaransh17 <dollaransh17@users.noreply.github.com>
2025-10-02 12:49:22 +02:00
Tom Matheussen f8a93b6561 Add Quality Scale to Satel Integra (#153122) 2025-10-02 12:48:34 +02:00
epenet 840a03f048 Add new dehumidifier fixture for Tuya (#153407) 2025-10-02 12:15:28 +02:00
Erwin Douna 85f3b5ce78 Firefly III add re-auth flow (#153303)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-10-02 12:15:10 +02:00
Michael f4284fec2f Explicit pass in the config entry to coordinator in airtouch4 (#153361)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-10-02 11:54:20 +02:00
Abílio Costa 3a89b3152f Move common Uptime Robot new device check logic to helper (#153094)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-02 10:52:22 +01:00
epenet a0356328c3 Use walrus and combine conditions in Tuya alarm control panel (#153426) 2025-10-02 11:52:08 +02:00
Michael J. Kidd 4b6f37b1d7 Pushover: Handle empty data section properly (#153397) 2025-10-02 11:48:03 +02:00
johanzander 716705fb5a Adds token authentication and usage of official API for Growatt MIN/TLX inverters (#149783) 2025-10-02 11:40:53 +02:00
J. Nick Koston d246836480 Bump aiohomekit to 3.2.19 (#153423) 2025-10-02 11:17:17 +02:00
Guido Schmitz 6ee2b82d15 Cleanup sync_callback in devolo Home Control (#153321) 2025-10-02 11:11:19 +02:00
Luke Lashley 73ff8d36a5 Bump python-roborock to 2.49.1 (#153396) 2025-10-02 11:09:44 +02:00
Manu 1397def3b8 Add last check-in sensor to Habitica integration (#153293) 2025-10-02 11:03:21 +02:00
epenet d443529041 Add more sensors to Tuya weather monitor (#153420) 2025-10-02 10:58:24 +02:00
G Johansson 373bb20f1b Remove deprecated entity feature constants in vacuum (#153364) 2025-10-02 10:46:34 +02:00
Erik Montnemery 3b44cce6dc Improve recorder migration test (#153405) 2025-10-02 10:45:04 +02:00
Joakim Plate 46056fe45b Correct blocking update in ToGrill with lack of notifications (#153387) 2025-10-02 10:44:42 +02:00
epenet 1816c190b2 Add test fixture for new Tuya cjkg category (#153411) 2025-10-02 10:43:54 +02:00
Josef Zweck 00abaee6b3 Increase onedrive upload chunk size (#153406) 2025-10-02 10:43:10 +02:00
Manu 3a301f54e0 Update markdown field description in ntfy integration (#153421) 2025-10-02 10:40:33 +02:00
Denis Shulyaka 762accbd6d Disable thinking for unsupported gemini models (#153415) 2025-10-02 10:38:31 +02:00
Tom Matheussen e0422d7d34 Fix Satel Integra creating new binary sensors on YAML import (#153419) 2025-10-02 10:37:41 +02:00
Erwin Douna 6ba2057a88 Bump pyportainer 1.0.3 (#153413) 2025-10-02 10:34:11 +02:00
epenet 752969bce5 Add test fixture for new Tuya jsq category (#153412) 2025-10-02 10:33:02 +02:00
Franck Nijhof efbdfd2954 Merge branch 'master' into dev 2025-10-02 07:06:58 +00:00
Erik Montnemery bb7a177a5d Improve recorder migration tests (#153388) 2025-10-02 07:45:04 +02:00
Kinachi249 9b56ca8cde Bump PyCync to 0.4.1 (#153401) 2025-10-02 07:11:34 +02:00
starkillerOG b0a08782e0 Add Roborock mop intensity translations (#153380) 2025-10-01 22:51:26 +02:00
G Johansson 6c9955f220 Remove deprecated constants in camera (#153363) 2025-10-01 22:20:34 +02:00
G Johansson f56b94c0f9 Remove deprecated constants from media_player (#153366) 2025-10-01 22:20:07 +02:00
G Johansson 3cf035820b Remove deprecated state constants from lock (#153367) 2025-10-01 22:16:52 +02:00
Erik Montnemery 99a796d066 Remove legacy history queries from recorder (#153324) 2025-10-01 22:06:56 +02:00
Erik Montnemery 1cd1b1aba8 Remove to_native method from recorder database schemas (#153334) 2025-10-01 21:25:05 +02:00
Ståle Storø Hauknes 4131c14629 Add parallel updates to airthings_ble (#153315) 2025-10-01 20:14:23 +02:00
Tom c2acda5796 Bump airOS module for alternative login url (#153317) 2025-10-01 20:11:35 +02:00
Marc Mueller 4806e7e9d9 Update cryptography to 46.0.2 (#153327) 2025-10-01 19:52:57 +02:00
Marc Mueller 76606fd44f Update types packages (#153330) 2025-10-01 19:51:37 +02:00
Andre Lengwenus 2983f1a3b6 Explicitly check for None in raw value processing of modbus (#153352) 2025-10-01 19:48:35 +02:00
Michael 8019779b3a Set config entry to None in ProxmoxVE (#153357) 2025-10-01 19:45:34 +02:00
Marc Mueller 62cdcbf422 Misc typing improvements (#153322) 2025-10-01 19:30:41 +02:00
Marc Mueller b12a5a36e1 Update bcrpyt to 5.0.0 (#153325) 2025-10-01 20:07:45 +03:00
epenet e32763e464 Add water heater fixture for Tuya tests (#153336) 2025-10-01 20:02:54 +03:00
Stefan Agner b85cf3f9d2 Bump aiohasupervisor to 0.3.3 (#153344) 2025-10-01 20:01:53 +03:00
puddly 3777bcc2af Do not reset the adapter twice during ZHA options flow migration (#153345) 2025-10-01 18:22:41 +02:00
Franck Nijhof 9a29cc53ef 2025.10.0 (#152881) 2025-10-01 18:17:36 +02:00
Maciej Bieniek 52cde48ff0 Add missing test for Shelly config flow (#153346) 2025-10-01 18:32:57 +03:00
Marc Mueller bf1da35303 Update pyOpenSSL to 25.3.0 (#153329) 2025-10-01 17:32:08 +02:00
Franck Nijhof 55d5e769b2 Bump version to 2025.10.0 2025-10-01 15:19:48 +00:00
Erwin Douna c1bf11da34 Bump pyportainer 1.0.2 (#153326) 2025-10-01 17:07:21 +02:00
Erwin Douna 3c20325b37 Bump pyfirefly 0.1.6 (#153335) 2025-10-01 17:06:31 +02:00
Franck Nijhof 6cd1283b00 Bump version to 2025.10.0b7 2025-10-01 14:51:37 +00:00
Maciej Bieniek dde60cdecb Improve mac_address_from_name() function to avoid double discovery of Shelly devices (#153343) 2025-10-01 14:51:09 +00:00
Michael Hansen f03b16bdf8 Bump intents to 2025.10.1 (#153340) 2025-10-01 14:51:07 +00:00
Maciej Bieniek fd8ccb8d8f Improve mac_address_from_name() function to avoid double discovery of Shelly devices (#153343) 2025-10-01 16:49:27 +02:00
Michael Hansen d76e947021 Bump intents to 2025.10.1 (#153340) 2025-10-01 09:39:08 -05:00
Erik Montnemery c91ed96543 Use pytest.mark.usefixtures in history tests (#153306) 2025-10-01 15:53:55 +02:00
HarvsG b164531ba8 Bayesian - add config entry tests (#153316) 2025-10-01 15:46:16 +02:00
Erik Montnemery 7c623a8704 Use pytest.mark.usefixtures in some recorder tests (#153313) 2025-10-01 15:38:51 +02:00
Maciej Bieniek 7ae3340336 Add test for full device snapshot for Shelly Wall Display XL (#153305) 2025-10-01 16:00:15 +03:00
Marc Mueller 653b73c601 Fix device_automation RuntimeWarning in tests (#153319) 2025-10-01 14:26:09 +02:00
Franck Nijhof f616e5a4e3 Bump version to 2025.10.0b6 2025-10-01 10:41:01 +00:00
Artur Pragacz c0317f60cc Add analytics platform to esphome (#153311) 2025-10-01 10:40:41 +00:00
Bram Kragten 8abfe424e1 Update frontend to 20251001.0 (#153300) 2025-10-01 10:40:40 +00:00
HarvsG 8de200de0b Fix Bayesian ConfigFlow templates in 2025.10 (#153289)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-01 10:40:38 +00:00
Joost Lekkerkerker f242e294be Add Konnected brand (#153280) 2025-10-01 10:40:36 +00:00
Joost Lekkerkerker 58cc7c8f84 Add Level brand (#153279) 2025-10-01 10:40:35 +00:00
Joost Lekkerkerker bd10f6ec08 Require cloud for Aladdin Connect (#153278)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-01 10:40:33 +00:00
puddly ed9cfb4c4b Use hardware bootloader reset methods for firmware config flows (#153277) 2025-10-01 10:40:32 +00:00
Joost Lekkerkerker a6b6e4c4b8 Add Eltako brand (#153276) 2025-10-01 10:40:31 +00:00
Robert Resch 36ff5c0d45 Bump aioecowitt to 2025.9.2 (#153273) 2025-10-01 10:40:29 +00:00
Artur Pragacz de6d34fec5 Filter out service type devices in extended analytics (#153271) 2025-10-01 10:40:28 +00:00
Erwin Douna 38f9067970 Portainer fix CONF_VERIFY_SSL (#153269)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-10-01 10:40:27 +00:00
Norbert Rittel 53a8a250d0 Replace "Climate name" with "Climate program" in ecobee action (#153264) 2025-10-01 10:40:25 +00:00
Artur Pragacz 00f6d26ede Add analytics platform to wled (#153258) 2025-10-01 10:40:24 +00:00
andreimoraru 6d09411c07 Bump yt-dlp to 2025.09.26 (#153252) 2025-10-01 10:40:22 +00:00
TheJulianJES 037e2bfd31 Fix ZHA unable to select "none" flow control (#153235) 2025-10-01 10:40:21 +00:00
TheJulianJES c893552d4a Replace remaining ZHA "radio" strings with "adapter" (#153234) 2025-10-01 10:40:20 +00:00
TheJulianJES 4fd10162c9 Improve ZHA multi-pan firmware repair text (#153232) 2025-10-01 10:40:18 +00:00
Joris Pelgröm 392ee5ae7e Use UnitOfTime.DAYS instead of custom unit for LetPot number entity (#153054) 2025-10-01 10:40:17 +00:00
puddly bf190609a0 Reduce Connect firmware install times by removing unnecessary firmware probing (#153012) 2025-10-01 10:40:15 +00:00
Samuel Xiao e982ac1e53 Switchbot Cloud: Fix Roller Shade not work issue (#152528) 2025-10-01 10:40:14 +00:00
Pete Sage b4747ea87b Fix Sonos Dialog Select type conversion part II (#152491)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-10-01 10:40:12 +00:00
HarvsG df69bcecb7 Pihole better logging of update errors (#152077) 2025-10-01 10:40:11 +00:00
Artur Pragacz 7c93d91bae Filter out service type devices in extended analytics (#153271) 2025-10-01 12:38:50 +02:00
Abílio Costa 07da0cfb2b Stop writing to config dir log file on supervised install (#146675)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-10-01 11:11:00 +01:00
Artur Pragacz b411a11c2c Add analytics platform to esphome (#153311) 2025-10-01 12:08:50 +02:00
epenet 0555b84d05 Add new cover fixture for Tuya (#153310) 2025-10-01 12:01:37 +02:00
TheJulianJES 790bddef63 Improve ZHA multi-pan firmware repair text (#153232) 2025-10-01 11:50:01 +02:00
TheJulianJES a3089b8aa7 Replace remaining ZHA "radio" strings with "adapter" (#153234) 2025-10-01 11:46:08 +02:00
puddly 77c8426d63 Use hardware bootloader reset methods for firmware config flows (#153277) 2025-10-01 11:43:28 +02:00
TheJulianJES faf226f6c2 Fix ZHA unable to select "none" flow control (#153235) 2025-10-01 11:42:50 +02:00
HarvsG 06d143b81a Fix Bayesian ConfigFlow templates in 2025.10 (#153289)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-10-01 11:39:23 +02:00
Erik Montnemery 08b6a0a702 Add device class filter to switcher_kis services (#153248) 2025-10-01 12:27:17 +03:00
Bram Kragten a20d1e3656 Update frontend to 20251001.0 (#153300) 2025-10-01 09:50:30 +02:00
Erwin Douna 36cc3682ca Add Firefly III integration (#147062)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-30 23:34:33 +02:00
Aviad Levy 1b495ecafa Add support for errored torrents in qBittorrent sensor (#153120)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-09-30 23:34:15 +02:00
puddly 7d1a0be07e Reduce Connect firmware install times by removing unnecessary firmware probing (#153012) 2025-09-30 22:41:51 +02:00
Geoffrey 327f65c991 Add switch domain to VegeHub integration (#148436)
Co-authored-by: GhoweVege <85890024+GhoweVege@users.noreply.github.com>
2025-09-30 22:38:05 +02:00
Manu 4ac89f6849 Add notify platform to Habitica (#150553) 2025-09-30 22:35:55 +02:00
Nojus db3b070ed0 Add meteo_lt integration (#152948)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-30 22:17:36 +02:00
anishsane 6d940f476a Add support for Media player Mute/Unmute intents (#150508) 2025-09-30 14:37:19 -05:00
Erwin Douna 1ca701dda4 Portainer fix CONF_VERIFY_SSL (#153269)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-09-30 21:36:04 +02:00
Joost Lekkerkerker 291c44100c Add Eltako brand (#153276) 2025-09-30 21:29:58 +02:00
Joost Lekkerkerker c8d676e06b Add Konnected brand (#153280) 2025-09-30 21:27:43 +02:00
Joost Lekkerkerker 4c1ae0eddc Add Level brand (#153279) 2025-09-30 21:21:21 +02:00
Norbert Rittel 39eadc814f Replace "Climate name" with "Climate program" in ecobee action (#153264) 2025-09-30 21:16:37 +02:00
Robert Resch f7ecad61ba Bump aioecowitt to 2025.9.2 (#153273) 2025-09-30 20:58:34 +02:00
Norbert Rittel fa4cb54549 Fix sentence-casing in two title strings of roomba (#153281) 2025-09-30 20:51:44 +02:00
Manu 2be33c5e0a Update quality scale of ntfy integration to platinum 🏆️ (#151785) 2025-09-30 20:36:18 +02:00
LG-ThinQ-Integration 904d7e5d5a Add air/water filter state in percent to LG ThinQ (#152150)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-30 20:26:47 +02:00
Pete Sage dbc4a65d48 Fix Sonos Dialog Select type conversion part II (#152491)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-30 20:25:19 +02:00
Pete Sage b93f4aabf1 Add tests for Sonos media metadata (#152622) 2025-09-30 20:24:57 +02:00
Joost Lekkerkerker 9eaa40c7a4 Require cloud for Aladdin Connect (#153278)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-30 19:57:24 +02:00
Lucas Mindêllo de Andrade b308a882fb Add Roomba J9 compatibility to the roomba integration (#145913)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-09-30 19:10:22 +02:00
Erik Montnemery 7f63ba2087 Improve saved state of RestoreSensor when using freezegun (#152740) 2025-09-30 18:27:56 +02:00
Erik Montnemery d7269cfcc6 Use pytest_unordered in additional service helper tests (#153255) 2025-09-30 18:26:32 +02:00
starkillerOG 2850a574f6 Add Reolink floodlight event entities (#152564) 2025-09-30 17:59:12 +02:00
Samuel Xiao dcb8d4f702 Add support model [relay switch 2pm] for switchbot cloud (#148381) 2025-09-30 17:49:32 +02:00
Samuel Xiao aeadc0c4b0 Add lock support to Switchbot Cloud (#148310) 2025-09-30 17:48:38 +02:00
Nathan Spencer 683c6b17be Add release url to Litter-Robot 4 update entity (#152504) 2025-09-30 17:47:27 +02:00
Samuel Xiao 69dd5c91b7 Switchbot Cloud: Fix Roller Shade not work issue (#152528) 2025-09-30 17:05:23 +02:00
HarvsG 5cf7dfca8f Pihole better logging of update errors (#152077) 2025-09-30 16:59:03 +02:00
Marc Mueller 62a49d4244 Update pandas to 2.3.3 (#153251) 2025-09-30 16:58:41 +02:00
falconindy 93ee6322f2 snoo: add button entity for calling start_snoo (#151052)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-09-30 16:57:58 +02:00
Artur Pragacz 914990b58a Add analytics platform to wled (#153258) 2025-09-30 10:39:32 -04:00
Joakim Sørensen f78bb5adb6 Bump hass-nabucasa from 1.1.2 to 1.2.0 (#153250) 2025-09-30 15:29:04 +02:00
Erik Montnemery 905f5e7289 Add device class filter to entity services (#153247) 2025-09-30 14:28:04 +01:00
Erik Montnemery ec503618c3 Handle errors in WS manifest/list (#153256) 2025-09-30 15:12:41 +02:00
Erik Montnemery 7a41cbc314 Skip unserializable flows in WS config_entries/flow/subscribe (#153259) 2025-09-30 15:12:19 +02:00
Erik Montnemery c58ba734e7 Correct target filter in osoenergy services (#153244) 2025-09-30 14:06:14 +02:00
Erik Montnemery 68f63be62f Correct target filter in litterrobot services (#153243) 2025-09-30 14:05:46 +02:00
Erik Montnemery 2aa4ca1351 Correct homekit service definition (#153242) 2025-09-30 14:04:09 +02:00
Imeon-Energy fbabb27787 Add forecast energy sensor to Imeon inverter integration (#152176)
Co-authored-by: TheBushBoy <theodavid@icloud.com>
2025-09-30 13:35:18 +02:00
Markus Jacobsen 0960d78eb5 Use initial received WebSocket state in Bang & Olufsen (#152432) 2025-09-30 13:34:43 +02:00
andreimoraru 474b40511f Bump yt-dlp to 2025.09.26 (#153252) 2025-09-30 13:19:06 +02:00
Jan-Philipp Benecke 18b80aced3 Record current quality scale of Electricity Maps (#149241) 2025-09-30 11:38:16 +02:00
Franck Nijhof c75dca743a Bump version to 2025.10.0b5 2025-09-30 09:21:25 +00:00
Jan Bouwhuis 00d667ed51 Add missing translation strings for added sensor device classes pm4 and reactive energy (#153215) 2025-09-30 09:21:19 +00:00
c0ffeeca7 51e098e807 ZHA: rename radio to adapter (#153206) 2025-09-30 09:21:17 +00:00
RogerSelwyn 5e2b27699e Handle return result from ebusd being "empty" (#153199) 2025-09-30 09:21:16 +00:00
Erik Montnemery be942c2888 Revert "Add mg/m³ as a valid UOM for sensor/number Carbon Monoxide device class" (#153196) 2025-09-30 09:21:14 +00:00
Erik Montnemery 584c1fbd97 Revert "Add comment on conversion factor for Carbon monoxide on dependency molecular weight" (#153195) 2025-09-30 09:21:13 +00:00
Joost Lekkerkerker abc5c6e2b4 Mark Konnected as Legacy (#153193) 2025-09-30 09:21:11 +00:00
Martin Hjelmare d9de964035 Add hardware Zigbee flow strategy (#153190) 2025-09-30 09:21:10 +00:00
Artur Pragacz bb02158d1a Filter out empty integration type in extended analytics (#153188) 2025-09-30 09:21:08 +00:00
Simone Chemelli be10f097c7 Bump aioamazondevices to 6.2.7 (#153185) 2025-09-30 09:21:07 +00:00
cdnninja 7084bca783 Correct vesync water tank lifted key (#153173) 2025-09-30 09:21:05 +00:00
Michael cd6f3a0fe5 Add newly added cpu temperatures to diagnostics in FRITZ!Tools (#153168) 2025-09-30 09:21:03 +00:00
starkillerOG af2888331d Bump reolink-aio to 0.16.0 (#153161) 2025-09-30 09:21:02 +00:00
Allen Porter b92e5d7131 Add missing translations for Model Context Protocol integration (#153147) 2025-09-30 09:21:00 +00:00
Tom Matheussen f7265c85d0 Fix entities not being created when adding subentries for Satel Integra (#153139) 2025-09-30 09:20:58 +00:00
G Johansson 8466dbf69f Fix event range in workday calendar (#153128) 2025-09-30 09:20:57 +00:00
Robert Resch 2dd0d69bcd Bump deebot-client to 15.0.0 (#153125) 2025-09-30 09:20:56 +00:00
Luca Graf 6783c4ad83 Ignore gateway device in ViCare integration (#153097) 2025-09-30 09:20:54 +00:00
G Johansson 07d7f4e18d Add timeout to dnsip (to handle stale connections) (#153086) 2025-09-30 09:20:53 +00:00
Simone Chemelli 54b1749986 Remove redundant code for Alexa Devices (#153083) 2025-09-30 09:20:51 +00:00
G Johansson eaf264361f Fix can exclude optional holidays in workday (#153082) 2025-09-30 09:20:50 +00:00
Kyle Worrall d8f6f17a4f Fix for Hue Integration motion aware areas (#153079)
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-30 09:20:48 +00:00
Joakim Plate 9a969cea63 Ensure togrill detects disconnected devices (#153067) 2025-09-30 09:20:47 +00:00
Maciej Bieniek ef16327b2b Add consumed energy sensor for Shelly pm1 and switch components (#153053) 2025-09-30 09:20:46 +00:00
Martin Hjelmare a6a6261168 Improve hardware flow strings (#153034) 2025-09-30 09:20:44 +00:00
Erwin Douna a01eb48db8 Portainer switch terminology to API token (#152958)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-09-30 09:20:43 +00:00
Christian McHugh eb103a8d9a Fix: Set EPH climate heating as on only when boiler is actively heating (#152914) 2025-09-30 09:20:41 +00:00
Shay Levy 2b5f989855 Add Shelly EV charger sensors (#152722) 2025-09-30 09:20:40 +00:00
Thomas D 4e247a6ebe Prevent duplicate entities for Volvo integration (#151779) 2025-09-30 09:20:38 +00:00
dependabot[bot] b964d362b7 Bump docker/login-action from 3.5.0 to 3.6.0 (#153239)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 11:14:17 +02:00
G Johansson 3914e41f3c Rename resolver to nameserver in dnsip (#153223) 2025-09-30 10:46:59 +02:00
Erik Montnemery 82bdfcb99b Correct target filter in ecovacs services (#153241) 2025-09-30 10:39:18 +03:00
Marc Mueller 976cea600f Use attribute names for match class (#153191) 2025-09-29 23:12:54 +02:00
Tom 8c8713c3f7 Rework test split for airOS reauthentication flow (#153221) 2025-09-29 22:07:18 +02:00
G Johansson 2359ae6ce7 Bump pysmhi to 1.1.0 (#153222) 2025-09-29 22:04:59 +02:00
Paul Bottein b570fd35c8 Replace legacy hass icons to mdi icons (#153204) 2025-09-29 20:04:21 +01:00
starkillerOG 9d94e6b3b4 Add Reolink bicycle sensitivity and delay (#153217) 2025-09-29 20:44:13 +02:00
Martin Hjelmare cfab789823 Add hardware Zigbee flow strategy (#153190) 2025-09-29 20:08:43 +02:00
Erik Montnemery 81917425dc Add test which fails on duplicated statistics units (#153202)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: jbouwh <jan@jbsoft.nl>
2025-09-29 20:07:59 +02:00
Jan Bouwhuis bfb62709d4 Add missing translation strings for added sensor device classes pm4 and reactive energy (#153215) 2025-09-29 19:55:09 +02:00
Joost Lekkerkerker ca3f2ee782 Mark Konnected as Legacy (#153193) 2025-09-29 18:22:29 +01:00
Ludovic BOUÉ fc8703a40f Matter DoorLock attributes (#151418)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-09-29 18:20:22 +01:00
c0ffeeca7 80517c7ac1 ZHA: rename radio to adapter (#153206) 2025-09-29 18:17:44 +01:00
Erik Montnemery 2b4b46eaf8 Add async_iterator util (#153194) 2025-09-29 18:54:23 +02:00
Martin Hjelmare 40b9dae608 Improve hardware flow strings (#153034) 2025-09-29 18:29:58 +02:00
Erik Montnemery 5975cd6e09 Revert "Add mg/m³ as a valid UOM for sensor/number Carbon Monoxide device class" (#153196) 2025-09-29 15:43:13 +01:00
RogerSelwyn 258c9ff52b Handle return result from ebusd being "empty" (#153199) 2025-09-29 16:08:42 +02:00
starkillerOG 89c5d498a4 Add Reolink Ai person type, vehicle type and animal type (#153170) 2025-09-29 15:39:29 +02:00
Artur Pragacz 76cb4d123a Filter out empty integration type in extended analytics (#153188) 2025-09-29 15:18:15 +02:00
Erik Montnemery f0c29c7699 Revert "Add comment on conversion factor for Carbon monoxide on dependency molecular weight" (#153195) 2025-09-29 14:56:42 +02:00
Kyle Worrall aa4151ced7 Fix for Hue Integration motion aware areas (#153079)
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-29 14:50:36 +02:00
G Johansson 0a6fa978fa Add timeout to dnsip (to handle stale connections) (#153086) 2025-09-29 14:49:38 +02:00
Simone Chemelli dc02002b9d Bump aioamazondevices to 6.2.7 (#153185) 2025-09-29 14:30:42 +02:00
cdnninja f071a3f38b Correct vesync water tank lifted key (#153173) 2025-09-29 14:29:25 +02:00
dependabot[bot] b935231e47 Bump actions/dependency-review-action from 4.7.3 to 4.8.0 (#153180)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-29 13:17:20 +02:00
dependabot[bot] b9f7613567 Bump github/codeql-action from 3.30.4 to 3.30.5 (#153179)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-29 13:15:53 +02:00
Maciej Bieniek 1289a031ab Add consumed energy sensor for Shelly pm1 and switch components (#153053) 2025-09-29 13:06:07 +03:00
Andrew Jackson 289546ef6d Bump aiomealie to 0.11.0 adding times to recipes (#153183) 2025-09-29 11:58:40 +02:00
Guido Schmitz aacff4db5d Rework devolo Home Control config flow tests (#147083)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-29 09:47:07 +02:00
starkillerOG f833b56122 Add Reolink siren state (#153169) 2025-09-29 08:42:38 +02:00
Tom Matheussen 7eb0f2993f Fix entities not being created when adding subentries for Satel Integra (#153139) 2025-09-28 21:37:35 -04:00
Michael abb341abfe Add newly added cpu temperatures to diagnostics in FRITZ!Tools (#153168) 2025-09-28 22:40:10 +02:00
starkillerOG 0d90614369 Bump reolink-aio to 0.16.0 (#153161) 2025-09-28 21:55:39 +02:00
starkillerOG ec84bebeea Add Reolink AI bicycle detection entity (#153163) 2025-09-28 21:54:59 +02:00
Shay Levy 9176867d6b Add Shelly EV charger sensors (#152722) 2025-09-28 22:45:11 +03:00
Allen Porter 281a137ff5 Add missing translations for Model Context Protocol integration (#153147) 2025-09-28 20:05:15 +02:00
tronikos d6543480ac Refactor SQL integration (#153135) 2025-09-28 19:03:13 +02:00
Luca Graf ae6391b866 Ignore gateway device in ViCare integration (#153097) 2025-09-28 16:04:22 +02:00
Joakim Plate 10b56e4258 Ensure togrill detects disconnected devices (#153067) 2025-09-28 15:34:56 +02:00
Erwin Douna 0ff2597957 Portainer add re-auth flow (#153077) 2025-09-28 15:31:50 +02:00
Artur Pragacz 026b28e962 Improve interview logging in Onkyo (#153095) 2025-09-28 15:26:40 +02:00
peteS-UK 9a1e67294a Extend timeout test in test_config_flow for Squeezebox to completion (#153080) 2025-09-28 15:20:47 +02:00
G Johansson cdb448a5cc Use automatic reload options flow in random (#153103) 2025-09-28 01:02:33 +01:00
G Johansson ab80e726e2 Use automatic reload options flow in filter (#153104) 2025-09-28 01:02:14 +01:00
G Johansson 2d5d0f67b2 Use automatic reload options flow in history_stats (#153115) 2025-09-28 01:01:33 +01:00
G Johansson d4100b6096 Use automatic reload options flow in mold_indicator (#153106) 2025-09-28 01:00:48 +01:00
G Johansson 955e854d77 Use automatic reload options flow in utility_meter (#153111) 2025-09-28 01:00:07 +01:00
G Johansson 0c37f88c49 Use automatic reload options flow in derivative (#153112) 2025-09-28 00:59:07 +01:00
G Johansson 48167eeb9c Use automatic reload options flow in worldclock (#153105) 2025-09-28 00:58:20 +01:00
G Johansson 24177197f7 Use automatic reload options flow in generic_thermostat (#153108) 2025-09-28 00:57:12 +01:00
G Johansson 863fc0ba97 Use automatic reload options flow in switch_as_x (#153109) 2025-09-28 00:52:26 +01:00
G Johansson 9f7b229d02 Use automatic reload options flow in template (#153110) 2025-09-28 00:50:00 +01:00
G Johansson ffd909f3d9 Use automatic reload options flow in group (#153116) 2025-09-28 00:48:44 +01:00
Tom 1ebf096a33 Add reauthentication flow to airOS (#153076)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-09-27 23:28:14 +02:00
Robert Resch 96d51965e5 Bump deebot-client to 15.0.0 (#153125) 2025-09-27 23:24:39 +02:00
G Johansson 04b510b020 Fix event range in workday calendar (#153128) 2025-09-27 23:22:39 +02:00
G Johansson c9a301d50e Use automatic reload options flow in systemmonitor (#153107) 2025-09-27 20:50:14 +02:00
G Johansson b304bd1a8b Use automatic reload options flow in local_file (#153114) 2025-09-27 20:49:39 +02:00
G Johansson b99525b231 Use automatic reload options flow in tod (#153113) 2025-09-27 20:45:40 +02:00
G Johansson 634db13990 Use automatic reload options flow in trend (#153117) 2025-09-27 20:44:53 +02:00
peteS-UK ad51a77989 Extend squeezebox config_flow test to completion (#153000)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-09-27 20:36:38 +02:00
G Johansson 3348a39e8a Use automatic reload options flow in generic_hygrostat (#153102) 2025-09-27 20:33:57 +02:00
Christian McHugh 81c2e356ec Fix: Set EPH climate heating as on only when boiler is actively heating (#152914) 2025-09-27 20:19:57 +02:00
Jan Bouwhuis de6c3512d2 Add IMAP fetch message part feature (#152845) 2025-09-27 14:49:26 +02:00
G Johansson 36dc1e938a Fix can exclude optional holidays in workday (#153082) 2025-09-27 14:40:29 +02:00
Sören Beye 07a78cf6f7 Squeezebox: Proxy all the thumbnails (#147199)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-09-27 14:39:15 +02:00
Erwin Douna eaa673e0c3 Portainer switch terminology to API token (#152958)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-09-27 14:32:25 +02:00
Simone Chemelli f2c4ca081f Remove redundant code for Alexa Devices (#153083) 2025-09-27 13:05:07 +02:00
Thomas D e3d707f0b4 Prevent duplicate entities for Volvo integration (#151779) 2025-09-27 12:29:11 +02:00
Tom fb93fed2e5 Bump airOS dependency (#153065) 2025-09-27 01:20:51 +02:00
Björn Dalfors 95dfc2f23d Bump nibe dependency to 2.19.0 (#153062) 2025-09-26 23:49:40 +01:00
Franck Nijhof 77f897a768 Bump version to 2025.10.0b4 2025-09-26 21:30:19 +00:00
Franck Nijhof 4f0a6ef9a1 Update Home Assistant base image to 2025.09.3 (#153064) 2025-09-26 21:30:12 +00:00
Franck Nijhof 408df2093a Update Home Assistant base image to 2025.09.3 (#153064) 2025-09-26 23:28:43 +02:00
Franck Nijhof 66c6b0f5fc Bump version to 2025.10.0b3 2025-09-26 20:37:41 +00:00
Josef Zweck dd01243391 Ensure token validity in lamarzocco (#153058) 2025-09-26 20:36:47 +00:00
SapuSeven 66c17e250a Add None-check for VeSync fan device.state.display_status (#153055) 2025-09-26 20:36:46 +00:00
DeerMaximum 723902e233 NINA Use better wording for filters (#153050) 2025-09-26 20:36:45 +00:00
Paul Bottein 59fdb9f3b5 Update frontend to 20250926.0 (#153049) 2025-09-26 20:36:44 +00:00
Martin Hjelmare d83502514a Fix Thread flow abort on multiple flows (#153048) 2025-09-26 20:36:43 +00:00
Stefan Agner 08e81b2ba6 Update Home Assistant base image to 2025.09.2 (#153035) 2025-09-26 20:36:41 +00:00
Josef Zweck 1e808c965d Bump pylamarzocco to 2.1.1 (#153027) 2025-09-26 20:36:40 +00:00
Stefan Agner 563b58c9aa Bump to home-assistant/wheels@2025.09.1 (#153025) 2025-09-26 20:36:39 +00:00
Artur Pragacz cf223880e8 Use satellite entity area in the assist pipeline (#153017) 2025-09-26 20:36:37 +00:00
J. Nick Koston 4058ca59ed Bump aioesphomeapi to 41.11.0 (#153014) 2025-09-26 20:36:36 +00:00
puddly 1386c01733 Allow ZHA discovery if discovery unique_id conflicts with config entry (#153009)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-26 20:36:35 +00:00
puddly 46504947f7 Bump ZHA to 0.0.73 (#153007) 2025-09-26 20:36:34 +00:00
Paulus Schoutsen 0a44682014 Push ESPHome discovery to ZJS addon (#153004) 2025-09-26 20:36:33 +00:00
Brandon Harvey 06a57473a9 Rename service to action in ESPHome (#152997) 2025-09-26 20:36:32 +00:00
Noah Husby fbed66ef1f Bump aiorussound to 4.8.2 (#152988) 2025-09-26 20:36:30 +00:00
puddly 99a0380ec5 Ignore discovery for existing ZHA entries (#152984) 2025-09-26 20:36:29 +00:00
Simone Chemelli 68c51dc7aa Fix PIN failure if starting with 0 for Comelit SimpleHome (#152983) 2025-09-26 20:36:28 +00:00
lliwog 3d945b0fc5 Fix EZVIZ devices merging due to empty MAC addr (#152939) (#152981)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-26 20:36:27 +00:00
Erwin Douna 7b26a93d38 Portainer add ability to skip SSL verification (#152955) 2025-09-26 20:36:25 +00:00
Tom 1b2eab00be Add SSL options during config_flow for airOS (#150325)
Co-authored-by: Åke Strandberg <ake@strandberg.eu>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-09-26 20:36:24 +00:00
RogerSelwyn 750e849f09 Protect against last_comms being None (#149366)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-26 20:36:22 +00:00
Eskander Bejaoui f32bf0cc3e nmap_tracker: Optimize default scan options (#153047) 2025-09-26 22:31:49 +02:00
peteS-UK dbbe3145b6 Replace patch of entity_registry in test_config_flow for Squeezebox (#153039) 2025-09-26 22:17:47 +02:00
Erik Montnemery f8bf3ea2ef Correct filter of target selector in motioneye services (#152971) 2025-09-26 22:08:19 +02:00
Bouwe Westerdijk 053bd31d43 Snapshot testing for Plugwise Switch platform (#153030) 2025-09-26 22:07:42 +02:00
DeerMaximum 1aefc3f37a NINA Use better wording for filters (#153050) 2025-09-26 22:05:10 +02:00
Joris Pelgröm 3de955d9ce Use UnitOfTime.DAYS instead of custom unit for LetPot number entity (#153054) 2025-09-26 21:58:17 +02:00
SapuSeven 0ff88fd366 Add None-check for VeSync fan device.state.display_status (#153055) 2025-09-26 21:57:01 +02:00
peteS-UK eb84020773 Replace platform setup functions with fixtures with autouse in Squeezebox tests (#153057) 2025-09-26 21:49:58 +02:00
Tom 4bbfea3c7c Add SSL options during config_flow for airOS (#150325)
Co-authored-by: Åke Strandberg <ake@strandberg.eu>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-09-26 21:38:27 +02:00
Josef Zweck 63d4fb7558 Ensure token validity in lamarzocco (#153058) 2025-09-26 21:36:03 +02:00
Artur Pragacz 953895cd81 Use satellite entity area in the assist pipeline (#153017) 2025-09-26 21:34:45 +02:00
Erwin Douna a6c3f4efc0 Portainer add ability to skip SSL verification (#152955) 2025-09-26 21:32:49 +02:00
Paul Bottein 11e880d034 Update frontend to 20250926.0 (#153049) 2025-09-26 21:31:47 +02:00
Martin Hjelmare e4d6bdb398 Fix Thread flow abort on multiple flows (#153048) 2025-09-26 18:48:51 +01:00
Andrew Jackson 6ced1783e3 Add discovery to Mealie (#151773)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-09-26 18:48:19 +02:00
Paulus Schoutsen 8051f78d10 Push ESPHome discovery to ZJS addon (#153004) 2025-09-26 10:12:56 -04:00
Josef Zweck b724176b23 Bump pylamarzocco to 2.1.1 (#153027) 2025-09-26 15:46:24 +02:00
Erik Montnemery fdca16ea92 Fix typing in ObjectSelectorConfig (#153043) 2025-09-26 15:18:18 +02:00
Stefan Agner f8fd8b432a Update Home Assistant base image to 2025.09.2 (#153035) 2025-09-26 13:03:39 +02:00
lliwog 9148ae70ce Fix EZVIZ devices merging due to empty MAC addr (#152939) (#152981)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-26 12:47:11 +02:00
RogerSelwyn 447cb26d28 Protect against last_comms being None (#149366)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-09-26 12:35:04 +02:00
J. Nick Koston 2af36465f6 Bump aioesphomeapi to 41.11.0 (#153014) 2025-09-26 12:31:59 +02:00
dependabot[bot] d5f7265424 Bump github/codeql-action from 3.30.3 to 3.30.4 (#153015)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 12:31:15 +02:00
Simone Chemelli cc16af7f2d Code optimization for Uptime Robot (#153031) 2025-09-26 12:29:02 +02:00
Retha Runolfsson 7a4d75bc44 Add garage door opener for switchbot integration (#148460) 2025-09-26 12:11:59 +02:00
Bouwe Westerdijk ec0380fd3b Snapshot testing for Plugwise Sensor platform (#153021) 2025-09-26 11:22:14 +02:00
Stefan Agner b17cc71dfb Bump to home-assistant/wheels@2025.09.1 (#153025) 2025-09-26 11:04:02 +02:00
Erik Montnemery 89b327ed7b Remove device filter from target selector in bang_olufsen services (#152957) 2025-09-26 09:02:14 +02:00
Simone Chemelli 9bf361a1b8 Fix PIN failure if starting with 0 for Comelit SimpleHome (#152983) 2025-09-26 08:59:03 +02:00
J. Diego Rodríguez Royo d11c171c75 Bump aiohomeconnect to version 0.20.0 (#153003) 2025-09-26 07:49:38 +02:00
puddly c523c45d17 Allow ZHA discovery if discovery unique_id conflicts with config entry (#153009)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-26 07:39:00 +02:00
puddly c1b9c0e1b6 Ignore discovery for existing ZHA entries (#152984) 2025-09-26 07:17:01 +02:00
puddly 487b9ff03e Bump ZHA to 0.0.73 (#153007) 2025-09-25 23:44:25 -04:00
Simone Chemelli ec62b0cdfb Code optimization for Uptime Robot (#152993) 2025-09-26 00:34:09 +01:00
Brandon Harvey 6d0470064f Rename service to action in ESPHome (#152997) 2025-09-25 14:54:06 -05:00
Simone Chemelli 7450b3fd1a Improve tests for Alexa Devices (#152995) 2025-09-25 21:39:44 +02:00
Noah Husby 5b70910d77 Bump aiorussound to 4.8.2 (#152988) 2025-09-25 20:34:29 +02:00
Franck Nijhof 6aaddad56b Bump version to 2025.10.0b2 2025-09-25 18:19:29 +00:00
Paul Bottein a5af974209 Update frontend to 20250925.1 (#152985) 2025-09-25 18:18:47 +00:00
Luke Lashley 09e45f6f54 Fix incorrect Roborock test (#152980) 2025-09-25 18:18:46 +00:00
Joost Lekkerkerker d857d8850c Bump pySmartThings to 3.3.0 (#152977) 2025-09-25 18:18:45 +00:00
J. Nick Koston ccc50f2412 Bump aioesphomeapi to 41.10.0 (#152975)
Co-authored-by: Michael Hansen <mike@rhasspy.org>
2025-09-25 18:18:43 +00:00
Maciej Bieniek 3905723900 Bump accuweather to version 4.2.2 (#152965) 2025-09-25 18:18:42 +00:00
Simone Chemelli cee88473a2 Remove deprecated sensors and update remaning for Alexa Devices (#151230) 2025-09-25 18:18:40 +00:00
Daniel Potthast cdf613d3f8 Update mvglive component (#146479)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-09-25 18:18:39 +00:00
Abílio Costa 52de5ff5ff Remove deprecated zone and event condition keys (#152986) 2025-09-25 19:23:40 +02:00
J. Nick Koston c4389a1679 Bump aioesphomeapi to 41.10.0 (#152975)
Co-authored-by: Michael Hansen <mike@rhasspy.org>
2025-09-25 19:21:17 +02:00
Norbert Rittel 35faaa6cae Add missing square brackets to references in fully_kiosk actions (#152987) 2025-09-25 19:19:27 +02:00
Paul Bottein 3c0b13975a Update frontend to 20250925.1 (#152985) 2025-09-25 19:05:12 +02:00
Simone Chemelli bc88696339 Remove deprecated sensors and update remaning for Alexa Devices (#151230) 2025-09-25 18:59:53 +02:00
Erik Montnemery 8f99c3f64a Remove device filter from target selector in lyric services (#152970) 2025-09-25 18:45:32 +02:00
Erik Montnemery 88016d96d4 Remove device and entity filter from target selector in homeassistant services (#152969) 2025-09-25 17:41:54 +01:00
Erik Montnemery 47df73b18f Remove device filter from target selector in google_mail services (#152968) 2025-09-25 18:32:12 +02:00
Maciej Bieniek 1c12d2b8cd Bump accuweather to version 4.2.2 (#152965) 2025-09-25 18:30:47 +02:00
Erik Montnemery eb38837a8c Replace target selector with device selector in fully_kiosk services (#152959)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-09-25 18:30:05 +02:00
Erik Montnemery 159c7fbfd1 Correct filter of target selector in sonos services (#152972) 2025-09-25 18:29:26 +02:00
Joost Lekkerkerker 7ee31f0884 Bump pySmartThings to 3.3.0 (#152977) 2025-09-25 17:57:30 +02:00
Daniel Potthast 0c5e12571a Update mvglive component (#146479)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-09-25 17:20:43 +02:00
Luke Lashley 9db973217f Fix incorrect Roborock test (#152980) 2025-09-25 17:18:24 +02:00
Artur Pragacz cf1a745283 Move condition-specific fields into options (#152635) 2025-09-25 15:55:50 +02:00
peteS-UK 834e3f1963 Add HassKey for hass.data in Squeezebox (#149129) 2025-09-25 14:05:40 +02:00
Joakim Sørensen 3f8f7573c9 Bump hass-nabucasa from 1.1.1 to 1.1.2 (#152950) 2025-09-25 11:34:14 +01:00
Franck Nijhof 156a0f1a3d Bump version to 2025.10.0b1 2025-09-25 09:37:33 +00:00
Paul Bottein cc2a5b43dd Update frontend to 20250925.0 (#152945) 2025-09-25 09:37:03 +00:00
Karsten Bade 0ae272f1f6 Add return types and docstring to sonos component (#152946) 2025-09-25 11:34:38 +02:00
Paul Bottein 8774295e2e Update frontend to 20250925.0 (#152945) 2025-09-25 11:33:01 +02:00
Erwin Douna 731064f7e9 Portainer fix unique entity (#152941)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-25 08:19:21 +00:00
Sab44 2f75661c20 Bump librehardwaremonitor-api to version 1.4.0 (#152938) 2025-09-25 08:19:20 +00:00
Paulus Schoutsen be6f056f30 Prevent common control calling async methods from thread (#152931)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-09-25 08:19:18 +00:00
Franck Nijhof 79599e1284 Add block Spook < 4.0.0 as breaking Home Assistant (#152930) 2025-09-25 08:19:17 +00:00
Paulus Schoutsen a255585ab6 Remove some more domains from common controls (#152927) 2025-09-25 08:19:15 +00:00
J. Nick Koston e9bde225fe Bump aioesphomeapi to 41.9.4 (#152923) 2025-09-25 08:19:14 +00:00
Franck Nijhof d9521ac2a0 Bump to home-assistant/wheels@2025.09.0 (#152920) 2025-09-25 08:19:13 +00:00
J. Nick Koston d8b24ccccd Bump aioesphomeapi to 41.9.3 to fix segfault (#152912) 2025-09-25 08:19:12 +00:00
J. Nick Koston b4417a76d5 Fix ESPHome reauth not being triggered on incorrect password (#152911) 2025-09-25 08:19:10 +00:00
Simone Chemelli 274f6eb54a Update IQS to platinum for Comelit SimpleHome (#152906) 2025-09-25 08:19:09 +00:00
Simone Chemelli 21a5aaf35c Update IQS to platinum for Alexa Devices (#152905) 2025-09-25 08:19:07 +00:00
Luke Lashley 05820a49d0 Fix logical error when user has no Roborock maps (#152752) 2025-09-25 08:19:06 +00:00
Erwin Douna 0c8d2594ef Portainer fix unique entity (#152941)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-25 09:49:22 +02:00
Franck Nijhof 17b12d29af Bump version to 2025.10.0b0 2025-09-24 18:57:19 +00:00
1732 changed files with 113611 additions and 23007 deletions
+6 -6
View File
@@ -190,7 +190,7 @@ jobs:
echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE
- name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -257,7 +257,7 @@ jobs:
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -326,20 +326,20 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.2.3"
- name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant'
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: matrix.registry == 'ghcr.io/home-assistant'
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -504,7 +504,7 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Login to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
+211 -509
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Initialize CodeQL
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
with:
category: "/language:python"
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
# - No PRs marked as no-stale
# - No issues (-1)
- name: 60 days stale PRs policy
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60
@@ -57,7 +57,7 @@ jobs:
# - No issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: 90 days stale issues
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ steps.token.outputs.token }}
days-before-stale: 90
@@ -87,7 +87,7 @@ jobs:
# - No Issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: Needs more information stale issues policy
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ steps.token.outputs.token }}
only-labels: "needs-more-information"
+2 -2
View File
@@ -160,7 +160,7 @@ jobs:
# home-assistant/wheels doesn't support sha pinning
- name: Build wheels
uses: home-assistant/wheels@2025.09.0
uses: home-assistant/wheels@2025.09.1
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
@@ -221,7 +221,7 @@ jobs:
# home-assistant/wheels doesn't support sha pinning
- name: Build wheels
uses: home-assistant/wheels@2025.09.0
uses: home-assistant/wheels@2025.09.1
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
-1
View File
@@ -79,7 +79,6 @@ junit.xml
.project
.pydevproject
.python-version
.tool-versions
# emacs auto backups
+1
View File
@@ -0,0 +1 @@
3.13
+4
View File
@@ -203,6 +203,7 @@ homeassistant.components.feedreader.*
homeassistant.components.file_upload.*
homeassistant.components.filesize.*
homeassistant.components.filter.*
homeassistant.components.firefly_iii.*
homeassistant.components.fitbit.*
homeassistant.components.flexit_bacnet.*
homeassistant.components.flux_led.*
@@ -220,6 +221,7 @@ homeassistant.components.generic_thermostat.*
homeassistant.components.geo_location.*
homeassistant.components.geocaching.*
homeassistant.components.gios.*
homeassistant.components.github.*
homeassistant.components.glances.*
homeassistant.components.go2rtc.*
homeassistant.components.goalzero.*
@@ -325,6 +327,7 @@ homeassistant.components.london_underground.*
homeassistant.components.lookin.*
homeassistant.components.lovelace.*
homeassistant.components.luftdaten.*
homeassistant.components.lunatone.*
homeassistant.components.madvr.*
homeassistant.components.manual.*
homeassistant.components.mastodon.*
@@ -553,6 +556,7 @@ homeassistant.components.vacuum.*
homeassistant.components.vallox.*
homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
homeassistant.components.volvo.*
Symlink
+1
View File
@@ -0,0 +1 @@
.github/copilot-instructions.md
Generated
+20 -8
View File
@@ -46,6 +46,8 @@ build.json @home-assistant/supervisor
/tests/components/accuweather/ @bieniu
/homeassistant/components/acmeda/ @atmurray
/tests/components/acmeda/ @atmurray
/homeassistant/components/actron_air/ @kclif9 @JagadishDhanamjayam
/tests/components/actron_air/ @kclif9 @JagadishDhanamjayam
/homeassistant/components/adax/ @danielhiversen @lazytarget
/tests/components/adax/ @danielhiversen @lazytarget
/homeassistant/components/adguard/ @frenck
@@ -492,6 +494,8 @@ build.json @home-assistant/supervisor
/tests/components/filesize/ @gjohansson-ST
/homeassistant/components/filter/ @dgomes
/tests/components/filter/ @dgomes
/homeassistant/components/firefly_iii/ @erwindouna
/tests/components/firefly_iii/ @erwindouna
/homeassistant/components/fireservicerota/ @cyberjunky
/tests/components/fireservicerota/ @cyberjunky
/homeassistant/components/firmata/ @DaAwesomeP
@@ -615,6 +619,8 @@ build.json @home-assistant/supervisor
/tests/components/greeneye_monitor/ @jkeljo
/homeassistant/components/group/ @home-assistant/core
/tests/components/group/ @home-assistant/core
/homeassistant/components/growatt_server/ @johanzander
/tests/components/growatt_server/ @johanzander
/homeassistant/components/guardian/ @bachya
/tests/components/guardian/ @bachya
/homeassistant/components/habitica/ @tr4nt0r
@@ -760,8 +766,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/intent/ @home-assistant/core @synesthesiam @arturpragacz
/tests/components/intent/ @home-assistant/core @synesthesiam @arturpragacz
/homeassistant/components/intesishome/ @jnimmo
/homeassistant/components/iometer/ @MaestroOnICe
/tests/components/iometer/ @MaestroOnICe
/homeassistant/components/iometer/ @jukrebs
/tests/components/iometer/ @jukrebs
/homeassistant/components/ios/ @robbiet480
/tests/components/ios/ @robbiet480
/homeassistant/components/iotawatt/ @gtdiehl @jyavenard
@@ -908,6 +914,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/luci/ @mzdrale
/homeassistant/components/luftdaten/ @fabaff @frenck
/tests/components/luftdaten/ @fabaff @frenck
/homeassistant/components/lunatone/ @MoonDevLT
/tests/components/lunatone/ @MoonDevLT
/homeassistant/components/lupusec/ @majuss @suaveolent
/tests/components/lupusec/ @majuss @suaveolent
/homeassistant/components/lutron/ @cdheiser @wilburCForce
@@ -953,6 +961,8 @@ build.json @home-assistant/supervisor
/tests/components/met_eireann/ @DylanGore
/homeassistant/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
/tests/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
/homeassistant/components/meteo_lt/ @xE1H
/tests/components/meteo_lt/ @xE1H
/homeassistant/components/meteoalarm/ @rolfberkenbosch
/homeassistant/components/meteoclimatic/ @adrianmo
/tests/components/meteoclimatic/ @adrianmo
@@ -1059,6 +1069,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/nilu/ @hfurubotten
/homeassistant/components/nina/ @DeerMaximum
/tests/components/nina/ @DeerMaximum
/homeassistant/components/nintendo_parental_controls/ @pantherale0
/tests/components/nintendo_parental_controls/ @pantherale0
/homeassistant/components/nissan_leaf/ @filcole
/homeassistant/components/noaa_tides/ @jdelaney72
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
@@ -1127,6 +1139,8 @@ build.json @home-assistant/supervisor
/tests/components/opengarage/ @danielhiversen
/homeassistant/components/openhome/ @bazwilliams
/tests/components/openhome/ @bazwilliams
/homeassistant/components/openrgb/ @felipecrs
/tests/components/openrgb/ @felipecrs
/homeassistant/components/opensky/ @joostlek
/tests/components/opensky/ @joostlek
/homeassistant/components/opentherm_gw/ @mvn23
@@ -1190,8 +1204,6 @@ build.json @home-assistant/supervisor
/tests/components/plex/ @jjlawren
/homeassistant/components/plugwise/ @CoMPaTech @bouwew
/tests/components/plugwise/ @CoMPaTech @bouwew
/homeassistant/components/plum_lightpad/ @ColinHarrington @prystupa
/tests/components/plum_lightpad/ @ColinHarrington @prystupa
/homeassistant/components/point/ @fredrike
/tests/components/point/ @fredrike
/homeassistant/components/pooldose/ @lmaertin
@@ -1407,8 +1419,8 @@ build.json @home-assistant/supervisor
/tests/components/sfr_box/ @epenet
/homeassistant/components/sftp_storage/ @maretodoric
/tests/components/sftp_storage/ @maretodoric
/homeassistant/components/sharkiq/ @JeffResc @funkybunch
/tests/components/sharkiq/ @JeffResc @funkybunch
/homeassistant/components/sharkiq/ @JeffResc @funkybunch @TheOneOgre
/tests/components/sharkiq/ @JeffResc @funkybunch @TheOneOgre
/homeassistant/components/shell_command/ @home-assistant/core
/tests/components/shell_command/ @home-assistant/core
/homeassistant/components/shelly/ @bieniu @thecode @chemelli74 @bdraco
@@ -1473,8 +1485,8 @@ build.json @home-assistant/supervisor
/tests/components/snoo/ @Lash-L
/homeassistant/components/snooz/ @AustinBrunkhorst
/tests/components/snooz/ @AustinBrunkhorst
/homeassistant/components/solaredge/ @frenck @bdraco
/tests/components/solaredge/ @frenck @bdraco
/homeassistant/components/solaredge/ @frenck @bdraco @tronikos
/tests/components/solaredge/ @frenck @bdraco @tronikos
/homeassistant/components/solaredge_local/ @drobtravels @scheric
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
/tests/components/solarlog/ @Ernst79 @dontinelli
+4 -2
View File
@@ -34,9 +34,11 @@ WORKDIR /usr/src
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
RUN uv python install 3.13.2
USER vscode
COPY .python-version ./
RUN uv python install
ENV VIRTUAL_ENV="/home/vscode/.local/ha-venv"
RUN uv venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
+5 -5
View File
@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.09.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.09.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.09.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.09.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.09.1
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.10.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.10.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.10.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.10.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.10.1
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io
+15 -15
View File
@@ -616,34 +616,34 @@ async def async_enable_logging(
),
)
# Log errors to a file if we have write access to file or config dir
logger = logging.getLogger()
logger.setLevel(logging.INFO if verbose else logging.WARNING)
if log_file is None:
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
default_log_path = hass.config.path(ERROR_LOG_FILENAME)
if "SUPERVISOR" in os.environ:
_LOGGER.info("Running in Supervisor, not logging to file")
# Rename the default log file if it exists, since previous versions created
# it even on Supervisor
if os.path.isfile(default_log_path):
with contextlib.suppress(OSError):
os.rename(default_log_path, f"{default_log_path}.old")
err_log_path = None
else:
err_log_path = default_log_path
else:
err_log_path = os.path.abspath(log_file)
err_path_exists = os.path.isfile(err_log_path)
err_dir = os.path.dirname(err_log_path)
# Check if we can write to the error log if it exists or that
# we can create files in the containing directory if not.
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
not err_path_exists and os.access(err_dir, os.W_OK)
):
if err_log_path:
err_handler = await hass.async_add_executor_job(
_create_log_file, err_log_path, log_rotate_days
)
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
logger = logging.getLogger()
logger.addHandler(err_handler)
logger.setLevel(logging.INFO if verbose else logging.WARNING)
# Save the log file location for access by other components.
hass.data[DATA_LOGGING] = err_log_path
else:
_LOGGER.error("Unable to set up error log %s (access denied)", err_log_path)
async_activate_log_queue_handler(hass)
+5
View File
@@ -0,0 +1,5 @@
{
"domain": "eltako",
"name": "Eltako",
"iot_standards": ["matter"]
}
-5
View File
@@ -1,5 +0,0 @@
{
"domain": "ibm",
"name": "IBM",
"integrations": ["watson_iot", "watson_tts"]
}
+5
View File
@@ -0,0 +1,5 @@
{
"domain": "konnected",
"name": "Konnected",
"integrations": ["konnected", "konnected_esphome"]
}
+5
View File
@@ -0,0 +1,5 @@
{
"domain": "level",
"name": "Level",
"iot_standards": ["matter"]
}
+11 -1
View File
@@ -12,11 +12,13 @@ from homeassistant.components.bluetooth import async_get_scanner
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ADDRESS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONF_IS_NEW_STYLE_SCALE
SCAN_INTERVAL = timedelta(seconds=15)
UPDATE_DEBOUNCE_TIME = 0.2
_LOGGER = logging.getLogger(__name__)
@@ -38,11 +40,19 @@ class AcaiaCoordinator(DataUpdateCoordinator[None]):
config_entry=entry,
)
debouncer = Debouncer(
hass=hass,
logger=_LOGGER,
cooldown=UPDATE_DEBOUNCE_TIME,
immediate=True,
function=self.async_update_listeners,
)
self._scale = AcaiaScale(
address_or_ble_device=entry.data[CONF_ADDRESS],
name=entry.title,
is_new_style_scale=entry.data[CONF_IS_NEW_STYLE_SCALE],
notify_callback=self.async_update_listeners,
notify_callback=debouncer.async_schedule_call,
scanner=async_get_scanner(hass),
)
@@ -71,4 +71,4 @@ POLLEN_CATEGORY_MAP = {
}
UPDATE_INTERVAL_OBSERVATION = timedelta(minutes=10)
UPDATE_INTERVAL_DAILY_FORECAST = timedelta(hours=6)
UPDATE_INTERVAL_HOURLY_FORECAST = timedelta(hours=30)
UPDATE_INTERVAL_HOURLY_FORECAST = timedelta(minutes=30)
@@ -1,6 +1,9 @@
{
"entity": {
"sensor": {
"air_quality": {
"default": "mdi:air-filter"
},
"cloud_ceiling": {
"default": "mdi:weather-fog"
},
@@ -34,9 +37,6 @@
"thunderstorm_probability_night": {
"default": "mdi:weather-lightning"
},
"translation_key": {
"default": "mdi:air-filter"
},
"tree_pollen": {
"default": "mdi:tree-outline"
},
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["accuweather"],
"requirements": ["accuweather==4.2.1"]
"requirements": ["accuweather==4.2.2"]
}
@@ -0,0 +1,57 @@
"""The Actron Air integration."""
from actron_neo_api import (
ActronAirNeoACSystem,
ActronNeoAPI,
ActronNeoAPIError,
ActronNeoAuthError,
)
from homeassistant.const import CONF_API_TOKEN, Platform
from homeassistant.core import HomeAssistant
from .const import _LOGGER
from .coordinator import (
ActronAirConfigEntry,
ActronAirRuntimeData,
ActronAirSystemCoordinator,
)
PLATFORM = [Platform.CLIMATE]
async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> bool:
"""Set up Actron Air integration from a config entry."""
api = ActronNeoAPI(refresh_token=entry.data[CONF_API_TOKEN])
systems: list[ActronAirNeoACSystem] = []
try:
systems = await api.get_ac_systems()
await api.update_status()
except ActronNeoAuthError:
_LOGGER.error("Authentication error while setting up Actron Air integration")
raise
except ActronNeoAPIError as err:
_LOGGER.error("API error while setting up Actron Air integration: %s", err)
raise
system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
for system in systems:
coordinator = ActronAirSystemCoordinator(hass, entry, api, system)
_LOGGER.debug("Setting up coordinator for system: %s", system["serial"])
await coordinator.async_config_entry_first_refresh()
system_coordinators[system["serial"]] = coordinator
entry.runtime_data = ActronAirRuntimeData(
api=api,
system_coordinators=system_coordinators,
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORM)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORM)
@@ -0,0 +1,259 @@
"""Climate platform for Actron Air integration."""
from typing import Any
from actron_neo_api import ActronAirNeoStatus, ActronAirNeoZone
from homeassistant.components.climate import (
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
FAN_MEDIUM,
ClimateEntity,
ClimateEntityFeature,
HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
PARALLEL_UPDATES = 0
FAN_MODE_MAPPING_ACTRONAIR_TO_HA = {
"AUTO": FAN_AUTO,
"LOW": FAN_LOW,
"MED": FAN_MEDIUM,
"HIGH": FAN_HIGH,
}
FAN_MODE_MAPPING_HA_TO_ACTRONAIR = {
v: k for k, v in FAN_MODE_MAPPING_ACTRONAIR_TO_HA.items()
}
HVAC_MODE_MAPPING_ACTRONAIR_TO_HA = {
"COOL": HVACMode.COOL,
"HEAT": HVACMode.HEAT,
"FAN": HVACMode.FAN_ONLY,
"AUTO": HVACMode.AUTO,
"OFF": HVACMode.OFF,
}
HVAC_MODE_MAPPING_HA_TO_ACTRONAIR = {
v: k for k, v in HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.items()
}
async def async_setup_entry(
hass: HomeAssistant,
entry: ActronAirConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Actron Air climate entities."""
system_coordinators = entry.runtime_data.system_coordinators
entities: list[ClimateEntity] = []
for coordinator in system_coordinators.values():
status = coordinator.data
name = status.ac_system.system_name
entities.append(ActronSystemClimate(coordinator, name))
entities.extend(
ActronZoneClimate(coordinator, zone)
for zone in status.remote_zone_info
if zone.exists
)
async_add_entities(entities)
class BaseClimateEntity(CoordinatorEntity[ActronAirSystemCoordinator], ClimateEntity):
"""Base class for Actron Air climate entities."""
_attr_has_entity_name = True
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
_attr_name = None
_attr_fan_modes = list(FAN_MODE_MAPPING_ACTRONAIR_TO_HA.values())
_attr_hvac_modes = list(HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.values())
def __init__(
self,
coordinator: ActronAirSystemCoordinator,
name: str,
) -> None:
"""Initialize an Actron Air unit."""
super().__init__(coordinator)
self._serial_number = coordinator.serial_number
class ActronSystemClimate(BaseClimateEntity):
"""Representation of the Actron Air system."""
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
def __init__(
self,
coordinator: ActronAirSystemCoordinator,
name: str,
) -> None:
"""Initialize an Actron Air unit."""
super().__init__(coordinator, name)
serial_number = coordinator.serial_number
self._attr_unique_id = serial_number
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, serial_number)},
name=self._status.ac_system.system_name,
manufacturer="Actron Air",
model_id=self._status.ac_system.master_wc_model,
sw_version=self._status.ac_system.master_wc_firmware_version,
serial_number=serial_number,
)
@property
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
return self._status.min_temp
@property
def max_temp(self) -> float:
"""Return the maximum temperature that can be set."""
return self._status.max_temp
@property
def _status(self) -> ActronAirNeoStatus:
"""Get the current status from the coordinator."""
return self.coordinator.data
@property
def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC mode."""
if not self._status.user_aircon_settings.is_on:
return HVACMode.OFF
mode = self._status.user_aircon_settings.mode
return HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.get(mode)
@property
def fan_mode(self) -> str | None:
"""Return the current fan mode."""
fan_mode = self._status.user_aircon_settings.fan_mode
return FAN_MODE_MAPPING_ACTRONAIR_TO_HA.get(fan_mode)
@property
def current_humidity(self) -> float:
"""Return the current humidity."""
return self._status.master_info.live_humidity_pc
@property
def current_temperature(self) -> float:
"""Return the current temperature."""
return self._status.master_info.live_temp_c
@property
def target_temperature(self) -> float:
"""Return the target temperature."""
return self._status.user_aircon_settings.temperature_setpoint_cool_c
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set a new fan mode."""
api_fan_mode = FAN_MODE_MAPPING_HA_TO_ACTRONAIR.get(fan_mode.lower())
await self._status.user_aircon_settings.set_fan_mode(api_fan_mode)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
ac_mode = HVAC_MODE_MAPPING_HA_TO_ACTRONAIR.get(hvac_mode)
await self._status.ac_system.set_system_mode(ac_mode)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
await self._status.user_aircon_settings.set_temperature(temperature=temp)
class ActronZoneClimate(BaseClimateEntity):
"""Representation of a zone within the Actron Air system."""
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
def __init__(
self,
coordinator: ActronAirSystemCoordinator,
zone: ActronAirNeoZone,
) -> None:
"""Initialize an Actron Air unit."""
super().__init__(coordinator, zone.title)
serial_number = coordinator.serial_number
self._zone_id: int = zone.zone_id
self._attr_unique_id: str = f"{serial_number}_zone_{zone.zone_id}"
self._attr_device_info: DeviceInfo = DeviceInfo(
identifiers={(DOMAIN, self._attr_unique_id)},
name=zone.title,
manufacturer="Actron Air",
model="Zone",
suggested_area=zone.title,
via_device=(DOMAIN, serial_number),
)
@property
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
return self._zone.min_temp
@property
def max_temp(self) -> float:
"""Return the maximum temperature that can be set."""
return self._zone.max_temp
@property
def _zone(self) -> ActronAirNeoZone:
"""Get the current zone data from the coordinator."""
status = self.coordinator.data
return status.zones[self._zone_id]
@property
def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC mode."""
if self._zone.is_active:
mode = self._zone.hvac_mode
return HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.get(mode)
return HVACMode.OFF
@property
def current_humidity(self) -> float | None:
"""Return the current humidity."""
return self._zone.humidity
@property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
return self._zone.live_temp_c
@property
def target_temperature(self) -> float | None:
"""Return the target temperature."""
return self._zone.temperature_setpoint_cool_c
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
is_enabled = hvac_mode != HVACMode.OFF
await self._zone.enable(is_enabled)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the temperature."""
await self._zone.set_temperature(temperature=kwargs["temperature"])
@@ -0,0 +1,132 @@
"""Setup config flow for Actron Air integration."""
import asyncio
from typing import Any
from actron_neo_api import ActronNeoAPI, ActronNeoAuthError
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_TOKEN
from homeassistant.exceptions import HomeAssistantError
from .const import _LOGGER, DOMAIN
class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Actron Air."""
def __init__(self) -> None:
"""Initialize the config flow."""
self._api: ActronNeoAPI | None = None
self._device_code: str | None = None
self._user_code: str = ""
self._verification_uri: str = ""
self._expires_minutes: str = "30"
self.login_task: asyncio.Task | None = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
if self._api is None:
_LOGGER.debug("Initiating device authorization")
self._api = ActronNeoAPI()
try:
device_code_response = await self._api.request_device_code()
except ActronNeoAuthError as err:
_LOGGER.error("OAuth2 flow failed: %s", err)
return self.async_abort(reason="oauth2_error")
self._device_code = device_code_response["device_code"]
self._user_code = device_code_response["user_code"]
self._verification_uri = device_code_response["verification_uri_complete"]
self._expires_minutes = str(device_code_response["expires_in"] // 60)
async def _wait_for_authorization() -> None:
"""Wait for the user to authorize the device."""
assert self._api is not None
assert self._device_code is not None
_LOGGER.debug("Waiting for device authorization")
try:
await self._api.poll_for_token(self._device_code)
_LOGGER.debug("Authorization successful")
except ActronNeoAuthError as ex:
_LOGGER.exception("Error while waiting for device authorization")
raise CannotConnect from ex
_LOGGER.debug("Checking login task")
if self.login_task is None:
_LOGGER.debug("Creating task for device authorization")
self.login_task = self.hass.async_create_task(_wait_for_authorization())
if self.login_task.done():
_LOGGER.debug("Login task is done, checking results")
if exception := self.login_task.exception():
if isinstance(exception, CannotConnect):
return self.async_show_progress_done(
next_step_id="connection_error"
)
return self.async_show_progress_done(next_step_id="timeout")
return self.async_show_progress_done(next_step_id="finish_login")
return self.async_show_progress(
step_id="user",
progress_action="wait_for_authorization",
description_placeholders={
"user_code": self._user_code,
"verification_uri": self._verification_uri,
"expires_minutes": self._expires_minutes,
},
progress_task=self.login_task,
)
async def async_step_finish_login(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the finalization of login."""
_LOGGER.debug("Finalizing authorization")
assert self._api is not None
try:
user_data = await self._api.get_user_info()
except ActronNeoAuthError as err:
_LOGGER.error("Error getting user info: %s", err)
return self.async_abort(reason="oauth2_error")
unique_id = str(user_data["id"])
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_data["email"],
data={CONF_API_TOKEN: self._api.refresh_token_value},
)
async def async_step_timeout(
self,
user_input: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Handle issues that need transition await from progress step."""
if user_input is None:
return self.async_show_form(
step_id="timeout",
)
del self.login_task
return await self.async_step_user()
async def async_step_connection_error(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle connection error from progress step."""
if user_input is None:
return self.async_show_form(step_id="connection_error")
# Reset state and try again
self._api = None
self._device_code = None
self.login_task = None
return await self.async_step_user()
class CannotConnect(HomeAssistantError):
"""Error to indicate we cannot connect."""
@@ -0,0 +1,6 @@
"""Constants used by Actron Air integration."""
import logging
_LOGGER = logging.getLogger(__package__)
DOMAIN = "actron_air"
@@ -0,0 +1,69 @@
"""Coordinator for Actron Air integration."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
from actron_neo_api import ActronAirNeoACSystem, ActronAirNeoStatus, ActronNeoAPI
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util import dt as dt_util
from .const import _LOGGER
STALE_DEVICE_TIMEOUT = timedelta(hours=24)
ERROR_NO_SYSTEMS_FOUND = "no_systems_found"
ERROR_UNKNOWN = "unknown_error"
@dataclass
class ActronAirRuntimeData:
"""Runtime data for the Actron Air integration."""
api: ActronNeoAPI
system_coordinators: dict[str, ActronAirSystemCoordinator]
type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData]
AUTH_ERROR_THRESHOLD = 3
SCAN_INTERVAL = timedelta(seconds=30)
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirNeoACSystem]):
"""System coordinator for Actron Air integration."""
def __init__(
self,
hass: HomeAssistant,
entry: ActronAirConfigEntry,
api: ActronNeoAPI,
system: ActronAirNeoACSystem,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name="Actron Air Status",
update_interval=SCAN_INTERVAL,
config_entry=entry,
)
self.system = system
self.serial_number = system["serial"]
self.api = api
self.status = self.api.state_manager.get_status(self.serial_number)
self.last_seen = dt_util.utcnow()
async def _async_update_data(self) -> ActronAirNeoStatus:
"""Fetch updates and merge incremental changes into the full state."""
await self.api.update_status()
self.status = self.api.state_manager.get_status(self.serial_number)
self.last_seen = dt_util.utcnow()
return self.status
def is_device_stale(self) -> bool:
"""Check if a device is stale (not seen for a while)."""
return (dt_util.utcnow() - self.last_seen) > STALE_DEVICE_TIMEOUT
@@ -0,0 +1,16 @@
{
"domain": "actron_air",
"name": "Actron Air",
"codeowners": ["@kclif9", "@JagadishDhanamjayam"],
"config_flow": true,
"dhcp": [
{
"hostname": "neo-*",
"macaddress": "FC0FE7*"
}
],
"documentation": "https://www.home-assistant.io/integrations/actron_air",
"iot_class": "cloud_polling",
"quality_scale": "bronze",
"requirements": ["actron-neo-api==0.1.84"]
}
@@ -0,0 +1,78 @@
rules:
# Bronze
action-setup:
status: exempt
comment: This integration does not have custom service actions.
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: This integration does not have custom service actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: This integration does not subscribe to external 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:
status: exempt
comment: No options flow
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: done
reauthentication-flow: todo
test-coverage: todo
# Gold
devices: done
diagnostics: todo
discovery-update-info:
status: exempt
comment: This integration uses DHCP discovery, however is cloud polling. Therefore there is no information to update.
discovery: done
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: todo
entity-category:
status: exempt
comment: This integration does not use entity categories.
entity-device-class:
status: exempt
comment: This integration does not use entity device classes.
entity-disabled-by-default:
status: exempt
comment: Not required for this integration at this stage.
entity-translations: todo
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
repair-issues:
status: exempt
comment: This integration does not have any known issues that require repair.
stale-devices: todo
# Platinum
async-dependency: done
inject-websession: todo
strict-typing: todo
@@ -0,0 +1,29 @@
{
"config": {
"step": {
"user": {
"title": "Actron Air OAuth2 Authorization"
},
"timeout": {
"title": "Authorization timeout",
"description": "The authorization process timed out. Please try again.",
"data": {}
},
"connection_error": {
"title": "Connection error",
"description": "Failed to connect to Actron Air. Please check your internet connection and try again.",
"data": {}
}
},
"progress": {
"wait_for_authorization": "To authenticate, open the following URL and login at Actron Air:\n{verification_uri}\nIf the code is not automatically copied, paste the following code to authorize the integration:\n\n```{user_code}```\n\n\nThe login attempt will time out after {expires_minutes} minutes."
},
"error": {
"oauth2_error": "Failed to start OAuth2 flow. Please try again later."
},
"abort": {
"oauth2_error": "Failed to start OAuth2 flow",
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
}
}
}
@@ -71,7 +71,14 @@ class AemetConfigFlow(ConfigFlow, domain=DOMAIN):
}
)
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
return self.async_show_form(
step_id="user",
data_schema=schema,
errors=errors,
description_placeholders={
"api_key_url": "https://opendata.aemet.es/centrodedescargas/altaUsuario"
},
)
@staticmethod
@callback
+1 -1
View File
@@ -14,7 +14,7 @@
"longitude": "[%key:common::config_flow::data::longitude%]",
"name": "Name of the integration"
},
"description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario"
"description": "To generate API key go to {api_key_url}"
}
}
},
+25 -4
View File
@@ -1,7 +1,9 @@
"""Airgradient Update platform."""
from datetime import timedelta
import logging
from airgradient import AirGradientConnectionError
from propcache.api import cached_property
from homeassistant.components.update import UpdateDeviceClass, UpdateEntity
@@ -13,6 +15,7 @@ from .entity import AirGradientEntity
PARALLEL_UPDATES = 1
SCAN_INTERVAL = timedelta(hours=1)
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
@@ -31,6 +34,7 @@ class AirGradientUpdate(AirGradientEntity, UpdateEntity):
"""Representation of Airgradient Update."""
_attr_device_class = UpdateDeviceClass.FIRMWARE
_server_unreachable_logged = False
def __init__(self, coordinator: AirGradientCoordinator) -> None:
"""Initialize the entity."""
@@ -47,10 +51,27 @@ class AirGradientUpdate(AirGradientEntity, UpdateEntity):
"""Return the installed version of the entity."""
return self.coordinator.data.measures.firmware_version
@property
def available(self) -> bool:
"""Return if entity is available."""
return super().available and self._attr_available
async def async_update(self) -> None:
"""Update the entity."""
self._attr_latest_version = (
await self.coordinator.client.get_latest_firmware_version(
self.coordinator.serial_number
try:
self._attr_latest_version = (
await self.coordinator.client.get_latest_firmware_version(
self.coordinator.serial_number
)
)
)
except AirGradientConnectionError:
self._attr_latest_version = None
self._attr_available = False
if not self._server_unreachable_logged:
_LOGGER.error(
"Unable to connect to AirGradient server to check for updates"
)
self._server_unreachable_logged = True
else:
self._server_unreachable_logged = False
self._attr_available = True
@@ -18,6 +18,10 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_USE_NEAREST, DOMAIN, NO_AIRLY_SENSORS
DESCRIPTION_PLACEHOLDERS = {
"developer_registration_url": "https://developer.airly.eu/register",
}
class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Airly."""
@@ -85,6 +89,7 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
}
),
errors=errors,
description_placeholders=DESCRIPTION_PLACEHOLDERS,
)
+1 -1
View File
@@ -2,7 +2,7 @@
"config": {
"step": {
"user": {
"description": "To generate API key go to https://developer.airly.eu/register",
"description": "To generate API key go to {developer_registration_url}",
"data": {
"name": "[%key:common::config_flow::data::name%]",
"api_key": "[%key:common::config_flow::data::api_key%]",
+90 -3
View File
@@ -2,12 +2,23 @@
from __future__ import annotations
import logging
from airos.airos8 import AirOS8
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN, SECTION_ADVANCED_SETTINGS
from .coordinator import AirOSConfigEntry, AirOSDataUpdateCoordinator
_PLATFORMS: list[Platform] = [
@@ -15,19 +26,24 @@ _PLATFORMS: list[Platform] = [
Platform.SENSOR,
]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
"""Set up Ubiquiti airOS from a config entry."""
# By default airOS 8 comes with self-signed SSL certificates,
# with no option in the web UI to change or upload a custom certificate.
session = async_get_clientsession(hass, verify_ssl=False)
session = async_get_clientsession(
hass, verify_ssl=entry.data[SECTION_ADVANCED_SETTINGS][CONF_VERIFY_SSL]
)
airos_device = AirOS8(
host=entry.data[CONF_HOST],
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
session=session,
use_ssl=entry.data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
)
coordinator = AirOSDataUpdateCoordinator(hass, entry, airos_device)
@@ -40,6 +56,77 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> boo
return True
async def async_migrate_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
"""Migrate old config entry."""
# This means the user has downgraded from a future version
if entry.version > 2:
return False
# 1.1 Migrate config_entry to add advanced ssl settings
if entry.version == 1 and entry.minor_version == 1:
new_minor_version = 2
new_data = {**entry.data}
advanced_data = {
CONF_SSL: DEFAULT_SSL,
CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL,
}
new_data[SECTION_ADVANCED_SETTINGS] = advanced_data
hass.config_entries.async_update_entry(
entry,
data=new_data,
minor_version=new_minor_version,
)
# 2.1 Migrate binary_sensor entity unique_id from device_id to mac_address
# Step 1 - migrate binary_sensor entity unique_id
# Step 2 - migrate device entity identifier
if entry.version == 1:
new_version = 2
new_minor_version = 1
mac_adress = dr.format_mac(entry.unique_id)
device_registry = dr.async_get(hass)
if device_entry := device_registry.async_get_device(
connections={(dr.CONNECTION_NETWORK_MAC, mac_adress)}
):
old_device_id = next(
(
device_id
for domain, device_id in device_entry.identifiers
if domain == DOMAIN
),
)
@callback
def update_unique_id(
entity_entry: er.RegistryEntry,
) -> dict[str, str] | None:
"""Update unique id from device_id to mac address."""
if old_device_id and entity_entry.unique_id.startswith(old_device_id):
suffix = entity_entry.unique_id.removeprefix(old_device_id)
new_unique_id = f"{mac_adress}{suffix}"
return {"new_unique_id": new_unique_id}
return None
await er.async_migrate_entries(hass, entry.entry_id, update_unique_id)
new_identifiers = device_entry.identifiers.copy()
new_identifiers.discard((DOMAIN, old_device_id))
new_identifiers.add((DOMAIN, mac_adress))
device_registry.async_update_device(
device_entry.id, new_identifiers=new_identifiers
)
hass.config_entries.async_update_entry(
entry, version=new_version, minor_version=new_minor_version
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, _PLATFORMS)
@@ -98,7 +98,7 @@ class AirOSBinarySensor(AirOSEntity, BinarySensorEntity):
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.data.host.device_id}_{description.key}"
self._attr_unique_id = f"{coordinator.data.derived.mac}_{description.key}"
@property
def is_on(self) -> bool:
+179 -39
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
from collections.abc import Mapping
import logging
from typing import Any
@@ -14,11 +15,28 @@ from airos.exceptions import (
)
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.config_entries import (
SOURCE_REAUTH,
SOURCE_RECONFIGURE,
ConfigFlow,
ConfigFlowResult,
)
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.data_entry_flow import section
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
TextSelectorType,
)
from .const import DOMAIN
from .const import DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN, SECTION_ADVANCED_SETTINGS
from .coordinator import AirOS8
_LOGGER = logging.getLogger(__name__)
@@ -28,6 +46,15 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
vol.Required(CONF_HOST): str,
vol.Required(CONF_USERNAME, default="ubnt"): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(SECTION_ADVANCED_SETTINGS): section(
vol.Schema(
{
vol.Required(CONF_SSL, default=DEFAULT_SSL): bool,
vol.Required(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): bool,
}
),
{"collapsed": True},
),
}
)
@@ -35,48 +62,161 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Ubiquiti airOS."""
VERSION = 1
VERSION = 2
MINOR_VERSION = 1
def __init__(self) -> None:
"""Initialize the config flow."""
super().__init__()
self.airos_device: AirOS8
self.errors: dict[str, str] = {}
async def async_step_user(
self,
user_input: dict[str, Any] | None = None,
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
"""Handle the manual input of host and credentials."""
self.errors = {}
if user_input is not None:
# By default airOS 8 comes with self-signed SSL certificates,
# with no option in the web UI to change or upload a custom certificate.
session = async_get_clientsession(self.hass, verify_ssl=False)
airos_device = AirOS8(
host=user_input[CONF_HOST],
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
session=session,
)
try:
await airos_device.login()
airos_data = await airos_device.status()
except (
AirOSConnectionSetupError,
AirOSDeviceConnectionError,
):
errors["base"] = "cannot_connect"
except (AirOSConnectionAuthenticationError, AirOSDataMissingError):
errors["base"] = "invalid_auth"
except AirOSKeyDataMissingError:
errors["base"] = "key_data_missing"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(airos_data.derived.mac)
self._abort_if_unique_id_configured()
validated_info = await self._validate_and_get_device_info(user_input)
if validated_info:
return self.async_create_entry(
title=airos_data.host.hostname, data=user_input
title=validated_info["title"],
data=validated_info["data"],
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=self.errors
)
async def _validate_and_get_device_info(
self, config_data: dict[str, Any]
) -> dict[str, Any] | None:
"""Validate user input with the device API."""
# By default airOS 8 comes with self-signed SSL certificates,
# with no option in the web UI to change or upload a custom certificate.
session = async_get_clientsession(
self.hass,
verify_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_VERIFY_SSL],
)
airos_device = AirOS8(
host=config_data[CONF_HOST],
username=config_data[CONF_USERNAME],
password=config_data[CONF_PASSWORD],
session=session,
use_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
)
try:
await airos_device.login()
airos_data = await airos_device.status()
except (
AirOSConnectionSetupError,
AirOSDeviceConnectionError,
):
self.errors["base"] = "cannot_connect"
except (AirOSConnectionAuthenticationError, AirOSDataMissingError):
self.errors["base"] = "invalid_auth"
except AirOSKeyDataMissingError:
self.errors["base"] = "key_data_missing"
except Exception:
_LOGGER.exception("Unexpected exception during credential validation")
self.errors["base"] = "unknown"
else:
await self.async_set_unique_id(airos_data.derived.mac)
if self.source in [SOURCE_REAUTH, SOURCE_RECONFIGURE]:
self._abort_if_unique_id_mismatch()
else:
self._abort_if_unique_id_configured()
return {"title": airos_data.host.hostname, "data": config_data}
return None
async def async_step_reauth(
self,
user_input: Mapping[str, Any],
) -> ConfigFlowResult:
"""Perform reauthentication upon an API authentication error."""
return await self.async_step_reauth_confirm(user_input)
async def async_step_reauth_confirm(
self,
user_input: Mapping[str, Any],
) -> ConfigFlowResult:
"""Perform reauthentication upon an API authentication error."""
self.errors = {}
if user_input:
validate_data = {**self._get_reauth_entry().data, **user_input}
if await self._validate_and_get_device_info(config_data=validate_data):
return self.async_update_reload_and_abort(
self._get_reauth_entry(),
data_updates=validate_data,
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD,
autocomplete="current-password",
)
),
}
),
errors=self.errors,
)
async def async_step_reconfigure(
self,
user_input: Mapping[str, Any] | None = None,
) -> ConfigFlowResult:
"""Handle reconfiguration of airOS."""
self.errors = {}
entry = self._get_reconfigure_entry()
current_data = entry.data
if user_input is not None:
validate_data = {**current_data, **user_input}
if await self._validate_and_get_device_info(config_data=validate_data):
return self.async_update_reload_and_abort(
entry,
data_updates=validate_data,
)
return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema(
{
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD,
autocomplete="current-password",
)
),
vol.Required(SECTION_ADVANCED_SETTINGS): section(
vol.Schema(
{
vol.Required(
CONF_SSL,
default=current_data[SECTION_ADVANCED_SETTINGS][
CONF_SSL
],
): bool,
vol.Required(
CONF_VERIFY_SSL,
default=current_data[SECTION_ADVANCED_SETTINGS][
CONF_VERIFY_SSL
],
): bool,
}
),
{"collapsed": True},
),
}
),
errors=self.errors,
)
+5
View File
@@ -7,3 +7,8 @@ DOMAIN = "airos"
SCAN_INTERVAL = timedelta(minutes=1)
MANUFACTURER = "Ubiquiti"
DEFAULT_VERIFY_SSL = False
DEFAULT_SSL = True
SECTION_ADVANCED_SETTINGS = "advanced_settings"
@@ -14,7 +14,7 @@ from airos.exceptions import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, SCAN_INTERVAL
@@ -47,9 +47,9 @@ class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOS8Data]):
try:
await self.airos_device.login()
return await self.airos_device.status()
except (AirOSConnectionAuthenticationError,) as err:
except AirOSConnectionAuthenticationError as err:
_LOGGER.exception("Error authenticating with airOS device")
raise ConfigEntryError(
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN, translation_key="invalid_auth"
) from err
except (
+14 -4
View File
@@ -2,11 +2,11 @@
from __future__ import annotations
from homeassistant.const import CONF_HOST
from homeassistant.const import CONF_HOST, CONF_SSL
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
from .const import DOMAIN, MANUFACTURER, SECTION_ADVANCED_SETTINGS
from .coordinator import AirOSDataUpdateCoordinator
@@ -20,17 +20,27 @@ class AirOSEntity(CoordinatorEntity[AirOSDataUpdateCoordinator]):
super().__init__(coordinator)
airos_data = self.coordinator.data
url_schema = (
"https"
if coordinator.config_entry.data[SECTION_ADVANCED_SETTINGS][CONF_SSL]
else "http"
)
configuration_url: str | None = (
f"https://{coordinator.config_entry.data[CONF_HOST]}"
f"{url_schema}://{coordinator.config_entry.data[CONF_HOST]}"
)
self._attr_device_info = DeviceInfo(
connections={(CONNECTION_NETWORK_MAC, airos_data.derived.mac)},
configuration_url=configuration_url,
identifiers={(DOMAIN, str(airos_data.host.device_id))},
identifiers={(DOMAIN, airos_data.derived.mac)},
manufacturer=MANUFACTURER,
model=airos_data.host.devmodel,
model_id=(
sku
if (sku := airos_data.derived.sku) not in ["UNKNOWN", "AMBIGUOUS"]
else None
),
name=airos_data.host.hostname,
sw_version=airos_data.host.fwversion,
)
+3 -2
View File
@@ -4,7 +4,8 @@
"codeowners": ["@CoMPaTech"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airos",
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["airos==0.5.1"]
"quality_scale": "silver",
"requirements": ["airos==0.5.6"]
}
@@ -32,11 +32,11 @@ rules:
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: todo
entity-unavailable: done
integration-owner: done
log-when-unavailable: todo
parallel-updates: todo
reauthentication-flow: todo
log-when-unavailable: done
parallel-updates: done
reauthentication-flow: done
test-coverage: done
# Gold
@@ -48,9 +48,9 @@ rules:
docs-examples: todo
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: todo
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: todo
docs-use-cases: done
dynamic-devices: todo
entity-category: done
entity-device-class: done
@@ -60,7 +60,7 @@ rules:
icon-translations:
status: exempt
comment: no (custom) icons used or envisioned
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues: todo
stale-devices: todo
+46 -1
View File
@@ -2,6 +2,35 @@
"config": {
"flow_title": "Ubiquiti airOS device",
"step": {
"reauth_confirm": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::airos::config::step::user::data_description::password%]"
}
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::airos::config::step::user::data_description::password%]"
},
"sections": {
"advanced_settings": {
"name": "[%key:component::airos::config::step::user::sections::advanced_settings::name%]",
"data": {
"ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data_description::ssl%]",
"verify_ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data_description::verify_ssl%]"
}
}
}
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
@@ -12,6 +41,19 @@
"host": "IP address or hostname of the airOS device",
"username": "Administrator username for the airOS device, normally 'ubnt'",
"password": "Password configured through the UISP app or web interface"
},
"sections": {
"advanced_settings": {
"name": "Advanced settings",
"data": {
"ssl": "Use HTTPS",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"ssl": "Whether the connection should be encrypted (required for most devices)",
"verify_ssl": "Whether the certificate should be verified when using HTTPS. This should be off for self-signed certificates"
}
}
}
}
},
@@ -22,7 +64,10 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unique_id_mismatch": "Re-authentication should be used for the same device not a new one"
}
},
"entity": {
+1 -1
View File
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioairq"],
"requirements": ["aioairq==0.4.6"]
"requirements": ["aioairq==0.4.7"]
}
+1 -1
View File
@@ -29,7 +29,7 @@
},
"data_description": {
"return_average": "air-Q allows to poll both the noisy sensor readings as well as the values averaged on the device (default)",
"clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behaviour is to clip such values to 0"
"clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behavior is to clip such values to 0"
}
}
}
@@ -23,6 +23,10 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
}
)
URL_API_INTEGRATION = {
"url": "https://dashboard.airthings.com/integrations/api-integration"
}
class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Airthings."""
@@ -37,11 +41,7 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="user",
data_schema=STEP_USER_DATA_SCHEMA,
description_placeholders={
"url": (
"https://dashboard.airthings.com/integrations/api-integration"
),
},
description_placeholders=URL_API_INTEGRATION,
)
errors = {}
@@ -65,5 +65,8 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title="Airthings", data=user_input)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
step_id="user",
data_schema=STEP_USER_DATA_SCHEMA,
errors=errors,
description_placeholders=URL_API_INTEGRATION,
)
@@ -4,9 +4,9 @@
"user": {
"data": {
"id": "ID",
"secret": "Secret",
"description": "Login at {url} to find your credentials"
}
"secret": "Secret"
},
"description": "Log in at {url} to find your credentials"
}
},
"error": {
@@ -6,8 +6,13 @@ import dataclasses
import logging
from typing import Any
from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice
from airthings_ble import (
AirthingsBluetoothDeviceData,
AirthingsDevice,
UnsupportedDeviceError,
)
from bleak import BleakError
from habluetooth import BluetoothServiceInfoBleak
import voluptuous as vol
from homeassistant.components import bluetooth
@@ -27,6 +32,7 @@ SERVICE_UUIDS = [
"b42e4a8e-ade7-11e4-89d3-123b93f75cba",
"b42e1c08-ade7-11e4-89d3-123b93f75cba",
"b42e3882-ade7-11e4-89d3-123b93f75cba",
"b42e90a2-ade7-11e4-89d3-123b93f75cba",
]
@@ -37,6 +43,7 @@ class Discovery:
name: str
discovery_info: BluetoothServiceInfo
device: AirthingsDevice
data: AirthingsBluetoothDeviceData
def get_name(device: AirthingsDevice) -> str:
@@ -44,7 +51,7 @@ def get_name(device: AirthingsDevice) -> str:
name = device.friendly_name()
if identifier := device.identifier:
name += f" ({identifier})"
name += f" ({device.model.value}{identifier})"
return name
@@ -62,8 +69,8 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovered_device: Discovery | None = None
self._discovered_devices: dict[str, Discovery] = {}
async def _get_device_data(
self, discovery_info: BluetoothServiceInfo
async def _get_device(
self, data: AirthingsBluetoothDeviceData, discovery_info: BluetoothServiceInfo
) -> AirthingsDevice:
ble_device = bluetooth.async_ble_device_from_address(
self.hass, discovery_info.address
@@ -72,10 +79,8 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.debug("no ble_device in _get_device_data")
raise AirthingsDeviceUpdateError("No ble_device")
airthings = AirthingsBluetoothDeviceData(_LOGGER)
try:
data = await airthings.update_device(ble_device)
device = await data.update_device(ble_device)
except BleakError as err:
_LOGGER.error(
"Error connecting to and getting data from %s: %s",
@@ -83,12 +88,15 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
err,
)
raise AirthingsDeviceUpdateError("Failed getting device data") from err
except UnsupportedDeviceError:
_LOGGER.debug("Skipping unsupported device: %s", discovery_info.name)
raise
except Exception as err:
_LOGGER.error(
"Unknown error occurred from %s: %s", discovery_info.address, err
)
raise
return data
return device
async def async_step_bluetooth(
self, discovery_info: BluetoothServiceInfo
@@ -98,17 +106,21 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(discovery_info.address)
self._abort_if_unique_id_configured()
data = AirthingsBluetoothDeviceData(logger=_LOGGER)
try:
device = await self._get_device_data(discovery_info)
device = await self._get_device(data=data, discovery_info=discovery_info)
except AirthingsDeviceUpdateError:
return self.async_abort(reason="cannot_connect")
except UnsupportedDeviceError:
return self.async_abort(reason="unsupported_device")
except Exception:
_LOGGER.exception("Unknown error occurred")
return self.async_abort(reason="unknown")
name = get_name(device)
self.context["title_placeholders"] = {"name": name}
self._discovered_device = Discovery(name, discovery_info, device)
self._discovered_device = Discovery(name, discovery_info, device, data=data)
return await self.async_step_bluetooth_confirm()
@@ -117,6 +129,12 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult:
"""Confirm discovery."""
if user_input is not None:
if (
self._discovered_device is not None
and self._discovered_device.device.firmware.need_firmware_upgrade
):
return self.async_abort(reason="firmware_upgrade_required")
return self.async_create_entry(
title=self.context["title_placeholders"]["name"], data={}
)
@@ -137,6 +155,9 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured()
discovery = self._discovered_devices[address]
if discovery.device.firmware.need_firmware_upgrade:
return self.async_abort(reason="firmware_upgrade_required")
self.context["title_placeholders"] = {
"name": discovery.name,
}
@@ -146,32 +167,53 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title=discovery.name, data={})
current_addresses = self._async_current_ids(include_ignore=False)
devices: list[BluetoothServiceInfoBleak] = []
for discovery_info in async_discovered_service_info(self.hass):
address = discovery_info.address
if address in current_addresses or address in self._discovered_devices:
continue
if MFCT_ID not in discovery_info.manufacturer_data:
continue
if not any(uuid in SERVICE_UUIDS for uuid in discovery_info.service_uuids):
_LOGGER.debug(
"Skipping unsupported device: %s (%s)", discovery_info.name, address
)
continue
devices.append(discovery_info)
for discovery_info in devices:
address = discovery_info.address
data = AirthingsBluetoothDeviceData(logger=_LOGGER)
try:
device = await self._get_device_data(discovery_info)
device = await self._get_device(data, discovery_info)
except AirthingsDeviceUpdateError:
return self.async_abort(reason="cannot_connect")
_LOGGER.error(
"Error connecting to and getting data from %s (%s)",
discovery_info.name,
discovery_info.address,
)
continue
except UnsupportedDeviceError:
_LOGGER.debug(
"Skipping unsupported device: %s (%s)",
discovery_info.name,
discovery_info.address,
)
continue
except Exception:
_LOGGER.exception("Unknown error occurred")
return self.async_abort(reason="unknown")
name = get_name(device)
self._discovered_devices[address] = Discovery(name, discovery_info, device)
_LOGGER.debug("Discovered Airthings device: %s (%s)", name, address)
self._discovered_devices[address] = Discovery(
name, discovery_info, device, data
)
if not self._discovered_devices:
return self.async_abort(reason="no_devices_found")
titles = {
address: discovery.device.name
address: get_name(discovery.device)
for (address, discovery) in self._discovered_devices.items()
}
return self.async_show_form(
@@ -17,6 +17,10 @@
{
"manufacturer_id": 820,
"service_uuid": "b42e3882-ade7-11e4-89d3-123b93f75cba"
},
{
"manufacturer_id": 820,
"service_uuid": "b42e90a2-ade7-11e4-89d3-123b93f75cba"
}
],
"codeowners": ["@vincegio", "@LaStrada"],
@@ -24,5 +28,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"iot_class": "local_polling",
"requirements": ["airthings-ble==0.9.2"]
"requirements": ["airthings-ble==1.1.1"]
}
@@ -16,10 +16,12 @@ from homeassistant.components.sensor import (
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
EntityCategory,
Platform,
UnitOfPressure,
UnitOfSoundPressure,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
@@ -112,8 +114,25 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"lux": SensorEntityDescription(
key="lux",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"noise": SensorEntityDescription(
key="noise",
translation_key="ambient_noise",
device_class=SensorDeviceClass.SOUND_PRESSURE,
native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
}
PARALLEL_UPDATES = 0
@callback
def async_migrate(hass: HomeAssistant, address: str, sensor_name: str) -> None:
@@ -6,6 +6,9 @@
"description": "[%key:component::bluetooth::config::step::user::description%]",
"data": {
"address": "[%key:common::config_flow::data::device%]"
},
"data_description": {
"address": "The Airthings devices discovered via Bluetooth."
}
},
"bluetooth_confirm": {
@@ -17,6 +20,8 @@
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"firmware_upgrade_required": "Your device requires a firmware upgrade. Please use the Airthings app (Android/iOS) to upgrade it.",
"unsupported_device": "Unsupported device",
"unknown": "[%key:common::config_flow::error::unknown%]"
}
},
@@ -36,6 +41,9 @@
},
"illuminance": {
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
},
"ambient_noise": {
"name": "Ambient noise"
}
}
}
@@ -2,17 +2,14 @@
from airtouch4pyapi import AirTouch
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from .coordinator import AirtouchDataUpdateCoordinator
from .coordinator import AirTouch4ConfigEntry, AirtouchDataUpdateCoordinator
PLATFORMS = [Platform.CLIMATE]
type AirTouch4ConfigEntry = ConfigEntry[AirtouchDataUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AirTouch4ConfigEntry) -> bool:
"""Set up AirTouch4 from a config entry."""
@@ -22,7 +19,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirTouch4ConfigEntry) ->
info = airtouch.GetAcs()
if not info:
raise ConfigEntryNotReady
coordinator = AirtouchDataUpdateCoordinator(hass, airtouch)
coordinator = AirtouchDataUpdateCoordinator(hass, entry, airtouch)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
@@ -2,26 +2,34 @@
import logging
from airtouch4pyapi import AirTouch
from airtouch4pyapi.airtouch import AirTouchStatus
from homeassistant.components.climate import SCAN_INTERVAL
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
type AirTouch4ConfigEntry = ConfigEntry[AirtouchDataUpdateCoordinator]
class AirtouchDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching Airtouch data."""
def __init__(self, hass, airtouch):
def __init__(
self, hass: HomeAssistant, entry: AirTouch4ConfigEntry, airtouch: AirTouch
) -> None:
"""Initialize global Airtouch data updater."""
self.airtouch = airtouch
super().__init__(
hass,
_LOGGER,
config_entry=entry,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
@@ -22,6 +22,17 @@ class OAuth2FlowHandler(
VERSION = CONFIG_FLOW_VERSION
MINOR_VERSION = CONFIG_FLOW_MINOR_VERSION
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Check we have the cloud integration set up."""
if "cloud" not in self.hass.config.components:
return self.async_abort(
reason="cloud_not_enabled",
description_placeholders={"default_config": "default_config"},
)
return await super().async_step_user(user_input)
async def async_step_reauth(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
@@ -24,7 +24,8 @@
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"wrong_account": "You are authenticated with a different account than the one set up. Please authenticate with the configured account."
"wrong_account": "You are authenticated with a different account than the one set up. Please authenticate with the configured account.",
"cloud_not_enabled": "Please make sure you run Home Assistant with `{default_config}` enabled in your configuration.yaml."
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
@@ -2,10 +2,9 @@
from __future__ import annotations
import asyncio
from datetime import timedelta
import logging
from typing import TYPE_CHECKING, Any, Final, final
from typing import Any, Final, final
from propcache.api import cached_property
import voluptuous as vol
@@ -28,8 +27,6 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_platform import EntityPlatform
from homeassistant.helpers.frame import ReportBehavior, report_usage
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.hass_dict import HassKey
@@ -149,68 +146,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
)
_alarm_control_panel_option_default_code: str | None = None
__alarm_legacy_state: bool = False
def __init_subclass__(cls, **kwargs: Any) -> None:
"""Post initialisation processing."""
super().__init_subclass__(**kwargs)
if any(method in cls.__dict__ for method in ("_attr_state", "state")):
# Integrations should use the 'alarm_state' property instead of
# setting the state directly.
cls.__alarm_legacy_state = True
def __setattr__(self, name: str, value: Any, /) -> None:
"""Set attribute.
Deprecation warning if setting '_attr_state' directly
unless already reported.
"""
if name == "_attr_state":
self._report_deprecated_alarm_state_handling()
return super().__setattr__(name, value)
@callback
def add_to_platform_start(
self,
hass: HomeAssistant,
platform: EntityPlatform,
parallel_updates: asyncio.Semaphore | None,
) -> None:
"""Start adding an entity to a platform."""
super().add_to_platform_start(hass, platform, parallel_updates)
if self.__alarm_legacy_state:
self._report_deprecated_alarm_state_handling()
@callback
def _report_deprecated_alarm_state_handling(self) -> None:
"""Report on deprecated handling of alarm state.
Integrations should implement alarm_state instead of using state directly.
"""
report_usage(
"is setting state directly."
f" Entity {self.entity_id} ({type(self)}) should implement the 'alarm_state'"
" property and return its state using the AlarmControlPanelState enum",
core_integration_behavior=ReportBehavior.ERROR,
custom_integration_behavior=ReportBehavior.LOG,
breaks_in_ha_version="2025.11",
integration_domain=self.platform.platform_name if self.platform else None,
exclude_integrations={DOMAIN},
)
@final
@property
def state(self) -> str | None:
"""Return the current state."""
if (alarm_state := self.alarm_state) is not None:
return alarm_state
if self._attr_state is not None:
# Backwards compatibility for integrations that set state directly
# Should be removed in 2025.11
if TYPE_CHECKING:
assert isinstance(self._attr_state, str)
return self._attr_state
return None
return self.alarm_state
@cached_property
def alarm_state(self) -> AlarmControlPanelState | None:
+10 -10
View File
@@ -1472,10 +1472,10 @@ class AlexaModeController(AlexaCapability):
# Return state instead of position when using ModeController.
mode = self.entity.state
if mode in (
cover.STATE_OPEN,
cover.STATE_OPENING,
cover.STATE_CLOSED,
cover.STATE_CLOSING,
cover.CoverState.OPEN,
cover.CoverState.OPENING,
cover.CoverState.CLOSED,
cover.CoverState.CLOSING,
STATE_UNKNOWN,
):
return f"{cover.ATTR_POSITION}.{mode}"
@@ -1594,11 +1594,11 @@ class AlexaModeController(AlexaCapability):
["Position", AlexaGlobalCatalog.SETTING_OPENING], False
)
self._resource.add_mode(
f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}",
f"{cover.ATTR_POSITION}.{cover.CoverState.OPEN}",
[AlexaGlobalCatalog.VALUE_OPEN],
)
self._resource.add_mode(
f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}",
f"{cover.ATTR_POSITION}.{cover.CoverState.CLOSED}",
[AlexaGlobalCatalog.VALUE_CLOSE],
)
self._resource.add_mode(
@@ -1651,22 +1651,22 @@ class AlexaModeController(AlexaCapability):
raise_labels.append(AlexaSemantics.ACTION_OPEN)
self._semantics.add_states_to_value(
[AlexaSemantics.STATES_CLOSED],
f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}",
f"{cover.ATTR_POSITION}.{cover.CoverState.CLOSED}",
)
self._semantics.add_states_to_value(
[AlexaSemantics.STATES_OPEN],
f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}",
f"{cover.ATTR_POSITION}.{cover.CoverState.OPEN}",
)
self._semantics.add_action_to_directive(
lower_labels,
"SetMode",
{"mode": f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}"},
{"mode": f"{cover.ATTR_POSITION}.{cover.CoverState.CLOSED}"},
)
self._semantics.add_action_to_directive(
raise_labels,
"SetMode",
{"mode": f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}"},
{"mode": f"{cover.ATTR_POSITION}.{cover.CoverState.OPEN}"},
)
return self._semantics.serialize_semantics()
+2 -2
View File
@@ -1261,9 +1261,9 @@ async def async_api_set_mode(
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
position = mode.split(".")[1]
if position == cover.STATE_CLOSED:
if position == cover.CoverState.CLOSED:
service = cover.SERVICE_CLOSE_COVER
elif position == cover.STATE_OPEN:
elif position == cover.CoverState.OPEN:
service = cover.SERVICE_OPEN_COVER
elif position == "custom":
service = cover.SERVICE_STOP_COVER
@@ -10,6 +10,7 @@ from aioamazondevices.api import AmazonDevice
from aioamazondevices.const import SENSOR_STATE_OFF
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
@@ -17,9 +18,12 @@ from homeassistant.components.binary_sensor import (
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
import homeassistant.helpers.entity_registry as er
from .const import _LOGGER, DOMAIN
from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
from .utils import async_update_unique_id
# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0
@@ -31,6 +35,7 @@ class AmazonBinarySensorEntityDescription(BinarySensorEntityDescription):
is_on_fn: Callable[[AmazonDevice, str], bool]
is_supported: Callable[[AmazonDevice, str], bool] = lambda device, key: True
is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: True
BINARY_SENSORS: Final = (
@@ -40,47 +45,52 @@ BINARY_SENSORS: Final = (
entity_category=EntityCategory.DIAGNOSTIC,
is_on_fn=lambda device, _: device.online,
),
AmazonBinarySensorEntityDescription(
key="detectionState",
device_class=BinarySensorDeviceClass.MOTION,
is_on_fn=lambda device, key: bool(
device.sensors[key].value != SENSOR_STATE_OFF
),
is_supported=lambda device, key: device.sensors.get(key) is not None,
is_available_fn=lambda device, key: (
device.online
and (sensor := device.sensors.get(key)) is not None
and sensor.error is False
),
),
)
DEPRECATED_BINARY_SENSORS: Final = (
AmazonBinarySensorEntityDescription(
key="bluetooth",
entity_category=EntityCategory.DIAGNOSTIC,
translation_key="bluetooth",
is_on_fn=lambda device, _: device.bluetooth_state,
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="babyCryDetectionState",
translation_key="baby_cry_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="beepingApplianceDetectionState",
translation_key="beeping_appliance_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="coughDetectionState",
translation_key="cough_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="dogBarkDetectionState",
translation_key="dog_bark_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
AmazonBinarySensorEntityDescription(
key="humanPresenceDetectionState",
device_class=BinarySensorDeviceClass.MOTION,
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
is_on_fn=lambda device, key: False,
),
AmazonBinarySensorEntityDescription(
key="waterSoundsDetectionState",
translation_key="water_sounds_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
is_on_fn=lambda device, key: False,
),
)
@@ -94,6 +104,27 @@ async def async_setup_entry(
coordinator = entry.runtime_data
entity_registry = er.async_get(hass)
# Replace unique id for "detectionState" binary sensor
await async_update_unique_id(
hass,
coordinator,
BINARY_SENSOR_DOMAIN,
"humanPresenceDetectionState",
"detectionState",
)
# Clean up deprecated sensors
for sensor_desc in DEPRECATED_BINARY_SENSORS:
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{sensor_desc.key}"
if entity_id := entity_registry.async_get_entity_id(
BINARY_SENSOR_DOMAIN, DOMAIN, unique_id
):
_LOGGER.debug("Removing deprecated entity %s", entity_id)
entity_registry.async_remove(entity_id)
known_devices: set[str] = set()
def _check_device() -> None:
@@ -125,3 +156,13 @@ class AmazonBinarySensorEntity(AmazonEntity, BinarySensorEntity):
return self.entity_description.is_on_fn(
self.device, self.entity_description.key
)
@property
def available(self) -> bool:
"""Return if entity is available."""
return (
self.entity_description.is_available_fn(
self.device, self.entity_description.key
)
and super().available
)
@@ -64,7 +64,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
data = await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except (CannotAuthenticate, TypeError):
except CannotAuthenticate:
errors["base"] = "invalid_auth"
except CannotRetrieveData:
errors["base"] = "cannot_retrieve_data"
@@ -112,7 +112,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
)
except CannotConnect:
errors["base"] = "cannot_connect"
except (CannotAuthenticate, TypeError):
except CannotAuthenticate:
errors["base"] = "invalid_auth"
except CannotRetrieveData:
errors["base"] = "cannot_retrieve_data"
@@ -68,7 +68,7 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
translation_key="cannot_retrieve_data_with_error",
translation_placeholders={"error": repr(err)},
) from err
except (CannotAuthenticate, TypeError) as err:
except CannotAuthenticate as err:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -60,7 +60,5 @@ def build_device_data(device: AmazonDevice) -> dict[str, Any]:
"online": device.online,
"serial number": device.serial_number,
"software version": device.software_version,
"do not disturb": device.do_not_disturb,
"response style": device.response_style,
"bluetooth state": device.bluetooth_state,
"sensors": device.sensors,
}
@@ -1,44 +1,4 @@
{
"entity": {
"binary_sensor": {
"bluetooth": {
"default": "mdi:bluetooth-off",
"state": {
"on": "mdi:bluetooth"
}
},
"baby_cry_detection": {
"default": "mdi:account-voice-off",
"state": {
"on": "mdi:account-voice"
}
},
"beeping_appliance_detection": {
"default": "mdi:bell-off",
"state": {
"on": "mdi:bell-ring"
}
},
"cough_detection": {
"default": "mdi:blur-off",
"state": {
"on": "mdi:blur"
}
},
"dog_bark_detection": {
"default": "mdi:dog-side-off",
"state": {
"on": "mdi:dog-side"
}
},
"water_sounds_detection": {
"default": "mdi:water-pump-off",
"state": {
"on": "mdi:water-pump"
}
}
}
},
"services": {
"send_sound": {
"service": "mdi:cast-audio"
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==6.0.0"]
"requirements": ["aioamazondevices==6.4.4"]
}
@@ -31,15 +31,20 @@ class AmazonSensorEntityDescription(SensorEntityDescription):
"""Amazon Devices sensor 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 (sensor := device.sensors.get(key)) is not None
and sensor.error is False
)
SENSORS: Final = (
AmazonSensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement_fn=lambda device, _key: (
native_unit_of_measurement_fn=lambda device, key: (
UnitOfTemperature.CELSIUS
if device.sensors[_key].scale == "CELSIUS"
if key in device.sensors and device.sensors[key].scale == "CELSIUS"
else UnitOfTemperature.FAHRENHEIT
),
state_class=SensorStateClass.MEASUREMENT,
@@ -99,3 +104,13 @@ class AmazonSensorEntity(AmazonEntity, SensorEntity):
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self.device.sensors[self.entity_description.key].value
@property
def available(self) -> bool:
"""Return if entity is available."""
return (
self.entity_description.is_available_fn(
self.device, self.entity_description.key
)
and super().available
)
@@ -58,26 +58,6 @@
}
},
"entity": {
"binary_sensor": {
"bluetooth": {
"name": "Bluetooth"
},
"baby_cry_detection": {
"name": "Baby crying"
},
"beeping_appliance_detection": {
"name": "Beeping appliance"
},
"cough_detection": {
"name": "Coughing"
},
"dog_bark_detection": {
"name": "Dog barking"
},
"water_sounds_detection": {
"name": "Water sounds"
}
},
"notify": {
"speak": {
"name": "Speak"
@@ -8,13 +8,21 @@ from typing import TYPE_CHECKING, Any, Final
from aioamazondevices.api import AmazonDevice
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SwitchEntity,
SwitchEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
from .utils import alexa_api_call
from .utils import (
alexa_api_call,
async_remove_dnd_from_virtual_group,
async_update_unique_id,
)
PARALLEL_UPDATES = 1
@@ -24,16 +32,19 @@ class AmazonSwitchEntityDescription(SwitchEntityDescription):
"""Alexa Devices switch entity description."""
is_on_fn: Callable[[AmazonDevice], bool]
subkey: str
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
)
method: str
SWITCHES: Final = (
AmazonSwitchEntityDescription(
key="do_not_disturb",
subkey="AUDIO_PLAYER",
key="dnd",
translation_key="do_not_disturb",
is_on_fn=lambda _device: _device.do_not_disturb,
is_on_fn=lambda device: bool(device.sensors["dnd"].value),
method="set_do_not_disturb",
),
)
@@ -48,6 +59,14 @@ async def async_setup_entry(
coordinator = entry.runtime_data
# Replace unique id for "DND" switch and remove from Speaker Group
await async_update_unique_id(
hass, coordinator, SWITCH_DOMAIN, "do_not_disturb", "dnd"
)
# Remove DND switch from virtual groups
await async_remove_dnd_from_virtual_group(hass, coordinator)
known_devices: set[str] = set()
def _check_device() -> None:
@@ -59,7 +78,7 @@ async def async_setup_entry(
AmazonSwitchEntity(coordinator, serial_num, switch_desc)
for switch_desc in SWITCHES
for serial_num in new_devices
if switch_desc.subkey in coordinator.data[serial_num].capabilities
if switch_desc.key in coordinator.data[serial_num].sensors
)
_check_device()
@@ -94,3 +113,13 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity):
def is_on(self) -> bool:
"""Return True if switch is on."""
return self.entity_description.is_on_fn(self.device)
@property
def available(self) -> bool:
"""Return if entity is available."""
return (
self.entity_description.is_available_fn(
self.device, self.entity_description.key
)
and super().available
)
@@ -4,11 +4,16 @@ from collections.abc import Awaitable, Callable, Coroutine
from functools import wraps
from typing import Any, Concatenate
from aioamazondevices.const import SPEAKER_GROUP_FAMILY
from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.entity_registry as er
from .const import DOMAIN
from .const import _LOGGER, DOMAIN
from .coordinator import AmazonDevicesCoordinator
from .entity import AmazonEntity
@@ -38,3 +43,41 @@ def alexa_api_call[_T: AmazonEntity, **_P](
) from err
return cmd_wrapper
async def async_update_unique_id(
hass: HomeAssistant,
coordinator: AmazonDevicesCoordinator,
domain: str,
old_key: str,
new_key: str,
) -> None:
"""Update unique id for entities created with old format."""
entity_registry = er.async_get(hass)
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{old_key}"
if entity_id := entity_registry.async_get_entity_id(domain, DOMAIN, unique_id):
_LOGGER.debug("Updating unique_id for %s", entity_id)
new_unique_id = unique_id.replace(old_key, new_key)
# Update the registry with the new unique_id
entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id)
async def async_remove_dnd_from_virtual_group(
hass: HomeAssistant,
coordinator: AmazonDevicesCoordinator,
) -> None:
"""Remove entity DND from virtual group."""
entity_registry = er.async_get(hass)
for serial_num in coordinator.data:
unique_id = f"{serial_num}-do_not_disturb"
entity_id = entity_registry.async_get_entity_id(
DOMAIN, SWITCH_DOMAIN, unique_id
)
is_group = coordinator.data[serial_num].device_family == SPEAKER_GROUP_FAMILY
if entity_id and is_group:
entity_registry.async_remove(entity_id)
_LOGGER.debug("Removed DND switch from virtual group %s", entity_id)
@@ -65,6 +65,31 @@ SENSOR_DESCRIPTIONS = [
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME280"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
key="BME680_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME680"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PRESSURE,
key="BME680_pressure",
translation_key="pressure",
native_unit_of_measurement=UnitOfPressure.PA,
suggested_unit_of_measurement=UnitOfPressure.MMHG,
suggested_display_precision=0,
translation_placeholders={"sensor_name": "BME680"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key="BME680_temperature",
translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME680"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PRESSURE,
key="BMP_pressure",
+25 -14
View File
@@ -505,7 +505,7 @@ DEFAULT_DEVICE_ANALYTICS_CONFIG = DeviceAnalyticsModifications()
DEFAULT_ENTITY_ANALYTICS_CONFIG = EntityAnalyticsModifications()
async def async_devices_payload(hass: HomeAssistant) -> dict:
async def async_devices_payload(hass: HomeAssistant) -> dict: # noqa: C901
"""Return detailed information about entities and devices."""
dev_reg = dr.async_get(hass)
ent_reg = er.async_get(hass)
@@ -513,6 +513,8 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
integration_inputs: dict[str, tuple[list[str], list[str]]] = {}
integration_configs: dict[str, AnalyticsModifications] = {}
removed_devices: set[str] = set()
# Get device list
for device_entry in dev_reg.devices.values():
if not device_entry.primary_config_entry:
@@ -525,6 +527,10 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
if config_entry is None:
continue
if device_entry.entry_type is dr.DeviceEntryType.SERVICE:
removed_devices.add(device_entry.id)
continue
integration_domain = config_entry.domain
integration_input = integration_inputs.setdefault(integration_domain, ([], []))
@@ -551,7 +557,7 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
for domain, integration_info in integration_inputs.items()
if (integration := integrations.get(domain)) is not None
and integration.is_built_in
and integration.integration_type in ("device", "hub")
and integration.manifest.get("integration_type") in ("device", "hub")
}
# Call integrations that implement the analytics platform
@@ -614,15 +620,15 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
device_config = integration_config.devices.get(device_id, device_config)
if device_config.remove:
removed_devices.add(device_id)
continue
device_entry = dev_reg.devices[device_id]
device_id_mapping[device_entry.id] = (integration_domain, len(devices_info))
device_id_mapping[device_id] = (integration_domain, len(devices_info))
devices_info.append(
{
"entities": [],
"entry_type": device_entry.entry_type,
"has_configuration_url": device_entry.configuration_url is not None,
"hw_version": device_entry.hw_version,
@@ -631,6 +637,7 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
"model_id": device_entry.model_id,
"sw_version": device_entry.sw_version,
"via_device": device_entry.via_device_id,
"entities": [],
}
)
@@ -669,7 +676,7 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
entity_entry = ent_reg.entities[entity_id]
entity_state = hass.states.get(entity_entry.entity_id)
entity_state = hass.states.get(entity_id)
entity_info = {
# LIMITATION: `assumed_state` can be overridden by users;
@@ -690,15 +697,19 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
"unit_of_measurement": entity_entry.unit_of_measurement,
}
if (
((device_id_ := entity_entry.device_id) is not None)
and ((new_device_id := device_id_mapping.get(device_id_)) is not None)
and (new_device_id[0] == integration_domain)
):
device_info = devices_info[new_device_id[1]]
device_info["entities"].append(entity_info)
else:
entities_info.append(entity_info)
if (device_id_ := entity_entry.device_id) is not None:
if device_id_ in removed_devices:
# The device was removed, so we remove the entity too
continue
if (
new_device_id := device_id_mapping.get(device_id_)
) is not None and (new_device_id[0] == integration_domain):
device_info = devices_info[new_device_id[1]]
device_info["entities"].append(entity_info)
continue
entities_info.append(entity_info)
return {
"version": "home-assistant:1",
@@ -4,12 +4,15 @@ from __future__ import annotations
from collections.abc import Mapping
from functools import partial
import json
import logging
from typing import Any, cast
import anthropic
import voluptuous as vol
from voluptuous_openapi import convert
from homeassistant.components.zone import ENTITY_ID_HOME
from homeassistant.config_entries import (
ConfigEntry,
ConfigEntryState,
@@ -18,7 +21,13 @@ from homeassistant.config_entries import (
ConfigSubentryFlow,
SubentryFlowResult,
)
from homeassistant.const import CONF_API_KEY, CONF_LLM_HASS_API, CONF_NAME
from homeassistant.const import (
ATTR_LATITUDE,
ATTR_LONGITUDE,
CONF_API_KEY,
CONF_LLM_HASS_API,
CONF_NAME,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import llm
from homeassistant.helpers.selector import (
@@ -37,12 +46,23 @@ from .const import (
CONF_RECOMMENDED,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
CONF_WEB_SEARCH_MAX_USES,
CONF_WEB_SEARCH_REGION,
CONF_WEB_SEARCH_TIMEZONE,
CONF_WEB_SEARCH_USER_LOCATION,
DEFAULT_CONVERSATION_NAME,
DOMAIN,
RECOMMENDED_CHAT_MODEL,
RECOMMENDED_MAX_TOKENS,
RECOMMENDED_TEMPERATURE,
RECOMMENDED_THINKING_BUDGET,
RECOMMENDED_WEB_SEARCH,
RECOMMENDED_WEB_SEARCH_MAX_USES,
RECOMMENDED_WEB_SEARCH_USER_LOCATION,
WEB_SEARCH_UNSUPPORTED_MODELS,
)
_LOGGER = logging.getLogger(__name__)
@@ -168,6 +188,14 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
CONF_THINKING_BUDGET, RECOMMENDED_THINKING_BUDGET
) >= user_input.get(CONF_MAX_TOKENS, RECOMMENDED_MAX_TOKENS):
errors[CONF_THINKING_BUDGET] = "thinking_budget_too_large"
if user_input.get(CONF_WEB_SEARCH, RECOMMENDED_WEB_SEARCH):
model = user_input.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL)
if model.startswith(tuple(WEB_SEARCH_UNSUPPORTED_MODELS)):
errors[CONF_WEB_SEARCH] = "web_search_unsupported_model"
elif user_input.get(
CONF_WEB_SEARCH_USER_LOCATION, RECOMMENDED_WEB_SEARCH_USER_LOCATION
):
user_input.update(await self._get_location_data())
if not errors:
if self._is_new:
@@ -215,6 +243,68 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
errors=errors or None,
)
async def _get_location_data(self) -> dict[str, str]:
"""Get approximate location data of the user."""
location_data: dict[str, str] = {}
zone_home = self.hass.states.get(ENTITY_ID_HOME)
if zone_home is not None:
client = await self.hass.async_add_executor_job(
partial(
anthropic.AsyncAnthropic,
api_key=self._get_entry().data[CONF_API_KEY],
)
)
location_schema = vol.Schema(
{
vol.Optional(
CONF_WEB_SEARCH_CITY,
description="Free text input for the city, e.g. `San Francisco`",
): str,
vol.Optional(
CONF_WEB_SEARCH_REGION,
description="Free text input for the region, e.g. `California`",
): str,
}
)
response = await client.messages.create(
model=RECOMMENDED_CHAT_MODEL,
messages=[
{
"role": "user",
"content": "Where are the following coordinates located: "
f"({zone_home.attributes[ATTR_LATITUDE]},"
f" {zone_home.attributes[ATTR_LONGITUDE]})? Please respond "
"only with a JSON object using the following schema:\n"
f"{convert(location_schema)}",
},
{
"role": "assistant",
"content": "{", # hints the model to skip any preamble
},
],
max_tokens=RECOMMENDED_MAX_TOKENS,
)
_LOGGER.debug("Model response: %s", response.content)
location_data = location_schema(
json.loads(
"{"
+ "".join(
block.text
for block in response.content
if isinstance(block, anthropic.types.TextBlock)
)
)
or {}
)
if self.hass.config.country:
location_data[CONF_WEB_SEARCH_COUNTRY] = self.hass.config.country
location_data[CONF_WEB_SEARCH_TIMEZONE] = self.hass.config.time_zone
_LOGGER.debug("Location data: %s", location_data)
return location_data
async_step_user = async_step_set_options
async_step_reconfigure = async_step_set_options
@@ -273,6 +363,18 @@ def anthropic_config_option_schema(
CONF_THINKING_BUDGET,
default=RECOMMENDED_THINKING_BUDGET,
): int,
vol.Optional(
CONF_WEB_SEARCH,
default=RECOMMENDED_WEB_SEARCH,
): bool,
vol.Optional(
CONF_WEB_SEARCH_MAX_USES,
default=RECOMMENDED_WEB_SEARCH_MAX_USES,
): int,
vol.Optional(
CONF_WEB_SEARCH_USER_LOCATION,
default=RECOMMENDED_WEB_SEARCH_USER_LOCATION,
): bool,
}
)
return schema
+21 -5
View File
@@ -18,10 +18,26 @@ RECOMMENDED_TEMPERATURE = 1.0
CONF_THINKING_BUDGET = "thinking_budget"
RECOMMENDED_THINKING_BUDGET = 0
MIN_THINKING_BUDGET = 1024
CONF_WEB_SEARCH = "web_search"
RECOMMENDED_WEB_SEARCH = False
CONF_WEB_SEARCH_USER_LOCATION = "user_location"
RECOMMENDED_WEB_SEARCH_USER_LOCATION = False
CONF_WEB_SEARCH_MAX_USES = "web_search_max_uses"
RECOMMENDED_WEB_SEARCH_MAX_USES = 5
CONF_WEB_SEARCH_CITY = "city"
CONF_WEB_SEARCH_REGION = "region"
CONF_WEB_SEARCH_COUNTRY = "country"
CONF_WEB_SEARCH_TIMEZONE = "timezone"
THINKING_MODELS = [
"claude-3-7-sonnet",
"claude-sonnet-4-0",
"claude-opus-4-0",
"claude-opus-4-1",
NON_THINKING_MODELS = [
"claude-3-5", # Both sonnet and haiku
"claude-3-opus",
"claude-3-haiku",
]
WEB_SEARCH_UNSUPPORTED_MODELS = [
"claude-3-haiku",
"claude-3-opus",
"claude-3-5-sonnet-20240620",
"claude-3-5-sonnet-20241022",
]
+276 -34
View File
@@ -1,12 +1,17 @@
"""Base entity for Anthropic."""
from collections.abc import AsyncGenerator, Callable, Iterable
from dataclasses import dataclass, field
import json
from typing import Any
import anthropic
from anthropic import AsyncStream
from anthropic.types import (
CitationsDelta,
CitationsWebSearchResultLocation,
CitationWebSearchResultLocationParam,
ContentBlockParam,
InputJSONDelta,
MessageDeltaUsage,
MessageParam,
@@ -16,11 +21,16 @@ from anthropic.types import (
RawContentBlockStopEvent,
RawMessageDeltaEvent,
RawMessageStartEvent,
RawMessageStopEvent,
RedactedThinkingBlock,
RedactedThinkingBlockParam,
ServerToolUseBlock,
ServerToolUseBlockParam,
SignatureDelta,
TextBlock,
TextBlockParam,
TextCitation,
TextCitationParam,
TextDelta,
ThinkingBlock,
ThinkingBlockParam,
@@ -29,9 +39,15 @@ from anthropic.types import (
ThinkingDelta,
ToolParam,
ToolResultBlockParam,
ToolUnionParam,
ToolUseBlock,
ToolUseBlockParam,
Usage,
WebSearchTool20250305Param,
WebSearchToolRequestErrorParam,
WebSearchToolResultBlock,
WebSearchToolResultBlockParam,
WebSearchToolResultError,
)
from anthropic.types.message_create_params import MessageCreateParamsStreaming
from voluptuous_openapi import convert
@@ -48,14 +64,21 @@ from .const import (
CONF_MAX_TOKENS,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
CONF_WEB_SEARCH_MAX_USES,
CONF_WEB_SEARCH_REGION,
CONF_WEB_SEARCH_TIMEZONE,
CONF_WEB_SEARCH_USER_LOCATION,
DOMAIN,
LOGGER,
MIN_THINKING_BUDGET,
NON_THINKING_MODELS,
RECOMMENDED_CHAT_MODEL,
RECOMMENDED_MAX_TOKENS,
RECOMMENDED_TEMPERATURE,
RECOMMENDED_THINKING_BUDGET,
THINKING_MODELS,
)
# Max number of back and forth with the LLM to generate a response
@@ -73,6 +96,69 @@ def _format_tool(
)
@dataclass(slots=True)
class CitationDetails:
"""Citation details for a content part."""
index: int = 0
"""Start position of the text."""
length: int = 0
"""Length of the relevant data."""
citations: list[TextCitationParam] = field(default_factory=list)
"""Citations for the content part."""
@dataclass(slots=True)
class ContentDetails:
"""Native data for AssistantContent."""
citation_details: list[CitationDetails] = field(default_factory=list)
def has_content(self) -> bool:
"""Check if there is any content."""
return any(detail.length > 0 for detail in self.citation_details)
def has_citations(self) -> bool:
"""Check if there are any citations."""
return any(detail.citations for detail in self.citation_details)
def add_citation_detail(self) -> None:
"""Add a new citation detail."""
if not self.citation_details or self.citation_details[-1].length > 0:
self.citation_details.append(
CitationDetails(
index=self.citation_details[-1].index
+ self.citation_details[-1].length
if self.citation_details
else 0
)
)
def add_citation(self, citation: TextCitation) -> None:
"""Add a citation to the current detail."""
if not self.citation_details:
self.citation_details.append(CitationDetails())
citation_param: TextCitationParam | None = None
if isinstance(citation, CitationsWebSearchResultLocation):
citation_param = CitationWebSearchResultLocationParam(
type="web_search_result_location",
title=citation.title,
url=citation.url,
cited_text=citation.cited_text,
encrypted_index=citation.encrypted_index,
)
if citation_param:
self.citation_details[-1].citations.append(citation_param)
def delete_empty(self) -> None:
"""Delete empty citation details."""
self.citation_details = [
detail for detail in self.citation_details if detail.citations
]
def _convert_content(
chat_content: Iterable[conversation.Content],
) -> list[MessageParam]:
@@ -81,15 +167,31 @@ def _convert_content(
for content in chat_content:
if isinstance(content, conversation.ToolResultContent):
tool_result_block = ToolResultBlockParam(
type="tool_result",
tool_use_id=content.tool_call_id,
content=json.dumps(content.tool_result),
)
if not messages or messages[-1]["role"] != "user":
if content.tool_name == "web_search":
tool_result_block: ContentBlockParam = WebSearchToolResultBlockParam(
type="web_search_tool_result",
tool_use_id=content.tool_call_id,
content=content.tool_result["content"]
if "content" in content.tool_result
else WebSearchToolRequestErrorParam(
type="web_search_tool_result_error",
error_code=content.tool_result.get("error_code", "unavailable"), # type: ignore[typeddict-item]
),
)
external_tool = True
else:
tool_result_block = ToolResultBlockParam(
type="tool_result",
tool_use_id=content.tool_call_id,
content=json.dumps(content.tool_result),
)
external_tool = False
if not messages or messages[-1]["role"] != (
"assistant" if external_tool else "user"
):
messages.append(
MessageParam(
role="user",
role="assistant" if external_tool else "user",
content=[tool_result_block],
)
)
@@ -151,13 +253,56 @@ def _convert_content(
redacted_thinking_block
)
if content.content:
messages[-1]["content"].append( # type: ignore[union-attr]
TextBlockParam(type="text", text=content.content)
)
current_index = 0
for detail in (
content.native.citation_details
if isinstance(content.native, ContentDetails)
else [CitationDetails(length=len(content.content))]
):
if detail.index > current_index:
# Add text block for any text without citations
messages[-1]["content"].append( # type: ignore[union-attr]
TextBlockParam(
type="text",
text=content.content[current_index : detail.index],
)
)
messages[-1]["content"].append( # type: ignore[union-attr]
TextBlockParam(
type="text",
text=content.content[
detail.index : detail.index + detail.length
],
citations=detail.citations,
)
if detail.citations
else TextBlockParam(
type="text",
text=content.content[
detail.index : detail.index + detail.length
],
)
)
current_index = detail.index + detail.length
if current_index < len(content.content):
# Add text block for any remaining text without citations
messages[-1]["content"].append( # type: ignore[union-attr]
TextBlockParam(
type="text",
text=content.content[current_index:],
)
)
if content.tool_calls:
messages[-1]["content"].extend( # type: ignore[union-attr]
[
ToolUseBlockParam(
ServerToolUseBlockParam(
type="server_tool_use",
id=tool_call.id,
name="web_search",
input=tool_call.tool_args,
)
if tool_call.external and tool_call.tool_name == "web_search"
else ToolUseBlockParam(
type="tool_use",
id=tool_call.id,
name=tool_call.tool_name,
@@ -173,10 +318,12 @@ def _convert_content(
return messages
async def _transform_stream(
async def _transform_stream( # noqa: C901 - This is complex, but better to have it in one place
chat_log: conversation.ChatLog,
stream: AsyncStream[MessageStreamEvent],
) -> AsyncGenerator[conversation.AssistantContentDeltaDict]:
) -> AsyncGenerator[
conversation.AssistantContentDeltaDict | conversation.ToolResultContentDeltaDict
]:
"""Transform the response stream into HA format.
A typical stream of responses might look something like the following:
@@ -209,11 +356,13 @@ async def _transform_stream(
if stream is None:
raise TypeError("Expected a stream of messages")
current_tool_block: ToolUseBlockParam | None = None
current_tool_block: ToolUseBlockParam | ServerToolUseBlockParam | None = None
current_tool_args: str
content_details = ContentDetails()
content_details.add_citation_detail()
input_usage: Usage | None = None
has_content = False
has_native = False
first_block: bool
async for response in stream:
LOGGER.debug("Received response: %s", response)
@@ -222,6 +371,7 @@ async def _transform_stream(
if response.message.role != "assistant":
raise ValueError("Unexpected message role")
input_usage = response.message.usage
first_block = True
elif isinstance(response, RawContentBlockStartEvent):
if isinstance(response.content_block, ToolUseBlock):
current_tool_block = ToolUseBlockParam(
@@ -232,17 +382,37 @@ async def _transform_stream(
)
current_tool_args = ""
elif isinstance(response.content_block, TextBlock):
if has_content:
if ( # Do not start a new assistant content just for citations, concatenate consecutive blocks with citations instead.
first_block
or (
not content_details.has_citations()
and response.content_block.citations is None
and content_details.has_content()
)
):
if content_details.has_citations():
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
yield {"role": "assistant"}
has_native = False
has_content = True
first_block = False
content_details.add_citation_detail()
if response.content_block.text:
content_details.citation_details[-1].length += len(
response.content_block.text
)
yield {"content": response.content_block.text}
elif isinstance(response.content_block, ThinkingBlock):
if has_native:
if first_block or has_native:
if content_details.has_citations():
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {"role": "assistant"}
has_native = False
has_content = False
first_block = False
elif isinstance(response.content_block, RedactedThinkingBlock):
LOGGER.debug(
"Some of Claudes internal reasoning has been automatically "
@@ -250,15 +420,60 @@ async def _transform_stream(
"responses"
)
if has_native:
if content_details.has_citations():
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {"role": "assistant"}
has_native = False
has_content = False
first_block = False
yield {"native": response.content_block}
has_native = True
elif isinstance(response.content_block, ServerToolUseBlock):
current_tool_block = ServerToolUseBlockParam(
type="server_tool_use",
id=response.content_block.id,
name=response.content_block.name,
input="",
)
current_tool_args = ""
elif isinstance(response.content_block, WebSearchToolResultBlock):
if content_details.has_citations():
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
yield {
"role": "tool_result",
"tool_call_id": response.content_block.tool_use_id,
"tool_name": "web_search",
"tool_result": {
"type": "web_search_tool_result_error",
"error_code": response.content_block.content.error_code,
}
if isinstance(
response.content_block.content, WebSearchToolResultError
)
else {
"content": [
{
"type": "web_search_result",
"encrypted_content": block.encrypted_content,
"page_age": block.page_age,
"title": block.title,
"url": block.url,
}
for block in response.content_block.content
]
},
}
first_block = True
elif isinstance(response, RawContentBlockDeltaEvent):
if isinstance(response.delta, InputJSONDelta):
current_tool_args += response.delta.partial_json
elif isinstance(response.delta, TextDelta):
content_details.citation_details[-1].length += len(response.delta.text)
yield {"content": response.delta.text}
elif isinstance(response.delta, ThinkingDelta):
yield {"thinking_content": response.delta.thinking}
@@ -271,6 +486,8 @@ async def _transform_stream(
)
}
has_native = True
elif isinstance(response.delta, CitationsDelta):
content_details.add_citation(response.delta.citation)
elif isinstance(response, RawContentBlockStopEvent):
if current_tool_block is not None:
tool_args = json.loads(current_tool_args) if current_tool_args else {}
@@ -281,6 +498,7 @@ async def _transform_stream(
id=current_tool_block["id"],
tool_name=current_tool_block["name"],
tool_args=tool_args,
external=current_tool_block["type"] == "server_tool_use",
)
]
}
@@ -290,6 +508,12 @@ async def _transform_stream(
chat_log.async_trace(_create_token_stats(input_usage, usage))
if response.delta.stop_reason == "refusal":
raise HomeAssistantError("Potential policy violation detected")
elif isinstance(response, RawMessageStopEvent):
if content_details.has_citations():
content_details.delete_empty()
yield {"native": content_details}
content_details = ContentDetails()
content_details.add_citation_detail()
def _create_token_stats(
@@ -337,21 +561,11 @@ class AnthropicBaseLLMEntity(Entity):
"""Generate an answer for the chat log."""
options = self.subentry.data
tools: list[ToolParam] | None = None
if chat_log.llm_api:
tools = [
_format_tool(tool, chat_log.llm_api.custom_serializer)
for tool in chat_log.llm_api.tools
]
system = chat_log.content[0]
if not isinstance(system, conversation.SystemContent):
raise TypeError("First message must be a system message")
messages = _convert_content(chat_log.content[1:])
client = self.entry.runtime_data
thinking_budget = options.get(CONF_THINKING_BUDGET, RECOMMENDED_THINKING_BUDGET)
model = options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL)
model_args = MessageCreateParamsStreaming(
@@ -361,10 +575,10 @@ class AnthropicBaseLLMEntity(Entity):
system=system.content,
stream=True,
)
if tools:
model_args["tools"] = tools
thinking_budget = options.get(CONF_THINKING_BUDGET, RECOMMENDED_THINKING_BUDGET)
if (
model.startswith(tuple(THINKING_MODELS))
not model.startswith(tuple(NON_THINKING_MODELS))
and thinking_budget >= MIN_THINKING_BUDGET
):
model_args["thinking"] = ThinkingConfigEnabledParam(
@@ -376,6 +590,34 @@ class AnthropicBaseLLMEntity(Entity):
CONF_TEMPERATURE, RECOMMENDED_TEMPERATURE
)
tools: list[ToolUnionParam] = []
if chat_log.llm_api:
tools = [
_format_tool(tool, chat_log.llm_api.custom_serializer)
for tool in chat_log.llm_api.tools
]
if options.get(CONF_WEB_SEARCH):
web_search = WebSearchTool20250305Param(
name="web_search",
type="web_search_20250305",
max_uses=options.get(CONF_WEB_SEARCH_MAX_USES),
)
if options.get(CONF_WEB_SEARCH_USER_LOCATION):
web_search["user_location"] = {
"type": "approximate",
"city": options.get(CONF_WEB_SEARCH_CITY, ""),
"region": options.get(CONF_WEB_SEARCH_REGION, ""),
"country": options.get(CONF_WEB_SEARCH_COUNTRY, ""),
"timezone": options.get(CONF_WEB_SEARCH_TIMEZONE, ""),
}
tools.append(web_search)
if tools:
model_args["tools"] = tools
client = self.entry.runtime_data
# To prevent infinite loops, we limit the number of iterations
for _iteration in range(MAX_TOOL_ITERATIONS):
try:
@@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/anthropic",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["anthropic==0.62.0"]
"requirements": ["anthropic==0.69.0"]
}
@@ -35,11 +35,17 @@
"temperature": "Temperature",
"llm_hass_api": "[%key:common::config_flow::data::llm_hass_api%]",
"recommended": "Recommended model settings",
"thinking_budget_tokens": "Thinking budget"
"thinking_budget": "Thinking budget",
"web_search": "Enable web search",
"web_search_max_uses": "Maximum web searches",
"user_location": "Include home location"
},
"data_description": {
"prompt": "Instruct how the LLM should respond. This can be a template.",
"thinking_budget_tokens": "The number of tokens the model can use to think about the response out of the total maximum number of tokens. Set to 1024 or greater to enable extended thinking."
"thinking_budget": "The number of tokens the model can use to think about the response out of the total maximum number of tokens. Set to 1024 or greater to enable extended thinking.",
"web_search": "The web search tool gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff",
"web_search_max_uses": "Limit the number of searches performed per response",
"user_location": "Localize search results based on home location"
}
}
},
@@ -48,7 +54,8 @@
"entry_not_loaded": "Cannot add things while the configuration is disabled."
},
"error": {
"thinking_budget_too_large": "Maximum tokens must be greater than the thinking budget."
"thinking_budget_too_large": "Maximum tokens must be greater than the thinking budget.",
"web_search_unsupported_model": "Web search is not supported by the selected model. Please choose a compatible model or disable web search."
}
}
}
+47 -26
View File
@@ -5,14 +5,9 @@ from __future__ import annotations
import asyncio
import logging
from random import randrange
import sys
from typing import Any, cast
from pyatv import connect, exceptions, scan
from pyatv.conf import AppleTV
from pyatv.const import DeviceModel, Protocol
from pyatv.convert import model_str
from pyatv.interface import AppleTV as AppleTVInterface, DeviceListener
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -29,7 +24,11 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryNotReady,
HomeAssistantError,
)
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send
@@ -43,6 +42,18 @@ from .const import (
SIGNAL_DISCONNECTED,
)
if sys.version_info < (3, 14):
from pyatv import connect, exceptions, scan
from pyatv.conf import AppleTV
from pyatv.const import DeviceModel, Protocol
from pyatv.convert import model_str
from pyatv.interface import AppleTV as AppleTVInterface, DeviceListener
else:
class DeviceListener:
"""Dummy class."""
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME_TV = "Apple TV"
@@ -53,31 +64,41 @@ BACKOFF_TIME_UPPER_LIMIT = 300 # Five minutes
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE]
AUTH_EXCEPTIONS = (
exceptions.AuthenticationError,
exceptions.InvalidCredentialsError,
exceptions.NoCredentialsError,
)
CONNECTION_TIMEOUT_EXCEPTIONS = (
OSError,
asyncio.CancelledError,
TimeoutError,
exceptions.ConnectionLostError,
exceptions.ConnectionFailedError,
)
DEVICE_EXCEPTIONS = (
exceptions.ProtocolError,
exceptions.NoServiceError,
exceptions.PairingError,
exceptions.BackOffError,
exceptions.DeviceIdMissingError,
)
if sys.version_info < (3, 14):
AUTH_EXCEPTIONS = (
exceptions.AuthenticationError,
exceptions.InvalidCredentialsError,
exceptions.NoCredentialsError,
)
CONNECTION_TIMEOUT_EXCEPTIONS = (
OSError,
asyncio.CancelledError,
TimeoutError,
exceptions.ConnectionLostError,
exceptions.ConnectionFailedError,
)
DEVICE_EXCEPTIONS = (
exceptions.ProtocolError,
exceptions.NoServiceError,
exceptions.PairingError,
exceptions.BackOffError,
exceptions.DeviceIdMissingError,
)
else:
AUTH_EXCEPTIONS = ()
CONNECTION_TIMEOUT_EXCEPTIONS = ()
DEVICE_EXCEPTIONS = ()
type AppleTvConfigEntry = ConfigEntry[AppleTVManager]
async def async_setup_entry(hass: HomeAssistant, entry: AppleTvConfigEntry) -> bool:
"""Set up a config entry for Apple TV."""
if sys.version_info >= (3, 14):
raise HomeAssistantError(
"Apple TV is not supported on Python 3.14. Please use Python 3.13."
)
manager = AppleTVManager(hass, entry)
if manager.is_on:
@@ -7,7 +7,7 @@
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
"iot_class": "local_push",
"loggers": ["pyatv", "srptools"],
"requirements": ["pyatv==0.16.1"],
"requirements": ["pyatv==0.16.1;python_version<'3.14'"],
"zeroconf": [
"_mediaremotetv._tcp.local.",
"_companion-link._tcp.local.",
+11 -4
View File
@@ -7,6 +7,8 @@ from typing import Any
from pyaprilaire.const import Attribute
from homeassistant.components.climate import (
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
FAN_AUTO,
FAN_ON,
PRESET_AWAY,
@@ -16,7 +18,12 @@ from homeassistant.components.climate import (
HVACAction,
HVACMode,
)
from homeassistant.const import PRECISION_HALVES, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.const import (
ATTR_TEMPERATURE,
PRECISION_HALVES,
PRECISION_WHOLE,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -232,15 +239,15 @@ class AprilaireClimate(BaseAprilaireEntity, ClimateEntity):
cool_setpoint = 0
heat_setpoint = 0
if temperature := kwargs.get("temperature"):
if temperature := kwargs.get(ATTR_TEMPERATURE):
if self.coordinator.data.get(Attribute.MODE) == 3:
cool_setpoint = temperature
else:
heat_setpoint = temperature
else:
if target_temp_low := kwargs.get("target_temp_low"):
if target_temp_low := kwargs.get(ATTR_TARGET_TEMP_LOW):
heat_setpoint = target_temp_low
if target_temp_high := kwargs.get("target_temp_high"):
if target_temp_high := kwargs.get(ATTR_TARGET_TEMP_HIGH):
cool_setpoint = target_temp_high
if cool_setpoint == 0 and heat_setpoint == 0:
@@ -41,6 +41,8 @@ from .pipeline import (
async_setup_pipeline_store,
async_update_pipeline,
)
from .select import AssistPipelineSelect, VadSensitivitySelect
from .vad import VadSensitivity
from .websocket_api import async_register_websocket_api
__all__ = (
@@ -51,11 +53,14 @@ __all__ = (
"SAMPLE_CHANNELS",
"SAMPLE_RATE",
"SAMPLE_WIDTH",
"AssistPipelineSelect",
"AudioSettings",
"Pipeline",
"PipelineEvent",
"PipelineEventType",
"PipelineNotFound",
"VadSensitivity",
"VadSensitivitySelect",
"WakeWordSettings",
"async_create_default_pipeline",
"async_get_pipelines",
@@ -1308,7 +1308,9 @@ class PipelineRun:
# instead of a full response.
all_targets_in_satellite_area = (
self._get_all_targets_in_satellite_area(
conversation_result.response, self._device_id
conversation_result.response,
self._satellite_id,
self._device_id,
)
)
@@ -1337,39 +1339,62 @@ class PipelineRun:
return (speech, all_targets_in_satellite_area)
def _get_all_targets_in_satellite_area(
self, intent_response: intent.IntentResponse, device_id: str | None
self,
intent_response: intent.IntentResponse,
satellite_id: str | None,
device_id: str | None,
) -> bool:
"""Return true if all targeted entities were in the same area as the device."""
if (
(intent_response.response_type != intent.IntentResponseType.ACTION_DONE)
or (not intent_response.matched_states)
or (not device_id)
):
return False
device_registry = dr.async_get(self.hass)
if (not (device := device_registry.async_get(device_id))) or (
not device.area_id
intent_response.response_type != intent.IntentResponseType.ACTION_DONE
or not intent_response.matched_states
):
return False
entity_registry = er.async_get(self.hass)
for state in intent_response.matched_states:
entity = entity_registry.async_get(state.entity_id)
if not entity:
device_registry = dr.async_get(self.hass)
area_id: str | None = None
if (
satellite_id is not None
and (target_entity_entry := entity_registry.async_get(satellite_id))
is not None
):
area_id = target_entity_entry.area_id
device_id = target_entity_entry.device_id
if area_id is None:
if device_id is None:
return False
if (entity_area_id := entity.area_id) is None:
if (entity.device_id is None) or (
(entity_device := device_registry.async_get(entity.device_id))
is None
):
device_entry = device_registry.async_get(device_id)
if device_entry is None:
return False
area_id = device_entry.area_id
if area_id is None:
return False
for state in intent_response.matched_states:
target_entity_entry = entity_registry.async_get(state.entity_id)
if target_entity_entry is None:
return False
target_area_id = target_entity_entry.area_id
if target_area_id is None:
if target_entity_entry.device_id is None:
return False
entity_area_id = entity_device.area_id
target_device_entry = device_registry.async_get(
target_entity_entry.device_id
)
if target_device_entry is None:
return False
if entity_area_id != device.area_id:
target_area_id = target_device_entry.area_id
if target_area_id != area_id:
return False
return True
+14 -7
View File
@@ -3,17 +3,17 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from collections import namedtuple
from collections.abc import Awaitable, Callable, Coroutine
import functools
import logging
from typing import Any, cast
from typing import Any, NamedTuple
from aioasuswrt.asuswrt import AsusWrt as AsusWrtLegacy
from aiohttp import ClientSession
from asusrouter import AsusRouter, AsusRouterError
from asusrouter.config import ARConfigKey
from asusrouter.modules.client import AsusClient
from asusrouter.modules.connection import ConnectionState
from asusrouter.modules.data import AsusData
from asusrouter.modules.homeassistant import convert_to_ha_data, convert_to_ha_sensors
from asusrouter.tools.connection import get_cookie_jar
@@ -61,7 +61,14 @@ SENSORS_TYPE_RATES = "sensors_rates"
SENSORS_TYPE_TEMPERATURES = "sensors_temperatures"
SENSORS_TYPE_UPTIME = "sensors_uptime"
WrtDevice = namedtuple("WrtDevice", ["ip", "name", "connected_to"]) # noqa: PYI024
class WrtDevice(NamedTuple):
"""WrtDevice structure."""
ip: str | None
name: str | None
conneted_to: str | None
_LOGGER = logging.getLogger(__name__)
@@ -80,7 +87,7 @@ def handle_errors_and_zip[_AsusWrtBridgeT: AsusWrtBridge](
"""Run library methods and zip results or manage exceptions."""
@functools.wraps(func)
async def _wrapper(self: _AsusWrtBridgeT) -> dict[str, Any]:
async def _wrapper(self: _AsusWrtBridgeT) -> dict[str, str]:
try:
data = await func(self)
except exceptions as exc:
@@ -219,7 +226,7 @@ class AsusWrtLegacyBridge(AsusWrtBridge):
@property
def is_connected(self) -> bool:
"""Get connected status."""
return cast(bool, self._api.is_connected)
return self._api.is_connected
async def async_connect(self) -> None:
"""Connect to the device."""
@@ -235,8 +242,7 @@ class AsusWrtLegacyBridge(AsusWrtBridge):
async def async_disconnect(self) -> None:
"""Disconnect to the device."""
if self._api is not None and self._protocol == PROTOCOL_TELNET:
self._api.connection.disconnect()
await self._api.async_disconnect()
async def async_get_connected_devices(self) -> dict[str, WrtDevice]:
"""Get list of connected devices."""
@@ -437,6 +443,7 @@ class AsusWrtHttpBridge(AsusWrtBridge):
if dev.connection is not None
and dev.description is not None
and dev.connection.ip_address is not None
and dev.state is ConnectionState.CONNECTED
}
async def async_get_available_sensors(self) -> dict[str, dict[str, Any]]:
@@ -10,8 +10,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AsusWrtConfigEntry
from .router import AsusWrtDevInfo, AsusWrtRouter
ATTR_LAST_TIME_REACHABLE = "last_time_reachable"
DEFAULT_DEVICE_NAME = "Unknown device"
@@ -58,8 +56,6 @@ def add_entities(
class AsusWrtDevice(ScannerEntity):
"""Representation of a AsusWrt device."""
_unrecorded_attributes = frozenset({ATTR_LAST_TIME_REACHABLE})
_attr_should_poll = False
def __init__(self, router: AsusWrtRouter, device: AsusWrtDevInfo) -> None:
@@ -97,11 +93,6 @@ class AsusWrtDevice(ScannerEntity):
def async_on_demand_update(self) -> None:
"""Update state."""
self._device = self._router.devices[self._device.mac]
self._attr_extra_state_attributes = {}
if self._device.last_activity:
self._attr_extra_state_attributes[ATTR_LAST_TIME_REACHABLE] = (
self._device.last_activity.isoformat(timespec="seconds")
)
self.async_write_ha_state()
async def async_added_to_hass(self) -> None:
+2 -4
View File
@@ -2,9 +2,7 @@
from __future__ import annotations
from typing import Any, TypeVar
T = TypeVar("T", dict[str, Any], list[Any], None)
from typing import Any
TRANSLATION_MAP = {
"wan_rx": "sensor_rx_bytes",
@@ -36,7 +34,7 @@ def clean_dict(raw: dict[str, Any]) -> dict[str, Any]:
return {k: v for k, v in raw.items() if v is not None or k.endswith("state")}
def translate_to_legacy(raw: T) -> T:
def translate_to_legacy[T: (dict[str, Any], list[Any], None)](raw: T) -> T:
"""Translate raw data to legacy format for dicts and lists."""
if raw is None:
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioasuswrt", "asusrouter", "asyncssh"],
"requirements": ["aioasuswrt==1.4.0", "asusrouter==1.21.0"]
"requirements": ["aioasuswrt==1.5.1", "asusrouter==1.21.0"]
}
+7 -4
View File
@@ -36,11 +36,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bo
raise ConfigEntryAuthFailed("Migration to OAuth required")
session = async_create_august_clientsession(hass)
implementation = (
await config_entry_oauth2_flow.async_get_config_entry_implementation(
hass, entry
try:
implementation = (
await config_entry_oauth2_flow.async_get_config_entry_implementation(
hass, entry
)
)
)
except ValueError as err:
raise ConfigEntryNotReady("OAuth implementation not available") from err
oauth_session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
august_gateway = AugustGateway(Path(hass.config.config_dir), session, oauth_session)
try:
@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/autarco",
"iot_class": "cloud_polling",
"requirements": ["autarco==3.1.0"]
"requirements": ["autarco==3.2.0"]
}
+16 -11
View File
@@ -136,17 +136,22 @@ class WellKnownOAuthInfoView(HomeAssistantView):
url_prefix = get_url(hass, require_current_request=True)
except NoURLAvailableError:
url_prefix = ""
return self.json(
{
"authorization_endpoint": f"{url_prefix}/auth/authorize",
"token_endpoint": f"{url_prefix}/auth/token",
"revocation_endpoint": f"{url_prefix}/auth/revoke",
"response_types_supported": ["code"],
"service_documentation": (
"https://developers.home-assistant.io/docs/auth_api"
),
}
)
metadata = {
"authorization_endpoint": f"{url_prefix}/auth/authorize",
"token_endpoint": f"{url_prefix}/auth/token",
"revocation_endpoint": f"{url_prefix}/auth/revoke",
"response_types_supported": ["code"],
"service_documentation": (
"https://developers.home-assistant.io/docs/auth_api"
),
}
# Add issuer only when we have a valid base URL (RFC 8414 compliance)
if url_prefix:
metadata["issuer"] = url_prefix
return self.json(metadata)
class AuthProvidersView(HomeAssistantView):
+5 -3
View File
@@ -26,9 +26,6 @@ async def async_setup_entry(
if CONF_HOST in config_entry.data:
coordinator = AwairLocalDataUpdateCoordinator(hass, config_entry, session)
config_entry.async_on_unload(
config_entry.add_update_listener(_async_update_listener)
)
else:
coordinator = AwairCloudDataUpdateCoordinator(hass, config_entry, session)
@@ -36,6 +33,11 @@ async def async_setup_entry(
config_entry.runtime_data = coordinator
if CONF_HOST in config_entry.data:
config_entry.async_on_unload(
config_entry.add_update_listener(_async_update_listener)
)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
return True
+3 -2
View File
@@ -17,6 +17,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import frame
from homeassistant.util import slugify
from homeassistant.util.async_iterator import AsyncIteratorReader, AsyncIteratorWriter
from . import util
from .agent import BackupAgent
@@ -144,7 +145,7 @@ class DownloadBackupView(HomeAssistantView):
return Response(status=HTTPStatus.NOT_FOUND)
else:
stream = await agent.async_download_backup(backup_id)
reader = cast(IO[bytes], util.AsyncIteratorReader(hass, stream))
reader = cast(IO[bytes], AsyncIteratorReader(hass.loop, stream))
worker_done_event = asyncio.Event()
@@ -152,7 +153,7 @@ class DownloadBackupView(HomeAssistantView):
"""Call by the worker thread when it's done."""
hass.loop.call_soon_threadsafe(worker_done_event.set)
stream = util.AsyncIteratorWriter(hass)
stream = AsyncIteratorWriter(hass.loop)
worker = threading.Thread(
target=util.decrypt_backup,
args=[backup, reader, stream, password, on_done, 0, []],
+2 -2
View File
@@ -38,6 +38,7 @@ from homeassistant.helpers import (
)
from homeassistant.helpers.json import json_bytes
from homeassistant.util import dt as dt_util, json as json_util
from homeassistant.util.async_iterator import AsyncIteratorReader
from . import util as backup_util
from .agent import (
@@ -72,7 +73,6 @@ from .models import (
)
from .store import BackupStore
from .util import (
AsyncIteratorReader,
DecryptedBackupStreamer,
EncryptedBackupStreamer,
make_backup_dir,
@@ -1525,7 +1525,7 @@ class BackupManager:
reader = await self.hass.async_add_executor_job(open, path.as_posix(), "rb")
else:
backup_stream = await agent.async_download_backup(backup_id)
reader = cast(IO[bytes], AsyncIteratorReader(self.hass, backup_stream))
reader = cast(IO[bytes], AsyncIteratorReader(self.hass.loop, backup_stream))
try:
await self.hass.async_add_executor_job(
validate_password_stream, reader, password
+10 -112
View File
@@ -4,7 +4,6 @@ from __future__ import annotations
import asyncio
from collections.abc import AsyncIterator, Callable, Coroutine
from concurrent.futures import CancelledError, Future
import copy
from dataclasses import dataclass, replace
from io import BytesIO
@@ -14,7 +13,7 @@ from pathlib import Path, PurePath
from queue import SimpleQueue
import tarfile
import threading
from typing import IO, Any, Self, cast
from typing import IO, Any, cast
import aiohttp
from securetar import SecureTarError, SecureTarFile, SecureTarReadError
@@ -23,6 +22,11 @@ from homeassistant.backup_restore import password_to_key
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import dt as dt_util
from homeassistant.util.async_iterator import (
Abort,
AsyncIteratorReader,
AsyncIteratorWriter,
)
from homeassistant.util.json import JsonObjectType, json_loads_object
from .const import BUF_SIZE, LOGGER
@@ -59,12 +63,6 @@ class BackupEmpty(DecryptError):
_message = "No tar files found in the backup."
class AbortCipher(HomeAssistantError):
"""Abort the cipher operation."""
_message = "Abort cipher operation."
def make_backup_dir(path: Path) -> None:
"""Create a backup directory if it does not exist."""
path.mkdir(exist_ok=True)
@@ -166,106 +164,6 @@ def validate_password(path: Path, password: str | None) -> bool:
return False
class AsyncIteratorReader:
"""Wrap an AsyncIterator."""
def __init__(self, hass: HomeAssistant, stream: AsyncIterator[bytes]) -> None:
"""Initialize the wrapper."""
self._aborted = False
self._hass = hass
self._stream = stream
self._buffer: bytes | None = None
self._next_future: Future[bytes | None] | None = None
self._pos: int = 0
async def _next(self) -> bytes | None:
"""Get the next chunk from the iterator."""
return await anext(self._stream, None)
def abort(self) -> None:
"""Abort the reader."""
self._aborted = True
if self._next_future is not None:
self._next_future.cancel()
def read(self, n: int = -1, /) -> bytes:
"""Read data from the iterator."""
result = bytearray()
while n < 0 or len(result) < n:
if not self._buffer:
self._next_future = asyncio.run_coroutine_threadsafe(
self._next(), self._hass.loop
)
if self._aborted:
self._next_future.cancel()
raise AbortCipher
try:
self._buffer = self._next_future.result()
except CancelledError as err:
raise AbortCipher from err
self._pos = 0
if not self._buffer:
# The stream is exhausted
break
chunk = self._buffer[self._pos : self._pos + n]
result.extend(chunk)
n -= len(chunk)
self._pos += len(chunk)
if self._pos == len(self._buffer):
self._buffer = None
return bytes(result)
def close(self) -> None:
"""Close the iterator."""
class AsyncIteratorWriter:
"""Wrap an AsyncIterator."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the wrapper."""
self._aborted = False
self._hass = hass
self._pos: int = 0
self._queue: asyncio.Queue[bytes | None] = asyncio.Queue(maxsize=1)
self._write_future: Future[bytes | None] | None = None
def __aiter__(self) -> Self:
"""Return the iterator."""
return self
async def __anext__(self) -> bytes:
"""Get the next chunk from the iterator."""
if data := await self._queue.get():
return data
raise StopAsyncIteration
def abort(self) -> None:
"""Abort the writer."""
self._aborted = True
if self._write_future is not None:
self._write_future.cancel()
def tell(self) -> int:
"""Return the current position in the iterator."""
return self._pos
def write(self, s: bytes, /) -> int:
"""Write data to the iterator."""
self._write_future = asyncio.run_coroutine_threadsafe(
self._queue.put(s), self._hass.loop
)
if self._aborted:
self._write_future.cancel()
raise AbortCipher
try:
self._write_future.result()
except CancelledError as err:
raise AbortCipher from err
self._pos += len(s)
return len(s)
def validate_password_stream(
input_stream: IO[bytes],
password: str | None,
@@ -342,7 +240,7 @@ def decrypt_backup(
finally:
# Write an empty chunk to signal the end of the stream
output_stream.write(b"")
except AbortCipher:
except Abort:
LOGGER.debug("Cipher operation aborted")
finally:
on_done(error)
@@ -430,7 +328,7 @@ def encrypt_backup(
finally:
# Write an empty chunk to signal the end of the stream
output_stream.write(b"")
except AbortCipher:
except Abort:
LOGGER.debug("Cipher operation aborted")
finally:
on_done(error)
@@ -557,8 +455,8 @@ class _CipherBackupStreamer:
self._hass.loop.call_soon_threadsafe(worker_status.done.set)
stream = await self._open_stream()
reader = AsyncIteratorReader(self._hass, stream)
writer = AsyncIteratorWriter(self._hass)
reader = AsyncIteratorReader(self._hass.loop, stream)
writer = AsyncIteratorWriter(self._hass.loop)
worker = threading.Thread(
target=self._cipher_func,
args=[
@@ -73,11 +73,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: BangOlufsenConfigEntry)
# Add the websocket and API client
entry.runtime_data = BangOlufsenData(websocket, client)
# Start WebSocket connection
await client.connect_notifications(remote_control=True, reconnect=True)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Start WebSocket connection once the platforms have been loaded.
# This ensures that the initial WebSocket notifications are dispatched to entities
await client.connect_notifications(remote_control=True, reconnect=True)
return True
@@ -125,7 +125,8 @@ async def async_setup_entry(
async_add_entities(
new_entities=[
BangOlufsenMediaPlayer(config_entry, config_entry.runtime_data.client)
]
],
update_before_add=True,
)
# Register actions.
@@ -266,34 +267,8 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
self._software_status.software_version,
)
# Get overall device state once. This is handled by WebSocket events the rest of the time.
product_state = await self._client.get_product_state()
# Get volume information.
if product_state.volume:
self._volume = product_state.volume
# Get all playback information.
# Ensure that the metadata is not None upon startup
if product_state.playback:
if product_state.playback.metadata:
self._playback_metadata = product_state.playback.metadata
self._remote_leader = product_state.playback.metadata.remote_leader
if product_state.playback.progress:
self._playback_progress = product_state.playback.progress
if product_state.playback.source:
self._source_change = product_state.playback.source
if product_state.playback.state:
self._playback_state = product_state.playback.state
# Set initial state
if self._playback_state.value:
self._state = self._playback_state.value
self._attr_media_position_updated_at = utcnow()
# Get the highest resolution available of the given images.
self._media_image = get_highest_resolution_artwork(self._playback_metadata)
# If the device has been updated with new sources, then the API will fail here.
await self._async_update_sources()
@@ -3,16 +3,12 @@ beolink_allstandby:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
beolink_expand:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
fields:
all_discovered:
required: false
@@ -37,8 +33,6 @@ beolink_join:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
fields:
jid_options:
collapsed: false
@@ -71,16 +65,12 @@ beolink_leave:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
beolink_unexpand:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
fields:
jid_options:
collapsed: false
@@ -272,6 +272,13 @@ async def async_setup_entry(
observations: list[ConfigType] = [
dict(subentry.data) for subentry in config_entry.subentries.values()
]
for observation in observations:
if observation[CONF_PLATFORM] == CONF_TEMPLATE:
observation[CONF_VALUE_TEMPLATE] = Template(
observation[CONF_VALUE_TEMPLATE], hass
)
prior: float = config[CONF_PRIOR]
probability_threshold: float = config[CONF_PROBABILITY_THRESHOLD]
device_class: BinarySensorDeviceClass | None = config.get(CONF_DEVICE_CLASS)
@@ -146,7 +146,7 @@
},
"state": {
"title": "Add a Bayesian sensor",
"description": "Add an observation which evaluates to `True` when the value of the sensor exactly matches *'To state'*. When `False`, it will update the prior with probabilities that are the inverse of those set below. This behaviour can be overridden by adding observations for the same entity's other states.",
"description": "Add an observation which evaluates to `True` when the value of the sensor exactly matches *'To state'*. When `False`, it will update the prior with probabilities that are the inverse of those set below. This behavior can be overridden by adding observations for the same entity's other states.",
"data": {
"name": "[%key:common::config_flow::data::name%]",
@@ -57,6 +57,7 @@ from .api import (
_get_manager,
async_address_present,
async_ble_device_from_address,
async_clear_address_from_match_history,
async_current_scanners,
async_discovered_service_info,
async_get_advertisement_callback,
@@ -112,9 +113,9 @@ __all__ = [
"BluetoothServiceInfo",
"BluetoothServiceInfoBleak",
"HaBluetoothConnector",
"HomeAssistantRemoteScanner",
"async_address_present",
"async_ble_device_from_address",
"async_clear_address_from_match_history",
"async_current_scanners",
"async_discovered_service_info",
"async_get_advertisement_callback",
+14
View File
@@ -193,6 +193,20 @@ def async_rediscover_address(hass: HomeAssistant, address: str) -> None:
_get_manager(hass).async_rediscover_address(address)
@hass_callback
def async_clear_address_from_match_history(hass: HomeAssistant, address: str) -> None:
"""Clear an address from the integration matcher history.
This allows future advertisements from this address to trigger discovery
even if the advertisement content has changed but the service data UUIDs
remain the same.
Unlike async_rediscover_address, this does not immediately re-trigger
discovery with the current advertisement in history.
"""
_get_manager(hass).async_clear_address_from_match_history(address)
@hass_callback
def async_register_scanner(
hass: HomeAssistant,
@@ -120,6 +120,19 @@ class HomeAssistantBluetoothManager(BluetoothManager):
if service_info := self._all_history.get(address):
self._async_trigger_matching_discovery(service_info)
@hass_callback
def async_clear_address_from_match_history(self, address: str) -> None:
"""Clear an address from the integration matcher history.
This allows future advertisements from this address to trigger discovery
even if the advertisement content has changed but the service data UUIDs
remain the same.
Unlike async_rediscover_address, this does not immediately re-trigger
discovery with the current advertisement in history.
"""
self._integration_matcher.async_clear_address(address)
def _discover_service_info(self, service_info: BluetoothServiceInfoBleak) -> None:
matched_domains = self._integration_matcher.match_domains(service_info)
if self._debug:
@@ -19,8 +19,8 @@
"bleak-retry-connector==4.4.3",
"bluetooth-adapters==2.1.0",
"bluetooth-auto-recovery==1.5.3",
"bluetooth-data-tools==1.28.2",
"dbus-fast==2.44.3",
"habluetooth==5.6.4"
"bluetooth-data-tools==1.28.3",
"dbus-fast==2.44.5",
"habluetooth==5.7.0"
]
}

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