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

67f0de441b/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

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

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 }}

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"

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:

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]

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.*

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

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

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:

View File

@@ -252,6 +252,7 @@ PRELOAD_STORAGE = [
"assist_pipeline.pipelines",
"core.analytics",
"auth_module.totp",
"backup",
]

View File

@@ -2,6 +2,7 @@
"domain": "microsoft",
"name": "Microsoft",
"integrations": [
"azure_data_explorer",
"azure_devops",
"azure_event_hub",
"azure_service_bus",

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}"),
)

View File

@@ -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,
}
)

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"

View File

@@ -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,
]

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)

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"]
}

View File

@@ -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"
}
}

View File

@@ -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

View File

@@ -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%]",

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 [

View File

@@ -6,5 +6,5 @@
"iot_class": "cloud_push",
"loggers": ["apprise"],
"quality_scale": "legacy",
"requirements": ["apprise==1.9.0"]
"requirements": ["apprise==1.9.1"]
}

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."""

View File

@@ -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"]
}

View File

@@ -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,

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)

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(

View File

@@ -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"],

View File

@@ -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",

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:

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),
},

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."
}
}
}

View File

@@ -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(

View File

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

View File

@@ -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,
)

View File

@@ -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
)

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:

View File

@@ -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."""

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,

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.

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]:

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"

View File

@@ -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")

View File

@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"requirements": ["aiocomelit==0.9.1"]
"requirements": ["aiocomelit==0.10.1"]
}

View File

@@ -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"]
}

View File

@@ -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"])

View File

@@ -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"]
}

View File

@@ -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:

View File

@@ -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()

View File

@@ -1,5 +1,10 @@
{
"entity": {
"button": {
"todo_clear": {
"default": "mdi:cart-off"
}
},
"todo": {
"ingredient_list": {
"default": "mdi:cart-plus"

View File

@@ -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"
},

View File

@@ -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",

View File

@@ -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."""

View File

@@ -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.

View File

@@ -30,5 +30,20 @@
}
}
}
},
"entity": {
"binary_sensor": {
"button": {
"name": "Button {key}"
},
"overload": {
"name": "Overload"
}
},
"sensor": {
"brightness": {
"name": "Brightness"
}
}
}
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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."
}
}
}

View File

@@ -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
}

View File

@@ -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%]",

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)
)

View File

@@ -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",

View File

@@ -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"
},

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.10", "deebot-client==9.4.0"]
"requirements": ["py-sucks==0.9.10", "deebot-client==10.0.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."""

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:

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),
),
)

View File

@@ -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"
},

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)

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]

View File

@@ -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)

View File

@@ -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

View File

@@ -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: >

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."""

View File

@@ -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

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

View File

@@ -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(

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."

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%]"

View File

@@ -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)

View File

@@ -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"
)

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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}"
}
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -1,4 +1,5 @@
{
"title": "Filter",
"services": {
"reload": {
"name": "[%key:common::action::reload%]",

View File

@@ -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:

View File

@@ -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"]
}

View File

@@ -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),
),
)

View File

@@ -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"]
}

View File

@@ -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."]
}

View File

@@ -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."""

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]),
]

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."""

View File

@@ -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