Compare commits

...

194 Commits

Author SHA1 Message Date
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
Franck Nijhof 77cdc10883 Bump version to 2024.1.0b5 2024-01-02 20:59:49 +01:00
Bram Kragten 5100ba252f Update frontend to 20240102.0 (#106898) 2024-01-02 20:59:40 +01:00
Allen Porter b5b8bc3102 Improve To-do service error handling (#106886) 2024-01-02 20:59:37 +01:00
Allen Porter 6f18a29241 Improve fitbit authentication error handling (#106885) 2024-01-02 20:59:34 +01:00
David F. Mulcahey 596f855eab Bump Zigpy to 0.60.4 (#106870) 2024-01-02 20:59:31 +01:00
J. Nick Koston 5877fe135c Close stale connections in yalexs_ble to ensure setup can proceed (#106842) 2024-01-02 20:59:28 +01:00
J. Nick Koston 26cf30fc3a Update switchbot to use close_stale_connections_by_address (#106835) 2024-01-02 20:59:25 +01:00
Sid 3419b8d082 Move urllib3 constraint to pyproject.toml (#106768) 2024-01-02 20:59:22 +01:00
Robert Groot 35fc26457b Changed setup of EnergyZero services (#106224)
* Changed setup of energyzero services

* PR review updates

* Dict access instead of get

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

* Added tests for unloaded state

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-01-02 20:59:18 +01:00
Franck Nijhof fc66dead64 Bump version to 2024.1.0b4 2024-01-02 12:59:23 +01:00
Erik Montnemery 056b06de13 Don't use entity_id in __repr__ of not added entity (#106861) 2024-01-02 12:59:11 +01:00
Matt Zimmerman e604bc8c9b Map missing preset mapping for heat mode "ready" in smarttub (#106856) 2024-01-02 12:59:08 +01:00
J. Nick Koston 59bed57d48 Fix incorrect state in Yale Access Bluetooth when lock status is unknown (#106851) 2024-01-02 12:59:05 +01:00
J. Nick Koston 8c25e2610e Bump yalexs-ble to 2.4.0 (#106834) 2024-01-02 12:59:01 +01:00
J. Nick Koston 38b8a1f95d Bump pySwitchbot to 0.43.0 (#106833) 2024-01-02 12:58:58 +01:00
J. Nick Koston 54a87cf047 Bump bleak-retry-connector to 3.4.0 (#106831) 2024-01-02 12:58:55 +01:00
Bram Kragten 448e98eac5 Update frontend to 20240101.0 (#106808) 2024-01-02 12:58:52 +01:00
Maikel Punie 6ca3c7a673 Bump pyduotecno to 2024.1.1 (#106801)
* Bump pyduotecno to 2024.0.1

* Bump pyduotecno to 2024.1.0

* small update
2024-01-02 12:58:49 +01:00
J. Nick Koston b1a55e9b19 Fix emulated_hue brightness check (#106783) 2024-01-02 12:58:46 +01:00
David Knowles 16d3d88fa3 Bump pyschlage to 2023.12.1 (#106782) 2024-01-02 12:58:43 +01:00
Christopher Bailey 39960caf36 Bump pyunifiprotect to v4.22.5 (#106781) 2024-01-02 12:58:40 +01:00
Benjamin Richter e6d2721d1b Fix fints account type check (#106082) 2024-01-02 12:58:37 +01:00
David Knowles fedb63720c Fix Hydrawise data not refreshing (#105923)
Co-authored-by: Robert Resch <robert@resch.dev>
2024-01-02 12:58:34 +01:00
Luke Lashley 77286e8f59 Constrain dacite to at least 1.7.0 (#105709) 2024-01-02 12:58:31 +01:00
Franck Nijhof a7d11120fa Bump version to 2024.1.0b3 2023-12-31 18:57:11 +01:00
Tobias Sauerwein c06df1957f Bump pyatmo to v8.0.2 (#106758) 2023-12-31 18:56:59 +01:00
puddly 99d575261d Bump ZHA dependencies (#106756)
* Bump ZHA dependencies

* Revert "Remove bellows thread, as it has been removed upstream"

This reverts commit c28053f4bf.
2023-12-31 18:56:57 +01:00
J. Nick Koston 3dca39d0f9 Bump habluetooth to 2.0.1 (#106750)
fixes switching scanners to quickly since the manager failed
to account for jitter in the auto discovered advertising interval

replaces and closes #96531

changelog: https://github.com/Bluetooth-Devices/habluetooth/compare/v2.0.0...v2.0.1
2023-12-31 18:56:53 +01:00
J. Nick Koston a11fd2aaa6 Bump pyunifiprotect to 4.22.4 (#106749)
changelog: https://github.com/AngellusMortis/pyunifiprotect/compare/v4.22.3...v4.22.4
2023-12-31 18:56:51 +01:00
starkillerOG 05768f5fbd Bump reolink_aio to 0.8.5 (#106747) 2023-12-31 18:56:48 +01:00
David F. Mulcahey 3d75603b4f Fix Zlinky energy polling in ZHA (#106738) 2023-12-31 18:56:44 +01:00
Franck Nijhof 2179d4de3d Add missing vacuum toggle service description (#106729) 2023-12-31 18:56:41 +01:00
J. Nick Koston 2255f6737c Pin lxml to 4.9.4 (#106694) 2023-12-31 18:56:38 +01:00
J. Nick Koston 456cb20fcd Fix missed cached_property for hvac_mode in climate (#106692) 2023-12-31 18:56:35 +01:00
Keilin Bickar 8dfbe6849e Bump asyncsleepiq to v1.4.1 (#106682)
Update asyncsleepiq to v1.4.1
2023-12-31 18:56:32 +01:00
J. Nick Koston 3dd998b622 Bump roombapy to 1.6.10 (#106678)
changelog: https://github.com/pschmitt/roombapy/compare/1.6.8...1.6.10

fixes #105323
2023-12-31 18:56:29 +01:00
J. Nick Koston 84da1638e8 Bump thermobeacon-ble to 0.6.2 (#106676)
changelog: https://github.com/Bluetooth-Devices/thermobeacon-ble/compare/v0.6.0...v0.6.2
2023-12-31 18:56:26 +01:00
Erik Montnemery 362e5ca09a Fix changed_variables in automation traces (#106665) 2023-12-31 18:56:23 +01:00
G Johansson 494dd2ef07 Handle no permission for disks in Systemmonitor (#106653) 2023-12-31 18:56:20 +01:00
G Johansson 767c55fbac Use set instead of list in Systemmonitor (#106650) 2023-12-31 18:56:17 +01:00
Erik Montnemery 5f3389b8e4 Fix yolink entity descriptions (#106649) 2023-12-31 18:56:14 +01:00
Jaroslav Hanslík c1e37a4cc3 Fixed native apparent temperature in WeatherEntity (#106645) 2023-12-31 18:56:11 +01:00
Jirka 3cd5f0568a Fix typo in Blink strings (#106641)
Update strings.json

Fixed typo.
2023-12-31 18:56:08 +01:00
Erik Montnemery f9150b78b3 Ensure it's safe to call Entity.__repr__ on non added entity (#106032) 2023-12-31 18:56:04 +01:00
Franck Nijhof c54af00ce9 Bump version to 2024.1.0b2 2023-12-29 11:29:50 +01:00
Erik Montnemery bb6f3bc830 Fix missing await when running shutdown jobs (#106632) 2023-12-29 11:28:15 +01:00
J. Nick Koston f84d865c51 Migrate light entity to use contains for LightEntityFeature with deprecation warnings (#106622) 2023-12-29 11:28:12 +01:00
J. Nick Koston 2147df4418 Add deprecation warning for siren supported features when using magic numbers (#106621) 2023-12-29 11:28:09 +01:00
J. Nick Koston af9f6a2b12 Add deprecation warning for lock supported features when using magic numbers (#106620) 2023-12-29 11:28:06 +01:00
J. Nick Koston 024d689b94 Add deprecation warning for alarm_control_panel supported features when using magic numbers (#106619) 2023-12-29 11:28:03 +01:00
J. Nick Koston 9f4790902a Add deprecation warning for cover supported features when using magic numbers (#106618) 2023-12-29 11:27:59 +01:00
J. Nick Koston 5d9177d6e6 Media player platform back-compat for custom components without MediaPlayerEntityFeature (#106616) 2023-12-29 11:27:56 +01:00
J. Nick Koston 70842f197e Vacuum platform back-compat for custom components without VacuumEntityFeature (#106614) 2023-12-29 11:27:53 +01:00
J. Nick Koston f03bb4a2da Humidifier platform back-compat for custom components without HumidifierEntityFeature (#106613) 2023-12-29 11:27:50 +01:00
J. Nick Koston aa6e904e86 Remote platform back-compat for custom components without RemoteEntityFeature (#106609) 2023-12-29 11:27:47 +01:00
J. Nick Koston 6224e630ac Water heater platform back-compat for custom components without WaterHeaterEntityFeature (#106608) 2023-12-29 11:27:44 +01:00
J. Nick Koston 04fe8260ab Fan platform back-compat for custom components without FanEntityFeature (#106607) 2023-12-29 11:27:41 +01:00
J. Nick Koston 4a98a6465e Climate platform back-compat for custom components without ClimateEntityFeature (#106605) 2023-12-29 11:27:38 +01:00
Joe Neuman 06f06b7595 Fix count bug in qBittorrent (#106603) 2023-12-29 11:27:35 +01:00
J. Nick Koston 16192cd7f2 Add helper to report deprecated entity supported features magic numbers (#106602) 2023-12-29 11:27:32 +01:00
Robert Hillis 982707afe6 Fix Netgear LTE halting startup (#106598) 2023-12-29 11:27:28 +01:00
Joost Lekkerkerker 911234ae8f Move aeptexas to aep_texas (#106595) 2023-12-29 11:25:58 +01:00
tronikos 55877b0953 Rename domain aepohio to aep_ohio (#106536)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-12-29 11:23:08 +01:00
J. Nick Koston 0623972ee0 Camera platform back-compat for custom components without CameraEntityFeature (#106529) 2023-12-29 11:11:03 +01:00
J. Nick Koston d407b9fca8 Update platform back-compat for custom components without UpdateEntityFeature (#106528) 2023-12-29 11:10:58 +01:00
305 changed files with 4743 additions and 1348 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
@@ -1,5 +1,5 @@
{
"domain": "aepohio",
"domain": "aep_ohio",
"name": "AEP Ohio",
"integration_type": "virtual",
"supported_by": "opower"
@@ -1,5 +1,5 @@
{
"domain": "aeptexas",
"domain": "aep_texas",
"name": "AEP Texas",
"integration_type": "virtual",
"supported_by": "opower"
@@ -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)
@@ -233,7 +228,12 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
@cached_property
def supported_features(self) -> AlarmControlPanelEntityFeature:
"""Return the list of supported features."""
return self._attr_supported_features
features = self._attr_supported_features
if type(features) is int: # noqa: E721
new_features = AlarmControlPanelEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@final
@property
@@ -244,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"]
}
@@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==1.10.0", "yalexs-ble==2.3.2"]
"requirements": ["yalexs==1.10.0", "yalexs-ble==2.4.0"]
}
@@ -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"]
}
+1 -1
View File
@@ -106,7 +106,7 @@
},
"exceptions": {
"integration_not_found": {
"message": "Integraion '{target}' not found in registry"
"message": "Integration '{target}' not found in registry"
},
"no_path": {
"message": "Can't write to directory {target}, no access to path!"
@@ -15,11 +15,11 @@
"quality_scale": "internal",
"requirements": [
"bleak==0.21.1",
"bleak-retry-connector==3.3.0",
"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.0"
"habluetooth==2.1.0"
]
}
+24 -6
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"
@@ -530,6 +527,19 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Flag supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> CameraEntityFeature:
"""Return the supported features as CameraEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = CameraEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@cached_property
def is_recording(self) -> bool:
"""Return true if the device is recording."""
@@ -570,7 +580,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""
if hasattr(self, "_attr_frontend_stream_type"):
return self._attr_frontend_stream_type
if CameraEntityFeature.STREAM not in self.supported_features:
if CameraEntityFeature.STREAM not in self.supported_features_compat:
return None
if self._rtsp_to_webrtc:
return StreamType.WEB_RTC
@@ -758,7 +768,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
async def _async_use_rtsp_to_webrtc(self) -> bool:
"""Determine if a WebRTC provider can be used for the camera."""
if CameraEntityFeature.STREAM not in self.supported_features:
if CameraEntityFeature.STREAM not in self.supported_features_compat:
return False
if DATA_RTSP_TO_WEB_RTC not in self.hass.data:
return False
@@ -1069,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())
+28 -9
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
@@ -227,6 +222,7 @@ CACHED_PROPERTIES_WITH_ATTR_ = {
"temperature_unit",
"current_humidity",
"target_humidity",
"hvac_mode",
"hvac_modes",
"hvac_action",
"current_temperature",
@@ -316,7 +312,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def capability_attributes(self) -> dict[str, Any] | None:
"""Return the capability attributes."""
supported_features = self.supported_features
supported_features = self.supported_features_compat
temperature_unit = self.temperature_unit
precision = self.precision
hass = self.hass
@@ -349,7 +345,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the optional state attributes."""
supported_features = self.supported_features
supported_features = self.supported_features_compat
temperature_unit = self.temperature_unit
precision = self.precision
hass = self.hass
@@ -414,7 +410,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the humidity we try to reach."""
return self._attr_target_humidity
@property
@cached_property
def hvac_mode(self) -> HVACMode | None:
"""Return hvac operation ie. heat, cool mode."""
return self._attr_hvac_mode
@@ -665,6 +661,19 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the list of supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> ClimateEntityFeature:
"""Return the supported features as ClimateEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = ClimateEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@cached_property
def min_temp(self) -> float:
"""Return the minimum temperature."""
@@ -720,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"]
}
+16 -7
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"
@@ -340,8 +337,12 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def supported_features(self) -> CoverEntityFeature:
"""Flag supported features."""
if self._attr_supported_features is not None:
return self._attr_supported_features
if (features := self._attr_supported_features) is not None:
if type(features) is int: # noqa: E721
new_features = CoverEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
supported_features = (
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP
@@ -480,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"]
@@ -489,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())
@@ -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"]
}
+1 -1
View File
@@ -52,7 +52,7 @@ class DuotecnoClimate(DuotecnoEntity, ClimateEntity):
_attr_translation_key = "duotecno"
@property
def current_temperature(self) -> int | None:
def current_temperature(self) -> float | None:
"""Get the current temperature."""
return self._unit.get_cur_temp()
@@ -7,5 +7,5 @@
"iot_class": "local_push",
"loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"],
"quality_scale": "silver",
"requirements": ["pyDuotecno==2023.11.1"]
"requirements": ["pyDuotecno==2024.1.1"]
}
@@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
from collections.abc import Iterable
from functools import lru_cache
import hashlib
from http import HTTPStatus
@@ -41,6 +42,7 @@ from homeassistant.components.light import (
ATTR_HS_COLOR,
ATTR_TRANSITION,
ATTR_XY_COLOR,
ColorMode,
LightEntityFeature,
)
from homeassistant.components.media_player import (
@@ -115,12 +117,19 @@ UNAUTHORIZED_USER = [
{"error": {"address": "/", "description": "unauthorized user", "type": "1"}}
]
DIMMABLE_SUPPORT_FEATURES = (
CoverEntityFeature.SET_POSITION
| FanEntityFeature.SET_SPEED
| MediaPlayerEntityFeature.VOLUME_SET
| ClimateEntityFeature.TARGET_TEMPERATURE
)
DIMMABLE_SUPPORTED_FEATURES_BY_DOMAIN = {
cover.DOMAIN: CoverEntityFeature.SET_POSITION,
fan.DOMAIN: FanEntityFeature.SET_SPEED,
media_player.DOMAIN: MediaPlayerEntityFeature.VOLUME_SET,
climate.DOMAIN: ClimateEntityFeature.TARGET_TEMPERATURE,
}
ENTITY_FEATURES_BY_DOMAIN = {
cover.DOMAIN: CoverEntityFeature,
fan.DOMAIN: FanEntityFeature,
media_player.DOMAIN: MediaPlayerEntityFeature,
climate.DOMAIN: ClimateEntityFeature,
}
@lru_cache(maxsize=32)
@@ -756,7 +765,6 @@ def _entity_unique_id(entity_id: str) -> str:
def state_to_json(config: Config, state: State) -> dict[str, Any]:
"""Convert an entity to its Hue bridge JSON representation."""
entity_features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
color_modes = state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES, [])
unique_id = _entity_unique_id(state.entity_id)
state_dict = get_entity_state_dict(config, state)
@@ -773,9 +781,9 @@ def state_to_json(config: Config, state: State) -> dict[str, Any]:
"manufacturername": "Home Assistant",
"swversion": "123",
}
color_supported = light.color_supported(color_modes)
color_temp_supported = light.color_temp_supported(color_modes)
is_light = state.domain == light.DOMAIN
color_supported = is_light and light.color_supported(color_modes)
color_temp_supported = is_light and light.color_temp_supported(color_modes)
if color_supported and color_temp_supported:
# Extended Color light (Zigbee Device ID: 0x0210)
# Same as Color light, but which supports additional setting of color temperature
@@ -820,9 +828,7 @@ def state_to_json(config: Config, state: State) -> dict[str, Any]:
HUE_API_STATE_BRI: state_dict[STATE_BRIGHTNESS],
}
)
elif entity_features & DIMMABLE_SUPPORT_FEATURES or light.brightness_supported(
color_modes
):
elif state_supports_hue_brightness(state, color_modes):
# Dimmable light (Zigbee Device ID: 0x0100)
# Supports groups, scenes, on/off and dimming
retval["type"] = "Dimmable light"
@@ -845,6 +851,21 @@ def state_to_json(config: Config, state: State) -> dict[str, Any]:
return retval
def state_supports_hue_brightness(
state: State, color_modes: Iterable[ColorMode]
) -> bool:
"""Return True if the state supports brightness."""
domain = state.domain
if domain == light.DOMAIN:
return light.brightness_supported(color_modes)
if not (required_feature := DIMMABLE_SUPPORTED_FEATURES_BY_DOMAIN.get(domain)):
return False
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
enum = ENTITY_FEATURES_BY_DOMAIN[domain]
features = enum(features) if type(features) is int else features # noqa: E721
return required_feature in features
def create_hue_success_response(
entity_number: str, attr: str, value: str
) -> dict[str, Any]:
@@ -5,12 +5,23 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
from .coordinator import EnergyZeroDataUpdateCoordinator
from .services import async_register_services
from .services import async_setup_services
PLATFORMS = [Platform.SENSOR]
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up EnergyZero services."""
async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -27,8 +38,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_register_services(hass, coordinator)
return True
@@ -9,6 +9,7 @@ from typing import Final
from energyzero import Electricity, Gas, VatOption
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@@ -17,11 +18,13 @@ from homeassistant.core import (
callback,
)
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import selector
from homeassistant.util import dt as dt_util
from .const import DOMAIN
from .coordinator import EnergyZeroDataUpdateCoordinator
ATTR_CONFIG_ENTRY: Final = "config_entry"
ATTR_START: Final = "start"
ATTR_END: Final = "end"
ATTR_INCL_VAT: Final = "incl_vat"
@@ -30,6 +33,11 @@ GAS_SERVICE_NAME: Final = "get_gas_prices"
ENERGY_SERVICE_NAME: Final = "get_energy_prices"
SERVICE_SCHEMA: Final = vol.Schema(
{
vol.Required(ATTR_CONFIG_ENTRY): selector.ConfigEntrySelector(
{
"integration": DOMAIN,
}
),
vol.Required(ATTR_INCL_VAT): bool,
vol.Optional(ATTR_START): str,
vol.Optional(ATTR_END): str,
@@ -75,12 +83,43 @@ def __serialize_prices(prices: Electricity | Gas) -> ServiceResponse:
}
def __get_coordinator(
hass: HomeAssistant, call: ServiceCall
) -> EnergyZeroDataUpdateCoordinator:
"""Get the coordinator from the entry."""
entry_id: str = call.data[ATTR_CONFIG_ENTRY]
entry: ConfigEntry | None = hass.config_entries.async_get_entry(entry_id)
if not entry:
raise ServiceValidationError(
f"Invalid config entry: {entry_id}",
translation_domain=DOMAIN,
translation_key="invalid_config_entry",
translation_placeholders={
"config_entry": entry_id,
},
)
if entry.state != ConfigEntryState.LOADED:
raise ServiceValidationError(
f"{entry.title} is not loaded",
translation_domain=DOMAIN,
translation_key="unloaded_config_entry",
translation_placeholders={
"config_entry": entry.title,
},
)
return hass.data[DOMAIN][entry_id]
async def __get_prices(
call: ServiceCall,
*,
coordinator: EnergyZeroDataUpdateCoordinator,
hass: HomeAssistant,
price_type: PriceType,
) -> ServiceResponse:
coordinator = __get_coordinator(hass, call)
start = __get_date(call.data.get(ATTR_START))
end = __get_date(call.data.get(ATTR_END))
@@ -108,22 +147,20 @@ async def __get_prices(
@callback
def async_register_services(
hass: HomeAssistant, coordinator: EnergyZeroDataUpdateCoordinator
):
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up EnergyZero services."""
hass.services.async_register(
DOMAIN,
GAS_SERVICE_NAME,
partial(__get_prices, coordinator=coordinator, price_type=PriceType.GAS),
partial(__get_prices, hass=hass, price_type=PriceType.GAS),
schema=SERVICE_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
ENERGY_SERVICE_NAME,
partial(__get_prices, coordinator=coordinator, price_type=PriceType.ENERGY),
partial(__get_prices, hass=hass, price_type=PriceType.ENERGY),
schema=SERVICE_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
@@ -1,5 +1,10 @@
get_gas_prices:
fields:
config_entry:
required: true
selector:
config_entry:
integration: energyzero
incl_vat:
required: true
default: true
@@ -17,6 +22,11 @@ get_gas_prices:
datetime:
get_energy_prices:
fields:
config_entry:
required: true
selector:
config_entry:
integration: energyzero
incl_vat:
required: true
default: true
@@ -12,6 +12,12 @@
"exceptions": {
"invalid_date": {
"message": "Invalid date provided. Got {date}"
},
"invalid_config_entry": {
"message": "Invalid config entry provided. Got {config_entry}"
},
"unloaded_config_entry": {
"message": "Invalid config entry provided. {config_entry} is not loaded."
}
},
"entity": {
@@ -50,6 +56,10 @@
"name": "Get gas prices",
"description": "Request gas prices from EnergyZero.",
"fields": {
"config_entry": {
"name": "Config Entry",
"description": "The config entry to use for this service."
},
"incl_vat": {
"name": "Including VAT",
"description": "Include VAT in the prices."
@@ -68,6 +78,10 @@
"name": "Get energy prices",
"description": "Request energy prices from EnergyZero.",
"fields": {
"config_entry": {
"name": "[%key:component::energyzero::services::get_gas_prices::fields::config_entry::name%]",
"description": "[%key:component::energyzero::services::get_gas_prices::fields::config_entry::description%]"
},
"incl_vat": {
"name": "[%key:component::energyzero::services::get_gas_prices::fields::incl_vat::name%]",
"description": "[%key:component::energyzero::services::get_gas_prices::fields::incl_vat::description%]"
@@ -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)},
+24 -6
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"
@@ -400,7 +397,7 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def capability_attributes(self) -> dict[str, list[str] | None]:
"""Return capability attributes."""
attrs = {}
supported_features = self.supported_features
supported_features = self.supported_features_compat
if (
FanEntityFeature.SET_SPEED in supported_features
@@ -415,7 +412,7 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def state_attributes(self) -> dict[str, float | str | None]:
"""Return optional state attributes."""
data: dict[str, float | str | None] = {}
supported_features = self.supported_features
supported_features = self.supported_features_compat
if FanEntityFeature.DIRECTION in supported_features:
data[ATTR_DIRECTION] = self.current_direction
@@ -439,6 +436,19 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Flag supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> FanEntityFeature:
"""Return the supported features as FanEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = FanEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@cached_property
def preset_mode(self) -> str | None:
"""Return the current preset mode, e.g., auto, smart, interval, favorite.
@@ -458,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())
+4 -4
View File
@@ -168,8 +168,8 @@ class FinTsClient:
if not account_information:
return False
if 1 <= account_information["type"] <= 9:
return True
if account_type := account_information.get("type"):
return 1 <= account_type <= 9
if (
account_information["iban"] in self.account_config
@@ -188,8 +188,8 @@ class FinTsClient:
if not account_information:
return False
if 30 <= account_information["type"] <= 39:
return True
if account_type := account_information.get("type"):
return 30 <= account_type <= 39
if (
account_information["iban"] in self.holdings_config
@@ -69,6 +69,8 @@ class FitbitOAuth2Implementation(AuthImplementation):
)
if err.status == HTTPStatus.UNAUTHORIZED:
raise FitbitAuthException(f"Unauthorized error: {err}") from err
if err.status == HTTPStatus.BAD_REQUEST:
raise FitbitAuthException(f"Bad Request error: {err}") from err
raise FitbitApiException(f"Server error response: {err}") from err
except aiohttp.ClientError as err:
raise FitbitApiException(f"Client connection error: {err}") from err
@@ -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==20231228.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
@@ -185,7 +180,7 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
ATTR_MAX_HUMIDITY: self.max_humidity,
}
if HumidifierEntityFeature.MODES in self.supported_features:
if HumidifierEntityFeature.MODES in self.supported_features_compat:
data[ATTR_AVAILABLE_MODES] = self.available_modes
return data
@@ -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
@@ -280,3 +275,26 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
def supported_features(self) -> HumidifierEntityFeature:
"""Return the list of supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> HumidifierEntityFeature:
"""Return the supported features as HumidifierEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = HumidifierEntityFeature(features)
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())
@@ -70,7 +70,7 @@ async def async_setup_entry(
config_entry.entry_id
]
entities = []
for controller in coordinator.data.controllers:
for controller in coordinator.data.controllers.values():
entities.append(
HydrawiseBinarySensor(coordinator, BINARY_SENSOR_STATUS, controller)
)
@@ -2,10 +2,11 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
from pydrawise import HydrawiseBase
from pydrawise.schema import User
from pydrawise.schema import Controller, User, Zone
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@@ -13,9 +14,20 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, LOGGER
class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[User]):
@dataclass
class HydrawiseData:
"""Container for data fetched from the Hydrawise API."""
user: User
controllers: dict[int, Controller]
zones: dict[int, Zone]
class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[HydrawiseData]):
"""The Hydrawise Data Update Coordinator."""
api: HydrawiseBase
def __init__(
self, hass: HomeAssistant, api: HydrawiseBase, scan_interval: timedelta
) -> None:
@@ -23,6 +35,13 @@ class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[User]):
super().__init__(hass, LOGGER, name=DOMAIN, update_interval=scan_interval)
self.api = api
async def _async_update_data(self) -> User:
async def _async_update_data(self) -> HydrawiseData:
"""Fetch the latest data from Hydrawise."""
return await self.api.get_user()
user = await self.api.get_user()
controllers = {}
zones = {}
for controller in user.controllers:
controllers[controller.id] = controller
for zone in controller.zones:
zones[zone.id] = zone
return HydrawiseData(user=user, controllers=controllers, zones=zones)
@@ -48,5 +48,8 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]):
@callback
def _handle_coordinator_update(self) -> None:
"""Get the latest data and updates the state."""
self.controller = self.coordinator.data.controllers[self.controller.id]
if self.zone:
self.zone = self.coordinator.data.zones[self.zone.id]
self._update_attrs()
super()._handle_coordinator_update()
+1 -1
View File
@@ -76,7 +76,7 @@ async def async_setup_entry(
]
async_add_entities(
HydrawiseSensor(coordinator, description, controller, zone)
for controller in coordinator.data.controllers
for controller in coordinator.data.controllers.values()
for zone in controller.zones
for description in SENSOR_TYPES
)
+1 -1
View File
@@ -81,7 +81,7 @@ async def async_setup_entry(
]
async_add_entities(
HydrawiseSwitch(coordinator, description, controller, zone)
for controller in coordinator.data.controllers
for controller in coordinator.data.controllers.values()
for zone in controller.zones
for description in SWITCH_TYPES
)
+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)
+50 -17
View File
@@ -345,11 +345,11 @@ def filter_turn_off_params(
light: LightEntity, params: dict[str, Any]
) -> dict[str, Any]:
"""Filter out params not used in turn off or not supported by the light."""
supported_features = light.supported_features
supported_features = light.supported_features_compat
if not supported_features & LightEntityFeature.FLASH:
if LightEntityFeature.FLASH not in supported_features:
params.pop(ATTR_FLASH, None)
if not supported_features & LightEntityFeature.TRANSITION:
if LightEntityFeature.TRANSITION not in supported_features:
params.pop(ATTR_TRANSITION, None)
return {k: v for k, v in params.items() if k in (ATTR_TRANSITION, ATTR_FLASH)}
@@ -357,13 +357,13 @@ def filter_turn_off_params(
def filter_turn_on_params(light: LightEntity, params: dict[str, Any]) -> dict[str, Any]:
"""Filter out params not supported by the light."""
supported_features = light.supported_features
supported_features = light.supported_features_compat
if not supported_features & LightEntityFeature.EFFECT:
if LightEntityFeature.EFFECT not in supported_features:
params.pop(ATTR_EFFECT, None)
if not supported_features & LightEntityFeature.FLASH:
if LightEntityFeature.FLASH not in supported_features:
params.pop(ATTR_FLASH, None)
if not supported_features & LightEntityFeature.TRANSITION:
if LightEntityFeature.TRANSITION not in supported_features:
params.pop(ATTR_TRANSITION, None)
supported_color_modes = (
@@ -989,7 +989,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def capability_attributes(self) -> dict[str, Any]:
"""Return capability attributes."""
data: dict[str, Any] = {}
supported_features = self.supported_features
supported_features = self.supported_features_compat
supported_color_modes = self._light_internal_supported_color_modes
if ColorMode.COLOR_TEMP in supported_color_modes:
@@ -1007,7 +1007,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
data[ATTR_MAX_MIREDS] = color_util.color_temperature_kelvin_to_mired(
self.min_color_temp_kelvin
)
if supported_features & LightEntityFeature.EFFECT:
if LightEntityFeature.EFFECT in supported_features:
data[ATTR_EFFECT_LIST] = self.effect_list
data[ATTR_SUPPORTED_COLOR_MODES] = sorted(supported_color_modes)
@@ -1061,8 +1061,9 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def state_attributes(self) -> dict[str, Any] | None:
"""Return state attributes."""
data: dict[str, Any] = {}
supported_features = self.supported_features
supported_features = self.supported_features_compat
supported_color_modes = self._light_internal_supported_color_modes
supported_features_value = supported_features.value
color_mode = self._light_internal_color_mode if self.is_on else None
if color_mode and color_mode not in supported_color_modes:
@@ -1081,7 +1082,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
data[ATTR_BRIGHTNESS] = self.brightness
else:
data[ATTR_BRIGHTNESS] = None
elif supported_features & SUPPORT_BRIGHTNESS:
elif supported_features_value & SUPPORT_BRIGHTNESS:
# Backwards compatibility for ambiguous / incomplete states
# Add warning in 2021.6, remove in 2021.10
if self.is_on:
@@ -1103,7 +1104,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
else:
data[ATTR_COLOR_TEMP_KELVIN] = None
data[ATTR_COLOR_TEMP] = None
elif supported_features & SUPPORT_COLOR_TEMP:
elif supported_features_value & SUPPORT_COLOR_TEMP:
# Backwards compatibility
# Add warning in 2021.6, remove in 2021.10
if self.is_on:
@@ -1133,7 +1134,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if color_mode:
data.update(self._light_internal_convert_color(color_mode))
if supported_features & LightEntityFeature.EFFECT:
if LightEntityFeature.EFFECT in supported_features:
data[ATTR_EFFECT] = self.effect if self.is_on else None
return data
@@ -1146,14 +1147,15 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
# Backwards compatibility for supported_color_modes added in 2021.4
# Add warning in 2021.6, remove in 2021.10
supported_features = self.supported_features
supported_features = self.supported_features_compat
supported_features_value = supported_features.value
supported_color_modes: set[ColorMode] = set()
if supported_features & SUPPORT_COLOR_TEMP:
if supported_features_value & SUPPORT_COLOR_TEMP:
supported_color_modes.add(ColorMode.COLOR_TEMP)
if supported_features & SUPPORT_COLOR:
if supported_features_value & SUPPORT_COLOR:
supported_color_modes.add(ColorMode.HS)
if supported_features & SUPPORT_BRIGHTNESS and not supported_color_modes:
if not supported_color_modes and supported_features_value & SUPPORT_BRIGHTNESS:
supported_color_modes = {ColorMode.BRIGHTNESS}
if not supported_color_modes:
@@ -1170,3 +1172,34 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def supported_features(self) -> LightEntityFeature:
"""Flag supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> LightEntityFeature:
"""Return the supported features as LightEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is not int: # noqa: E721
return features
new_features = LightEntityFeature(features)
if self._deprecated_supported_features_reported is True:
return new_features
self._deprecated_supported_features_reported = True
report_issue = self._suggest_report_issue()
report_issue += (
" and reference "
"https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation"
)
_LOGGER.warning(
(
"Entity %s (%s) is using deprecated supported features"
" values which will be removed in HA Core 2025.1. Instead it should use"
" %s and color modes, please %s"
),
self.entity_id,
type(self),
repr(new_features),
report_issue,
)
return new_features
+15 -5
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
@@ -278,7 +275,12 @@ class LockEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@cached_property
def supported_features(self) -> LockEntityFeature:
"""Return the list of supported features."""
return self._attr_supported_features
features = self._attr_supported_features
if type(features) is int: # noqa: E721
new_features = LockEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
async def async_internal_added_to_hass(self) -> None:
"""Call when the sensor entity is added to hass."""
@@ -310,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,
)
)
@@ -766,6 +766,19 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Flag media player features that are supported."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> MediaPlayerEntityFeature:
"""Return the supported features as MediaPlayerEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = MediaPlayerEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
def turn_on(self) -> None:
"""Turn the media player on."""
raise NotImplementedError()
@@ -905,85 +918,87 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def support_play(self) -> bool:
"""Boolean if play is supported."""
return MediaPlayerEntityFeature.PLAY in self.supported_features
return MediaPlayerEntityFeature.PLAY in self.supported_features_compat
@final
@property
def support_pause(self) -> bool:
"""Boolean if pause is supported."""
return MediaPlayerEntityFeature.PAUSE in self.supported_features
return MediaPlayerEntityFeature.PAUSE in self.supported_features_compat
@final
@property
def support_stop(self) -> bool:
"""Boolean if stop is supported."""
return MediaPlayerEntityFeature.STOP in self.supported_features
return MediaPlayerEntityFeature.STOP in self.supported_features_compat
@final
@property
def support_seek(self) -> bool:
"""Boolean if seek is supported."""
return MediaPlayerEntityFeature.SEEK in self.supported_features
return MediaPlayerEntityFeature.SEEK in self.supported_features_compat
@final
@property
def support_volume_set(self) -> bool:
"""Boolean if setting volume is supported."""
return MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
return MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat
@final
@property
def support_volume_mute(self) -> bool:
"""Boolean if muting volume is supported."""
return MediaPlayerEntityFeature.VOLUME_MUTE in self.supported_features
return MediaPlayerEntityFeature.VOLUME_MUTE in self.supported_features_compat
@final
@property
def support_previous_track(self) -> bool:
"""Boolean if previous track command supported."""
return MediaPlayerEntityFeature.PREVIOUS_TRACK in self.supported_features
return MediaPlayerEntityFeature.PREVIOUS_TRACK in self.supported_features_compat
@final
@property
def support_next_track(self) -> bool:
"""Boolean if next track command supported."""
return MediaPlayerEntityFeature.NEXT_TRACK in self.supported_features
return MediaPlayerEntityFeature.NEXT_TRACK in self.supported_features_compat
@final
@property
def support_play_media(self) -> bool:
"""Boolean if play media command supported."""
return MediaPlayerEntityFeature.PLAY_MEDIA in self.supported_features
return MediaPlayerEntityFeature.PLAY_MEDIA in self.supported_features_compat
@final
@property
def support_select_source(self) -> bool:
"""Boolean if select source command supported."""
return MediaPlayerEntityFeature.SELECT_SOURCE in self.supported_features
return MediaPlayerEntityFeature.SELECT_SOURCE in self.supported_features_compat
@final
@property
def support_select_sound_mode(self) -> bool:
"""Boolean if select sound mode command supported."""
return MediaPlayerEntityFeature.SELECT_SOUND_MODE in self.supported_features
return (
MediaPlayerEntityFeature.SELECT_SOUND_MODE in self.supported_features_compat
)
@final
@property
def support_clear_playlist(self) -> bool:
"""Boolean if clear playlist command supported."""
return MediaPlayerEntityFeature.CLEAR_PLAYLIST in self.supported_features
return MediaPlayerEntityFeature.CLEAR_PLAYLIST in self.supported_features_compat
@final
@property
def support_shuffle_set(self) -> bool:
"""Boolean if shuffle is supported."""
return MediaPlayerEntityFeature.SHUFFLE_SET in self.supported_features
return MediaPlayerEntityFeature.SHUFFLE_SET in self.supported_features_compat
@final
@property
def support_grouping(self) -> bool:
"""Boolean if player grouping is supported."""
return MediaPlayerEntityFeature.GROUPING in self.supported_features
return MediaPlayerEntityFeature.GROUPING in self.supported_features_compat
async def async_toggle(self) -> None:
"""Toggle the power on the media player."""
@@ -1012,7 +1027,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if (
self.volume_level is not None
and self.volume_level < 1
and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat
):
await self.async_set_volume_level(
min(1, self.volume_level + self.volume_step)
@@ -1030,7 +1045,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if (
self.volume_level is not None
and self.volume_level > 0
and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features
and MediaPlayerEntityFeature.VOLUME_SET in self.supported_features_compat
):
await self.async_set_volume_level(
max(0, self.volume_level - self.volume_step)
@@ -1073,7 +1088,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def capability_attributes(self) -> dict[str, Any]:
"""Return capability attributes."""
data: dict[str, Any] = {}
supported_features = self.supported_features
supported_features = self.supported_features_compat
if (
source_list := self.source_list
@@ -1280,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}")
@@ -12,5 +12,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["pyatmo"],
"requirements": ["pyatmo==8.0.1"]
"requirements": ["pyatmo==8.0.2"]
}
@@ -170,7 +170,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DATA_HASS_CONFIG] = config
if lte_config := config.get(DOMAIN):
await hass.async_create_task(import_yaml(hass, lte_config))
hass.async_create_task(import_yaml(hass, lte_config))
return True
+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,13 @@ 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"])
return len(
[
torrent

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