Compare commits

...

134 Commits

Author SHA1 Message Date
Franck Nijhof 0f3fb2eb85 2024.1.5 (#108502) 2024-01-20 19:58:13 +01:00
Franck Nijhof 4c4dc6a086 Pin pandas to 2.1.4 (#108509) 2024-01-20 18:09:33 +01:00
Franck Nijhof 71370f65d5 Bump version to 2024.1.5 2024-01-20 15:51:05 +01:00
Erik Montnemery ea8fc64dd6 Fix empty files included by !include_dir_named (#108489)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-01-20 15:50:50 +01:00
Steven Looman fb700cc847 Bump async-upnp-client to 0.38.1 (#108382) 2024-01-20 15:50:45 +01:00
Franck Nijhof 6e6a5ff52c 2024.1.4 (#108379) 2024-01-19 20:21:12 +01:00
Franck Nijhof 43f1c0927f Revert "Add debugging to assist in debugging already configured error (#108134)"
This reverts commit d2feee86b7.
2024-01-19 19:26:37 +01:00
Franck Nijhof 916e5de9d1 Bump version to 2024.1.4 2024-01-19 15:44:20 +01:00
puddly e2a6097141 Bump ZHA dependency zigpy to 0.60.6 (#108266)
Bump zigpy to 0.60.6
2024-01-19 15:43:02 +01:00
Robert Svensson 005af2eb4c Bump aiounifi to v69 to improve websocket logging (#108265) 2024-01-19 15:42:57 +01:00
Erwin Douna 59e12ad0c1 Bump PyTado to 0.17.4 (#108255)
Bump to 17.4
2024-01-19 15:42:54 +01:00
starkillerOG 6ecb562a80 Bump reolink_aio to 0.8.7 (#108248) 2024-01-19 15:42:50 +01:00
starkillerOG da5d4fe4ae Use cache update for WIFI blinds (#108224) 2024-01-19 15:42:47 +01:00
John Allen 901b7b6278 Send target temp to Shelly TRV in F when needed (#108188) 2024-01-19 15:42:44 +01:00
Allen Porter d2feee86b7 Add debugging to assist in debugging already configured error (#108134) 2024-01-19 15:42:41 +01:00
cnico 5521ab0b35 Bump flipr-api to 1.5.1 (#108130)
Flipr-api version update for resolution of issue https://github.com/home-assistant/core/issues/105778
2024-01-19 15:42:38 +01:00
Aaron Bach 7fb2a8a3cd Bump aioridwell to 2024.01.0 (#108126) 2024-01-19 15:42:34 +01:00
puddly 304b950f1a Speed up ZHA initialization and improve startup responsiveness (#108103)
* Limit concurrency of startup traffic to allow for interactive usage

* Drop `retryable_req`, we already have request retrying

* Oops, `min` -> `max`

* Add a comment describing why `async_initialize` is not concurrent

* Fix existing unit tests

* Break out fetching mains state into its own function to unit test
2024-01-19 15:41:45 +01:00
Joost Lekkerkerker 99f9f0205a Use compat for supported features in media player (#108102) 2024-01-19 15:15:09 +01:00
Robert Resch 497d2f5677 Bump Jinja2 to 3.1.3 (#108082) 2024-01-19 15:15:06 +01:00
Marcel van der Veldt 7fee6c5279 Fix turning on the light with a specific color (#108080) 2024-01-19 15:15:03 +01:00
Cody C 70492a80cc Fix malformed user input error on MJPEG config flow (#108058) 2024-01-19 15:14:59 +01:00
Shay Levy ed31adc6db Fix Shelly Gen1 entity description restore (#108052)
* Fix Shelly Gen1 entity description restore

* Update tests/components/shelly/test_sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2024-01-19 15:14:56 +01:00
Brett Adams 9c6f87dd11 Improve coordinator logic in Tessie to allow sleep (#107988)
* Poll status before state

* Tests
2024-01-19 15:14:52 +01:00
G Johansson 507cccdd53 Don't load entities for docker virtual ethernet interfaces in System Monitor (#107966) 2024-01-19 15:14:07 +01:00
puddly 8a3eb149b7 Reload ZHA only a single time when the connection is lost multiple times (#107963)
* Reload only a single time when the connection is lost multiple times

* Ignore when reset task finishes, allow only one reset per `ZHAGateway`
2024-01-19 15:14:04 +01:00
Simone Chemelli 20b88e30f5 Update sleep period for Shelly devices with buggy fw (#107961)
* update sleep period for Shelly devices with buggy fw

* code quality

* update model list

* add test

* Apply review comments

* fix test

* use costant
2024-01-19 15:14:01 +01:00
Arie Catsman 9551ff31ec Bump pyenphase to 1.17.0 (#107950) 2024-01-19 15:13:58 +01:00
G Johansson 488acc3252 Fix duplicate unique id in System Monitor (again) (#107947)
Fix duplicate unique id in System Monitor
2024-01-19 15:13:55 +01:00
G Johansson 80387be061 Skip disk types in System Monitor (#107943)
* Skip disk types in System Monitor

* change back
2024-01-19 15:13:52 +01:00
J. Nick Koston c0c9fb0f00 Bump aiohomekit to 3.1.3 (#107929)
changelog: https://github.com/Jc2k/aiohomekit/compare/3.1.2...3.1.3

fixes maybe #97888
2024-01-19 15:13:49 +01:00
Sid e2ef889687 Bump openwebifpy to 4.2.1 (#107894) 2024-01-19 15:13:45 +01:00
Pedro Lamas 5c99c6e823 Fix loading empty yaml files with include_dir_named (#107853) 2024-01-19 15:13:42 +01:00
Arie Catsman a8be7c27ad Bump Pyenphase to 1.16.0 (#107719) 2024-01-19 15:13:39 +01:00
Paarth Shah 596f19055e Fix MatrixBot not resolving room aliases per-command (#106347) 2024-01-19 15:13:32 +01:00
Franck Nijhof 99ee57aefc 2024.1.3 (#107883) 2024-01-12 18:01:20 +01:00
Franck Nijhof 9c6cb5347c Bump version to 2024.1.3 2024-01-12 16:10:51 +01:00
Malte Franken 51c75b020d Bump aio_geojson_generic_client to 0.4 (#107866) 2024-01-12 16:09:18 +01:00
Kevin Worrel 061d2d3ccf Fix for exception in screenlogic.set_color_mode (#107850) 2024-01-12 16:09:12 +01:00
Franck Nijhof 97b596a00d Fix Tailwind cover stuck in closing state (#107827) 2024-01-12 16:09:08 +01:00
J. Nick Koston e3a44e499c Bump bluetooth deps (#107816) 2024-01-12 16:08:26 +01:00
Ido Flatow 48766c08e0 Fix switcher kis logging incorrect property for device's name (#107775)
* use of incorrect property for device's name

* Update switch.py according to Ruff formatter
2024-01-12 16:04:20 +01:00
Christopher Bailey f7ad7c4235 Rework events for UniFi Protect (#107771) 2024-01-12 16:04:17 +01:00
Christopher Bailey 38f9fd5734 Bump pyunifiprotect to 4.23.2 (#107769) 2024-01-12 16:04:13 +01:00
Christopher Bailey 05964d6bad Bump pyunifiprotect to 4.23.1 (#107758) 2024-01-12 16:04:10 +01:00
J. Nick Koston 70d1e6a270 Fix ld2410_ble not being able to setup because it has a stale connection (#107754) 2024-01-12 16:04:07 +01:00
Eugene Tiutiunnyk 28da1ac69e Fix Mac address check in kef integration (#107746)
Fix the check for Mac address in kef integration (#106072)

It might be due to an update of `getmac` dependency in some case the mac
was resolved to "00:00:00:00:00:00" instead of the anticipated `None`.
With that the original bug #47678 where a duplicated entity would be
created in case of HA is restarted while the KEF speaker is offline
came back. The PR #52902 was applied back in time to fix that issue.
Now, this change is a continuation of the previous efforts. The solution
was tested for about two months and it does address the bug with
creating duplicated entities in case of KEF speakers being offline.
2024-01-12 16:04:04 +01:00
Simone Chemelli 504e4a7923 Fix "not-logged" edge cases for Comelit VEDO (#107741) 2024-01-12 16:04:01 +01:00
Jan Bouwhuis 40547974fb Fix mqtt text text min max config params can not be equal (#107738)
Fix mqtt text text min max kan not be equal
2024-01-12 16:03:57 +01:00
YogevBokobza b87bbd1529 Bump aioswitcher to 3.4.1 (#107730)
* switcher: added support for device_key logic included in aioswitcher==3.4.1

* switcher: small fix

* switcher: after lint

* switcher: fix missing device_key in tests

* remove device_key function

* fix missing device_key in tests
2024-01-12 16:03:54 +01:00
Jan-Philipp Benecke d89659f196 Allow configuration of min_gradient from UI to be negative in Trend (#107720)
Allow configuration of min_gradient to be negative from UI
2024-01-12 16:03:50 +01:00
Martin Hjelmare 00b899ca3c Fix cloud tts loading (#107714) 2024-01-12 16:03:45 +01:00
J. Nick Koston 765c520d7a Clamp tplink color temp to valid range (#107695) 2024-01-12 15:59:05 +01:00
Maciej Bieniek 2a46f201cb Fix device_class type for Shelly Gen1 sleeping sensors (#107683) 2024-01-12 15:59:01 +01:00
Jan Bouwhuis 7f8a157788 Redact sensitive data in alexa debug logging (#107676)
* Redact sensitive data in alexa debug logging

* Add wrappers to diagnostics module

* Test http api log is redacted
2024-01-12 15:58:58 +01:00
Jan Bouwhuis f993e923a3 Fix invalid alexa climate or water_heater state report with double listed targetSetpoint (#107673) 2024-01-12 15:58:55 +01:00
G Johansson 3386e0e766 Fix duplicated resource issue in System Monitor (#107671)
* Fix duplicated resource issue

* Only slug the argument
2024-01-12 15:58:52 +01:00
Jonas Fors Lellky 0705be607f Set max and min temp for flexit_bacnet climate entity (#107665)
107655: Set max and min temp for flexit_bacnet climate entity
2024-01-12 15:58:49 +01:00
Erik Montnemery 5b84e50dc0 Prevent overriding cached attribute as property (#107657)
* Prevent overriding cached attribute as property

* Remove debug
2024-01-12 15:58:46 +01:00
Marc Mueller 26da7402a2 Fix tplink_lte setup (#107642) 2024-01-12 15:58:42 +01:00
Erwin Douna f1fc5abbc2 Fix Tado unique mobile device dispatcher (#107631)
* Add unique home ID device dispatch

* Adding fixture for new setup

* Minor refactor work

* Add check for unlinked to different homes

* If the interface returns an error

* Proper error handling

* Feedback fixes

* Comments for error in client

* Typo

* Update homeassistant/components/tado/__init__.py

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

* Update homeassistant/components/tado/__init__.py

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

* Update devices fix standard

* Dispatch out of loop

* Update dispatcher

* Clean up

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-01-12 15:58:39 +01:00
Shay Levy 644a823c45 Bump aioshelly to 7.1.0 (#107593) 2024-01-12 15:58:35 +01:00
elmurato fcab683cc0 Bump mcstatus to v11.1.1 (#107546)
* Bump mcstatus to 11.1.0

* Bump mcstatus to v11.1.1
2024-01-12 15:58:32 +01:00
starkillerOG 7bdabce68f Bump reolink_aio to 0.8.6 (#107541) 2024-01-12 15:58:29 +01:00
mkmer cf1a528b7a Bump blinkpy to 0.22.5 (#107537)
bump blinkpy 0.22.5
2024-01-12 15:58:26 +01:00
Erik Montnemery f18ab5e1cc Don't include position in binary valve attributes (#107531) 2024-01-12 15:58:23 +01:00
nic bee76db1c3 Retry zoneminder connection setup (#107519)
* zoneminder setup retry connection

Makes ZM setup be async for enabling connection retry attempts

This also requires zm-py version bump v0.5.4 as that
dependency was patched in conjunction to resolve this issue

Closes #105271

Signed-off-by: Nic Boet <nic@boet.cc>

* ruff format

Signed-off-by: Nic Boet <nic@boet.cc>

* ruff fixes

Signed-off-by: Nic Boet <nic@boet.cc>

* RequestsConnectionError

Signed-off-by: Nic Boet <nic@boet.cc>

* revert async changes

Signed-off-by: Nic Boet <nic@boet.cc>

---------

Signed-off-by: Nic Boet <nic@boet.cc>
2024-01-12 15:58:19 +01:00
nic 652fa7d693 Bump zm-py version to v0.5.3 for zoneminder (#107331)
zm-py version bump for zoneminder

Signed-off-by: Nic Boet <nic@boet.cc>
2024-01-12 15:58:14 +01:00
Manuel Rüger 73ba77deb6 Fix Luftdaten sensor id string (#107506)
Luftdaten: fix sensor id string
2024-01-12 15:54:24 +01:00
Marc Mueller 66307c5acb Fix asyncio.gather call (#107500) 2024-01-12 15:54:21 +01:00
Simone Chemelli 86822018d8 Fix reauth flow for Comelit VEDO (#107461) 2024-01-12 15:54:17 +01:00
Cyrill Raccaud b333be8945 Fix Swiss public transport initial data for attributes (#107452)
faster initial data for attributes
2024-01-12 15:54:14 +01:00
Joost Lekkerkerker a58483f93c Remove name from faa_delays (#107418) 2024-01-12 15:54:11 +01:00
G Johansson 7c06f05108 Handle OSError during setup for System Monitor (#107396)
* Handle OSError during setup for System Monitor

* Clean string copy

* debug

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-01-12 15:54:08 +01:00
G Johansson 175e07fe3b Fix language flavors in holiday (#107392) 2024-01-12 15:54:05 +01:00
Matthias Alphart 9bc0217738 Fix KNX telegram device trigger not firing after integration reload (#107388) 2024-01-12 15:54:02 +01:00
mkmer 53ab892575 Reduce polling rate in Blink (#107386) 2024-01-12 15:53:58 +01:00
David Bonnes 409a254fe5 Fix evohome high_precision temps not retreived consistently (#107366)
* initial commit

* doctweak

* remove hint

* doctweak
2024-01-12 15:53:56 +01:00
Ben Morton 54e62b4095 Add support for the Spotify DJ (#107268)
* Add support for the Spotify DJ playlist by mocking the playlist response
Add error handling for playlist lookup to ensure it doesn't break current playback state loading

* Run linters
Add exception type to playlist lookup error handling

* Fix typo in comment

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-01-12 15:53:53 +01:00
Cyrill Raccaud 4e991388fb Fix missing unique_id for spt integration (#107087)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-01-12 15:53:50 +01:00
vexofp 1acae5a62d Prevent toggle from calling stop on covers which do not support it (#106848)
* Prevent toggle from calling stop on covers which do not support it

* Update homeassistant/components/cover/__init__.py

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2024-01-12 15:53:46 +01:00
Arie Catsman 9fe351f363 Catch missing inverter in Enphase Envoy (#106730)
* bug: prevent invalid key when empty invereter arrays is returned.

Some envoy fw versions return an empty inverter array
every 4 hours when no production is taking place.
Prevent collection failure due to this as other data
seems fine. Inveretrs will show unknown during this cycle.

* refactor: replace try/catch with test and make warning debug

* Update homeassistant/components/enphase_envoy/sensor.py

* Update homeassistant/components/enphase_envoy/sensor.py

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-01-12 15:53:43 +01:00
Brandon Rothweiler f9dacedf0f Add diagnostics to A. O. Smith integration (#106343)
* Add diagnostics to A. O. Smith integration

* Bump py-aosmith to 1.0.4

* remove redactions from test fixture
2024-01-12 15:53:37 +01:00
Franck Nijhof 4eddbe7b47 2024.1.2 (#107365) 2024-01-06 13:17:47 +01:00
Franck Nijhof cab833160d Bump version to 2024.1.2 2024-01-06 12:25:05 +01:00
David F. Mulcahey 003d2be477 Fix assertion error when unloading ZHA with pollable entities (#107311) 2024-01-06 12:24:35 +01:00
Sid 5a01b55fd1 enigma2: fix exception when device in deep sleep, fix previous track (#107296)
enigma2: fix exception when device in deep sleep; previous track
2024-01-06 12:24:32 +01:00
Shay Levy 0dbb4105bc Fix Shelly missing Gen value for older devices (#107294) 2024-01-06 12:24:29 +01:00
Joost Lekkerkerker 5ff6284e0f Fix passing correct location id to streamlabs water (#107291) 2024-01-06 12:24:25 +01:00
Aidan Timson 97674cee88 Fix support for play/pause functionality in System Bridge (#103423)
Fix support for play/pause functionality
2024-01-06 12:24:21 +01:00
Franck Nijhof 24a8a512d6 2024.1.1 (#107239) 2024-01-05 16:25:49 +01:00
Franck Nijhof 658f1cf5c5 Bump version to 2024.1.1 2024-01-05 13:01:35 +01:00
Raman Gupta d012817190 Bump zwave-js-server-python to 0.55.3 (#107225)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-01-05 13:01:06 +01:00
Joakim Sørensen 056701d218 Use supported_features_compat in update.install service (#107224) 2024-01-05 13:01:03 +01:00
Petru Paler c3963b26e7 Fix entity property cache creation arguments (#107221) 2024-01-05 13:00:59 +01:00
tronikos 4ade5e46d9 Disable IPv6 in the opower integration to fix AEP utilities (#107203) 2024-01-05 13:00:55 +01:00
Brett Adams c242dcd1f2 Hotfix cache logic bug in Tessie (#107187) 2024-01-05 13:00:52 +01:00
Michael 4e126d68b7 Fix switch states in AVM FRITZ!Box Tools (#107183) 2024-01-05 13:00:49 +01:00
Erwin Douna d7e1a4fa20 Bump to PyTado 0.17.3 (#107181) 2024-01-05 13:00:46 +01:00
J. Nick Koston 3215dfee6d Bump aiohomekit to 3.1.2 (#107177) 2024-01-05 13:00:43 +01:00
Matt Emerick-Law c78d691d30 Bump Orvibo to 1.1.2 (#107162)
* Bump python-orvibo version

Fixes https://github.com/home-assistant/core/issues/106923

* Add version number

* Remove version

* Bump python-orvibo version
2024-01-05 13:00:39 +01:00
Bram Kragten 04bf569308 Update frontend to 20240104.0 (#107155) 2024-01-05 13:00:36 +01:00
Erik Montnemery b8576b8091 Include deprecated constants in wildcard imports (#107114) 2024-01-05 13:00:33 +01:00
J. Nick Koston a7aa5c0e52 Bump habluetooth to 2.0.2 (#107097) 2024-01-05 13:00:29 +01:00
J. Nick Koston d600b76801 Fix missing backwards compatibility layer for water_heater supported_features (#107091) 2024-01-05 13:00:26 +01:00
Erik Montnemery c56d118e8b Deduplicate handling of duplicated constants (#107074)
* Deduplicate handling of duplicated constants

* Use DeprecatedConstant + DeprecatedConstantEnum

* Fixup

* Remove test cases with unnamed tuples
2024-01-05 13:00:23 +01:00
Robert Resch 80b45edb2e Fix mobile_app cloudhook creation (#107068) 2024-01-05 13:00:20 +01:00
Joost Lekkerkerker 5529a85a2b Fix data access in streamlabs water (#107062)
* Fix data access in streamlabs water

* Fix data access in streamlabs water
2024-01-05 13:00:17 +01:00
Joost Lekkerkerker e2acc70128 Use async_register in streamlabswater (#107060) 2024-01-05 13:00:14 +01:00
J. Nick Koston 427077a4c9 Fix missing backwards compatiblity layer for humidifier supported_features (#107026)
fixes #107018
2024-01-05 13:00:10 +01:00
Maciej Bieniek 8c9875c3cc Get Shelly RPC device gen from config entry data (#107019)
Use gen from config entry data
2024-01-05 13:00:07 +01:00
Sid ce5455fefc Bump openwebifpy to 4.0.4 (#107000) 2024-01-05 13:00:04 +01:00
Ståle Storø Hauknes aef129afaf Close stale connections (Airthings BLE) (#106748)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-01-05 13:00:01 +01:00
Sid 1c94a94ba2 bump openwebifpy to 4.0.3 (#106593) 2024-01-05 12:59:56 +01:00
Patrick Frazer d87baba96f Bump dropmqttapi to 1.0.2 (#106978) 2024-01-05 12:57:58 +01:00
Joe Neuman 5a0997bac0 Fix qBittorrent torrent count when empty (#106903)
* Fix qbittorrent torrent cound when empty

* lint fix

* Change based on comment
2024-01-05 12:57:55 +01:00
Marc Mueller e70af204ee Enable strict typing for airthings_ble (#106815) 2024-01-05 12:57:50 +01:00
Franck Nijhof fb0cc6c5d0 2024.1.0 (#106970) 2024-01-03 18:15:49 +01:00
Franck Nijhof 15cecbd4a4 Bump version to 2024.1.0 2024-01-03 17:13:22 +01:00
Franck Nijhof 8cf47c4925 Bump version to 2024.1.0b8 2024-01-03 15:29:59 +01:00
Bram Kragten cd8d95a04d Update frontend to 20240103.3 (#106963) 2024-01-03 15:29:51 +01:00
Jonas Fors Lellky 015752ff11 Set precision to halves in flexit_bacnet (#106959)
flexit_bacnet: set precision to halves for target temperature
2024-01-03 15:29:48 +01:00
jan iversen 9d697c5026 Only set precision in modbus if not configured. (#106952)
Only set precision if not configured.
2024-01-03 15:29:42 +01:00
Bram Kragten 4595c3edaa Update frontend to 20240103.1 (#106948) 2024-01-03 15:29:38 +01:00
Robert Resch e745542431 Fix creating cloud hook twice for mobile_app (#106945) 2024-01-03 15:29:33 +01:00
Franck Nijhof 2be72fd891 Bump version to 2024.1.0b7 2024-01-03 11:35:43 +01:00
Bram Kragten 2b43f5fcda Update frontend to 20240103.0 (#106942) 2024-01-03 11:35:35 +01:00
Erwin Douna 3295722e70 Change Tado deprecation version to 2024.7.0 (#106938)
Change version to 2024.7.0
2024-01-03 11:35:30 +01:00
Franck Nijhof f98bbf88b1 Bump version to 2024.1.0b6 2024-01-03 09:56:28 +01:00
Michael 0226b3f10c Remove group_members from significant attributes in media player (#106916) 2024-01-03 09:55:55 +01:00
Jan-Philipp Benecke 5986967db7 Avoid triggering ping device tracker home after restore (#106913) 2024-01-03 09:55:52 +01:00
Michael Hansen 95ef2dd7f9 Bump intents to 2024.1.2 (#106909) 2024-01-03 09:55:49 +01:00
Erwin Douna 527d9fbb6b Add try-catch for invalid auth to Tado (#106774)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-01-03 09:55:45 +01:00
Michael 5eb1073b4a Apply late review comments on media player (#106727) 2024-01-03 09:55:41 +01:00
243 changed files with 3400 additions and 1095 deletions
+1
View File
@@ -49,6 +49,7 @@ homeassistant.components.aftership.*
homeassistant.components.air_quality.*
homeassistant.components.airly.*
homeassistant.components.airnow.*
homeassistant.components.airthings_ble.*
homeassistant.components.airvisual.*
homeassistant.components.airvisual_pro.*
homeassistant.components.airzone.*
+1 -1
View File
@@ -1550,7 +1550,7 @@ build.json @home-assistant/supervisor
/tests/components/zodiac/ @JulienTant
/homeassistant/components/zone/ @home-assistant/core
/tests/components/zone/ @home-assistant/core
/homeassistant/components/zoneminder/ @rohankapoorcom
/homeassistant/components/zoneminder/ @rohankapoorcom @nabbi
/homeassistant/components/zwave_js/ @home-assistant/z-wave
/tests/components/zwave_js/ @home-assistant/z-wave
/homeassistant/components/zwave_me/ @lawfulchaos @Z-Wave-Me @PoltoS
@@ -4,7 +4,8 @@ from __future__ import annotations
from datetime import timedelta
import logging
from airthings_ble import AirthingsBluetoothDeviceData
from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice
from bleak_retry_connector import close_stale_connections_by_address
from homeassistant.components import bluetooth
from homeassistant.config_entries import ConfigEntry
@@ -30,6 +31,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
is_metric = hass.config.units is METRIC_SYSTEM
assert address is not None
await close_stale_connections_by_address(address)
ble_device = bluetooth.async_ble_device_from_address(hass, address)
if not ble_device:
@@ -37,13 +40,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
f"Could not find Airthings device with address {address}"
)
async def _async_update_method():
async def _async_update_method() -> AirthingsDevice:
"""Get data from Airthings BLE."""
ble_device = bluetooth.async_ble_device_from_address(hass, address)
airthings = AirthingsBluetoothDeviceData(_LOGGER, elevation, is_metric)
try:
data = await airthings.update_device(ble_device)
data = await airthings.update_device(ble_device) # type: ignore[arg-type]
except Exception as err:
raise UpdateFailed(f"Unable to fetch data: {err}") from err
@@ -24,6 +24,7 @@ from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -52,12 +53,6 @@ if TYPE_CHECKING:
else:
from homeassistant.backports.functools import cached_property
# As we import constants of the cost module here, we need to add the following
# functions to check for deprecated constants again
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
_LOGGER: Final = logging.getLogger(__name__)
SCAN_INTERVAL: Final = timedelta(seconds=30)
@@ -249,3 +244,13 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
ATTR_CHANGED_BY: self.changed_by,
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
}
# As we import constants of the const module here, we need to add the following
# functions to check for deprecated constants again
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -5,6 +5,7 @@ from typing import Final
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -60,10 +61,6 @@ _DEPRECATED_SUPPORT_ALARM_ARM_VACATION: Final = DeprecatedConstantEnum(
AlarmControlPanelEntityFeature.ARM_VACATION, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
CONDITION_TRIGGERED: Final = "is_triggered"
CONDITION_DISARMED: Final = "is_disarmed"
CONDITION_ARMED_HOME: Final = "is_armed_home"
@@ -71,3 +68,10 @@ CONDITION_ARMED_AWAY: Final = "is_armed_away"
CONDITION_ARMED_NIGHT: Final = "is_armed_night"
CONDITION_ARMED_VACATION: Final = "is_armed_vacation"
CONDITION_ARMED_CUSTOM_BYPASS: Final = "is_armed_custom_bypass"
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+5 -4
View File
@@ -15,6 +15,9 @@ from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.storage import Store
from homeassistant.util import dt as dt_util
from .const import STORAGE_ACCESS_TOKEN, STORAGE_REFRESH_TOKEN
from .diagnostics import async_redact_lwa_params
_LOGGER = logging.getLogger(__name__)
LWA_TOKEN_URI = "https://api.amazon.com/auth/o2/token"
@@ -24,8 +27,6 @@ PREEMPTIVE_REFRESH_TTL_IN_SECONDS = 300
STORAGE_KEY = "alexa_auth"
STORAGE_VERSION = 1
STORAGE_EXPIRE_TIME = "expire_time"
STORAGE_ACCESS_TOKEN = "access_token"
STORAGE_REFRESH_TOKEN = "refresh_token"
class Auth:
@@ -56,7 +57,7 @@ class Auth:
}
_LOGGER.debug(
"Calling LWA to get the access token (first time), with: %s",
json.dumps(lwa_params),
json.dumps(async_redact_lwa_params(lwa_params)),
)
return await self._async_request_new_token(lwa_params)
@@ -133,7 +134,7 @@ class Auth:
return None
response_json = await response.json()
_LOGGER.debug("LWA response body : %s", response_json)
_LOGGER.debug("LWA response body : %s", async_redact_lwa_params(response_json))
access_token: str = response_json["access_token"]
refresh_token: str = response_json["refresh_token"]
+10 -6
View File
@@ -1112,13 +1112,17 @@ class AlexaThermostatController(AlexaCapability):
"""Return what properties this entity supports."""
properties = [{"name": "thermostatMode"}]
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE:
if self.entity.domain == climate.DOMAIN:
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
properties.append({"name": "lowerSetpoint"})
properties.append({"name": "upperSetpoint"})
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE:
properties.append({"name": "targetSetpoint"})
elif (
self.entity.domain == water_heater.DOMAIN
and supported & water_heater.WaterHeaterEntityFeature.TARGET_TEMPERATURE
):
properties.append({"name": "targetSetpoint"})
if supported & water_heater.WaterHeaterEntityFeature.TARGET_TEMPERATURE:
properties.append({"name": "targetSetpoint"})
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
properties.append({"name": "lowerSetpoint"})
properties.append({"name": "upperSetpoint"})
return properties
def properties_proactively_reported(self) -> bool:
+3
View File
@@ -90,6 +90,9 @@ API_THERMOSTAT_PRESETS = {climate.PRESET_ECO: "ECO"}
# we add PRESET_MODE_NA if a fan / humidifier has only one preset_mode
PRESET_MODE_NA = "-"
STORAGE_ACCESS_TOKEN = "access_token"
STORAGE_REFRESH_TOKEN = "refresh_token"
class Cause:
"""Possible causes for property changes.
@@ -0,0 +1,34 @@
"""Diagnostics helpers for Alexa."""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import callback
STORAGE_ACCESS_TOKEN = "access_token"
STORAGE_REFRESH_TOKEN = "refresh_token"
TO_REDACT_LWA = {
CONF_CLIENT_ID,
CONF_CLIENT_SECRET,
STORAGE_ACCESS_TOKEN,
STORAGE_REFRESH_TOKEN,
}
TO_REDACT_AUTH = {"correlationToken", "token"}
@callback
def async_redact_lwa_params(lwa_params: dict[str, str]) -> dict[str, str]:
"""Redact lwa_params."""
return async_redact_data(lwa_params, TO_REDACT_LWA)
@callback
def async_redact_auth_data(mapping: Mapping[Any, Any]) -> dict[str, str]:
"""React auth data."""
return async_redact_data(mapping, TO_REDACT_AUTH)
@@ -144,7 +144,6 @@ async def async_api_accept_grant(
Async friendly.
"""
auth_code: str = directive.payload["grant"]["code"]
_LOGGER.debug("AcceptGrant code: %s", auth_code)
if config.supports_auth:
await config.async_accept_grant(auth_code)
+12 -2
View File
@@ -25,6 +25,7 @@ from .const import (
CONF_LOCALE,
EVENT_ALEXA_SMART_HOME,
)
from .diagnostics import async_redact_auth_data
from .errors import AlexaBridgeUnreachableError, AlexaError
from .handlers import HANDLERS
from .state_report import AlexaDirective
@@ -149,12 +150,21 @@ class SmartHomeView(HomeAssistantView):
user: User = request["hass_user"]
message: dict[str, Any] = await request.json()
_LOGGER.debug("Received Alexa Smart Home request: %s", message)
if _LOGGER.isEnabledFor(logging.DEBUG):
_LOGGER.debug(
"Received Alexa Smart Home request: %s",
async_redact_auth_data(message),
)
response = await async_handle_message(
hass, self.smart_home_config, message, context=core.Context(user_id=user.id)
)
_LOGGER.debug("Sending Alexa Smart Home response: %s", response)
if _LOGGER.isEnabledFor(logging.DEBUG):
_LOGGER.debug(
"Sending Alexa Smart Home response: %s",
async_redact_auth_data(response),
)
return b"" if response is None else self.json(response)
@@ -34,6 +34,7 @@ from .const import (
DOMAIN,
Cause,
)
from .diagnostics import async_redact_auth_data
from .entities import ENTITY_ADAPTERS, AlexaEntity, generate_alexa_id
from .errors import AlexaInvalidEndpointError, NoTokenAvailable, RequireRelink
@@ -43,6 +44,8 @@ if TYPE_CHECKING:
_LOGGER = logging.getLogger(__name__)
DEFAULT_TIMEOUT = 10
TO_REDACT = {"correlationToken", "token"}
class AlexaDirective:
"""An incoming Alexa directive."""
@@ -379,7 +382,9 @@ async def async_send_changereport_message(
response_text = await response.text()
if _LOGGER.isEnabledFor(logging.DEBUG):
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
_LOGGER.debug(
"Sent: %s", json.dumps(async_redact_auth_data(message_serialized))
)
_LOGGER.debug("Received (%s): %s", response.status, response_text)
if response.status == HTTPStatus.ACCEPTED:
@@ -533,7 +538,9 @@ async def async_send_doorbell_event_message(
response_text = await response.text()
if _LOGGER.isEnabledFor(logging.DEBUG):
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
_LOGGER.debug(
"Sent: %s", json.dumps(async_redact_auth_data(message_serialized))
)
_LOGGER.debug("Received (%s): %s", response.status, response_text)
if response.status == HTTPStatus.ACCEPTED:
@@ -0,0 +1,39 @@
"""Diagnostics support for A. O. Smith."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from . import AOSmithData
from .const import DOMAIN
TO_REDACT = {
"address",
"city",
"contactId",
"dsn",
"email",
"firstName",
"heaterSsid",
"id",
"lastName",
"phone",
"postalCode",
"registeredOwner",
"serial",
"ssid",
"state",
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
data: AOSmithData = hass.data[DOMAIN][config_entry.entry_id]
all_device_info = await data.client.get_all_device_info()
return async_redact_data(all_device_info, TO_REDACT)
@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aosmith",
"iot_class": "cloud_polling",
"requirements": ["py-aosmith==1.0.1"]
"requirements": ["py-aosmith==1.0.4"]
}
@@ -58,6 +58,7 @@ from homeassistant.helpers import condition
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import (
DeprecatedConstant,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -147,10 +148,6 @@ _DEPRECATED_AutomationTriggerInfo = DeprecatedConstant(
TriggerInfo, "TriggerInfo", "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
@bind_hass
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
@@ -1108,3 +1105,11 @@ def websocket_config(
"config": automation.raw_config,
},
)
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -19,6 +19,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -218,10 +219,6 @@ _DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
BinarySensorDeviceClass.WINDOW, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
# mypy: disallow-any-generics
@@ -303,3 +300,11 @@ class BinarySensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
if (is_on := self.is_on) is None:
return None
return STATE_ON if is_on else STATE_OFF
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -13,7 +13,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = 30
SCAN_INTERVAL = 300
class BlinkUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
+1 -1
View File
@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/blink",
"iot_class": "cloud_polling",
"loggers": ["blinkpy"],
"requirements": ["blinkpy==0.22.4"]
"requirements": ["blinkpy==0.22.5"]
}
@@ -17,9 +17,9 @@
"bleak==0.21.1",
"bleak-retry-connector==3.4.0",
"bluetooth-adapters==0.16.2",
"bluetooth-auto-recovery==1.2.3",
"bluetooth-auto-recovery==1.3.0",
"bluetooth-data-tools==1.19.0",
"dbus-fast==2.21.0",
"habluetooth==2.0.1"
"habluetooth==2.1.0"
]
}
+9 -4
View File
@@ -54,6 +54,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -123,10 +124,6 @@ _DEPRECATED_SUPPORT_STREAM: Final = DeprecatedConstantEnum(
CameraEntityFeature.STREAM, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
RTSP_PREFIXES = {"rtsp://", "rtsps://", "rtmp://"}
DEFAULT_CONTENT_TYPE: Final = "image/jpeg"
@@ -1082,3 +1079,11 @@ async def async_handle_record_service(
duration=service_call.data[CONF_DURATION],
lookback=service_call.data[CONF_LOOKBACK],
)
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+6 -2
View File
@@ -5,6 +5,7 @@ from typing import Final
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -47,6 +48,9 @@ _DEPRECATED_STREAM_TYPE_HLS = DeprecatedConstantEnum(StreamType.HLS, "2025.1")
_DEPRECATED_STREAM_TYPE_WEB_RTC = DeprecatedConstantEnum(StreamType.WEB_RTC, "2025.1")
# Both can be removed if no deprecated constant are in this module anymore
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+11 -6
View File
@@ -28,6 +28,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
make_entity_service_schema,
)
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -141,12 +142,6 @@ SET_TEMPERATURE_SCHEMA = vol.All(
),
)
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
# mypy: disallow-any-generics
@@ -734,3 +729,13 @@ async def async_service_temperature_set(
kwargs[value] = temp
await entity.async_set_temperature(**kwargs)
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+6 -2
View File
@@ -5,6 +5,7 @@ from functools import partial
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -188,6 +189,9 @@ _DEPRECATED_SUPPORT_AUX_HEAT = DeprecatedConstantEnum(
ClimateEntityFeature.AUX_HEAT, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+28 -9
View File
@@ -5,6 +5,7 @@ import asyncio
from collections.abc import Awaitable, Callable
from datetime import datetime, timedelta
from enum import Enum
from typing import cast
from hass_nabucasa import Cloud
import voluptuous as vol
@@ -176,6 +177,22 @@ def async_active_subscription(hass: HomeAssistant) -> bool:
return async_is_logged_in(hass) and not hass.data[DOMAIN].subscription_expired
async def async_get_or_create_cloudhook(hass: HomeAssistant, webhook_id: str) -> str:
"""Get or create a cloudhook."""
if not async_is_connected(hass):
raise CloudNotConnected
if not async_is_logged_in(hass):
raise CloudNotAvailable
cloud: Cloud[CloudClient] = hass.data[DOMAIN]
cloudhooks = cloud.client.cloudhooks
if hook := cloudhooks.get(webhook_id):
return cast(str, hook["cloudhook_url"])
return await async_create_cloudhook(hass, webhook_id)
@bind_hass
async def async_create_cloudhook(hass: HomeAssistant, webhook_id: str) -> str:
"""Create a cloudhook."""
@@ -274,7 +291,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
}
async def _on_start() -> None:
"""Discover platforms."""
"""Handle cloud started after login."""
nonlocal loaded
# Prevent multiple discovery
@@ -282,14 +299,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return
loaded = True
tts_info = {"platform_loaded": tts_platform_loaded}
await async_load_platform(hass, Platform.TTS, DOMAIN, tts_info, config)
await tts_platform_loaded.wait()
# The config entry should be loaded after the legacy tts platform is loaded
# to make sure that the tts integration is setup before we try to migrate
# old assist pipelines in the cloud stt entity.
await hass.config_entries.flow.async_init(DOMAIN, context={"source": "system"})
async def _on_connect() -> None:
@@ -318,6 +327,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
account_link.async_setup(hass)
hass.async_create_task(
async_load_platform(
hass,
Platform.TTS,
DOMAIN,
{"platform_loaded": tts_platform_loaded},
config,
)
)
async_call_later(
hass=hass,
delay=timedelta(hours=STARTUP_REPAIR_DELAY),
@@ -72,6 +72,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
_reauth_entry: ConfigEntry | None
_reauth_host: str
_reauth_port: int
_reauth_type: str
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -109,6 +110,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
)
self._reauth_host = entry_data[CONF_HOST]
self._reauth_port = entry_data.get(CONF_PORT, DEFAULT_PORT)
self._reauth_type = entry_data.get(CONF_TYPE, BRIDGE)
self.context["title_placeholders"] = {"host": self._reauth_host}
return await self.async_step_reauth_confirm()
@@ -127,6 +129,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
{
CONF_HOST: self._reauth_host,
CONF_PORT: self._reauth_port,
CONF_TYPE: self._reauth_type,
}
| user_input,
)
@@ -144,6 +147,7 @@ class ComelitConfigFlow(ConfigFlow, domain=DOMAIN):
CONF_HOST: self._reauth_host,
CONF_PORT: self._reauth_port,
CONF_PIN: user_input[CONF_PIN],
CONF_TYPE: self._reauth_type,
},
)
self.hass.async_create_task(
@@ -81,15 +81,11 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
try:
await self.api.login()
return await self._async_update_system_data()
except exceptions.CannotConnect as err:
_LOGGER.warning("Connection error for %s", self._host)
await self.api.close()
raise UpdateFailed(f"Error fetching data: {repr(err)}") from err
except (exceptions.CannotConnect, exceptions.CannotRetrieveData) as err:
raise UpdateFailed(repr(err)) from err
except exceptions.CannotAuthenticate:
raise ConfigEntryAuthFailed
return {}
@abstractmethod
async def _async_update_system_data(self) -> dict[str, Any]:
"""Class method for updating data."""
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/comelit",
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"requirements": ["aiocomelit==0.7.0"]
"requirements": ["aiocomelit==0.7.3"]
}
@@ -7,5 +7,5 @@
"integration_type": "system",
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": ["hassil==1.5.1", "home-assistant-intents==2023.12.05"]
"requirements": ["hassil==1.5.1", "home-assistant-intents==2024.1.2"]
}
+10 -5
View File
@@ -34,6 +34,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -143,10 +144,6 @@ _DEPRECATED_SUPPORT_SET_TILT_POSITION = DeprecatedConstantEnum(
CoverEntityFeature.SET_TILT_POSITION, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
ATTR_CURRENT_POSITION = "current_position"
ATTR_CURRENT_TILT_POSITION = "current_tilt_position"
ATTR_POSITION = "position"
@@ -484,7 +481,7 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def _get_toggle_function(
self, fns: dict[str, Callable[_P, _R]]
) -> Callable[_P, _R]:
if CoverEntityFeature.STOP | self.supported_features and (
if self.supported_features & CoverEntityFeature.STOP and (
self.is_closing or self.is_opening
):
return fns["stop"]
@@ -493,3 +490,11 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if self._cover_is_last_toggle_direction_open:
return fns["close"]
return fns["open"]
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -6,6 +6,7 @@ from functools import partial
from homeassistant.const import ATTR_GPS_ACCURACY, STATE_HOME # noqa: F401
from homeassistant.core import HomeAssistant
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -57,12 +58,6 @@ from .legacy import ( # noqa: F401
see,
)
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
@bind_hass
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
@@ -83,3 +78,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
await async_setup_legacy_integration(hass, config)
return True
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -9,6 +9,7 @@ from typing import Final
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -44,10 +45,6 @@ _DEPRECATED_SOURCE_TYPE_BLUETOOTH_LE: Final = DeprecatedConstantEnum(
SourceType.BLUETOOTH_LE, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
CONF_SCAN_INTERVAL: Final = "interval_seconds"
SCAN_INTERVAL: Final = timedelta(seconds=12)
@@ -71,3 +68,10 @@ ATTR_CONSIDER_HOME: Final = "consider_home"
ATTR_IP: Final = "ip"
CONNECTED_DEVICE_REGISTERED: Final = "device_tracker_connected_device_registered"
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -8,7 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
"iot_class": "local_push",
"loggers": ["async_upnp_client"],
"requirements": ["async-upnp-client==0.38.0", "getmac==0.9.4"],
"requirements": ["async-upnp-client==0.38.1", "getmac==0.9.4"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
@@ -8,7 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["async-upnp-client==0.38.0"],
"requirements": ["async-upnp-client==0.38.1"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",
@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/drop_connect",
"iot_class": "local_push",
"mqtt": ["drop_connect/discovery/#"],
"requirements": ["dropmqttapi==1.0.1"]
"requirements": ["dropmqttapi==1.0.2"]
}
@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/enigma2",
"iot_class": "local_polling",
"loggers": ["openwebif"],
"requirements": ["openwebifpy==4.0.2"]
"requirements": ["openwebifpy==4.2.1"]
}
@@ -1,6 +1,7 @@
"""Support for Enigma2 media players."""
from __future__ import annotations
from aiohttp.client_exceptions import ClientConnectorError
from openwebif.api import OpenWebIfDevice
from openwebif.enums import RemoteControlCodes
import voluptuous as vol
@@ -20,6 +21,7 @@ from homeassistant.const import (
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -96,9 +98,13 @@ async def async_setup_platform(
source_bouquet=config.get(CONF_SOURCE_BOUQUET),
)
async_add_entities(
[Enigma2Device(config[CONF_NAME], device, await device.get_about())]
)
try:
about = await device.get_about()
except ClientConnectorError as err:
await device.close()
raise PlatformNotReady from err
async_add_entities([Enigma2Device(config[CONF_NAME], device, about)])
class Enigma2Device(MediaPlayerEntity):
@@ -169,8 +175,8 @@ class Enigma2Device(MediaPlayerEntity):
await self._device.send_remote_control_action(RemoteControlCodes.CHANNEL_UP)
async def async_media_previous_track(self) -> None:
"""Send next track command."""
self._device.send_remote_control_action(RemoteControlCodes.CHANNEL_DOWN)
"""Send previous track command."""
await self._device.send_remote_control_action(RemoteControlCodes.CHANNEL_DOWN)
async def async_mute_volume(self, mute: bool) -> None:
"""Mute or unmute."""
@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"requirements": ["pyenphase==1.15.2"],
"requirements": ["pyenphase==1.17.0"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."
@@ -479,10 +479,20 @@ class EnvoyInverterEntity(EnvoySensorBaseEntity):
)
@property
def native_value(self) -> datetime.datetime | float:
def native_value(self) -> datetime.datetime | float | None:
"""Return the state of the sensor."""
inverters = self.data.inverters
assert inverters is not None
# Some envoy fw versions return an empty inverter array every 4 hours when
# no production is taking place. Prevent collection failure due to this
# as other data seems fine. Inverters will show unknown during this cycle.
if self._serial_number not in inverters:
_LOGGER.debug(
"Inverter %s not in returned inverters array (size: %s)",
self._serial_number,
len(inverters),
)
return None
return self.entity_description.value_fn(inverters[self._serial_number])
+6 -1
View File
@@ -497,7 +497,6 @@ class EvoBroker:
session_id = get_session_id(self.client_v1)
self.temps = {} # these are now stale, will fall back to v2 temps
try:
temps = await self.client_v1.get_temperatures()
@@ -523,6 +522,11 @@ class EvoBroker:
),
err,
)
self.temps = {} # high-precision temps now considered stale
except Exception:
self.temps = {} # high-precision temps now considered stale
raise
else:
if str(self.client_v1.location_id) != self._location.locationId:
@@ -654,6 +658,7 @@ class EvoChild(EvoDevice):
assert isinstance(self._evo_device, evo.HotWater | evo.Zone) # mypy check
if self._evo_broker.temps.get(self._evo_id) is not None:
# use high-precision temps if available
return self._evo_broker.temps[self._evo_id]
return self._evo_device.temperature
@@ -118,7 +118,6 @@ class FAABinarySensor(CoordinatorEntity[FAADataUpdateCoordinator], BinarySensorE
super().__init__(coordinator)
self.entity_description = description
_id = coordinator.data.code
self._attr_name = f"{_id} {description.name}"
self._attr_unique_id = f"{_id}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, _id)},
+9 -4
View File
@@ -26,6 +26,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -76,10 +77,6 @@ _DEPRECATED_SUPPORT_PRESET_MODE = DeprecatedConstantEnum(
FanEntityFeature.PRESET_MODE, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
SERVICE_INCREASE_SPEED = "increase_speed"
SERVICE_DECREASE_SPEED = "decrease_speed"
SERVICE_OSCILLATE = "oscillate"
@@ -471,3 +468,11 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if hasattr(self, "_attr_preset_modes"):
return self._attr_preset_modes
return None
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -19,7 +19,7 @@ from homeassistant.components.climate import (
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_HALVES, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
@@ -27,6 +27,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
DOMAIN,
MAX_TEMP,
MIN_TEMP,
PRESET_TO_VENTILATION_MODE_MAP,
VENTILATION_TO_PRESET_MODE_MAP,
)
@@ -65,8 +67,10 @@ class FlexitClimateEntity(ClimateEntity):
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
)
_attr_target_temperature_step = PRECISION_WHOLE
_attr_target_temperature_step = PRECISION_HALVES
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_max_temp = MAX_TEMP
_attr_min_temp = MIN_TEMP
def __init__(self, device: FlexitBACnet) -> None:
"""Initialize the unit."""
@@ -15,6 +15,9 @@ from homeassistant.components.climate import (
DOMAIN = "flexit_bacnet"
MAX_TEMP = 30
MIN_TEMP = 10
VENTILATION_TO_PRESET_MODE_MAP = {
VENTILATION_MODE_STOP: PRESET_NONE,
VENTILATION_MODE_AWAY: PRESET_AWAY,
+1 -1
View File
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/flipr",
"iot_class": "cloud_polling",
"loggers": ["flipr_api"],
"requirements": ["flipr-api==1.5.0"]
"requirements": ["flipr-api==1.5.1"]
}
+1
View File
@@ -1063,6 +1063,7 @@ class SwitchInfo(TypedDict):
type: str
callback_update: Callable
callback_switch: Callable
init_state: bool
class FritzBoxBaseEntity:
+10 -11
View File
@@ -166,9 +166,7 @@ async def _async_wifi_entities_list(
_LOGGER.debug("WiFi networks list: %s", networks)
return [
FritzBoxWifiSwitch(
avm_wrapper, device_friendly_name, index, data["switch_name"]
)
FritzBoxWifiSwitch(avm_wrapper, device_friendly_name, index, data)
for index, data in networks.items()
]
@@ -310,18 +308,16 @@ class FritzBoxBaseCoordinatorSwitch(CoordinatorEntity[AvmWrapper], SwitchEntity)
await self._async_handle_turn_on_off(turn_on=False)
class FritzBoxBaseSwitch(FritzBoxBaseEntity):
class FritzBoxBaseSwitch(FritzBoxBaseEntity, SwitchEntity):
"""Fritz switch base class."""
_attr_is_on: bool | None = False
def __init__(
self,
avm_wrapper: AvmWrapper,
device_friendly_name: str,
switch_info: SwitchInfo,
) -> None:
"""Init Fritzbox port switch."""
"""Init Fritzbox base switch."""
super().__init__(avm_wrapper, device_friendly_name)
self._description = switch_info["description"]
@@ -330,6 +326,7 @@ class FritzBoxBaseSwitch(FritzBoxBaseEntity):
self._type = switch_info["type"]
self._update = switch_info["callback_update"]
self._switch = switch_info["callback_switch"]
self._attr_is_on = switch_info["init_state"]
self._name = f"{self._friendly_name} {self._description}"
self._unique_id = f"{self._avm_wrapper.unique_id}-{slugify(self._description)}"
@@ -381,7 +378,7 @@ class FritzBoxBaseSwitch(FritzBoxBaseEntity):
self._attr_is_on = turn_on
class FritzBoxPortSwitch(FritzBoxBaseSwitch, SwitchEntity):
class FritzBoxPortSwitch(FritzBoxBaseSwitch):
"""Defines a FRITZ!Box Tools PortForward switch."""
def __init__(
@@ -412,6 +409,7 @@ class FritzBoxPortSwitch(FritzBoxBaseSwitch, SwitchEntity):
type=SWITCH_TYPE_PORTFORWARD,
callback_update=self._async_fetch_update,
callback_switch=self._async_switch_on_off_executor,
init_state=port_mapping["NewEnabled"],
)
super().__init__(avm_wrapper, device_friendly_name, switch_info)
@@ -553,7 +551,7 @@ class FritzBoxProfileSwitch(FritzDeviceBase, SwitchEntity):
return True
class FritzBoxWifiSwitch(FritzBoxBaseSwitch, SwitchEntity):
class FritzBoxWifiSwitch(FritzBoxBaseSwitch):
"""Defines a FRITZ!Box Tools Wifi switch."""
def __init__(
@@ -561,7 +559,7 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch, SwitchEntity):
avm_wrapper: AvmWrapper,
device_friendly_name: str,
network_num: int,
network_name: str,
network_data: dict,
) -> None:
"""Init Fritz Wifi switch."""
self._avm_wrapper = avm_wrapper
@@ -571,12 +569,13 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch, SwitchEntity):
self._network_num = network_num
switch_info = SwitchInfo(
description=f"Wi-Fi {network_name}",
description=f"Wi-Fi {network_data['switch_name']}",
friendly_name=device_friendly_name,
icon="mdi:wifi",
type=SWITCH_TYPE_WIFINETWORK,
callback_update=self._async_fetch_update,
callback_switch=self._async_switch_on_off_executor,
init_state=network_data["enabled"],
)
super().__init__(self._avm_wrapper, device_friendly_name, switch_info)
@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20240102.0"]
"requirements": ["home-assistant-frontend==20240104.0"]
}
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["aio_geojson_generic_client"],
"requirements": ["aio-geojson-generic-client==0.3"]
"requirements": ["aio-geojson-generic-client==0.4"]
}
@@ -43,6 +43,18 @@ async def async_setup_entry(
)
language = lang
break
if (
obj_holidays.supported_languages
and language not in obj_holidays.supported_languages
and (default_language := obj_holidays.default_language)
):
obj_holidays = country_holidays(
country,
subdiv=province,
years={dt_util.now().year, dt_util.now().year + 1},
language=default_language,
)
language = default_language
async_add_entities(
[
@@ -14,6 +14,6 @@
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"iot_class": "local_push",
"loggers": ["aiohomekit", "commentjson"],
"requirements": ["aiohomekit==3.1.1"],
"requirements": ["aiohomekit==3.1.3"],
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
}
@@ -24,6 +24,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -81,12 +82,6 @@ DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(HumidifierDeviceClass))
# use the HumidifierDeviceClass enum instead.
DEVICE_CLASSES = [cls.value for cls in HumidifierDeviceClass]
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
# mypy: disallow-any-generics
@@ -214,7 +209,7 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
if self.target_humidity is not None:
data[ATTR_HUMIDITY] = self.target_humidity
if HumidifierEntityFeature.MODES in self.supported_features:
if HumidifierEntityFeature.MODES in self.supported_features_compat:
data[ATTR_MODE] = self.mode
return data
@@ -293,3 +288,13 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+6 -2
View File
@@ -5,6 +5,7 @@ from functools import partial
from homeassistant.helpers.deprecation import (
DeprecatedConstant,
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -66,6 +67,9 @@ _DEPRECATED_SUPPORT_MODES = DeprecatedConstantEnum(
HumidifierEntityFeature.MODES, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+1 -1
View File
@@ -118,7 +118,7 @@ async def async_setup_platform(
mode = get_ip_mode(host)
mac = await hass.async_add_executor_job(partial(get_mac_address, **{mode: host}))
if mac is None:
if mac is None or mac == "00:00:00:00:00:00":
raise PlatformNotReady("Cannot get the ip address of kef speaker.")
unique_id = f"kef-{mac}"
+3
View File
@@ -82,6 +82,9 @@ DATA_HASS_CONFIG: Final = "knx_hass_config"
ATTR_COUNTER: Final = "counter"
ATTR_SOURCE: Final = "source"
# dispatcher signal for KNX interface device triggers
SIGNAL_KNX_TELEGRAM_DICT: Final = "knx_telegram_dict"
AsyncMessageCallbackType = Callable[[Telegram], Awaitable[None]]
MessageCallbackType = Callable[[Telegram], None]
@@ -9,11 +9,12 @@ from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEM
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
from homeassistant.helpers import selector
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
from . import KNXModule
from .const import DOMAIN
from .const import DOMAIN, SIGNAL_KNX_TELEGRAM_DICT
from .project import KNXProject
from .schema import ga_list_validator
from .telegrams import TelegramDict
@@ -87,7 +88,6 @@ async def async_attach_trigger(
trigger_data = trigger_info["trigger_data"]
dst_addresses: list[str] = config.get(EXTRA_FIELD_DESTINATION, [])
job = HassJob(action, f"KNX device trigger {trigger_info}")
knx: KNXModule = hass.data[DOMAIN]
@callback
def async_call_trigger_action(telegram: TelegramDict) -> None:
@@ -99,6 +99,8 @@ async def async_attach_trigger(
{"trigger": {**trigger_data, **telegram}},
)
return knx.telegrams.async_listen_telegram(
async_call_trigger_action, name="KNX device trigger call"
return async_dispatcher_connect(
hass,
signal=SIGNAL_KNX_TELEGRAM_DICT,
target=async_call_trigger_action,
)
+3 -1
View File
@@ -11,10 +11,11 @@ from xknx.telegram import Telegram
from xknx.telegram.apci import GroupValueResponse, GroupValueWrite
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.storage import Store
import homeassistant.util.dt as dt_util
from .const import DOMAIN
from .const import DOMAIN, SIGNAL_KNX_TELEGRAM_DICT
from .project import KNXProject
STORAGE_VERSION: Final = 1
@@ -87,6 +88,7 @@ class Telegrams:
"""Handle incoming and outgoing telegrams from xknx."""
telegram_dict = self.telegram_to_dict(telegram)
self.recent_telegrams.append(telegram_dict)
async_dispatcher_send(self.hass, SIGNAL_KNX_TELEGRAM_DICT, telegram_dict)
for job in self._jobs:
self.hass.async_run_hass_job(job, telegram_dict)
@@ -2,7 +2,11 @@
import logging
from bleak_retry_connector import BleakError, close_stale_connections, get_device
from bleak_retry_connector import (
BleakError,
close_stale_connections_by_address,
get_device,
)
from ld2410_ble import LD2410BLE
from homeassistant.components import bluetooth
@@ -24,6 +28,9 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up LD2410 BLE from a config entry."""
address: str = entry.data[CONF_ADDRESS]
await close_stale_connections_by_address(address)
ble_device = bluetooth.async_ble_device_from_address(
hass, address.upper(), True
) or await get_device(address)
@@ -32,8 +39,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
f"Could not find LD2410B device with address {address}"
)
await close_stale_connections(ble_device)
ld2410_ble = LD2410BLE(ble_device)
coordinator = LD2410BLECoordinator(hass, ld2410_ble)
+9 -4
View File
@@ -33,6 +33,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -70,10 +71,6 @@ class LockEntityFeature(IntFlag):
# Please use the LockEntityFeature enum instead.
_DEPRECATED_SUPPORT_OPEN = DeprecatedConstantEnum(LockEntityFeature.OPEN, "2025.1")
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
PROP_TO_ATTR = {"changed_by": ATTR_CHANGED_BY, "code_format": ATTR_CODE_FORMAT}
# mypy: disallow-any-generics
@@ -315,3 +312,11 @@ class LockEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
return
self._lock_option_default_code = ""
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -3,7 +3,7 @@
"step": {
"user": {
"data": {
"station_id": "Sensor ID",
"sensor_id": "Sensor ID",
"show_on_map": "Show on map"
}
}
+5 -2
View File
@@ -223,9 +223,12 @@ class MatrixBot:
def _load_commands(self, commands: list[ConfigCommand]) -> None:
for command in commands:
# Set the command for all listening_rooms, unless otherwise specified.
command.setdefault(CONF_ROOMS, list(self._listening_rooms.values()))
if rooms := command.get(CONF_ROOMS):
command[CONF_ROOMS] = [self._listening_rooms[room] for room in rooms]
else:
command[CONF_ROOMS] = list(self._listening_rooms.values())
# COMMAND_SCHEMA guarantees that exactly one of CONF_WORD and CONF_expression are set.
# COMMAND_SCHEMA guarantees that exactly one of CONF_WORD and CONF_EXPRESSION are set.
if (word_command := command.get(CONF_WORD)) is not None:
for room_id in command[CONF_ROOMS]:
self._word_commands.setdefault(room_id, {})
+12
View File
@@ -89,6 +89,10 @@ class MatterLight(MatterEntity, LightEntity):
colorY=int(matter_xy[1]),
# It's required in TLV. We don't implement transition time yet.
transitionTime=0,
# allow setting the color while the light is off,
# by setting the optionsMask to 1 (=ExecuteIfOff)
optionsMask=1,
optionsOverride=1,
)
)
@@ -103,6 +107,10 @@ class MatterLight(MatterEntity, LightEntity):
saturation=int(matter_hs[1]),
# It's required in TLV. We don't implement transition time yet.
transitionTime=0,
# allow setting the color while the light is off,
# by setting the optionsMask to 1 (=ExecuteIfOff)
optionsMask=1,
optionsOverride=1,
)
)
@@ -114,6 +122,10 @@ class MatterLight(MatterEntity, LightEntity):
colorTemperatureMireds=color_temp,
# It's required in TLV. We don't implement transition time yet.
transitionTime=0,
# allow setting the color while the light is off,
# by setting the optionsMask to 1 (=ExecuteIfOff)
optionsMask=1,
optionsOverride=1,
)
)
@@ -1295,7 +1295,7 @@ async def websocket_browse_media(
connection.send_error(msg["id"], "entity_not_found", "Entity not found")
return
if MediaPlayerEntityFeature.BROWSE_MEDIA not in player.supported_features:
if MediaPlayerEntityFeature.BROWSE_MEDIA not in player.supported_features_compat:
connection.send_message(
websocket_api.error_message(
msg["id"], ERR_NOT_SUPPORTED, "Player does not support browsing media"
@@ -11,7 +11,6 @@ from homeassistant.helpers.significant_change import (
from . import (
ATTR_ENTITY_PICTURE_LOCAL,
ATTR_GROUP_MEMBERS,
ATTR_MEDIA_POSITION,
ATTR_MEDIA_POSITION_UPDATED_AT,
ATTR_MEDIA_VOLUME_LEVEL,
@@ -25,9 +24,8 @@ INSIGNIFICANT_ATTRIBUTES: set[str] = {
SIGNIFICANT_ATTRIBUTES: set[str] = {
ATTR_ENTITY_PICTURE_LOCAL,
ATTR_GROUP_MEMBERS,
*ATTR_TO_PROPERTY,
}
} - INSIGNIFICANT_ATTRIBUTES
@callback
@@ -44,18 +42,10 @@ def async_check_significant_change(
return True
old_attrs_s = set(
{
k: v
for k, v in old_attrs.items()
if k in SIGNIFICANT_ATTRIBUTES - INSIGNIFICANT_ATTRIBUTES
}.items()
{k: v for k, v in old_attrs.items() if k in SIGNIFICANT_ATTRIBUTES}.items()
)
new_attrs_s = set(
{
k: v
for k, v in new_attrs.items()
if k in SIGNIFICANT_ATTRIBUTES - INSIGNIFICANT_ATTRIBUTES
}.items()
{k: v for k, v in new_attrs.items() if k in SIGNIFICANT_ATTRIBUTES}.items()
)
changed_attrs: set[str] = {item[0] for item in old_attrs_s ^ new_attrs_s}
@@ -2,8 +2,10 @@
from __future__ import annotations
import asyncio
from collections.abc import Coroutine
import json
import logging
from typing import Any
import aiohttp
from aiohttp.hdrs import CONTENT_TYPE
@@ -267,11 +269,11 @@ class MicrosoftFace:
"""Store group/person data and IDs."""
return self._store
async def update_store(self):
async def update_store(self) -> None:
"""Load all group/person data into local store."""
groups = await self.call_api("get", "persongroups")
remove_tasks = []
remove_tasks: list[Coroutine[Any, Any, None]] = []
new_entities = []
for group in groups:
g_id = group["personGroupId"]
@@ -293,7 +295,7 @@ class MicrosoftFace:
self._store[g_id][person["name"]] = person["personId"]
if remove_tasks:
await asyncio.gather(remove_tasks)
await asyncio.gather(*remove_tasks)
await self._component.async_add_entities(new_entities)
async def call_api(self, method, function, data=None, binary=False, params=None):
@@ -7,5 +7,5 @@
"iot_class": "local_polling",
"loggers": ["dnspython", "mcstatus"],
"quality_scale": "gold",
"requirements": ["mcstatus==11.0.0"]
"requirements": ["mcstatus==11.1.1"]
}
@@ -54,7 +54,7 @@ def async_get_schema(
if show_name:
schema = {
vol.Optional(CONF_NAME, default=defaults.get(CONF_NAME)): str,
vol.Required(CONF_NAME, default=defaults.get(CONF_NAME)): str,
**schema,
}
@@ -36,6 +36,7 @@ from .const import (
)
from .helpers import savable_state
from .http_api import RegistrationsView
from .util import async_create_cloud_hook
from .webhook import handle_webhook
PLATFORMS = [Platform.SENSOR, Platform.BINARY_SENSOR, Platform.DEVICE_TRACKER]
@@ -103,26 +104,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
registration_name = f"Mobile App: {registration[ATTR_DEVICE_NAME]}"
webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook)
async def create_cloud_hook() -> None:
"""Create a cloud hook."""
hook = await cloud.async_create_cloudhook(hass, webhook_id)
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_CLOUDHOOK_URL: hook}
)
async def manage_cloudhook(state: cloud.CloudConnectionState) -> None:
if (
state is cloud.CloudConnectionState.CLOUD_CONNECTED
and CONF_CLOUDHOOK_URL not in entry.data
):
await create_cloud_hook()
await async_create_cloud_hook(hass, webhook_id, entry)
if (
CONF_CLOUDHOOK_URL not in registration
CONF_CLOUDHOOK_URL not in entry.data
and cloud.async_active_subscription(hass)
and cloud.async_is_connected(hass)
):
await create_cloud_hook()
await async_create_cloud_hook(hass, webhook_id, entry)
entry.async_on_unload(cloud.async_listen_connection_change(hass, manage_cloudhook))
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -35,6 +35,7 @@ from .const import (
SCHEMA_APP_DATA,
)
from .helpers import supports_encryption
from .util import async_create_cloud_hook
class RegistrationsView(HomeAssistantView):
@@ -69,8 +70,8 @@ class RegistrationsView(HomeAssistantView):
webhook_id = secrets.token_hex()
if cloud.async_active_subscription(hass):
data[CONF_CLOUDHOOK_URL] = await cloud.async_create_cloudhook(
hass, webhook_id
data[CONF_CLOUDHOOK_URL] = await async_create_cloud_hook(
hass, webhook_id, None
)
data[CONF_WEBHOOK_ID] = webhook_id
@@ -1,8 +1,11 @@
"""Mobile app utility functions."""
from __future__ import annotations
import asyncio
from typing import TYPE_CHECKING
from homeassistant.components import cloud
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from .const import (
@@ -10,6 +13,7 @@ from .const import (
ATTR_PUSH_TOKEN,
ATTR_PUSH_URL,
ATTR_PUSH_WEBSOCKET_CHANNEL,
CONF_CLOUDHOOK_URL,
DATA_CONFIG_ENTRIES,
DATA_DEVICES,
DATA_NOTIFY,
@@ -53,3 +57,19 @@ def get_notify_service(hass: HomeAssistant, webhook_id: str) -> str | None:
return target_service
return None
_CLOUD_HOOK_LOCK = asyncio.Lock()
async def async_create_cloud_hook(
hass: HomeAssistant, webhook_id: str, entry: ConfigEntry | None
) -> str:
"""Create a cloud hook."""
async with _CLOUD_HOOK_LOCK:
hook = await cloud.async_get_or_create_cloudhook(hass, webhook_id)
if entry:
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_CLOUDHOOK_URL: hook}
)
return hook
+1 -1
View File
@@ -190,7 +190,7 @@ BASE_STRUCT_SCHEMA = BASE_COMPONENT_SCHEMA.extend(
vol.Optional(CONF_STRUCTURE): cv.string,
vol.Optional(CONF_SCALE, default=1): number_validator,
vol.Optional(CONF_OFFSET, default=0): number_validator,
vol.Optional(CONF_PRECISION, default=0): cv.positive_int,
vol.Optional(CONF_PRECISION): cv.positive_int,
vol.Optional(
CONF_SWAP,
): vol.In(
@@ -185,10 +185,8 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
self._swap = config[CONF_SWAP]
self._data_type = config[CONF_DATA_TYPE]
self._structure: str = config[CONF_STRUCTURE]
self._precision = config[CONF_PRECISION]
self._scale = config[CONF_SCALE]
if self._scale < 1 and not self._precision:
self._precision = 2
self._precision = config.get(CONF_PRECISION, 2 if self._scale < 1 else 0)
self._offset = config[CONF_OFFSET]
self._slave_count = config.get(CONF_SLAVE_COUNT, None) or config.get(
CONF_VIRTUAL_COUNT, 0
@@ -5,7 +5,7 @@ import logging
from socket import timeout
from typing import Any
from motionblinds import ParseException
from motionblinds import DEVICE_TYPES_WIFI, ParseException
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@@ -59,7 +59,9 @@ class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator):
def update_blind(self, blind):
"""Fetch data from a blind."""
try:
if self._wait_for_push:
if blind.device_type in DEVICE_TYPES_WIFI:
blind.Update_from_cache()
elif self._wait_for_push:
blind.Update()
else:
blind.Update_trigger()
+2 -2
View File
@@ -70,8 +70,8 @@ MQTT_TEXT_ATTRIBUTES_BLOCKED = frozenset(
def valid_text_size_configuration(config: ConfigType) -> ConfigType:
"""Validate that the text length configuration is valid, throws if it isn't."""
if config[CONF_MIN] >= config[CONF_MAX]:
raise vol.Invalid("text length min must be >= max")
if config[CONF_MIN] > config[CONF_MAX]:
raise vol.Invalid("text length min must be <= max")
if config[CONF_MAX] > MAX_LENGTH_STATE_STATE:
raise vol.Invalid(f"max text length must be <= {MAX_LENGTH_STATE_STATE}")
+8 -4
View File
@@ -38,6 +38,7 @@ from homeassistant.const import (
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -70,10 +71,6 @@ _DEPRECATED_MODE_AUTO: Final = DeprecatedConstantEnum(NumberMode.AUTO, "2025.1")
_DEPRECATED_MODE_BOX: Final = DeprecatedConstantEnum(NumberMode.BOX, "2025.1")
_DEPRECATED_MODE_SLIDER: Final = DeprecatedConstantEnum(NumberMode.SLIDER, "2025.1")
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
class NumberDeviceClass(StrEnum):
"""Device class for numbers."""
@@ -481,3 +478,10 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = {
NumberDeviceClass.TEMPERATURE: TemperatureConverter,
}
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -3,6 +3,7 @@ from __future__ import annotations
from collections.abc import Mapping
import logging
import socket
from typing import Any
from opower import (
@@ -38,7 +39,7 @@ async def _validate_login(
) -> dict[str, str]:
"""Validate login data and return any errors."""
api = Opower(
async_create_clientsession(hass),
async_create_clientsession(hass, family=socket.AF_INET),
login_data[CONF_UTILITY],
login_data[CONF_USERNAME],
login_data[CONF_PASSWORD],
@@ -1,6 +1,7 @@
"""Coordinator to handle Opower connections."""
from datetime import datetime, timedelta
import logging
import socket
from types import MappingProxyType
from typing import Any, cast
@@ -51,7 +52,7 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, Forecast]]):
update_interval=timedelta(hours=12),
)
self.api = Opower(
aiohttp_client.async_get_clientsession(hass),
aiohttp_client.async_get_clientsession(hass, family=socket.AF_INET),
entry_data[CONF_UTILITY],
entry_data[CONF_USERNAME],
entry_data[CONF_PASSWORD],
@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/orvibo",
"iot_class": "local_push",
"loggers": ["orvibo"],
"requirements": ["orvibo==1.1.1"]
"requirements": ["orvibo==1.1.2"]
}
@@ -136,7 +136,7 @@ async def async_setup_entry(
class PingDeviceTracker(CoordinatorEntity[PingUpdateCoordinator], ScannerEntity):
"""Representation of a Ping device tracker."""
_first_offline: datetime | None = None
_last_seen: datetime | None = None
def __init__(
self, config_entry: ConfigEntry, coordinator: PingUpdateCoordinator
@@ -171,14 +171,12 @@ class PingDeviceTracker(CoordinatorEntity[PingUpdateCoordinator], ScannerEntity)
def is_connected(self) -> bool:
"""Return true if ping returns is_alive or considered home."""
if self.coordinator.data.is_alive:
self._first_offline = None
return True
self._last_seen = dt_util.utcnow()
now = dt_util.utcnow()
if self._first_offline is None:
self._first_offline = now
return (self._first_offline + self._consider_home_interval) > now
return (
self._last_seen is not None
and (dt_util.utcnow() - self._last_seen) < self._consider_home_interval
)
@property
def entity_registry_enabled_default(self) -> bool:
@@ -165,6 +165,10 @@ def count_torrents_in_states(
coordinator: QBittorrentDataCoordinator, states: list[str]
) -> int:
"""Count the number of torrents in specified states."""
# When torrents are not in the returned data, there are none, return 0.
if "torrents" not in coordinator.data:
return 0
if not states:
return len(coordinator.data["torrents"])
+9 -4
View File
@@ -27,6 +27,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -92,10 +93,6 @@ _DEPRECATED_SUPPORT_ACTIVITY = DeprecatedConstantEnum(
)
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(dir_with_deprecated_constants, module_globals=globals())
REMOTE_SERVICE_ACTIVITY_SCHEMA = make_entity_service_schema(
{vol.Optional(ATTR_ACTIVITY): cv.string}
)
@@ -262,3 +259,11 @@ class RemoteEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
await self.hass.async_add_executor_job(
ft.partial(self.delete_command, **kwargs)
)
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = ft.partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -18,5 +18,5 @@
"documentation": "https://www.home-assistant.io/integrations/reolink",
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"requirements": ["reolink-aio==0.8.5"]
"requirements": ["reolink-aio==0.8.7"]
}
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["aioridwell"],
"requirements": ["aioridwell==2023.07.0"]
"requirements": ["aioridwell==2024.01.0"]
}
@@ -39,7 +39,7 @@
"samsungctl[websocket]==0.7.1",
"samsungtvws[async,encrypted]==2.6.0",
"wakeonlan==2.1.0",
"async-upnp-client==0.38.0"
"async-upnp-client==0.38.1"
],
"ssdp": [
{
@@ -61,10 +61,7 @@ def async_load_screenlogic_services(hass: HomeAssistant):
color_num,
)
try:
if not await coordinator.gateway.async_set_color_lights(color_num):
raise HomeAssistantError(
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'"
)
await coordinator.gateway.async_set_color_lights(color_num)
# Debounced refresh to catch any secondary
# changes in the device
await coordinator.async_request_refresh()
+11 -6
View File
@@ -59,6 +59,7 @@ from homeassistant.helpers.config_validation import (
PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -120,12 +121,6 @@ __all__ = [
"SensorStateClass",
]
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
# mypy: disallow-any-generics
@@ -955,3 +950,13 @@ def async_rounded_state(hass: HomeAssistant, entity_id: str, state: State) -> st
value = f"{numerical_value:z.{precision}f}"
return value
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+8 -4
View File
@@ -38,6 +38,7 @@ from homeassistant.const import (
)
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -468,10 +469,6 @@ _DEPRECATED_STATE_CLASS_TOTAL_INCREASING: Final = DeprecatedConstantEnum(
)
STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass]
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = {
SensorDeviceClass.ATMOSPHERIC_PRESSURE: PressureConverter,
SensorDeviceClass.CURRENT: ElectricCurrentConverter,
@@ -631,3 +628,10 @@ DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
SensorDeviceClass.WEIGHT: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.WIND_SPEED: {SensorStateClass.MEASUREMENT},
}
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -30,12 +30,15 @@ from homeassistant.helpers.device_registry import (
from homeassistant.helpers.typing import ConfigType
from .const import (
BLOCK_EXPECTED_SLEEP_PERIOD,
BLOCK_WRONG_SLEEP_PERIOD,
CONF_COAP_PORT,
CONF_SLEEP_PERIOD,
DATA_CONFIG_ENTRY,
DEFAULT_COAP_PORT,
DOMAIN,
LOGGER,
MODELS_WITH_WRONG_SLEEP_PERIOD,
PUSH_UPDATE_ISSUE_ID,
)
from .coordinator import (
@@ -162,6 +165,22 @@ async def _async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
sleep_period = entry.data.get(CONF_SLEEP_PERIOD)
shelly_entry_data = get_entry_data(hass)[entry.entry_id]
# Some old firmware have a wrong sleep period hardcoded value.
# Following code block will force the right value for affected devices
if (
sleep_period == BLOCK_WRONG_SLEEP_PERIOD
and entry.data["model"] in MODELS_WITH_WRONG_SLEEP_PERIOD
):
LOGGER.warning(
"Updating stored sleep period for %s: from %s to %s",
entry.title,
sleep_period,
BLOCK_EXPECTED_SLEEP_PERIOD,
)
data = {**entry.data}
data[CONF_SLEEP_PERIOD] = sleep_period = BLOCK_EXPECTED_SLEEP_PERIOD
hass.config_entries.async_update_entry(entry, data=data)
async def _async_block_device_setup() -> None:
"""Set up a block based device that is online."""
shelly_entry_data.block = ShellyBlockCoordinator(hass, entry, device)
@@ -15,7 +15,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_ON, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity_registry import RegistryEntry
from homeassistant.helpers.restore_state import RestoreEntity
from .const import CONF_SLEEP_PERIOD
@@ -210,16 +209,6 @@ RPC_SENSORS: Final = {
}
def _build_block_description(entry: RegistryEntry) -> BlockBinarySensorDescription:
"""Build description when restoring block attribute entities."""
return BlockBinarySensorDescription(
key="",
name="",
icon=entry.original_icon,
device_class=entry.original_device_class,
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
@@ -248,7 +237,6 @@ async def async_setup_entry(
async_add_entities,
SENSORS,
BlockSleepingBinarySensor,
_build_block_description,
)
else:
async_setup_entry_attribute_entities(
@@ -257,7 +245,6 @@ async def async_setup_entry(
async_add_entities,
SENSORS,
BlockBinarySensor,
_build_block_description,
)
async_setup_entry_rest(
hass,
@@ -316,6 +316,21 @@ class BlockSleepingClimate(
"""Set new target temperature."""
if (current_temp := kwargs.get(ATTR_TEMPERATURE)) is None:
return
# Shelly TRV accepts target_t in Fahrenheit or Celsius, but you must
# send the units that the device expects
if self.block is not None and self.block.channel is not None:
therm = self.coordinator.device.settings["thermostats"][
int(self.block.channel)
]
LOGGER.debug("Themostat settings: %s", therm)
if therm.get("target_t", {}).get("units", "C") == "F":
current_temp = TemperatureConverter.convert(
cast(float, current_temp),
UnitOfTemperature.CELSIUS,
UnitOfTemperature.FAHRENHEIT,
)
await self.set_state_full_path(target_t_enabled=1, target_t=f"{current_temp}")
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
+10 -8
View File
@@ -25,6 +25,7 @@ from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
from .const import (
CONF_BLE_SCANNER_MODE,
CONF_GEN,
CONF_SLEEP_PERIOD,
DOMAIN,
LOGGER,
@@ -35,6 +36,7 @@ from .coordinator import async_reconnect_soon
from .utils import (
get_block_device_sleep_period,
get_coap_context,
get_device_entry_gen,
get_info_auth,
get_info_gen,
get_model_name,
@@ -84,7 +86,7 @@ async def validate_input(
"title": rpc_device.name,
CONF_SLEEP_PERIOD: sleep_period,
"model": rpc_device.shelly.get("model"),
"gen": gen,
CONF_GEN: gen,
}
# Gen1
@@ -99,7 +101,7 @@ async def validate_input(
"title": block_device.name,
CONF_SLEEP_PERIOD: get_block_device_sleep_period(block_device.settings),
"model": block_device.model,
"gen": gen,
CONF_GEN: gen,
}
@@ -153,7 +155,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
**user_input,
CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD],
"model": device_info["model"],
"gen": device_info["gen"],
CONF_GEN: device_info[CONF_GEN],
},
)
errors["base"] = "firmware_not_fully_provisioned"
@@ -190,7 +192,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
CONF_HOST: self.host,
CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD],
"model": device_info["model"],
"gen": device_info["gen"],
CONF_GEN: device_info[CONF_GEN],
},
)
errors["base"] = "firmware_not_fully_provisioned"
@@ -288,7 +290,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
"host": self.host,
CONF_SLEEP_PERIOD: self.device_info[CONF_SLEEP_PERIOD],
"model": self.device_info["model"],
"gen": self.device_info["gen"],
CONF_GEN: self.device_info[CONF_GEN],
},
)
self._set_confirm_only()
@@ -321,7 +323,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
except (DeviceConnectionError, InvalidAuthError, FirmwareUnsupported):
return self.async_abort(reason="reauth_unsuccessful")
if self.entry.data.get("gen", 1) != 1:
if get_device_entry_gen(self.entry) != 1:
user_input[CONF_USERNAME] = "admin"
try:
await validate_input(self.hass, host, info, user_input)
@@ -334,7 +336,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
await self.hass.config_entries.async_reload(self.entry.entry_id)
return self.async_abort(reason="reauth_successful")
if self.entry.data.get("gen", 1) in BLOCK_GENERATIONS:
if get_device_entry_gen(self.entry) in BLOCK_GENERATIONS:
schema = {
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
@@ -363,7 +365,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
"""Return options flow support for this handler."""
return (
config_entry.data.get("gen") in RPC_GENERATIONS
get_device_entry_gen(config_entry) in RPC_GENERATIONS
and not config_entry.data.get(CONF_SLEEP_PERIOD)
and config_entry.data.get("model") != MODEL_WALL_DISPLAY
)
+15
View File
@@ -14,7 +14,10 @@ from aioshelly.const import (
MODEL_DIMMER,
MODEL_DIMMER_2,
MODEL_DUO,
MODEL_DW,
MODEL_DW_2,
MODEL_GAS,
MODEL_HT,
MODEL_MOTION,
MODEL_MOTION_2,
MODEL_RGBW2,
@@ -55,6 +58,12 @@ MODELS_SUPPORTING_LIGHT_EFFECTS: Final = (
MODEL_RGBW2,
)
MODELS_WITH_WRONG_SLEEP_PERIOD: Final = (
MODEL_DW,
MODEL_DW_2,
MODEL_HT,
)
# Bulbs that support white & color modes
DUAL_MODE_LIGHT_MODELS: Final = (
MODEL_BULB,
@@ -176,6 +185,10 @@ KELVIN_MAX_VALUE: Final = 6500
KELVIN_MIN_VALUE_WHITE: Final = 2700
KELVIN_MIN_VALUE_COLOR: Final = 3000
# Sleep period
BLOCK_WRONG_SLEEP_PERIOD = 21600
BLOCK_EXPECTED_SLEEP_PERIOD = 43200
UPTIME_DEVIATION: Final = 5
# Time to wait before reloading entry upon device config change
@@ -214,3 +227,5 @@ DEVICES_WITHOUT_FIRMWARE_CHANGELOG = (
MODEL_MOTION_2,
MODEL_VALVE,
)
CONF_GEN = "gen"
@@ -57,7 +57,11 @@ from .const import (
UPDATE_PERIOD_MULTIPLIER,
BLEScannerMode,
)
from .utils import get_rpc_device_wakeup_period, update_device_fw_info
from .utils import (
get_device_entry_gen,
get_rpc_device_wakeup_period,
update_device_fw_info,
)
_DeviceT = TypeVar("_DeviceT", bound="BlockDevice|RpcDevice")
@@ -135,7 +139,7 @@ class ShellyCoordinatorBase(DataUpdateCoordinator[None], Generic[_DeviceT]):
manufacturer="Shelly",
model=aioshelly.const.MODEL_NAMES.get(self.model, self.model),
sw_version=self.sw_version,
hw_version=f"gen{self.device.gen} ({self.model})",
hw_version=f"gen{get_device_entry_gen(self.entry)} ({self.model})",
configuration_url=f"http://{self.entry.data[CONF_HOST]}",
)
self.device_id = device_entry.id
+7 -21
View File
@@ -39,7 +39,6 @@ def async_setup_entry_attribute_entities(
async_add_entities: AddEntitiesCallback,
sensors: Mapping[tuple[str, str], BlockEntityDescription],
sensor_class: Callable,
description_class: Callable[[RegistryEntry], BlockEntityDescription],
) -> None:
"""Set up entities for attributes."""
coordinator = get_entry_data(hass)[config_entry.entry_id].block
@@ -56,7 +55,6 @@ def async_setup_entry_attribute_entities(
coordinator,
sensors,
sensor_class,
description_class,
)
@@ -113,7 +111,6 @@ def async_restore_block_attribute_entities(
coordinator: ShellyBlockCoordinator,
sensors: Mapping[tuple[str, str], BlockEntityDescription],
sensor_class: Callable,
description_class: Callable[[RegistryEntry], BlockEntityDescription],
) -> None:
"""Restore block attributes entities."""
entities = []
@@ -128,11 +125,12 @@ def async_restore_block_attribute_entities(
continue
attribute = entry.unique_id.split("-")[-1]
description = description_class(entry)
block_type = entry.unique_id.split("-")[-2].split("_")[0]
entities.append(
sensor_class(coordinator, None, attribute, description, entry, sensors)
)
if description := sensors.get((block_type, attribute)):
entities.append(
sensor_class(coordinator, None, attribute, description, entry)
)
if not entities:
return
@@ -444,7 +442,7 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, Entity):
"""Available."""
available = super().available
if not available or not self.entity_description.available:
if not available or not self.entity_description.available or self.block is None:
return available
return self.entity_description.available(self.block)
@@ -559,10 +557,8 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity):
attribute: str,
description: BlockEntityDescription,
entry: RegistryEntry | None = None,
sensors: Mapping[tuple[str, str], BlockEntityDescription] | None = None,
) -> None:
"""Initialize the sleeping sensor."""
self.sensors = sensors
self.last_state: State | None = None
self.coordinator = coordinator
self.attribute = attribute
@@ -587,11 +583,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity):
@callback
def _update_callback(self) -> None:
"""Handle device update."""
if (
self.block is not None
or not self.coordinator.device.initialized
or self.sensors is None
):
if self.block is not None or not self.coordinator.device.initialized:
super()._update_callback()
return
@@ -607,13 +599,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity):
if sensor_id != entity_sensor:
continue
description = self.sensors.get((block.type, sensor_id))
if description is None:
continue
self.block = block
self.entity_description = description
LOGGER.debug("Entity %s attached to block", self.name)
super()._update_callback()
return
@@ -9,7 +9,7 @@
"iot_class": "local_push",
"loggers": ["aioshelly"],
"quality_scale": "platinum",
"requirements": ["aioshelly==7.0.0"],
"requirements": ["aioshelly==7.1.0"],
"zeroconf": [
{
"type": "_http._tcp.local.",
+1 -20
View File
@@ -1,7 +1,6 @@
"""Number for Shelly."""
from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any, Final, cast
@@ -56,22 +55,6 @@ NUMBERS: Final = {
}
def _build_block_description(entry: RegistryEntry) -> BlockNumberDescription:
"""Build description when restoring block attribute entities."""
assert entry.capabilities
return BlockNumberDescription(
key="",
name="",
icon=entry.original_icon,
native_unit_of_measurement=entry.unit_of_measurement,
device_class=entry.original_device_class,
native_min_value=cast(float, entry.capabilities.get("min")),
native_max_value=cast(float, entry.capabilities.get("max")),
native_step=cast(float, entry.capabilities.get("step")),
mode=cast(NumberMode, entry.capabilities.get("mode")),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
@@ -85,7 +68,6 @@ async def async_setup_entry(
async_add_entities,
NUMBERS,
BlockSleepingNumber,
_build_block_description,
)
@@ -101,11 +83,10 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, RestoreNumber):
attribute: str,
description: BlockNumberDescription,
entry: RegistryEntry | None = None,
sensors: Mapping[tuple[str, str], BlockNumberDescription] | None = None,
) -> None:
"""Initialize the sleeping sensor."""
self.restored_data: NumberExtraStoredData | None = None
super().__init__(coordinator, block, attribute, description, entry, sensors)
super().__init__(coordinator, block, attribute, description, entry)
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
+1 -16
View File
@@ -1,7 +1,6 @@
"""Sensor for Shelly."""
from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Final, cast
@@ -962,17 +961,6 @@ RPC_SENSORS: Final = {
}
def _build_block_description(entry: RegistryEntry) -> BlockSensorDescription:
"""Build description when restoring block attribute entities."""
return BlockSensorDescription(
key="",
name="",
icon=entry.original_icon,
native_unit_of_measurement=entry.unit_of_measurement,
device_class=entry.original_device_class,
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
@@ -1001,7 +989,6 @@ async def async_setup_entry(
async_add_entities,
SENSORS,
BlockSleepingSensor,
_build_block_description,
)
else:
async_setup_entry_attribute_entities(
@@ -1010,7 +997,6 @@ async def async_setup_entry(
async_add_entities,
SENSORS,
BlockSensor,
_build_block_description,
)
async_setup_entry_rest(
hass, config_entry, async_add_entities, REST_SENSORS, RestSensor
@@ -1074,10 +1060,9 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, RestoreSensor):
attribute: str,
description: BlockSensorDescription,
entry: RegistryEntry | None = None,
sensors: Mapping[tuple[str, str], BlockSensorDescription] | None = None,
) -> None:
"""Initialize the sleeping sensor."""
super().__init__(coordinator, block, attribute, description, entry, sensors)
super().__init__(coordinator, block, attribute, description, entry)
self.restored_data: SensorExtraStoredData | None = None
async def async_added_to_hass(self) -> None:
+3 -2
View File
@@ -34,6 +34,7 @@ from homeassistant.util.dt import utcnow
from .const import (
BASIC_INPUTS_EVENTS_TYPES,
CONF_COAP_PORT,
CONF_GEN,
DEFAULT_COAP_PORT,
DEVICES_WITHOUT_FIRMWARE_CHANGELOG,
DOMAIN,
@@ -281,7 +282,7 @@ def get_info_auth(info: dict[str, Any]) -> bool:
def get_info_gen(info: dict[str, Any]) -> int:
"""Return the device generation from shelly info."""
return int(info.get("gen", 1))
return int(info.get(CONF_GEN, 1))
def get_model_name(info: dict[str, Any]) -> str:
@@ -325,7 +326,7 @@ def get_rpc_entity_name(
def get_device_entry_gen(entry: ConfigEntry) -> int:
"""Return the device generation from config entry."""
return entry.data.get("gen", 1)
return entry.data.get(CONF_GEN, 1)
def get_rpc_key_instances(keys_dict: dict[str, Any], key: str) -> list[str]:
+11 -6
View File
@@ -17,6 +17,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA_BASE,
)
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -53,12 +54,6 @@ TURN_ON_SCHEMA = {
vol.Optional(ATTR_VOLUME_LEVEL): cv.small_float,
}
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
class SirenTurnOnServiceParameters(TypedDict, total=False):
"""Represent possible parameters to siren.turn_on service data dict type."""
@@ -218,3 +213,13 @@ class SirenEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
+6 -2
View File
@@ -6,6 +6,7 @@ from typing import Final
from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
@@ -47,6 +48,9 @@ _DEPRECATED_SUPPORT_DURATION: Final = DeprecatedConstantEnum(
SirenEntityFeature.DURATION, "2025.1"
)
# Both can be removed if no deprecated constant are in this module anymore
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())
@@ -61,6 +61,10 @@ REPEAT_MODE_MAPPING_TO_SPOTIFY = {
value: key for key, value in REPEAT_MODE_MAPPING_TO_HA.items()
}
# This is a minimal representation of the DJ playlist that Spotify now offers
# The DJ is not fully integrated with the playlist API, so needs to have the playlist response mocked in order to maintain functionality
SPOTIFY_DJ_PLAYLIST = {"uri": "spotify:playlist:37i9dQZF1EYkqdzj48dyYq", "name": "DJ"}
async def async_setup_entry(
hass: HomeAssistant,
@@ -423,7 +427,19 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
if context and (self._playlist is None or self._playlist["uri"] != uri):
self._playlist = None
if context["type"] == MediaType.PLAYLIST:
self._playlist = self.data.client.playlist(uri)
# The Spotify API does not currently support doing a lookup for the DJ playlist, so just use the minimal mock playlist object
if uri == SPOTIFY_DJ_PLAYLIST["uri"]:
self._playlist = SPOTIFY_DJ_PLAYLIST
else:
# Make sure any playlist lookups don't break the current playback state update
try:
self._playlist = self.data.client.playlist(uri)
except SpotifyException:
_LOGGER.debug(
"Unable to load spotify playlist '%s'. Continuing without playlist data",
uri,
)
self._playlist = None
device = self._currently_playing.get("device")
if device is not None:

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