Compare commits

..

922 Commits

Author SHA1 Message Date
G Johansson
e20bda33a4 Fix entity test 2025-01-19 21:29:42 +01:00
G Johansson
f2125a1133 Reset tts test 2 2025-01-19 21:29:42 +01:00
G Johansson
557caa7ecb Reset tts test 2025-01-19 21:29:42 +01:00
G Johansson
d973e42c97 Reset kira test 2025-01-19 21:29:42 +01:00
G Johansson
428c7ced94 Reset blackbird test 2025-01-19 21:29:42 +01:00
G Johansson
7b14bd89a0 Fix template preview 2025-01-19 21:29:42 +01:00
G Johansson
f1f45d6b5b Fixes 2025-01-19 21:29:42 +01:00
G Johansson
50e4311416 Remove 2025-01-19 21:29:42 +01:00
G Johansson
1a32a2f9c2 Mod tests 2025-01-19 21:29:42 +01:00
G Johansson
f9cecc0cd5 Fix some tests 2025-01-19 21:29:42 +01:00
G Johansson
6fe2612f1d Fix comment 2025-01-19 21:29:42 +01:00
G Johansson
43fbb2ab7b Don't allow entities without platform 2025-01-19 21:29:42 +01:00
G Johansson
2295e3779a Ensure entity platform in cover tests (#135917) 2025-01-19 21:29:28 +01:00
G Johansson
53f80e9759 Ensure entity platform in camera tests (#135918) 2025-01-19 21:28:50 +01:00
Joost Lekkerkerker
f5d35bca72 Implement cloudhooks for Overseerr (#134680) 2025-01-19 21:28:08 +01:00
Norbert Rittel
77221f53b3 Fix sentence-casing in PurpleAir integration strings (#135981) 2025-01-19 21:27:01 +01:00
jsuar
a2d76cac5a Fix Slack file upload (#135818)
* pgrade Slack integration to use AsyncWebClient and support files_upload_v2

- Replaced deprecated WebClient with AsyncWebClient throughout the integration.
- Removed the unsupported `run_async` parameter.
- Added a helper function to resolve channel names to channel IDs.
- Updated `_async_send_local_file_message` and `_async_send_remote_file_message` to handle Slack's new API requirements, including per-channel uploads.
- Updated dependency from slackclient==2.5.0 to slack-sdk>=3.0.0.
- Improved error handling and logging for channel resolution and file uploads.

* Fix test to use AsyncWebClient for Slack authentication flow

* Fix Slack authentication URL by removing the www subdomain

* Refactor Slack file upload functionality and add utility for file uploads
2025-01-19 21:09:04 +01:00
Joakim Plate
a69786f64f Set friendly name for PT2262 sensors to masked name (#135988) 2025-01-19 21:07:05 +01:00
Scott K Logan
2900baac04 Bump aioraven to 0.7.1 (#136017) 2025-01-19 21:05:34 +01:00
Duco Sebel
2092456c7e Bumb python-homewizard-energy to 8.1.0 (#136016) 2025-01-19 21:03:30 +01:00
Joakim Plate
2bedb2cadb Correct translation key for data bits in rfxtrx (#135990) 2025-01-19 20:43:47 +01:00
Marc Mueller
5329356f20 Update numpy to 2.2.2 (#135982) 2025-01-19 20:35:32 +01:00
Paulus Schoutsen
0c68854fdf Migrate tests from OpenAI to conversation integration (#135963) 2025-01-19 20:32:59 +01:00
David Knowles
8777dd9065 Bump pydrawise to 2025.1.0 (#135998) 2025-01-19 20:31:30 +01:00
J. Diego Rodríguez Royo
57294fa461 Do not base power switch state on appliance's operation state at Home Connect (#135932) 2025-01-19 20:24:48 +01:00
J. Nick Koston
3a078d5414 Handle invalid datetime in onvif (#136014) 2025-01-19 20:16:40 +01:00
Joakim Plate
568a27000d Correct type for off delay in rfxtrx (#135994) 2025-01-19 20:09:05 +01:00
Maikel Punie
4612f4da19 Fix velbus via devices (#135986) 2025-01-19 20:07:32 +01:00
Manu
ec45cb4939 Improve exception handling in Habitica integration (#135950) 2025-01-19 19:51:55 +01:00
Jan Bouwhuis
ccd7b1c21a Add incomfort heater serialnr to device info (#136012) 2025-01-19 19:51:04 +01:00
Norbert Rittel
3ee2dc9790 Make strings of create_scene action UI- and translation-friendly (#136004) 2025-01-19 19:43:47 +01:00
Jan Bouwhuis
889f699e5d Disable noisy diagnostic incomfort sensors by default (#135992) 2025-01-19 19:28:19 +01:00
Jan Bouwhuis
5ffae140af Add diagnostics feature to incomfort integration (#136009) 2025-01-19 19:27:36 +01:00
Jan Bouwhuis
04eb86e5a0 Cleanup incomfort translation strings (#135991) 2025-01-19 15:30:03 +01:00
Jan Bouwhuis
3077a4cdee Add re-configure flow incomfort integration (#135887)
* Add re-configure flow incomfort integration

* End with abort flow in reconfigure failure flow

* Apply parenthesis
2025-01-19 15:16:26 +01:00
Joost Lekkerkerker
02bf8447b3 Fix unset coordinator in Switchbot cloud (#135985) 2025-01-19 15:15:32 +01:00
Joost Lekkerkerker
cf29ef91ee Fix switchbot cloud library logger (#135987) 2025-01-19 15:15:21 +01:00
Andrew Sayre
439f22f584 Fix HEOS device information (#135940) 2025-01-19 15:07:00 +01:00
Jan Bouwhuis
b17c36eeff Add re-authentication flow to incomfort integration (#135861) 2025-01-19 14:26:21 +01:00
Mick Montorier-Aberman
41fe863b72 Refactor SwitchBot Cloud make_device_data (#135698) 2025-01-19 14:22:21 +01:00
Norbert Rittel
dfc4cdf785 Improve descriptions in list_notifications action, fix casing (#135838) 2025-01-19 13:43:35 +01:00
Christopher Fenner
654e111c23 Fix fan speed in auto mode in ViCare integration (#134256) 2025-01-19 13:39:38 +01:00
Norbert Rittel
9d5fe77b71 Remove unnecessary "title" keys to use default setup flow instead (#135512) 2025-01-19 13:34:22 +01:00
Manu
958b1e7759 Move integration setup to coordinator _async_setup in Bring (#135711) 2025-01-19 13:29:21 +01:00
Norbert Rittel
2f5545e7b8 Fix name and descriptions of actions in EZVIZ integration etc. (#135858) 2025-01-19 13:28:49 +01:00
Erwin Douna
15d57692d9 SMA add diagnostics (#135852) 2025-01-19 13:28:15 +01:00
Sid
a55bd593af Rework enigma2 tests (#135475) 2025-01-19 13:24:47 +01:00
Manuel Stahl
3978c4cdb3 Add type annotations to stiebel eltron component (#135228) 2025-01-19 13:21:59 +01:00
Norbert Rittel
4690aef8b8 Further clarify the meaning of Sensibo's Climate React mode (#135833)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-19 13:21:37 +01:00
Brett Adams
6292d6c0dc Add streaming to device tracker platform in Teslemetry (#135962)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-19 13:20:40 +01:00
Matthias Alphart
af0f416497 Fix KNX default state updater option (#135611) 2025-01-19 12:53:09 +01:00
Manu
acbb15a496 Set dependency-transparency and async-dependency in Habitica IQS (#135902) 2025-01-19 12:51:49 +01:00
Glenn Vandeuren (aka Iondependent)
9f3b39a2d2 Round brightness in Niko Home Control (#135920) 2025-01-19 12:51:05 +01:00
Norbert Rittel
5a91562d1d Fix grammar and plural handling in action descriptions (#135654) 2025-01-19 12:37:28 +01:00
J. Diego Rodríguez Royo
ac58494b55 Improve program related sensors at Home Connect (#135929) 2025-01-19 12:02:23 +01:00
J. Diego Rodríguez Royo
33d552e3f7 Add power switch only if it is available at Home Connect (#135930) 2025-01-19 11:58:38 +01:00
Norbert Rittel
f3222045ae Change 'device_id' to translatable 'device ID', fix typos in LCN (#135978) 2025-01-19 11:56:34 +01:00
J. Nick Koston
0d968267a2 Improve remote Bluetooth scanner manufacturer data (#135961)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-19 11:55:13 +01:00
cdnninja
85bea5b70e Vesync switch humidifier to property (#135949) 2025-01-19 11:43:16 +01:00
Erik Montnemery
02347d5d36 Improve backup store in tests (#135974) 2025-01-19 11:13:37 +01:00
Paulus Schoutsen
754de6f998 Add shared history for conversation agents (#135903)
* Add shared history for conversation agents

* Remove unused code

* Add support for native history items

* Store all assistant responses as assistant in history

* Add history support to DefaultAgent.async_handle_intents

* Make local fallback work

* Add default agent history

* Add history cleanup

* Add tests

* ChatHistory -> ChatSession

* Address comments

* Update snapshots
2025-01-18 22:33:03 -05:00
Norbert Rittel
32d7a23bff Fix duplicated "effect" in Speed field descriptions of flux_led (#135948) 2025-01-18 15:13:28 -10:00
J. Nick Koston
fe4e001fa5 Bump bluetooth-adapters to 0.21.0 (#135957) 2025-01-18 15:10:15 -10:00
J. Nick Koston
725d835fab Bump aiooui to 0.1.9 (#135956) 2025-01-18 15:01:55 -10:00
J. Nick Koston
640da1cc67 Bump aiooui to 0.1.8 (#135945) 2025-01-19 00:53:59 +01:00
Marc Mueller
6690b121c0 Fix unicode chars in zha tests (#135954) 2025-01-18 13:47:30 -10:00
Marc Mueller
8a3ef101e6 Replace additional deprecated USBServiceInfo imports (#135953) 2025-01-18 17:43:07 -06:00
J. Nick Koston
09ae388f4e Bump bleak-retry-connector to 3.7.0 (#135939) 2025-01-18 12:02:18 -10:00
Matthias Alphart
659450dac9 Update knx-frontend to 2025.1.18.164225 (#135941) 2025-01-18 22:33:41 +01:00
Álvaro Fernández Rojas
37c3a9546c Update aioairzone to v0.9.9 (#135866)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-18 10:57:54 -10:00
Norbert Rittel
b32c401c24 Fix inconsistently spelled occurrences of "ID" in telegram_bot integration (#135928)
* Make all occurrences of "ID" in telegram_bot consistent

- change all remaining occurrences of "id" or "Id" to the correct spelling "ID"
- change "chat_id" to the UI-friedly "chat ID"
- use "ID of the chat …" in descriptions, matching "ID of the message …"
- fix the edit_replymarkup action's description to also use "Edits …", matching all other descriptions with "Sends …" or "Edits …"

* Use translatable descriptions for the Timeout fields

Uses the description from the online documentation that can be translated while the current ones use the action name which makes it difficult to handle in other languages.
2025-01-18 14:54:44 -06:00
Marc Mueller
19e5b091c5 Use HassKey for assist_pipeline singleton (#135875) 2025-01-18 09:52:13 -10:00
Marc Mueller
24c50e0988 Fix aiodns DeprecationWarning in tests (#135921) 2025-01-18 08:04:01 -10:00
Joost Lekkerkerker
fe8a93d62f Add reauthentication to SmartThings (#135673)
* Add reauthentication to SmartThings

* Add reauthentication to SmartThings

* Add reauthentication to SmartThings

* Add reauthentication to SmartThings
2025-01-18 18:41:24 +01:00
Glenn Vandeuren (aka Iondependent)
b39c2719d7 Update NHC lib to v0.3.4 (#135923)
Update NHC to v0.3.4
2025-01-18 18:47:20 +02:00
Marc Mueller
0c9fd7c482 Fix DeprecationWarnings in mcp_server (#135927)
* Fix DeprecationWarnings in mcp_server

* Spelling
2025-01-18 18:43:35 +02:00
Marc Mueller
dedcef7230 Fix acmeda pytest usefixtures spelling (#135919) 2025-01-18 17:08:07 +01:00
Manu
595f49ee9f Set strict-typing in Habitica quality scale record (#135899)
* Set strict-typing in Habitica quality scale record

* cast
2025-01-18 16:35:35 +01:00
G Johansson
5a7b6cd7a0 Remove asserting name in tts test (no entity platform) (#135726)
* Ensure entity platform in tts tests

* Correct placement

* Remove name test

* Remove hass
2025-01-18 14:47:53 +01:00
Maciej Bieniek
f0c6b47522 Increase test coverage for IMGW-PIB (#135915) 2025-01-18 13:31:17 +01:00
Joris Pelgröm
d349c47694 Add reauth flow to LetPot integration (#135734) 2025-01-18 06:11:35 -06:00
Josef Zweck
f878465a9a Fix imgw_pib tests (#135913) 2025-01-18 06:07:28 -06:00
Nathan Spencer
81b7d01a7d Bump pylitterbot to 2024.0.0 (#135891) 2025-01-18 13:01:09 +01:00
Manu
f5dd3ef530 Increase test coverage in Habitica integration (#135896)
Add tests to Habitica integration
2025-01-18 12:59:23 +01:00
Brett Adams
88f16807a0 Bump Teslemetry Stream to 0.6.6 (#135905)
bump66
2025-01-18 12:38:20 +01:00
Manu
76d9bcbdfb Set parallel-updates in Habitica quality scale record (#135901) 2025-01-18 11:17:58 +01:00
tronikos
f01598aadd Use runtime_data in Opower (#135910)
* Use runtime_data in Opower

* Fix async_unload_entry

* Fix async_unload_entry

* fix
2025-01-18 11:14:31 +01:00
Josef Zweck
c56eee3639 Fix bmw_connected_drive tests (#135911) 2025-01-18 11:10:52 +01:00
tronikos
06d8bc658f Fix typo in Opower log message (#135909) 2025-01-18 10:39:40 +01:00
Noah Husby
f724ae9a01 Record IQS for Russound RNET (#134692) 2025-01-18 08:33:49 +01:00
J. Nick Koston
bbe897745e Bump onvif-zeep-async to 3.2.2 (#135898) 2025-01-17 19:30:21 -10:00
Ernst Klamer
089c9c41ba Add BThome hold press event (#135871)
* add hold_press

* add hold_press

* add hold_press

* add hold_press
2025-01-18 02:23:25 +02:00
J. Nick Koston
43fe4ebbbe Prevent HomeKit from going unavailable when min/max is reversed (#135892) 2025-01-17 14:08:17 -10:00
J. Nick Koston
fc1b6292cd Bump dbus-fast to 2.30.2 (#135874) 2025-01-17 13:05:18 -10:00
J. Nick Koston
174f3ca755 Bump ulid-transform to 1.2.0 (#135882) 2025-01-17 12:06:28 -10:00
J. Nick Koston
51d277fc0c Bump bluetooth-data-tools to 1.22.0 (#135879) 2025-01-17 12:06:01 -10:00
J. Nick Koston
b98e1a1d2f Bump habluetooth to 3.9.0 (#135877) 2025-01-17 12:05:41 -10:00
J. Nick Koston
a08e42399d Bump fnv-hash-fast to 1.2.2 (#135872) 2025-01-17 12:04:53 -10:00
epenet
2b0e383b2e Use new ServiceInfo location in zha (#135703) 2025-01-17 22:56:59 +01:00
J. Nick Koston
9868138fc4 Bump aioesphomeapi to 28.0.1 (#135869) 2025-01-17 11:53:29 -10:00
epenet
c601170b1d Use new ServiceInfo location in devolo_home_network (#135690) 2025-01-17 21:01:05 +01:00
Raphael Hehl
5ea5413064 Remove device_class from NFC and fingerprint event descriptions (#135867) 2025-01-17 09:49:01 -10:00
Marc Mueller
abc256fb3e Add overload for async singleton call with HassKey (#134059) 2025-01-17 19:22:48 +01:00
G Johansson
2ec971ad9d Remove not needed name from config flow in SMHI (#134841) 2025-01-17 19:21:13 +01:00
Erik Montnemery
235fda55fe Validate config entry when adding or updating entity registry entry (#135067) 2025-01-17 19:18:13 +01:00
epenet
028a0d4eec Remove call to get_serial_by_id in homeassistant_sky_connect (#135751) 2025-01-17 19:10:56 +01:00
epenet
14f3868c26 Fix flaky test in acmeda (#135846) 2025-01-17 19:08:32 +01:00
Indu Prakash
54e4e8a7bb Fix humidifier on off status update (#135743) 2025-01-17 18:49:33 +01:00
Andre Lengwenus
a8cb618f96 Add missing data_descriptions to strings.json for LCN (#135674) 2025-01-17 18:09:19 +01:00
Norbert Rittel
ca5aca4ab9 Fix "set" / "sets" in action names and descriptions, spelling of "dB" (#135659) 2025-01-17 18:08:48 +01:00
Guido Schmitz
ea7e53d10d Add zeroconf dependency to devolo Home Network manifest (#135708) 2025-01-17 18:08:26 +01:00
Norbert Rittel
c7de3112fb Fix several issues in a string of IHC integration (#135618) 2025-01-17 18:02:33 +01:00
hahn-th
4a64c797d4 Add doorbell event to homematicip_cloud (#133269) 2025-01-17 17:54:15 +01:00
Max R
2a514ebc3f Update yolink "play on speaker hub" action to allow optional values (to match YoLink API) (#133099) 2025-01-17 17:43:47 +01:00
jesperraemaekers
44b577cadb Bump Weheat to 2025.1.15 (#135626) 2025-01-17 17:43:15 +01:00
Brett Adams
24bb623567 Add streaming to Teslemetry cover platform (#135660) 2025-01-17 17:38:03 +01:00
tronikos
9e0df89bee Log errors in opower (#135497) 2025-01-17 17:33:48 +01:00
Norbert Rittel
fb309a3f98 Fix description of "x10_all_units_off" action (#135000) 2025-01-17 17:18:38 +01:00
Mick Vleeshouwer
829d3bf621 Add support for EvoHomeController in Overkiz (#133777) 2025-01-17 17:13:25 +01:00
Bouwe Westerdijk
a2afc1b670 Plugwise test-code improvements (#134193) 2025-01-17 17:12:09 +01:00
Norbert Rittel
7b413b5faf Clarify action descriptions regarding Lost device sound and state (#134277) 2025-01-17 16:56:14 +01:00
Markus Adrario
734d1898cf Homee: fix cover if it has no up/down attribute (#135563) 2025-01-17 15:51:18 +01:00
Joost Lekkerkerker
c651e2b3c3 Enable RUF101 (#135835) 2025-01-17 13:01:07 +01:00
Joost Lekkerkerker
ef8b8fbbaa Enable RUF023 (#135830) 2025-01-17 12:28:27 +01:00
Renier Moorcroft
23e04ced9c Image entity key error when camera is ignored in EZVIZ (#134343) 2025-01-17 12:27:44 +01:00
Noah Husby
13a7ad759c Add media position & seek to Russound RIO (#134372) 2025-01-17 12:03:52 +01:00
Andre Lengwenus
99d250f222 Set target value on LCN regulator lock (#133870) 2025-01-17 11:15:42 +01:00
Brett Adams
689d7d3cd9 Add Energy History to Tesla Fleet (#126878)
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: JEMcats <hurst-status09@icloud.com>
Co-authored-by: JEMcats <jakobmattheis@icloud.com>
2025-01-17 10:34:35 +01:00
Joost Lekkerkerker
b4f4b06f29 Enable RUF021 (#135832) 2025-01-17 10:20:45 +01:00
Simon
85b4be2f16 Add model option to speak action for ElevenLabs (#133902) 2025-01-17 10:18:07 +01:00
dcmeglio
5e0bbf65e4 Gracefully handle webhook unsubscription if error occurs while contacting Withings (#134271) 2025-01-17 10:14:41 +01:00
Richard Kroegel
514b74096a Improve BMW test quality (#133704) 2025-01-17 09:58:46 +01:00
Richard Kroegel
b1d8994751 Add BMW quality scale details (#132017)
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2025-01-17 09:49:58 +01:00
J. Nick Koston
c215aee940 Reduce duplicate code in the Bluetooth WebSocket API (#135808) 2025-01-17 10:30:14 +02:00
Manu
5f9457ab6e Update quality scale docs-installation-parameters rule for IronOS integration (#133318) 2025-01-17 09:23:04 +01:00
Redge
76cdfe861c Add 'silent' to HTML5_SHOWNOTIFICATION_PARAMETERS (#135709) 2025-01-17 09:16:45 +01:00
epenet
bd91cc4bdc Use new ServiceInfo location in bosch_shc (#135689) 2025-01-17 09:15:20 +01:00
G Johansson
cde3ba5504 Ensure entity platform in dsmr_reader tests (#135718) 2025-01-17 09:14:40 +01:00
G Johansson
21256cab85 Ensure entity platform in google_assistant tests (#135719) 2025-01-17 09:14:25 +01:00
G Johansson
d62a66eaf2 Ensure entity platform in google_wifi tests (#135720) 2025-01-17 09:14:08 +01:00
Marc Mueller
46b17b539c Use new syntax for TypeVar defaults (#135780) 2025-01-17 09:12:52 +01:00
G Johansson
6aed2dcc0f Ensure entity platform in homeassistant tests (#135721) 2025-01-17 09:11:07 +01:00
G Johansson
7430238c0a Ensure entity platform in kira tests (#135723) 2025-01-17 09:10:47 +01:00
G Johansson
cd88913daf Ensure entity platform in mochad tests (#135725) 2025-01-17 09:10:29 +01:00
G Johansson
8e39c65759 Ensure entity platform in universal tests (#135727) 2025-01-17 09:10:09 +01:00
G Johansson
0f8785d8bc Ensure entity platform in alert tests (#135714) 2025-01-17 08:44:40 +01:00
Petro31
566f514a75 Allow is_state_attr to check attributes for None (#132879) 2025-01-17 08:41:10 +01:00
G Johansson
f3683f0b5e Ensure entity platform in blackbird tests (#135715) 2025-01-17 08:34:47 +01:00
J. Nick Koston
a39137c3fc Bump zeroconf to 0.140.1 (#135815) 2025-01-17 08:29:44 +01:00
J. Nick Koston
c2b6c4b4fc Small cleanups to lifx services to reduce code (#135817) 2025-01-16 19:39:48 -10:00
J. Nick Koston
daac986e00 Bump dbus-fast to 2.29.0 (#135804) 2025-01-16 15:10:01 -10:00
Avi Miller
02ec1d1b71 New paint_theme service added to the LIFX integration (#135667)
* New paint_theme service added to the LIFX integration

Signed-off-by: Avi Miller <me@dje.li>
Co-authored-by: J. Nick Koston <nick@koston.org>

* Move effect selection into a dispatch table

Signed-off-by: Avi Miller <me@dje.li>

---------

Signed-off-by: Avi Miller <me@dje.li>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-16 14:41:09 -10:00
Erwin Douna
632c166201 SMA update code owners (#135812)
Update code owners
2025-01-16 23:48:40 +01:00
Joost Lekkerkerker
8b12f5270e Enable more RUF rules (#135770)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-01-16 23:43:14 +01:00
Maciej Bieniek
b0d3aa1c34 Bump imgw_pib to version 1.0.9 and remove hydrological detail entities (#134668) 2025-01-16 23:42:03 +01:00
Konrad Vité
e6c696933f Fix DiscoveryFlowHandler when discovery_function returns bool (#133563)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-16 12:31:16 -10:00
G Johansson
e5164496cf Ensure entity platform in vacuum tests (#135786) 2025-01-16 23:27:54 +01:00
G Johansson
88c3be4ecf Ensure entity platform in light tests (#135787) 2025-01-16 23:26:50 +01:00
G Johansson
619917c679 Ensure entity platform in media_player tests (#135788) 2025-01-16 23:26:18 +01:00
Norbert Rittel
e433c2250c Several strings fixes in the emoncms integration (#135792) 2025-01-16 23:22:28 +01:00
Erwin Douna
59429dea39 Bump SMA to 0.7.5 (#135799) 2025-01-16 23:20:36 +01:00
Sid
3e4d92f6a7 Bump eheimdigital to 1.0.5 (#135802) 2025-01-16 23:19:41 +01:00
Norbert Rittel
a3d24f2472 Fix spelling of "API" and use consistent term "API token" (#135795) 2025-01-16 23:18:54 +01:00
Erwin Douna
46c5591336 SMA add serial number in DeviceInfo (#135809)
SSIA
2025-01-16 23:17:42 +01:00
Norbert Rittel
99f24ca59c Fix service description to match HA style, fix casing (#135797) 2025-01-16 23:15:07 +01:00
Erik Montnemery
1fee0a5aa2 Improve backup store in tests (#135798) 2025-01-16 23:14:19 +01:00
Shay Levy
ef34a33a7b Remove misleading "Current" in NUT power sensor names (#135800) 2025-01-16 23:07:43 +01:00
G Johansson
bb505baae7 Ensure entity platform in core config tests (#135729) 2025-01-16 12:06:20 -10:00
Jan Bouwhuis
b446eaf2d0 Improve incomfort test coverage (#135806) 2025-01-16 23:04:57 +01:00
Jan Bouwhuis
60d51bf4ad Assign entity_category for incomfort entities (#135807) 2025-01-16 23:03:48 +01:00
puddly
9b66ba61a8 USB device add/remove callbacks (#131224) 2025-01-16 11:53:15 -10:00
J. Nick Koston
eb651a8a71 Bump govee-ble to 0.42.0 (#135801) 2025-01-16 11:37:12 -10:00
Avi Miller
1b520e37e2 Update aiolifx-themes to 0.6.4 (#135805)
* Restore support for Python 3.12

Signed-off-by: Avi Miller <me@dje.li>

* Bump aiolifx-themes to 0.6.4

Signed-off-by: Avi Miller <me@dje.li>

---------

Signed-off-by: Avi Miller <me@dje.li>
2025-01-16 23:33:54 +02:00
puddly
9331b1572c Implement a polling fallback for USB monitor (#130918) 2025-01-16 11:14:53 -10:00
Paulus Schoutsen
762bc7b8d1 Add broadcast intent (#135337) 2025-01-16 14:41:53 -06:00
J. Nick Koston
6e255060c6 Add Bluetooth config entries for remote scanners (#135543) 2025-01-16 09:52:52 -10:00
Steve HOLWEG
93b3d76ee2 Add button to move netatmo cover to preferred position (#134722) 2025-01-16 18:34:30 +00:00
Markus Jacobsen
e188d9a00c Fix Bang & Olufsen event testing (#135707)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-01-16 18:06:18 +00:00
Gerben Jongerius
d908d2ab55 Bump youless-api to 2.2.0 (#135781)
Bump version youless 2.2.0
2025-01-16 17:44:09 +02:00
Duco Sebel
55bde60f1a Move HomeWizard config options to class (#135778) 2025-01-16 15:44:59 +01:00
Jan Bouwhuis
5ca68cb273 Improve incomfort coordinator logging (#135777) 2025-01-16 15:24:40 +01:00
Erik Montnemery
2e189480a5 Improve backup decrypt exceptions (#135765) 2025-01-16 16:07:13 +02:00
Joost Lekkerkerker
eb98f110d3 Fix Vicare patch (#135773) 2025-01-16 15:41:24 +02:00
Paulus Schoutsen
5cf56207fe Add temperature and humidity entities to area registry (#135423)
* Add temperature and humidity entities to area registry

* Fix service test

* Add validation

* ABC

* More ABC

* More ABC 2

* Fix tests

* ABC 3

* ABC 4
2025-01-16 08:25:26 -05:00
Joost Lekkerkerker
9f7a38f189 Enable RUF022 (#135767) 2025-01-16 13:48:24 +01:00
epenet
476935050a Use new ServiceInfo location in dlna_dmr (#135691) 2025-01-16 13:41:09 +01:00
epenet
27c2f2333e Use new ServiceInfo location in esphome (#135692) 2025-01-16 13:40:13 +01:00
Christopher Fenner
40a3e19ce5 Add further ventilation-related sensors to ViCare (#131496) 2025-01-16 13:38:40 +01:00
Matthew FitzGerald-Chamberlain
9d7706c9be Aprilaire - Fix humidifier showing when it is not available (#133984) 2025-01-16 13:37:44 +01:00
Max Cabrajac
a67bc12bb8 Change AdGuard Home URL field validator to accept paths (#127957) 2025-01-16 13:34:30 +01:00
Erik Montnemery
6cbe18ebbd Bump securetar to 2025.1.3 (#135762)
* Bump securetar to 2025.1.3

* Remove outdated fixture
2025-01-16 13:26:52 +01:00
epenet
1cff45b8b7 Use new ServiceInfo location in apple_tv (#135688) 2025-01-16 13:20:46 +01:00
Joost Lekkerkerker
fc39b6792c Enable RUF100 (#135760) 2025-01-16 13:06:33 +01:00
DrDonoso
3638d25f6a Add message_thread_id to telegram_text and telegram_command events (#130738)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-16 13:03:42 +01:00
Martin Hjelmare
421f9aa638 Avoid using the backup manager in restore tests (#135757)
* Fix typing

* Refactor test restore backup

* Refactor test restore backup wrong password

* Refactor test restore backup wrong parameters

* Update manager state after rebase

* Remove not needed patch
2025-01-16 12:49:27 +01:00
Simone Rescio
9a1b965c7f Fix rmtree in translation script on MacOS (#129352) 2025-01-16 12:39:37 +01:00
Erik Montnemery
9db6be11f7 Support decrypting backups when downloading (#135728)
* Support decrypting backups when downloading

* Close stream

* Use test helper

* Wait for worker to finish

* Simplify

* Update backup.json

* Simplify

* Revert change from the future
2025-01-16 12:36:12 +01:00
Tyron
6fdccda225 Return Chat IDs on Telegram Bot (#131274)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-16 12:27:19 +01:00
epenet
1172887c80 Use new ServiceInfo location in zwave_js (#135704) 2025-01-16 10:55:48 +01:00
epenet
f3b7317373 Use new ServiceInfo location in homeassistant_sky_connect (#135693) 2025-01-16 10:55:14 +01:00
puddly
edddd6edfb Reduce USB rescan cooldown from 1 minute to 10 seconds (#135712)
* Reduce USB rescan cooldown from 1 minute to 1 second

* Increase cooldown to 10s as a middle ground
2025-01-16 11:08:38 +02:00
J. Nick Koston
016a274698 Bump govee-ble to 0.41.0 (#135750)
Adds support for the H5130 pressure/presence sensor

changelog: https://github.com/Bluetooth-Devices/govee-ble/compare/v0.40.0...v0.41.0
2025-01-16 10:48:29 +02:00
G Johansson
c89d60fb5d Ensure entity platform in light tests (#135724) 2025-01-16 09:21:49 +01:00
dotvav
b5a7d0258a Palazzetti integration: Update integration quality scale (#135752)
Update integration quality scale
2025-01-16 09:19:37 +01:00
Norbert Rittel
137666982d Reword action descriptions to match Home Assistant style (#135733)
* Reword action descriptions to match Home Assistant style

This commit changes the two action descriptions of the Husqvarna Automower integration to use the descriptive language that is standard in Home Assistant.

This helps in fixing or preventing wrong (machine) translations.

This is done using the wording from the online documentation by using "Lets the mower … ", moving the actual result more into focus.

* Re-add "either" to first description
2025-01-16 09:18:23 +01:00
Martin Hjelmare
77a351f992 Add receive backup tests (#135680)
* Clean up test_receive_backup_busy_manager

* Test receive backup agent error

* Test file write error during backup receive

* Test read tar error during backup receive

* Test non agent upload error during backup receive

* Test file read error during backup receive
2025-01-16 08:41:59 +01:00
G Johansson
a8645ea4ed Ensure entity platform in bluetooth tests (#135716) 2025-01-15 21:24:37 -10:00
Brett Adams
e886c9e054 Slow down polling for Tesla Fleet (#135747)
Slow down polling
2025-01-15 23:28:15 -05:00
Jamin
79ee2e954b Use SIP URI for VoIP device identifier (#135603)
* Use SIP URI for VoIP device identifier

Use the SIP URI instead of just host/IP address to identify VoIP
devices. This will allow calls initiating from Home Assistant to the
device as well as allows devices connecting through a PBX to be uniquely
identified.

* Add tests

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-01-15 20:59:58 -05:00
J. Nick Koston
e736ca72f0 Handle invalid HS color values in HomeKit Bridge (#135739) 2025-01-15 13:33:58 -10:00
epenet
be06ef46c1 Use new ServiceInfo location in wmspro (#135702)
* Use new ServiceInfo location in wmspro

* Fix self.source
2025-01-15 21:22:05 +01:00
Steven B.
51e3bf42f2 Add dynamic child device handling to tplink integration (#135229)
Add dynamic child device handling to tplink integration. For child devices that could be added/removed to hubs.
2025-01-15 20:45:06 +01:00
Ik-12
c6cab3259c Create switches for controlling policy-based routes (#134473)
Create switches for controlling policy-based routes (aka "traffic routes" in the Unifi API).
2025-01-15 20:37:33 +01:00
Sid
146d6bbc68 Bump eheimdigital to 1.0.4 (#135722) 2025-01-15 21:29:29 +02:00
Erik Montnemery
f36a10126c Add WS command backup/can_decrypt_on_download (#135662)
* Add WS command backup/can_decrypt_on_download

* Wrap errors

* Add default messages to exceptions

* Improve test coverage
2025-01-15 19:40:29 +01:00
Steven B.
3622e8331b Update tplink quality_scale.yaml (#135705) 2025-01-15 16:53:57 +01:00
epenet
241fc2af67 Use new ServiceInfo location in insteon (#135694) 2025-01-15 16:35:27 +01:00
epenet
9d7c917771 Use new ServiceInfo location in modem_callerid (#135695) 2025-01-15 16:17:43 +01:00
epenet
d3bedd693a Use new ServiceInfo location in rabbitair (#135696) 2025-01-15 16:17:09 +01:00
epenet
082ef3f85f Use new ServiceInfo location in rainforest_raven (#135697) 2025-01-15 16:15:51 +01:00
epenet
5e648ebb5c Use new ServiceInfo location in tplink (#135700) 2025-01-15 16:14:55 +01:00
epenet
7a442af9fa Use new ServiceInfo location in sonos (#135699) 2025-01-15 16:14:21 +01:00
epenet
406c00997f Use new ServiceInfo location in components (part 3) (#135687) 2025-01-15 15:49:45 +01:00
epenet
19a89ebcf3 Use new ServiceInfo location in components (part 2) (#135685) 2025-01-15 15:49:01 +01:00
epenet
bc8a2b58d3 Use new ServiceInfo location in components (part 1) (#135682) 2025-01-15 15:43:46 +01:00
Mick Montorier-Aberman
6a50648223 Call async_forward_setup_entry after the first refresh in SwitchBot Cloud (#135625) 2025-01-15 14:33:21 +00:00
epenet
e83ee00af8 Move UsbServiceInfo to service_info helpers (#135663)
* Move UsbServiceInfo to service_info helpers

* Adjust components
2025-01-15 15:10:25 +01:00
Robert Resch
8ae02aaba0 Add missing camera functions to pylint type hints plugin (#135676) 2025-01-15 14:53:08 +01:00
puddly
0eea265415 Bump python-otbr-api to 2.7.0 (#135638)
Bump OTBR API to 2.7.0

Bump `python-otbr-api` to 2.7.0 in `thread` as well
2025-01-15 15:04:42 +02:00
epenet
8c13daf6d9 Move SsdpServiceInfo to service_info helpers (#135661)
* Move SsdpServiceInfo to service_info helpers

* docstring

* Move string constants

* Adjust components
2025-01-15 15:00:27 +02:00
Martin Hjelmare
4ccc686295 Improve logging of backup upload errors (#135672)
Improve logging for upload errors
2025-01-15 14:59:42 +02:00
epenet
31c36beb2e Move DhcpServiceInfo to service_info helpers (#135658)
* Move DhcpServiceInfo to service_info helpers

* Fix mypy/pylint
2025-01-15 13:09:18 +01:00
Norbert Rittel
9c5c1a35a4 Fix descriptions of send_command action for consistency (#135670)
Three small fixes for the description keys of the send_command action of the Homeworks integration:
- use third-person singular for descriptive wording
- Change to "the command" to match "the controller" in two strings

Both ensure better and more consistent machine and human translations.
2025-01-15 14:00:40 +02:00
epenet
b046ca9abe Move ZeroconfServiceInfo to service_info helpers (#135653)
* Move ZeroconfServiceInfo to service_info helpers

* Adjust deprecation date

* Fix mypy/pylint

* Fix DeprecatedConstant

* Add deprecation test

* Adjust

* Also deprecate ATTR_PROPERTIES_ID
2025-01-15 12:25:42 +01:00
Avi Miller
650e14379c Bump aiolifx-themes to v0.6.2 (#135645)
* Bump aiolifx-themes to v0.6.1

Signed-off-by: Avi Miller <me@dje.li>

* Bump aiolifx-themes to 0.6.2 to fix deps issue with 0.6.1

Signed-off-by: Avi Miller <me@dje.li>

---------

Signed-off-by: Avi Miller <me@dje.li>
2025-01-15 12:59:15 +02:00
Jan Bouwhuis
1421f4c124 Set MQTT quality scale to platinum (#135612)
* Set MQTT quality scale to platinum

* Add  test for type stub
2025-01-15 10:51:41 +01:00
Jan Bouwhuis
f0257fec88 Fix mqtt number state validation (#135621) 2025-01-15 10:13:27 +01:00
Joost Lekkerkerker
8a35261fd8 Remove unused noqas (#135583) 2025-01-15 10:02:18 +01:00
hahn-th
f57640c2cd Bump homematicip to 1.1.6 (#135649) 2025-01-15 09:31:48 +01:00
TimL
23a2b19ca0 Bump pysmlight v0.1.5 (#135647) 2025-01-15 09:58:38 +02:00
TimL
6cbbfec5f5 Reduce scan interval on SMLIGHT firmware updates (#135650)
Reduce scan interval on firmware updates
2025-01-15 09:56:01 +02:00
Brett Adams
65df8b946f Update buttons in Teslemetry (#135631)
* Update button

* tests
2025-01-15 08:32:46 +01:00
Thomas55555
4b37b367de Dynamic devices for Husqvarna Automower (#133227)
* Dynamic devices for Husqvarna Automower

* callbacks

* add stayout-zones together

* add alltogether on init

* fix stale lock names

* also for workareas

* separate "normal" vs callback entity adding

* mark quality scale

* Apply suggestions from code review

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

* Apply suggestions from code review

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

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-01-15 08:31:24 +01:00
Norbert Rittel
c1520a9b20 Fix spelling of EnOcean in strings file of the integration (#135622) 2025-01-15 01:49:10 +01:00
Marc Mueller
239aa94b6f Update Python version for mypy to 3.13 (#135636) 2025-01-15 01:43:13 +01:00
Marc Mueller
c4d8cda92b Update mypy-dev to 1.15.0a2 (#135633) 2025-01-15 00:54:54 +01:00
mkmer
6e88c6570e Return OFF in hvac_action for Honeywell climate (#135620) 2025-01-14 23:15:49 +00:00
Marc Mueller
ecc89fd9a9 Fix spotify typing for Python 3.13 (#135628) 2025-01-15 00:02:22 +01:00
Ville Skyttä
18de735619 More UpCloud config entry refactors (#135548) 2025-01-14 22:49:00 +01:00
Jordan Sitkin
f80f6d9e3d Add PaddleSwitchPico (Pico Paddle Remote) device trigger to Lutron Caseta (#135615) 2025-01-14 10:28:10 -10:00
Erik Montnemery
c408bd6aad Bump securetar to 2025.1.2 (#135614) 2025-01-14 20:39:58 +01:00
Glenn Waters
faf2c64cc4 Bump elkm1-lib to 2.2.11 (#135616) 2025-01-14 09:14:41 -10:00
Franck Nijhof
60bdc13c94 Drop Python 3.12 support (#135589) 2025-01-14 16:23:15 +01:00
epenet
fa96168488 Rename onewire entity classes (#135601) 2025-01-14 15:44:18 +01:00
Manu
526277da0f Add entity pictures to Habitica integration (#134179) 2025-01-14 15:23:22 +01:00
Jan Bouwhuis
934f59449d Make mqtt integration exports explicit (#135595) 2025-01-14 15:17:28 +01:00
adam-the-hero
026df07451 Fix Watergate Power supply mode description and MQTT/Wifi uptimes (#135085) 2025-01-14 14:40:01 +01:00
Indu Prakash
38d008bb66 Add vesync number platform (#135564) 2025-01-14 14:33:48 +01:00
Rob
406c3b5925 Adding support for new Lutron RGB tape light (#130731) 2025-01-14 14:07:20 +01:00
Manu
7cc61d1b86 Skip fetching deactivated shopping lists in Bring integration (#135336)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-14 14:07:07 +01:00
Joost Lekkerkerker
421c4889bf Use device supplied ranges in LaMetric (#135590) 2025-01-14 14:02:17 +01:00
Joost Lekkerkerker
d6ee7a2c1e Add serial number to LaMetric (#135591) 2025-01-14 13:54:08 +01:00
Brett Adams
6a032baa48 Add streaming binary sensors to Teslemetry (#135248)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-14 13:46:10 +01:00
Erik Montnemery
edc7c0ff2f Bump securetar to 2025.1.1 (#135582) 2025-01-14 13:28:43 +01:00
jesperraemaekers
8109efe810 Reverted async-dependency to todo for Weheat (#135588) 2025-01-14 13:27:47 +01:00
Joost Lekkerkerker
5e50b11114 Avoid core documentation url hosted elsewhere (#130513)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-01-14 13:17:25 +01:00
jiriappl
4f796174fd Match the upstream alt id of the new Levoit air purifier (#135426) 2025-01-14 13:17:09 +01:00
Joost Lekkerkerker
5fc3618b4a Bump demetriek to 1.2.0 (#135580) 2025-01-14 12:56:31 +01:00
Steven B.
d970b728ce Update tplink quality_scale.yaml (#135209) 2025-01-14 12:41:48 +01:00
Joost Lekkerkerker
c66176cfa5 Unignore ruff rule ISC001 (#135581) 2025-01-14 12:40:43 +01:00
Indu Prakash
6f138c71b4 Remove incorrect logging about Unknown device (#135585) 2025-01-14 12:38:31 +01:00
Krisjanis Lejejs
6e80ad505b Bump hass-nabucasa from 0.87.0 to 0.88.1 (#135521)
* Bump hass-nabucasa from 0.87.0 to 0.88.0

* Bump hass-nabucasa from 0.88.0 to 0.88.1

* Fix Alexa breaking changes
2025-01-14 12:17:22 +01:00
Joost Lekkerkerker
8db63adc11 Bump ruff to 0.9.1 (#135197) 2025-01-14 11:46:12 +01:00
Jan Bouwhuis
2b51ab1c75 Set MQTT quality scale to gold (#135579) 2025-01-14 11:45:07 +01:00
jesperraemaekers
f4e7c9d6c3 Bump Weheat to 2025.1.14 (#135578) 2025-01-14 11:36:26 +01:00
Josef Zweck
6359a75977 Cleanup tedee callbacks (#135577) 2025-01-14 11:34:37 +01:00
Maikel Punie
096c6b8575 Mark Velbus test coverage as done (#135571) 2025-01-14 11:32:33 +01:00
Maikel Punie
959cea45b8 Migrate Velbus to have Entity name (#135520) 2025-01-14 11:30:10 +01:00
Jan-Philipp Benecke
e3f03c9da1 Set inexogy quality scale to silver (#135547) 2025-01-14 11:20:35 +01:00
Ville Skyttä
1426c421f3 Use percent formatting in logging per guidelines (#135550) 2025-01-14 11:15:38 +01:00
Indu Prakash
58df5f2394 Add iprak to to vesync code owners (#135562) 2025-01-14 10:51:13 +01:00
Erik Montnemery
d333fa320f Fix nmbs sensor unique_id (#135576) 2025-01-14 10:24:48 +01:00
jesperraemaekers
6d7e9f10d9 Set PARALLEL_UPDATES for Weheat (#135574)
Add PARALLEL_UPDATES
2025-01-14 11:19:28 +02:00
Manu
0c144092c6 Bump habiticalib to v.0.3.3 (#135551) 2025-01-14 10:07:23 +01:00
Artur Pragacz
1de4d0efda Fix deprecated enums (#134824) 2025-01-14 10:04:48 +01:00
Erik Montnemery
440cd5bee0 Improve improv via BLE log messages (#135575) 2025-01-14 10:00:21 +01:00
Master-Guy
09e2168f72 Changed json.schemas.url for devcontainers (#135281) 2025-01-13 21:46:32 +01:00
Michael Hansen
b897e6a85f Use STT/TTS languages for LLM fallback (#135533) 2025-01-13 14:17:12 -06:00
Norbert Rittel
3e9b410b7c Fix grammar issue in 'invalid_auth' string (#135546)
Remove that wrong comma and add a "that" to clarify the meaning of the error message.
2025-01-13 20:56:10 +01:00
Jan-Philipp Benecke
3c825bb826 Set PARALLEL_UPDATES for inexogy (#135545) 2025-01-13 20:48:24 +01:00
Jan-Philipp Benecke
e8ad391df2 Add data_descriptions to inexogy config flow (#135536) 2025-01-13 20:31:13 +01:00
JJ
504ed83ffb Add person component to strict type checking (#132754) 2025-01-13 20:11:17 +01:00
Maikel Punie
eaaab4ccfe Velbus add subdevices for din-rail modules (#131371) 2025-01-13 20:10:45 +01:00
jesperraemaekers
4ddb72314d Add quality scale for weheat (#135384) 2025-01-13 20:09:19 +01:00
Maikel Punie
c489f94026 Velbus unsubscribe to the status updates on removal (#135530) 2025-01-13 20:08:04 +01:00
Maikel Punie
38dcc782d1 Velbus update unique-config-entry quality score (#135524) 2025-01-13 20:07:47 +01:00
qbus-iot
2d2f4f5cec Add new integration Qbus (#127280)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Thomas D <11554546+thomasddn@users.noreply.github.com>
2025-01-13 19:06:52 +00:00
Álvaro Fernández Rojas
ca34541b04 Register Airzone WebServer device (#135538) 2025-01-13 20:06:19 +01:00
Jan Bouwhuis
984c380e13 Add option to allow to use setpoint instead of override for legacy incomfort RF gateway (#135143)
* Add option to allow to use setpoint in stead of override for legacy incomfort RF gateway

* Add test to assert state with legacy_setpoint_status option

* Use selector

* Update homeassistant/components/incomfort/strings.json

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

* Follow up on code review

* Rephrase data_description

* Rephrase

* Use async_schedule_reload helper

* Move option flow after config flow

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-13 19:50:06 +01:00
G Johansson
1c053485a9 Bump smhi-pkg to 1.0.19 (#135537) 2025-01-13 20:40:01 +02:00
Steven B.
ab28115d2b Cleanup tplink test framework (#135205) 2025-01-13 19:32:22 +01:00
Indu Prakash
d986fe7a07 Add humidifier entity for Vesync devices (#134333) 2025-01-13 19:26:18 +01:00
Jan Bouwhuis
6fd73730cc Bump aioimaplib to 2.0.0 (#135448) 2025-01-13 19:19:06 +01:00
Jan Bouwhuis
b93aa760c5 Refactor the MQTT option and reconfigure flow (#133342)
* Move entry options to entry.options en remove broker setup from mqtt option flow

* UPdate diagnostics to export both entry data and options

* Parameterize entry options directly not depending on migration

* Update tests to use v2 entry and add separate migration test

* use start_reconfigure_flow helper

* Update quality scale comment

* Do minor entry upgrade, and do not force to upgrade entry

* Ensure options are read from older entries

* Add comment

* Follow up on code review

* Assert config entry version checking the broker connection

* Update comment
2025-01-13 19:00:18 +01:00
epenet
b84a4dc120 Add zeroconf discovery to onewire (#135295) 2025-01-13 17:52:37 +00:00
Manu
cdcc7dbbe8 Deprecate sensors in Habitica integration (#134036)
* Deprecate sensors

* move to setup, remove disabled

* changes

* add breaking version to string

* fixes

* fix entity id in tests
2025-01-13 18:35:14 +01:00
Maikel Punie
8d38279993 Bump velbusaio to 2025.1.0 (#135525) 2025-01-13 17:18:46 +01:00
dotvav
153496b5f4 Palazzetti integration: Add support for additional fans (#135377)
* Add support for second and third fans

* Update test mock and snapshot

* Test coverage and error message

* Rename fans left and right instead of 2 and 3
2025-01-13 17:17:46 +01:00
jesperraemaekers
1fa3d90d73 Removing unused API file form weheat (#135518) 2025-01-13 17:14:10 +01:00
Robert Resch
1e4c7e832d Bump go2rtc recommended version to 1.9.8 (#135523) 2025-01-13 17:02:23 +01:00
Maxim Mikityanskiy
275365a9d3 Expose raw PM2.5 in Airgradient (#135457) 2025-01-13 14:42:53 +00:00
G Johansson
4709a3162c Change Trafikverket Train to use station signatures (#131416)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-13 15:38:02 +01:00
Franck Nijhof
157548609b Revert "Make all three numbered lists consistent, using 1. 1. 1. for the syntax" (#135510) 2025-01-13 14:18:47 +00:00
Manu
fc0a6c2ff3 Refactor number/select to use common method in IronOS (#134173) 2025-01-13 14:50:55 +01:00
Klaas Schoute
0d116ec6a2 Improve tests of energyzero integration (#133452)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-01-13 14:49:01 +01:00
Marc Mueller
6060f637a8 Update getmac to 0.9.5 (#135506) 2025-01-13 14:47:32 +01:00
Shay Levy
ba9ad009e9 Fix LG webOS TV trigger validation (#135312)
* Fix LG webOS TV trigger validation

* Raise if not loaded
2025-01-13 15:37:40 +02:00
Norbert Rittel
ec5759d3b9 Fix typos "Login" > "Log in" and "Setup" > "Set up" (#135306) 2025-01-13 14:16:25 +01:00
Norbert Rittel
c7a5c49a03 Small fixes in the strings file of the azure_data_explorer integration (#135309) 2025-01-13 14:16:00 +01:00
Dave T
9b55faa879 Refactor config flow tests in generic camera (#134385)
Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
Co-authored-by: Allen Porter <allen.porter@gmail.com>
2025-01-13 14:15:21 +01:00
jesperraemaekers
6fd9476bb9 Refresh token before setting up weheat (#135264) 2025-01-13 14:01:57 +01:00
dotvav
d33ee130bc Bump pypalazzetti to 0.1.19 (#135465) 2025-01-13 13:59:34 +01:00
dotvav
e1ffd9380d Replace climate fan speed 'silent' with a button (#135075) 2025-01-13 13:51:20 +01:00
Norbert Rittel
fc6695b05c Use proper sentence-case for all strings in azure_event_hub (#135328) 2025-01-13 13:47:40 +01:00
Duco Sebel
8f71d7a6f3 Move HomeWizard API initialisation to async_setup_entry (#135315) 2025-01-13 13:35:50 +01:00
Manu
4dbf2b0320 Fix grey dailies with weekly frequency and no weekdays selected in Habitica (#135419) 2025-01-13 13:20:15 +01:00
Austin Mroczek
3aa466806e TotalConnect update quality_scale with documentation updates (#134049) 2025-01-13 13:11:56 +01:00
Lukas Schlötterer
7b63c17101 Add kV and MV unit conversion for voltages (#135396) 2025-01-13 13:00:35 +01:00
Brett Adams
dae87db244 Fix when live status is blank in Telsemetry (#130408) 2025-01-13 12:44:36 +01:00
Norbert Rittel
fba1b4be5b Replace "click" with "select" to fit for mobile app (#135382) 2025-01-13 12:32:07 +01:00
Mick Vleeshouwer
c15073cc27 Fix incorrect cast in HitachiAirToWaterHeatingZone in Overkiz (#135468) 2025-01-13 12:11:01 +01:00
Paul Daumlechner
25041aa02d Add dhcp discovery to velux (#135138)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-13 12:01:04 +01:00
Norbert Rittel
96ad2b6ed8 Replace "Login …" with "Log in …" in two strings of Habitica integration (#135383) 2025-01-13 11:55:55 +01:00
epenet
a649ff4a91 Add hassio discovery to onewire (#135294) 2025-01-13 11:55:18 +01:00
Joost Lekkerkerker
1ceebd92a9 Change icon ID name in Lametric (#135368) 2025-01-13 11:48:00 +01:00
Artur Pragacz
b009f11013 Fix referenced objects in script sequences (#135499) 2025-01-13 11:40:53 +01:00
Maikel Punie
2d67aca550 Rework velbus services to deprecated the interface parameter (#134816)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-13 11:36:20 +01:00
LG-ThinQ-Integration
98ef32c668 Add remain, running, schedule time sensors to LG ThinQ (#131133)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-01-13 11:29:09 +01:00
Erik Montnemery
3a0072d42d Fix typing in zha update entity (#135500) 2025-01-13 11:27:20 +01:00
Maciej Bieniek
86ea68eaec Add missing total active returned energy sensor for Shelly Mini PM Gen3 (#135433)
Add missing total active returned energy sensor for Mini PM Gen3
2025-01-13 11:12:04 +01:00
Michael
e67a131bd9 Bump uv to 0.5.18 (#135454) 2025-01-13 09:11:46 +01:00
dependabot[bot]
c36d73e469 Bump github/codeql-action from 3.28.0 to 3.28.1 (#135492)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 08:54:42 +01:00
Khole
ac279d9794 Replace pyhiveapi with pyhive-integration (#135482) 2025-01-13 08:50:25 +01:00
J. Nick Koston
4e5bf5ac22 Ensure ESPHome cleanups Bluetooth scanner data upon removal (#135470)
* Add bluetooth API to remove scanners that are no longer used

- Cleanup the advertisment history right away when a scanner is removed

In the future we will do some additional cleanup

* coverage

* finish tests

* Ensure ESPHome cleanups Bluetooth scanner data upon removal

needs https://github.com/home-assistant/core/pull/135408
2025-01-12 22:41:49 -05:00
J. Nick Koston
2e5e2c50dd Ensure Shelly cleanups Bluetooth scanner data upon removal (#135472)
* Add bluetooth API to remove scanners that are no longer used

- Cleanup the advertisment history right away when a scanner is removed

In the future we will do some additional cleanup

* coverage

* finish tests

* Ensure Shelly cleanups Bluetooth scanner data upon removal

needs https://github.com/home-assistant/core/pull/135408
2025-01-12 22:41:21 -05:00
J. Nick Koston
c9a7afe439 Add bluetooth API to remove scanners that are no longer used (#135408) 2025-01-12 14:03:05 -10:00
Ville Skyttä
0a444de39c Refactor upcloud to use config entry runtime data (#135449) 2025-01-13 01:43:37 +02:00
Ravaka Razafimanantsoa
559c411dd2 Add current and voltage for plugs to switchbot_cloud (#135458)
SwitchBot Cloud: Adding current and voltage for plugs
2025-01-12 18:42:06 +01:00
tronikos
61ea732caa Fix strings for the Google integrations (#135445) 2025-01-12 09:15:33 -08:00
Ravaka Razafimanantsoa
11ebc27bfe Bump switchbot-api to 2.3.1 (#135451) 2025-01-12 17:29:01 +01:00
Manu
ccb94ac6a6 Update translations and error messages in Bring! integration (#135455)
* Update translations and error messages

* use placeholder for field name

* change key for translation string
2025-01-12 16:27:31 +01:00
WaterInTheLake
ab0dfe304c Fix translation string: numbering in list (#135441) 2025-01-12 14:42:45 +01:00
Norbert Rittel
8b0be70fdd Fix descriptions of send_message action of Bring! integration (#135446)
* Make "Urgent message" selector consistent, use "Bring!" as name

- Replace one occurrence of "bring" with the brand name "Bring!"
- Change description of action to third-person singular for consistency in Home Assistant
- Make all occurrences of the selector "Urgent message" consistent (in sentence case) so they all get consistent translations, too
- Change one related error message to refer to the UI name of the required "Article" field

* Changed ` to '  to avoid Regex problems

* Reverted change to notify_missing_argument_item

Reverted to avoid failing test

* Reverted change to "bring"

* Add "is" to description of "Article"

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

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-01-12 14:36:23 +01:00
tronikos
f7df214dd8 Fix config entries typo s/entruis/entries/ (#135431)
Fix typo s/entruis/entries/
2025-01-12 11:07:45 +01:00
Andrew Sayre
11fa6b2e4e Bump pyheos to 1.0.0 (#135415) 2025-01-11 23:06:06 -06:00
Joost Lekkerkerker
52c57eb2e5 Actually use translated entity names in Lametric (#135381) 2025-01-11 23:15:49 +01:00
Manu
0d85f54e76 Add sensors for inventory items to Habitica (#135331)
Add sensors for inventory items
2025-01-11 21:31:36 +01:00
Norbert Rittel
b3af12c9b1 Reword action descriptions for better translations in Teslemetry (#135370)
Slightly reword action descriptions for better translations

Currently only one of the action descriptions in the Teslemetry integration uses the descriptive form of third person plural.

This commit changes the remaining descriptions to adopt the same language and changes "the" to "a" as the actual action target is defined below that in the UI.
2025-01-11 20:15:41 +00:00
Jeff Terrace
6571ebf15b Add additional Tapo ONVIF Person/Vehicle/Line/Tamper/Intrusion events (#135399) 2025-01-11 09:52:46 -10:00
Norbert Rittel
2237ed9af7 Make all three numbered lists consistent, using 1. 1. 1. for the syntax (#135400)
Make all three numbered lists use 1. 1. 1. for the syntax

Currently only two of the setup descriptions of the Nest integration use automatic syntax for a numbered list.

This commit makes the third one consistent, using 1. 1. 1. as well.

This helps translators in Lokalise understand that this is the expected format for all numbered lists in Home Assistant.
2025-01-11 21:44:59 +02:00
YogevBokobza
c442935fdd Switcher runner child lock support (#133270)
* Switcher runner child lock support

* fix based on requested changes

* Update homeassistant/components/switcher_kis/switch.py

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

* Fix

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-11 21:01:10 +02:00
Jeff Terrace
6dc9c6819f Add @jterrace to onvif integration owners (#135398) 2025-01-11 19:30:51 +01:00
Shay Levy
a745e079e9 Add reconfigure to LG webOS TV (#135360)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-11 16:16:35 +01:00
Markus Lanthaler
19f460614e Enable slowly-changing, important diagnostics for connected devices by default (#134776) 2025-01-11 15:29:31 +01:00
Brett Adams
20d6ba4286 Bump Teslemetry Stream (#135344)
bump
2025-01-11 16:09:53 +02:00
dotvav
4cf7a51a05 Palazzetti Quality Scale update after doc improvement (#135277) 2025-01-11 13:24:00 +01:00
Simon Lamon
8e2b284a7f Add more typings to nmbs sensor (#135359) 2025-01-11 13:04:37 +01:00
Erwin Douna
74c3e9629f Fix Tado config flow (#135353) 2025-01-11 12:52:40 +01:00
Norbert Rittel
907f1e062a Fix spelling of "Log in …" and "API key" in LOQED integration (#135347) 2025-01-11 12:51:56 +01:00
Simon Lamon
fd169affd7 Remove code owner for nmbs (#135357) 2025-01-11 12:49:10 +01:00
dependabot[bot]
81c390d3b8 Bump docker/build-push-action from 6.10.0 to 6.11.0 (#135254) 2025-01-11 12:32:30 +01:00
dependabot[bot]
d356d4bb82 Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#135255) 2025-01-11 12:31:46 +01:00
Joost Lekkerkerker
4d93fbcb52 Fix backup formatting (#135350) 2025-01-11 11:15:00 +01:00
Simon Lamon
b9259b6f77 Add config flow to NMBS (#121548)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-11 10:31:47 +01:00
Norbert Rittel
22b84450e8 Small fixes in setup flow strings, correct sentence-case (#135349) 2025-01-11 10:10:40 +01:00
Norbert Rittel
9ef93517e7 Fix spelling of "Log in", fix "outdated student" (#135348) 2025-01-11 10:00:59 +01:00
J. Nick Koston
cdc96fdf6f Add bluetooth subscribe_advertisements WebSocket API (#134291) 2025-01-10 16:49:53 -10:00
Paulus Schoutsen
ab8af033c0 Extract resolve announcement media ID for AssistSatelliteEntity (#134917) 2025-01-10 18:33:49 -08:00
J. Nick Koston
619dee5d93 Bump habluetooth to 3.8.0 (#135322)
changelog: https://github.com/Bluetooth-Devices/habluetooth/compare/v3.7.0...v3.8.0
2025-01-10 23:50:03 +02:00
Duco Sebel
00c3b8cc3e Use LOGGER from homewizard.const instead per-file loggers (#135320) 2025-01-10 23:49:36 +02:00
Manu
bf747bb733 Fix Habitica gems/hourglass sensors (#135323) 2025-01-10 23:47:05 +02:00
Robert Resch
560d15effb Don't store uv's lockfile in hassfest image (#135214) 2025-01-10 21:15:44 +01:00
Quentame
39aa0339ac Bump Freebox to 1.2.2 (#135313) 2025-01-10 21:47:48 +02:00
Norbert Rittel
675cc32534 Fix typos, replace duplicated strings with references (#135303) 2025-01-10 18:21:39 +01:00
Norbert Rittel
31b45e6d3f Fix typos and inconsistent spelling of "tedee" brand name (#135305)
- Change "Setup your tedee locks" to "Set up …"
- Remove two excessive commas
- Change one occurrence of "Tedee" to "tedee".
2025-01-10 18:20:50 +01:00
Shay Levy
6fd4d7acaa Use runtime_data in LG webOS TV (#135301) 2025-01-10 19:16:25 +02:00
Maciej Bieniek
c4b4cad335 Bump aioshelly to version 12.3.1 (#135299) 2025-01-10 17:18:00 +01:00
Norbert Rittel
32d3fe714f Grammar and consistency fixes in hdmi_cec strings (#135292) 2025-01-10 16:15:14 +01:00
Manu
6fd0760f25 Add USB-PD Mode select entity to IronOS integration (#134901)
Add USB-PD Mode select entity
2025-01-10 14:07:14 +01:00
Norbert Rittel
59d61104d1 Replace 'entity_id' with UI-friendly, localizable 'entity ID' (#135232)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-10 14:06:58 +01:00
dotvav
028c5349ac Bump pypalazzetti to 0.1.16 (#135269) 2025-01-10 14:06:17 +01:00
Antoine Reversat
9388879b78 Mark FGLAir entities unavailable if they are reporting to be offline (#135202) 2025-01-10 13:24:33 +01:00
Norbert Rittel
246a9f95a3 Smaller grammar fixes, replace 'entity_id' with UI-friendly 'ID' (#135236)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-10 13:23:33 +01:00
Duco Sebel
f31f6d7ed0 Adjust HomeWizard to use updated python-homewizard-energy library (#135046) 2025-01-10 13:19:55 +01:00
Shay Levy
1f0eda8e47 Move LG webOS TV actions to entitiy services (#135285) 2025-01-10 13:02:03 +01:00
cdnninja
bce7e9ba5e Simplify vesync init loading (#135052) 2025-01-10 12:30:29 +01:00
epenet
475a2fb828 Discover new devices at runtime in onewire (#135199) 2025-01-10 11:53:31 +01:00
epenet
24c70caf33 Improve formatting in component files (#135261)
* Improve formatting in component files

* Apply suggestions from code review
2025-01-10 11:46:15 +01:00
Jan Bouwhuis
eba090c9ef Allow to process kelvin as color_temp for mqtt template light (#133957) 2025-01-10 11:43:36 +01:00
epenet
b5971ec55d Add model_id and serial_number to onewire device info (#135279) 2025-01-10 11:18:50 +01:00
epenet
ad84490541 Fix incorrect test in test_core_config (#135260) 2025-01-10 11:10:23 +01:00
Maikel Punie
033064f832 Velbus light platform code cleanup (#134482) 2025-01-10 11:10:09 +01:00
epenet
a2d9920aa9 Fix missing comma in ollama MODEL_NAMES (#135262) 2025-01-10 11:09:49 +01:00
epenet
8386eaa92b Split long strings in stream hls tests (#135271) 2025-01-10 11:09:20 +01:00
epenet
aa741a9207 Combine short strings in components (#135265) 2025-01-10 11:07:51 +01:00
Norbert Rittel
024b9ae414 Change 'entity_id' to UI-friendly 'Entity ID', fix spelling of "setpoint" (#135234)
In addition this makes the description of the first action consistent, using third-person singular like the other two and adhering to the HA standard.
2025-01-10 11:06:08 +01:00
epenet
02956f9a83 Improve formatting in component test files (#135267)
Improve formatting in test files
2025-01-10 10:53:45 +01:00
Shay Levy
9d1989125f Fix LG webOS TV media player test coverage (#135225)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-10 10:44:23 +01:00
epenet
04d5cc8f79 Combine short byte strings in xiaomi_ble tests (#135268) 2025-01-10 10:19:28 +01:00
epenet
e29ead2a36 Split long strings in components (#135263) 2025-01-10 10:13:36 +01:00
epenet
5df7092f41 Improve formatting in core files (#135256)
* Adjust core files formatting

* Adjust translations script
2025-01-10 09:08:37 +01:00
Norbert Rittel
823feae0f9 Make description of alarm_arm_vacation consistent (#135257)
Small fix to also use "Arms …" in the description of the alarm_arm_vacation action, making it consistent with the other two alarm_arm_… actions.
2025-01-10 08:45:06 +01:00
J. Nick Koston
3c6113e37c Remove per engine max bind vars (#135153) 2025-01-09 22:50:13 +00:00
peteS-UK
139b747a70 Expand Squeezebox auth test for config_flow to finish on create_entry (#133612)
Expand auth test to create_entry
2025-01-09 23:47:53 +01:00
J. Nick Koston
da30dbcfe4 Bump fnv-hash-fast to 1.1.0 (#135237) 2025-01-09 12:03:08 -10:00
Simone Chemelli
0deb46295d Refactor Vodafone Station tests (#134956) 2025-01-09 22:22:37 +01:00
Bram Kragten
1abcac5fb5 Update frontend to 20250109.0 (#135235) 2025-01-09 22:13:39 +01:00
J. Nick Koston
3b6f47e438 Bump anyio to 4.8.0 (#135224) 2025-01-09 22:12:34 +01:00
Simone Chemelli
6e1a13f878 Add support for Shelly BLU TRV (#128439)
* feat: add support for Shelly BLU TRV

* chore: apply some fixes

* make BLUTRV a separate device

* apply review comment

* review comments and small optimization

* add HVACMode.OFF

* a couple of fixes

* 2 more fixes

* better approach

* cleanup

* small optimization

* remove cooling as not supported by firmware

* tweaks

* humidity and entity name

* fix naming

* allign async_set_hvac_mode

* align settings

* restore temp

* fix

* remove OFF

* cleanup

* hvac_mode

* add tests

* typo

* more tests

* bump aioshelly
2025-01-09 21:28:36 +01:00
epenet
ee865d2f0f Add exception-translations rule to quality_scale pytest validation (#131914)
* Add exception-translations rule to quality_scale pytest validation

* Adjust

* Return empty dict if file is missing

* Fix

* Improve typing

* Address comments

* Update tests/components/conftest.py

* Update tests/components/conftest.py

* Update tests/components/conftest.py

---------

Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-09 21:21:47 +01:00
epenet
dd57c75e64 Use remove-prefix/suffix introduced in Python 3.9 (#135206)
Use removeprefix/removesuffix
2025-01-09 21:15:22 +01:00
J. Nick Koston
0cc586a3ac Bump zeroconf to 0.139.0 (#135213) 2025-01-09 19:01:49 +01:00
Brett Adams
b6c0257c43 Add streaming sensors to Teslemetry (#132783)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-09 18:58:12 +01:00
Jan Bouwhuis
cabdae98e8 Allow to process kelvin as color_temp for mqtt json light (#133955) 2025-01-09 18:34:42 +01:00
Shay Levy
07482de4ab Fix LG webOS TV init test coverage (#135194) 2025-01-09 18:29:17 +01:00
epenet
31719bc84c Refactor onewire hub (#135186)
* Improve type hints in onewire hub

* More cleanups

* Improve

* Get host/port from entry data

* Use DeviceInfo object
2025-01-09 18:17:21 +01:00
Steven B.
1ca5f79708 Use typed config entry in tplink coordinator (#135182) 2025-01-09 17:43:38 +01:00
Ruslan Sayfutdinov
a5f70dec96 Make generated files appear as generated (#134991) 2025-01-09 17:26:46 +01:00
Allen Porter
6e111d18ec Allow unregistering LLM APIs (#135162) 2025-01-09 08:18:25 -08:00
Jan Bouwhuis
ec37e1ff8d Allow to process kelvin as color_temp for mqtt basic light (#133953) 2025-01-09 16:31:09 +01:00
epenet
8705fd8546 Avoid unnecessary executor calls in onewire (#135187) 2025-01-09 16:11:33 +02:00
Norbert Rittel
050a17db4d Use friendly names in add_to_playlist action, fix "ID" (#134978) 2025-01-09 13:45:32 +01:00
Andre Lengwenus
9dc4597f59 Update module properties on module scan for LCN (#135018) 2025-01-09 13:44:57 +01:00
Arie Catsman
9dd7021d63 No need to set unique_id in enphase_envoy reauth step (#133615)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-01-09 13:31:29 +01:00
Kerey Roper
6a4160bcc4 add support for dimming/brightening X10 lamps (#130196)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-09 13:07:24 +01:00
Norbert Rittel
411d14c2ce Update title and description for setup dialog of thethingsnetwork (#134954) 2025-01-09 13:07:03 +01:00
Joost Lekkerkerker
d7315f4500 Add event entities to Overseerr (#134975) 2025-01-09 12:48:09 +01:00
epenet
c4ac648a2b Add select platform to onewire (#135181)
* Add select platform to onewire

* Add tests

* Apply suggestions from code review
2025-01-09 12:45:49 +01:00
epenet
e9616f38d8 Update scaffold to use internal _PLATFORM constant (#135177) 2025-01-09 12:41:29 +01:00
Steven B.
1550086dd6 Fix stale docstrings in tplink integration (#135183) 2025-01-09 12:37:32 +01:00
beginner2047
8e28b7b49b Add yue language support to Google Translate TTS (#134480) 2025-01-09 12:16:54 +01:00
epenet
4a33b1d936 Set PARALLEL_UPDATES to 0 in onewire (#135178) 2025-01-09 12:15:32 +01:00
epenet
8bfdbc173a Use snapshot_platform helper in onewire tests (#135176)
* Use snapshot_platform helper in onewire tests

* Snapshot device registry
2025-01-09 11:45:29 +01:00
Cyrill Raccaud
3ce4c47cfc Add uuid as unique_id to config entries for Cookidoo (#134831) 2025-01-09 11:28:28 +01:00
Steven B.
0d9ac25257 Add and cleanup tplink translations (#135120) 2025-01-09 11:28:10 +01:00
epenet
15e785b974 Move OneWire PLATFORM constant back to init (#135172) 2025-01-09 11:22:08 +01:00
Antoine Reversat
13527768cc Add outside temperature sensor to fujitsu_fglair (#130717) 2025-01-09 11:21:27 +01:00
Jan-Philipp Benecke
071e675d9d Mark docs-installation-parameters and docs-removal-instructions for inexogy as done (#135126) 2025-01-09 11:21:07 +01:00
epenet
316a61fcde Deprecate raw_value attribute in onewire entity (#135171)
* Drop raw_value attribute in onewire entity

* Deprecate only
2025-01-09 11:20:08 +01:00
Erik Montnemery
9901f3c3dd Add jitter to backup start time to avoid thundering herd (#135065) 2025-01-09 10:53:33 +01:00
J. Nick Koston
c9d8c59b45 Bump zeroconf to 0.138.1 (#135148) 2025-01-09 10:33:24 +01:00
Nikolay Vasilchuk
0184d8e954 Deprecate StarLine engine switch attributes (#133958)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-09 10:24:04 +01:00
Brynley McDonald
2f892678f6 Fix Flick Electric Pricing (#135154) 2025-01-09 10:09:04 +01:00
G Johansson
fe8cae8eb5 Make devices dynamic in Sensibo (#134935) 2025-01-09 09:02:14 +01:00
G Johansson
64752af4c2 Change minimum SQLite version to 3.40.1 (#135042)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-08 16:34:36 -10:00
G Johansson
c5f80dd01d Render select entity unavailable when active feature is missing in Sensibo (#135031) 2025-01-08 22:55:31 +01:00
Quentame
2704090418 Fix Météo-France setup in non French cities (because of failed next rain sensor) (#134782) 2025-01-08 22:51:37 +01:00
Tomer Shemesh
f01c860c44 Add support for Lutron Wood Tilt Blinds (#135057) 2025-01-08 22:40:13 +01:00
Shay Levy
bb4a497247 Impove LG webOS TV tests quality (#135130)
* Impove LG webOS TV tests quality

* Review comments
2025-01-08 23:12:09 +02:00
Joris Pelgröm
488c5a6b9f Use is in FlowResultType enum comparison in integration scaffold tests (#135133) 2025-01-08 22:10:29 +01:00
Louis Christ
acbd501ede Add DataUpdateCoordinator to bluesound integration (#135125) 2025-01-08 22:09:59 +01:00
Shay Levy
d06cd1ad3b Set PARALLEL_UPDATES in LG webOS TV (#135135) 2025-01-08 22:08:13 +01:00
Joris Pelgröm
4129697dd9 Add LetPot integration (#134925) 2025-01-08 21:38:52 +01:00
Ståle Storø Hauknes
4086d092ff Add suggested precision for Airthings BLE integration (#134985)
Add suggested precision
2025-01-08 21:05:42 +01:00
Arie Catsman
988a0639f4 Remove enphase_envoy config flow tests that make no sense (#133833) 2025-01-08 20:09:06 +01:00
Steven B.
c9c553047c Add quality scale file to tplink integration (#135017) 2025-01-08 20:08:04 +01:00
Arie Catsman
f05cffea17 Update enphase_envoy test_init to use str for unique_id and test for loaded config entry (#133810) 2025-01-08 20:06:51 +01:00
Hervé Cauwelier
d2a188ad3c Improve holidays config form and naming (#133663)
The holidays library is improving over time, let's make use of their
data for a more user-friendly experience.

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-08 17:19:28 +01:00
epenet
02e30edc6c Improve onewire options flow tests (#135109) 2025-01-08 17:00:35 +01:00
G Johansson
0e52ea482f Fix hvac_modes never empty in Sensibo (#135029) 2025-01-08 15:27:26 +01:00
epenet
d46be61b6f Split simple and recovery in onewire config-flow user tests (#135102) 2025-01-08 15:25:39 +01:00
epenet
f05e234c30 Refactor patching in onewire tests (#135070) 2025-01-08 15:14:51 +01:00
jb101010-2
bc09e825a9 Bump pysuezV2 to 2.0.3 (#135080) 2025-01-08 15:12:56 +01:00
Steven B.
6f6d485530 Raise HomeAssistantError from tplink light effect service (#135081) 2025-01-08 15:12:21 +01:00
Steven B.
63eb27df7b Add PARALLEL_UPDATES constant to tplink integration platforms (#135083) 2025-01-08 15:11:06 +01:00
elmurato
da29b2f711 Add quality_scale.yaml to Minecraft Server (#132551) 2025-01-08 15:00:33 +01:00
farkasdi
c2f6f93f1d Update addition logger string in fan.py (#135098) 2025-01-08 14:58:50 +01:00
Dawid Pietryga
39143a2e79 Add satel integra switches and alarm control panels unique_id (#129636) 2025-01-08 14:49:43 +01:00
dontinelli
99e65c38b0 Add binary sensors to fyta (#134900)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-08 14:41:33 +01:00
epenet
ec7d2f3731 Add quality_scale file to onewire (#134951) 2025-01-08 14:41:20 +01:00
epenet
d43187327f Remove rounding from onewire sensors (#135095) 2025-01-08 14:25:05 +01:00
Austin Mroczek
8be01ac9d6 TotalConnect improved config flow and test before setup (#133852)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-08 12:37:04 +01:00
Erik Montnemery
e052ab27f2 Fix DSMR migration (#135068) 2025-01-08 11:20:35 +00:00
Shay Levy
43ec63eabc Cleanup LG webOS TV name (#135028) 2025-01-08 12:06:02 +01:00
starkillerOG
7a2a6cf7d8 Add Reolink unexpected error translation (#134807) 2025-01-08 10:58:28 +01:00
puddly
eff440d2a8 Fix ZHA "referencing a non existing via_device" warning (#135008) 2025-01-08 10:51:57 +01:00
Andrew Sayre
3fea4efb9f Update pyheos to 0.9.0 (#134947)
Bump pyheos
2025-01-08 10:36:02 +02:00
Matthias Alphart
dc1928f3eb Delete KNX config storage when removing the integration (#135071) 2025-01-08 09:35:44 +01:00
epenet
f8618e65f6 Improve type hints in onewire tests (#134993) 2025-01-08 09:33:04 +01:00
G Johansson
e99aaed7fa Fix climate react type (#135030) 2025-01-08 10:30:14 +02:00
starkillerOG
d000558227 Fix channel retrieval for Reolink DUO V1 connected to a NVR (#135035)
fix channel retrieval for DUO V1 connected to a NVR
2025-01-08 10:28:01 +02:00
Thomas55555
7daf442271 Bump aioautomower to 2025.1.0 (#135039) 2025-01-08 10:26:48 +02:00
Cyrill Raccaud
b8f458458b Bump cookidoo-api to 0.12.2 (#135045)
fix cookidoo .co.uk countries and group api endpoint
2025-01-08 10:24:09 +02:00
J. Nick Koston
85ecb04abf Bump dbus-fast to 2.28.0 (#135049)
changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v2.24.3...v2.28.0
2025-01-08 10:19:03 +02:00
Joakim Sørensen
20db7fdc96 Implement upload retry logic in CloudBackupAgent (#135062)
* Implement upload retry logic in CloudBackupAgent

* Update backup.py

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

* nit

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-01-08 08:16:18 +01:00
Diogo Gomes
a1d43b9387 Add weather warning sensor to IPMA (#134054)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-01-07 22:11:24 +00:00
dontinelli
de9c05ad53 Add new sensors to fyta (#135032) 2025-01-07 22:01:21 +01:00
Brett Adams
a01521b224 Bump pyaussiebb to 0.1.5 (#134943)
Bump
2025-01-07 21:54:39 +01:00
Norbert Rittel
2413bb4f52 Improve Huawei LTE suspend integration service description (#135021) 2025-01-07 19:30:56 -01:00
Steven B.
1496da8e94 Add data description translations to all tplink config flow steps (#135022) 2025-01-07 20:26:00 +01:00
Mick Vleeshouwer
802ad55493 Catch errors in automation (instead of raise unexpected error) in Overkiz (#135026)
Catch errors in automation (instead of raise unexpected error)
2025-01-07 20:24:39 +01:00
Allen Porter
48da88583f Bump voluptuous openapi to 0.0.6 (#134998) 2025-01-07 19:09:11 +01:00
Mick Vleeshouwer
0ab66a4ed1 Improve logic for event polling duration in Overkiz (#133617) 2025-01-07 19:06:57 +01:00
epenet
3b13c5bfdd Move OneWireConfigEntry type definition (#135004) 2025-01-07 19:04:31 +01:00
Kevin Worrel
42532e9695 Add Controller state sensor to screenlogic (#133827) 2025-01-07 19:02:34 +01:00
Luke Lashley
0dd9845501 Add total cleaning count sensor to Roborock (#135015) 2025-01-07 19:01:04 +01:00
Simone Chemelli
3a213b2d17 Use standard "entity_registry_enabled_by_default" fixture (#134962) 2025-01-07 18:21:26 +01:00
epenet
d155d93462 Set PARALLEL_UPDATES to 1 in onewire (#135006) 2025-01-07 17:19:48 +01:00
Erik Montnemery
5888b83f22 Validate device id when adding or updating entity registry entry (#134982) 2025-01-07 16:10:51 +00:00
epenet
471f77fea4 Add reconfigure to onewire (#134996)
* Add reconfigure to onewire

* Adjust _async_abort_entries_match
2025-01-07 17:08:53 +01:00
Allen Porter
c684b06734 Simplify roborock coordinator (#134700)
* Update roborock coordinator to require maps on startup

* Fix indent in merge
2025-01-07 07:09:32 -08:00
Erik Montnemery
393551d696 Fix DSMR migration (#134990) 2025-01-07 15:42:07 +01:00
Andrew Sayre
24b81df0e6 Update HEOS Quality Scale docs-related items (#134466)
Update docs items
2025-01-07 08:26:42 -06:00
Allen Porter
a66cf62b09 Update roborock tests to only load the platform under test (#134694) 2025-01-07 14:08:12 +01:00
epenet
901099325b Set parallel-updates and scan-interval explicitly in onewire (#134953) 2025-01-07 14:06:19 +01:00
epenet
30695cfef5 Simplify onewire config-flow (#134952) 2025-01-07 13:22:16 +01:00
Joakim Sørensen
5d2a8e8208 Increase cloud backup download timeout (#134961)
Increese download timeout
2025-01-07 13:18:02 +01:00
Norbert Rittel
4019045e7b Use sentence case, capitalize "IP Secure" and "ID" (#134966) 2025-01-07 12:08:37 +01:00
Norbert Rittel
ec2c8da1c5 Change "id" to uppercase for consistency (#134971) 2025-01-07 12:03:44 +01:00
J. Nick Koston
d1e8a2a32d Bump zeroconf to 0.137.2 (#134942) 2025-01-07 10:44:22 +01:00
Kelyan Pegeot Selme
feeee2d15e Bump renault-api to 0.2.9 (#134858)
* chore: Bump Renault api version

* Update requirements_all.txt

* Update requirements_test_all.txt
2025-01-07 10:13:40 +01:00
David Rapan
8a052177a4 Update Shelly integration: Remove double "Error fetching ..." from error messages (#134950)
refactor: Remove double "Error fetching" from error messages
2025-01-07 10:12:10 +02:00
Eli Schleifer
875727ed27 add proxy view for unifiprotect to grab snapshot at specific time (#133546)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-06 13:49:58 -10:00
Raphael Hehl
f1c62000e1 UnifiProtect Refactor light control methods to use new API (#134625) 2025-01-06 13:48:22 -10:00
Norbert Rittel
e38f21c4ef Fix spelling of "ID", slightly reword action descriptions (#134778) 2025-01-07 00:25:42 +01:00
Franck Nijhof
00c052bb22 Revert "Remove deprecated supported features warning in ..." (multiple) (#134933) 2025-01-07 00:08:02 +01:00
Simone Chemelli
111ef13a3f Add device tracker test for Vodafone Station (#134334) 2025-01-06 23:17:50 +01:00
J. Nick Koston
89c73f56b1 Migrate to using aiohttp-asyncmdnsresolver for aiohttp resolver (#134830) 2025-01-06 12:06:28 -10:00
Paulus Schoutsen
d13c14eedb Add support for extra_system_prompt to OpenAI (#134931) 2025-01-06 23:01:13 +01:00
G Johansson
9532e98166 Remove deprecated config entry import from bluesound (#134926) 2025-01-06 22:58:29 +01:00
G Johansson
6884d790ca Remove deprecated hdr switch from reolink (#134924) 2025-01-06 22:46:59 +01:00
G Johansson
6ab45f8c9e Bump holidays to 0.64 (#134922) 2025-01-06 22:45:04 +01:00
Artur Pragacz
7009a96711 Revert "Remove deprecated supported features warning in LightEntity" (#134927) 2025-01-06 22:39:24 +01:00
Josef Zweck
a47fa08a9b Add device trackers to enabled_by_default fixture (#134446) 2025-01-06 22:03:32 +01:00
Norbert Rittel
4eb23f3039 Remove excessive newline code, fix "ID", enhance descriptions (#134920) 2025-01-06 20:54:26 +01:00
Klaas Schoute
1c314b5c02 Bump powerfox to v1.2.0 (#134908) 2025-01-06 20:52:54 +01:00
Tomer Shemesh
edee58f114 Bump pylutron-caseta to 0.23.0 (#134906) 2025-01-06 20:44:06 +01:00
Manu
ef652e57d1 Add bring_api to loggers in Bring integration (#134897)
Add bring-api to loggers
2025-01-06 20:37:01 +01:00
Paulus Schoutsen
b956aa68da Handle discovering user configured Wyoming flow (#134916) 2025-01-06 20:26:49 +01:00
Michael
75ce89dc41 Bump py-synologydsm-api to 2.6.0 (#134914)
bump py-synologydsm-api to 2.6.0
2025-01-06 20:08:58 +01:00
Manu
a9540e893f Fix wrong power limit decimal place in IronOS (#134902) 2025-01-06 19:55:47 +01:00
Bram Kragten
dd5625436b Update frontend to 20250106.0 (#134905) 2025-01-06 19:11:01 +01:00
Paulus Schoutsen
7a484ee0ae Add extra prompt to assist pipeline and conversation (#124743)
* Add extra prompt to assist pipeline and conversation

* extra_prompt -> extra_system_prompt

* Fix rebase

* Fix tests
2025-01-06 12:58:42 -05:00
starkillerOG
e5c5d1bcfd Fix Reolink playback of recodings (#134652) 2025-01-06 18:54:32 +01:00
Glenn Reilly
56a9cd010e fix typo "looses" to "loses" in MQTT configuration message (#134894) 2025-01-06 17:59:31 +01:00
Steven B.
b7b5577f0c Bump python-kasa to 0.9.1 (#134893)
Bump tplink python-kasa dependency to 0.9.1
2025-01-06 16:58:33 +01:00
Norbert Rittel
0787257cc0 Use uppercase for "ID" and sentence-case for "name" / "icon" (#134890) 2025-01-06 16:30:40 +01:00
Thijs W.
54263f1325 Bump pymodbus version to 3.8.3 (#134809) 2025-01-06 14:56:17 +00:00
Luke Lashley
14d2f2c589 Add extra failure exceptions during roborock setup (#134889)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-06 15:46:21 +01:00
starkillerOG
c533f63a87 Add Decorquip virtual motion blinds integration (#134402) 2025-01-06 15:36:38 +01:00
Ludovic BOUÉ
cd30f75be9 Matter Battery replacement icon (#134460) 2025-01-06 15:35:42 +01:00
jb101010-2
527775a5f1 Bump pysuezV2 to 2.0.1 (#134769) 2025-01-06 15:27:23 +01:00
Klaas Schoute
99d7f462a0 Add heat meter to Powerfox integration (#134799) 2025-01-06 15:23:47 +01:00
J. Diego Rodríguez Royo
67e2379d2b Iterate over a copy of the list of programs at Home Connect select setup entry (#134684) 2025-01-06 15:21:02 +01:00
Norbert Rittel
fb0047ead0 Use correct uppercase for "ID" and sentence-case otherwise (#134815) 2025-01-06 15:15:31 +01:00
Norbert Rittel
9764d704bd Fix a few typos or grammar issues in asus_wrt (#134813) 2025-01-06 15:15:08 +01:00
Norbert Rittel
3690d7c2b4 Fix spelling of "set up", change "id" to uppercase (#134888) 2025-01-06 14:12:52 +00:00
Norbert Rittel
204b5989e0 Replace "id" with "ID" for consistency across HA (#134798) 2025-01-06 15:10:29 +01:00
G Johansson
3892f6d8f3 Remove deprecated binary sensor battery charging from technove (#134844) 2025-01-06 15:03:52 +01:00
J. Diego Rodríguez Royo
140ff50eaf Fix how function arguments are passed on actions at Home Connect (#134845) 2025-01-06 15:03:25 +01:00
Avi Miller
5ef06b1f33 Bump aiolifx-themes to update colors (#134846) 2025-01-06 15:02:57 +01:00
Manu
9638bee8de Bump pynecil to v4.0.1 (#134852) 2025-01-06 14:55:50 +01:00
Norbert Rittel
cd88a8cebd Fix missing sentence-casing etc. in several strings (#134775) 2025-01-06 14:53:28 +01:00
G Johansson
d896b4e66a Raise ImportError in python_script (#134792) 2025-01-06 14:52:40 +01:00
Robin Wohlers-Reichel
e4eb414be8 Bump solax to 3.2.3 (#134876) 2025-01-06 14:47:52 +01:00
Joakim Sørensen
fce5be928e Log upload BackupAgentError (#134865)
* Log out BackupAgentError

* Update homeassistant/components/backup/manager.py

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

* Update homeassistant/components/backup/manager.py

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

* Format

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-01-06 14:19:34 +01:00
Joakim Sørensen
c4455c709b Log cloud backup upload response status (#134871)
Log the status of the upload response
2025-01-06 13:10:38 +01:00
Allen Porter
2c7a1446b8 Update Roborock config flow message when an account is already configured (#134854) 2025-01-06 11:24:06 +01:00
G Johansson
20cf21d88e Add horizontal swing to Sensibo (#132117)
* Add horizontal swing to Sensibo

* Fixes

* Only load select if already there

* Remove feature check

* Fixes

* Mods

* Last bits

* Mod

* Fixes

* Mods

* Fix test

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-06 11:20:11 +01:00
G Johansson
eafbf1d1fd Add get device capabilities action call for Sensibo (#134596)
* Add get device capabilities action call for Sensibo

* Tests

* Mod

* Fix services

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-06 11:09:08 +01:00
Michael
acd95975e4 Make ChunkAsyncStreamIterator an aiohttp helper (#134843)
make ChunkAsyncStreamIterator a generic aiohttp helper
2025-01-06 04:37:07 +01:00
G Johansson
bc22e34fc3 Add python_script to strict typing (#134822) 2025-01-05 22:22:54 -05:00
G Johansson
bf0cf1c30f Set single_config_entry in System monitor manifest (#134838) 2025-01-05 22:17:08 -05:00
G Johansson
e95bfe438b Pass config entry directly to coordinator in System monitor (#134837) 2025-01-05 22:16:58 -05:00
Norbert Rittel
0a457979ec Fix spelling of "ID", slightly reword action description (#134817)
This commit fixes the spelling of "ID" (uppercase for abbreviations) and slightly changes the action description to use third-person singular.

The latter ensures proper (machine) translations, keeping a descriptive style.
2025-01-05 18:36:17 -08:00
Allen Porter
2f295efb3f Update roborock to ensure every room has a name, falling back to a placeholder (#134733)
* Update roborock to ensure every room has a name, falling back to a placeholder

* Change Map to Room
2025-01-05 18:28:17 -08:00
J. Nick Koston
74613ae0c4 Bump habluetooth to 3.7.0 (#134833) 2025-01-05 12:44:37 -10:00
Raphael Hehl
4d4cfabfba Bump uiprotect to version 7.4.1 (#134829) 2025-01-05 11:25:44 -10:00
Norbert Rittel
7ae81bae4c Fix spelling of "ID" in Roku integration (#134779)
* Fix spelling of "ID" and "Ethernet" in Roku integration

Small commit replacing "id" with "ID" and "ethernet" with "Ethernet".

* Revert entity change

Web editor does not support the necessary tests.
2025-01-05 20:21:06 +01:00
Norbert Rittel
7ec10bfd6f Use uppercase "ID" in Home Connect strings (#134783) 2025-01-05 20:19:45 +01:00
cdnninja
d662a4465c Remove unneeded vesync device base class (#134499)
* Remove unneeded entity to make code cleaner

* Update light.py

* Update fan.py

* Typing.

* Update homeassistant/components/vesync/common.py

Co-authored-by: Allen Porter <allen.porter@gmail.com>

* Wrap

---------

Co-authored-by: Allen Porter <allen.porter@gmail.com>
2025-01-05 09:18:52 -08:00
Markus Lanthaler
66b4b24612 Add latest Nighthawk WiFi 7 routers to V2 models (#134765)
Click `WiFi Routers` | `Nighthawk WiFi 7 Router` on https://www.netgear.com/support/ to see the list of devices
2025-01-05 16:59:34 +01:00
TheJulianJES
a2077405e2 Bump ZHA to 0.0.45 (#134726) 2025-01-05 16:49:58 +01:00
Lucas Gasenzer
f0a1a6c2ad Add ATTR_MODEL to DeviceInfo for Aranet (#134307)
* add ATTR_MODEL to DeviceInfo

* add tests for device context

* Upstream change type.name --> type.model

* fix test to represent model names
2025-01-05 16:16:12 +02:00
Norbert Rittel
32b7b5aa66 Small fixes in the strings file of the Vera integration (#134780)
Just for consistency …
2025-01-05 15:10:32 +01:00
Norbert Rittel
871a7d0dc1 Use uppercase "Chime" for product name, fix "MAC address" (#134748) 2025-01-05 14:31:02 +01:00
Michael
da807001ab Register base device entry during coordinator setup in AVM Fritz!Tools integration (#134764)
* register base device entry during coordinator setup

* make mypy happy
2025-01-05 08:16:33 -05:00
Norbert Rittel
a104799893 Fix spelling of "MAC (address)" and "Slide" name in slide_local (#134747) 2025-01-05 13:06:48 +01:00
Duco Sebel
45d1624d70 Bumb python-homewizard-energy to 7.0.1 (#134753) 2025-01-05 12:37:06 +01:00
Lucas Gasenzer
1059cf3f07 Bump aranet4 to 2.5.0 (#134752)
update aranet4 to 2.5.0 before pull request
2025-01-05 13:10:08 +02:00
Norbert Rittel
dd34a10934 Fix swapped letter order in "°F" and "°C" temperature units (#134750)
Fixes the wrong order "F°" and "C°" for the temperature units.
2025-01-05 10:43:32 +01:00
Klaas Schoute
d4f3dd2335 Bump powerfox to v1.1.0 (#134730) 2025-01-05 10:10:55 +01:00
Sid
0ecb1ea8cf Bump openwebifpy to 4.3.1 (#134746) 2025-01-05 10:04:59 +01:00
Sid
3d5a42749d Bump ruff to 0.8.6 (#134745) 2025-01-05 09:47:42 +01:00
Rylie Pavlik
a2c2d37eb1 Add support for "Lumin Smart Light" LD-0003 (#133328) 2025-01-04 13:53:16 -10:00
Andrew Sayre
f68c16586d Deprecate HEOS sign_in and sign_out actions (#134616) 2025-01-05 00:13:46 +01:00
Norbert Rittel
11d80065ef Fix spelling of "MAC (address)" and "Gateway" name (#134724) 2025-01-05 00:05:15 +01:00
Norbert Rittel
7012648bf8 Fix typos / grammar in nasweb integration (#134721) 2025-01-04 23:23:26 +01:00
Norbert Rittel
d96b2499e2 Fix typos / grammar in description of create_task action (#134705) 2025-01-04 23:21:23 +01:00
Norbert Rittel
a41bdfe0cc Fix wrong description of group.set action (#134697) 2025-01-04 23:20:30 +01:00
dontinelli
0d3872a4c7 Change from host to ip in zeroconf discovery for slide_local (#134709) 2025-01-04 21:28:47 +01:00
Norbert Rittel
65d8d071dd Remove excessive newline codes from squeezebox strings (#134682) 2025-01-04 18:42:28 +01:00
Allen Porter
bb97a16756 Add prompts to MCP server (#134619)
* Add prompts to MCP server

* Improve test coverage for get prompt error cases
2025-01-04 12:35:05 -05:00
Andrew Sayre
c9a607aa45 Clean-up HEOS entity event setup (#134683)
* Use async_on_remove

* Remove redundant signal clearing
2025-01-04 12:32:19 -05:00
Allen Porter
c7993eff99 Bump gcal_sync to 7.0.0 (#134687) 2025-01-04 12:30:57 -05:00
Cyrill Raccaud
8a880d6134 Cookidoo exotic domains (#134676) 2025-01-04 16:33:42 +01:00
Brynley McDonald
cc0fb80481 Fix Flick Electric authentication (#134611) 2025-01-04 16:21:21 +01:00
epenet
276806d3e1 Fix hive color tunable light (#134628) 2025-01-04 16:19:38 +01:00
Franck Nijhof
0589df7d95 Update demetriek to 1.1.1 (#134663) 2025-01-04 16:19:16 +01:00
Joost Lekkerkerker
aab676a313 Add Overseerr service to get requests (#134229)
* Add service to get requests

* Add service to get requests

* Add service to get requests

* fix

* Add tests
2025-01-04 15:53:15 +01:00
Joost Lekkerkerker
7f473b8260 Prefer a local webhook for Overseerr (#134667) 2025-01-04 15:39:47 +01:00
Shay Levy
fea4a00424 Remove LG WebOS TV legacy uuid migration (#134671) 2025-01-04 15:31:36 +01:00
Cyrill Raccaud
7d146ddae0 Bump cookidoo-api library to 0.11.1 of for Cookidoo (#134661) 2025-01-04 15:02:00 +01:00
Franck Nijhof
8f06e0903f Update peblar to 0.3.3 (#134658) 2025-01-04 14:34:45 +01:00
Maikel Punie
677ba3a6a6 Add velbus cover platform testcases (#134654) 2025-01-04 14:07:25 +01:00
Franck Nijhof
a322deaab8 Update twentemilieu to 2.2.1 (#134651) 2025-01-04 14:05:24 +01:00
Franck Nijhof
584439cade Update guppy to 3.1.5 (#134646) 2025-01-04 13:24:33 +01:00
Joost Lekkerkerker
baa13debcc Remove call to remove slide (#134647) 2025-01-04 12:56:58 +01:00
Cyrill Raccaud
1d42890748 Set logging in manifest for Cookidoo (#134645) 2025-01-04 12:23:22 +01:00
Norbert Rittel
622d23cadd Fix description of device_id field of reconnect_client actions (#134275) 2025-01-04 12:21:25 +01:00
G Johansson
ebeb2ecb09 Replace aioclient_mock in Sensibo tests (#134543) 2025-01-04 12:14:58 +01:00
Norbert Rittel
b3cb2928fc Fix typo 'devide_id', use uppercase for abbreviations ID and LED (#134634) 2025-01-04 12:01:39 +01:00
J. Nick Koston
b639466453 Bump bleak-esphome to 2.0.0 (#134580) 2025-01-04 11:30:41 +01:00
Teemu R.
69241e4ca6 Mention case-sensitivity in tplink credentials prompt (#134606) 2025-01-04 11:12:46 +01:00
Allen Porter
80371a865e Bump ical to 8.3.0 (#134617)
* Bump ical to 8.3.0

* Update snapshots
2025-01-04 09:49:56 +01:00
Maikel Punie
c9dbb205dd Add velbus diagnostics tests (#134621) 2025-01-04 09:10:34 +01:00
Raphael Hehl
197ff932af Bump uiprotect to version 7.2.0 (#134587) 2025-01-04 00:27:06 +01:00
G Johansson
287b7eec13 Clean up docstrings in Sensibo (#134591) 2025-01-04 00:24:51 +01:00
Maikel Punie
e6da6d9612 Add velbus light and sensor platform testcases (#134485)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-03 20:42:01 +01:00
peteS-UK
d4f38099ae Small fix to allow playing of expandable favorites on Squeezebox (#134572) 2025-01-03 20:28:05 +01:00
Manu
9f2cb7bf56 Add image platform to Habitica integration (#129009) 2025-01-03 20:23:43 +01:00
Maikel Punie
8a84abd50f Velbus diagnostics code cleanup (#134553) 2025-01-03 20:15:58 +01:00
Nerdix
b15e08ca9c Add sleep switch for all Foscam cameras if more than 1 camera are configured (#126064) 2025-01-03 20:15:09 +01:00
Norbert Rittel
3fb980901e Improve habitica action descriptions (#134563) 2025-01-03 20:07:30 +01:00
Joost Lekkerkerker
bd3a3fd26c Require at least bronze for new integrations (#134537)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-01-03 18:14:27 +00:00
Andrew Sayre
dfcb977a1d Add HEOS Reauth Flow (#134465) 2025-01-03 18:11:10 +00:00
Abílio Costa
94ad6ae814 Bump whirlpool-sixth-sense to 0.18.11 (#134562) 2025-01-03 17:45:27 +01:00
G Johansson
97aa93f92b Add supported features property in Sensibo (#134479) 2025-01-03 17:30:18 +01:00
G Johansson
ee025198e8 Update quality scale for Sensibo (#134551) 2025-01-03 17:28:02 +01:00
puddly
90265e2afd Move SiLabs firmware probing helper from ZHA into homeassistant_hardware (#131586)
* Move firmware probing helper out of ZHA and into hardware

* Add a unit test
2025-01-03 10:57:39 -05:00
Bram Kragten
a53554dad3 Update frontend to 20250103.0 (#134561) 2025-01-03 16:36:40 +01:00
Joost Lekkerkerker
2b6ad84cf5 Set Ituran to silver (#134538) 2025-01-03 16:31:31 +01:00
Erik Montnemery
92655fd640 Log cloud backup agent file list (#134556) 2025-01-03 16:30:14 +01:00
Maciej Bieniek
e43f72c452 Add support for xvoltage sensor for Shelly Plus UNI (#134261)
* Add support for xvoltage sensor

* Cleaning
2025-01-03 15:27:47 +01:00
Manu
9320ccfa4f Remove deprecated sensors in Habitica integration (#134320)
* Remove deprecated sensors

* remove todos/dailies also from enum
2025-01-03 14:48:26 +01:00
Erik Montnemery
336af8b551 Avoid early COMPLETED event when restoring backup (#134546) 2025-01-03 14:44:24 +01:00
starkillerOG
8a2f8dc736 Add Reolink proxy for playback (#133916) 2025-01-03 14:24:39 +01:00
Erik Montnemery
dc048bfcf5 Simplify error handling when creating backup (#134528) 2025-01-03 14:16:05 +01:00
Norbert Rittel
fb474827b5 Fix description of google_assistant.request_sync action (#134535) 2025-01-03 14:08:54 +01:00
Markus Adrario
eec5fb2133 Add Homee integration to Core (#133738)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-03 13:44:06 +01:00
Erik Montnemery
8ad7c522f4 Add backup as after_dependency of frontend (#134534) 2025-01-03 13:35:56 +01:00
Maikel Punie
c7f6630718 Velbus add init testcases (#134533) 2025-01-03 13:29:01 +01:00
Marc Mueller
afa95293dc Enable strict typing for pandora (#134536) 2025-01-03 13:23:39 +01:00
G Johansson
36582f9ac2 Refactor all Sensibo tests (#134478)
* Add me json

* Mods

* Mods

* More

* Mods

* Mods

* clean

* last bits

* Fix

* unique id

* return_value

* remove blocking

* Fix rebase
2025-01-03 12:44:47 +01:00
Ståle Storø Hauknes
19852ecc24 Add state_class to Airthings integration (#134503)
Add state class
2025-01-03 11:55:24 +01:00
Manu
5726d090b0 Add get_tasks action to Habitica integration (#127687)
Add get_tasks action
2025-01-03 11:53:30 +01:00
Indu Prakash
add401ffcf Add coordinator to vesync (#134087) 2025-01-03 11:33:16 +01:00
Robert Svensson
fd12ae2ccd Handle deCONZ color temp 0 is never used when calculating kelvin CT (#134521) 2025-01-03 10:51:20 +01:00
Franck Nijhof
e15eda3aa2 Only load Peblar customization update entity when present (#134526) 2025-01-03 10:51:05 +01:00
Dan Raper
cc0adcf47f Add switch platform to Ohme (#134347)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-03 10:39:41 +01:00
Franck Nijhof
06580ce10f Update peblar to v0.3.2 (#134524) 2025-01-03 10:37:39 +01:00
Erik Montnemery
b78e39da2d Fix activating backup retention config on startup (#134523) 2025-01-03 10:29:29 +01:00
G Johansson
46824a2a53 Add quality scale to Sensibo (#134296) 2025-01-03 10:23:25 +01:00
Joost Lekkerkerker
ee01289ee8 Bump python-overseerr to 0.5.0 (#134522) 2025-01-03 10:22:59 +01:00
Erik Montnemery
0bd22eabc7 Improve recorder schema migration error test (#134518) 2025-01-03 10:05:07 +01:00
Erik Montnemery
c901352bef Add error prints for recorder fatal errors (#134517) 2025-01-03 10:01:35 +01:00
Joost Lekkerkerker
23ed62c1bc Push Overseerr updates via webhook (#134187) 2025-01-03 08:26:01 +01:00
Paulus Schoutsen
0ef254bc9a Fix backup dir not existing (#134506) 2025-01-03 00:21:19 -05:00
rrooggiieerr
629d108078 Use the latest version of the pyserial-asyncio-fast library (#134501) 2025-01-03 03:15:46 +01:00
Marc Mueller
6f3544fa47 Add types package for pexpect (#134461) 2025-01-03 02:53:08 +01:00
Franck Nijhof
cb389d29ea Fix input_datetime.set_datetime not accepting 0 timestamp value (#134489) 2025-01-02 23:45:00 +01:00
Josef Zweck
ac26ca2da5 Bump aioacaia to 0.1.13 (#134496) 2025-01-03 00:28:29 +02:00
G Johansson
d5bcb73d33 Bump psutil to 6.1.1 (#134494) 2025-01-02 22:45:24 +01:00
Marc Mueller
e6a18357db Update pillow to 11.1.0 (#134469) 2025-01-02 22:36:14 +01:00
G Johansson
13ec0659ff Remove deprecated uptime sensor from qnap_qsw (#134493) 2025-01-02 22:29:50 +01:00
G Johansson
a7fb20ab58 Remove deprecated attributes from ecovacs (#134492) 2025-01-02 22:19:51 +01:00
G Johansson
657da47458 Remove worldclock config entry import (#134491) 2025-01-02 21:45:20 +01:00
Franck Nijhof
a4708876a9 Update peblar to 0.3.1 (#134486) 2025-01-02 21:41:54 +01:00
G Johansson
4239c5b557 Improve error strings in Sensibo (#134487) 2025-01-02 21:19:20 +01:00
G Johansson
836354bb99 Use username as config entry title in Sensibo (#134488) 2025-01-02 21:18:19 +01:00
Norbert Rittel
a7af042e57 Fix a few small typos in peblar (#134481) 2025-01-02 21:17:29 +01:00
Franck Nijhof
09476ade82 Remove sneaked in IronOS submodule (#134477) 2025-01-02 20:22:17 +01:00
Andrea Arcangeli
25937d7868 open_meteo: correct UTC timezone handling in hourly forecast (#129664)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-02 19:37:36 +01:00
Duco Sebel
4e74d14beb Include host in Peblar EV-Charger discovery setup description (#133954)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-01-02 19:34:51 +01:00
SparkyDan555
309b7eb436 Change Reolink person binary sensor icon (#134472) 2025-01-02 19:18:40 +01:00
Erik Montnemery
cf238cd8f7 Don't start recorder if a database from the future is used (#134467) 2025-01-02 18:56:23 +01:00
Robert Resch
ee46edffa3 Bump deebot-client to 10.1.0 (#134470) 2025-01-02 18:54:27 +01:00
Erik Montnemery
876b3423ba Improve hassio backup create and restore parameter checks (#134434) 2025-01-02 17:52:50 +01:00
Norbert Rittel
2752a35e23 Remove excessive newline codes from strings.json (#134468) 2025-01-02 17:43:49 +01:00
Craig Andrews
9e8df72c0d Improve is docker env checks (#132404)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Sander Hoentjen <sander@hoentjen.eu>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-02 17:21:49 +01:00
Bram Kragten
5439613bff Update frontend to 20250102.0 (#134462) 2025-01-02 17:17:57 +01:00
Ілля Піскурьов
3b5455bc49 Add support for specifying hvac_onoff_register value on modbus (#128366)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-01-02 15:18:05 +00:00
Noah Husby
104151d322 Remove deprecated YAML import from MPD (#134459) 2025-01-02 16:08:33 +01:00
Martin Hjelmare
a329828bdf Handle backup errors more consistently (#133522)
* Add backup manager and read writer errors

* Clean up not needed default argument

* Clean up todo comment

* Trap agent bugs during upload

* Always release stream

* Clean up leftover

* Update test for backup with automatic settings

* Fix use of vol.Any

* Refactor test helper

* Only update successful timestamp if completed event is sent

* Always delete surplus copies

* Fix after rebase

* Fix after rebase

* Revert "Fix use of vol.Any"

This reverts commit 28fd7a544899bb6ed05f771e9e608bc5b41d2b5e.

* Inherit BackupReaderWriterError in IncorrectPasswordError

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-01-02 15:45:46 +01:00
Marc Mueller
aa9e721e8b Update pexpect to 4.9.0 (#134450) 2025-01-02 15:36:44 +01:00
Josef Zweck
1b49f88be9 Bump aioacaia to 0.1.12 (#134454) 2025-01-02 15:33:22 +01:00
Marc Mueller
c345f2d548 Improve pandora media_player typing (#134447) 2025-01-02 13:55:59 +01:00
Manu
1d731875ae Remove deprecated yaml import from pyLoad integration (#134200) 2025-01-02 13:29:55 +01:00
Erik Montnemery
0c3489c1b3 Adjust language in backup integration (#134440)
* Adjust language in backup integration

* Update tests
2025-01-02 13:29:46 +01:00
Marc Mueller
c5865c6d18 Add types package for pyserial (#134444) 2025-01-02 13:21:20 +01:00
Norbert Rittel
e1a0fb2f1a Improve action descriptions with some more detail from the docs (#134120)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-02 12:52:51 +01:00
Erik Montnemery
d725cdae13 Initialize AppleTVConfigFlow.identifiers (#134443) 2025-01-02 12:49:03 +01:00
Erik Montnemery
e1bd82ea32 Export IncorrectPasswordError from backup integration (#134436) 2025-01-02 12:40:10 +01:00
Thomas55555
4bcc551b61 Add sw_version to apsystems (#134441) 2025-01-02 12:28:48 +01:00
Marc Mueller
08019e76d8 Update types packages (#134433) 2025-01-02 12:00:29 +01:00
Sven Naumann
0b32342bf0 Add mode selector to Twinkly (#134041) 2025-01-02 10:54:29 +00:00
Krzysztof Dąbrowski
add4e1a708 Add state attributes translations to GIOS (#134390) 2025-01-02 11:38:12 +01:00
Stefan Agner
fb3105bdc0 Improve Supervisor backup error handling (#134346)
* Raise Home Assistant error in case backup restore fails

This change raises a Home Assistant error in case the backup restore
fails. The Supervisor is checking some common issues before starting
the actual restore in background. This early checks raise an exception
(represented by a HTTP 400 error). This change catches such errors and
raises a Home Assistant error with the message from the Supervisor
exception.

* Add test coverage
2025-01-02 11:37:25 +01:00
Norbert Rittel
3845acd0ce Improve names and descriptions in neato.custom_cleaning action (#134399) 2025-01-02 11:04:23 +01:00
Manu
b45c68554c Remove habitipy references in Habitica integration (#134419) 2025-01-02 10:47:40 +01:00
G Johansson
8a45aa4c42 Add translations to all Sensibo errors (#134422)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-01-02 10:46:55 +01:00
G Johansson
51ccba12af Add action translations to Sensibo (#134420) 2025-01-02 10:45:20 +01:00
John Barreiros
c8699dc066 Add current_humidity state attribute to Google Nest climate entity (#134426) 2025-01-02 10:44:15 +01:00
ashionky
87454babfa Add debug log and Optimize code (#134328)
* debug log

* add sw_version hw_version

* log
2025-01-02 09:10:01 +01:00
Andrew Sayre
c9ff575628 Add HEOS options flow for optional authentication (#134105)
* Add heos options flow

* Add options flow tests

* Test error condition during options sign out

* Use credentials when setting up

* Update warning instructions

* Simplify exception logic

* Cover unknown command error condition

* Add test for options

* Correct const import location

* Review feedback

* Update per feedback

* Parameterize tests and remaining feedback

* Correct log level in init

* nitpick feedback
2025-01-02 09:07:34 +01:00
G Johansson
877d16273b Fix SQL sensor name (#134414) 2025-01-02 08:51:49 +01:00
Marc Mueller
dc5bfba902 Update mypy-dev to 1.15.0a1 (#134416) 2025-01-02 08:45:05 +01:00
TheJulianJES
5e7a405f34 Bump ZHA to 0.0.44 (#134427) 2025-01-02 08:43:38 +01:00
Matthew FitzGerald-Chamberlain
5228f3d85c Improve support for Aprilaire S86WMUPR (#133974) 2025-01-02 08:39:57 +01:00
G Johansson
2efc75fdf5 Add base entity to Mill (#134415) 2025-01-02 07:31:54 +01:00
Michael Hansen
a435fd12f0 Bump intents to 2025.1.1 (#134424) 2025-01-01 21:03:17 -05:00
Allen Porter
a5d0c3528c Add the Model Context Protocol Server integration (#134122)
* Add the Model Context Protocol Server integration

* Remove unusued code in init

* Fix comment wording

* Use util.uild for unique ids

* Set config entry title to the LLM API name

* Extract an SSE parser and update comments

* Update comments and defend against already closed sessions

* Shorten description

* Update homeassistant/components/mcp_server/__init__.py

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

* Change integration type to service

---------

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2025-01-01 19:38:33 -05:00
Daniel Hjelseth Høyer
5e981d00a4 Add mill number platform (#134044)
* Mill number, max heating power

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill number, max heating power

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill number, max heating power

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill number, max heating power

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill number, max heating power

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* type

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

---------

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2025-01-01 23:25:42 +01:00
G Johansson
97dc72a6e2 Move available property to base entity in Sensibo (#134410)
* Move available property to base entity in Sensibo

* Fix test
2025-01-01 23:02:06 +01:00
Maikel Punie
088b097a03 Velbus select platform testcases (#134394) 2025-01-01 17:39:39 +01:00
Jan Bouwhuis
85c94e6403 Calculate number of discovery topics correctly (#134393) 2025-01-01 16:55:41 +01:00
Maikel Punie
a2ef1604af Add Velbus climate platform tests (#134387) 2025-01-01 16:01:02 +01:00
Sven Naumann
55dc4b0d2c Implement base entity class for Twinkly (#134382)
* implement base entity class for twinkly

* Update homeassistant/components/twinkly/entity.py

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

* super init

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-01 13:49:13 +01:00
Adam Štrauch
18e8a3b185 Add new ID LAP-V201S-AEUR for Vital200S AirPurifier in Vesync integration (#133999) 2025-01-01 13:10:40 +01:00
cdnninja
3a68a0a67f Vesync unload error when not all platforms used (#134166) 2025-01-01 13:03:39 +01:00
Josef Zweck
7ab2d2e07a Cleanup lamarzocco tests (#134383) 2025-01-01 13:00:14 +01:00
Keith
809629c0e2 Add integration for igloohome devices (#130657)
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-01-01 12:55:04 +01:00
G Johansson
2be578a33f Add diagnostics to Trafikverket Weatherstation (#134314) 2025-01-01 12:32:35 +01:00
Maikel Punie
5cff79ce50 Add velbus switch platform testcases (#134207) 2025-01-01 12:11:27 +01:00
Brett Adams
513c8487c5 Check vehicle metadata (#134381) 2025-01-01 12:09:15 +01:00
Kenny Root
031de8da51 Bump zabbix-utils to 2.0.2 (#134373) 2025-01-01 11:42:16 +01:00
G Johansson
2e1463b9e9 Add placeholder url to Sensibo api description (#134342) 2024-12-31 20:09:49 -06:00
Norbert Rittel
9a58440296 Use "restore from" in field descriptions of restore_partial action (#134285) 2024-12-31 23:29:15 +01:00
Joost Lekkerkerker
26e0fcdb08 Improve Mealie set mealplan service (#130606)
* Improve Mealie set mealplan service

* Fix

* Fix
2024-12-31 17:06:42 -05:00
Bram Kragten
e835e41d59 Update frontend to 20241231.0 (#134363) 2024-12-31 17:04:28 -05:00
Niels Mündler
c53c0a13be Bump pysynthru version to 0.8.0 (#134294) 2024-12-31 23:03:35 +01:00
Jan Bouwhuis
8098122dfe Ensure an entity platform is added in mqtt tests (#134331) 2024-12-31 23:01:55 +01:00
starkillerOG
1d6ecbd1d5 Change Reolink test switch entity ID (#134339) 2024-12-31 22:57:43 +01:00
Dan Raper
c8276ec325 Bump ohmepy to 1.2.3 (#134348) 2024-12-31 22:54:20 +01:00
Josef Zweck
ddfad614ab Bump pylamarzocco to 1.4.6 (#134367) 2024-12-31 22:49:29 +01:00
Norbert Rittel
8eb21749b5 Remove leftover newline codes and periods from strings.json (#134354) 2024-12-31 22:39:07 +01:00
Josef Zweck
a6ba25d3d4 Use text selectors for lamarzocco config flow (#134368) 2024-12-31 22:38:31 +01:00
starkillerOG
1e70a0060b Add Reolink baby crying binary sensor (#134290)
* Add baby crying detection

* Bump reolink-aio to 0.11.6
2024-12-31 22:27:01 +01:00
Noah Husby
6c47f03d17 Bump aiorussound to 4.4.0 (#134366) 2024-12-31 22:21:14 +01:00
Markus Jacobsen
2054988790 Add Bang & Olufsen button Event entities (#127550)
* Add button events

* Remove unused common keys
Rename Preset to Favourite

* Add event testing

* Add check for Beoconnect Core

* Rename device controls

* Add test for Beoconnect core event entity creation

* Fix config entry type

* Add a type checking check before assertion

* Add icon translations

* Remove useless defined icons

* Remove base event class

* Update homeassistant/components/bang_olufsen/event.py

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

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2024-12-31 21:55:24 +01:00
tronikos
f1ad3040b8 Allow automations to pass any conversation_id for Google Generative AI (#134251) 2024-12-31 15:52:29 -05:00
Michael Hansen
53ca31c112 Bump hassil to 2.1.0 (#134359) 2024-12-31 15:52:15 -05:00
Michael Hansen
23459a0355 Revert speech seconds to 0.3 (#134360) 2024-12-31 20:04:41 +01:00
Simone Chemelli
a8bfe285bf Bump aioshelly to 12.2.0 (#134352) 2024-12-31 17:16:12 +01:00
Noah Husby
0888d1a169 Bump aiorussound to 4.3.0 (#134242)
* Bump aiorussound to 4.3.0

* Force CI
2024-12-31 16:14:24 +01:00
Dave T
8b20272272 Refactor and simplify config flow in generic camera (#134330)
Refactor and simplify config flow
2024-12-31 07:05:50 -08:00
Bram Kragten
06b33e5589 Set backup manager state to completed when restore is finished (#134283) 2024-12-31 15:01:06 +01:00
Brynley McDonald
9348569f90 Update Flick Electric API (#133475) 2024-12-31 14:28:24 +01:00
starkillerOG
4a9d545ffe Bump reolink-aio to 0.11.6 (#134286) 2024-12-31 10:31:40 +01:00
Simone Chemelli
277ee03145 Full test coverage for Vodafone Station sensor platform (#133285)
Co-authored-by: Joostlek <joostlek@outlook.com>
2024-12-31 09:55:54 +01:00
Norbert Rittel
6c9c17f129 Update description of the script toggle action (#134093) 2024-12-31 07:07:52 +00:00
Dave T
bf59241dab Add stream preview to options flow in generic camera (#133927)
* Add stream preview to options flow

* Increase test coverage

* Code review: use correct flow handler type in cast

* Restore test coverage to 100%

* Remove error and test that can't be triggered yet
2024-12-30 15:46:42 -08:00
Indu Prakash
57b7635b70 Bump pyvesync to 2.1.15 (#134156)
Bumped pyvesync to 2.1.15
2024-12-30 23:33:41 +01:00
G Johansson
4b96266647 Set parallel updates in Trafikverket Train (#134302) 2024-12-30 23:18:35 +01:00
G Johansson
6266a4153d Explicitly set config entry in Trafikverket Train coordinator (#134304) 2024-12-30 23:18:22 +01:00
G Johansson
a9949a0aab Use typed config entry everywhere in Trafikverket Train (#134303) 2024-12-30 23:17:21 +01:00
G Johansson
428a74fa48 Explicitly set config entry in Trafikverket Ferry coordinator (#134305) 2024-12-30 23:17:04 +01:00
G Johansson
9f1023b195 Explicitly set config entry in Trafikverket Weatherstation coordinator (#134310) 2024-12-30 23:16:41 +01:00
G Johansson
256fc54aa1 Set parallel updates in Trafiverket Weatherstation (#134309) 2024-12-30 23:16:16 +01:00
G Johansson
94c1b9a434 Use typed config entry everywhere in Trafikverket Weatherstation (#134308) 2024-12-30 23:15:54 +01:00
G Johansson
275c15e2ae Set parallel updates in Trafikverket Ferry (#134301) 2024-12-30 22:50:47 +01:00
G Johansson
9cdbcd93cd Use typed config entry everywhere in Trafikverket Ferry (#134300) 2024-12-30 22:48:33 +01:00
G Johansson
f2e856b8a2 Use typed config entry in Trafikverket Camera (#134299) 2024-12-30 22:48:22 +01:00
G Johansson
820f04e1e1 Add parallel updates to camera platform in Trafikverket Camera (#134298) 2024-12-30 22:48:12 +01:00
Noah Husby
b7541f098c Add discovery to Russound RIO (#134245) 2024-12-30 22:46:08 +01:00
Norbert Rittel
a345e80368 Replace unnecessary abbreviations in set_room_temperature action (#134278) 2024-12-30 21:28:38 +01:00
Norbert Rittel
7a3d9a9345 Replace "service" with "action" (#134279) 2024-12-30 21:26:53 +01:00
Dan Raper
a0fb6df5ba Add battery sensor to ohme (#134222)
* Add battery sensor to ohme

* Forgot the snapshots!

* Add translation key to battery

* Change car to vehicle and fix snapshot tests

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

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

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

* Item `ColorTemperature` renamed to `ColorTemperatureMireds`

* Update ColorControl bitmaps and attributes

* Bump Python Matter server to 7.0.0 (Matter 1.4)

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

* Bump pylamarzocco to 1.4.5

---------

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

* Add tests

* Test reauth starts

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

* tests, invalid_credentials string

* test only api_key

* section consts

* test config entry

* test reauth is triggered

* set reauthentication-flow to done

* use consts in tests

* reauth_entry

* changes

* fix import

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

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

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

* Add habiticalib to init and coordinator

* Migrate Habitica config flow to habiticalib

* migrate init to habiticalib

* migrate buttons to habiticalib

* migrate switch to habiticalib

* update habiticalib

* cast_skill action

* migrate update_score

* migrate transformation items action

* migrate quest actions

* fix fixture errors

* Migrate coordinator data and content

* bump habiticalib

* Remove habitipy and use wrapper in habiticalub

* changes

* some fixes

* minor refactoring

* class_needed annotation

* Update diagnostics

* do integration setup in coordinator setup

* small changes

* raise HomeAssistantError for TooManyRequestsError

* fix docstring

* update tests

* changes to tests/snapshots

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

* Add tests for entry setup

* Use the same fixtures for setup and config flow

* Update tests/components/elevenlabs/test_setup.py

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

---------

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

* Add tests

* load platform

* refactor

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

* Add tests

* prevent switch bouncing

* some changes

* icons

* update tests

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

* Mods

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

Revert invalid done rules and mark inapplicable ones as exempted.

* Mark entity disabled as todo

* Mark devices as todo

* missing push

* Update homeassistant/components/suez_water/quality_scale.yaml

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

* Update quality_scale.yaml

* Update quality_scale.yaml again

---------

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

* Review fixes

* No more afraid check :)

* review again

* Apply suggestions from code review

---------

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

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

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

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

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

* Add Overseerr integration

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix

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

* Remove exception that is never thrown

* Update exceptions thrown in generic tests

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

* Mods

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

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

* Update strings.json

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

* Trigger Build

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

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

* fix line field

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

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

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

* Rename vacuum services for clarity and consistency

* Apply suggestions from code review

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

* Add integration field to vacuum service targets for Roborock

---------

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

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

* set clear button to disabled per default

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

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

* make sure ninja is up to date

* Revert "make sure ninja is up to date"

This reverts commit a26dd8b768.

* test

* Revert "test"

This reverts commit 972f40e3ee.

* try a single build

* try a single build

* Revert "Revert "test""

This reverts commit ec282ce021.

* Revert "Revert "Revert "test"""

This reverts commit 315599cbae.

* Revert "try a single build"

This reverts commit 63529dd2c5.

* Revert "try a single build"

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

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

* sort

* Sort them all

---------

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

partially fixes #133904

* backup.backup as well
2024-12-23 22:19:28 +01:00
Abílio Costa
253098d79c Mark missing IQS requirements for Idasen Desk as done (#133910) 2024-12-23 21:38:27 +01:00
Abílio Costa
53ebf84339 Add cronsim to default dependencies (#133913) 2024-12-23 21:34:36 +01:00
J. Nick Koston
7cfbc3eeae Fix duplicate call to async_register_preload_platform (#133909) 2024-12-23 09:20:44 -10:00
Franck Nijhof
8d32531bc1 Bump version to 2025.2.0dev0 (#133893) 2024-12-23 17:54:32 +01:00
Simon
30d95f37d8 Add removal instructions to ElevenLabs (#133895) 2024-12-23 18:37:19 +02:00
2049 changed files with 83601 additions and 29414 deletions

View File

@@ -62,7 +62,7 @@
"json.schemas": [
{
"fileMatch": ["homeassistant/components/*/manifest.json"],
"url": "./script/json_schemas/manifest_schema.json"
"url": "${containerWorkspaceFolder}/script/json_schemas/manifest_schema.json"
}
]
}

11
.gitattributes vendored
View File

@@ -11,3 +11,14 @@
*.pcm binary
Dockerfile.dev linguist-language=Dockerfile
# Generated files
CODEOWNERS linguist-generated=true
Dockerfile linguist-generated=true
homeassistant/generated/*.py linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
requirements_all.txt linguist-generated=true
requirements_test_all.txt linguist-generated=true
requirements_test_pre_commit.txt linguist-generated=true
script/hassfest/docker/Dockerfile linguist-generated=true

View File

@@ -69,7 +69,7 @@ jobs:
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
- name: Upload translations
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: translations
path: translations.tar.gz
@@ -509,7 +509,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -522,7 +522,7 @@ jobs:
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile

View File

@@ -40,9 +40,9 @@ env:
CACHE_VERSION: 11
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 9
HA_SHORT_VERSION: "2025.1"
DEFAULT_PYTHON: "3.12"
ALL_PYTHON_VERSIONS: "['3.12', '3.13']"
HA_SHORT_VERSION: "2025.2"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['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
@@ -537,7 +537,7 @@ jobs:
python --version
uv pip freeze >> pip_freeze.txt
- name: Upload pip_freeze artifact
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pip-freeze-${{ matrix.python-version }}
path: pip_freeze.txt
@@ -661,7 +661,7 @@ jobs:
. venv/bin/activate
python -m script.licenses extract --output-file=licenses-${{ matrix.python-version }}.json
- name: Upload licenses
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
path: licenses-${{ matrix.python-version }}.json
@@ -877,7 +877,7 @@ jobs:
. venv/bin/activate
python -m script.split_tests ${{ needs.info.outputs.test_group_count }} tests
- name: Upload pytest_buckets
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest_buckets
path: pytest_buckets.txt
@@ -979,14 +979,14 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1106,7 +1106,7 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${mariadb}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1114,7 +1114,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1236,7 +1236,7 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${postgresql}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1244,7 +1244,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1378,14 +1378,14 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml

View File

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

View File

@@ -10,7 +10,7 @@ on:
- "**strings.json"
env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.13"
jobs:
upload:

View File

@@ -17,7 +17,7 @@ on:
- "script/gen_requirements_all.py"
env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.13"
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name}}
@@ -76,18 +76,37 @@ jobs:
# Use C-Extension for SQLAlchemy
echo "REQUIRE_SQLALCHEMY_CEXT=1"
# Add additional pip wheel build constraints
echo "PIP_CONSTRAINT=build_constraints.txt"
) > .env_file
- name: Write pip wheel build constraints
run: |
(
# ninja 1.11.1.2 + 1.11.1.3 seem to be broken on at least armhf
# this caused the numpy builds to fail
# https://github.com/scikit-build/ninja-python-distributions/issues/274
echo "ninja==1.11.1.1"
) > build_constraints.txt
- name: Upload env_file
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: env_file
path: ./.env_file
include-hidden-files: true
overwrite: true
- name: Upload build_constraints
uses: actions/upload-artifact@v4.6.0
with:
name: build_constraints
path: ./build_constraints.txt
overwrite: true
- name: Upload requirements_diff
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: requirements_diff
path: ./requirements_diff.txt
@@ -99,7 +118,7 @@ jobs:
python -m script.gen_requirements_all ci
- name: Upload requirements_all_wheels
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: requirements_all_wheels
path: ./requirements_all_wheels_*.txt
@@ -123,6 +142,11 @@ jobs:
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.1.8
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.1.8
with:
@@ -142,7 +166,7 @@ jobs:
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-dev"
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-ng-dev"
skip-binary: aiohttp;multidict;propcache;yarl;SQLAlchemy
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
@@ -167,6 +191,11 @@ jobs:
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.1.8
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.1.8
with:
@@ -205,7 +234,7 @@ jobs:
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;zlib-dev"
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-ng-dev"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
@@ -219,7 +248,7 @@ jobs:
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;zlib-dev"
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-ng-dev"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
@@ -233,7 +262,7 @@ jobs:
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;zlib-dev"
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-ng-dev"
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.3
rev: v0.9.1
hooks:
- id: ruff
args:

View File

@@ -224,6 +224,7 @@ homeassistant.components.gpsd.*
homeassistant.components.greeneye_monitor.*
homeassistant.components.group.*
homeassistant.components.guardian.*
homeassistant.components.habitica.*
homeassistant.components.hardkernel.*
homeassistant.components.hardware.*
homeassistant.components.here_travel_time.*
@@ -291,6 +292,7 @@ homeassistant.components.lcn.*
homeassistant.components.ld2410_ble.*
homeassistant.components.led_ble.*
homeassistant.components.lektrico.*
homeassistant.components.letpot.*
homeassistant.components.lidarr.*
homeassistant.components.lifx.*
homeassistant.components.light.*
@@ -311,6 +313,7 @@ homeassistant.components.manual.*
homeassistant.components.mastodon.*
homeassistant.components.matrix.*
homeassistant.components.matter.*
homeassistant.components.mcp_server.*
homeassistant.components.mealie.*
homeassistant.components.media_extractor.*
homeassistant.components.media_player.*
@@ -362,11 +365,14 @@ homeassistant.components.openuv.*
homeassistant.components.oralb.*
homeassistant.components.otbr.*
homeassistant.components.overkiz.*
homeassistant.components.overseerr.*
homeassistant.components.p1_monitor.*
homeassistant.components.pandora.*
homeassistant.components.panel_custom.*
homeassistant.components.peblar.*
homeassistant.components.peco.*
homeassistant.components.persistent_notification.*
homeassistant.components.person.*
homeassistant.components.pi_hole.*
homeassistant.components.ping.*
homeassistant.components.plugwise.*
@@ -380,6 +386,8 @@ homeassistant.components.pure_energie.*
homeassistant.components.purpleair.*
homeassistant.components.pushbullet.*
homeassistant.components.pvoutput.*
homeassistant.components.python_script.*
homeassistant.components.qbus.*
homeassistant.components.qnap_qsw.*
homeassistant.components.rabbitair.*
homeassistant.components.radarr.*

View File

@@ -1,5 +1,5 @@
{
// Please keep this file in sync with settings in home-assistant/.devcontainer/devcontainer.json
// Please keep this file (mostly!) in sync with settings in home-assistant/.devcontainer/devcontainer.json
// Added --no-cov to work around TypeError: message must be set
// https://github.com/microsoft/vscode-python/issues/14067
"python.testing.pytestArgs": ["--no-cov"],
@@ -12,6 +12,7 @@
"fileMatch": [
"homeassistant/components/*/manifest.json"
],
// This value differs between working with devcontainer and locally, therefor this value should NOT be in sync!
"url": "./script/json_schemas/manifest_schema.json"
}
]

46
CODEOWNERS generated
View File

@@ -637,6 +637,8 @@ build.json @home-assistant/supervisor
/tests/components/homeassistant_sky_connect/ @home-assistant/core
/homeassistant/components/homeassistant_yellow/ @home-assistant/core
/tests/components/homeassistant_yellow/ @home-assistant/core
/homeassistant/components/homee/ @Taraman17
/tests/components/homee/ @Taraman17
/homeassistant/components/homekit/ @bdraco
/tests/components/homekit/ @bdraco
/homeassistant/components/homekit_controller/ @Jc2k @bdraco
@@ -686,6 +688,8 @@ build.json @home-assistant/supervisor
/tests/components/icloud/ @Quentame @nzapponi
/homeassistant/components/idasen_desk/ @abmantis
/tests/components/idasen_desk/ @abmantis
/homeassistant/components/igloohome/ @keithle888
/tests/components/igloohome/ @keithle888
/homeassistant/components/ign_sismologia/ @exxamalte
/tests/components/ign_sismologia/ @exxamalte
/homeassistant/components/image/ @home-assistant/core
@@ -827,6 +831,8 @@ build.json @home-assistant/supervisor
/tests/components/led_ble/ @bdraco
/homeassistant/components/lektrico/ @lektrico
/tests/components/lektrico/ @lektrico
/homeassistant/components/letpot/ @jpelgrom
/tests/components/letpot/ @jpelgrom
/homeassistant/components/lg_netcast/ @Drafteed @splinter98
/tests/components/lg_netcast/ @Drafteed @splinter98
/homeassistant/components/lg_thinq/ @LG-ThinQ-Integration
@@ -887,6 +893,8 @@ build.json @home-assistant/supervisor
/tests/components/matrix/ @PaarthShah
/homeassistant/components/matter/ @home-assistant/matter
/tests/components/matter/ @home-assistant/matter
/homeassistant/components/mcp_server/ @allenporter
/tests/components/mcp_server/ @allenporter
/homeassistant/components/mealie/ @joostlek @andrew-codechimp
/tests/components/mealie/ @joostlek @andrew-codechimp
/homeassistant/components/meater/ @Sotolotl @emontnemery
@@ -1016,7 +1024,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/nina/ @DeerMaximum
/tests/components/nina/ @DeerMaximum
/homeassistant/components/nissan_leaf/ @filcole
/homeassistant/components/nmbs/ @thibmaek
/homeassistant/components/noaa_tides/ @jdelaney72
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
/tests/components/nobo_hub/ @echoromeo @oyvindwe
@@ -1068,8 +1075,8 @@ build.json @home-assistant/supervisor
/tests/components/onewire/ @garbled1 @epenet
/homeassistant/components/onkyo/ @arturpragacz @eclair4151
/tests/components/onkyo/ @arturpragacz @eclair4151
/homeassistant/components/onvif/ @hunterjm
/tests/components/onvif/ @hunterjm
/homeassistant/components/onvif/ @hunterjm @jterrace
/tests/components/onvif/ @hunterjm @jterrace
/homeassistant/components/open_meteo/ @frenck
/tests/components/open_meteo/ @frenck
/homeassistant/components/openai_conversation/ @balloob
@@ -1103,8 +1110,10 @@ build.json @home-assistant/supervisor
/tests/components/otbr/ @home-assistant/core
/homeassistant/components/ourgroceries/ @OnFreund
/tests/components/ourgroceries/ @OnFreund
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117 @alexfp14
/tests/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117 @alexfp14
/homeassistant/components/overkiz/ @imicknl
/tests/components/overkiz/ @imicknl
/homeassistant/components/overseerr/ @joostlek
/tests/components/overseerr/ @joostlek
/homeassistant/components/ovo_energy/ @timmo001
/tests/components/ovo_energy/ @timmo001
/homeassistant/components/p1_monitor/ @klaasnicolaas
@@ -1135,8 +1144,8 @@ build.json @home-assistant/supervisor
/tests/components/plaato/ @JohNan
/homeassistant/components/plex/ @jjlawren
/tests/components/plex/ @jjlawren
/homeassistant/components/plugwise/ @CoMPaTech @bouwew @frenck
/tests/components/plugwise/ @CoMPaTech @bouwew @frenck
/homeassistant/components/plugwise/ @CoMPaTech @bouwew
/tests/components/plugwise/ @CoMPaTech @bouwew
/homeassistant/components/plum_lightpad/ @ColinHarrington @prystupa
/tests/components/plum_lightpad/ @ColinHarrington @prystupa
/homeassistant/components/point/ @fredrike
@@ -1182,6 +1191,8 @@ build.json @home-assistant/supervisor
/tests/components/pyload/ @tr4nt0r
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
/tests/components/qbittorrent/ @geoffreylagaisse @finder39
/homeassistant/components/qbus/ @Qbus-iot @thomasddn
/tests/components/qbus/ @Qbus-iot @thomasddn
/homeassistant/components/qingping/ @bdraco
/tests/components/qingping/ @bdraco
/homeassistant/components/qld_bushfire/ @exxamalte
@@ -1278,6 +1289,7 @@ build.json @home-assistant/supervisor
/tests/components/ruckus_unleashed/ @lanrat @ms264556 @gabe565
/homeassistant/components/russound_rio/ @noahhusby
/tests/components/russound_rio/ @noahhusby
/homeassistant/components/russound_rnet/ @noahhusby
/homeassistant/components/ruuvi_gateway/ @akx
/tests/components/ruuvi_gateway/ @akx
/homeassistant/components/ruuvitag_ble/ @akx
@@ -1371,8 +1383,8 @@ build.json @home-assistant/supervisor
/tests/components/slide_local/ @dontinelli
/homeassistant/components/slimproto/ @marcelveldt
/tests/components/slimproto/ @marcelveldt
/homeassistant/components/sma/ @kellerza @rklomp
/tests/components/sma/ @kellerza @rklomp
/homeassistant/components/sma/ @kellerza @rklomp @erwindouna
/tests/components/sma/ @kellerza @rklomp @erwindouna
/homeassistant/components/smappee/ @bsmappee
/tests/components/smappee/ @bsmappee
/homeassistant/components/smart_meter_texas/ @grahamwetzler
@@ -1478,8 +1490,8 @@ build.json @home-assistant/supervisor
/tests/components/system_bridge/ @timmo001
/homeassistant/components/systemmonitor/ @gjohansson-ST
/tests/components/systemmonitor/ @gjohansson-ST
/homeassistant/components/tado/ @chiefdragon @erwindouna
/tests/components/tado/ @chiefdragon @erwindouna
/homeassistant/components/tado/ @erwindouna
/tests/components/tado/ @erwindouna
/homeassistant/components/tag/ @balloob @dmulcahey
/tests/components/tag/ @balloob @dmulcahey
/homeassistant/components/tailscale/ @frenck
@@ -1573,8 +1585,8 @@ build.json @home-assistant/supervisor
/tests/components/triggercmd/ @rvmey
/homeassistant/components/tts/ @home-assistant/core
/tests/components/tts/ @home-assistant/core
/homeassistant/components/tuya/ @Tuya @zlinoliver @frenck
/tests/components/tuya/ @Tuya @zlinoliver @frenck
/homeassistant/components/tuya/ @Tuya @zlinoliver
/tests/components/tuya/ @Tuya @zlinoliver
/homeassistant/components/twentemilieu/ @frenck
/tests/components/twentemilieu/ @frenck
/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 @Olen
@@ -1618,15 +1630,15 @@ build.json @home-assistant/supervisor
/tests/components/valve/ @home-assistant/core
/homeassistant/components/velbus/ @Cereal2nd @brefra
/tests/components/velbus/ @Cereal2nd @brefra
/homeassistant/components/velux/ @Julius2342 @DeerMaximum
/tests/components/velux/ @Julius2342 @DeerMaximum
/homeassistant/components/velux/ @Julius2342 @DeerMaximum @pawlizio
/tests/components/velux/ @Julius2342 @DeerMaximum @pawlizio
/homeassistant/components/venstar/ @garbled1 @jhollowe
/tests/components/venstar/ @garbled1 @jhollowe
/homeassistant/components/versasense/ @imstevenxyz
/homeassistant/components/version/ @ludeeus
/tests/components/version/ @ludeeus
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak
/homeassistant/components/vicare/ @CFenner
/tests/components/vicare/ @CFenner
/homeassistant/components/vilfo/ @ManneW

4
Dockerfile generated
View File

@@ -13,7 +13,7 @@ ENV \
ARG QEMU_CPU
# Install uv
RUN pip3 install uv==0.5.8
RUN pip3 install uv==0.5.18
WORKDIR /usr/src
@@ -55,7 +55,7 @@ RUN \
"armv7") go2rtc_suffix='arm' ;; \
*) go2rtc_suffix=${BUILD_ARCH} ;; \
esac \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.7/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.8/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& chmod +x /bin/go2rtc \
# Verify go2rtc can be executed
&& go2rtc --version

View File

@@ -308,7 +308,7 @@ class AuthStore:
credentials.data = data
self._async_schedule_save()
async def async_load(self) -> None: # noqa: C901
async def async_load(self) -> None:
"""Load the users."""
if self._loaded:
raise RuntimeError("Auth storage is already loaded")

View File

@@ -4,9 +4,8 @@ from __future__ import annotations
import logging
import types
from typing import Any, Generic
from typing import Any
from typing_extensions import TypeVar
import voluptuous as vol
from voluptuous.humanize import humanize_error
@@ -35,12 +34,6 @@ DATA_REQS: HassKey[set[str]] = HassKey("mfa_auth_module_reqs_processed")
_LOGGER = logging.getLogger(__name__)
_MultiFactorAuthModuleT = TypeVar(
"_MultiFactorAuthModuleT",
bound="MultiFactorAuthModule",
default="MultiFactorAuthModule",
)
class MultiFactorAuthModule:
"""Multi-factor Auth Module of validation function."""
@@ -102,7 +95,9 @@ class MultiFactorAuthModule:
raise NotImplementedError
class SetupFlow(data_entry_flow.FlowHandler, Generic[_MultiFactorAuthModuleT]):
class SetupFlow[_MultiFactorAuthModuleT: MultiFactorAuthModule = MultiFactorAuthModule](
data_entry_flow.FlowHandler
):
"""Handler for the setup flow."""
def __init__(

View File

@@ -17,12 +17,12 @@ POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
__all__ = [
"POLICY_SCHEMA",
"merge_policies",
"PermissionLookup",
"PolicyType",
"AbstractPermissions",
"PolicyPermissions",
"OwnerPermissions",
"PermissionLookup",
"PolicyPermissions",
"PolicyType",
"merge_policies",
]

View File

@@ -5,9 +5,8 @@ from __future__ import annotations
from collections.abc import Mapping
import logging
import types
from typing import Any, Generic
from typing import Any
from typing_extensions import TypeVar
import voluptuous as vol
from voluptuous.humanize import humanize_error
@@ -47,8 +46,6 @@ AUTH_PROVIDER_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
_AuthProviderT = TypeVar("_AuthProviderT", bound="AuthProvider", default="AuthProvider")
class AuthProvider:
"""Provider of user authentication."""
@@ -195,9 +192,8 @@ async def load_auth_provider_module(
return module
class LoginFlow(
class LoginFlow[_AuthProviderT: AuthProvider = AuthProvider](
FlowHandler[AuthFlowContext, AuthFlowResult, tuple[str, str]],
Generic[_AuthProviderT],
):
"""Handler for the login flow."""

View File

@@ -119,7 +119,7 @@ def _extract_backup(
Path(
tempdir,
"extracted",
f"homeassistant.tar{'.gz' if backup_meta["compressed"] else ''}",
f"homeassistant.tar{'.gz' if backup_meta['compressed'] else ''}",
),
gzip=backup_meta["compressed"],
key=password_to_key(restore_content.password)

View File

@@ -31,7 +31,7 @@ def _check_import_call_allowed(mapped_args: dict[str, Any]) -> bool:
def _check_file_allowed(mapped_args: dict[str, Any]) -> bool:
# If the file is in /proc we can ignore it.
args = mapped_args["args"]
path = args[0] if type(args[0]) is str else str(args[0]) # noqa: E721
path = args[0] if type(args[0]) is str else str(args[0])
return path.startswith(ALLOWED_FILE_PREFIXES)

View File

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

View File

@@ -34,17 +34,17 @@
"services": {
"capture_image": {
"name": "Capture image",
"description": "Request a new image capture from a camera device.",
"description": "Requests a new image capture from a camera device.",
"fields": {
"entity_id": {
"name": "Entity",
"description": "Entity id of the camera to request an image."
"description": "Entity ID of the camera to request an image from."
}
}
},
"change_setting": {
"name": "Change setting",
"description": "Change an Abode system setting.",
"description": "Changes an Abode system setting.",
"fields": {
"setting": {
"name": "Setting",
@@ -58,11 +58,11 @@
},
"trigger_automation": {
"name": "Trigger automation",
"description": "Trigger an Abode automation.",
"description": "Triggers an Abode automation.",
"fields": {
"entity_id": {
"name": "Entity",
"description": "Entity id of the automation to trigger."
"description": "Entity ID of the automation to trigger."
}
}
}

View File

@@ -70,7 +70,7 @@ class PulseHub:
async def async_notify_update(self, update_type: aiopulse.UpdateType) -> None:
"""Evaluate entities when hub reports that update has occurred."""
LOGGER.debug("Hub {update_type.name} updated")
LOGGER.debug("Hub %s updated", update_type.name)
if update_type == aiopulse.UpdateType.rollers:
await update_devices(self.hass, self.config_entry, self.api.rollers)

View File

@@ -3,9 +3,9 @@
from __future__ import annotations
import logging
import telnetlib # pylint: disable=deprecated-module
from typing import Final
import telnetlib # pylint: disable=deprecated-module
import voluptuous as vol
from homeassistant.components.device_tracker import (

View File

@@ -34,9 +34,12 @@ from .const import (
SERVICE_REMOVE_URL,
)
SERVICE_URL_SCHEMA = vol.Schema({vol.Required(CONF_URL): cv.url})
SERVICE_URL_SCHEMA = vol.Schema({vol.Required(CONF_URL): vol.Any(cv.url, cv.path)})
SERVICE_ADD_URL_SCHEMA = vol.Schema(
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_URL): cv.url}
{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_URL): vol.Any(cv.url, cv.path),
}
)
SERVICE_REFRESH_SCHEMA = vol.Schema(
{vol.Optional(CONF_FORCE, default=False): cv.boolean}

View File

@@ -66,7 +66,7 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Motion sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} motion'
self._attr_name = f"{self._zone['name']} motion"
self._attr_unique_id += "-motion"
@property
@@ -84,7 +84,7 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone MyZone sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} myZone'
self._attr_name = f"{self._zone['name']} myZone"
self._attr_unique_id += "-myzone"
@property

View File

@@ -103,7 +103,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Vent Sensor."""
super().__init__(instance, ac_key, zone_key=zone_key)
self._attr_name = f'{self._zone["name"]} vent'
self._attr_name = f"{self._zone['name']} vent"
self._attr_unique_id += "-vent"
@property
@@ -131,7 +131,7 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone wireless signal sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} signal'
self._attr_name = f"{self._zone['name']} signal"
self._attr_unique_id += "-signal"
@property
@@ -165,7 +165,7 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Temp Sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} temperature'
self._attr_name = f"{self._zone['name']} temperature"
self._attr_unique_id += "-temp"
@property

View File

@@ -11,10 +11,10 @@ from airgradient import (
from awesomeversion import AwesomeVersion
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_MODEL
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN
@@ -37,7 +37,7 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
await self.client.set_configuration_control(ConfigurationControl.LOCAL)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
self.data[CONF_HOST] = host = discovery_info.host

View File

@@ -137,6 +137,15 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
entity_registry_enabled_default=False,
value_fn=lambda status: status.raw_total_volatile_organic_component,
),
AirGradientMeasurementSensorEntityDescription(
key="pm02_raw",
translation_key="raw_pm02",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.raw_pm02,
),
)
CONFIG_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (

View File

@@ -119,6 +119,9 @@
"raw_nitrogen": {
"name": "Raw NOx"
},
"raw_pm02": {
"name": "Raw PM2.5"
},
"display_pm_standard": {
"name": "[%key:component::airgradient::entity::select::display_pm_standard::name%]",
"state": {

View File

@@ -39,45 +39,54 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="temp",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
),
"humidity": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
"pressure": SensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
),
"battery": SensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
"co2": SensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"voc": SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"light": SensorEntityDescription(
key="light",
native_unit_of_measurement=PERCENTAGE,
translation_key="light",
state_class=SensorStateClass.MEASUREMENT,
),
"virusRisk": SensorEntityDescription(
key="virusRisk",
translation_key="virus_risk",
state_class=SensorStateClass.MEASUREMENT,
),
"mold": SensorEntityDescription(
key="mold",
translation_key="mold",
state_class=SensorStateClass.MEASUREMENT,
),
"rssi": SensorEntityDescription(
key="rssi",
@@ -85,16 +94,19 @@ SENSORS: dict[str, SensorEntityDescription] = {
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
"pm1": SensorEntityDescription(
key="pm1",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
),
"pm25": SensorEntityDescription(
key="pm25",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
}
@@ -143,8 +155,7 @@ class AirthingsHeaterEnergySensor(
self._id = airthings_device.device_id
self._attr_device_info = DeviceInfo(
configuration_url=(
"https://dashboard.airthings.com/devices/"
f"{airthings_device.device_id}"
f"https://dashboard.airthings.com/devices/{airthings_device.device_id}"
),
identifiers={(DOMAIN, airthings_device.device_id)},
name=airthings_device.name,

View File

@@ -67,18 +67,21 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"humidity": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"pressure": SensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"battery": SensorEntityDescription(
key="battery",
@@ -86,24 +89,28 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
),
"co2": SensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"voc": SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"illuminance": SensorEntityDescription(
key="illuminance",
translation_key="illuminance",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
}

View File

@@ -50,7 +50,7 @@ SENSOR_DESCRIPTIONS = (
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: int(
history.get(
f'Outdoor {"AQI(US)" if settings["is_aqi_usa"] else "AQI(CN)"}', -1
f"Outdoor {'AQI(US)' if settings['is_aqi_usa'] else 'AQI(CN)'}", -1
)
),
translation_key="outdoor_air_quality_index",

View File

@@ -5,7 +5,14 @@ from __future__ import annotations
import logging
from typing import Any
from aioairzone.const import AZD_MAC, AZD_WEBSERVER, DEFAULT_SYSTEM_ID
from aioairzone.const import (
AZD_FIRMWARE,
AZD_FULL_NAME,
AZD_MAC,
AZD_MODEL,
AZD_WEBSERVER,
DEFAULT_SYSTEM_ID,
)
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
from homeassistant.config_entries import ConfigEntry
@@ -17,6 +24,7 @@ from homeassistant.helpers import (
entity_registry as er,
)
from .const import DOMAIN, MANUFACTURER
from .coordinator import AirzoneUpdateCoordinator
PLATFORMS: list[Platform] = [
@@ -88,6 +96,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
entry.runtime_data = coordinator
device_registry = dr.async_get(hass)
ws_data: dict[str, Any] | None = coordinator.data.get(AZD_WEBSERVER)
if ws_data is not None:
mac = ws_data.get(AZD_MAC, "")
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, mac)},
identifiers={(DOMAIN, f"{entry.entry_id}_ws")},
manufacturer=MANUFACTURER,
model=ws_data.get(AZD_MODEL),
name=ws_data.get(AZD_FULL_NAME),
sw_version=ws_data.get(AZD_FIRMWARE),
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View File

@@ -10,12 +10,12 @@ from aioairzone.exceptions import AirzoneError, InvalidSystem
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
import voluptuous as vol
from homeassistant.components import dhcp
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import DOMAIN
@@ -93,7 +93,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_dhcp(
self, discovery_info: dhcp.DhcpServiceInfo
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle DHCP discovery."""
self._discovered_ip = discovery_info.ip

View File

@@ -68,8 +68,9 @@ class AirzoneSystemEntity(AirzoneEntity):
model=self.get_airzone_value(AZD_MODEL),
name=f"System {self.system_id}",
sw_version=self.get_airzone_value(AZD_FIRMWARE),
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
)
if AZD_WEBSERVER in self.coordinator.data:
self._attr_device_info["via_device"] = (DOMAIN, f"{entry.entry_id}_ws")
self._attr_unique_id = entry.unique_id or entry.entry_id
@property
@@ -102,8 +103,9 @@ class AirzoneHotWaterEntity(AirzoneEntity):
manufacturer=MANUFACTURER,
model="DHW",
name=self.get_airzone_value(AZD_NAME),
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
)
if AZD_WEBSERVER in self.coordinator.data:
self._attr_device_info["via_device"] = (DOMAIN, f"{entry.entry_id}_ws")
self._attr_unique_id = entry.unique_id or entry.entry_id
def get_airzone_value(self, key: str) -> Any:

View File

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

View File

@@ -474,25 +474,30 @@ class ClimateCapabilities(AlexaEntity):
# If we support two modes, one being off, we allow turning on too.
supported_features = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if (
self.entity.domain == climate.DOMAIN
and climate.HVACMode.OFF
in (self.entity.attributes.get(climate.ATTR_HVAC_MODES) or [])
or self.entity.domain == climate.DOMAIN
and (
supported_features
& (
climate.ClimateEntityFeature.TURN_ON
| climate.ClimateEntityFeature.TURN_OFF
(
self.entity.domain == climate.DOMAIN
and climate.HVACMode.OFF
in (self.entity.attributes.get(climate.ATTR_HVAC_MODES) or [])
)
or (
self.entity.domain == climate.DOMAIN
and (
supported_features
& (
climate.ClimateEntityFeature.TURN_ON
| climate.ClimateEntityFeature.TURN_OFF
)
)
)
or self.entity.domain == water_heater.DOMAIN
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
or (
self.entity.domain == water_heater.DOMAIN
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
)
):
yield AlexaPowerController(self.entity)
if (
self.entity.domain == climate.DOMAIN
or self.entity.domain == water_heater.DOMAIN
if self.entity.domain == climate.DOMAIN or (
self.entity.domain == water_heater.DOMAIN
and (
supported_features
& water_heater.WaterHeaterEntityFeature.OPERATION_MODE

View File

@@ -317,9 +317,8 @@ async def async_enable_proactive_mode(
if should_doorbell:
old_state = data["old_state"]
if (
new_state.domain == event.DOMAIN
or new_state.state == STATE_ON
if new_state.domain == event.DOMAIN or (
new_state.state == STATE_ON
and (old_state is None or old_state.state != STATE_ON)
):
await async_send_doorbell_event_message(

View File

@@ -21,7 +21,7 @@
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"invalid_unique_id": "Impossible to determine a valid unique id for the device"
"invalid_unique_id": "Impossible to determine a valid unique ID for the device"
}
},
"options": {
@@ -38,17 +38,17 @@
}
},
"apps": {
"title": "Configure Android Apps",
"description": "Configure application id {app_id}",
"title": "Configure Android apps",
"description": "Configure application ID {app_id}",
"data": {
"app_name": "Application Name",
"app_name": "Application name",
"app_id": "Application ID",
"app_delete": "Check to delete this application"
}
},
"rules": {
"title": "Configure Android state detection rules",
"description": "Configure detection rule for application id {rule_id}",
"description": "Configure detection rule for application ID {rule_id}",
"data": {
"rule_id": "[%key:component::androidtv::options::step::apps::data::app_id%]",
"rule_values": "List of state detection rules (see documentation)",

View File

@@ -14,7 +14,6 @@ from androidtvremote2 import (
)
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import (
SOURCE_REAUTH,
ConfigEntry,
@@ -31,6 +30,7 @@ from homeassistant.helpers.selector import (
SelectSelectorConfig,
SelectSelectorMode,
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_APP_ICON, CONF_APP_NAME, CONF_APPS, CONF_ENABLE_IME, DOMAIN
from .helpers import create_api, get_enable_ime
@@ -142,7 +142,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
_LOGGER.debug("Android TV device found via zeroconf: %s", discovery_info)

View File

@@ -44,12 +44,12 @@
}
},
"apps": {
"title": "Configure Android Apps",
"description": "Configure application id {app_id}",
"title": "Configure Android apps",
"description": "Configure application ID {app_id}",
"data": {
"app_name": "Application Name",
"app_name": "Application name",
"app_id": "Application ID",
"app_icon": "Application Icon",
"app_icon": "Application icon",
"app_delete": "Check to delete this application"
}
}

View File

@@ -34,6 +34,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
SchemaFlowFormStep,
SchemaOptionsFlowHandler,
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_CREDENTIALS, CONF_IDENTIFIERS, CONF_START_OFF, DOMAIN
@@ -204,7 +205,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle device found via zeroconf."""
if discovery_info.ip_address.version == 6:

View File

@@ -38,7 +38,7 @@ from homeassistant.loader import (
from homeassistant.util import slugify
from homeassistant.util.hass_dict import HassKey
__all__ = ["ClientCredential", "AuthorizationServer", "async_import_client_credential"]
__all__ = ["AuthorizationServer", "ClientCredential", "async_import_client_credential"]
_LOGGER = logging.getLogger(__name__)

View File

@@ -50,7 +50,7 @@ async def async_setup_entry(
descriptions: list[AprilaireHumidifierDescription] = []
if coordinator.data.get(Attribute.HUMIDIFICATION_AVAILABLE) in (0, 1, 2):
if coordinator.data.get(Attribute.HUMIDIFICATION_AVAILABLE) in (1, 2):
descriptions.append(
AprilaireHumidifierDescription(
key="humidifier",
@@ -67,7 +67,7 @@ async def async_setup_entry(
)
)
if coordinator.data.get(Attribute.DEHUMIDIFICATION_AVAILABLE) in (0, 1):
if coordinator.data.get(Attribute.DEHUMIDIFICATION_AVAILABLE) == 1:
descriptions.append(
AprilaireHumidifierDescription(
key="dehumidifier",

View File

@@ -29,6 +29,8 @@ class ApSystemsSensorData:
class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
"""Coordinator used for all sensors."""
device_version: str
def __init__(self, hass: HomeAssistant, api: APsystemsEZ1M) -> None:
"""Initialize my coordinator."""
super().__init__(
@@ -46,6 +48,7 @@ class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
raise UpdateFailed from None
self.api.max_power = device_info.maxPower
self.api.min_power = device_info.minPower
self.device_version = device_info.devVer
async def _async_update_data(self) -> ApSystemsSensorData:
try:

View File

@@ -21,7 +21,8 @@ class ApSystemsEntity(Entity):
"""Initialize the APsystems entity."""
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, data.device_id)},
serial_number=data.device_id,
manufacturer="APsystems",
model="EZ1-M",
serial_number=data.device_id,
sw_version=data.coordinator.device_version.split(" ")[1],
)

View File

@@ -19,5 +19,5 @@
"documentation": "https://www.home-assistant.io/integrations/aranet",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["aranet4==2.4.0"]
"requirements": ["aranet4==2.5.0"]
}

View File

@@ -22,6 +22,7 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
ATTR_MANUFACTURER,
ATTR_MODEL,
ATTR_NAME,
ATTR_SW_VERSION,
CONCENTRATION_PARTS_PER_MILLION,
@@ -142,6 +143,7 @@ def _sensor_device_info_to_hass(
if adv.readings and adv.readings.name:
hass_device_info[ATTR_NAME] = adv.readings.name
hass_device_info[ATTR_MANUFACTURER] = ARANET_MANUFACTURER_NAME
hass_device_info[ATTR_MODEL] = adv.readings.type.model
if adv.manufacturer_data:
hass_device_info[ATTR_SW_VERSION] = str(adv.manufacturer_data.version)
return hass_device_info

View File

@@ -9,10 +9,10 @@ from arcam.fmj.client import Client, ConnectionFailed
from arcam.fmj.utils import get_uniqueid_from_host, get_uniqueid_from_udn
import voluptuous as vol
from homeassistant.components import ssdp
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_UDN, SsdpServiceInfo
from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN
@@ -88,12 +88,12 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
)
async def async_step_ssdp(
self, discovery_info: ssdp.SsdpServiceInfo
self, discovery_info: SsdpServiceInfo
) -> ConfigFlowResult:
"""Handle a discovered device."""
host = str(urlparse(discovery_info.ssdp_location).hostname)
port = DEFAULT_PORT
uuid = get_uniqueid_from_udn(discovery_info.upnp[ssdp.ATTR_UPNP_UDN])
uuid = get_uniqueid_from_udn(discovery_info.upnp[ATTR_UPNP_UDN])
if not uuid:
return self.async_abort(reason="cannot_connect")

View File

@@ -90,7 +90,7 @@ class ArubaDeviceScanner(DeviceScanner):
"""Retrieve data from Aruba Access Point and return parsed result."""
connect = f"ssh {self.username}@{self.host} -o HostKeyAlgorithms=ssh-rsa"
ssh = pexpect.spawn(connect)
ssh: pexpect.spawn[str] = pexpect.spawn(connect, encoding="utf-8")
query = ssh.expect(
[
"password:",
@@ -125,12 +125,12 @@ class ArubaDeviceScanner(DeviceScanner):
ssh.expect("#")
ssh.sendline("show clients")
ssh.expect("#")
devices_result = ssh.before.split(b"\r\n")
devices_result = (ssh.before or "").splitlines()
ssh.sendline("exit")
devices: dict[str, dict[str, str]] = {}
for device in devices_result:
if match := _DEVICES_REGEX.search(device.decode("utf-8")):
if match := _DEVICES_REGEX.search(device):
devices[match.group("ip")] = {
"ip": match.group("ip"),
"mac": match.group("mac").upper(),

View File

@@ -6,5 +6,5 @@
"iot_class": "local_polling",
"loggers": ["pexpect", "ptyprocess"],
"quality_scale": "legacy",
"requirements": ["pexpect==4.6.0"]
"requirements": ["pexpect==4.9.0"]
}

View File

@@ -46,24 +46,24 @@ from .websocket_api import async_register_websocket_api
__all__ = (
"DOMAIN",
"async_create_default_pipeline",
"async_get_pipelines",
"async_migrate_engine",
"async_setup",
"async_pipeline_from_audio_stream",
"async_update_pipeline",
"EVENT_RECORDING",
"OPTION_PREFERRED",
"SAMPLES_PER_CHUNK",
"SAMPLE_CHANNELS",
"SAMPLE_RATE",
"SAMPLE_WIDTH",
"AudioSettings",
"Pipeline",
"PipelineEvent",
"PipelineEventType",
"PipelineNotFound",
"WakeWordSettings",
"EVENT_RECORDING",
"OPTION_PREFERRED",
"SAMPLES_PER_CHUNK",
"SAMPLE_RATE",
"SAMPLE_WIDTH",
"SAMPLE_CHANNELS",
"async_create_default_pipeline",
"async_get_pipelines",
"async_migrate_engine",
"async_pipeline_from_audio_stream",
"async_setup",
"async_update_pipeline",
)
CONFIG_SCHEMA = vol.Schema(
@@ -108,6 +108,7 @@ async def async_pipeline_from_audio_stream(
device_id: str | None = None,
start_stage: PipelineStage = PipelineStage.STT,
end_stage: PipelineStage = PipelineStage.TTS,
conversation_extra_system_prompt: str | None = None,
) -> None:
"""Create an audio pipeline from an audio stream.
@@ -119,6 +120,7 @@ async def async_pipeline_from_audio_stream(
stt_metadata=stt_metadata,
stt_stream=stt_stream,
wake_word_phrase=wake_word_phrase,
conversation_extra_system_prompt=conversation_extra_system_prompt,
run=PipelineRun(
hass,
context=context,

View File

@@ -50,6 +50,7 @@ from homeassistant.util import (
language as language_util,
ulid as ulid_util,
)
from homeassistant.util.hass_dict import HassKey
from homeassistant.util.limited_size_dict import LimitedSizeDict
from .audio_enhancer import AudioEnhancer, EnhancedAudioChunk, MicroVadSpeexEnhancer
@@ -91,6 +92,8 @@ ENGINE_LANGUAGE_PAIRS = (
("tts_engine", "tts_language"),
)
KEY_ASSIST_PIPELINE: HassKey[PipelineData] = HassKey(DOMAIN)
def validate_language(data: dict[str, Any]) -> Any:
"""Validate language settings."""
@@ -248,7 +251,7 @@ async def async_create_default_pipeline(
The default pipeline will use the homeassistant conversation agent and the
specified stt / tts engines.
"""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
pipeline_store = pipeline_data.pipeline_store
pipeline_settings = _async_resolve_default_pipeline_settings(
hass,
@@ -283,7 +286,7 @@ def _async_get_pipeline_from_conversation_entity(
@callback
def async_get_pipeline(hass: HomeAssistant, pipeline_id: str | None = None) -> Pipeline:
"""Get a pipeline by id or the preferred pipeline."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
if pipeline_id is None:
# A pipeline was not specified, use the preferred one
@@ -306,7 +309,7 @@ def async_get_pipeline(hass: HomeAssistant, pipeline_id: str | None = None) -> P
@callback
def async_get_pipelines(hass: HomeAssistant) -> list[Pipeline]:
"""Get all pipelines."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
return list(pipeline_data.pipeline_store.data.values())
@@ -329,7 +332,7 @@ async def async_update_pipeline(
prefer_local_intents: bool | UndefinedType = UNDEFINED,
) -> None:
"""Update a pipeline."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
updates: dict[str, Any] = pipeline.to_json()
updates.pop("id")
@@ -587,7 +590,7 @@ class PipelineRun:
):
raise InvalidPipelineStagesError(self.start_stage, self.end_stage)
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
if self.pipeline.id not in pipeline_data.pipeline_debug:
pipeline_data.pipeline_debug[self.pipeline.id] = LimitedSizeDict(
size_limit=STORED_PIPELINE_RUNS
@@ -615,7 +618,7 @@ class PipelineRun:
def process_event(self, event: PipelineEvent) -> None:
"""Log an event and call listener."""
self.event_callback(event)
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
if self.id not in pipeline_data.pipeline_debug[self.pipeline.id]:
# This run has been evicted from the logged pipeline runs already
return
@@ -650,7 +653,7 @@ class PipelineRun:
)
)
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
pipeline_data.pipeline_runs.remove_run(self)
async def prepare_wake_word_detection(self) -> None:
@@ -1010,16 +1013,29 @@ class PipelineRun:
self.intent_agent = agent_info.id
async def recognize_intent(
self, intent_input: str, conversation_id: str | None, device_id: str | None
self,
intent_input: str,
conversation_id: str | None,
device_id: str | None,
conversation_extra_system_prompt: str | None,
) -> str:
"""Run intent recognition portion of pipeline. Returns text to speak."""
if self.intent_agent is None:
raise RuntimeError("Recognize intent was not prepared")
if self.pipeline.conversation_language == MATCH_ALL:
# LLMs support all languages ('*') so use pipeline language for
# intent fallback.
input_language = self.pipeline.language
# LLMs support all languages ('*') so use languages from the
# pipeline for intent fallback.
#
# We prioritize the STT and TTS languages because they may be more
# specific, such as "zh-CN" instead of just "zh". This is necessary
# for languages whose intents are split out by region when
# preferring local intent matching.
input_language = (
self.pipeline.stt_language
or self.pipeline.tts_language
or self.pipeline.language
)
else:
input_language = self.pipeline.conversation_language
@@ -1045,10 +1061,12 @@ class PipelineRun:
device_id=device_id,
language=input_language,
agent_id=self.intent_agent,
extra_system_prompt=conversation_extra_system_prompt,
)
processed_locally = self.intent_agent == conversation.HOME_ASSISTANT_AGENT
conversation_result: conversation.ConversationResult | None = None
agent_id = user_input.agent_id
intent_response: intent.IntentResponse | None = None
if user_input.agent_id != conversation.HOME_ASSISTANT_AGENT:
# Sentence triggers override conversation agent
if (
@@ -1058,14 +1076,12 @@ class PipelineRun:
)
) is not None:
# Sentence trigger matched
trigger_response = intent.IntentResponse(
agent_id = "sentence_trigger"
intent_response = intent.IntentResponse(
self.pipeline.conversation_language
)
trigger_response.async_set_speech(trigger_response_text)
conversation_result = conversation.ConversationResult(
response=trigger_response,
conversation_id=user_input.conversation_id,
)
intent_response.async_set_speech(trigger_response_text)
# Try local intents first, if preferred.
elif self.pipeline.prefer_local_intents and (
intent_response := await conversation.async_handle_intents(
@@ -1073,13 +1089,31 @@ class PipelineRun:
)
):
# Local intent matched
conversation_result = conversation.ConversationResult(
response=intent_response,
conversation_id=user_input.conversation_id,
)
agent_id = conversation.HOME_ASSISTANT_AGENT
processed_locally = True
if conversation_result is None:
# It was already handled, create response and add to chat history
if intent_response is not None:
async with conversation.async_get_chat_session(
self.hass, user_input
) as chat_session:
speech: str = intent_response.speech.get("plain", {}).get(
"speech", ""
)
chat_session.async_add_message(
conversation.ChatMessage(
role="assistant",
agent_id=agent_id,
content=speech,
native=intent_response,
)
)
conversation_result = conversation.ConversationResult(
response=intent_response,
conversation_id=chat_session.conversation_id,
)
else:
# Fall back to pipeline conversation agent
conversation_result = await conversation.async_converse(
hass=self.hass,
@@ -1090,6 +1124,10 @@ class PipelineRun:
language=user_input.language,
agent_id=user_input.agent_id,
)
speech = conversation_result.response.speech.get("plain", {}).get(
"speech", ""
)
except Exception as src_error:
_LOGGER.exception("Unexpected error during intent recognition")
raise IntentRecognitionError(
@@ -1109,10 +1147,6 @@ class PipelineRun:
)
)
speech: str = conversation_result.response.speech.get("plain", {}).get(
"speech", ""
)
return speech
async def prepare_text_to_speech(self) -> None:
@@ -1213,7 +1247,7 @@ class PipelineRun:
return
# Forward to device audio capture
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
audio_queue = pipeline_data.device_audio_queues.get(self._device_id)
if audio_queue is None:
return
@@ -1392,8 +1426,13 @@ class PipelineInput:
"""Input for text-to-speech. Required when start_stage = tts."""
conversation_id: str | None = None
"""Identifier for the conversation."""
conversation_extra_system_prompt: str | None = None
"""Extra prompt information for the conversation agent."""
device_id: str | None = None
"""Identifier of the device that is processing the input/output of the pipeline."""
async def execute(self) -> None:
"""Run pipeline."""
@@ -1453,9 +1492,9 @@ class PipelineInput:
if stt_audio_buffer:
# Send audio in the buffer first to speech-to-text, then move on to stt_stream.
# This is basically an async itertools.chain.
async def buffer_then_audio_stream() -> (
AsyncGenerator[EnhancedAudioChunk]
):
async def buffer_then_audio_stream() -> AsyncGenerator[
EnhancedAudioChunk
]:
# Buffered audio
for chunk in stt_audio_buffer:
yield chunk
@@ -1483,6 +1522,7 @@ class PipelineInput:
intent_input,
self.conversation_id,
self.device_id,
self.conversation_extra_system_prompt,
)
if tts_input.strip():
current_stage = PipelineStage.TTS
@@ -1864,7 +1904,7 @@ class PipelineStore(Store[SerializedPipelineStorageCollection]):
return old_data
@singleton(DOMAIN)
@singleton(KEY_ASSIST_PIPELINE, async_=True)
async def async_setup_pipeline_store(hass: HomeAssistant) -> PipelineData:
"""Set up the pipeline storage collection."""
pipeline_store = PipelineStorageCollection(

View File

@@ -9,8 +9,8 @@ from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import collection, entity_registry as er, restore_state
from .const import DOMAIN, OPTION_PREFERRED
from .pipeline import AssistDevice, PipelineData, PipelineStorageCollection
from .const import OPTION_PREFERRED
from .pipeline import KEY_ASSIST_PIPELINE, AssistDevice
from .vad import VadSensitivity
@@ -30,7 +30,7 @@ def get_chosen_pipeline(
if state is None or state.state == OPTION_PREFERRED:
return None
pipeline_store: PipelineStorageCollection = hass.data[DOMAIN].pipeline_store
pipeline_store = hass.data[KEY_ASSIST_PIPELINE].pipeline_store
return next(
(item.id for item in pipeline_store.async_items() if item.name == state.state),
None,
@@ -80,7 +80,7 @@ class AssistPipelineSelect(SelectEntity, restore_state.RestoreEntity):
"""When entity is added to Home Assistant."""
await super().async_added_to_hass()
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
pipeline_store = pipeline_data.pipeline_store
self.async_on_remove(
pipeline_store.async_add_change_set_listener(self._pipelines_updated)
@@ -116,9 +116,7 @@ class AssistPipelineSelect(SelectEntity, restore_state.RestoreEntity):
@callback
def _update_options(self) -> None:
"""Handle pipeline update."""
pipeline_store: PipelineStorageCollection = self.hass.data[
DOMAIN
].pipeline_store
pipeline_store = self.hass.data[KEY_ASSIST_PIPELINE].pipeline_store
options = [OPTION_PREFERRED]
options.extend(sorted(item.name for item in pipeline_store.async_items()))
self._attr_options = options

View File

@@ -1,9 +1,6 @@
"""Assist pipeline Websocket API."""
import asyncio
# Suppressing disable=deprecated-module is needed for Python 3.11
import audioop # pylint: disable=deprecated-module
import base64
from collections.abc import AsyncGenerator, Callable
import contextlib
@@ -11,6 +8,8 @@ import logging
import math
from typing import Any, Final
# Suppressing disable=deprecated-module is needed for Python 3.11
import audioop # pylint: disable=deprecated-module
import voluptuous as vol
from homeassistant.components import conversation, stt, tts, websocket_api
@@ -22,7 +21,6 @@ from homeassistant.util import language as language_util
from .const import (
DEFAULT_PIPELINE_TIMEOUT,
DEFAULT_WAKE_WORD_TIMEOUT,
DOMAIN,
EVENT_RECORDING,
SAMPLE_CHANNELS,
SAMPLE_RATE,
@@ -30,9 +28,9 @@ from .const import (
)
from .error import PipelineNotFound
from .pipeline import (
KEY_ASSIST_PIPELINE,
AudioSettings,
DeviceAudioQueue,
PipelineData,
PipelineError,
PipelineEvent,
PipelineEventType,
@@ -284,7 +282,7 @@ def websocket_list_runs(
msg: dict[str, Any],
) -> None:
"""List pipeline runs for which debug data is available."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
pipeline_id = msg["pipeline_id"]
if pipeline_id not in pipeline_data.pipeline_debug:
@@ -320,7 +318,7 @@ def websocket_list_devices(
msg: dict[str, Any],
) -> None:
"""List assist devices."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
ent_reg = er.async_get(hass)
connection.send_result(
msg["id"],
@@ -351,7 +349,7 @@ def websocket_get_run(
msg: dict[str, Any],
) -> None:
"""Get debug data for a pipeline run."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
pipeline_id = msg["pipeline_id"]
pipeline_run_id = msg["pipeline_run_id"]
@@ -456,7 +454,7 @@ async def websocket_device_capture(
msg: dict[str, Any],
) -> None:
"""Capture raw audio from a satellite device and forward to client."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
device_id = msg["device_id"]
# Number of seconds to record audio in wall clock time

View File

@@ -30,8 +30,8 @@ from .websocket_api import async_register_websocket_api
__all__ = [
"DOMAIN",
"AssistSatelliteAnnouncement",
"AssistSatelliteEntity",
"AssistSatelliteConfiguration",
"AssistSatelliteEntity",
"AssistSatelliteEntityDescription",
"AssistSatelliteEntityFeature",
"AssistSatelliteWakeWord",

View File

@@ -187,47 +187,10 @@ class AssistSatelliteEntity(entity.Entity):
"""
await self._cancel_running_pipeline()
media_id_source: Literal["url", "media_id", "tts"] | None = None
if message is None:
message = ""
if not media_id:
media_id_source = "tts"
# Synthesize audio and get URL
pipeline_id = self._resolve_pipeline()
pipeline = async_get_pipeline(self.hass, pipeline_id)
tts_options: dict[str, Any] = {}
if pipeline.tts_voice is not None:
tts_options[tts.ATTR_VOICE] = pipeline.tts_voice
if self.tts_options is not None:
tts_options.update(self.tts_options)
media_id = tts_generate_media_source_id(
self.hass,
message,
engine=pipeline.tts_engine,
language=pipeline.tts_language,
options=tts_options,
)
if media_source.is_media_source_id(media_id):
if not media_id_source:
media_id_source = "media_id"
media = await media_source.async_resolve_media(
self.hass,
media_id,
None,
)
media_id = media.url
if not media_id_source:
media_id_source = "url"
# Resolve to full URL
media_id = async_process_play_media_url(self.hass, media_id)
announcement = await self._resolve_announcement_media_id(message, media_id)
if self._is_announcing:
raise SatelliteBusyError
@@ -237,9 +200,7 @@ class AssistSatelliteEntity(entity.Entity):
try:
# Block until announcement is finished
await self.async_announce(
AssistSatelliteAnnouncement(message, media_id, media_id_source)
)
await self.async_announce(announcement)
finally:
self._is_announcing = False
self._set_state(AssistSatelliteState.IDLE)
@@ -428,3 +389,48 @@ class AssistSatelliteEntity(entity.Entity):
vad_sensitivity = vad.VadSensitivity(vad_sensitivity_state.state)
return vad.VadSensitivity.to_seconds(vad_sensitivity)
async def _resolve_announcement_media_id(
self, message: str, media_id: str | None
) -> AssistSatelliteAnnouncement:
"""Resolve the media ID."""
media_id_source: Literal["url", "media_id", "tts"] | None = None
if not media_id:
media_id_source = "tts"
# Synthesize audio and get URL
pipeline_id = self._resolve_pipeline()
pipeline = async_get_pipeline(self.hass, pipeline_id)
tts_options: dict[str, Any] = {}
if pipeline.tts_voice is not None:
tts_options[tts.ATTR_VOICE] = pipeline.tts_voice
if self.tts_options is not None:
tts_options.update(self.tts_options)
media_id = tts_generate_media_source_id(
self.hass,
message,
engine=pipeline.tts_engine,
language=pipeline.tts_language,
options=tts_options,
)
if media_source.is_media_source_id(media_id):
if not media_id_source:
media_id_source = "media_id"
media = await media_source.async_resolve_media(
self.hass,
media_id,
None,
)
media_id = media.url
if not media_id_source:
media_id_source = "url"
# Resolve to full URL
media_id = async_process_play_media_url(self.hass, media_id)
return AssistSatelliteAnnouncement(message, media_id, media_id_source)

View File

@@ -0,0 +1,69 @@
"""Assist Satellite intents."""
import voluptuous as vol
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, intent
from .const import DOMAIN, AssistSatelliteEntityFeature
async def async_setup_intents(hass: HomeAssistant) -> None:
"""Set up the intents."""
intent.async_register(hass, BroadcastIntentHandler())
class BroadcastIntentHandler(intent.IntentHandler):
"""Broadcast a message."""
intent_type = intent.INTENT_BROADCAST
description = "Broadcast a message through the home"
@property
def slot_schema(self) -> dict | None:
"""Return a slot schema."""
return {vol.Required("message"): str}
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
"""Broadcast a message."""
hass = intent_obj.hass
ent_reg = er.async_get(hass)
# Find all assist satellite entities that are not the one invoking the intent
entities = {
entity: entry
for entity in hass.states.async_entity_ids(DOMAIN)
if (entry := ent_reg.async_get(entity))
and entry.supported_features & AssistSatelliteEntityFeature.ANNOUNCE
}
if intent_obj.device_id:
entities = {
entity: entry
for entity, entry in entities.items()
if entry.device_id != intent_obj.device_id
}
await hass.services.async_call(
DOMAIN,
"announce",
{"message": intent_obj.slots["message"]["value"]},
blocking=True,
context=intent_obj.context,
target={"entity_id": list(entities)},
)
response = intent_obj.create_response()
response.async_set_speech("Done")
response.response_type = intent.IntentResponseType.ACTION_DONE
response.async_set_results(
success_results=[
intent.IntentResponseTarget(
type=intent.IntentResponseTargetType.ENTITY,
id=entity,
name=state.name if (state := hass.states.get(entity)) else entity,
)
for entity in entities
]
)
return response

View File

@@ -31,8 +31,8 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"invalid_unique_id": "Impossible to determine a valid unique id for the device",
"no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible"
"invalid_unique_id": "Impossible to determine a valid unique ID for the device",
"no_unique_id": "A device without a valid unique ID is already configured. Configuration of multiple instances is not possible"
}
},
"options": {
@@ -42,7 +42,7 @@
"consider_home": "Seconds to wait before considering a device away",
"track_unknown": "Track unknown / unnamed devices",
"interface": "The interface that you want statistics from (e.g. eth0, eth1 etc)",
"dnsmasq": "The location in the router of the dnsmasq.leases files",
"dnsmasq": "The location of the dnsmasq.leases file in the router",
"require_ip": "Devices must have IP (for access point mode)"
}
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/aussie_broadband",
"iot_class": "cloud_polling",
"loggers": ["aussiebb"],
"requirements": ["pyaussiebb==0.1.4"]
"requirements": ["pyaussiebb==0.1.5"]
}

View File

@@ -636,9 +636,9 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
alias = ""
if "trigger" in run_variables:
if "description" in run_variables["trigger"]:
reason = f' by {run_variables["trigger"]["description"]}'
reason = f" by {run_variables['trigger']['description']}"
if "alias" in run_variables["trigger"]:
alias = f' trigger \'{run_variables["trigger"]["alias"]}\''
alias = f" trigger '{run_variables['trigger']['alias']}'"
self._logger.debug("Automation%s triggered%s", alias, reason)
# Create a new context referring to the old context.

View File

@@ -11,11 +11,12 @@ from python_awair.exceptions import AuthError, AwairError
from python_awair.user import AwairUser
import voluptuous as vol
from homeassistant.components import onboarding, zeroconf
from homeassistant.components import onboarding
from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_DEVICE, CONF_HOST
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN, LOGGER
@@ -29,7 +30,7 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
host: str
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""

View File

@@ -10,7 +10,6 @@ from urllib.parse import urlsplit
import voluptuous as vol
from homeassistant.components import dhcp, ssdp, zeroconf
from homeassistant.config_entries import (
SOURCE_IGNORE,
SOURCE_REAUTH,
@@ -32,6 +31,14 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from homeassistant.helpers.service_info.ssdp import (
ATTR_UPNP_FRIENDLY_NAME,
ATTR_UPNP_PRESENTATION_URL,
ATTR_UPNP_SERIAL,
SsdpServiceInfo,
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from homeassistant.helpers.typing import VolDictType
from homeassistant.util.network import is_link_local
@@ -190,7 +197,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
return await self.async_step_user()
async def async_step_dhcp(
self, discovery_info: dhcp.DhcpServiceInfo
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for a DHCP discovered Axis device."""
return await self._process_discovered_device(
@@ -203,21 +210,21 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
)
async def async_step_ssdp(
self, discovery_info: ssdp.SsdpServiceInfo
self, discovery_info: SsdpServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for a SSDP discovered Axis device."""
url = urlsplit(discovery_info.upnp[ssdp.ATTR_UPNP_PRESENTATION_URL])
url = urlsplit(discovery_info.upnp[ATTR_UPNP_PRESENTATION_URL])
return await self._process_discovered_device(
{
CONF_HOST: url.hostname,
CONF_MAC: format_mac(discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL]),
CONF_NAME: f"{discovery_info.upnp[ssdp.ATTR_UPNP_FRIENDLY_NAME]}",
CONF_MAC: format_mac(discovery_info.upnp[ATTR_UPNP_SERIAL]),
CONF_NAME: f"{discovery_info.upnp[ATTR_UPNP_FRIENDLY_NAME]}",
CONF_PORT: url.port,
}
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for a Zeroconf discovered Axis device."""
return await self._process_discovered_device(

View File

@@ -2,10 +2,10 @@
"config": {
"step": {
"user": {
"title": "Setup your Azure Data Explorer integration",
"title": "Set up Azure Data Explorer",
"description": "Enter connection details",
"data": {
"cluster_ingest_uri": "Cluster Ingest URI",
"cluster_ingest_uri": "Cluster ingestion URI",
"authority_id": "Authority ID",
"client_id": "Client ID",
"client_secret": "Client secret",
@@ -14,7 +14,7 @@
"use_queued_ingestion": "Use queued ingestion"
},
"data_description": {
"cluster_ingest_uri": "Ingest-URI of the cluster",
"cluster_ingest_uri": "Ingestion URI of the cluster",
"use_queued_ingestion": "Must be enabled when using ADX free cluster"
}
}

View File

@@ -2,26 +2,26 @@
"config": {
"step": {
"user": {
"title": "Set up your Azure Event Hub integration",
"title": "Set up Azure Event Hub",
"data": {
"event_hub_instance_name": "Event Hub Instance Name",
"use_connection_string": "Use Connection String"
"event_hub_instance_name": "Event Hub instance name",
"use_connection_string": "Use connection string"
}
},
"conn_string": {
"title": "Connection String method",
"title": "Connection string method",
"description": "Please enter the connection string for: {event_hub_instance_name}",
"data": {
"event_hub_connection_string": "Event Hub Connection String"
"event_hub_connection_string": "Event Hub connection string"
}
},
"sas": {
"title": "SAS Credentials method",
"title": "SAS credentials method",
"description": "Please enter the SAS (shared access signature) credentials for: {event_hub_instance_name}",
"data": {
"event_hub_namespace": "Event Hub Namespace",
"event_hub_sas_policy": "Event Hub SAS Policy",
"event_hub_sas_key": "Event Hub SAS Key"
"event_hub_namespace": "Event Hub namespace",
"event_hub_sas_policy": "Event Hub SAS policy",
"event_hub_sas_key": "Event Hub SAS key"
}
}
},
@@ -38,7 +38,7 @@
"options": {
"step": {
"init": {
"title": "Options for the Azure Event Hub.",
"title": "Options for Azure Event Hub.",
"data": {
"send_interval": "Interval between sending batches to the hub."
}

View File

@@ -35,7 +35,6 @@ from .websocket import async_register_websocket_handlers
__all__ = [
"AddonInfo",
"AgentBackup",
"ManagerBackup",
"BackupAgent",
"BackupAgentError",
"BackupAgentPlatformProtocol",
@@ -46,6 +45,7 @@ __all__ = [
"Folder",
"IncorrectPasswordError",
"LocalBackupAgent",
"ManagerBackup",
"NewBackup",
"WrittenBackup",
]

View File

@@ -7,6 +7,7 @@ from collections.abc import Callable
from dataclasses import dataclass, field, replace
from datetime import datetime, timedelta
from enum import StrEnum
import random
from typing import TYPE_CHECKING, Self, TypedDict
from cronsim import CronSim
@@ -28,6 +29,10 @@ if TYPE_CHECKING:
CRON_PATTERN_DAILY = "45 4 * * *"
CRON_PATTERN_WEEKLY = "45 4 * * {}"
# Randomize the start time of the backup by up to 60 minutes to avoid
# all backups running at the same time.
BACKUP_START_TIME_JITTER = 60 * 60
class StoredBackupConfig(TypedDict):
"""Represent the stored backup config."""
@@ -329,6 +334,8 @@ class BackupSchedule:
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected error creating automatic backup")
next_time += timedelta(seconds=random.randint(0, BACKUP_START_TIME_JITTER))
LOGGER.debug("Scheduling next automatic backup at %s", next_time)
manager.remove_next_backup_event = async_track_point_in_time(
manager.hass, _create_backup, next_time
)

View File

@@ -4,18 +4,23 @@ from __future__ import annotations
import asyncio
from http import HTTPStatus
from typing import cast
import threading
from typing import IO, cast
from aiohttp import BodyPartReader
from aiohttp.hdrs import CONTENT_DISPOSITION
from aiohttp.web import FileResponse, Request, Response, StreamResponse
from multidict import istr
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import slugify
from . import util
from .agent import BackupAgent
from .const import DATA_MANAGER
from .manager import BackupManager
@callback
@@ -43,8 +48,13 @@ class DownloadBackupView(HomeAssistantView):
agent_id = request.query.getone("agent_id")
except KeyError:
return Response(status=HTTPStatus.BAD_REQUEST)
try:
password = request.query.getone("password")
except KeyError:
password = None
manager = request.app[KEY_HASS].data[DATA_MANAGER]
hass = request.app[KEY_HASS]
manager = hass.data[DATA_MANAGER]
if agent_id not in manager.backup_agents:
return Response(status=HTTPStatus.BAD_REQUEST)
agent = manager.backup_agents[agent_id]
@@ -58,6 +68,24 @@ class DownloadBackupView(HomeAssistantView):
headers = {
CONTENT_DISPOSITION: f"attachment; filename={slugify(backup.name)}.tar"
}
if not password:
return await self._send_backup_no_password(
request, headers, backup_id, agent_id, agent, manager
)
return await self._send_backup_with_password(
hass, request, headers, backup_id, agent_id, password, agent, manager
)
async def _send_backup_no_password(
self,
request: Request,
headers: dict[istr, str],
backup_id: str,
agent_id: str,
agent: BackupAgent,
manager: BackupManager,
) -> StreamResponse | FileResponse | Response:
if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
@@ -70,6 +98,50 @@ class DownloadBackupView(HomeAssistantView):
await response.write(chunk)
return response
async def _send_backup_with_password(
self,
hass: HomeAssistant,
request: Request,
headers: dict[istr, str],
backup_id: str,
agent_id: str,
password: str,
agent: BackupAgent,
manager: BackupManager,
) -> StreamResponse | FileResponse | Response:
reader: IO[bytes]
if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
try:
reader = await hass.async_add_executor_job(open, path.as_posix(), "rb")
except FileNotFoundError:
return Response(status=HTTPStatus.NOT_FOUND)
else:
stream = await agent.async_download_backup(backup_id)
reader = cast(IO[bytes], util.AsyncIteratorReader(hass, stream))
worker_done_event = asyncio.Event()
def on_done() -> None:
"""Call by the worker thread when it's done."""
hass.loop.call_soon_threadsafe(worker_done_event.set)
stream = util.AsyncIteratorWriter(hass)
worker = threading.Thread(
target=util.decrypt_backup, args=[reader, stream, password, on_done]
)
try:
worker.start()
response = StreamResponse(status=HTTPStatus.OK, headers=headers)
await response.prepare(request)
async for chunk in stream:
await response.write(chunk)
return response
finally:
reader.close()
await worker_done_event.wait()
class UploadBackupView(HomeAssistantView):
"""Generate backup view."""

View File

@@ -10,11 +10,11 @@ from enum import StrEnum
import hashlib
import io
import json
from pathlib import Path
from pathlib import Path, PurePath
import shutil
import tarfile
import time
from typing import TYPE_CHECKING, Any, Protocol, TypedDict
from typing import IO, TYPE_CHECKING, Any, Protocol, TypedDict, cast
import aiohttp
from securetar import SecureTarFile, atomic_contents_add
@@ -31,6 +31,7 @@ from homeassistant.helpers import (
from homeassistant.helpers.json import json_bytes
from homeassistant.util import dt as dt_util
from . import util as backup_util
from .agent import (
BackupAgent,
BackupAgentError,
@@ -48,7 +49,13 @@ from .const import (
)
from .models import AgentBackup, BackupManagerError, Folder
from .store import BackupStore
from .util import make_backup_dir, read_backup, validate_password
from .util import (
AsyncIteratorReader,
make_backup_dir,
read_backup,
validate_password,
validate_password_stream,
)
@dataclass(frozen=True, kw_only=True, slots=True)
@@ -248,6 +255,14 @@ class BackupReaderWriterError(HomeAssistantError):
class IncorrectPasswordError(BackupReaderWriterError):
"""Raised when the password is incorrect."""
_message = "The password provided is incorrect."
class DecryptOnDowloadNotSupported(BackupManagerError):
"""Raised when on-the-fly decryption is not supported."""
_message = "On-the-fly decryption is not supported for this backup."
class BackupManager:
"""Define the format that backup managers can have."""
@@ -430,17 +445,21 @@ class BackupManager:
return_exceptions=True,
)
for idx, result in enumerate(sync_backup_results):
agent_id = agent_ids[idx]
if isinstance(result, BackupReaderWriterError):
# writer errors will affect all agents
# no point in continuing
raise BackupManagerError(str(result)) from result
if isinstance(result, BackupAgentError):
agent_errors[agent_ids[idx]] = result
agent_errors[agent_id] = result
LOGGER.error("Upload failed for %s: %s", agent_id, result)
continue
if isinstance(result, Exception):
# trap bugs from agents
agent_errors[agent_ids[idx]] = result
LOGGER.error("Unexpected error: %s", result, exc_info=result)
agent_errors[agent_id] = result
LOGGER.error(
"Unexpected error for %s: %s", agent_id, result, exc_info=result
)
continue
if isinstance(result, BaseException):
raise result
@@ -752,7 +771,7 @@ class BackupManager:
backup_name = (
name
or f"{"Automatic" if with_automatic_settings else "Custom"} backup {HAVERSION}"
or f"{'Automatic' if with_automatic_settings else 'Custom'} backup {HAVERSION}"
)
try:
@@ -800,12 +819,10 @@ class BackupManager:
"""Finish a backup."""
if TYPE_CHECKING:
assert self._backup_task is not None
backup_success = False
try:
written_backup = await self._backup_task
except Exception as err:
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED)
)
if with_automatic_settings:
self._update_issue_backup_failed()
@@ -831,33 +848,15 @@ class BackupManager:
agent_ids=agent_ids,
open_stream=written_backup.open_stream,
)
except BaseException:
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED)
)
raise # manager or unexpected error
finally:
try:
await written_backup.release_stream()
except Exception:
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED)
)
raise
await written_backup.release_stream()
self.known_backups.add(written_backup.backup, agent_errors)
if agent_errors:
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED)
)
else:
if not agent_errors:
if with_automatic_settings:
# create backup was successful, update last_completed_automatic_backup
self.config.data.last_completed_automatic_backup = dt_util.now()
self.store.save()
self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.COMPLETED)
)
backup_success = True
if with_automatic_settings:
self._update_issue_after_agent_upload(agent_errors)
@@ -868,6 +867,14 @@ class BackupManager:
finally:
self._backup_task = None
self._backup_finish_task = None
self.async_on_backup_event(
CreateBackupEvent(
stage=None,
state=CreateBackupState.COMPLETED
if backup_success
else CreateBackupState.FAILED,
)
)
self.async_on_backup_event(IdleEvent())
async def async_restore_backup(
@@ -998,6 +1005,41 @@ class BackupManager:
translation_placeholders={"failed_agents": ", ".join(agent_errors)},
)
async def async_can_decrypt_on_download(
self,
backup_id: str,
*,
agent_id: str,
password: str | None,
) -> None:
"""Check if we are able to decrypt the backup on download."""
try:
agent = self.backup_agents[agent_id]
except KeyError as err:
raise BackupManagerError(f"Invalid agent selected: {agent_id}") from err
if not await agent.async_get_backup(backup_id):
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
)
reader: IO[bytes]
if agent_id in self.local_backup_agents:
local_agent = self.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
reader = await self.hass.async_add_executor_job(open, path.as_posix(), "rb")
else:
backup_stream = await agent.async_download_backup(backup_id)
reader = cast(IO[bytes], AsyncIteratorReader(self.hass, backup_stream))
try:
validate_password_stream(reader, password)
except backup_util.IncorrectPassword as err:
raise IncorrectPasswordError from err
except backup_util.UnsupportedSecureTarVersion as err:
raise DecryptOnDowloadNotSupported from err
except backup_util.DecryptError as err:
raise BackupManagerError(str(err)) from err
finally:
reader.close()
class KnownBackups:
"""Track known backups."""
@@ -1242,6 +1284,17 @@ class CoreBackupReaderWriter(BackupReaderWriter):
if not database_included:
excludes = excludes + EXCLUDE_DATABASE_FROM_BACKUP
def is_excluded_by_filter(path: PurePath) -> bool:
"""Filter to filter excludes."""
for exclude in excludes:
if not path.match(exclude):
continue
LOGGER.debug("Ignoring %s because of %s", path, exclude)
return True
return False
outer_secure_tarfile = SecureTarFile(
tar_file_path, "w", gzip=False, bufsize=BUF_SIZE
)
@@ -1260,7 +1313,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
atomic_contents_add(
tar_file=core_tar,
origin_path=Path(self._hass.config.path()),
excludes=excludes,
file_filter=is_excluded_by_filter,
arcname="data",
)
return (tar_file_path, tar_file_path.stat().st_size)
@@ -1369,7 +1422,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
validate_password, path, password
)
if not password_valid:
raise IncorrectPasswordError("The password provided is incorrect.")
raise IncorrectPasswordError
def _write_restore_file() -> None:
"""Write the restore file."""
@@ -1387,7 +1440,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
)
await self._hass.async_add_executor_job(_write_restore_file)
await self._hass.services.async_call("homeassistant", "restart", {})
await self._hass.services.async_call("homeassistant", "restart", blocking=True)
def _generate_backup_id(date: str, name: str) -> str:

View File

@@ -8,5 +8,5 @@
"integration_type": "system",
"iot_class": "calculated",
"quality_scale": "internal",
"requirements": ["cronsim==2.6", "securetar==2024.11.0"]
"requirements": ["cronsim==2.6", "securetar==2025.1.3"]
}

View File

@@ -3,22 +3,51 @@
from __future__ import annotations
import asyncio
from pathlib import Path
from collections.abc import AsyncIterator, Callable
import copy
from io import BytesIO
import json
from pathlib import Path, PurePath
from queue import SimpleQueue
import tarfile
from typing import cast
from typing import IO, Self, cast
import aiohttp
from securetar import SecureTarFile
from securetar import SecureTarError, SecureTarFile, SecureTarReadError
from homeassistant.backup_restore import password_to_key
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.json import JsonObjectType, json_loads_object
from .const import BUF_SIZE, LOGGER
from .models import AddonInfo, AgentBackup, Folder
class DecryptError(HomeAssistantError):
"""Error during decryption."""
_message = "Unexpected error during decryption."
class UnsupportedSecureTarVersion(DecryptError):
"""Unsupported securetar version."""
_message = "Unsupported securetar version."
class IncorrectPassword(DecryptError):
"""Invalid password or corrupted backup."""
_message = "Invalid password or corrupted backup."
class BackupEmpty(DecryptError):
"""No tar files found in the backup."""
_message = "No tar files found in the backup."
def make_backup_dir(path: Path) -> None:
"""Create a backup directory if it does not exist."""
path.mkdir(exist_ok=True)
@@ -106,6 +135,159 @@ def validate_password(path: Path, password: str | None) -> bool:
return False
class AsyncIteratorReader:
"""Wrap an AsyncIterator."""
def __init__(self, hass: HomeAssistant, stream: AsyncIterator[bytes]) -> None:
"""Initialize the wrapper."""
self._hass = hass
self._stream = stream
self._buffer: bytes | None = None
self._pos: int = 0
async def _next(self) -> bytes | None:
"""Get the next chunk from the iterator."""
return await anext(self._stream, None)
def read(self, n: int = -1, /) -> bytes:
"""Read data from the iterator."""
result = bytearray()
while n < 0 or len(result) < n:
if not self._buffer:
self._buffer = asyncio.run_coroutine_threadsafe(
self._next(), self._hass.loop
).result()
self._pos = 0
if not self._buffer:
# The stream is exhausted
break
chunk = self._buffer[self._pos : self._pos + n]
result.extend(chunk)
n -= len(chunk)
self._pos += len(chunk)
if self._pos == len(self._buffer):
self._buffer = None
return bytes(result)
def close(self) -> None:
"""Close the iterator."""
class AsyncIteratorWriter:
"""Wrap an AsyncIterator."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the wrapper."""
self._hass = hass
self._queue: asyncio.Queue[bytes | None] = asyncio.Queue(maxsize=1)
def __aiter__(self) -> Self:
"""Return the iterator."""
return self
async def __anext__(self) -> bytes:
"""Get the next chunk from the iterator."""
if data := await self._queue.get():
return data
raise StopAsyncIteration
def write(self, s: bytes, /) -> int:
"""Write data to the iterator."""
asyncio.run_coroutine_threadsafe(self._queue.put(s), self._hass.loop).result()
return len(s)
def validate_password_stream(
input_stream: IO[bytes],
password: str | None,
) -> None:
"""Decrypt a backup."""
with (
tarfile.open(fileobj=input_stream, mode="r|", bufsize=BUF_SIZE) as input_tar,
):
for obj in input_tar:
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
continue
istf = SecureTarFile(
None, # Not used
gzip=False,
key=password_to_key(password) if password is not None else None,
mode="r",
fileobj=input_tar.extractfile(obj),
)
with istf.decrypt(obj) as decrypted:
if istf.securetar_header.plaintext_size is None:
raise UnsupportedSecureTarVersion
try:
decrypted.read(1) # Read a single byte to trigger the decryption
except SecureTarReadError as err:
raise IncorrectPassword from err
return
raise BackupEmpty
def decrypt_backup(
input_stream: IO[bytes],
output_stream: IO[bytes],
password: str | None,
on_done: Callable[[], None],
) -> None:
"""Decrypt a backup."""
try:
with (
tarfile.open(
fileobj=input_stream, mode="r|", bufsize=BUF_SIZE
) as input_tar,
tarfile.open(
fileobj=output_stream, mode="w|", bufsize=BUF_SIZE
) as output_tar,
):
_decrypt_backup(input_tar, output_tar, password)
except (DecryptError, SecureTarError, tarfile.TarError) as err:
LOGGER.warning("Error decrypting backup: %s", err)
finally:
output_stream.write(b"") # Write an empty chunk to signal the end of the stream
on_done()
def _decrypt_backup(
input_tar: tarfile.TarFile,
output_tar: tarfile.TarFile,
password: str | None,
) -> None:
"""Decrypt a backup."""
for obj in input_tar:
# We compare with PurePath to avoid issues with different path separators,
# for example when backup.json is added as "./backup.json"
if PurePath(obj.name) == PurePath("backup.json"):
# Rewrite the backup.json file to indicate that the backup is decrypted
if not (reader := input_tar.extractfile(obj)):
raise DecryptError
metadata = json_loads_object(reader.read())
metadata["protected"] = False
updated_metadata_b = json.dumps(metadata).encode()
metadata_obj = copy.deepcopy(obj)
metadata_obj.size = len(updated_metadata_b)
output_tar.addfile(metadata_obj, BytesIO(updated_metadata_b))
continue
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
output_tar.addfile(obj, input_tar.extractfile(obj))
continue
istf = SecureTarFile(
None, # Not used
gzip=False,
key=password_to_key(password) if password is not None else None,
mode="r",
fileobj=input_tar.extractfile(obj),
)
with istf.decrypt(obj) as decrypted:
if (plaintext_size := istf.securetar_header.plaintext_size) is None:
raise UnsupportedSecureTarVersion
decrypted_obj = copy.deepcopy(obj)
decrypted_obj.size = plaintext_size
output_tar.addfile(decrypted_obj, decrypted)
async def receive_file(
hass: HomeAssistant, contents: aiohttp.BodyPartReader, path: Path
) -> None:

View File

@@ -9,7 +9,11 @@ from homeassistant.core import HomeAssistant, callback
from .config import ScheduleState
from .const import DATA_MANAGER, LOGGER
from .manager import IncorrectPasswordError, ManagerStateEvent
from .manager import (
DecryptOnDowloadNotSupported,
IncorrectPasswordError,
ManagerStateEvent,
)
from .models import Folder
@@ -24,6 +28,7 @@ def async_register_websocket_handlers(hass: HomeAssistant, with_hassio: bool) ->
websocket_api.async_register_command(hass, handle_details)
websocket_api.async_register_command(hass, handle_info)
websocket_api.async_register_command(hass, handle_can_decrypt_on_download)
websocket_api.async_register_command(hass, handle_create)
websocket_api.async_register_command(hass, handle_create_with_automatic_settings)
websocket_api.async_register_command(hass, handle_delete)
@@ -147,6 +152,38 @@ async def handle_restore(
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required("type"): "backup/can_decrypt_on_download",
vol.Required("backup_id"): str,
vol.Required("agent_id"): str,
vol.Required("password"): str,
}
)
@websocket_api.async_response
async def handle_can_decrypt_on_download(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Check if the supplied password is correct."""
try:
await hass.data[DATA_MANAGER].async_can_decrypt_on_download(
msg["backup_id"],
agent_id=msg["agent_id"],
password=msg.get("password"),
)
except IncorrectPasswordError:
connection.send_error(msg["id"], "password_incorrect", "Incorrect password")
except DecryptOnDowloadNotSupported:
connection.send_error(
msg["id"], "decrypt_not_supported", "Decrypt on download not supported"
)
else:
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command(
{

View File

@@ -10,9 +10,9 @@ from aiobafi6 import Device, Service
from aiobafi6.discovery import PORT
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_IP_ADDRESS
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN, RUN_TIMEOUT
from .models import BAFDiscovery
@@ -44,7 +44,7 @@ class BAFFlowHandler(ConfigFlow, domain=DOMAIN):
self.discovery: BAFDiscovery | None = None
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
if discovery_info.ip_address.version == 6:

View File

@@ -20,7 +20,7 @@ class BalboaEntity(Entity):
"""Initialize the control."""
mac = client.mac_address
model = client.model
self._attr_unique_id = f'{model}-{key}-{mac.replace(":","")[-6:]}'
self._attr_unique_id = f"{model}-{key}-{mac.replace(':', '')[-6:]}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, mac)},
name=model,

View File

@@ -34,7 +34,7 @@ class BangOlufsenData:
type BangOlufsenConfigEntry = ConfigEntry[BangOlufsenData]
PLATFORMS = [Platform.MEDIA_PLAYER]
PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER]
async def async_setup_entry(hass: HomeAssistant, entry: BangOlufsenConfigEntry) -> bool:

View File

@@ -10,10 +10,10 @@ from mozart_api.exceptions import ApiException
from mozart_api.mozart_client import MozartClient
import voluptuous as vol
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_MODEL
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from homeassistant.util.ssl import get_default_context
from .const import (

View File

@@ -79,6 +79,7 @@ class WebsocketNotification(StrEnum):
"""Enum for WebSocket notification types."""
ACTIVE_LISTENING_MODE = "active_listening_mode"
BUTTON = "button"
PLAYBACK_ERROR = "playback_error"
PLAYBACK_METADATA = "playback_metadata"
PLAYBACK_PROGRESS = "playback_progress"
@@ -203,14 +204,60 @@ FALLBACK_SOURCES: Final[SourceArray] = SourceArray(
),
]
)
# Map for storing compatibility of devices.
MODEL_SUPPORT_DEVICE_BUTTONS: Final[str] = "device_buttons"
MODEL_SUPPORT_MAP = {
MODEL_SUPPORT_DEVICE_BUTTONS: (
BangOlufsenModel.BEOLAB_8,
BangOlufsenModel.BEOLAB_28,
BangOlufsenModel.BEOSOUND_2,
BangOlufsenModel.BEOSOUND_A5,
BangOlufsenModel.BEOSOUND_A9,
BangOlufsenModel.BEOSOUND_BALANCE,
BangOlufsenModel.BEOSOUND_EMERGE,
BangOlufsenModel.BEOSOUND_LEVEL,
BangOlufsenModel.BEOSOUND_THEATRE,
)
}
# Device events
BANG_OLUFSEN_WEBSOCKET_EVENT: Final[str] = f"{DOMAIN}_websocket_event"
# Dict used to translate native Bang & Olufsen event names to string.json compatible ones
EVENT_TRANSLATION_MAP: dict[str, str] = {
"shortPress (Release)": "short_press_release",
"longPress (Timeout)": "long_press_timeout",
"longPress (Release)": "long_press_release",
"veryLongPress (Timeout)": "very_long_press_timeout",
"veryLongPress (Release)": "very_long_press_release",
}
CONNECTION_STATUS: Final[str] = "CONNECTION_STATUS"
DEVICE_BUTTONS: Final[list[str]] = [
"Bluetooth",
"Microphone",
"Next",
"PlayPause",
"Preset1",
"Preset2",
"Preset3",
"Preset4",
"Previous",
"Volume",
]
DEVICE_BUTTON_EVENTS: Final[list[str]] = [
"short_press_release",
"long_press_timeout",
"long_press_release",
"very_long_press_timeout",
"very_long_press_release",
]
# Beolink Converter NL/ML sources need to be transformed to upper case
BEOLINK_JOIN_SOURCES_TO_UPPER = (
"aux_a",

View File

@@ -0,0 +1,76 @@
"""Event entities for the Bang & Olufsen integration."""
from __future__ import annotations
from homeassistant.components.event import EventDeviceClass, EventEntity
from homeassistant.const import CONF_MODEL
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BangOlufsenConfigEntry
from .const import (
CONNECTION_STATUS,
DEVICE_BUTTON_EVENTS,
DEVICE_BUTTONS,
MODEL_SUPPORT_DEVICE_BUTTONS,
MODEL_SUPPORT_MAP,
WebsocketNotification,
)
from .entity import BangOlufsenEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: BangOlufsenConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Sensor entities from config entry."""
if config_entry.data[CONF_MODEL] in MODEL_SUPPORT_MAP[MODEL_SUPPORT_DEVICE_BUTTONS]:
async_add_entities(
BangOlufsenButtonEvent(config_entry, button_type)
for button_type in DEVICE_BUTTONS
)
class BangOlufsenButtonEvent(BangOlufsenEntity, EventEntity):
"""Event class for Button events."""
_attr_device_class = EventDeviceClass.BUTTON
_attr_entity_registry_enabled_default = False
_attr_event_types = DEVICE_BUTTON_EVENTS
def __init__(self, config_entry: BangOlufsenConfigEntry, button_type: str) -> None:
"""Initialize Button."""
super().__init__(config_entry, config_entry.runtime_data.client)
self._attr_unique_id = f"{self._unique_id}_{button_type}"
# Make the native button name Home Assistant compatible
self._attr_translation_key = button_type.lower()
self._button_type = button_type
async def async_added_to_hass(self) -> None:
"""Listen to WebSocket button events."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{self._unique_id}_{CONNECTION_STATUS}",
self._async_update_connection_state,
)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{self._unique_id}_{WebsocketNotification.BUTTON}_{self._button_type}",
self._async_handle_event,
)
)
@callback
def _async_handle_event(self, event: str) -> None:
"""Handle event."""
self._trigger_event(event)
self.async_write_ha_state()

View File

@@ -1,7 +1,12 @@
{
"common": {
"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.",
"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."
"long_press_release": "Release of long press",
"long_press_timeout": "Long press",
"short_press_release": "Release of short press",
"very_long_press_release": "Release of very long press",
"very_long_press_timeout": "Very long press"
},
"config": {
"error": {
@@ -29,6 +34,150 @@
}
}
},
"entity": {
"event": {
"bluetooth": {
"name": "Bluetooth",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"microphone": {
"name": "Microphone",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"next": {
"name": "Next",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"playpause": {
"name": "Play / Pause",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"preset1": {
"name": "Favourite 1",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"preset2": {
"name": "Favourite 2",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"preset3": {
"name": "Favourite 3",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"preset4": {
"name": "Favourite 4",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"previous": {
"name": "Previous",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
},
"volume": {
"name": "Volume",
"state_attributes": {
"event_type": {
"state": {
"short_press_release": "[%key:component::bang_olufsen::common::short_press_release%]",
"long_press_timeout": "[%key:component::bang_olufsen::common::long_press_timeout%]",
"long_press_release": "[%key:component::bang_olufsen::common::long_press_release%]",
"very_long_press_timeout": "[%key:component::bang_olufsen::common::very_long_press_timeout%]",
"very_long_press_release": "[%key:component::bang_olufsen::common::very_long_press_release%]"
}
}
}
}
}
},
"selector": {
"source_ids": {
"options": {

View File

@@ -3,8 +3,10 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from mozart_api.models import (
ButtonEvent,
ListeningModeProps,
PlaybackContentMetadata,
PlaybackError,
@@ -26,6 +28,7 @@ from homeassistant.util.enum import try_parse_enum
from .const import (
BANG_OLUFSEN_WEBSOCKET_EVENT,
CONNECTION_STATUS,
EVENT_TRANSLATION_MAP,
WebsocketNotification,
)
from .entity import BangOlufsenBase
@@ -54,6 +57,8 @@ class BangOlufsenWebsocket(BangOlufsenBase):
self._client.get_active_listening_mode_notifications(
self.on_active_listening_mode
)
self._client.get_button_notifications(self.on_button_notification)
self._client.get_playback_error_notifications(
self.on_playback_error_notification
)
@@ -104,6 +109,19 @@ class BangOlufsenWebsocket(BangOlufsenBase):
notification,
)
def on_button_notification(self, notification: ButtonEvent) -> None:
"""Send button dispatch."""
# State is expected to always be available.
if TYPE_CHECKING:
assert notification.state
# Send to event entity
async_dispatcher_send(
self.hass,
f"{self._unique_id}_{WebsocketNotification.BUTTON}_{notification.button}",
EVENT_TRANSLATION_MAP[notification.state],
)
def on_notification_notification(
self, notification: WebsocketNotificationTag
) -> None:

View File

@@ -131,7 +131,10 @@ def _no_overlapping(configs: list[dict]) -> list[dict]:
for i, tup in enumerate(intervals):
if len(intervals) > i + 1 and tup.below > intervals[i + 1].above:
raise vol.Invalid(
f"Ranges for bayesian numeric state entities must not overlap, but {ent_id} has overlapping ranges, above:{tup.above}, below:{tup.below} overlaps with above:{intervals[i+1].above}, below:{intervals[i+1].below}."
"Ranges for bayesian numeric state entities must not overlap, "
f"but {ent_id} has overlapping ranges, above:{tup.above}, "
f"below:{tup.below} overlaps with above:{intervals[i + 1].above}, "
f"below:{intervals[i + 1].below}."
)
return configs
@@ -206,7 +209,10 @@ async def async_setup_platform(
broken_observations: list[dict[str, Any]] = []
for observation in observations:
if CONF_P_GIVEN_F not in observation:
text: str = f"{name}/{observation.get(CONF_ENTITY_ID,'')}{observation.get(CONF_VALUE_TEMPLATE,'')}"
text = (
f"{name}/{observation.get(CONF_ENTITY_ID, '')}"
f"{observation.get(CONF_VALUE_TEMPLATE, '')}"
)
raise_no_prob_given_false(hass, text)
_LOGGER.error("Missing prob_given_false YAML entry for %s", text)
broken_observations.append(observation)

View File

@@ -15,10 +15,10 @@ from blebox_uniapi.error import (
from blebox_uniapi.session import ApiHost
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from . import get_maybe_authenticated_session
from .const import (
@@ -84,7 +84,7 @@ class BleBoxConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
hass = self.hass

View File

@@ -5,7 +5,7 @@
"data": {
"api_token": "[%key:common::config_flow::data::api_token%]"
},
"description": "Enter your Blue Current api token",
"description": "Enter your Blue Current API token",
"title": "Authentication"
}
},
@@ -19,7 +19,7 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"wrong_account": "Wrong account: Please authenticate with the api key for {email}."
"wrong_account": "Wrong account: Please authenticate with the API token for {email}."
}
},
"entity": {

View File

@@ -136,7 +136,7 @@ def _extract_blueprint_from_community_topic(
)
return ImportedBlueprint(
f'{post["username"]}/{topic["slug"]}', block_content, blueprint
f"{post['username']}/{topic['slug']}", block_content, blueprint
)
@@ -173,8 +173,7 @@ async def fetch_blueprint_from_github_url(
parsed_import_url = yarl.URL(import_url)
suggested_filename = f"{parsed_import_url.parts[1]}/{parsed_import_url.parts[-1]}"
if suggested_filename.endswith(".yaml"):
suggested_filename = suggested_filename[:-5]
suggested_filename = suggested_filename.removesuffix(".yaml")
return ImportedBlueprint(suggested_filename, raw_yaml, blueprint)

View File

@@ -14,10 +14,13 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
from .coordinator import BluesoundCoordinator
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
PLATFORMS = [Platform.MEDIA_PLAYER]
PLATFORMS = [
Platform.MEDIA_PLAYER,
]
@dataclass
@@ -26,6 +29,7 @@ class BluesoundRuntimeData:
player: Player
sync_status: SyncStatus
coordinator: BluesoundCoordinator
type BluesoundConfigEntry = ConfigEntry[BluesoundRuntimeData]
@@ -33,9 +37,6 @@ type BluesoundConfigEntry = ConfigEntry[BluesoundRuntimeData]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Bluesound."""
if DOMAIN not in hass.data:
hass.data[DOMAIN] = []
return True
@@ -46,13 +47,16 @@ async def async_setup_entry(
host = config_entry.data[CONF_HOST]
port = config_entry.data[CONF_PORT]
session = async_get_clientsession(hass)
async with Player(host, port, session=session, default_timeout=10) as player:
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError as ex:
raise ConfigEntryNotReady(f"Error connecting to {host}:{port}") from ex
player = Player(host, port, session=session, default_timeout=10)
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError as ex:
raise ConfigEntryNotReady(f"Error connecting to {host}:{port}") from ex
config_entry.runtime_data = BluesoundRuntimeData(player, sync_status)
coordinator = BluesoundCoordinator(hass, player, sync_status)
await coordinator.async_config_entry_first_refresh()
config_entry.runtime_data = BluesoundRuntimeData(player, sync_status, coordinator)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

View File

@@ -7,10 +7,10 @@ from pyblu import Player, SyncStatus
from pyblu.errors import PlayerUnreachableError
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN
from .media_player import DEFAULT_PORT
@@ -71,29 +71,8 @@ class BluesoundConfigFlow(ConfigFlow, domain=DOMAIN):
),
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Import bluesound config entry from configuration.yaml."""
session = async_get_clientsession(self.hass)
async with Player(
import_data[CONF_HOST], import_data[CONF_PORT], session=session
) as player:
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError:
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(
format_unique_id(sync_status.mac, import_data[CONF_PORT])
)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=sync_status.name,
data=import_data,
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle a flow initialized by zeroconf discovery."""
if discovery_info.port is not None:

View File

@@ -0,0 +1,160 @@
"""Define a base coordinator for Bluesound entities."""
from __future__ import annotations
import asyncio
from collections.abc import Callable, Coroutine
import contextlib
from dataclasses import dataclass, replace
from datetime import timedelta
import logging
from pyblu import Input, Player, Preset, Status, SyncStatus
from pyblu.errors import PlayerUnreachableError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
NODE_OFFLINE_CHECK_TIMEOUT = timedelta(minutes=3)
PRESET_AND_INPUTS_INTERVAL = timedelta(minutes=15)
@dataclass
class BluesoundData:
"""Define a class to hold Bluesound data."""
sync_status: SyncStatus
status: Status
presets: list[Preset]
inputs: list[Input]
def cancel_task(task: asyncio.Task) -> Callable[[], Coroutine[None, None, None]]:
"""Cancel a task."""
async def _cancel_task() -> None:
task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await task
return _cancel_task
class BluesoundCoordinator(DataUpdateCoordinator[BluesoundData]):
"""Define an object to hold Bluesound data."""
def __init__(
self, hass: HomeAssistant, player: Player, sync_status: SyncStatus
) -> None:
"""Initialize."""
self.player = player
self._inital_sync_status = sync_status
super().__init__(
hass,
logger=_LOGGER,
name=sync_status.name,
)
async def _async_setup(self) -> None:
assert self.config_entry is not None
preset = await self.player.presets()
inputs = await self.player.inputs()
status = await self.player.status()
self.async_set_updated_data(
BluesoundData(
sync_status=self._inital_sync_status,
status=status,
presets=preset,
inputs=inputs,
)
)
status_loop_task = self.hass.async_create_background_task(
self._poll_status_loop(),
name=f"bluesound.poll_status_loop_{self.data.sync_status.id}",
)
self.config_entry.async_on_unload(cancel_task(status_loop_task))
sync_status_loop_task = self.hass.async_create_background_task(
self._poll_sync_status_loop(),
name=f"bluesound.poll_sync_status_loop_{self.data.sync_status.id}",
)
self.config_entry.async_on_unload(cancel_task(sync_status_loop_task))
presets_and_inputs_loop_task = self.hass.async_create_background_task(
self._poll_presets_and_inputs_loop(),
name=f"bluesound.poll_presets_and_inputs_loop_{self.data.sync_status.id}",
)
self.config_entry.async_on_unload(cancel_task(presets_and_inputs_loop_task))
async def _async_update_data(self) -> BluesoundData:
return self.data
async def _poll_presets_and_inputs_loop(self) -> None:
while True:
await asyncio.sleep(PRESET_AND_INPUTS_INTERVAL.total_seconds())
try:
preset = await self.player.presets()
inputs = await self.player.inputs()
self.async_set_updated_data(
replace(
self.data,
presets=preset,
inputs=inputs,
)
)
except PlayerUnreachableError as ex:
self.async_set_update_error(ex)
except asyncio.CancelledError:
return
except Exception as ex: # noqa: BLE001 - this loop should never stop
self.async_set_update_error(ex)
async def _poll_status_loop(self) -> None:
"""Loop which polls the status of the player."""
while True:
try:
status = await self.player.status(
etag=self.data.status.etag, poll_timeout=120, timeout=125
)
self.async_set_updated_data(
replace(
self.data,
status=status,
)
)
except PlayerUnreachableError as ex:
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
except asyncio.CancelledError:
return
except Exception as ex: # noqa: BLE001 - this loop should never stop
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
async def _poll_sync_status_loop(self) -> None:
"""Loop which polls the sync status of the player."""
while True:
try:
sync_status = await self.player.sync_status(
etag=self.data.sync_status.etag, poll_timeout=120, timeout=125
)
self.async_set_updated_data(
replace(
self.data,
sync_status=sync_status,
)
)
except PlayerUnreachableError as ex:
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
except asyncio.CancelledError:
raise
except Exception as ex: # noqa: BLE001 - this loop should never stop
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())

View File

@@ -2,20 +2,16 @@
from __future__ import annotations
import asyncio
from asyncio import CancelledError, Task
from contextlib import suppress
from asyncio import Task
from datetime import datetime, timedelta
import logging
from typing import TYPE_CHECKING, Any
from pyblu import Input, Player, Preset, Status, SyncStatus
from pyblu.errors import PlayerUnreachableError
import voluptuous as vol
from homeassistant.components import media_source
from homeassistant.components.media_player import (
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
BrowseMedia,
MediaPlayerEntity,
MediaPlayerEntityFeature,
@@ -23,16 +19,10 @@ from homeassistant.components.media_player import (
MediaType,
async_process_play_media_url,
)
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import CONF_HOST, CONF_HOSTS, CONF_NAME, CONF_PORT
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import (
config_validation as cv,
entity_platform,
issue_registry as ir,
)
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.device_registry import (
CONNECTION_NETWORK_MAC,
DeviceInfo,
@@ -43,10 +33,11 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
)
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
import homeassistant.util.dt as dt_util
from .const import ATTR_BLUESOUND_GROUP, ATTR_MASTER, DOMAIN, INTEGRATION_TITLE
from .const import ATTR_BLUESOUND_GROUP, ATTR_MASTER, DOMAIN
from .coordinator import BluesoundCoordinator
from .utils import dispatcher_join_signal, dispatcher_unjoin_signal, format_unique_id
if TYPE_CHECKING:
@@ -64,71 +55,8 @@ SERVICE_JOIN = "join"
SERVICE_SET_TIMER = "set_sleep_timer"
SERVICE_UNJOIN = "unjoin"
NODE_OFFLINE_CHECK_TIMEOUT = 180
NODE_RETRY_INITIATION = timedelta(minutes=3)
SYNC_STATUS_INTERVAL = timedelta(minutes=5)
POLL_TIMEOUT = 120
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOSTS): vol.All(
cv.ensure_list,
[
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
],
)
}
)
async def _async_import(hass: HomeAssistant, config: ConfigType) -> None:
"""Import config entry from configuration.yaml."""
if not hass.config_entries.async_entries(DOMAIN):
# Start import flow
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
)
if (
result["type"] == FlowResultType.ABORT
and result["reason"] == "cannot_connect"
):
ir.async_create_issue(
hass,
DOMAIN,
f"deprecated_yaml_import_issue_{result['reason']}",
breaks_in_ha_version="2025.2.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=ir.IssueSeverity.WARNING,
translation_key=f"deprecated_yaml_import_issue_{result['reason']}",
translation_placeholders={
"domain": DOMAIN,
"integration_title": INTEGRATION_TITLE,
},
)
return
ir.async_create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"deprecated_yaml_{DOMAIN}",
breaks_in_ha_version="2025.2.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_yaml",
translation_placeholders={
"domain": DOMAIN,
"integration_title": INTEGRATION_TITLE,
},
)
async def async_setup_entry(
hass: HomeAssistant,
@@ -137,10 +65,10 @@ async def async_setup_entry(
) -> None:
"""Set up the Bluesound entry."""
bluesound_player = BluesoundPlayer(
config_entry.runtime_data.coordinator,
config_entry.data[CONF_HOST],
config_entry.data[CONF_PORT],
config_entry.runtime_data.player,
config_entry.runtime_data.sync_status,
)
platform = entity_platform.async_get_current_platform()
@@ -155,27 +83,10 @@ async def async_setup_entry(
)
platform.async_register_entity_service(SERVICE_UNJOIN, None, "async_unjoin")
hass.data[DATA_BLUESOUND].append(bluesound_player)
async_add_entities([bluesound_player], update_before_add=True)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None,
) -> None:
"""Trigger import flows."""
hosts = config.get(CONF_HOSTS, [])
for host in hosts:
import_data = {
CONF_HOST: host[CONF_HOST],
CONF_PORT: host.get(CONF_PORT, 11000),
}
hass.async_create_task(_async_import(hass, import_data))
class BluesoundPlayer(MediaPlayerEntity):
class BluesoundPlayer(CoordinatorEntity[BluesoundCoordinator], MediaPlayerEntity):
"""Representation of a Bluesound Player."""
_attr_media_content_type = MediaType.MUSIC
@@ -184,12 +95,15 @@ class BluesoundPlayer(MediaPlayerEntity):
def __init__(
self,
coordinator: BluesoundCoordinator,
host: str,
port: int,
player: Player,
sync_status: SyncStatus,
) -> None:
"""Initialize the media player."""
super().__init__(coordinator)
sync_status = coordinator.data.sync_status
self.host = host
self.port = port
self._poll_status_loop_task: Task[None] | None = None
@@ -197,15 +111,14 @@ class BluesoundPlayer(MediaPlayerEntity):
self._id = sync_status.id
self._last_status_update: datetime | None = None
self._sync_status = sync_status
self._status: Status | None = None
self._inputs: list[Input] = []
self._presets: list[Preset] = []
self._status: Status = coordinator.data.status
self._inputs: list[Input] = coordinator.data.inputs
self._presets: list[Preset] = coordinator.data.presets
self._group_name: str | None = None
self._group_list: list[str] = []
self._bluesound_device_name = sync_status.name
self._player = player
self._is_leader = False
self._leader: BluesoundPlayer | None = None
self._last_status_update = dt_util.utcnow()
self._attr_unique_id = format_unique_id(sync_status.mac, port)
# there should always be one player with the default port per mac
@@ -228,52 +141,10 @@ class BluesoundPlayer(MediaPlayerEntity):
via_device=(DOMAIN, format_mac(sync_status.mac)),
)
async def _poll_status_loop(self) -> None:
"""Loop which polls the status of the player."""
while True:
try:
await self.async_update_status()
except PlayerUnreachableError:
_LOGGER.error(
"Node %s:%s is offline, retrying later", self.host, self.port
)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
except CancelledError:
_LOGGER.debug(
"Stopping the polling of node %s:%s", self.host, self.port
)
return
except: # noqa: E722 - this loop should never stop
_LOGGER.exception(
"Unexpected error for %s:%s, retrying later", self.host, self.port
)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
async def _poll_sync_status_loop(self) -> None:
"""Loop which polls the sync status of the player."""
while True:
try:
await self.update_sync_status()
except PlayerUnreachableError:
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
except CancelledError:
raise
except: # noqa: E722 - all errors must be caught for this loop
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
async def async_added_to_hass(self) -> None:
"""Start the polling task."""
await super().async_added_to_hass()
self._poll_status_loop_task = self.hass.async_create_background_task(
self._poll_status_loop(),
name=f"bluesound.poll_status_loop_{self.host}:{self.port}",
)
self._poll_sync_status_loop_task = self.hass.async_create_background_task(
self._poll_sync_status_loop(),
name=f"bluesound.poll_sync_status_loop_{self.host}:{self.port}",
)
assert self._sync_status.id is not None
self.async_on_remove(
async_dispatcher_connect(
@@ -294,105 +165,24 @@ class BluesoundPlayer(MediaPlayerEntity):
"""Stop the polling task."""
await super().async_will_remove_from_hass()
assert self._poll_status_loop_task is not None
if self._poll_status_loop_task.cancel():
# the sleeps in _poll_loop will raise CancelledError
with suppress(CancelledError):
await self._poll_status_loop_task
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._sync_status = self.coordinator.data.sync_status
self._status = self.coordinator.data.status
self._inputs = self.coordinator.data.inputs
self._presets = self.coordinator.data.presets
assert self._poll_sync_status_loop_task is not None
if self._poll_sync_status_loop_task.cancel():
# the sleeps in _poll_sync_status_loop will raise CancelledError
with suppress(CancelledError):
await self._poll_sync_status_loop_task
self.hass.data[DATA_BLUESOUND].remove(self)
async def async_update(self) -> None:
"""Update internal status of the entity."""
if not self.available:
return
with suppress(PlayerUnreachableError):
await self.async_update_presets()
await self.async_update_captures()
async def async_update_status(self) -> None:
"""Use the poll session to always get the status of the player."""
etag = None
if self._status is not None:
etag = self._status.etag
try:
status = await self._player.status(
etag=etag, poll_timeout=POLL_TIMEOUT, timeout=POLL_TIMEOUT + 5
)
self._attr_available = True
self._last_status_update = dt_util.utcnow()
self._status = status
self.async_write_ha_state()
except PlayerUnreachableError:
self._attr_available = False
self._last_status_update = None
self._status = None
self.async_write_ha_state()
_LOGGER.error(
"Client connection error, marking %s as offline",
self._bluesound_device_name,
)
raise
async def update_sync_status(self) -> None:
"""Update the internal status."""
etag = None
if self._sync_status:
etag = self._sync_status.etag
sync_status = await self._player.sync_status(
etag=etag, poll_timeout=POLL_TIMEOUT, timeout=POLL_TIMEOUT + 5
)
self._sync_status = sync_status
self._last_status_update = dt_util.utcnow()
self._group_list = self.rebuild_bluesound_group()
if sync_status.leader is not None:
self._is_leader = False
leader_id = f"{sync_status.leader.ip}:{sync_status.leader.port}"
leader_device = [
device
for device in self.hass.data[DATA_BLUESOUND]
if device.id == leader_id
]
if leader_device and leader_id != self.id:
self._leader = leader_device[0]
else:
self._leader = None
_LOGGER.error("Leader not found %s", leader_id)
else:
if self._leader is not None:
self._leader = None
followers = self._sync_status.followers
self._is_leader = followers is not None
self.async_write_ha_state()
async def async_update_captures(self) -> None:
"""Update Capture sources."""
inputs = await self._player.inputs()
self._inputs = inputs
async def async_update_presets(self) -> None:
"""Update Presets."""
presets = await self._player.presets()
self._presets = presets
@property
def state(self) -> MediaPlayerState:
"""Return the state of the device."""
if self._status is None:
if self.available is False:
return MediaPlayerState.OFF
if self.is_grouped and not self.is_leader:
@@ -409,7 +199,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_title(self) -> str | None:
"""Title of current playing media."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
return self._status.name
@@ -417,7 +207,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_artist(self) -> str | None:
"""Artist of current playing media (Music track only)."""
if self._status is None:
if self.available is False:
return None
if self.is_grouped and not self.is_leader:
@@ -428,7 +218,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_album_name(self) -> str | None:
"""Artist of current playing media (Music track only)."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
return self._status.album
@@ -436,7 +226,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_image_url(self) -> str | None:
"""Image url of current playing media."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
url = self._status.image
@@ -451,7 +241,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_position(self) -> int | None:
"""Position of current playing media in seconds."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
mediastate = self.state
@@ -470,7 +260,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_duration(self) -> int | None:
"""Duration of current playing media in seconds."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
duration = self._status.total_seconds
@@ -487,16 +277,11 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def volume_level(self) -> float | None:
"""Volume level of the media player (0..1)."""
volume = None
volume = self._status.volume
if self._status is not None:
volume = self._status.volume
if self.is_grouped:
volume = self._sync_status.volume
if volume is None:
return None
return volume / 100
@property
@@ -529,7 +314,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def source_list(self) -> list[str] | None:
"""List of available input sources."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
sources = [x.text for x in self._inputs]
@@ -540,7 +325,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def source(self) -> str | None:
"""Name of the current input source."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
if self._status.input_id is not None:
@@ -557,7 +342,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def supported_features(self) -> MediaPlayerEntityFeature:
"""Flag of media commands that are supported."""
if self._status is None:
if self.available is False:
return MediaPlayerEntityFeature(0)
if self.is_grouped and not self.is_leader:
@@ -659,16 +444,21 @@ class BluesoundPlayer(MediaPlayerEntity):
if self.sync_status.leader is None and self.sync_status.followers is None:
return []
player_entities: list[BluesoundPlayer] = self.hass.data[DATA_BLUESOUND]
config_entries: list[BluesoundConfigEntry] = (
self.hass.config_entries.async_entries(DOMAIN)
)
sync_status_list = [
x.runtime_data.coordinator.data.sync_status for x in config_entries
]
leader_sync_status: SyncStatus | None = None
if self.sync_status.leader is None:
leader_sync_status = self.sync_status
else:
required_id = f"{self.sync_status.leader.ip}:{self.sync_status.leader.port}"
for x in player_entities:
if x.sync_status.id == required_id:
leader_sync_status = x.sync_status
for sync_status in sync_status_list:
if sync_status.id == required_id:
leader_sync_status = sync_status
break
if leader_sync_status is None or leader_sync_status.followers is None:
@@ -676,9 +466,9 @@ class BluesoundPlayer(MediaPlayerEntity):
follower_ids = [f"{x.ip}:{x.port}" for x in leader_sync_status.followers]
follower_names = [
x.sync_status.name
for x in player_entities
if x.sync_status.id in follower_ids
sync_status.name
for sync_status in sync_status_list
if sync_status.id in follower_ids
]
follower_names.insert(0, leader_sync_status.name)
return follower_names

View File

@@ -22,6 +22,7 @@ from bluetooth_adapters import (
adapter_model,
adapter_unique_name,
get_adapters,
get_manufacturer_from_mac,
)
from bluetooth_data_tools import monotonic_time_coarse as MONOTONIC_TIME
from habluetooth import (
@@ -51,7 +52,7 @@ from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.issue_registry import async_delete_issue
from homeassistant.loader import async_get_bluetooth
from . import passive_update_processor
from . import passive_update_processor, websocket_api
from .api import (
_get_manager,
async_address_present,
@@ -66,6 +67,7 @@ from .api import (
async_rediscover_address,
async_register_callback,
async_register_scanner,
async_remove_scanner,
async_scanner_by_source,
async_scanner_count,
async_scanner_devices_by_address,
@@ -77,6 +79,9 @@ from .const import (
CONF_ADAPTER,
CONF_DETAILS,
CONF_PASSIVE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DOMAIN,
CONF_SOURCE_MODEL,
DOMAIN,
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS,
@@ -92,9 +97,24 @@ if TYPE_CHECKING:
from homeassistant.helpers.typing import ConfigType
__all__ = [
"FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS",
"MONOTONIC_TIME",
"SOURCE_LOCAL",
"BaseHaRemoteScanner",
"BaseHaScanner",
"BluetoothCallback",
"BluetoothCallbackMatcher",
"BluetoothChange",
"BluetoothScannerDevice",
"BluetoothScanningMode",
"BluetoothServiceInfo",
"BluetoothServiceInfoBleak",
"HaBluetoothConnector",
"HomeAssistantRemoteScanner",
"async_address_present",
"async_ble_device_from_address",
"async_discovered_service_info",
"async_get_advertisement_callback",
"async_get_fallback_availability_interval",
"async_get_learned_advertising_interval",
"async_get_scanner",
@@ -103,26 +123,12 @@ __all__ = [
"async_rediscover_address",
"async_register_callback",
"async_register_scanner",
"async_set_fallback_availability_interval",
"async_track_unavailable",
"async_remove_scanner",
"async_scanner_by_source",
"async_scanner_count",
"async_scanner_devices_by_address",
"async_get_advertisement_callback",
"BaseHaScanner",
"HomeAssistantRemoteScanner",
"BluetoothCallbackMatcher",
"BluetoothChange",
"BluetoothServiceInfo",
"BluetoothServiceInfoBleak",
"BluetoothScanningMode",
"BluetoothCallback",
"BluetoothScannerDevice",
"HaBluetoothConnector",
"BaseHaRemoteScanner",
"SOURCE_LOCAL",
"FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS",
"MONOTONIC_TIME",
"async_set_fallback_availability_interval",
"async_track_unavailable",
]
_LOGGER = logging.getLogger(__name__)
@@ -232,6 +238,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
set_manager(manager)
await storage_setup_task
await manager.async_setup()
websocket_api.async_setup(hass)
hass.async_create_background_task(
_async_start_adapter_discovery(hass, manager, bluetooth_adapters),
@@ -312,6 +319,38 @@ async def async_update_device(
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry for a bluetooth scanner."""
if source_entry_id := entry.data.get(CONF_SOURCE_CONFIG_ENTRY_ID):
if not (source_entry := hass.config_entries.async_get_entry(source_entry_id)):
# Cleanup the orphaned entry using a call_soon to ensure
# we can return before the entry is removed
hass.loop.call_soon(
hass_callback(
lambda: hass.async_create_task(
hass.config_entries.async_remove(entry.entry_id),
"remove orphaned bluetooth entry {entry.entry_id}",
)
)
)
address = entry.unique_id
assert address is not None
assert source_entry is not None
source_domain = entry.data[CONF_SOURCE_DOMAIN]
if mac_manufacturer := await get_manufacturer_from_mac(address):
manufacturer = f"{mac_manufacturer} ({source_domain})"
else:
manufacturer = source_domain
details = AdapterDetails(
address=address,
product=entry.data.get(CONF_SOURCE_MODEL),
manufacturer=manufacturer,
)
await async_update_device(
hass,
entry,
source_entry.title,
details,
)
return True
manager = _get_manager(hass)
address = entry.unique_id
assert address is not None

View File

@@ -132,7 +132,7 @@ class ActiveBluetoothDataUpdateCoordinator[_T](PassiveBluetoothDataUpdateCoordin
)
self.last_poll_successful = False
return
except Exception: # noqa: BLE001
except Exception:
if self.last_poll_successful:
self.logger.exception("%s: Failure while polling", self.address)
self.last_poll_successful = False

View File

@@ -127,7 +127,7 @@ class ActiveBluetoothProcessorCoordinator[_DataT](
)
self.last_poll_successful = False
return
except Exception: # noqa: BLE001
except Exception:
if self.last_poll_successful:
self.logger.exception("%s: Failure while polling", self.address)
self.last_poll_successful = False

View File

@@ -178,9 +178,20 @@ def async_register_scanner(
hass: HomeAssistant,
scanner: BaseHaScanner,
connection_slots: int | None = None,
source_domain: str | None = None,
source_model: str | None = None,
source_config_entry_id: str | None = None,
) -> CALLBACK_TYPE:
"""Register a BleakScanner."""
return _get_manager(hass).async_register_scanner(scanner, connection_slots)
return _get_manager(hass).async_register_hass_scanner(
scanner, connection_slots, source_domain, source_model, source_config_entry_id
)
@hass_callback
def async_remove_scanner(hass: HomeAssistant, source: str) -> None:
"""Permanently remove a BleakScanner by source address."""
return _get_manager(hass).async_remove_scanner(source)
@hass_callback

View File

@@ -18,7 +18,12 @@ from habluetooth import get_manager
import voluptuous as vol
from homeassistant.components import onboarding
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.core import callback
from homeassistant.helpers.schema_config_entry_flow import (
SchemaFlowFormStep,
@@ -26,7 +31,16 @@ from homeassistant.helpers.schema_config_entry_flow import (
)
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN
from .const import (
CONF_ADAPTER,
CONF_DETAILS,
CONF_PASSIVE,
CONF_SOURCE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DOMAIN,
CONF_SOURCE_MODEL,
DOMAIN,
)
from .util import adapter_title
OPTIONS_SCHEMA = vol.Schema(
@@ -63,6 +77,8 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
self, discovery_info: DiscoveryInfoType
) -> ConfigFlowResult:
"""Handle a flow initialized by discovery."""
if discovery_info and CONF_SOURCE in discovery_info:
return await self.async_step_external_scanner(discovery_info)
self._adapter = cast(str, discovery_info[CONF_ADAPTER])
self._details = cast(AdapterDetails, discovery_info[CONF_DETAILS])
await self.async_set_unique_id(self._details[ADAPTER_ADDRESS])
@@ -167,6 +183,24 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
),
)
async def async_step_external_scanner(
self, user_input: dict[str, Any]
) -> ConfigFlowResult:
"""Handle a flow initialized by an external scanner."""
source = user_input[CONF_SOURCE]
await self.async_set_unique_id(source)
data = {
CONF_SOURCE: source,
CONF_SOURCE_MODEL: user_input[CONF_SOURCE_MODEL],
CONF_SOURCE_DOMAIN: user_input[CONF_SOURCE_DOMAIN],
CONF_SOURCE_CONFIG_ENTRY_ID: user_input[CONF_SOURCE_CONFIG_ENTRY_ID],
}
self._abort_if_unique_id_configured(updates=data)
manager = get_manager()
scanner = manager.async_scanner_by_source(source)
assert scanner is not None
return self.async_create_entry(title=scanner.name, data=data)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -177,8 +211,10 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> SchemaOptionsFlowHandler:
) -> SchemaOptionsFlowHandler | RemoteAdapterOptionsFlowHandler:
"""Get the options flow for this handler."""
if CONF_SOURCE in config_entry.data:
return RemoteAdapterOptionsFlowHandler()
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
@classmethod
@@ -186,3 +222,13 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
"""Return options flow support for this handler."""
return bool((manager := get_manager()) and manager.supports_passive_scan)
class RemoteAdapterOptionsFlowHandler(OptionsFlow):
"""Handle a option flow for remote adapters."""
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle options flow."""
return self.async_abort(reason="remote_adapters_not_supported")

View File

@@ -18,6 +18,12 @@ CONF_DETAILS = "details"
CONF_PASSIVE = "passive"
CONF_SOURCE: Final = "source"
CONF_SOURCE_DOMAIN: Final = "source_domain"
CONF_SOURCE_MODEL: Final = "source_model"
CONF_SOURCE_CONFIG_ENTRY_ID: Final = "source_config_entry_id"
SOURCE_LOCAL: Final = "local"
DATA_MANAGER: Final = "bluetooth_manager"

View File

@@ -22,7 +22,13 @@ from homeassistant.core import (
from homeassistant.helpers import discovery_flow
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .const import DOMAIN
from .const import (
CONF_SOURCE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DOMAIN,
CONF_SOURCE_MODEL,
DOMAIN,
)
from .match import (
ADDRESS,
CALLBACK,
@@ -44,11 +50,11 @@ class HomeAssistantBluetoothManager(BluetoothManager):
"""Manage Bluetooth for Home Assistant."""
__slots__ = (
"hass",
"storage",
"_integration_matcher",
"_callback_index",
"_cancel_logging_listener",
"_integration_matcher",
"hass",
"storage",
)
def __init__(
@@ -240,6 +246,39 @@ class HomeAssistantBluetoothManager(BluetoothManager):
unregister()
self._async_save_scanner_history(scanner)
@hass_callback
def async_register_hass_scanner(
self,
scanner: BaseHaScanner,
connection_slots: int | None = None,
source_domain: str | None = None,
source_model: str | None = None,
source_config_entry_id: str | None = None,
) -> CALLBACK_TYPE:
"""Register a scanner."""
cancel = self.async_register_scanner(scanner, connection_slots)
if (
isinstance(scanner, BaseHaRemoteScanner)
and source_domain
and source_config_entry_id
and not self.hass.config_entries.async_entry_for_domain_unique_id(
DOMAIN, scanner.source
)
):
self.hass.async_create_task(
self.hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
data={
CONF_SOURCE: scanner.source,
CONF_SOURCE_DOMAIN: source_domain,
CONF_SOURCE_MODEL: source_model,
CONF_SOURCE_CONFIG_ENTRY_ID: source_config_entry_id,
},
)
)
return cancel
def async_register_scanner(
self,
scanner: BaseHaScanner,
@@ -253,6 +292,18 @@ class HomeAssistantBluetoothManager(BluetoothManager):
unregister = super().async_register_scanner(scanner, connection_slots)
return partial(self._async_unregister_scanner, scanner, unregister)
@hass_callback
def async_remove_scanner(self, source: str) -> None:
"""Remove a scanner."""
self.storage.async_remove_advertisement_history(source)
if entry := self.hass.config_entries.async_entry_for_domain_unique_id(
DOMAIN, source
):
self.hass.async_create_task(
self.hass.config_entries.async_remove(entry.entry_id),
f"Removing {source} Bluetooth config entry",
)
@hass_callback
def _handle_config_entry_removed(
self,

View File

@@ -15,11 +15,11 @@
"quality_scale": "internal",
"requirements": [
"bleak==0.22.3",
"bleak-retry-connector==3.6.0",
"bluetooth-adapters==0.20.2",
"bleak-retry-connector==3.7.0",
"bluetooth-adapters==0.21.0",
"bluetooth-auto-recovery==1.4.2",
"bluetooth-data-tools==1.20.0",
"dbus-fast==2.24.3",
"habluetooth==3.6.0"
"bluetooth-data-tools==1.22.0",
"dbus-fast==2.30.2",
"habluetooth==3.9.0"
]
}

View File

@@ -92,7 +92,7 @@ def seen_all_fields(
class IntegrationMatcher:
"""Integration matcher for the bluetooth integration."""
__slots__ = ("_integration_matchers", "_matched", "_matched_connectable", "_index")
__slots__ = ("_index", "_integration_matchers", "_matched", "_matched_connectable")
def __init__(self, integration_matchers: list[BluetoothMatcher]) -> None:
"""Initialize the matcher."""
@@ -164,12 +164,12 @@ class BluetoothMatcherIndexBase[
__slots__ = (
"local_name",
"service_uuid",
"service_data_uuid",
"manufacturer_id",
"service_uuid_set",
"service_data_uuid_set",
"manufacturer_id_set",
"service_data_uuid",
"service_data_uuid_set",
"service_uuid",
"service_uuid_set",
)
def __init__(self) -> None:

View File

@@ -4,8 +4,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Any
from typing_extensions import TypeVar
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.update_coordinator import (
BaseCoordinatorEntity,
@@ -20,12 +18,6 @@ if TYPE_CHECKING:
from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak
_PassiveBluetoothDataUpdateCoordinatorT = TypeVar(
"_PassiveBluetoothDataUpdateCoordinatorT",
bound="PassiveBluetoothDataUpdateCoordinator",
default="PassiveBluetoothDataUpdateCoordinator",
)
class PassiveBluetoothDataUpdateCoordinator(
BasePassiveBluetoothCoordinator, BaseDataUpdateCoordinatorProtocol
@@ -98,7 +90,9 @@ class PassiveBluetoothDataUpdateCoordinator(
self.async_update_listeners()
class PassiveBluetoothCoordinatorEntity( # pylint: disable=hass-enforce-class-module
class PassiveBluetoothCoordinatorEntity[
_PassiveBluetoothDataUpdateCoordinatorT: PassiveBluetoothDataUpdateCoordinator = PassiveBluetoothDataUpdateCoordinator
]( # pylint: disable=hass-enforce-class-module
BaseCoordinatorEntity[_PassiveBluetoothDataUpdateCoordinatorT]
):
"""A class for entities using DataUpdateCoordinator."""

View File

@@ -38,6 +38,12 @@ class BluetoothStorage:
"""Get all scanners."""
return list(self._data.keys())
@callback
def async_remove_advertisement_history(self, scanner: str) -> None:
"""Remove discovered devices by scanner."""
if self._data.pop(scanner, None):
self._store.async_delay_save(self._async_get_data, SCANNER_SAVE_DELAY)
@callback
def async_get_advertisement_history(
self, scanner: str

View File

@@ -33,6 +33,9 @@
"passive": "Passive scanning"
}
}
},
"abort": {
"remote_adapters_not_supported": "Bluetooth configuration for remote adapters is not supported."
}
}
}

View File

@@ -0,0 +1,150 @@
"""The bluetooth integration websocket apis."""
from __future__ import annotations
from collections.abc import Callable, Iterable
from functools import lru_cache, partial
import time
from typing import Any
from habluetooth import BluetoothScanningMode
from home_assistant_bluetooth import BluetoothServiceInfoBleak
import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.json import json_bytes
from .api import _get_manager, async_register_callback
from .match import BluetoothCallbackMatcher
from .models import BluetoothChange
@callback
def async_setup(hass: HomeAssistant) -> None:
"""Set up the bluetooth websocket API."""
websocket_api.async_register_command(hass, ws_subscribe_advertisements)
@lru_cache(maxsize=1024)
def serialize_service_info(
service_info: BluetoothServiceInfoBleak, time_diff: float
) -> dict[str, Any]:
"""Serialize a BluetoothServiceInfoBleak object."""
return {
"name": service_info.name,
"address": service_info.address,
"rssi": service_info.rssi,
"manufacturer_data": {
str(manufacturer_id): manufacturer_data.hex()
for manufacturer_id, manufacturer_data in service_info.manufacturer_data.items()
},
"service_data": {
service_uuid: service_data.hex()
for service_uuid, service_data in service_info.service_data.items()
},
"service_uuids": service_info.service_uuids,
"source": service_info.source,
"connectable": service_info.connectable,
"time": service_info.time + time_diff,
"tx_power": service_info.tx_power,
}
class _AdvertisementSubscription:
"""Class to hold and manage the subscription data."""
def __init__(
self,
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
ws_msg_id: int,
match_dict: BluetoothCallbackMatcher,
) -> None:
"""Initialize the subscription data."""
self.hass = hass
self.match_dict = match_dict
self.pending_service_infos: list[BluetoothServiceInfoBleak] = []
self.ws_msg_id = ws_msg_id
self.connection = connection
self.pending = True
# Keep time_diff precise to 2 decimal places
# so the cached serialization can be reused,
# however we still want to calculate it each
# subscription in case the system clock is wrong
# and gets corrected.
self.time_diff = round(time.time() - time.monotonic(), 2)
@callback
def _async_unsubscribe(
self, cancel_callbacks: tuple[Callable[[], None], ...]
) -> None:
"""Unsubscribe the callback."""
for cancel_callback in cancel_callbacks:
cancel_callback()
@callback
def async_start(self) -> None:
"""Start the subscription."""
connection = self.connection
cancel_adv_callback = async_register_callback(
self.hass,
self._async_on_advertisement,
self.match_dict,
BluetoothScanningMode.PASSIVE,
)
cancel_disappeared_callback = _get_manager(
self.hass
).async_register_disappeared_callback(self._async_removed)
connection.subscriptions[self.ws_msg_id] = partial(
self._async_unsubscribe, (cancel_adv_callback, cancel_disappeared_callback)
)
self.pending = False
self.connection.send_message(
json_bytes(websocket_api.result_message(self.ws_msg_id))
)
self._async_added(self.pending_service_infos)
self.pending_service_infos.clear()
def _async_event_message(self, message: dict[str, Any]) -> None:
self.connection.send_message(
json_bytes(websocket_api.event_message(self.ws_msg_id, message))
)
def _async_added(self, service_infos: Iterable[BluetoothServiceInfoBleak]) -> None:
self._async_event_message(
{
"add": [
serialize_service_info(service_info, self.time_diff)
for service_info in service_infos
]
}
)
def _async_removed(self, address: str) -> None:
self._async_event_message({"remove": [{"address": address}]})
@callback
def _async_on_advertisement(
self, service_info: BluetoothServiceInfoBleak, change: BluetoothChange
) -> None:
"""Handle the callback."""
if self.pending:
self.pending_service_infos.append(service_info)
return
self._async_added((service_info,))
@websocket_api.websocket_command(
{
vol.Required("type"): "bluetooth/subscribe_advertisements",
}
)
@websocket_api.async_response
async def ws_subscribe_advertisements(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None:
"""Handle subscribe advertisements websocket command."""
_AdvertisementSubscription(
hass, connection, msg["id"], BluetoothCallbackMatcher(connectable=False)
).async_start()

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