Compare commits

...
Sign in to create a new pull request.

705 commits

Author SHA1 Message Date
Josef Zweck
1ce8bfdaa4
Use test helpers for acaia buttons (#130626) 2024-11-14 16:34:17 +01:00
Robert Resch
cd12720085
Add Python version to issue ID (#130611) 2024-11-14 16:31:33 +01:00
epenet
c7ee7dc880
Refactor translation checks (#130585)
* Refactor translation checks

* Adjust

* Improve

* Restore await

* Delay pytest.fail until the end of the test
2024-11-14 16:26:05 +01:00
epenet
472414a8d6
Add missing translation string to smarty (#130624) 2024-11-14 16:17:08 +01:00
Lennard Beers
0c44c632d4
Add number platform to eq3btsmart (#130429) 2024-11-14 15:38:38 +01:00
Álvaro Fernández Rojas
61d0de3042
Bump aioairzone to 0.9.6 (#130559)
* Update aioairzone to v0.9.6

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Remove _async_migrator_mac_empty and improve tests

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Remove WebServer empty mac fixes as requested by @epenet

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

---------

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2024-11-14 15:27:10 +01:00
Thibaut
01332a542c
Removing myself from template codeowners (#130617)
* Removing myself as codeowners

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2024-11-14 15:23:55 +01:00
Andre Lengwenus
3d84e35268
Move lcn non-config_entry related code to async_setup (#130603)
* Move non-config_entry related code to async_setup

* Remove action unload
2024-11-14 14:27:19 +01:00
Josef Zweck
eea782bbfe
Add acaia integration (#130059)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-14 13:28:38 +01:00
Lennard Beers
a949d18c30
Bump eq3btsmart to 1.4.1 (#130426) 2024-11-14 13:04:22 +01:00
Marc Mueller
a748897bd2
Update hassfest image to Python 3.13 (#130607) 2024-11-14 12:44:06 +01:00
Robert Resch
3201142fd8
Fix hassfest by adding go2rtc reqs (#130602) 2024-11-14 11:01:26 +01:00
starkillerOG
d0a58b68e8
Bump reolink-aio to 0.11.1 (#130600) 2024-11-14 10:48:25 +01:00
Simone Chemelli
93f79be2f4
Update uptime deviation for Vodafone Station (#130571)
Update sensor.py
2024-11-14 10:35:03 +01:00
Robert Resch
46cfe6aa32
Refactor camera WebRTC tests (#130581) 2024-11-14 10:28:04 +01:00
Robert Resch
301043ec38
Add require_webrtc_support decorator (#130519) 2024-11-14 10:27:45 +01:00
puddly
245fc246d8
Ensure ZHA setup works with container installs (#130470) 2024-11-14 10:13:29 +01:00
Noah Husby
58fd917cb7
Disable brightness from devices with no display in Cambridge Audio (#130369) 2024-11-14 10:11:44 +01:00
Steven B.
2c1d1f5777
Do not trigger events for updated ring events (#130430) 2024-11-14 10:09:58 +01:00
Luke Lashley
938b1eca22
Fix when the Roborock map is being provisioned (#130574) 2024-11-14 09:52:28 +01:00
Brett Adams
2fda4c82de
Force login prompt in Tesla Fleet (#130576) 2024-11-14 09:46:24 +01:00
J. Nick Koston
4200913d03
Fix non-thread-safe operation in powerview number (#130557) 2024-11-14 09:45:08 +01:00
Tony
4aad614497
Bump aioruckus to 0.42 (#130487) 2024-11-14 09:43:59 +01:00
epenet
6a3b4a6a23
Adjust minimum scapy version to 2.6.1 (#130565) 2024-11-13 17:49:39 -06:00
Michael Hansen
51c6ee97b1
Upgrade to hassil 2.0 (#130544)
* Working on hassil 2.0

* Bump to hassil 2.0

* Update snapshots

* Remove debug logging
2024-11-13 16:50:08 -05:00
Simon Lamon
4002bc3c25
Downgrade devcontainer to Python 3.12 again (#130562) 2024-11-13 22:03:34 +01:00
J. Nick Koston
c35ef6bda3
Bump aiohttp to 3.11.0 (#130542) 2024-11-13 19:32:14 +01:00
Marc Mueller
ed5560aec2
Update base image to Python 3.13 and deprecated 3.12 (#130425) 2024-11-13 19:28:53 +01:00
Sheldon Ip
0a5a2de78e
Fix translations in subaru (#130486) 2024-11-13 18:46:52 +01:00
Brig Lamoreaux
7fd337d67f
fix translation in srp_energy (#130540) 2024-11-13 18:42:26 +01:00
Marc Mueller
5f68d405b2
Update huum to 0.7.12 (#130527) 2024-11-13 17:26:27 +01:00
Erik Montnemery
093b16c723
Make WS command backup/generate send events (#130524)
* Make WS command backup/generate send events

* Update backup.create service
2024-11-13 16:16:49 +01:00
Steven B.
ac4cb52dbb
Bump ring-doorbell to 0.9.12 (#130419) 2024-11-13 08:04:23 -06:00
dunnmj
72b976f832
Add Sky remote integration (#124507)
Co-authored-by: Kyle Cooke <saty9@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-13 14:29:04 +01:00
Daniel Hjelseth Høyer
f6bc5f050e
Bump millheater to 0.12.2 (#130454) 2024-11-13 14:28:19 +01:00
epenet
8300afc00d
Improve type hints in fritz config flow (#130511)
* Improve type hints in fritz config flow

* Improve coverage

* Apply suggestions from code review

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>

---------

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
2024-11-13 13:45:52 +01:00
epenet
ab11b84678
Improve type hints in fritzbox config flow (#130509) 2024-11-13 13:01:54 +01:00
Joost Lekkerkerker
b78453b85b
Bump aiowithings to 3.1.3 (#130504) 2024-11-13 12:21:15 +01:00
Joost Lekkerkerker
b270e4556c
Avoid core manifest to have an issue tracker (#130514) 2024-11-13 12:16:07 +01:00
Joost Lekkerkerker
e90893e2bc
Fix Music Assistant manifest (#130515) 2024-11-13 11:43:31 +01:00
dependabot[bot]
a06e7e31b9
Bump github/codeql-action from 3.27.1 to 3.27.3 (#130489)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.1 to 3.27.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3.27.1...v3.27.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-13 11:06:38 +01:00
Robert Resch
2eaaadd736
Add go2rtc recommended version (#130508) 2024-11-13 11:01:05 +01:00
G Johansson
0ac00ef092
Fix legacy _attr_state handling in AlarmControlPanel (#130479) 2024-11-13 10:55:28 +01:00
Robert Resch
3092297979
Bump go2rtc-client to 0.1.1 (#130498) 2024-11-13 09:55:52 +01:00
Thomas55555
827875473b
Fix RecursionError in Husqvarna Automower coordinator (#123085)
* reach maximum recursion depth exceeded in tests

* second background task

* Update homeassistant/components/husqvarna_automower/coordinator.py

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

* Update homeassistant/components/husqvarna_automower/coordinator.py

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

* test

* modify test

* tests

* use correct exception

* reset mock

* use recursion_limit

* remove unneeded ticks

* test TimeoutException

* set lower recursionlimit

* remove not that important comment and move the other

* test that we connect and listen successfully

* Simulate hass shutting down

* skip testing against the recursion limit

* Update homeassistant/components/husqvarna_automower/coordinator.py

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

* mock

* Remove comment

* Revert "mock"

This reverts commit e8ddaea3d7.

* Move patch to decorator

* Make execution of patched methods predictable

* Parametrize test, make mocked start_listening block

* Apply suggestions from code review

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Erik <erik@montnemery.com>
2024-11-13 09:54:37 +01:00
Joost Lekkerkerker
5cce369ce8
Bump aiowithings to 3.1.2 (#130469) 2024-11-13 07:55:33 +01:00
Joost Lekkerkerker
fdb773c921
Add title to water heater component (#130446) 2024-11-13 07:55:13 +01:00
starkillerOG
8b505a2273
Bump reolink_aio to 0.11.0 (#130481) 2024-11-13 07:35:51 +01:00
Charles Garwood
a9f468509b
Bump zwave-js-server-python to 0.59.1 (#130468) 2024-11-13 07:14:39 +01:00
J. Nick Koston
4ff8b8015c
Bump aiohttp to 3.11.0rc2 (#130484) 2024-11-12 22:07:26 -06:00
mrspouse
5c52e865a0
Correct spelling of BloodGlucoseConcentrationConverter (#130449)
* Correct spelling of BloodGlucoseConcentrationConverter

* Correct spelling of BloodGlucoseConcentrationConverter
2024-11-12 21:48:42 +01:00
Kelvin Dekker
6bfc0cbb0c
Fix typo in file strings (#130465) 2024-11-12 21:33:52 +01:00
G Johansson
388473ecd7
Add diagnostics to Nord Pool (#130461) 2024-11-12 19:55:27 +01:00
G Johansson
285468d85f
Fix translation in statistics (#130455)
* Fix translation in statistics

* Update homeassistant/components/statistics/strings.json
2024-11-12 18:44:32 +01:00
epenet
167025a18c
Simplify modern_forms config flow (#130441)
* Simplify modern_forms config flow

* Rename variable

* Drop CONF_NAME
2024-11-12 18:03:37 +01:00
Joakim Sørensen
ac0c75a598
Add upload capability to the backup integration (#128546)
* Add upload capability to the backup integration

* Limit context switch

* rename

* coverage for http

* Test receiving a backup file

* Update test_manager.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-11-12 15:27:53 +01:00
Robert Resch
cb9cc0f801
Go2rtc bump and set ffmpeg logs to debug (#130371) 2024-11-12 11:53:14 +01:00
Lennard Beers
7758d8ba48
Add switch platform to eq3btsmart (#130363) 2024-11-12 11:42:25 +01:00
epenet
7045b776b6
Use report_usage in helpers (#130365) 2024-11-12 09:25:13 +01:00
J. Nick Koston
22aed92461
Bump aiohttp to 3.11.0rc1 (#130320) 2024-11-12 08:29:01 +01:00
LG-ThinQ-Integration
60bf0f6b06
Fix fan's warning TURN_ON, TURN_OFF (#130327)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2024-11-12 08:26:28 +01:00
G Johansson
3eab72b2aa
Improve exception handling in Nord Pool (#130386)
* Improve exception handling in Nord Pool

* Improve auth string

* Remove auth
2024-11-11 23:02:48 +01:00
Daniel Hjelseth Høyer
d1c3e1caa9
Bump Tibber 0.30.8 (#130388) 2024-11-11 21:05:52 +01:00
Sid
8b547551e2
Bump ruff to 0.7.3 (#130390) 2024-11-11 21:05:41 +01:00
epenet
f1ce7ee8ce
Adjust logging for OptionsFlow deprecation (#130360) 2024-11-11 21:02:09 +01:00
J. Nick Koston
e388e9f396
Fix missing title placeholders in powerwall reauth (#130389) 2024-11-11 20:48:49 +01:00
Markus Lanthaler
96c12fdd10
Update tuya-device-sharing-sdk to version 0.2.1 (#130333) 2024-11-11 20:40:37 +01:00
Noah Husby
e97a5f927c
Bump aiorussound to 4.1.0 (#130382) 2024-11-11 20:26:45 +01:00
epenet
313309a7e0
Remove deprecated YAML loaders (#130364) 2024-11-11 20:24:51 +01:00
Barry vd. Heuvel
ebe62501d6
Bump Weheat wh-python to 2024.11.02 (#130337) 2024-11-11 20:14:12 +01:00
Robert Resch
c54369fe93
Add go2rtc to devcontainer (#130380) 2024-11-11 20:13:20 +01:00
Marc Mueller
c89bf6a9aa
Update pillow to 11.0.0 (#130194) 2024-11-11 20:12:32 +01:00
epenet
906bdda6fa
Use report_usage in integrations (#130366) 2024-11-11 20:09:26 +01:00
Andre Lengwenus
f3708549f0
Code cleanup for LCN integration (#130385) 2024-11-11 20:08:38 +01:00
Andre Lengwenus
3f34ddd74f
Bump lcn-frontend to 0.2.2 (#130383) 2024-11-11 20:07:12 +01:00
Marc Mueller
b19c44b4a5
Update pydantic to 1.10.19 (#130373) 2024-11-11 12:01:47 -06:00
Erik Montnemery
0cc50bc7bc
Fix copy-paste error in STATISTIC_UNIT_TO_UNIT_CONVERTER (#130375) 2024-11-11 11:09:06 -06:00
Joost Lekkerkerker
e56dec2c8e
Bump spotifyaio to 0.8.8 (#130372) 2024-11-11 17:35:54 +01:00
Olivier Corradi
e797149a16
Rename "CO2 Signal" display name to Electricity Maps for consistency (#130242)
* Update strings.json for Electricity Maps

* Update strings.json

* Update config_flow.py

* Update test_config_flow.py

* Fix test
2024-11-11 17:34:29 +01:00
Simon Lamon
c96f1c87a6
Bump python-linkplay to 0.0.20 (#130348) 2024-11-11 17:30:27 +01:00
Erik Elkins
388c5807ea
Add Switchbot Hub 2, Switchbot Meter Pro and Switchbot Meter Pro (CO2) devices to Switchbot Cloud integration. (#130295) 2024-11-11 16:10:52 +01:00
Robert Resch
41c6eeedca
Bump deebot-client to 8.4.1 (#130357) 2024-11-11 15:41:18 +01:00
Lennard Beers
829632b0af
Add binary sensor platform to eq3btsmart (#130352) 2024-11-11 14:27:52 +01:00
Erik Montnemery
5293fc73d8
Sort some code in cloud preferences (#130345)
Sort some code in cloud prefs
2024-11-11 13:21:16 +01:00
Simon Lamon
870bf388e0
Add seek support to LinkPlay (#130349) 2024-11-11 12:49:56 +01:00
Simon Lamon
7a4dac1eb1
Add Spotify and Tidal to playingmode mapping (#130351) 2024-11-11 12:46:02 +01:00
Erik Montnemery
88480d154a
Fix typo in BaseBackupManager.async_restore_backup (#130329) 2024-11-11 12:10:49 +01:00
Lennard Beers
5497c440d9
Prepare eq3btsmart base entity for additional platforms (#130340) 2024-11-11 11:46:11 +01:00
Lennard Beers
1e26cf13d6
Use runtime data for eq3btsmart (#130334) 2024-11-11 10:59:50 +01:00
Nerdix
0dd208a4b9
Add alarm count sensor for Kostal Inverters (#130324) 2024-11-11 09:07:47 +01:00
dependabot[bot]
c3492bc0ed
Bump github/codeql-action from 3.27.0 to 3.27.1 (#130323) 2024-11-11 08:14:42 +01:00
G Johansson
85bf8d1374
Fix Homekit error handling alarm state unknown or unavailable (#130311) 2024-11-10 22:40:23 +00:00
Jan Bouwhuis
e040eb0ff2
Remove extra state attributes from some QNAP sensors (#130310) 2024-11-10 22:26:00 +01:00
Max Shcherbina
d7f41ff8a9
Update generic thermostat strings for clarity and accuracy (#130243) 2024-11-10 22:13:38 +01:00
Jan Bouwhuis
de5437f61e
Remove YAML warning for thethingsnetwork after warning for 6 months (#130307) 2024-11-10 22:12:31 +01:00
Jan Bouwhuis
c52a893e21
Remove YAML import from lcl integration after 6 months deprecation (#130305) 2024-11-10 21:10:18 +01:00
Joost Lekkerkerker
f7f1830b7e
Add support for binary sensor states in Google Assistant (#127652) 2024-11-10 20:34:24 +01:00
Simon Lamon
784ad20fb6
Add diagnostics to LinkPlay (#126768) 2024-11-10 20:31:40 +01:00
Richard Cox
0468e7e7a3
Update Sonarr config flow to standardize ports (#127625)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-11-10 20:23:23 +01:00
dotvav
88c227681d
Bump pypalazzetti to 0.1.11 (#130293) 2024-11-10 20:13:31 +01:00
Lennard Beers
3a37ff13a6
Bump eq3btsmart to 1.2.1 (#130297) 2024-11-10 20:12:46 +01:00
Simone Chemelli
73929e6791
Avoid Shelly data update during shutdown (#130301) 2024-11-10 20:11:42 +01:00
Manu
980b0fa5e6
Deprecate api_call action in Habitica integration (#128119) 2024-11-10 19:37:41 +01:00
Tsvi Mostovicz
fbc4a87166
Remove Jewish Calendar config flow upgrade (#129612) 2024-11-10 19:35:01 +01:00
Allen Porter
7f9ec2a79e
Ignore WebRTC candidates for nest cameras (#130294) 2024-11-10 19:27:40 +01:00
Jan Bouwhuis
d8b55d39e4
Remove tibber legacy notify service after 6 months of deprecation (#130292) 2024-11-10 19:27:11 +01:00
Jan Bouwhuis
ee41725b53
Remove jewish_calendar yaml support after 6 months of deprecation (#130291) 2024-11-10 16:51:08 +01:00
J. Diego Rodríguez Royo
ae1203336d
Add links to deprecation issue message for Home Connect Binary door (#129779) 2024-11-10 16:37:53 +01:00
Michael
f10063c9be
Fix translation key for done response in conversation (#130247) 2024-11-10 16:28:58 +01:00
Åke Strandberg
1da4579a09
Add more f-series models to myuplink (#130283) 2024-11-10 15:46:50 +01:00
Jan Bouwhuis
7fd9339ad8
Remove unused file CONFIG_SCHEMA (#130287) 2024-11-10 15:34:08 +01:00
Jan Bouwhuis
de391fa98b
Remove geniushub yaml support after 6 months of deprecation (#130285)
* Remove geniushub YAML import after 6 moths of deprecation

* Update homeassistant/components/geniushub/__init__.py

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-10 14:58:44 +01:00
J. Nick Koston
70211ab78e
Bump aiohttp to 3.11.0rc0 (#130284) 2024-11-10 13:45:46 +00:00
Nicholas Romyn
a1a08f7755
Ecobee aux cutover threshold (#129474)
* removing extra blank space

* Adding EcobeeAuxCutoverThreshold

First pass.

* minor reorg and changes; testing local check-in

* Adding entity, setting device class and name

* Bumping max value slightly to hopefully accomodate celsius, setting numberMode=box

* fixing the entity name for aux cutover threshold

* Combined async_add_entities

* Using a list comprehension

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

* fixing stuff with listcomprehension

* exchanging call to list.append() to extend with list comprehension

* Updating the class name and the entity name to match the device UI.
Removing abbreviations from entity names

* Fixing tests to match new entity names

* respecting 88 column limit

* Formatting

* Adding test coverage for update/set compressorMinTemp values

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-10 14:13:01 +01:00
G Johansson
433321136d
Remove incorrect mark fixture in nordpool (#130278) 2024-11-10 12:28:18 +01:00
Manu
0677bba5bd
Add actions for scoring habits and rewards in Habitica (#129605) 2024-11-10 12:26:07 +01:00
G Johansson
d0ad834d93
Move manual trigger entity tests (#130134) 2024-11-10 12:14:13 +01:00
Simon Lamon
7d2d6a82b0
Allow dynamic max preset in linkplay play preset (#130160) 2024-11-10 12:02:55 +01:00
Allen Porter
e8dc62411a
Improve nest camera stream expiration to be defensive against errors (#130265) 2024-11-10 12:01:59 +01:00
G Johansson
7925007ab4
Bump psutil to 6.1.0 (#130254) 2024-11-10 12:00:45 +01:00
dotvav
7515deddab
Palazzetti DHCP Discovery (#129731)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-11-10 11:48:52 +01:00
Marc Mueller
e382f924e6
Add support for Python 3.13 (#129442) 2024-11-10 11:38:56 +01:00
Max Shcherbina
7fdcb98518
Update description for generic hygrostat description (#130244) 2024-11-10 11:25:32 +01:00
Noah Husby
d0dbca41f7
Support additional media player states for Russound RIO (#130261) 2024-11-10 11:20:55 +01:00
G Johansson
f3229c723c
Bump pynordpool to 0.2.2 (#130257) 2024-11-10 11:19:10 +01:00
J. Nick Koston
cafa598fd6
Bump aiohttp to 3.11.0b5 (#130264) 2024-11-10 11:18:12 +01:00
Allen Porter
73a62a09b0
Update nest tests to unload config entries to perform clean teardown (#130266) 2024-11-10 09:54:52 +01:00
Lothar Bach
ecd8dde347
Fix path to tesla fleet key file in config folder (#130124)
* Tesla Fleet load key file from config folder

* Fix test

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2024-11-09 23:21:29 +01:00
Marc Mueller
31a2bb1b98
Fix flaky modbus tests (#130252) 2024-11-09 22:58:16 +01:00
Max Shcherbina
0fc019305e
Fix typo in reminder date language string in Todoist integration (#130241) 2024-11-09 21:38:29 +01:00
Marc Mueller
adb1c59859
Update grpcio to 1.67.1 (#130240) 2024-11-09 21:37:56 +01:00
Manu
5d0277a0d1
Add actions for quest handling to Habitica (#129650) 2024-11-09 19:34:25 +01:00
Allen Porter
21d81d5a5c
Bump google-nest-sdm to 6.1.5 (#130229) 2024-11-09 19:02:15 +01:00
DeerMaximum
0de4bfcc2c
Add missing translation string for NINA (#129826) 2024-11-09 18:33:28 +01:00
jjlawren
2cc5486794
Bump SoCo to 0.30.6 (#130223) 2024-11-09 17:14:40 +01:00
Noah Husby
e3315383ab
Improve entity test coverage for Russound RIO (#129828) 2024-11-09 17:13:57 +01:00
Markus Jacobsen
31b505828b
Simplify Bang & Olufsen source determination (#130072) 2024-11-09 17:13:07 +01:00
Daniel Oltmanns
b61580a937
Add fan preset mode icons and strings to vesync (#129584) 2024-11-09 16:48:00 +01:00
Markus Jacobsen
928e5348e4
Add custom integration action sections support to hassfest (#130148) 2024-11-09 16:47:02 +01:00
Josef Zweck
622682eb43
Change update after button press for lamarzocco (#129616) 2024-11-09 16:42:10 +01:00
Simon Lamon
97fa568876
No longer thrown an error when device is offline in linkplay (#130161) 2024-11-09 16:11:34 +01:00
Manu
c10f078f2a
Add sensors for attribute points (str, int, per, con) to Habitica (#130186) 2024-11-09 16:04:10 +01:00
Simone Chemelli
e6d16f06fc
Fix uptime sensor for Vodafone Station (#130215) 2024-11-09 15:55:39 +01:00
Daniel Hjelseth Høyer
c89ab7a142
Bump pyTibber (#130216) 2024-11-09 15:54:58 +01:00
Jan Bouwhuis
6837ea947c
Cleanup yaml import and legacy file notify service (#130219) 2024-11-09 15:54:18 +01:00
Marco
5f0f29704b
Add smarty reset filters timer button (#129637) 2024-11-09 13:32:00 +01:00
Manu
1f43dc6676
Fix cast skill test in Habitica (#130213) 2024-11-09 13:12:04 +01:00
Marc Mueller
4d7405de2c
Install zlib-dev for pillow wheel build (#130211) 2024-11-09 13:03:26 +01:00
Max Shcherbina
4adffdd1a6
Fix wording in Google Calendar create_event strings for consistency (#130183) 2024-11-09 13:01:59 +01:00
Manu
4e2f5bdb7d
Add tests for cast skill action in Habitica (#129596) 2024-11-09 12:45:50 +01:00
starkillerOG
03bc711c51
Add Reolink chime vehicle tone (#129835) 2024-11-09 12:25:06 +01:00
Marc Mueller
8b8e949bdf
Update wheel builder to 2024.11.0 (#130209) 2024-11-09 12:07:20 +01:00
Erik Montnemery
69ba0d3a50
Report update_percentage in ezviz update entity (#129377) 2024-11-09 11:35:18 +01:00
epenet
25fb70f281
Add blood glucose concentration device class (#129340) 2024-11-09 11:29:24 +01:00
Tom Gamull
0304588bb8
Fix missing unit of measurement for blink wifi strength (#128409) 2024-11-09 11:19:36 +01:00
Josef Zweck
08f5081197
Rename lamarzocco library (#130204) 2024-11-09 11:03:48 +01:00
jb101010-2
701f35488c
Add water price sensor to suez water (#130141)
* Suez water: add water price sensor

* sensor description

* clean up
2024-11-09 10:57:22 +01:00
G Johansson
d11012b2b7
Move check thresholds valid to platform schema in threshold (#129540) 2024-11-09 10:50:11 +01:00
Josef Zweck
8384100e1b
Rename tedee library (#130203) 2024-11-09 10:46:38 +01:00
Tristan Bastian
cd0349ee4d
Bump tplink-omada-client to 1.4.3 (#130184) 2024-11-09 10:41:08 +01:00
Marc Mueller
b413e481cb
Update numpy to 2.1.3 (#130191) 2024-11-09 10:12:52 +01:00
Diogo Gomes
9f7e6048f8
Code quality improvements on utility_meter (#129918)
* clean

* update snapshot

* move name, native_value and native_unit_of_measurement to _attr's

* Apply suggestions from code review

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

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-09 00:17:43 +01:00
IceBotYT
2802b77f21
Bump nice-go to 0.3.10 (#130173)
Bump Nice G.O. to 0.3.10
2024-11-09 00:12:14 +01:00
J. Nick Koston
964ad43a27
Bump orjson to 3.10.11 (#130182) 2024-11-09 00:07:05 +01:00
TheJulianJES
182be6e0ea
Fix failing UniFi Protect tests on some systems (#129516) 2024-11-08 23:10:29 +01:00
Jakob Schlyter
cd11f01ace
Add support for MW/GW/TW and GWh/TWh (#130089) 2024-11-08 22:12:16 +01:00
G Johansson
742eca5927
Use TemplateStateFromEntityId in Template trigger entity (#130136) 2024-11-08 22:09:43 +01:00
murfy76
48e7fed901
Add voc and formaldehyde to Tuya CO2 Detector (#130119) 2024-11-08 22:03:01 +01:00
Marc Mueller
0a4c0fe7cc
Add option to specify additional markers for wheel build requirements (#129949) 2024-11-08 21:09:53 +01:00
Jan Bouwhuis
9037cb8a7d
Fix typo in go2rtc (#130165)
Fix typo in original
2024-11-08 20:38:38 +01:00
Jan Bouwhuis
c97cc34879
Use f-strings in go2rtc code and test and do not use abbreviation (#130158) 2024-11-08 20:16:46 +01:00
Sheldon Ip
1ac9217630
Fix translations in ollama (#130164) 2024-11-08 20:15:17 +01:00
Simon Lamon
e4036a2f14
Bump python-linkplay to v0.0.18 (#130159) 2024-11-08 20:14:33 +01:00
G Johansson
da9c73a767
Add reconfigure flow to Nord Pool (#130151) 2024-11-08 19:53:52 +01:00
Diogo Gomes
e4aaaf10c3
Fix utility_meter on DST changes (#129862)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-08 18:44:15 +01:00
Louis Christ
a7be76ba0a
Fix volume_up not working in some cases in bluesound integration (#130146) 2024-11-08 18:40:43 +01:00
Allen Porter
f7cc91903c
Fix bugs in nest stream expiration handling (#130150) 2024-11-08 18:37:00 +01:00
Jan Bouwhuis
4a8a674bd3
Refrase imap fetch service description string (#130152) 2024-11-08 18:36:19 +01:00
Robert Resch
a8db25fbd8
Split test doesn't need to be executed per Python version (#130147) 2024-11-08 18:05:05 +01:00
Klaas Schoute
2dc81ed866
Force int value on port in P1Monitor (#130084) 2024-11-08 16:15:57 +01:00
Shai Ungar
c4762f3ff4
Fix issue when timestamp is None (#130133)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-08 16:15:28 +01:00
Martin Hjelmare
14285973b8
Bump ha-ffmpeg to 3.2.2 (#130142) 2024-11-08 16:00:24 +01:00
epenet
353ccf3ea7
Only apply OptionsFlowWithConfigEntry deprecation to core (#130054)
* Only apply OptionsFlowWithConfigEntry deprecation to core

* Fix match string in pytest.raises

* Improve coverage
2024-11-08 15:55:19 +01:00
Lektri.co
6b90d8ff1a
Add binary sensor platform to the Lektrico integration (#129872) 2024-11-08 15:54:46 +01:00
Robert Resch
51e691f832
Add go2rtc workaround for HA managed one until upstream fixes it (#130139) 2024-11-08 15:54:14 +01:00
Joost Lekkerkerker
6c7ac7a6ef
Bump spotifyaio to 0.8.7 (#130140) 2024-11-08 15:53:26 +01:00
Bram Kragten
52ed1bf44a
Update frontend to 20241106.2 (#130128) 2024-11-08 15:13:05 +01:00
Petar Petrov
3eab0b704e
Get/Set custom config parameter for zwave_js node (#129332)
* Get/Set custom config parameter for zwave_js node

* add tests

* handle errors on set

* test FailedCommand
2024-11-08 15:12:18 +01:00
G Johansson
1f32e02ba2
Add Nord Pool integration (#129983) 2024-11-08 15:10:51 +01:00
epenet
074418f8f7
Drop OptionsFlowWithConfigEntry usage in homeassistant_hardware (#130078)
* Drop OptionsFlowWithConfigEntry usage in homeassistant_hardware

* Add homeassistant_hardware as other components rely on it

* Maybe core_files not needed after all
2024-11-08 14:53:46 +01:00
Martin Hjelmare
b711b17193
Remove Z-Wave incorrect lock service descriptions (#130034) 2024-11-08 14:50:41 +01:00
Steven B.
03c3d09583
Enable overriding connection port for tplink devices (#129619)
Enable setting a port override during manual config entry setup.

The feature will be undocumented as it's quite a specialized use case generally used for testing purposes.
2024-11-08 14:41:00 +01:00
Robert Resch
f49547d598
Bump uv to 0.5.0 (#130127) 2024-11-08 14:19:46 +01:00
jb101010-2
7678be8e2b
Suez water: simplify config flow (#130083)
Simplify config flow for suez water. Counter_id can now be automatically be fetched by the integration.
The value is provided only in the source code of suez website and therefore not easily accessible to user not familiar with devlopment.
Still possible to explicitly set the value for user with multiple value or value defined elsewhere.
2024-11-08 14:01:36 +01:00
epenet
7672215095
Trigger full CI run on homeassistant_hardware integration changes (#130129)
Add components/homeassistant_hardware to core files
2024-11-08 13:46:40 +01:00
epenet
18cf96b92b
Bring emoncms coverage to 100% (#130092)
Remove mock_setup_entry from emoncms OptionsFlow test
2024-11-08 13:42:19 +01:00
epenet
94d597fd41
Add checks for flow title/description placeholders (#129140)
* Add checks for title placeholders

* Check both title and description

* Improve comment
2024-11-08 13:33:19 +01:00
Alexandre CUER
24b47b50ea
Migrate from entry unique id to emoncms unique id (#129133)
* Migrate from entry unique id to emoncms unique id

* Use a placeholder for the documentation URL

* Use async_set_unique_id in config_flow

* use _abort_if_unique_id_configured in config_flow

* Avoid single-use variable

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

* Add async_migrate_entry

* Remove commented code

* Downgrade version if user add server without uuid

* Improve code quality

* Move code migrating HA to emoncms uuid to init

* Fit doc url in less than 88 chars

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

* Improve code quality

* Only update unique_id with async_update_entry

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

* Make emoncms_client compulsory to get_feed_list

* Improve readability with unique id functions

* Rmv test to give more sense to _migrate_unique_id

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-08 13:29:10 +01:00
Markus Jacobsen
e3dfa84d65
Bang & Olufsen add beolink grouping (#113438)
* Add Beolink custom services
Add support for media player grouping via beolink
Give media player entity name

* Fix progress not being set to None as Beolink listener
Revert naming changes

* Update API
simplify Beolink attributes

* Improve beolink custom services

* Fix Beolink expandable source check
Add unexpand return value
Set entity name on initialization

* Handle entity naming as intended

* Fix "null" Beolink self friendly name

* Add regex service input validation
Add all_discovered to beolink_expand service
Improve beolink_expand response

* Add service icons

* Fix merge
Remove unnecessary assignment

* Remove invalid typing
Update response typing for updated API

* Revert to old typed response dict method
Remove mypy ignore line
Fix jid possibly used before assignment

* Re add debugging logging

* Fix coroutine
Fix formatting

* Remove unnecessary update control

* Make tests pass
Fix remote leader media position bug
Improve remote leader BangOlufsenSource comparison

* Fix naming and add callback decorators

* Move regex service check to variable
Suppress KeyError
Update tests

* Re-add hass running check

* Improve comments, naming and type hinting

* Remove old temporary fix

* Convert logged warning to raised exception for invalid media_player
Simplify code using walrus operator

* Fix test for invalid media_player grouping

* Improve method naming

* Improve _beolink_sources explanation

* Improve _beolink_sources explanation

* Fix tests

* Remove service responses
Fix and add tests

* Change service to action where applicable

* Show playback progress for listeners

* Fix testing

* Remove useless initialization

* Fix allstandby name

* Fix various casts with assertions
Fix comment placement
Fix group leader group_members rebase error
Replace entity_id method call with attribute

* Add syrupy snapshots for Beolink tests, checking entity states
Use test JIDs 3 and 4 instead of 2 and 3 to avoid invalid attributes in testing

* Add sections for fields using Beolink JIDs directly

* Fix typo

* FIx rebase mistake

* Sort actions alphabetically
2024-11-08 12:06:29 +01:00
nasWebio
ed1366f463
Add NASweb integration (#98118)
* Add NASweb integration

* Fix DeviceInfo import

* Remove commented out code

* Change class name for uniquness

* Drop CoordinatorEntity inheritance

* Rename class Output to more descriptive: RelaySwitch

* Update required webio-api version

* Implement on-the-fly addition/removal of entities

* Set coordinator name matching device name

* Set entities with too old status as unavailable

* Drop Optional in favor of modern typing

* Fix spelling of a variable

* Rename commons to more fitting name: helper

* Remove redundant code

* Let unload fail when there is no coordinator

* Fix bad docstring

* Rename cord to coordinator for clarity

* Remove default value for pop and let it raise exception

* Drop workaround and use get_url from helper.network

* Use webhook to send data from device

* Deinitialize coordinator when no longer needed

* Use Python formattable string

* Use dataclass to store integration data in hass.data

* Raise ConfigEntryNotReady when appropriate

* Refactor NASwebData class

* Move RelaySwitch to switch.py

* Fix ConfigFlow tests

* Create issues when entry fails to load

* Respond when correctly received status update

* Depend on webhook instead of http

* Create issue when status is not received during entry set up

* Make issue_id unique across integration entries

* Remove unnecessary initializations

* Inherit CoordinatorEntity to avoid code duplication

* Optimize property access via assignment in __init__

* Use preexisting mechanism to fill schema with user input

* Fix translation strings

* Handle unavailable or unreachable internal url

* Implement custom coordinator for push driven data updates

* Move module-specific constants to respective modules

* Fix requirements_all.txt

* Fix CODEOWNERS file

* Raise ConfigEntryError instead of issue creation

* Fix entity registry import

* Use HassKey as key in hass.data

* Use typed ConfigEntry

* Store runtime data in config entry

* Rewrite to be more Pythonic

* Move add/remove of switch entities to switch.py

* Skip unnecessary check

* Remove unnecessary type hints

* Remove unnecessary nonlocal

* Use a more descriptive docstring

* Add docstrings to NASwebCoordinator

* Fix formatting

* Use correct return type

* Fix tests to align with changed code

* Remove commented code

* Use serial number as config entry id

* Catch AbortFlow exception

* Update tests to check ConfigEntry Unique ID

* Remove unnecessary form abort
2024-11-08 12:03:32 +01:00
Josef Zweck
5d5908a03f
Add missing string to tedee plus test (#130081) 2024-11-08 08:47:28 +01:00
Kelvin Dekker
3062bad19e
Fix typo in insteon strings (#130085) 2024-11-08 08:47:02 +01:00
Bram Kragten
28832cbd3e
Update frontend to 20241106.1 (#130086) 2024-11-08 08:46:48 +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
Franck Nijhof
782417528c
Bump version to 2024.11.0b9 2024-11-06 18:25:29 +01:00
Robert Resch
7757423d18
Bump go2rtc-client to 0.1.0 (#129965) 2024-11-06 18:24:12 +01:00
Joost Lekkerkerker
e5a28f4f25
Remove deprecation issues for LCN once entities removed (#129955) 2024-11-06 18:21:32 +01:00
Erik Montnemery
c18d50910f
Call async_refresh_providers when camera entity feature changes (#129941) 2024-11-06 18:21:28 +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
Franck Nijhof
3b840c684b
Bump version to 2024.11.0b8 2024-11-06 15:44:10 +01:00
Bram Kragten
bc84fdc64a
Update frontend to 20241106.0 (#129953) 2024-11-06 15:43:33 +01:00
Robert Resch
401262c23d
Bump go2rtc-client to 0.0.1b5 (#129952) 2024-11-06 15:42:22 +01:00
Manu
795384ca2d
Improve error messages in Habitica (#129948)
Improve error messages
2024-11-06 15:41:44 +01:00
J. Diego Rodríguez Royo
dfc3423c83
Delete binary door deprecation issue on unload at Home Connect (#129947) 2024-11-06 15:41:39 +01:00
Robert Resch
22b5071c26
Bump go2rtc-client to 0.0.1b4 (#129942) 2024-11-06 15:40:30 +01:00
Joost Lekkerkerker
4b9524c5c1
Write squeezebox player state after query (#129939) 2024-11-06 15:39:07 +01:00
Joost Lekkerkerker
9cd46c7f03
Bump spotifyaio to 0.8.5 (#129938) 2024-11-06 15:39:03 +01:00
Robert Resch
232a6868ff
Fix native sync WebRTC offer (#129931) 2024-11-06 15:39:00 +01:00
Kunal Aggarwal
361e0d4fc7
Adding "peaceful" status as on value to Tuya Presence Sensor (#129925) 2024-11-06 15:38:57 +01:00
Paulus Schoutsen
26d8d5343a
Ensure all template names are strings (#129921) 2024-11-06 15:38:53 +01:00
starkillerOG
995aab8347
Bump reolink_aio to 0.10.4 (#129914) 2024-11-06 15:38:50 +01:00
Robert Resch
399011552b
Disable uv cache (#129912) 2024-11-06 15:38:46 +01:00
Markus Jacobsen
0c9f30364c
Update Bang & Olufsen source list as availability changes (#129910) 2024-11-06 15:38:43 +01:00
Louis Christ
bdc17621ee
Map "stop" to MediaPlayerState.IDLE in bluesound integration (#129904)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-11-06 15:38:40 +01:00
Joost Lekkerkerker
399c53a57e
Bump spotifyaio to 0.8.4 (#129899) 2024-11-06 15:38:36 +01:00
Daniel Hjelseth Høyer
f55e13bde4
Bump pyTibber to 0.30.4 (#129844) 2024-11-06 15:38:32 +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
Michael Hansen
48d9df89ac
Bump intents and add HassRespond test (#129830) 2024-11-06 15:36:46 +01:00
kingal123
adf836d9ac
Update pylutron to 0.2.16 (#129653)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-06 15:33:16 +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
Franck Nijhof
211ce43127
Bump version to 2024.11.0b7 2024-11-05 20:33:48 +01:00
G Johansson
f5555df990
Bump holidays to 0.60 (#129909) 2024-11-05 20:33:39 +01:00
Paul Bottein
82c2422990
Update frontend to 20241105.0 (#129906) 2024-11-05 20:33:36 +01:00
Erik Montnemery
734ebc1adb
Improve improv BLE error handling (#129902) 2024-11-05 20:33:33 +01:00
Paulus Schoutsen
eb3371beef
Change Ollama default to llama3.2 (#129901) 2024-11-05 20:33:30 +01:00
Manu
e1ef1063fe
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 20:33:27 +01:00
Diogo Gomes
c355a53485
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 20:33:23 +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
Franck Nijhof
c85eb6bf8e
Bump version to 2024.11.0b6 2024-11-05 16:51:05 +01:00
Joost Lekkerkerker
cc30d34e87
Remove timers from LG ThinQ (#129898) 2024-11-05 16:50:41 +01:00
Erik Montnemery
14875a1101
Map go2rtc log levels to Python log levels (#129894) 2024-11-05 16:50:38 +01:00
Joost Lekkerkerker
030aebb97f
Use default package for yt-dlp (#129886) 2024-11-05 16:50:35 +01:00
Erik Montnemery
6e2f36b6d4
Log go2rtc output with warning level on error (#129882) 2024-11-05 16:50:32 +01:00
Robert Resch
25a05eb156
Append a 1 to all go2rtc ports to avoid port conflicts (#129881) 2024-11-05 16:50:29 +01:00
J. Diego Rodríguez Royo
b71c4377f6
Removed stale translation and improved set_setting translation at Home Connect (#129878) 2024-11-05 16:50:25 +01:00
Michael Arthur
d671341864
Update snapshot for lg thinq (#129856)
update snapshot for lg thinq

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-11-05 16:39:02 +01:00
Mike Degatano
383f712d43
Add repair for add-on boot fail (#129847) 2024-11-05 16:38:59 +01:00
Alex Bush
8a20cd77a0
Bump pyfibaro to 0.8.0 (#129846) 2024-11-05 16:38:56 +01:00
Richard Kroegel
14023644ef
Bump bimmer_connected to 0.16.4 (#129838) 2024-11-05 16:38:53 +01:00
dotvav
496fc42b94
Bump pypalazzetti to 0.1.10 (#129832) 2024-11-05 16:38:50 +01:00
Erik Montnemery
da0688ce8e
Validate go2rtc server version (#129810) 2024-11-05 16:38:47 +01:00
Robert Resch
89d3707cb7
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 16:38:44 +01:00
Kunal Aggarwal
3f5e395e2f
Adding new on values for Tuya Presence Detection Sensor (#129801) 2024-11-05 16:38:41 +01:00
Joost Lekkerkerker
00ea1cab9f
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-05 16:38:37 +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
Paulus Schoutsen
c7b2ffbc8e Bump version to 2024.11.0b5 2024-11-05 03:00:18 +00:00
J. Nick Koston
3a1502e2bb Disable SRTP for unifiprotect RTSPS stream (#129852) 2024-11-05 02:59:23 +00:00
J. Nick Koston
b830f83a34 Bump uiprotect to 6.4.0 (#129851) 2024-11-05 02:59:23 +00:00
J. Nick Koston
2982e733bc Fix unifiprotect supported features being set too late (#129850) 2024-11-05 02:59:22 +00:00
starkillerOG
e89ce215c6 Bump reolink-aio to 0.10.3 (#129841) 2024-11-05 02:59:21 +00:00
G Johansson
b6345f8d07 Fix translations in hydrawise (#129834) 2024-11-05 02:59:20 +00:00
G Johansson
9d261bab48 Fix translation in ovo energy (#129833) 2024-11-05 02:59:19 +00:00
Michael Hansen
b6f875134e Add HassRespond intent (#129755)
* Add HassHello intent

* Rename to HassRespond

* LLM's ignore HassRespond intent
2024-11-05 02:59:18 +00:00
Artur Pragacz
90ceebdf91 Fix source mapping in Onkyo (#129716)
* Fix source mapping

* Fix copy paste
2024-11-05 02:59:18 +00: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
Paulus Schoutsen
03e6a13896 Bump version to 2024.11.0b4 2024-11-04 18:48:58 +00:00
G Johansson
9fb3261f02 Fix translations in landisgyr (#129831) 2024-11-04 18:48:37 +00:00
Bram Kragten
0bc6b8b0d4 Update frontend to 20241104.0 (#129829) 2024-11-04 18:48:36 +00:00
G Johansson
18d2ced045 Fix translations in homeworks (#129824) 2024-11-04 18:48:35 +00:00
Robert Resch
6c75e0bee1 Remove all ice_servers on native sync WebRTC cameras (#129819) 2024-11-04 18:48:35 +00:00
Steven B.
0b981f42bb Bump python-kasa to 0.7.7 (#129817)
Bump tplink dependency python-kasa to 0.7.7
2024-11-04 18:48:34 +00:00
Paulus Schoutsen
82868a8588 Fix ESPHome dashboard check (#129812) 2024-11-04 18:48:33 +00:00
Erik Montnemery
6e93777f54 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 18:47:41 +00:00
Erik Montnemery
9349292464 Fix aborting flows for single config entry integrations (#129805) 2024-11-04 18:43:56 +00:00
Robert Resch
7084b3b52c Update go2rtc stream if stream_source is not matching (#129804) 2024-11-04 18:43:55 +00:00
epenet
0f0f5fd0ab Fix incorrect description placeholders in azure event hub (#129803) 2024-11-04 18:43:54 +00:00
Joost Lekkerkerker
cb0b942db3 Improve error handling in Spotify (#129799) 2024-11-04 18:43:53 +00:00
Erik Montnemery
b1c9f83952 Fix stringification of discovered hassio uuid (#129797) 2024-11-04 18:43:52 +00:00
Joost Lekkerkerker
1ff0efc97b Bump yt-dlp to 2024.11.04 (#129794) 2024-11-04 18:43:51 +00:00
Robert Resch
a4da2a9eb5 Use RTCIceCandidate instead of str for candidate (#129793) 2024-11-04 18:43:51 +00:00
Antoine Reversat
ba3cfb5f87 Bump ayla-iot-unofficial to 1.4.3 (#129743)
Upgrade to ayla-iot-unofficial v1.4.3
2024-11-04 18:43:50 +00:00
Luca Angemi
bf196935f6 Add state class to precipitation_intensity in Aemet (#129670)
Update sensor.py
2024-11-04 18:43:49 +00:00
Joost Lekkerkerker
6e98343706 Update Spotify state after mutation (#129607) 2024-11-04 18:43:48 +00:00
Erik Montnemery
de453ab5c1 Add watchdog to monitor and respawn go2rtc server (#129497) 2024-11-04 18:43:47 +00:00
Andre Lengwenus
f408de4fc3 Bump lcn-frontend to 0.2.1 (#129457) 2024-11-04 18:43:47 +00: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
Bram Kragten
5141a4d292 Bump version to 2024.11.0b3 2024-11-04 09:32:53 +01:00
LG-ThinQ-Integration
cf8b7607ae Bump thinqconnect to 1.0.0 (#129769)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2024-11-04 09:31:43 +01:00
Joost Lekkerkerker
b38fe00387 Bump spotifyaio to 0.8.3 (#129729) 2024-11-04 09:31:42 +01:00
J. Nick Koston
5d446f0e14 Bump HAP-python to 4.9.2 (#129715) 2024-11-04 09:31:41 +01:00
Josef Zweck
a592ece9c8 Add missing translation string to lamarzocco (#129713)
* add missing translation string

* Update strings.json

* import pytest again
2024-11-04 09:31:40 +01:00
Allen Porter
9cb60c61d1 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-04 09:31:39 +01:00
J. Nick Koston
90ed06c354 Bump DoorBirdPy to 3.0.8 (#129709) 2024-11-04 09:31:39 +01:00
Manu
22d64cb8f4 Bump bring-api to 0.9.1 (#129702) 2024-11-04 09:31:38 +01:00
Nathan Spencer
453039e860 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 09:31:37 +01:00
Simon Lamon
e727162225 Bump python-linkplay to 0.0.17 (#129683) 2024-11-04 09:31:36 +01:00
Ståle Storø Hauknes
a898a5996e Bump Airthings BLE to 0.9.2 (#129659)
Bump airthings ble
2024-11-04 09:31:35 +01:00
Jesse Hills
d501bb8d52 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-04 09:31:34 +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
Bram Kragten
5ef45fd12e Bump version to 2024.11.0b2 2024-11-02 20:42:48 +01:00
Klaas Schoute
8a293a41f5 Bump autarco lib to v3.1.0 (#129684)
Bump autarco to v3.1.0
2024-11-02 20:42:44 +01:00
J. Nick Koston
931820a170 Bump sensorpush-ble to 1.7.1 (#129657) 2024-11-02 20:42:44 +01:00
J. Nick Koston
e9944b964a Bump aioesphomeapi to 27.0.1 (#129643) 2024-11-02 20:42:43 +01:00
J. Nick Koston
dbae1d2f8b Bump aiohomekit to 3.2.6 (#129640) 2024-11-02 20:42:42 +01:00
Joost Lekkerkerker
0dc8feba05 Bump spotifyaio to 0.8.2 (#129639) 2024-11-02 20:42:41 +01:00
Robert Resch
5c7c2347f7 Bump webrtc-models to 0.2.0 (#129627) 2024-11-02 20:42:40 +01:00
J. Nick Koston
d069907948 Pin async-timeout to 4.0.3 (#129592) 2024-11-02 20:42:39 +01:00
Erik Montnemery
725ab477a8 Revert "Create a script service schema based on fields" (#129591) 2024-11-02 20:42:38 +01:00
Robert Resch
d05ee9ff60 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-11-02 20:42:36 +01:00
Joost Lekkerkerker
3c1f6d97cc Bump aiowithings to 3.1.1 (#129586) 2024-11-02 20:42:33 +01:00
epenet
5fe827f6c4 Fix flaky camera test (#129576) 2024-11-02 20:42:31 +01:00
Erik Montnemery
76f9a93ed7 Bump aiohasupervisor to version 0.2.1 (#129574) 2024-11-02 20:42:30 +01:00
Joost Lekkerkerker
df2506bfbb Bump spotifyaio to 0.8.1 (#129573) 2024-11-02 20:42:29 +01:00
Joost Lekkerkerker
b25ab04d2c Fix Geniushub setup (#129569) 2024-11-02 20:42:28 +01:00
Steven B.
6f094e8a54 Check for async web offer overrides in camera capabilities (#129519) 2024-11-02 20:42:27 +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
Bram Kragten
41590f91ac Bump version to 2024.11.0b1 2024-10-31 16:38:09 +01:00
Paul Bottein
e9d1f4f46e Update frontend to 20241031.0 (#129583) 2024-10-31 16:36:58 +01:00
epenet
7f287412ba Log type as well as value for unique_id checks (#129575) 2024-10-31 16:36:57 +01:00
Erik Montnemery
2df094de2b Stringify discovered hassio uuid (#129572)
* Stringify discovered hassio uuid

* Correct DiscoveryKey

* Adjust tests
2024-10-31 16:36:56 +01:00
starkillerOG
964ab5b351 Log Reolink select value KeyError only once (#129559) 2024-10-31 16:36:55 +01:00
Brett Adams
3f6e9a54fe 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 16:36:55 +01:00
J. Nick Koston
4ec5d5ae1e Bump yarl to 1.17.1 (#129539)
changelog: https://github.com/aio-libs/yarl/compare/v1.17.0...v1.17.1
2024-10-31 16:36:54 +01:00
Erik Montnemery
c49b155c29 Allow importing homeassistant.core.Config until 2025.11 (#129537) 2024-10-31 16:36:53 +01:00
Luca Angemi
fc602b1888 Fix bthome UnitOfConductivity (#129535)
Fix unit
2024-10-31 16:36:52 +01:00
G Johansson
81421992a2 Missing config_flow in manifest for local_file (#129529) 2024-10-31 16:36:51 +01:00
starkillerOG
4ef31f9331 Bump reolink_aio to 0.10.2 (#129528) 2024-10-31 16:36:50 +01:00
G Johansson
d7e304badf 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-31 16:36:49 +01:00
cryptk
bf3f1b4b49 Bump uiprotect to 6.3.2 (#129513) 2024-10-31 16:36:49 +01:00
Jan Bouwhuis
2ac0ff03fc Fix current temperature calculation for incomfort boiler (#129496) 2024-10-31 16:36:48 +01:00
Aurore
d10553d624 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-31 16:36:47 +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
Marcel van der Veldt
4dc2433e8b
Revert "Add musicassistant integration (#128919)" (#129565)
This reverts commit 568bdef61f.
2024-10-31 12:18:10 +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
Bram Kragten
60c93456c0 Merge branch 'dev' into rc 2024-10-30 18:33:24 +01:00
G Johansson
a4f210379d
Raise on non-string unique id for config entry (#125950)
* Raise on non-string unique id for config entry

* Add test update entry

* Fix breaking

* Add check get_entry_by_domain_and_unique_id

* Naming

* Add test

* Fix logic

* No unique id

* Fix tests

* Fixes

* Fix gardena

* Not related to this PR

* Update docstring and comment

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-10-30 18:09:50 +01:00
Bram Kragten
27e6205a37 Merge branch 'dev' into rc 2024-10-30 17:41:05 +01:00
G Johansson
3db6d82904
Add name to description placeholders automatically for reauth flows (#129232)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-10-30 17:38:59 +01:00
puddly
b8ddfd642e
Bump ZHA dependencies (#129510) 2024-10-30 17:38:24 +01:00
Bram Kragten
c98acd42db Bump version to 2024.11.0b0 2024-10-30 17:34:45 +01:00
Paul Bottein
39f418f2d2
Update frontend to 20241030.0 (#129508) 2024-10-30 17:31:41 +01:00
Jan Bouwhuis
9fbd484dfe
Add progress support to MQTT update platform (#129468)
* Add progress support to MQTT update platform and add validation on state updates

* Clean up cast to type class

* Add support for display_precision attribute
2024-10-30 17:22:55 +01:00
Jan Bouwhuis
1773f2aadc
Allow MQTT device based auto discovery (#118757)
* Allow MQTT device based auto discovery

* Fix merge error

* Remove unused import

* Fix discovery device based topics

* Fix cannot delete twice

* Improve cleanup test

* Follow up comment

* Typo

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Explain more

* Use tuple

* Default a device payload to have priority over a platform based payload

* Add unique_id to sensor test data

* Set migration flag to mark a discovery topic for migration

* Correct type hint

* Make unique_id required for components in device based discovery payload

* Remove CONF_MIGRATE_DISCOVERY from platform schema

* Unload discovered MQTT item to allow migration

* Follow up comments from code review

* ruff

* Subscribe to platform discovery wildcards first

* Use normal dict

* Use dict to persist wildcard subscription order

* Remove missed unused parameter

* Add a comment to explain we use a dict  to preserve the subscription order

* Add wildcard subscription order test

* Remove discovery flag from test

* Improve discovery migration origin logging

* Assert initial  wildcard discovery topics subscription order and after reconnect

* Improve log messages

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2024-10-30 17:10:15 +01:00
Michael Hansen
cb1b72d6ba
Bump intents to 2024.10.30 (#129505) 2024-10-30 16:20:59 +01:00
Manu
f5a2ec961d
Remove unused snapshots from Habitica (#129499) 2024-10-30 15:44:21 +01:00
Krisjanis Lejejs
bf40e77d65
Add Stun server with port 3478 (#129501) 2024-10-30 15:40:23 +01:00
Jozef Kruszynski
568bdef61f
Add musicassistant integration (#128919)
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
2024-10-30 14:57:01 +01:00
Manu
2303521778
Use common translation strings for Habitica (#129498) 2024-10-30 14:56:47 +01:00
Josef Zweck
3bf2946d13
Change type of the config_entry in coordinator in tedee (#129502) 2024-10-30 14:53:11 +01:00
Josef Zweck
484e5cb3e8
Explicitly pass config_entry to coordinator in lamarzocco (#129434)
* Update __init__.py

* Update coordinator.py

* Update coordinator.py

* ruff

* Update coordinator.py

* move type to coordinator
2024-10-30 14:43:41 +01:00
Josef Zweck
fbe8b6c34d
Pass config_entry explicitly to coordinator in tedee (#129432)
* pass entry

* pass entry

* Update coordinator.py

* move type definition
2024-10-30 14:42:19 +01:00
Jan Bouwhuis
4e7397dc9d
Test discovery subscriptions not done when discovery is disabled (#129458)
Test discovery subscriptions not performend when discovery is disabled
2024-10-30 14:38:44 +01:00
starkillerOG
a6189106e1
Reolink add TCP push event connection as primary method (#129490) 2024-10-30 14:34:32 +01:00
Artur Pragacz
ed6123a3e6
Add reconfigure step to Onkyo config flow (#129088) 2024-10-30 14:31:43 +01:00
Noah Husby
0cd5deaa3f
Add audio output select to Cambridge Audio (#129366) 2024-10-30 14:28:01 +01:00
Allen Porter
6c047e2678
Refresh Nest WebRTC streams before expiration (#129478) 2024-10-30 14:25:43 +01:00
Martin Hjelmare
405a480cae
Create repair issue for legacy webrtc provider (#129334)
* Add repair issue

* Add tests

* Add option to not use builtin go2rtc provider

* Add test

* Add domain to new providers

* Add learn more url

* Update placeholder

* Promote the builtin provider

* Refactor provider storage

* Move check for legacy provider conflict to refresh

* Test provider registration race

* Add test for registering the same legacy provider twice

* Test test_get_not_supported_legacy_provider

* Remove blank line between bullets

* Call it built-in

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

* Revert "Add option to not use builtin go2rtc provider"

This reverts commit 4e31bad6c0c23d5a1c0935c985351808a46163d6.

* Revert "Add test"

This reverts commit ddf85fd4db2c78b15c1cdc716804b965f3a1f4e3.

* Update issue description

* async_close_session is optional

* Clean up after rebase

* Add required domain property to provider tests

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-30 14:11:17 +01:00
Erik Montnemery
b4e69bab71
Improve shutdown of esphome ffmpeg proxy (#129326)
* Improve shutdown of esphome ffmpeg proxy

* Add test
2024-10-30 13:46:05 +01:00
Erik Montnemery
db81edfb2b
Add config entry to go2rtc (#129436)
* Add config entry to go2rtc

* Address review comments

* Remove config entry if go2rtc is not configured

* Allow importing default_config

* Address review comment
2024-10-30 13:39:54 +01:00
Martin Hjelmare
24829bc44f
Fix webrtc provider interface and tests (#129488)
* Fix webrtc provider tests

* Remove future code

* Add a test of the optional provider interface
2024-10-30 13:24:23 +01:00
starkillerOG
c8594045df
Bump reolink_aio to 0.10.1 (#129493) 2024-10-30 13:19:45 +01:00
YogevBokobza
ea3f9b971f
Bump aioswitcher to 4.4.0 (#129489) 2024-10-30 12:50:38 +01:00
Robert Resch
380974eed4
Remove hassio from ALLOWED_USED_COMPONENTS and move some functions to helper (#127228)
* Remove hassio from ALLOWED_USED_COMPONENTS

* Move HassioServiceInfo to helpers.service_info

* Deprecate moved functions

* Add note about deprecation

* Fix tests

* Implement suggestion

* Typo

* Update pyproject.toml

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

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-10-30 12:43:41 +01:00
Alistair Francis
8151403bf6
Bump automower-ble to 0.2.0 (#129473) 2024-10-30 12:31:11 +01:00
Christopher Fenner
16f5e76f00
Update PyViCare dependency to 2.35.0 (#129038) 2024-10-30 12:21:54 +01:00
J. Nick Koston
b6b178cac0
Fix nexia emergency heat migration (#129365) 2024-10-30 12:20:19 +01:00
Robert Resch
0f020366e3
Bump go2rtc-client to 0.0.1b3 (#129486) 2024-10-30 12:13:03 +01:00
LG-ThinQ-Integration
27a19be369
Add translation_key in LG ThinQ (#129476)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2024-10-30 11:28:28 +01:00
Blake Bryant
0c166eb307
Bump pydeako to 0.5.4 (#129475) 2024-10-30 11:25:11 +01:00
Erik Montnemery
79d73c28a7
Deduplicate wav creation in esphome ffmpeg_proxy tests (#129484) 2024-10-30 10:35:19 +01:00
LG-ThinQ-Integration
2aed01b530
Add entity_category to avoid header_toggle for switch (#129477)
add entity_category to avoid header_toggle

Co-authored-by: yunseon.park <yunseon.park@lge.com>
2024-10-30 10:34:04 +01:00
Erik Montnemery
3fb0d61271
Remove useless code from esphome ffmpeg_proxy tests (#129481) 2024-10-30 09:56:12 +01:00
Erik Montnemery
599acaf514
Improve demo integration's update entity (#129401)
* Improve demo integration's update entity

* Improve tests
2024-10-30 08:06:22 +01:00
TimL
5f4103a4a7
Allow smlight device to reboot before updating firmware data coordinator (#127442)
* Add delay before updating firmware coordinator

* fix update tests

* change sleep to 1s

* Timeout incase reboot fails

* update test

* test reboot timeout

* log hostname in warning
2024-10-30 08:02:30 +01:00
Kayden van Rijn
c7c72231c7
Bump opower to 0.8.6 (#129454)
* Bump opower to 0.8.6

* Bump opower to 0.8.6
2024-10-29 22:44:06 -07:00
Manu
6887a4419e
Add calendar platform to Habitica integration (#128248)
* Add calendar platform

* Add tests

* add missing reminders filter by date

* Add +1 day to todo end

* add 1 day to dailies, remove unused line of code

* Removing reminders calendar to a separate PR

* fix upcoming event for dailies

* util function for rrule string

* Add test for get_recurrence_rule

* use habitica daystart and account for isDue flag

* yesterdaily is still an active event

* Fix yesterdailies and add attribute

* Update snapshot

* Use iter, return attribute with None value

* various changes

* update snapshot

* fix merge error

* update snapshot

* change date range filtering for todos

* use datetimes instead of date in async_get_events

* Sort events

* Update snapshot

* add method for todos

* filter for upcoming events

* dailies

* refactor todos

* update dailies logic

* dedent loops
2024-10-29 20:53:49 -07:00
Erik Montnemery
db5cb6233c
Correct condition signalling non-live DB migration is in progress (#129464) 2024-10-29 12:26:52 -10:00
Robert Resch
963829712d
Add CameraCapabilities (#128455) 2024-10-29 21:36:30 +01:00
Steven B.
46ceccfbb3
Use new try_connect_all discover command in tplink config flow (#128994)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-10-29 10:26:34 -10:00
J. Nick Koston
aaf3039967
Bump DoorBirdPy to 3.0.7 (#129114) 2024-10-29 10:06:24 -10:00
Shay Levy
2509f18def
Bump aioshelly to 12.0.1 (#129453) 2024-10-29 22:01:38 +02:00
Krisjanis Lejejs
a1e2d79613
Add cloud ICE server registration (#128942)
* Add cloud ICE server registration

* Add ice_servers to prefs, fix registration flow

* Add support for list of ICE servers

* Add ICE server cleanup on cloud logout, create tests

* Fix RTCIceServer types

* Update homeassistant/components/cloud/client.py

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

* Improve tests based on PR reviews

* Improve tests

* Use set_cloud_prefs fixture

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2024-10-29 20:35:52 +01:00
Andre Lengwenus
96ba5c3983
Remove LCN translation placeholder key (#129452) 2024-10-29 20:27:13 +01:00
ollo69
041282190a
Allow set ScreenCap interval as option for AndroidTV (#124470)
Co-authored-by: Joostlek <joostlek@outlook.com>
2024-10-29 20:24:20 +01:00
functionpointer
8cdd5de75c
Change Tibber get_prices action to return datetimes as str (#123901) 2024-10-29 20:15:08 +01:00
Michael
a95c232f11
Add addon support to Home Assistant Analytics Insights (#128806) 2024-10-29 20:13:56 +01:00
Andre Lengwenus
c9aba288b4
Add switch entities for LCN key-locks and regulator-locks (#127731)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 20:08:30 +01:00
G Johansson
35a9d502af
Use coordinator async_setup in dwd weather (#129448) 2024-10-29 20:07:37 +01:00
G Johansson
409c8783fe
Use coordinator async_setup in iotty (#129449) 2024-10-29 20:07:13 +01:00
Keilin Bickar
3adc3d7732
Add sensors for energy trends for devices (#129439)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 20:02:08 +01:00
Steven B.
ec19712388
Bump tplink python-kasa dependency to 0.7.6 (#129444) 2024-10-29 09:00:43 -10:00
Åke Strandberg
2c89e89c84
Improve mapping of myuplink entities (#129137) 2024-10-29 19:59:04 +01:00
Manu
e602a464db
Add tests for buttons in Habitica integration (#128194)
* Add tests for button platform

* update tests

* Add skill buttons

* Assert state, add fixtures/parametrization

* entity as list
2024-10-29 19:03:41 +01:00
Erik Montnemery
ffc0651d89
Report update_percentage in zwave_js update entity (#129386) 2024-10-29 13:31:34 -04:00
Erik Montnemery
7162efd836
Remove duplicated entity_picture config from MQTT update entity (#129390) 2024-10-29 18:22:06 +01:00
epenet
8e7d782102
Move validation routine out of wallbox coordinator (#129415) 2024-10-29 18:13:11 +01:00
Marc Mueller
dc2028f99c
Fix devolo_home_network DataCoordinator arguments (#129441) 2024-10-29 18:06:42 +01:00
Adam Goode
f12ba5f7a9
Unexport unavailable metrics in Prometheus (#125492) 2024-10-29 17:56:54 +01:00
Erik Montnemery
45fb21e32d
Suppress update entity's update_percentage when update not in progress (#129397) 2024-10-29 17:56:09 +01:00
Erik Montnemery
ecbb417736
Report update_percentage in esphome update entity (#129376) 2024-10-29 17:51:54 +01:00
Erik Montnemery
3a59a862d5
Report update_percentage in smlight update entity (#129383) 2024-10-29 17:50:43 +01:00
Erik Montnemery
e34fab0045
Report update_percentage in tessie update entity (#129385) 2024-10-29 17:48:29 +01:00
Erik Montnemery
7254ebe0e3
Report update_percentage in teslemetry update entity (#129384) 2024-10-29 17:48:03 +01:00
Keilin Bickar
b43bc3f32d
Add Sense Devices for entities (#129182) 2024-10-29 17:44:19 +01:00
Erik Montnemery
ca3d13b5cc
Sort some code in core_config (#129388) 2024-10-29 17:26:08 +01:00
Robert Resch
c8818bcce3
Bump go2rtc to 1.9.6 (#129430) 2024-10-29 16:46:58 +01:00
Guido Schmitz
b234b5937a
Disable pylint for DevoloScannerEntity (#129429) 2024-10-29 16:40:38 +01:00
Krisjanis Lejejs
1bdef0f2f7
Bump hass-nabucasa to 0.83.0 (#129422) 2024-10-29 16:34:02 +01:00
Erik Montnemery
56fb61bd6f
Refactor esphome ffmpeg proxy (#129330) 2024-10-29 16:26:32 +01:00
epenet
2c7d0b8909
Initialise coordinator with config_entry in components (part 1) (#128080) 2024-10-29 16:18:04 +01:00
Marcel van der Veldt
cbb8d76da7
Add support for vacuum cleaners to the Matter integration (#129420) 2024-10-29 16:17:40 +01:00
Erik Montnemery
cce925c06c
Fix bad falsy-check in homeassistant.set_location service (#129389) 2024-10-29 16:11:48 +01:00
Marco
505a4bfc34
Add Smarty versions to device (#129418) 2024-10-29 16:06:15 +01:00
Robert Resch
58e151966c
Fix go2rtc no audio issue (#129428) 2024-10-29 16:01:51 +01:00
Michael
8a6c9b7afc
Remove Mobile App config entries, when the related user gets removed (#129268)
* remove config entries, when related user gets removed

* add test
2024-10-29 15:53:00 +01:00
Jirka
e72e2071b0
Fix typo in nest string (#129423)
Update strings.json

Fixed typos
2024-10-29 15:38:55 +01:00
epenet
5d3af27928
Set config_entry explicitly in history stats coordinator (#129417)
Set config_entry explicitely in history stats coordinator
2024-10-29 15:32:56 +01:00
Petar Petrov
5dc0bedbc4
Allow fetching HA url to display it in the network settings (#128432)
* Allow fetching HA url to display it in the network settings

* add tests

* use a constant for the url types

* just return all url types

* Prefer callback without await

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2024-10-29 15:28:54 +01:00
epenet
8f7ae2665c
Set config_entry explicitly in switcher kis coordinator (#129419) 2024-10-29 16:14:36 +02:00
epenet
10fdf819d3
Set config_entry explicitely in scrape coordinator (#129416) 2024-10-29 14:54:24 +01:00
LG-ThinQ-Integration
02928601ef
Add min, max for WATER_HEATER device (#129414)
Co-authored-by: jangwon.lee <jangwon.lee@lge.com>
2024-10-29 14:52:26 +01:00
LG-ThinQ-Integration
c227f6dc2c
Add timer sensor entity which has rw hour and read-only minute (#129413)
Co-authored-by: jangwon.lee <jangwon.lee@lge.com>
2024-10-29 14:44:06 +01:00
Mike Degatano
673f0224c9
Continue migration of methods from handler to aiohasupervisor (#129183) 2024-10-29 14:33:21 +01:00
Manu
79c602f59c
Fix available conditions for chilling frost and stealth in Habitica (#129234)
Co-authored-by: Joostlek <joostlek@outlook.com>
2024-10-29 14:24:23 +01:00
Raj Laud
07c070e253
Refactor squeezebox integration media_player to use coordinator (#127695) 2024-10-29 14:21:28 +01:00
Vendetta01
9bda3bd477
Fix bosch shc multi controller support (#127844)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 14:19:33 +01:00
Marc Hörsken
2c9ad9562e
Fix visualization by inverting open/closed state of patio awnings (#128079) 2024-10-29 14:09:49 +01:00
Manu
c264ee22e7
Add tests for switch platform of Habitica integration (#128204) 2024-10-29 14:08:05 +01:00
J. Diego Rodríguez Royo
f194a689cc
Fetch power off state for Home Connect appliances' power switch (#129289) 2024-10-29 13:56:45 +01:00
David Bonnes
a36b350954
Fix evohome HVAC modes for VisionPro Wifi systems (#129161)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 13:37:35 +01:00
Josef Zweck
db4278fb9d
Cleanup select mappings in lamarzocco (#129407) 2024-10-29 13:32:14 +01:00
David Bonnes
39ba4cff2f
Refactor evohome tests as per best practice (#129229)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 13:29:10 +01:00
Christopher Fenner
d68da74790
Add number entities to set target temp for cooling programs in ViCare (#127267)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 13:28:12 +01:00
Tomer Shemesh
5fc45cd736
Add support for Lutron HWQS Proc discovery (#129274) 2024-10-29 13:27:44 +01:00
Guido Schmitz
5ae2f3d081
Add own coordinator to devolo_home_network (#128159) 2024-10-29 13:23:28 +01:00
Josef Zweck
478bf643bf
Add smart standby functionality to lamarzocco (#129333)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 13:22:37 +01:00
Daniel Hjelseth Høyer
7929895b11
Change Tibber request spread (#129276) 2024-10-29 13:12:07 +01:00
Erik Montnemery
da11a72b4c
Create repair asking user to remove duplicate config entries (#127948)
Co-authored-by: Joostlek <joostlek@outlook.com>
2024-10-29 13:10:56 +01:00
Mike Degatano
1649368cee
Bump aiohasupervisor to 0.2.0 (#129348) 2024-10-29 13:07:59 +01:00
dontinelli
a528d62c16
Add test for extended data in setup for solarlog (#129345) 2024-10-29 13:07:48 +01:00
Guido Schmitz
bd13dbdad0
Use new generic notation in devolo_home_network (#129080) 2024-10-29 13:07:13 +01:00
Allen Porter
8e7ffd9e16
Update Nest configuration flow to handle upcoming changes to Pub/Sub provisioning (#128909)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-10-29 12:58:36 +01:00
Manu
f0bff09b5e
Bump habitipy to 0.3.3 (#129322) 2024-10-29 12:48:20 +01:00
J. Diego Rodríguez Royo
0e959b3019
Added deprecation to binary door sensor at Home Connect (#129245)
Co-authored-by: Joostlek <joostlek@outlook.com>
2024-10-29 12:46:39 +01:00
Thomas55555
983cd9c3fc
Add and remove entities during runtime in Husqvarna Automower (#127878) 2024-10-29 12:46:04 +01:00
Erik Montnemery
2236ca3e12
Fix typo in cv.url_no_path (#129402) 2024-10-29 12:06:59 +01:00
Robert Resch
f3afa6a7d9
Fix hassfest docker image by pinning Python 3.12 (#129403) 2024-10-29 11:57:20 +01:00
Brett Adams
ce7e2e3243
Clean up SensorRestore in Tesla Fleet (#129116)
* Remove, fix, and test restore

* slightly better comment

* use restore instead

* parametrize test

* Apply suggestions from code review

* revert change to Teslemetry

* revert change to Teslemetry

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2024-10-29 11:41:35 +01:00
Robert Resch
13416825b1
Go2rtc server start is waiting until we got the api listen stdout line (#129391) 2024-10-29 11:28:40 +01:00
J. Nick Koston
6c664e7ba9
Bump protobuf to 5.28.3 (#129370) 2024-10-29 11:22:31 +01:00
LG-ThinQ-Integration
34359617b5
Bump thinqconnect to 0.9.9 (#129394) 2024-10-29 11:16:19 +01:00
Erik Montnemery
9e2696b9bc
Report update_percentage in matter update entity (#129380) 2024-10-29 10:57:52 +01:00
Paul Bottein
bf840e8bfa
Use device name for matter entities (#127798) 2024-10-29 10:54:25 +01:00
Robert Resch
1f03c140f5
Bump go2rtc-client to 0.0.1b2 (#129395) 2024-10-29 10:45:00 +01:00
Marc Mueller
2de161ce0e
Fix mariadb recorder tests for Python 3.13 (#129303) 2024-10-29 09:17:47 +01:00
Marc Mueller
1171106afb
Run postgres job on ubuntu 24.04 [ci] (#129381) 2024-10-29 09:15:04 +01:00
Robert Resch
f57ae73071
Bump webrtc-models to 0.1.0 (#129373) 2024-10-29 08:33:54 +01:00
Robert Resch
59872b5698
Enable strict typing for go2rtc (#129374) 2024-10-29 08:25:49 +01:00
Robert Resch
7cd8ea00d1
Bump uv to 0.4.28 (#129372) 2024-10-28 21:20:59 -10:00
Robert Resch
4b2f38926a
Bump go2rtc binary to 1.9.5 (#129371) 2024-10-29 08:01:59 +01:00
Allen Porter
537c95cf29
Update nest to use the async WebRTC APIs (#129369)
* Update nest to use the new `async_handle_webrtc_offer` APIs.

* Close sessions when sessions end

* Switch to the correct close API
2024-10-29 07:18:59 +01:00
epenet
81a5722708
Fix flaky DHCP tests in CI (#129327) 2024-10-28 13:41:50 -10:00
Jan Bouwhuis
c150b913ac
Use URL validation schema for mqtt update entity_picture and remove custom implementation (#129360) 2024-10-28 23:36:17 +01:00
J. Nick Koston
3e4b67db6c
Bump yarl to 1.17.0 (#129358) 2024-10-28 23:11:14 +01:00
G Johansson
d727f8ff50
Clarify event tracking in docstrings for track_state_change/report (#129338)
* Clarify event tracking in docstrings for track_state_change/report

* Fixes

* Update homeassistant/helpers/event.py

* Update homeassistant/helpers/event.py

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

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-10-28 23:05:06 +01:00
1257 changed files with 43728 additions and 11616 deletions

View file

@ -79,6 +79,7 @@ components: &components
- homeassistant/components/group/**
- homeassistant/components/hassio/**
- homeassistant/components/homeassistant/**
- homeassistant/components/homeassistant_hardware/**
- homeassistant/components/http/**
- homeassistant/components/image/**
- homeassistant/components/input_boolean/**

View file

@ -10,7 +10,7 @@ on:
env:
BUILD_TYPE: core
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.13"
PIP_TIMEOUT: 60
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"
@ -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 }}

View file

@ -40,9 +40,9 @@ 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']"
ALL_PYTHON_VERSIONS: "['3.12', '3.13']"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support
@ -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:
@ -819,11 +819,7 @@ jobs:
needs:
- info
- base
strategy:
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: |
@ -836,11 +832,11 @@ jobs:
libgammu-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.3.0
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
@ -858,7 +854,7 @@ jobs:
- name: Upload pytest_buckets
uses: actions/upload-artifact@v4.4.3
with:
name: pytest_buckets-${{ matrix.python-version }}
name: pytest_buckets
path: pytest_buckets.txt
overwrite: true
@ -923,7 +919,7 @@ jobs:
- name: Download pytest_buckets
uses: actions/download-artifact@v4.1.8
with:
name: pytest_buckets-${{ matrix.python-version }}
name: pytest_buckets
- name: Compile English translations
run: |
. venv/bin/activate
@ -949,6 +945,7 @@ jobs:
--timeout=9 \
--durations=10 \
--numprocesses auto \
--snapshot-details \
--dist=loadfile \
${cov_params[@]} \
-o console_output_style=count \
@ -1071,6 +1068,7 @@ jobs:
-qq \
--timeout=20 \
--numprocesses 1 \
--snapshot-details \
${cov_params[@]} \
-o console_output_style=count \
--durations=10 \
@ -1102,7 +1100,7 @@ jobs:
./script/check_dirty
pytest-postgres:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
services:
postgres:
image: ${{ matrix.postgresql-group }}
@ -1142,7 +1140,9 @@ jobs:
sudo apt-get -y install \
bluez \
ffmpeg \
libturbojpeg \
libturbojpeg
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
sudo apt-get -y install \
postgresql-server-dev-14
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
@ -1197,6 +1197,7 @@ jobs:
-qq \
--timeout=9 \
--numprocesses 1 \
--snapshot-details \
${cov_params[@]} \
-o console_output_style=count \
--durations=0 \
@ -1343,6 +1344,7 @@ jobs:
-qq \
--timeout=9 \
--numprocesses auto \
--snapshot-details \
${cov_params[@]} \
-o console_output_style=count \
--durations=0 \

View file

@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.27.0
uses: github/codeql-action/init@v3.27.3
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.27.0
uses: github/codeql-action/analyze@v3.27.3
with:
category: "/language:python"

View file

@ -112,7 +112,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp312"]
abi: ["cp312", "cp313"]
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
@ -135,14 +135,14 @@ jobs:
sed -i "/uv/d" requirements_diff.txt
- name: Build wheels
uses: home-assistant/wheels@2024.07.1
uses: home-assistant/wheels@2024.11.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "libffi-dev;openssl-dev;yaml-dev;nasm"
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-dev"
skip-binary: aiohttp;multidict;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
@ -156,7 +156,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp312"]
abi: ["cp312", "cp313"]
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
@ -198,6 +198,7 @@ jobs:
split -l $(expr $(expr $(cat requirements_all.txt | wc -l) + 1) / 3) requirements_all_wheels_${{ matrix.arch }}.txt requirements_all.txt
- name: Create requirements for cython<3
if: matrix.abi == 'cp312'
run: |
# Some dependencies still require 'cython<3'
# and don't yet use isolated build environments.
@ -208,7 +209,8 @@ jobs:
cat homeassistant/package_constraints.txt | grep 'pydantic==' >> requirements_old-cython.txt
- name: Build wheels (old cython)
uses: home-assistant/wheels@2024.07.1
uses: home-assistant/wheels@2024.11.0
if: matrix.abi == 'cp312'
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
@ -223,43 +225,43 @@ jobs:
pip: "'cython<3'"
- name: Build wheels (part 1)
uses: home-assistant/wheels@2024.07.1
uses: home-assistant/wheels@2024.11.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev;nasm"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pydantic;pymicro-vad;yarl
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev;nasm;zlib-dev"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txtaa"
- name: Build wheels (part 2)
uses: home-assistant/wheels@2024.07.1
uses: home-assistant/wheels@2024.11.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev;nasm"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pydantic;pymicro-vad;yarl
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev;nasm;zlib-dev"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txtab"
- name: Build wheels (part 3)
uses: home-assistant/wheels@2024.07.1
uses: home-assistant/wheels@2024.11.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev;nasm"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pydantic;pymicro-vad;yarl
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev;nasm;zlib-dev"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txtac"

View file

@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.1
rev: v0.7.3
hooks:
- id: ruff
args:
@ -90,7 +90,7 @@ repos:
pass_filenames: false
language: script
types: [text]
files: ^(script/hassfest/metadata\.py|homeassistant/const\.py$|pyproject\.toml)$
files: ^(script/hassfest/metadata\.py|homeassistant/const\.py$|pyproject\.toml|homeassistant/components/go2rtc/const\.py)$
- id: hassfest-mypy-config
name: hassfest-mypy-config
entry: script/run-in-env.sh python3 -m script.hassfest -p mypy_config

View file

@ -209,6 +209,7 @@ homeassistant.components.geo_location.*
homeassistant.components.geocaching.*
homeassistant.components.gios.*
homeassistant.components.glances.*
homeassistant.components.go2rtc.*
homeassistant.components.goalzero.*
homeassistant.components.google.*
homeassistant.components.google_assistant_sdk.*
@ -323,11 +324,13 @@ 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.*
homeassistant.components.nam.*
homeassistant.components.nanoleaf.*
homeassistant.components.nasweb.*
homeassistant.components.neato.*
homeassistant.components.nest.*
homeassistant.components.netatmo.*
@ -337,6 +340,7 @@ homeassistant.components.nfandroidtv.*
homeassistant.components.nightscout.*
homeassistant.components.nissan_leaf.*
homeassistant.components.no_ip.*
homeassistant.components.nordpool.*
homeassistant.components.notify.*
homeassistant.components.notion.*
homeassistant.components.number.*

View file

@ -40,6 +40,8 @@ build.json @home-assistant/supervisor
# Integrations
/homeassistant/components/abode/ @shred86
/tests/components/abode/ @shred86
/homeassistant/components/acaia/ @zweckj
/tests/components/acaia/ @zweckj
/homeassistant/components/accuweather/ @bieniu
/tests/components/accuweather/ @bieniu
/homeassistant/components/acmeda/ @atmurray
@ -496,8 +498,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 +956,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
@ -968,6 +972,8 @@ build.json @home-assistant/supervisor
/tests/components/nam/ @bieniu
/homeassistant/components/nanoleaf/ @milanmeu @joostlek
/tests/components/nanoleaf/ @milanmeu @joostlek
/homeassistant/components/nasweb/ @nasWebio
/tests/components/nasweb/ @nasWebio
/homeassistant/components/neato/ @Santobert
/tests/components/neato/ @Santobert
/homeassistant/components/nederlandse_spoorwegen/ @YarmoM
@ -1008,6 +1014,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/noaa_tides/ @jdelaney72
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
/tests/components/nobo_hub/ @echoromeo @oyvindwe
/homeassistant/components/nordpool/ @gjohansson-ST
/tests/components/nordpool/ @gjohansson-ST
/homeassistant/components/notify/ @home-assistant/core
/tests/components/notify/ @home-assistant/core
/homeassistant/components/notify_events/ @matrozov @papajojo
@ -1338,6 +1346,8 @@ build.json @home-assistant/supervisor
/tests/components/siren/ @home-assistant/core @raman325
/homeassistant/components/sisyphus/ @jkeljo
/homeassistant/components/sky_hub/ @rogerselwyn
/homeassistant/components/sky_remote/ @dunnmj @saty9
/tests/components/sky_remote/ @dunnmj @saty9
/homeassistant/components/skybell/ @tkdrob
/tests/components/skybell/ @tkdrob
/homeassistant/components/slack/ @tkdrob @fletcherau
@ -1479,8 +1489,8 @@ build.json @home-assistant/supervisor
/tests/components/tedee/ @patrickhilker @zweckj
/homeassistant/components/tellduslive/ @fredrike
/tests/components/tellduslive/ @fredrike
/homeassistant/components/template/ @PhracturedBlue @tetienne @home-assistant/core
/tests/components/template/ @PhracturedBlue @tetienne @home-assistant/core
/homeassistant/components/template/ @PhracturedBlue @home-assistant/core
/tests/components/template/ @PhracturedBlue @home-assistant/core
/homeassistant/components/tesla_fleet/ @Bre77
/tests/components/tesla_fleet/ @Bre77
/homeassistant/components/tesla_wall_connector/ @einarhauks

View file

@ -7,12 +7,13 @@ FROM ${BUILD_FROM}
# Synchronize with homeassistant/core.py:async_stop
ENV \
S6_SERVICES_GRACETIME=240000 \
UV_SYSTEM_PYTHON=true
UV_SYSTEM_PYTHON=true \
UV_NO_CACHE=true
ARG QEMU_CPU
# Install uv
RUN pip3 install uv==0.4.22
RUN pip3 install uv==0.5.0
WORKDIR /usr/src
@ -54,7 +55,7 @@ RUN \
"armv7") go2rtc_suffix='arm' ;; \
*) go2rtc_suffix=${BUILD_ARCH} ;; \
esac \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.4/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.7/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& chmod +x /bin/go2rtc \
# Verify go2rtc can be executed
&& go2rtc --version

View file

@ -35,6 +35,9 @@ RUN \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Add go2rtc binary
COPY --from=ghcr.io/alexxit/go2rtc:latest /usr/local/bin/go2rtc /bin/go2rtc
# Install uv
RUN pip3 install uv

View file

@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2024.06.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2024.06.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2024.06.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2024.06.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2024.06.1
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2024.11.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2024.11.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2024.11.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2024.11.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2024.11.0
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io

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

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

View file

@ -515,7 +515,7 @@ async def async_from_config_dict(
issue_registry.async_create_issue(
hass,
core.DOMAIN,
"python_version",
f"python_version_{required_python_version}",
is_fixable=False,
severity=issue_registry.IssueSeverity.WARNING,
breaks_in_ha_version=REQUIRED_NEXT_PYTHON_HA_RELEASE,

View file

@ -0,0 +1,5 @@
{
"domain": "sky",
"name": "Sky",
"integrations": ["sky_hub", "sky_remote"]
}

View file

@ -0,0 +1,29 @@
"""Initialize the Acaia component."""
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from .coordinator import AcaiaConfigEntry, AcaiaCoordinator
PLATFORMS = [
Platform.BUTTON,
]
async def async_setup_entry(hass: HomeAssistant, entry: AcaiaConfigEntry) -> bool:
"""Set up acaia as config entry."""
coordinator = AcaiaCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AcaiaConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View file

@ -0,0 +1,61 @@
"""Button entities for Acaia scales."""
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import Any
from aioacaia.acaiascale import AcaiaScale
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .coordinator import AcaiaConfigEntry
from .entity import AcaiaEntity
@dataclass(kw_only=True, frozen=True)
class AcaiaButtonEntityDescription(ButtonEntityDescription):
"""Description for acaia button entities."""
press_fn: Callable[[AcaiaScale], Coroutine[Any, Any, None]]
BUTTONS: tuple[AcaiaButtonEntityDescription, ...] = (
AcaiaButtonEntityDescription(
key="tare",
translation_key="tare",
press_fn=lambda scale: scale.tare(),
),
AcaiaButtonEntityDescription(
key="reset_timer",
translation_key="reset_timer",
press_fn=lambda scale: scale.reset_timer(),
),
AcaiaButtonEntityDescription(
key="start_stop",
translation_key="start_stop",
press_fn=lambda scale: scale.start_stop_timer(),
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AcaiaConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up button entities and services."""
coordinator = entry.runtime_data
async_add_entities(AcaiaButton(coordinator, description) for description in BUTTONS)
class AcaiaButton(AcaiaEntity, ButtonEntity):
"""Representation of an Acaia button."""
entity_description: AcaiaButtonEntityDescription
async def async_press(self) -> None:
"""Handle the button press."""
await self.entity_description.press_fn(self._scale)

View file

@ -0,0 +1,149 @@
"""Config flow for Acaia integration."""
import logging
from typing import Any
from aioacaia.exceptions import AcaiaDeviceNotFound, AcaiaError, AcaiaUnknownDevice
from aioacaia.helpers import is_new_scale
import voluptuous as vol
from homeassistant.components.bluetooth import (
BluetoothServiceInfoBleak,
async_discovered_service_info,
)
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ADDRESS, CONF_NAME
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)
from .const import CONF_IS_NEW_STYLE_SCALE, DOMAIN
_LOGGER = logging.getLogger(__name__)
class AcaiaConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for acaia."""
def __init__(self) -> None:
"""Initialize the config flow."""
self._discovered: dict[str, Any] = {}
self._discovered_devices: dict[str, str] = {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
errors: dict[str, str] = {}
if user_input is not None:
mac = format_mac(user_input[CONF_ADDRESS])
try:
is_new_style_scale = await is_new_scale(mac)
except AcaiaDeviceNotFound:
errors["base"] = "device_not_found"
except AcaiaError:
_LOGGER.exception("Error occurred while connecting to the scale")
errors["base"] = "unknown"
except AcaiaUnknownDevice:
return self.async_abort(reason="unsupported_device")
else:
await self.async_set_unique_id(mac)
self._abort_if_unique_id_configured()
if not errors:
return self.async_create_entry(
title=self._discovered_devices[user_input[CONF_ADDRESS]],
data={
CONF_ADDRESS: mac,
CONF_IS_NEW_STYLE_SCALE: is_new_style_scale,
},
)
for device in async_discovered_service_info(self.hass):
self._discovered_devices[device.address] = device.name
if not self._discovered_devices:
return self.async_abort(reason="no_devices_found")
options = [
SelectOptionDict(
value=device_mac,
label=f"{device_name} ({device_mac})",
)
for device_mac, device_name in self._discovered_devices.items()
]
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_ADDRESS): SelectSelector(
SelectSelectorConfig(
options=options,
mode=SelectSelectorMode.DROPDOWN,
)
)
}
),
errors=errors,
)
async def async_step_bluetooth(
self, discovery_info: BluetoothServiceInfoBleak
) -> ConfigFlowResult:
"""Handle a discovered Bluetooth device."""
self._discovered[CONF_ADDRESS] = mac = format_mac(discovery_info.address)
self._discovered[CONF_NAME] = discovery_info.name
await self.async_set_unique_id(mac)
self._abort_if_unique_id_configured()
try:
self._discovered[CONF_IS_NEW_STYLE_SCALE] = await is_new_scale(
discovery_info.address
)
except AcaiaDeviceNotFound:
_LOGGER.debug("Device not found during discovery")
return self.async_abort(reason="device_not_found")
except AcaiaError:
_LOGGER.debug(
"Error occurred while connecting to the scale during discovery",
exc_info=True,
)
return self.async_abort(reason="unknown")
except AcaiaUnknownDevice:
_LOGGER.debug("Unsupported device during discovery")
return self.async_abort(reason="unsupported_device")
return await self.async_step_bluetooth_confirm()
async def async_step_bluetooth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle confirmation of Bluetooth discovery."""
if user_input is not None:
return self.async_create_entry(
title=self._discovered[CONF_NAME],
data={
CONF_ADDRESS: self._discovered[CONF_ADDRESS],
CONF_IS_NEW_STYLE_SCALE: self._discovered[CONF_IS_NEW_STYLE_SCALE],
},
)
self.context["title_placeholders"] = placeholders = {
CONF_NAME: self._discovered[CONF_NAME]
}
self._set_confirm_only()
return self.async_show_form(
step_id="bluetooth_confirm",
description_placeholders=placeholders,
)

View file

@ -0,0 +1,4 @@
"""Constants for component."""
DOMAIN = "acaia"
CONF_IS_NEW_STYLE_SCALE = "is_new_style_scale"

View file

@ -0,0 +1,86 @@
"""Coordinator for Acaia integration."""
from __future__ import annotations
from datetime import timedelta
import logging
from aioacaia.acaiascale import AcaiaScale
from aioacaia.exceptions import AcaiaDeviceNotFound, AcaiaError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ADDRESS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONF_IS_NEW_STYLE_SCALE
SCAN_INTERVAL = timedelta(seconds=15)
_LOGGER = logging.getLogger(__name__)
type AcaiaConfigEntry = ConfigEntry[AcaiaCoordinator]
class AcaiaCoordinator(DataUpdateCoordinator[None]):
"""Class to handle fetching data from the scale."""
config_entry: AcaiaConfigEntry
def __init__(self, hass: HomeAssistant, entry: AcaiaConfigEntry) -> None:
"""Initialize coordinator."""
super().__init__(
hass,
_LOGGER,
name="acaia coordinator",
update_interval=SCAN_INTERVAL,
config_entry=entry,
)
self._scale = AcaiaScale(
address_or_ble_device=entry.data[CONF_ADDRESS],
name=entry.title,
is_new_style_scale=entry.data[CONF_IS_NEW_STYLE_SCALE],
notify_callback=self.async_update_listeners,
)
@property
def scale(self) -> AcaiaScale:
"""Return the scale object."""
return self._scale
async def _async_update_data(self) -> None:
"""Fetch data."""
# scale is already connected, return
if self._scale.connected:
return
# scale is not connected, try to connect
try:
await self._scale.connect(setup_tasks=False)
except (AcaiaDeviceNotFound, AcaiaError, TimeoutError) as ex:
_LOGGER.debug(
"Could not connect to scale: %s, Error: %s",
self.config_entry.data[CONF_ADDRESS],
ex,
)
self._scale.device_disconnected_handler(notify=False)
return
# connected, set up background tasks
if not self._scale.heartbeat_task or self._scale.heartbeat_task.done():
self._scale.heartbeat_task = self.config_entry.async_create_background_task(
hass=self.hass,
target=self._scale.send_heartbeats(),
name="acaia_heartbeat_task",
)
if not self._scale.process_queue_task or self._scale.process_queue_task.done():
self._scale.process_queue_task = (
self.config_entry.async_create_background_task(
hass=self.hass,
target=self._scale.process_queue(),
name="acaia_process_queue_task",
)
)

View file

@ -0,0 +1,40 @@
"""Base class for Acaia entities."""
from dataclasses import dataclass
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AcaiaCoordinator
@dataclass
class AcaiaEntity(CoordinatorEntity[AcaiaCoordinator]):
"""Common elements for all entities."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AcaiaCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize the entity."""
super().__init__(coordinator)
self.entity_description = entity_description
self._scale = coordinator.scale
self._attr_unique_id = f"{self._scale.mac}_{entity_description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._scale.mac)},
manufacturer="Acaia",
model=self._scale.model,
suggested_area="Kitchen",
)
@property
def available(self) -> bool:
"""Returns whether entity is available."""
return super().available and self._scale.connected

View file

@ -0,0 +1,15 @@
{
"entity": {
"button": {
"tare": {
"default": "mdi:scale-balance"
},
"reset_timer": {
"default": "mdi:timer-refresh"
},
"start_stop": {
"default": "mdi:timer-play"
}
}
}
}

View file

@ -0,0 +1,29 @@
{
"domain": "acaia",
"name": "Acaia",
"bluetooth": [
{
"manufacturer_id": 16962
},
{
"local_name": "ACAIA*"
},
{
"local_name": "PYXIS-*"
},
{
"local_name": "LUNAR-*"
},
{
"local_name": "PROCHBT001"
}
],
"codeowners": ["@zweckj"],
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/acaia",
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["aioacaia"],
"requirements": ["aioacaia==0.1.6"]
}

View file

@ -0,0 +1,38 @@
{
"config": {
"flow_title": "{name}",
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"unsupported_device": "This device is not supported."
},
"error": {
"device_not_found": "Device could not be found.",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"bluetooth_confirm": {
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
},
"user": {
"description": "[%key:component::bluetooth::config::step::user::description%]",
"data": {
"address": "[%key:common::config_flow::data::device%]"
}
}
}
},
"entity": {
"button": {
"tare": {
"name": "Tare"
},
"reset_timer": {
"name": "Reset timer"
},
"start_stop": {
"name": "Start/stop timer"
}
}
}
}

View file

@ -7,7 +7,6 @@ from typing import Any
from adguardhome import AdGuardHome, AdGuardHomeConnectionError
import voluptuous as vol
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_HOST,
@ -18,6 +17,7 @@ from homeassistant.const import (
CONF_VERIFY_SSL,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import DOMAIN

View file

@ -55,6 +55,7 @@ async def async_setup_entry(
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=entry,
name="Advantage Air",
update_method=async_get,
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),

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)

View file

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

View file

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

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
@ -249,6 +248,7 @@ WEATHER_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
name="Rain",
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
state_class=SensorStateClass.MEASUREMENT,
),
AemetSensorEntityDescription(
key=ATTR_API_RAIN_PROB,
@ -263,6 +263,7 @@ WEATHER_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
name="Snow",
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
state_class=SensorStateClass.MEASUREMENT,
),
AemetSensorEntityDescription(
key=ATTR_API_SNOW_PROB,

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

View file

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

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(

View file

@ -42,6 +42,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) ->
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=entry,
name=DOMAIN,
update_method=_update_method,
update_interval=SCAN_INTERVAL,

View file

@ -24,5 +24,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"iot_class": "local_polling",
"requirements": ["airthings-ble==0.9.1"]
"requirements": ["airthings-ble==0.9.2"]
}

View file

@ -204,6 +204,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirVisualConfigEntry) ->
coordinator = DataUpdateCoordinator(
hass,
LOGGER,
config_entry=entry,
name=async_get_geography_id(entry.data),
# We give a placeholder update interval in order to create the coordinator;
# then, below, we use the coordinator's presence (along with any other

View file

@ -81,6 +81,7 @@ async def async_setup_entry(
coordinator = DataUpdateCoordinator(
hass,
LOGGER,
config_entry=entry,
name="Node/Pro data",
update_interval=UPDATE_INTERVAL,
update_method=async_get_data,

View file

@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone",
"iot_class": "local_polling",
"loggers": ["aioairzone"],
"requirements": ["aioairzone==0.9.5"]
"requirements": ["aioairzone==0.9.6"]
}

View file

@ -6,7 +6,7 @@ import asyncio
from datetime import timedelta
from functools import partial
import logging
from typing import Any, Final, final
from typing import TYPE_CHECKING, Any, Final, final
from propcache import cached_property
import voluptuous as vol
@ -221,9 +221,15 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
@property
def state(self) -> str | None:
"""Return the current state."""
if (alarm_state := self.alarm_state) is None:
return None
return alarm_state
if (alarm_state := self.alarm_state) is not None:
return alarm_state
if self._attr_state is not None:
# Backwards compatibility for integrations that set state directly
# Should be removed in 2025.11
if TYPE_CHECKING:
assert isinstance(self._attr_state, str)
return self._attr_state
return None
@cached_property
def alarm_state(self) -> AlarmControlPanelState | None:

View file

@ -1083,7 +1083,13 @@ async def async_api_arm(
arm_state = directive.payload["armState"]
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
if entity.state != alarm_control_panel.AlarmControlPanelState.DISARMED:
# Per Alexa Documentation: users are not allowed to switch from armed_away
# directly to another armed state without first disarming the system.
# https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-securitypanelcontroller.html#arming
if (
entity.state == alarm_control_panel.AlarmControlPanelState.ARMED_AWAY
and arm_state != "ARMED_AWAY"
):
msg = "You must disarm the system before you can set the requested arm state."
raise AlexaSecurityPanelAuthorizationRequired(msg)

View file

@ -29,6 +29,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.entity_registry as er
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.storage import Store
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.loader import (
@ -136,7 +137,7 @@ class Analytics:
@property
def supervisor(self) -> bool:
"""Return bool if a supervisor is present."""
return hassio.is_hassio(self.hass)
return is_hassio(self.hass)
async def load(self) -> None:
"""Load preferences."""

View file

@ -1,7 +1,7 @@
{
"domain": "analytics",
"name": "Analytics",
"after_dependencies": ["energy", "recorder"],
"after_dependencies": ["energy", "hassio", "recorder"],
"codeowners": ["@home-assistant/core", "@ludeeus"],
"dependencies": ["api", "websocket_api"],
"documentation": "https://www.home-assistant.io/integrations/analytics",

View file

@ -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
@ -27,6 +26,7 @@ from homeassistant.helpers.selector import (
)
from .const import (
CONF_TRACKED_ADDONS,
CONF_TRACKED_CUSTOM_INTEGRATIONS,
CONF_TRACKED_INTEGRATIONS,
DOMAIN,
@ -45,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
@ -55,8 +57,12 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
if not user_input.get(CONF_TRACKED_INTEGRATIONS) and not user_input.get(
CONF_TRACKED_CUSTOM_INTEGRATIONS
if all(
[
not user_input.get(CONF_TRACKED_ADDONS),
not user_input.get(CONF_TRACKED_INTEGRATIONS),
not user_input.get(CONF_TRACKED_CUSTOM_INTEGRATIONS),
]
):
errors["base"] = "no_integrations_selected"
else:
@ -64,6 +70,7 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
title="Home Assistant Analytics Insights",
data={},
options={
CONF_TRACKED_ADDONS: user_input.get(CONF_TRACKED_ADDONS, []),
CONF_TRACKED_INTEGRATIONS: user_input.get(
CONF_TRACKED_INTEGRATIONS, []
),
@ -77,6 +84,7 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
session=async_get_clientsession(self.hass)
)
try:
addons = await client.get_addons()
integrations = await client.get_integrations()
custom_integrations = await client.get_custom_integrations()
except HomeassistantAnalyticsConnectionError:
@ -99,6 +107,13 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
data_schema=vol.Schema(
{
vol.Optional(CONF_TRACKED_ADDONS): SelectSelector(
SelectSelectorConfig(
options=list(addons),
multiple=True,
sort=True,
)
),
vol.Optional(CONF_TRACKED_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=options,
@ -118,7 +133,7 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
)
class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlow):
"""Handle Homeassistant Analytics options."""
async def async_step_init(
@ -127,14 +142,19 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
"""Manage the options."""
errors: dict[str, str] = {}
if user_input is not None:
if not user_input.get(CONF_TRACKED_INTEGRATIONS) and not user_input.get(
CONF_TRACKED_CUSTOM_INTEGRATIONS
if all(
[
not user_input.get(CONF_TRACKED_ADDONS),
not user_input.get(CONF_TRACKED_INTEGRATIONS),
not user_input.get(CONF_TRACKED_CUSTOM_INTEGRATIONS),
]
):
errors["base"] = "no_integrations_selected"
else:
return self.async_create_entry(
title="",
data={
CONF_TRACKED_ADDONS: user_input.get(CONF_TRACKED_ADDONS, []),
CONF_TRACKED_INTEGRATIONS: user_input.get(
CONF_TRACKED_INTEGRATIONS, []
),
@ -148,6 +168,7 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
session=async_get_clientsession(self.hass)
)
try:
addons = await client.get_addons()
integrations = await client.get_integrations()
custom_integrations = await client.get_custom_integrations()
except HomeassistantAnalyticsConnectionError:
@ -168,6 +189,13 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
data_schema=self.add_suggested_values_to_schema(
vol.Schema(
{
vol.Optional(CONF_TRACKED_ADDONS): SelectSelector(
SelectSelectorConfig(
options=list(addons),
multiple=True,
sort=True,
)
),
vol.Optional(CONF_TRACKED_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=options,
@ -184,6 +212,6 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
),
},
),
self.options,
self.config_entry.options,
),
)

View file

@ -4,6 +4,7 @@ import logging
DOMAIN = "analytics_insights"
CONF_TRACKED_ADDONS = "tracked_addons"
CONF_TRACKED_INTEGRATIONS = "tracked_integrations"
CONF_TRACKED_CUSTOM_INTEGRATIONS = "tracked_custom_integrations"

View file

@ -12,11 +12,13 @@ from python_homeassistant_analytics import (
HomeassistantAnalyticsConnectionError,
HomeassistantAnalyticsNotModifiedError,
)
from python_homeassistant_analytics.models import Addon
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
CONF_TRACKED_ADDONS,
CONF_TRACKED_CUSTOM_INTEGRATIONS,
CONF_TRACKED_INTEGRATIONS,
DOMAIN,
@ -33,6 +35,7 @@ class AnalyticsData:
active_installations: int
reports_integrations: int
addons: dict[str, int]
core_integrations: dict[str, int]
custom_integrations: dict[str, int]
@ -53,6 +56,7 @@ class HomeassistantAnalyticsDataUpdateCoordinator(DataUpdateCoordinator[Analytic
update_interval=timedelta(hours=12),
)
self._client = client
self._tracked_addons = self.config_entry.options.get(CONF_TRACKED_ADDONS, [])
self._tracked_integrations = self.config_entry.options[
CONF_TRACKED_INTEGRATIONS
]
@ -62,6 +66,7 @@ class HomeassistantAnalyticsDataUpdateCoordinator(DataUpdateCoordinator[Analytic
async def _async_update_data(self) -> AnalyticsData:
try:
addons_data = await self._client.get_addons()
data = await self._client.get_current_analytics()
custom_data = await self._client.get_custom_integrations()
except HomeassistantAnalyticsConnectionError as err:
@ -70,6 +75,9 @@ class HomeassistantAnalyticsDataUpdateCoordinator(DataUpdateCoordinator[Analytic
) from err
except HomeassistantAnalyticsNotModifiedError:
return self.data
addons = {
addon: get_addon_value(addons_data, addon) for addon in self._tracked_addons
}
core_integrations = {
integration: data.integrations.get(integration, 0)
for integration in self._tracked_integrations
@ -81,11 +89,19 @@ class HomeassistantAnalyticsDataUpdateCoordinator(DataUpdateCoordinator[Analytic
return AnalyticsData(
data.active_installations,
data.reports_integrations,
addons,
core_integrations,
custom_integrations,
)
def get_addon_value(data: dict[str, Addon], name_slug: str) -> int:
"""Get addon value."""
if name_slug in data:
return data[name_slug].total
return 0
def get_custom_integration_value(
data: dict[str, CustomIntegration], domain: str
) -> int:

View file

@ -29,6 +29,20 @@ class AnalyticsSensorEntityDescription(SensorEntityDescription):
value_fn: Callable[[AnalyticsData], StateType]
def get_addon_entity_description(
name_slug: str,
) -> AnalyticsSensorEntityDescription:
"""Get addon entity description."""
return AnalyticsSensorEntityDescription(
key=f"addon_{name_slug}_active_installations",
translation_key="addons",
name=name_slug,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement="active installations",
value_fn=lambda data: data.addons.get(name_slug),
)
def get_core_integration_entity_description(
domain: str, name: str
) -> AnalyticsSensorEntityDescription:
@ -89,6 +103,13 @@ async def async_setup_entry(
analytics_data.coordinator
)
entities: list[HomeassistantAnalyticsSensor] = []
entities.extend(
HomeassistantAnalyticsSensor(
coordinator,
get_addon_entity_description(addon_name_slug),
)
for addon_name_slug in coordinator.data.addons
)
entities.extend(
HomeassistantAnalyticsSensor(
coordinator,

View file

@ -3,10 +3,12 @@
"step": {
"user": {
"data": {
"tracked_addons": "Addons",
"tracked_integrations": "Integrations",
"tracked_custom_integrations": "Custom integrations"
},
"data_description": {
"tracked_addons": "Select the addons you want to track",
"tracked_integrations": "Select the integrations you want to track",
"tracked_custom_integrations": "Select the custom integrations you want to track"
}
@ -24,10 +26,12 @@
"step": {
"init": {
"data": {
"tracked_addons": "[%key:component::analytics_insights::config::step::user::data::tracked_addons%]",
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_integrations%]",
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_custom_integrations%]"
},
"data_description": {
"tracked_addons": "[%key:component::analytics_insights::config::step::user::data_description::tracked_addons%]",
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_integrations%]",
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_custom_integrations%]"
}

View file

@ -4,6 +4,7 @@ from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass
import logging
import os
from typing import Any
@ -40,6 +41,7 @@ from .const import (
CONF_ADB_SERVER_IP,
CONF_ADB_SERVER_PORT,
CONF_ADBKEY,
CONF_SCREENCAP_INTERVAL,
CONF_STATE_DETECTION_RULES,
DEFAULT_ADB_SERVER_PORT,
DEVICE_ANDROIDTV,
@ -66,6 +68,8 @@ RELOAD_OPTIONS = [CONF_STATE_DETECTION_RULES]
_INVALID_MACS = {"ff:ff:ff:ff:ff:ff"}
_LOGGER = logging.getLogger(__name__)
@dataclass
class AndroidTVRuntimeData:
@ -157,6 +161,32 @@ async def async_connect_androidtv(
return aftv, None
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
_LOGGER.debug(
"Migrating configuration from version %s.%s", entry.version, entry.minor_version
)
if entry.version == 1:
new_options = {**entry.options}
# Migrate MinorVersion 1 -> MinorVersion 2: New option
if entry.minor_version < 2:
new_options = {**new_options, CONF_SCREENCAP_INTERVAL: 0}
hass.config_entries.async_update_entry(
entry, options=new_options, minor_version=2, version=1
)
_LOGGER.debug(
"Migration to configuration version %s.%s successful",
entry.version,
entry.minor_version,
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: AndroidTVConfigEntry) -> bool:
"""Set up Android Debug Bridge platform."""

View file

@ -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
@ -34,7 +34,7 @@ from .const import (
CONF_APPS,
CONF_EXCLUDE_UNNAMED_APPS,
CONF_GET_SOURCES,
CONF_SCREENCAP,
CONF_SCREENCAP_INTERVAL,
CONF_STATE_DETECTION_RULES,
CONF_TURN_OFF_COMMAND,
CONF_TURN_ON_COMMAND,
@ -43,7 +43,7 @@ from .const import (
DEFAULT_EXCLUDE_UNNAMED_APPS,
DEFAULT_GET_SOURCES,
DEFAULT_PORT,
DEFAULT_SCREENCAP,
DEFAULT_SCREENCAP_INTERVAL,
DEVICE_CLASSES,
DOMAIN,
PROP_ETHMAC,
@ -76,6 +76,7 @@ class AndroidTVFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
VERSION = 1
MINOR_VERSION = 2
@callback
def _show_setup_form(
@ -185,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
@ -236,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(
{
@ -253,10 +252,12 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry):
CONF_EXCLUDE_UNNAMED_APPS, DEFAULT_EXCLUDE_UNNAMED_APPS
),
): bool,
vol.Optional(
CONF_SCREENCAP,
default=options.get(CONF_SCREENCAP, DEFAULT_SCREENCAP),
): bool,
vol.Required(
CONF_SCREENCAP_INTERVAL,
default=options.get(
CONF_SCREENCAP_INTERVAL, DEFAULT_SCREENCAP_INTERVAL
),
): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=15)),
vol.Optional(
CONF_TURN_OFF_COMMAND,
description={

View file

@ -9,6 +9,7 @@ CONF_APPS = "apps"
CONF_EXCLUDE_UNNAMED_APPS = "exclude_unnamed_apps"
CONF_GET_SOURCES = "get_sources"
CONF_SCREENCAP = "screencap"
CONF_SCREENCAP_INTERVAL = "screencap_interval"
CONF_STATE_DETECTION_RULES = "state_detection_rules"
CONF_TURN_OFF_COMMAND = "turn_off_command"
CONF_TURN_ON_COMMAND = "turn_on_command"
@ -18,7 +19,7 @@ DEFAULT_DEVICE_CLASS = "auto"
DEFAULT_EXCLUDE_UNNAMED_APPS = False
DEFAULT_GET_SOURCES = True
DEFAULT_PORT = 5555
DEFAULT_SCREENCAP = True
DEFAULT_SCREENCAP_INTERVAL = 5
DEVICE_ANDROIDTV = "androidtv"
DEVICE_FIRETV = "firetv"

View file

@ -2,10 +2,9 @@
from __future__ import annotations
from datetime import timedelta
from datetime import datetime, timedelta
import hashlib
import logging
from typing import Any
from androidtv.constants import APPS, KEYS
from androidtv.setup_async import AndroidTVAsync, FireTVAsync
@ -23,19 +22,19 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import Throttle
from homeassistant.util.dt import utcnow
from . import AndroidTVConfigEntry
from .const import (
CONF_APPS,
CONF_EXCLUDE_UNNAMED_APPS,
CONF_GET_SOURCES,
CONF_SCREENCAP,
CONF_SCREENCAP_INTERVAL,
CONF_TURN_OFF_COMMAND,
CONF_TURN_ON_COMMAND,
DEFAULT_EXCLUDE_UNNAMED_APPS,
DEFAULT_GET_SOURCES,
DEFAULT_SCREENCAP,
DEFAULT_SCREENCAP_INTERVAL,
DEVICE_ANDROIDTV,
SIGNAL_CONFIG_ENTITY,
)
@ -48,8 +47,6 @@ ATTR_DEVICE_PATH = "device_path"
ATTR_HDMI_INPUT = "hdmi_input"
ATTR_LOCAL_PATH = "local_path"
MIN_TIME_BETWEEN_SCREENCAPS = timedelta(seconds=60)
SERVICE_ADB_COMMAND = "adb_command"
SERVICE_DOWNLOAD = "download"
SERVICE_LEARN_SENDEVENT = "learn_sendevent"
@ -125,7 +122,8 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
self._app_name_to_id: dict[str, str] = {}
self._get_sources = DEFAULT_GET_SOURCES
self._exclude_unnamed_apps = DEFAULT_EXCLUDE_UNNAMED_APPS
self._screencap = DEFAULT_SCREENCAP
self._screencap_delta: timedelta | None = None
self._last_screencap: datetime | None = None
self.turn_on_command: str | None = None
self.turn_off_command: str | None = None
@ -159,7 +157,13 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
self._exclude_unnamed_apps = options.get(
CONF_EXCLUDE_UNNAMED_APPS, DEFAULT_EXCLUDE_UNNAMED_APPS
)
self._screencap = options.get(CONF_SCREENCAP, DEFAULT_SCREENCAP)
screencap_interval: int = options.get(
CONF_SCREENCAP_INTERVAL, DEFAULT_SCREENCAP_INTERVAL
)
if screencap_interval > 0:
self._screencap_delta = timedelta(minutes=screencap_interval)
else:
self._screencap_delta = None
self.turn_off_command = options.get(CONF_TURN_OFF_COMMAND)
self.turn_on_command = options.get(CONF_TURN_ON_COMMAND)
@ -183,7 +187,7 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
async def _async_get_screencap(self, prev_app_id: str | None = None) -> None:
"""Take a screen capture from the device when enabled."""
if (
not self._screencap
not self._screencap_delta
or self.state in {MediaPlayerState.OFF, None}
or not self.available
):
@ -193,11 +197,18 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
force: bool = prev_app_id is not None
if force:
force = prev_app_id != self._attr_app_id
await self._adb_get_screencap(no_throttle=force)
await self._adb_get_screencap(force)
@Throttle(MIN_TIME_BETWEEN_SCREENCAPS)
async def _adb_get_screencap(self, **kwargs: Any) -> None:
"""Take a screen capture from the device every 60 seconds."""
async def _adb_get_screencap(self, force: bool = False) -> None:
"""Take a screen capture from the device every configured minutes."""
time_elapsed = self._screencap_delta is not None and (
self._last_screencap is None
or (utcnow() - self._last_screencap) >= self._screencap_delta
)
if not (force or time_elapsed):
return
self._last_screencap = utcnow()
if media_data := await self._adb_screencap():
self._media_image = media_data, "image/png"
self._attr_media_image_hash = hashlib.sha256(media_data).hexdigest()[:16]

View file

@ -31,7 +31,7 @@
"apps": "Configure applications list",
"get_sources": "Retrieve the running apps as the list of sources",
"exclude_unnamed_apps": "Exclude apps with unknown name from the sources list",
"screencap": "Use screen capture for album art",
"screencap_interval": "Interval in minutes between screen capture for album art (set 0 to disable)",
"state_detection_rules": "Configure state detection rules",
"turn_off_command": "ADB shell turn off command (leave empty for default)",
"turn_on_command": "ADB shell turn on command (leave empty for default)"

View file

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

View file

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

View file

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

View file

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

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:

View file

@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/autarco",
"iot_class": "cloud_polling",
"requirements": ["autarco==3.0.0"]
"requirements": ["autarco==3.1.0"]
}

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

View file

@ -124,7 +124,9 @@ class AEHConfigFlow(ConfigFlow, domain=DOMAIN):
step_id=STEP_CONN_STRING,
data_schema=CONN_STRING_SCHEMA,
errors=errors,
description_placeholders=self._data[CONF_EVENT_HUB_INSTANCE_NAME],
description_placeholders={
"event_hub_instance_name": self._data[CONF_EVENT_HUB_INSTANCE_NAME]
},
last_step=True,
)
@ -144,7 +146,9 @@ class AEHConfigFlow(ConfigFlow, domain=DOMAIN):
step_id=STEP_SAS,
data_schema=SAS_SCHEMA,
errors=errors,
description_placeholders=self._data[CONF_EVENT_HUB_INSTANCE_NAME],
description_placeholders={
"event_hub_instance_name": self._data[CONF_EVENT_HUB_INSTANCE_NAME]
},
last_step=True,
)

View file

@ -1,8 +1,8 @@
"""The Backup integration."""
from homeassistant.components.hassio import is_hassio
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.typing import ConfigType
from .const import DATA_MANAGER, DOMAIN, LOGGER
@ -32,7 +32,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_handle_create_service(call: ServiceCall) -> None:
"""Service handler for creating backups."""
await backup_manager.async_create_backup()
await backup_manager.async_create_backup(on_progress=None)
if backup_task := backup_manager.backup_task:
await backup_task
hass.services.async_register(DOMAIN, "create", async_handle_create_service)

View file

@ -17,6 +17,7 @@ LOGGER = getLogger(__package__)
EXCLUDE_FROM_BACKUP = [
"__pycache__/*",
".DS_Store",
".HA_RESTORE",
"*.db-shm",
"*.log.*",
"*.log",

View file

@ -2,23 +2,26 @@
from __future__ import annotations
import asyncio
from http import HTTPStatus
from typing import cast
from aiohttp import BodyPartReader
from aiohttp.hdrs import CONTENT_DISPOSITION
from aiohttp.web import FileResponse, Request, Response
from homeassistant.components.http import KEY_HASS, HomeAssistantView
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
from homeassistant.core import HomeAssistant, callback
from homeassistant.util import slugify
from .const import DOMAIN
from .manager import BaseBackupManager
from .const import DATA_MANAGER
@callback
def async_register_http_views(hass: HomeAssistant) -> None:
"""Register the http views."""
hass.http.register_view(DownloadBackupView)
hass.http.register_view(UploadBackupView)
class DownloadBackupView(HomeAssistantView):
@ -36,7 +39,7 @@ class DownloadBackupView(HomeAssistantView):
if not request["hass_user"].is_admin:
return Response(status=HTTPStatus.UNAUTHORIZED)
manager: BaseBackupManager = request.app[KEY_HASS].data[DOMAIN]
manager = request.app[KEY_HASS].data[DATA_MANAGER]
backup = await manager.async_get_backup(slug=slug)
if backup is None or not backup.path.exists():
@ -48,3 +51,29 @@ class DownloadBackupView(HomeAssistantView):
CONTENT_DISPOSITION: f"attachment; filename={slugify(backup.name)}.tar"
},
)
class UploadBackupView(HomeAssistantView):
"""Generate backup view."""
url = "/api/backup/upload"
name = "api:backup:upload"
@require_admin
async def post(self, request: Request) -> Response:
"""Upload a backup file."""
manager = request.app[KEY_HASS].data[DATA_MANAGER]
reader = await request.multipart()
contents = cast(BodyPartReader, await reader.next())
try:
await manager.async_receive_backup(contents=contents)
except OSError as err:
return Response(
body=f"Can't write backup file {err}",
status=HTTPStatus.INTERNAL_SERVER_ERROR,
)
except asyncio.CancelledError:
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)
return Response(status=HTTPStatus.CREATED)

View file

@ -4,18 +4,24 @@ from __future__ import annotations
import abc
import asyncio
from collections.abc import Callable
from dataclasses import asdict, dataclass
import hashlib
import io
import json
from pathlib import Path
from queue import SimpleQueue
import shutil
import tarfile
from tarfile import TarError
from tempfile import TemporaryDirectory
import time
from typing import Any, Protocol, cast
import aiohttp
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
@ -29,6 +35,13 @@ from .const import DOMAIN, EXCLUDE_FROM_BACKUP, LOGGER
BUF_SIZE = 2**20 * 4 # 4MB
@dataclass(slots=True)
class NewBackup:
"""New backup class."""
slug: str
@dataclass(slots=True)
class Backup:
"""Backup class."""
@ -44,6 +57,15 @@ class Backup:
return {**asdict(self), "path": self.path.as_posix()}
@dataclass(slots=True)
class BackupProgress:
"""Backup progress class."""
done: bool
stage: str | None
success: bool | None
class BackupPlatformProtocol(Protocol):
"""Define the format that backup platforms can have."""
@ -60,7 +82,7 @@ class BaseBackupManager(abc.ABC):
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the backup manager."""
self.hass = hass
self.backing_up = False
self.backup_task: asyncio.Task | None = None
self.backups: dict[str, Backup] = {}
self.loaded_platforms = False
self.platforms: dict[str, BackupPlatformProtocol] = {}
@ -124,7 +146,16 @@ class BaseBackupManager(abc.ABC):
self.loaded_platforms = True
@abc.abstractmethod
async def async_create_backup(self, **kwargs: Any) -> Backup:
async def async_restore_backup(self, slug: str, **kwargs: Any) -> None:
"""Restore a backup."""
@abc.abstractmethod
async def async_create_backup(
self,
*,
on_progress: Callable[[BackupProgress], None] | None,
**kwargs: Any,
) -> NewBackup:
"""Generate a backup."""
@abc.abstractmethod
@ -142,6 +173,15 @@ class BaseBackupManager(abc.ABC):
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None:
"""Remove a backup."""
@abc.abstractmethod
async def async_receive_backup(
self,
*,
contents: aiohttp.BodyPartReader,
**kwargs: Any,
) -> None:
"""Receive and store a backup file from upload."""
class BackupManager(BaseBackupManager):
"""Backup manager for the Backup integration."""
@ -217,17 +257,93 @@ class BackupManager(BaseBackupManager):
LOGGER.debug("Removed backup located at %s", backup.path)
self.backups.pop(slug)
async def async_create_backup(self, **kwargs: Any) -> Backup:
"""Generate a backup."""
if self.backing_up:
raise HomeAssistantError("Backup already in progress")
async def async_receive_backup(
self,
*,
contents: aiohttp.BodyPartReader,
**kwargs: Any,
) -> None:
"""Receive and store a backup file from upload."""
queue: SimpleQueue[tuple[bytes, asyncio.Future[None] | None] | None] = (
SimpleQueue()
)
temp_dir_handler = await self.hass.async_add_executor_job(TemporaryDirectory)
target_temp_file = Path(
temp_dir_handler.name, contents.filename or "backup.tar"
)
def _sync_queue_consumer() -> None:
with target_temp_file.open("wb") as file_handle:
while True:
if (_chunk_future := queue.get()) is None:
break
_chunk, _future = _chunk_future
if _future is not None:
self.hass.loop.call_soon_threadsafe(_future.set_result, None)
file_handle.write(_chunk)
fut: asyncio.Future[None] | None = None
try:
fut = self.hass.async_add_executor_job(_sync_queue_consumer)
megabytes_sending = 0
while chunk := await contents.read_chunk(BUF_SIZE):
megabytes_sending += 1
if megabytes_sending % 5 != 0:
queue.put_nowait((chunk, None))
continue
chunk_future = self.hass.loop.create_future()
queue.put_nowait((chunk, chunk_future))
await asyncio.wait(
(fut, chunk_future),
return_when=asyncio.FIRST_COMPLETED,
)
if fut.done():
# The executor job failed
break
queue.put_nowait(None) # terminate queue consumer
finally:
if fut is not None:
await fut
def _move_and_cleanup() -> None:
shutil.move(target_temp_file, self.backup_dir / target_temp_file.name)
temp_dir_handler.cleanup()
await self.hass.async_add_executor_job(_move_and_cleanup)
await self.load_backups()
async def async_create_backup(
self,
*,
on_progress: Callable[[BackupProgress], None] | None,
**kwargs: Any,
) -> NewBackup:
"""Generate a backup."""
if self.backup_task:
raise HomeAssistantError("Backup already in progress")
backup_name = f"Core {HAVERSION}"
date_str = dt_util.now().isoformat()
slug = _generate_slug(date_str, backup_name)
self.backup_task = self.hass.async_create_task(
self._async_create_backup(backup_name, date_str, slug, on_progress),
name="backup_manager_create_backup",
eager_start=False, # To ensure the task is not started before we return
)
return NewBackup(slug=slug)
async def _async_create_backup(
self,
backup_name: str,
date_str: str,
slug: str,
on_progress: Callable[[BackupProgress], None] | None,
) -> Backup:
"""Generate a backup."""
success = False
try:
self.backing_up = True
await self.async_pre_backup_actions()
backup_name = f"Core {HAVERSION}"
date_str = dt_util.now().isoformat()
slug = _generate_slug(date_str, backup_name)
backup_data = {
"slug": slug,
@ -254,9 +370,12 @@ class BackupManager(BaseBackupManager):
if self.loaded_backups:
self.backups[slug] = backup
LOGGER.debug("Generated new backup with slug %s", slug)
success = True
return backup
finally:
self.backing_up = False
if on_progress:
on_progress(BackupProgress(done=True, stage=None, success=success))
self.backup_task = None
await self.async_post_backup_actions()
def _mkdir_and_generate_backup_contents(
@ -291,6 +410,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."""

View file

@ -8,6 +8,7 @@ from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from .const import DATA_MANAGER, LOGGER
from .manager import BackupProgress
@callback
@ -22,6 +23,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
@ -39,7 +41,7 @@ async def handle_info(
msg["id"],
{
"backups": list(backups.values()),
"backing_up": manager.backing_up,
"backing_up": manager.backup_task is not None,
},
)
@ -85,6 +87,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
@ -94,7 +114,11 @@ async def handle_create(
msg: dict[str, Any],
) -> None:
"""Generate a backup."""
backup = await hass.data[DATA_MANAGER].async_create_backup()
def on_progress(progress: BackupProgress) -> None:
connection.send_message(websocket_api.event_message(msg["id"], progress))
backup = await hass.data[DATA_MANAGER].async_create_backup(on_progress=on_progress)
connection.send_result(msg["id"], backup)
@ -108,7 +132,6 @@ async def handle_backup_start(
) -> None:
"""Backup start notification."""
manager = hass.data[DATA_MANAGER]
manager.backing_up = True
LOGGER.debug("Backup start notification")
try:
@ -130,7 +153,6 @@ async def handle_backup_end(
) -> None:
"""Backup end notification."""
manager = hass.data[DATA_MANAGER]
manager.backing_up = False
LOGGER.debug("Backup end notification")
try:

View file

@ -17,46 +17,9 @@ from homeassistant.components.media_player import (
class BangOlufsenSource:
"""Class used for associating device source ids with friendly names. May not include all sources."""
URI_STREAMER: Final[Source] = Source(
name="Audio Streamer",
id="uriStreamer",
is_seekable=False,
)
BLUETOOTH: Final[Source] = Source(
name="Bluetooth",
id="bluetooth",
is_seekable=False,
)
CHROMECAST: Final[Source] = Source(
name="Chromecast built-in",
id="chromeCast",
is_seekable=False,
)
LINE_IN: Final[Source] = Source(
name="Line-In",
id="lineIn",
is_seekable=False,
)
SPDIF: Final[Source] = Source(
name="Optical",
id="spdif",
is_seekable=False,
)
NET_RADIO: Final[Source] = Source(
name="B&O Radio",
id="netRadio",
is_seekable=False,
)
DEEZER: Final[Source] = Source(
name="Deezer",
id="deezer",
is_seekable=True,
)
TIDAL: Final[Source] = Source(
name="Tidal",
id="tidal",
is_seekable=True,
)
LINE_IN: Final[Source] = Source(name="Line-In", id="lineIn")
SPDIF: Final[Source] = Source(name="Optical", id="spdif")
URI_STREAMER: Final[Source] = Source(name="Audio Streamer", id="uriStreamer")
BANG_OLUFSEN_STATES: dict[str, MediaPlayerState] = {
@ -170,20 +133,6 @@ VALID_MEDIA_TYPES: Final[tuple] = (
MediaType.CHANNEL,
)
# Sources on the device that should not be selectable by the user
HIDDEN_SOURCE_IDS: Final[tuple] = (
"airPlay",
"bluetooth",
"chromeCast",
"generator",
"local",
"dlna",
"qplay",
"wpl",
"pl",
"beolink",
"usbIn",
)
# Fallback sources to use in case of API failure.
FALLBACK_SOURCES: Final[SourceArray] = SourceArray(
@ -191,7 +140,7 @@ FALLBACK_SOURCES: Final[SourceArray] = SourceArray(
Source(
id="uriStreamer",
is_enabled=True,
is_playable=False,
is_playable=True,
name="Audio Streamer",
type=SourceTypeEnum(value="uriStreamer"),
is_seekable=False,
@ -199,7 +148,7 @@ FALLBACK_SOURCES: Final[SourceArray] = SourceArray(
Source(
id="bluetooth",
is_enabled=True,
is_playable=False,
is_playable=True,
name="Bluetooth",
type=SourceTypeEnum(value="bluetooth"),
is_seekable=False,
@ -207,7 +156,7 @@ FALLBACK_SOURCES: Final[SourceArray] = SourceArray(
Source(
id="spotify",
is_enabled=True,
is_playable=False,
is_playable=True,
name="Spotify Connect",
type=SourceTypeEnum(value="spotify"),
is_seekable=True,

View file

@ -0,0 +1,9 @@
{
"services": {
"beolink_join": { "service": "mdi:location-enter" },
"beolink_expand": { "service": "mdi:location-enter" },
"beolink_unexpand": { "service": "mdi:location-exit" },
"beolink_leave": { "service": "mdi:close-circle-outline" },
"beolink_allstandby": { "service": "mdi:close-circle-multiple-outline" }
}
}

View file

@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Any, cast
from aiohttp import ClientConnectorError
from mozart_api import __version__ as MOZART_API_VERSION
from mozart_api.exceptions import ApiException
from mozart_api.exceptions import ApiException, NotFoundException
from mozart_api.models import (
Action,
Art,
@ -38,6 +38,7 @@ from mozart_api.models import (
VolumeState,
)
from mozart_api.mozart_client import MozartClient, get_highest_resolution_artwork
import voluptuous as vol
from homeassistant.components import media_source
from homeassistant.components.media_player import (
@ -55,10 +56,17 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MODEL, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_current_platform,
)
from homeassistant.util.dt import utcnow
from . import BangOlufsenConfigEntry
@ -70,7 +78,6 @@ from .const import (
CONNECTION_STATUS,
DOMAIN,
FALLBACK_SOURCES,
HIDDEN_SOURCE_IDS,
VALID_MEDIA_TYPES,
BangOlufsenMediaType,
BangOlufsenSource,
@ -117,6 +124,58 @@ async def async_setup_entry(
]
)
# Register actions.
platform = async_get_current_platform()
jid_regex = vol.Match(
r"(^\d{4})[.](\d{7})[.](\d{8})(@products\.bang-olufsen\.com)$"
)
platform.async_register_entity_service(
name="beolink_join",
schema={vol.Optional("beolink_jid"): jid_regex},
func="async_beolink_join",
)
platform.async_register_entity_service(
name="beolink_expand",
schema={
vol.Exclusive("all_discovered", "devices", ""): cv.boolean,
vol.Exclusive(
"beolink_jids",
"devices",
"Define either specific Beolink JIDs or all discovered",
): vol.All(
cv.ensure_list,
[jid_regex],
),
},
func="async_beolink_expand",
)
platform.async_register_entity_service(
name="beolink_unexpand",
schema={
vol.Required("beolink_jids"): vol.All(
cv.ensure_list,
[jid_regex],
),
},
func="async_beolink_unexpand",
)
platform.async_register_entity_service(
name="beolink_leave",
schema=None,
func="async_beolink_leave",
)
platform.async_register_entity_service(
name="beolink_allstandby",
schema=None,
func="async_beolink_allstandby",
)
class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
"""Representation of a media player."""
@ -157,6 +216,8 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# Beolink compatible sources
self._beolink_sources: dict[str, bool] = {}
self._remote_leader: BeolinkLeader | None = None
# Extra state attributes for showing Beolink: peer(s), listener(s), leader and self
self._beolink_attributes: dict[str, dict[str, dict[str, str]]] = {}
async def async_added_to_hass(self) -> None:
"""Turn on the dispatchers."""
@ -166,9 +227,11 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
CONNECTION_STATUS: self._async_update_connection_state,
WebsocketNotification.ACTIVE_LISTENING_MODE: self._async_update_sound_modes,
WebsocketNotification.BEOLINK: self._async_update_beolink,
WebsocketNotification.CONFIGURATION: self._async_update_name_and_beolink,
WebsocketNotification.PLAYBACK_ERROR: self._async_update_playback_error,
WebsocketNotification.PLAYBACK_METADATA: self._async_update_playback_metadata_and_beolink,
WebsocketNotification.PLAYBACK_PROGRESS: self._async_update_playback_progress,
WebsocketNotification.PLAYBACK_SOURCE: self._async_update_sources,
WebsocketNotification.PLAYBACK_STATE: self._async_update_playback_state,
WebsocketNotification.REMOTE_MENU_CHANGED: self._async_update_sources,
WebsocketNotification.SOURCE_CHANGE: self._async_update_source_change,
@ -230,6 +293,9 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
await self._async_update_sound_modes()
# Update beolink attributes and device name.
await self._async_update_name_and_beolink()
async def async_update(self) -> None:
"""Update queue settings."""
# The WebSocket event listener is the main handler for connection state.
@ -243,7 +309,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
if queue_settings.shuffle is not None:
self._attr_shuffle = queue_settings.shuffle
async def _async_update_sources(self) -> None:
async def _async_update_sources(self, _: Source | None = None) -> None:
"""Get sources for the specific product."""
# Audio sources
@ -270,10 +336,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
self._audio_sources = {
source.id: source.name
for source in cast(list[Source], sources.items)
if source.is_enabled
and source.id
and source.name
and source.id not in HIDDEN_SOURCE_IDS
if source.is_enabled and source.id and source.name and source.is_playable
}
# Some sources are not Beolink expandable, meaning that they can't be joined by
@ -375,9 +438,44 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
self.async_write_ha_state()
async def _async_update_name_and_beolink(self) -> None:
"""Update the device friendly name."""
beolink_self = await self._client.get_beolink_self()
# Update device name
device_registry = dr.async_get(self.hass)
assert self.device_entry is not None
device_registry.async_update_device(
device_id=self.device_entry.id,
name=beolink_self.friendly_name,
)
await self._async_update_beolink()
async def _async_update_beolink(self) -> None:
"""Update the current Beolink leader, listeners, peers and self."""
self._beolink_attributes = {}
assert self.device_entry is not None
assert self.device_entry.name is not None
# Add Beolink self
self._beolink_attributes = {
"beolink": {"self": {self.device_entry.name: self._beolink_jid}}
}
# Add Beolink peers
peers = await self._client.get_beolink_peers()
if len(peers) > 0:
self._beolink_attributes["beolink"]["peers"] = {}
for peer in peers:
self._beolink_attributes["beolink"]["peers"][peer.friendly_name] = (
peer.jid
)
# Add Beolink listeners / leader
self._remote_leader = self._playback_metadata.remote_leader
@ -397,9 +495,14 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# Add self
group_members.append(self.entity_id)
self._beolink_attributes["beolink"]["leader"] = {
self._remote_leader.friendly_name: self._remote_leader.jid,
}
# If not listener, check if leader.
else:
beolink_listeners = await self._client.get_beolink_listeners()
beolink_listeners_attribute = {}
# Check if the device is a leader.
if len(beolink_listeners) > 0:
@ -420,6 +523,18 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
for beolink_listener in beolink_listeners
]
)
# Update Beolink attributes
for beolink_listener in beolink_listeners:
for peer in peers:
if peer.jid == beolink_listener.jid:
# Get the friendly names for the listeners from the peers
beolink_listeners_attribute[peer.friendly_name] = (
beolink_listener.jid
)
break
self._beolink_attributes["beolink"]["listeners"] = (
beolink_listeners_attribute
)
self._attr_group_members = group_members
@ -573,38 +688,19 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
@property
def source(self) -> str | None:
"""Return the current audio source."""
# Try to fix some of the source_change chromecast weirdness.
if hasattr(self._playback_metadata, "title"):
# source_change is chromecast but line in is selected.
if self._playback_metadata.title == BangOlufsenSource.LINE_IN.name:
return BangOlufsenSource.LINE_IN.name
# source_change is chromecast but bluetooth is selected.
if self._playback_metadata.title == BangOlufsenSource.BLUETOOTH.name:
return BangOlufsenSource.BLUETOOTH.name
# source_change is line in, bluetooth or optical but stale metadata is sent through the WebSocket,
# And the source has not changed.
if self._source_change.id in (
BangOlufsenSource.BLUETOOTH.id,
BangOlufsenSource.LINE_IN.id,
BangOlufsenSource.SPDIF.id,
):
return BangOlufsenSource.CHROMECAST.name
# source_change is chromecast and there is metadata but no artwork. Bluetooth does support metadata but not artwork
# So i assume that it is bluetooth and not chromecast
if (
hasattr(self._playback_metadata, "art")
and self._playback_metadata.art is not None
and len(self._playback_metadata.art) == 0
and self._source_change.id == BangOlufsenSource.CHROMECAST.id
):
return BangOlufsenSource.BLUETOOTH.name
return self._source_change.name
@property
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return information that is not returned anywhere else."""
attributes: dict[str, Any] = {}
# Add Beolink attributes
if self._beolink_attributes:
attributes.update(self._beolink_attributes)
return attributes
async def async_turn_off(self) -> None:
"""Set the device to "networkStandby"."""
await self._client.post_standby()
@ -876,23 +972,30 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# Beolink compatible B&O device.
# Repeated presses / calls will cycle between compatible playing devices.
if len(group_members) == 0:
await self._async_beolink_join()
await self.async_beolink_join()
return
# Get JID for each group member
jids = [self._get_beolink_jid(group_member) for group_member in group_members]
await self._async_beolink_expand(jids)
await self.async_beolink_expand(jids)
async def async_unjoin_player(self) -> None:
"""Unjoin Beolink session. End session if leader."""
await self._async_beolink_leave()
await self.async_beolink_leave()
async def _async_beolink_join(self) -> None:
# Custom actions:
async def async_beolink_join(self, beolink_jid: str | None = None) -> None:
"""Join a Beolink multi-room experience."""
await self._client.join_latest_beolink_experience()
if beolink_jid is None:
await self._client.join_latest_beolink_experience()
else:
await self._client.join_beolink_peer(jid=beolink_jid)
async def _async_beolink_expand(self, beolink_jids: list[str]) -> None:
async def async_beolink_expand(
self, beolink_jids: list[str] | None = None, all_discovered: bool = False
) -> None:
"""Expand a Beolink multi-room experience with a device or devices."""
# Ensure that the current source is expandable
if not self._beolink_sources[cast(str, self._source_change.id)]:
raise ServiceValidationError(
@ -904,10 +1007,37 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
},
)
# Try to expand to all defined devices
for beolink_jid in beolink_jids:
await self._client.post_beolink_expand(jid=beolink_jid)
# Expand to all discovered devices
if all_discovered:
peers = await self._client.get_beolink_peers()
async def _async_beolink_leave(self) -> None:
for peer in peers:
try:
await self._client.post_beolink_expand(jid=peer.jid)
except NotFoundException:
_LOGGER.warning("Unable to expand to %s", peer.jid)
# Try to expand to all defined devices
elif beolink_jids:
for beolink_jid in beolink_jids:
try:
await self._client.post_beolink_expand(jid=beolink_jid)
except NotFoundException:
_LOGGER.warning(
"Unable to expand to %s. Is the device available on the network?",
beolink_jid,
)
async def async_beolink_unexpand(self, beolink_jids: list[str]) -> None:
"""Unexpand a Beolink multi-room experience with a device or devices."""
# Unexpand all defined devices
for beolink_jid in beolink_jids:
await self._client.post_beolink_unexpand(jid=beolink_jid)
async def async_beolink_leave(self) -> None:
"""Leave the current Beolink experience."""
await self._client.post_beolink_leave()
async def async_beolink_allstandby(self) -> None:
"""Set all connected Beolink devices to standby."""
await self._client.post_beolink_allstandby()

View file

@ -0,0 +1,79 @@
beolink_allstandby:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
beolink_expand:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
fields:
all_discovered:
required: false
example: false
selector:
boolean:
jid_options:
collapsed: false
fields:
beolink_jids:
required: false
example: >-
[
1111.2222222.33333333@products.bang-olufsen.com,
4444.5555555.66666666@products.bang-olufsen.com
]
selector:
object:
beolink_join:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
fields:
jid_options:
collapsed: false
fields:
beolink_jid:
required: false
example: 1111.2222222.33333333@products.bang-olufsen.com
selector:
text:
beolink_leave:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
beolink_unexpand:
target:
entity:
integration: bang_olufsen
domain: media_player
device:
integration: bang_olufsen
fields:
jid_options:
collapsed: false
fields:
beolink_jids:
required: true
example: >-
[
1111.2222222.33333333@products.bang-olufsen.com,
4444.5555555.66666666@products.bang-olufsen.com
]
selector:
object:

View file

@ -1,4 +1,8 @@
{
"common": {
"jid_options_name": "JID options",
"jid_options_description": "Advanced grouping options, where devices' unique Beolink IDs (Called JIDs) are used directly. JIDs can be found in the state attributes of the media player entity."
},
"config": {
"error": {
"api_exception": "[%key:common::config_flow::error::cannot_connect%]",
@ -25,6 +29,68 @@
}
}
},
"services": {
"beolink_allstandby": {
"name": "Beolink all standby",
"description": "Set all Connected Beolink devices to standby."
},
"beolink_expand": {
"name": "Beolink expand",
"description": "Expand current Beolink experience.",
"fields": {
"all_discovered": {
"name": "All discovered",
"description": "Expand Beolink experience to all discovered devices."
},
"beolink_jids": {
"name": "Beolink JIDs",
"description": "Specify which Beolink JIDs will join current Beolink experience."
}
},
"sections": {
"jid_options": {
"name": "[%key:component::bang_olufsen::common::jid_options_name%]",
"description": "[%key:component::bang_olufsen::common::jid_options_description%]"
}
}
},
"beolink_join": {
"name": "Beolink join",
"description": "Join a Beolink experience.",
"fields": {
"beolink_jid": {
"name": "Beolink JID",
"description": "Manually specify Beolink JID to join."
}
},
"sections": {
"jid_options": {
"name": "[%key:component::bang_olufsen::common::jid_options_name%]",
"description": "[%key:component::bang_olufsen::common::jid_options_description%]"
}
}
},
"beolink_leave": {
"name": "Beolink leave",
"description": "Leave a Beolink experience."
},
"beolink_unexpand": {
"name": "Beolink unexpand",
"description": "Unexpand from current Beolink experience.",
"fields": {
"beolink_jids": {
"name": "Beolink JIDs",
"description": "Specify which Beolink JIDs will leave from current Beolink experience."
}
},
"sections": {
"jid_options": {
"name": "[%key:component::bang_olufsen::common::jid_options_name%]",
"description": "[%key:component::bang_olufsen::common::jid_options_description%]"
}
}
}
},
"exceptions": {
"m3u_invalid_format": {
"message": "Media sources with the .m3u extension are not supported."

View file

@ -63,6 +63,9 @@ class BangOlufsenWebsocket(BangOlufsenBase):
self._client.get_playback_progress_notifications(
self.on_playback_progress_notification
)
self._client.get_playback_source_notifications(
self.on_playback_source_notification
)
self._client.get_playback_state_notifications(
self.on_playback_state_notification
)
@ -117,6 +120,11 @@ class BangOlufsenWebsocket(BangOlufsenBase):
self.hass,
f"{self._unique_id}_{WebsocketNotification.BEOLINK}",
)
elif notification_type is WebsocketNotification.CONFIGURATION:
async_dispatcher_send(
self.hass,
f"{self._unique_id}_{WebsocketNotification.CONFIGURATION}",
)
elif notification_type is WebsocketNotification.REMOTE_MENU_CHANGED:
async_dispatcher_send(
self.hass,
@ -157,6 +165,14 @@ class BangOlufsenWebsocket(BangOlufsenBase):
notification,
)
def on_playback_source_notification(self, notification: Source) -> None:
"""Send playback_source dispatch."""
async_dispatcher_send(
self.hass,
f"{self._unique_id}_{WebsocketNotification.PLAYBACK_SOURCE}",
notification,
)
def on_source_change_notification(self, notification: Source) -> None:
"""Send source_change dispatch."""
async_dispatcher_send(

View file

@ -10,7 +10,11 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import EntityCategory, UnitOfTemperature
from homeassistant.const import (
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -32,6 +36,8 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key=TYPE_WIFI_STRENGTH,
translation_key="wifi_strength",
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),

View file

@ -364,12 +364,13 @@ class BluesoundPlayer(MediaPlayerEntity):
if self.is_grouped and not self.is_master:
return MediaPlayerState.IDLE
status = self._status.state
if status in ("pause", "stop"):
return MediaPlayerState.PAUSED
if status in ("stream", "play"):
return MediaPlayerState.PLAYING
return MediaPlayerState.IDLE
match self._status.state:
case "pause":
return MediaPlayerState.PAUSED
case "stream" | "play":
return MediaPlayerState.PLAYING
case _:
return MediaPlayerState.IDLE
@property
def media_title(self) -> str | None:
@ -769,7 +770,7 @@ class BluesoundPlayer(MediaPlayerEntity):
async def async_set_volume_level(self, volume: float) -> None:
"""Send volume_up command to media player."""
volume = int(volume * 100)
volume = int(round(volume * 100))
volume = min(100, volume)
volume = max(0, volume)

View file

@ -7,7 +7,11 @@ from typing import Any
from bimmer_connected.api.authentication import MyBMWAuthentication
from bimmer_connected.api.regions import get_region_from_name
from bimmer_connected.models import MyBMWAPIError, MyBMWAuthError
from bimmer_connected.models import (
MyBMWAPIError,
MyBMWAuthError,
MyBMWCaptchaMissingError,
)
from httpx import RequestError
import voluptuous as vol
@ -17,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
@ -54,6 +58,8 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
try:
await auth.login()
except MyBMWCaptchaMissingError as ex:
raise MissingCaptcha from ex
except MyBMWAuthError as ex:
raise InvalidAuth from ex
except (MyBMWAPIError, RequestError) as ex:
@ -98,6 +104,8 @@ class BMWConfigFlow(ConfigFlow, domain=DOMAIN):
CONF_REFRESH_TOKEN: info.get(CONF_REFRESH_TOKEN),
CONF_GCID: info.get(CONF_GCID),
}
except MissingCaptcha:
errors["base"] = "missing_captcha"
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
@ -145,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(
@ -192,3 +200,7 @@ class CannotConnect(HomeAssistantError):
class InvalidAuth(HomeAssistantError):
"""Error to indicate there is invalid auth."""
class MissingCaptcha(HomeAssistantError):
"""Error to indicate the captcha token is missing."""

View file

@ -7,7 +7,12 @@ import logging
from bimmer_connected.account import MyBMWAccount
from bimmer_connected.api.regions import get_region_from_name
from bimmer_connected.models import GPSPosition, MyBMWAPIError, MyBMWAuthError
from bimmer_connected.models import (
GPSPosition,
MyBMWAPIError,
MyBMWAuthError,
MyBMWCaptchaMissingError,
)
from httpx import RequestError
from homeassistant.config_entries import ConfigEntry
@ -61,6 +66,12 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
try:
await self.account.get_vehicles()
except MyBMWCaptchaMissingError as err:
# If a captcha is required (user/password login flow), always trigger the reauth flow
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="missing_captcha",
) from err
except MyBMWAuthError as err:
# Allow one retry interval before raising AuthFailed to avoid flaky API issues
if self.last_update_success:

View file

@ -7,5 +7,5 @@
"iot_class": "cloud_polling",
"loggers": ["bimmer_connected"],
"quality_scale": "platinum",
"requirements": ["bimmer-connected[china]==0.16.3"]
"requirements": ["bimmer-connected[china]==0.16.4"]
}

View file

@ -11,7 +11,8 @@
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"missing_captcha": "Captcha validation missing"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
@ -200,6 +201,9 @@
"exceptions": {
"invalid_poi": {
"message": "Invalid data for point of interest: {poi_exception}"
},
"missing_captcha": {
"message": "Login requires captcha validation"
}
}
}

View file

@ -39,16 +39,21 @@ HOST_SCHEMA = vol.Schema(
)
def write_tls_asset(hass: HomeAssistant, filename: str, asset: bytes) -> None:
def write_tls_asset(
hass: HomeAssistant, folder: str, filename: str, asset: bytes
) -> None:
"""Write the tls assets to disk."""
makedirs(hass.config.path(DOMAIN), exist_ok=True)
with open(hass.config.path(DOMAIN, filename), "w", encoding="utf8") as file_handle:
makedirs(hass.config.path(DOMAIN, folder), exist_ok=True)
with open(
hass.config.path(DOMAIN, folder, filename), "w", encoding="utf8"
) as file_handle:
file_handle.write(asset.decode("utf-8"))
def create_credentials_and_validate(
hass: HomeAssistant,
host: str,
unique_id: str,
user_input: dict[str, Any],
zeroconf_instance: zeroconf.HaZeroconf,
) -> dict[str, Any] | None:
@ -57,13 +62,15 @@ def create_credentials_and_validate(
result = helper.register(host, "HomeAssistant")
if result is not None:
write_tls_asset(hass, CONF_SHC_CERT, result["cert"])
write_tls_asset(hass, CONF_SHC_KEY, result["key"])
# Save key/certificate pair for each registered host separately
# otherwise only the last registered host is accessible.
write_tls_asset(hass, unique_id, CONF_SHC_CERT, result["cert"])
write_tls_asset(hass, unique_id, CONF_SHC_KEY, result["key"])
session = SHCSession(
host,
hass.config.path(DOMAIN, CONF_SHC_CERT),
hass.config.path(DOMAIN, CONF_SHC_KEY),
hass.config.path(DOMAIN, unique_id, CONF_SHC_CERT),
hass.config.path(DOMAIN, unique_id, CONF_SHC_KEY),
True,
zeroconf_instance,
)
@ -143,11 +150,16 @@ class BoschSHCConfigFlow(ConfigFlow, domain=DOMAIN):
errors: dict[str, str] = {}
if user_input is not None:
zeroconf_instance = await zeroconf.async_get_instance(self.hass)
# unique_id uniquely identifies the registered controller and is used
# to save the key/certificate pair for each controller separately
unique_id = self.info["unique_id"]
assert unique_id
try:
result = await self.hass.async_add_executor_job(
create_credentials_and_validate,
self.hass,
self.host,
unique_id,
user_input,
zeroconf_instance,
)
@ -167,13 +179,18 @@ class BoschSHCConfigFlow(ConfigFlow, domain=DOMAIN):
else:
assert result
entry_data = {
CONF_SSL_CERTIFICATE: self.hass.config.path(DOMAIN, CONF_SHC_CERT),
CONF_SSL_KEY: self.hass.config.path(DOMAIN, CONF_SHC_KEY),
# Each host has its own key/certificate pair
CONF_SSL_CERTIFICATE: self.hass.config.path(
DOMAIN, unique_id, CONF_SHC_CERT
),
CONF_SSL_KEY: self.hass.config.path(
DOMAIN, unique_id, CONF_SHC_KEY
),
CONF_HOST: self.host,
CONF_TOKEN: result["token"],
CONF_HOSTNAME: result["token"].split(":", 1)[1],
}
existing_entry = await self.async_set_unique_id(self.info["unique_id"])
existing_entry = await self.async_set_unique_id(unique_id)
if existing_entry:
return self.async_update_reload_and_abort(
existing_entry,

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

View file

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/bring",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["bring-api==0.9.0"]
"requirements": ["bring-api==0.9.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,
),
)

View file

@ -66,7 +66,8 @@
"name": "List access",
"state": {
"registered": "Private",
"shared": "Shared"
"shared": "Shared",
"invitation": "Invitation pending"
}
}
}

View file

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

View file

@ -364,7 +364,7 @@ SENSOR_DESCRIPTIONS = {
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.CONDUCTIVITY}_{Units.CONDUCTIVITY}",
device_class=SensorDeviceClass.CONDUCTIVITY,
native_unit_of_measurement=UnitOfConductivity.MICROSIEMENS,
native_unit_of_measurement=UnitOfConductivity.MICROSIEMENS_PER_CM,
state_class=SensorStateClass.MEASUREMENT,
),
}

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

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

View file

@ -8,6 +8,9 @@
"dim": "mdi:brightness-6",
"off": "mdi:brightness-3"
}
},
"audio_output": {
"default": "mdi:audio-input-stereo-minijack"
}
},
"switch": {

View file

@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["aiostreammagic"],
"requirements": ["aiostreammagic==2.8.4"],
"requirements": ["aiostreammagic==2.8.5"],
"zeroconf": ["_stream-magic._tcp.local.", "_smoip._tcp.local."]
}

View file

@ -1,7 +1,7 @@
"""Support for Cambridge Audio select entities."""
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from dataclasses import dataclass, field
from aiostreammagic import StreamMagicClient
from aiostreammagic.models import DisplayBrightness
@ -19,21 +19,61 @@ from .entity import CambridgeAudioEntity
class CambridgeAudioSelectEntityDescription(SelectEntityDescription):
"""Describes Cambridge Audio select entity."""
options_fn: Callable[[StreamMagicClient], list[str]] = field(default=lambda _: [])
load_fn: Callable[[StreamMagicClient], bool] = field(default=lambda _: True)
value_fn: Callable[[StreamMagicClient], str | None]
set_value_fn: Callable[[StreamMagicClient, str], Awaitable[None]]
async def _audio_output_set_value_fn(client: StreamMagicClient, value: str) -> None:
"""Set the audio output using the display name."""
audio_output_id = next(
(output.id for output in client.audio_output.outputs if value == output.name),
None,
)
assert audio_output_id is not None
await client.set_audio_output(audio_output_id)
def _audio_output_value_fn(client: StreamMagicClient) -> str | None:
"""Convert the current audio output id to name."""
return next(
(
output.name
for output in client.audio_output.outputs
if client.state.audio_output == output.id
),
None,
)
CONTROL_ENTITIES: tuple[CambridgeAudioSelectEntityDescription, ...] = (
CambridgeAudioSelectEntityDescription(
key="display_brightness",
translation_key="display_brightness",
options=[x.value for x in DisplayBrightness],
options=[
DisplayBrightness.BRIGHT.value,
DisplayBrightness.DIM.value,
DisplayBrightness.OFF.value,
],
entity_category=EntityCategory.CONFIG,
load_fn=lambda client: client.display.brightness != DisplayBrightness.NONE,
value_fn=lambda client: client.display.brightness,
set_value_fn=lambda client, value: client.set_display_brightness(
DisplayBrightness(value)
),
),
CambridgeAudioSelectEntityDescription(
key="audio_output",
translation_key="audio_output",
entity_category=EntityCategory.CONFIG,
options_fn=lambda client: [
output.name for output in client.audio_output.outputs
],
load_fn=lambda client: len(client.audio_output.outputs) > 0,
value_fn=_audio_output_value_fn,
set_value_fn=_audio_output_set_value_fn,
),
)
@ -46,7 +86,9 @@ async def async_setup_entry(
client: StreamMagicClient = entry.runtime_data
entities: list[CambridgeAudioSelect] = [
CambridgeAudioSelect(client, description) for description in CONTROL_ENTITIES
CambridgeAudioSelect(client, description)
for description in CONTROL_ENTITIES
if description.load_fn(client)
]
async_add_entities(entities)
@ -65,6 +107,9 @@ class CambridgeAudioSelect(CambridgeAudioEntity, SelectEntity):
super().__init__(client)
self.entity_description = description
self._attr_unique_id = f"{client.info.unit_id}-{description.key}"
options_fn = description.options_fn(client)
if options_fn:
self._attr_options = options_fn
@property
def current_option(self) -> str | None:

View file

@ -32,6 +32,9 @@
"dim": "Dim",
"off": "[%key:common::state::off%]"
}
},
"audio_output": {
"name": "Audio output"
}
},
"switch": {

View file

@ -6,7 +6,7 @@ import asyncio
import collections
from collections.abc import Awaitable, Callable, Coroutine
from contextlib import suppress
from dataclasses import asdict
from dataclasses import asdict, dataclass
from datetime import datetime, timedelta
from enum import IntFlag
from functools import partial
@ -18,9 +18,9 @@ from typing import Any, Final, final
from aiohttp import hdrs, web
import attr
from propcache import cached_property
from propcache import cached_property, under_cached_property
import voluptuous as vol
from webrtc_models import RTCIceServer
from webrtc_models import RTCIceCandidate, RTCIceServer
from homeassistant.components import websocket_api
from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView
@ -177,6 +177,13 @@ class Image:
content: bytes = attr.ib()
@dataclass(frozen=True)
class CameraCapabilities:
"""Camera capabilities."""
frontend_stream_types: set[StreamType]
@bind_hass
async def async_request_stream(hass: HomeAssistant, entity_id: str, fmt: str) -> str:
"""Request a stream for a camera entity."""
@ -352,6 +359,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
websocket_api.async_register_command(hass, ws_camera_stream)
websocket_api.async_register_command(hass, websocket_get_prefs)
websocket_api.async_register_command(hass, websocket_update_prefs)
websocket_api.async_register_command(hass, ws_camera_capabilities)
async_register_ws(hass)
await component.async_setup(config)
@ -412,7 +420,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
def get_ice_servers() -> list[RTCIceServer]:
if hass.config.webrtc.ice_servers:
return hass.config.webrtc.ice_servers
return [RTCIceServer(urls="stun:stun.home-assistant.io:80")]
return [
RTCIceServer(
urls=[
"stun:stun.home-assistant.io:80",
"stun:stun.home-assistant.io:3478",
]
),
]
async_register_ice_servers(hass, get_ice_servers)
return True
@ -461,8 +476,11 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_state: None = None # State is determined by is_on
_attr_supported_features: CameraEntityFeature = CameraEntityFeature(0)
__supports_stream: CameraEntityFeature | None = None
def __init__(self) -> None:
"""Initialize a camera."""
self._cache: dict[str, Any] = {}
self.stream: Stream | None = None
self.stream_options: dict[str, str | bool | float] = {}
self.content_type: str = DEFAULT_CONTENT_TYPE
@ -472,9 +490,13 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
self._create_stream_lock: asyncio.Lock | None = None
self._webrtc_provider: CameraWebRTCProvider | None = None
self._legacy_webrtc_provider: CameraWebRTCLegacyProvider | None = None
self._webrtc_sync_offer = (
self._supports_native_sync_webrtc = (
type(self).async_handle_web_rtc_offer != Camera.async_handle_web_rtc_offer
)
self._supports_native_async_webrtc = (
type(self).async_handle_async_webrtc_offer
!= Camera.async_handle_async_webrtc_offer
)
@cached_property
def entity_picture(self) -> str:
@ -611,7 +633,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
Integrations can override with a native WebRTC implementation.
"""
if self._webrtc_sync_offer:
if self._supports_native_sync_webrtc:
try:
answer = await self.async_handle_web_rtc_offer(offer_sdp)
except ValueError as ex:
@ -767,6 +789,9 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
async def async_internal_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_internal_added_to_hass()
self.__supports_stream = (
self.supported_features_compat & CameraEntityFeature.STREAM
)
await self.async_refresh_providers(write_state=False)
async def async_refresh_providers(self, *, write_state: bool = True) -> None:
@ -776,21 +801,29 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
providers or inputs to the state attributes change.
"""
old_provider = self._webrtc_provider
new_provider = await self._async_get_supported_webrtc_provider(
async_get_supported_provider
)
old_legacy_provider = self._legacy_webrtc_provider
new_provider = None
new_legacy_provider = None
if new_provider is None:
# Only add the legacy provider if the new provider is not available
new_legacy_provider = await self._async_get_supported_webrtc_provider(
async_get_supported_legacy_provider
# Skip all providers if the camera has a native WebRTC implementation
if not (
self._supports_native_sync_webrtc or self._supports_native_async_webrtc
):
# Camera doesn't have a native WebRTC implementation
new_provider = await self._async_get_supported_webrtc_provider(
async_get_supported_provider
)
if new_provider is None:
# Only add the legacy provider if the new provider is not available
new_legacy_provider = await self._async_get_supported_webrtc_provider(
async_get_supported_legacy_provider
)
if old_provider != new_provider or old_legacy_provider != new_legacy_provider:
self._webrtc_provider = new_provider
self._legacy_webrtc_provider = new_legacy_provider
self._invalidate_camera_capabilities_cache()
if write_state:
self.async_write_ha_state()
@ -814,20 +847,26 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the WebRTC client configuration and extend it with the registered ice servers."""
config = self._async_get_webrtc_client_configuration()
ice_servers = [
server
for servers in self.hass.data.get(DATA_ICE_SERVERS, [])
for server in servers()
]
config.configuration.ice_servers.extend(ice_servers)
if not self._supports_native_sync_webrtc:
# Until 2024.11, the frontend was not resolving any ice servers
# The async approach was added 2024.11 and new integrations need to use it
ice_servers = [
server
for servers in self.hass.data.get(DATA_ICE_SERVERS, [])
for server in servers()
]
config.configuration.ice_servers.extend(ice_servers)
config.get_candidates_upfront = (
self._webrtc_sync_offer or self._legacy_webrtc_provider is not None
self._supports_native_sync_webrtc
or self._legacy_webrtc_provider is not None
)
return config
async def async_on_webrtc_candidate(self, session_id: str, candidate: str) -> None:
async def async_on_webrtc_candidate(
self, session_id: str, candidate: RTCIceCandidate
) -> None:
"""Handle a WebRTC candidate."""
if self._webrtc_provider:
await self._webrtc_provider.async_on_webrtc_candidate(session_id, candidate)
@ -840,6 +879,43 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
if self._webrtc_provider:
self._webrtc_provider.async_close_session(session_id)
@callback
def _invalidate_camera_capabilities_cache(self) -> None:
"""Invalidate the camera capabilities cache."""
self._cache.pop("camera_capabilities", None)
@final
@under_cached_property
def camera_capabilities(self) -> CameraCapabilities:
"""Return the camera capabilities."""
frontend_stream_types = set()
if CameraEntityFeature.STREAM in self.supported_features_compat:
if self._supports_native_sync_webrtc or self._supports_native_async_webrtc:
# The camera has a native WebRTC implementation
frontend_stream_types.add(StreamType.WEB_RTC)
else:
frontend_stream_types.add(StreamType.HLS)
if self._webrtc_provider:
frontend_stream_types.add(StreamType.WEB_RTC)
return CameraCapabilities(frontend_stream_types)
@callback
def async_write_ha_state(self) -> None:
"""Write the state to the state machine.
Schedules async_refresh_providers if support of streams have changed.
"""
super().async_write_ha_state()
if self.__supports_stream != (
supports_stream := self.supported_features_compat
& CameraEntityFeature.STREAM
):
self.__supports_stream = supports_stream
self._invalidate_camera_capabilities_cache()
self.hass.async_create_task(self.async_refresh_providers())
class CameraView(HomeAssistantView):
"""Base CameraView."""
@ -930,6 +1006,24 @@ class CameraMjpegStream(CameraView):
raise web.HTTPBadRequest from err
@websocket_api.websocket_command(
{
vol.Required("type"): "camera/capabilities",
vol.Required("entity_id"): cv.entity_id,
}
)
@websocket_api.async_response
async def ws_camera_capabilities(
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
) -> None:
"""Handle get camera capabilities websocket command.
Async friendly.
"""
camera = get_camera_from_entity_id(hass, msg["entity_id"])
connection.send_result(msg["id"], asdict(camera.camera_capabilities))
@websocket_api.websocket_command(
{
vol.Required("type"): "camera/stream",

View file

@ -46,6 +46,10 @@
}
}
}
},
"legacy_webrtc_provider": {
"title": "Detected use of legacy WebRTC provider registered by {legacy_integration}",
"description": "The {legacy_integration} integration has registered a legacy WebRTC provider. Home Assistant prefers using the built-in modern WebRTC provider registered by the {builtin_integration} integration.\n\nBenefits of the built-in integration are:\n\n- The camera stream is started faster.\n- More camera devices are supported.\n\nTo fix this issue, you can either keep using the built-in modern WebRTC provider and remove the {legacy_integration} integration or remove the {builtin_integration} integration to use the legacy provider, and then restart Home Assistant."
}
},
"services": {

View file

@ -2,20 +2,21 @@
from __future__ import annotations
from abc import ABC, abstractmethod
import asyncio
from collections.abc import Awaitable, Callable, Iterable
from dataclasses import asdict, dataclass, field
from functools import cache, partial
from functools import cache, partial, wraps
import logging
from typing import TYPE_CHECKING, Any, Protocol
import voluptuous as vol
from webrtc_models import RTCConfiguration, RTCIceServer
from webrtc_models import RTCConfiguration, RTCIceCandidate, RTCIceServer
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from homeassistant.util.hass_dict import HassKey
from homeassistant.util.ulid import ulid
@ -31,7 +32,7 @@ _LOGGER = logging.getLogger(__name__)
DATA_WEBRTC_PROVIDERS: HassKey[set[CameraWebRTCProvider]] = HassKey(
"camera_webrtc_providers"
)
DATA_WEBRTC_LEGACY_PROVIDERS: HassKey[set[CameraWebRTCLegacyProvider]] = HassKey(
DATA_WEBRTC_LEGACY_PROVIDERS: HassKey[dict[str, CameraWebRTCLegacyProvider]] = HassKey(
"camera_webrtc_legacy_providers"
)
DATA_ICE_SERVERS: HassKey[list[Callable[[], Iterable[RTCIceServer]]]] = HassKey(
@ -77,7 +78,14 @@ class WebRTCAnswer(WebRTCMessage):
class WebRTCCandidate(WebRTCMessage):
"""WebRTC candidate."""
candidate: str
candidate: RTCIceCandidate
def as_dict(self) -> dict[str, Any]:
"""Return a dict representation of the message."""
return {
"type": self._get_type(),
"candidate": self.candidate.candidate,
}
@dataclass(frozen=True)
@ -113,13 +121,20 @@ class WebRTCClientConfiguration:
return data
class CameraWebRTCProvider(Protocol):
class CameraWebRTCProvider(ABC):
"""WebRTC provider."""
@property
@abstractmethod
def domain(self) -> str:
"""Return the integration domain of the provider."""
@callback
@abstractmethod
def async_is_supported(self, stream_source: str) -> bool:
"""Determine if the provider supports the stream source."""
@abstractmethod
async def async_handle_async_webrtc_offer(
self,
camera: Camera,
@ -129,12 +144,16 @@ class CameraWebRTCProvider(Protocol):
) -> None:
"""Handle the WebRTC offer and return the answer via the provided callback."""
async def async_on_webrtc_candidate(self, session_id: str, candidate: str) -> None:
@abstractmethod
async def async_on_webrtc_candidate(
self, session_id: str, candidate: RTCIceCandidate
) -> None:
"""Handle the WebRTC candidate."""
@callback
def async_close_session(self, session_id: str) -> None:
"""Close the session."""
return ## This is an optional method so we need a default here.
class CameraWebRTCLegacyProvider(Protocol):
@ -149,10 +168,10 @@ class CameraWebRTCLegacyProvider(Protocol):
"""Handle the WebRTC offer and return an answer."""
def _async_register_webrtc_provider[_T](
@callback
def async_register_webrtc_provider(
hass: HomeAssistant,
key: HassKey[set[_T]],
provider: _T,
provider: CameraWebRTCProvider,
) -> Callable[[], None]:
"""Register a WebRTC provider.
@ -161,7 +180,7 @@ def _async_register_webrtc_provider[_T](
if DOMAIN not in hass.data:
raise ValueError("Unexpected state, camera not loaded")
providers = hass.data.setdefault(key, set())
providers = hass.data.setdefault(DATA_WEBRTC_PROVIDERS, set())
@callback
def remove_provider() -> None:
@ -176,20 +195,9 @@ def _async_register_webrtc_provider[_T](
return remove_provider
@callback
def async_register_webrtc_provider(
hass: HomeAssistant,
provider: CameraWebRTCProvider,
) -> Callable[[], None]:
"""Register a WebRTC provider.
The first provider to satisfy the offer will be used.
"""
return _async_register_webrtc_provider(hass, DATA_WEBRTC_PROVIDERS, provider)
async def _async_refresh_providers(hass: HomeAssistant) -> None:
"""Check all cameras for any state changes for registered providers."""
_async_check_conflicting_legacy_provider(hass)
component = hass.data[DATA_COMPONENT]
await asyncio.gather(
@ -197,6 +205,49 @@ async def _async_refresh_providers(hass: HomeAssistant) -> None:
)
type WsCommandWithCamera = Callable[
[websocket_api.ActiveConnection, dict[str, Any], Camera],
Awaitable[None],
]
def require_webrtc_support(
error_code: str,
) -> Callable[[WsCommandWithCamera], websocket_api.AsyncWebSocketCommandHandler]:
"""Validate that the camera supports WebRTC."""
def decorate(
func: WsCommandWithCamera,
) -> websocket_api.AsyncWebSocketCommandHandler:
"""Decorate func."""
@wraps(func)
async def validate(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Validate that the camera supports WebRTC."""
entity_id = msg["entity_id"]
camera = get_camera_from_entity_id(hass, entity_id)
if camera.frontend_stream_type != StreamType.WEB_RTC:
connection.send_error(
msg["id"],
error_code,
(
"Camera does not support WebRTC,"
f" frontend_stream_type={camera.frontend_stream_type}"
),
)
return
await func(connection, msg, camera)
return validate
return decorate
@websocket_api.websocket_command(
{
vol.Required("type"): "camera/webrtc/offer",
@ -205,8 +256,9 @@ async def _async_refresh_providers(hass: HomeAssistant) -> None:
}
)
@websocket_api.async_response
@require_webrtc_support("webrtc_offer_failed")
async def ws_webrtc_offer(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
connection: websocket_api.ActiveConnection, msg: dict[str, Any], camera: Camera
) -> None:
"""Handle the signal path for a WebRTC stream.
@ -218,20 +270,7 @@ async def ws_webrtc_offer(
Async friendly.
"""
entity_id = msg["entity_id"]
offer = msg["offer"]
camera = get_camera_from_entity_id(hass, entity_id)
if camera.frontend_stream_type != StreamType.WEB_RTC:
connection.send_error(
msg["id"],
"webrtc_offer_failed",
(
"Camera does not support WebRTC,"
f" frontend_stream_type={camera.frontend_stream_type}"
),
)
return
session_id = ulid()
connection.subscriptions[msg["id"]] = partial(
camera.close_webrtc_session, session_id
@ -270,23 +309,11 @@ async def ws_webrtc_offer(
}
)
@websocket_api.async_response
@require_webrtc_support("webrtc_get_client_config_failed")
async def ws_get_client_config(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
connection: websocket_api.ActiveConnection, msg: dict[str, Any], camera: Camera
) -> None:
"""Handle get WebRTC client config websocket command."""
entity_id = msg["entity_id"]
camera = get_camera_from_entity_id(hass, entity_id)
if camera.frontend_stream_type != StreamType.WEB_RTC:
connection.send_error(
msg["id"],
"webrtc_get_client_config_failed",
(
"Camera does not support WebRTC,"
f" frontend_stream_type={camera.frontend_stream_type}"
),
)
return
config = camera.async_get_webrtc_client_configuration().to_frontend_dict()
connection.send_result(
msg["id"],
@ -303,24 +330,14 @@ async def ws_get_client_config(
}
)
@websocket_api.async_response
@require_webrtc_support("webrtc_candidate_failed")
async def ws_candidate(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
connection: websocket_api.ActiveConnection, msg: dict[str, Any], camera: Camera
) -> None:
"""Handle WebRTC candidate websocket command."""
entity_id = msg["entity_id"]
camera = get_camera_from_entity_id(hass, entity_id)
if camera.frontend_stream_type != StreamType.WEB_RTC:
connection.send_error(
msg["id"],
"webrtc_candidate_failed",
(
"Camera does not support WebRTC,"
f" frontend_stream_type={camera.frontend_stream_type}"
),
)
return
await camera.async_on_webrtc_candidate(msg["session_id"], msg["candidate"])
await camera.async_on_webrtc_candidate(
msg["session_id"], RTCIceCandidate(msg["candidate"])
)
connection.send_message(websocket_api.result_message(msg["id"]))
@ -333,11 +350,11 @@ def async_register_ws(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, ws_candidate)
async def _async_get_supported_provider[
_T: CameraWebRTCLegacyProvider | CameraWebRTCProvider
](hass: HomeAssistant, camera: Camera, key: HassKey[set[_T]]) -> _T | None:
async def async_get_supported_provider(
hass: HomeAssistant, camera: Camera
) -> CameraWebRTCProvider | None:
"""Return the first supported provider for the camera."""
providers = hass.data.get(key)
providers = hass.data.get(DATA_WEBRTC_PROVIDERS)
if not providers or not (stream_source := await camera.stream_source()):
return None
@ -348,20 +365,19 @@ async def _async_get_supported_provider[
return None
async def async_get_supported_provider(
hass: HomeAssistant, camera: Camera
) -> CameraWebRTCProvider | None:
"""Return the first supported provider for the camera."""
return await _async_get_supported_provider(hass, camera, DATA_WEBRTC_PROVIDERS)
async def async_get_supported_legacy_provider(
hass: HomeAssistant, camera: Camera
) -> CameraWebRTCLegacyProvider | None:
"""Return the first supported provider for the camera."""
return await _async_get_supported_provider(
hass, camera, DATA_WEBRTC_LEGACY_PROVIDERS
)
providers = hass.data.get(DATA_WEBRTC_LEGACY_PROVIDERS)
if not providers or not (stream_source := await camera.stream_source()):
return None
for provider in providers.values():
if await provider.async_is_supported(stream_source):
return provider
return None
@callback
@ -424,7 +440,49 @@ def async_register_rtsp_to_web_rtc_provider(
The first provider to satisfy the offer will be used.
"""
if DOMAIN not in hass.data:
raise ValueError("Unexpected state, camera not loaded")
legacy_providers = hass.data.setdefault(DATA_WEBRTC_LEGACY_PROVIDERS, {})
if domain in legacy_providers:
raise ValueError("Provider already registered")
provider_instance = _CameraRtspToWebRTCProvider(provider)
return _async_register_webrtc_provider(
hass, DATA_WEBRTC_LEGACY_PROVIDERS, provider_instance
)
@callback
def remove_provider() -> None:
legacy_providers.pop(domain)
hass.async_create_task(_async_refresh_providers(hass))
legacy_providers[domain] = provider_instance
hass.async_create_task(_async_refresh_providers(hass))
return remove_provider
@callback
def _async_check_conflicting_legacy_provider(hass: HomeAssistant) -> None:
"""Check if a legacy provider is registered together with the builtin provider."""
builtin_provider_domain = "go2rtc"
if (
(legacy_providers := hass.data.get(DATA_WEBRTC_LEGACY_PROVIDERS))
and (providers := hass.data.get(DATA_WEBRTC_PROVIDERS))
and any(provider.domain == builtin_provider_domain for provider in providers)
):
for domain in legacy_providers:
ir.async_create_issue(
hass,
DOMAIN,
f"legacy_webrtc_provider_{domain}",
is_fixable=False,
is_persistent=False,
issue_domain=domain,
learn_more_url="https://www.home-assistant.io/integrations/go2rtc/",
severity=ir.IssueSeverity.WARNING,
translation_key="legacy_webrtc_provider",
translation_placeholders={
"legacy_integration": domain,
"builtin_integration": builtin_provider_domain,
},
)

View file

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

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:

View file

@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable
from datetime import datetime
from http import HTTPStatus
import logging
@ -11,12 +12,14 @@ from typing import Any, Literal
import aiohttp
from hass_nabucasa.client import CloudClient as Interface, RemoteActivationNotAllowed
from webrtc_models import RTCIceServer
from homeassistant.components import google_assistant, persistent_notification, webhook
from homeassistant.components.alexa import (
errors as alexa_errors,
smart_home as alexa_smart_home,
)
from homeassistant.components.camera.webrtc import async_register_ice_servers
from homeassistant.components.google_assistant import smart_home as ga
from homeassistant.const import __version__ as HA_VERSION
from homeassistant.core import Context, HassJob, HomeAssistant, callback
@ -27,7 +30,7 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss
from homeassistant.util.aiohttp import MockRequest, serialize_response
from . import alexa_config, google_config
from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN
from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN, PREF_ENABLE_CLOUD_ICE_SERVERS
from .prefs import CloudPreferences
_LOGGER = logging.getLogger(__name__)
@ -60,6 +63,7 @@ class CloudClient(Interface):
self._alexa_config_init_lock = asyncio.Lock()
self._google_config_init_lock = asyncio.Lock()
self._relayer_region: str | None = None
self._cloud_ice_servers_listener: Callable[[], None] | None = None
@property
def base_path(self) -> Path:
@ -187,6 +191,49 @@ class CloudClient(Interface):
if is_new_user:
await gconf.async_sync_entities(gconf.agent_user_id)
async def setup_cloud_ice_servers(_: datetime) -> None:
async def register_cloud_ice_server(
ice_servers: list[RTCIceServer],
) -> Callable[[], None]:
"""Register cloud ice server."""
def get_ice_servers() -> list[RTCIceServer]:
return ice_servers
return async_register_ice_servers(self._hass, get_ice_servers)
async def async_register_cloud_ice_servers_listener(
prefs: CloudPreferences,
) -> None:
is_cloud_ice_servers_enabled = (
self.cloud.is_logged_in
and not self.cloud.subscription_expired
and prefs.cloud_ice_servers_enabled
)
if is_cloud_ice_servers_enabled:
if self._cloud_ice_servers_listener is None:
self._cloud_ice_servers_listener = await self.cloud.ice_servers.async_register_ice_servers_listener(
register_cloud_ice_server
)
elif self._cloud_ice_servers_listener:
self._cloud_ice_servers_listener()
self._cloud_ice_servers_listener = None
async def async_prefs_updated(prefs: CloudPreferences) -> None:
updated_prefs = prefs.last_updated
if (
updated_prefs is None
or PREF_ENABLE_CLOUD_ICE_SERVERS not in updated_prefs
):
return
await async_register_cloud_ice_servers_listener(prefs)
await async_register_cloud_ice_servers_listener(self._prefs)
self._prefs.async_listen_updates(async_prefs_updated)
tasks = []
if self._prefs.alexa_enabled and self._prefs.alexa_report_state:
@ -195,6 +242,8 @@ class CloudClient(Interface):
if self._prefs.google_enabled:
tasks.append(enable_google)
tasks.append(setup_cloud_ice_servers)
if tasks:
await asyncio.gather(*(task(None) for task in tasks))
@ -222,6 +271,10 @@ class CloudClient(Interface):
self._google_config.async_deinitialize()
self._google_config = None
if self._cloud_ice_servers_listener:
self._cloud_ice_servers_listener()
self._cloud_ice_servers_listener = None
@callback
def user_message(self, identifier: str, title: str, message: str) -> None:
"""Create a message for user to UI."""

View file

@ -43,6 +43,7 @@ PREF_GOOGLE_SETTINGS_VERSION = "google_settings_version"
PREF_TTS_DEFAULT_VOICE = "tts_default_voice"
PREF_GOOGLE_CONNECTED = "google_connected"
PREF_REMOTE_ALLOW_REMOTE_ENABLE = "remote_allow_remote_enable"
PREF_ENABLE_CLOUD_ICE_SERVERS = "cloud_ice_servers_enabled"
DEFAULT_TTS_DEFAULT_VOICE = ("en-US", "JennyNeural")
DEFAULT_DISABLE_2FA = False
DEFAULT_ALEXA_REPORT_STATE = True

View file

@ -42,6 +42,7 @@ from .const import (
PREF_ALEXA_REPORT_STATE,
PREF_DISABLE_2FA,
PREF_ENABLE_ALEXA,
PREF_ENABLE_CLOUD_ICE_SERVERS,
PREF_ENABLE_GOOGLE,
PREF_GOOGLE_REPORT_STATE,
PREF_GOOGLE_SECURE_DEVICES_PIN,
@ -439,15 +440,16 @@ def validate_language_voice(value: tuple[str, str]) -> tuple[str, str]:
@websocket_api.websocket_command(
{
vol.Required("type"): "cloud/update_prefs",
vol.Optional(PREF_ENABLE_GOOGLE): bool,
vol.Optional(PREF_ENABLE_ALEXA): bool,
vol.Optional(PREF_ALEXA_REPORT_STATE): bool,
vol.Optional(PREF_ENABLE_ALEXA): bool,
vol.Optional(PREF_ENABLE_CLOUD_ICE_SERVERS): bool,
vol.Optional(PREF_ENABLE_GOOGLE): bool,
vol.Optional(PREF_GOOGLE_REPORT_STATE): bool,
vol.Optional(PREF_GOOGLE_SECURE_DEVICES_PIN): vol.Any(None, str),
vol.Optional(PREF_REMOTE_ALLOW_REMOTE_ENABLE): bool,
vol.Optional(PREF_TTS_DEFAULT_VOICE): vol.All(
vol.Coerce(tuple), validate_language_voice
),
vol.Optional(PREF_REMOTE_ALLOW_REMOTE_ENABLE): bool,
}
)
@websocket_api.async_response

View file

@ -8,6 +8,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["hass_nabucasa"],
"requirements": ["hass-nabucasa==0.81.1"],
"requirements": ["hass-nabucasa==0.84.0"],
"single_config_entry": true
}

View file

@ -32,6 +32,7 @@ from .const import (
PREF_CLOUD_USER,
PREF_CLOUDHOOKS,
PREF_ENABLE_ALEXA,
PREF_ENABLE_CLOUD_ICE_SERVERS,
PREF_ENABLE_GOOGLE,
PREF_ENABLE_REMOTE,
PREF_GOOGLE_CONNECTED,
@ -162,20 +163,21 @@ class CloudPreferences:
async def async_update(
self,
*,
google_enabled: bool | UndefinedType = UNDEFINED,
alexa_enabled: bool | UndefinedType = UNDEFINED,
remote_enabled: bool | UndefinedType = UNDEFINED,
google_secure_devices_pin: str | None | UndefinedType = UNDEFINED,
cloudhooks: dict[str, dict[str, str | bool]] | UndefinedType = UNDEFINED,
cloud_user: str | UndefinedType = UNDEFINED,
alexa_report_state: bool | UndefinedType = UNDEFINED,
google_report_state: bool | UndefinedType = UNDEFINED,
tts_default_voice: tuple[str, str] | UndefinedType = UNDEFINED,
remote_domain: str | None | UndefinedType = UNDEFINED,
alexa_settings_version: int | UndefinedType = UNDEFINED,
google_settings_version: int | UndefinedType = UNDEFINED,
cloud_ice_servers_enabled: bool | UndefinedType = UNDEFINED,
cloud_user: str | UndefinedType = UNDEFINED,
cloudhooks: dict[str, dict[str, str | bool]] | UndefinedType = UNDEFINED,
google_connected: bool | UndefinedType = UNDEFINED,
google_enabled: bool | UndefinedType = UNDEFINED,
google_report_state: bool | UndefinedType = UNDEFINED,
google_secure_devices_pin: str | None | UndefinedType = UNDEFINED,
google_settings_version: int | UndefinedType = UNDEFINED,
remote_allow_remote_enable: bool | UndefinedType = UNDEFINED,
remote_domain: str | None | UndefinedType = UNDEFINED,
remote_enabled: bool | UndefinedType = UNDEFINED,
tts_default_voice: tuple[str, str] | UndefinedType = UNDEFINED,
) -> None:
"""Update user preferences."""
prefs = {**self._prefs}
@ -184,20 +186,21 @@ class CloudPreferences:
{
key: value
for key, value in (
(PREF_ENABLE_GOOGLE, google_enabled),
(PREF_ENABLE_ALEXA, alexa_enabled),
(PREF_ENABLE_REMOTE, remote_enabled),
(PREF_GOOGLE_SECURE_DEVICES_PIN, google_secure_devices_pin),
(PREF_CLOUDHOOKS, cloudhooks),
(PREF_CLOUD_USER, cloud_user),
(PREF_ALEXA_REPORT_STATE, alexa_report_state),
(PREF_GOOGLE_REPORT_STATE, google_report_state),
(PREF_ALEXA_SETTINGS_VERSION, alexa_settings_version),
(PREF_GOOGLE_SETTINGS_VERSION, google_settings_version),
(PREF_TTS_DEFAULT_VOICE, tts_default_voice),
(PREF_REMOTE_DOMAIN, remote_domain),
(PREF_CLOUD_USER, cloud_user),
(PREF_CLOUDHOOKS, cloudhooks),
(PREF_ENABLE_ALEXA, alexa_enabled),
(PREF_ENABLE_CLOUD_ICE_SERVERS, cloud_ice_servers_enabled),
(PREF_ENABLE_GOOGLE, google_enabled),
(PREF_ENABLE_REMOTE, remote_enabled),
(PREF_GOOGLE_CONNECTED, google_connected),
(PREF_GOOGLE_REPORT_STATE, google_report_state),
(PREF_GOOGLE_SECURE_DEVICES_PIN, google_secure_devices_pin),
(PREF_GOOGLE_SETTINGS_VERSION, google_settings_version),
(PREF_REMOTE_ALLOW_REMOTE_ENABLE, remote_allow_remote_enable),
(PREF_REMOTE_DOMAIN, remote_domain),
(PREF_TTS_DEFAULT_VOICE, tts_default_voice),
)
if value is not UNDEFINED
}
@ -239,6 +242,7 @@ class CloudPreferences:
PREF_ALEXA_REPORT_STATE: self.alexa_report_state,
PREF_CLOUDHOOKS: self.cloudhooks,
PREF_ENABLE_ALEXA: self.alexa_enabled,
PREF_ENABLE_CLOUD_ICE_SERVERS: self.cloud_ice_servers_enabled,
PREF_ENABLE_GOOGLE: self.google_enabled,
PREF_ENABLE_REMOTE: self.remote_enabled,
PREF_GOOGLE_DEFAULT_EXPOSE: self.google_default_expose,
@ -362,6 +366,14 @@ class CloudPreferences:
"""
return self._prefs.get(PREF_TTS_DEFAULT_VOICE, DEFAULT_TTS_DEFAULT_VOICE) # type: ignore[no-any-return]
@property
def cloud_ice_servers_enabled(self) -> bool:
"""Return if cloud ICE servers are enabled."""
cloud_ice_servers_enabled: bool = self._prefs.get(
PREF_ENABLE_CLOUD_ICE_SERVERS, True
)
return cloud_ice_servers_enabled
async def get_cloud_user(self) -> str:
"""Return ID of Home Assistant Cloud system user."""
user = await self._load_cloud_user()
@ -409,6 +421,7 @@ class CloudPreferences:
PREF_ENABLE_ALEXA: True,
PREF_ENABLE_GOOGLE: True,
PREF_ENABLE_REMOTE: False,
PREF_ENABLE_CLOUD_ICE_SERVERS: True,
PREF_GOOGLE_CONNECTED: False,
PREF_GOOGLE_DEFAULT_EXPOSE: DEFAULT_EXPOSED_DOMAINS,
PREF_GOOGLE_ENTITY_CONFIGS: {},

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