Compare commits

...

259 Commits

Author SHA1 Message Date
Erik 4c0fcbb824 Don't fail on camera 2024-11-08 13:26:04 +01:00
Erik d8716b6b72 Don't fail on unifiprotect 2024-11-08 13:25:23 +01:00
Erik e88092dc52 Extend checks 2024-11-08 13:25:23 +01:00
Erik 6e4b5f31d4 Extend checks 2024-11-08 13:25:23 +01:00
Erik df6763328f Remove explicit templating of knx service data 2024-11-08 13:25:23 +01:00
Erik 51e409c1c7 Check all services 2024-11-08 13:25:23 +01:00
Erik 8c9bf7e41c Fail on templated services 2024-11-08 13:23:12 +01:00
Luke Lashley ce94073321 Bump python-roborock to 2.7.2 (#130100) 2024-11-08 08:39:41 +01:00
J. Nick Koston fa61e02207 Bump aiohttp to 3.11.0b4 (#130097) 2024-11-08 08:36:30 +01:00
Robert Resch d1dab83f10 Merge both stun server into one as it's the same server only on a different port (#130019)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-08 08:22:47 +01:00
Erik Montnemery 2b7d593ebe Avoid collision when replacing existing config entry with same unique id (#130062) 2024-11-08 07:45:16 +01:00
Allen Porter e407b4730d Fix KeyError in nest integration when the old key format does not exist (#130057)
* Fix bug in nest setup when the old key format does not exist

* Further simplify the entry.data check

* Update homeassistant/components/nest/api.py

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-07 20:03:07 -08:00
YogevBokobza 0d19e85a0d Align Switcher cover platform with changes from light platform (#130094)
Switcher small fix for cover
2024-11-08 02:59:30 +02:00
YogevBokobza dac6271e01 Add Switcher Lights support (#129494)
* switcher lights integration

* fix based on requested changes

* Update light.py

* switcher fix based on requested changes

* fix linting

* fix linting

* Update light.py

* Update light.py

* Update homeassistant/components/switcher_kis/light.py

* Update light.py

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2024-11-07 22:06:34 +02:00
Marc Mueller 8cae8edc55 Remove temporary pint constraint (#130070) 2024-11-07 19:10:24 +01:00
epenet a3b0909e3f Add new frame helper to better distinguish custom and core integrations (#130025)
* Add new frame helper to clarify options available

* Adjust

* Improve

* Use report_usage in core

* Add tests

* Use is/is not

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

* Use enum.auto()

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2024-11-07 18:23:35 +01:00
Markus ee30520b57 Fix esphome mqtt discovery by handling case where payload is a empty string (#129969)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-11-07 11:16:01 -06:00
Erik Montnemery 536e686892 Don't create repairs asking user to remove duplicate flipr config entries (#130058)
* Don't create repairs asking user to remove duplicate flipr config entries

* Improve comments
2024-11-07 17:38:10 +01:00
epenet ef767c2b9f Improve tests for frame helper (#130046)
* Improve tests for frame helper

* Improve comments

* Add ids

* Apply suggestions from code review
2024-11-07 17:35:58 +01:00
Frank Wickström c1ecc13cb3 Bump huum to 0.7.11 (#130047)
* Update huum dependency 0.7.10 -> 0.7.11

This change includes an explicit MIT license for the package.

* Remove huum from license exceptions list
2024-11-07 17:18:36 +01:00
Erik Montnemery c5e3ba536c Don't create repairs asking user to remove duplicate ignored config entries (#130056) 2024-11-07 17:07:23 +01:00
jb101010-2 0e324c074a Bump PySuez to 1.3.1 (#129825) 2024-11-07 14:25:38 +01:00
epenet a3ba7803db Add checks for translation placeholders (#129963)
* Add checks for translation placeholders

* Remove async

* Apply suggestions from code review

* Apply suggestions from code review

* Apply suggestions from code review
2024-11-07 13:12:00 +01:00
Marc Mueller 49bf5db5ff Update pytest warnings filter (#130027) 2024-11-07 12:55:54 +01:00
Franck Nijhof 50981c26ad Merge branch 'master' into dev 2024-11-07 10:58:42 +01:00
Allen Porter 2adbf7c933 Bump google-nest-sdm to 6.1.4 (#130005)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-07 10:50:40 +01:00
Brett Adams 838ef0bb9f Fix Trunks in Teslemetry and Tesla Fleet (#129986) 2024-11-07 10:36:43 +01:00
sean t 43c2658962 Bump agent-py to 0.0.24 (#130018)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-07 10:34:54 +01:00
epenet bbefa971d8 Add missing placeholder description to twitch (#130013) 2024-11-07 10:32:23 +01:00
Petar Petrov cb97f2f13c Bump zwave-js-server-python to 0.59.0 (#129482) 2024-11-07 10:06:28 +01:00
epenet a657b9bb84 Add temporary package constraint on flexparser and pint to fix CI (#130016)
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
2024-11-07 09:57:14 +01:00
Erik Montnemery 2d2f55a4df Report update_percentage in shelly update entity (#129382)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2024-11-07 08:52:20 +01:00
Michael Hansen df16e6d022 Bump intents to 2024.11.6 (#129982) 2024-11-07 08:29:44 +01:00
Marc Mueller 56212c6fa5 Update numpy to 2.1.2 and pandas to 2.2.3 (#129958) 2024-11-07 08:24:47 +01:00
Keilin Bickar bc964ce7f0 Update sense energy library to 0.13.3 (#129998) 2024-11-07 08:14:54 +01:00
Mike Degatano ed4f55406c Replace Supervisor resolution API calls with aiohasupervisor (#129599)
* Replace Supervisor resolution API calls with aiohasupervisor

* Use consistent types to avoid uuid issues

* Fix mocking in http test

* Changes from feedback

* Put hass first

* Fix typo

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-11-07 01:33:51 +01:00
epenet 03d5b18974 Remove options property from OptionFlow (#129890)
* Remove options property from OptionFlow

* Update test_config_entries.py

* Partial revert of "Remove deprecated property setters in option flows (#129773)"

* Partial revert "Use new helper properties in crownstone options flow (#129774)"

* Restore onewire init

* Restore onvif

* Restore roborock

* Use deepcopy in onewire

* Restore steam_online

* Restore initial options property in OptionsFlowWithConfigEntry

* re-add options property in SchemaOptionsFlowHandler

* Restore test

* Cleanup
2024-11-06 23:28:01 +01:00
J. Nick Koston 53c486ccd1 Bump aiohttp to 3.11.0b3 (#129363) 2024-11-06 15:59:31 -06:00
Steven B. 9a2a177b28 Bump ring library ring-doorbell to 0.9.9 (#129966) 2024-11-06 15:46:08 -06:00
Franck Nijhof 18e12740d9 2024.11.0 (#129970) 2024-11-06 20:10:51 +01:00
Franck Nijhof 5a24b670a2 Ran ruff 2024-11-06 19:32:23 +01:00
Franck Nijhof 94c5c8f42e Bump version to 2024.11.0 2024-11-06 19:29:07 +01:00
Manu e84d5fba11 Add state invitation to list access sensor in Bring integration (#129960) 2024-11-06 19:28:54 +01:00
Manu b808c0c5eb Add state invitation to list access sensor in Bring integration (#129960) 2024-11-06 19:15:25 +01:00
Robert Resch d4adb1f298 Bump go2rtc-client to 0.1.0 (#129965) 2024-11-06 17:59:04 +01:00
Erik Montnemery fe0a822721 Call async_refresh_providers when camera entity feature changes (#129941) 2024-11-06 17:37:23 +01:00
Joost Lekkerkerker 9f427893b1 Remove deprecation issues for LCN once entities removed (#129955) 2024-11-06 17:00:20 +01:00
epenet dea31e5744 Ensure that all files in a folder are in the same test bucket (#129946) 2024-11-06 15:38:24 +01:00
epenet 51d6948848 Use read-only options in google cloud options flow (#129927) 2024-11-06 15:15:35 +01:00
epenet 7ce74cb5ec Use read-only options in onkyo options flow (#129929) 2024-11-06 15:14:59 +01:00
Bram Kragten 29ba140816 Update frontend to 20241106.0 (#129953) 2024-11-06 14:53:59 +01:00
Robert Resch 0ca4f3e1ba Bump go2rtc-client to 0.0.1b5 (#129952) 2024-11-06 14:52:21 +01:00
J. Diego Rodríguez Royo 0430e6794e Delete binary door deprecation issue on unload at Home Connect (#129947) 2024-11-06 14:44:17 +01:00
Marc Mueller 29fa7f827a Fix audit-licenses check for multiple Python versions [ci] (#129951) 2024-11-06 14:20:14 +01:00
Tsvi Mostovicz 57d1001603 Move Jewish Calendar to runtime data (#129609) 2024-11-06 14:19:58 +01:00
Brett Adams 96de4b3828 Improve history coordinator in Teslemetry (#128235) 2024-11-06 13:40:37 +01:00
Teemu R. c6cb2884f4 Add motion sensor setting to tplink (#129393) 2024-11-06 13:40:17 +01:00
Manu 27e81fe0ed Improve error messages in Habitica (#129948)
Improve error messages
2024-11-06 13:23:43 +01:00
Louis Christ 2c1db10986 Map "stop" to MediaPlayerState.IDLE in bluesound integration (#129904)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-06 13:10:23 +01:00
epenet a7ba4bd086 Use read-only options in emoncms options flow (#129926)
* Use read-only options in emoncms options flow

* Don't store URL and API_KEY in entry options
2024-11-06 13:09:05 +01:00
Robert Resch 25449b424f Bump go2rtc-client to 0.0.1b4 (#129942) 2024-11-06 12:05:23 +01:00
Markus Jacobsen f6f89bd807 Update Bang & Olufsen source list as availability changes (#129910) 2024-11-06 11:52:00 +01:00
Daniel Hjelseth Høyer 370d7d6bdf Bump pyTibber to 0.30.4 (#129844) 2024-11-06 11:44:54 +01:00
Kunal Aggarwal 4dbf3359c1 Adding "peaceful" status as on value to Tuya Presence Sensor (#129925) 2024-11-06 11:43:41 +01:00
Joost Lekkerkerker 25eb7173bf Write squeezebox player state after query (#129939) 2024-11-06 11:32:59 +01:00
Joost Lekkerkerker 648c3d500b Bump spotifyaio to 0.8.5 (#129938) 2024-11-06 11:32:35 +01:00
epenet 33016c2977 Use new helper properties in netatmo options flow (#129781)
* Use new helper properties in netatmo options flow

* Update homeassistant/components/netatmo/config_flow.py

* Apply suggestions from code review

* Improve

* Keep options

* Simplify
2024-11-06 10:37:55 +01:00
Robert Resch 5679b061d2 Fix native sync WebRTC offer (#129931) 2024-11-06 10:07:10 +01:00
Nicholas Romyn 2eb2bdd615 Consolidating async_add_entities into one call in Ecobee (#129917)
* Consolidating async_add_entities into one call.

* changing to comprehension.
2024-11-06 08:25:18 +01:00
epenet 184cbfea23 Use read-only options in lastfm options flow (#129928)
Use read-only options in lstfm options flow
2024-11-06 08:14:54 +01:00
dependabot[bot] f88bc008e5 Bump actions/attest-build-provenance from 1.4.3 to 1.4.4 (#129924) 2024-11-06 08:13:41 +01:00
Paulus Schoutsen a927312fb5 Ensure all template names are strings (#129921) 2024-11-05 22:36:26 -05:00
starkillerOG 5f13db2356 Bump reolink_aio to 0.10.4 (#129914) 2024-11-06 00:05:05 +01:00
kingal123 64e84e2aa0 Update pylutron to 0.2.16 (#129653)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-05 22:23:14 +01:00
Michael Hansen 901457e7aa Bump intents and add HassRespond test (#129830) 2024-11-05 22:22:49 +01:00
Robert Resch 89a9c2ec24 Disable uv cache (#129912) 2024-11-05 22:18:41 +01:00
Joost Lekkerkerker 9e04457472 Bump spotifyaio to 0.8.4 (#129899) 2024-11-05 21:04:58 +01:00
Ville Skyttä 6ecdbb677f Bump huawei-lte-api to 1.10.0 (#129911) 2024-11-05 21:03:26 +01:00
G Johansson 79de1d9ed4 Bump holidays to 0.60 (#129909) 2024-11-05 20:26:22 +01:00
Paul Bottein 7fefa5c235 Update frontend to 20241105.0 (#129906) 2024-11-05 20:25:15 +01:00
Brett Adams 94db78a0be Add signing support to Tesla Fleet (#128407)
* Add command signing

* wip

* Update tests

* requirements

* Add test
2024-11-05 20:04:55 +01:00
Diogo Gomes 83a1b06b56 Set friendly name of utility meter select entity when configured through YAML (#128267)
* set select friendly name in YAML

* backward compatibility added

* clean

* cleaner backward compatibility approach

* don't introduce default unique_id

* split test according to review
2024-11-05 19:59:43 +01:00
epenet 1e42a38473 Remove usage of options property in OptionsFlow (part 2) (#129897) 2024-11-05 19:53:05 +01:00
epenet c54ed53a81 Remove usage of options property in OptionsFlow (part 1) (#129895)
* Remove usage of options property in OptionsFlow

* Improve
2024-11-05 19:51:20 +01:00
Manu 611a952232 Prevent update entity becoming unavailable on device disconnect in IronOS (#129840)
* Don't render update entity unavailable when Pinecil device disconnects

* fixes
2024-11-05 18:39:10 +01:00
Erik Montnemery 05e76105ad Improve improv BLE error handling (#129902) 2024-11-05 11:12:05 -05:00
Paulus Schoutsen ed56e5d631 Change Ollama default to llama3.2 (#129901) 2024-11-05 17:02:44 +01:00
Manu 9253fa4471 Add binary sensor platform to Habitica integration (#129613)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-05 17:01:38 +01:00
Joost Lekkerkerker 5f36062ef3 Remove timers from LG ThinQ (#129898) 2024-11-05 16:32:05 +01:00
Erik Montnemery e562b6f42b Map go2rtc log levels to Python log levels (#129894) 2024-11-05 15:57:33 +01:00
dotvav b76a94bd42 Bump pypalazzetti to 0.1.10 (#129832) 2024-11-05 15:34:25 +01:00
Joost Lekkerkerker 4e11ff05de Use default package for yt-dlp (#129886) 2024-11-05 15:23:41 +01:00
J. Diego Rodríguez Royo 080e3d7a42 Removed stale translation and improved set_setting translation at Home Connect (#129878) 2024-11-05 15:17:03 +01:00
Michael Hansen 69e3348cd7 Use different VAD thresholds for before and during voice command (#129848)
* Use two VAD thresholds

* Fix VoiceActivityTimeout class

* Update homeassistant/components/assist_pipeline/audio_enhancer.py

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-05 08:01:45 -06:00
Alexandre CUER 6caa4baa00 Fix missing translation string in emoncms (#129859) 2024-11-05 14:58:25 +01:00
Robert Resch 4729b19dc6 Skip adding providers if the camera has native WebRTC (#129808)
* Skip adding providers if the camera has native WebRTC

* Update homeassistant/components/camera/__init__.py

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

* Implement suggestion

* Add tests

* Shorten test name

* Fix test

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-11-05 14:44:37 +01:00
Richard Kroegel 8abbc4abbc Bump bimmer_connected to 0.16.4 (#129838) 2024-11-05 14:13:48 +01:00
Erik Montnemery 3a667bce8c Log go2rtc output with warning level on error (#129882) 2024-11-05 14:05:04 +01:00
starkillerOG 4c86102daf Add Reolink PTZ tilt position sensor (#129837) 2024-11-05 13:39:45 +01:00
Karl Beecken 15bf652f37 Bump python-tado to 0.17.7 (#129842) 2024-11-05 12:30:48 +01:00
Robert Resch eafed2b86c Append a 1 to all go2rtc ports to avoid port conflicts (#129881) 2024-11-05 12:29:51 +01:00
epenet 79901cede9 Drop initialize_options helper from OptionsFlow (#129870) 2024-11-05 12:02:33 +01:00
tdfountain 27dc82d7d0 Add device model ID if provided by NUT (#124189)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-11-05 11:57:00 +01:00
Mike Degatano ae37c8cc7a Add repair for add-on boot fail (#129847) 2024-11-05 11:53:01 +01:00
Kunal Aggarwal 5eadfcc524 Adding new on values for Tuya Presence Detection Sensor (#129801) 2024-11-05 11:52:38 +01:00
Manu 5fd1e23255 Bump pynecil to 0.2.1 (#129843) 2024-11-05 11:52:11 +01:00
Teemu R. 72bcc6702f Add child lock for tplink thermostats (#129649) 2024-11-05 11:14:53 +01:00
Erik Montnemery 8889464e04 Validate go2rtc server version (#129810) 2024-11-05 11:09:10 +01:00
G Johansson af58b0c3b7 Add reconfigure flow to yale_smart_alarm (#129536) 2024-11-05 11:05:20 +01:00
epenet e9e20229a3 Drop use of initialize_options in androidtv_remote (#129855) 2024-11-05 10:57:03 +01:00
Alex Bush 80ff6dc618 Bump pyfibaro to 0.8.0 (#129846) 2024-11-05 10:56:34 +01:00
epenet fa30100160 Fix flaky tests in device_sun_light_trigger (#129871) 2024-11-05 10:55:40 +01:00
epenet e6c20333b3 Remove dead code in translation checks (#129875) 2024-11-05 10:47:37 +01:00
Joakim Sørensen 3858400a6f Bump hass-nabucasa from 0.83.0 to 0.84.0 (#129873) 2024-11-05 10:10:23 +01:00
epenet 95eefbac20 Drop use of initialize_options in androidtv (#129854)
* Drop use of initialize_options in androidtv

* Initialize instance attribute in init method

* Adjust
2024-11-05 09:01:29 +01:00
epenet e1e731eb48 Drop use of initialize_options in onkyo (#129869)
* Drop use of initialize_options in onkyo

* Apply suggestions from code review

Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>

---------

Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2024-11-05 08:56:58 +01:00
Michael Arthur f7ce4ff25c Update snapshot for lg thinq (#129856)
update snapshot for lg thinq

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-05 08:15:42 +01:00
Artur Pragacz 617e87e02c Fix source mapping in Onkyo (#129716)
* Fix source mapping

* Fix copy paste
2024-11-04 21:56:47 -05:00
starkillerOG dafd54ba2b Bump reolink-aio to 0.10.3 (#129841) 2024-11-04 21:34:40 -05:00
J. Nick Koston e8c3539709 Disable SRTP for unifiprotect RTSPS stream (#129852) 2024-11-04 16:13:52 -06:00
J. Nick Koston e5263dc0c8 Bump uiprotect to 6.4.0 (#129851) 2024-11-04 15:43:22 -06:00
J. Nick Koston 3584c710b9 Fix unifiprotect supported features being set too late (#129850) 2024-11-04 15:13:56 -06:00
G Johansson 0b56ef5699 Fix translation in ovo energy (#129833) 2024-11-04 19:57:49 +01:00
G Johansson 90bd9bb626 Fix translations in hydrawise (#129834) 2024-11-04 19:57:00 +01:00
Bram Kragten 7863927c3a Update frontend to 20241104.0 (#129829) 2024-11-04 19:39:46 +01:00
G Johansson 9fcf757021 Fix translations in landisgyr (#129831) 2024-11-04 19:35:35 +01:00
epenet fc0547ccdf Pass the config entry explicitly in aemet coordinator (#128097) 2024-11-04 19:23:48 +01:00
Joost Lekkerkerker 22f8f117fb Add basic testing framework to LG ThinQ (#127785)
Co-authored-by: jangwon.lee <jangwon.lee@lge.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: YunseonPark-LGE <34848373+YunseonPark-LGE@users.noreply.github.com>
Co-authored-by: LG-ThinQ-Integration <LG-ThinQ-Integration@lge.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-11-04 19:22:12 +01:00
epenet 2052579efc Set config_entry explicitly in todoist coordinator (#129421) 2024-11-04 19:18:36 +01:00
epenet b8f2583bc3 Set config_entry explicitly in caldav coordinator (#129424) 2024-11-04 19:17:53 +01:00
epenet 6323a078e1 Set config_entry explicitly in wled coordinator (#129425) 2024-11-04 19:17:07 +01:00
G Johansson ca0be3ec8a Use coordinator async_setup in vizio (#129450) 2024-11-04 19:16:22 +01:00
epenet 91157c21ef Reapply "Fix unused snapshots not triggering failure in CI" (#129311) 2024-11-04 18:59:27 +01:00
epenet cc4fae10f5 Cleanup deprecated OptionsFlowWithConfigEntry (part 2) (#129754) 2024-11-04 18:55:49 +01:00
epenet d180ff417d Cleanup deprecated OptionsFlowWithConfigEntry (part 3) (#129756) 2024-11-04 18:55:01 +01:00
epenet 8870b657d1 Use new helper properties in hyperion options flow (#129777) 2024-11-04 18:54:22 +01:00
epenet 81735b7b47 Use new helper properties in konnected options flow (#129778) 2024-11-04 18:50:00 +01:00
Marc Mueller 7fd261347b Update charset-normalizer to 3.4.0 (#129821) 2024-11-04 18:49:19 +01:00
Robert Resch df796d432e Remove all ice_servers on native sync WebRTC cameras (#129819) 2024-11-04 18:41:37 +01:00
Steven B. f6e36615d6 Bump python-kasa to 0.7.7 (#129817)
Bump tplink dependency python-kasa to 0.7.7
2024-11-04 18:39:39 +01:00
Noah Husby 0278735dbf Use translated errors in Russound RIO (#129820) 2024-11-04 18:07:11 +01:00
tdfountain 9c8d8fef16 Suggest area for NUT based on device location (#129770) 2024-11-04 18:06:45 +01:00
G Johansson 6897b24c10 Fix translations in homeworks (#129824) 2024-11-04 18:03:37 +01:00
G Johansson a2a3f59e65 Fix missing translation in jewish_calendar (#129822) 2024-11-04 18:01:39 +01:00
G Johansson 2626a74840 Fix translations in honeywell (#129823) 2024-11-04 18:00:31 +01:00
Paulus Schoutsen 689260f581 Fix ESPHome dashboard check (#129812) 2024-11-04 17:37:14 +01:00
G Johansson f1a2c8be4b Stop recording of non-changing attributes in threshold (#129541) 2024-11-04 17:36:25 +01:00
epenet 0579d565dd Fix incorrect description placeholders in azure event hub (#129803) 2024-11-04 17:35:47 +01:00
Max Muth f141f5f908 Update codeowners of Fritz integration (#129595)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-04 17:26:12 +01:00
Antoine Reversat 0c25252d9f Bump ayla-iot-unofficial to 1.4.3 (#129743)
Upgrade to ayla-iot-unofficial v1.4.3
2024-11-04 17:20:15 +01:00
Jake Martin 400b377aa8 Bump monzopy to 1.4.2 (#129726)
* Bump monzopy to 1.4.0

* Bump to 1.4.2

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-04 16:55:02 +01:00
Manu a5f3c434e0 Improve exceptions in habitica cast skill action (#129603)
* Raise a different exception when entry not loaded

* adjust type hints

* move `get_config_entry` to services module
2024-11-04 16:46:38 +01:00
epenet 365f8046ac Use new helper properties in yeelight options flow (#129791) 2024-11-04 16:09:50 +01:00
Erik Montnemery 4ac35d40cd Fix create flow logic for single config entry integrations (#129807)
* Fix create flow logic for single config entry integrations

* Adjust MQTT test
2024-11-04 15:45:29 +01:00
J. Nick Koston 7691991a93 Small cleanups to the websocket command phase (#129712)
* Small cleanups to the websocket command phase

- Remove unused argument
- Avoid multiple NamedTuple property lookups

* Update homeassistant/components/websocket_api/http.py

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Apply suggestions from code review

* touch ups

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-04 15:33:15 +01:00
Willem-Jan van Rootselaar d0c45b1857 Bump python-bsblan to 1.2.1 (#129635)
* Bump python-bsblan dependency to version 1.1.0

* Bump python-bsblan dependency to version 1.2.0

* Bump python-bsblan dependency to version 1.2.1

* Update test diagnostics snapshots to use numeric values and add error handling
2024-11-04 15:31:44 +01:00
Joost Lekkerkerker 02750452df Update Spotify state after mutation (#129607) 2024-11-04 15:01:37 +01:00
Marc Mueller 41a81cbf15 Switch back to av 13.1.0 (#129699) 2024-11-04 14:48:28 +01:00
Andre Lengwenus ff621d5bf3 Bump lcn-frontend to 0.2.1 (#129457) 2024-11-04 14:45:20 +01:00
epenet 6d561a9796 Remove deprecated property setters in option flows (#129773) 2024-11-04 14:21:26 +01:00
Erik Montnemery 4784199038 Fix aborting flows for single config entry integrations (#129805) 2024-11-04 13:59:10 +01:00
Robert Resch df35c8e707 Update go2rtc stream if stream_source is not matching (#129804) 2024-11-04 13:58:12 +01:00
Erik Montnemery 57eeaf1f75 Add watchdog to monitor and respawn go2rtc server (#129497) 2024-11-04 13:42:42 +01:00
Joakim Sørensen 3cadc1796f Use JSON as format for .HA_RESTORE (#129792)
* Use JSON as format for .HA_RESTORE

* Adjust bakup manager test
2024-11-04 13:07:11 +01:00
Joost Lekkerkerker ae06f734ce Improve error handling in Spotify (#129799) 2024-11-04 12:34:00 +01:00
Erik Montnemery 08a53362a7 Fix stringification of discovered hassio uuid (#129797) 2024-11-04 12:26:34 +01:00
jb101010-2 274c928ec0 Add coordinator to suez_water (#129242)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-04 12:18:12 +01:00
Robert Resch d75dda0c05 Use RTCIceCandidate instead of str for candidate (#129793) 2024-11-04 10:38:27 +01:00
Joost Lekkerkerker 0c40fcdaeb Bump yt-dlp to 2024.11.04 (#129794) 2024-11-04 10:33:08 +01:00
G Johansson 0a1ba8a4a3 Small code quality improvement/cleanup in random (#129542) 2024-11-04 09:52:35 +01:00
epenet 018acc0a3c Use new helper properties in crownstone options flow (#129774) 2024-11-04 09:43:25 +01:00
epenet 3a293c6bc4 Use new helper properties in dsmr options flow (#129775) 2024-11-04 09:43:10 +01:00
epenet 9155d56190 Use new helper properties in flux_led options flow (#129776) 2024-11-04 09:42:58 +01:00
epenet 461dc13da9 Use new helper properties in motioneye options flow (#129780) 2024-11-04 09:40:13 +01:00
epenet b48e2127b8 Use new helper properties in plaato options flow (#129782) 2024-11-04 09:39:56 +01:00
epenet 11ab992dbb Use new helper properties in recollect_waste options flow (#129783) 2024-11-04 09:39:41 +01:00
epenet 4be2cdf90a Use new helper properties in steam_online options flow (#129785) 2024-11-04 09:39:27 +01:00
epenet cdd5cb2876 Use new helper properties in tomorrowio options flow (#129787) 2024-11-04 09:39:13 +01:00
epenet cdc67aa891 Use new helper properties in verisure options flow (#129788) 2024-11-04 09:38:41 +01:00
epenet 6a22a2b867 Use new helper properties in watttime options flow (#129789) 2024-11-04 09:38:24 +01:00
epenet 0883b23d0c Use new helper properties in yalexs_ble options flow (#129790) 2024-11-04 09:38:11 +01:00
epenet 595459bfda Use new helper properties in rfxtrx options flow (#129784) 2024-11-04 09:34:20 +01:00
Ståle Storø Hauknes 7ab8ff56b3 Bump Airthings BLE to 0.9.2 (#129659)
Bump airthings ble
2024-11-04 08:11:18 +01:00
Nathan Spencer eda36512ec Change alexa arm handler to allow switching arm states unless in armed_away mode (#129701)
* Change alexa arm handler to allow switching arm states unless in armed_away mode

* Address PR comments
2024-11-04 07:49:48 +01:00
LG-ThinQ-Integration 04aee812f8 Bump thinqconnect to 1.0.0 (#129769)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2024-11-04 07:17:50 +01:00
Allen Porter 6718cce203 Fix nest streams broken due to CameraCapabilities change (#129711)
* Fix nest streams broken due to CameraCapabilities change

* Fix stream cleanup

* Apply suggestions from code review

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/nest/camera.py

---------

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2024-11-03 20:45:09 -08:00
Bouwe Westerdijk 49f0bb6990 Bump plugwise to v1.5.0 (#129668)
* Bump plugwise to v1.5.0

* And adapt
2024-11-03 23:30:21 -05:00
Simon Lamon 38afcbb21f Bump python-linkplay to 0.0.17 (#129683) 2024-11-03 22:56:45 -05:00
Jesse Hills 87ab2beddf Only set ESPHome configuration url to addon if there is an existing configuration for the device (#129356)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-11-03 18:16:49 -06:00
tdfountain a05a34239d Show NUT device serial number if provided in Device Info (#124168) 2024-11-03 17:27:27 -06:00
epenet f11aba9648 Fix flaky tests in advantage_air (#129758) 2024-11-03 17:25:37 -06:00
Michael Hansen c2ef119e50 Add HassRespond intent (#129755)
* Add HassHello intent

* Rename to HassRespond

* LLM's ignore HassRespond intent
2024-11-03 16:38:52 -06:00
epenet 8b6c99776e Cleanup unnecessary OptionsFlowWithConfigEntry (part 1) (#129752)
* Cleanup unnecessary OptionsFlowWithConfigEntry

* Fix emoncms

* Fix imap

* Fix met

* Fix workday
2024-11-03 22:57:18 +01:00
Joost Lekkerkerker 463bffaeb6 Bump spotifyaio to 0.8.3 (#129729) 2024-11-03 21:55:12 +01:00
hahn-th 0cfd8032c0 Add Measurement StateClass to HomematicIP Cloud Wind and Rain Sensor (#129724)
Add Meassurement StateClass to Wind and Rain Sensor
2024-11-03 21:07:59 +01:00
Luca Angemi 144d5ff0cc Add state class to precipitation_intensity in Aemet (#129670)
Update sensor.py
2024-11-03 21:06:46 +01:00
G Johansson ab5c65b08c Improve code quality in yale_smart_alarm options flow (#129531)
* Improve code quality in yale_smart_alarm options flow

* mods

* Fix
2024-11-03 21:04:53 +01:00
Josef Zweck 6b33bf3961 Add missing translation string to lamarzocco (#129713)
* add missing translation string

* Update strings.json

* import pytest again
2024-11-03 20:56:08 +01:00
epenet 89eb395e2d Add OptionsFlow helper for a mutable copy of the config entry options (#129718)
* Add OptionsFlow helper for a mutable copy of the config entry options

* Add tests

* Improve coverage

* error_if_core=False

* Adjust report

* Avoid mutli-line ternary
2024-11-03 20:37:58 +01:00
G Johansson d671d48869 Small cleanup mold_indicator (#129736) 2024-11-03 19:17:37 +01:00
J. Nick Koston ed582fae91 Bump HAP-python to 4.9.2 (#129715) 2024-11-03 11:27:57 -06:00
Manu 4d5c3ee0aa Bump bring-api to 0.9.1 (#129702) 2024-11-03 10:46:16 -06:00
epenet 02046fcdb4 Fix advantage_air CI failure (#129735) 2024-11-03 17:29:33 +01:00
Josef Zweck fbe27749a0 Correct length of the serials in lamarzocco tests (#129725) 2024-11-03 13:35:42 +01:00
Josef Zweck eddab96a69 Add DHCP discovery to lamarzocco (#129675)
* Add DHCP discovery to lamarzocco

* ensure serial is upper

* shorten pattern

* parametrize across models
2024-11-03 09:44:35 +01:00
J. Nick Koston ed3376352d Bump DoorBirdPy to 3.0.8 (#129709) 2024-11-02 22:43:21 -05:00
J. Nick Koston dfbb763031 Disable cleanup_closed on python 3.12.7+ and 3.13.1+ (#129645) 2024-11-02 22:15:56 -05:00
Marc Mueller 5cf13d9273 Additional stream typing improvements (#129695) 2024-11-02 22:22:31 +01:00
Erik Montnemery e18ffc53f2 Revert "Create a script service schema based on fields" (#129591) 2024-11-02 20:39:17 +01:00
Marc Mueller 0eea3176d6 Minor stream typing improvements (#129691) 2024-11-02 19:29:09 +01:00
Marc Mueller 4f20977a8e Update mypy-dev to 1.14.0a2 (#129625) 2024-11-02 19:15:50 +01:00
Marc Mueller 5bd63bb56b Replace AVError with FFmpegError (#129689) 2024-11-02 19:14:59 +01:00
Marc Mueller f7103da818 Refactor av.open calls to support type annotations (#129688) 2024-11-02 19:03:32 +01:00
Klaas Schoute bf4922a7ef Bump autarco lib to v3.1.0 (#129684)
Bump autarco to v3.1.0
2024-11-02 18:42:56 +01:00
J. Nick Koston 6f7eac5c6d Bump sensorpush-ble to 1.7.1 (#129657) 2024-11-02 12:26:31 -05:00
epenet d6e73a89f3 Cleanup unnecessary __init__ method in OptionsFlow (#129651)
* Cleanup unnecessary init step in OptionsFlow

* Increase coverage
2024-11-02 18:15:41 +01:00
Sid 269aefd405 Bump ruff to 0.7.2 (#129669) 2024-11-02 11:29:08 +01:00
J. Nick Koston a6865f1639 Bump aiohomekit to 3.2.6 (#129640) 2024-11-01 14:01:33 -05:00
J. Nick Koston f55aa0b86e Bump aioesphomeapi to 27.0.1 (#129643) 2024-11-01 13:16:15 -05:00
Joost Lekkerkerker 02b34f05aa Bump spotifyaio to 0.8.2 (#129639) 2024-11-01 18:25:26 +01:00
Joost Lekkerkerker 37f42707e5 Fix Geniushub setup (#129569) 2024-11-01 17:33:39 +01:00
Robert Resch 17f3ba1434 Bump webrtc-models to 0.2.0 (#129627) 2024-11-01 17:24:44 +01:00
Joakim Sørensen 31dcc25ba5 Add handler to restore a backup file with the backup integration (#128365)
* Early pushout of restore handling for core/container

* Adjust after rebase

* Move logging definition, we should only do this if we go ahead with the restore

* First round

* More paths

* Add async_restore_backup to base class

* Block restore of new backup files

* manager tests

* Add websocket test

* Add testing to main

* Add coverage for missing backup file

* Catch FileNotFoundError instead

* Patch Path.read_text instead

* Remove HA_RESTORE from keep

* Use secure paths

* Fix restart test

* extend coverage

* Mock argv

* Adjustments
2024-11-01 16:25:22 +01:00
Joost Lekkerkerker 4da93f6a5e Bump spotifyaio to 0.8.1 (#129573) 2024-11-01 15:12:15 +01:00
Marc Mueller 5ed7d32749 Remove unnecessary asyncio EventLoopPolicy init_watcher backport (#129628) 2024-11-01 13:44:49 +01:00
epenet ab5b9dbdc9 Add OptionsFlow helpers to get the current config entry (#129562)
* Add OptionsFlow helpers to get the current config entry

* Add tests

* Improve

* Add ValueError to indicate that the config entry is not available in `__init__` method

* Use a property

* Update config_entries.py

* Update config_entries.py

* Update config_entries.py

* Add a property setter for compatibility

* Add report

* Update config_flow.py

* Add tests

* Update test_config_entries.py
2024-11-01 12:54:35 +01:00
Marco 3b28bf07d1 Add boost switch to Smarty (#129466) 2024-11-01 11:08:55 +01:00
epenet b626c9b450 Ensure entry_id is set on reauth/reconfigure flows (#129319)
* Ensure entry_id is set on reauth/reconfigure flows

* Improve

* Improve

* Use report helper

* Adjust deprecation date

* Update config_entries.py

* Improve message and adjust tests

* Apply suggestions from code review

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

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2024-11-01 10:29:58 +01:00
Willem-Jan van Rootselaar 5430eca93e Bump python-bsblan to 1.0.0 (#129617) 2024-11-01 10:23:30 +01:00
epenet b41c477f44 Fix flaky camera test (#129576) 2024-11-01 10:15:20 +01:00
Robert Resch 5900413c08 Add zwave_js node_capabilities and invoke_cc_api websocket commands (#125327)
* Add zwave_js node_capabilities and invoke_cc_api websocket commands

* Map isSecure to is_secure

* Add tests

* Add error handling

* fix

* Use to_dict function

* Make response compatible with current expectations

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-11-01 00:32:01 +01:00
Marc Mueller c2ceab741f Remove unnecessary husqvarna_automower_ble test fixture (#129577) 2024-11-01 00:00:52 +01:00
J. Nick Koston 45ff4940eb Pin async-timeout to 4.0.3 (#129592) 2024-10-31 16:18:31 -05:00
Robert Resch 9c8a15cb64 Add go2rtc debug_ui yaml key to enable go2rtc ui (#129587)
* Add go2rtc debug_ui yaml key to enable go2rtc ui

* Apply suggestions from code review

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

* Order imports

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-10-31 20:56:53 +01:00
Erik Montnemery b09e54c961 Bump aiohasupervisor to version 0.2.1 (#129574) 2024-10-31 19:37:31 +01:00
Steven B. f44b7e202a Check for async web offer overrides in camera capabilities (#129519) 2024-10-31 18:57:40 +01:00
Joost Lekkerkerker 0f535e979f Bump aiowithings to 3.1.1 (#129586) 2024-10-31 18:28:53 +01:00
G Johansson 4c2c01b4f6 Use shorthand attribute for native_value in mold_indicator (#129538) 2024-10-31 17:40:14 +01:00
G Johansson b1d48fe9a2 Use class attributes in Times of Day (#129543)
* mypy ignore assignment in Times of Day so we can drop all type checking

* class attributes
2024-10-31 17:37:33 +01:00
Paul Bottein b1dfc3cd23 Update frontend to 20241031.0 (#129583) 2024-10-31 16:35:36 +01:00
epenet 696efe349e Log type as well as value for unique_id checks (#129575) 2024-10-31 15:10:27 +01:00
Jan Bouwhuis 6a32722acc Fix current temperature calculation for incomfort boiler (#129496) 2024-10-31 14:57:09 +01:00
Erik Montnemery 8eaec56c6b Stringify discovered hassio uuid (#129572)
* Stringify discovered hassio uuid

* Correct DiscoveryKey

* Adjust tests
2024-10-31 13:54:27 +01:00
Thomas55555 60d3c9342d Fix flakey test in Husqvarna Automower (#129571) 2024-10-31 13:20:59 +01:00
TheJulianJES 2bd5039f28 Fix capitalization in Philips Hue strings (#129552) 2024-10-31 10:04:51 +01:00
G Johansson 8b1b14a704 Missing config_flow in manifest for local_file (#129529) 2024-10-31 09:50:32 +01:00
starkillerOG 5e674ce1d0 Log Reolink select value KeyError only once (#129559) 2024-10-31 09:49:27 +01:00
Brett Adams 3656bcf752 Fix "home" route in Tesla Fleet & Teslemetry (#129546)
* translate Home to home

* refactor for mypy

* Fix home state

* Revert key change

* Add testing
2024-10-31 08:56:03 +01:00
J. Nick Koston 39093fc2bc Bump yarl to 1.17.1 (#129539)
changelog: https://github.com/aio-libs/yarl/compare/v1.17.0...v1.17.1
2024-10-30 23:56:29 +01:00
Teemu R. efa5838be4 Add last alert timestamp for tplink waterleak (#128644)
* Add last alert timestamp for tplink waterleak

* Fix snapshot
2024-10-30 23:25:30 +01:00
Erik Montnemery 1c6ad2fa66 Allow importing homeassistant.core.Config until 2025.11 (#129537) 2024-10-30 22:56:59 +01:00
starkillerOG af144e1b77 Bump reolink_aio to 0.10.2 (#129528) 2024-10-30 23:24:07 +02:00
Luca Angemi b451bfed81 Fix bthome UnitOfConductivity (#129535)
Fix unit
2024-10-30 23:22:17 +02:00
G Johansson 3e32c50936 Fix async_config_entry_first_refresh used after config entry is loaded in speedtestdotcom (#129527)
* Fix async_config_entry_first_refresh used after config entry is loaded in speedtestdotcom

* is
2024-10-30 21:17:03 +01:00
Bram Kragten 208b15637a Bump version to 2024.12 (#129525) 2024-10-30 20:59:56 +01:00
Marcel van der Veldt c958cce769 Bump Music Assistant Client library to 1.0.5 (#129518) 2024-10-30 19:34:43 +01:00
epenet 602ec54579 Set config_entry explicitly to None in relevant components (#129427)
Set config_entry explicitly to None in components
2024-10-30 19:32:10 +01:00
cryptk fa2bfc5d9d Bump uiprotect to 6.3.2 (#129513) 2024-10-30 18:43:34 +01:00
Aurore 94f906b34c Fix timeout issue on Roomba integration when adding a new device (#129230)
* Update const.py

DEFAULT_DELAY = 1 to DEFAULT_DELAY = 100 to fix timeout when adding a new device

* Update config_flow.py

continuous=False to continuous=True to fix timeout when adding a new device

* Update homeassistant/components/roomba/const.py

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>

* Update test_config_flow.py

Change CONF_DELAY to match DEFAULT_DELAY (30 sec instead of 1)

* Update tests/components/roomba/test_config_flow.py

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>

* Use constant for DEFAULT_DELAY in tests

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: jbouwh <jan@jbsoft.nl>
2024-10-30 18:41:10 +01:00
417 changed files with 7140 additions and 2802 deletions
+1 -1
View File
@@ -531,7 +531,7 @@ jobs:
- name: Generate artifact attestation
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
uses: actions/attest-build-provenance@ef244123eb79f2f7a7e75d99086184180e6d0018 # v1.4.4
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
+9 -5
View File
@@ -40,7 +40,7 @@ env:
CACHE_VERSION: 11
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 9
HA_SHORT_VERSION: "2024.11"
HA_SHORT_VERSION: "2024.12"
DEFAULT_PYTHON: "3.12"
ALL_PYTHON_VERSIONS: "['3.12']"
# 10.3 is the oldest supported version
@@ -622,13 +622,13 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.3.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.1.2
with:
@@ -823,7 +823,7 @@ jobs:
fail-fast: false
matrix:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
name: Split tests for full run Python ${{ matrix.python-version }}
name: Split tests for full run
steps:
- name: Install additional OS dependencies
run: |
@@ -949,6 +949,7 @@ jobs:
--timeout=9 \
--durations=10 \
--numprocesses auto \
--snapshot-details \
--dist=loadfile \
${cov_params[@]} \
-o console_output_style=count \
@@ -1071,6 +1072,7 @@ jobs:
-qq \
--timeout=20 \
--numprocesses 1 \
--snapshot-details \
${cov_params[@]} \
-o console_output_style=count \
--durations=10 \
@@ -1199,6 +1201,7 @@ jobs:
-qq \
--timeout=9 \
--numprocesses 1 \
--snapshot-details \
${cov_params[@]} \
-o console_output_style=count \
--durations=0 \
@@ -1345,6 +1348,7 @@ jobs:
-qq \
--timeout=9 \
--numprocesses auto \
--snapshot-details \
${cov_params[@]} \
-o console_output_style=count \
--durations=0 \
+1 -1
View File
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.1
rev: v0.7.2
hooks:
- id: ruff
args:
+1
View File
@@ -324,6 +324,7 @@ homeassistant.components.moon.*
homeassistant.components.mopeka.*
homeassistant.components.motionmount.*
homeassistant.components.mqtt.*
homeassistant.components.music_assistant.*
homeassistant.components.my.*
homeassistant.components.mysensors.*
homeassistant.components.myuplink.*
+4 -2
View File
@@ -496,8 +496,8 @@ build.json @home-assistant/supervisor
/tests/components/freebox/ @hacf-fr @Quentame
/homeassistant/components/freedompro/ @stefano055415
/tests/components/freedompro/ @stefano055415
/homeassistant/components/fritz/ @mammuth @AaronDavidSchneider @chemelli74 @mib1185
/tests/components/fritz/ @mammuth @AaronDavidSchneider @chemelli74 @mib1185
/homeassistant/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185
/tests/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185
/homeassistant/components/fritzbox/ @mib1185 @flabbamann
/tests/components/fritzbox/ @mib1185 @flabbamann
/homeassistant/components/fritzbox_callmonitor/ @cdce8p
@@ -954,6 +954,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/msteams/ @peroyvind
/homeassistant/components/mullvad/ @meichthys
/tests/components/mullvad/ @meichthys
/homeassistant/components/music_assistant/ @music-assistant
/tests/components/music_assistant/ @music-assistant
/homeassistant/components/mutesync/ @currentoor
/tests/components/mutesync/ @currentoor
/homeassistant/components/my/ @home-assistant/core
+4
View File
@@ -9,6 +9,7 @@ import os
import sys
import threading
from .backup_restore import restore_backup
from .const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
FAULT_LOG_FILENAME = "home-assistant.log.fault"
@@ -182,6 +183,9 @@ def main() -> int:
return scripts.run(args.script)
config_dir = os.path.abspath(os.path.join(os.getcwd(), args.config))
if restore_backup(config_dir):
return RESTART_EXIT_CODE
ensure_config_path(config_dir)
# pylint: disable-next=import-outside-toplevel
+126
View File
@@ -0,0 +1,126 @@
"""Home Assistant module to handle restoring backups."""
from dataclasses import dataclass
import json
import logging
from pathlib import Path
import shutil
import sys
from tempfile import TemporaryDirectory
from awesomeversion import AwesomeVersion
import securetar
from .const import __version__ as HA_VERSION
RESTORE_BACKUP_FILE = ".HA_RESTORE"
KEEP_PATHS = ("backups",)
_LOGGER = logging.getLogger(__name__)
@dataclass
class RestoreBackupFileContent:
"""Definition for restore backup file content."""
backup_file_path: Path
def restore_backup_file_content(config_dir: Path) -> RestoreBackupFileContent | None:
"""Return the contents of the restore backup file."""
instruction_path = config_dir.joinpath(RESTORE_BACKUP_FILE)
try:
instruction_content = json.loads(instruction_path.read_text(encoding="utf-8"))
return RestoreBackupFileContent(
backup_file_path=Path(instruction_content["path"])
)
except (FileNotFoundError, json.JSONDecodeError):
return None
def _clear_configuration_directory(config_dir: Path) -> None:
"""Delete all files and directories in the config directory except for the backups directory."""
keep_paths = [config_dir.joinpath(path) for path in KEEP_PATHS]
config_contents = sorted(
[entry for entry in config_dir.iterdir() if entry not in keep_paths]
)
for entry in config_contents:
entrypath = config_dir.joinpath(entry)
if entrypath.is_file():
entrypath.unlink()
elif entrypath.is_dir():
shutil.rmtree(entrypath)
def _extract_backup(config_dir: Path, backup_file_path: Path) -> None:
"""Extract the backup file to the config directory."""
with (
TemporaryDirectory() as tempdir,
securetar.SecureTarFile(
backup_file_path,
gzip=False,
mode="r",
) as ostf,
):
ostf.extractall(
path=Path(tempdir, "extracted"),
members=securetar.secure_path(ostf),
filter="fully_trusted",
)
backup_meta_file = Path(tempdir, "extracted", "backup.json")
backup_meta = json.loads(backup_meta_file.read_text(encoding="utf8"))
if (
backup_meta_version := AwesomeVersion(
backup_meta["homeassistant"]["version"]
)
) > HA_VERSION:
raise ValueError(
f"You need at least Home Assistant version {backup_meta_version} to restore this backup"
)
with securetar.SecureTarFile(
Path(
tempdir,
"extracted",
f"homeassistant.tar{'.gz' if backup_meta["compressed"] else ''}",
),
gzip=backup_meta["compressed"],
mode="r",
) as istf:
for member in istf.getmembers():
if member.name == "data":
continue
member.name = member.name.replace("data/", "")
_clear_configuration_directory(config_dir)
istf.extractall(
path=config_dir,
members=[
member
for member in securetar.secure_path(istf)
if member.name != "data"
],
filter="fully_trusted",
)
def restore_backup(config_dir_path: str) -> bool:
"""Restore the backup file if any.
Returns True if a restore backup file was found and restored, False otherwise.
"""
config_dir = Path(config_dir_path)
if not (restore_content := restore_backup_file_content(config_dir)):
return False
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
backup_file_path = restore_content.backup_file_path
_LOGGER.info("Restoring %s", backup_file_path)
try:
_extract_backup(config_dir, backup_file_path)
except FileNotFoundError as err:
raise ValueError(f"Backup file {backup_file_path} does not exist") from err
_LOGGER.info("Restore complete, restarting")
return True
+2 -13
View File
@@ -1,6 +1,5 @@
"""The AEMET OpenData component."""
from dataclasses import dataclass
import logging
from aemet_opendata.exceptions import AemetError, TownNotFound
@@ -13,20 +12,10 @@ from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from .const import CONF_STATION_UPDATES, PLATFORMS
from .coordinator import WeatherUpdateCoordinator
from .coordinator import AemetConfigEntry, AemetData, WeatherUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
type AemetConfigEntry = ConfigEntry[AemetData]
@dataclass
class AemetData:
"""Aemet runtime data."""
name: str
coordinator: WeatherUpdateCoordinator
async def async_setup_entry(hass: HomeAssistant, entry: AemetConfigEntry) -> bool:
"""Set up AEMET OpenData as config entry."""
@@ -46,7 +35,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AemetConfigEntry) -> boo
except AemetError as err:
raise ConfigEntryNotReady(err) from err
weather_coordinator = WeatherUpdateCoordinator(hass, aemet)
weather_coordinator = WeatherUpdateCoordinator(hass, entry, aemet)
await weather_coordinator.async_config_entry_first_refresh()
entry.runtime_data = AemetData(name=name, coordinator=weather_coordinator)
@@ -3,6 +3,7 @@
from __future__ import annotations
from asyncio import timeout
from dataclasses import dataclass
from datetime import timedelta
import logging
from typing import Any, Final, cast
@@ -19,6 +20,7 @@ from aemet_opendata.helpers import dict_nested_value
from aemet_opendata.interface import AEMET
from homeassistant.components.weather import Forecast
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -29,6 +31,16 @@ _LOGGER = logging.getLogger(__name__)
API_TIMEOUT: Final[int] = 120
WEATHER_UPDATE_INTERVAL = timedelta(minutes=10)
type AemetConfigEntry = ConfigEntry[AemetData]
@dataclass
class AemetData:
"""Aemet runtime data."""
name: str
coordinator: WeatherUpdateCoordinator
class WeatherUpdateCoordinator(DataUpdateCoordinator):
"""Weather data update coordinator."""
@@ -36,6 +48,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
def __init__(
self,
hass: HomeAssistant,
entry: AemetConfigEntry,
aemet: AEMET,
) -> None:
"""Initialize coordinator."""
@@ -44,6 +57,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
super().__init__(
hass,
_LOGGER,
config_entry=entry,
name=DOMAIN,
update_interval=WEATHER_UPDATE_INTERVAL,
)
@@ -15,7 +15,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from . import AemetConfigEntry
from .coordinator import AemetConfigEntry
TO_REDACT_CONFIG = [
CONF_API_KEY,
+1 -2
View File
@@ -55,7 +55,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util
from . import AemetConfigEntry
from .const import (
ATTR_API_CONDITION,
ATTR_API_FORECAST_CONDITION,
@@ -87,7 +86,7 @@ from .const import (
ATTR_API_WIND_SPEED,
CONDITIONS_MAP,
)
from .coordinator import WeatherUpdateCoordinator
from .coordinator import AemetConfigEntry, WeatherUpdateCoordinator
from .entity import AemetEntity
+1 -2
View File
@@ -27,9 +27,8 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AemetConfigEntry
from .const import CONDITIONS_MAP
from .coordinator import WeatherUpdateCoordinator
from .coordinator import AemetConfigEntry, WeatherUpdateCoordinator
from .entity import AemetEntity
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/agent_dvr",
"iot_class": "local_polling",
"loggers": ["agent"],
"requirements": ["agent-py==0.0.23"]
"requirements": ["agent-py==0.0.24"]
}
+6 -10
View File
@@ -1,5 +1,7 @@
"""Config flow for AirNow integration."""
from __future__ import annotations
import logging
from typing import Any
@@ -12,7 +14,6 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS
from homeassistant.core import HomeAssistant, callback
@@ -120,12 +121,12 @@ class AirNowConfigFlow(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlow:
) -> AirNowOptionsFlowHandler:
"""Return the options flow."""
return AirNowOptionsFlowHandler(config_entry)
return AirNowOptionsFlowHandler()
class AirNowOptionsFlowHandler(OptionsFlowWithConfigEntry):
class AirNowOptionsFlowHandler(OptionsFlow):
"""Handle an options flow for AirNow."""
async def async_step_init(
@@ -136,12 +137,7 @@ class AirNowOptionsFlowHandler(OptionsFlowWithConfigEntry):
return self.async_create_entry(data=user_input)
options_schema = vol.Schema(
{
vol.Optional(CONF_RADIUS): vol.All(
int,
vol.Range(min=5),
),
}
{vol.Optional(CONF_RADIUS): vol.All(int, vol.Range(min=5))}
)
return self.async_show_form(
@@ -16,7 +16,6 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -46,9 +45,11 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> HomeassistantAnalyticsOptionsFlowHandler:
"""Get the options flow for this handler."""
return HomeassistantAnalyticsOptionsFlowHandler(config_entry)
return HomeassistantAnalyticsOptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -132,7 +133,7 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
)
class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlow):
"""Handle Homeassistant Analytics options."""
async def async_step_init(
@@ -211,6 +212,6 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
),
},
),
self.options,
self.config_entry.options,
),
)
@@ -13,7 +13,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import CONF_DEVICE_CLASS, CONF_HOST, CONF_PORT
from homeassistant.core import callback
@@ -186,16 +186,14 @@ class AndroidTVFlowHandler(ConfigFlow, domain=DOMAIN):
return OptionsFlowHandler(config_entry)
class OptionsFlowHandler(OptionsFlowWithConfigEntry):
class OptionsFlowHandler(OptionsFlow):
"""Handle an option flow for Android Debug Bridge."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
super().__init__(config_entry)
self._apps: dict[str, Any] = self.options.setdefault(CONF_APPS, {})
self._state_det_rules: dict[str, Any] = self.options.setdefault(
CONF_STATE_DETECTION_RULES, {}
self._apps: dict[str, Any] = dict(config_entry.options.get(CONF_APPS, {}))
self._state_det_rules: dict[str, Any] = dict(
config_entry.options.get(CONF_STATE_DETECTION_RULES, {})
)
self._conf_app_id: str | None = None
self._conf_rule_id: str | None = None
@@ -237,7 +235,7 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry):
SelectOptionDict(value=k, label=v) for k, v in apps_list.items()
]
rules = [RULES_NEW_ID, *self._state_det_rules]
options = self.options
options = self.config_entry.options
data_schema = vol.Schema(
{
@@ -20,7 +20,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME
from homeassistant.core import callback
@@ -221,13 +221,12 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
return AndroidTVRemoteOptionsFlowHandler(config_entry)
class AndroidTVRemoteOptionsFlowHandler(OptionsFlowWithConfigEntry):
class AndroidTVRemoteOptionsFlowHandler(OptionsFlow):
"""Android TV Remote options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
super().__init__(config_entry)
self._apps: dict[str, Any] = self.options.setdefault(CONF_APPS, {})
self._apps: dict[str, Any] = dict(config_entry.options.get(CONF_APPS, {}))
self._conf_app_id: str | None = None
@callback
@@ -121,7 +121,6 @@ class AnthropicOptionsFlow(OptionsFlow):
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self.last_rendered_recommended = config_entry.options.get(
CONF_RECOMMENDED, False
)
@@ -22,8 +22,8 @@ class EnhancedAudioChunk:
timestamp_ms: int
"""Timestamp relative to start of audio stream (milliseconds)"""
is_speech: bool | None
"""True if audio chunk likely contains speech, False if not, None if unknown"""
speech_probability: float | None
"""Probability that audio chunk contains speech (0-1), None if unknown"""
class AudioEnhancer(ABC):
@@ -70,27 +70,27 @@ class MicroVadSpeexEnhancer(AudioEnhancer):
)
self.vad: MicroVad | None = None
self.threshold = 0.5
if self.is_vad_enabled:
self.vad = MicroVad()
_LOGGER.debug("Initialized microVAD with threshold=%s", self.threshold)
_LOGGER.debug("Initialized microVAD")
def enhance_chunk(self, audio: bytes, timestamp_ms: int) -> EnhancedAudioChunk:
"""Enhance 10ms chunk of PCM audio @ 16Khz with 16-bit mono samples."""
is_speech: bool | None = None
speech_probability: float | None = None
assert len(audio) == BYTES_PER_CHUNK
if self.vad is not None:
# Run VAD
speech_prob = self.vad.Process10ms(audio)
is_speech = speech_prob > self.threshold
speech_probability = self.vad.Process10ms(audio)
if self.audio_processor is not None:
# Run noise suppression and auto gain
audio = self.audio_processor.Process10ms(audio).audio
return EnhancedAudioChunk(
audio=audio, timestamp_ms=timestamp_ms, is_speech=is_speech
audio=audio,
timestamp_ms=timestamp_ms,
speech_probability=speech_probability,
)
@@ -780,7 +780,9 @@ class PipelineRun:
# speaking the voice command.
audio_chunks_for_stt.extend(
EnhancedAudioChunk(
audio=chunk_ts[0], timestamp_ms=chunk_ts[1], is_speech=False
audio=chunk_ts[0],
timestamp_ms=chunk_ts[1],
speech_probability=None,
)
for chunk_ts in result.queued_audio
)
@@ -827,7 +829,7 @@ class PipelineRun:
if wake_word_vad is not None:
chunk_seconds = (len(chunk.audio) // sample_width) / sample_rate
if not wake_word_vad.process(chunk_seconds, chunk.is_speech):
if not wake_word_vad.process(chunk_seconds, chunk.speech_probability):
raise WakeWordTimeoutError(
code="wake-word-timeout", message="Wake word was not detected"
)
@@ -955,7 +957,7 @@ class PipelineRun:
if stt_vad is not None:
chunk_seconds = (len(chunk.audio) // sample_width) / sample_rate
if not stt_vad.process(chunk_seconds, chunk.is_speech):
if not stt_vad.process(chunk_seconds, chunk.speech_probability):
# Silence detected at the end of voice command
self.process_event(
PipelineEvent(
@@ -1221,7 +1223,7 @@ class PipelineRun:
yield EnhancedAudioChunk(
audio=sub_chunk,
timestamp_ms=timestamp_ms,
is_speech=None, # no VAD
speech_probability=None, # no VAD
)
timestamp_ms += MS_PER_CHUNK
+42 -20
View File
@@ -75,7 +75,7 @@ class AudioBuffer:
class VoiceCommandSegmenter:
"""Segments an audio stream into voice commands."""
speech_seconds: float = 0.3
speech_seconds: float = 0.1
"""Seconds of speech before voice command has started."""
command_seconds: float = 1.0
@@ -96,6 +96,12 @@ class VoiceCommandSegmenter:
timed_out: bool = False
"""True a timeout occurred during voice command."""
before_command_speech_threshold: float = 0.2
"""Probability threshold for speech before voice command."""
in_command_speech_threshold: float = 0.5
"""Probability threshold for speech during voice command."""
_speech_seconds_left: float = 0.0
"""Seconds left before considering voice command as started."""
@@ -124,7 +130,7 @@ class VoiceCommandSegmenter:
self._reset_seconds_left = self.reset_seconds
self.in_command = False
def process(self, chunk_seconds: float, is_speech: bool | None) -> bool:
def process(self, chunk_seconds: float, speech_probability: float | None) -> bool:
"""Process samples using external VAD.
Returns False when command is done.
@@ -142,7 +148,12 @@ class VoiceCommandSegmenter:
self.timed_out = True
return False
if speech_probability is None:
speech_probability = 0.0
if not self.in_command:
# Before command
is_speech = speech_probability > self.before_command_speech_threshold
if is_speech:
self._reset_seconds_left = self.reset_seconds
self._speech_seconds_left -= chunk_seconds
@@ -160,24 +171,29 @@ class VoiceCommandSegmenter:
if self._reset_seconds_left <= 0:
self._speech_seconds_left = self.speech_seconds
self._reset_seconds_left = self.reset_seconds
elif not is_speech:
# Silence in command
self._reset_seconds_left = self.reset_seconds
self._silence_seconds_left -= chunk_seconds
self._command_seconds_left -= chunk_seconds
if (self._silence_seconds_left <= 0) and (self._command_seconds_left <= 0):
# Command finished successfully
self.reset()
_LOGGER.debug("Voice command finished")
return False
else:
# Speech in command.
# Reset silence counter if enough speech.
self._reset_seconds_left -= chunk_seconds
self._command_seconds_left -= chunk_seconds
if self._reset_seconds_left <= 0:
self._silence_seconds_left = self.silence_seconds
# In command
is_speech = speech_probability > self.in_command_speech_threshold
if not is_speech:
# Silence in command
self._reset_seconds_left = self.reset_seconds
self._silence_seconds_left -= chunk_seconds
self._command_seconds_left -= chunk_seconds
if (self._silence_seconds_left <= 0) and (
self._command_seconds_left <= 0
):
# Command finished successfully
self.reset()
_LOGGER.debug("Voice command finished")
return False
else:
# Speech in command.
# Reset silence counter if enough speech.
self._reset_seconds_left -= chunk_seconds
self._command_seconds_left -= chunk_seconds
if self._reset_seconds_left <= 0:
self._silence_seconds_left = self.silence_seconds
self._reset_seconds_left = self.reset_seconds
return True
@@ -226,6 +242,9 @@ class VoiceActivityTimeout:
reset_seconds: float = 0.5
"""Seconds of speech before resetting timeout."""
speech_threshold: float = 0.5
"""Threshold for speech."""
_silence_seconds_left: float = 0.0
"""Seconds left before considering voice command as stopped."""
@@ -241,12 +260,15 @@ class VoiceActivityTimeout:
self._silence_seconds_left = self.silence_seconds
self._reset_seconds_left = self.reset_seconds
def process(self, chunk_seconds: float, is_speech: bool | None) -> bool:
def process(self, chunk_seconds: float, speech_probability: float | None) -> bool:
"""Process samples using external VAD.
Returns False when timeout is reached.
"""
if is_speech:
if speech_probability is None:
speech_probability = 0.0
if speech_probability > self.speech_threshold:
# Speech
self._reset_seconds_left -= chunk_seconds
if self._reset_seconds_left <= 0:
+7 -6
View File
@@ -18,7 +18,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import (
CONF_HOST,
@@ -59,9 +59,11 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> AxisOptionsFlowHandler:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> AxisOptionsFlowHandler:
"""Get the options flow for this handler."""
return AxisOptionsFlowHandler(config_entry)
return AxisOptionsFlowHandler()
def __init__(self) -> None:
"""Initialize the Axis config flow."""
@@ -264,7 +266,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
return await self.async_step_user()
class AxisOptionsFlowHandler(OptionsFlowWithConfigEntry):
class AxisOptionsFlowHandler(OptionsFlow):
"""Handle Axis device options."""
config_entry: AxisConfigEntry
@@ -282,8 +284,7 @@ class AxisOptionsFlowHandler(OptionsFlowWithConfigEntry):
) -> ConfigFlowResult:
"""Manage the Axis device stream options."""
if user_input is not None:
self.options.update(user_input)
return self.async_create_entry(title="", data=self.options)
return self.async_create_entry(data=self.config_entry.options | user_input)
schema = {}
+1
View File
@@ -17,6 +17,7 @@ LOGGER = getLogger(__package__)
EXCLUDE_FROM_BACKUP = [
"__pycache__/*",
".DS_Store",
".HA_RESTORE",
"*.db-shm",
"*.log.*",
"*.log",
@@ -16,6 +16,7 @@ from typing import Any, Protocol, cast
from securetar import SecureTarFile, atomic_contents_add
from homeassistant.backup_restore import RESTORE_BACKUP_FILE
from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
@@ -123,6 +124,10 @@ class BaseBackupManager(abc.ABC):
LOGGER.debug("Loaded %s platforms", len(self.platforms))
self.loaded_platforms = True
@abc.abstractmethod
async def async_restore_backup(self, slug: str, **kwargs: Any) -> None:
"""Restpre a backup."""
@abc.abstractmethod
async def async_create_backup(self, **kwargs: Any) -> Backup:
"""Generate a backup."""
@@ -291,6 +296,25 @@ class BackupManager(BaseBackupManager):
return tar_file_path.stat().st_size
async def async_restore_backup(self, slug: str, **kwargs: Any) -> None:
"""Restore a backup.
This will write the restore information to .HA_RESTORE which
will be handled during startup by the restore_backup module.
"""
if (backup := await self.async_get_backup(slug=slug)) is None:
raise HomeAssistantError(f"Backup {slug} not found")
def _write_restore_file() -> None:
"""Write the restore file."""
Path(self.hass.config.path(RESTORE_BACKUP_FILE)).write_text(
json.dumps({"path": backup.path.as_posix()}),
encoding="utf-8",
)
await self.hass.async_add_executor_job(_write_restore_file)
await self.hass.services.async_call("homeassistant", "restart", {})
def _generate_slug(date: str, name: str) -> str:
"""Generate a backup slug."""
@@ -22,6 +22,7 @@ def async_register_websocket_handlers(hass: HomeAssistant, with_hassio: bool) ->
websocket_api.async_register_command(hass, handle_info)
websocket_api.async_register_command(hass, handle_create)
websocket_api.async_register_command(hass, handle_remove)
websocket_api.async_register_command(hass, handle_restore)
@websocket_api.require_admin
@@ -85,6 +86,24 @@ async def handle_remove(
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required("type"): "backup/restore",
vol.Required("slug"): str,
}
)
@websocket_api.async_response
async def handle_restore(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Restore a backup."""
await hass.data[DATA_MANAGER].async_restore_backup(msg["slug"])
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command({vol.Required("type"): "backup/generate"})
@websocket_api.async_response
@@ -21,7 +21,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_SOURCE, CONF_USERNAME
from homeassistant.core import HomeAssistant, callback
@@ -153,10 +153,10 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> BMWOptionsFlow:
"""Return a MyBMW option flow."""
return BMWOptionsFlow(config_entry)
return BMWOptionsFlow()
class BMWOptionsFlow(OptionsFlowWithConfigEntry):
class BMWOptionsFlow(OptionsFlow):
"""Handle a option flow for MyBMW."""
async def async_step_init(
+2 -1
View File
@@ -16,7 +16,8 @@
"list_access": {
"default": "mdi:account-lock",
"state": {
"shared": "mdi:account-group"
"shared": "mdi:account-group",
"invitation": "mdi:account-multiple-plus"
}
}
},
+1 -1
View File
@@ -79,7 +79,7 @@ SENSOR_DESCRIPTIONS: tuple[BringSensorEntityDescription, ...] = (
translation_key=BringSensor.LIST_ACCESS,
value_fn=lambda lst, _: lst["status"].lower(),
entity_category=EntityCategory.DIAGNOSTIC,
options=["registered", "shared"],
options=["registered", "shared", "invitation"],
device_class=SensorDeviceClass.ENUM,
),
)
+2 -1
View File
@@ -66,7 +66,8 @@
"name": "List access",
"state": {
"registered": "Private",
"shared": "Shared"
"shared": "Shared",
"invitation": "Invitation pending"
}
}
}
@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["bsblan"],
"requirements": ["python-bsblan==0.6.4"]
"requirements": ["python-bsblan==1.2.1"]
}
+5 -1
View File
@@ -109,6 +109,7 @@ async def async_setup_platform(
entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, device_id, hass=hass)
coordinator = CalDavUpdateCoordinator(
hass,
None,
calendar=calendar,
days=days,
include_all_day=True,
@@ -126,6 +127,7 @@ async def async_setup_platform(
entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, device_id, hass=hass)
coordinator = CalDavUpdateCoordinator(
hass,
None,
calendar=calendar,
days=days,
include_all_day=False,
@@ -152,6 +154,7 @@ async def async_setup_entry(
async_generate_entity_id(ENTITY_ID_FORMAT, calendar.name, hass=hass),
CalDavUpdateCoordinator(
hass,
entry,
calendar=calendar,
days=CONFIG_ENTRY_DEFAULT_DAYS,
include_all_day=True,
@@ -204,7 +207,8 @@ class WebDavCalendarEntity(CoordinatorEntity[CalDavUpdateCoordinator], CalendarE
if self._supports_offset:
self._attr_extra_state_attributes = {
"offset_reached": is_offset_reached(
self._event.start_datetime_local, self.coordinator.offset
self._event.start_datetime_local,
self.coordinator.offset, # type: ignore[arg-type]
)
if self._event
else False
+18 -3
View File
@@ -6,6 +6,9 @@ from datetime import date, datetime, time, timedelta
from functools import partial
import logging
import re
from typing import TYPE_CHECKING
import caldav
from homeassistant.components.calendar import CalendarEvent, extract_offset
from homeassistant.core import HomeAssistant
@@ -14,6 +17,9 @@ from homeassistant.util import dt as dt_util
from .api import get_attr_value
if TYPE_CHECKING:
from . import CalDavConfigEntry
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
@@ -23,11 +29,20 @@ OFFSET = "!!"
class CalDavUpdateCoordinator(DataUpdateCoordinator[CalendarEvent | None]):
"""Class to utilize the calendar dav client object to get next event."""
def __init__(self, hass, calendar, days, include_all_day, search):
def __init__(
self,
hass: HomeAssistant,
entry: CalDavConfigEntry | None,
calendar: caldav.Calendar,
days: int,
include_all_day: bool,
search: str | None,
) -> None:
"""Set up how we are going to search the WebDav calendar."""
super().__init__(
hass,
_LOGGER,
config_entry=entry,
name=f"CalDAV {calendar.name}",
update_interval=MIN_TIME_BETWEEN_UPDATES,
)
@@ -35,7 +50,7 @@ class CalDavUpdateCoordinator(DataUpdateCoordinator[CalendarEvent | None]):
self.days = days
self.include_all_day = include_all_day
self.search = search
self.offset = None
self.offset: timedelta | None = None
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
@@ -109,7 +124,7 @@ class CalDavUpdateCoordinator(DataUpdateCoordinator[CalendarEvent | None]):
_start_of_tomorrow = start_of_tomorrow
if _start_of_today <= start_dt < _start_of_tomorrow:
new_event = event.copy()
new_vevent = new_event.instance.vevent
new_vevent = new_event.instance.vevent # type: ignore[attr-defined]
if hasattr(new_vevent, "dtend"):
dur = new_vevent.dtend.value - new_vevent.dtstart.value
new_vevent.dtend.value = start_dt + dur
+6 -2
View File
@@ -421,8 +421,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if hass.config.webrtc.ice_servers:
return hass.config.webrtc.ice_servers
return [
RTCIceServer(urls="stun:stun.home-assistant.io:80"),
RTCIceServer(urls="stun:stun.home-assistant.io:3478"),
RTCIceServer(
urls=[
"stun:stun.home-assistant.io:80",
"stun:stun.home-assistant.io:3478",
]
),
]
async_register_ice_servers(hass, get_ice_servers)
@@ -52,7 +52,7 @@ class CanaryConfigFlow(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
"""Get the options flow for this handler."""
return CanaryOptionsFlowHandler(config_entry)
return CanaryOptionsFlowHandler()
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Handle a flow initiated by configuration file."""
@@ -104,10 +104,6 @@ class CanaryConfigFlow(ConfigFlow, domain=DOMAIN):
class CanaryOptionsFlowHandler(OptionsFlow):
"""Handle Canary client options."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+2 -3
View File
@@ -41,7 +41,7 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> CastOptionsFlowHandler:
"""Get the options flow for this handler."""
return CastOptionsFlowHandler(config_entry)
return CastOptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -109,9 +109,8 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
class CastOptionsFlowHandler(OptionsFlow):
"""Handle Google Cast options."""
def __init__(self, config_entry: ConfigEntry) -> None:
def __init__(self) -> None:
"""Initialize Google Cast options flow."""
self.config_entry = config_entry
self.updated_config: dict[str, Any] = {}
async def async_step_init(self, user_input: None = None) -> ConfigFlowResult:
+1 -1
View File
@@ -8,6 +8,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["hass_nabucasa"],
"requirements": ["hass-nabucasa==0.83.0"],
"requirements": ["hass-nabucasa==0.84.0"],
"single_config_entry": true
}
@@ -158,16 +158,12 @@ class CoinbaseConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for Coinbase."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -4,5 +4,5 @@
"codeowners": ["@Petro31"],
"documentation": "https://www.home-assistant.io/integrations/compensation",
"iot_class": "calculated",
"requirements": ["numpy==1.26.4"]
"requirements": ["numpy==2.1.2"]
}
@@ -154,16 +154,12 @@ class Control4ConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for Control4."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==1.7.4", "home-assistant-intents==2024.11.4"]
"requirements": ["hassil==1.7.4", "home-assistant-intents==2024.11.6"]
}
@@ -213,18 +213,19 @@ class CrownstoneOptionsFlowHandler(BaseCrownstoneFlowHandler, OptionsFlow):
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize Crownstone options."""
super().__init__(OPTIONS_FLOW, self.async_create_new_entry)
self.entry = config_entry
self.updated_options = config_entry.options.copy()
self.options = config_entry.options.copy()
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage Crownstone options."""
self.cloud: CrownstoneCloud = self.hass.data[DOMAIN][self.entry.entry_id].cloud
self.cloud: CrownstoneCloud = self.hass.data[DOMAIN][
self.config_entry.entry_id
].cloud
spheres = {sphere.name: sphere.cloud_id for sphere in self.cloud.cloud_data}
usb_path = self.entry.options.get(CONF_USB_PATH)
usb_sphere = self.entry.options.get(CONF_USB_SPHERE)
usb_path = self.config_entry.options.get(CONF_USB_PATH)
usb_sphere = self.config_entry.options.get(CONF_USB_SPHERE)
options_schema = vol.Schema(
{vol.Optional(CONF_USE_USB_OPTION, default=usb_path is not None): bool}
@@ -243,14 +244,14 @@ class CrownstoneOptionsFlowHandler(BaseCrownstoneFlowHandler, OptionsFlow):
if user_input[CONF_USE_USB_OPTION] and usb_path is None:
return await self.async_step_usb_config()
if not user_input[CONF_USE_USB_OPTION] and usb_path is not None:
self.updated_options[CONF_USB_PATH] = None
self.updated_options[CONF_USB_SPHERE] = None
self.options[CONF_USB_PATH] = None
self.options[CONF_USB_SPHERE] = None
elif (
CONF_USB_SPHERE_OPTION in user_input
and spheres[user_input[CONF_USB_SPHERE_OPTION]] != usb_sphere
):
sphere_id = spheres[user_input[CONF_USB_SPHERE_OPTION]]
self.updated_options[CONF_USB_SPHERE] = sphere_id
self.options[CONF_USB_SPHERE] = sphere_id
return self.async_create_new_entry()
@@ -260,7 +261,7 @@ class CrownstoneOptionsFlowHandler(BaseCrownstoneFlowHandler, OptionsFlow):
"""Create a new entry."""
# these attributes will only change when a usb was configured
if self.usb_path is not None and self.usb_sphere_id is not None:
self.updated_options[CONF_USB_PATH] = self.usb_path
self.updated_options[CONF_USB_SPHERE] = self.usb_sphere_id
self.options[CONF_USB_PATH] = self.usb_path
self.options[CONF_USB_SPHERE] = self.usb_sphere_id
return super().async_create_entry(title="", data=self.updated_options)
return super().async_create_entry(title="", data=self.options)
@@ -74,9 +74,11 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> DeconzOptionsFlowHandler:
"""Get the options flow for this handler."""
return DeconzOptionsFlowHandler(config_entry)
return DeconzOptionsFlowHandler()
def __init__(self) -> None:
"""Initialize the deCONZ config flow."""
@@ -299,11 +301,6 @@ class DeconzOptionsFlowHandler(OptionsFlow):
gateway: DeconzHub
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize deCONZ options flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -315,8 +312,7 @@ class DeconzOptionsFlowHandler(OptionsFlow):
) -> ConfigFlowResult:
"""Manage the deconz devices options."""
if user_input is not None:
self.options.update(user_input)
return self.async_create_entry(title="", data=self.options)
return self.async_create_entry(data=self.config_entry.options | user_input)
schema_options = {}
for option, default in (
@@ -47,7 +47,6 @@ class OptionsFlowHandler(OptionsFlow):
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)
async def async_step_init(
@@ -52,10 +52,6 @@ CONFIG_SCHEMA = vol.Schema({vol.Optional(CONF_HOST): str})
class OptionsFlowHandler(OptionsFlow):
"""Options for the component."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Init object."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -119,7 +115,7 @@ class DenonAvrFlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -69,16 +69,12 @@ class DexcomConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> DexcomOptionsFlowHandler:
"""Get the options flow for this handler."""
return DexcomOptionsFlowHandler(config_entry)
return DexcomOptionsFlowHandler()
class DexcomOptionsFlowHandler(OptionsFlow):
"""Handle a option flow for Dexcom."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -74,7 +74,7 @@ class DlnaDmrFlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlow:
"""Define the config flow to handle options."""
return DlnaDmrOptionsFlowHandler(config_entry)
return DlnaDmrOptionsFlowHandler()
async def async_step_user(self, user_input: FlowInput = None) -> ConfigFlowResult:
"""Handle a flow initialized by the user.
@@ -327,10 +327,6 @@ class DlnaDmrOptionsFlowHandler(OptionsFlow):
Configures the single instance and updates the existing config entry.
"""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -14,7 +14,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import CONF_NAME, CONF_PORT
from homeassistant.core import callback
@@ -101,7 +101,7 @@ class DnsIPConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> DnsIPOptionsFlowHandler:
"""Return Option handler."""
return DnsIPOptionsFlowHandler(config_entry)
return DnsIPOptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -165,7 +165,7 @@ class DnsIPConfigFlow(ConfigFlow, domain=DOMAIN):
)
class DnsIPOptionsFlowHandler(OptionsFlowWithConfigEntry):
class DnsIPOptionsFlowHandler(OptionsFlow):
"""Handle a option config flow for dnsip integration."""
async def async_step_init(
@@ -213,16 +213,12 @@ class DoorBirdConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for doorbird."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+5 -7
View File
@@ -171,9 +171,11 @@ class DSMRFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> DSMROptionFlowHandler:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> DSMROptionFlowHandler:
"""Get the options flow for this handler."""
return DSMROptionFlowHandler(config_entry)
return DSMROptionFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -311,10 +313,6 @@ class DSMRFlowHandler(ConfigFlow, domain=DOMAIN):
class DSMROptionFlowHandler(OptionsFlow):
"""Handle options."""
def __init__(self, entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.entry = entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -328,7 +326,7 @@ class DSMROptionFlowHandler(OptionsFlow):
{
vol.Optional(
CONF_TIME_BETWEEN_UPDATE,
default=self.entry.options.get(
default=self.config_entry.options.get(
CONF_TIME_BETWEEN_UPDATE, DEFAULT_TIME_BETWEEN_UPDATE
),
): vol.All(vol.Coerce(int), vol.Range(min=0)),
+17 -16
View File
@@ -31,25 +31,26 @@ async def async_setup_entry(
"""Set up the ecobee thermostat switch entity."""
data: EcobeeData = hass.data[DOMAIN]
async_add_entities(
[
EcobeeVentilator20MinSwitch(
data,
index,
(await dt_util.async_get_time_zone(thermostat["location"]["timeZone"]))
or dt_util.get_default_time_zone(),
)
entities: list[SwitchEntity] = [
EcobeeVentilator20MinSwitch(
data,
index,
(await dt_util.async_get_time_zone(thermostat["location"]["timeZone"]))
or dt_util.get_default_time_zone(),
)
for index, thermostat in enumerate(data.ecobee.thermostats)
if thermostat["settings"]["ventilatorType"] != "none"
]
entities.extend(
(
EcobeeSwitchAuxHeatOnly(data, index)
for index, thermostat in enumerate(data.ecobee.thermostats)
if thermostat["settings"]["ventilatorType"] != "none"
],
update_before_add=True,
if thermostat["settings"]["hasHeatPump"]
)
)
async_add_entities(
EcobeeSwitchAuxHeatOnly(data, index)
for index, thermostat in enumerate(data.ecobee.thermostats)
if thermostat["settings"]["hasHeatPump"]
)
async_add_entities(entities, update_before_add=True)
class EcobeeVentilator20MinSwitch(EcobeeBaseEntity, SwitchEntity):
@@ -14,7 +14,6 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
@@ -103,13 +102,12 @@ class ElevenLabsConfigFlow(ConfigFlow, domain=DOMAIN):
return ElevenLabsOptionsFlow(config_entry)
class ElevenLabsOptionsFlow(OptionsFlowWithConfigEntry):
class ElevenLabsOptionsFlow(OptionsFlow):
"""ElevenLabs options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
super().__init__(config_entry)
self.api_key: str = self.config_entry.data[CONF_API_KEY]
self.api_key: str = config_entry.data[CONF_API_KEY]
# id -> name
self.voices: dict[str, str] = {}
self.models: dict[str, str] = {}
@@ -170,7 +168,7 @@ class ElevenLabsOptionsFlow(OptionsFlowWithConfigEntry):
vol.Required(CONF_CONFIGURE_VOICE, default=False): bool,
}
),
self.options,
self.config_entry.options,
)
async def async_step_voice_settings(
+23 -13
View File
@@ -1,5 +1,7 @@
"""Configflow for the emoncms integration."""
from __future__ import annotations
from typing import Any
from pyemoncms import EmoncmsClient
@@ -9,7 +11,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import CONF_API_KEY, CONF_URL
from homeassistant.core import HomeAssistant, callback
@@ -68,7 +70,7 @@ class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlowWithConfigEntry:
) -> EmoncmsOptionsFlow:
"""Get the options flow for this handler."""
return EmoncmsOptionsFlow(config_entry)
@@ -77,6 +79,7 @@ class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult:
"""Initiate a flow via the UI."""
errors: dict[str, str] = {}
description_placeholders = {}
if user_input is not None:
self._async_abort_entries_match(
@@ -89,7 +92,8 @@ class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN):
self.hass, user_input[CONF_URL], user_input[CONF_API_KEY]
)
if not result[CONF_SUCCESS]:
errors["base"] = result[CONF_MESSAGE]
errors["base"] = "api_error"
description_placeholders = {"details": result[CONF_MESSAGE]}
else:
self.include_only_feeds = user_input.get(CONF_ONLY_INCLUDE_FEEDID)
self.url = user_input[CONF_URL]
@@ -113,6 +117,7 @@ class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN):
user_input,
),
errors=errors,
description_placeholders=description_placeholders,
)
async def async_step_choose_feeds(
@@ -167,32 +172,36 @@ class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN):
return result
class EmoncmsOptionsFlow(OptionsFlowWithConfigEntry):
class EmoncmsOptionsFlow(OptionsFlow):
"""Emoncms Options flow handler."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize emoncms options flow."""
self._url = config_entry.data[CONF_URL]
self._api_key = config_entry.data[CONF_API_KEY]
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the options."""
errors: dict[str, str] = {}
data = self.options if self.options else self._config_entry.data
url = data[CONF_URL]
api_key = data[CONF_API_KEY]
include_only_feeds = data.get(CONF_ONLY_INCLUDE_FEEDID, [])
description_placeholders = {}
include_only_feeds = self.config_entry.options.get(
CONF_ONLY_INCLUDE_FEEDID,
self.config_entry.data.get(CONF_ONLY_INCLUDE_FEEDID, []),
)
options: list = include_only_feeds
result = await get_feed_list(self.hass, url, api_key)
result = await get_feed_list(self.hass, self._url, self._api_key)
if not result[CONF_SUCCESS]:
errors["base"] = result[CONF_MESSAGE]
errors["base"] = "api_error"
description_placeholders = {"details": result[CONF_MESSAGE]}
else:
options = get_options(result[CONF_MESSAGE])
dropdown = {"options": options, "mode": "dropdown", "multiple": True}
if user_input:
include_only_feeds = user_input[CONF_ONLY_INCLUDE_FEEDID]
return self.async_create_entry(
title=sensor_name(url),
data={
CONF_URL: url,
CONF_API_KEY: api_key,
CONF_ONLY_INCLUDE_FEEDID: include_only_feeds,
},
)
@@ -207,4 +216,5 @@ class EmoncmsOptionsFlow(OptionsFlowWithConfigEntry):
}
),
errors=errors,
description_placeholders=description_placeholders,
)
+5 -4
View File
@@ -138,10 +138,11 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the emoncms sensors."""
config = entry.options if entry.options else entry.data
name = sensor_name(config[CONF_URL])
exclude_feeds = config.get(CONF_EXCLUDE_FEEDID)
include_only_feeds = config.get(CONF_ONLY_INCLUDE_FEEDID)
name = sensor_name(entry.data[CONF_URL])
exclude_feeds = entry.data.get(CONF_EXCLUDE_FEEDID)
include_only_feeds = entry.options.get(
CONF_ONLY_INCLUDE_FEEDID, entry.data.get(CONF_ONLY_INCLUDE_FEEDID)
)
if exclude_feeds is None and include_only_feeds is None:
return
@@ -1,5 +1,8 @@
{
"config": {
"error": {
"api_error": "An error occured in the pyemoncms API : {details}"
},
"step": {
"user": {
"data": {
@@ -19,6 +22,9 @@
}
},
"options": {
"error": {
"api_error": "[%key:component::emoncms::config::error::api_error%]"
},
"step": {
"init": {
"data": {
@@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense-energy==0.13.2"]
"requirements": ["sense-energy==0.13.3"]
}
+1 -1
View File
@@ -331,7 +331,7 @@ class EnergyManager:
"device_consumption",
):
if key in update:
data[key] = update[key] # type: ignore[literal-required]
data[key] = update[key]
self.data = data
self._store.async_delay_save(lambda: data, 60)
@@ -16,7 +16,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant, callback
@@ -66,9 +66,11 @@ class EnphaseConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> EnvoyOptionsFlowHandler:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> EnvoyOptionsFlowHandler:
"""Options flow handler for Enphase_Envoy."""
return EnvoyOptionsFlowHandler(config_entry)
return EnvoyOptionsFlowHandler()
@callback
def _async_generate_schema(self) -> vol.Schema:
@@ -288,7 +290,7 @@ class EnphaseConfigFlow(ConfigFlow, domain=DOMAIN):
)
class EnvoyOptionsFlowHandler(OptionsFlowWithConfigEntry):
class EnvoyOptionsFlowHandler(OptionsFlow):
"""Envoy config flow options handler."""
async def async_step_init(
@@ -257,6 +257,9 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
self, discovery_info: MqttServiceInfo
) -> ConfigFlowResult:
"""Handle MQTT discovery."""
if not discovery_info.payload:
return self.async_abort(reason="mqtt_missing_payload")
device_info = json_loads_object(discovery_info.payload)
if "mac" not in device_info:
return self.async_abort(reason="mqtt_missing_mac")
@@ -482,16 +485,12 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for esphome."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -31,6 +31,7 @@ class ESPHomeDashboardCoordinator(DataUpdateCoordinator[dict[str, ConfiguredDevi
super().__init__(
hass,
_LOGGER,
config_entry=None,
name="ESPHome Dashboard",
update_interval=timedelta(minutes=5),
always_update=False,
@@ -8,7 +8,8 @@
"service_received": "Action received",
"mqtt_missing_mac": "Missing MAC address in MQTT properties.",
"mqtt_missing_api": "Missing API port in MQTT properties.",
"mqtt_missing_ip": "Missing IP address in MQTT properties."
"mqtt_missing_ip": "Missing IP address in MQTT properties.",
"mqtt_missing_payload": "Missing MQTT Payload."
},
"error": {
"resolve_error": "Can't resolve address of the ESP. If this error persists, please set a static IP address",
@@ -240,6 +240,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=None,
name=f"{DOMAIN}_coordinator",
update_interval=config[DOMAIN][CONF_SCAN_INTERVAL],
update_method=broker.async_update,
@@ -150,7 +150,7 @@ class EzvizConfigFlow(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> EzvizOptionsFlowHandler:
"""Get the options flow for this handler."""
return EzvizOptionsFlowHandler(config_entry)
return EzvizOptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -391,10 +391,6 @@ class EzvizConfigFlow(ConfigFlow, domain=DOMAIN):
class EzvizOptionsFlowHandler(OptionsFlow):
"""Handle EZVIZ client options."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -15,7 +15,6 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import CONF_URL
from homeassistant.core import HomeAssistant, callback
@@ -46,9 +45,11 @@ class FeedReaderConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlow:
"""Get the options flow for this handler."""
return FeedReaderOptionsFlowHandler(config_entry)
return FeedReaderOptionsFlowHandler()
def show_user_form(
self,
@@ -147,7 +148,7 @@ class FeedReaderConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="reconfigure_successful")
class FeedReaderOptionsFlowHandler(OptionsFlowWithConfigEntry):
class FeedReaderOptionsFlowHandler(OptionsFlow):
"""Handle an options flow."""
async def async_step_init(
@@ -162,7 +163,9 @@ class FeedReaderOptionsFlowHandler(OptionsFlowWithConfigEntry):
{
vol.Optional(
CONF_MAX_ENTRIES,
default=self.options.get(CONF_MAX_ENTRIES, DEFAULT_MAX_ENTRIES),
default=self.config_entry.options.get(
CONF_MAX_ENTRIES, DEFAULT_MAX_ENTRIES
),
): cv.positive_int,
}
)
+7 -4
View File
@@ -1,5 +1,7 @@
"""Config flow for file integration."""
from __future__ import annotations
from copy import deepcopy
import os
from typing import Any
@@ -11,7 +13,6 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import (
CONF_FILE_PATH,
@@ -74,9 +75,11 @@ class FileConfigFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> FileOptionsFlowHandler:
"""Get the options flow for this handler."""
return FileOptionsFlowHandler(config_entry)
return FileOptionsFlowHandler()
async def validate_file_path(self, file_path: str) -> bool:
"""Ensure the file path is valid."""
@@ -151,7 +154,7 @@ class FileConfigFlowHandler(ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title=title, data=data, options=options)
class FileOptionsFlowHandler(OptionsFlowWithConfigEntry):
class FileOptionsFlowHandler(OptionsFlow):
"""Handle File options."""
async def async_step_init(
@@ -71,9 +71,11 @@ class FluxLedConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> FluxLedOptionsFlow:
"""Get the options flow for the Flux LED component."""
return FluxLedOptionsFlow(config_entry)
return FluxLedOptionsFlow()
async def async_step_dhcp(
self, discovery_info: dhcp.DhcpServiceInfo
@@ -320,10 +322,6 @@ class FluxLedConfigFlow(ConfigFlow, domain=DOMAIN):
class FluxLedOptionsFlow(OptionsFlow):
"""Handle flux_led options."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize the flux_led options flow."""
self._config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -332,7 +330,7 @@ class FluxLedOptionsFlow(OptionsFlow):
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
options = self._config_entry.options
options = self.config_entry.options
options_schema = vol.Schema(
{
vol.Optional(
@@ -41,7 +41,7 @@ class ForecastSolarFlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> ForecastSolarOptionFlowHandler:
"""Get the options flow for this handler."""
return ForecastSolarOptionFlowHandler(config_entry)
return ForecastSolarOptionFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -91,10 +91,6 @@ class ForecastSolarFlowHandler(ConfigFlow, domain=DOMAIN):
class ForecastSolarOptionFlowHandler(OptionsFlow):
"""Handle options."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -52,10 +52,6 @@ TEST_CONNECTION_ERROR_DICT = {
class ForkedDaapdOptionsFlowHandler(OptionsFlow):
"""Handle a forked-daapd options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -122,7 +118,7 @@ class ForkedDaapdFlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> ForkedDaapdOptionsFlowHandler:
"""Return options flow handler."""
return ForkedDaapdOptionsFlowHandler(config_entry)
return ForkedDaapdOptionsFlowHandler()
async def validate_input(self, user_input):
"""Validate the user input."""
@@ -23,7 +23,6 @@ from homeassistant.config_entries import (
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import (
CONF_HOST,
@@ -60,9 +59,11 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
def async_get_options_flow(
config_entry: ConfigEntry,
) -> FritzBoxToolsOptionsFlowHandler:
"""Get the options flow for this handler."""
return FritzBoxToolsOptionsFlowHandler(config_entry)
return FritzBoxToolsOptionsFlowHandler()
def __init__(self) -> None:
"""Initialize FRITZ!Box Tools flow."""
@@ -393,7 +394,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
)
class FritzBoxToolsOptionsFlowHandler(OptionsFlowWithConfigEntry):
class FritzBoxToolsOptionsFlowHandler(OptionsFlow):
"""Handle an options flow."""
async def async_step_init(
@@ -404,19 +405,18 @@ class FritzBoxToolsOptionsFlowHandler(OptionsFlowWithConfigEntry):
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
options = self.config_entry.options
data_schema = vol.Schema(
{
vol.Optional(
CONF_CONSIDER_HOME,
default=self.options.get(
default=options.get(
CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds()
),
): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=900)),
vol.Optional(
CONF_OLD_DISCOVERY,
default=self.options.get(
CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY
),
default=options.get(CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY),
): bool,
}
)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"domain": "fritz",
"name": "AVM FRITZ!Box Tools",
"codeowners": ["@mammuth", "@AaronDavidSchneider", "@chemelli74", "@mib1185"],
"codeowners": ["@AaronDavidSchneider", "@chemelli74", "@mib1185"],
"config_flow": true,
"dependencies": ["network"],
"documentation": "https://www.home-assistant.io/integrations/fritz",
@@ -141,7 +141,7 @@ class FritzBoxCallMonitorConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> FritzBoxCallMonitorOptionsFlowHandler:
"""Get the options flow for this handler."""
return FritzBoxCallMonitorOptionsFlowHandler(config_entry)
return FritzBoxCallMonitorOptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -278,10 +278,6 @@ class FritzBoxCallMonitorConfigFlow(ConfigFlow, domain=DOMAIN):
class FritzBoxCallMonitorOptionsFlowHandler(OptionsFlow):
"""Handle a fritzbox_callmonitor options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize."""
self.config_entry = config_entry
@classmethod
def _are_prefixes_valid(cls, prefixes: str | None) -> bool:
"""Check if prefixes are valid."""
@@ -324,7 +324,7 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> GenericOptionsFlowHandler:
"""Get the options flow for this handler."""
return GenericOptionsFlowHandler(config_entry)
return GenericOptionsFlowHandler()
def check_for_existing(self, options: dict[str, Any]) -> bool:
"""Check whether an existing entry is using the same URLs."""
@@ -409,9 +409,8 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN):
class GenericOptionsFlowHandler(OptionsFlow):
"""Handle Generic IP Camera options."""
def __init__(self, config_entry: ConfigEntry) -> None:
def __init__(self) -> None:
"""Initialize Generic IP Camera options flow."""
self.config_entry = config_entry
self.preview_cam: dict[str, Any] = {}
self.user_input: dict[str, Any] = {}
@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/generic",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["ha-av==10.1.1", "Pillow==10.4.0"]
"requirements": ["av==13.1.0", "Pillow==10.4.0"]
}
@@ -211,16 +211,12 @@ class GitHubConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for GitHub."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self,
user_input: dict[str, Any] | None = None,
@@ -238,16 +238,12 @@ class OAuth2FlowHandler(
config_entry: ConfigEntry,
) -> OptionsFlow:
"""Create an options flow."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Google Calendar options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -84,16 +84,12 @@ class OAuth2FlowHandler(
config_entry: ConfigEntry,
) -> OptionsFlow:
"""Create the options flow."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Google Assistant SDK options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -15,7 +15,7 @@ from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
OptionsFlow,
)
from homeassistant.core import callback
from homeassistant.helpers.selector import (
@@ -135,10 +135,10 @@ class GoogleCloudConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> GoogleCloudOptionsFlowHandler:
"""Create the options flow."""
return GoogleCloudOptionsFlowHandler(config_entry)
return GoogleCloudOptionsFlowHandler()
class GoogleCloudOptionsFlowHandler(OptionsFlowWithConfigEntry):
class GoogleCloudOptionsFlowHandler(OptionsFlow):
"""Google Cloud options flow."""
async def async_step_init(
@@ -169,7 +169,7 @@ class GoogleCloudOptionsFlowHandler(OptionsFlowWithConfigEntry):
)
),
**tts_options_schema(
self.options, voices, from_config_flow=True
self.config_entry.options, voices, from_config_flow=True
).schema,
vol.Optional(
CONF_STT_MODEL,
@@ -182,6 +182,6 @@ class GoogleCloudOptionsFlowHandler(OptionsFlowWithConfigEntry):
),
}
),
self.options,
self.config_entry.options,
),
)
@@ -52,7 +52,7 @@ async def async_tts_voices(
def tts_options_schema(
config_options: dict[str, Any],
config_options: Mapping[str, Any],
voices: dict[str, list[str]],
from_config_flow: bool = False,
) -> vol.Schema:
@@ -163,7 +163,6 @@ class GoogleGenerativeAIOptionsFlow(OptionsFlow):
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self.last_rendered_recommended = config_entry.options.get(
CONF_RECOMMENDED, False
)
@@ -148,10 +148,6 @@ def default_options(hass: HomeAssistant) -> dict[str, str]:
class GoogleOptionsFlow(OptionsFlow):
"""Handle an options flow for Google Travel Time."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize google options flow."""
self.config_entry = config_entry
async def async_step_init(self, user_input=None) -> ConfigFlowResult:
"""Handle the initial step."""
if user_input is not None:
@@ -213,7 +209,7 @@ class GoogleTravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> GoogleOptionsFlow:
"""Get the options flow for this handler."""
return GoogleOptionsFlow(config_entry)
return GoogleOptionsFlow()
async def async_step_user(self, user_input=None) -> ConfigFlowResult:
"""Handle the initial step."""
@@ -30,6 +30,7 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.CALENDAR,
Platform.SENSOR,
@@ -0,0 +1,85 @@
"""Binary sensor platform for Habitica integration."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum
from typing import Any
from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ASSETS_URL
from .entity import HabiticaBase
from .types import HabiticaConfigEntry
@dataclass(kw_only=True, frozen=True)
class HabiticaBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Habitica Binary Sensor Description."""
value_fn: Callable[[dict[str, Any]], bool | None]
entity_picture: Callable[[dict[str, Any]], str | None]
class HabiticaBinarySensor(StrEnum):
"""Habitica Entities."""
PENDING_QUEST = "pending_quest"
def get_scroll_image_for_pending_quest_invitation(user: dict[str, Any]) -> str | None:
"""Entity picture for pending quest invitation."""
if user["party"]["quest"].get("key") and user["party"]["quest"]["RSVPNeeded"]:
return f"inventory_quest_scroll_{user["party"]["quest"]["key"]}.png"
return None
BINARY_SENSOR_DESCRIPTIONS: tuple[HabiticaBinarySensorEntityDescription, ...] = (
HabiticaBinarySensorEntityDescription(
key=HabiticaBinarySensor.PENDING_QUEST,
translation_key=HabiticaBinarySensor.PENDING_QUEST,
value_fn=lambda user: user["party"]["quest"]["RSVPNeeded"],
entity_picture=get_scroll_image_for_pending_quest_invitation,
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: HabiticaConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the habitica binary sensors."""
coordinator = config_entry.runtime_data
async_add_entities(
HabiticaBinarySensorEntity(coordinator, description)
for description in BINARY_SENSOR_DESCRIPTIONS
)
class HabiticaBinarySensorEntity(HabiticaBase, BinarySensorEntity):
"""Representation of a Habitica binary sensor."""
entity_description: HabiticaBinarySensorEntityDescription
@property
def is_on(self) -> bool | None:
"""If the binary sensor is on."""
return self.entity_description.value_fn(self.coordinator.data.user)
@property
def entity_picture(self) -> str | None:
"""Return the entity picture to use in the frontend, if any."""
if entity_picture := self.entity_description.entity_picture(
self.coordinator.data.user
):
return f"{ASSETS_URL}{entity_picture}"
return None
@@ -135,6 +135,14 @@
"on": "mdi:sleep"
}
}
},
"binary_sensor": {
"pending_quest": {
"default": "mdi:script-outline",
"state": {
"on": "mdi:script-text-outline"
}
}
}
},
"services": {
+17 -8
View File
@@ -9,6 +9,7 @@ from typing import Any
from aiohttp import ClientResponseError
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_NAME, CONF_NAME
from homeassistant.core import (
HomeAssistant,
@@ -54,6 +55,21 @@ SERVICE_CAST_SKILL_SCHEMA = vol.Schema(
)
def get_config_entry(hass: HomeAssistant, entry_id: str) -> HabiticaConfigEntry:
"""Return config entry or raise if not found or not loaded."""
if not (entry := hass.config_entries.async_get_entry(entry_id)):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_found",
)
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_loaded",
)
return entry
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up services for Habitica integration."""
@@ -86,14 +102,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
async def cast_skill(call: ServiceCall) -> ServiceResponse:
"""Skill action."""
entry: HabiticaConfigEntry | None
if not (
entry := hass.config_entries.async_get_entry(call.data[ATTR_CONFIG_ENTRY])
):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_found",
)
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
coordinator = entry.runtime_data
skill = {
"pickpocket": {"spellId": "pickPocket", "cost": "10 MP"},
@@ -38,6 +38,11 @@
}
},
"entity": {
"binary_sensor": {
"pending_quest": {
"name": "Pending quest invitation"
}
},
"button": {
"run_cron": {
"name": "Start my day"
@@ -219,7 +224,10 @@
"message": "Unable to cast skill, your character does not have the skill or spell {skill}."
},
"entry_not_found": {
"message": "The selected character is currently not configured or loaded in Home Assistant."
"message": "The selected character is not configured in Home Assistant."
},
"entry_not_loaded": {
"message": "The selected character is currently not loaded or disabled in Home Assistant."
},
"task_not_found": {
"message": "Unable to cast skill, could not find the task {task}"
@@ -28,7 +28,6 @@ from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from .const import DOMAIN, PREVIOUS_ACTIVE_ACTIVITY, UNIQUE_ID
from .data import HarmonyConfigEntry
from .util import (
find_best_name_for_remote,
find_unique_id_for_remote,
@@ -156,7 +155,7 @@ class HarmonyConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
async def _async_create_entry_from_valid_input(
self, validated: dict[str, Any], user_input: dict[str, Any]
@@ -186,10 +185,6 @@ def _options_from_user_input(user_input: dict[str, Any]) -> dict[str, Any]:
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for Harmony."""
def __init__(self, config_entry: HarmonyConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
-14
View File
@@ -137,17 +137,3 @@ class SupervisorEntityModel(StrEnum):
CORE = "Home Assistant Core"
SUPERVIOSR = "Home Assistant Supervisor"
HOST = "Home Assistant Host"
class SupervisorIssueContext(StrEnum):
"""Context for supervisor issues."""
ADDON = "addon"
CORE = "core"
DNS_SERVER = "dns_server"
MOUNT = "mount"
OS = "os"
PLUGIN = "plugin"
SUPERVISOR = "supervisor"
STORE = "store"
SYSTEM = "system"
@@ -91,15 +91,6 @@ async def async_create_backup(
return await hassio.send_command(command, payload=payload, timeout=None)
@bind_hass
@_api_bool
async def async_apply_suggestion(hass: HomeAssistant, suggestion_uuid: str) -> dict:
"""Apply a suggestion from supervisor's resolution center."""
hassio: HassIO = hass.data[DOMAIN]
command = f"/resolution/suggestion/{suggestion_uuid}"
return await hassio.send_command(command, timeout=None)
@api_data
async def async_get_green_settings(hass: HomeAssistant) -> dict[str, bool]:
"""Return settings specific to Home Assistant Green."""
@@ -245,26 +236,6 @@ class HassIO:
"""
return self.send_command("/ingress/panels", method="get")
@api_data
def get_resolution_info(self) -> Coroutine:
"""Return data for Supervisor resolution center.
This method returns a coroutine.
"""
return self.send_command("/resolution/info", method="get")
@api_data
def get_suggestions_for_issue(
self, issue_id: str
) -> Coroutine[Any, Any, dict[str, Any]]:
"""Return suggestions for issue from Supervisor resolution center.
This method returns a coroutine.
"""
return self.send_command(
f"/resolution/issue/{issue_id}/suggestions", method="get"
)
@_api_bool
async def update_hass_api(
self, http_config: dict[str, Any], refresh_token: RefreshToken
@@ -304,14 +275,6 @@ class HassIO:
"/supervisor/options", payload={"diagnostics": diagnostics}
)
@_api_bool
def apply_suggestion(self, suggestion_uuid: str) -> Coroutine:
"""Apply a suggestion from supervisor's resolution center.
This method returns a coroutine.
"""
return self.send_command(f"/resolution/suggestion/{suggestion_uuid}")
async def send_command(
self,
command: str,
+49 -33
View File
@@ -7,6 +7,10 @@ from dataclasses import dataclass, field
from datetime import datetime
import logging
from typing import Any, NotRequired, TypedDict
from uuid import UUID
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import ContextType, Issue as SupervisorIssue
from homeassistant.core import HassJob, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -20,12 +24,8 @@ from homeassistant.helpers.issue_registry import (
from .const import (
ATTR_DATA,
ATTR_HEALTHY,
ATTR_ISSUES,
ATTR_SUGGESTIONS,
ATTR_SUPPORTED,
ATTR_UNHEALTHY,
ATTR_UNHEALTHY_REASONS,
ATTR_UNSUPPORTED,
ATTR_UNSUPPORTED_REASONS,
ATTR_UPDATE_KEY,
ATTR_WS_EVENT,
@@ -45,10 +45,9 @@ from .const import (
PLACEHOLDER_KEY_REFERENCE,
REQUEST_REFRESH_DELAY,
UPDATE_KEY_SUPERVISOR,
SupervisorIssueContext,
)
from .coordinator import get_addons_info
from .handler import HassIO, HassioAPIError
from .handler import HassIO, get_supervisor_client
ISSUE_KEY_UNHEALTHY = "unhealthy"
ISSUE_KEY_UNSUPPORTED = "unsupported"
@@ -120,9 +119,9 @@ class SuggestionDataType(TypedDict):
class Suggestion:
"""Suggestion from Supervisor which resolves an issue."""
uuid: str
uuid: UUID
type: str
context: SupervisorIssueContext
context: ContextType
reference: str | None = None
@property
@@ -134,9 +133,9 @@ class Suggestion:
def from_dict(cls, data: SuggestionDataType) -> Suggestion:
"""Convert from dictionary representation."""
return cls(
uuid=data["uuid"],
uuid=UUID(data["uuid"]),
type=data["type"],
context=SupervisorIssueContext(data["context"]),
context=ContextType(data["context"]),
reference=data["reference"],
)
@@ -155,9 +154,9 @@ class IssueDataType(TypedDict):
class Issue:
"""Issue from Supervisor."""
uuid: str
uuid: UUID
type: str
context: SupervisorIssueContext
context: ContextType
reference: str | None = None
suggestions: list[Suggestion] = field(default_factory=list, compare=False)
@@ -171,9 +170,9 @@ class Issue:
"""Convert from dictionary representation."""
suggestions: list[SuggestionDataType] = data.get("suggestions", [])
return cls(
uuid=data["uuid"],
uuid=UUID(data["uuid"]),
type=data["type"],
context=SupervisorIssueContext(data["context"]),
context=ContextType(data["context"]),
reference=data["reference"],
suggestions=[
Suggestion.from_dict(suggestion) for suggestion in suggestions
@@ -190,7 +189,8 @@ class SupervisorIssues:
self._client = client
self._unsupported_reasons: set[str] = set()
self._unhealthy_reasons: set[str] = set()
self._issues: dict[str, Issue] = {}
self._issues: dict[UUID, Issue] = {}
self._supervisor_client = get_supervisor_client(hass)
@property
def unhealthy_reasons(self) -> set[str]:
@@ -283,7 +283,7 @@ class SupervisorIssues:
async_create_issue(
self._hass,
DOMAIN,
issue.uuid,
issue.uuid.hex,
is_fixable=bool(issue.suggestions),
severity=IssueSeverity.WARNING,
translation_key=issue.key,
@@ -292,19 +292,37 @@ class SupervisorIssues:
self._issues[issue.uuid] = issue
async def add_issue_from_data(self, data: IssueDataType) -> None:
async def add_issue_from_data(self, data: SupervisorIssue) -> None:
"""Add issue from data to list after getting latest suggestions."""
try:
data["suggestions"] = (
await self._client.get_suggestions_for_issue(data["uuid"])
)[ATTR_SUGGESTIONS]
except HassioAPIError:
suggestions = (
await self._supervisor_client.resolution.suggestions_for_issue(
data.uuid
)
)
except SupervisorError:
_LOGGER.error(
"Could not get suggestions for supervisor issue %s, skipping it",
data["uuid"],
data.uuid.hex,
)
return
self.add_issue(Issue.from_dict(data))
self.add_issue(
Issue(
uuid=data.uuid,
type=str(data.type),
context=data.context,
reference=data.reference,
suggestions=[
Suggestion(
uuid=suggestion.uuid,
type=str(suggestion.type),
context=suggestion.context,
reference=suggestion.reference,
)
for suggestion in suggestions
],
)
)
def remove_issue(self, issue: Issue) -> None:
"""Remove an issue from the list. Delete a repair if necessary."""
@@ -312,13 +330,13 @@ class SupervisorIssues:
return
if issue.key in ISSUE_KEYS_FOR_REPAIRS:
async_delete_issue(self._hass, DOMAIN, issue.uuid)
async_delete_issue(self._hass, DOMAIN, issue.uuid.hex)
del self._issues[issue.uuid]
def get_issue(self, issue_id: str) -> Issue | None:
"""Get issue from key."""
return self._issues.get(issue_id)
return self._issues.get(UUID(issue_id))
async def setup(self) -> None:
"""Create supervisor events listener."""
@@ -331,8 +349,8 @@ class SupervisorIssues:
async def _update(self, _: datetime | None = None) -> None:
"""Update issues from Supervisor resolution center."""
try:
data = await self._client.get_resolution_info()
except HassioAPIError as err:
data = await self._supervisor_client.resolution.info()
except SupervisorError as err:
_LOGGER.error("Failed to update supervisor issues: %r", err)
async_call_later(
self._hass,
@@ -340,18 +358,16 @@ class SupervisorIssues:
HassJob(self._update, cancel_on_shutdown=True),
)
return
self.unhealthy_reasons = set(data[ATTR_UNHEALTHY])
self.unsupported_reasons = set(data[ATTR_UNSUPPORTED])
self.unhealthy_reasons = set(data.unhealthy)
self.unsupported_reasons = set(data.unsupported)
# Remove any cached issues that weren't returned
for issue_id in set(self._issues.keys()) - {
issue["uuid"] for issue in data[ATTR_ISSUES]
}:
for issue_id in set(self._issues) - {issue.uuid for issue in data.issues}:
self.remove_issue(self._issues[issue_id])
# Add/update any issues that came back
await asyncio.gather(
*[self.add_issue_from_data(issue) for issue in data[ATTR_ISSUES]]
*[self.add_issue_from_data(issue) for issue in data.issues]
)
@callback
+16 -11
View File
@@ -6,6 +6,8 @@ from collections.abc import Callable, Coroutine
from types import MethodType
from typing import Any
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import ContextType
import voluptuous as vol
from homeassistant.components.repairs import RepairsFlow
@@ -20,9 +22,8 @@ from .const import (
PLACEHOLDER_KEY_ADDON,
PLACEHOLDER_KEY_COMPONENTS,
PLACEHOLDER_KEY_REFERENCE,
SupervisorIssueContext,
)
from .handler import async_apply_suggestion
from .handler import get_supervisor_client
from .issues import Issue, Suggestion
HELP_URLS = {
@@ -51,9 +52,10 @@ class SupervisorIssueRepairFlow(RepairsFlow):
_data: dict[str, Any] | None = None
_issue: Issue | None = None
def __init__(self, issue_id: str) -> None:
def __init__(self, hass: HomeAssistant, issue_id: str) -> None:
"""Initialize repair flow."""
self._issue_id = issue_id
self._supervisor_client = get_supervisor_client(hass)
super().__init__()
@property
@@ -124,9 +126,12 @@ class SupervisorIssueRepairFlow(RepairsFlow):
if not confirmed and suggestion.key in SUGGESTION_CONFIRMATION_REQUIRED:
return self._async_form_for_suggestion(suggestion)
if await async_apply_suggestion(self.hass, suggestion.uuid):
return self.async_create_entry(data={})
return self.async_abort(reason="apply_suggestion_fail")
try:
await self._supervisor_client.resolution.apply_suggestion(suggestion.uuid)
except SupervisorError:
return self.async_abort(reason="apply_suggestion_fail")
return self.async_create_entry(data={})
@staticmethod
def _async_step(
@@ -163,9 +168,9 @@ class DockerConfigIssueRepairFlow(SupervisorIssueRepairFlow):
if issue.key == self.issue.key or issue.type != self.issue.type:
continue
if issue.context == SupervisorIssueContext.CORE:
if issue.context == ContextType.CORE:
components.insert(0, "Home Assistant")
elif issue.context == SupervisorIssueContext.ADDON:
elif issue.context == ContextType.ADDON:
components.append(
next(
(
@@ -210,11 +215,11 @@ async def async_create_fix_flow(
supervisor_issues = get_issues_info(hass)
issue = supervisor_issues and supervisor_issues.get_issue(issue_id)
if issue and issue.key == ISSUE_KEY_SYSTEM_DOCKER_CONFIG:
return DockerConfigIssueRepairFlow(issue_id)
return DockerConfigIssueRepairFlow(hass, issue_id)
if issue and issue.key in {
ISSUE_KEY_ADDON_DETACHED_ADDON_REMOVED,
ISSUE_KEY_ADDON_BOOT_FAIL,
}:
return AddonIssueRepairFlow(issue_id)
return AddonIssueRepairFlow(hass, issue_id)
return SupervisorIssueRepairFlow(issue_id)
return SupervisorIssueRepairFlow(hass, issue_id)
@@ -113,7 +113,7 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> HERETravelTimeOptionsFlow:
"""Get the options flow."""
return HERETravelTimeOptionsFlow(config_entry)
return HERETravelTimeOptionsFlow()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -297,9 +297,8 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
class HERETravelTimeOptionsFlow(OptionsFlow):
"""Handle HERE Travel Time options."""
def __init__(self, config_entry: ConfigEntry) -> None:
def __init__(self) -> None:
"""Initialize HERE Travel Time options flow."""
self.config_entry = config_entry
self._config: dict[str, Any] = {}
async def async_step_init(
@@ -182,7 +182,6 @@ class HiveOptionsFlowHandler(OptionsFlow):
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize Hive options flow."""
self.hive = None
self.config_entry = config_entry
self.interval = config_entry.options.get(CONF_SCAN_INTERVAL, 120)
async def async_step_init(
@@ -362,15 +362,14 @@ class HomeKitConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
class OptionsFlowHandler(OptionsFlow):
"""Handle a option flow for homekit."""
def __init__(self, config_entry: ConfigEntry) -> None:
def __init__(self) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self.hk_options: dict[str, Any] = {}
self.included_cameras: list[str] = []
@@ -420,6 +420,7 @@ class HomematicipWindspeedSensor(HomematicipGenericEntity, SensorEntity):
_attr_device_class = SensorDeviceClass.WIND_SPEED
_attr_native_unit_of_measurement = UnitOfSpeed.KILOMETERS_PER_HOUR
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the windspeed sensor."""
@@ -451,6 +452,7 @@ class HomematicipTodayRainSensor(HomematicipGenericEntity, SensorEntity):
_attr_device_class = SensorDeviceClass.PRECIPITATION
_attr_native_unit_of_measurement = UnitOfPrecipitationDepth.MILLIMETERS
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
@@ -129,16 +129,12 @@ class HoneywellConfigFlow(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> HoneywellOptionsFlowHandler:
"""Options callback for Honeywell."""
return HoneywellOptionsFlowHandler(config_entry)
return HoneywellOptionsFlowHandler()
class HoneywellOptionsFlowHandler(OptionsFlow):
"""Config flow options for Honeywell."""
def __init__(self, entry: ConfigEntry) -> None:
"""Initialize Honeywell options flow."""
self.config_entry = entry
async def async_step_init(self, user_input=None) -> ConfigFlowResult:
"""Manage the options."""
if user_input is not None:
@@ -16,6 +16,9 @@
}
}
},
"abort": {
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
},
"error": {
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
@@ -69,7 +69,7 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Get options flow."""
return OptionsFlowHandler(config_entry)
return OptionsFlowHandler()
async def _async_show_user_form(
self,
@@ -345,10 +345,6 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
class OptionsFlowHandler(OptionsFlow):
"""Huawei LTE options flow."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -7,7 +7,7 @@
"iot_class": "local_polling",
"loggers": ["huawei_lte_api.Session"],
"requirements": [
"huawei-lte-api==1.9.3",
"huawei-lte-api==1.10.0",
"stringcase==1.2.0",
"url-normalize==1.4.3"
],
+2 -10
View File
@@ -57,8 +57,8 @@ class HueFlowHandler(ConfigFlow, domain=DOMAIN):
) -> HueV1OptionsFlowHandler | HueV2OptionsFlowHandler:
"""Get the options flow for this handler."""
if config_entry.data.get(CONF_API_VERSION, 1) == 1:
return HueV1OptionsFlowHandler(config_entry)
return HueV2OptionsFlowHandler(config_entry)
return HueV1OptionsFlowHandler()
return HueV2OptionsFlowHandler()
def __init__(self) -> None:
"""Initialize the Hue flow."""
@@ -280,10 +280,6 @@ class HueFlowHandler(ConfigFlow, domain=DOMAIN):
class HueV1OptionsFlowHandler(OptionsFlow):
"""Handle Hue options for V1 implementation."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize Hue options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -315,10 +311,6 @@ class HueV1OptionsFlowHandler(OptionsFlow):
class HueV2OptionsFlowHandler(OptionsFlow):
"""Handle Hue options for V2 implementation."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize Hue options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+3 -3
View File
@@ -137,15 +137,15 @@
"services": {
"hue_activate_scene": {
"name": "Activate scene",
"description": "Activates a hue scene stored in the hue hub.",
"description": "Activates a Hue scene stored in the Hue hub.",
"fields": {
"group_name": {
"name": "Group",
"description": "Name of hue group/room from the hue app."
"description": "Name of Hue group/room from the Hue app."
},
"scene_name": {
"name": "Scene",
"description": "Name of hue scene from the hue app."
"description": "Name of Hue scene from the Hue app."
},
"dynamic": {
"name": "Dynamic",
+1 -1
View File
@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/huum",
"iot_class": "cloud_polling",
"requirements": ["huum==0.7.10"]
"requirements": ["huum==0.7.11"]
}

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