Compare commits

..

167 Commits

Author SHA1 Message Date
farmio
7aea90c301 Move KNXModule class to separate module 2025-06-03 09:38:52 +02:00
epenet
5df05fb6dd Move async_register_services to async_setup (#146092) 2025-06-03 08:38:02 +02:00
Simone Chemelli
f295ca27af Bump aioamazondevices to 3.0.5 (#146073) 2025-06-03 01:18:49 +03:00
Marc Mueller
8f75cc6a33 Update pyatmo to 9.2.1 (#146077) 2025-06-02 23:47:50 +02:00
Marc Mueller
19c71f0f49 Update python-homewizard-energy to 8.3.3 (#146076) 2025-06-02 23:34:50 +02:00
Marc Mueller
22c2028c00 Update typing-extensions to 4.14.0 (#146054) 2025-06-02 23:15:53 +02:00
Ian
39f687e3a3 Bump ollama to 0.5.1 (#146063)
* Bump ollama to 0.5.1
* Add ollama to license exceptions
2025-06-02 22:43:00 +02:00
Shay Levy
6692b9b71f Fix Shelly BLU TRV calibrate button (#146066) 2025-06-02 22:38:17 +03:00
J. Nick Koston
2f5787e7be Bump aiohttp to 3.12.7 (#146028) 2025-06-02 21:27:08 +02:00
Simone Chemelli
bbda1761bf Avoid services unload for Isy994 (#146069)
* Avoid services unload for Isy994

* cleanup
2025-06-02 21:19:10 +02:00
Robert Resch
ecc10e9793 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-02 20:48:40 +02:00
Simone Chemelli
9e1e889fd7 Rename mispelled services python files (#146049) 2025-06-02 20:41:31 +02:00
Michael
eefe1e6f0f Don't use multi-line conditionals in immich (#146062) 2025-06-02 19:58:54 +02:00
Marc Mueller
397ed87f2d Update aiohomekit to 3.2.15 (#146059)
* Update aiohomekit to 3.2.15

* Remove Python version exception for homekit_controller
2025-06-02 18:23:04 +01:00
Marc Mueller
15830f383e Update pyoverkiz to 1.17.2 (#146056) 2025-06-02 18:21:26 +01:00
epenet
87395efc6e Add awesomeversion to dependency version checks (#146047) 2025-06-02 17:28:13 +02:00
Marc Mueller
27d79bb10a Update yamllint to 1.37.1 (#146038) 2025-06-02 16:35:31 +02:00
Simone Chemelli
7427db70aa Move async_setup_services to async_setup (#146048)
* Moved async_setup_services to async_setup

* fix schema missing
2025-06-02 16:23:20 +02:00
Marc Mueller
77d5bffa85 Update pytest warnings filter (#146024) 2025-06-02 16:01:23 +02:00
Marc Mueller
ab7c7b8d89 Update ruff to 0.11.12 (#146037)
* Update ruff to 0.11.12
* Replace ruff legacy alias with ruff-check
2025-06-02 16:01:10 +02:00
Simon Lamon
93b8cc38d8 Small nmbs sensor attributes refactoring (#145956)
Attributes refactoring
2025-06-02 15:13:23 +02:00
Pete Sage
e5f95b3aff Add diagnostics tests for Sonos (#146040)
* fix: add tests for diagnostics

* fix: add new files

* fix: add new files
2025-06-02 15:12:34 +02:00
starkillerOG
613728ad3b Improve debug logging Reolink (#146033)
Add debug logging
2025-06-02 15:12:13 +02:00
starkillerOG
cb1bfe6ebe 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 f96030a6c8dccca7888b6d1274d5ed3a251ac03c.
2025-06-02 15:11:56 +02:00
Joost Lekkerkerker
434179ab3f Remove NMBS YAML import (#145733)
* Remove NMBS YAML import

* Remove NMBS YAML import
2025-06-02 15:10:46 +02:00
TimL
eb53277fcc Bump pysmlight to 0.2.6 (#146039)
Co-authored-by: Tim Lunn <tim@feathertop.org>
2025-06-02 15:04:34 +02:00
J. Nick Koston
850ddb3667 Bump grpcio to 1.72.1 (#146029) 2025-06-02 15:04:02 +02:00
epenet
5a727a4fa3 Avoid constant alias for integration DOMAIN (#145788)
* Avoid constant alias for integration DOMAIN

* Tweak

* Improve

* Three more

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-06-02 10:37:29 +02:00
karwosts
33fc700952 Make sun solar_rising a binary_sensor (#140956)
* Make sun solar_rising a binary_sensor.

* Add a state translation

* code review

* fix test

* move PLATFORMS

* Update strings.json
2025-06-02 10:32:48 +02:00
Joakim Sørensen
ad493e077e Submit legacy integrations for analytics (#145787)
* Submit legacy integrations for analytics

* adjustments
2025-06-02 10:29:17 +02:00
Marc Mueller
a2b2f6f20a Update pre-commit to 4.2.0 (#145986) 2025-06-02 09:56:20 +02:00
Marc Mueller
ee57fd413a Update freezegun to 1.5.2 (#145982) 2025-06-02 09:53:12 +02:00
Martin Hjelmare
f5d585e0f0 Fix removal of devices during Z-Wave migration (#145867) 2025-06-02 09:52:02 +02:00
Simone Chemelli
1899388f35 Add diagnostics to Amazon devices (#145964) 2025-06-02 09:48:42 +02:00
Allen Porter
4d833e9b1c Bump ical to 10.0.0 (#145954) 2025-06-02 09:47:05 +02:00
Robert Resch
6d827cd412 Deprecate hddtemp (#145850) 2025-06-02 09:45:14 +02:00
epenet
ebfbea39ff Use async_load_fixture in twitch tests (#146016) 2025-06-02 09:27:53 +02:00
dependabot[bot]
89a40f1c48 Bump dawidd6/action-download-artifact from 9 to 10 (#146015) 2025-06-02 09:21:26 +02:00
epenet
664eb7af10 Use async_load_fixture in moehlenhoff_alpha2 tests (#146012) 2025-06-02 08:59:19 +02:00
epenet
33b99b6627 Use async_load_fixture in netatmo tests (#146013) 2025-06-02 08:59:11 +02:00
epenet
0cf2ee0bcb Remove unnecessary DOMAIN alias in tests (l-r) (#146009)
* Remove unnecessary DOMAIN alias in tests (l-r)

* Keep late import in lirc
2025-06-02 08:54:55 +02:00
hanwg
85a86c3f11 Add config flow for telegram bot integration (#144617)
* added config flow for telegram integration

* added chat id in config entry title and added config flow tests

* fix import issue when there are no notifiers in configuration.yaml

* Revert "fix import issue when there are no notifiers in configuration.yaml"

This reverts commit b5b83e2a9a5d8cd1572f3e8c36e360b0de80b58b.

* Revert "added chat id in config entry title and added config flow tests"

This reverts commit 30c2bb4ae4d850dae931a5f7e1525cf19e3be5d8.

* Revert "added config flow for telegram integration"

This reverts commit 1f44afcd45e3a017b8c5f681dc39a160617018ce.

* added config and subentry flows

* added options flow to configure webhooks

* refactor module setup so it only load once

* moved service registration from async_setup_entry to async_setup

* Apply suggestions from code review

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

* import only last yaml config

* import only last yaml config

* reduced scope of try-block

* create issue when importing from yaml

* Apply suggestions from code review

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

* handle options update by reloading telegram bot

* handle import errors for create issue

* include bot's platform when creating issues

* handle options reload without needing HA restart

* moved url and trusted_networks inputs from options to new config flow step

* Apply suggestions from code review

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

* minor fixes

* refactor config flow

* moved constants to const.py

* Apply suggestions from code review

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

* Update homeassistant/components/telegram_bot/config_flow.py

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

* Update homeassistant/components/telegram_bot/config_flow.py

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

* Update homeassistant/components/telegram_bot/config_flow.py

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

* added options flow tests

* Update homeassistant/components/telegram_bot/__init__.py

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

* Update homeassistant/components/telegram_bot/__init__.py

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

* Update homeassistant/components/telegram_bot/__init__.py

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

* Update homeassistant/components/telegram_bot/config_flow.py

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

* Update homeassistant/components/telegram_bot/config_flow.py

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

* added reconfigure flow

* added reauth flow

* added tests for reconfigure flow

* added tests for reauth

* added tests for subentry flow

* added tests for user and webhooks flow with error scenarios

* added import flow tests

* handle webhook deregister exception

* added config entry id to all services

* fix leave chat bug

* Update homeassistant/components/telegram_bot/__init__.py

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

* removed leave chat bug fixes

* Update homeassistant/components/telegram_bot/strings.json

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

* handle other error types for import

* reuse translations

* added test for duplicated config entry for user step

* added tests

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-02 08:52:31 +02:00
epenet
de4a5fa30b Remove unnecessary DOMAIN alias in tests (s-z) (#146010) 2025-06-02 08:48:37 +02:00
Marc Mueller
43ac550ca0 Update pydantic to 2.11.5 (#145985)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-02 08:48:22 +02:00
Marc Mueller
c3c4d224b2 Update PyTurboJPEG to 1.8.0 (#145984)
Co-authored-by: Allen Porter <allen.porter@gmail.com>
2025-06-02 08:40:10 +02:00
Marc Mueller
6f865beacd Update attrs to 25.3.0 (#145977) 2025-06-02 07:58:35 +02:00
Marc Mueller
de25195383 Update bcrypt to 4.3.0 (#145978) 2025-06-02 07:56:51 +02:00
Marc Mueller
0139d2cabf Update cryptography to 45.0.3 (#145979) 2025-06-02 07:53:58 +02:00
Marc Mueller
17542614b5 Update aiohttp-cors to 0.8.1 (#145976)
* Update aiohttp-cors to 0.8.1

* Fix mypy
2025-06-02 07:52:23 +02:00
Marc Mueller
885367e690 Update coverage to 7.8.2 (#145983) 2025-06-02 07:47:56 +02:00
Marc Mueller
f8c44aad25 Update pytest-cov to 6.1.1 (#145989) 2025-06-02 07:34:11 +02:00
Marc Mueller
2323cc2869 Update numpy to 2.2.6 (#145981) 2025-06-01 21:23:30 -07:00
Marc Mueller
7f0249bbf7 Update pytest-timeout to 2.4.0 (#145990) 2025-06-02 06:17:39 +02:00
Marc Mueller
7a23b778a4 Update pytest-xdist to 3.7.0 (#145991) 2025-06-02 06:16:17 +02:00
Marc Mueller
d910924032 Update syrupy to 4.9.1 (#145992) 2025-06-02 06:14:52 +02:00
Marc Mueller
0b93a8c2f2 Update types packages (#145993) 2025-06-02 06:13:08 +02:00
Marc Mueller
5e377b89fc Update pytest-asyncio to 1.0.0 (#145988)
* Update pytest-asyncio to 1.0.0

* Remove event_loop fixture uses
2025-06-02 06:12:22 +02:00
Marc Mueller
dd85a1e5f0 Update mypy-dev to 1.17.0a2 (#146002)
* Update mypy-dev to 1.17.0a2

* Fix
2025-06-02 06:06:38 +02:00
Simone Chemelli
b96a7aebcd Bump aioamazondevices to 3.0.4 (#145971) 2025-06-01 21:15:18 +02:00
Michael
3cfcf382da Bump aioimmich to 0.8.0 (#145908) 2025-06-01 21:14:19 +02:00
epenet
ed9fd2c643 Use async_load_fixture in async test functions (b-i) (#145714)
* Use async_load_fixture in async test functions (b-i)

* Adjust
2025-06-01 06:31:37 -07:00
epenet
a007e8dc26 Use async_load_fixture in async test functions (l-z) (#145717)
* Use async_load_fixture in async test functions (l-z)

* Adjust
2025-06-01 06:29:17 -07:00
TimL
b318644998 Bump pysmlight to v0.2.5 (#145949) 2025-06-01 03:14:08 +02:00
Ståle Storø Hauknes
0434eea3ab Add sound pressure to Airthings (#145946)
Add sound pressure
2025-06-01 02:05:19 +02:00
Josef Zweck
c19b984660 Increase update intervals in lamarzocco (#145939) 2025-05-31 20:25:57 +02:00
Josef Zweck
0d6bb8a325 Bump pylamarzocco to 2.0.8 (#145938) 2025-05-31 20:25:47 +02:00
Joost Lekkerkerker
094b969301 Add more Amazon Devices DHCP matches (#145776) 2025-05-31 20:25:24 +02:00
Brett Adams
ddef6fdb98 Add streaming to charge cable connected in Teslemetry (#145880) 2025-05-31 20:01:10 +02:00
Robert Resch
cabf7860b3 Deprecate snips integration (#145784) 2025-05-31 20:00:34 +02:00
Bram Kragten
0c0a2403e5 Update frontend to 20250531.0 (#145933) 2025-05-31 17:54:36 +02:00
tronikos
be6c3d8bbd Bump opower to 0.12.3 (#145918) 2025-05-31 11:22:49 +02:00
Josef Zweck
c01536ee58 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 11:19:32 +02:00
J. Nick Koston
a9f36a50e4 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 11:12:00 +02:00
Samuel Xiao
6d11c0395f Bump switchbot-api to 2.4.0 (#145786)
* update switchbot-api version to 2.4.0

* debug for test code
2025-05-30 20:22:40 +02:00
Brett Adams
66bb638dd0 Bump tesla-fleet-api to 1.1.1. (#145869)
bump
2025-05-30 20:21:51 +02:00
Iskra kranj
0d72bfef70 Bump pyiskra to 0.1.19 (#145889) 2025-05-30 20:21:14 +02:00
markhannon
6e44552d41 Minor cleanup of Zimi Integration (#144293) 2025-05-30 19:53:33 +02:00
Simon Lamon
9ec02633b3 Bump python-linkplay to v0.2.9 (#145892) 2025-05-30 19:35:08 +02:00
Jordan Harvey
5d340332bf Bump pyprobeplus to 1.0.1 (#145897) 2025-05-30 19:33:03 +02:00
J. Diego Rodríguez Royo
1e973c1d74 Bump aiohomeconnect to 0.17.1 (#145873) 2025-05-30 01:40:11 +02:00
starkillerOG
618ada64f8 Ensure Reolink host device is setup first (#145843) 2025-05-29 19:32:21 +02:00
Robert Resch
2d6802e06a Deprecate tensorflow (#145806)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-05-29 14:35:35 +01:00
starkillerOG
9687a34a70 Reolink fallback to download command for playback (#145842) 2025-05-29 15:31:50 +02:00
Michael
5ba0ceb6c2 Bump aioimmich to 0.7.0 (#145845) 2025-05-29 15:30:02 +02:00
G Johansson
d8e3e88c63 Fix language selections in workday (#145813) 2025-05-29 15:28:54 +02:00
Robert Resch
d1d1bca29d Deprecate sms integration (#145847) 2025-05-29 14:12:51 +02:00
Michael
80189495c5 Use mime type provided by Immich (#145830)
use mime type from immich instead of guessing it
2025-05-29 10:28:02 +02:00
Josef Zweck
cad6c72cfa Bump aiotedee to 0.2.23 (#145822)
* Bump aiotedee to 0.2.23

* update snapshot
2025-05-29 10:35:05 +03:00
J. Nick Koston
23ac22e213 Remove default args to ESPHome test fixture calls (#145840) 2025-05-29 01:45:37 -05:00
J. Nick Koston
55e664fc0d Bump aiohttp to 3.12.4 (#145838) 2025-05-28 21:08:01 -05:00
Brett Adams
881ce45afa Fix Tessie volume max and step (#145835)
* Use fixed volume max and step

* Update snapshot
2025-05-29 03:58:29 +02:00
André Lersveen
b80195df81 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-29 03:52:05 +02:00
Matthew FitzGerald-Chamberlain
e57ce0a9df Bump pyaprilaire to 0.9.1 (#145836) 2025-05-29 03:43:28 +02:00
J. Nick Koston
ff66ad7705 Bump aiohttp to 3.12.3 (#145837) 2025-05-28 19:38:06 -05:00
Robert Resch
33e98ebffa Remove decora-wifi from excluded requirements (#145832) 2025-05-29 00:14:38 +02:00
Robert Resch
8fd9e2046e Deprecate decora integration (#145807) 2025-05-28 23:54:48 +02:00
Bram Kragten
32c2f47ab5 Update frontend to 20250528.0 (#145828)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-05-28 23:17:14 +02:00
Ståle Storø Hauknes
e2fc2dce84 Move Airthings coordinator to separate module (#145827)
* Create coordinator

* Fix sensor.py
2025-05-28 22:38:33 +02:00
Michael
afa97f8ec1 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 20:51:27 +02:00
Michael
2708c1c94c Fix Immich media source browsing with multiple config entries (#145823)
fix media source browsing with multiple config entries
2025-05-28 20:49:20 +02:00
Michael Hansen
d76ed6a3c2 Bump intents to 2025.5.28 (#145816) 2025-05-28 21:14:13 +03:00
epenet
695f69bd90 Remove unnecessary DOMAIN alias in tests (e-k) (#145818) 2025-05-28 21:06:25 +03:00
epenet
7da8e24e21 Remove unnecessary DOMAIN alias in tests (a-d) (#145817) 2025-05-28 21:00:38 +03:00
David Bonnes
9d0fc0d513 Fix HOMEASSISTANT_STOP unsubscribe in data update coordinator (#145809)
* initial commit

* a better approach

* Add comment
2025-05-28 17:52:51 +01:00
Robert Resch
ca567aa7fc Deprecate lirc integration (#145797) 2025-05-28 17:28:37 +01:00
Robert Resch
27af2d8ec6 Deprecate keyboard integration (#145805) 2025-05-28 17:22:18 +02:00
Lennart Nederstigt
59ea6f375a Add hardwired chime toggle to Reolink Battery Doorbell (#145779)
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
2025-05-28 17:10:38 +02:00
Marc Mueller
6c365c94ed Update sqlalchemy to 2.0.41 (#145790) 2025-05-28 16:39:10 +02:00
Marc Mueller
6693fc764f Update httpcore to 1.0.9 and h11 to 0.16.0 (#145789) 2025-05-28 16:35:11 +02:00
starkillerOG
e855b6c2bc Bump reolink-aio to 0.13.4 (#145799) 2025-05-28 16:33:20 +02:00
Abílio Costa
23a1dddc23 Add Shelly zwave virtual integration (#145749) 2025-05-28 14:56:47 +01:00
epenet
bd5fef1ddb Use async_load_fixture in async test functions (a) (#145718) 2025-05-28 15:51:49 +02:00
epenet
c3ade400fb Use Platform constant in tests (#145801)
* Use Platform constant in tests

* spelling

* Fix platform
2025-05-28 15:51:37 +02:00
epenet
1889f0ef66 Use Platform constant in hue tests (#145798) 2025-05-28 14:43:48 +02:00
epenet
6b28af8282 Remove unnecessary DOMAIN alias in components (#145791) 2025-05-28 14:04:35 +02:00
Robert Resch
f59001d45f Deprecate pandora integration (#145785) 2025-05-28 13:12:55 +02:00
Erik Montnemery
a857461059 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 12:26:28 +02:00
epenet
e4cc842584 Use async_load_json_(array/object)_fixture in async test functions (#145773) 2025-05-28 12:09:05 +02:00
Robert Resch
bb52058920 Deprecate GStreamer integration (#145768) 2025-05-28 11:16:08 +02:00
J. Diego Rodríguez Royo
c1676570da Add more information about possible hostnames at Home Connect (#145770) 2025-05-28 10:57:01 +02:00
G Johansson
4858b2171e Modernize tests for smhi (#139334)
* Modernize tests for smhi

* Fixes

* Mods

* Fix weather

* Coverage 100%

* Fix init test

* Fixes

* Fixes

* Remove waits
2025-05-28 10:56:07 +02:00
Jan Bouwhuis
192aa76cd7 Ensure mqtt sensor unit of measurement validation for state class measurement_angle (#145648) 2025-05-28 10:16:40 +02:00
Josef Zweck
ddf611bfdf Fix uom for prebrew numbers in lamarzocco (#145772) 2025-05-28 10:15:24 +02:00
Robert Resch
3164394982 Deprecate dlib image processing integrations (#145767) 2025-05-28 09:58:44 +02:00
Josef Zweck
b250a03ff5 Bump pylamarzocco to 2.0.7 (#145763) 2025-05-28 09:39:33 +02:00
dependabot[bot]
2dd7f035f6 Bump docker/build-push-action from 6.17.0 to 6.18.0 (#145764)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-28 09:10:37 +02:00
Joost Lekkerkerker
2c08b3f30c Add more Amazon Devices DHCP matches (#145754) 2025-05-28 08:43:59 +02:00
Josef Zweck
c3ec30ce3b Update otp description for amazon_devices (#145701)
* Update otp description from amazon_devices

* separate

* Update strings.json
2025-05-28 08:13:28 +02:00
Erik Montnemery
9d4375ca76 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-27 23:00:52 +02:00
Raphael Hehl
3870b87db9 Bump uiprotect to version 7.10.1 (#145737)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-05-27 22:58:46 +02:00
Joost Lekkerkerker
ff2fd7e9ef Add DHCP discovery to LG ThinQ (#145746) 2025-05-27 16:45:30 -04:00
G Johansson
719dd09eb3 Fix dns resolver error in dnsip config flow validation (#145735)
Fix dns resolver error in dnsip
2025-05-27 22:17:34 +02:00
Bram Kragten
2cf2613dbd Update frontend to 20250527.0 (#145741) 2025-05-27 22:12:07 +02:00
Jan Bouwhuis
181a3d142e Revert "squeezebox Better result for testing (#144622)" (#145739)
This reverts commit 987af8f7df.
2025-05-27 21:36:51 +02:00
Elias Wernicke
c20ad5fde1 Add complete intent function for shopping list component (#128565)
* add intent

* add tests

* raise IntentHandleError

* add check for non completed

* Prefer completing non complete items

* cleanup

* cleanup tests

* rename test

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>

* remove duplicated test

* update test

* complete all items

* fix event

* remove type def

* return speech slots

---------

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-05-27 14:35:14 -05:00
Erwin Douna
4fcebf18dc Tado update mobile devices interval (#145738)
Update the mobile devices interval to five minutes
2025-05-27 21:27:52 +02:00
Joost Lekkerkerker
a6e04be076 Remove niko_home_control YAML import (#145732) 2025-05-27 19:58:05 +02:00
Erwin Douna
330a8e197d MELCloud remove deprecated YAML import strings (#145731)
Remove deprecated YAML import strings
2025-05-27 19:50:31 +02:00
Joost Lekkerkerker
4300e846e6 Fix unbound local variable in Acmeda config flow (#145729) 2025-05-27 19:29:04 +02:00
Kevin Stillhammer
07fd1f99df Support addresses with comma in google_travel_time (#145663)
Support addresses with comma
2025-05-27 18:53:45 +02:00
Kevin Stillhammer
481639bcf9 Catch PermissionDenied(Route API disabled) in google_travel_time (#145722)
Catch PermissionDenied(Route API disabled)
2025-05-27 18:45:49 +02:00
Martin Hjelmare
376008940b 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 17:46:21 +02:00
epenet
b2c2db3394 Add check for transient packages restricting Python version (#145695) 2025-05-27 17:45:51 +02:00
Kevin Stillhammer
a636e38d24 Debug log the update response in google_travel_time (#145725)
Debug log the update response
2025-05-27 17:44:48 +02:00
Martin Hjelmare
ae1294830c Remove static pin code length Matter sensors (#145711)
* Remove static Matter sensors

* Clean up translation strings
2025-05-27 17:35:11 +02:00
Robin Lintermann
d87fdf028b Improve smarla base entity (#145710) 2025-05-27 15:58:19 +02:00
Petar Petrov
6f5d5d4cdb Change text of installing and starting Z-WaveJs add-on steps (#145702) 2025-05-27 14:51:22 +02:00
epenet
12fdd7034a Simplify boolean check in onewire (#145700) 2025-05-27 13:30:44 +02:00
Martin Hjelmare
f295d72cd9 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 12:54:57 +02:00
Petar Petrov
2605fda185 Remove confirm screen after Z-Wave usb discovery (#145682)
* Remove confirm screen after Z-Wave usb discovery

* Simplify async_step_usb
2025-05-27 12:53:30 +02:00
Joost Lekkerkerker
2189dc3e2a Use string type for amazon devices OTP code (#145698) 2025-05-27 12:33:02 +02:00
Franck Nijhof
8364d8a2e3 Bump version to 2025.7.0dev0 (#145647)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-05-27 10:59:34 +02:00
epenet
96c9636086 Add check for packages restricting Python version (#145690)
* Add check for packages restricting Python version

* Apply suggestions from code review

* until

* until
2025-05-27 10:44:00 +02:00
Petar Petrov
7b1dfc35d1 Change description on recommended/custom Z-Wave install step (#145688)
Change description on recommended/custom Z-WaveJS step
2025-05-27 10:04:29 +02:00
Norbert Rittel
2e94730491 Replace "Invalid API key" with common string in overseerr (#145689)
Replace "Invalid API key" with common string
2025-05-27 09:56:16 +02:00
Markus Adrario
11c6998bf2 Add homee siren platform (#145675)
* port siren.py from custom component

* Add Siren Tests

* last small nits
2025-05-27 09:48:59 +02:00
epenet
055a024d10 Add async-timeout to forbidden packages (#145679) 2025-05-27 08:57:35 +02:00
Joost Lekkerkerker
f73afd71fd Fix Amazon devices offline handling (#145656) 2025-05-27 08:49:25 +02:00
Jan Bouwhuis
ec64194ab9 Fix justnimbus CI test (#145681) 2025-05-27 08:48:06 +02:00
karwosts
d49a613c62 Add read_only entity_id to Trend options flow (#145657) 2025-05-27 08:42:08 +02:00
Artur Pragacz
6fc064fa6a Test that recorder is not promoted to earlier stage in bootstrap (#142695)
Test that recorder is not promoted to earlier stage
2025-05-27 08:23:39 +02:00
Artur Pragacz
b36b591ccf Improve error message for global timeout (#141563)
* Improve error message for global timeout

* Add test

* Message works with zone too
2025-05-27 07:49:18 +02:00
J. Nick Koston
d25ba79427 Bump aiohttp to 3.12.2 (#145671) 2025-05-26 21:58:46 -05:00
Joost Lekkerkerker
df35f30321 Handle Google Nest DHCP flows (#145658)
* Handle Google Nest DHCP flows

* Handle Google Nest DHCP flows
2025-05-26 15:01:35 -07:00
Jan Bouwhuis
1e3d06a993 Fix translation for sensor measurement angle state class (#145649) 2025-05-26 22:47:53 +01:00
Florian von Garrel
2ee6bf7340 Add update platform to paperless integration (#145638)
* Add uüdate platform to paperless integration

* Add tests to paperless

* Add translation

* Fixed update unavailable

* Fetch remote version in update platform

* changed diagnostics

* changed diagnostic data

* Code quality

* revert changes

* code quality
2025-05-26 23:24:53 +02:00
Joost Lekkerkerker
13a8e5e021 Fix Aquacell snapshot (#145651) 2025-05-26 23:08:07 +02:00
708 changed files with 9370 additions and 10710 deletions

View File

@@ -94,7 +94,7 @@ jobs:
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@v9
uses: dawidd6/action-download-artifact@v10
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/frontend
@@ -105,7 +105,7 @@ jobs:
- name: Download nightly wheels of intents
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@v9
uses: dawidd6/action-download-artifact@v10
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/intents-package
@@ -509,7 +509,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -522,7 +522,7 @@ jobs:
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile

View File

@@ -40,7 +40,7 @@ env:
CACHE_VERSION: 2
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2025.6"
HA_SHORT_VERSION: "2025.7"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['3.13']"
# 10.3 is the oldest supported version
@@ -360,7 +360,7 @@ jobs:
- name: Run ruff
run: |
. venv/bin/activate
pre-commit run --hook-stage manual ruff --all-files --show-diff-on-failure
pre-commit run --hook-stage manual ruff-check --all-files --show-diff-on-failure
env:
RUFF_OUTPUT_FORMAT: github

View File

@@ -1,8 +1,8 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.0
rev: v0.11.12
hooks:
- id: ruff
- id: ruff-check
args:
- --fix
- id: ruff-format
@@ -30,7 +30,7 @@ repos:
- --branch=master
- --branch=rc
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.35.1
rev: v1.37.1
hooks:
- id: yamllint
- repo: https://github.com/pre-commit/mirrors-prettier

View File

@@ -65,8 +65,8 @@ homeassistant.components.aladdin_connect.*
homeassistant.components.alarm_control_panel.*
homeassistant.components.alert.*
homeassistant.components.alexa.*
homeassistant.components.alexa_devices.*
homeassistant.components.alpha_vantage.*
homeassistant.components.amazon_devices.*
homeassistant.components.amazon_polly.*
homeassistant.components.amberelectric.*
homeassistant.components.ambient_network.*

2
.vscode/tasks.json vendored
View File

@@ -45,7 +45,7 @@
{
"label": "Ruff",
"type": "shell",
"command": "pre-commit run ruff --all-files",
"command": "pre-commit run ruff-check --all-files",
"group": {
"kind": "test",
"isDefault": true

4
CODEOWNERS generated
View File

@@ -89,8 +89,8 @@ build.json @home-assistant/supervisor
/tests/components/alert/ @home-assistant/core @frenck
/homeassistant/components/alexa/ @home-assistant/cloud @ochlocracy @jbouwh
/tests/components/alexa/ @home-assistant/cloud @ochlocracy @jbouwh
/homeassistant/components/alexa_devices/ @chemelli74
/tests/components/alexa_devices/ @chemelli74
/homeassistant/components/amazon_devices/ @chemelli74
/tests/components/amazon_devices/ @chemelli74
/homeassistant/components/amazon_polly/ @jschlyter
/homeassistant/components/amberelectric/ @madpilot
/tests/components/amberelectric/ @madpilot

View File

@@ -171,8 +171,6 @@ FRONTEND_INTEGRATIONS = {
# Stage 0 is divided into substages. Each substage has a name, a set of integrations and a timeout.
# The substage containing recorder should have no timeout, as it could cancel a database migration.
# Recorder freezes "recorder" timeout during a migration, but it does not freeze other timeouts.
# The substages preceding it should also have no timeout, until we ensure that the recorder
# is not accidentally promoted as a dependency of any of the integrations in them.
# If we add timeouts to the frontend substages, we should make sure they don't apply in recovery mode.
STAGE_0_INTEGRATIONS = (
# Load logging and http deps as soon as possible
@@ -929,7 +927,11 @@ async def _async_set_up_integrations(
await _async_setup_multi_components(hass, stage_all_domains, config)
continue
try:
async with hass.timeout.async_timeout(timeout, cool_down=COOLDOWN_TIME):
async with hass.timeout.async_timeout(
timeout,
cool_down=COOLDOWN_TIME,
cancel_message=f"Bootstrap stage {name} timeout",
):
await _async_setup_multi_components(hass, stage_all_domains, config)
except TimeoutError:
_LOGGER.warning(
@@ -941,7 +943,11 @@ async def _async_set_up_integrations(
# Wrap up startup
_LOGGER.debug("Waiting for startup to wrap up")
try:
async with hass.timeout.async_timeout(WRAP_UP_TIMEOUT, cool_down=COOLDOWN_TIME):
async with hass.timeout.async_timeout(
WRAP_UP_TIMEOUT,
cool_down=COOLDOWN_TIME,
cancel_message="Bootstrap startup wrap up timeout",
):
await hass.async_block_till_done()
except TimeoutError:
_LOGGER.warning(

View File

@@ -3,7 +3,7 @@
"name": "Amazon",
"integrations": [
"alexa",
"alexa_devices",
"amazon_devices",
"amazon_polly",
"aws",
"aws_s3",

View File

@@ -15,7 +15,7 @@ from homeassistant.helpers.entity_platform import (
)
from . import AgentDVRConfigEntry
from .const import ATTRIBUTION, CAMERA_SCAN_INTERVAL_SECS, DOMAIN as AGENT_DOMAIN
from .const import ATTRIBUTION, CAMERA_SCAN_INTERVAL_SECS, DOMAIN
SCAN_INTERVAL = timedelta(seconds=CAMERA_SCAN_INTERVAL_SECS)
@@ -82,7 +82,7 @@ class AgentCamera(MjpegCamera):
still_image_url=f"{device.client._server_url}{device.still_image_url}&size={device.mjpegStreamWidth}x{device.mjpegStreamHeight}", # noqa: SLF001
)
self._attr_device_info = DeviceInfo(
identifiers={(AGENT_DOMAIN, self.unique_id)},
identifiers={(DOMAIN, self.unique_id)},
manufacturer="Agent",
model="Camera",
name=f"{device.client.name} {device.name}",

View File

@@ -51,16 +51,9 @@ class AirGradientCoordinator(DataUpdateCoordinator[AirGradientData]):
async def _async_setup(self) -> None:
"""Set up the coordinator."""
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
self._current_version = (
await self.client.get_current_measures()
).firmware_version
async def _async_update_data(self) -> AirGradientData:
try:

View File

@@ -5,23 +5,22 @@ from __future__ import annotations
from datetime import timedelta
import logging
from airthings import Airthings, AirthingsDevice, AirthingsError
from airthings import Airthings
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_SECRET, DOMAIN
from .const import CONF_SECRET
from .coordinator import AirthingsDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.SENSOR]
SCAN_INTERVAL = timedelta(minutes=6)
type AirthingsDataCoordinatorType = DataUpdateCoordinator[dict[str, AirthingsDevice]]
type AirthingsConfigEntry = ConfigEntry[AirthingsDataCoordinatorType]
type AirthingsConfigEntry = ConfigEntry[AirthingsDataUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) -> bool:
@@ -32,21 +31,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) ->
async_get_clientsession(hass),
)
async def _update_method() -> dict[str, AirthingsDevice]:
"""Get the latest data from Airthings."""
try:
return await airthings.update_devices() # type: ignore[no-any-return]
except AirthingsError as err:
raise UpdateFailed(f"Unable to fetch data: {err}") from err
coordinator = AirthingsDataUpdateCoordinator(hass, airthings)
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=entry,
name=DOMAIN,
update_method=_update_method,
update_interval=SCAN_INTERVAL,
)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator

View File

@@ -0,0 +1,36 @@
"""The Airthings integration."""
from datetime import timedelta
import logging
from airthings import Airthings, AirthingsDevice, AirthingsError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=6)
class AirthingsDataUpdateCoordinator(DataUpdateCoordinator[dict[str, AirthingsDevice]]):
"""Coordinator for Airthings data updates."""
def __init__(self, hass: HomeAssistant, airthings: Airthings) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_method=self._update_method,
update_interval=SCAN_INTERVAL,
)
self.airthings = airthings
async def _update_method(self) -> dict[str, AirthingsDevice]:
"""Get the latest data from Airthings."""
try:
return await self.airthings.update_devices() # type: ignore[no-any-return]
except AirthingsError as err:
raise UpdateFailed(f"Unable to fetch data: {err}") from err

View File

@@ -19,6 +19,7 @@ from homeassistant.const import (
SIGNAL_STRENGTH_DECIBELS,
EntityCategory,
UnitOfPressure,
UnitOfSoundPressure,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -27,8 +28,9 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AirthingsConfigEntry, AirthingsDataCoordinatorType
from . import AirthingsConfigEntry
from .const import DOMAIN
from .coordinator import AirthingsDataUpdateCoordinator
SENSORS: dict[str, SensorEntityDescription] = {
"radonShortTermAvg": SensorEntityDescription(
@@ -54,6 +56,12 @@ SENSORS: dict[str, SensorEntityDescription] = {
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
),
"sla": SensorEntityDescription(
key="sla",
device_class=SensorDeviceClass.SOUND_PRESSURE,
native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
state_class=SensorStateClass.MEASUREMENT,
),
"battery": SensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
@@ -140,7 +148,7 @@ async def async_setup_entry(
class AirthingsHeaterEnergySensor(
CoordinatorEntity[AirthingsDataCoordinatorType], SensorEntity
CoordinatorEntity[AirthingsDataUpdateCoordinator], SensorEntity
):
"""Representation of a Airthings Sensor device."""
@@ -149,7 +157,7 @@ class AirthingsHeaterEnergySensor(
def __init__(
self,
coordinator: AirthingsDataCoordinatorType,
coordinator: AirthingsDataUpdateCoordinator,
airthings_device: AirthingsDevice,
entity_description: SensorEntityDescription,
) -> None:

View File

@@ -1,12 +0,0 @@
{
"domain": "alexa_devices",
"name": "Alexa Devices",
"codeowners": ["@chemelli74"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/alexa_devices",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "bronze",
"requirements": ["aioamazondevices==3.1.2"]
}

View File

@@ -1,4 +1,4 @@
"""Alexa Devices integration."""
"""Amazon Devices integration."""
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
@@ -13,7 +13,7 @@ PLATFORMS = [
async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Set up Alexa Devices platform."""
"""Set up Amazon Devices platform."""
coordinator = AmazonDevicesCoordinator(hass, entry)

View File

@@ -13,7 +13,6 @@ 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
@@ -26,7 +25,7 @@ PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AmazonBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Alexa Devices binary sensor entity description."""
"""Amazon Devices binary sensor entity description."""
is_on_fn: Callable[[AmazonDevice], bool]
@@ -35,12 +34,10 @@ 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,
),
@@ -52,7 +49,7 @@ async def async_setup_entry(
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Alexa Devices binary sensors based on a config entry."""
"""Set up Amazon Devices binary sensors based on a config entry."""
coordinator = entry.runtime_data

View File

@@ -1,4 +1,4 @@
"""Config flow for Alexa Devices integration."""
"""Config flow for Amazon Devices integration."""
from __future__ import annotations
@@ -17,7 +17,7 @@ from .const import CONF_LOGIN_DATA, DOMAIN
class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Alexa Devices."""
"""Handle a config flow for Amazon Devices."""
async def async_step_user(
self, user_input: dict[str, Any] | None = None

View File

@@ -1,8 +1,8 @@
"""Alexa Devices constants."""
"""Amazon Devices constants."""
import logging
_LOGGER = logging.getLogger(__package__)
DOMAIN = "alexa_devices"
DOMAIN = "amazon_devices"
CONF_LOGIN_DATA = "login_data"

View File

@@ -1,4 +1,4 @@
"""Support for Alexa Devices."""
"""Support for Amazon Devices."""
from datetime import timedelta
@@ -23,7 +23,7 @@ type AmazonConfigEntry = ConfigEntry[AmazonDevicesCoordinator]
class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
"""Base coordinator for Alexa Devices."""
"""Base coordinator for Amazon Devices."""
config_entry: AmazonConfigEntry

View File

@@ -1,4 +1,4 @@
"""Diagnostics support for Alexa Devices integration."""
"""Diagnostics support for Amazon Devices integration."""
from __future__ import annotations

View File

@@ -1,4 +1,4 @@
"""Defines a base Alexa Devices entity."""
"""Defines a base Amazon Devices entity."""
from aioamazondevices.api import AmazonDevice
from aioamazondevices.const import SPEAKER_GROUP_MODEL
@@ -12,7 +12,7 @@ from .coordinator import AmazonDevicesCoordinator
class AmazonEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
"""Defines a base Alexa Devices entity."""
"""Defines a base Amazon Devices entity."""
_attr_has_entity_name = True
@@ -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) or {}
model = model_details.get("model")
model_details = coordinator.api.get_model_details(self.device)
model = model_details["model"] if model_details else None
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, serial_num)},
name=self.device.account_name,
model=model,
model_id=self.device.device_type,
manufacturer=model_details.get("manufacturer", "Amazon"),
hw_version=model_details.get("hw_version"),
manufacturer="Amazon",
hw_version=model_details["hw_version"] if model_details else None,
sw_version=(
self.device.software_version if model != SPEAKER_GROUP_MODEL else None
),

View File

@@ -0,0 +1,122 @@
{
"domain": "amazon_devices",
"name": "Amazon Devices",
"codeowners": ["@chemelli74"],
"config_flow": true,
"dhcp": [
{ "macaddress": "007147*" },
{ "macaddress": "00FC8B*" },
{ "macaddress": "0812A5*" },
{ "macaddress": "086AE5*" },
{ "macaddress": "08849D*" },
{ "macaddress": "089115*" },
{ "macaddress": "08A6BC*" },
{ "macaddress": "08C224*" },
{ "macaddress": "0CDC91*" },
{ "macaddress": "0CEE99*" },
{ "macaddress": "1009F9*" },
{ "macaddress": "109693*" },
{ "macaddress": "10BF67*" },
{ "macaddress": "10CE02*" },
{ "macaddress": "140AC5*" },
{ "macaddress": "149138*" },
{ "macaddress": "1848BE*" },
{ "macaddress": "1C12B0*" },
{ "macaddress": "1C4D66*" },
{ "macaddress": "1C93C4*" },
{ "macaddress": "1CFE2B*" },
{ "macaddress": "244CE3*" },
{ "macaddress": "24CE33*" },
{ "macaddress": "2873F6*" },
{ "macaddress": "2C71FF*" },
{ "macaddress": "34AFB3*" },
{ "macaddress": "34D270*" },
{ "macaddress": "38F73D*" },
{ "macaddress": "3C5CC4*" },
{ "macaddress": "3CE441*" },
{ "macaddress": "440049*" },
{ "macaddress": "40A2DB*" },
{ "macaddress": "40A9CF*" },
{ "macaddress": "40B4CD*" },
{ "macaddress": "443D54*" },
{ "macaddress": "44650D*" },
{ "macaddress": "485F2D*" },
{ "macaddress": "48785E*" },
{ "macaddress": "48B423*" },
{ "macaddress": "4C1744*" },
{ "macaddress": "4CEFC0*" },
{ "macaddress": "5007C3*" },
{ "macaddress": "50D45C*" },
{ "macaddress": "50DCE7*" },
{ "macaddress": "50F5DA*" },
{ "macaddress": "5C415A*" },
{ "macaddress": "6837E9*" },
{ "macaddress": "6854FD*" },
{ "macaddress": "689A87*" },
{ "macaddress": "68B691*" },
{ "macaddress": "68DBF5*" },
{ "macaddress": "68F63B*" },
{ "macaddress": "6C0C9A*" },
{ "macaddress": "6C5697*" },
{ "macaddress": "7458F3*" },
{ "macaddress": "74C246*" },
{ "macaddress": "74D637*" },
{ "macaddress": "74E20C*" },
{ "macaddress": "74ECB2*" },
{ "macaddress": "786C84*" },
{ "macaddress": "78A03F*" },
{ "macaddress": "7C6166*" },
{ "macaddress": "7C6305*" },
{ "macaddress": "7CD566*" },
{ "macaddress": "8871E5*" },
{ "macaddress": "901195*" },
{ "macaddress": "90235B*" },
{ "macaddress": "90A822*" },
{ "macaddress": "90F82E*" },
{ "macaddress": "943A91*" },
{ "macaddress": "98226E*" },
{ "macaddress": "98CCF3*" },
{ "macaddress": "9CC8E9*" },
{ "macaddress": "A002DC*" },
{ "macaddress": "A0D2B1*" },
{ "macaddress": "A40801*" },
{ "macaddress": "A8E621*" },
{ "macaddress": "AC416A*" },
{ "macaddress": "AC63BE*" },
{ "macaddress": "ACCCFC*" },
{ "macaddress": "B0739C*" },
{ "macaddress": "B0CFCB*" },
{ "macaddress": "B0F7C4*" },
{ "macaddress": "B85F98*" },
{ "macaddress": "C091B9*" },
{ "macaddress": "C095CF*" },
{ "macaddress": "C49500*" },
{ "macaddress": "C86C3D*" },
{ "macaddress": "CC9EA2*" },
{ "macaddress": "CCF735*" },
{ "macaddress": "DC54D7*" },
{ "macaddress": "D8BE65*" },
{ "macaddress": "D8FBD6*" },
{ "macaddress": "DC91BF*" },
{ "macaddress": "DCA0D0*" },
{ "macaddress": "E0F728*" },
{ "macaddress": "EC2BEB*" },
{ "macaddress": "EC8AC4*" },
{ "macaddress": "ECA138*" },
{ "macaddress": "F02F9E*" },
{ "macaddress": "F0272D*" },
{ "macaddress": "F0F0A4*" },
{ "macaddress": "F4032A*" },
{ "macaddress": "F854B8*" },
{ "macaddress": "FC492D*" },
{ "macaddress": "FC65DE*" },
{ "macaddress": "FCA183*" },
{ "macaddress": "FCE9D8*" }
],
"documentation": "https://www.home-assistant.io/integrations/amazon_devices",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "bronze",
"requirements": ["aioamazondevices==3.0.5"]
}

View File

@@ -7,7 +7,6 @@ from dataclasses import dataclass
from typing import Any, Final
from aioamazondevices.api import AmazonDevice, AmazonEchoApi
from aioamazondevices.const import SPEAKER_GROUP_FAMILY
from homeassistant.components.notify import NotifyEntity, NotifyEntityDescription
from homeassistant.core import HomeAssistant
@@ -21,9 +20,8 @@ PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
class AmazonNotifyEntityDescription(NotifyEntityDescription):
"""Alexa Devices notify entity description."""
"""Amazon Devices notify entity description."""
is_supported: Callable[[AmazonDevice], bool] = lambda _device: True
method: Callable[[AmazonEchoApi, AmazonDevice, str], Awaitable[None]]
subkey: str
@@ -33,7 +31,6 @@ NOTIFY: Final = (
key="speak",
translation_key="speak",
subkey="AUDIO_PLAYER",
is_supported=lambda _device: _device.device_family != SPEAKER_GROUP_FAMILY,
method=lambda api, device, message: api.call_alexa_speak(device, message),
),
AmazonNotifyEntityDescription(
@@ -52,7 +49,7 @@ async def async_setup_entry(
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Alexa Devices notification entity based on a config entry."""
"""Set up Amazon Devices notification entity based on a config entry."""
coordinator = entry.runtime_data
@@ -61,7 +58,6 @@ async def async_setup_entry(
for sensor_desc in NOTIFY
for serial_num in coordinator.data
if sensor_desc.subkey in coordinator.data[serial_num].capabilities
and sensor_desc.is_supported(coordinator.data[serial_num])
)

View File

@@ -45,9 +45,7 @@ rules:
discovery-update-info:
status: exempt
comment: Network information not relevant
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
discovery: done
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo

View File

@@ -12,16 +12,16 @@
"step": {
"user": {
"data": {
"country": "[%key:component::alexa_devices::common::data_country%]",
"country": "[%key:component::amazon_devices::common::data_country%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
"code": "[%key:component::amazon_devices::common::data_description_code%]"
},
"data_description": {
"country": "[%key:component::alexa_devices::common::data_description_country%]",
"username": "[%key:component::alexa_devices::common::data_description_username%]",
"password": "[%key:component::alexa_devices::common::data_description_password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
"country": "[%key:component::amazon_devices::common::data_description_country%]",
"username": "[%key:component::amazon_devices::common::data_description_username%]",
"password": "[%key:component::amazon_devices::common::data_description_password%]",
"code": "[%key:component::amazon_devices::common::data_description_code%]"
}
}
},

View File

@@ -20,7 +20,7 @@ PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
class AmazonSwitchEntityDescription(SwitchEntityDescription):
"""Alexa Devices switch entity description."""
"""Amazon Devices switch entity description."""
is_on_fn: Callable[[AmazonDevice], bool]
subkey: str
@@ -43,7 +43,7 @@ async def async_setup_entry(
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Alexa Devices switches based on a config entry."""
"""Set up Amazon Devices switches based on a config entry."""
coordinator = entry.runtime_data

View File

@@ -24,7 +24,7 @@ from homeassistant.components.recorder import (
get_instance as get_recorder_instance,
)
from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
from homeassistant.const import ATTR_DOMAIN, BASE_PLATFORMS, __version__ as HA_VERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
@@ -225,7 +225,8 @@ class Analytics:
LOGGER.error(err)
return
configuration_set = set(yaml_configuration)
configuration_set = _domains_from_yaml_config(yaml_configuration)
er_platforms = {
entity.platform
for entity in ent_reg.entities.values()
@@ -370,3 +371,13 @@ class Analytics:
for entry in entries
if entry.source != SOURCE_IGNORE and entry.disabled_by is None
)
def _domains_from_yaml_config(yaml_configuration: dict[str, Any]) -> set[str]:
"""Extract domains from the YAML configuration."""
domains = set(yaml_configuration)
for platforms in conf_util.extract_platform_integrations(
yaml_configuration, BASE_PLATFORMS
).values():
domains.update(platforms)
return domains

View File

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

View File

@@ -89,7 +89,7 @@ class ArubaDeviceScanner(DeviceScanner):
def get_aruba_data(self) -> dict[str, dict[str, str]] | None:
"""Retrieve data from Aruba Access Point and return parsed result."""
connect = f"ssh {self.username}@{self.host}"
connect = f"ssh {self.username}@{self.host} -o HostKeyAlgorithms=ssh-rsa"
ssh: pexpect.spawn[str] = pexpect.spawn(connect, encoding="utf-8")
query = ssh.expect(
[

View File

@@ -11,7 +11,7 @@ from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac
from homeassistant.helpers.dispatcher import async_dispatcher_send
from ..const import ATTR_MANUFACTURER, DOMAIN as AXIS_DOMAIN
from ..const import ATTR_MANUFACTURER, DOMAIN
from .config import AxisConfig
from .entity_loader import AxisEntityLoader
from .event_source import AxisEventSource
@@ -79,7 +79,7 @@ class AxisHub:
config_entry_id=self.config.entry.entry_id,
configuration_url=self.api.config.url,
connections={(CONNECTION_NETWORK_MAC, self.unique_id)},
identifiers={(AXIS_DOMAIN, self.unique_id)},
identifiers={(DOMAIN, self.unique_id)},
manufacturer=ATTR_MANUFACTURER,
model=f"{self.config.model} {self.product_type}",
name=self.config.name,

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.49.0"
"habluetooth==3.48.2"
]
}

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, True, False, True)
super().__init__(panel, area_id, unique_id, False, 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.6.0", "icalendar==6.1.0"]
"requirements": ["caldav==1.3.9", "icalendar==6.1.0"]
}

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/camera",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["PyTurboJPEG==1.7.5"]
"requirements": ["PyTurboJPEG==1.8.0"]
}

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/compensation",
"iot_class": "calculated",
"quality_scale": "legacy",
"requirements": ["numpy==2.2.2"]
"requirements": ["numpy==2.2.6"]
}

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.6.10"]
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.5.28"]
}

View File

@@ -9,7 +9,7 @@ from homeassistant.const import ATTR_DEVICE_ID, CONF_EVENT, CONF_ID
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from .const import CONF_GESTURE, DOMAIN as DECONZ_DOMAIN
from .const import CONF_GESTURE, DOMAIN
from .deconz_event import CONF_DECONZ_ALARM_EVENT, CONF_DECONZ_EVENT
from .device_trigger import (
CONF_BOTH_BUTTONS,
@@ -200,6 +200,6 @@ def async_describe_events(
}
async_describe_event(
DECONZ_DOMAIN, CONF_DECONZ_ALARM_EVENT, async_describe_deconz_alarm_event
DOMAIN, CONF_DECONZ_ALARM_EVENT, async_describe_deconz_alarm_event
)
async_describe_event(DECONZ_DOMAIN, CONF_DECONZ_EVENT, async_describe_deconz_event)
async_describe_event(DOMAIN, CONF_DECONZ_EVENT, async_describe_deconz_event)

View File

@@ -6,10 +6,8 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_SOURCE, Platform
from homeassistant.core import HomeAssistant
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
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -19,28 +17,6 @@ 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)
entry.async_on_unload(
async_handle_source_entity_changes(
hass,
helper_config_entry_id=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

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/dnsip",
"iot_class": "cloud_polling",
"requirements": ["aiodns==3.5.0"]
"requirements": ["aiodns==3.4.0"]
}

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.3.0"]
"requirements": ["py-sucks==0.9.11", "deebot-client==13.2.1"]
}

View File

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

View File

@@ -23,18 +23,17 @@ from homeassistant.const import (
STATE_UNKNOWN,
UnitOfTemperature,
)
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, Event, HomeAssistant
from homeassistant.core import 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(
{
@@ -59,21 +58,6 @@ 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,15 +180,9 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
)
return
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
identifiers={
(
DOMAIN,
self.envoy_serial_number,
)
},
connections={connection},
device_registry.async_update_device(
device_id=envoy_device.id,
new_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.11.2"]
"requirements": ["env-canada==0.10.2"]
}

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
import asyncio
from functools import partial
import logging
import re
from typing import TYPE_CHECKING, Any, NamedTuple
from aioesphomeapi import (
@@ -22,7 +23,6 @@ from aioesphomeapi import (
RequiresEncryptionAPIError,
UserService,
UserServiceArgType,
parse_log_message,
)
from awesomeversion import AwesomeVersion
import voluptuous as vol
@@ -110,6 +110,11 @@ 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
@@ -382,15 +387,13 @@ class ESPHomeManager:
def _async_on_log(self, msg: SubscribeLogsResponse) -> None:
"""Handle a log message from the API."""
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,
)
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"),
)
@callback
def _async_get_equivalent_log_level(self) -> LogLevel:

View File

@@ -17,9 +17,9 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==32.2.1",
"aioesphomeapi==31.1.0",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==2.16.0"
"bleak-esphome==2.15.1"
],
"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[self.unique_id] = cast(
self._entry_data.media_player_formats[static_info.unique_id] = cast(
MediaPlayerInfo, static_info
).supported_formats
@@ -114,8 +114,9 @@ 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.unique_id)
self._entry_data.media_player_formats.get(self._static_info.unique_id)
)
if (
@@ -138,7 +139,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.unique_id, None)
self._entry_data.media_player_formats.pop(self.entity_id, None)
def _get_proxy_url(
self,

View File

@@ -84,7 +84,6 @@ 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==20250531.3"]
"requirements": ["home-assistant-frontend==20250531.0"]
}

View File

@@ -5,18 +5,11 @@ import voluptuous as vol
from homeassistant.components.humidifier import HumidifierDeviceClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, CONF_UNIQUE_ID, Platform
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import (
config_validation as cv,
discovery,
entity_registry as er,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.device import (
async_entity_id_to_device_id,
async_remove_stale_devices_links_keep_entity_device,
)
from homeassistant.helpers.event import async_track_entity_registry_updated_event
from homeassistant.helpers.helper_integration import async_handle_source_entity_changes
from homeassistant.helpers.typing import ConfigType
DOMAIN = "generic_hygrostat"
@@ -95,54 +88,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.options[CONF_HUMIDIFIER],
)
def set_humidifier_entity_id_or_uuid(source_entity_id: str) -> None:
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_HUMIDIFIER: 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)
entry.async_on_unload(
# We use async_handle_source_entity_changes to track changes to the humidifer,
# but not the humidity sensor because the generic_hygrostat adds itself to the
# humidifier's device.
async_handle_source_entity_changes(
hass,
helper_config_entry_id=entry.entry_id,
set_source_entity_id_or_uuid=set_humidifier_entity_id_or_uuid,
source_device_id=async_entity_id_to_device_id(
hass, entry.options[CONF_HUMIDIFIER]
),
source_entity_id_or_uuid=entry.options[CONF_HUMIDIFIER],
source_entity_removed=source_entity_removed,
)
)
async def async_sensor_updated(
event: Event[er.EventEntityRegistryUpdatedData],
) -> None:
"""Handle entity registry update."""
data = event.data
if data["action"] != "update":
return
if "entity_id" not in data["changes"]:
return
# Entity_id changed, update the config entry
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_SENSOR: data["entity_id"]},
)
entry.async_on_unload(
async_track_entity_registry_updated_event(
hass, entry.options[CONF_SENSOR], async_sensor_updated
)
)
await hass.config_entries.async_forward_entry_setups(entry, (Platform.HUMIDIFIER,))
entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
return True

View File

@@ -1,16 +1,12 @@
"""The generic_thermostat component."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device import (
async_entity_id_to_device_id,
async_remove_stale_devices_links_keep_entity_device,
)
from homeassistant.helpers.event import async_track_entity_registry_updated_event
from homeassistant.helpers.helper_integration import async_handle_source_entity_changes
from .const import CONF_HEATER, CONF_SENSOR, PLATFORMS
from .const import CONF_HEATER, PLATFORMS
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -21,55 +17,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.entry_id,
entry.options[CONF_HEATER],
)
def set_humidifier_entity_id_or_uuid(source_entity_id: str) -> None:
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_HEATER: 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)
entry.async_on_unload(
# We use async_handle_source_entity_changes to track changes to the heater, but
# not the temperature sensor because the generic_hygrostat adds itself to the
# heater's device.
async_handle_source_entity_changes(
hass,
helper_config_entry_id=entry.entry_id,
set_source_entity_id_or_uuid=set_humidifier_entity_id_or_uuid,
source_device_id=async_entity_id_to_device_id(
hass, entry.options[CONF_HEATER]
),
source_entity_id_or_uuid=entry.options[CONF_HEATER],
source_entity_removed=source_entity_removed,
)
)
async def async_sensor_updated(
event: Event[er.EventEntityRegistryUpdatedData],
) -> None:
"""Handle entity registry update."""
data = event.data
if data["action"] != "update":
return
if "entity_id" not in data["changes"]:
return
# Entity_id changed, update the config entry
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_SENSOR: data["entity_id"]},
)
entry.async_on_unload(
async_track_entity_registry_updated_event(
hass, entry.options[CONF_SENSOR], async_sensor_updated
)
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
return True

View File

@@ -12,7 +12,6 @@ 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 (
@@ -26,7 +25,6 @@ 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
@@ -85,17 +83,7 @@ async def async_send_text_commands(
) as assistant:
command_response_list = []
for command in commands:
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
resp = await hass.async_add_executor_job(assistant.assist, command)
text_response = resp[0]
_LOGGER.debug("command: %s\nresponse: %s", command, text_response)
audio_response = resp[2]

View File

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

View File

@@ -24,9 +24,11 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Google Mail platform."""
"""Set up the Google Mail integration."""
hass.data.setdefault(DOMAIN, {})[DATA_HASS_CONFIG] = config
await async_setup_services(hass)
return True
@@ -52,8 +54,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: GoogleMailConfigEntry) -
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
)
await async_setup_services(hass)
return True

View File

@@ -7,17 +7,26 @@ from google_photos_library_api.api import GooglePhotosLibraryApi
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from . import api
from .const import DOMAIN
from .coordinator import GooglePhotosConfigEntry, GooglePhotosUpdateCoordinator
from .services import async_register_services
__all__ = [
"DOMAIN",
]
__all__ = ["DOMAIN"]
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Google Photos integration."""
async_register_services(hass)
return True
async def async_setup_entry(
@@ -48,8 +57,6 @@ async def async_setup_entry(
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
async_register_services(hass)
return True

View File

@@ -152,11 +152,10 @@ def async_register_services(hass: HomeAssistant) -> None:
}
return None
if not hass.services.has_service(DOMAIN, UPLOAD_SERVICE):
hass.services.async_register(
DOMAIN,
UPLOAD_SERVICE,
async_handle_upload,
schema=UPLOAD_SERVICE_SCHEMA,
supports_response=SupportsResponse.OPTIONAL,
)
hass.services.async_register(
DOMAIN,
UPLOAD_SERVICE,
async_handle_upload,
schema=UPLOAD_SERVICE_SCHEMA,
supports_response=SupportsResponse.OPTIONAL,
)

View File

@@ -55,7 +55,7 @@ from homeassistant.helpers.issue_registry import (
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
from .const import CONF_IGNORE_NON_NUMERIC, DOMAIN as GROUP_DOMAIN
from .const import CONF_IGNORE_NON_NUMERIC, DOMAIN
from .entity import GroupEntity
DEFAULT_NAME = "Sensor Group"
@@ -509,7 +509,7 @@ class SensorGroup(GroupEntity, SensorEntity):
return state_classes[0]
async_create_issue(
self.hass,
GROUP_DOMAIN,
DOMAIN,
f"{self.entity_id}_state_classes_not_matching",
is_fixable=False,
is_persistent=False,
@@ -566,7 +566,7 @@ class SensorGroup(GroupEntity, SensorEntity):
return device_classes[0]
async_create_issue(
self.hass,
GROUP_DOMAIN,
DOMAIN,
f"{self.entity_id}_device_classes_not_matching",
is_fixable=False,
is_persistent=False,
@@ -654,7 +654,7 @@ class SensorGroup(GroupEntity, SensorEntity):
if device_class:
async_create_issue(
self.hass,
GROUP_DOMAIN,
DOMAIN,
f"{self.entity_id}_uoms_not_matching_device_class",
is_fixable=False,
is_persistent=False,
@@ -670,7 +670,7 @@ class SensorGroup(GroupEntity, SensorEntity):
else:
async_create_issue(
self.hass,
GROUP_DOMAIN,
DOMAIN,
f"{self.entity_id}_uoms_not_matching_no_device_class",
is_fixable=False,
is_persistent=False,

View File

@@ -9,10 +9,8 @@ from functools import partial
import logging
import os
import re
import struct
from typing import Any, NamedTuple
import aiofiles
from aiohasupervisor import SupervisorError
import voluptuous as vol
@@ -39,7 +37,6 @@ 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 (
@@ -54,7 +51,6 @@ 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,
)
@@ -113,7 +109,7 @@ from .coordinator import (
get_core_info, # noqa: F401
get_core_stats, # noqa: F401
get_host_info, # noqa: F401
get_info,
get_info, # noqa: F401
get_issues_info, # noqa: F401
get_os_info,
get_supervisor_info, # noqa: F401
@@ -172,11 +168,6 @@ 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."""
@@ -234,17 +225,6 @@ SCHEMA_RESTORE_PARTIAL = SCHEMA_RESTORE_FULL.extend(
)
def _is_32_bit() -> bool:
size = struct.calcsize("P")
return size * 8 == 32
async def _get_arch() -> str:
async with aiofiles.open("/etc/apk/arch") as arch_file:
raw_arch = await arch_file.read()
return {"x86": "i386"}.get(raw_arch, raw_arch)
class APIEndpointSettings(NamedTuple):
"""Settings for API endpoint."""
@@ -566,62 +546,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh()
hass.data[ADDONS_COORDINATOR] = coordinator
arch = await _get_arch()
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
board = os_info.get("board")
unsupported_board = board in {"tinker", "odroid-xu4", "rpi2"}
unsupported_os_on_board = board in {"rpi3", "rpi4"}
if is_haos and (unsupported_board or unsupported_os_on_board):
issue_id = "deprecated_os_"
if unsupported_os_on_board:
issue_id += "aarch64"
elif unsupported_board:
issue_id += "armv7"
ir.async_create_issue(
hass,
"homeassistant",
issue_id,
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/",
},
)
bit32 = _is_32_bit()
deprecated_architecture = bit32 and not (
unsupported_board or unsupported_os_on_board
)
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,
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

@@ -5,13 +5,26 @@ from __future__ import annotations
from homeassistant.const import CONF_API_KEY, CONF_MODE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.start import async_at_started
from homeassistant.util import dt as dt_util
from .const import TRAVEL_MODE_PUBLIC
from .const import (
CONF_ARRIVAL_TIME,
CONF_DEPARTURE_TIME,
CONF_DESTINATION_ENTITY_ID,
CONF_DESTINATION_LATITUDE,
CONF_DESTINATION_LONGITUDE,
CONF_ORIGIN_ENTITY_ID,
CONF_ORIGIN_LATITUDE,
CONF_ORIGIN_LONGITUDE,
CONF_ROUTE_MODE,
TRAVEL_MODE_PUBLIC,
)
from .coordinator import (
HereConfigEntry,
HERERoutingDataUpdateCoordinator,
HERETransitDataUpdateCoordinator,
)
from .model import HERETravelTimeConfig
PLATFORMS = [Platform.SENSOR]
@@ -20,13 +33,29 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: HereConfigEntry)
"""Set up HERE Travel Time from a config entry."""
api_key = config_entry.data[CONF_API_KEY]
arrival = dt_util.parse_time(config_entry.options.get(CONF_ARRIVAL_TIME, ""))
departure = dt_util.parse_time(config_entry.options.get(CONF_DEPARTURE_TIME, ""))
here_travel_time_config = HERETravelTimeConfig(
destination_latitude=config_entry.data.get(CONF_DESTINATION_LATITUDE),
destination_longitude=config_entry.data.get(CONF_DESTINATION_LONGITUDE),
destination_entity_id=config_entry.data.get(CONF_DESTINATION_ENTITY_ID),
origin_latitude=config_entry.data.get(CONF_ORIGIN_LATITUDE),
origin_longitude=config_entry.data.get(CONF_ORIGIN_LONGITUDE),
origin_entity_id=config_entry.data.get(CONF_ORIGIN_ENTITY_ID),
travel_mode=config_entry.data[CONF_MODE],
route_mode=config_entry.options[CONF_ROUTE_MODE],
arrival=arrival,
departure=departure,
)
cls: type[HERETransitDataUpdateCoordinator | HERERoutingDataUpdateCoordinator]
if config_entry.data[CONF_MODE] in {TRAVEL_MODE_PUBLIC, "publicTransportTimeTable"}:
cls = HERETransitDataUpdateCoordinator
else:
cls = HERERoutingDataUpdateCoordinator
data_coordinator = cls(hass, config_entry, api_key)
data_coordinator = cls(hass, config_entry, api_key, here_travel_time_config)
config_entry.runtime_data = data_coordinator
async def _async_update_at_start(_: HomeAssistant) -> None:

View File

@@ -26,7 +26,7 @@ from here_transit import (
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MODE, UnitOfLength
from homeassistant.const import UnitOfLength
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.location import find_coordinates
@@ -34,21 +34,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from homeassistant.util import dt as dt_util
from homeassistant.util.unit_conversion import DistanceConverter
from .const import (
CONF_ARRIVAL_TIME,
CONF_DEPARTURE_TIME,
CONF_DESTINATION_ENTITY_ID,
CONF_DESTINATION_LATITUDE,
CONF_DESTINATION_LONGITUDE,
CONF_ORIGIN_ENTITY_ID,
CONF_ORIGIN_LATITUDE,
CONF_ORIGIN_LONGITUDE,
CONF_ROUTE_MODE,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
ROUTE_MODE_FASTEST,
)
from .model import HERETravelTimeAPIParams, HERETravelTimeData
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, ROUTE_MODE_FASTEST
from .model import HERETravelTimeConfig, HERETravelTimeData
BACKOFF_MULTIPLIER = 1.1
@@ -60,7 +47,7 @@ type HereConfigEntry = ConfigEntry[
class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]):
"""HERETravelTime DataUpdateCoordinator for the routing API."""
"""here_routing DataUpdateCoordinator."""
config_entry: HereConfigEntry
@@ -69,6 +56,7 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]
hass: HomeAssistant,
config_entry: HereConfigEntry,
api_key: str,
config: HERETravelTimeConfig,
) -> None:
"""Initialize."""
super().__init__(
@@ -79,34 +67,41 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
)
self._api = HERERoutingApi(api_key)
self.config = config
async def _async_update_data(self) -> HERETravelTimeData:
"""Get the latest data from the HERE Routing API."""
params = prepare_parameters(self.hass, self.config_entry)
origin, destination, arrival, departure = prepare_parameters(
self.hass, self.config
)
route_mode = (
RoutingMode.FAST
if self.config.route_mode == ROUTE_MODE_FASTEST
else RoutingMode.SHORT
)
_LOGGER.debug(
(
"Requesting route for origin: %s, destination: %s, route_mode: %s,"
" mode: %s, arrival: %s, departure: %s"
),
params.origin,
params.destination,
params.route_mode,
TransportMode(params.travel_mode),
params.arrival,
params.departure,
origin,
destination,
route_mode,
TransportMode(self.config.travel_mode),
arrival,
departure,
)
try:
response = await self._api.route(
transport_mode=TransportMode(params.travel_mode),
origin=here_routing.Place(params.origin[0], params.origin[1]),
destination=here_routing.Place(
params.destination[0], params.destination[1]
),
routing_mode=params.route_mode,
arrival_time=params.arrival,
departure_time=params.departure,
transport_mode=TransportMode(self.config.travel_mode),
origin=here_routing.Place(origin[0], origin[1]),
destination=here_routing.Place(destination[0], destination[1]),
routing_mode=route_mode,
arrival_time=arrival,
departure_time=departure,
return_values=[Return.POLYINE, Return.SUMMARY],
spans=[Spans.NAMES],
)
@@ -180,7 +175,7 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]
class HERETransitDataUpdateCoordinator(
DataUpdateCoordinator[HERETravelTimeData | None]
):
"""HERETravelTime DataUpdateCoordinator for the transit API."""
"""HERETravelTime DataUpdateCoordinator."""
config_entry: HereConfigEntry
@@ -189,6 +184,7 @@ class HERETransitDataUpdateCoordinator(
hass: HomeAssistant,
config_entry: HereConfigEntry,
api_key: str,
config: HERETravelTimeConfig,
) -> None:
"""Initialize."""
super().__init__(
@@ -199,31 +195,32 @@ class HERETransitDataUpdateCoordinator(
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
)
self._api = HERETransitApi(api_key)
self.config = config
async def _async_update_data(self) -> HERETravelTimeData | None:
"""Get the latest data from the HERE Routing API."""
params = prepare_parameters(self.hass, self.config_entry)
origin, destination, arrival, departure = prepare_parameters(
self.hass, self.config
)
_LOGGER.debug(
(
"Requesting transit route for origin: %s, destination: %s, arrival: %s,"
" departure: %s"
),
params.origin,
params.destination,
params.arrival,
params.departure,
origin,
destination,
arrival,
departure,
)
try:
response = await self._api.route(
origin=here_transit.Place(
latitude=params.origin[0], longitude=params.origin[1]
),
origin=here_transit.Place(latitude=origin[0], longitude=origin[1]),
destination=here_transit.Place(
latitude=params.destination[0], longitude=params.destination[1]
latitude=destination[0], longitude=destination[1]
),
arrival_time=params.arrival,
departure_time=params.departure,
arrival_time=arrival,
departure_time=departure,
return_values=[
here_transit.Return.POLYLINE,
here_transit.Return.TRAVEL_SUMMARY,
@@ -288,8 +285,8 @@ class HERETransitDataUpdateCoordinator(
def prepare_parameters(
hass: HomeAssistant,
config_entry: HereConfigEntry,
) -> HERETravelTimeAPIParams:
config: HERETravelTimeConfig,
) -> tuple[list[str], list[str], str | None, str | None]:
"""Prepare parameters for the HERE api."""
def _from_entity_id(entity_id: str) -> list[str]:
@@ -308,55 +305,32 @@ def prepare_parameters(
return formatted_coordinates
# Destination
if (
destination_entity_id := config_entry.data.get(CONF_DESTINATION_ENTITY_ID)
) is not None:
destination = _from_entity_id(str(destination_entity_id))
if config.destination_entity_id is not None:
destination = _from_entity_id(config.destination_entity_id)
else:
destination = [
str(config_entry.data[CONF_DESTINATION_LATITUDE]),
str(config_entry.data[CONF_DESTINATION_LONGITUDE]),
str(config.destination_latitude),
str(config.destination_longitude),
]
# Origin
if (origin_entity_id := config_entry.data.get(CONF_ORIGIN_ENTITY_ID)) is not None:
origin = _from_entity_id(str(origin_entity_id))
if config.origin_entity_id is not None:
origin = _from_entity_id(config.origin_entity_id)
else:
origin = [
str(config_entry.data[CONF_ORIGIN_LATITUDE]),
str(config_entry.data[CONF_ORIGIN_LONGITUDE]),
str(config.origin_latitude),
str(config.origin_longitude),
]
# Arrival/Departure
arrival: datetime | None = None
if (
conf_arrival := dt_util.parse_time(
config_entry.options.get(CONF_ARRIVAL_TIME, "")
)
) is not None:
arrival = next_datetime(conf_arrival)
departure: datetime | None = None
if (
conf_departure := dt_util.parse_time(
config_entry.options.get(CONF_DEPARTURE_TIME, "")
)
) is not None:
departure = next_datetime(conf_departure)
arrival: str | None = None
departure: str | None = None
if config.arrival is not None:
arrival = next_datetime(config.arrival).isoformat()
if config.departure is not None:
departure = next_datetime(config.departure).isoformat()
route_mode = (
RoutingMode.FAST
if config_entry.options[CONF_ROUTE_MODE] == ROUTE_MODE_FASTEST
else RoutingMode.SHORT
)
return HERETravelTimeAPIParams(
destination=destination,
origin=origin,
travel_mode=config_entry.data[CONF_MODE],
route_mode=route_mode,
arrival=arrival,
departure=departure,
)
return (origin, destination, arrival, departure)
def build_hass_attribution(sections: list[dict[str, Any]]) -> str | None:

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from datetime import time
from typing import TypedDict
@@ -21,12 +21,16 @@ class HERETravelTimeData(TypedDict):
@dataclass
class HERETravelTimeAPIParams:
"""Configuration for polling the HERE API."""
class HERETravelTimeConfig:
"""Configuration for HereTravelTimeDataUpdateCoordinator."""
destination: list[str]
origin: list[str]
destination_latitude: float | None
destination_longitude: float | None
destination_entity_id: str | None
origin_latitude: float | None
origin_longitude: float | None
origin_entity_id: str | None
travel_mode: str
route_mode: str
arrival: datetime | None
departure: datetime | None
arrival: time | None
departure: time | None

View File

@@ -8,10 +8,8 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ENTITY_ID, CONF_STATE
from homeassistant.core import HomeAssistant
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 homeassistant.helpers.template import Template
from .const import CONF_DURATION, CONF_END, CONF_START, PLATFORMS
@@ -53,30 +51,6 @@ async def async_setup_entry(
entry.options[CONF_ENTITY_ID],
)
def set_source_entity_id_or_uuid(source_entity_id: str) -> None:
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_ENTITY_ID: source_entity_id},
)
async def source_entity_removed() -> None:
# The source entity has been removed, we remove the config entry because
# history_stats does not allow replacing the input entity.
await hass.config_entries.async_remove(entry.entry_id)
entry.async_on_unload(
async_handle_source_entity_changes(
hass,
helper_config_entry_id=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_ENTITY_ID]
),
source_entity_id_or_uuid=entry.options[CONF_ENTITY_ID],
source_entity_removed=source_entity_removed,
)
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(update_listener))

View File

@@ -107,7 +107,7 @@ OPTIONS_FLOW = {
}
class HistoryStatsConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
class StatisticsConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
"""Handle a config flow for History stats."""
config_flow = CONFIG_FLOW

View File

@@ -25,12 +25,17 @@ 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=selected_categories,
categories=categories,
)
if language == "en":
for lang in obj_holidays.supported_languages:
@@ -40,7 +45,7 @@ def _get_obj_holidays_and_language(
subdiv=province,
years={dt_util.now().year, dt_util.now().year + 1},
language=lang,
categories=selected_categories,
categories=categories,
)
language = lang
break
@@ -54,7 +59,7 @@ def _get_obj_holidays_and_language(
subdiv=province,
years={dt_util.now().year, dt_util.now().year + 1},
language=default_language,
categories=selected_categories,
categories=categories,
)
language = default_language
@@ -72,11 +77,6 @@ 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.74", "babel==2.15.0"]
"requirements": ["holidays==0.73", "babel==2.15.0"]
}

View File

@@ -12,13 +12,3 @@ 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

@@ -1,7 +1,4 @@
{
"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"
@@ -16,7 +13,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. Be aware that the setup of Home Connect is more complicated than many other integrations. Press **Submit** to continue setting up Home Connect."
"description": "Home Assistant has found a Home Connect device on your network. Press **Submit** to continue setting up Home Connect."
}
},
"abort": {

View File

@@ -4,10 +4,8 @@ import asyncio
from collections.abc import Callable, Coroutine
import itertools as it
import logging
import struct
from typing import Any
from typing import TYPE_CHECKING, Any
import aiofiles
import voluptuous as vol
from homeassistant import config as conf_util, core_config
@@ -40,6 +38,7 @@ 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,
@@ -96,17 +95,6 @@ DEPRECATION_URL = (
)
def _is_32_bit() -> bool:
size = struct.calcsize("P")
return size * 8 == 32
async def _get_arch() -> str:
async with aiofiles.open("/etc/apk/arch") as arch_file:
raw_arch = (await arch_file.read()).strip()
return {"x86": "i386", "x86_64": "amd64"}.get(raw_arch, raw_arch)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: C901
"""Set up general services related to Home Assistant."""
@@ -414,42 +402,79 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
info = await async_get_system_info(hass)
installation_type = info["installation_type"][15:]
if installation_type in {"Core", "Container"}:
deprecated_method = installation_type == "Core"
bit32 = _is_32_bit()
arch = info["arch"]
if bit32 and installation_type == "Container":
arch = await _get_arch()
ir.async_create_issue(
hass,
DOMAIN,
"deprecated_container",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_container",
translation_placeholders={"arch": arch},
)
deprecated_architecture = bit32 and installation_type != "Container"
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
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_type": installation_type,
"arch": arch,
"installation_guide": "https://www.home-assistant.io/installation/",
},
)
elif installation_type == "Container":
ir.async_create_issue(
hass,
DOMAIN,
"deprecated_container_armv7",
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
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,
},
)
return True

View File

@@ -20,11 +20,11 @@
},
"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."
"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 all \"{integration_title}\" config entries to fix this issue."
},
"deprecated_system_packages_yaml_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 the {domain} entry from your configuration.yaml file and restart Home Assistant."
"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."
},
"historic_currency": {
"title": "The configured currency is no longer in use",
@@ -107,9 +107,9 @@
"title": "Deprecation notice: 32-bit architecture",
"description": "This system uses 32-bit hardware (`{arch}`), which has been deprecated and will no longer receive updates after the release of Home Assistant 2025.12. As your hardware is no longer capable of running newer versions of Home Assistant, you will need to migrate to new hardware."
},
"deprecated_container": {
"deprecated_container_armv7": {
"title": "[%key:component::homeassistant::issues::deprecated_architecture::title%]",
"description": "This system is running on a 32-bit operating system (`{arch}`), which has been deprecated and will no longer receive updates after the release of Home Assistant 2025.12. Check if your system is capable of running a 64-bit operating system. If not, you will need to migrate to new hardware."
"description": "This system is running on a 32-bit operating system (`armv7`), which has been deprecated and will no longer receive updates after the release of Home Assistant 2025.12. Check if your system is capable of running a 64-bit operating system. If not, you will need to migrate to new hardware."
},
"deprecated_os_aarch64": {
"title": "[%key:component::homeassistant::issues::deprecated_architecture::title%]",

View File

@@ -27,6 +27,7 @@ PLATFORMS = [
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SIREN,
Platform.SWITCH,
Platform.VALVE,
]

View File

@@ -0,0 +1,49 @@
"""The homee siren platform."""
from typing import Any
from pyHomee.const import AttributeType
from homeassistant.components.siren import SirenEntity, SirenEntityFeature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import HomeeConfigEntry
from .entity import HomeeEntity
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
config_entry: HomeeConfigEntry,
async_add_devices: AddConfigEntryEntitiesCallback,
) -> None:
"""Add siren entities for homee."""
async_add_devices(
HomeeSiren(attribute, config_entry)
for node in config_entry.runtime_data.nodes
for attribute in node.attributes
if attribute.type == AttributeType.SIREN
)
class HomeeSiren(HomeeEntity, SirenEntity):
"""Representation of a homee siren device."""
_attr_name = None
_attr_supported_features = SirenEntityFeature.TURN_ON | SirenEntityFeature.TURN_OFF
@property
def is_on(self) -> bool:
"""Return the state of the siren."""
return self._attribute.current_value == 1.0
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the siren on."""
await self.async_set_homee_value(1)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the siren off."""
await self.async_set_homee_value(0)

View File

@@ -14,6 +14,6 @@
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"iot_class": "local_push",
"loggers": ["aiohomekit", "commentjson"],
"requirements": ["aiohomekit==3.2.14"],
"requirements": ["aiohomekit==3.2.15"],
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
}

View File

@@ -63,6 +63,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
)
)
await async_setup_services(hass)
return True
@@ -83,7 +85,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomematicIPConfigEntry)
if not await hap.async_setup():
return False
await async_setup_services(hass)
_async_remove_obsolete_entities(hass, entry, hap)
# Register on HA stop event to gracefully shutdown HomematicIP Cloud connection

View File

@@ -112,7 +112,6 @@ 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
@@ -128,7 +127,6 @@ class HomematicipHAP:
self.config_entry.data.get(HMIPC_AUTHTOKEN),
self.config_entry.data.get(HMIPC_NAME),
)
except HmipcConnectionError as err:
raise ConfigEntryNotReady from err
except Exception as err: # noqa: BLE001
@@ -211,13 +209,39 @@ class HomematicipHAP:
for device in self.home.devices:
device.fire_update_event()
async def async_connect(self, home: AsyncHome) -> None:
"""Connect to HomematicIP Cloud Websocket."""
await home.enable_events()
async def async_connect(self) -> None:
"""Start WebSocket connection."""
tries = 0
while True:
retry_delay = 2 ** min(tries, 8)
home.set_on_connected_handler(self.ws_connected_handler)
home.set_on_disconnected_handler(self.ws_disconnected_handler)
home.set_on_reconnect_handler(self.ws_reconnected_handler)
try:
await self.home.get_current_state_async()
hmip_events = self.home.enable_events()
tries = 0
await hmip_events
except HmipConnectionError:
_LOGGER.error(
(
"Error connecting to HomematicIP with HAP %s. "
"Retrying in %d seconds"
),
self.config_entry.unique_id,
retry_delay,
)
if self._ws_close_requested:
break
self._ws_close_requested = False
tries += 1
try:
self._retry_task = self.hass.async_create_task(
asyncio.sleep(retry_delay)
)
await self._retry_task
except asyncio.CancelledError:
break
async def async_reset(self) -> bool:
"""Close the websocket connection."""
@@ -243,26 +267,6 @@ 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.info("Websocket connection to HomematicIP Cloud 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 Cloud closed")
self._ws_connection_closed.set()
async def ws_reconnected_handler(self, reason: str) -> None:
"""Handle websocket reconnection."""
_LOGGER.info(
"Websocket connection to HomematicIP Cloud re-established due to reason: %s",
reason,
)
self._ws_connection_closed.set()
async def get_hap(
self,
hass: HomeAssistant,
@@ -286,7 +290,6 @@ class HomematicipHAP:
raise HmipcConnectionError from err
home.on_update(self.async_update)
home.on_create(self.async_create_entity)
await self.async_connect(home)
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 DeviceType, OpticalSignalBehaviour, RGBColorState
from homematicip.base.enums import 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,12 +44,9 @@ async def async_setup_entry(
hap = config_entry.runtime_data
entities: list[HomematicipGenericEntity] = []
for device in hap.home.devices:
if (
isinstance(device, SwitchMeasuring)
and getattr(device, "deviceType", None) == DeviceType.BRAND_SWITCH_MEASURING
):
if isinstance(device, BrandSwitchMeasuring):
entities.append(HomematicipLightMeasuring(hap, device))
if isinstance(device, BrandSwitchNotificationLight):
elif 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.5"]
"requirements": ["homematicip==2.0.1.1"]
}

View File

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

View File

@@ -4,19 +4,20 @@ 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
@@ -42,10 +43,12 @@ async def async_setup_entry(
if isinstance(group, (ExtendedLinkedSwitchingGroup, SwitchingGroup))
]
for device in hap.home.devices:
if (
isinstance(device, SwitchMeasuring)
and getattr(device, "deviceType", None) != DeviceType.BRAND_SWITCH_MEASURING
):
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)):
entities.append(HomematicipSwitchMeasuring(hap, device))
elif isinstance(device, WiredSwitch8):
entities.extend(

View File

@@ -12,6 +12,6 @@
"iot_class": "local_polling",
"loggers": ["homewizard_energy"],
"quality_scale": "platinum",
"requirements": ["python-homewizard-energy==v8.3.2"],
"requirements": ["python-homewizard-energy==8.3.3"],
"zeroconf": ["_hwenergy._tcp.local.", "_homewizard._tcp.local."]
}

View File

@@ -39,14 +39,14 @@ def setup_cors(app: Application, origins: list[str]) -> None:
cors = aiohttp_cors.setup(
app,
defaults={
host: aiohttp_cors.ResourceOptions(
host: aiohttp_cors.ResourceOptions( # type: ignore[no-untyped-call]
allow_headers=ALLOWED_CORS_HEADERS, allow_methods="*"
)
for host in origins
},
)
cors_added = set()
cors_added: set[str] = set()
def _allow_cors(
route: AbstractRoute | AbstractResource,
@@ -69,13 +69,13 @@ def setup_cors(app: Application, origins: list[str]) -> None:
if path_str in cors_added:
return
cors.add(route, config)
cors.add(route, config) # type: ignore[arg-type]
cors_added.add(path_str)
app[KEY_ALLOW_ALL_CORS] = lambda route: _allow_cors(
route,
{
"*": aiohttp_cors.ResourceOptions(
"*": aiohttp_cors.ResourceOptions( # type: ignore[no-untyped-call]
allow_headers=ALLOWED_CORS_HEADERS, allow_methods="*"
)
},

View File

@@ -5,13 +5,24 @@ from aiohue.util import normalize_bridge_id
from homeassistant.components import persistent_notification
from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.typing import ConfigType
from .bridge import HueBridge, HueConfigEntry
from .const import DOMAIN, SERVICE_HUE_ACTIVATE_SCENE
from .const import DOMAIN
from .migration import check_migration
from .services import async_register_services
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Hue integration."""
async_register_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
"""Set up a bridge from a config entry."""
@@ -23,9 +34,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
if not await bridge.async_initialize_bridge():
return False
# register Hue domain services
async_register_services(hass)
api = bridge.api
# For backwards compat
@@ -106,7 +114,4 @@ async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
"""Unload a config entry."""
unload_success = await entry.runtime_data.async_reset()
if not hass.config_entries.async_loaded_entries(DOMAIN):
hass.services.async_remove(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE)
return unload_success
return await entry.runtime_data.async_reset()

View File

@@ -59,21 +59,20 @@ def async_register_services(hass: HomeAssistant) -> None:
group_name,
)
if not hass.services.has_service(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE):
# Register a local handler for scene activation
hass.services.async_register(
DOMAIN,
SERVICE_HUE_ACTIVATE_SCENE,
verify_domain_control(hass, DOMAIN)(hue_activate_scene),
schema=vol.Schema(
{
vol.Required(ATTR_GROUP_NAME): cv.string,
vol.Required(ATTR_SCENE_NAME): cv.string,
vol.Optional(ATTR_TRANSITION): cv.positive_int,
vol.Optional(ATTR_DYNAMIC): cv.boolean,
}
),
)
# Register a local handler for scene activation
hass.services.async_register(
DOMAIN,
SERVICE_HUE_ACTIVATE_SCENE,
verify_domain_control(hass, DOMAIN)(hue_activate_scene),
schema=vol.Schema(
{
vol.Required(ATTR_GROUP_NAME): cv.string,
vol.Required(ATTR_SCENE_NAME): cv.string,
vol.Optional(ATTR_TRANSITION): cv.positive_int,
vol.Optional(ATTR_DYNAMIC): cv.boolean,
}
),
)
async def hue_activate_scene_v1(

View File

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

View File

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

View File

@@ -6,10 +6,8 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
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 CONF_SOURCE_SENSOR
@@ -23,29 +21,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.options[CONF_SOURCE_SENSOR],
)
def set_source_entity_id_or_uuid(source_entity_id: str) -> None:
hass.config_entries.async_update_entry(
entry,
options={**entry.options, CONF_SOURCE_SENSOR: 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)
entry.async_on_unload(
async_handle_source_entity_changes(
hass,
helper_config_entry_id=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_SENSOR]
),
source_entity_id_or_uuid=entry.options[CONF_SOURCE_SENSOR],
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

@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pyiqvia"],
"requirements": ["numpy==2.2.2", "pyiqvia==2022.04.0"]
"requirements": ["numpy==2.2.6", "pyiqvia==2022.04.0"]
}

View File

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

View File

@@ -26,6 +26,7 @@ from homeassistant.helpers import (
device_registry as dr,
)
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.typing import ConfigType
from .const import (
_LOGGER,
@@ -46,7 +47,7 @@ from .const import (
)
from .helpers import _categorize_nodes, _categorize_programs
from .models import IsyConfigEntry, IsyData
from .services import async_setup_services, async_unload_services
from .services import async_setup_services
from .util import _async_cleanup_registry_entries
CONFIG_SCHEMA = vol.Schema(
@@ -55,6 +56,14 @@ CONFIG_SCHEMA = vol.Schema(
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the ISY 994 integration."""
async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
"""Set up the ISY 994 integration."""
isy_config = entry.data
@@ -167,9 +176,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_auto_update)
)
# Register Integration-wide Services:
async_setup_services(hass)
return True
@@ -221,9 +227,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool
_LOGGER.debug("ISY Stopping Event Stream and automatic updates")
entry.runtime_data.root.websocket.stop()
if not hass.config_entries.async_loaded_entries(DOMAIN):
async_unload_services(hass)
return unload_ok

View File

@@ -137,10 +137,6 @@ def async_get_entities(hass: HomeAssistant) -> dict[str, Entity]:
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Create and register services for the ISY integration."""
existing_services = hass.services.async_services_for_domain(DOMAIN)
if existing_services and SERVICE_SEND_PROGRAM_COMMAND in existing_services:
# Integration-level services have already been added. Return.
return
async def async_send_program_command_service_handler(service: ServiceCall) -> None:
"""Handle a send program command service call."""
@@ -230,18 +226,3 @@ def async_setup_services(hass: HomeAssistant) -> None:
schema=cv.make_entity_service_schema(SERVICE_RENAME_NODE_SCHEMA),
service_func=_async_rename_node,
)
@callback
def async_unload_services(hass: HomeAssistant) -> None:
"""Unload services for the ISY integration."""
existing_services = hass.services.async_services_for_domain(DOMAIN)
if not existing_services or SERVICE_SEND_PROGRAM_COMMAND not in existing_services:
return
_LOGGER.debug("Unloading ISY994 Services")
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_PROGRAM_COMMAND)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_RAW_NODE_COMMAND)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_NODE_COMMAND)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_GET_ZWAVE_PARAMETER)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SET_ZWAVE_PARAMETER)

View File

@@ -30,7 +30,7 @@ from .const import (
DOMAIN,
)
from .entity import JewishCalendarConfigEntry, JewishCalendarData
from .service import async_setup_services
from .services import async_setup_services
_LOGGER = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]

View File

@@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/jewish_calendar",
"iot_class": "calculated",
"loggers": ["hdate"],
"requirements": ["hdate[astral]==1.1.2"],
"requirements": ["hdate[astral]==1.1.0"],
"single_config_entry": true
}

View File

@@ -73,7 +73,7 @@ INFO_SENSORS: tuple[JewishCalendarSensorDescription, ...] = (
translation_key="weekly_portion",
device_class=SensorDeviceClass.ENUM,
options_fn=lambda _: [str(p) for p in Parasha],
value_fn=lambda results: results.after_tzais_date.upcoming_shabbat.parasha,
value_fn=lambda results: str(results.after_tzais_date.upcoming_shabbat.parasha),
),
JewishCalendarSensorDescription(
key="holiday",
@@ -98,13 +98,17 @@ INFO_SENSORS: tuple[JewishCalendarSensorDescription, ...] = (
key="omer_count",
translation_key="omer_count",
entity_registry_enabled_default=False,
value_fn=lambda results: results.after_shkia_date.omer.total_days,
value_fn=lambda results: (
results.after_shkia_date.omer.total_days
if results.after_shkia_date.omer
else 0
),
),
JewishCalendarSensorDescription(
key="daf_yomi",
translation_key="daf_yomi",
entity_registry_enabled_default=False,
value_fn=lambda results: results.daytime_date.daf_yomi,
value_fn=lambda results: str(results.daytime_date.daf_yomi),
),
)
@@ -221,7 +225,7 @@ async def async_setup_entry(
JewishCalendarTimeSensor(config_entry, description)
for description in TIME_SENSORS
)
async_add_entities(sensors, update_before_add=True)
async_add_entities(sensors)
class JewishCalendarBaseSensor(JewishCalendarEntity, SensorEntity):
@@ -229,7 +233,12 @@ class JewishCalendarBaseSensor(JewishCalendarEntity, SensorEntity):
_attr_entity_category = EntityCategory.DIAGNOSTIC
async def async_update(self) -> None:
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:
"""Update the state of the sensor."""
now = dt_util.now()
_LOGGER.debug("Now: %s Location: %r", now, self.data.location)

View File

@@ -1,34 +1,17 @@
"""Support KNX devices."""
"""The KNX integration."""
from __future__ import annotations
import contextlib
import logging
from pathlib import Path
from typing import Final
import voluptuous as vol
from xknx import XKNX
from xknx.core import XknxConnectionState
from xknx.core.state_updater import StateTrackerType, TrackerOptions
from xknx.core.telegram_queue import TelegramQueue
from xknx.dpt import DPTBase
from xknx.exceptions import ConversionError, CouldNotParseTelegram, XKNXException
from xknx.io import ConnectionConfig, ConnectionType, SecureConfig
from xknx.telegram import AddressFilter, Telegram
from xknx.telegram.address import DeviceGroupAddress, GroupAddress, InternalGroupAddress
from xknx.telegram.apci import GroupValueResponse, GroupValueWrite
from xknx.exceptions import XKNXException
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_EVENT,
CONF_HOST,
CONF_PORT,
CONF_TYPE,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import Event, HomeAssistant
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.reload import async_integration_yaml_config
@@ -36,40 +19,17 @@ from homeassistant.helpers.storage import STORAGE_DIR
from homeassistant.helpers.typing import ConfigType
from .const import (
CONF_KNX_CONNECTION_TYPE,
CONF_KNX_EXPOSE,
CONF_KNX_INDIVIDUAL_ADDRESS,
CONF_KNX_KNXKEY_FILENAME,
CONF_KNX_KNXKEY_PASSWORD,
CONF_KNX_LOCAL_IP,
CONF_KNX_MCAST_GRP,
CONF_KNX_MCAST_PORT,
CONF_KNX_RATE_LIMIT,
CONF_KNX_ROUTE_BACK,
CONF_KNX_ROUTING,
CONF_KNX_ROUTING_BACKBONE_KEY,
CONF_KNX_ROUTING_SECURE,
CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE,
CONF_KNX_SECURE_DEVICE_AUTHENTICATION,
CONF_KNX_SECURE_USER_ID,
CONF_KNX_SECURE_USER_PASSWORD,
CONF_KNX_STATE_UPDATER,
CONF_KNX_TELEGRAM_LOG_SIZE,
CONF_KNX_TUNNEL_ENDPOINT_IA,
CONF_KNX_TUNNELING,
CONF_KNX_TUNNELING_TCP,
CONF_KNX_TUNNELING_TCP_SECURE,
DATA_HASS_CONFIG,
DOMAIN,
KNX_ADDRESS,
KNX_MODULE_KEY,
SUPPORTED_PLATFORMS_UI,
SUPPORTED_PLATFORMS_YAML,
TELEGRAM_LOG_DEFAULT,
)
from .device import KNXInterfaceDevice
from .expose import KNXExposeSensor, KNXExposeTime, create_knx_exposure
from .project import STORAGE_KEY as PROJECT_STORAGE_KEY, KNXProject
from .expose import create_knx_exposure
from .knx_module import KNXModule
from .project import STORAGE_KEY as PROJECT_STORAGE_KEY
from .schema import (
BinarySensorSchema,
ButtonSchema,
@@ -92,12 +52,10 @@ from .schema import (
WeatherSchema,
)
from .services import register_knx_services
from .storage.config_store import STORAGE_KEY as CONFIG_STORAGE_KEY, KNXConfigStore
from .telegrams import STORAGE_KEY as TELEGRAMS_STORAGE_KEY, Telegrams
from .storage.config_store import STORAGE_KEY as CONFIG_STORAGE_KEY
from .telegrams import STORAGE_KEY as TELEGRAMS_STORAGE_KEY
from .websocket import register_panel
_LOGGER = logging.getLogger(__name__)
_KNX_YAML_CONFIG: Final = "knx_yaml_config"
CONFIG_SCHEMA = vol.Schema(
@@ -162,6 +120,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[KNX_MODULE_KEY] = knx_module
entry.async_on_unload(entry.add_update_listener(async_update_entry))
if CONF_KNX_EXPOSE in config:
for expose_config in config[CONF_KNX_EXPOSE]:
knx_module.exposures.append(
@@ -255,243 +215,3 @@ async def async_remove_config_entry_device(
if entity.device_id == device_entry.id:
await knx_module.config_store.delete_entity(entity.entity_id)
return True
class KNXModule:
"""Representation of KNX Object."""
def __init__(
self, hass: HomeAssistant, config: ConfigType, entry: ConfigEntry
) -> None:
"""Initialize KNX module."""
self.hass = hass
self.config_yaml = config
self.connected = False
self.exposures: list[KNXExposeSensor | KNXExposeTime] = []
self.service_exposures: dict[str, KNXExposeSensor | KNXExposeTime] = {}
self.entry = entry
self.project = KNXProject(hass=hass, entry=entry)
self.config_store = KNXConfigStore(hass=hass, config_entry=entry)
default_state_updater = (
TrackerOptions(tracker_type=StateTrackerType.EXPIRE, update_interval_min=60)
if self.entry.data[CONF_KNX_STATE_UPDATER]
else TrackerOptions(
tracker_type=StateTrackerType.INIT, update_interval_min=60
)
)
self.xknx = XKNX(
address_format=self.project.get_address_format(),
connection_config=self.connection_config(),
rate_limit=self.entry.data[CONF_KNX_RATE_LIMIT],
state_updater=default_state_updater,
)
self.xknx.connection_manager.register_connection_state_changed_cb(
self.connection_state_changed_cb
)
self.telegrams = Telegrams(
hass=hass,
xknx=self.xknx,
project=self.project,
log_size=entry.data.get(CONF_KNX_TELEGRAM_LOG_SIZE, TELEGRAM_LOG_DEFAULT),
)
self.interface_device = KNXInterfaceDevice(
hass=hass, entry=entry, xknx=self.xknx
)
self._address_filter_transcoder: dict[AddressFilter, type[DPTBase]] = {}
self.group_address_transcoder: dict[DeviceGroupAddress, type[DPTBase]] = {}
self.knx_event_callback: TelegramQueue.Callback = self.register_event_callback()
self.entry.async_on_unload(
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop)
)
self.entry.async_on_unload(self.entry.add_update_listener(async_update_entry))
async def start(self) -> None:
"""Start XKNX object. Connect to tunneling or Routing device."""
await self.project.load_project(self.xknx)
await self.config_store.load_data()
await self.telegrams.load_history()
await self.xknx.start()
async def stop(self, event: Event | None = None) -> None:
"""Stop XKNX object. Disconnect from tunneling or Routing device."""
await self.xknx.stop()
await self.telegrams.save_history()
def connection_config(self) -> ConnectionConfig:
"""Return the connection_config."""
_conn_type: str = self.entry.data[CONF_KNX_CONNECTION_TYPE]
_knxkeys_file: str | None = (
self.hass.config.path(
STORAGE_DIR,
self.entry.data[CONF_KNX_KNXKEY_FILENAME],
)
if self.entry.data.get(CONF_KNX_KNXKEY_FILENAME) is not None
else None
)
if _conn_type == CONF_KNX_ROUTING:
return ConnectionConfig(
connection_type=ConnectionType.ROUTING,
individual_address=self.entry.data[CONF_KNX_INDIVIDUAL_ADDRESS],
multicast_group=self.entry.data[CONF_KNX_MCAST_GRP],
multicast_port=self.entry.data[CONF_KNX_MCAST_PORT],
local_ip=self.entry.data.get(CONF_KNX_LOCAL_IP),
auto_reconnect=True,
secure_config=SecureConfig(
knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD),
knxkeys_file_path=_knxkeys_file,
),
threaded=True,
)
if _conn_type == CONF_KNX_TUNNELING:
return ConnectionConfig(
connection_type=ConnectionType.TUNNELING,
gateway_ip=self.entry.data[CONF_HOST],
gateway_port=self.entry.data[CONF_PORT],
local_ip=self.entry.data.get(CONF_KNX_LOCAL_IP),
route_back=self.entry.data.get(CONF_KNX_ROUTE_BACK, False),
auto_reconnect=True,
secure_config=SecureConfig(
knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD),
knxkeys_file_path=_knxkeys_file,
),
threaded=True,
)
if _conn_type == CONF_KNX_TUNNELING_TCP:
return ConnectionConfig(
connection_type=ConnectionType.TUNNELING_TCP,
individual_address=self.entry.data.get(CONF_KNX_TUNNEL_ENDPOINT_IA),
gateway_ip=self.entry.data[CONF_HOST],
gateway_port=self.entry.data[CONF_PORT],
auto_reconnect=True,
secure_config=SecureConfig(
knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD),
knxkeys_file_path=_knxkeys_file,
),
threaded=True,
)
if _conn_type == CONF_KNX_TUNNELING_TCP_SECURE:
return ConnectionConfig(
connection_type=ConnectionType.TUNNELING_TCP_SECURE,
individual_address=self.entry.data.get(CONF_KNX_TUNNEL_ENDPOINT_IA),
gateway_ip=self.entry.data[CONF_HOST],
gateway_port=self.entry.data[CONF_PORT],
secure_config=SecureConfig(
user_id=self.entry.data.get(CONF_KNX_SECURE_USER_ID),
user_password=self.entry.data.get(CONF_KNX_SECURE_USER_PASSWORD),
device_authentication_password=self.entry.data.get(
CONF_KNX_SECURE_DEVICE_AUTHENTICATION
),
knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD),
knxkeys_file_path=_knxkeys_file,
),
auto_reconnect=True,
threaded=True,
)
if _conn_type == CONF_KNX_ROUTING_SECURE:
return ConnectionConfig(
connection_type=ConnectionType.ROUTING_SECURE,
individual_address=self.entry.data[CONF_KNX_INDIVIDUAL_ADDRESS],
multicast_group=self.entry.data[CONF_KNX_MCAST_GRP],
multicast_port=self.entry.data[CONF_KNX_MCAST_PORT],
local_ip=self.entry.data.get(CONF_KNX_LOCAL_IP),
secure_config=SecureConfig(
backbone_key=self.entry.data.get(CONF_KNX_ROUTING_BACKBONE_KEY),
latency_ms=self.entry.data.get(
CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE
),
knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD),
knxkeys_file_path=_knxkeys_file,
),
auto_reconnect=True,
threaded=True,
)
return ConnectionConfig(
auto_reconnect=True,
individual_address=self.entry.data.get(
CONF_KNX_TUNNEL_ENDPOINT_IA, # may be configured at knxkey upload
),
secure_config=SecureConfig(
knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD),
knxkeys_file_path=_knxkeys_file,
),
threaded=True,
)
def connection_state_changed_cb(self, state: XknxConnectionState) -> None:
"""Call invoked after a KNX connection state change was received."""
self.connected = state == XknxConnectionState.CONNECTED
for device in self.xknx.devices:
device.after_update()
def telegram_received_cb(self, telegram: Telegram) -> None:
"""Call invoked after a KNX telegram was received."""
# Not all telegrams have serializable data.
data: int | tuple[int, ...] | None = None
value = None
if (
isinstance(telegram.payload, (GroupValueWrite, GroupValueResponse))
and telegram.payload.value is not None
and isinstance(
telegram.destination_address, (GroupAddress, InternalGroupAddress)
)
):
data = telegram.payload.value.value
if transcoder := (
self.group_address_transcoder.get(telegram.destination_address)
or next(
(
_transcoder
for _filter, _transcoder in self._address_filter_transcoder.items()
if _filter.match(telegram.destination_address)
),
None,
)
):
try:
value = transcoder.from_knx(telegram.payload.value)
except (ConversionError, CouldNotParseTelegram) as err:
_LOGGER.warning(
(
"Error in `knx_event` at decoding type '%s' from"
" telegram %s\n%s"
),
transcoder.__name__,
telegram,
err,
)
self.hass.bus.async_fire(
"knx_event",
{
"data": data,
"destination": str(telegram.destination_address),
"direction": telegram.direction.value,
"value": value,
"source": str(telegram.source_address),
"telegramtype": telegram.payload.__class__.__name__,
},
)
def register_event_callback(self) -> TelegramQueue.Callback:
"""Register callback for knx_event within XKNX TelegramQueue."""
address_filters = []
for filter_set in self.config_yaml[CONF_EVENT]:
_filters = list(map(AddressFilter, filter_set[KNX_ADDRESS]))
address_filters.extend(_filters)
if (dpt := filter_set.get(CONF_TYPE)) and (
transcoder := DPTBase.parse_transcoder(dpt)
):
self._address_filter_transcoder.update(
dict.fromkeys(_filters, transcoder)
)
return self.xknx.telegram_queue.register_telegram_received_cb(
self.telegram_received_cb,
address_filters=address_filters,
group_addresses=[],
match_for_outgoing=True,
)

View File

@@ -1,4 +1,4 @@
"""Support for KNX/IP binary sensors."""
"""Support for KNX binary sensor entities."""
from __future__ import annotations
@@ -25,7 +25,6 @@ from homeassistant.helpers.entity_platform import (
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import ConfigType
from . import KNXModule
from .const import (
ATTR_COUNTER,
ATTR_SOURCE,
@@ -39,6 +38,7 @@ from .const import (
KNX_MODULE_KEY,
)
from .entity import KnxUiEntity, KnxUiEntityPlatformController, KnxYamlEntity
from .knx_module import KNXModule
from .storage.const import CONF_ENTITY, CONF_GA_PASSIVE, CONF_GA_SENSOR, CONF_GA_STATE

View File

@@ -1,4 +1,4 @@
"""Support for KNX/IP buttons."""
"""Support for KNX button entities."""
from __future__ import annotations
@@ -11,9 +11,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import ConfigType
from . import KNXModule
from .const import CONF_PAYLOAD_LENGTH, KNX_ADDRESS, KNX_MODULE_KEY
from .entity import KnxYamlEntity
from .knx_module import KNXModule
async def async_setup_entry(

View File

@@ -1,4 +1,4 @@
"""Support for KNX/IP climate devices."""
"""Support for KNX climate entities."""
from __future__ import annotations
@@ -37,9 +37,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import ConfigType
from . import KNXModule
from .const import CONTROLLER_MODES, CURRENT_HVAC_ACTIONS, KNX_MODULE_KEY
from .entity import KnxYamlEntity
from .knx_module import KNXModule
from .schema import ClimateSchema
ATTR_COMMAND_VALUE = "command_value"

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