Compare commits

...

472 Commits

Author SHA1 Message Date
jbouwh 4ad5ce6d52 Set climate state to off if is_on is False 2025-01-01 13:17:57 +00:00
Jan Bouwhuis a117a3cba9 Update homeassistant/components/climate/__init__.py
Co-authored-by: Allen Porter <allen@thebends.org>
2024-12-31 21:52:52 +01:00
jbouwh e2d9ca9cd9 Allow a climate entity to have an independent on / off state attribute 2024-12-30 21:57:19 +00:00
Dan Raper a0fb6df5ba Add battery sensor to ohme (#134222)
* Add battery sensor to ohme

* Forgot the snapshots!

* Add translation key to battery

* Change car to vehicle and fix snapshot tests

* Fix snapshot again - not sure what was going on with my local dev env
2024-12-30 20:15:11 +01:00
Bram Kragten 04020d5a56 Update frontend to 20241230.0 (#134284) 2024-12-30 20:04:50 +01:00
Norbert Rittel f785b17314 Fix two descriptions of yeelight actions (#134282) 2024-12-30 19:22:12 +01:00
Andrew Jackson 6631c57cfb Bump aiomealie to 0.9.5 (#134274) 2024-12-30 17:47:58 +01:00
Norbert Rittel bc76dc3c34 Remove excessive period at end of action name (#134272) 2024-12-30 16:22:30 +01:00
Ludovic BOUÉ ea4931ca3a Bump Python Matter server to 7.0.0 (Matter 1.4) (#132502)
* Matter 1.4 rename BridgedDevice device type

BREAKING change in the client: BridgedDevice is renamed to BridgedNode in the device types with Matter 1.4

* `ColorMode` enum type is renamed to `ColorModeEnum`

* Item `ColorTemperature` renamed to `ColorTemperatureMireds`

* Update ColorControl bitmaps and attributes

* Bump Python Matter server to 7.0.0 (Matter 1.4)

* Bump requirements to Python Matter server to 7.0.0
2024-12-30 15:41:14 +01:00
Arne Keller dd20204bf0 ollama: update to 0.4.5 (#134265) 2024-12-30 14:42:46 +01:00
Norbert Rittel ef46c62bc6 Make triggers and condition for monetary sensor consistent (#131184) 2024-12-30 13:47:16 +01:00
Alberto Geniola 2bb6e03a36 Bump elmax-api (#133845) 2024-12-30 13:46:53 +01:00
G Johansson 2288f89415 Fix duplicate sensor disk entities in Systemmonitor (#134139) 2024-12-30 13:38:48 +01:00
Josef Zweck e7ab5afc14 Bump pylamarzocco to 1.4.5 (#134259)
* Bump pylamarzocco to 1.4.4

* Bump pylamarzocco to 1.4.5

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-30 12:27:32 +01:00
Adam Goode 4db88dfaff Quickly process unavailable metrics in Prometheus (#133219) 2024-12-30 12:05:33 +01:00
Joost Lekkerkerker 906c95048c Record LG WebOS TV Quality scale (#133732)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2024-12-30 11:45:44 +01:00
Manu df38c1b1d7 Remove deprecated yaml import from OTP integration (#134196) 2024-12-30 11:12:16 +01:00
tronikos af97bf1c5f Fix 400 This voice does not support speaking rate or pitch parameters at this time for Google Cloud Journey voices (#134255) 2024-12-30 09:20:35 +01:00
tronikos a7c2d96ecf Avoid KeyError for ignored entries in async_step_zeroconf of Android TV Remote (#134250) 2024-12-30 10:13:51 +02:00
Noah Husby 1b06b4e45b Remove unused translations from Russound RIO (#134246) 2024-12-30 10:11:37 +02:00
Manu b74b9bc360 Bump habiticalib to v0.3.2 (#134244) 2024-12-30 10:10:18 +02:00
Brett Adams 810689ce66 Handle missing application credentials in Tesla Fleet (#134237)
* Handle missing application credentials

* Add tests

* Test reauth starts

* Only catch ValueError
2024-12-29 22:21:18 -08:00
G Johansson 249d93574a Set Scrape sensor unavailable when errors (#134143) 2024-12-29 22:59:57 +01:00
Michael e2c59f276a Bump aiopegelonline to 0.1.1 (#134230)
bump aiopegelonline to 0.1.1
2024-12-29 21:36:49 +01:00
Manu 9804e8aa98 Add reauth flow to Habitica integration (#131676)
* Add reauth flow to Habitica integration

* tests, invalid_credentials string

* test only api_key

* section consts

* test config entry

* test reauth is triggered

* set reauthentication-flow to done

* use consts in tests

* reauth_entry

* changes

* fix import

* changes
2024-12-29 21:12:36 +01:00
Paul Daumlechner 53e69af088 Bump pyvlx to 0.2.26 (#115483) 2024-12-29 10:00:26 -10:00
tronikos 1530edbe20 Bump opower to 0.8.7 (#134228)
* Bump opower to 0.8.7

* update deps
2024-12-29 11:44:33 -08:00
Paulus Schoutsen 7dbf32d693 Bump frontend to 20241229.0 (#134225) 2024-12-29 13:35:46 -05:00
Michael Hansen 49646ad994 Bump VoIP utils to 0.2.2 (#134219) 2024-12-29 11:56:27 -06:00
G Johansson 1e652db37f Use config entry runtime data in Open-Meteo (#134198) 2024-12-29 18:16:41 +01:00
Dan Raper 88d366b0c5 Add slot list service to ohme (#134170)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-29 18:07:12 +01:00
Lucas Gasenzer 65147f8d4c Fix Wake on LAN Port input as Box instead of Slider (#134216) 2024-12-29 18:03:41 +01:00
Simone Chemelli 52b919101a Bump aiocomelit to 0.10.1 (#134214) 2024-12-29 17:30:52 +01:00
Aaron Bach 24fd74d839 Change SimpliSafe websocket reconnection log to DEBUG-level (#134063)
* Change SimpliSafe websocket reconnection log to `DEBUG`-level

* revert
2024-12-29 11:23:44 -05:00
Marc Mueller 2599faa622 Fix method subtyping [helpers] (#134213) 2024-12-29 17:16:38 +01:00
Marc Mueller 3df91cfba5 Fix method subtyping [recorder] (#134212) 2024-12-29 17:16:11 +01:00
Marc Mueller d3fab42c85 Fix method subtyping [knx] (#134211) 2024-12-29 16:41:23 +01:00
Marc Mueller beb881492a Fix method subtyping [elkm1] (#134210) 2024-12-29 16:40:51 +01:00
Matthias Alphart 9d7c7f9fcf Update knx-frontend to 2024.12.26.233449 (#134184) 2024-12-29 16:39:37 +01:00
Shay Levy 419307a7c4 Bump aioswitcher to 6.0.0 (#134185) 2024-12-29 15:42:33 +01:00
G Johansson 409dc4ad48 Move coordinator to own file in Open-Meteo (#134197) 2024-12-29 15:25:40 +01:00
Michael 7704ef95a4 Make feedreader recoverable (#134202)
raise ConfigEntryNotReady on connection errors during setup
2024-12-29 15:08:15 +01:00
Manu 0db07a033b Migrate Habitica integration to habiticalib (#131032)
* Migrate data to habiticalib

* Add habiticalib to init and coordinator

* Migrate Habitica config flow to habiticalib

* migrate init to habiticalib

* migrate buttons to habiticalib

* migrate switch to habiticalib

* update habiticalib

* cast_skill action

* migrate update_score

* migrate transformation items action

* migrate quest actions

* fix fixture errors

* Migrate coordinator data and content

* bump habiticalib

* Remove habitipy and use wrapper in habiticalub

* changes

* some fixes

* minor refactoring

* class_needed annotation

* Update diagnostics

* do integration setup in coordinator setup

* small changes

* raise HomeAssistantError for TooManyRequestsError

* fix docstring

* update tests

* changes to tests/snapshots

* fix update_todo_item
2024-12-29 15:00:31 +01:00
Joost Lekkerkerker 4717eb3142 Bump python-overseerr to 0.4.0 (#134192) 2024-12-29 15:46:30 +02:00
Joost Lekkerkerker c23f5c9f2c Make elevenlabs recoverable (#134094)
* Make elevenlabs recoverable

* Add tests for entry setup

* Use the same fixtures for setup and config flow

* Update tests/components/elevenlabs/test_setup.py

Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>

---------

Co-authored-by: Simon Sorg <simon.sorg@student.hpi.de>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>
2024-12-29 14:26:59 +01:00
Michael 873b078bb3 Make PEGELONLINE recoverable (#134199) 2024-12-29 14:07:45 +01:00
Manu 0dd93a18c5 Add button platform to IronOS integration (#133678)
* Add button platform to IronOS integration

* Add tests

* load platform

* refactor

* update tests
2024-12-29 12:39:13 +01:00
Maikel Punie da96e2077b Add Velbus Button tests (#134186)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-29 11:55:52 +01:00
Manu 1d69cf11a5 Bump pynecil to v3.0.1 (#134174) 2024-12-29 10:06:29 +02:00
Manu adb1fbbbc4 Add switch platform to IronOS integration (#133691)
* Add switch platform

* Add tests

* prevent switch bouncing

* some changes

* icons

* update tests

* changes
2024-12-28 21:59:06 +01:00
G Johansson 645f2e44b9 Fix Nord Pool empty response (#134033)
* Fix Nord Pool empty response

* Mods

* reset validate prices
2024-12-28 21:38:04 +01:00
Artur Pragacz b3aede611a Fix Onkyo volume rounding (#134157) 2024-12-28 21:34:01 +01:00
jb101010-2 72a96249b1 Suez_water: clear quality scale (#134027)
* Suez_water: clear quality scale

Revert invalid done rules and mark inapplicable ones as exempted.

* Mark entity disabled as todo

* Mark devices as todo

* missing push

* Update homeassistant/components/suez_water/quality_scale.yaml

Co-authored-by: Josef Zweck <josef@zweck.dev>

* Update quality_scale.yaml

* Update quality_scale.yaml again

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2024-12-28 20:59:11 +01:00
Joost Lekkerkerker 80dbce14ec Add binary sensor to Tile (#134153) 2024-12-28 16:49:14 +01:00
Manu 0376f75ee3 Bump pynecil to v3.0.0 (#134151) 2024-12-28 16:48:28 +01:00
jb101010-2 e58bd62c68 Suez_water: use meter id as unique_id (#133959)
* Suez_water: use meter id as unique_id

* Review fixes

* No more afraid check :)

* review again

* Apply suggestions from code review

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2024-12-28 16:25:10 +01:00
Matthias Alphart 6dbcd130b0 Add quality_scale.yaml for KNX (#133937)
* Add quality_scale.yaml

* Update quality_scale.yaml
2024-12-28 16:24:49 +01:00
Andrew Jackson 4639f57014 Remove deprecated Mastodon yaml config import (#134040)
* Remove Mastodon yaml import

* Revert removal of async_migrate_entry
2024-12-28 16:22:32 +01:00
G Johansson 4080455c12 Use x,y in roborock action call (#134133)
* Use x,y in roborock action call

* Fix description
2024-12-28 16:12:09 +01:00
Joost Lekkerkerker df7d518f38 Add versions to Tile device (#134150)
* Add versions to Tile device

* Add versions to Tile device
2024-12-28 16:04:36 +01:00
Joost Lekkerkerker 47adfb574f Bump python-overseerr to 0.3.0 (#134147)
Bump Overseerr to 0.3.0
2024-12-28 15:44:15 +01:00
Joost Lekkerkerker 4c5d0c2ec4 Add Tile device tracker tests (#134137) 2024-12-28 15:36:56 +01:00
G Johansson 4febe43021 Add missing device classes in scrape (#134141) 2024-12-28 15:36:23 +01:00
Maikel Punie af13979855 Add Velbus binary sensor tests (#134132)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-28 14:57:48 +01:00
Marc Mueller d9f2140df3 Add ClassVar annotation for singleton patterns (#134135) 2024-12-28 13:17:15 +01:00
Joost Lekkerkerker cc80108629 Bump yt-dlp to 2024.12.23 (#134131) 2024-12-28 13:13:07 +01:00
Joost Lekkerkerker 16af76b968 Add Tile device tests (#134138) 2024-12-28 13:10:13 +01:00
Joost Lekkerkerker 590f0ce61f Refactor Tile tests (#134130) 2024-12-28 12:37:21 +01:00
Allen Porter 14059c6df8 Remove unused parameters from function calls in rainbird (#134124)
Remove unused parameters from rainbird function calls
2024-12-28 11:34:27 +00:00
Joost Lekkerkerker 268c21addd Add Overseerr integration (#133981)
* Add Overseerr integration

* Add Overseerr integration

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix
2024-12-28 11:50:36 +01:00
Andre Lengwenus 565fa4ea1f Remove incorrect device check in LCN events (#134116) 2024-12-28 09:26:49 +01:00
Raj Laud 28cd7f2473 Bump pysqueezebox to v0.11.1 (#134097) 2024-12-28 09:24:22 +01:00
Noah Husby aceb1b39ba Add mute support to Russound RIO (#134118) 2024-12-28 09:22:13 +01:00
Allen Porter 6edf06f8a4 Converge stream av open methods, options, and error handling (#134020)
* Converge stream av open methods, options, and error handling

* Remove exception that is never thrown

* Update exceptions thrown in generic tests

* Increase stream test coverage
2024-12-27 18:47:33 -08:00
Noah Husby 07ae9b15d0 Bump aiorussound to 4.2.0 (#134117) 2024-12-27 18:23:57 -08:00
G Johansson d676169b04 Cleanup devices in Nord Pool from reconfiguration (#134043)
* Cleanup devices in Nord Pool from reconfiguration

* Mods

* Mod
2024-12-27 21:33:37 +01:00
Noah Husby 24ce3d7daa Remove deprecated yaml import for Russound RIO (#134072) 2024-12-27 21:27:33 +01:00
Joost Lekkerkerker 417e736746 Migrate Tile to use entry.runtime_data (#134107) 2024-12-27 21:25:36 +01:00
Cyrill Raccaud bb8d4ca255 Add unit test for sensors in swiss public transport (#134115)
* add unit test for sensors

* clean up
2024-12-27 21:21:45 +01:00
Joost Lekkerkerker 375af6cb1c Introduce base entity for Tile (#134109) 2024-12-27 21:18:01 +01:00
Jan Bouwhuis 263e0acd3a Set PARALLEL_UPDATES for incomfort entity platforms (#134110) 2024-12-27 20:43:30 +01:00
Erwin Douna da531d0e4e Bump Tado to 0.18.5 (#133988) 2024-12-27 20:26:19 +01:00
Joost Lekkerkerker 844e36c8fe Bump python-homeassistant-analytics to 0.8.1 (#134101) 2024-12-27 20:21:12 +01:00
Joost Lekkerkerker 9976c07f89 Remove YAML import from Tile (#134108) 2024-12-27 20:15:48 +01:00
Aaron Bach 7df9d2e938 Bump pytile to 2024.12.0 (#134103) 2024-12-27 20:04:35 +01:00
Joost Lekkerkerker 52318f5f37 Extract Tile coordinator in separate file (#134104) 2024-12-27 19:30:13 +01:00
Joost Lekkerkerker b9c2b3f7e3 Remove Tile unique id migration (#134106) 2024-12-27 19:25:10 +01:00
Andrew Sayre a9ff5b8007 Bump pyheos to v0.8.0 (#134069)
Bump pyheos and update usage
2024-12-27 11:01:35 -06:00
Joost Lekkerkerker 7076ba7c9d Make google tasks recoverable (#134092) 2024-12-27 08:52:33 -08:00
Josef Zweck 5e0088feaa Add azure_data_explorer to microsoft brand (#134088) 2024-12-27 15:36:07 +01:00
Franck Nijhof f8399b2c0f Revert "Add state_class to EcoWittSensorTypes.DEGREE" (#134079) 2024-12-27 13:17:47 +01:00
Matthias Alphart 415fdf4956 Fix KNX config flow translations and add data descriptions (#134078)
* Fix KNX config flow translations and add data descriptions

* Update strings.json

* typo
2024-12-27 12:59:52 +01:00
Noah Husby ad89004189 Remove timeout from Russound RIO initialization (#134070) 2024-12-27 11:01:10 +01:00
Noah Husby b6afbe4b29 Bump aiorussound to 4.1.1 (#134058)
* Bump aiorussound to 4.1.1

* Trigger Build

* Trigger Build
2024-12-26 22:03:50 -06:00
Cyrill Raccaud 402340955e Fix swiss public transport line field none (#133964)
* fix #133116

The line can theoretically be none, when no line info is available (lets say walking sections first?)

* fix line field

* add unit test with missing line field
2024-12-27 00:24:47 +01:00
Raphael Hehl b2a160d926 Roborock Add vacuum_goto service (#133994)
* Roborock Add vacuum_goto service to control vacuum movement to specified coordinates

* roborock Add type specification for x_coord and y_coord in vacuum_goto service

* roborock Add get_current_position service to retrieve vacuum's current coordinates

* Rename vacuum services for clarity and consistency

* Apply suggestions from code review

Co-authored-by: G Johansson <goran.johansson@shiftit.se>

* Add integration field to vacuum service targets for Roborock

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2024-12-27 00:20:09 +01:00
Thomas Kunzfeld 9840785363 Add state_class to EcoWittSensorTypes.DEGREE (#134004)
Add state_class to EcoWittSensorTypes.DEGREE (#129260)
2024-12-27 00:12:54 +01:00
jb101010-2 a53c92d4b5 Suez_water: remove redundant log on refresh failure (#134025)
Suez_water: remove redundent log on refresh failure
2024-12-27 00:05:28 +01:00
Marc Mueller adc97b6c15 Fix unifiprotect DeprecationWarnings in tests (#134060) 2024-12-26 23:50:03 +01:00
Jan Bouwhuis 7b2a5d0684 Remove mqtt publish templates after 6 months of deprecation (#134056) 2024-12-26 23:25:44 +01:00
Diogo Gomes acb511d395 Bump pyipma to 3.0.8 (#134055)
bump pyipma
2024-12-26 21:01:53 +00:00
Norbert Rittel c025390c6c Replace "service" with "action" plus fixed descriptions (#134053) 2024-12-26 15:39:18 -05:00
J. Nick Koston 942fbdedcf Ensure all states have been migrated to use timestamps (#134007) 2024-12-26 07:48:55 -10:00
Allen Porter 3bfb6707e9 Fix Nest ConfigEntry typing (#134021) 2024-12-26 09:27:20 -08:00
Norbert Rittel 5172139579 Use correct uppercase for abbreviations (#134028)
Fix the spelling of "SSDP" and "MAC" (address) to ensure proper translations.
2024-12-26 11:09:30 +01:00
Norbert Rittel cfb43c7b58 Fix typo in get_command action description (#134026) 2024-12-26 09:56:08 +01:00
Allen Porter 45657ece7c Improve Google Tasks error messages (#134023) 2024-12-26 09:53:20 +01:00
Erwin Douna f7fe2f2122 Tado update code owners (#133987)
Update code owners
2024-12-26 09:13:24 +01:00
Allen Porter c75222e63c Bump python-google-nest-sdm to 7.0.0 (#134016)
Update python-google-nest-sdm to 7.0.0
2024-12-26 00:03:44 -05:00
Brett Adams 299250ebec Bump Tesla Fleet API library (#134019)
Bump Tesla Fleet
2024-12-25 23:26:55 -05:00
Josef Zweck ed8e242049 Bump pylamarzocco to 1.4.3 (#134008) 2024-12-25 16:25:13 -08:00
Cyrill Raccaud 95e4a40ad5 Update silver docs for swiss public transport (#134001)
update docs
2024-12-25 21:36:30 +01:00
Christopher Fenner e61717ce7a Fulfill IQS rule docs-removal-instructions in ViCare integration (#133982)
update iqs state
2024-12-25 15:30:33 +01:00
Cyrill Raccaud 73b6bd8bd3 Add config flow data description to swiss public transport (#133997)
* add config flow data description

* improve strings
2024-12-25 15:20:09 +01:00
Cyrill Raccaud 60774c69cd Add clear shopping list button for Cookidoo (#133583)
* add clear button

* set clear button to disabled per default

* add actions exception
2024-12-25 14:58:19 +01:00
Cyrill Raccaud c383b41a12 Add parallel updates to swiss public transport (#133996)
add parallel updates
2024-12-25 14:55:34 +01:00
J. Nick Koston 05a8b773b9 Bump numpy to 2.2.1 (#133844)
* Bump numpy to 2.2.1

changelog: https://github.com/numpy/numpy/compare/v2.2.0...v2.2.1

* make sure ninja is up to date

* Revert "make sure ninja is up to date"

This reverts commit a26dd8b768.

* test

* Revert "test"

This reverts commit 972f40e3ee.

* try a single build

* try a single build

* Revert "Revert "test""

This reverts commit ec282ce021.

* Revert "Revert "Revert "test"""

This reverts commit 315599cbae.

* Revert "try a single build"

This reverts commit 63529dd2c5.

* Revert "try a single build"

This reverts commit 7058ae9288.
2024-12-25 11:27:00 +02:00
G-Two 1bee423c22 Bump subarulink to 0.7.13 (#133970) 2024-12-25 10:13:04 +02:00
Marc Mueller 687afd23bc Add pip wheel build constraints to fix numpy builds (#133962) 2024-12-24 15:06:21 -10:00
cdnninja 0020c48a15 Update pyvesync version (#131433) 2024-12-24 17:51:40 +01:00
Bram Kragten 760cbcc596 Update frontend to 20241224.0 (#133963) 2024-12-24 16:41:36 +01:00
Philipp Danner da8f4e5b57 fix "Slow" response leads to "Could not find a charging station" #124129 (#133889)
fix #124129
2024-12-24 14:00:34 +01:00
Claudio Ruggeri - CR-Tech 5c0659c8df Fix reload modbus component issue (#133820)
fix issue 116675
2024-12-24 13:57:18 +01:00
Marc Mueller 15806c2af6 Update Jinja2 to 3.1.5 (#133951) 2024-12-24 13:44:09 +01:00
Maikel Punie 97d8d16cc5 Bump velbusaio to 2024.12.3 (#133939) 2024-12-24 12:35:22 +02:00
Khole 33435fa36f Hive: Fix error when device goes offline (#133848) 2024-12-24 10:42:35 +01:00
Joost Lekkerkerker 6fc1cfded9 Use SignedSession in Xbox (#133938) 2024-12-24 10:17:02 +01:00
Franck Nijhof a9d6a42781 Update apprise to v1.9.1 (#133936) 2024-12-24 10:15:21 +01:00
Kevin Worrel f2a706ecf7 Make screenlogic state enums lowercase (#133866) 2024-12-24 09:12:18 +01:00
G-Two 4a2ae7f6fd Stop using shared aiohttp client session for Subaru integration (#133931) 2024-12-24 08:59:51 +01:00
Franck Nijhof 771ead9d7b Prevent imports from tests in core codebase (#133928)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-12-24 08:40:05 +01:00
Jordi 2d5e2aa4b4 Add Harvey virtual integration (#133874)
Add harvey virtual integration
2024-12-24 08:01:50 +01:00
Franck Nijhof 6f11524b84 Remove myself as codeowner from Tuya integration (#133921) 2024-12-24 07:55:44 +01:00
Dave T 561f319e3b Fix missing % in string for generic camera (#133925)
Fix missing % in generic camera string
2024-12-24 07:45:13 +01:00
Franck Nijhof 0c9ec4b699 Fix Peblar import in data coordinator (#133926) 2024-12-24 07:42:48 +01:00
Brett Adams cbb2930805 Slow down polling in Teslemetry (#133924) 2024-12-24 01:59:36 +01:00
Franck Nijhof aa29a93fbe Remove myself as codeowner from Plugwise (#133920) 2024-12-24 01:34:23 +01:00
J. Nick Koston ff4ba553c4 Sort integration platforms preload list (#133905)
* Sort integration platforms preload list

https://github.com/home-assistant/core/pull/133856#discussion_r1895385026

* sort

* Sort them all

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2024-12-23 23:38:59 +01:00
Thomas55555 2f101c5054 Catch ClientConnectorError and TimeOutError in APSystems (#132027) 2024-12-23 22:49:59 +01:00
karwosts 72e2b835d9 Fix a history stats bug when window and tracked state change simultaneously (#133770) 2024-12-23 22:47:26 +01:00
Martin Mrazik 8f6e4cd294 Map RGB+CCT to RGB for WLED (#133900) 2024-12-23 22:26:38 +01:00
Mick Vleeshouwer bd0edd4996 Revise codeowners for Overkiz (#133784) 2024-12-23 22:24:22 +01:00
J. Nick Koston 3f441e7090 Ensure cloud and recorder backup platforms do not have to wait for the import executor (#133907)
* Ensure cloud and recorder backup platforms do not have to wait for the import executor

partially fixes #133904

* backup.backup as well
2024-12-23 22:19:28 +01:00
Abílio Costa 253098d79c Mark missing IQS requirements for Idasen Desk as done (#133910) 2024-12-23 21:38:27 +01:00
Abílio Costa 53ebf84339 Add cronsim to default dependencies (#133913) 2024-12-23 21:34:36 +01:00
J. Nick Koston 7cfbc3eeae Fix duplicate call to async_register_preload_platform (#133909) 2024-12-23 09:20:44 -10:00
Franck Nijhof 8d32531bc1 Bump version to 2025.2.0dev0 (#133893) 2024-12-23 17:54:32 +01:00
Simon 30d95f37d8 Add removal instructions to ElevenLabs (#133895) 2024-12-23 18:37:19 +02:00
Michael bbb5f9e717 Preload supported color properties in fritzbox lights (#133798) 2024-12-23 16:40:38 +01:00
Duco Sebel 6cbc803b28 Streamline Peblar translations (#133883) 2024-12-23 16:38:34 +01:00
Steven B. abe00884ea Use SD stream for tplink mpeg stream (#133879) 2024-12-23 16:37:42 +01:00
Steven B. 0cbc77ad3f Make tplink entities unavailable if camera is off (#133877) 2024-12-23 16:36:57 +01:00
Bram Kragten 5487e8673c Update frontend to 20241223.1 (#133886) 2024-12-23 16:03:56 +01:00
Matthias Alphart 45ae2f4736 Set Fronius integration quality scale to gold (#133884) 2024-12-23 15:54:57 +01:00
Robert Resch 8e86c3c775 Add Ecovacs station entities (#133876)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-23 15:54:40 +01:00
Álvaro Fernández Rojas 5ef12c3993 Add AEMET Weather Radar images (#131386)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-23 15:51:21 +01:00
Glenn Vandeuren (aka Iondependent) 43a420cf01 Add cover to the niko_home_control integration (#133801)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-23 15:47:09 +01:00
starkillerOG 70648da8fd Improve firmware update required issue (#133878) 2024-12-23 15:05:45 +01:00
Steven B. 7f6a77ad2f Fix tplink camera entity unique id (#133880) 2024-12-23 15:53:17 +02:00
Simon 386a722393 ElevenLabs invalid api key config flow testing (#133822) 2024-12-23 12:05:31 +01:00
Robert Resch c5fe25a001 Bump deebot-client to 10.0.1 (#133634) 2024-12-23 12:05:29 +01:00
Omni Flux cf3d4eb26a Respect ESPHome ClimateTrait supports_current_temperature (#132149) 2024-12-23 11:35:59 +01:00
Joost Lekkerkerker 939365887f Add coordinator to Twinkly (#133793) 2024-12-23 11:35:37 +01:00
epenet e3cf5c47b2 Add compatibility code for deprecated WaterHeaterEntityEntityDescription (#133351) 2024-12-23 11:28:20 +01:00
Franck Nijhof b2170ad732 Mark Peblar Rocksolid EV Chargers Platinum (#133823) 2024-12-23 11:23:26 +01:00
Franck Nijhof ed7da35de4 Add coordinator error handling for Peblar Rocksolid EV Chargers (#133809) 2024-12-23 11:11:25 +01:00
Marcel van der Veldt 83f5ca5a30 Add actions with response values to Music Assistant (#133521)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: OzGav <gavnosp@hotmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-23 11:10:10 +01:00
Franck Nijhof 1f8f85d6eb Merge branch 'master' into dev 2024-12-23 09:40:42 +00:00
Duco Sebel 59d8c79371 Use user defined charge limit for charge limit range in Peblar (#133868) 2024-12-23 10:27:53 +01:00
Matrix a6f6317299 Add Leak detect entity for YoLink water meter controller (#131682) 2024-12-23 09:24:02 +01:00
dontinelli b1fe247eed Upgrade QS from silver to gold for slide_local (#133863)
Upgrade QS to gold
2024-12-23 09:23:13 +01:00
mrtlhfr 8991cd4f46 Adding initial support for Tuya Electric Fireplaces (#133503) 2024-12-23 09:23:04 +01:00
Matthias Alphart 9e1ba004d4 Add translated enum entity for Fronius error code (#133394) 2024-12-23 09:17:52 +01:00
J. Nick Koston ddb3edca5d Bump PySwitchbot to 0.55.4 (#133861) 2024-12-23 08:44:01 +01:00
jon6fingrs 4321d27ed3 Ensure icalendar==6.1.0 is installed for caldav integration (#133541) 2024-12-23 08:39:43 +01:00
dependabot[bot] ad0ee8f2d6 Bump github/codeql-action from 3.27.9 to 3.28.0 (#133862) 2024-12-23 08:18:23 +01:00
Teemu R. 6cdbdadc24 Ignore devices (bravias) with 'video' service_type for songpal discovery (#133724) 2024-12-22 19:38:10 -10:00
Martin Weinelt cf45c67055 Fix TypeError in maxcube climate action inference logic (#133853)
The maxcube-api library initializes the valve_position as a None value,
so that during initialization if the cube does not respond quickly enough
the comparison fails to compare a None-Type to an integer.
2024-12-22 23:26:11 -05:00
J. Nick Koston 3658cdba4c Ensure late import in backup of hassio.backup does not block the event loop (#133857)
* Ensure late import in backup of components.hassio.backup does not block the event loop

Preload backup when loading hassio to ensure it happens in the executor

https://github.com/home-assistant/core/blob/67f0de441b489890efa802a325f187b761098ad6/homeassistant/components/backup/__init__.py#L57

* improve comment
2024-12-22 23:19:44 -05:00
TheJulianJES dcc9be02ca Bump ZHA to 0.0.43 (#133854)
* Bump ZHA to 0.0.43

* Add strings for v2 quirk entities
2024-12-22 23:19:05 -05:00
J. Nick Koston de1b6a0dfc Add backup to the list of storage preloads (#133855) 2024-12-22 23:17:13 -05:00
J. Nick Koston 29fa40a5cf Add backup the list of integrations platforms to preload (#133856)
`backup` is now at the top of the startup time list. This will help reduce it.
2024-12-22 23:07:05 -05:00
Christopher Fenner 67f0de441b Fulfill IQS rule runtime-data in ViCare integration (#133633) 2024-12-23 00:06:01 +01:00
J. Nick Koston 353f085474 Bump anyio to 4.7.0 (#133842) 2024-12-22 13:05:51 -10:00
J. Nick Koston 00a1ae0eeb Bump protobuf to 5.29.2 (#133839) 2024-12-22 23:58:39 +01:00
Raphael Hehl df26166047 Unifiprotect: add error message if the get_user_keyring_info permissions are not sufficient (#133841) 2024-12-22 23:58:13 +01:00
Steven B. 8ab936b87c Add detection switches to tplink integration (#133828) 2024-12-22 23:54:44 +01:00
Mick Vleeshouwer 4ed0c21a4a Add data descriptions to Config Flow in Overkiz (#133758) 2024-12-22 23:35:50 +01:00
jb101010-2 a3657a0fef Suez_water: fix yesterday sensor extra_state invalid typing (#133425) 2024-12-22 23:21:52 +01:00
Andre Lengwenus 74b425a06e Reload on connection lost for LCN integration (#133638) 2024-12-22 23:20:01 +01:00
Norbert Rittel 0560b634e3 Make To-do action names and descriptions consistent with HA standard (#133734) 2024-12-22 23:14:01 +01:00
Mick Vleeshouwer 8eebbd45bd Bump pyOverkiz to 1.15.5 (#133835) 2024-12-22 22:52:35 +01:00
Joost Lekkerkerker c9ad87d464 Add light tests for Niko Home Control (#133750) 2024-12-22 22:44:15 +01:00
Lucas Gasenzer c2358d5158 Add Switchbot Water Leak Detector (BLE) (#133799)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-12-22 11:37:57 -10:00
Josef Zweck 2d2b979c7d Bump pylamarzocco to 1.4.2 (#133826) 2024-12-22 21:55:04 +01:00
Steven B. ebcb478f52 Add pan/tilt features to tplink integration (#133829) 2024-12-22 10:53:14 -10:00
Raphael Hehl 0f18f128fd Unifiprotect Add user information retrieval for NFC and fingerprint events (#132604)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-12-22 10:50:30 -10:00
G Johansson 368e958457 Load data for multiple days in Nord Pool (#133371)
* Load data for multiple days in Nord Pool

* Fix current day

* Fix tests

* Fix services

* Fix fixtures

* Mod get_data_current_day

* Mods

* simplify further
2024-12-22 21:10:12 +01:00
Noah Husby 26180486e7 Add media browsing to Cambridge Audio (#129106)
* Add media browsing to Cambridge Audio

* Remove one folder logic

* Remove class mapping for presets
2024-12-22 21:05:07 +01:00
Robert Resch 0ad9af0feb Add already exists config flow tests for Ecovacs (#133572)
Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-12-22 20:23:55 +01:00
dontinelli 07322c6992 Add reconfigure flow to slide_local (#133669) 2024-12-22 19:57:34 +01:00
Steven B. b1f6563fb2 Add camera platform to tplink integration (#129180)
Co-authored-by: Teemu R. <tpr@iki.fi>
2024-12-22 19:56:33 +01:00
J. Nick Koston 475f19c140 Bump flux_led to 1.1.0 (#133818) 2024-12-22 19:56:09 +01:00
Andre Lengwenus 344a03d9ce Remove unused fixture from LCN tests (#133821) 2024-12-22 19:55:45 +01:00
Raphael Hehl 6c70586f7e Add get_user_keyring_info service to UniFi Protect integration (#133138)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-12-22 08:54:14 -10:00
Franck Nijhof 0f1835139f Add number error handling for Peblar Rocksolid EV Chargers (#133803) 2024-12-22 19:53:00 +01:00
Franck Nijhof de5a49363e Add switch error handling for Peblar Rocksolid EV Chargers (#133805) 2024-12-22 19:47:31 +01:00
Arie Catsman c6789d70a4 Remove unneeded type for enphase_envoy coordinator in async_unload_entry (#133817) 2024-12-22 19:26:35 +01:00
Simon a2aba77973 Fix typo in ElevenLabs (#133819) 2024-12-22 19:26:15 +01:00
Franck Nijhof 84a3a9d495 Add select error handling for Peblar Rocksolid EV Chargers (#133804) 2024-12-22 19:25:55 +01:00
Barry vd. Heuvel d994884726 Add binary states for Weheat indoor unit (#133811)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-22 19:07:01 +01:00
Joost Lekkerkerker 0e9965150e Show device name in Twinkly discovery (#133814) 2024-12-22 19:00:49 +01:00
Barry vd. Heuvel feca7c28cf Add Compressor, Inside Unit and Energy Output fields to Weheat (#129632) 2024-12-22 18:45:33 +01:00
Steven Looman 0ba32e1d3a Bump async-upnp-client to 0.42.0 (#133806) 2024-12-22 07:18:05 -10:00
J. Nick Koston 6179da4321 Bump PySwitchbot to 0.55.3 (#133812)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-22 18:16:47 +01:00
jesperraemaekers 93c0eb73d2 Bump Weheat to 2024.12.22 (#133796) 2024-12-22 17:44:15 +01:00
Dave T 484f149e61 Add config flow stream preview to generic camera (#122563)
Co-authored-by: Allen Porter <allen.porter@gmail.com>
2024-12-22 17:31:03 +01:00
Steven B. 3cc75c3cf6 Use feature checks in tplink integration (#133795)
Clean up to use new upstream API:

* Use Feature attributes to check for supported

* Use color_temp range and update tests
2024-12-22 16:17:23 +01:00
Franck Nijhof 26d5c55d11 Add button error handling for Peblar Rocksolid EV Chargers (#133802) 2024-12-22 15:35:45 +01:00
Franck Nijhof 959f20c523 Add reconfigure flow to Peblar Rocksolid EV Chargers integration (#133785) 2024-12-22 14:23:12 +01:00
Franck Nijhof 075f95b9c4 Add base entity to Peblar Rocksolid EV Chargers integration (#133794) 2024-12-22 14:01:31 +01:00
PierreAronnax 1e68ae1bb8 Remove myself from govee_ble codeowners (#133790) 2024-12-22 13:35:36 +01:00
Joost Lekkerkerker 5ef3901b44 Add base entity for Niko Home Control (#133744) 2024-12-22 13:32:15 +01:00
Mick Vleeshouwer 56b58cec3e Fix errors in HitachiDHW in Overkiz (#133765)
* Small changes to fix errors in DHW

* Update

* Bugfix in float/int mistake

* Fix typing

* Fix code style

* Fix mypy
2024-12-22 12:17:09 +01:00
Franck Nijhof cdd73a5c5a Set parallel updates for Peblar Rocksolid EV Chargers integration (#133786) 2024-12-22 12:16:08 +01:00
Arie Catsman 88eb550ec1 Update quality-scale status for enphase_envoy config_flow missing data descriptions (#133726) 2024-12-22 12:01:13 +01:00
Joost Lekkerkerker 7be3cad1db Refactor Twinkly tests (#133725) 2024-12-22 12:00:24 +01:00
Franck Nijhof 31c6443a9b Add button platform to Peblar Rocksolid EV Chargers integration (#133780) 2024-12-22 11:51:01 +01:00
Mick Vleeshouwer 84d359c0d9 Fix binary_sensor typing in Overkiz (#133782) 2024-12-22 11:33:32 +01:00
Mick Vleeshouwer 619aed39b7 Use new UnitOfEnergy constants in Overkiz (#133778) 2024-12-22 10:36:07 +01:00
Mick Vleeshouwer 3f1acff652 Add support for HitachiAirToWaterHeatingZone in Overkiz (#133768)
* Add support for HitachiAirToWaterHeatingZone in Overkiz

* Clean up

* Fix typing

* Fix typing

* Fix typing

* Adapt to new PyOverkiz
2024-12-22 10:31:09 +01:00
Mick Vleeshouwer c3d0a01776 Migrate to runtime data in Overkiz (#133760)
* Migrate to runtime data

* Revert

* Improve typing
2024-12-22 10:25:59 +01:00
Arie Catsman cd6da9d9e8 Merge similar tests to parameterized tests for enphase_envoy (#133740) 2024-12-22 10:07:35 +01:00
Franck Nijhof 0c24afec6c Update integration quality scale for Peblar Rocksolid EV Chargers (#133764) 2024-12-22 10:03:16 +01:00
Mick Vleeshouwer 284ccbc778 Add additional Hitachi sensors to Overkiz (#133772)
Add additional Hitachi sensors
2024-12-22 09:40:06 +01:00
Mick Vleeshouwer cef182c596 Bump pyOverkiz to 1.15.4 (#133769)
Bump pyoverkiz to 1.15.4
2024-12-22 10:02:58 +02:00
Austin Mroczek d322398d06 TotalConnect use entry.runtime_data (#133756)
* use entry.runtime_data

* type the entry

* update quality scale

* recommended fixes

* Update homeassistant/components/totalconnect/alarm_control_panel.py

* Update homeassistant/components/totalconnect/binary_sensor.py

* Update homeassistant/components/totalconnect/button.py

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-22 08:59:54 +01:00
J. Nick Koston c2a9b0ff52 Reduce complexity to find unused data_ids and attributes_ids for db engines with slow range select (#133752) 2024-12-21 19:38:11 -10:00
J. Nick Koston 662dea28ed Replace queries using distinct with correlated scalar subqueries to significantly improve purge performance (#133748)
Replace queries using distinct with correlated scalar subqueries

like #133553 and #133699 PostgreSQL does not support skip/loose index scan https://wiki.postgresql.org/wiki/Loose_indexscan

This makes the `distinct` query (see section `Selecting Distinct Values` in the wiki above) to find the unused ids
very expense. We can replace them with correlated scalar subqueries
as done in #133553 to avoid the `distinct`
2024-12-21 22:25:48 -05:00
Franck Nijhof 9fcf8f22d2 Add reauthentication support to Peblar Rocksolid EV Chargers integration (#133757) 2024-12-21 23:00:29 +01:00
Franck Nijhof 9dc20b5709 Add more sensors to Peblar Rocksolid EV Chargers integration (#133754)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-21 22:40:15 +01:00
Franck Nijhof 5e4e1ce5a7 Add binary sensor platform to Peblar Rocksolid EV Chargers integration (#133755) 2024-12-21 22:29:11 +01:00
Franck Nijhof 85519a312c Fix Peblar current limit user setting value (#133753) 2024-12-21 22:23:36 +01:00
Franck Nijhof 81ce5f4505 Update peblar to v0.3.0 (#133751) 2024-12-21 21:26:40 +01:00
Franck Nijhof c67e2047e3 Add switch platform to Peblar Rocksolid EV Chargers integration (#133749) 2024-12-21 20:28:55 +01:00
Franck Nijhof 04276d3523 Add number platform to Peblar Rocksolid EV Chargers integration (#133739) 2024-12-21 20:16:18 +01:00
Joost Lekkerkerker f2df57e230 Add DHCP discovery to Withings (#133737) 2024-12-21 19:36:13 +01:00
Glenn Vandeuren (aka Iondependent) 0037799bfe Change niko_home_control library to nhc to get push updates (#132750)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2024-12-21 19:28:11 +01:00
Steven B. 944ad9022d Bump tplink python-kasa dependency to 0.9.0 (#133735)
Release notes: https://github.com/python-kasa/python-kasa/releases/tag/0.9.0
2024-12-21 19:04:09 +01:00
Tom 4b6febc757 Add reconfiguration flow to Plugwise (#132878)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-21 16:44:14 +01:00
Norbert Rittel ac2090d2f5 Replace "service" with "action" in Z-Wave action descriptions (#133727)
Replace all occurrence of "service" with "action"

Clean up the remaining occurrences of "service" with "action" to be consistent with the new terminology in Home Assistant.
2024-12-21 16:16:12 +01:00
Norbert Rittel 3dad5f6896 Replace two outdated occurrences of "service" with "action" (#133728) 2024-12-21 15:54:02 +01:00
Arie Catsman cc134c820b Reuse title of deleted enphase_envoy config entry if present (#133611) 2024-12-21 15:49:24 +01:00
Arie Catsman ef31413a59 Add missing asserts to enphase_envoy config flow test (#133730) 2024-12-21 15:20:10 +01:00
Álvaro Fernández Rojas 9292bfc6ed Update AEMET-OpenData to v0.6.4 (#133723) 2024-12-21 15:19:55 +01:00
Franck Nijhof 9e6c1d5b62 Add power and energy related sensors to Peblar Rocksolid EV Chargers (#133729) 2024-12-21 15:18:08 +01:00
Franck Nijhof 7e2d382ff4 Update aiohasupervisor to 0.2.2b5 (#133722) 2024-12-21 15:10:35 +01:00
Matthias Alphart b5a7a41ebe KNX: Option to select specific tunnel endpoint on TCP connections (#131996) 2024-12-21 15:10:14 +01:00
Franck Nijhof a3febc4449 Add select platform to Peblar Rocksolid EV Chargers integration (#133720) 2024-12-21 14:23:16 +01:00
Norbert Rittel 5abc03c21e Fix spelling of "Gateway PIN" and remove two excessive spaces (#133716) 2024-12-21 13:26:48 +01:00
Joost Lekkerkerker dc9133f919 Use mac address in Twinkly for unique id (#133717) 2024-12-21 13:26:09 +01:00
Arie Catsman a3fad89d0d Use super constructor self.config_entry in enphase_envoy coordinator (#133718) 2024-12-21 13:19:04 +01:00
Franck Nijhof a3fab094c3 Add device test for Peblar Rocksolid EV Chargers (#133713) 2024-12-21 13:03:44 +01:00
G Johansson 11efec49db Fix test coverage in workday (#133616) 2024-12-21 12:45:00 +01:00
Jan Bouwhuis 6b666b3a0f Test color_temp updates are processed when an mqtt json light is turned off (#133715) 2024-12-21 12:44:00 +01:00
Franck Nijhof 7e24b353ac Add updates tests for Peblar Rocksolid EV Chargers (#133712) 2024-12-21 12:31:58 +01:00
Maciej Bieniek 5665abf991 Store Twinkly runtime data in config entry (#133714) 2024-12-21 12:31:40 +01:00
Artur Pragacz 6314d7a44c Fix section translations check (#133683) 2024-12-21 12:31:17 +01:00
Manu b106b88f5c Adjust freezer tick in settings tests of IronOS integration (#133707) 2024-12-21 12:21:11 +01:00
Franck Nijhof dbe04f17ad Add sensors tests for Peblar Rocksolid EV Chargers (#133710) 2024-12-21 12:20:03 +01:00
Maciej Bieniek aad1d6a25d Use MAC address in Twinkly DeviceInfo.connections (#133708) 2024-12-21 12:19:11 +01:00
Franck Nijhof 7326555f03 Add diagnostic to Peblar Rocksolid EV Chargers integration (#133706) 2024-12-21 11:38:33 +01:00
Álvaro Fernández Rojas 5c2d769b54 Enable AEMET data cache (#131226) 2024-12-21 11:30:46 +01:00
Josef Zweck 66e863a2e3 Allow lamarzocco to reconnect websocket (#133635) 2024-12-21 11:29:24 +01:00
Richard Kroegel 78c9e47428 Improve BMW config flow (#133705) 2024-12-21 11:20:46 +01:00
J. Nick Koston 4e316429d3 Handle WebsocketConnectionError during mqtt auto reconnect (#133697)
followup to #133610 to handle the exception in the auto reconnect
path as well

fixes #132985
2024-12-21 11:18:47 +01:00
Norbert Rittel 989a3d1e24 Change "pin" to correct "PIN" for consistent translations (#133681) 2024-12-21 11:15:11 +01:00
Norbert Rittel 4efcf18c70 Change "pin" to "PIN" for consistency with common string (#133682) 2024-12-21 11:14:47 +01:00
Norbert Rittel 7998a05742 Replace lowercase "pin" in error message with the correct "PIN" (#133684) 2024-12-21 11:14:14 +01:00
Norbert Rittel 4ee9f813aa Fix inconsistent use of "pin" vs. "PIN" (#133685) 2024-12-21 11:13:44 +01:00
Franck Nijhof 859993e443 Add update platform to Peblar Rocksolid EV Chargers integration (#133570)
* Add update platform to Peblar Rocksolid EV Chargers integration

* Use device class translations
2024-12-21 10:55:00 +01:00
Allen Porter 4a063c3f9e Update the Google Tasks quality scale with documentation improvements (#133701) 2024-12-21 10:54:13 +01:00
J. Nick Koston 43fab48d4e Improve purge performance for PostgreSQL with large databases (#133699) 2024-12-21 10:53:15 +01:00
J. Nick Koston 02785a4ded Simplify query to find oldest state (#133700) 2024-12-21 10:37:16 +01:00
Andrew Sayre e43f4466e0 Improve HEOS group handling (#132213)
* Move register method to GroupManager

* Remove GroupManager mapping when entity removed

* Add test for when unloaded

* Error when group member not found

* Use entity registery to remove entity

* Update tests per feedback
2024-12-21 09:40:33 +01:00
Erik Montnemery 82f54eb9d2 Adjust the default backup name (#133668) 2024-12-21 09:38:59 +01:00
Abílio Costa 954b6133cb Use common mock fixture in Idasen Desk config flow tests (#133679) 2024-12-21 09:35:47 +01:00
Florent Thoumie 9c70ec4150 iaqualink: fix load_verify_locations() blocking call (#133459)
* Try to fix blocking call

* Fix lint
2024-12-21 09:26:38 +01:00
Dan Raper 3788e942a7 Bump Ohme library version to 1.2.0 (#133666)
Bump library version
2024-12-21 09:25:34 +01:00
Kevin Worrel e38a85da64 Add entity translation strings for ScreenLogic (#130708)
* Add translation strings for entities

* Translation key updates

* Mach original name

* Remove state translations

* Sentence case entity names

* Fix tests

* Add missing translation_key for Air temperature

* Revert inadvertant entity_id change on last_dose_time sensors

* Update homeassistant/components/screenlogic/strings.json

Lowercase 'entry'

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

* Define translations for each circuit delay sensor

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-21 09:25:21 +01:00
greyeee 861d9b3341 Add initial support for SwitchBot relay switch (#130863)
* Support relay switch

* 更新下版本

* add test case

* change to async_abort

* Upgrade PySwitchbot to 0.53.2

* change unit to volt

* upgrade pySwitchbot dependency

* bump lib, will be split into a seperate PR after testing is finished

* dry

* dry

* dry

* dry

* dry

* dry

* dry

* update tests

* fixes

* fixes

* cleanups

* fixes

* fixes

* fixes

* bump again

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-21 00:49:30 +01:00
J. Nick Koston b6819cbff3 Bump PySwitchbot to 0.55.2 (#133690)
changelog: https://github.com/sblibs/pySwitchbot/compare/0.54.0...0.55.2
2024-12-21 00:13:21 +01:00
Joost Lekkerkerker 9a0035e090 Fix Mealie test coverage (#133659) 2024-12-20 23:45:54 +01:00
Luke Lashley 1e420f16f7 Update Roborock to 2.8.4 (#133680) 2024-12-20 22:01:56 +01:00
G Johansson b29be34f55 Allow Filter title to be translated (#128929) 2024-12-20 21:21:41 +01:00
Michael Hansen 8607ba884c Bump intents to 2024.12.20 (#133676) 2024-12-20 14:23:12 -05:00
Joost Lekkerkerker 2639bdbefd Add parallel updates to Mealie (#133660) 2024-12-20 19:21:37 +00:00
Norbert Rittel c780933fa0 Reword invoke_pin action to avoid misunderstanding with "PIN" (#133665)
* Reword invoke_pin action to avoid misunderstanding with "PIN"

The previous mismatch between "PIN" and "pin" in the invoke_pin caused wrong translations as "PIN" was interpreted as the abbreviation for "Personal Identification Number".

This commit fixes this by explaining "pin" as related to "pinning" content on the device.

In addition the very "invoke" is replaced by "play" which every user and translator will understand immediately.

Along with those changes this commit reverts my previous change to "PIN" in all strings that made things worse.

* Use "Pin ID" for the field variable
2024-12-20 19:12:48 +01:00
Erik Montnemery a23b37114e Improve recorder data migrator tests (#133628) 2024-12-20 07:41:14 -10:00
Abílio Costa 17f0c24895 Replace tests for Idasen Desk with parameterized test (#133672) 2024-12-20 18:24:57 +01:00
Steven B. 6ed345f773 Add check for client errors to stream component (#132866) 2024-12-20 07:20:24 -10:00
elmurato 233395c181 Add missing await in Minecraft Server (#133670) 2024-12-20 17:58:31 +01:00
Andre Lengwenus 92195ff77d Bump pypck to 0.8.1 (#133646)
Co-authored-by: Robert Resch <robert@resch.dev>
2024-12-20 17:10:37 +01:00
Joost Lekkerkerker ad7a334147 Add translations to Mealie exceptions (#133648) 2024-12-20 16:08:31 +01:00
Mick Vleeshouwer 87f5a7057e Fix target temperature for AtlanticElectricalTowelDryer in Overkiz (#133657) 2024-12-20 16:00:44 +01:00
Erik Montnemery 5afb9a5053 Validate password before restoring backup (#133647)
* Validate password before restoring backup

* Raise specific error when password is incorrect
2024-12-20 15:43:46 +01:00
shapournemati-iotty 1c0135880d Add outlet device class to iotty switch entity (#132912)
* upgrade iottycloud lib to 0.3.0

* Add outlet

* test outlet turn on and turn off

* test add outlet

* Refactor code to use only one SwitchEntity  with an EntityDescription to distinguish Outlet and Lightswitch

* Refactor switch entities to reduce duplicated code

* Refactor tests to reduce duplicated code

* Refactor code to improve abstraction layer using specific types instead of generics

* Remove print and redundant field
2024-12-20 15:33:05 +01:00
Erik Montnemery f49111a4d9 Bump aiohasupervisor to version 0.2.2b4 (#133652)
* Bump aiohasupervisor to version 0.2.2b4

* Update test
2024-12-20 09:23:21 -05:00
Erik Montnemery 10478f4ca5 Fix logic in backup retention filter (#133654) 2024-12-20 09:19:57 -05:00
Norbert Rittel 9f43a7a17b Fix inconsistent spelling of "PIN" and "ID" (#133653)
* Fix inconsistent spelling of "PIN" and "ID"

Several actions contain an inconsistent spelling of "PIN" and "ID" with lowercase characters.

Especially to avoid (automated) mistranslations as (connection) pin etc. this needs to be corrected.

* Fix lowercase "blink" as well

Additional commit to fix the wrong spelling of "blink" along the way.
2024-12-20 15:17:47 +01:00
Joost Lekkerkerker cbb4c06195 Add Mealie to strict typing (#133644) 2024-12-20 15:17:08 +01:00
Norbert Rittel 83fdc07df0 Fix inconsistent spelling of "PIN" vs. "pin" (#133655)
As "PIN" is an abbreviation it should be all-caps throughout the UI.

This commit fixes two inconsistent occurrences in the invoke_pin action.
2024-12-20 15:15:16 +01:00
Norbert Rittel 0d309aa632 Fix inconsistent spelling of "PIN" vs. "pin" (#133656)
Part of the strings in the strings.json use an inconsistent spelling of "PIN" as "pin"

This commit fixes this to ensure correct and consistent translations, too.
2024-12-20 15:14:04 +01:00
Erik Montnemery 7d04eef5c5 Reject duplicates in WS command backup/config/update (#133650)
* Reject duplicates in WS command backup/config/update

* Add tests
2024-12-20 15:06:55 +01:00
Joost Lekkerkerker 870dc4dbea Record Analytics Insights quality scale (#133571)
* Record Analytics Insights quality scale

* Record Analytics Insights quality scale

* Record Analytics Insights quality scale

* Update homeassistant/components/analytics_insights/quality_scale.yaml

Co-authored-by: Josef Zweck <josef@zweck.dev>

* Update homeassistant/components/analytics_insights/quality_scale.yaml

Co-authored-by: Josef Zweck <josef@zweck.dev>

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2024-12-20 13:14:41 +01:00
Erik Montnemery 4c96b83297 Fix reading extra metadata for local backups (#133643) 2024-12-20 13:13:25 +01:00
Erik Montnemery 5834ecb13e Fix homeassistant_included flag for local backups (#133640) 2024-12-20 12:36:54 +01:00
Cyrill Raccaud e62a563ec1 Add Swiss Public Transport quality scale record (#131629) 2024-12-20 12:30:55 +01:00
Joost Lekkerkerker 6a599dc27a Record NYT Games quality scale (#133592)
* Record NYT Games quality scale

* Record NYT Games quality scale

* Fix
2024-12-20 12:30:35 +01:00
Joost Lekkerkerker df383a3a31 Record Mealie quality scale (#133587)
* Record Mealie quality scale

* Record NYT Games quality scale

* Fix

* Fix

* Fix
2024-12-20 12:29:54 +01:00
Joost Lekkerkerker 86e43b7196 Record Knocki quality scale (#133582)
* Record Knocki quality scale

* Record Knocki quality scale

* Fix
2024-12-20 12:29:04 +01:00
dontinelli b5c4608373 Upgrade QS from bronze to silver for slide_local (#133560) 2024-12-20 12:25:45 +01:00
Josef Zweck bddd8624bb Add scale support to lamarzocco (#133335) 2024-12-20 12:24:15 +01:00
Franck Nijhof 6974f61703 2024.12.5 (#133636) 2024-12-20 11:45:57 +01:00
Erik Montnemery 3df992790d Bump aiohasupervisor to version 0.2.2b3 (#133631) 2024-12-20 10:59:52 +01:00
Kenny Root b391dfe647 Switch to official Zabbix Python API (#131674) 2024-12-20 10:59:30 +01:00
Franck Nijhof e7bdf1467b Bump version to 2024.12.5 2024-12-20 09:51:57 +00:00
J. Nick Koston ff9df15cb0 Handle mqtt.WebsocketConnectionError when connecting to the MQTT broker (#133610)
fixes #132985
2024-12-20 09:43:13 +00:00
Joost Lekkerkerker fdde9d3a52 Fix Twinkly raise on progress (#133601) 2024-12-20 09:43:09 +00:00
Marcel van der Veldt 8c1a18b383 Handle null value for elapsed time in Music Assistant (#133597) 2024-12-20 09:43:05 +00:00
J. Nick Koston 367749d93c Bump aiohttp to 3.11.11 (#133530) 2024-12-20 09:42:34 +00:00
Joakim Plate 13f32c6720 Bump gardena_bluetooth to 1.5.0 (#133502) 2024-12-20 09:41:07 +00:00
Joakim Plate f8e1a786be Update fjäråskupan to 2.3.2 (#133499) 2024-12-20 09:41:03 +00:00
Joakim Plate cd5a46f11d Update fjäråskupan to 2.3.1 (#133493) 2024-12-20 09:41:00 +00:00
Luke Lashley 0140aa7240 Update Roborock to 2.8.1 (#133492) 2024-12-20 09:40:56 +00:00
Mick Vleeshouwer 92f50c63b1 Don't raise Overkiz user flow unique_id check (#133471) 2024-12-20 09:40:52 +00:00
Mick Vleeshouwer 1afeabfd64 Bump pyOverkiz to 1.15.3 (#133458) 2024-12-20 09:40:49 +00:00
Quentame 709d15a79b Bump Freebox to 1.2.1 (#133455) 2024-12-20 09:40:45 +00:00
J. Nick Koston cf4dbcfebf Ensure screenlogic retries if the protocol adapter is still booting (#133444)
* Ensure screenlogic retries if the protocol adapter is still booting

If the protocol adapter is still booting, it will disconnect and never
retry

```
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 640, in __async_setup_with_context
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/screenlogic/__init__.py", line 65, in async_setup_entry
    await gateway.async_connect(**connect_info)
  File "/usr/local/lib/python3.13/site-packages/screenlogicpy/gateway.py", line 142, in async_connect
    connectPkg = await async_connect_to_gateway(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<4 lines>...
    )
    ^
  File "/usr/local/lib/python3.13/site-packages/screenlogicpy/requests/login.py", line 107, in async_connect_to_gateway
    mac_address = await async_gateway_connect(transport, protocol, max_retries)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/screenlogicpy/requests/login.py", line 77, in async_gateway_connect
    raise ScreenLogicConnectionError("Host unexpectedly disconnected.")
screenlogicpy.const.common.ScreenLogicConnectionError: Host unexpectedly disconnected.
```

* coverage
2024-12-20 09:40:42 +00:00
Ron Weikamp 59e6fa5138 Bugfix: also schedule time based integration when source is 0 (#133438)
* Bugfix also schedule time based integration when source is 0

* Update tests/components/integration/test_sensor.py

Co-authored-by: Diogo Gomes <diogogomes@gmail.com>

* Improve comment in test. Remove redundant assertion.

---------

Co-authored-by: Diogo Gomes <diogogomes@gmail.com>
2024-12-20 09:40:38 +00:00
IceBotYT 8400ef8441 Add support for Nice G.O. HAE00080 wall station (#133186) 2024-12-20 09:40:35 +00:00
IceBotYT 6188db18c2 Bump nice-go to 1.0.0 (#133185)
* Bump Nice G.O. to 1.0.0

* Mypy

* Pytest
2024-12-20 09:40:31 +00:00
Jonas Fors Lellky 7e6392f062 Define setpoints as constants in flexit_bacnet (#133580)
* Define setpoints as consts

* Use a regular comment instead of docstring

* Un-indent comment
2024-12-20 10:11:50 +01:00
Joakim Sørensen 10191e7a23 Add async_register_backup_agents_listener to cloud/backup (#133584)
* Add async_register_backup_agents_listener to cloud/backup

* Coverage

* more coverage
2024-12-20 08:55:00 +01:00
G Johansson ad34bc8910 Add min/max price sensor to Nord Pool (#133534)
* Add min/max price sensor to Nord Pool

* Last fixes

* Make link in strings

* Replace func
2024-12-20 08:26:36 +01:00
Manu 26212798a3 Fixes and code cleanup for IronOS integration (#133579)
* Fix typing and cleanup in IronOS integration

* fix test not using freezer

* changes

* fix timedelta
2024-12-20 08:25:08 +01:00
J. Nick Koston 3d20c5c5d6 Remove lower bound for history start time state query (#133607)
Remove lower bound for start time state query

With the new query in #133553 we do not need a lower bound
on the search since it will always use index now and we
always want the newest value in the index before the
provided timestamp. The lower bound is redudant at this
point as it will always be older than the oldest
time point for the state. It only made sense when
the query would have had to examine a time window
of states instead of doing an index only search.
2024-12-20 08:24:47 +01:00
Christopher Fenner afae257a12 Bump PyViCare to 2.39.1 (#133619) 2024-12-20 01:14:48 +01:00
Quentame 64aba0c1a3 Bump Freebox to 1.2.1 (#133455) 2024-12-20 00:48:03 +01:00
J. Nick Koston 551a584ca6 Handle mqtt.WebsocketConnectionError when connecting to the MQTT broker (#133610)
fixes #132985
2024-12-19 21:39:39 +01:00
Jan-Philipp Benecke b261c7f18a Mark docs-installation-parameters for SABnzbd as done (#133609) 2024-12-19 20:29:12 +01:00
Joost Lekkerkerker 61e5f10d12 Fix Twinkly raise on progress (#133601) 2024-12-19 20:27:08 +01:00
adam-the-hero 2413fc4c0d Fix Watergate Water meter volume sensor (#133606) 2024-12-19 20:25:24 +01:00
Abílio Costa e6ef3fe507 Update Idasen Desk user flow step strings (#133605) 2024-12-19 20:24:10 +01:00
J. Nick Koston 04bcc8d3d3 Bump yalexs-ble to 2.5.6 (#133593) 2024-12-19 09:13:51 -10:00
Joost Lekkerkerker 52683c5f75 Improve Airgradient config flow tests (#133594) 2024-12-19 19:58:33 +01:00
Raphael Hehl 2f77cda822 Add basic UniFi Protect AiPort support (#133523)
* UnifiProtect add basic support for AiPort devices

* Sort ignore-words

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2024-12-19 08:18:21 -10:00
Marcel van der Veldt a97434976e Handle null value for elapsed time in Music Assistant (#133597) 2024-12-19 19:00:18 +01:00
epenet e357e0a406 Set default min/max color temperature in template lights (#133549) 2024-12-19 18:40:04 +01:00
Andrew Jackson 1a068d99d6 Add data descriptions to Mealie integration (#133590) 2024-12-19 18:28:50 +01:00
Joost Lekkerkerker 95b3d27b60 Update Airgradient quality scale (#133569) 2024-12-19 18:23:40 +01:00
Allen Porter a3ef3cce3e Improve Google Tasks coordinator updates behavior (#133316) 2024-12-19 16:41:47 +01:00
Erik Montnemery 255f85eb2f Fix boot loop after restoring backup (#133581) 2024-12-19 16:04:59 +01:00
Josef Zweck 94c7d18346 Bump pylamarzocco to 1.4.1 (#133557) 2024-12-19 13:36:32 +01:00
Noah Husby eb8ee1339c Set Russound RIO quality scale to silver (#133494) 2024-12-19 12:40:23 +01:00
Stefan Agner 962f1bad32 Add mW as unit of measurement for Matter electrical power sensors (#133504) 2024-12-19 11:40:05 +00:00
Erik Montnemery dd215b3d5d Revert "Revert "Simplify recorder RecorderRunsManager (#131785)"" (#133564)
Revert "Revert "Simplify recorder RecorderRunsManager" (#133201)"

This reverts commit 980b8a91e6.
2024-12-19 12:32:15 +01:00
Erik Montnemery bb7abd037c Revert "Revert "Improve recorder history queries (#131702)"" (#133561)
Revert "Revert "Improve recorder history queries (#131702)" (#133203)"

This reverts commit 74e4654c26.
2024-12-19 11:50:12 +01:00
J. Nick Koston d35b34f142 Replace start time state query with single correlated scalar subquery (#133553) 2024-12-19 00:14:32 -10:00
dependabot[bot] 1c119518db Bump codecov/codecov-action from 5.1.1 to 5.1.2 (#133547)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 10:52:10 +01:00
Norbert Rittel 9a6c749714 Change 'GSuite' to 'Workspace', fix 'Start' field label (#133554)
* Change 'GSuite' to 'Workspace', fix 'Start' field label

Several years ago Google renamed "G Suite" to "Google Workspace", this commit applies the same change to one of the  field descriptions of the set_vacation action.

In addition the "Start" field of the action currently uses the common action (!) for Start which is wrong in this context, it stands for the beginning here.

This commit changes this back to a local definition of this label just like "End".

In German for example "Start" needs to be "Beginn" in this context while the common action is translated as "Starten".

* Use "Google Workspace" for more clarity

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-19 10:51:30 +01:00
Norbert Rittel 79484ea7f5 Grammar fixes for action names and descriptions (#133559)
Several KNX actions contain a wrong "s" at the end of their verbs while those are missing in several of the descriptions.

This commit changes all those to make them consistent with the remaining actions in KNX and the standard terminology in Home Assistant.
2024-12-19 10:50:12 +01:00
Franck Nijhof 3568bdca65 Update Home Assistant base image to 2024.12.0 (#133558) 2024-12-19 10:48:43 +01:00
Erik Montnemery a76f82080b Create repair issues when automatic backup fails (#133513)
* Create repair issues when automatic backup fails

* Improve test coverage

* Adjust issues
2024-12-19 10:40:07 +01:00
Christopher Fenner cd384cadbe Fulfill IQS rule config-flow in ViCare integration (#133524)
* add data_description

* Apply suggestions from code review

Co-authored-by: Josef Zweck <josef@zweck.dev>

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2024-12-19 10:04:26 +01:00
J. Nick Koston 69a8d3f3c1 Revert "Optimize start time state queries for PostgreSQL" (#133555) 2024-12-18 23:01:58 -10:00
J. Nick Koston a3fb6e8f92 Bump pydantic to 2.10.4 (#133539)
changelog: https://github.com/pydantic/pydantic/compare/v2.10.3...v2.10.4
2024-12-19 10:01:40 +01:00
Erik Montnemery c8480627ca Add comment motivating magic number for MySQL error codes (#133516)
* Add comment motivating magic number for MySQL error codes

* Pick nits
2024-12-19 09:56:32 +01:00
Franck Nijhof 893f605d61 Revert "Update docker base image to 2024.12.1" (#133552)
Revert "Update docker base image to 2024.12.1 (#133323)"

This reverts commit 66dcd38701.
2024-12-19 09:42:22 +01:00
epenet ddd2ba6c4a Set default min/max color temperature in hue lights (#133548) 2024-12-19 08:36:29 +01:00
Stefan Agner 681863f80e Use mV and mA as units for electrical power measurement in Matter (#133505) 2024-12-19 08:32:46 +01:00
J. Nick Koston 99698ef95d Optimize start time state queries for PostgreSQL (#133228) 2024-12-18 19:41:53 -10:00
Franck Nijhof 3fe08a7223 Add zeroconf discovery to Peblar Rocksolid EV chargers (#133529) 2024-12-19 00:39:14 +01:00
J. Nick Koston 35601480d2 Bump aiohttp to 3.11.11 (#133530) 2024-12-18 23:48:39 +01:00
Abílio Costa 0076bd8389 Simplify Idasen Desk entity properties (#133536) 2024-12-18 23:47:24 +01:00
Franck Nijhof 9f3c549f8d Add integration setup tests to Peblar Rocksolid EV Chargers (#133532) 2024-12-18 23:46:18 +01:00
Norbert Rittel 03707e6308 Improve field descriptions for Download file action (#133413)
* Improve field descriptions for Download file action

Currently two of the field descriptions for the Download file action don't explain exactly what should be entered but rather explain these like additional actions.

The third, the Overwrite file option is misleading as it does not refer to an existing file.

This commit fixes both issues by explaining the purpose of all three fields in a slightly more detailed fashion.

* Update homeassistant/components/downloader/strings.json

Co-authored-by: Josef Zweck <josef@zweck.dev>

* Update homeassistant/components/downloader/strings.json

Co-authored-by: Josef Zweck <josef@zweck.dev>

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2024-12-18 22:40:30 +01:00
Abílio Costa 9e6a8638dd Bump idasen-ha to 2.6.3 (#133508)
This is a minor bump that adds py.typed
2024-12-18 22:38:57 +01:00
Norbert Rittel 2a9082559a Fix names and description of two actions (#133528)
The two actions enable_motion_recording and disable_motion_recording use "Enables" and "Disables" in their names.

This is inconsistent with the name of the actions, all other actions of this component, and the standard way of naming them, too.

In addition the description of the latter misses the "s" which causes additional inconsistency – especially in translations.
2024-12-18 22:35:58 +01:00
starkillerOG ba3fca53b0 Reolink platinum quality scale (#133514) 2024-12-18 21:49:32 +01:00
Raphael Hehl e4bb351d2d Bump uiprotect to 7.1.0 (#133520)
* Bump uiprotect to version 7.1.0

* Add aiports to bootstrap fixture in unifiprotect tests
2024-12-18 21:41:22 +01:00
Christopher Fenner 1bdda0249e Bump PyViCare to 2.39.0 (#133519) 2024-12-18 21:38:52 +01:00
Erik Montnemery ff8bc763c3 Ensure indices needed by data migrators exist (#133367)
* Ensure indices needed by data migrators exist

* Update test

* Improve test

* Ignore index error on char(0) columns

* Adjust tests

* Address review comments

* Add comment motivating magic number
2024-12-18 21:29:52 +01:00
dontinelli 8a8be71f96 Add tests for cover and increase test coverage for slide_local (#133515) 2024-12-18 20:53:05 +01:00
starkillerOG 19e6867f1a Reolink translate errors (#132301) 2024-12-18 20:22:33 +01:00
Norbert Rittel c8f050ecbc Fix the local_file.update_file_path action's name and description (#133509) 2024-12-18 20:08:57 +01:00
IceBotYT b7ff27122a Add support for Nice G.O. HAE00080 wall station (#133186) 2024-12-18 19:47:41 +01:00
Shay Levy 3a8b0b3ea6 Use Switcher _async_call_api in climate (#133230) 2024-12-18 19:46:52 +01:00
mvn23 0ff2a0d66d Add "cancel room setpoint override" button to opentherm_gw (#132162) 2024-12-18 19:46:30 +01:00
Joakim Plate 4daf6dd41d Bump gardena_bluetooth to 1.5.0 (#133502) 2024-12-18 19:39:35 +01:00
Thomas55555 51bead3229 Update number platform values before add in APSystems and add tests (#131938)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-12-18 19:34:49 +01:00
Manu 352e948d56 Add tests for already_configured erros in IronOS integration (#132265) 2024-12-18 19:33:33 +01:00
Manu 70ad4ee454 Add select platform to IronOS (#132218) 2024-12-18 19:32:51 +01:00
TJ Horner 53ef96c63e weatherkit: use stale data for up to an hour if updates fail (#130398) 2024-12-18 19:21:03 +01:00
Franck Nijhof bb2d027532 Add Peblar Rocksolid EV Chargers integration (#133501)
* Add Peblar Rocksolid EV Chargers integration

* Process review comments
2024-12-18 19:11:13 +01:00
Erik Montnemery 51d63ba508 Store automatic backup flag in backup metadata (#133500) 2024-12-18 18:30:46 +01:00
Arie Catsman fc622e398f add exception translation to enphase_envoy (#132483) 2024-12-18 18:24:12 +01:00
peteS-UK 920de90603 Increase Squeezebox config_flow test coverage to 100% (#133484)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-18 18:22:22 +01:00
Joakim Plate a6089b497a Update fjäråskupan to 2.3.2 (#133499) 2024-12-18 18:03:27 +01:00
Erik Montnemery 5516f3609d Rename strategy backup to automatic backup (#133489)
* Rename strategy backup to automatic backup

* Update homeassistant/components/backup/config.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-12-18 17:35:11 +01:00
Joakim Plate a1558213c4 Update fjäråskupan to 2.3.1 (#133493) 2024-12-18 16:53:15 +01:00
Luke Lashley 2564533dae Update Roborock to 2.8.1 (#133492) 2024-12-18 16:22:39 +01:00
Noah Husby f46e764982 Update quality scale for Russound RIO (#133093) 2024-12-18 16:06:48 +01:00
dontinelli d6c201de4a Add exceptions and translations for slide_local (#133490) 2024-12-18 15:33:11 +01:00
mkmer c9f1829c0b Add (de)humidifier platform to Honeywell (#132287)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-18 15:27:40 +01:00
dontinelli 1e075cdac7 Add diagnostics to slide_local (#133488) 2024-12-18 15:21:17 +01:00
Philip Baylas fce6d6246f Change log level of connection failure to info (#132625)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-12-18 15:07:03 +01:00
Maciej Bieniek 3132700492 Add ability to translate ENUM sensor states in Unifi integration (#131921) 2024-12-18 15:02:44 +01:00
adam-the-hero 943b1d9f08 Add sensors platform to Watergate integration (#133015) 2024-12-18 14:52:25 +01:00
Markus Jacobsen 2d6d313e5c Complete adding custom integration action sections support to hassfest (#132443) 2024-12-18 14:50:12 +01:00
Guido Schmitz 9716183997 Add entity translations to devolo Home Control (#132927) 2024-12-18 14:38:29 +01:00
Andre Lengwenus a46a0ad2b4 Add device_id parameter to LCN actions (service calls) (#129590) 2024-12-18 14:35:02 +01:00
J. Diego Rodríguez Royo c06bc53724 Deprecate Home Connect program switches (#131641) 2024-12-18 14:26:37 +01:00
Bas Brussee 4399d09820 Allow data description in sections (#128965)
* Allow data description in sections

* update format with ruff

* Add data_description to kitchen_sink input section

---------

Co-authored-by: Erik <erik@montnemery.com>
2024-12-18 14:02:08 +01:00
Abílio Costa ca2c7280eb Remove uneeded logger param from Idasen Desk Coordinator (#133485) 2024-12-18 13:59:56 +01:00
Erik Montnemery ecb3bf79f3 Revert "Add support for subentries to config entries" (#133470)
Revert "Add support for subentries to config entries (#117355)"

This reverts commit ad15786115.
2024-12-18 13:51:05 +01:00
Joost Lekkerkerker 2aba1d399b Rename test file to singular form (#133482) 2024-12-18 12:47:30 +00:00
greyeee be25cb7aa7 Add support for SwitchBot Relay Switch 1 and Relay Switch 1PM (#132327) 2024-12-18 13:19:45 +01:00
Mick Vleeshouwer 3bb6256572 Add test button for SmokeSensor in Overkiz (#133476) 2024-12-18 11:48:10 +01:00
Mick Vleeshouwer fc4100833e Change device class from Volume to Volume Storage in Overkiz (#133473)
Change device class from Volume to Volume Storage
2024-12-18 11:43:04 +01:00
Erik Montnemery 992afc4cd3 Set the with_strategy_settings to None for unknown backups (#133466) 2024-12-18 11:27:07 +01:00
Mick Vleeshouwer 7730f423b3 Add identify device class in Overkiz (#133474) 2024-12-18 11:22:32 +01:00
Mick Vleeshouwer 05b0c56191 Use enum instead of string for button entities key in Overkiz (#133472) 2024-12-18 11:22:22 +01:00
Mick Vleeshouwer fa0e54e658 Don't raise Overkiz user flow unique_id check (#133471) 2024-12-18 11:05:52 +01:00
Joakim Sørensen 869a0d7abc Add name to cloud connection info response (#133468) 2024-12-18 11:01:38 +01:00
dotvav 90208d2eb1 Bump pypalazzetti to 0.1.15 (#133433) 2024-12-18 10:58:25 +01:00
J. Diego Rodríguez Royo a6520d2627 Handle Home Connect error at diagnostics (#131644) 2024-12-18 10:52:45 +01:00
epenet 8b8c409916 Fix test-before-setup IQS check (#133467) 2024-12-18 10:44:19 +01:00
Ron Weikamp a2be5a383c Bugfix: also schedule time based integration when source is 0 (#133438)
* Bugfix also schedule time based integration when source is 0

* Update tests/components/integration/test_sensor.py

Co-authored-by: Diogo Gomes <diogogomes@gmail.com>

* Improve comment in test. Remove redundant assertion.

---------

Co-authored-by: Diogo Gomes <diogogomes@gmail.com>
2024-12-18 10:41:46 +01:00
Tomer Shemesh 39d781905d Add ssdp discovery to Onkyo (#131066) 2024-12-18 10:21:37 +01:00
Abílio Costa 5fb5e933e2 Use a common base entity for Idasen Desk (#132496)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-18 10:20:14 +01:00
Mick Vleeshouwer 413a578fdb Bump pyOverkiz to 1.15.3 (#133458) 2024-12-18 10:19:57 +01:00
Jan-Philipp Benecke c1cf0e23b2 Lift SABnzbd to bronze quality scale (#133453) 2024-12-18 10:10:42 +01:00
Noah Husby a449ca65be Improve test coverage for Russound RIO (#133096)
* Improve test coverage for Russound RIO

* Update

* Update
2024-12-18 09:33:17 +01:00
Arie Catsman 4c91d1b402 Add support for ACB batteries to Enphase Envoy (#131298)
* Add support for ACB batteries to Enphase Envoy

* Add tests for ACB battery support in ENphase Envoy

* make acb state sensordeviceclass ENUM

* Capitalize strings and use common idle
2024-12-18 08:48:37 +01:00
Noah Husby fab92d1cf8 Add reconfigure flow to Russound RIO (#133091)
* Add reconfigure flow to Russound RIO

* Mark reconfiguration flow as done

* Update

* Update
2024-12-18 08:40:27 +01:00
Assaf Inbal c10473844f Add sensors to Ituran integration (#133359)
Add sensors to Ituran
2024-12-18 08:36:42 +01:00
dependabot[bot] dfdd83789a Bump actions/upload-artifact from 4.4.3 to 4.5.0 (#133461) 2024-12-18 08:05:39 +01:00
J. Nick Koston 9bff9c5e7b Ensure screenlogic retries if the protocol adapter is still booting (#133444)
* Ensure screenlogic retries if the protocol adapter is still booting

If the protocol adapter is still booting, it will disconnect and never
retry

```
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 640, in __async_setup_with_context
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/screenlogic/__init__.py", line 65, in async_setup_entry
    await gateway.async_connect(**connect_info)
  File "/usr/local/lib/python3.13/site-packages/screenlogicpy/gateway.py", line 142, in async_connect
    connectPkg = await async_connect_to_gateway(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<4 lines>...
    )
    ^
  File "/usr/local/lib/python3.13/site-packages/screenlogicpy/requests/login.py", line 107, in async_connect_to_gateway
    mac_address = await async_gateway_connect(transport, protocol, max_retries)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/screenlogicpy/requests/login.py", line 77, in async_gateway_connect
    raise ScreenLogicConnectionError("Host unexpectedly disconnected.")
screenlogicpy.const.common.ScreenLogicConnectionError: Host unexpectedly disconnected.
```

* coverage
2024-12-17 20:57:43 -05:00
Abílio Costa e73512e11c Add integration_type to Idasen Desk (#132486)
* Add Idasen Desk quality scale record

* Update wrong checks

* Add integration_type to Idasen Desk
2024-12-17 23:49:04 +01:00
G Johansson 4c60e36f4f Add Get price service to Nord Pool (#130185)
* Add get_price service to Nord Pool

* Tests and fixes

* Fixes

* Not used fixtures

* update qs

* Fixes

* docstring

* Remove selector from strings

* Mod service
2024-12-17 21:59:20 +01:00
G Johansson f8cd6204ca Fix reconfigure in Nord Pool (#133431) 2024-12-17 21:30:49 +01:00
Jan-Philipp Benecke eae25023e7 Do not remove services when last config entry is unloaded in SABnzbd (#133449) 2024-12-17 21:27:41 +01:00
Klaas Schoute 21c3bf48f9 Allow only single instance of easyenergy integration (#133447) 2024-12-17 21:02:39 +01:00
Franck Nijhof a5eb816dcf 2024.12.4 (#133422) 2024-12-17 15:28:28 +01:00
Franck Nijhof 517f3faa0a Bump version to 2024.12.4 2024-12-17 12:14:26 +00:00
G Johansson b4015805f7 Bump holidays to 0.63 (#133391) 2024-12-17 12:14:04 +00:00
Jonas Fors Lellky a56ad0273b Fix fan setpoints for flexit_bacnet (#133388) 2024-12-17 12:14:00 +00:00
Maciej Bieniek 2bc917c842 Bump imgw-pib to version 1.0.7 (#133364) 2024-12-17 12:13:57 +00:00
Michael 97f22b3a3d Allow load_verify_locations with only cadata passed (#133299) 2024-12-17 12:13:53 +00:00
J. Nick Koston a48a5adc81 Set code_arm_required to False for homekit_controller (#133284) 2024-12-17 12:13:50 +00:00
J. Nick Koston eb86b00dd4 Bump yalexs-ble to 2.5.5 (#133229)
changelog: https://github.com/bdraco/yalexs-ble/compare/v2.5.4...v2.5.5
2024-12-17 12:13:47 +00:00
Jan Bouwhuis e93256951e Bump incomfort-client to v0.6.4 (#133205) 2024-12-17 12:13:43 +00:00
Erik Montnemery 3b0ab421b0 Revert "Improve recorder history queries (#131702)" (#133203) 2024-12-17 12:11:13 +00:00
Erik Montnemery ca47253d81 Revert "Simplify recorder RecorderRunsManager" (#133201)
Revert "Simplify recorder RecorderRunsManager (#131785)"

This reverts commit cf0ee63507.
2024-12-17 12:08:41 +00:00
Avi Miller 9b0a489753 Bump aiolifx to 1.1.2 and add new HomeKit product prefixes (#133191)
Signed-off-by: Avi Miller <me@dje.li>
2024-12-17 11:44:54 +00:00
Conor Eager 9b02db008e Bump starlink-grpc-core to 1.2.1 to fix missing ping (#133183) 2024-12-17 11:44:50 +00:00
J. Nick Koston 223817a7fb Bump yalexs-ble to 2.5.4 (#133172) 2024-12-17 11:44:47 +00:00
G Johansson cdea9b5d3a Fix strptime in python_script (#133159)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2024-12-17 11:44:41 +00:00
Joost Lekkerkerker 8286ec9e60 Bump yt-dlp to 2024.12.13 (#133129) 2024-12-17 11:44:38 +00:00
rappenze cce7b9ac34 Fix fibaro climate hvac mode (#132508) 2024-12-17 11:44:34 +00:00
Erik Montnemery a42c0230c9 Simplify recorder RecorderRunsManager (#131785) 2024-12-17 11:44:30 +00:00
1081 changed files with 47842 additions and 11458 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
- name: Upload translations
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: translations
path: translations.tar.gz
+14 -14
View File
@@ -40,7 +40,7 @@ env:
CACHE_VERSION: 11
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 9
HA_SHORT_VERSION: "2025.1"
HA_SHORT_VERSION: "2025.2"
DEFAULT_PYTHON: "3.12"
ALL_PYTHON_VERSIONS: "['3.12', '3.13']"
# 10.3 is the oldest supported version
@@ -537,7 +537,7 @@ jobs:
python --version
uv pip freeze >> pip_freeze.txt
- name: Upload pip_freeze artifact
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: pip-freeze-${{ matrix.python-version }}
path: pip_freeze.txt
@@ -661,7 +661,7 @@ jobs:
. venv/bin/activate
python -m script.licenses extract --output-file=licenses-${{ matrix.python-version }}.json
- name: Upload licenses
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
path: licenses-${{ matrix.python-version }}.json
@@ -877,7 +877,7 @@ jobs:
. venv/bin/activate
python -m script.split_tests ${{ needs.info.outputs.test_group_count }} tests
- name: Upload pytest_buckets
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: pytest_buckets
path: pytest_buckets.txt
@@ -979,14 +979,14 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1106,7 +1106,7 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${mariadb}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1114,7 +1114,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1236,7 +1236,7 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${postgresql}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1244,7 +1244,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1273,7 +1273,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@v5.1.1
uses: codecov/codecov-action@v5.1.2
with:
fail_ci_if_error: true
flags: full-suite
@@ -1378,14 +1378,14 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1411,7 +1411,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@v5.1.1
uses: codecov/codecov-action@v5.1.2
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
+2 -2
View File
@@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.27.9
uses: github/codeql-action/init@v3.28.0
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.27.9
uses: github/codeql-action/analyze@v3.28.0
with:
category: "/language:python"
+32 -3
View File
@@ -76,18 +76,37 @@ jobs:
# Use C-Extension for SQLAlchemy
echo "REQUIRE_SQLALCHEMY_CEXT=1"
# Add additional pip wheel build constraints
echo "PIP_CONSTRAINT=build_constraints.txt"
) > .env_file
- name: Write pip wheel build constraints
run: |
(
# ninja 1.11.1.2 + 1.11.1.3 seem to be broken on at least armhf
# this caused the numpy builds to fail
# https://github.com/scikit-build/ninja-python-distributions/issues/274
echo "ninja==1.11.1.1"
) > build_constraints.txt
- name: Upload env_file
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: env_file
path: ./.env_file
include-hidden-files: true
overwrite: true
- name: Upload build_constraints
uses: actions/upload-artifact@v4.5.0
with:
name: build_constraints
path: ./build_constraints.txt
overwrite: true
- name: Upload requirements_diff
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: requirements_diff
path: ./requirements_diff.txt
@@ -99,7 +118,7 @@ jobs:
python -m script.gen_requirements_all ci
- name: Upload requirements_all_wheels
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v4.5.0
with:
name: requirements_all_wheels
path: ./requirements_all_wheels_*.txt
@@ -123,6 +142,11 @@ jobs:
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.1.8
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.1.8
with:
@@ -167,6 +191,11 @@ jobs:
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.1.8
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.1.8
with:
+1 -1
View File
@@ -12,7 +12,7 @@ repos:
hooks:
- id: codespell
args:
- --ignore-words-list=astroid,checkin,currenty,hass,iif,incomfort,lookin,nam,NotIn
- --ignore-words-list=aiport,astroid,checkin,currenty,hass,iif,incomfort,lookin,nam,NotIn
- --skip="./.*,*.csv,*.json,*.ambr"
- --quiet-level=2
exclude_types: [csv, json, html]
+3
View File
@@ -311,6 +311,7 @@ homeassistant.components.manual.*
homeassistant.components.mastodon.*
homeassistant.components.matrix.*
homeassistant.components.matter.*
homeassistant.components.mealie.*
homeassistant.components.media_extractor.*
homeassistant.components.media_player.*
homeassistant.components.media_source.*
@@ -361,8 +362,10 @@ homeassistant.components.openuv.*
homeassistant.components.oralb.*
homeassistant.components.otbr.*
homeassistant.components.overkiz.*
homeassistant.components.overseerr.*
homeassistant.components.p1_monitor.*
homeassistant.components.panel_custom.*
homeassistant.components.peblar.*
homeassistant.components.peco.*
homeassistant.components.persistent_notification.*
homeassistant.components.pi_hole.*
+17 -12
View File
@@ -578,8 +578,8 @@ build.json @home-assistant/supervisor
/tests/components/google_tasks/ @allenporter
/homeassistant/components/google_travel_time/ @eifinger
/tests/components/google_travel_time/ @eifinger
/homeassistant/components/govee_ble/ @bdraco @PierreAronnax
/tests/components/govee_ble/ @bdraco @PierreAronnax
/homeassistant/components/govee_ble/ @bdraco
/tests/components/govee_ble/ @bdraco
/homeassistant/components/govee_light_local/ @Galorhallen
/tests/components/govee_light_local/ @Galorhallen
/homeassistant/components/gpsd/ @fabaff @jrieger
@@ -1066,8 +1066,8 @@ build.json @home-assistant/supervisor
/tests/components/ondilo_ico/ @JeromeHXP
/homeassistant/components/onewire/ @garbled1 @epenet
/tests/components/onewire/ @garbled1 @epenet
/homeassistant/components/onkyo/ @arturpragacz
/tests/components/onkyo/ @arturpragacz
/homeassistant/components/onkyo/ @arturpragacz @eclair4151
/tests/components/onkyo/ @arturpragacz @eclair4151
/homeassistant/components/onvif/ @hunterjm
/tests/components/onvif/ @hunterjm
/homeassistant/components/open_meteo/ @frenck
@@ -1103,8 +1103,10 @@ build.json @home-assistant/supervisor
/tests/components/otbr/ @home-assistant/core
/homeassistant/components/ourgroceries/ @OnFreund
/tests/components/ourgroceries/ @OnFreund
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117 @alexfp14
/tests/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117 @alexfp14
/homeassistant/components/overkiz/ @imicknl
/tests/components/overkiz/ @imicknl
/homeassistant/components/overseerr/ @joostlek
/tests/components/overseerr/ @joostlek
/homeassistant/components/ovo_energy/ @timmo001
/tests/components/ovo_energy/ @timmo001
/homeassistant/components/p1_monitor/ @klaasnicolaas
@@ -1113,6 +1115,8 @@ build.json @home-assistant/supervisor
/tests/components/palazzetti/ @dotvav
/homeassistant/components/panel_custom/ @home-assistant/frontend
/tests/components/panel_custom/ @home-assistant/frontend
/homeassistant/components/peblar/ @frenck
/tests/components/peblar/ @frenck
/homeassistant/components/peco/ @IceBotYT
/tests/components/peco/ @IceBotYT
/homeassistant/components/pegel_online/ @mib1185
@@ -1133,8 +1137,8 @@ build.json @home-assistant/supervisor
/tests/components/plaato/ @JohNan
/homeassistant/components/plex/ @jjlawren
/tests/components/plex/ @jjlawren
/homeassistant/components/plugwise/ @CoMPaTech @bouwew @frenck
/tests/components/plugwise/ @CoMPaTech @bouwew @frenck
/homeassistant/components/plugwise/ @CoMPaTech @bouwew
/tests/components/plugwise/ @CoMPaTech @bouwew
/homeassistant/components/plum_lightpad/ @ColinHarrington @prystupa
/tests/components/plum_lightpad/ @ColinHarrington @prystupa
/homeassistant/components/point/ @fredrike
@@ -1476,8 +1480,8 @@ build.json @home-assistant/supervisor
/tests/components/system_bridge/ @timmo001
/homeassistant/components/systemmonitor/ @gjohansson-ST
/tests/components/systemmonitor/ @gjohansson-ST
/homeassistant/components/tado/ @chiefdragon @erwindouna
/tests/components/tado/ @chiefdragon @erwindouna
/homeassistant/components/tado/ @erwindouna
/tests/components/tado/ @erwindouna
/homeassistant/components/tag/ @balloob @dmulcahey
/tests/components/tag/ @balloob @dmulcahey
/homeassistant/components/tailscale/ @frenck
@@ -1571,8 +1575,8 @@ build.json @home-assistant/supervisor
/tests/components/triggercmd/ @rvmey
/homeassistant/components/tts/ @home-assistant/core
/tests/components/tts/ @home-assistant/core
/homeassistant/components/tuya/ @Tuya @zlinoliver @frenck
/tests/components/tuya/ @Tuya @zlinoliver @frenck
/homeassistant/components/tuya/ @Tuya @zlinoliver
/tests/components/tuya/ @Tuya @zlinoliver
/homeassistant/components/twentemilieu/ @frenck
/tests/components/twentemilieu/ @frenck
/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 @Olen
@@ -1740,6 +1744,7 @@ build.json @home-assistant/supervisor
/tests/components/youless/ @gjong
/homeassistant/components/youtube/ @joostlek
/tests/components/youtube/ @joostlek
/homeassistant/components/zabbix/ @kruton
/homeassistant/components/zamg/ @killer0071234
/tests/components/zamg/ @killer0071234
/homeassistant/components/zengge/ @emontnemery
+5 -5
View File
@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2024.12.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2024.12.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2024.12.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2024.12.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2024.12.1
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2024.12.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2024.12.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2024.12.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2024.12.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2024.12.0
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io
+3
View File
@@ -64,6 +64,9 @@ def restore_backup_file_content(config_dir: Path) -> RestoreBackupFileContent |
)
except (FileNotFoundError, KeyError, json.JSONDecodeError):
return None
finally:
# Always remove the backup instruction file to prevent a boot loop
instruction_path.unlink(missing_ok=True)
def _clear_configuration_directory(config_dir: Path, keep: Iterable[str]) -> None:
+1
View File
@@ -252,6 +252,7 @@ PRELOAD_STORAGE = [
"assist_pipeline.pipelines",
"core.analytics",
"auth_module.totp",
"backup",
]
+1
View File
@@ -2,6 +2,7 @@
"domain": "microsoft",
"name": "Microsoft",
"integrations": [
"azure_data_explorer",
"azure_devops",
"azure_event_hub",
"azure_service_bus",
+15 -1
View File
@@ -1,6 +1,7 @@
"""The AEMET OpenData component."""
import logging
import shutil
from aemet_opendata.exceptions import AemetError, TownNotFound
from aemet_opendata.interface import AEMET, ConnectionOptions, UpdateFeature
@@ -10,8 +11,9 @@ from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CON
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.storage import STORAGE_DIR
from .const import CONF_STATION_UPDATES, PLATFORMS
from .const import CONF_RADAR_UPDATES, CONF_STATION_UPDATES, DOMAIN, PLATFORMS
from .coordinator import AemetConfigEntry, AemetData, WeatherUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
@@ -24,11 +26,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: AemetConfigEntry) -> boo
latitude = entry.data[CONF_LATITUDE]
longitude = entry.data[CONF_LONGITUDE]
update_features: int = UpdateFeature.FORECAST
if entry.options.get(CONF_RADAR_UPDATES, False):
update_features |= UpdateFeature.RADAR
if entry.options.get(CONF_STATION_UPDATES, True):
update_features |= UpdateFeature.STATION
options = ConnectionOptions(api_key, update_features)
aemet = AEMET(aiohttp_client.async_get_clientsession(hass), options)
aemet.set_api_data_dir(hass.config.path(STORAGE_DIR, f"{DOMAIN}-{entry.unique_id}"))
try:
await aemet.select_coordinates(latitude, longitude)
except TownNotFound as err:
@@ -57,3 +63,11 @@ async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Remove a config entry."""
await hass.async_add_executor_job(
shutil.rmtree,
hass.config.path(STORAGE_DIR, f"{DOMAIN}-{entry.unique_id}"),
)
@@ -17,10 +17,11 @@ from homeassistant.helpers.schema_config_entry_flow import (
SchemaOptionsFlowHandler,
)
from .const import CONF_STATION_UPDATES, DEFAULT_NAME, DOMAIN
from .const import CONF_RADAR_UPDATES, CONF_STATION_UPDATES, DEFAULT_NAME, DOMAIN
OPTIONS_SCHEMA = vol.Schema(
{
vol.Required(CONF_RADAR_UPDATES, default=False): bool,
vol.Required(CONF_STATION_UPDATES, default=True): bool,
}
)
+2 -1
View File
@@ -51,8 +51,9 @@ from homeassistant.components.weather import (
from homeassistant.const import Platform
ATTRIBUTION = "Powered by AEMET OpenData"
CONF_RADAR_UPDATES = "radar_updates"
CONF_STATION_UPDATES = "station_updates"
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
PLATFORMS = [Platform.IMAGE, Platform.SENSOR, Platform.WEATHER]
DEFAULT_NAME = "AEMET"
DOMAIN = "aemet"
@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Any
from aemet_opendata.const import AOD_COORDS
from aemet_opendata.const import AOD_COORDS, AOD_IMG_BYTES
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import (
@@ -26,6 +26,7 @@ TO_REDACT_CONFIG = [
TO_REDACT_COORD = [
AOD_COORDS,
AOD_IMG_BYTES,
]
+86
View File
@@ -0,0 +1,86 @@
"""Support for the AEMET OpenData images."""
from __future__ import annotations
from typing import Final
from aemet_opendata.const import AOD_DATETIME, AOD_IMG_BYTES, AOD_IMG_TYPE, AOD_RADAR
from aemet_opendata.helpers import dict_nested_value
from homeassistant.components.image import Image, ImageEntity, ImageEntityDescription
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .coordinator import AemetConfigEntry, WeatherUpdateCoordinator
from .entity import AemetEntity
AEMET_IMAGES: Final[tuple[ImageEntityDescription, ...]] = (
ImageEntityDescription(
key=AOD_RADAR,
translation_key="weather_radar",
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AemetConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AEMET OpenData image entities based on a config entry."""
domain_data = config_entry.runtime_data
name = domain_data.name
coordinator = domain_data.coordinator
unique_id = config_entry.unique_id
assert unique_id is not None
async_add_entities(
AemetImage(
hass,
name,
coordinator,
description,
unique_id,
)
for description in AEMET_IMAGES
if dict_nested_value(coordinator.data["lib"], [description.key]) is not None
)
class AemetImage(AemetEntity, ImageEntity):
"""Implementation of an AEMET OpenData image."""
entity_description: ImageEntityDescription
def __init__(
self,
hass: HomeAssistant,
name: str,
coordinator: WeatherUpdateCoordinator,
description: ImageEntityDescription,
unique_id: str,
) -> None:
"""Initialize the image."""
super().__init__(coordinator, name, unique_id)
ImageEntity.__init__(self, hass)
self.entity_description = description
self._attr_unique_id = f"{unique_id}-{description.key}"
self._async_update_attrs()
@callback
def _handle_coordinator_update(self) -> None:
"""Update attributes when the coordinator updates."""
self._async_update_attrs()
super()._handle_coordinator_update()
@callback
def _async_update_attrs(self) -> None:
"""Update image attributes."""
image_data = self.get_aemet_value([self.entity_description.key])
self._cached_image = Image(
content_type=image_data.get(AOD_IMG_TYPE),
content=image_data.get(AOD_IMG_BYTES),
)
self._attr_image_last_updated = image_data.get(AOD_DATETIME)
+1 -1
View File
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/aemet",
"iot_class": "cloud_polling",
"loggers": ["aemet_opendata"],
"requirements": ["AEMET-OpenData==0.6.3"]
"requirements": ["AEMET-OpenData==0.6.4"]
}
@@ -18,10 +18,18 @@
}
}
},
"entity": {
"image": {
"weather_radar": {
"name": "Weather radar"
}
}
},
"options": {
"step": {
"init": {
"data": {
"radar_updates": "Gather data from AEMET weather radar",
"station_updates": "Gather data from AEMET weather stations"
}
}
@@ -31,7 +31,9 @@ rules:
# Silver
action-exceptions: todo
config-entry-unloading: done
docs-configuration-parameters: todo
docs-configuration-parameters:
status: exempt
comment: No options to configure
docs-installation-parameters: todo
entity-unavailable: done
integration-owner: done
@@ -41,12 +43,16 @@ rules:
status: exempt
comment: |
This integration does not require authentication.
test-coverage: done
test-coverage: todo
# Gold
devices: done
diagnostics: done
discovery-update-info: done
discovery: done
discovery-update-info:
status: todo
comment: DHCP is still possible
discovery:
status: todo
comment: DHCP is still possible
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
@@ -41,7 +41,7 @@
}
},
"enable_motion_recording": {
"name": "Enables motion recording",
"name": "Enable motion recording",
"description": "Enables recording a clip to camera storage when motion is detected.",
"fields": {
"entity_id": {
@@ -51,8 +51,8 @@
}
},
"disable_motion_recording": {
"name": "Disables motion recording",
"description": "Disable recording a clip to camera storage when motion is detected.",
"name": "Disable motion recording",
"description": "Disables recording a clip to camera storage when motion is detected.",
"fields": {
"entity_id": {
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
@@ -11,12 +11,7 @@ from python_homeassistant_analytics import (
from python_homeassistant_analytics.models import IntegrationType
import voluptuous as vol
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
@@ -25,6 +20,7 @@ from homeassistant.helpers.selector import (
SelectSelectorConfig,
)
from . import AnalyticsInsightsConfigEntry
from .const import (
CONF_TRACKED_ADDONS,
CONF_TRACKED_CUSTOM_INTEGRATIONS,
@@ -46,7 +42,7 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
config_entry: AnalyticsInsightsConfigEntry,
) -> HomeassistantAnalyticsOptionsFlowHandler:
"""Get the options flow for this handler."""
return HomeassistantAnalyticsOptionsFlowHandler()
@@ -7,6 +7,6 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["python_homeassistant_analytics"],
"requirements": ["python-homeassistant-analytics==0.8.0"],
"requirements": ["python-homeassistant-analytics==0.8.1"],
"single_config_entry": true
}
@@ -0,0 +1,100 @@
rules:
# Bronze
action-setup:
status: exempt
comment: |
This integration does not provide additional actions.
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
This integration does not provide additional actions.
docs-high-level-description: todo
docs-installation-instructions: todo
docs-removal-instructions: todo
entity-event-setup:
status: exempt
comment: |
Entities of this integration does not explicitly subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions:
status: exempt
comment: |
This integration does not provide actions.
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo
entity-unavailable:
status: done
comment: |
The coordinator handles this.
integration-owner: done
log-when-unavailable:
status: done
comment: |
The coordinator handles this.
parallel-updates: todo
reauthentication-flow:
status: exempt
comment: |
This integration does not require authentication.
test-coverage: todo
# Gold
devices: done
diagnostics: todo
discovery-update-info:
status: exempt
comment: |
This integration is a cloud service and thus does not support discovery.
discovery:
status: exempt
comment: |
This integration is a cloud service and thus does not support discovery.
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
dynamic-devices:
status: exempt
comment: |
This integration has a fixed single service.
entity-category: done
entity-device-class:
status: exempt
comment: |
This integration does not have entities with device classes.
entity-disabled-by-default: done
entity-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow:
status: exempt
comment: All the options of this integration are managed via the options flow
repair-issues:
status: exempt
comment: |
This integration doesn't have any cases where raising an issue is needed.
stale-devices:
status: exempt
comment: |
This integration has a fixed single service.
# Platinum
async-dependency: done
inject-websession: done
strict-typing: done
@@ -156,7 +156,12 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
# and one of them, which could end up being in discovery_info.host, is from a
# different device. If any of the discovery_info.ip_addresses matches the
# existing host, don't update the host.
if existing_config_entry and len(discovery_info.ip_addresses) > 1:
if (
existing_config_entry
# Ignored entries don't have host
and CONF_HOST in existing_config_entry.data
and len(discovery_info.ip_addresses) > 1
):
existing_host = existing_config_entry.data[CONF_HOST]
if existing_host != self.host:
if existing_host in [
@@ -6,5 +6,5 @@
"iot_class": "cloud_push",
"loggers": ["apprise"],
"quality_scale": "legacy",
"requirements": ["apprise==1.9.0"]
"requirements": ["apprise==1.9.1"]
}
+10 -2
View File
@@ -2,6 +2,8 @@
from __future__ import annotations
from aiohttp import ClientConnectorError
from homeassistant.components.number import NumberDeviceClass, NumberEntity, NumberMode
from homeassistant.const import UnitOfPower
from homeassistant.core import HomeAssistant
@@ -20,7 +22,7 @@ async def async_setup_entry(
) -> None:
"""Set up the sensor platform."""
add_entities([ApSystemsMaxOutputNumber(config_entry.runtime_data)])
add_entities([ApSystemsMaxOutputNumber(config_entry.runtime_data)], True)
class ApSystemsMaxOutputNumber(ApSystemsEntity, NumberEntity):
@@ -45,7 +47,13 @@ class ApSystemsMaxOutputNumber(ApSystemsEntity, NumberEntity):
async def async_update(self) -> None:
"""Set the state with the value fetched from the inverter."""
self._attr_native_value = await self._api.get_max_power()
try:
status = await self._api.get_max_power()
except (TimeoutError, ClientConnectorError):
self._attr_available = False
else:
self._attr_available = True
self._attr_native_value = status
async def async_set_native_value(self, value: float) -> None:
"""Set the desired output power."""
@@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.5"]
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.6"]
}
@@ -5,6 +5,10 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.typing import ConfigType
# Pre-import backup to avoid it being imported
# later when the import executor is busy and delaying
# startup
from . import backup # noqa: F401
from .agent import (
BackupAgent,
BackupAgentError,
+20 -20
View File
@@ -33,8 +33,8 @@ class StoredBackupConfig(TypedDict):
"""Represent the stored backup config."""
create_backup: StoredCreateBackupConfig
last_attempted_strategy_backup: str | None
last_completed_strategy_backup: str | None
last_attempted_automatic_backup: str | None
last_completed_automatic_backup: str | None
retention: StoredRetentionConfig
schedule: StoredBackupSchedule
@@ -44,8 +44,8 @@ class BackupConfigData:
"""Represent loaded backup config data."""
create_backup: CreateBackupConfig
last_attempted_strategy_backup: datetime | None = None
last_completed_strategy_backup: datetime | None = None
last_attempted_automatic_backup: datetime | None = None
last_completed_automatic_backup: datetime | None = None
retention: RetentionConfig
schedule: BackupSchedule
@@ -59,12 +59,12 @@ class BackupConfigData:
include_folders = None
retention = data["retention"]
if last_attempted_str := data["last_attempted_strategy_backup"]:
if last_attempted_str := data["last_attempted_automatic_backup"]:
last_attempted = dt_util.parse_datetime(last_attempted_str)
else:
last_attempted = None
if last_attempted_str := data["last_completed_strategy_backup"]:
if last_attempted_str := data["last_completed_automatic_backup"]:
last_completed = dt_util.parse_datetime(last_attempted_str)
else:
last_completed = None
@@ -79,8 +79,8 @@ class BackupConfigData:
name=data["create_backup"]["name"],
password=data["create_backup"]["password"],
),
last_attempted_strategy_backup=last_attempted,
last_completed_strategy_backup=last_completed,
last_attempted_automatic_backup=last_attempted,
last_completed_automatic_backup=last_completed,
retention=RetentionConfig(
copies=retention["copies"],
days=retention["days"],
@@ -90,20 +90,20 @@ class BackupConfigData:
def to_dict(self) -> StoredBackupConfig:
"""Convert backup config data to a dict."""
if self.last_attempted_strategy_backup:
last_attempted = self.last_attempted_strategy_backup.isoformat()
if self.last_attempted_automatic_backup:
last_attempted = self.last_attempted_automatic_backup.isoformat()
else:
last_attempted = None
if self.last_completed_strategy_backup:
last_completed = self.last_completed_strategy_backup.isoformat()
if self.last_completed_automatic_backup:
last_completed = self.last_completed_automatic_backup.isoformat()
else:
last_completed = None
return StoredBackupConfig(
create_backup=self.create_backup.to_dict(),
last_attempted_strategy_backup=last_attempted,
last_completed_strategy_backup=last_completed,
last_attempted_automatic_backup=last_attempted,
last_completed_automatic_backup=last_completed,
retention=self.retention.to_dict(),
schedule=self.schedule.to_dict(),
)
@@ -286,7 +286,7 @@ class BackupSchedule:
self._unschedule_next(manager)
now = dt_util.now()
if (cron_event := self.cron_event) is None:
seed_time = manager.config.data.last_completed_strategy_backup or now
seed_time = manager.config.data.last_completed_automatic_backup or now
cron_event = self.cron_event = CronSim(cron_pattern, seed_time)
next_time = next(cron_event)
@@ -316,7 +316,7 @@ class BackupSchedule:
include_homeassistant=True, # always include HA
name=config_data.create_backup.name,
password=config_data.create_backup.password,
with_strategy_settings=True,
with_automatic_settings=True,
)
except Exception: # noqa: BLE001
# another more specific exception will be added
@@ -404,14 +404,14 @@ async def _delete_filtered_backups(
get_agent_errors,
)
# only delete backups that are created by the backup strategy
# only delete backups that are created with the saved automatic settings
backups = {
backup_id: backup
for backup_id, backup in backups.items()
if backup.with_strategy_settings
if backup.with_automatic_settings
}
LOGGER.debug("Total strategy backups: %s", backups)
LOGGER.debug("Total automatic backups: %s", backups)
filtered_backups = backup_filter(backups)
@@ -467,7 +467,7 @@ async def delete_backups_exceeding_configured_count(manager: BackupManager) -> N
sorted(
backups.items(),
key=lambda backup_item: backup_item[1].date,
)[: len(backups) - manager.config.data.retention.copies]
)[: max(len(backups) - manager.config.data.retention.copies, 0)]
)
await _delete_filtered_backups(manager, _backups_filter)
+108 -32
View File
@@ -23,7 +23,11 @@ from homeassistant.backup_restore import RESTORE_BACKUP_FILE, password_to_key
from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import integration_platform
from homeassistant.helpers import (
instance_id,
integration_platform,
issue_registry as ir,
)
from homeassistant.helpers.json import json_bytes
from homeassistant.util import dt as dt_util
@@ -44,7 +48,11 @@ from .const import (
)
from .models import AgentBackup, Folder
from .store import BackupStore
from .util import make_backup_dir, read_backup
from .util import make_backup_dir, read_backup, validate_password
class IncorrectPasswordError(HomeAssistantError):
"""Raised when the password is incorrect."""
@dataclass(frozen=True, kw_only=True, slots=True)
@@ -60,7 +68,7 @@ class ManagerBackup(AgentBackup):
agent_ids: list[str]
failed_agent_ids: list[str]
with_strategy_settings: bool
with_automatic_settings: bool | None
@dataclass(frozen=True, kw_only=True, slots=True)
@@ -200,6 +208,7 @@ class BackupReaderWriter(abc.ABC):
*,
agent_ids: list[str],
backup_name: str,
extra_metadata: dict[str, bool | str],
include_addons: list[str] | None,
include_all_addons: bool,
include_database: bool,
@@ -445,16 +454,18 @@ class BackupManager:
if (backup_id := agent_backup.backup_id) not in backups:
if known_backup := self.known_backups.get(backup_id):
failed_agent_ids = known_backup.failed_agent_ids
with_strategy_settings = known_backup.with_strategy_settings
else:
failed_agent_ids = []
with_strategy_settings = False
with_automatic_settings = self.is_our_automatic_backup(
agent_backup, await instance_id.async_get(self.hass)
)
backups[backup_id] = ManagerBackup(
agent_ids=[],
addons=agent_backup.addons,
backup_id=backup_id,
date=agent_backup.date,
database_included=agent_backup.database_included,
extra_metadata=agent_backup.extra_metadata,
failed_agent_ids=failed_agent_ids,
folders=agent_backup.folders,
homeassistant_included=agent_backup.homeassistant_included,
@@ -462,7 +473,7 @@ class BackupManager:
name=agent_backup.name,
protected=agent_backup.protected,
size=agent_backup.size,
with_strategy_settings=with_strategy_settings,
with_automatic_settings=with_automatic_settings,
)
backups[backup_id].agent_ids.append(agent_ids[idx])
@@ -494,16 +505,18 @@ class BackupManager:
if backup is None:
if known_backup := self.known_backups.get(backup_id):
failed_agent_ids = known_backup.failed_agent_ids
with_strategy_settings = known_backup.with_strategy_settings
else:
failed_agent_ids = []
with_strategy_settings = False
with_automatic_settings = self.is_our_automatic_backup(
result, await instance_id.async_get(self.hass)
)
backup = ManagerBackup(
agent_ids=[],
addons=result.addons,
backup_id=result.backup_id,
date=result.date,
database_included=result.database_included,
extra_metadata=result.extra_metadata,
failed_agent_ids=failed_agent_ids,
folders=result.folders,
homeassistant_included=result.homeassistant_included,
@@ -511,12 +524,28 @@ class BackupManager:
name=result.name,
protected=result.protected,
size=result.size,
with_strategy_settings=with_strategy_settings,
with_automatic_settings=with_automatic_settings,
)
backup.agent_ids.append(agent_ids[idx])
return (backup, agent_errors)
@staticmethod
def is_our_automatic_backup(
backup: AgentBackup, our_instance_id: str
) -> bool | None:
"""Check if a backup was created by us and return automatic_settings flag.
Returns `None` if the backup was not created by us, or if the
automatic_settings flag is not a boolean.
"""
if backup.extra_metadata.get("instance_id") != our_instance_id:
return None
with_automatic_settings = backup.extra_metadata.get("with_automatic_settings")
if not isinstance(with_automatic_settings, bool):
return None
return with_automatic_settings
async def async_delete_backup(self, backup_id: str) -> dict[str, Exception]:
"""Delete a backup."""
agent_errors: dict[str, Exception] = {}
@@ -598,7 +627,7 @@ class BackupManager:
open_stream=written_backup.open_stream,
)
await written_backup.release_stream()
self.known_backups.add(written_backup.backup, agent_errors, False)
self.known_backups.add(written_backup.backup, agent_errors)
async def async_create_backup(
self,
@@ -611,7 +640,7 @@ class BackupManager:
include_homeassistant: bool,
name: str | None,
password: str | None,
with_strategy_settings: bool = False,
with_automatic_settings: bool = False,
) -> NewBackup:
"""Create a backup."""
new_backup = await self.async_initiate_backup(
@@ -623,7 +652,7 @@ class BackupManager:
include_homeassistant=include_homeassistant,
name=name,
password=password,
with_strategy_settings=with_strategy_settings,
with_automatic_settings=with_automatic_settings,
)
assert self._backup_finish_task
await self._backup_finish_task
@@ -640,14 +669,14 @@ class BackupManager:
include_homeassistant: bool,
name: str | None,
password: str | None,
with_strategy_settings: bool = False,
with_automatic_settings: bool = False,
) -> NewBackup:
"""Initiate generating a backup."""
if self.state is not BackupManagerState.IDLE:
raise HomeAssistantError(f"Backup manager busy: {self.state}")
if with_strategy_settings:
self.config.data.last_attempted_strategy_backup = dt_util.now()
if with_automatic_settings:
self.config.data.last_attempted_automatic_backup = dt_util.now()
self.store.save()
self.async_on_backup_event(
@@ -663,13 +692,15 @@ class BackupManager:
include_homeassistant=include_homeassistant,
name=name,
password=password,
with_strategy_settings=with_strategy_settings,
with_automatic_settings=with_automatic_settings,
)
except Exception:
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED)
)
self.async_on_backup_event(IdleEvent())
if with_automatic_settings:
self._update_issue_backup_failed()
raise
async def _async_create_backup(
@@ -683,7 +714,7 @@ class BackupManager:
include_homeassistant: bool,
name: str | None,
password: str | None,
with_strategy_settings: bool,
with_automatic_settings: bool,
) -> NewBackup:
"""Initiate generating a backup."""
if not agent_ids:
@@ -695,10 +726,17 @@ class BackupManager:
"Cannot include all addons and specify specific addons"
)
backup_name = name or f"Core {HAVERSION}"
backup_name = (
name
or f"{"Automatic" if with_automatic_settings else "Custom"} {HAVERSION}"
)
new_backup, self._backup_task = await self._reader_writer.async_create_backup(
agent_ids=agent_ids,
backup_name=backup_name,
extra_metadata={
"instance_id": await instance_id.async_get(self.hass),
"with_automatic_settings": with_automatic_settings,
},
include_addons=include_addons,
include_all_addons=include_all_addons,
include_database=include_database,
@@ -708,13 +746,13 @@ class BackupManager:
password=password,
)
self._backup_finish_task = self.hass.async_create_task(
self._async_finish_backup(agent_ids, with_strategy_settings),
self._async_finish_backup(agent_ids, with_automatic_settings),
name="backup_manager_finish_backup",
)
return new_backup
async def _async_finish_backup(
self, agent_ids: list[str], with_strategy_settings: bool
self, agent_ids: list[str], with_automatic_settings: bool
) -> None:
if TYPE_CHECKING:
assert self._backup_task is not None
@@ -725,6 +763,8 @@ class BackupManager:
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED)
)
if with_automatic_settings:
self._update_issue_backup_failed()
else:
LOGGER.debug(
"Generated new backup with backup_id %s, uploading to agents %s",
@@ -743,13 +783,12 @@ class BackupManager:
open_stream=written_backup.open_stream,
)
await written_backup.release_stream()
if with_strategy_settings:
# create backup was successful, update last_completed_strategy_backup
self.config.data.last_completed_strategy_backup = dt_util.now()
if with_automatic_settings:
# create backup was successful, update last_completed_automatic_backup
self.config.data.last_completed_automatic_backup = dt_util.now()
self.store.save()
self.known_backups.add(
written_backup.backup, agent_errors, with_strategy_settings
)
self._update_issue_after_agent_upload(agent_errors)
self.known_backups.add(written_backup.backup, agent_errors)
# delete old backups more numerous than copies
await delete_backups_exceeding_configured_count(self)
@@ -855,6 +894,38 @@ class BackupManager:
self._backup_event_subscriptions.append(on_event)
return remove_subscription
def _update_issue_backup_failed(self) -> None:
"""Update issue registry when a backup fails."""
ir.async_create_issue(
self.hass,
DOMAIN,
"automatic_backup_failed",
is_fixable=False,
is_persistent=True,
learn_more_url="homeassistant://config/backup",
severity=ir.IssueSeverity.WARNING,
translation_key="automatic_backup_failed_create",
)
def _update_issue_after_agent_upload(
self, agent_errors: dict[str, Exception]
) -> None:
"""Update issue registry after a backup is uploaded to agents."""
if not agent_errors:
ir.async_delete_issue(self.hass, DOMAIN, "automatic_backup_failed")
return
ir.async_create_issue(
self.hass,
DOMAIN,
"automatic_backup_failed",
is_fixable=False,
is_persistent=True,
learn_more_url="homeassistant://config/backup",
severity=ir.IssueSeverity.WARNING,
translation_key="automatic_backup_failed_upload_agents",
translation_placeholders={"failed_agents": ", ".join(agent_errors)},
)
class KnownBackups:
"""Track known backups."""
@@ -870,7 +941,6 @@ class KnownBackups:
backup["backup_id"]: KnownBackup(
backup_id=backup["backup_id"],
failed_agent_ids=backup["failed_agent_ids"],
with_strategy_settings=backup["with_strategy_settings"],
)
for backup in stored_backups
}
@@ -883,13 +953,11 @@ class KnownBackups:
self,
backup: AgentBackup,
agent_errors: dict[str, Exception],
with_strategy_settings: bool,
) -> None:
"""Add a backup."""
self._backups[backup.backup_id] = KnownBackup(
backup_id=backup.backup_id,
failed_agent_ids=list(agent_errors),
with_strategy_settings=with_strategy_settings,
)
self._manager.store.save()
@@ -911,14 +979,12 @@ class KnownBackup:
backup_id: str
failed_agent_ids: list[str]
with_strategy_settings: bool
def to_dict(self) -> StoredKnownBackup:
"""Convert known backup to a dict."""
return {
"backup_id": self.backup_id,
"failed_agent_ids": self.failed_agent_ids,
"with_strategy_settings": self.with_strategy_settings,
}
@@ -927,7 +993,6 @@ class StoredKnownBackup(TypedDict):
backup_id: str
failed_agent_ids: list[str]
with_strategy_settings: bool
class CoreBackupReaderWriter(BackupReaderWriter):
@@ -945,6 +1010,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
*,
agent_ids: list[str],
backup_name: str,
extra_metadata: dict[str, bool | str],
include_addons: list[str] | None,
include_all_addons: bool,
include_database: bool,
@@ -969,6 +1035,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
agent_ids=agent_ids,
backup_id=backup_id,
backup_name=backup_name,
extra_metadata=extra_metadata,
include_database=include_database,
date_str=date_str,
on_progress=on_progress,
@@ -987,6 +1054,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
backup_id: str,
backup_name: str,
date_str: str,
extra_metadata: dict[str, bool | str],
include_database: bool,
on_progress: Callable[[ManagerStateEvent], None],
password: str | None,
@@ -1012,6 +1080,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
backup_data = {
"compressed": True,
"date": date_str,
"extra": extra_metadata,
"homeassistant": {
"exclude_database": not include_database,
"version": HAVERSION,
@@ -1035,6 +1104,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
backup_id=backup_id,
database_included=include_database,
date=date_str,
extra_metadata=extra_metadata,
folders=[],
homeassistant_included=True,
homeassistant_version=HAVERSION,
@@ -1206,6 +1276,12 @@ class CoreBackupReaderWriter(BackupReaderWriter):
remove_after_restore = True
password_valid = await self._hass.async_add_executor_job(
validate_password, path, password
)
if not password_valid:
raise IncorrectPasswordError("The password provided is incorrect.")
def _write_restore_file() -> None:
"""Write the restore file."""
Path(self._hass.config.path(RESTORE_BACKUP_FILE)).write_text(
@@ -33,6 +33,7 @@ class AgentBackup:
backup_id: str
date: str
database_included: bool
extra_metadata: dict[str, bool | str]
folders: list[Folder]
homeassistant_included: bool
homeassistant_version: str | None # None if homeassistant_included is False
@@ -44,6 +45,12 @@ class AgentBackup:
"""Return a dict representation of this backup."""
return asdict(self)
def as_frontend_json(self) -> dict:
"""Return a dict representation of this backup for sending to frontend."""
return {
key: val for key, val in asdict(self).items() if key != "extra_metadata"
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
"""Create an instance from a JSON serialization."""
@@ -52,6 +59,7 @@ class AgentBackup:
backup_id=data["backup_id"],
date=data["date"],
database_included=data["database_included"],
extra_metadata=data["extra_metadata"],
folders=[Folder(folder) for folder in data["folders"]],
homeassistant_included=data["homeassistant_included"],
homeassistant_version=data["homeassistant_version"],
@@ -1,4 +1,14 @@
{
"issues": {
"automatic_backup_failed_create": {
"title": "Automatic backup could not be created",
"description": "The automatic backup could not be created. Please check the logs for more information. Another attempt will be made at the next scheduled time if a backup schedule is configured."
},
"automatic_backup_failed_upload_agents": {
"title": "Automatic backup could not be uploaded to agents",
"description": "The automatic backup could not be uploaded to agents {failed_agents}. Please check the logs for more information. Another attempt will be made at the next scheduled time if a backup schedule is configured."
}
},
"services": {
"create": {
"name": "Create backup",
+38 -1
View File
@@ -9,11 +9,13 @@ import tarfile
from typing import cast
import aiohttp
from securetar import SecureTarFile
from homeassistant.backup_restore import password_to_key
from homeassistant.core import HomeAssistant
from homeassistant.util.json import JsonObjectType, json_loads_object
from .const import BUF_SIZE
from .const import BUF_SIZE, LOGGER
from .models import AddonInfo, AgentBackup, Folder
@@ -50,6 +52,7 @@ def read_backup(backup_path: Path) -> AgentBackup:
if (
homeassistant := cast(JsonObjectType, data.get("homeassistant"))
) and "version" in homeassistant:
homeassistant_included = True
homeassistant_version = cast(str, homeassistant["version"])
database_included = not cast(
bool, homeassistant.get("exclude_database", False)
@@ -60,6 +63,7 @@ def read_backup(backup_path: Path) -> AgentBackup:
backup_id=cast(str, data["slug"]),
database_included=database_included,
date=cast(str, data["date"]),
extra_metadata=cast(dict[str, bool | str], data.get("extra", {})),
folders=folders,
homeassistant_included=homeassistant_included,
homeassistant_version=homeassistant_version,
@@ -69,6 +73,39 @@ def read_backup(backup_path: Path) -> AgentBackup:
)
def validate_password(path: Path, password: str | None) -> bool:
"""Validate the password."""
with tarfile.open(path, "r:", bufsize=BUF_SIZE) as backup_file:
compressed = False
ha_tar_name = "homeassistant.tar"
try:
ha_tar = backup_file.extractfile(ha_tar_name)
except KeyError:
compressed = True
ha_tar_name = "homeassistant.tar.gz"
try:
ha_tar = backup_file.extractfile(ha_tar_name)
except KeyError:
LOGGER.error("No homeassistant.tar or homeassistant.tar.gz found")
return False
try:
with SecureTarFile(
path, # Not used
gzip=compressed,
key=password_to_key(password) if password is not None else None,
mode="r",
fileobj=ha_tar,
):
# If we can read the tar file, the password is correct
return True
except tarfile.ReadError:
LOGGER.debug("Invalid password")
return False
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected error validating password")
return False
async def receive_file(
hass: HomeAssistant, contents: aiohttp.BodyPartReader, path: Path
) -> None:
+30 -22
View File
@@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant, callback
from .config import ScheduleState
from .const import DATA_MANAGER, LOGGER
from .manager import ManagerStateEvent
from .manager import IncorrectPasswordError, ManagerStateEvent
from .models import Folder
@@ -25,7 +25,7 @@ def async_register_websocket_handlers(hass: HomeAssistant, with_hassio: bool) ->
websocket_api.async_register_command(hass, handle_details)
websocket_api.async_register_command(hass, handle_info)
websocket_api.async_register_command(hass, handle_create)
websocket_api.async_register_command(hass, handle_create_with_strategy_settings)
websocket_api.async_register_command(hass, handle_create_with_automatic_settings)
websocket_api.async_register_command(hass, handle_delete)
websocket_api.async_register_command(hass, handle_restore)
websocket_api.async_register_command(hass, handle_subscribe_events)
@@ -51,9 +51,9 @@ async def handle_info(
"agent_errors": {
agent_id: str(err) for agent_id, err in agent_errors.items()
},
"backups": list(backups.values()),
"last_attempted_strategy_backup": manager.config.data.last_attempted_strategy_backup,
"last_completed_strategy_backup": manager.config.data.last_completed_strategy_backup,
"backups": [backup.as_frontend_json() for backup in backups.values()],
"last_attempted_automatic_backup": manager.config.data.last_attempted_automatic_backup,
"last_completed_automatic_backup": manager.config.data.last_completed_automatic_backup,
},
)
@@ -81,7 +81,7 @@ async def handle_details(
"agent_errors": {
agent_id: str(err) for agent_id, err in agent_errors.items()
},
"backup": backup,
"backup": backup.as_frontend_json() if backup else None,
},
)
@@ -131,16 +131,20 @@ async def handle_restore(
msg: dict[str, Any],
) -> None:
"""Restore a backup."""
await hass.data[DATA_MANAGER].async_restore_backup(
msg["backup_id"],
agent_id=msg["agent_id"],
password=msg.get("password"),
restore_addons=msg.get("restore_addons"),
restore_database=msg["restore_database"],
restore_folders=msg.get("restore_folders"),
restore_homeassistant=msg["restore_homeassistant"],
)
connection.send_result(msg["id"])
try:
await hass.data[DATA_MANAGER].async_restore_backup(
msg["backup_id"],
agent_id=msg["agent_id"],
password=msg.get("password"),
restore_addons=msg.get("restore_addons"),
restore_database=msg["restore_database"],
restore_folders=msg.get("restore_folders"),
restore_homeassistant=msg["restore_homeassistant"],
)
except IncorrectPasswordError:
connection.send_error(msg["id"], "password_incorrect", "Incorrect password")
else:
connection.send_result(msg["id"])
@websocket_api.require_admin
@@ -181,11 +185,11 @@ async def handle_create(
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required("type"): "backup/generate_with_strategy_settings",
vol.Required("type"): "backup/generate_with_automatic_settings",
}
)
@websocket_api.async_response
async def handle_create_with_strategy_settings(
async def handle_create_with_automatic_settings(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
@@ -202,7 +206,7 @@ async def handle_create_with_strategy_settings(
include_homeassistant=True, # always include HA
name=config_data.create_backup.name,
password=config_data.create_backup.password,
with_strategy_settings=True,
with_automatic_settings=True,
)
connection.send_result(msg["id"], backup)
@@ -291,11 +295,15 @@ async def handle_config_info(
vol.Required("type"): "backup/config/update",
vol.Optional("create_backup"): vol.Schema(
{
vol.Optional("agent_ids"): vol.All(list[str]),
vol.Optional("include_addons"): vol.Any(list[str], None),
vol.Optional("agent_ids"): vol.All([str], vol.Unique()),
vol.Optional("include_addons"): vol.Any(
vol.All([str], vol.Unique()), None
),
vol.Optional("include_all_addons"): bool,
vol.Optional("include_database"): bool,
vol.Optional("include_folders"): vol.Any([vol.Coerce(Folder)], None),
vol.Optional("include_folders"): vol.Any(
vol.All([vol.Coerce(Folder)], vol.Unique()), None
),
vol.Optional("name"): vol.Any(str, None),
vol.Optional("password"): vol.Any(str, None),
},
+5 -5
View File
@@ -84,16 +84,16 @@
}
},
"send_pin": {
"name": "Send pin",
"description": "Sends a new PIN to blink for 2FA.",
"name": "Send PIN",
"description": "Sends a new PIN to Blink for 2FA.",
"fields": {
"pin": {
"name": "Pin",
"description": "PIN received from blink. Leave empty if you only received a verification email."
"name": "PIN",
"description": "PIN received from Blink. Leave empty if you only received a verification email."
},
"config_entry_id": {
"name": "Integration ID",
"description": "The Blink Integration id."
"description": "The Blink Integration ID."
}
}
}
@@ -103,9 +103,10 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
data: dict[str, Any] = {}
_existing_entry_data: Mapping[str, Any] | None = None
def __init__(self) -> None:
"""Initialize the config flow."""
self.data: dict[str, Any] = {}
self._existing_entry_data: dict[str, Any] = {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -175,19 +176,15 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Show the change password step."""
existing_data = (
dict(self._existing_entry_data) if self._existing_entry_data else {}
)
if user_input is not None:
return await self.async_step_user(existing_data | user_input)
return await self.async_step_user(self._existing_entry_data | user_input)
return self.async_show_form(
step_id="change_password",
data_schema=RECONFIGURE_SCHEMA,
description_placeholders={
CONF_USERNAME: existing_data[CONF_USERNAME],
CONF_REGION: existing_data[CONF_REGION],
CONF_USERNAME: self._existing_entry_data[CONF_USERNAME],
CONF_REGION: self._existing_entry_data[CONF_REGION],
},
)
@@ -195,14 +192,14 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle configuration by re-auth."""
self._existing_entry_data = entry_data
self._existing_entry_data = dict(entry_data)
return await self.async_step_change_password()
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a reconfiguration flow initialized by the user."""
self._existing_entry_data = self._get_reconfigure_entry().data
self._existing_entry_data = dict(self._get_reconfigure_entry().data)
return await self.async_step_change_password()
async def async_step_captcha(
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/caldav",
"iot_class": "cloud_polling",
"loggers": ["caldav", "vobject"],
"requirements": ["caldav==1.3.9"]
"requirements": ["caldav==1.3.9", "icalendar==6.1.0"]
}
@@ -0,0 +1,85 @@
"""Support for media browsing."""
from aiostreammagic import StreamMagicClient
from aiostreammagic.models import Preset
from homeassistant.components.media_player import BrowseMedia, MediaClass
from homeassistant.core import HomeAssistant
async def async_browse_media(
hass: HomeAssistant,
client: StreamMagicClient,
media_content_id: str | None,
media_content_type: str | None,
) -> BrowseMedia:
"""Browse media."""
if media_content_type == "presets":
return await _presets_payload(client.preset_list.presets)
return await _root_payload(
hass,
client,
)
async def _root_payload(
hass: HomeAssistant,
client: StreamMagicClient,
) -> BrowseMedia:
"""Return root payload for Cambridge Audio."""
children: list[BrowseMedia] = []
if client.preset_list.presets:
children.append(
BrowseMedia(
title="Presets",
media_class=MediaClass.DIRECTORY,
media_content_id="",
media_content_type="presets",
thumbnail="https://brands.home-assistant.io/_/cambridge_audio/logo.png",
can_play=False,
can_expand=True,
)
)
return BrowseMedia(
title="Cambridge Audio",
media_class=MediaClass.DIRECTORY,
media_content_id="",
media_content_type="root",
can_play=False,
can_expand=True,
children=children,
)
async def _presets_payload(presets: list[Preset]) -> BrowseMedia:
"""Create payload to list presets."""
children: list[BrowseMedia] = []
for preset in presets:
if preset.state != "OK":
continue
children.append(
BrowseMedia(
title=preset.name,
media_class=MediaClass.MUSIC,
media_content_id=str(preset.preset_id),
media_content_type="preset",
can_play=True,
can_expand=False,
thumbnail=preset.art_url,
)
)
return BrowseMedia(
title="Presets",
media_class=MediaClass.DIRECTORY,
media_content_id="",
media_content_type="presets",
can_play=False,
can_expand=True,
children=children,
)
@@ -13,6 +13,7 @@ from aiostreammagic import (
)
from homeassistant.components.media_player import (
BrowseMedia,
MediaPlayerDeviceClass,
MediaPlayerEntity,
MediaPlayerEntityFeature,
@@ -24,7 +25,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import CambridgeAudioConfigEntry
from . import CambridgeAudioConfigEntry, media_browser
from .const import (
CAMBRIDGE_MEDIA_TYPE_AIRABLE,
CAMBRIDGE_MEDIA_TYPE_INTERNET_RADIO,
@@ -34,7 +35,8 @@ from .const import (
from .entity import CambridgeAudioEntity, command
BASE_FEATURES = (
MediaPlayerEntityFeature.SELECT_SOURCE
MediaPlayerEntityFeature.BROWSE_MEDIA
| MediaPlayerEntityFeature.SELECT_SOURCE
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.TURN_ON
| MediaPlayerEntityFeature.PLAY_MEDIA
@@ -338,3 +340,13 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
if media_type == CAMBRIDGE_MEDIA_TYPE_INTERNET_RADIO:
await self.client.play_radio_url("Radio", media_id)
async def async_browse_media(
self,
media_content_type: MediaType | str | None = None,
media_content_id: str | None = None,
) -> BrowseMedia:
"""Implement the media browsing helper."""
return await media_browser.async_browse_media(
self.hass, self.client, media_content_id, media_content_type
)
+2 -2
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
import configparser
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, ClassVar
from urllib.parse import urlparse
import aiohttp
@@ -129,7 +129,7 @@ class ChromecastInfo:
class ChromeCastZeroconf:
"""Class to hold a zeroconf instance."""
__zconf: zeroconf.HaZeroconf | None = None
__zconf: ClassVar[zeroconf.HaZeroconf | None] = None
@classmethod
def set_zeroconf(cls, zconf: zeroconf.HaZeroconf) -> None:
@@ -240,6 +240,7 @@ CACHED_PROPERTIES_WITH_ATTR_ = {
"preset_mode",
"preset_modes",
"is_aux_heat",
"is_on",
"fan_mode",
"fan_modes",
"swing_mode",
@@ -280,6 +281,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_hvac_mode: HVACMode | None
_attr_hvac_modes: list[HVACMode]
_attr_is_aux_heat: bool | None
_attr_is_on: bool | None
_attr_max_humidity: float = DEFAULT_MAX_HUMIDITY
_attr_max_temp: float
_attr_min_humidity: float = DEFAULT_MIN_HUMIDITY
@@ -352,11 +354,33 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
hvac_mode = self.hvac_mode
if hvac_mode is None:
return None
if hasattr(self, "_attr_is_on") and self._attr_is_on is False:
return HVACMode.OFF.value
# Support hvac_mode as string for custom integration backwards compatibility
if not isinstance(hvac_mode, HVACMode):
return HVACMode(hvac_mode).value # type: ignore[unreachable]
return hvac_mode.value
@property
def is_on(self) -> bool | None:
"""Return True if the climate is turned on.
The climate's on/off state can be be controlled independently
from the hvac_action and hvac_mode if the _attr_is_on attribute is set.
If the _attr_is_on attribute is set, then return that value.
Otherwise, return True if hvac_action is not None and not HVACAction.OFF.
Return None if hvac_action is None,
otherwise return True if hvac_mode is not HVACMode.OFF.
"""
if hasattr(self, "_attr_is_on"):
return self._attr_is_on
if self.hvac_action is not None:
return self.hvac_action != HVACAction.OFF
if self.hvac_mode is None:
return None
return self.hvac_mode != HVACMode.OFF
@property
def precision(self) -> float:
"""Return the precision of the system."""
+8 -1
View File
@@ -36,7 +36,14 @@ from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from homeassistant.util.signal_type import SignalType
from . import account_link, http_api
# Pre-import backup to avoid it being imported
# later when the import executor is busy and delaying
# startup
from . import (
account_link,
backup, # noqa: F401
http_api,
)
from .client import CloudClient
from .const import (
CONF_ACCOUNT_LINK_SERVER,
+28 -2
View File
@@ -3,7 +3,7 @@
from __future__ import annotations
import base64
from collections.abc import AsyncIterator, Callable, Coroutine
from collections.abc import AsyncIterator, Callable, Coroutine, Mapping
import hashlib
from typing import Any, Self
@@ -18,9 +18,10 @@ from hass_nabucasa.cloud_api import (
from homeassistant.components.backup import AgentBackup, BackupAgent, BackupAgentError
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .client import CloudClient
from .const import DATA_CLOUD, DOMAIN
from .const import DATA_CLOUD, DOMAIN, EVENT_CLOUD_EVENT
_STORAGE_BACKUP = "backup"
@@ -45,6 +46,31 @@ async def async_get_backup_agents(
return [CloudBackupAgent(hass=hass, cloud=cloud)]
@callback
def async_register_backup_agents_listener(
hass: HomeAssistant,
*,
listener: Callable[[], None],
**kwargs: Any,
) -> Callable[[], None]:
"""Register a listener to be called when agents are added or removed."""
@callback
def unsub() -> None:
"""Unsubscribe from events."""
unsub_signal()
@callback
def handle_event(data: Mapping[str, Any]) -> None:
"""Handle event."""
if data["type"] not in ("login", "logout"):
return
listener()
unsub_signal = async_dispatcher_connect(hass, EVENT_CLOUD_EVENT, handle_event)
return unsub
class ChunkAsyncStreamIterator:
"""Async iterator for chunked streams.
+1
View File
@@ -306,6 +306,7 @@ class CloudClient(Interface):
},
"version": HA_VERSION,
"instance_id": self.prefs.instance_id,
"name": self._hass.config.location_name,
}
async def async_alexa_message(self, payload: dict[Any, Any]) -> dict[Any, Any]:
+2
View File
@@ -18,6 +18,8 @@ DATA_CLOUD: HassKey[Cloud[CloudClient]] = HassKey(DOMAIN)
DATA_PLATFORMS_SETUP: HassKey[dict[str, asyncio.Event]] = HassKey(
"cloud_platforms_setup"
)
EVENT_CLOUD_EVENT = "cloud_event"
REQUEST_TIMEOUT = 10
PREF_ENABLE_ALEXA = "alexa_enabled"
@@ -34,6 +34,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.util.location import async_detect_location_info
from .alexa_config import entity_supported as entity_supported_by_alexa
@@ -41,6 +42,7 @@ from .assist_pipeline import async_create_cloud_pipeline
from .client import CloudClient
from .const import (
DATA_CLOUD,
EVENT_CLOUD_EVENT,
LOGIN_MFA_TIMEOUT,
PREF_ALEXA_REPORT_STATE,
PREF_DISABLE_2FA,
@@ -278,6 +280,8 @@ class CloudLoginView(HomeAssistantView):
new_cloud_pipeline_id = await async_create_cloud_pipeline(hass)
else:
new_cloud_pipeline_id = None
async_dispatcher_send(hass, EVENT_CLOUD_EVENT, {"type": "login"})
return self.json({"success": True, "cloud_pipeline": new_cloud_pipeline_id})
@@ -297,6 +301,7 @@ class CloudLogoutView(HomeAssistantView):
async with asyncio.timeout(REQUEST_TIMEOUT):
await cloud.logout()
async_dispatcher_send(hass, EVENT_CLOUD_EVENT, {"type": "logout"})
return self.json_message("ok")
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"requirements": ["aiocomelit==0.9.1"]
"requirements": ["aiocomelit==0.10.1"]
}
@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/compensation",
"iot_class": "calculated",
"quality_scale": "legacy",
"requirements": ["numpy==2.2.0"]
"requirements": ["numpy==2.2.1"]
}
@@ -46,13 +46,6 @@ def async_setup(hass: HomeAssistant) -> bool:
hass.http.register_view(OptionManagerFlowIndexView(hass.config_entries.options))
hass.http.register_view(OptionManagerFlowResourceView(hass.config_entries.options))
hass.http.register_view(
SubentryManagerFlowIndexView(hass.config_entries.subentries)
)
hass.http.register_view(
SubentryManagerFlowResourceView(hass.config_entries.subentries)
)
websocket_api.async_register_command(hass, config_entries_get)
websocket_api.async_register_command(hass, config_entry_disable)
websocket_api.async_register_command(hass, config_entry_get_single)
@@ -61,9 +54,6 @@ def async_setup(hass: HomeAssistant) -> bool:
websocket_api.async_register_command(hass, config_entries_progress)
websocket_api.async_register_command(hass, ignore_config_flow)
websocket_api.async_register_command(hass, config_subentry_delete)
websocket_api.async_register_command(hass, config_subentry_list)
return True
@@ -295,63 +285,6 @@ class OptionManagerFlowResourceView(
return await super().post(request, flow_id)
class SubentryManagerFlowIndexView(
FlowManagerIndexView[config_entries.ConfigSubentryFlowManager]
):
"""View to create subentry flows."""
url = "/api/config/config_entries/subentries/flow"
name = "api:config:config_entries:subentries:flow"
@require_admin(
error=Unauthorized(perm_category=CAT_CONFIG_ENTRIES, permission=POLICY_EDIT)
)
@RequestDataValidator(
vol.Schema(
{
vol.Required("handler"): vol.All(vol.Coerce(tuple), (str, str)),
vol.Optional("show_advanced_options", default=False): cv.boolean,
},
extra=vol.ALLOW_EXTRA,
)
)
async def post(self, request: web.Request, data: dict[str, Any]) -> web.Response:
"""Handle a POST request.
handler in request is [entry_id, subentry_type].
"""
return await super()._post_impl(request, data)
def get_context(self, data: dict[str, Any]) -> dict[str, Any]:
"""Return context."""
context = super().get_context(data)
context["source"] = config_entries.SOURCE_USER
return context
class SubentryManagerFlowResourceView(
FlowManagerResourceView[config_entries.ConfigSubentryFlowManager]
):
"""View to interact with the subentry flow manager."""
url = "/api/config/config_entries/subentries/flow/{flow_id}"
name = "api:config:config_entries:subentries:flow:resource"
@require_admin(
error=Unauthorized(perm_category=CAT_CONFIG_ENTRIES, permission=POLICY_EDIT)
)
async def get(self, request: web.Request, /, flow_id: str) -> web.Response:
"""Get the current state of a data_entry_flow."""
return await super().get(request, flow_id)
@require_admin(
error=Unauthorized(perm_category=CAT_CONFIG_ENTRIES, permission=POLICY_EDIT)
)
async def post(self, request: web.Request, flow_id: str) -> web.Response:
"""Handle a POST request."""
return await super().post(request, flow_id)
@websocket_api.require_admin
@websocket_api.websocket_command({"type": "config_entries/flow/progress"})
def config_entries_progress(
@@ -655,62 +588,3 @@ async def _async_matching_config_entries_json_fragments(
)
or (filter_is_not_helper and entry.domain not in integrations)
]
@websocket_api.require_admin
@websocket_api.websocket_command(
{
"type": "config_entries/subentries/list",
"entry_id": str,
}
)
@websocket_api.async_response
async def config_subentry_list(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""List subentries of a config entry."""
entry = get_entry(hass, connection, msg["entry_id"], msg["id"])
if entry is None:
return
result = [
{
"subentry_id": subentry.subentry_id,
"title": subentry.title,
"unique_id": subentry.unique_id,
}
for subentry_id, subentry in entry.subentries.items()
]
connection.send_result(msg["id"], result)
@websocket_api.require_admin
@websocket_api.websocket_command(
{
"type": "config_entries/subentries/delete",
"entry_id": str,
"subentry_id": str,
}
)
@websocket_api.async_response
async def config_subentry_delete(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Delete a subentry of a config entry."""
entry = get_entry(hass, connection, msg["entry_id"], msg["id"])
if entry is None:
return
try:
hass.config_entries.async_remove_subentry(entry, msg["subentry_id"])
except config_entries.UnknownSubEntry:
connection.send_error(
msg["id"], websocket_api.const.ERR_NOT_FOUND, "Config subentry not found"
)
return
connection.send_result(msg["id"])
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==2.0.5", "home-assistant-intents==2024.12.9"]
"requirements": ["hassil==2.0.5", "home-assistant-intents==2024.12.20"]
}
@@ -16,7 +16,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import CookidooConfigEntry, CookidooDataUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.TODO]
PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.TODO]
async def async_setup_entry(hass: HomeAssistant, entry: CookidooConfigEntry) -> bool:
@@ -0,0 +1,70 @@
"""Support for Cookidoo buttons."""
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from cookidoo_api import Cookidoo, CookidooException
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .coordinator import CookidooConfigEntry, CookidooDataUpdateCoordinator
from .entity import CookidooBaseEntity
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class CookidooButtonEntityDescription(ButtonEntityDescription):
"""Describes cookidoo button entity."""
press_fn: Callable[[Cookidoo], Awaitable[None]]
TODO_CLEAR = CookidooButtonEntityDescription(
key="todo_clear",
translation_key="todo_clear",
press_fn=lambda client: client.clear_shopping_list(),
entity_registry_enabled_default=False,
)
async def async_setup_entry(
hass: HomeAssistant,
entry: CookidooConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Cookidoo button entities based on a config entry."""
coordinator = entry.runtime_data
async_add_entities([CookidooButton(coordinator, TODO_CLEAR)])
class CookidooButton(CookidooBaseEntity, ButtonEntity):
"""Defines an Cookidoo button."""
entity_description: CookidooButtonEntityDescription
def __init__(
self,
coordinator: CookidooDataUpdateCoordinator,
description: CookidooButtonEntityDescription,
) -> None:
"""Initialize cookidoo button."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{description.key}"
async def async_press(self) -> None:
"""Press the button."""
try:
await self.entity_description.press_fn(self.coordinator.cookidoo)
except CookidooException as e:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="button_clear_todo_failed",
) from e
await self.coordinator.async_refresh()
@@ -1,5 +1,10 @@
{
"entity": {
"button": {
"todo_clear": {
"default": "mdi:cart-off"
}
},
"todo": {
"ingredient_list": {
"default": "mdi:cart-plus"
@@ -48,6 +48,11 @@
}
},
"entity": {
"button": {
"todo_clear": {
"name": "Clear shopping list and additional purchases"
}
},
"todo": {
"ingredient_list": {
"name": "Shopping list"
@@ -58,6 +63,9 @@
}
},
"exceptions": {
"button_clear_todo_failed": {
"message": "Failed to clear all items from the Cookidoo shopping list"
},
"todo_save_item_failed": {
"message": "Failed to save {name} to Cookidoo shopping list"
},
@@ -50,7 +50,7 @@
"services": {
"get_command": {
"name": "Get command",
"description": "Send sa generic HTTP get command.",
"description": "Sends a generic HTTP get command.",
"fields": {
"command": {
"name": "Command",
@@ -81,14 +81,8 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity):
or self._binary_sensor_property.sensor_type
)
if device_instance.binary_sensor_property[element_uid].sub_type != "":
self._attr_name = device_instance.binary_sensor_property[
element_uid
].sub_type.capitalize()
else:
self._attr_name = device_instance.binary_sensor_property[
element_uid
].sensor_type.capitalize()
if device_instance.binary_sensor_property[element_uid].sub_type == "overload":
self._attr_translation_key = "overload"
self._value = self._binary_sensor_property.state
@@ -129,7 +123,8 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity):
self._key = key
self._attr_is_on = False
self._attr_name = f"Button {key}"
self._attr_translation_key = "button"
self._attr_translation_placeholders = {"key": str(key)}
def _sync(self, message: tuple) -> None:
"""Update the binary sensor state."""
@@ -116,9 +116,11 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity):
self._multi_level_sensor_property.sensor_type
)
self._attr_native_unit_of_measurement = self._multi_level_sensor_property.unit
self._attr_name = self._multi_level_sensor_property.sensor_type.capitalize()
self._value = self._multi_level_sensor_property.value
if self._multi_level_sensor_property.sensor_type == "light":
self._attr_translation_key = "brightness"
if element_uid.startswith("devolo.VoltageMultiLevelSensor:"):
self._attr_entity_registry_enabled_default = False
@@ -128,7 +130,6 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity):
_attr_entity_category = EntityCategory.DIAGNOSTIC
_attr_native_unit_of_measurement = PERCENTAGE
_attr_name = "Battery level"
_attr_device_class = SensorDeviceClass.BATTERY
_attr_state_class = SensorStateClass.MEASUREMENT
@@ -175,8 +176,6 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
device_instance.consumption_property[element_uid], consumption
)
self._attr_name = f"{consumption.capitalize()} consumption"
@property
def unique_id(self) -> str:
"""Return the unique ID of the entity.
@@ -30,5 +30,20 @@
}
}
}
},
"entity": {
"binary_sensor": {
"button": {
"name": "Button {key}"
},
"overload": {
"name": "Overload"
}
},
"sensor": {
"brightness": {
"name": "Brightness"
}
}
}
}
@@ -8,7 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
"iot_class": "local_push",
"loggers": ["async_upnp_client"],
"requirements": ["async-upnp-client==0.41.0", "getmac==0.9.4"],
"requirements": ["async-upnp-client==0.42.0", "getmac==0.9.4"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
@@ -7,7 +7,7 @@
"dependencies": ["ssdp"],
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
"iot_class": "local_polling",
"requirements": ["async-upnp-client==0.41.0"],
"requirements": ["async-upnp-client==0.42.0"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",
@@ -23,15 +23,15 @@
},
"subdir": {
"name": "Subdirectory",
"description": "Download into subdirectory."
"description": "Relative download path."
},
"filename": {
"name": "Filename",
"description": "Determine the filename."
"description": "Custom name for the downloaded file."
},
"overwrite": {
"name": "Overwrite",
"description": "Whether to overwrite the file or not."
"description": "Overwrite file if it exists."
}
}
}
@@ -4,6 +4,8 @@
"codeowners": ["@klaasnicolaas"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["easyenergy==2.1.2"]
"requirements": ["easyenergy==2.1.2"],
"single_config_entry": true
}
@@ -57,11 +57,11 @@
"services": {
"get_gas_prices": {
"name": "Get gas prices",
"description": "Request gas prices from easyEnergy.",
"description": "Requests gas prices from easyEnergy.",
"fields": {
"config_entry": {
"name": "Config Entry",
"description": "The config entry to use for this service."
"description": "The configuration entry to use for this action."
},
"incl_vat": {
"name": "VAT Included",
@@ -79,7 +79,7 @@
},
"get_energy_usage_prices": {
"name": "Get energy usage prices",
"description": "Request usage energy prices from easyEnergy.",
"description": "Requests usage energy prices from easyEnergy.",
"fields": {
"config_entry": {
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::config_entry::name%]",
@@ -101,7 +101,7 @@
},
"get_energy_return_prices": {
"name": "Get energy return prices",
"description": "Request return energy prices from easyEnergy.",
"description": "Requests return energy prices from easyEnergy.",
"fields": {
"config_entry": {
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::config_entry::name%]",
+48 -2
View File
@@ -2,7 +2,12 @@
from dataclasses import dataclass
from deebot_client.capabilities import CapabilityExecute, CapabilityLifeSpan
from deebot_client.capabilities import (
CapabilityExecute,
CapabilityExecuteTypes,
CapabilityLifeSpan,
)
from deebot_client.commands import StationAction
from deebot_client.events import LifeSpan
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
@@ -11,7 +16,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import EcovacsConfigEntry
from .const import SUPPORTED_LIFESPANS
from .const import SUPPORTED_LIFESPANS, SUPPORTED_STATION_ACTIONS
from .entity import (
EcovacsCapabilityEntityDescription,
EcovacsDescriptionEntity,
@@ -35,6 +40,13 @@ class EcovacsLifespanButtonEntityDescription(ButtonEntityDescription):
component: LifeSpan
@dataclass(kw_only=True, frozen=True)
class EcovacsStationActionButtonEntityDescription(ButtonEntityDescription):
"""Ecovacs station action button entity description."""
action: StationAction
ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = (
EcovacsButtonEntityDescription(
capability_fn=lambda caps: caps.map.relocation if caps.map else None,
@@ -44,6 +56,16 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsButtonEntityDescription, ...] = (
),
)
STATION_ENTITY_DESCRIPTIONS = tuple(
EcovacsStationActionButtonEntityDescription(
action=action,
key=f"station_action_{action.name.lower()}",
translation_key=f"station_action_{action.name.lower()}",
)
for action in SUPPORTED_STATION_ACTIONS
)
LIFESPAN_ENTITY_DESCRIPTIONS = tuple(
EcovacsLifespanButtonEntityDescription(
component=component,
@@ -74,6 +96,15 @@ async def async_setup_entry(
for description in LIFESPAN_ENTITY_DESCRIPTIONS
if description.component in device.capabilities.life_span.types
)
entities.extend(
EcovacsStationActionButtonEntity(
device, device.capabilities.station.action, description
)
for device in controller.devices
if device.capabilities.station
for description in STATION_ENTITY_DESCRIPTIONS
if description.action in device.capabilities.station.action.types
)
async_add_entities(entities)
@@ -103,3 +134,18 @@ class EcovacsResetLifespanButtonEntity(
await self._device.execute_command(
self._capability.reset(self.entity_description.component)
)
class EcovacsStationActionButtonEntity(
EcovacsDescriptionEntity[CapabilityExecuteTypes[StationAction]],
ButtonEntity,
):
"""Ecovacs station action button entity."""
entity_description: EcovacsStationActionButtonEntityDescription
async def async_press(self) -> None:
"""Press the button."""
await self._device.execute_command(
self._capability.execute(self.entity_description.action)
)
@@ -2,6 +2,7 @@
from enum import StrEnum
from deebot_client.commands import StationAction
from deebot_client.events import LifeSpan
DOMAIN = "ecovacs"
@@ -19,8 +20,11 @@ SUPPORTED_LIFESPANS = (
LifeSpan.SIDE_BRUSH,
LifeSpan.UNIT_CARE,
LifeSpan.ROUND_MOP,
LifeSpan.STATION_FILTER,
)
SUPPORTED_STATION_ACTIONS = (StationAction.EMPTY_DUSTBIN,)
LEGACY_SUPPORTED_LIFESPANS = (
"main_brush",
"side_brush",
@@ -27,11 +27,17 @@
"reset_lifespan_side_brush": {
"default": "mdi:broom"
},
"reset_lifespan_station_filter": {
"default": "mdi:air-filter"
},
"reset_lifespan_unit_care": {
"default": "mdi:robot-vacuum"
},
"reset_lifespan_round_mop": {
"default": "mdi:broom"
},
"station_action_empty_dustbin": {
"default": "mdi:delete-restore"
}
},
"event": {
@@ -72,6 +78,9 @@
"lifespan_side_brush": {
"default": "mdi:broom"
},
"lifespan_station_filter": {
"default": "mdi:air-filter"
},
"lifespan_unit_care": {
"default": "mdi:robot-vacuum"
},
@@ -87,6 +96,9 @@
"network_ssid": {
"default": "mdi:wifi"
},
"station_state": {
"default": "mdi:home"
},
"stats_area": {
"default": "mdi:floor-plan"
},
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.10", "deebot-client==9.4.0"]
"requirements": ["py-sucks==0.9.10", "deebot-client==10.0.1"]
}
+1 -1
View File
@@ -95,7 +95,7 @@ async def async_setup_entry(
class EcovacsNumberEntity(
EcovacsDescriptionEntity[CapabilitySet[EventT, int]],
EcovacsDescriptionEntity[CapabilitySet[EventT, [int]]],
NumberEntity,
):
"""Ecovacs number entity."""
+2 -2
View File
@@ -66,7 +66,7 @@ async def async_setup_entry(
class EcovacsSelectEntity(
EcovacsDescriptionEntity[CapabilitySetTypes[EventT, str]],
EcovacsDescriptionEntity[CapabilitySetTypes[EventT, [str], str]],
SelectEntity,
):
"""Ecovacs select entity."""
@@ -77,7 +77,7 @@ class EcovacsSelectEntity(
def __init__(
self,
device: Device,
capability: CapabilitySetTypes[EventT, str],
capability: CapabilitySetTypes[EventT, [str], str],
entity_description: EcovacsSelectEntityDescription,
**kwargs: Any,
) -> None:
+11 -1
View File
@@ -16,6 +16,7 @@ from deebot_client.events import (
NetworkInfoEvent,
StatsEvent,
TotalStatsEvent,
station,
)
from sucks import VacBot
@@ -46,7 +47,7 @@ from .entity import (
EcovacsLegacyEntity,
EventT,
)
from .util import get_supported_entitites
from .util import get_name_key, get_options, get_supported_entitites
@dataclass(kw_only=True, frozen=True)
@@ -136,6 +137,15 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
# Station
EcovacsSensorEntityDescription[station.StationEvent](
capability_fn=lambda caps: caps.station.state if caps.station else None,
value_fn=lambda e: get_name_key(e.state),
key="station_state",
translation_key="station_state",
device_class=SensorDeviceClass.ENUM,
options=get_options(station.State),
),
)
@@ -46,6 +46,9 @@
"relocate": {
"name": "Relocate"
},
"reset_lifespan_base_station_filter": {
"name": "Reset station filter lifespan"
},
"reset_lifespan_blade": {
"name": "Reset blade lifespan"
},
@@ -66,6 +69,9 @@
},
"reset_lifespan_side_brush": {
"name": "Reset side brush lifespan"
},
"station_action_empty_dustbin": {
"name": "Empty dustbin"
}
},
"event": {
@@ -107,6 +113,9 @@
}
}
},
"lifespan_base_station_filter": {
"name": "Station filter lifespan"
},
"lifespan_blade": {
"name": "Blade lifespan"
},
@@ -140,6 +149,13 @@
"network_ssid": {
"name": "Wi-Fi SSID"
},
"station_state": {
"name": "Station state",
"state": {
"idle": "[%key:common::state::idle%]",
"emptying_dustbin": "Emptying dustbin"
}
},
"stats_area": {
"name": "Area cleaned"
},
+1 -1
View File
@@ -131,7 +131,7 @@ class EcovacsSwitchEntity(
await super().async_added_to_hass()
async def on_event(event: EnableEvent) -> None:
self._attr_is_on = event.enable
self._attr_is_on = event.enabled
self.async_write_ha_state()
self._subscribe(self._capability.event, on_event)
+11
View File
@@ -7,6 +7,8 @@ import random
import string
from typing import TYPE_CHECKING
from deebot_client.events.station import State
from homeassistant.core import HomeAssistant, callback
from homeassistant.util import slugify
@@ -47,4 +49,13 @@ def get_supported_entitites(
@callback
def get_name_key(enum: Enum) -> str:
"""Return the lower case name of the enum."""
if enum is State.EMPTYING:
# Will be fixed in the next major release of deebot-client
return "emptying_dustbin"
return enum.name.lower()
@callback
def get_options(enum: type[Enum]) -> list[str]:
"""Return the options for the enum."""
return [get_name_key(option) for option in enum]
@@ -6,11 +6,16 @@ from dataclasses import dataclass
from elevenlabs import AsyncElevenLabs, Model
from elevenlabs.core import ApiError
from httpx import ConnectError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryError
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryError,
ConfigEntryNotReady,
)
from homeassistant.helpers.httpx_client import get_async_client
from .const import CONF_MODEL
@@ -35,10 +40,10 @@ class ElevenLabsData:
model: Model
type EleventLabsConfigEntry = ConfigEntry[ElevenLabsData]
type ElevenLabsConfigEntry = ConfigEntry[ElevenLabsData]
async def async_setup_entry(hass: HomeAssistant, entry: EleventLabsConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ElevenLabsConfigEntry) -> bool:
"""Set up ElevenLabs text-to-speech from a config entry."""
entry.add_update_listener(update_listener)
httpx_client = get_async_client(hass)
@@ -48,6 +53,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: EleventLabsConfigEntry)
model_id = entry.options[CONF_MODEL]
try:
model = await get_model_by_id(client, model_id)
except ConnectError as err:
raise ConfigEntryNotReady("Failed to connect") from err
except ApiError as err:
raise ConfigEntryAuthFailed("Auth failed") from err
@@ -60,15 +67,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: EleventLabsConfigEntry)
return True
async def async_unload_entry(
hass: HomeAssistant, entry: EleventLabsConfigEntry
) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ElevenLabsConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def update_listener(
hass: HomeAssistant, config_entry: EleventLabsConfigEntry
hass: HomeAssistant, config_entry: ElevenLabsConfigEntry
) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(config_entry.entry_id)
@@ -19,7 +19,7 @@ from homeassistant.helpers.selector import (
SelectSelectorConfig,
)
from . import EleventLabsConfigEntry
from . import ElevenLabsConfigEntry
from .const import (
CONF_CONFIGURE_VOICE,
CONF_MODEL,
@@ -92,7 +92,7 @@ class ElevenLabsConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
def async_get_options_flow(
config_entry: EleventLabsConfigEntry,
config_entry: ElevenLabsConfigEntry,
) -> OptionsFlow:
"""Create the options flow."""
return ElevenLabsOptionsFlow(config_entry)
@@ -101,7 +101,7 @@ class ElevenLabsConfigFlow(ConfigFlow, domain=DOMAIN):
class ElevenLabsOptionsFlow(OptionsFlow):
"""ElevenLabs options flow."""
def __init__(self, config_entry: EleventLabsConfigEntry) -> None:
def __init__(self, config_entry: ElevenLabsConfigEntry) -> None:
"""Initialize options flow."""
self.api_key: str = config_entry.data[CONF_API_KEY]
# id -> name
@@ -7,17 +7,13 @@ rules:
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage:
status: todo
comment: >
We should have every test end in either ABORT or CREATE_ENTRY.
test_invalid_api_key should assert the kind of error that is raised.
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions: done
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: todo
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: >
+2 -2
View File
@@ -22,7 +22,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import EleventLabsConfigEntry
from . import ElevenLabsConfigEntry
from .const import (
CONF_OPTIMIZE_LATENCY,
CONF_SIMILARITY,
@@ -56,7 +56,7 @@ def to_voice_settings(options: MappingProxyType[str, Any]) -> VoiceSettings:
async def async_setup_entry(
hass: HomeAssistant,
config_entry: EleventLabsConfigEntry,
config_entry: ElevenLabsConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up ElevenLabs tts platform via config entry."""
@@ -49,7 +49,7 @@ class ElkBinarySensor(ElkAttachedEntity, BinarySensorEntity):
_element: Zone
_attr_entity_registry_enabled_default = False
def _element_changed(self, _: Element, changeset: Any) -> None:
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
# Zone in NORMAL state is OFF; any other state is ON
self._attr_is_on = bool(
self._element.logical_status != ZoneLogicalStatus.NORMAL
+5 -5
View File
@@ -120,7 +120,7 @@ class ElkCounter(ElkSensor):
_attr_icon = "mdi:numeric"
_element: Counter
def _element_changed(self, _: Element, changeset: Any) -> None:
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
self._attr_native_value = self._element.value
@@ -153,7 +153,7 @@ class ElkKeypad(ElkSensor):
attrs["last_keypress"] = self._element.last_keypress
return attrs
def _element_changed(self, _: Element, changeset: Any) -> None:
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
self._attr_native_value = temperature_to_state(
self._element.temperature, UNDEFINED_TEMPERATURE
)
@@ -173,7 +173,7 @@ class ElkPanel(ElkSensor):
attrs["system_trouble_status"] = self._element.system_trouble_status
return attrs
def _element_changed(self, _: Element, changeset: Any) -> None:
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
if self._elk.is_connected():
self._attr_native_value = (
"Paused" if self._element.remote_programming_status else "Connected"
@@ -188,7 +188,7 @@ class ElkSetting(ElkSensor):
_attr_translation_key = "setting"
_element: Setting
def _element_changed(self, _: Element, changeset: Any) -> None:
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
self._attr_native_value = self._element.value
@property
@@ -257,7 +257,7 @@ class ElkZone(ElkSensor):
return UnitOfElectricPotential.VOLT
return None
def _element_changed(self, _: Element, changeset: Any) -> None:
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
if self._element.definition == ZoneType.TEMPERATURE:
self._attr_native_value = temperature_to_state(
self._element.temperature, UNDEFINED_TEMPERATURE
@@ -151,7 +151,9 @@ class ElmaxConfigFlow(ConfigFlow, domain=DOMAIN):
port=self._panel_direct_port,
)
)
ssl_context = build_direct_ssl_context(cadata=self._panel_direct_ssl_cert)
ssl_context = await self.hass.async_add_executor_job(
build_direct_ssl_context, self._panel_direct_ssl_cert
)
# Attempt the connection to make sure the pin works. Also, take the chance to retrieve the panel ID via APIs.
client_api_url = get_direct_api_url(
+1 -1
View File
@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/elmax",
"iot_class": "cloud_polling",
"loggers": ["elmax_api"],
"requirements": ["elmax-api==0.0.6.3"],
"requirements": ["elmax-api==0.0.6.4rc0"],
"zeroconf": [
{
"type": "_elmax-ssl._tcp.local."
+2 -2
View File
@@ -50,7 +50,7 @@
"data": {
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]",
"panel_pin": "Panel Pin"
"panel_pin": "Panel PIN"
}
}
},
@@ -58,7 +58,7 @@
"no_panel_online": "No online Elmax control panel was found.",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"network_error": "A network error occurred",
"invalid_pin": "The provided pin is invalid",
"invalid_pin": "The provided PIN is invalid",
"invalid_mode": "Invalid or unsupported mode",
"reauth_panel_disappeared": "The given panel is no longer associated to this user. Please log in using an account associated to this panel.",
"unknown": "[%key:common::config_flow::error::unknown%]"
@@ -51,8 +51,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> b
# wait for the next discovery to find the device at its new address
# and update the config entry so we do not mix up devices.
raise ConfigEntryNotReady(
f"Unexpected device found at {host}; expected {entry.unique_id}, "
f"found {envoy.serial_number}"
translation_domain=DOMAIN,
translation_key="unexpected_device",
translation_placeholders={
"host": host,
"expected_serial": str(entry.unique_id),
"actual_serial": str(envoy.serial_number),
},
)
entry.runtime_data = coordinator
@@ -72,7 +77,7 @@ async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def async_unload_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> bool:
"""Unload a config entry."""
coordinator: EnphaseUpdateCoordinator = entry.runtime_data
coordinator = entry.runtime_data
coordinator.async_cancel_token_refresh()
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@@ -141,9 +141,13 @@ class EnphaseConfigFlow(ConfigFlow, domain=DOMAIN):
and entry.data[CONF_HOST] == self.ip_address
):
_LOGGER.debug(
"Zeroconf update envoy with this ip and blank serial in unique_id",
"Zeroconf update envoy with this ip and blank unique_id",
)
title = f"{ENVOY} {serial}" if entry.title == ENVOY else ENVOY
# Found an entry with blank unique_id (prior deleted) with same ip
# If the title is still default shorthand 'Envoy' then append serial
# to differentiate multiple Envoy. Don't change the title if any other
# title is still present in the old entry.
title = f"{ENVOY} {serial}" if entry.title == ENVOY else entry.title
return self.async_update_reload_and_abort(
entry, title=title, unique_id=serial, reason="already_configured"
)
@@ -18,7 +18,7 @@ from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
import homeassistant.util.dt as dt_util
from .const import INVALID_AUTH_ERRORS
from .const import DOMAIN, INVALID_AUTH_ERRORS
SCAN_INTERVAL = timedelta(seconds=60)
@@ -37,6 +37,7 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
envoy_serial_number: str
envoy_firmware: str
config_entry: EnphaseConfigEntry
def __init__(
self, hass: HomeAssistant, envoy: Envoy, entry: EnphaseConfigEntry
@@ -44,7 +45,6 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Initialize DataUpdateCoordinator for the envoy."""
self.envoy = envoy
entry_data = entry.data
self.entry = entry
self.username = entry_data[CONF_USERNAME]
self.password = entry_data[CONF_PASSWORD]
self._setup_complete = False
@@ -107,7 +107,7 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
await envoy.setup()
assert envoy.serial_number is not None
self.envoy_serial_number = envoy.serial_number
if token := self.entry.data.get(CONF_TOKEN):
if token := self.config_entry.data.get(CONF_TOKEN):
with contextlib.suppress(*INVALID_AUTH_ERRORS):
# Always set the username and password
# so we can refresh the token if needed
@@ -136,9 +136,9 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
# as long as the token is valid
_LOGGER.debug("%s: Updating token in config entry from auth", self.name)
self.hass.config_entries.async_update_entry(
self.entry,
self.config_entry,
data={
**self.entry.data,
**self.config_entry.data,
CONF_TOKEN: envoy.auth.token,
},
)
@@ -158,9 +158,23 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
# token likely expired or firmware changed, try to re-authenticate
self._setup_complete = False
continue
raise ConfigEntryAuthFailed from err
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="authentication_error",
translation_placeholders={
"host": envoy.host,
"args": err.args[0],
},
) from err
except EnvoyError as err:
raise UpdateFailed(f"Error communicating with API: {err}") from err
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="envoy_error",
translation_placeholders={
"host": envoy.host,
"args": err.args[0],
},
) from err
# if we have a firmware version from previous setup, compare to current one
# when envoy gets new firmware there will be an authentication failure
@@ -175,7 +189,7 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
)
# reload the integration to get all established again
self.hass.async_create_task(
self.hass.config_entries.async_reload(self.entry.entry_id)
self.hass.config_entries.async_reload(self.config_entry.entry_id)
)
# remember firmware version for next time
self.envoy_firmware = envoy.firmware
@@ -7,29 +7,16 @@ rules:
status: done
comment: fixed 1 minute cycle based on Enphase Envoy device characteristics
brands: done
common-modules:
status: done
comment: |
In coordinator.py, you set self.entry = entry, while after the super constructor,
you can access the entry via self.config_entry (you would have to overwrite the
type to make sure you don't have to assert not None every time)done
common-modules: done
config-flow-test-coverage:
status: todo
comment: |
- test_form is missing an assertion for the unique id of the resulting entry
- Let's also have test_user_no_serial_number assert the unique_id (as in, it can't be set to the serial_number since we dont have one, so let's assert what it will result in)
- Let's have every test result in either CREATE_ENTRY or ABORT (like test_form_invalid_auth or test_form_cannot_connect, they can be parametrized)
- test_zeroconf_token_firmware and test_zeroconf_pre_token_firmware can also be parametrized I think
- test_zero_conf_malformed_serial_property - with pytest.raises(KeyError) as ex::
I don't believe this should be able to raise a KeyError Shouldn't we abort the flow?
test_reauth -> Let's also assert result before we start with the async_configure part
config-flow:
status: todo
comment: |
- async_step_zeroconf -> a config entry title is considered userland,
so if someone renamed their entry, it will be reverted back with the code at L146.
- async_step_reaut L160: I believe that the unique is already set when starting a reauth flow
- The config flow is missing data descriptions for the other fields
dependency-transparency: done
docs-actions:
status: done
@@ -48,11 +35,7 @@ rules:
comment: no events used.
entity-unique-id: done
has-entity-name: done
runtime-data:
status: done
comment: |
async_unload_entry- coordinator: EnphaseUpdateCoordinator = entry.runtime_data
You can remove the EnphaseUpdateCoordinator as the type can now be inferred thanks to the typed config entry
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
@@ -108,9 +91,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations:
status: todo
comment: pending https://github.com/home-assistant/core/pull/132483
exception-translations: done
icon-translations: todo
reconfiguration-flow: done
repair-issues:
@@ -10,6 +10,8 @@ from operator import attrgetter
from typing import TYPE_CHECKING
from pyenphase import (
EnvoyACBPower,
EnvoyBatteryAggregate,
EnvoyEncharge,
EnvoyEnchargeAggregate,
EnvoyEnchargePower,
@@ -723,6 +725,78 @@ ENCHARGE_AGGREGATE_SENSORS = (
)
@dataclass(frozen=True, kw_only=True)
class EnvoyAcbBatterySensorEntityDescription(SensorEntityDescription):
"""Describes an Envoy ACB Battery sensor entity."""
value_fn: Callable[[EnvoyACBPower], int | str]
ACB_BATTERY_POWER_SENSORS = (
EnvoyAcbBatterySensorEntityDescription(
key="acb_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
value_fn=attrgetter("power"),
),
EnvoyAcbBatterySensorEntityDescription(
key="acb_soc",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
value_fn=attrgetter("state_of_charge"),
),
EnvoyAcbBatterySensorEntityDescription(
key="acb_battery_state",
translation_key="acb_battery_state",
device_class=SensorDeviceClass.ENUM,
options=["discharging", "idle", "charging", "full"],
value_fn=attrgetter("state"),
),
)
ACB_BATTERY_ENERGY_SENSORS = (
EnvoyAcbBatterySensorEntityDescription(
key="acb_available_energy",
translation_key="acb_available_energy",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
device_class=SensorDeviceClass.ENERGY_STORAGE,
value_fn=attrgetter("charge_wh"),
),
)
@dataclass(frozen=True, kw_only=True)
class EnvoyAggregateBatterySensorEntityDescription(SensorEntityDescription):
"""Describes an Envoy aggregate Ensemble and ACB Battery sensor entity."""
value_fn: Callable[[EnvoyBatteryAggregate], int]
AGGREGATE_BATTERY_SENSORS = (
EnvoyAggregateBatterySensorEntityDescription(
key="aggregated_soc",
translation_key="aggregated_soc",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
value_fn=attrgetter("state_of_charge"),
),
EnvoyAggregateBatterySensorEntityDescription(
key="aggregated_available_energy",
translation_key="aggregated_available_energy",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
device_class=SensorDeviceClass.ENERGY_STORAGE,
value_fn=attrgetter("available_energy"),
),
EnvoyAggregateBatterySensorEntityDescription(
key="aggregated_max_battery_capacity",
translation_key="aggregated_max_capacity",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
device_class=SensorDeviceClass.ENERGY_STORAGE,
value_fn=attrgetter("max_available_capacity"),
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: EnphaseConfigEntry,
@@ -847,6 +921,20 @@ async def async_setup_entry(
EnvoyEnpowerEntity(coordinator, description)
for description in ENPOWER_SENSORS
)
if envoy_data.acb_power:
entities.extend(
EnvoyAcbBatteryPowerEntity(coordinator, description)
for description in ACB_BATTERY_POWER_SENSORS
)
entities.extend(
EnvoyAcbBatteryEnergyEntity(coordinator, description)
for description in ACB_BATTERY_ENERGY_SENSORS
)
if envoy_data.battery_aggregate:
entities.extend(
AggregateBatteryEntity(coordinator, description)
for description in AGGREGATE_BATTERY_SENSORS
)
async_add_entities(entities)
@@ -1228,3 +1316,60 @@ class EnvoyEnpowerEntity(EnvoySensorBaseEntity):
enpower = self.data.enpower
assert enpower is not None
return self.entity_description.value_fn(enpower)
class EnvoyAcbBatteryPowerEntity(EnvoySensorBaseEntity):
"""Envoy ACB Battery power sensor entity."""
entity_description: EnvoyAcbBatterySensorEntityDescription
def __init__(
self,
coordinator: EnphaseUpdateCoordinator,
description: EnvoyAcbBatterySensorEntityDescription,
) -> None:
"""Initialize ACB Battery entity."""
super().__init__(coordinator, description)
acb_data = self.data.acb_power
assert acb_data is not None
self._attr_unique_id = f"{self.envoy_serial_num}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{self.envoy_serial_num}_acb")},
manufacturer="Enphase",
model="ACB",
name=f"ACB {self.envoy_serial_num}",
via_device=(DOMAIN, self.envoy_serial_num),
)
@property
def native_value(self) -> int | str | None:
"""Return the state of the ACB Battery power sensors."""
acb = self.data.acb_power
assert acb is not None
return self.entity_description.value_fn(acb)
class EnvoyAcbBatteryEnergyEntity(EnvoySystemSensorEntity):
"""Envoy combined ACB and Ensemble Battery Aggregate energy sensor entity."""
entity_description: EnvoyAcbBatterySensorEntityDescription
@property
def native_value(self) -> int | str:
"""Return the state of the aggregate energy sensors."""
acb = self.data.acb_power
assert acb is not None
return self.entity_description.value_fn(acb)
class AggregateBatteryEntity(EnvoySystemSensorEntity):
"""Envoy combined ACB and Ensemble Battery Aggregate sensor entity."""
entity_description: EnvoyAggregateBatterySensorEntityDescription
@property
def native_value(self) -> int:
"""Return the state of the aggregate sensors."""
battery_aggregate = self.data.battery_aggregate
assert battery_aggregate is not None
return self.entity_description.value_fn(battery_aggregate)
@@ -337,6 +337,30 @@
},
"configured_reserve_soc": {
"name": "Configured reserve battery level"
},
"acb_battery_state": {
"name": "Battery state",
"state": {
"discharging": "Discharging",
"idle": "[%key:common::state::idle%]",
"charging": "Charging",
"full": "Full"
}
},
"acb_available_energy": {
"name": "Available ACB battery energy"
},
"acb_max_capacity": {
"name": "ACB Battery capacity"
},
"aggregated_available_energy": {
"name": "Aggregated available battery energy"
},
"aggregated_max_capacity": {
"name": "Aggregated Battery capacity"
},
"aggregated_soc": {
"name": "Aggregated battery soc"
}
},
"switch": {
@@ -347,5 +371,16 @@
"name": "Grid enabled"
}
}
},
"exceptions": {
"unexpected_device": {
"message": "Unexpected Envoy serial-number found at {host}; expected {expected_serial}, found {actual_serial}"
},
"authentication_error": {
"message": "Envoy authentication failure on {host}: {args}"
},
"envoy_error": {
"message": "Error communicating with Envoy API on {host}: {args}"
}
}
}
@@ -230,6 +230,8 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
@esphome_float_state_property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
if not self._static_info.supports_current_temperature:
return None
return self._state.current_temperature
@property
@@ -14,6 +14,7 @@ import feedparser
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.storage import Store
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
@@ -101,7 +102,11 @@ class FeedReaderCoordinator(
async def async_setup(self) -> None:
"""Set up the feed manager."""
feed = await self._async_fetch_feed()
try:
feed = await self._async_fetch_feed()
except UpdateFailed as err:
raise ConfigEntryNotReady from err
self.logger.debug("Feed data fetched from %s : %s", self.url, feed["feed"])
if feed_author := feed["feed"].get("author"):
self.feed_author = html.unescape(feed_author)
@@ -1,4 +1,5 @@
{
"title": "Filter",
"services": {
"reload": {
"name": "[%key:common::action::reload%]",
@@ -4,8 +4,6 @@ from __future__ import annotations
from typing import Any
from fjaraskupan import COMMAND_LIGHT_ON_OFF
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -62,7 +60,6 @@ class Light(CoordinatorEntity[FjaraskupanCoordinator], LightEntity):
if self.is_on:
async with self.coordinator.async_connect_and_update() as device:
await device.send_dim(0)
await device.send_command(COMMAND_LIGHT_ON_OFF)
@property
def is_on(self) -> bool:
@@ -14,5 +14,5 @@
"documentation": "https://www.home-assistant.io/integrations/fjaraskupan",
"iot_class": "local_polling",
"loggers": ["bleak", "fjaraskupan"],
"requirements": ["fjaraskupan==2.3.0"]
"requirements": ["fjaraskupan==2.3.2"]
}
@@ -23,6 +23,9 @@ from . import FlexitCoordinator
from .const import DOMAIN
from .entity import FlexitEntity
_MAX_FAN_SETPOINT = 100
_MIN_FAN_SETPOINT = 30
@dataclass(kw_only=True, frozen=True)
class FlexitNumberEntityDescription(NumberEntityDescription):
@@ -34,6 +37,24 @@ class FlexitNumberEntityDescription(NumberEntityDescription):
set_native_value_fn: Callable[[FlexitBACnet], Callable[[int], Awaitable[None]]]
# Setpoints for Away, Home and High are dependent of each other. Fireplace and Cooker Hood
# have setpoints between 0 (MIN_FAN_SETPOINT) and 100 (MAX_FAN_SETPOINT).
# See the table below for all the setpoints.
#
# | Mode | Setpoint | Min | Max |
# |:------------|----------|:----------------------|:----------------------|
# | HOME | Supply | AWAY Supply setpoint | 100 |
# | HOME | Extract | AWAY Extract setpoint | 100 |
# | AWAY | Supply | 30 | HOME Supply setpoint |
# | AWAY | Extract | 30 | HOME Extract setpoint |
# | HIGH | Supply | HOME Supply setpoint | 100 |
# | HIGH | Extract | HOME Extract setpoint | 100 |
# | COOKER_HOOD | Supply | 30 | 100 |
# | COOKER_HOOD | Extract | 30 | 100 |
# | FIREPLACE | Supply | 30 | 100 |
# | FIREPLACE | Extract | 30 | 100 |
NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
FlexitNumberEntityDescription(
key="away_extract_fan_setpoint",
@@ -45,7 +66,7 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_away,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda device: int(device.fan_setpoint_extract_air_home),
native_min_value_fn=lambda _: 30,
native_min_value_fn=lambda _: _MIN_FAN_SETPOINT,
),
FlexitNumberEntityDescription(
key="away_supply_fan_setpoint",
@@ -57,7 +78,7 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_away,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda device: int(device.fan_setpoint_supply_air_home),
native_min_value_fn=lambda _: 30,
native_min_value_fn=lambda _: _MIN_FAN_SETPOINT,
),
FlexitNumberEntityDescription(
key="cooker_hood_extract_fan_setpoint",
@@ -68,8 +89,8 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_extract_air_cooker,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_cooker,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda _: _MIN_FAN_SETPOINT,
),
FlexitNumberEntityDescription(
key="cooker_hood_supply_fan_setpoint",
@@ -80,8 +101,8 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_supply_air_cooker,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_cooker,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda _: _MIN_FAN_SETPOINT,
),
FlexitNumberEntityDescription(
key="fireplace_extract_fan_setpoint",
@@ -92,8 +113,8 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_extract_air_fire,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_fire,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda _: _MIN_FAN_SETPOINT,
),
FlexitNumberEntityDescription(
key="fireplace_supply_fan_setpoint",
@@ -104,8 +125,8 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_supply_air_fire,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_fire,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_min_value_fn=lambda _: 30,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda _: _MIN_FAN_SETPOINT,
),
FlexitNumberEntityDescription(
key="high_extract_fan_setpoint",
@@ -116,7 +137,7 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_extract_air_high,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_high,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda device: int(device.fan_setpoint_extract_air_home),
),
FlexitNumberEntityDescription(
@@ -128,7 +149,7 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_supply_air_high,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_high,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda device: int(device.fan_setpoint_supply_air_home),
),
FlexitNumberEntityDescription(
@@ -140,7 +161,7 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_extract_air_home,
set_native_value_fn=lambda device: device.set_fan_setpoint_extract_air_home,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda device: int(device.fan_setpoint_extract_air_away),
),
FlexitNumberEntityDescription(
@@ -152,7 +173,7 @@ NUMBERS: tuple[FlexitNumberEntityDescription, ...] = (
native_value_fn=lambda device: device.fan_setpoint_supply_air_home,
set_native_value_fn=lambda device: device.set_fan_setpoint_supply_air_home,
native_unit_of_measurement=PERCENTAGE,
native_max_value_fn=lambda _: 100,
native_max_value_fn=lambda _: _MAX_FAN_SETPOINT,
native_min_value_fn=lambda device: int(device.fan_setpoint_supply_air_away),
),
)
@@ -53,5 +53,5 @@
"documentation": "https://www.home-assistant.io/integrations/flux_led",
"iot_class": "local_push",
"loggers": ["flux_led"],
"requirements": ["flux-led==1.0.4"]
"requirements": ["flux-led==1.1.0"]
}
@@ -7,6 +7,6 @@
"documentation": "https://www.home-assistant.io/integrations/freebox",
"iot_class": "local_polling",
"loggers": ["freebox_api"],
"requirements": ["freebox-api==1.1.0"],
"requirements": ["freebox-api==1.2.1"],
"zeroconf": ["_fbx-api._tcp.local."]
}
@@ -27,6 +27,7 @@ class FritzboxCoordinatorData:
devices: dict[str, FritzhomeDevice]
templates: dict[str, FritzhomeTemplate]
supported_color_properties: dict[str, tuple[dict, list]]
class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorData]):
@@ -49,7 +50,7 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
self.new_devices: set[str] = set()
self.new_templates: set[str] = set()
self.data = FritzboxCoordinatorData({}, {})
self.data = FritzboxCoordinatorData({}, {}, {})
async def async_setup(self) -> None:
"""Set up the coordinator."""
@@ -120,6 +121,7 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
devices = self.fritz.get_devices()
device_data = {}
supported_color_properties = self.data.supported_color_properties
for device in devices:
# assume device as unavailable, see #55799
if (
@@ -136,6 +138,13 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
device_data[device.ain] = device
# pre-load supported colors and color temps for new devices
if device.has_color and device.ain not in supported_color_properties:
supported_color_properties[device.ain] = (
device.get_colors(),
device.get_color_temps(),
)
template_data = {}
if self.has_templates:
templates = self.fritz.get_templates()
@@ -145,7 +154,11 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
self.new_devices = device_data.keys() - self.data.devices.keys()
self.new_templates = template_data.keys() - self.data.templates.keys()
return FritzboxCoordinatorData(devices=device_data, templates=template_data)
return FritzboxCoordinatorData(
devices=device_data,
templates=template_data,
supported_color_properties=supported_color_properties,
)
async def _async_update_data(self) -> FritzboxCoordinatorData:
"""Fetch all device data."""
+20 -28
View File
@@ -57,7 +57,6 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
) -> None:
"""Initialize the FritzboxLight entity."""
super().__init__(coordinator, ain, None)
self._supported_hs: dict[int, list[int]] = {}
self._attr_supported_color_modes = {ColorMode.ONOFF}
if self.data.has_color:
@@ -65,6 +64,26 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
elif self.data.has_level:
self._attr_supported_color_modes = {ColorMode.BRIGHTNESS}
(supported_colors, supported_color_temps) = (
coordinator.data.supported_color_properties.get(self.data.ain, ({}, []))
)
# Fritz!DECT 500 only supports 12 values for hue, with 3 saturations each.
# Map supported colors to dict {hue: [sat1, sat2, sat3]} for easier lookup
self._supported_hs: dict[int, list[int]] = {}
for values in supported_colors.values():
hue = int(values[0][0])
self._supported_hs[hue] = [
int(values[0][1]),
int(values[1][1]),
int(values[2][1]),
]
if supported_color_temps:
# only available for color bulbs
self._attr_max_color_temp_kelvin = int(max(supported_color_temps))
self._attr_min_color_temp_kelvin = int(min(supported_color_temps))
@property
def is_on(self) -> bool:
"""If the light is currently on or off."""
@@ -148,30 +167,3 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
"""Turn the light off."""
await self.hass.async_add_executor_job(self.data.set_state_off)
await self.coordinator.async_refresh()
async def async_added_to_hass(self) -> None:
"""Get light attributes from device after entity is added to hass."""
await super().async_added_to_hass()
def _get_color_data() -> tuple[dict, list]:
return (self.data.get_colors(), self.data.get_color_temps())
(
supported_colors,
supported_color_temps,
) = await self.hass.async_add_executor_job(_get_color_data)
if supported_color_temps:
# only available for color bulbs
self._attr_max_color_temp_kelvin = int(max(supported_color_temps))
self._attr_min_color_temp_kelvin = int(min(supported_color_temps))
# Fritz!DECT 500 only supports 12 values for hue, with 3 saturations each.
# Map supported colors to dict {hue: [sat1, sat2, sat3]} for easier lookup
for values in supported_colors.values():
hue = int(values[0][0])
self._supported_hs[hue] = [
int(values[0][1]),
int(values[1][1]),
int(values[2][1]),
]
+161
View File
@@ -68,6 +68,167 @@ def get_inverter_status_message(code: StateType) -> InverterStatusCodeOption | N
return _INVERTER_STATUS_CODES.get(code) # type: ignore[arg-type]
INVERTER_ERROR_CODES: Final[dict[int, str]] = {
0: "no_error",
102: "ac_voltage_too_high",
103: "ac_voltage_too_low",
105: "ac_frequency_too_high",
106: "ac_frequency_too_low",
107: "ac_grid_outside_permissible_limits",
108: "stand_alone_operation_detected",
112: "rcmu_error",
240: "arc_detection_triggered",
241: "arc_detection_triggered",
242: "arc_detection_triggered",
243: "arc_detection_triggered",
301: "overcurrent_ac",
302: "overcurrent_dc",
303: "dc_module_over_temperature",
304: "ac_module_over_temperature",
305: "no_power_fed_in_despite_closed_relay",
306: "pv_output_too_low_for_feeding_energy_into_the_grid",
307: "low_pv_voltage_dc_input_voltage_too_low",
308: "intermediate_circuit_voltage_too_high",
309: "dc_input_voltage_mppt_1_too_high",
311: "polarity_of_dc_strings_reversed",
313: "dc_input_voltage_mppt_2_too_high",
314: "current_sensor_calibration_timeout",
315: "ac_current_sensor_error",
316: "interrupt_check_fail",
325: "overtemperature_in_connection_area",
326: "fan_1_error",
327: "fan_2_error",
401: "no_communication_with_power_stage_set",
406: "ac_module_temperature_sensor_faulty_l1",
407: "ac_module_temperature_sensor_faulty_l2",
408: "dc_component_measured_in_grid_too_high",
412: "fixed_voltage_mode_out_of_range",
415: "safety_cut_out_triggered",
416: "no_communication_between_power_stage_and_control_system",
417: "hardware_id_problem",
419: "unique_id_conflict",
420: "no_communication_with_hybrid_manager",
421: "hid_range_error",
425: "no_communication_with_power_stage_set",
426: "possible_hardware_fault",
427: "possible_hardware_fault",
428: "possible_hardware_fault",
431: "software_problem",
436: "functional_incompatibility_between_pc_boards",
437: "power_stage_set_problem",
438: "functional_incompatibility_between_pc_boards",
443: "intermediate_circuit_voltage_too_low_or_asymmetric",
445: "compatibility_error_invalid_power_stage_configuration",
447: "insulation_fault",
448: "neutral_conductor_not_connected",
450: "guard_cannot_be_found",
451: "memory_error_detected",
452: "communication",
502: "insulation_error_on_solar_panels",
509: "no_energy_fed_into_grid_past_24_hours",
515: "no_communication_with_filter",
516: "no_communication_with_storage_unit",
517: "power_derating_due_to_high_temperature",
518: "internal_dsp_malfunction",
519: "no_communication_with_storage_unit",
520: "no_energy_fed_by_mppt1_past_24_hours",
522: "dc_low_string_1",
523: "dc_low_string_2",
558: "functional_incompatibility_between_pc_boards",
559: "functional_incompatibility_between_pc_boards",
560: "derating_caused_by_over_frequency",
564: "functional_incompatibility_between_pc_boards",
566: "arc_detector_switched_off",
567: "grid_voltage_dependent_power_reduction_active",
601: "can_bus_full",
603: "ac_module_temperature_sensor_faulty_l3",
604: "dc_module_temperature_sensor_faulty",
607: "rcmu_error",
608: "functional_incompatibility_between_pc_boards",
701: "internal_processor_status",
702: "internal_processor_status",
703: "internal_processor_status",
704: "internal_processor_status",
705: "internal_processor_status",
706: "internal_processor_status",
707: "internal_processor_status",
708: "internal_processor_status",
709: "internal_processor_status",
710: "internal_processor_status",
711: "internal_processor_status",
712: "internal_processor_status",
713: "internal_processor_status",
714: "internal_processor_status",
715: "internal_processor_status",
716: "internal_processor_status",
721: "eeprom_reinitialised",
722: "internal_processor_status",
723: "internal_processor_status",
724: "internal_processor_status",
725: "internal_processor_status",
726: "internal_processor_status",
727: "internal_processor_status",
728: "internal_processor_status",
729: "internal_processor_status",
730: "internal_processor_status",
731: "initialisation_error_usb_flash_drive_not_supported",
732: "initialisation_error_usb_stick_over_current",
733: "no_usb_flash_drive_connected",
734: "update_file_not_recognised_or_missing",
735: "update_file_does_not_match_device",
736: "write_or_read_error_occurred",
737: "file_could_not_be_opened",
738: "log_file_cannot_be_saved",
740: "initialisation_error_file_system_error_on_usb",
741: "error_during_logging_data_recording",
743: "error_during_update_process",
745: "update_file_corrupt",
746: "error_during_update_process",
751: "time_lost",
752: "real_time_clock_communication_error",
753: "real_time_clock_in_emergency_mode",
754: "internal_processor_status",
755: "internal_processor_status",
757: "real_time_clock_hardware_error",
758: "real_time_clock_in_emergency_mode",
760: "internal_hardware_error",
761: "internal_processor_status",
762: "internal_processor_status",
763: "internal_processor_status",
764: "internal_processor_status",
765: "internal_processor_status",
766: "emergency_power_derating_activated",
767: "internal_processor_status",
768: "different_power_limitation_in_hardware_modules",
772: "storage_unit_not_available",
773: "software_update_invalid_country_setup",
775: "pmc_power_stage_set_not_available",
776: "invalid_device_type",
781: "internal_processor_status",
782: "internal_processor_status",
783: "internal_processor_status",
784: "internal_processor_status",
785: "internal_processor_status",
786: "internal_processor_status",
787: "internal_processor_status",
788: "internal_processor_status",
789: "internal_processor_status",
790: "internal_processor_status",
791: "internal_processor_status",
792: "internal_processor_status",
793: "internal_processor_status",
794: "internal_processor_status",
1001: "insulation_measurement_triggered",
1024: "inverter_settings_changed_restart_required",
1030: "wired_shut_down_triggered",
1036: "grid_frequency_exceeded_limit_reconnecting",
1112: "mains_voltage_dependent_power_reduction",
1175: "too_little_dc_power_for_feed_in_operation",
1196: "inverter_required_setup_values_not_received",
65000: "dc_connection_inverter_battery_interrupted",
}
class MeterLocationCodeOption(StrEnum):
"""Meter location codes for Fronius meters."""
@@ -11,5 +11,6 @@
"documentation": "https://www.home-assistant.io/integrations/fronius",
"iot_class": "local_polling",
"loggers": ["pyfronius"],
"quality_scale": "gold",
"requirements": ["PyFronius==0.7.3"]
}

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