Compare commits

...

171 Commits

Author SHA1 Message Date
Franck Nijhof b6c8718ae4
Bump version to 2025.6.0b7 2025-06-11 10:17:18 +00:00
Åke Strandberg c8b70cc0fb
Graceful handling of missing datapoint in myuplink (#146517) 2025-06-11 10:17:09 +00:00
Robert Resch 6d1f621e55
Bump deebot-client to 13.3.0 (#146507) 2025-06-11 10:17:08 +00:00
Erik Montnemery 671a33b31c
Do not remove derivative config entry when input sensor is removed (#146506)
* Do not remove derivative config entry when input sensor is removed

* Add comments

* Update homeassistant/helpers/helper_integration.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-11 10:17:06 +00:00
Michael Hansen 7afc469306
Bump intents to 2025.6.10 (#146491) 2025-06-11 10:17:05 +00:00
Felix Schneider 8fd52248b7
Bump `apsystems` to `2.7.0` (#146485) 2025-06-11 10:17:04 +00:00
Joost Lekkerkerker 69ba2aab11
Remove DHCP discovery from Amazon Devices (#146476) 2025-06-11 10:17:02 +00:00
Tsvi Mostovicz f1df6dcda5
Fix Jewish calendar not updating (#146465) 2025-06-11 10:17:01 +00:00
Joost Lekkerkerker 43e16bb913
Split deprecated system issue in 2 places (#146453) 2025-06-11 10:15:11 +00:00
Petro31 4147211f94
Add color_temp_kelvin to set_temperature action variables (#146448) 2025-06-11 10:10:40 +00:00
Joost Lekkerkerker 63e49c5d3c
Explain Nest setup (#146217) 2025-06-11 10:10:39 +00:00
hahn-th 35580c0849
Bump homematicip to 2.0.4 (#144096)
* Bump to 2.0.2 with all necessary changes

* bump to prerelease

* add addiional tests

* Bump to homematicip 2.0.3

* do not delete device

* Setup BRAND_SWITCH_MEASURING as light

* bump to 2.0.4

* refactor test_remove_obsolete_entities

* move test

* use const from homematicip lib
2025-06-11 10:10:38 +00:00
Franck Nijhof 8949a595fe
Bump version to 2025.6.0b6 2025-06-10 17:45:26 +00:00
Simone Chemelli bf8ef0a767
Fix EntityCategory for binary_sensor platform in Amazon Devices (#146472)
* Fix EntityCategory for  binary_sensor platform in Amazon Devices

* update snapshots
2025-06-10 17:41:02 +00:00
Simone Chemelli 39962a3f48
Avoid closing shared aiohttp session in Vodafone Station (#146471) 2025-06-10 17:41:00 +00:00
G Johansson 4964621014
Fix incorrect categories handling in holiday (#146470) 2025-06-10 17:40:59 +00:00
Joost Lekkerkerker 18e1a26da1
Catch exception before retrying in AirGradient (#146460) 2025-06-10 17:40:58 +00:00
Joost Lekkerkerker 1d91ca5716
Bump pySmartThings to 3.2.4 (#146459) 2025-06-10 17:40:57 +00:00
Luca Schröder 1040646610
Update caldav to 1.6.0 (#146456)
Fixes #140798
2025-06-10 17:40:56 +00:00
Robert Resch fcd71931e7
Update wording deprecated system package integration repair (#146450)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-10 17:40:55 +00:00
Joost Lekkerkerker bdbb74aff1
Return expected state in SmartThings water heater (#146449) 2025-06-10 17:40:53 +00:00
Marc Mueller 6f4029983a
Update requests to 2.32.4 (#146445) 2025-06-10 17:40:52 +00:00
J. Diego Rodríguez Royo b2d25b1883
Improvements for Home Connect application credentials string (#146443) 2025-06-10 17:40:51 +00:00
J. Diego Rodríguez Royo ba19d4f043
Fix typo at application credentials string at Home Connect integration (#146442)
Fix typos
2025-06-10 17:40:50 +00:00
tronikos b222fe5afa
Handle grpc errors in Google Assistant SDK (#146438) 2025-06-10 17:40:49 +00:00
Franck Nijhof f945defa2b
Reformat Dockerfile to reduce merge conflicts (#146435) 2025-06-10 17:38:37 +00:00
J. Nick Koston 4f0e4bc1ca
Bump aiohttp to 3.12.12 (#146426) 2025-06-10 17:28:13 +00:00
J. Nick Koston 41abc8404d
Bump yarl to 1.20.1 (#146424) 2025-06-10 17:26:03 +00:00
Jamin 2b08c4c344
Check hangup error in voip (#146423)
Check hangup error

Prevent an error where the call end future may have already been set
when a hangup is detected.
2025-06-10 17:26:02 +00:00
J. Nick Koston 97d91ddddb
Bump propcache to 0.3.2 (#146418) 2025-06-10 17:26:01 +00:00
Whitney Young ec30b12fd1
Fix initial state of UV protection window (#146408)
The `binary_sensor` is created when the config entry is loaded after the
`async_config_entry_first_refresh` has completed (during the forward of
setup to platforms). Therefore, the update coordinator will already have
data and will not trigger the invocation of
`_handle_coordinator_update`.

Fixing this just means performing the same update at initialization.
2025-06-10 17:25:59 +00:00
Erik Montnemery 9997fc11b1
Handle changes to source entity in derivative helper (#146407)
* Handle changes to source entity in derivative helper

* Rename helper function, improve docstring

* Add tests

* Improve derivative tests

* Deduplicate tests

* Rename helpers/helper_entity.py to helpers/helper_integration.py

* Rename tests
2025-06-10 17:25:59 +00:00
wittypluck c6ff0e6492
Fix CO concentration unit in OpenWeatherMap (#146403) 2025-06-10 17:25:58 +00:00
G Johansson a3220ecae6
Bump pynordpool to 0.3.0 (#146396) 2025-06-10 17:25:57 +00:00
Erik Montnemery 218864d08c
Update switch_as_x to handle wrapped switch moved to another device (#146387)
* Update switch_as_x to handle wrapped switch moved to another device

* Reload switch_as_x config entry after updating device

* Make sure the switch_as_x entity is not removed
2025-06-10 17:25:56 +00:00
Erik Montnemery 3d0d70ece6
Fix switch_as_x entity_id tracking (#146386) 2025-06-10 17:25:55 +00:00
Simone Chemelli f629731930
Bump aioamazondevices to 3.0.6 (#146385) 2025-06-10 17:25:53 +00:00
J. Nick Koston e7a7b2417b
Bump aioesphomeapi to 32.2.1 (#146375) 2025-06-10 17:03:20 +00:00
Michael Davie 0b24a9abc3
Bump env-canada to v0.11.2 (#146371) 2025-06-10 17:03:19 +00:00
David Knowles ca77b5210f
Bump pydrawise to 2025.6.0 (#146369) 2025-06-10 17:03:18 +00:00
Simon Lamon 0874f1c350
Bump python-linkplay to v0.2.10 (#146359) 2025-06-10 17:03:17 +00:00
Jan-Philipp Benecke d89b99f42b
Improve error logging in trend binary sensor (#146358) 2025-06-10 17:03:16 +00:00
J. Diego Rodríguez Royo 7bd6ec68a8
Explain Home Connect setup (#146356)
* Explain Home Connect setup

* Avoid using "we"

* Fix login spelling

* Fix signup spelling
2025-06-10 17:03:15 +00:00
J. Nick Koston bfe2eeb833
Shift ESPHome log parsing to the library (#146349) 2025-06-10 17:03:14 +00:00
Klaas Schoute e97ab1fe3c
Change interval for Powerfox integration (#146348) 2025-06-10 17:03:13 +00:00
J. Nick Koston b3ee2a8885
Bump aioesphomeapi to 32.2.0 (#146344) 2025-06-10 17:03:12 +00:00
Michael 80b09e3212
Bump py-synologydsm-api to 2.7.3 (#146338)
bump py-synologydsm-api to 2.7.3
2025-06-10 17:03:11 +00:00
tronikos 0eb3714abc
Allow different manufacturer than Amazon in Amazon Devices (#146333) 2025-06-10 17:03:10 +00:00
Sanjay Govind 7991977443
Fix bosch alarm areas not correctly subscribing to alarms (#146322)
* Fix bosch alarm areas not correctly subscribing to alarms

* add test
2025-06-10 17:03:09 +00:00
J. Nick Koston 5e5431c9f9
Use entity unique id for ESPHome media player formats (#146318) 2025-06-10 17:03:08 +00:00
Simon Lamon 1fc05d1a30
Do not probe linkplay device if another config entry already contains the host (#146305)
* Do not probe if config entry already contains the host

* Add unit test

* Use common fixture
2025-06-10 17:03:07 +00:00
J. Nick Koston 21833e7c31
Bump aiohttp to 3.12.11 (#146298) 2025-06-10 16:59:07 +00:00
G Johansson 79daeb23a9
Bump holidays to 0.74 (#146290) 2025-06-10 16:55:33 +00:00
J. Nick Koston 761c2578fb
Bump aiohttp-fast-zlib to 0.3.0 (#146285)
changelog: https://github.com/Bluetooth-Devices/aiohttp-fast-zlib/compare/v0.2.3...v0.3.0

proper aiohttp 3.12 support
2025-06-10 16:48:33 +00:00
Brett Adams 4d3145e559
Add missing write state to Teslemetry (#146267) 2025-06-10 16:47:33 +00:00
Michael 91e29a3bf1
Bump aioimmich to 0.9.1 (#146222)
bump aioimmich to 0.9.1
2025-06-10 16:47:32 +00:00
Joost Lekkerkerker f6a4486c65
Explain Withings setup (#146216) 2025-06-10 16:47:31 +00:00
Joost Lekkerkerker fc8b512931
Remove zeroconf discovery from Spotify (#146213) 2025-06-10 16:47:30 +00:00
Brett Adams e5dd15da82
Fix Export Rule Select Entity in Tessie (#146203)
Fix TessieExportRuleSelectEntity
2025-06-10 16:47:29 +00:00
Brett Adams e4140d71ab
Prevent energy history returning zero in Teslemetry (#146202) 2025-06-10 16:47:28 +00:00
J. Nick Koston 8312780c47
Bump aiohttp to 3.12.9 (#146178) 2025-06-10 16:44:14 +00:00
Raphael Hehl 5accc3dec2
Bump uiprotect to 7.11.0 (#146171)
Bump uiprotect to version 7.11.0
2025-06-10 16:42:40 +00:00
Iskra kranj d875989866
Bump pyiskra to 0.1.21 (#146156) 2025-06-10 16:42:39 +00:00
Michael 38c92a2338
Bump aioimmich to 0.9.0 (#146154)
bump aioimmich to 0.9.0
2025-06-10 16:42:38 +00:00
J. Nick Koston ce76b5db16
Bump aiohttp to 3.12.8 (#146153) 2025-06-10 16:39:58 +00:00
Ian dfc4889d45
Throttle Nextbus if we are reaching the rate limit (#146064)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-06-10 16:32:59 +00:00
Andrea Turri 41431282ee
Add evaporate water program id for Miele oven (#145996) 2025-06-10 16:32:58 +00:00
Arie Catsman 5821b2f03c
fix possible mac collision in enphase_envoy (#145549)
* fix possible mac collision in enphase_envoy

* remove redundant device registry async_get
2025-06-10 16:32:57 +00:00
starkillerOG 78d2bf736c
Reolink conserve battery (#145452) 2025-06-10 16:32:56 +00:00
Franck Nijhof 6c098c3e0a
Bump version to 2025.6.0b5 2025-06-04 09:02:53 +00:00
J. Nick Koston bfb140d2e9
Bump aioesphomeapi to 32.0.0 (#146135) 2025-06-04 09:00:59 +00:00
J. Nick Koston f71a1a7a89
Bump protobuf to 6.31.1 (#146128)
changelog: https://github.com/protocolbuffers/protobuf/compare/v30.2...v31.1
2025-06-04 09:00:57 +00:00
Erwin Douna e8aab39620
SMA fix strings (#146112)
* Fix

* Feedback
2025-06-04 09:00:55 +00:00
J. Nick Koston 1d578d8563
Bump habluetooth to 3.49.0 (#146111)
* Bump habluetooth to 3.49.0

changelog: https://github.com/Bluetooth-Devices/habluetooth/compare/v3.48.2...v3.49.0

* update diag

* diag
2025-06-04 09:00:53 +00:00
J. Nick Koston abfd443541
Bump bleak-esphome to 2.16.0 (#146110) 2025-06-04 09:00:51 +00:00
Brett Adams 81cbb6e5cf
Fix BMS and Charge states in Teslemetry (#146091)
Fix BMS and Charge states
2025-06-04 09:00:49 +00:00
Retha Runolfsson 010c5cab87
Fix nightlatch option for all switchbot locks (#146090) 2025-06-04 09:00:47 +00:00
SNoof85 415858119a
Add state class measurement to Freebox temperature sensors (#146074) 2025-06-04 09:00:44 +00:00
Simone Chemelli 1838a731d6
Bump aioamazondevices to 3.0.5 (#146073) 2025-06-04 09:00:42 +00:00
Shay Levy 1e304fad65
Fix Shelly BLU TRV calibrate button (#146066) 2025-06-04 09:00:40 +00:00
Michael 999c9b3dc5
Don't use multi-line conditionals in immich (#146062) 2025-06-04 09:00:37 +00:00
epenet e15edbd54b
Adjust SamsungTV on/off logging (#146045)
* Adjust SamsungTV on/off logging

* Update coordinator.py
2025-06-04 09:00:35 +00:00
epenet e5cb77d168
Adjust ConnectionFailure logging in SamsungTV (#146044) 2025-06-04 09:00:32 +00:00
starkillerOG cf521d4c7c
Improve debug logging Reolink (#146033)
Add debug logging
2025-06-04 09:00:25 +00:00
J. Nick Koston 6f09474193
Bump grpcio to 1.72.1 (#146029) 2025-06-04 09:00:21 +00:00
Robert Resch 7626933352
Bump go2rtc-client to 0.2.1 (#146019)
* Bump go2rtc-client to 0.2.0

* Bump go2rtc-client to 0.2.1

* Clean up hassfest exception

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-04 08:56:07 +00:00
starkillerOG 9e1d8c2fc6
Bump reolink-aio to 0.13.5 (#145974)
* Add debug logging

* Bump reolink-aio to 0.13.5

* Revert "Add debug logging"

This reverts commit f96030a6c8.
2025-06-04 08:43:30 +00:00
Simone Chemelli 6defed2915
Bump aioamazondevices to 3.0.4 (#145971) 2025-06-04 08:43:28 +00:00
Simone Chemelli d729eed7c2
Add diagnostics to Amazon devices (#145964) 2025-06-04 08:43:26 +00:00
Noah Groß f280032dcf
Bump python-picnic-api2 to 1.3.1 (#145962) 2025-06-04 08:43:21 +00:00
Allen Porter 7e85137012
Bump ical to 10.0.0 (#145954) 2025-06-04 08:43:18 +00:00
TimL 88f2c3abd3
Bump pysmlight to v0.2.5 (#145949) 2025-06-04 08:43:15 +00:00
Michael 1a21e01f85
Bump aioimmich to 0.8.0 (#145908) 2025-06-04 08:43:13 +00:00
Ian d302e817c8
NextBus: Bump py_nextbusnext to 2.2.0 (#145904) 2025-06-04 08:43:09 +00:00
Martin Hjelmare 1e1b0424d7
Fix removal of devices during Z-Wave migration (#145867) 2025-06-04 08:43:07 +00:00
Robert Resch 03f028b7e2
Deprecate hddtemp (#145850) 2025-06-04 08:43:05 +00:00
Robert Resch b1d35de8e4
Deprecate eddystone temperature integration (#145833) 2025-06-04 08:43:02 +00:00
Erwin Douna ea6b9e5260
SMA add missing strings for DHCP (#145782) 2025-06-04 08:42:59 +00:00
Bram Kragten 06d869aaa5 Bump version to 2025.6.0b4 2025-05-31 21:25:06 +02:00
Josef Zweck 907cebdd6d Increase update intervals in lamarzocco (#145939) 2025-05-31 21:25:02 +02:00
Josef Zweck 745902bc7e Bump pylamarzocco to 2.0.8 (#145938) 2025-05-31 21:25:01 +02:00
Bram Kragten ef0b3c9f9c Update frontend to 20250531.0 (#145933) 2025-05-31 21:25:00 +02:00
J. Nick Koston 532c077ddf Bump aiohttp to 3.12.6 (#145919)
* Bump aiohttp to 3.12.5

changelog: https://github.com/aio-libs/aiohttp/compare/v3.12.4...v3.12.5

* .6

* fix mock
2025-05-31 21:24:59 +02:00
tronikos cd905a6593 Bump opower to 0.12.3 (#145918) 2025-05-31 21:24:59 +02:00
Josef Zweck d0bf9d9bfb Move server device creation to init in jellyfin (#145910)
* Move server device creation to init in jellyfin

* move device creation to after coordinator refresh
2025-05-31 21:24:58 +02:00
Jordan Harvey ddc79a631d Bump pyprobeplus to 1.0.1 (#145897) 2025-05-31 21:24:57 +02:00
Simon Lamon 6015f60db4 Bump python-linkplay to v0.2.9 (#145892) 2025-05-31 21:24:57 +02:00
Iskra kranj a6608bd7ea Bump pyiskra to 0.1.19 (#145889) 2025-05-31 21:24:56 +02:00
Brett Adams fb2d8c6406 Add streaming to charge cable connected in Teslemetry (#145880) 2025-05-31 21:24:55 +02:00
Brett Adams c84ffb54d2 Bump tesla-fleet-api to 1.1.1. (#145869)
bump
2025-05-31 21:24:54 +02:00
Samuel Xiao 306bbdc697 Bump switchbot-api to 2.4.0 (#145786)
* update switchbot-api version to 2.4.0

* debug for test code
2025-05-31 21:24:54 +02:00
Robert Resch 9879ecad85 Deprecate snips integration (#145784) 2025-05-31 21:24:53 +02:00
Joost Lekkerkerker f0fcef5744 Add more Amazon Devices DHCP matches (#145776) 2025-05-31 21:24:52 +02:00
Bram Kragten aa8a6058b5 Bump version to 2025.6.0b3 2025-05-30 12:56:51 +02:00
J. Diego Rodríguez Royo 48103bd244 Bump aiohomeconnect to 0.17.1 (#145873) 2025-05-30 12:56:45 +02:00
Robert Resch 600ac17a5f Deprecate sms integration (#145847) 2025-05-30 12:56:44 +02:00
Michael d46f28792c Bump aioimmich to 0.7.0 (#145845) 2025-05-30 12:56:43 +02:00
starkillerOG 0f7379c941 Reolink fallback to download command for playback (#145842) 2025-05-30 12:56:43 +02:00
J. Nick Koston 4317fad798 Bump aiohttp to 3.12.4 (#145838) 2025-05-30 12:56:42 +02:00
J. Nick Koston 5cfccb7e1d Bump aiohttp to 3.12.3 (#145837) 2025-05-30 12:56:41 +02:00
Matthew FitzGerald-Chamberlain 097eecd78a Bump pyaprilaire to 0.9.1 (#145836) 2025-05-30 12:56:40 +02:00
Brett Adams 64b4642c49 Fix Tessie volume max and step (#145835)
* Use fixed volume max and step

* Update snapshot
2025-05-30 12:56:39 +02:00
Michael 0e87d14ca8 Use mime type provided by Immich (#145830)
use mime type from immich instead of guessing it
2025-05-30 12:56:38 +02:00
Josef Zweck 4d22b35a9f Bump aiotedee to 0.2.23 (#145822)
* Bump aiotedee to 0.2.23

* update snapshot
2025-05-30 12:56:37 +02:00
G Johansson 26586b4514 Fix language selections in workday (#145813) 2025-05-30 12:56:36 +02:00
Robert Resch 95fb2a7d7f Deprecate decora integration (#145807) 2025-05-30 12:56:35 +02:00
Robert Resch fa66ea31d3 Deprecate tensorflow (#145806)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-05-30 12:56:34 +02:00
André Lersveen e0d3b819e5 Set correct nobo_hub max temperature (#145751)
Max temperature 30°C is implemented upstream in pynobo and the Nobø Energy Hub app also stops at 30°C.
2025-05-30 12:56:34 +02:00
Bram Kragten 17a0b4f3d0 Bump version to 2025.6.0b2 2025-05-28 23:18:38 +02:00
Bram Kragten d0d228d9f4 Update frontend to 20250528.0 (#145828)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-05-28 23:18:33 +02:00
Michael 309acb961b Fix Immich media source browsing with multiple config entries (#145823)
fix media source browsing with multiple config entries
2025-05-28 23:18:32 +02:00
Michael Hansen 12f8ebb3ea Bump intents to 2025.5.28 (#145816) 2025-05-28 23:18:32 +02:00
David Bonnes 612861061c Fix HOMEASSISTANT_STOP unsubscribe in data update coordinator (#145809)
* initial commit

* a better approach

* Add comment
2025-05-28 23:18:31 +02:00
Robert Resch 83af5ec36b Deprecate keyboard integration (#145805) 2025-05-28 23:18:30 +02:00
starkillerOG 74102d0319 Bump reolink-aio to 0.13.4 (#145799) 2025-05-28 23:18:29 +02:00
Robert Resch fbd05a0fcf Deprecate lirc integration (#145797) 2025-05-28 23:18:29 +02:00
Robert Resch a53c786fe0 Deprecate pandora integration (#145785) 2025-05-28 23:18:28 +02:00
Josef Zweck eb2728e5b9 Fix uom for prebrew numbers in lamarzocco (#145772) 2025-05-28 23:18:27 +02:00
J. Diego Rodríguez Royo 3f17223387 Add more information about possible hostnames at Home Connect (#145770) 2025-05-28 23:18:26 +02:00
Robert Resch 74104cf107 Deprecate GStreamer integration (#145768) 2025-05-28 23:18:25 +02:00
Robert Resch 13b4879723 Deprecate dlib image processing integrations (#145767) 2025-05-28 23:18:25 +02:00
Erik Montnemery f1ec0b2c59 Handle late abort when creating subentry (#145765)
* Handle late abort when creating subentry

* Move error handling to the base class

* Narrow down expected error in test
2025-05-28 23:18:24 +02:00
Josef Zweck 6d44daf599 Bump pylamarzocco to 2.0.7 (#145763) 2025-05-28 23:18:23 +02:00
Joost Lekkerkerker 644a6f5569 Add more Amazon Devices DHCP matches (#145754) 2025-05-28 23:18:22 +02:00
Abílio Costa fb83396522 Add Shelly zwave virtual integration (#145749) 2025-05-28 23:18:22 +02:00
Raphael Hehl e825bd0bdb Bump uiprotect to version 7.10.1 (#145737)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-05-28 23:18:21 +02:00
G Johansson 61823ec7e2 Fix dns resolver error in dnsip config flow validation (#145735)
Fix dns resolver error in dnsip
2025-05-28 23:18:20 +02:00
Michael cd133cbbe3 Add level of collections in Immich media source tree (#145734)
* add layer for collections in media source tree

* re-arange tests, add test for collection layer

* fix
2025-05-28 23:18:19 +02:00
Erik Montnemery 0e7a1bb76c Make async_remove_stale_devices_links_keep_entity_device move entities (#145719)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-05-28 23:18:18 +02:00
Josef Zweck f86bf69ebc Update otp description for amazon_devices (#145701)
* Update otp description from amazon_devices

* separate

* Update strings.json
2025-05-28 23:18:18 +02:00
Jan Bouwhuis adddf330fd Ensure mqtt sensor unit of measurement validation for state class `measurement_angle` (#145648) 2025-05-28 23:18:17 +02:00
Bram Kragten 10adb57b83 Bump version to 2025.6.0b1 2025-05-27 22:16:13 +02:00
Bram Kragten 3160fe9abc Update frontend to 20250527.0 (#145741) 2025-05-27 22:14:02 +02:00
Erwin Douna 6adb27d173 Tado update mobile devices interval (#145738)
Update the mobile devices interval to five minutes
2025-05-27 22:14:01 +02:00
Joost Lekkerkerker 6e6aae2ea3 Fix unbound local variable in Acmeda config flow (#145729) 2025-05-27 22:14:00 +02:00
Kevin Stillhammer 41a140d16c Debug log the update response in google_travel_time (#145725)
Debug log the update response
2025-05-27 22:14:00 +02:00
Kevin Stillhammer 8880ab6498 Catch PermissionDenied(Route API disabled) in google_travel_time (#145722)
Catch PermissionDenied(Route API disabled)
2025-05-27 22:13:59 +02:00
Martin Hjelmare 389becc4f6 Disable advanced window cover position Matter sensor by default (#145713)
* Disable advanced window cover position Matter sensor by default

* Enanble disabled sensors in snapshot test
2025-05-27 22:13:58 +02:00
Martin Hjelmare 923530972a Remove static pin code length Matter sensors (#145711)
* Remove static Matter sensors

* Clean up translation strings
2025-05-27 22:13:57 +02:00
Martin Hjelmare b84850df9f Fix error stack trace for HomeAssistantError in websocket service call (#145699)
* Add test

* Fix error stack trace for HomeAssistantError in websocket service call
2025-05-27 22:13:56 +02:00
Joost Lekkerkerker 9e7dc1d11d Use string type for amazon devices OTP code (#145698) 2025-05-27 22:13:56 +02:00
Petar Petrov 2830ed6147 Change description on recommended/custom Z-Wave install step (#145688)
Change description on recommended/custom Z-WaveJS step
2025-05-27 22:13:55 +02:00
Petar Petrov bfa919d078 Remove confirm screen after Z-Wave usb discovery (#145682)
* Remove confirm screen after Z-Wave usb discovery

* Simplify async_step_usb
2025-05-27 22:13:54 +02:00
Jan Bouwhuis f09c28e61f Fix justnimbus CI test (#145681) 2025-05-27 22:13:54 +02:00
J. Nick Koston bfdba7713e Bump aiohttp to 3.12.2 (#145671) 2025-05-27 22:13:53 +02:00
Kevin Stillhammer d6cadc1e3f Support addresses with comma in google_travel_time (#145663)
Support addresses with comma
2025-05-27 22:13:52 +02:00
Joost Lekkerkerker 20a6a3f195 Handle Google Nest DHCP flows (#145658)
* Handle Google Nest DHCP flows

* Handle Google Nest DHCP flows
2025-05-27 22:13:51 +02:00
Joost Lekkerkerker f60de45b52 Fix Amazon devices offline handling (#145656) 2025-05-27 22:13:50 +02:00
Joost Lekkerkerker 77031d1ae4 Fix Aquacell snapshot (#145651) 2025-05-27 22:13:49 +02:00
Jan Bouwhuis 9483a88ee1 Fix translation for sensor measurement angle state class (#145649) 2025-05-27 22:13:48 +02:00
Franck Nijhof 3438a4f063
Bump version to 2025.6.0b0 2025-05-26 20:31:18 +00:00
264 changed files with 5220 additions and 1553 deletions

View File

@ -0,0 +1,6 @@
{
"domain": "shelly",
"name": "shelly",
"integrations": ["shelly"],
"iot_standards": ["zwave"]
}

View File

@ -40,9 +40,10 @@ class AcmedaFlowHandler(ConfigFlow, domain=DOMAIN):
entry.unique_id for entry in self._async_current_entries()
}
hubs: list[aiopulse.Hub] = []
with suppress(TimeoutError):
async with timeout(5):
hubs: list[aiopulse.Hub] = [
hubs = [
hub
async for hub in aiopulse.Hub.discover()
if hub.id not in already_configured

View File

@ -51,9 +51,16 @@ class AirGradientCoordinator(DataUpdateCoordinator[AirGradientData]):
async def _async_setup(self) -> None:
"""Set up the coordinator."""
self._current_version = (
await self.client.get_current_measures()
).firmware_version
try:
self._current_version = (
await self.client.get_current_measures()
).firmware_version
except AirGradientError as error:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_error",
translation_placeholders={"error": str(error)},
) from error
async def _async_update_data(self) -> AirGradientData:
try:

View File

@ -13,6 +13,7 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@ -34,10 +35,12 @@ BINARY_SENSORS: Final = (
AmazonBinarySensorEntityDescription(
key="online",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
is_on_fn=lambda _device: _device.online,
),
AmazonBinarySensorEntityDescription(
key="bluetooth",
entity_category=EntityCategory.DIAGNOSTIC,
translation_key="bluetooth",
is_on_fn=lambda _device: _device.bluetooth_state,
),

View File

@ -57,7 +57,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
): CountrySelector(),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_CODE): cv.positive_int,
vol.Required(CONF_CODE): cv.string,
}
),
)

View File

@ -0,0 +1,66 @@
"""Diagnostics support for Amazon Devices integration."""
from __future__ import annotations
from typing import Any
from aioamazondevices.api import AmazonDevice
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntry
from .coordinator import AmazonConfigEntry
TO_REDACT = {CONF_PASSWORD, CONF_USERNAME, CONF_NAME, "title"}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: AmazonConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator = entry.runtime_data
devices: list[dict[str, dict[str, Any]]] = [
build_device_data(device) for device in coordinator.data.values()
]
return {
"entry": async_redact_data(entry.as_dict(), TO_REDACT),
"device_info": {
"last_update success": coordinator.last_update_success,
"last_exception": repr(coordinator.last_exception),
"devices": devices,
},
}
async def async_get_device_diagnostics(
hass: HomeAssistant, entry: AmazonConfigEntry, device_entry: DeviceEntry
) -> dict[str, Any]:
"""Return diagnostics for a device."""
coordinator = entry.runtime_data
assert device_entry.serial_number
return build_device_data(coordinator.data[device_entry.serial_number])
def build_device_data(device: AmazonDevice) -> dict[str, Any]:
"""Build device data for diagnostics."""
return {
"account name": device.account_name,
"capabilities": device.capabilities,
"device family": device.device_family,
"device type": device.device_type,
"device cluster members": device.device_cluster_members,
"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,
}

View File

@ -25,15 +25,15 @@ class AmazonEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
"""Initialize the entity."""
super().__init__(coordinator)
self._serial_num = serial_num
model_details = coordinator.api.get_model_details(self.device)
model = model_details["model"] if model_details else None
model_details = coordinator.api.get_model_details(self.device) or {}
model = model_details.get("model")
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, serial_num)},
name=self.device.account_name,
model=model,
model_id=self.device.device_type,
manufacturer="Amazon",
hw_version=model_details["hw_version"] if model_details else None,
manufacturer=model_details.get("manufacturer", "Amazon"),
hw_version=model_details.get("hw_version"),
sw_version=(
self.device.software_version if model != SPEAKER_GROUP_MODEL else None
),
@ -50,4 +50,8 @@ class AmazonEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
@property
def available(self) -> bool:
"""Return True if entity is available."""
return super().available and self._serial_num in self.coordinator.data
return (
super().available
and self._serial_num in self.coordinator.data
and self.device.online
)

View File

@ -3,31 +3,10 @@
"name": "Amazon Devices",
"codeowners": ["@chemelli74"],
"config_flow": true,
"dhcp": [
{ "macaddress": "08A6BC*" },
{ "macaddress": "10BF67*" },
{ "macaddress": "440049*" },
{ "macaddress": "443D54*" },
{ "macaddress": "48B423*" },
{ "macaddress": "4C1744*" },
{ "macaddress": "50D45C*" },
{ "macaddress": "50DCE7*" },
{ "macaddress": "68F63B*" },
{ "macaddress": "74D637*" },
{ "macaddress": "7C6166*" },
{ "macaddress": "901195*" },
{ "macaddress": "943A91*" },
{ "macaddress": "98226E*" },
{ "macaddress": "9CC8E9*" },
{ "macaddress": "A8E621*" },
{ "macaddress": "C095CF*" },
{ "macaddress": "D8BE65*" },
{ "macaddress": "EC2BEB*" }
],
"documentation": "https://www.home-assistant.io/integrations/amazon_devices",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "bronze",
"requirements": ["aioamazondevices==2.1.1"]
"requirements": ["aioamazondevices==3.0.6"]
}

View File

@ -45,7 +45,9 @@ rules:
discovery-update-info:
status: exempt
comment: Network information not relevant
discovery: done
discovery:
status: exempt
comment: There are a ton of mac address ranges in use, but also by kindles which are not supported by this integration
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo

View File

@ -5,7 +5,7 @@
"data_description_country": "The country of your Amazon account.",
"data_description_username": "The email address of your Amazon account.",
"data_description_password": "The password of your Amazon account.",
"data_description_code": "The one-time password sent to your email address."
"data_description_code": "The one-time password to log in to your account. Currently, only tokens from OTP applications are supported."
},
"config": {
"flow_title": "{username}",

View File

@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["pyaprilaire"],
"requirements": ["pyaprilaire==0.9.0"]
"requirements": ["pyaprilaire==0.9.1"]
}

View File

@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["APsystemsEZ1"],
"requirements": ["apsystems-ez1==2.6.0"]
"requirements": ["apsystems-ez1==2.7.0"]
}

View File

@ -21,6 +21,6 @@
"bluetooth-auto-recovery==1.5.2",
"bluetooth-data-tools==1.28.1",
"dbus-fast==2.43.0",
"habluetooth==3.48.2"
"habluetooth==3.49.0"
]
}

View File

@ -50,7 +50,7 @@ class AreaAlarmControlPanel(BoschAlarmAreaEntity, AlarmControlPanelEntity):
def __init__(self, panel: Panel, area_id: int, unique_id: str) -> None:
"""Initialise a Bosch Alarm control panel entity."""
super().__init__(panel, area_id, unique_id, False, False, True)
super().__init__(panel, area_id, unique_id, True, False, True)
self._attr_unique_id = self._area_unique_id
@property

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/caldav",
"iot_class": "cloud_polling",
"loggers": ["caldav", "vobject"],
"requirements": ["caldav==1.3.9", "icalendar==6.1.0"]
"requirements": ["caldav==1.6.0", "icalendar==6.1.0"]
}

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.5.7"]
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.6.10"]
}

View File

@ -1 +1,3 @@
"""The decora component."""
DOMAIN = "decora"

View File

@ -21,7 +21,11 @@ from homeassistant.components.light import (
LightEntity,
)
from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from . import DOMAIN
if TYPE_CHECKING:
from homeassistant.core import HomeAssistant
@ -90,6 +94,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up an Decora switch."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Leviton Decora",
},
)
lights = []
for address, device_config in config[CONF_DEVICES].items():
device = {}

View File

@ -2,12 +2,18 @@
from __future__ import annotations
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_SOURCE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device import (
async_entity_id_to_device_id,
async_remove_stale_devices_links_keep_entity_device,
)
from homeassistant.helpers.helper_integration import async_handle_source_entity_changes
from .const import DOMAIN
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@ -17,6 +23,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass, entry.entry_id, entry.options[CONF_SOURCE]
)
def set_source_entity_id_or_uuid(source_entity_id: str) -> None:
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_SOURCE: source_entity_id},
)
async def source_entity_removed() -> None:
# The source entity has been removed, we need to clean the device links.
async_remove_stale_devices_links_keep_entity_device(hass, entry.entry_id, None)
entity_registry = er.async_get(hass)
entry.async_on_unload(
async_handle_source_entity_changes(
hass,
helper_config_entry_id=entry.entry_id,
get_helper_entity_id=lambda: entity_registry.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, entry.entry_id
),
set_source_entity_id_or_uuid=set_source_entity_id_or_uuid,
source_device_id=async_entity_id_to_device_id(
hass, entry.options[CONF_SOURCE]
),
source_entity_id_or_uuid=entry.options[CONF_SOURCE],
source_entity_removed=source_entity_removed,
)
)
await hass.config_entries.async_forward_entry_setups(entry, (Platform.SENSOR,))
entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
return True

View File

@ -1 +1,3 @@
"""The dlib_face_detect component."""
DOMAIN = "dlib_face_detect"

View File

@ -11,10 +11,17 @@ from homeassistant.components.image_processing import (
ImageProcessingFaceEntity,
)
from homeassistant.const import ATTR_LOCATION, CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE
from homeassistant.core import HomeAssistant, split_entity_id
from homeassistant.core import (
DOMAIN as HOMEASSISTANT_DOMAIN,
HomeAssistant,
split_entity_id,
)
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA
@ -25,6 +32,20 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Dlib Face detection platform."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Dlib Face Detect",
},
)
source: list[dict[str, str]] = config[CONF_SOURCE]
add_entities(
DlibFaceDetectEntity(camera[CONF_ENTITY_ID], camera.get(CONF_NAME))

View File

@ -1 +1,4 @@
"""The dlib_face_identify component."""
CONF_FACES = "faces"
DOMAIN = "dlib_face_identify"

View File

@ -15,14 +15,20 @@ from homeassistant.components.image_processing import (
ImageProcessingFaceEntity,
)
from homeassistant.const import ATTR_NAME, CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE
from homeassistant.core import HomeAssistant, split_entity_id
from homeassistant.core import (
DOMAIN as HOMEASSISTANT_DOMAIN,
HomeAssistant,
split_entity_id,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import CONF_FACES, DOMAIN
_LOGGER = logging.getLogger(__name__)
CONF_FACES = "faces"
PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend(
{
@ -39,6 +45,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Dlib Face detection platform."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Dlib Face Identify",
},
)
confidence: float = config[CONF_CONFIDENCE]
faces: dict[str, str] = config[CONF_FACES]
source: list[dict[str, str]] = config[CONF_SOURCE]

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio
import contextlib
from typing import Any
from typing import Any, Literal
import aiodns
from aiodns.error import DNSError
@ -62,16 +62,16 @@ async def async_validate_hostname(
"""Validate hostname."""
async def async_check(
hostname: str, resolver: str, qtype: str, port: int = 53
hostname: str, resolver: str, qtype: Literal["A", "AAAA"], port: int = 53
) -> bool:
"""Return if able to resolve hostname."""
result = False
result: bool = False
with contextlib.suppress(DNSError):
result = bool(
await aiodns.DNSResolver( # type: ignore[call-overload]
nameservers=[resolver], udp_port=port, tcp_port=port
).query(hostname, qtype)
_resolver = aiodns.DNSResolver(
nameservers=[resolver], udp_port=port, tcp_port=port
)
result = bool(await _resolver.query(hostname, qtype))
return result
result: dict[str, bool] = {}

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.11", "deebot-client==13.2.1"]
"requirements": ["py-sucks==0.9.11", "deebot-client==13.3.0"]
}

View File

@ -1 +1,6 @@
"""The eddystone_temperature component."""
DOMAIN = "eddystone_temperature"
CONF_BEACONS = "beacons"
CONF_INSTANCE = "instance"
CONF_NAMESPACE = "namespace"

View File

@ -23,17 +23,18 @@ from homeassistant.const import (
STATE_UNKNOWN,
UnitOfTemperature,
)
from homeassistant.core import Event, HomeAssistant
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, Event, HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import CONF_BEACONS, CONF_INSTANCE, CONF_NAMESPACE, DOMAIN
_LOGGER = logging.getLogger(__name__)
CONF_BEACONS = "beacons"
CONF_BT_DEVICE_ID = "bt_device_id"
CONF_INSTANCE = "instance"
CONF_NAMESPACE = "namespace"
BEACON_SCHEMA = vol.Schema(
{
@ -58,6 +59,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Validate configuration, create devices and start monitoring thread."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Eddystone",
},
)
bt_device_id: int = config[CONF_BT_DEVICE_ID]
beacons: dict[str, dict[str, str]] = config[CONF_BEACONS]

View File

@ -180,9 +180,15 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
)
return
device_registry.async_update_device(
device_id=envoy_device.id,
new_connections={connection},
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
identifiers={
(
DOMAIN,
self.envoy_serial_number,
)
},
connections={connection},
)
_LOGGER.debug("added connection: %s to %s", connection, self.name)

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
"iot_class": "cloud_polling",
"loggers": ["env_canada"],
"requirements": ["env-canada==0.10.2"]
"requirements": ["env-canada==0.11.2"]
}

View File

@ -22,5 +22,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["eq3btsmart"],
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.15.1"]
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.16.0"]
}

View File

@ -226,6 +226,7 @@ class EsphomeEntity(EsphomeBaseEntity, Generic[_InfoT, _StateT]):
_static_info: _InfoT
_state: _StateT
_has_state: bool
unique_id: str
def __init__(
self,

View File

@ -5,7 +5,6 @@ from __future__ import annotations
import asyncio
from functools import partial
import logging
import re
from typing import TYPE_CHECKING, Any, NamedTuple
from aioesphomeapi import (
@ -23,6 +22,7 @@ from aioesphomeapi import (
RequiresEncryptionAPIError,
UserService,
UserServiceArgType,
parse_log_message,
)
from awesomeversion import AwesomeVersion
import voluptuous as vol
@ -110,11 +110,6 @@ LOGGER_TO_LOG_LEVEL = {
logging.ERROR: LogLevel.LOG_LEVEL_ERROR,
logging.CRITICAL: LogLevel.LOG_LEVEL_ERROR,
}
# 7-bit and 8-bit C1 ANSI sequences
# https://stackoverflow.com/questions/14693701/how-can-i-remove-the-ansi-escape-sequences-from-a-string-in-python
ANSI_ESCAPE_78BIT = re.compile(
rb"(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])"
)
@callback
@ -387,13 +382,15 @@ class ESPHomeManager:
def _async_on_log(self, msg: SubscribeLogsResponse) -> None:
"""Handle a log message from the API."""
log: bytes = msg.message
_LOGGER.log(
LOG_LEVEL_TO_LOGGER.get(msg.level, logging.DEBUG),
"%s: %s",
self.entry.title,
ANSI_ESCAPE_78BIT.sub(b"", log).decode("utf-8", "backslashreplace"),
)
for line in parse_log_message(
msg.message.decode("utf-8", "backslashreplace"), "", strip_ansi_escapes=True
):
_LOGGER.log(
LOG_LEVEL_TO_LOGGER.get(msg.level, logging.DEBUG),
"%s: %s",
self.entry.title,
line,
)
@callback
def _async_get_equivalent_log_level(self) -> LogLevel:

View File

@ -17,9 +17,9 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==31.1.0",
"aioesphomeapi==32.2.1",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==2.15.1"
"bleak-esphome==2.16.0"
],
"zeroconf": ["_esphomelib._tcp.local."]
}

View File

@ -78,7 +78,7 @@ class EsphomeMediaPlayer(
if self._static_info.supports_pause:
flags |= MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.PLAY
self._attr_supported_features = flags
self._entry_data.media_player_formats[static_info.unique_id] = cast(
self._entry_data.media_player_formats[self.unique_id] = cast(
MediaPlayerInfo, static_info
).supported_formats
@ -114,9 +114,8 @@ class EsphomeMediaPlayer(
media_id = async_process_play_media_url(self.hass, media_id)
announcement = kwargs.get(ATTR_MEDIA_ANNOUNCE)
bypass_proxy = kwargs.get(ATTR_MEDIA_EXTRA, {}).get(ATTR_BYPASS_PROXY)
supported_formats: list[MediaPlayerSupportedFormat] | None = (
self._entry_data.media_player_formats.get(self._static_info.unique_id)
self._entry_data.media_player_formats.get(self.unique_id)
)
if (
@ -139,7 +138,7 @@ class EsphomeMediaPlayer(
async def async_will_remove_from_hass(self) -> None:
"""Handle entity being removed."""
await super().async_will_remove_from_hass()
self._entry_data.media_player_formats.pop(self.entity_id, None)
self._entry_data.media_player_formats.pop(self.unique_id, None)
def _get_proxy_url(
self,

View File

@ -84,6 +84,7 @@ async def async_setup_entry(
name=f"Freebox {sensor_name}",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
)
for sensor_name in router.sensors_temperature

View File

@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250526.0"]
"requirements": ["home-assistant-frontend==20250531.0"]
}

View File

@ -8,6 +8,6 @@
"integration_type": "system",
"iot_class": "local_polling",
"quality_scale": "internal",
"requirements": ["go2rtc-client==0.1.3b0"],
"requirements": ["go2rtc-client==0.2.1"],
"single_config_entry": true
}

View File

@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/google",
"iot_class": "cloud_polling",
"loggers": ["googleapiclient"],
"requirements": ["gcal-sync==7.1.0", "oauth2client==4.1.3", "ical==9.2.5"]
"requirements": ["gcal-sync==7.1.0", "oauth2client==4.1.3", "ical==10.0.0"]
}

View File

@ -12,6 +12,7 @@ import aiohttp
from aiohttp import web
from gassist_text import TextAssistant
from google.oauth2.credentials import Credentials
from grpc import RpcError
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.media_player import (
@ -25,6 +26,7 @@ from homeassistant.components.media_player import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID, CONF_ACCESS_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
from homeassistant.helpers.event import async_call_later
@ -83,7 +85,17 @@ async def async_send_text_commands(
) as assistant:
command_response_list = []
for command in commands:
resp = await hass.async_add_executor_job(assistant.assist, command)
try:
resp = await hass.async_add_executor_job(assistant.assist, command)
except RpcError as err:
_LOGGER.error(
"Failed to send command '%s' to Google Assistant: %s",
command,
err,
)
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="grpc_error"
) from err
text_response = resp[0]
_LOGGER.debug("command: %s\nresponse: %s", command, text_response)
audio_response = resp[2]

View File

@ -57,5 +57,10 @@
}
}
}
},
"exceptions": {
"grpc_error": {
"message": "Failed to communicate with Google Assistant"
}
}
}

View File

@ -50,7 +50,12 @@ from .const import (
UNITS_IMPERIAL,
UNITS_METRIC,
)
from .helpers import InvalidApiKeyException, UnknownException, validate_config_entry
from .helpers import (
InvalidApiKeyException,
PermissionDeniedException,
UnknownException,
validate_config_entry,
)
RECONFIGURE_SCHEMA = vol.Schema(
{
@ -188,6 +193,8 @@ async def validate_input(
user_input[CONF_ORIGIN],
user_input[CONF_DESTINATION],
)
except PermissionDeniedException:
return {"base": "permission_denied"}
except InvalidApiKeyException:
return {"base": "invalid_auth"}
except TimeoutError:

View File

@ -7,6 +7,7 @@ from google.api_core.exceptions import (
Forbidden,
GatewayTimeout,
GoogleAPIError,
PermissionDenied,
Unauthorized,
)
from google.maps.routing_v2 import (
@ -19,10 +20,18 @@ from google.maps.routing_v2 import (
from google.type import latlng_pb2
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from homeassistant.helpers.location import find_coordinates
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@ -37,7 +46,7 @@ def convert_to_waypoint(hass: HomeAssistant, location: str) -> Waypoint | None:
try:
formatted_coordinates = coordinates.split(",")
vol.Schema(cv.gps(formatted_coordinates))
except (AttributeError, vol.ExactSequenceInvalid):
except (AttributeError, vol.Invalid):
return Waypoint(address=location)
return Waypoint(
location=Location(
@ -67,6 +76,9 @@ async def validate_config_entry(
await client.compute_routes(
request, metadata=[("x-goog-fieldmask", field_mask)]
)
except PermissionDenied as permission_error:
_LOGGER.error("Permission denied: %s", permission_error.message)
raise PermissionDeniedException from permission_error
except (Unauthorized, Forbidden) as unauthorized_error:
_LOGGER.error("Request denied: %s", unauthorized_error.message)
raise InvalidApiKeyException from unauthorized_error
@ -84,3 +96,30 @@ class InvalidApiKeyException(Exception):
class UnknownException(Exception):
"""Unknown API Error."""
class PermissionDeniedException(Exception):
"""Permission Denied Error."""
def create_routes_api_disabled_issue(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Create an issue for the Routes API being disabled."""
async_create_issue(
hass,
DOMAIN,
f"routes_api_disabled_{entry.entry_id}",
learn_more_url="https://www.home-assistant.io/integrations/google_travel_time#setup",
is_fixable=False,
severity=IssueSeverity.ERROR,
translation_key="routes_api_disabled",
translation_placeholders={
"entry_title": entry.title,
"enable_api_url": "https://cloud.google.com/endpoints/docs/openapi/enable-api",
"api_key_restrictions_url": "https://cloud.google.com/docs/authentication/api-keys#adding-api-restrictions",
},
)
def delete_routes_api_disabled_issue(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Delete the issue for the Routes API being disabled."""
async_delete_issue(hass, DOMAIN, f"routes_api_disabled_{entry.entry_id}")

View File

@ -7,7 +7,7 @@ import logging
from typing import TYPE_CHECKING, Any
from google.api_core.client_options import ClientOptions
from google.api_core.exceptions import GoogleAPIError
from google.api_core.exceptions import GoogleAPIError, PermissionDenied
from google.maps.routing_v2 import (
ComputeRoutesRequest,
Route,
@ -58,7 +58,11 @@ from .const import (
TRAVEL_MODES_TO_GOOGLE_SDK_ENUM,
UNITS_TO_GOOGLE_SDK_ENUM,
)
from .helpers import convert_to_waypoint
from .helpers import (
convert_to_waypoint,
create_routes_api_disabled_issue,
delete_routes_api_disabled_issue,
)
_LOGGER = logging.getLogger(__name__)
@ -271,8 +275,14 @@ class GoogleTravelTimeSensor(SensorEntity):
response = await self._client.compute_routes(
request, metadata=[("x-goog-fieldmask", FIELD_MASK)]
)
_LOGGER.debug("Received response: %s", response)
if response is not None and len(response.routes) > 0:
self._route = response.routes[0]
delete_routes_api_disabled_issue(self.hass, self._config_entry)
except PermissionDenied:
_LOGGER.error("Routes API is disabled for this API key")
create_routes_api_disabled_issue(self.hass, self._config_entry)
self._route = None
except GoogleAPIError as ex:
_LOGGER.error("Error getting travel time: %s", ex)
self._route = None

View File

@ -21,6 +21,7 @@
}
},
"error": {
"permission_denied": "The Routes API is not enabled for this API key. Please see the setup instructions for detailed information.",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
@ -100,5 +101,11 @@
"fewer_transfers": "Fewer transfers"
}
}
},
"issues": {
"routes_api_disabled": {
"title": "The Routes API must be enabled",
"description": "Your Google Travel Time integration `{entry_title}` uses an API key which does not have the Routes API enabled.\n\n Please follow the instructions to [enable the API for your project]({enable_api_url}) and make sure your [API key restrictions]({api_key_restrictions_url}) allow access to the Routes API.\n\n After enabling the API this issue will be resolved automatically."
}
}
}

View File

@ -1 +1,3 @@
"""The gstreamer component."""
DOMAIN = "gstreamer"

View File

@ -19,16 +19,18 @@ from homeassistant.components.media_player import (
async_process_play_media_url,
)
from homeassistant.const import CONF_NAME, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
CONF_PIPELINE = "pipeline"
DOMAIN = "gstreamer"
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
{vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_PIPELINE): cv.string}
@ -48,6 +50,20 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Gstreamer platform."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "GStreamer",
},
)
name = config.get(CONF_NAME)
pipeline = config.get(CONF_PIPELINE)

View File

@ -37,6 +37,7 @@ from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
discovery_flow,
issue_registry as ir,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.deprecation import (
@ -51,9 +52,11 @@ from homeassistant.helpers.hassio import (
get_supervisor_ip as _get_supervisor_ip,
is_hassio as _is_hassio,
)
from homeassistant.helpers.issue_registry import IssueSeverity
from homeassistant.helpers.service_info.hassio import (
HassioServiceInfo as _HassioServiceInfo,
)
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from homeassistant.util.async_ import create_eager_task
@ -109,7 +112,7 @@ from .coordinator import (
get_core_info, # noqa: F401
get_core_stats, # noqa: F401
get_host_info, # noqa: F401
get_info, # noqa: F401
get_info,
get_issues_info, # noqa: F401
get_os_info,
get_supervisor_info, # noqa: F401
@ -168,6 +171,11 @@ SERVICE_RESTORE_PARTIAL = "restore_partial"
VALID_ADDON_SLUG = vol.Match(re.compile(r"^[-_.A-Za-z0-9]+$"))
DEPRECATION_URL = (
"https://www.home-assistant.io/blog/2025/05/22/"
"deprecating-core-and-supervised-installation-methods-and-32-bit-systems/"
)
def valid_addon(value: Any) -> str:
"""Validate value is a valid addon slug."""
@ -546,6 +554,63 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh()
hass.data[ADDONS_COORDINATOR] = coordinator
system_info = await async_get_system_info(hass)
def deprecated_setup_issue() -> None:
os_info = get_os_info(hass)
info = get_info(hass)
if os_info is None or info is None:
return
is_haos = info.get("hassos") is not None
arch = system_info["arch"]
board = os_info.get("board")
supported_board = board in {"rpi3", "rpi4", "tinker", "odroid-xu4", "rpi2"}
if is_haos and arch == "armv7" and supported_board:
issue_id = "deprecated_os_"
if board in {"rpi3", "rpi4"}:
issue_id += "aarch64"
elif board in {"tinker", "odroid-xu4", "rpi2"}:
issue_id += "armv7"
ir.async_create_issue(
hass,
"homeassistant",
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_guide": "https://www.home-assistant.io/installation/",
},
)
deprecated_architecture = False
if arch in {"i386", "armhf"} or (arch == "armv7" and not supported_board):
deprecated_architecture = True
if not is_haos or deprecated_architecture:
issue_id = "deprecated"
if not is_haos:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
"homeassistant",
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": "OS" if is_haos else "Supervised",
"arch": arch,
},
)
listener()
listener = coordinator.async_add_listener(deprecated_setup_issue)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View File

@ -1 +1,3 @@
"""The hddtemp component."""
DOMAIN = "hddtemp"

View File

@ -22,11 +22,14 @@ from homeassistant.const import (
CONF_PORT,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
ATTR_DEVICE = "device"
@ -56,6 +59,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the HDDTemp sensor."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "hddtemp",
},
)
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)

View File

@ -25,17 +25,12 @@ def _get_obj_holidays_and_language(
selected_categories: list[str] | None,
) -> tuple[HolidayBase, str]:
"""Get the object for the requested country and year."""
if selected_categories is None:
categories = [PUBLIC]
else:
categories = [PUBLIC, *selected_categories]
obj_holidays = country_holidays(
country,
subdiv=province,
years={dt_util.now().year, dt_util.now().year + 1},
language=language,
categories=categories,
categories=selected_categories,
)
if language == "en":
for lang in obj_holidays.supported_languages:
@ -45,7 +40,7 @@ def _get_obj_holidays_and_language(
subdiv=province,
years={dt_util.now().year, dt_util.now().year + 1},
language=lang,
categories=categories,
categories=selected_categories,
)
language = lang
break
@ -59,7 +54,7 @@ def _get_obj_holidays_and_language(
subdiv=province,
years={dt_util.now().year, dt_util.now().year + 1},
language=default_language,
categories=categories,
categories=selected_categories,
)
language = default_language
@ -77,6 +72,11 @@ async def async_setup_entry(
categories: list[str] | None = config_entry.options.get(CONF_CATEGORIES)
language = hass.config.language
if categories is None:
categories = [PUBLIC]
else:
categories = [PUBLIC, *categories]
obj_holidays, language = await hass.async_add_executor_job(
_get_obj_holidays_and_language, country, province, language, categories
)

View File

@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.73", "babel==2.15.0"]
"requirements": ["holidays==0.74", "babel==2.15.0"]
}

View File

@ -12,3 +12,13 @@ async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationSe
authorize_url=OAUTH2_AUTHORIZE,
token_url=OAUTH2_TOKEN,
)
async def async_get_description_placeholders(hass: HomeAssistant) -> dict[str, str]:
"""Return description placeholders for the credentials dialog."""
return {
"developer_dashboard_url": "https://developer.home-connect.com/",
"applications_url": "https://developer.home-connect.com/applications",
"register_application_url": "https://developer.home-connect.com/application/add",
"redirect_url": "https://my.home-assistant.io/redirect/oauth",
}

View File

@ -10,17 +10,17 @@
"macaddress": "C8D778*"
},
{
"hostname": "(bosch|siemens)-*",
"hostname": "(balay|bosch|neff|siemens)-*",
"macaddress": "68A40E*"
},
{
"hostname": "siemens-*",
"hostname": "(siemens|neff)-*",
"macaddress": "38B4D3*"
}
],
"documentation": "https://www.home-assistant.io/integrations/home_connect",
"iot_class": "cloud_push",
"loggers": ["aiohomeconnect"],
"requirements": ["aiohomeconnect==0.17.0"],
"requirements": ["aiohomeconnect==0.17.1"],
"zeroconf": ["_homeconnect._tcp.local."]
}

View File

@ -1,4 +1,7 @@
{
"application_credentials": {
"description": "Login to Home Connect requires a client ID and secret. To acquire them, please follow the following steps.\n\n1. Visit the [Home Connect Developer Program website]({developer_dashboard_url}) and sign up for a development account.\n1. Enter the email of your login for the original Home Connect app under **Default Home Connect User Account for Testing** in the signup process.\n1. Go to the [Applications]({applications_url}) page and select [Register Application]({register_application_url}) and set the fields to the following values:\n * **Application ID**: Home Assistant (or any other name that makes sense)\n * **OAuth Flow**: Authorization Code Grant Flow\n * **Redirect URI**: `{redirect_url}`\n\nIn the newly created application's details, you will find the **Client ID** and the **Client Secret**."
},
"common": {
"confirmed": "Confirmed",
"present": "Present"
@ -13,7 +16,7 @@
"description": "The Home Connect integration needs to re-authenticate your account"
},
"oauth_discovery": {
"description": "Home Assistant has found a Home Connect device on your network. Press **Submit** to continue setting up Home Connect."
"description": "Home Assistant has found a Home Connect device on your network. Be aware that the setup of Home Connect is more complicated than many other integrations. Press **Submit** to continue setting up Home Connect."
}
},
"abort": {

View File

@ -4,7 +4,7 @@ import asyncio
from collections.abc import Callable, Coroutine
import itertools as it
import logging
from typing import TYPE_CHECKING, Any
from typing import Any
import voluptuous as vol
@ -38,7 +38,6 @@ from homeassistant.helpers import (
restore_state,
)
from homeassistant.helpers.entity_component import async_update_entity
from homeassistant.helpers.importlib import async_import_module
from homeassistant.helpers.issue_registry import IssueSeverity
from homeassistant.helpers.service import (
async_extract_config_entry_ids,
@ -402,46 +401,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
info = await async_get_system_info(hass)
installation_type = info["installation_type"][15:]
deprecated_method = installation_type in {
"Core",
"Supervised",
}
arch = info["arch"]
if arch == "armv7":
if installation_type == "OS":
# Local import to avoid circular dependencies
# We use the import helper because hassio
# may not be loaded yet and we don't want to
# do blocking I/O in the event loop to import it.
if TYPE_CHECKING:
# pylint: disable-next=import-outside-toplevel
from homeassistant.components import hassio
else:
hassio = await async_import_module(
hass, "homeassistant.components.hassio"
)
os_info = hassio.get_os_info(hass)
assert os_info is not None
issue_id = "deprecated_os_"
board = os_info.get("board")
if board in {"rpi3", "rpi4"}:
issue_id += "aarch64"
elif board in {"tinker", "odroid-xu4", "rpi2"}:
issue_id += "armv7"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_guide": "https://www.home-assistant.io/installation/",
},
)
elif installation_type == "Container":
if installation_type in {"Core", "Container"}:
deprecated_method = installation_type == "Core"
arch = info["arch"]
if arch == "armv7" and installation_type == "Container":
ir.async_create_issue(
hass,
DOMAIN,
@ -452,29 +415,31 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
severity=IssueSeverity.WARNING,
translation_key="deprecated_container_armv7",
)
deprecated_architecture = False
if arch in {"i386", "armhf"} or (arch == "armv7" and deprecated_method):
deprecated_architecture = True
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": installation_type,
"arch": arch,
},
)
deprecated_architecture = False
if arch in {"i386", "armhf"} or (
arch == "armv7" and installation_type != "Container"
):
deprecated_architecture = True
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": installation_type,
"arch": arch,
},
)
return True

View File

@ -18,9 +18,13 @@
"title": "The {integration_title} YAML configuration is being removed",
"description": "Configuring {integration_title} using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
},
"deprecated_system_packages_config_flow_integration": {
"title": "The {integration_title} integration is being removed",
"description": "The {integration_title} integration is being removed as it depends on system packages that can only be installed on systems running a deprecated architecture. To resolve this, remove all \"{integration_title}\" config entries."
},
"deprecated_system_packages_yaml_integration": {
"title": "The {integration_title} integration is being removed",
"description": "The {integration_title} integration is being removed as it requires additional system packages, which can't be installed on supported Home Assistant installations. Remove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
"description": "The {integration_title} integration is being removed as it depends on system packages that can only be installed on systems running a deprecated architecture. To resolve this, remove the {domain} entry from your configuration.yaml file and restart Home Assistant."
},
"historic_currency": {
"title": "The configured currency is no longer in use",

View File

@ -112,6 +112,7 @@ class HomematicipHAP:
self.config_entry = config_entry
self._ws_close_requested = False
self._ws_connection_closed = asyncio.Event()
self._retry_task: asyncio.Task | None = None
self._tries = 0
self._accesspoint_connected = True
@ -218,6 +219,8 @@ class HomematicipHAP:
try:
await self.home.get_current_state_async()
hmip_events = self.home.enable_events()
self.home.set_on_connected_handler(self.ws_connected_handler)
self.home.set_on_disconnected_handler(self.ws_disconnected_handler)
tries = 0
await hmip_events
except HmipConnectionError:
@ -267,6 +270,18 @@ class HomematicipHAP:
"Reset connection to access point id %s", self.config_entry.unique_id
)
async def ws_connected_handler(self) -> None:
"""Handle websocket connected."""
_LOGGER.debug("WebSocket connection to HomematicIP established")
if self._ws_connection_closed.is_set():
await self.get_state()
self._ws_connection_closed.clear()
async def ws_disconnected_handler(self) -> None:
"""Handle websocket disconnection."""
_LOGGER.warning("WebSocket connection to HomematicIP closed")
self._ws_connection_closed.set()
async def get_hap(
self,
hass: HomeAssistant,
@ -290,6 +305,7 @@ class HomematicipHAP:
raise HmipcConnectionError from err
home.on_update(self.async_update)
home.on_create(self.async_create_entity)
hass.loop.create_task(self.async_connect())
return home

View File

@ -4,16 +4,16 @@ from __future__ import annotations
from typing import Any
from homematicip.base.enums import OpticalSignalBehaviour, RGBColorState
from homematicip.base.enums import DeviceType, OpticalSignalBehaviour, RGBColorState
from homematicip.base.functionalChannels import NotificationLightChannel
from homematicip.device import (
BrandDimmer,
BrandSwitchMeasuring,
BrandSwitchNotificationLight,
Dimmer,
DinRailDimmer3,
FullFlushDimmer,
PluggableDimmer,
SwitchMeasuring,
WiredDimmer3,
)
from packaging.version import Version
@ -44,9 +44,12 @@ async def async_setup_entry(
hap = config_entry.runtime_data
entities: list[HomematicipGenericEntity] = []
for device in hap.home.devices:
if isinstance(device, BrandSwitchMeasuring):
if (
isinstance(device, SwitchMeasuring)
and getattr(device, "deviceType", None) == DeviceType.BRAND_SWITCH_MEASURING
):
entities.append(HomematicipLightMeasuring(hap, device))
elif isinstance(device, BrandSwitchNotificationLight):
if isinstance(device, BrandSwitchNotificationLight):
device_version = Version(device.firmwareVersion)
entities.append(HomematicipLight(hap, device))

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/homematicip_cloud",
"iot_class": "cloud_push",
"loggers": ["homematicip"],
"requirements": ["homematicip==2.0.1.1"]
"requirements": ["homematicip==2.0.4"]
}

View File

@ -11,12 +11,10 @@ from homematicip.base.functionalChannels import (
FunctionalChannel,
)
from homematicip.device import (
BrandSwitchMeasuring,
EnergySensorsInterface,
FloorTerminalBlock6,
FloorTerminalBlock10,
FloorTerminalBlock12,
FullFlushSwitchMeasuring,
HeatingThermostat,
HeatingThermostatCompact,
HeatingThermostatEvo,
@ -26,9 +24,9 @@ from homematicip.device import (
MotionDetectorOutdoor,
MotionDetectorPushButton,
PassageDetector,
PlugableSwitchMeasuring,
PresenceDetectorIndoor,
RoomControlDeviceAnalog,
SwitchMeasuring,
TemperatureDifferenceSensor2,
TemperatureHumiditySensorDisplay,
TemperatureHumiditySensorOutdoor,
@ -143,14 +141,7 @@ async def async_setup_entry(
),
):
entities.append(HomematicipIlluminanceSensor(hap, device))
if isinstance(
device,
(
PlugableSwitchMeasuring,
BrandSwitchMeasuring,
FullFlushSwitchMeasuring,
),
):
if isinstance(device, SwitchMeasuring):
entities.append(HomematicipPowerSensor(hap, device))
entities.append(HomematicipEnergySensor(hap, device))
if isinstance(device, (WeatherSensor, WeatherSensorPlus, WeatherSensorPro)):

View File

@ -4,20 +4,19 @@ from __future__ import annotations
from typing import Any
from homematicip.base.enums import DeviceType
from homematicip.device import (
BrandSwitch2,
BrandSwitchMeasuring,
DinRailSwitch,
DinRailSwitch4,
FullFlushInputSwitch,
FullFlushSwitchMeasuring,
HeatingSwitch2,
MultiIOBox,
OpenCollector8Module,
PlugableSwitch,
PlugableSwitchMeasuring,
PrintedCircuitBoardSwitch2,
PrintedCircuitBoardSwitchBattery,
SwitchMeasuring,
WiredSwitch8,
)
from homematicip.group import ExtendedLinkedSwitchingGroup, SwitchingGroup
@ -43,12 +42,10 @@ async def async_setup_entry(
if isinstance(group, (ExtendedLinkedSwitchingGroup, SwitchingGroup))
]
for device in hap.home.devices:
if isinstance(device, BrandSwitchMeasuring):
# BrandSwitchMeasuring inherits PlugableSwitchMeasuring
# This entity is implemented in the light platform and will
# not be added in the switch platform
pass
elif isinstance(device, (PlugableSwitchMeasuring, FullFlushSwitchMeasuring)):
if (
isinstance(device, SwitchMeasuring)
and getattr(device, "deviceType", None) != DeviceType.BRAND_SWITCH_MEASURING
):
entities.append(HomematicipSwitchMeasuring(hap, device))
elif isinstance(device, WiredSwitch8):
entities.extend(

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/hydrawise",
"iot_class": "cloud_polling",
"loggers": ["pydrawise"],
"requirements": ["pydrawise==2025.3.0"]
"requirements": ["pydrawise==2025.6.0"]
}

View File

@ -8,5 +8,5 @@
"iot_class": "local_polling",
"loggers": ["aioimmich"],
"quality_scale": "silver",
"requirements": ["aioimmich==0.6.0"]
"requirements": ["aioimmich==0.9.1"]
}

View File

@ -3,7 +3,6 @@
from __future__ import annotations
from logging import getLogger
import mimetypes
from aiohttp.web import HTTPNotFound, Request, Response, StreamResponse
from aioimmich.exceptions import ImmichError
@ -30,11 +29,8 @@ LOGGER = getLogger(__name__)
async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
"""Set up Immich media source."""
entries = hass.config_entries.async_entries(
DOMAIN, include_disabled=False, include_ignore=False
)
hass.http.register_view(ImmichMediaView(hass))
return ImmichMediaSource(hass, entries)
return ImmichMediaSource(hass)
class ImmichMediaSourceIdentifier:
@ -42,12 +38,14 @@ class ImmichMediaSourceIdentifier:
def __init__(self, identifier: str) -> None:
"""Split identifier into parts."""
parts = identifier.split("/")
# coonfig_entry.unique_id/album_id/asset_it/filename
parts = identifier.split("|")
# config_entry.unique_id|collection|collection_id|asset_id|file_name|mime_type
self.unique_id = parts[0]
self.album_id = parts[1] if len(parts) > 1 else None
self.asset_id = parts[2] if len(parts) > 2 else None
self.file_name = parts[3] if len(parts) > 2 else None
self.collection = parts[1] if len(parts) > 1 else None
self.collection_id = parts[2] if len(parts) > 2 else None
self.asset_id = parts[3] if len(parts) > 3 else None
self.file_name = parts[4] if len(parts) > 3 else None
self.mime_type = parts[5] if len(parts) > 3 else None
class ImmichMediaSource(MediaSource):
@ -55,18 +53,17 @@ class ImmichMediaSource(MediaSource):
name = "Immich"
def __init__(self, hass: HomeAssistant, entries: list[ConfigEntry]) -> None:
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize Immich media source."""
super().__init__(DOMAIN)
self.hass = hass
self.entries = entries
async def async_browse_media(
self,
item: MediaSourceItem,
) -> BrowseMediaSource:
"""Return media."""
if not self.hass.config_entries.async_loaded_entries(DOMAIN):
if not (entries := self.hass.config_entries.async_loaded_entries(DOMAIN)):
raise BrowseError("Immich is not configured")
return BrowseMediaSource(
domain=DOMAIN,
@ -78,15 +75,16 @@ class ImmichMediaSource(MediaSource):
can_expand=True,
children_media_class=MediaClass.DIRECTORY,
children=[
*await self._async_build_immich(item),
*await self._async_build_immich(item, entries),
],
)
async def _async_build_immich(
self, item: MediaSourceItem
self, item: MediaSourceItem, entries: list[ConfigEntry]
) -> list[BrowseMediaSource]:
"""Handle browsing different immich instances."""
if not item.identifier:
LOGGER.debug("Render all Immich instances")
return [
BrowseMediaSource(
domain=DOMAIN,
@ -97,7 +95,7 @@ class ImmichMediaSource(MediaSource):
can_play=False,
can_expand=True,
)
for entry in self.entries
for entry in entries
]
identifier = ImmichMediaSourceIdentifier(item.identifier)
entry: ImmichConfigEntry | None = (
@ -108,8 +106,22 @@ class ImmichMediaSource(MediaSource):
assert entry
immich_api = entry.runtime_data.api
if identifier.album_id is None:
# Get Albums
if identifier.collection is None:
LOGGER.debug("Render all collections for %s", entry.title)
return [
BrowseMediaSource(
domain=DOMAIN,
identifier=f"{identifier.unique_id}|albums",
media_class=MediaClass.DIRECTORY,
media_content_type=MediaClass.IMAGE,
title="albums",
can_play=False,
can_expand=True,
)
]
if identifier.collection_id is None:
LOGGER.debug("Render all albums for %s", entry.title)
try:
albums = await immich_api.albums.async_get_all_albums()
except ImmichError:
@ -118,80 +130,85 @@ class ImmichMediaSource(MediaSource):
return [
BrowseMediaSource(
domain=DOMAIN,
identifier=f"{item.identifier}/{album.album_id}",
identifier=f"{identifier.unique_id}|albums|{album.album_id}",
media_class=MediaClass.DIRECTORY,
media_content_type=MediaClass.IMAGE,
title=album.name,
title=album.album_name,
can_play=False,
can_expand=True,
thumbnail=f"/immich/{identifier.unique_id}/{album.thumbnail_asset_id}/thumb.jpg/thumbnail",
thumbnail=f"/immich/{identifier.unique_id}/{album.album_thumbnail_asset_id}/thumbnail/image/jpg",
)
for album in albums
]
# Request items of album
LOGGER.debug(
"Render all assets of album %s for %s",
identifier.collection_id,
entry.title,
)
try:
album_info = await immich_api.albums.async_get_album_info(
identifier.album_id
identifier.collection_id
)
except ImmichError:
return []
ret = [
BrowseMediaSource(
domain=DOMAIN,
identifier=(
f"{identifier.unique_id}/"
f"{identifier.album_id}/"
f"{asset.asset_id}/"
f"{asset.file_name}"
),
media_class=MediaClass.IMAGE,
media_content_type=asset.mime_type,
title=asset.file_name,
can_play=False,
can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/{asset.file_name}/thumbnail",
)
for asset in album_info.assets
if asset.mime_type.startswith("image/")
]
ret: list[BrowseMediaSource] = []
for asset in album_info.assets:
if not (mime_type := asset.original_mime_type) or not mime_type.startswith(
("image/", "video/")
):
continue
ret.extend(
BrowseMediaSource(
domain=DOMAIN,
identifier=(
f"{identifier.unique_id}/"
f"{identifier.album_id}/"
f"{asset.asset_id}/"
f"{asset.file_name}"
),
media_class=MediaClass.VIDEO,
media_content_type=asset.mime_type,
title=asset.file_name,
can_play=True,
can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail.jpg/thumbnail",
if mime_type.startswith("image/"):
media_class = MediaClass.IMAGE
can_play = False
thumb_mime_type = mime_type
else:
media_class = MediaClass.VIDEO
can_play = True
thumb_mime_type = "image/jpeg"
ret.append(
BrowseMediaSource(
domain=DOMAIN,
identifier=(
f"{identifier.unique_id}|albums|"
f"{identifier.collection_id}|"
f"{asset.asset_id}|"
f"{asset.original_file_name}|"
f"{mime_type}"
),
media_class=media_class,
media_content_type=mime_type,
title=asset.original_file_name,
can_play=can_play,
can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail/{thumb_mime_type}",
)
)
for asset in album_info.assets
if asset.mime_type.startswith("video/")
)
return ret
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
"""Resolve media to a url."""
identifier = ImmichMediaSourceIdentifier(item.identifier)
if identifier.file_name is None:
raise Unresolvable("No file name")
mime_type, _ = mimetypes.guess_type(identifier.file_name)
if not isinstance(mime_type, str):
raise Unresolvable("No file extension")
try:
identifier = ImmichMediaSourceIdentifier(item.identifier)
except IndexError as err:
raise Unresolvable(
f"Could not parse identifier: {item.identifier}"
) from err
if identifier.mime_type is None:
raise Unresolvable(
f"Could not resolve identifier that has no mime-type: {item.identifier}"
)
return PlayMedia(
(
f"/immich/{identifier.unique_id}/{identifier.asset_id}/{identifier.file_name}/fullsize"
f"/immich/{identifier.unique_id}/{identifier.asset_id}/fullsize/{identifier.mime_type}"
),
mime_type,
identifier.mime_type,
)
@ -212,10 +229,10 @@ class ImmichMediaView(HomeAssistantView):
if not self.hass.config_entries.async_loaded_entries(DOMAIN):
raise HTTPNotFound
asset_id, file_name, size = location.split("/")
mime_type, _ = mimetypes.guess_type(file_name)
if not isinstance(mime_type, str):
raise HTTPNotFound
try:
asset_id, size, mime_type_base, mime_type_format = location.split("/")
except ValueError as err:
raise HTTPNotFound from err
entry: ImmichConfigEntry | None = (
self.hass.config_entries.async_entry_for_domain_unique_id(
@ -226,7 +243,7 @@ class ImmichMediaView(HomeAssistantView):
immich_api = entry.runtime_data.api
# stream response for videos
if mime_type.startswith("video/"):
if mime_type_base == "video":
try:
resp = await immich_api.assets.async_play_video_stream(asset_id)
except ImmichError as exc:
@ -243,4 +260,4 @@ class ImmichMediaView(HomeAssistantView):
image = await immich_api.assets.async_view_asset(asset_id, size)
except ImmichError as exc:
raise HTTPNotFound from exc
return Response(body=image, content_type=mime_type)
return Response(body=image, content_type=f"{mime_type_base}/{mime_type_format}")

View File

@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["pyiskra"],
"requirements": ["pyiskra==0.1.15"]
"requirements": ["pyiskra==0.1.21"]
}

View File

@ -7,7 +7,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input
from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, PLATFORMS
from .const import CONF_CLIENT_DEVICE_ID, DEFAULT_NAME, DOMAIN, PLATFORMS
from .coordinator import JellyfinConfigEntry, JellyfinDataUpdateCoordinator
@ -35,9 +35,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) ->
coordinator = JellyfinDataUpdateCoordinator(
hass, entry, client, server_info, user_id
)
await coordinator.async_config_entry_first_refresh()
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
entry_type=dr.DeviceEntryType.SERVICE,
identifiers={(DOMAIN, coordinator.server_id)},
manufacturer=DEFAULT_NAME,
name=coordinator.server_name,
sw_version=coordinator.server_version,
)
entry.runtime_data = coordinator
entry.async_on_unload(client.stop)

View File

@ -4,10 +4,10 @@ from __future__ import annotations
from typing import Any
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DEFAULT_NAME, DOMAIN
from .const import DOMAIN
from .coordinator import JellyfinDataUpdateCoordinator
@ -24,11 +24,7 @@ class JellyfinServerEntity(JellyfinEntity):
"""Initialize the Jellyfin entity."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, coordinator.server_id)},
manufacturer=DEFAULT_NAME,
name=coordinator.server_name,
sw_version=coordinator.server_version,
)

View File

@ -225,7 +225,7 @@ async def async_setup_entry(
JewishCalendarTimeSensor(config_entry, description)
for description in TIME_SENSORS
)
async_add_entities(sensors)
async_add_entities(sensors, update_before_add=True)
class JewishCalendarBaseSensor(JewishCalendarEntity, SensorEntity):
@ -233,12 +233,7 @@ class JewishCalendarBaseSensor(JewishCalendarEntity, SensorEntity):
_attr_entity_category = EntityCategory.DIAGNOSTIC
async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass."""
await super().async_added_to_hass()
await self.async_update_data()
async def async_update_data(self) -> None:
async def async_update(self) -> None:
"""Update the state of the sensor."""
now = dt_util.now()
_LOGGER.debug("Now: %s Location: %r", now, self.data.location)

View File

@ -11,8 +11,9 @@ from homeassistant.const import (
SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_UP,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType
DOMAIN = "keyboard"
@ -24,6 +25,20 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Listen for keyboard events."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Keyboard",
},
)
keyboard = PyKeyboard()
keyboard.special_key_assignment()

View File

@ -20,8 +20,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN
SCAN_INTERVAL = timedelta(seconds=15)
SETTINGS_UPDATE_INTERVAL = timedelta(hours=1)
SCHEDULE_UPDATE_INTERVAL = timedelta(minutes=5)
SETTINGS_UPDATE_INTERVAL = timedelta(hours=8)
SCHEDULE_UPDATE_INTERVAL = timedelta(minutes=30)
STATISTICS_UPDATE_INTERVAL = timedelta(minutes=15)
_LOGGER = logging.getLogger(__name__)

View File

@ -37,5 +37,5 @@
"iot_class": "cloud_push",
"loggers": ["pylamarzocco"],
"quality_scale": "platinum",
"requirements": ["pylamarzocco==2.0.6"]
"requirements": ["pylamarzocco==2.0.8"]
}

View File

@ -119,7 +119,7 @@ ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = (
key="prebrew_on",
translation_key="prebrew_time_on",
device_class=NumberDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.MINUTES,
native_unit_of_measurement=UnitOfTime.SECONDS,
native_step=PRECISION_TENTHS,
native_min_value=0,
native_max_value=10,
@ -158,7 +158,7 @@ ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = (
key="prebrew_off",
translation_key="prebrew_time_off",
device_class=NumberDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.MINUTES,
native_unit_of_measurement=UnitOfTime.SECONDS,
native_step=PRECISION_TENTHS,
native_min_value=0,
native_max_value=10,

View File

@ -31,6 +31,9 @@ class LinkPlayConfigFlow(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult:
"""Handle Zeroconf discovery."""
# Do not probe the device if the host is already configured
self._async_abort_entries_match({CONF_HOST: discovery_info.host})
session: ClientSession = await async_get_client_session(self.hass)
bridge: LinkPlayBridge | None = None

View File

@ -7,6 +7,6 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["linkplay"],
"requirements": ["python-linkplay==0.2.8"],
"requirements": ["python-linkplay==0.2.10"],
"zeroconf": ["_linkplay._tcp.local."]
}

View File

@ -7,8 +7,9 @@ import time
import lirc
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType
_LOGGER = logging.getLogger(__name__)
@ -26,6 +27,20 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the LIRC capability."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "LIRC",
},
)
# blocking=True gives unexpected behavior (multiple responses for 1 press)
# also by not blocking, we allow hass to shut down the thread gracefully
# on exit.

View File

@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/local_calendar",
"iot_class": "local_polling",
"loggers": ["ical"],
"requirements": ["ical==9.2.5"]
"requirements": ["ical==10.0.0"]
}

View File

@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/local_todo",
"iot_class": "local_polling",
"requirements": ["ical==9.2.5"]
"requirements": ["ical==10.0.0"]
}

View File

@ -967,33 +967,12 @@ DISCOVERY_SCHEMAS = [
# don't discover this entry if the supported state list is empty
secondary_value_is_not=[],
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="MinPINCodeLength",
translation_key="min_pin_code_length",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=None,
),
entity_class=MatterSensor,
required_attributes=(clusters.DoorLock.Attributes.MinPINCodeLength,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="MaxPINCodeLength",
translation_key="max_pin_code_length",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=None,
),
entity_class=MatterSensor,
required_attributes=(clusters.DoorLock.Attributes.MaxPINCodeLength,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="TargetPositionLiftPercent100ths",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
translation_key="window_covering_target_position",
measurement_to_ha=lambda x: round((10000 - x) / 100),
native_unit_of_measurement=PERCENTAGE,

View File

@ -390,12 +390,6 @@
"evse_user_max_charge_current": {
"name": "User max charge current"
},
"min_pin_code_length": {
"name": "Min PIN code length"
},
"max_pin_code_length": {
"name": "Max PIN code length"
},
"window_covering_target_position": {
"name": "Target opening position"
}

View File

@ -527,6 +527,7 @@ OVEN_PROGRAM_ID: dict[int, str] = {
116: "custom_program_20",
323: "pyrolytic",
326: "descale",
327: "evaporate_water",
335: "shabbat_program",
336: "yom_tov",
356: "defrost",

View File

@ -542,6 +542,7 @@
"endive_strips": "Endive (strips)",
"espresso": "Espresso",
"espresso_macchiato": "Espresso macchiato",
"evaporate_water": "Evaporate water",
"express": "Express",
"express_20": "Express 20'",
"extra_quiet": "Extra quiet",

View File

@ -39,6 +39,7 @@ from homeassistant.components.light import (
from homeassistant.components.sensor import (
CONF_STATE_CLASS,
DEVICE_CLASS_UNITS,
STATE_CLASS_UNITS,
SensorDeviceClass,
SensorStateClass,
)
@ -640,6 +641,13 @@ def validate_sensor_platform_config(
):
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom"
if (
(state_class := config.get(CONF_STATE_CLASS)) is not None
and state_class in STATE_CLASS_UNITS
and config.get(CONF_UNIT_OF_MEASUREMENT) not in STATE_CLASS_UNITS[state_class]
):
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom_for_state_class"
return errors
@ -676,11 +684,19 @@ class PlatformField:
@callback
def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
"""Return a context based unit of measurement selector."""
if (state_class := user_data.get(CONF_STATE_CLASS)) in STATE_CLASS_UNITS:
return SelectSelector(
SelectSelectorConfig(
options=[str(uom) for uom in STATE_CLASS_UNITS[state_class]],
sort=True,
custom_value=True,
)
)
if (
user_data is None
or (device_class := user_data.get(CONF_DEVICE_CLASS)) is None
or device_class not in DEVICE_CLASS_UNITS
):
device_class := user_data.get(CONF_DEVICE_CLASS)
) is None or device_class not in DEVICE_CLASS_UNITS:
return TEXT_SELECTOR
return SelectSelector(
SelectSelectorConfig(

View File

@ -14,6 +14,7 @@ from homeassistant.components.sensor import (
DEVICE_CLASS_UNITS,
DEVICE_CLASSES_SCHEMA,
ENTITY_ID_FORMAT,
STATE_CLASS_UNITS,
STATE_CLASSES_SCHEMA,
RestoreSensor,
SensorDeviceClass,
@ -117,6 +118,17 @@ def validate_sensor_state_and_device_class_config(config: ConfigType) -> ConfigT
f"got `{CONF_DEVICE_CLASS}` '{device_class}'"
)
if (
(state_class := config.get(CONF_STATE_CLASS)) is not None
and state_class in STATE_CLASS_UNITS
and (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT))
not in STATE_CLASS_UNITS[state_class]
):
raise vol.Invalid(
f"The unit of measurement '{unit_of_measurement}' is not valid "
f"together with state class '{state_class}'"
)
if (device_class := config.get(CONF_DEVICE_CLASS)) is None or (
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
) is None:

View File

@ -644,6 +644,7 @@
"invalid_template": "Invalid template",
"invalid_supported_color_modes": "Invalid supported color modes selection",
"invalid_uom": "The unit of measurement \"{unit_of_measurement}\" is not supported by the selected device class, please either remove the device class, select a device class which supports \"{unit_of_measurement}\", or pick a supported unit of measurement from the list",
"invalid_uom_for_state_class": "The unit of measurement \"{unit_of_measurement}\" is not supported by the selected state class, please either remove the state class, select a state class which supports \"{unit_of_measurement}\", or pick a supported unit of measurement from the list",
"invalid_url": "Invalid URL",
"last_reset_not_with_state_class_total": "The last reset value template option should be used with state class 'Total' only",
"max_below_min_kelvin": "Max Kelvin value should be greater than min Kelvin value",

View File

@ -293,8 +293,8 @@ class MyUplinkDevicePointSensor(MyUplinkEntity, SensorEntity):
@property
def native_value(self) -> StateType:
"""Sensor state value."""
device_point = self.coordinator.data.points[self.device_id][self.point_id]
if device_point.value == MARKER_FOR_UNKNOWN_VALUE:
device_point = self.coordinator.data.points[self.device_id].get(self.point_id)
if device_point is None or device_point.value == MARKER_FOR_UNKNOWN_VALUE:
return None
return device_point.value # type: ignore[no-any-return]

View File

@ -31,7 +31,6 @@ from homeassistant.helpers.selector import (
SelectSelectorConfig,
SelectSelectorMode,
)
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from homeassistant.util import get_random_string
from . import api
@ -441,9 +440,3 @@ class NestFlowHandler(
if self._structure_config_title:
title = self._structure_config_title
return self.async_create_entry(title=title, data=self._data)
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle a flow initialized by discovery."""
return await self.async_step_user()

View File

@ -47,6 +47,9 @@
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "The Nest integration needs to re-authenticate your account"
},
"oauth_discovery": {
"description": "Home Assistant has found a Google Nest device on your network. Be aware that the set up of Google Nest is more complicated than most other integrations and requires setting up a Nest Device Access project which **requires paying Google a US $5 fee**. Press **Submit** to continue setting up Google Nest."
}
},
"error": {

View File

@ -1,8 +1,8 @@
"""NextBus data update coordinator."""
from datetime import timedelta
from datetime import datetime, timedelta
import logging
from typing import Any
from typing import Any, override
from py_nextbus import NextBusClient
from py_nextbus.client import NextBusFormatError, NextBusHTTPError
@ -15,8 +15,14 @@ from .util import RouteStop
_LOGGER = logging.getLogger(__name__)
# At what percentage of the request limit should the coordinator pause making requests
UPDATE_INTERVAL_SECONDS = 30
THROTTLE_PRECENTAGE = 80
class NextBusDataUpdateCoordinator(DataUpdateCoordinator):
class NextBusDataUpdateCoordinator(
DataUpdateCoordinator[dict[RouteStop, dict[str, Any]]]
):
"""Class to manage fetching NextBus data."""
def __init__(self, hass: HomeAssistant, agency: str) -> None:
@ -26,7 +32,7 @@ class NextBusDataUpdateCoordinator(DataUpdateCoordinator):
_LOGGER,
config_entry=None, # It is shared between multiple entries
name=DOMAIN,
update_interval=timedelta(seconds=30),
update_interval=timedelta(seconds=UPDATE_INTERVAL_SECONDS),
)
self.client = NextBusClient(agency_id=agency)
self._agency = agency
@ -49,9 +55,26 @@ class NextBusDataUpdateCoordinator(DataUpdateCoordinator):
"""Check if this coordinator is tracking any routes."""
return len(self._route_stops) > 0
async def _async_update_data(self) -> dict[str, Any]:
@override
async def _async_update_data(self) -> dict[RouteStop, dict[str, Any]]:
"""Fetch data from NextBus."""
if (
# If we have predictions, check the rate limit
self._predictions
# If are over our rate limit percentage, we should throttle
and self.client.rate_limit_percent >= THROTTLE_PRECENTAGE
# But only if we have a reset time to unthrottle
and self.client.rate_limit_reset is not None
# Unless we are after the reset time
and datetime.now() < self.client.rate_limit_reset
):
self.logger.debug(
"Rate limit threshold reached. Skipping updates for. Routes: %s",
str(self._route_stops),
)
return self._predictions
_stops_to_route_stops: dict[str, set[RouteStop]] = {}
for route_stop in self._route_stops:
_stops_to_route_stops.setdefault(route_stop.stop_id, set()).add(route_stop)
@ -60,7 +83,7 @@ class NextBusDataUpdateCoordinator(DataUpdateCoordinator):
"Updating data from API. Routes: %s", str(_stops_to_route_stops)
)
def _update_data() -> dict:
def _update_data() -> dict[RouteStop, dict[str, Any]]:
"""Fetch data from NextBus."""
self.logger.debug("Updating data from API (executor)")
predictions: dict[RouteStop, dict[str, Any]] = {}

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/nextbus",
"iot_class": "cloud_polling",
"loggers": ["py_nextbus"],
"requirements": ["py-nextbusnext==2.1.2"]
"requirements": ["py-nextbusnext==2.2.0"]
}

View File

@ -40,7 +40,7 @@ SUPPORT_FLAGS = (
PRESET_MODES = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_AWAY]
MIN_TEMPERATURE = 7
MAX_TEMPERATURE = 40
MAX_TEMPERATURE = 30
async def async_setup_entry(

View File

@ -8,6 +8,6 @@
"iot_class": "cloud_polling",
"loggers": ["pynordpool"],
"quality_scale": "platinum",
"requirements": ["pynordpool==0.2.4"],
"requirements": ["pynordpool==0.3.0"],
"single_config_entry": true
}

View File

@ -49,6 +49,10 @@ class OpenUvBinarySensor(OpenUvEntity, BinarySensorEntity):
@callback
def _handle_coordinator_update(self) -> None:
"""Update the entity from the latest data."""
self._update_attrs()
super()._handle_coordinator_update()
def _update_attrs(self) -> None:
data = self.coordinator.data
for key in ("from_time", "to_time", "from_uv", "to_uv"):
@ -78,5 +82,3 @@ class OpenUvBinarySensor(OpenUvEntity, BinarySensorEntity):
ATTR_PROTECTION_WINDOW_STARTING_TIME: as_local(from_dt),
}
)
super()._handle_coordinator_update()

View File

@ -31,3 +31,8 @@ class OpenUvEntity(CoordinatorEntity):
name="OpenUV",
entry_type=DeviceEntryType.SERVICE,
)
self._update_attrs()
def _update_attrs(self) -> None:
"""Override point for updating attributes during init."""

View File

@ -10,7 +10,6 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
PERCENTAGE,
UV_INDEX,
@ -170,7 +169,7 @@ AIRPOLLUTION_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
),
SensorEntityDescription(
key=ATTR_API_AIRPOLLUTION_CO,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.CO,
state_class=SensorStateClass.MEASUREMENT,
),

View File

@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/opower",
"iot_class": "cloud_polling",
"loggers": ["opower"],
"requirements": ["opower==0.12.2"]
"requirements": ["opower==0.12.3"]
}

View File

@ -1 +1,3 @@
"""The pandora component."""
DOMAIN = "pandora"

View File

@ -27,10 +27,13 @@ from homeassistant.const import (
SERVICE_VOLUME_DOWN,
SERVICE_VOLUME_UP,
)
from homeassistant.core import Event, HomeAssistant
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, Event, HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
@ -53,6 +56,21 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Pandora media player platform."""
create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
breaks_in_ha_version="2025.12.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=IssueSeverity.WARNING,
translation_key="deprecated_system_packages_yaml_integration",
translation_placeholders={
"domain": DOMAIN,
"integration_title": "Pandora",
},
)
if not _pianobar_exists():
return
pandora = PandoraMediaPlayer("Pandora")

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/picnic",
"iot_class": "cloud_polling",
"loggers": ["python_picnic_api2"],
"requirements": ["python-picnic-api2==1.2.4"]
"requirements": ["python-picnic-api2==1.3.1"]
}

View File

@ -8,4 +8,4 @@ from typing import Final
DOMAIN: Final = "powerfox"
LOGGER = logging.getLogger(__package__)
SCAN_INTERVAL = timedelta(minutes=1)
SCAN_INTERVAL = timedelta(seconds=10)

View File

@ -15,5 +15,5 @@
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "bronze",
"requirements": ["pyprobeplus==1.0.0"]
"requirements": ["pyprobeplus==1.0.1"]
}

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