Compare commits

..

722 Commits

Author SHA1 Message Date
G Johansson
ed68a21afd Don't load from platform yaml 2025-06-30 16:42:26 +00:00
G Johansson
612cc91423 Add tests 2025-06-30 16:42:25 +00:00
G Johansson
170989ef30 Fixes 2025-06-30 16:42:25 +00:00
G Johansson
4aebf41c59 Fixes 2025-06-30 16:42:25 +00:00
G Johansson
abbaaf4ff5 Add config flow to compensation helper 2025-06-30 16:42:15 +00:00
epenet
1e3ebd5650 Use correctly formatted MAC in incomfort tests (#147819) 2025-06-30 18:02:42 +02:00
epenet
53936ab062 Use async_load_fixture in weatherflow_cloud (#147816) 2025-06-30 18:01:14 +02:00
Bouwe Westerdijk
b52a248def Bump plugwise to v1.7.7 and adapt (#147809) 2025-06-30 14:40:10 +01:00
Jeef
ea70229426 Add Weatherflow Cloud wind support via websocket (#125611)
* rebase off of dev

* update tests

* update tests

* addressing PR finally

* API to back

* adding a return type

* need to test

* removed teh extra check on available

* some changes

* ready for re-review

* change assertions

* remove icon function

* update ambr

* ruff

* update snapshot and push

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* enhnaced tests

* better coverage

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* Update homeassistant/components/weatherflow_cloud/coordinator.py

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

* remove comments

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-30 15:26:17 +02:00
Erik Montnemery
741a3d5009 Remove backup helper (#143558)
* Remove backup helper

* Update aws_s3 tests
2025-06-30 14:11:10 +02:00
Pete Sage
ee8830cc77 Person ble_trackers for non-home zones not processed correctly (#138475)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-30 13:35:19 +02:00
Bouwe Westerdijk
7fbf25e862 Plugwise: remove outdated fixtures (#147806) 2025-06-30 12:15:52 +02:00
epenet
e642cd45ae Enforce async_load_fixture in async test functions (#145709) 2025-06-30 11:56:26 +02:00
dependabot[bot]
179e1c2b00 Bump github/codeql-action from 3.29.0 to 3.29.1 (#147799)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 11:53:30 +02:00
Phill (pssc)
52a99aea0c Squeezebox: Fix Allow server device details to merge with players with the same MAC (#133517)
* Disambiguate bewtween servers and player to stop them being merged

* ruff format

* make SqueezeLite players not a service

* ruff

* Tidy redunant code

* config url

* revert config url

* change to domain server

* use default to see how they are mereged with server device

* refactor to use defaults so where a player is part of a bigger ie server service device in the same intergration it doesnt replace its information

* ruff

* make test match the new data

* Fix merge

* Fix tests

* Fix meregd test data

* Fix all tests add new test for merged device in reg

* Remove info from device_info so its only a lookup

* manual merge of server player shared devices

* Fix format of merged entires

* fixes for testing

* Fix test with input from @peteS-UK device knowlonger exits for this test

* Fix test now device doesnt exits for tests

* Update homeassistant/components/squeezebox/media_player.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix Copilots formatting

* Apply suggestions from code review

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-30 11:41:22 +02:00
Paulus Schoutsen
c7b2f236be Type Z-Wave JS config entry (#147456)
* Type Z-Wave JS config entry

* Migrate to data class
2025-06-30 11:15:12 +02:00
Evan Severson
a6e3da43ca Fixed pushbullet handling of fields longer than 255 characters (#146993) 2025-06-30 11:08:50 +02:00
Steffen Rusitschka
4d58024d5d Add publish_string_states config to zabbix (#134773)
* Add include_strings config to zabbix

* Remove commented code

* Fix ruff formatting

* Update homeassistant/components/zabbix/__init__.py

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>

* Update homeassistant/components/zabbix/__init__.py

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>

* Don't use dict.get, CONF_INCLUDE_STRINGS has a default value and will always be set.

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

* Convert to string only when include_strings is true

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

* change to guard

* Fix review comments

* ruff, mypy, pylint fixes

* more ruff, mypy fixes

* and another ruff format fix

---------

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-30 10:52:33 +02:00
Manu
c7603b39ec Fix inputs to correctly handle Fahrenheit in IronOS (#135421)
* Fix inputs to correctly handle Fahrenheit in IronOS

* some refactoring

* add boost switch entity

* Revert switch entity

* refactor

* remove commented code

* some changes
2025-06-30 10:44:39 +02:00
epenet
c17ee0d123 Allow binary sensor template to return state unknown (#128861)
* Allow binary sensor template to return state unknown

* Add tests

* Adjust TriggerBinarySensorEntity

* Add restore tests for BinarySensorTemplate

* Add tests for TriggerBinarySensorEntity

* Tweak

* Tweak

* Adjust tests

* Adjust
2025-06-30 10:06:05 +02:00
Alexandre CUER
97c1e21a69 Add possibility to synchronize automatically all available feeds in emoncms (#128122)
* Add checkbox in options to sync all feeds once

* Add sync mode selector in async_step_user
Remove checkbox in options

* Correct use of SYNC_MODE & SYNC_MODE_AUTO in tests

* Use dropdown for mode selection

* rmv_unused_const

* Add separate tests + use SelectSelector
2025-06-30 10:05:07 +02:00
starkillerOG
c9a6b1fd45 Bump reolink_aio to 0.14.2 (#147797) 2025-06-30 09:39:02 +02:00
mkmer
05ceee568e Honeywell: Don't use shared session (#147772) 2025-06-29 21:22:59 +02:00
Shay Levy
08a6b38699 Bump aioshelly to 13.7.1 (#146221)
* Bump aioshelly to 13.8.0

* Change version to 13.7.1
2025-06-29 21:41:50 +03:00
Norbert Rittel
4add346272 Deduplicate strings and fix sentence-casing in proximity (#147777)
* Deduplicate strings and fix sentence-casing in `proximity`

* Update test_init.py
2025-06-29 21:00:16 +03:00
Andre Lengwenus
369c8d1e0d Bump pypck to 0.8.10 (#147774) 2025-06-29 20:58:41 +03:00
tronikos
25ab47a587 Move the async_reload on updates in async_setup_entry in Google Generative AI (#147748)
Move the async_reload on updates in async_setup_entry
2025-06-29 07:56:37 +02:00
Marc Hörsken
617ea1925c Update pywmspro to 0.3.0 to wait for short-lived actions (#147679)
Replace action delays with detailed action responses.
2025-06-29 07:33:44 +02:00
cdnninja
8bacab4f9c Fix Vesync set_percentage error (#147751) 2025-06-29 07:22:04 +02:00
J. Nick Koston
6d28b99344 Preserve httpx boolean behavior in REST integration after aiohttp conversion (#147738) 2025-06-28 17:24:09 -05:00
cnico
bbd1cbf5c9 Correct Chlorine unit definition in flipr integration (#147537)
* Correction of bug 145683

* constant for chlorine unit correction

* constant name correction

* Review correction
2025-06-28 22:29:24 +01:00
starkillerOG
43450d4489 Reduce idle timeout of HLS stream to conserve camera battery life (#147728)
* Reduce IDLE timeout of HLS stream to conserve camera battery life

* adjust tests
2025-06-28 22:20:47 +02:00
J. Nick Koston
f8c052e0ce Improve rest error logging (#147736)
* Improve rest error logging

* Improve rest error logging

* Improve rest error logging

* Improve rest error logging

* Improve rest error logging

* top level
2025-06-28 22:18:46 +02:00
Florian von Garrel
1f3bdfc7b7 bump pypaperless to 4.1.1 (#147735) 2025-06-28 22:13:51 +02:00
Antoni Czaplicki
0652bffd68 Bump vulcan-api to 2.4.2 (#146857) 2025-06-28 22:11:59 +02:00
Manu
8322611099 Use test parametrization in ista EcoTrend integration (#147729) 2025-06-28 21:57:51 +02:00
Marc Hörsken
134967b817 Fix error if cover position is not available or unknown (#147732) 2025-06-28 21:57:26 +02:00
Shay Levy
39abae36f0 Fix Shelly Block entity removal (#147694) 2025-06-28 22:40:58 +03:00
Marc Mueller
227760f203 Fix RuntimeWarnings in homeassistant_yellow tests (#147724) 2025-06-28 20:31:01 +02:00
Jan Bouwhuis
969809456e Move MQTT device sw and hw version to collapsed section in subentry flow (#147685)
Move MQTT device sw and hw version to collapsed section
2025-06-28 11:25:59 +02:00
Daniel Hjelseth Høyer
d2e8a48b2c Bump pytibber to 0.31.6 (#147703) 2025-06-28 10:11:17 +02:00
epenet
ea6332ee42 Move backup services to separate module (#146427) 2025-06-27 20:54:56 +02:00
Erik Montnemery
91c3b43d7f Improve comment for helpers.entity.entity_sources (#146529) 2025-06-27 20:54:19 +02:00
Thomas55555
1d82d44794 Add device prefix to summary in Husqvarna Automower (#147405) 2025-06-27 20:34:50 +02:00
Thomas55555
571376badc Bump aioautomower to 1.0.1 (#147683) 2025-06-27 20:28:45 +02:00
Manu
32236b2f4d Add reconfiguration flow to PlayStation Network (#147552) 2025-06-27 20:17:06 +02:00
Samuel Xiao
18c1953bc5 Add lock models to switchbot cloud (#147569) 2025-06-27 20:16:21 +02:00
Bernardus Jansen
d874c28dc9 Add previously missing state classes to dsmr sensors (#147633) 2025-06-27 19:45:36 +02:00
Brett Adams
19d89c8952 Fix energy history in Teslemetry (#147646) 2025-06-27 19:43:03 +02:00
Ludovic BOUÉ
e3ba1f34ca Matter TemperatureControl (#145706)
* TemperatureControl

* Add tests

* Commands.SetTemperature

* Update homeassistant/components/matter/number.py

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

* Apply suggestions from code review

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

* Update number.py

* Update number.py

* Update number.py

* Update homeassistant/components/matter/number.py

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

* Refactor MatterRangeNumber to streamline command handling in async_set_native_value

* testing requested changes

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-27 19:41:39 +02:00
Thomas55555
b630fb0520 Respect availability of parent class in Husqvarna Automower (#147649) 2025-06-27 19:38:42 +02:00
Ville Skyttä
5129f89086 Finish config flow in huawei_lte SSDP test (#147542) 2025-06-27 19:00:01 +02:00
Ville Skyttä
0be0e22e76 Simplify rflink dimmable set_level parsing (#147636) 2025-06-27 18:59:10 +02:00
epenet
b8500b338a Improve tests for binary sensor template (#147657) 2025-06-27 18:58:16 +02:00
Simone Chemelli
4cab3a0465 Bump aioamazondevices to 3.1.22 (#147681) 2025-06-27 18:44:01 +02:00
hanwg
ff711324d5 Add codeowner for Telegram bot (#147680) 2025-06-27 18:18:01 +02:00
Michael
113e7dc003 Add data descriptions to PEGELONLINE integration (#147594) 2025-06-27 18:16:38 +02:00
Shay Levy
2120ff6a0a Fix Shelly entity removal (#147665) 2025-06-27 18:50:35 +03:00
Marc Mueller
8ee5c30754 Update ruff to 0.12.1 (#147677) 2025-06-27 17:40:08 +02:00
Paul Bottein
a1518b96c4 Update frontend to 20250627.0 (#147668) 2025-06-27 17:28:14 +02:00
Petar Petrov
bba7f5c3f0 Z-WaveJS config flow: Change keys question (#147518)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-06-27 17:27:43 +02:00
Manu
8a5671af76 Remove dweet.io integration (#147645) 2025-06-27 17:23:42 +02:00
Raphael Hehl
8a18dea8c7 UniFi Protect removing early access checks and issue creation (#147432)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-27 17:15:34 +02:00
Thomas55555
4b02f22724 Bump aioautomower to 1.0.0 (#147676) 2025-06-27 17:02:52 +02:00
mkmer
7229c2ca2c Bump aiosomecomfort to 0.0.33 (#147673) 2025-06-27 16:32:25 +02:00
Norbert Rittel
d83eddf13b Fix sentence-casing and spacing of button in thermopro (#147671) 2025-06-27 15:53:18 +02:00
Josef Zweck
4a192a7b09 Bump jellyfin-apiclient-python to 1.11.0 (#147658) 2025-06-27 11:07:14 +02:00
Josef Zweck
58c434887e Fix: Unhandled NoneType sessions in jellyfin (#147659) 2025-06-27 11:00:23 +02:00
Abílio Costa
78c2405e61 Bump whirlpool to 0.21.1 (#147611) 2025-06-27 10:33:49 +02:00
Josef Zweck
8cc4105984 Make jellyfin not single config entry (#147656) 2025-06-27 10:31:13 +02:00
Josef Zweck
917f1e4c6f Make entities unavailable when machine is physically off in lamarzocco (#147426) 2025-06-27 10:03:14 +02:00
hanwg
3879f6d2ef Fix Telegram bot yaml import for webhooks containing None value for URL (#147586) 2025-06-27 10:03:03 +02:00
Norbert Rittel
78060e4833 Clarify descriptions of subaru.unlock_specific_door action (#147655) 2025-06-27 10:01:44 +02:00
Guido Schmitz
fda66c4be4 Handle deleted devices dynamically in devolo Home Control (#147585) 2025-06-27 09:52:00 +02:00
Michael
21131d00b3 Fix config schema to make credentials optional in NUT flows (#147593) 2025-06-27 09:51:28 +02:00
puddly
a84313de33 Allow setup of Zigbee/Thread for ZBT-1 and Yellow without internet access (#147549)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-06-27 09:50:45 +02:00
Manu
c73346e6b3 Bump pynecil to v4.1.1 (#147648) 2025-06-27 09:31:35 +02:00
Franck Nijhof
55a37a2936 Extend GitHub Copilot instructions with new learnings from reviews (#147652) 2025-06-27 09:01:09 +02:00
Abílio Costa
e481f14335 Simplify reolink light tests (#147637) 2025-06-27 08:58:09 +02:00
Petar Petrov
1ca03c8ae9 Do not factory reset old Z-Wave controller during migration (#147576)
* Do not factory reset old Z-Wave controller during migration

* PR comments

* remove obsolete test
2025-06-27 08:02:12 +02:00
Ville Skyttä
61b43ca1fc Remove unnecessary wilight trigger regex use (#147638) 2025-06-26 23:16:21 +01:00
Joost Lekkerkerker
1b2be083c2 Make sure Google Generative AI integration migration is clean (#147625) 2025-06-26 23:03:36 +02:00
Joost Lekkerkerker
4bdf3d6f30 Make sure OpenAI integration migration is clean (#147627) 2025-06-26 23:03:11 +02:00
Joost Lekkerkerker
43535ede8b Make sure Anthropic integration migration is clean (#147629) 2025-06-26 23:02:59 +02:00
Joost Lekkerkerker
9bd0762799 Make sure Ollama integration migration is clean (#147630) 2025-06-26 23:02:35 +02:00
Ville Skyttä
1bb653b4f7 Remove unused config regexps (#147631) 2025-06-26 23:02:14 +02:00
Franck Nijhof
2655edcfc8 Extend GitHub Copilot instructions and make it suitable for Claude Code (#147632) 2025-06-26 23:00:02 +02:00
Franck Nijhof
7a08edc3dd Add Claude to gitignore (#147622) 2025-06-26 21:06:34 +02:00
Abílio Costa
b3131355b0 Use non-autospec mock for Reolink's light tests (#147621) 2025-06-26 21:05:23 +02:00
Abílio Costa
06d04c001d Use non-autospec mock for Reolink's host tests (#147619) 2025-06-26 20:55:46 +02:00
Jack Powell
babecdf32c Add Diagnostics to PlayStation Network (#147607)
* Add Diagnostics support to PlayStation_Network

* Remove unused constant

* minor cleanup

* Redact additional data

* Redact additional data
2025-06-26 20:52:07 +02:00
Renat Sibgatulin
17cd39748b Create a new client session for air-Q to fix cookie polution (#147027) 2025-06-26 19:59:49 +02:00
Simone Chemelli
c2f1e86a4e Add action exceptions to Alexa Devices (#147546) 2025-06-26 19:59:02 +02:00
Joost Lekkerkerker
61a32466b6 Hide Telegram bot proxy URL behind section (#147613)
Co-authored-by: Manu <4445816+tr4nt0r@users.noreply.github.com>
2025-06-26 19:55:38 +02:00
Manu
aef08091f8 Fix asset url in Habitica integration (#147612) 2025-06-26 19:52:58 +02:00
Joost Lekkerkerker
1416f0f1e0 Fix meaters not being added after a reload (#147614) 2025-06-26 19:52:29 +02:00
HarvsG
af7b1a76bc Add description placeholders to SchemaFlowFormStep (#147544)
* test description placeholders

* Update test_schema_config_entry_flow.py

* fix copy and paste indentation

* Apply suggestions from code review

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-26 19:51:31 +02:00
Maximilian Arzberger
bf88fcd5bf Add Manual Charge Switch for Installers for Kostal Plenticore (#146932)
* Add Manual Charge Switch for Installers

* Update stale docstring

* Installer config fixture

* fix ruff
2025-06-26 19:50:27 +02:00
Joost Lekkerkerker
35478e3162 Set Google AI model as device model (#147582)
* Set Google AI model as device model

* fix
2025-06-26 19:44:15 +02:00
Joost Lekkerkerker
69af74a593 Improve explanation on how to get API token in Telegram (#147605) 2025-06-26 18:21:56 +02:00
tronikos
b4dd912bee Refactor in Google AI TTS in preparation for STT (#147562) 2025-06-26 11:53:16 -04:00
Bram Kragten
b5821ef499 Update frontend to 20250626.0 (#147601) 2025-06-26 17:46:45 +02:00
Fabio Natanael Kepler
1a92d4530e Fix playing TTS and local media source over DLNA (#134903)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-26 17:12:15 +02:00
Joost Lekkerkerker
7b80c1c693 Add default conversation name for OpenAI integration (#147597) 2025-06-26 17:11:48 +02:00
Joost Lekkerkerker
e7cc03c1d9 Add default title to migrated Claude entry (#147598) 2025-06-26 17:11:13 +02:00
Luca Angemi
69f0b6244a Remove default icon for wind direction sensor for Buienradar (#147603)
* Fix wind direction state class sensor

* Remove default icon for wind direction sensor
2025-06-26 17:05:59 +02:00
Joost Lekkerkerker
01205f8a14 Add default title to migrated Ollama entry (#147599) 2025-06-26 17:05:26 +02:00
hanwg
68924d23ab Fix Telegram bot default target when sending messages (#147470)
* handle targets

* updated error message

* validate chat id for single target

* add validation for chat id

* handle empty target

* handle empty target
2025-06-26 16:43:09 +02:00
Artur Pragacz
40f553a007 Migrate device connections to a normalized form (#140383)
* Normalize device connections migration

* Update version

* Slightly improve tests

* Update homeassistant/helpers/device_registry.py

* Add validators

* Fix validator

* Move format mac function too

* Add validator test

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-26 15:33:34 +02:00
Robin Lintermann
bc46894b74 Fixed issue when tests (should) fail in Smarla (#146102)
* Fixed issue when tests (should) fail

* Use usefixture decorator

* Throw ConfigEntryError instead of AuthFailed
2025-06-26 15:30:03 +02:00
Anders Peter Fugmann
6f4615f012 Bump dependency on pyW215 for DLink integration to 0.8.0 (#147534) 2025-06-26 12:56:46 +02:00
Joost Lekkerkerker
4244d2f66f Set right model in OpenAI conversation (#147575) 2025-06-26 12:49:33 +02:00
Petar Petrov
a73dafe097 Hide unnamed paths when selecting a USB Z-Wave adapter (#147571)
* Hide unnamed paths when selecting a USB Z-Wave adapter

* remove pointless sorting
2025-06-26 12:15:02 +02:00
Stefan Agner
be49296547 Deduplicate shared logic in Matter vacuum commands (#147578)
Get the run mode by tag in a single place to avoid code duplication.
Also raise an error if the run mode (unexpectedly) is not found.
2025-06-26 11:54:52 +02:00
Marcel van der Veldt
d55ecd885e Do not make the favorite button unavailable when no content playing on a Music Assistant player (#147579) 2025-06-26 11:49:06 +02:00
Luca Angemi
076248c455 Fix wind direction state class sensor for AEMET (#147535) 2025-06-26 11:07:07 +02:00
Petar Petrov
13ce27c94c Remove obsolete routing info when migrating a Z-Wave network (#147568) 2025-06-26 11:06:36 +02:00
Joost Lekkerkerker
4b9b08ece5 Show current Lametric version if there is no newer version (#147538) 2025-06-26 10:55:31 +02:00
Simone Chemelli
79df38eff2 Improve config flow strings for Alexa Devices (#147523) 2025-06-26 10:52:14 +02:00
tronikos
fb133664e4 Include subentries in Google Generative AI diagnostics (#147558) 2025-06-26 10:50:47 +02:00
Marcel van der Veldt
38669ce96c Fix sending commands to Matter vacuum (#147567) 2025-06-26 10:47:24 +02:00
Petar Petrov
651b33d49b Bump zwave-js-server-python to 0.65.0 (#147561)
* Bump zwave-js-server-python to 0.65.0

* update tests
2025-06-26 10:11:25 +03:00
Erik Montnemery
3b64db5f76 Set end date for when allowing unique id collisions in config entries (#147516)
* Set end date for when allowing unique id collisions in config entries

* Update test
2025-06-26 08:20:26 +02:00
tronikos
0f95fe566c Use default title for migrated Google Generative AI entries (#147551) 2025-06-25 22:30:41 -04:00
Simone Chemelli
6290facffb Fix unload for Alexa Devices (#147548) 2025-06-26 01:55:58 +02:00
tronikos
f0a78aadbe Fixes in Google AI TTS (#147501)
* Fix Google AI not using correct config options after subentries migration

* Fixes in Google AI TTS

* Fix tests by @IvanLH

* Change type name.

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2025-06-25 18:12:23 -04:00
Pete Sage
345ec97dd5 Add enum sensor for Sonos Power Source (#147449)
* feat: add power source sensor

* fix: translations

* fix:cleanup

* fix: simpify

* fix: improve coverage

* fix: improve coverage

* fix: add missing test

* fix: call it charging_base

* fix: disable entity by default

* update snapshots

* Update homeassistant/components/sonos/strings.json

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

* fix: update test

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-25 23:49:06 +02:00
Franck Nijhof
1286b5d9d8 Bump version to 2025.8.0dev0 (#147531) 2025-06-25 21:38:35 +02:00
Erik Montnemery
26e3caea9a Add support for condition platforms to provide multiple conditions (#147376) 2025-06-25 18:10:30 +01:00
Bouwe Westerdijk
2b5f5f641d Bump plugwise to v1.7.6 (#147508) 2025-06-25 18:48:38 +02:00
Simone Chemelli
99079d2980 Bump aioamazondevices to 3.1.19 (#147462) 2025-06-25 18:47:09 +02:00
Retha Runolfsson
2800921a5d Remove force latch mode for locklite in switchbot integration (#147474) 2025-06-25 18:45:37 +02:00
Jan Bouwhuis
3268b9ee18 Fix typo's in MQTT translation strings (#147489) 2025-06-25 18:45:09 +02:00
Bram Kragten
02c3cdd5d4 Update frontend to 20250625.0 (#147521) 2025-06-25 18:44:46 +02:00
Manu
f34f17bc24 Update codeowners of PlayStation Network integration (#147510)
Add myself as codeowner
2025-06-25 18:35:48 +02:00
Erik Montnemery
1fb587bf03 Allow core integrations to describe their triggers (#147075)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-06-25 17:35:15 +01:00
Pete Sage
d8258924f7 Remove mapping of entity_ids to speakers in Sonos (#147506)
* fix

* fix: change entity_id mappings

* fix: translate errors

* fix:merge issues

* fix: translate error messages

* fix: improve test coverage

* fix: remove unneeded strings
2025-06-25 18:29:23 +02:00
Retha Runolfsson
c05d8aab1c Add floor lamp and strip light 3 for switchbot integration (#147517) 2025-06-25 18:01:10 +02:00
Nathan Larsen
e210681751 Fix API POST endpoints json parsing error-handling (#134326)
* Fix API POST endpoints json parsing error-handling

* Add tests

* Fix mypy and ruff errors

* Fix coverage by removing non-needed error handling

* Correct error handling and improve tests

---------

Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Erik <erik@montnemery.com>
2025-06-25 16:58:21 +02:00
Thomas D
809aced9cc Add cover platform to Qbus integration (#147420)
* Add scene platform

* Add cover platform

* Refactor receiving state

* Fix wrong auto-merged code
2025-06-25 15:38:43 +02:00
ocrease
977e8adbfb Fix operational state and vacuum state for matter vacuum (#147466) 2025-06-25 15:23:38 +02:00
Michael
c54ce7eabd Split models and helpers from coordinator module in AVM Fritz!Box tools (#147412)
* split models from coordinator

* split helpers from coordinator
2025-06-25 14:50:07 +02:00
Retha Runolfsson
c5f8acfe93 Add effect mode support for switchbot light (#147326)
* add support for strip light3 and floor lamp

* clear the color mode

* add led unit test

* use property for effect

* fix color mode issue

* remove new products

* fix adv data

* adjust log level

* add translation and icon
2025-06-25 14:45:07 +02:00
Pavel Skuratovich
8393f17bb3 Fix sensor state class for fuel sensor in StarLine integration (#146769) 2025-06-25 14:34:11 +02:00
Guido Schmitz
8918b0d7a9 Add missing reauth_confirm strings to devolo Home Control (#147496) 2025-06-25 14:33:37 +02:00
Manu
c447729ce4 Add sensor platform to PlayStation Network (#147469) 2025-06-25 14:33:02 +02:00
Guido Schmitz
12812049ea Split setup tests in devolo Home Network (#147498) 2025-06-25 14:14:33 +02:00
J. Nick Koston
47811e13a6 Bump PySwitchbot to 0.67.0 (#147503)
changelog: https://github.com/sblibs/pySwitchbot/compare/0.66.0...0.67.0
2025-06-25 13:58:39 +02:00
Erik Montnemery
7587fc985f Bump py-dormakaba-dkey to 1.0.6 (#147499) 2025-06-25 13:31:43 +02:00
puddly
716ec1eef2 Bump ZHA to 0.0.61 (#147472)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2025-06-25 13:27:57 +02:00
Gábor Kiss
b95af2d86b Fix ESPHome entity_id generation if name contains unicode characters (#146796)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-25 13:19:55 +02:00
Andre Lengwenus
bca7502611 Add quality scale for LCN (#147367)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-06-25 11:50:00 +01:00
J. Diego Rodríguez Royo
1e4fbebf49 Improve Home Connect diagnostics exposing more data (#147492) 2025-06-25 11:49:54 +02:00
Pete Sage
c9e9575a3d Add tests for join and unjoin service calls in Sonos (#145602)
* fix: add tests for join and unjoin

* fix: update comments

* fix: update comments

* fix: refactor to common functions

* fix: refactor to common functions

* fix: add type def

* fix: add return types

* fix: add return types

* fix: correct type annontation for uui_ds

* fix: update comments

* fix: merge issues

* fix: merge issue

* fix: raise homeassistanterror on timeout

* fix: add comments

* fix: simplify test

* fix: simplify test

* fix: simplify test
2025-06-25 11:38:51 +02:00
tronikos
f897a728f1 Fix Google AI not using correct config options after subentries migration (#147493) 2025-06-25 11:25:01 +02:00
J. Diego Rodríguez Royo
0bbb168862 Add Home Connect DHCP information (#147494)
* Add Home Connect DHCP information

* Add tests
2025-06-25 11:24:38 +02:00
J. Nick Koston
0a884c7253 Add subdevices support to ESPHome (#147343) 2025-06-25 21:24:30 +12:00
Joakim Sørensen
58e60fdfac Bump hass-nabucasa from 0.103.0 to 0.104.0 (#147488) 2025-06-25 11:15:09 +02:00
Joost Lekkerkerker
33bd35bff4 Migrate Meater to use HassKey (#147485)
* Migrate Meater to use HassKey

* Update homeassistant/components/meater/sensor.py

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

* Migrate Meater to use HassKey

* Migrate Meater to use HassKey

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-06-25 10:36:58 +02:00
Simone Rescio
f4b95ff5f1 Ezviz battery camera work mode (#130478)
* Add support for EzViz Battery Camera work mode

* feat: address review comment, add 'battery' to work mode string

* feat: optimize entity addition for Ezviz select component

* refactor: streamline error handling in Ezviz select actions

* Update library

* update library

* Bump api to pin mqtt to compatable version

* fix after rebase

* Update code owners

* codeowners

* Add support for EzViz Battery Camera work mode

* feat: address review comment, add 'battery' to work mode string

* feat: optimize entity addition for Ezviz select component

* refactor: streamline error handling in Ezviz select actions

* feat: address review item simplify Ezviz select actions by removing base class and moving methods

* chore: fix ruff lint

* feat: check for SupportExt before adding battery select

* chore: cleanup logging

* feat: restored battery work mode, separated defnitions for sound and battery selects, check SupportExt with type casting

* Apply suggestions from code review

---------

Co-authored-by: Pierre-Jean Buffard <pierre-jean.buffard@dataiku.com>
Co-authored-by: Renier Moorcroft <66512715+RenierM26@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-25 09:41:18 +02:00
Simone Chemelli
f800248c10 Add more binary sensors to Alexa Devices (#146402)
* Add more binary sensors to Amazon Devices

* apply review comment

* Add sensor platform to Amazon Devices

* Revert "Add sensor platform to Amazon Devices"

This reverts commit 25a9ca673e450634a17bdb79462b14aa855aca10.

* clean

* fix logic after latest changes

* apply review comments
2025-06-25 09:33:13 +02:00
Retha Runolfsson
d0b2d1dc92 Add evaporative humidifier for switchbot integration (#146235)
* add support for evaporative humidifier

* add evaporative humidifier unit test

* clear the humidifier action in pyswitchbot

* fix ruff

* fix Sentence-casing issue

* add icon translation

* remove last run success

* use icon translations for water level

* remove the translation for last run success
2025-06-25 09:32:33 +02:00
Jan Bouwhuis
85e9919bbd Add entity category option to entities set up via an MQTT subentry (#146776)
* Add entity category option to entities set up via an MQTT subentry

* Rephrase

* typo

* Move entity category to entity details - remove service to action

* Move entity category to entity platform config flow step
2025-06-25 09:28:37 +02:00
Joost Lekkerkerker
51fb1ab8b6 Refactor Meater availability (#146956)
* Refactor Meater availability

* Fix

* Fix
2025-06-25 09:23:27 +02:00
epenet
066e840e06 Migrate lookin to use runtime_data (#147479) 2025-06-25 09:17:43 +02:00
Joost Lekkerkerker
7031167895 Set has entity name to True in Meater (#146954)
* Set has entity name to True in Meater

* Fix

* Fix
2025-06-25 08:59:28 +02:00
epenet
69bf79d3bd Migrate local_calendar to use runtime_data (#147481) 2025-06-25 08:47:29 +02:00
epenet
909d950b50 Migrate luftdaten to use runtime_data (#147480) 2025-06-25 08:07:34 +02:00
epenet
51da1bc25a Migrate loqed to use runtime_data (#147478)
* Migrate loqed to use runtime_data

* Fix tests
2025-06-25 08:07:17 +02:00
epenet
f22b623968 Move luftdaten coordinator to separate module (#147477) 2025-06-25 07:48:56 +02:00
epenet
2bcdc03661 Migrate lupusec to use runtime_data (#147476) 2025-06-25 07:48:30 +02:00
epenet
10d1affd81 Migrate lyric to use runtime_data (#147475) 2025-06-25 07:48:20 +02:00
Manu
91e7b75a44 Fix errors in legacy platform in PlayStation Network integration (#147471)
fix legacy platform presence
2025-06-25 06:48:45 +02:00
natepugh
42aaa888a1 Bump pyairnow to 1.3.1 (#147388)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-06-24 23:47:56 +01:00
Guido Schmitz
7b8ebb0803 Move DevoloMultiLevelSwitchDeviceEntity in devolo Home Control (#147450) 2025-06-24 22:42:42 +02:00
Paulus Schoutsen
c270ea4e0c Fix media accept config type (#147445) 2025-06-24 16:41:43 -04:00
Paulus Schoutsen
c93e45c0f2 Add missing config entry type for Husqvarna (#147455)
Add missing type for husqvarna
2025-06-24 22:37:35 +02:00
Michael Hansen
19b773df85 Only send ESPHome intent progress when necessary (#147458)
* Only send intent progress when necessary

* cover

* Fix logic

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-24 16:35:38 -04:00
puddly
9e7c7ec97e Flash ZBT-1 and Yellow firmwares from Core instead of using addons (#145019)
* Make `async_flash_firmware` a public helper

* [ZBT-1] Implement flashing for Zigbee and Thread within the config flow

* WIP: Begin fixing unit tests

* WIP: Unit tests, pass 2

* WIP: pass 3

* Fix hardware unit tests

* Have the individual hardware integrations depend on the firmware flasher

* Break out firmware filter into its own helper

* Mirror to Yellow

* Simplify

* Simplify

* Revert "Have the individual hardware integrations depend on the firmware flasher"

This reverts commit 096f4297dc.

* Move `async_flash_silabs_firmware` into `util`

* Fix existing unit tests

* Unconditionally upgrade Zigbee firmware during installation

* Fix failing error case unit tests

* Fix remaining failing unit tests

* Increase test coverage

* 100% test coverage

* Remove old translation strings

* Add new translation strings

* Do not probe OTBR firmware when completing the flow

* More translation strings

* Probe OTBR firmware info before starting the addon
2025-06-24 16:21:02 -04:00
Paulus Schoutsen
f735331699 Convert Ollama to subentries (#147286)
* Convert Ollama to subentries

* Add latest changes from Google subentries

* Move config entry type to init
2025-06-24 16:13:34 -04:00
Maciej Bieniek
5a20ef3f3f Bump aioshelly to version 13.7.0 (#147453) 2025-06-24 23:03:22 +03:00
Simone Chemelli
5ef054f2e0 Add quality scale bronze to SamsungTV (#142288) 2025-06-24 21:41:39 +02:00
Manu
b9fc198a7e Set quality scale to 🥇 gold for ista EcoTrend integration (#143462) 2025-06-24 21:25:53 +02:00
HarvsG
ad4fae7f59 Custom sentence triggers should be marked as processed locally (#145704)
* Mark custom sentence triggers a local agent

* Don't change agent ID

* adds tests to confirm processed_locally is True

* move asserts to after null check
2025-06-24 14:25:40 -05:00
Paulus Schoutsen
265de91fba Add type for wiz (#147454) 2025-06-24 15:13:51 -04:00
Paul Bottein
7322fe40da Define fields for assist ask_question action (#147219)
* Define fields for assist ask_question action

* Update hassfest

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-06-24 15:00:14 -04:00
Paulus Schoutsen
8eb906fad9 Migrate OpenAI to config subentries (#147282)
* Migrate OpenAI to config subentries

* Add latest changes from Google subentries

* Update homeassistant/components/openai_conversation/__init__.py

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-24 15:00:05 -04:00
Sven Naumann
4d9843172b Fix nfandroidtv service notify disappears when restarting home assistant (#128958)
* move connect to android tv host from init to short before sending a message

* Don't swallow exceptions

* use string literals for exception

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-24 20:09:45 +02:00
Abílio Costa
e8a534be9c Add missing method mock to Reolink chime test (#147447) 2025-06-24 20:06:54 +02:00
Abílio Costa
3148719864 Use newer mock in recent Reolink test (#147448) 2025-06-24 19:06:42 +01:00
Nathan Spencer
abfb7afcb7 Bump pylitterbot to 2024.2.1 (#147443) 2025-06-24 19:26:35 +02:00
Abílio Costa
fe4ff4f835 Use non-autospec mock for Reolink switch tests (#147441) 2025-06-24 19:19:41 +02:00
Michael Hansen
cefc8822b6 Support streaming TTS in wyoming (#147392)
* Support streaming TTS in wyoming

* Add test

* Refactor to avoid repeated task creation

* Manually manage client lifecycle
2025-06-24 13:04:40 -04:00
Michael Hansen
3dc8676b99 Add TTS streaming to Wyoming satellites (#147438)
* Add TTS streaming using intent-progress

* Handle incomplete header
2025-06-24 12:00:02 -05:00
Abílio Costa
0f112bb9c4 Use non-autospec mock for Reolink service tests (#147440) 2025-06-24 18:37:05 +02:00
Nathan Spencer
54e5107c34 Add total cycles sensor for Litter-Robot (#147435)
* Add total cycles sensor for Litter-Robot

* Add translatable unit of measurement cycles
2025-06-24 18:24:15 +02:00
karwosts
657a068087 Cleanup some duplicated code (#147439) 2025-06-24 17:22:13 +01:00
Luca Angemi
af6c2b5c8a Add device class to wind direction sensors for AEMET (#147430) 2025-06-24 16:25:16 +01:00
Manu
d5a8fa9c5c Add DHCP discovery to PlayStation Network integration (#147422)
Add DHCP discovery for PSN
2025-06-24 17:17:02 +02:00
Abílio Costa
cefde21140 Update Shelly test snapshots (#147429) 2025-06-24 18:08:27 +03:00
hanwg
160163b0cc Remove deprecated proxy params from Telegram bot integration (#147288) 2025-06-24 16:46:31 +02:00
Michael Hansen
6ce594539f Bump wyoming to 1.7.1 (#147385)
* Bump wyoming to 1.7.0

* Bump to 1.7.1 for Python version fix

* Address mypy errors
2025-06-24 09:28:09 -05:00
Robert Resch
4ca39ec7c3 Add range icons for wind_direction sensor device class (#147090)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-06-24 15:00:03 +01:00
Paul Bottein
cfdd7fbbce Add fields and multiple support to object selector (#147215)
* Add schema supports to object selector

* Update format

* Update homeassistant/helpers/selector.py

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

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-06-24 09:54:06 -04:00
Paulus Schoutsen
1cb36f4c18 Convert Claude to use subentries (#147285)
* Convert Claude to use subentries

* Add latest changes from Google subentries

* Revert accidental change to Google
2025-06-24 15:36:09 +02:00
Petar Petrov
602c1c64b3 Update ZwaveJS config flow strings (#147421)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-06-24 14:30:12 +01:00
Andre Lengwenus
3b8d6eb851 Log LCN connection established with log level info (#147424) 2025-06-24 15:24:25 +02:00
Franck Nijhof
9bb98eb514 Merge branch 'master' into dev 2025-06-24 13:19:28 +00:00
karwosts
39c431c55c Add 'max_sub_interval' option to derivative sensor (#125870)
* Add 'max_sub_interval' option to derivative sensor

* add strings

* little coverage

* improve test accuracy

* reimplement at dev head

* string

* handle unavailable

* simplify

* Add self to codeowner

* fix on remove

* Update homeassistant/components/derivative/sensor.py

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

* Fix parenthesis

* sort strings

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-24 15:05:28 +02:00
Franck Nijhof
0171b527d8 2025.6.3 (#147419)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-06-24 14:58:48 +02:00
Paulus Schoutsen
7cccdf2205 Add accept keyword to Media selector (#145527)
* Add accept keyword to Media selector

* Adjust test
2025-06-24 07:36:48 -05:00
Petro31
97f3bb3da5 Add default to from_json (#146211) 2025-06-24 14:27:14 +02:00
Parker Wahle
fc62a6cd89 Add streaming support w/ audio to Android IP Webcam integration (#126009)
* Add streaming support w/ audio to Android IP Webcam integration

* ruff reformat

* Fix ruff

* Break long comments and strings

* Add camera test

* Fix docstring

* Remove dead code

* Call library function to get URL

* Simplify

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-24 13:54:34 +02:00
Josef Zweck
23b90f5984 Add door state sensors to tedee (#147386) 2025-06-24 12:30:13 +01:00
Paulus Schoutsen
63ac14a19b AI task generate_text -> generate_data (#147370) 2025-06-24 12:12:29 +01:00
Franck Nijhof
94fd9d1657 Bump version to 2025.6.3 2025-06-24 11:09:26 +00:00
Bram Kragten
2f89317fed Update frontend to 20250531.4 (#147414) 2025-06-24 11:09:03 +00:00
Manu
38c7eaf70a Add reauth flow to PlayStation Network integration (#147397)
* Add reauth flow to psn integration

* changes

* catch auth error in coordinator
2025-06-24 12:20:08 +02:00
dependabot[bot]
02e33c3551 Bump sigstore/cosign-installer from 3.8.2 to 3.9.0 (#147072)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-24 10:50:32 +02:00
Bram Kragten
d5187a6a40 Update frontend to 20250531.4 (#147414) 2025-06-24 10:49:51 +02:00
CubeZ2mDeveloper
703032ab27 Added auto-discovery configuration for SONOFF Dongle Max in zha. (#140574)
Co-authored-by: zetao.zheng <1050713479@qq.com>
2025-06-24 10:19:08 +02:00
Maciej Bieniek
438aa3486d Add full device snapshot tests for Shelly (#145620) 2025-06-24 10:16:46 +02:00
Duco Sebel
f2944f4d8e Add support for v2 API for HomeWizard kWh Meter (#147214) 2025-06-24 10:14:06 +02:00
Erik Montnemery
b8044f60fc Fix trigger config validation (#147408) 2025-06-24 10:13:44 +02:00
Michael Hansen
c67b497f30 Bump intents to 2025.6.23 (#147391) 2025-06-24 10:13:04 +02:00
puddly
aefd9c9b41 Bump universal-silabs-flasher to 0.0.31 (#147393) 2025-06-24 10:11:46 +02:00
Stefan Agner
e5d19baf3e Add container arch to system info (#147372) 2025-06-24 09:52:21 +02:00
Manu
121239bcf7 Fix unbound var and tests in PlayStation Network integration (#147398)
fix unbound var and test mocks
2025-06-24 08:53:45 +02:00
Geoff
eff35e93bd New core integration for VegeHub (#129598)
* Initial commit for VegeHub integration

* Moved several pieces to library, continuing.

* All device contact moved to library

* Updated documentation link

* Fixed an error in strings.json

* Removed commented out code and unused file

* Removed unneeded info logging, and a few missed lines of commented code

* Added/removed comments for clarity

* Converted integration to use webhooks.

* Update __init__.py to remove unnecessary code.

Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>

* Remove unnecessary code from config_flow.py

Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>

* Simplify unique_id assertion.

* Switch to CONF_ constant for user input

* Added explanation for passing exception.

* Got rid of try-except, since I don't really handle the exceptions her anyway.

* Moved data transform to vegehub library

* Changed references to use HA constants.

* Fixed assigning and returning _attr properties.

* Moved temperature sensor transform to the library.

* Moved sensor names to strings.json

* Made webhook names unique to avoid collisions when multiple devices are added.

* Converted to using entry.runtime_data

* Removed options flow for first PR

* Removed switch support to limit PR to one platform

* Removed/updated outdated tests

* Update homeassistant/components/vegehub/__init__.py

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

* Got rid of strings in favor of constants.

* Got rid of unnecessary check

* Imported constant directly.

* Added custom type for entry

* Expanded CONF_ constants into sensor.py

* Get rid of extra `str` and `get`

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

* Added type to errors

* Added try/except to MAC address retrieval

* Moved functionality out of ConfigFlow that shouldn't have been there

* Removed IP:MAC tracking from ConfigFlow

* Added retries to VegeHub PyPI package, and implemented them in integration

* Removed different sensor types for now

* Fixed typo

* Changed abort to error

* Fixed error reporting in config flow

* Further simplify sensor.py to handle all sensors the same

* Added comment to clarify

* Got rid of unused constants

* Removed unused strings in strings.json

* Added quality_scale.yaml

* Fixed problems in sensor init

* Moved config url and sw version storage into vegehub package

* Get rid of extra declaration

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

* Removed unnecessary task

* Fix type for entry

* Added a test before setup

* Fixed tests and got test coverage of config flow to 100%

* Fixed test descriptions

* Implemented a coordinator

* Removed unused property

* Fixed a few minor issues with the coordinator implementation

* Removed unused function

* Fixed some tests

* Trying to fix a problem with re-initialization when server reboots. Mostly working.

* Moved hub.setup from async_setup_entry to config flow to avoid running it on system reboot

* Delete tests/testing_config/.storage/http.auth

* Fixed errors in coordinator.py

* Added IP validation for manual input IP addresses

* Moved data into self._discovered to simplify

* Removed redundant typing

* Shortened sensor unique ID and added coordinator handler

* Added call to super()._handle_coordinator_update() so state gets handled correctly

* Fixed == and is

* Got rid of "slot" and moved functionality to lib

* Got rid of mocked aiohttp calls in favor of just mocking the vegehub library

* Rewrote config flow to make more sense.

* Changed order of data and data_description

* Changes to sensor.py

* Got rid of async_update_data in coordinator and moved async_set_updated_data into webhook callback

* Changed sensor updates so that they keep using last known values if update doesn't contain data for them

* Changed config flow to use homeassistant.helpers.service_info zeroconf instead of homeassistant.components zeroconf

* Added types to test parameters

* Changes and notes in config_flow.py

* Minor fix to get existing tests working before making changes to tests

* Removed unused data and simplified data passing

* Fixed tests, removed unused data, moved sensor tests to snapshots

* Mocked async_setup_entry and async_unload_entry

* Eliminated retry step so that retries just happen in the user flow or zeroconf_confirm

* Bumped the library version

* Bumped library version again

* Changed test-before-setup test

* Improved use of coordinator

* Almost done reworking tests. A few more changes still needed.

* Added via device to sensor.py and key reference to strings.json

* Webhook tests are almost, but not quite, working

* Fully functional again

* Change error to assert

* made identifiers and via_device the same

* made the via_device just be the mac

* Fixed strings.json and updated translations

* Fixed test_sensor.py

* Cleaned up tests and added autouse to several fixtures to simplify

* Switched from error to assert, and added exemption to quality scale.

* Cleaned up some tests and added update of IP if unique ID of discovered device is the same.

* Improved zeroconfig to update IP and hostname, and added a test to make sure those work.

* Fixed a comment.

* Improved ip/hostname update test.

* Changed Hub to VegeHub in strings.json for clarity.

* Switched to using a base entity to simplify and make adding platforms in the future easier.

* Moved the vegehub object into the coordinator to simplify.

* Removed actuators from sensors, and added unique name for battery sensor

* Changed coordinator to manage its own data, changed sensors to use descriptions and return their value as a property

* Updated data retrieval keys

* Minor updates to several files

* Fixed a few things for pytest

* Reverted to explicit check for None for pytest

* Fixed a comment and a variable name

* Fixed a comment

* Fix

* Bumped depenency version to eliminate pytest from dependencies.

---------

Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-24 06:55:34 +02:00
Paulus Schoutsen
0cf7952964 Remove duplicated subentry device update in Google Gen AI + add merge test (#147396)
* late comments on Google subentries

* Add test that merges 2 config entries
2025-06-23 22:34:06 -04:00
Paulus Schoutsen
56f4039ac2 Migrate Google Gen AI to use subentries (#147281)
* Migrate Google Gen AI to use subentries

* Add reconfig successful msg

* Address comments

* Do not allow addin subentry when not loaded

* Let HA do the migration

* Use config_entries.async_setup

* Remove fallback name on base entity

* Fix

* Fix

* Fix device name assignment in entity and tts modules

* Fix tests

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-23 20:59:32 -04:00
Andrey Kupreychik
6641cb3799 Handle router initialization, connection errors, and missing interfaces in options flow (#143475)
* Handle router initialization and connection errors in options flow

Added checks in the Keenetic NDMS2 options flow to handle cases where the integration is not initialized or there are connection errors. Relevant user feedback and abort reasons are now provided to ensure a better user experience.

* Add filtering saved/default options for interfaces before preparing an options form
2025-06-23 23:52:23 +02:00
Jack Powell
c671ff3cf1 Add PlayStation Network Integration (#133901)
* clean pull request

* Create one device per console

* Requested changes

* Pr/tr4nt0r/1 (#2)

* clean pull request

* Create one device per console

* device setup

* Merge PR1 - Dynamic Device Support

* Merge PR1 - Dynamic Device Support

---------

Co-authored-by: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com>

* nitpicks

* Update config_flow test

* Update quality_scale.yaml

* repair integrations.json

* minor updates

* Add translation string for invalid account

* misc changes post review

* Minor strings updates

* strengthen config_flow test

* Requested changes

* Applied patch to commit a358725

* migrate PlayStationNetwork helper classes to HA

* Revert to standard psn library

* Updates to media_player logic

* add default_factory, change registered_platforms to set

* Improve test coverage

* Add snapshot test for media_player platform

* fix token parse error

* Parametrize media player test

* Add PS3 support

* Add PS3 support

* Add concurrent console support

* Adjust psnawp rate limit

* Convert to package PlatformType

* Update dependency to PSNAWP==3.0.0

* small improvements

* Add PlayStation PC Support

* Refactor active sessions list

* shift async logic to helper

* Implemented suggested changes

* Suggested changes

* Updated tests

* Suggested changes

* Fix test

* Suggested changes

* Suggested changes

* Update config_flow tests

* Group remaining api call in single executor

---------

Co-authored-by: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-23 23:46:06 +02:00
Manu
646ddf9c2d Add sensors to ntfy integration (#145262)
* Add sensors

* small changes

* test coverage

* changes

* update snapshot
2025-06-23 23:17:43 +02:00
Paulus Schoutsen
95abd69cc6 Add media class to media player search and play intent (#147097)
Co-authored-by: Michael Hansen <mike@rhasspy.org>
2025-06-23 16:12:32 -05:00
Arie Catsman
ab0ea753e9 Optimize Enphase envoy translation strings. (#147389)
optimize Enphase envoy translation strings.
2025-06-23 22:59:50 +02:00
Åke Strandberg
9b915e996b Refactor states and strings for Miele plate power steps (#144992)
* WIP

* Fix type check

* Empty commit
2025-06-23 22:40:46 +02:00
hanwg
dc948e3b6c Add strict typing for Telegram bot integration (#147262)
add strict typing
2025-06-23 22:22:00 +02:00
epenet
8b6205be25 Remove JuiceNet integration (#147206) 2025-06-23 21:46:51 +02:00
Paulus Schoutsen
7f99cd2d2b Clean up start_subentry_reconfigure_flow API for tests (#147381) 2025-06-23 21:45:33 +02:00
Petro31
b4fe6f3843 Add trigger based fan entities to template integration (#145497)
* Add trigger based fan entities to template integration

* more changes

* add tests

* update doc strings

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-23 21:20:55 +02:00
Erik Montnemery
c29879274a Refactor DeviceAutomationConditionProtocol (#147377) 2025-06-23 21:18:56 +02:00
Joost Lekkerkerker
512449a76d Add Bluetooth connection to LaMetric (#147342) 2025-06-23 21:01:01 +02:00
Alex Biddulph
fc91047d8d Add sensors for detailed Enphase inverter readings (#146916)
* Add extra details to Enphase inverters

* Bump pyenphase version to 2.1.0

* Add new inverter sensors and translations

* Add new endpoint

* Start updating tests

* Remove duplicate class

* Add `max_reported` sensor

* Move translation strings to correct location

* Update fixtures and snapshots

* Update unit tests

* Fix linting

* Apply suggestions from code review

Co-authored-by: Arie Catsman <120491684+catsmanac@users.noreply.github.com>

* Fix Telegram bot parsing of inline keyboard (#146376)

* bug fix for inline keyboard

* update inline keyboard test

* Update tests/components/telegram_bot/test_telegram_bot.py

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

* revert last_message_id and updated tests

* removed TypeError test

---------

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

* Handle the new JSON payload from traccar clients (#147254)

* Set `entity_id`

* Update unit tests

* Bump aioamazondevices to 3.1.14 (#147257)

* Bump pyseventeentrack to 1.1.1 (#147253)

Update pyseventeentrack requirement to version 1.1.1

* Bump uiprotect to version 7.14.1 (#147280)

* Fix `state_class`es for energy production

* Make `max_reported` `name` more descriptive

* Update snapshots

* Reuse some translations

* Remove unnecessary translation keys

* Update unit tests

* Update homeassistant/components/enphase_envoy/strings.json

* Update homeassistant/components/enphase_envoy/strings.json

* Fix

---------

Co-authored-by: Arie Catsman <120491684+catsmanac@users.noreply.github.com>
Co-authored-by: hanwg <han.wuguang@gmail.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Joakim Sørensen <joasoe@proton.me>
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
Co-authored-by: Shai Ungar <shai.ungar@riskified.com>
Co-authored-by: Raphael Hehl <7577984+RaHehl@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-23 20:59:18 +02:00
starkillerOG
6af290eb74 Add Reolink Telephoto main stream (#146975) 2025-06-23 20:53:09 +02:00
starkillerOG
dd3d6f116e Rename second Reolink lens from "autotrack" to "telephoto" (#146898)
* Rename second Reolink lens from "autotrack" to "telephoto"

* Adjust tests
2025-06-23 20:45:24 +02:00
starkillerOG
b4af9a31cb Add multiple cmd_id pushes for Reolink floodlight (#146685)
Allow for multiple cmd_id pushes
2025-06-23 20:44:35 +02:00
starkillerOG
2862f76fca Add support for Reolink Floodlight PoE/WiFi (#146778)
* Add support for Floodlight PoE/WiFi

* Adjust test

* Add test
2025-06-23 20:43:01 +02:00
Matthias Alphart
3806e5b65c Set KNX to quality scale "silver" (#144879)
Update KNX integration quality scale
2025-06-23 20:41:00 +02:00
Marcel van der Veldt
673a2e35ad Add button entity to Music Assistant to add currently playing item to favorites (#145626)
* Add action to Music Assistant to add currently playing item to favorites

* add test

* Convert to button entity

* review comments

* Update test_button.ambr

* Fix

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-06-23 20:39:46 +02:00
Franck Nijhof
773c25041a 2025.6.2 (#147355)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Brian Rogers <brg468@hotmail.com>
Co-authored-by: Raphael Hehl <7577984+RaHehl@users.noreply.github.com>
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
Co-authored-by: Andre Lengwenus <alengwenus@gmail.com>
Co-authored-by: Chris Talkington <chris@talkingtontech.com>
Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: elmurato <1382097+elmurato@users.noreply.github.com>
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
Co-authored-by: Hessel <hesselonline@users.noreply.github.com>
Co-authored-by: Ernst Klamer <e.klamer@gmail.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Ravaka Razafimanantsoa <3774520+SeraphicRav@users.noreply.github.com>
Co-authored-by: Allen Porter <allen.porter@gmail.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: J. Diego Rodríguez Royo <jdrr1998@hotmail.com>
Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Co-authored-by: hahn-th <15319212+hahn-th@users.noreply.github.com>
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Joakim Sørensen <joasoe@proton.me>
Co-authored-by: Michael Hansen <mike@rhasspy.org>
Fix blocking open in Minecraft Server (#146820)
Fix missing key for ecosmart in older Wallbox models (#146847)
Fix device type filtering in sensor (#146945)
Fix incorrect use of zip in service.async_get_all_descriptions (#147013)
Fix Shelly entity names for gen1 sleeping devices (#147019)
Fix log in onedrive (#147029)
Fix Charge Cable binary sensor in Teslemetry (#147136)
fix too many requests by API (#147197)
Fix reload for Shelly devices with no script support (#147344)
2025-06-23 20:37:52 +02:00
Petro31
e494f66c02 Add label_description to template engine (#147138)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-06-23 19:21:29 +01:00
tronikos
2833e97625 Default to gemini-2.5-flash (#147334) 2025-06-23 20:11:16 +02:00
Joost Lekkerkerker
442fb88011 Add update platform to LaMetric (#147354) 2025-06-23 20:08:13 +02:00
epenet
6b242fd277 Migrate lifx to use runtime_data and HassKey (#147348) 2025-06-23 20:01:21 +02:00
Joost Lekkerkerker
06ed452d8f Add Matter protocol to Switchbot (#147356) 2025-06-23 17:45:31 +00:00
G Johansson
a7de947f00 Add vacuum activity to pylint type hints check (#147162) 2025-06-23 18:12:18 +01:00
epenet
dfa3fddd35 Migrate livisi to use runtime_data (#147352) 2025-06-23 19:09:38 +02:00
Josef Zweck
ce115cbfe1 Bump aiotedee to 0.2.25 (#147349)
* Bump aiotedee to 0.2.24

* bump to 25

* fix snapshot
2025-06-23 19:08:48 +02:00
epenet
e1d5d312b8 Migrate linear_garage_door to use runtime_data (#147351)
Migrate linear_garage_door to use runtime_data/HassKey
2025-06-23 19:08:32 +02:00
Andre Lengwenus
27565df86f Add PARALLEL_UPDATES constant to binary_sensor and sensor for LCN (#147369)
Add PARALLEL_UPDATES to binary_sensor and sensor
2025-06-23 19:08:18 +02:00
Petro31
7eaa60b17c Add trigger vacuum entities to template integration (#145534)
* Add trigger vacuum entities to template integration

* remove comment

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-06-23 18:10:44 +02:00
Foscam-wangzhengyu
7bb9936e81 Replace foscam dependency (#145766)
* Update Public Library

* Update conftest.py
2025-06-23 18:10:31 +02:00
J. Nick Koston
ccbc5ed65b Bump aioesphomeapi to 3.1.1 (#147345) 2025-06-23 17:50:56 +02:00
Ludovic BOUÉ
e98ec38ad8 Matter energy optimization opt-out attribute (#147096)
* ESAStateEnum

* Update snapshot

* Add test

* Update homeassistant/components/matter/strings.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Update homeassistant/components/matter/strings.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Update homeassistant/components/matter/icons.json

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

* Update homeassistant/components/matter/sensor.py

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

* Update homeassistant/components/matter/strings.json

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

* Update sensor.py

* Update snapshot

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-23 17:26:27 +02:00
Paulus Schoutsen
a11e274434 Address AI Task late comments (#147313) 2025-06-23 16:58:42 +02:00
Alena Bugrova
f8267b13d7 Add Altruist integration to Core (#146158)
* add altruist integration and tests

* requested fixes + remove some deprecated sensors

* add tests for unknown sensor and device attribute in config_flow

* use CONF_ in data_schema

* suggested fixes

* remove test_setup_entry_success

* create ZeroconfServiceInfo in tests

* use CONF_IP_ADDRESS in tests

* add unique id assert

* add integration to strict-typing, set unavailable if no sensor key in data, change device name

* use add_suggested_values_to_schema, mmHg for pressure

* update snapshots and config entry name in tests

* remove changes in devcontainer config

* fixture for create client error, typing in tests, remove "Altruist" from device name

* change native_value_fn return type

* change sensor.py docstring

* remove device id from entry data, fix docstrings

* remove checks for client and device attributes

* use less variables in tests

* change creating AltruistSensor, remove device from arguments

* Update homeassistant/components/altruist/sensor.py

* Update homeassistant/components/altruist/quality_scale.yaml

* Update homeassistant/components/altruist/quality_scale.yaml

* Update quality_scale.yaml

* hassfest run

* suggested fixes

* set suggested_unit_of_measurement for pressure

* use mock_config_entry, update snapshots

* abort if cant create client on zeroconf step

* move sensor names in translatin placeholders

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-06-23 16:57:51 +02:00
Sanjay Govind
3798e99ac8 Update bosch_alarm to platinum quality scale (#145027)
* update quality scale for bosch_alarm

* update quality scale

* update quality scale
2025-06-23 16:42:14 +02:00
LG-ThinQ-Integration
fa71c40ff5 Bump thinqconnect to 1.0.7 (#147073)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-06-23 16:12:28 +02:00
Petro31
8e6edf5e34 Add trigger based locks to template integration (#145528)
* Add trigger based locks to template integration

* fix comments
2025-06-23 16:11:15 +02:00
Petro31
c1e32aa9b7 Add trigger template alarm control panels (#145461)
* Add trigger template alarm control panels

* updates

* fix jumbled imports

* fix comments
2025-06-23 16:10:50 +02:00
Michael Heyman
b48ebeaa8a Tilt Pi integration (#139726)
* Create component via script.scaffold

* Create sensor definition

* Define coordinator

* Define config flow

* Refine sensor definition and add tests

* Refine coordinator after testing end to end

* Redefine sensor in a more idiomatic way

* Use entity (common-module)

* Follow config-flow conventions more closely

* Use custom ConfigEntry to conform to strict-typing

* Define API object instead of using aio directly

* Test before setup in init

* Add diagnostics

* Make some more quality changes

* Move scan interval to const

* Commit generated files

* Add quality scale

* feedback: Apply consistent language to Tilt Pi refs

* feedback: Remove empty manifest fields

* feedback: Use translations instead of hardcoded name

* feedback: Remove diagnostics

* feedback: Idiomatic and general improvements

* Use tilt-pi library

* feedback: Coordinator data returns dict

* feedback: Move client creation to coordinator

* feedback: Request only Tilt Pi URL from user

* Update homeassistant/components/tilt_pi/entity.py

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

* Update homeassistant/components/tilt_pi/sensor.py

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

* Update homeassistant/components/tilt_pi/entity.py

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

* feedback: Avoid redundant keyword arguments in function calls

* feedback: Remove unused models and variables

* feedback: Use icons.json

* feedback: Style best practices

* Update homeassistant/components/tilt_pi/entity.py

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

* Update tests/components/tilt_pi/test_config_flow.py

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

* feedback: Improve config flow unit tests

* feedback: Patch TiltPi client mock

* feedback: Mark entity-device-class as done

* feedback: Align quaity scale with current state

* feeback: Create brands file for Tilt brand

* feedback: Demonstrate recovery in config flow

* feedback: Test coordinator behavior via sensors

* Update homeassistant/components/tilt_pi/config_flow.py

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

* Update homeassistant/components/tilt_pi/coordinator.py

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

* Update homeassistant/components/tilt_pi/quality_scale.yaml

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

* Update homeassistant/components/tilt_pi/quality_scale.yaml

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

* Update homeassistant/components/tilt_pi/quality_scale.yaml

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

* Update homeassistant/components/tilt_pi/config_flow.py

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

* feedback: Update tilt_pi quality scale

* feedback: Move const to coordinator

* feedback: Correct strings.json for incorrect and missing fields

* feedback: Use tiltpi package version published via CI

* Run ruff format manually

* Add missing string for invalid host

* Fix

* Fix

---------

Co-authored-by: Michael Heyman <michaelheyman@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-06-23 16:09:41 +02:00
Joost Lekkerkerker
0c08b4fc8b Add Matter protocol to Switchbot (#147356) 2025-06-23 16:06:19 +02:00
Ludovic BOUÉ
9ae3129f16 Matter battery storage (#147235)
* BatCapacity

* BatCapacity

* PowerSourceBatTimeRemaining

* BatChargeState

* Update strings.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Review fixes

* Remove uneeded BatCapacity

* Update strings.json

* Update strings.json

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

* Update snapshots

* Update strings.json

* Update snapshot

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-23 16:04:40 +02:00
Joost Lekkerkerker
d38c880c45 Bump demetriek to 1.3.0 (#147350)
* Bump demetriek to 1.3.0

* Fix
2025-06-23 15:32:57 +02:00
epenet
7ec2e0c524 Move lyric coordinator to separate module (#147357) 2025-06-23 15:10:12 +02:00
Franck Nijhof
570315687f Bump version to 2025.6.2 2025-06-23 12:55:05 +00:00
Simone Chemelli
dfd42863bb Fix reload for Shelly devices with no script support (#147344) 2025-06-23 12:54:48 +00:00
J. Nick Koston
105618734c Bump aioesphomeapi to 33.0.0 (#147296)
fixes compat warning with protobuf 6.x

changelog: https://github.com/esphome/aioesphomeapi/compare/v32.2.4...v33.0.0

Not a breaking change for HA since we are already on protobuf 6
2025-06-23 12:54:47 +00:00
Michael Hansen
9d0701198f Bump aioesphomeapi to 32.2.4 (#147100)
Bump aioesphomeapi
2025-06-23 12:54:46 +00:00
Raphael Hehl
f9d5cb957f Bump uiprotect to version 7.14.1 (#147280) 2025-06-23 12:52:42 +00:00
Simone Chemelli
f7d9334445 Bump aioamazondevices to 3.1.14 (#147257) 2025-06-23 12:52:41 +00:00
Joakim Sørensen
60be2cb168 Handle the new JSON payload from traccar clients (#147254) 2025-06-23 12:52:40 +00:00
Robert Resch
ddf8e0de4b Bump deebot-client to 13.4.0 (#147221) 2025-06-23 12:50:27 +00:00
Hessel
7cc6e28916 Wallbox fix too many requests by API (#147197) 2025-06-23 12:50:26 +00:00
hahn-th
802fcab1c6 Bump homematicip to 2.0.6 (#147151) 2025-06-23 12:50:25 +00:00
Marc Mueller
3534396028 [ci] Bump cache key version (#147148) 2025-06-23 12:50:24 +00:00
Brett Adams
458aa3cc22 Fix Charge Cable binary sensor in Teslemetry (#147136) 2025-06-23 12:50:22 +00:00
Martin Hjelmare
39b64b0af3 Improve advanced Z-Wave battery discovery (#147127) 2025-06-23 12:50:21 +00:00
Raphael Hehl
c66d411826 Bump uiprotect to version 7.14.0 (#147102) 2025-06-23 12:50:20 +00:00
Simone Chemelli
0b383b7493 Bump aioamazondevices to 3.1.12 (#147055)
* Bump aioamazondevices to 3.1.10

* bump to 3.1.12
2025-06-23 12:50:19 +00:00
Simone Chemelli
1a3384e8b4 Bump aioamazondevices to 3.1.4 (#146883) 2025-06-23 12:50:18 +00:00
Josef Zweck
2c357265b0 Handle missing widget in lamarzocco (#147047) 2025-06-23 12:47:00 +00:00
Josef Zweck
c395c77cd3 Bump pylamarzocco to 2.0.9 (#147046) 2025-06-23 12:46:59 +00:00
puddly
57eceeea38 Bump ZHA to 0.0.60 (#147045) 2025-06-23 12:46:58 +00:00
J. Diego Rodríguez Royo
f75ba9172c Bump aiohomeconnect to 0.18.0 (#147044) 2025-06-23 12:46:57 +00:00
G Johansson
a15d722f0e Bump holidays lib to 0.75 (#147043) 2025-06-23 12:46:55 +00:00
Josef Zweck
a07531d0e7 Fix log in onedrive (#147029) 2025-06-23 12:46:54 +00:00
Martin Hjelmare
96d6cacae4 Disable Z-Wave idle notification button (#147026)
* Update test

* Disable Z-Wave idle notification button

* Update tests
2025-06-23 12:46:53 +00:00
Maciej Bieniek
766ddfaacc Fix Shelly entity names for gen1 sleeping devices (#147019) 2025-06-23 12:46:52 +00:00
Martin Hjelmare
5ea6cb3846 Disable Z-Wave indidator CC entities by default (#147018)
* Update discovery tests

* Disable Z-Wave indidator CC entities by default
2025-06-23 12:46:52 +00:00
Erik Montnemery
912c4804cb Fix incorrect use of zip in service.async_get_all_descriptions (#147013)
* Fix incorrect use of zip in service.async_get_all_descriptions

* Fix lint errors in test
2025-06-23 12:46:50 +00:00
Allen Porter
8f13520a1c Bump ical to 10.0.4 (#147005)
* Bump ical to 10.0.4

* Bump ical to 10.0.4 in google
2025-06-23 12:46:50 +00:00
Joost Lekkerkerker
d2d5b29e2b Bump pySmartThings to 3.2.5 (#146983) 2025-06-23 12:46:48 +00:00
Ravaka Razafimanantsoa
94a2642ce9 Switchbot Cloud: Fix device type filtering in sensor (#146945)
* Add Smart Lock Ultra support and fix device type filtering in sensor integration

* Adding fix in binary sensor

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-23 12:46:47 +00:00
Josef Zweck
d0060a2b21 Add debug log for update in onedrive (#146907) 2025-06-23 12:46:46 +00:00
starkillerOG
9b744e2fef Bump reolink-aio to 0.14.1 (#146903) 2025-06-23 12:46:45 +00:00
Ernst Klamer
d66dee5411 Bump bthome-ble to 3.13.1 (#146871) 2025-06-23 12:46:45 +00:00
Hessel
da97756157 Fix missing key for ecosmart in older Wallbox models (#146847)
* fix 146839, missing key

* added tests for this issue

* added tests for this issue

* added tests for this issue, formatting

* Prevent loading select on missing key

* Prevent loading select on missing key - formatting fixed

* Update homeassistant/components/wallbox/coordinator.py

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-23 12:46:43 +00:00
Andre Lengwenus
a7b2f800f8 Bump pypck to 0.8.8 (#146841) 2025-06-23 12:46:42 +00:00
starkillerOG
05831493e2 Bump motion blinds to 0.6.28 (#146831) 2025-06-23 12:46:42 +00:00
J. Nick Koston
8e685b1626 Bump aiohttp to 3.12.13 (#146830)
changelog: https://github.com/aio-libs/aiohttp/compare/v3.12.12...v3.12.13

Likely does not affect us at all but just in case, tagging
2025-06-23 12:44:59 +00:00
Franck Nijhof
87ecf552dc Add unique ID support to Trend integration YAML configuration (#147346) 2025-06-23 14:44:58 +02:00
Simone Chemelli
a6e6b6db5a Bump aioamazondevices to 3.1.3 (#146828) 2025-06-23 12:42:08 +00:00
elmurato
d684360ebd Fix blocking open in Minecraft Server (#146820)
Fix blocking open by dnspython
2025-06-23 12:42:07 +00:00
Maciej Bieniek
01a133a2b8 Use Shelly main device area as suggested area for sub-devices (#146810) 2025-06-23 12:42:06 +00:00
Chris Talkington
b249ae408f Update rokuecp to 0.19.5 (#146788) 2025-06-23 12:42:05 +00:00
Andre Lengwenus
04b3227b9b Bump pypck to 0.8.7 (#146657) 2025-06-23 12:42:04 +00:00
starkillerOG
0a8d117129 Bump reolink-aio to 0.14.0 (#146566) 2025-06-23 12:42:03 +00:00
Raphael Hehl
83f26f7393 Bump uiprotect to 7.13.0 (#146410) 2025-06-23 12:42:02 +00:00
Raphael Hehl
9ed6f226c6 Bump uiprotect to 7.12.0 (#146337) 2025-06-23 12:42:00 +00:00
Brian Rogers
2ba9cb1510 Remove address info from Rachio calendar events (#145896)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-23 12:42:00 +00:00
Simone Chemelli
2e155831e6 Fix reload for Shelly devices with no script support (#147344) 2025-06-23 14:38:16 +02:00
Hessel
756b858840 Wallbox fix too many requests by API (#147197) 2025-06-23 14:10:50 +02:00
rrooggiieerr
b2520394f4 Lametric add configuration url (#147118)
* Set cofiguration URL to LaMetric device web interface

* Update LaMetric unit tests to accomodate fro configuration url
2025-06-23 13:47:52 +02:00
Maciej Bieniek
2a97b128c3 Bump IMGW-PIB backend library to version 1.1.0 (#147341) 2025-06-23 13:42:33 +02:00
epenet
d06da8c2da Migrate lcn to use runtime_data (#147333) 2025-06-23 13:41:53 +02:00
Erik Montnemery
2bfb09cb11 Improve test of WS command get_services cache handling (#147134) 2025-06-23 13:29:29 +02:00
Guido Schmitz
bf733fdec5 Remove config flow unique_id migration from devolo Home Control (#147327)
Remove config flow unique_id conversion from devolo Home Control
2025-06-23 13:16:57 +02:00
epenet
3b4eb7c749 Migrate lametric to use runtime_data (#147328)
* Migrate lametric to use runtime_data

* One more

* Drop unused hass_config
2025-06-23 13:15:08 +02:00
Artur Pragacz
1119716c32 Clean superfluous cloud deps from pyproject (#147223) 2025-06-23 13:15:01 +02:00
epenet
0ab23ccb51 Migrate landisgyr_heat_meter to use runtime_data (#147329) 2025-06-23 13:14:38 +02:00
Matrix
436fcb7e85 Fixed YoLink incorrect valve status (#147021)
* Fix valve status

* Fix as suggested
2025-06-23 13:14:18 +02:00
epenet
4d2f0f2de6 Migrate laundrify to use runtime_data (#147331)
* Migrate laundrify to use runtime_data

* Adjust test
2025-06-23 13:14:11 +02:00
Ludovic BOUÉ
82c1751f85 Matter dishwasher alarm (#146842)
* Update binary_sensor.py

* Update silabs_dishwasher.json

DishwasherAlarm

* DishwasherAlarm

* Update snapshot

* DishwasherAlarm

* test_dishwasher_alarm

* DishwasherAlarm

* Update silabs_dishwasher.json

* Update snapshot
2025-06-23 12:40:37 +02:00
epenet
a2785a86dc Migrate ld2410_ble to use runtime_data (#147335) 2025-06-23 12:13:10 +02:00
epenet
741e89383b Migrate leaone to use runtime_data (#147336) 2025-06-23 12:12:32 +02:00
epenet
f64533e9e0 Migrate led_ble to use runtime_data (#147337) 2025-06-23 12:11:03 +02:00
epenet
b13dd4e6ca Migrate lg_netcast to use runtime_data (#147338) 2025-06-23 12:09:51 +02:00
Noah Husby
35f310748e Add switch entity to Russound RIO (#147323)
* Add switch entity to Russound RIO

* Add switch snapshot
2025-06-23 10:42:36 +02:00
epenet
69d2cd0ac0 Migrate lastfm to use runtime_data (#147330) 2025-06-23 10:40:48 +02:00
Marc Mueller
10c573bbc3 Use PEP 695 TypeVar syntax for unifi (#147157) 2025-06-23 10:34:35 +02:00
Brian Rogers
93030ad48d Remove address info from Rachio calendar events (#145896)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-23 10:31:35 +02:00
Noah Husby
a7290f92cf Add number entity to Russound RIO (#147228)
* Add number entity to Russound RIO

* Fixes

* Fix tests

* Change entity name
2025-06-23 00:02:16 +02:00
Simone Chemelli
b47706f360 Add sensor platform to Alexa Devices (#146469)
* Add sensor platform to Amazon Devices

* fix merge after rename

* fix requirements

* cleanup

* Revert "cleanup"

This reverts commit f34892da8a.

* tests

* move logic in sensor entity description

* update tests

* apply review comment

* apply review comments
2025-06-23 00:01:15 +02:00
DeerMaximum
25968925e7 Use has_entity_name in NINA (#146755)
* Comply with has-entity-name rule.

* Fix tests
2025-06-22 23:57:33 +02:00
msw
fcba1183f8 Add water filter replacement and usage sensors to SmartThings (#147279)
* Add "Filter status" binary sensor for Samsung refrigerators

* Add "Water filter usage" sensor for Samsung refrigerators
2025-06-22 23:57:02 +02:00
Guido Schmitz
75946065f2 Combine executor calls in devolo Home Control (#147216) 2025-06-22 23:55:51 +02:00
Markus Adrario
3734c4e91d fix reconfig in case of no connection. (#147275) 2025-06-22 19:05:56 +02:00
Michael
7d421bf223 Fix regex patterns in foobot sensor tests (#147306) 2025-06-22 19:02:43 +02:00
Michael
41e53297c2 Add update entity to immich integration (#147273)
* add update entity

* remove unneccessary entity description

* rename update entity to version

* simplify test

* define static attribute outside of the constructor

* move min version check into coordinator
2025-06-22 16:54:48 +02:00
J. Nick Koston
d4e7667ea0 Bump aioesphomeapi to 33.0.0 (#147296)
fixes compat warning with protobuf 6.x

changelog: https://github.com/esphome/aioesphomeapi/compare/v32.2.4...v33.0.0

Not a breaking change for HA since we are already on protobuf 6
2025-06-22 10:24:16 -04:00
Ravaka Razafimanantsoa
daa4ddabfe Switchbot Cloud: Fix device type filtering in sensor (#146945)
* Add Smart Lock Ultra support and fix device type filtering in sensor integration

* Adding fix in binary sensor

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-22 14:12:09 +02:00
Michael
8cead00bc7 Bump aioimmich to 0.10.1 (#147293)
bump aioimmich to 0.10.1
2025-06-22 12:19:03 +02:00
G Johansson
db3090078b Remove deprecated support feature values in camera (#146988) 2025-06-22 09:31:16 +02:00
Ludovic BOUÉ
66e2fd997b Battery voltage translation key (#147238)
* Add translation_key

* Update strings.json

* Update snapshots

* Switch icon to DC

* Update snapshots
2025-06-22 09:27:44 +02:00
Raphael Hehl
a102eaf0cd Bump uiprotect to version 7.14.1 (#147280) 2025-06-22 02:14:26 +02:00
Shai Ungar
f3533dff44 Bump pyseventeentrack to 1.1.1 (#147253)
Update pyseventeentrack requirement to version 1.1.1
2025-06-21 22:50:53 +01:00
Simone Chemelli
c453eed32d Bump aioamazondevices to 3.1.14 (#147257) 2025-06-21 15:44:22 +02:00
Joakim Sørensen
79a9f34150 Handle the new JSON payload from traccar clients (#147254) 2025-06-21 11:53:17 +02:00
hanwg
7442f7af28 Fix Telegram bot parsing of inline keyboard (#146376)
* bug fix for inline keyboard

* update inline keyboard test

* Update tests/components/telegram_bot/test_telegram_bot.py

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

* revert last_message_id and updated tests

* removed TypeError test

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-21 03:21:10 +02:00
Markus Adrario
2e5de732a7 Bump pyHomee to version 1.2.10 (#147248)
bump pyHomee to version 1.2.10
2025-06-21 00:32:14 +01:00
Michael Hansen
9bcd74c449 Change async_supports_streaming_input to an instance method (#147245) 2025-06-20 15:39:22 -05:00
Noah Husby
ace18e540b Bump aiorussound to 4.6.1 (#147233) 2025-06-20 21:59:59 +02:00
Michael Hansen
65f897793d Use string instead of boolean for voice event (#147244)
Use string instead of bool
2025-06-20 15:18:03 -04:00
Robert Resch
435c08685d Bump deebot-client to 13.4.0 (#147221) 2025-06-20 20:22:33 +02:00
J. Diego Rodríguez Royo
95f292c43d Bump aiohomeconnect to 0.18.1 (#147236) 2025-06-20 19:27:29 +02:00
Manu
9346c584c3 Add reconfigure flow to ntfy integration (#143743) 2025-06-20 18:42:47 +02:00
Michael Hansen
6738085391 Minor clean up missed in previous PR (#147229) 2025-06-20 10:54:11 -05:00
Markus Adrario
d9e5bad55e Use entity name in homee (#147142)
* add name to HomeeEntity

* review change
2025-06-20 16:55:48 +02:00
Maciej Bieniek
f7429f3431 Fix Shelly entity names for gen1 sleeping devices (#147019) 2025-06-20 15:19:39 +02:00
Petar Petrov
46aea5d9dc Bump zwave-js-server-python to 0.64.0 (#147176) 2025-06-20 14:59:54 +02:00
Paulus Schoutsen
33bde48c9c AI Task integration (#145128)
* Add AI Task integration

* Remove GenTextTaskType

* Add AI Task prefs

* Add action to LLM task

* Remove WS command

* Rename result to text for GenTextTaskResult

* Apply suggestions from code review

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

* Add supported feature for generate text

* Update const.py

Co-authored-by: HarvsG <11440490+HarvsG@users.noreply.github.com>

* Update homeassistant/components/ai_task/services.yaml

Co-authored-by: HarvsG <11440490+HarvsG@users.noreply.github.com>

* Use WS API to set preferences

* Simplify pref storage

* Simplify pref test

* Update homeassistant/components/ai_task/services.yaml

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

---------

Co-authored-by: Allen Porter <allen.porter@gmail.com>
Co-authored-by: HarvsG <11440490+HarvsG@users.noreply.github.com>
2025-06-20 08:56:08 -04:00
Noah Husby
1b73acc025 Add sub-device support to Russound RIO (#146763) 2025-06-20 14:52:34 +02:00
Guido Schmitz
e28965770e Add translations for devolo Home Control exceptions (#147099)
* Add translations for devolo Home Control exceptions

* Adapt invalid_auth message

* Adapt connection_failed message
2025-06-20 14:31:16 +02:00
Kevin Stillhammer
f9d4bde0f6 Bump here-routing to 1.2.0 (#147204)
* Bump here-routing to 1.2.0

* Fix mypy typing errors

* Correct types for call assertion
2025-06-20 13:44:14 +02:00
Duco Sebel
a493bdc208 Implement battery group mode in HomeWizard (#146770)
* Implement battery group mode for HomeWizard P1

* Clean up test

* Disable 'entity_registry_enabled_default'

* Fix failing tests because of 'entity_registry_enabled_default'

* Proof entities are disabled by default

* Undo dev change

* Update homeassistant/components/homewizard/select.py

* Update homeassistant/components/homewizard/select.py

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

* Update homeassistant/components/homewizard/strings.json

* Apply suggestions from code review

* Update tests due to updated translations

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-20 13:19:45 +02:00
Markus Adrario
9ae9ad1e43 Improve test-coverage for homee locks (#147160)
test for unknown user
2025-06-20 12:28:49 +02:00
epenet
1b60ea8951 Migrate lutron to use runtime_data (#147198) 2025-06-20 12:26:07 +02:00
epenet
313eaff14e Migrate kaleidescape to use runtime_data (#147171)
* Migrate kaleidescape to use runtime_data

* Adjust tests
2025-06-20 12:25:57 +02:00
epenet
7dfd68f8c0 Migrate keenetic_ndms2 to use runtime_data (#147194)
* Migrate keenetic_ndms2 to use runtime_data

* Adjust tests
2025-06-20 12:23:59 +02:00
epenet
544fd2a4a6 Migrate lacrosse_view to use runtime_data (#147202) 2025-06-20 12:23:29 +02:00
epenet
cd51070219 Migrate kmtronic to use runtime_data (#147193) 2025-06-20 11:39:13 +02:00
Marc Mueller
3c91c78383 Use PEP 695 TypeVar syntax for ecovacs (#147153) 2025-06-20 10:41:25 +02:00
Brett Adams
96e0d1f5c6 Fix Charge Cable binary sensor in Teslemetry (#147136) 2025-06-20 10:39:43 +02:00
epenet
2859e7de9b Migrate kodi to use runtime_data (#147191) 2025-06-20 10:38:01 +02:00
Robert Resch
88683a318d Add support of taking a camera snapshot via go2rtc (#145205) 2025-06-20 10:34:43 +02:00
epenet
84e9422254 Move juicenet coordinator to separate module (#147168) 2025-06-20 10:33:17 +02:00
epenet
fde36d5034 Simplify update_listener in konnected (#147172) 2025-06-20 10:31:28 +02:00
Andre Lengwenus
8c1e43c07c Bump pypck to 0.8.9 (#147174) 2025-06-20 10:28:35 +02:00
epenet
05343392a7 Simplify update_listener in keenetic_ndms2 (#147173) 2025-06-20 10:27:47 +02:00
epenet
32314dbb13 Simplify update_listener in kmtronic (#147184) 2025-06-20 10:27:07 +02:00
epenet
8f661fc5cf Migrate kegtron to use runtime_data (#147177) 2025-06-20 10:26:53 +02:00
epenet
e315cb9859 Migrate kostal_plenticore to use runtime_data (#147188) 2025-06-20 10:25:08 +02:00
epenet
d0e77eb1e2 Migrate keymitt_ble to use runtime_data (#147179) 2025-06-20 10:24:56 +02:00
epenet
e23cac8bef Simplify remove listener in kodi (#147183) 2025-06-20 10:23:41 +02:00
epenet
973700542b Move kmtronic coordinator to separate module (#147182) 2025-06-20 10:19:19 +02:00
Krisjanis Lejejs
2e21493c19 Bump hass-nabucasa from 0.102.0 to 0.103.0 (#147186) 2025-06-20 10:18:03 +02:00
Markus Adrario
73bed96a0f remove unwanted attribute in homee sensor tests (#147158) 2025-06-20 08:11:20 +02:00
Markus Adrario
0a5d13f104 fix and improve cover tests for homee (#147164) 2025-06-20 08:10:44 +02:00
epenet
d16ec81727 Migrate justnimbus to use runtime_data (#147170) 2025-06-20 08:10:06 +02:00
Martin Hjelmare
11564e3df5 Fix Z-Wave device class endpoint discovery (#142171)
* Add test fixture and test for Glass 9 shutter

* Fix zwave_js device class discovery matcher

* Fall back to node device class

* Fix test_special_meters modifying node state

* Handle value added after node ready
2025-06-20 08:56:20 +03:00
Michael Hansen
341d9f15f0 Add ask_question action to Assist satellite (#145233)
* Add get_response to Assist satellite and ESPHome

* Rename get_response to ask_question

* Add possible answers to questions

* Add wildcard support and entity test

* Add ESPHome test

* Refactor to remove async_ask_question

* Use single entity_id instead of target

* Fix error message

* Remove ESPHome test

* Clean up

* Revert fix
2025-06-19 16:50:14 -05:00
Marc Mueller
2c13c70e12 Update ruff to 0.12.0 (#147106) 2025-06-19 20:39:09 +02:00
Marc Mueller
73d0d87705 Use PEP 695 TypeVar syntax for nextdns (#147155) 2025-06-19 20:26:07 +02:00
Marc Mueller
b8dfb2c850 Use PEP 695 TypeVar syntax for eheimdigital (#147154) 2025-06-19 20:25:45 +02:00
Marc Mueller
cf67a68454 Use PEP 695 TypeVar syntax for paperless_ngx (#147156) 2025-06-19 20:24:51 +02:00
karwosts
b003429912 Expose statistics selector, use for recorder.get_statistics (#147056)
* Expose statistics selector, use for `recorder.get_statistics`

* code review

* syntax formatting

* rerun ci
2025-06-19 20:04:28 +02:00
hahn-th
4aff032442 Bump homematicip to 2.0.6 (#147151) 2025-06-19 18:55:14 +02:00
Martin Hjelmare
da3d8a6332 Improve advanced Z-Wave battery discovery (#147127) 2025-06-19 18:56:47 +03:00
Marc Mueller
7a5c088149 [ci] Bump cache key version (#147148) 2025-06-19 17:42:30 +02:00
Norbert Rittel
31eec6f471 Add missing hyphen to "mains-powered" and "battery-powered" in zha (#147128)
Add missing hyphen to "mains-powered" and "battery-powered"
2025-06-19 14:36:40 +03:00
G Johansson
c602a0e279 Deprecated hass.http.register_static_path now raises error (#147039) 2025-06-19 13:14:42 +02:00
Marc Mueller
513045e489 Update pytest warnings filter (#147132) 2025-06-19 13:07:42 +02:00
Erik Montnemery
0db6520802 Add comment in helpers.llm.ActionTool explaining limitations (#147116) 2025-06-19 12:59:35 +02:00
Erik Montnemery
5bc2e271d2 Re-raise annotated_yaml.YAMLException as HomeAssistantError (#147129)
* Re-raise annotated_yaml.YAMLException as HomeAssistantError

* Fix comment
2025-06-19 12:52:01 +02:00
G Johansson
77dca49c75 Fix pylint plugin for vacuum entity (#146467)
* Clean out legacy VacuumEntity from pylint plugins

* Fix

* Fix pylint for vacuum

* More fixes

* Revert partial

* Add back state
2025-06-19 12:49:10 +02:00
Franck Nijhof
1baba8b880 Adjust feature request links in issue reporting (#147130) 2025-06-19 12:36:43 +02:00
Markus Adrario
875d81cab2 update pyHomee to v1.2.9 (#147094) 2025-06-19 12:04:59 +02:00
Raphael Hehl
956f726ef3 Bump uiprotect to version 7.14.0 (#147102) 2025-06-19 11:20:29 +02:00
epenet
fada81e1ce Bump ovoenergy to 2.0.1 (#147112) 2025-06-19 08:46:03 +02:00
Simon Lamon
6a16424bb4 Fix nightly build (#147110)
Update builder.yml
2025-06-19 08:20:19 +02:00
Abílio Costa
f90a740429 Use non-autospec mock for Reolink's binary_sensor, camera and diag tests (#147095) 2025-06-19 08:03:48 +02:00
Michael Hansen
3dba7e5bd2 Send intent progress events to ESPHome (#146966) 2025-06-18 22:12:37 -04:00
Erik Montnemery
8d8ff011fc Minor improvements of service helper (#147079) 2025-06-19 00:17:12 +01:00
Michael Hansen
6befd065a1 Bump aioesphomeapi to 32.2.4 (#147100)
Bump aioesphomeapi
2025-06-18 15:49:44 -05:00
Abílio Costa
9adf493acd Use non-autospec mock for Reolink's init tests (#146991) 2025-06-18 17:58:50 +01:00
Michael Hansen
a29d5fb56c tts_output is optional in run-start (#147092) 2025-06-18 12:08:53 -04:00
Petro31
bcb87cf812 Support variables, icon, and picture for all compatible template platforms (#145893)
* Fix template entity variables in blueprints

* add picture and icon tests

* add variable test for all platforms

* apply comments

* Update all test names
2025-06-18 16:49:46 +02:00
Jan Bouwhuis
d01758cea8 Ensure mqtt sensor has a valid native unit of measurement (#146722) 2025-06-18 15:48:38 +02:00
Joakim Sørensen
5487bfe1d9 Bump hass-nabucasa from 0.101.0 to 0.102.0 (#147087) 2025-06-18 15:47:01 +02:00
Simone Chemelli
fec65f40fc Bump aioamazondevices to 3.1.12 (#147055)
* Bump aioamazondevices to 3.1.10

* bump to 3.1.12
2025-06-18 10:20:51 +02:00
Guido Schmitz
596951ea9f Cleanup devolo Home Control tests (#147051) 2025-06-18 09:24:09 +02:00
Norbert Rittel
75d6b885cf Fix typo in state name references of homee (#146905)
Fix typo in state references

Replace wrong semicolons with colon.
2025-06-18 09:23:37 +02:00
Guido Schmitz
3fad76dfa1 Use missed typed ConfigEntry in devolo Home Control (#147049) 2025-06-18 09:22:37 +02:00
Pete Sage
43d8a151ab Remove internals from Sonos test_init.py (#147063)
* fix: test init

* fix: revert

* fix: revert

* fix: revert

* fix: revert

* fix: simplify
2025-06-18 09:21:21 +02:00
starkillerOG
07110e288d If no Reolink HTTP api available, do not set configuration_url (#146684)
* If no http api available, do not set configuration_url

* Add tests
2025-06-18 09:16:08 +02:00
Jan-Philipp Benecke
ba2aac4614 Bump aiowebdav2 to 0.4.6 (#147054) 2025-06-18 09:15:27 +02:00
msw
3449dae7a2 Capitalize "Ice Bites" and switch to "Cubed ice" (#147060) (#147061) 2025-06-18 09:14:45 +02:00
G Johansson
b8cd3f3635 Bump holidays lib to 0.75 (#147043) 2025-06-18 10:11:01 +03:00
Martin Hjelmare
be53ad5449 Disable Z-Wave idle notification button (#147026)
* Update test

* Disable Z-Wave idle notification button

* Update tests
2025-06-18 08:29:04 +03:00
J. Diego Rodríguez Royo
ffd940e07c Set quality scale at Home Connect manifest (#147050) 2025-06-17 21:42:40 +01:00
Josef Zweck
5e31b5ac4f Handle missing widget in lamarzocco (#147047) 2025-06-17 21:25:27 +02:00
puddly
81257f9d57 Bump ZHA to 0.0.60 (#147045) 2025-06-17 22:06:53 +03:00
Josef Zweck
ce1678719a Bump pylamarzocco to 2.0.9 (#147046) 2025-06-17 20:59:41 +02:00
Guido Schmitz
fc6844b3c9 Add _attr_has_entity_name to devolo Home Network device tracker platform (#146978)
* Add _attr_has_entity_name to devolo Home Network device tracker platform

* Set name

* Fix tests
2025-06-17 20:49:52 +02:00
J. Diego Rodríguez Royo
8e82e3aa3a Bump aiohomeconnect to 0.18.0 (#147044) 2025-06-17 20:48:09 +02:00
G Johansson
3bc68941e6 Remove not used constant in climate (#147041) 2025-06-17 20:43:16 +02:00
Josef Zweck
e69b38ab2c Fix log in onedrive (#147029) 2025-06-17 19:57:52 +02:00
Abílio Costa
ed9503324d Fix flaky Reolink webhook test (#147036) 2025-06-17 17:18:48 +01:00
Allen Porter
22a06a6c2e Bump ical to 10.0.4 (#147005)
* Bump ical to 10.0.4

* Bump ical to 10.0.4 in google
2025-06-17 07:06:51 -07:00
Michael Hansen
3b611b9b03 Add TTS response timeout for idle state (#146984)
* Add TTS response timeout for idle state

* Consider time spent sending TTS audio in timeout
2025-06-17 09:39:18 -04:00
Noah Husby
79cc3bffc6 Bump aiorussound to 4.6.0 (#147023) 2025-06-17 14:40:56 +02:00
Martin Hjelmare
5c455304a5 Disable Z-Wave indidator CC entities by default (#147018)
* Update discovery tests

* Disable Z-Wave indidator CC entities by default
2025-06-17 15:39:22 +03:00
Erik Montnemery
058f860be7 Fix incorrect use of zip in service.async_get_all_descriptions (#147013)
* Fix incorrect use of zip in service.async_get_all_descriptions

* Fix lint errors in test
2025-06-17 14:24:31 +02:00
Joost Lekkerkerker
ef319c966d Bump nextcord to 3.1.0 (#147020) 2025-06-17 14:11:55 +02:00
Robin Lintermann
adc4e9fdc1 Bump pysmarlaapi version to 0.9.0 (#146629)
Bump pysmarlaapi version
Fix default values of entities
2025-06-17 11:23:50 +02:00
Maciej Bieniek
40a00fb790 Address late review for NextDNS integration (#146980)
key instead of Key
2025-06-17 11:23:03 +02:00
G Johansson
0926b16095 Remove deprecated support feature values in cover (#146987) 2025-06-17 10:46:08 +02:00
G Johansson
308c89af4a Remove deprecated support feature values in media_player (#146986) 2025-06-17 10:33:41 +02:00
G Johansson
b0c2a47288 Remove deprecated support feature values in vacuum (#146982) 2025-06-17 10:32:58 +02:00
Joost Lekkerkerker
c446cce2cc Bump pySmartThings to 3.2.5 (#146983) 2025-06-16 22:44:14 +01:00
Abílio Costa
e02267ad89 Improve bootstrap file logging test (#146670) 2025-06-16 21:55:16 +01:00
Thomas55555
36381e6753 Bump aioautomower to 2025.6.0 (#146979) 2025-06-16 22:52:23 +02:00
Manu
6533562f4e Rename Xiaomi Miio integration to Xiaomi Home (#146555)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-06-16 21:51:54 +01:00
Ludovic BOUÉ
1bc6ea98ce Set Matter SolarPower tagList in fixture (#146837)
Update solar_power.json

Set tagList to [{"0":null,"1":15,"2":2,"3":"Solar"}]
2025-06-16 22:46:27 +02:00
elmurato
bab34b844b Fix blocking open in Minecraft Server (#146820)
Fix blocking open by dnspython
2025-06-16 22:46:11 +02:00
Etienne C.
ad3dac0373 Removed rounding of durations in Here Travel Time sensors (#146838)
* Removed rounding of durations

* Set duration sensors unit to seconds

* Updated Here Travel Time tests

* Update homeassistant/components/here_travel_time/sensor.py

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

* Update homeassistant/components/here_travel_time/sensor.py

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

* Updated Here Travel Time tests

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-16 22:20:01 +02:00
Maciej Bieniek
c5d93e5456 Fix translation key in NextDNS integration (#146976)
* Fix translation key

* Better wording
2025-06-16 21:37:19 +02:00
J. Diego Rodríguez Royo
ef9b46dce5 Record current IQS state for Home Connect (#131703)
* Home Connect quality scale

* Update current iqs

* Docs rules done

* parallel-updates rule

* Complete appropriate-polling's comment

* Apply suggestions

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-16 21:30:06 +02:00
Abílio Costa
6f3ceb83c2 Use non-autospec mock for Reolink's button tests (#146969) 2025-06-16 21:14:02 +02:00
Joost Lekkerkerker
589577a04c Add diagnostics support to Meater (#146967) 2025-06-16 20:17:30 +02:00
Joost Lekkerkerker
cb21bb6542 Make Meater cook state an enum (#146958) 2025-06-16 19:13:34 +01:00
mswilson
ad64139b8e Add switch for Samsung ice bites (and rename ice maker) (#146925)
* Add switch for ice bites (and rename ice maker)

Fixes: home-assistant/home-assistant.io#37826

* Fix tests

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-16 19:31:49 +02:00
Joost Lekkerkerker
9ae0cfc7e5 Create entities directly on setup in Meater (#146953)
* Don't wait an update when adding devices in Meater

* Fix
2025-06-16 18:23:20 +02:00
Joost Lekkerkerker
dffaf49eca Use runtime data in Meater (#146961) 2025-06-16 17:18:21 +02:00
Maciej Bieniek
4add783108 Use entity base class for NextDNS entities (#146934)
* Add entity module

* Add NextDnsEntityDescription class

* Remove NextDnsEntityDescription

* Create DeviceInfo in entity module

* Use property
2025-06-16 16:58:47 +02:00
Joost Lekkerkerker
421251308f Add Meater sensor tests (#146952) 2025-06-16 16:19:35 +02:00
Aviad Levy
cce878213f Add Telegram Bot message reactions (#146354) 2025-06-16 14:48:59 +01:00
Joost Lekkerkerker
664441eaec Improve Meater config flow tests (#146951) 2025-06-16 15:40:43 +02:00
Maciej Bieniek
d4686a3cce Add config flow data description for NextDNS (#146938)
* Add config flow data description

* Better wording
2025-06-16 15:28:25 +02:00
Hessel
6e92247799 Fix missing key for ecosmart in older Wallbox models (#146847)
* fix 146839, missing key

* added tests for this issue

* added tests for this issue

* added tests for this issue, formatting

* Prevent loading select on missing key

* Prevent loading select on missing key - formatting fixed

* Update homeassistant/components/wallbox/coordinator.py

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-06-16 15:15:17 +02:00
Etienne C.
f5355c833e Add duration device class in Here Travel Time sensors (#146804) 2025-06-16 15:14:43 +02:00
Joost Lekkerkerker
add9f4c5ab Move Meater coordinator to module (#146946)
* Move Meater coordinator to module

* Fix tests
2025-06-16 14:48:44 +02:00
starkillerOG
38973fe64a Add Reolink privacy mask switch (#146906) 2025-06-16 14:40:19 +02:00
epenet
d657964729 Simplify habitica service actions (#146746) 2025-06-16 14:37:38 +02:00
Nathan Spencer
25c408484c Set goalzero total run time sensor device class to duration (#146897) 2025-06-16 14:35:56 +02:00
Florian von Garrel
c335b5b37c Add verify ssl option to paperless-ngx integration (#146802)
* add verify ssl config option

* Refactoring

* Use .get() with default value instead of migration

* Reconfigure fix

* minor changes
2025-06-16 14:31:22 +02:00
Josef Zweck
61b00892c3 Add debug log for update in onedrive (#146907) 2025-06-16 14:17:36 +02:00
Maciej Bieniek
e47e2c92fe Change PARALLEL_UPDATES to 0 for read-only NextDNS platforms (#146939)
Change PARALLEL_UPDATES to 0 for read-only platforms
2025-06-16 14:11:48 +02:00
Duco Sebel
3283965b45 Re-enable v2 API support for HomeWizard P1 Meter (#146927) 2025-06-16 14:11:35 +02:00
epenet
4a9cbc79f2 Bump pysml to 0.1.5 (#146935) 2025-06-16 12:56:03 +01:00
epenet
33978ce59e Bump pyosoenergyapi to 1.1.5 (#146942) 2025-06-16 12:46:38 +01:00
epenet
d5262231a1 Bump pymysensors to 0.25.0 (#146941) 2025-06-16 13:37:39 +02:00
Brett Adams
b563f9078a Significantly improve Tesla Fleet config flow (#146794)
* Improved config flow

* Tests

* Improvements

* Dashboard url & tests

* Apply suggestions from code review

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* revert oauth change

* fully restore oauth file

* remove CONF_DOMAIN

* Add pick_implementation back in

* Use try else

* Improve translation

* use CONF_DOMAIN

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-06-16 13:29:17 +02:00
epenet
e8667dfbe0 Bump nessclient to 1.2.0 (#146937) 2025-06-16 12:11:57 +01:00
dependabot[bot]
8d4f5d78ff Bump dawidd6/action-download-artifact from 10 to 11 (#146928)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 10 to 11.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v10...v11)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-version: '11'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 10:42:10 +02:00
mbo18
e354a850c9 Bump python-rflink to 0.0.67 (#146908)
* update python-rflink

* remove from FORBIDDEN_PACKAGE_EXCEPTIONS
2025-06-16 10:36:20 +02:00
Ernst Klamer
5ea026d369 Bump bthome-ble to 3.13.1 (#146871) 2025-06-16 11:29:00 +03:00
Brett Adams
ddfe17d0a4 Bump tesla-fleet-api to match Protobuf compatibility (#146918)
Bump for v1.2.0
2025-06-16 10:12:34 +02:00
Yuxin Wang
85aa7bef1e Add sensor categorizations for APCUPSD (#146863)
* Add sensor categorizations

* Fix snapshot problem

* Fix snapshot problem
2025-06-16 08:43:31 +02:00
Paulus Schoutsen
8498928e47 Move Google Gen AI fixture to allow reuse (#146921) 2025-06-15 23:00:27 -04:00
Franck Nijhof
a75646d047 2025.6.1 (#146764) 2025-06-13 22:15:26 +02:00
Franck Nijhof
25d1480f2a Hotfix ruff warnings 2025-06-13 18:09:05 +00:00
hahn-th
2175754a1f Fix throttling issue in HomematicIP Cloud (#146683)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-13 17:57:39 +00:00
Franck Nijhof
df5f253146 Bump version to 2025.6.1 2025-06-13 17:20:24 +00:00
J. Nick Koston
a017d9415b Bump aiodns to 3.5.0 (#146758) 2025-06-13 17:20:09 +00:00
Marc Mueller
e89c3b1e92 Ignore lingering pycares shutdown thread (#146733) 2025-06-13 17:20:07 +00:00
Allen Porter
d4ffeedc87 Partial revert of update to remote calendar to fix issue where calendar does not update (#146702)
Partial revert
2025-06-13 17:20:06 +00:00
Allen Porter
cb74b2663f Revert scan interval change in local calendar (#146700) 2025-06-13 17:20:05 +00:00
tronikos
4ec711bd63 Fix opower to work with aiohttp>=3.12.7 by disabling cookie quoting (#146697)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-13 17:20:03 +00:00
Simone Chemelli
e81c8ce44d Bump aioamazondevices to 3.1.2 (#146690) 2025-06-13 17:20:02 +00:00
Simone Chemelli
c2cf348255 Filter speak notify entity for WHA devices in Alexa Devices (#146688) 2025-06-13 17:20:00 +00:00
Simon Lamon
e048a3da38 Bump linkplay to v0.2.12 (#146669) 2025-06-13 17:19:59 +00:00
Tsvi Mostovicz
7cf3116f5b Bump hdate to 1.1.2 (#146659) 2025-06-13 17:19:58 +00:00
epenet
5cd7ea06ad Bump wakeonlan to 3.1.0 (#146655)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-13 17:19:56 +00:00
Simone Chemelli
52c62b31fd Fix cookies with aiohttp >= 3.12.7 for Vodafone Station (#146647) 2025-06-13 17:19:55 +00:00
Paul Bottein
b2bb0aeb64 Update frontend to 20250531.3 (#146638) 2025-06-13 17:19:53 +00:00
Vasilis Valatsos
f0fc87e2b6 Drop HostKeyAlgorithms in aruba (#146619) 2025-06-13 17:19:52 +00:00
epenet
e7a88e99f9 Fix fan is_on status in xiaomi_miio (#146592) 2025-06-13 17:19:50 +00:00
Avi Miller
c3e3a36b4c Fix palette handling for LIFX Ceiling SKY effect (#146582)
Signed-off-by: Avi Miller <me@dje.li>
2025-06-13 17:19:49 +00:00
Franck Nijhof
7aa6c8b941 2025.6.0 (#145650) 2025-06-11 21:28:56 +02:00
Franck Nijhof
54d8d71de5 Bump version to 2025.6.0 2025-06-11 19:14:05 +00:00
Robert Resch
fb4c77d43b Add aiofiles to pyproject.toml (#146561) 2025-06-11 18:39:53 +00:00
Franck Nijhof
cada2f84a9 Hotfix ruff warnings 2025-06-11 18:13:03 +00:00
Franck Nijhof
dc4627f413 Bump version to 2025.6.0b9 2025-06-11 18:07:37 +00:00
Joost Lekkerkerker
02524b8b9b Make issue creation check architecture instead of uname (#146537) 2025-06-11 18:06:36 +00:00
andreimoraru
60b8230ecc Bump yt-dlp to 2025.06.09 (#146553)
* Bumped yt-dlp to 2025.06.09

* fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-11 18:00:53 +00:00
Paul Bottein
75e6f23a82 Update frontend to 20250531.2 (#146551) 2025-06-11 18:00:52 +00:00
Petar Petrov
1f221712a2 Remove the Delete button on the ZwaveJS device page (#146544) 2025-06-11 18:00:51 +00:00
Paul Bottein
43797c03cc Update frontend to 20250531.1 (#146542) 2025-06-11 18:00:49 +00:00
Erik Montnemery
89637a618e Handle changes to source entities in generic_thermostat helper (#146541) 2025-06-11 18:00:48 +00:00
Erik Montnemery
fd605e0abe Handle changes to source entities in generic_hygrostat helper (#146538) 2025-06-11 17:58:52 +00:00
Franck Nijhof
e73bcc73b5 Bump version to 2025.6.0b8 2025-06-11 17:26:20 +00:00
Erik Montnemery
c02707a90f Handle changes to source entity in statistics helper (#146523) 2025-06-11 17:25:57 +00:00
Erik Montnemery
232f853d68 Simplify helper_integration.async_handle_source_entity_changes (#146516) 2025-06-11 17:22:28 +00:00
Tsvi Mostovicz
91e296a0c8 Bump hdate to 1.1.1 (#146536) 2025-06-11 16:58:43 +00:00
Simon Lamon
bcedb06862 Bump linkplay to v0.2.11 (#146530) 2025-06-11 16:56:36 +00:00
Erik Montnemery
2ab32220ed Handle changes to source entity in utility_meter (#146526) 2025-06-11 16:56:35 +00:00
Erik Montnemery
273ccb3929 Handle changes to source entity in trend helper (#146525) 2025-06-11 16:56:33 +00:00
Erik Montnemery
caaa4d5f35 Handle changes to source entity in threshold helper (#146524) 2025-06-11 16:56:32 +00:00
Erik Montnemery
0cf1fd1d41 Handle changes to source entity in integration helper (#146522) 2025-06-11 16:54:26 +00:00
Erik Montnemery
5ee39df330 Handle changes to source entity in history_stats helper (#146521) 2025-06-11 16:54:25 +00:00
Martin Hjelmare
cc972d20f6 Remove Z-Wave useless reconfigure options (#146520)
* Remove emulate hardware option

* Remove log level option
2025-06-11 16:54:24 +00:00
Erik Montnemery
e0f32cfd54 Allow removing entity registry items twice (#146519) 2025-06-11 16:54:22 +00:00
Jesse Hills
6384c800c3 Fix solax state class of Today's Generated Energy (#146492) 2025-06-11 16:50:15 +00:00
tronikos
82de2ed8e1 Rename Amazon Devices to Alexa Devices (#146362)
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-11 16:50:14 +00:00
Aidan Timson
af72d1854f Add guide for Honeywell Lyric application credentials setup (#146281)
* Add guide for Honeywell Lyric application credentials setup

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-06-11 16:50:13 +00:00
Shay Levy
0cff7cbccd Remove stale Shelly BLU TRV devices (#145994)
* Remove stale Shelly BLU TRV devices

* Add test

* Remove config entry from device
2025-06-11 16:50:11 +00:00
Kevin Stillhammer
6f4e16eed1 Fix stale options in here_travel_time (#145911) 2025-06-11 16:50:10 +00:00
Petro31
66be2f9240 Fix delay_on and delay_off restarting when a new trigger occurs during the delay (#145050) 2025-06-11 16:50:09 +00:00
Franck Nijhof
b6c8718ae4 Bump version to 2025.6.0b7 2025-06-11 10:17:18 +00:00
Åke Strandberg
c8b70cc0fb Graceful handling of missing datapoint in myuplink (#146517) 2025-06-11 10:17:09 +00:00
Robert Resch
6d1f621e55 Bump deebot-client to 13.3.0 (#146507) 2025-06-11 10:17:08 +00:00
Erik Montnemery
671a33b31c Do not remove derivative config entry when input sensor is removed (#146506)
* Do not remove derivative config entry when input sensor is removed

* Add comments

* Update homeassistant/helpers/helper_integration.py

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

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-11 10:17:06 +00:00
Michael Hansen
7afc469306 Bump intents to 2025.6.10 (#146491) 2025-06-11 10:17:05 +00:00
Felix Schneider
8fd52248b7 Bump apsystems to 2.7.0 (#146485) 2025-06-11 10:17:04 +00:00
Joost Lekkerkerker
69ba2aab11 Remove DHCP discovery from Amazon Devices (#146476) 2025-06-11 10:17:02 +00:00
Tsvi Mostovicz
f1df6dcda5 Fix Jewish calendar not updating (#146465) 2025-06-11 10:17:01 +00:00
Joost Lekkerkerker
43e16bb913 Split deprecated system issue in 2 places (#146453) 2025-06-11 10:15:11 +00:00
Petro31
4147211f94 Add color_temp_kelvin to set_temperature action variables (#146448) 2025-06-11 10:10:40 +00:00
Joost Lekkerkerker
63e49c5d3c Explain Nest setup (#146217) 2025-06-11 10:10:39 +00:00
hahn-th
35580c0849 Bump homematicip to 2.0.4 (#144096)
* Bump to 2.0.2 with all necessary changes

* bump to prerelease

* add addiional tests

* Bump to homematicip 2.0.3

* do not delete device

* Setup BRAND_SWITCH_MEASURING as light

* bump to 2.0.4

* refactor test_remove_obsolete_entities

* move test

* use const from homematicip lib
2025-06-11 10:10:38 +00:00
Franck Nijhof
8949a595fe Bump version to 2025.6.0b6 2025-06-10 17:45:26 +00:00
Simone Chemelli
bf8ef0a767 Fix EntityCategory for binary_sensor platform in Amazon Devices (#146472)
* Fix EntityCategory for  binary_sensor platform in Amazon Devices

* update snapshots
2025-06-10 17:41:02 +00:00
Simone Chemelli
39962a3f48 Avoid closing shared aiohttp session in Vodafone Station (#146471) 2025-06-10 17:41:00 +00:00
G Johansson
4964621014 Fix incorrect categories handling in holiday (#146470) 2025-06-10 17:40:59 +00:00
Joost Lekkerkerker
18e1a26da1 Catch exception before retrying in AirGradient (#146460) 2025-06-10 17:40:58 +00:00
Joost Lekkerkerker
1d91ca5716 Bump pySmartThings to 3.2.4 (#146459) 2025-06-10 17:40:57 +00:00
Luca Schröder
1040646610 Update caldav to 1.6.0 (#146456)
Fixes #140798
2025-06-10 17:40:56 +00:00
Robert Resch
fcd71931e7 Update wording deprecated system package integration repair (#146450)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-10 17:40:55 +00:00
Joost Lekkerkerker
bdbb74aff1 Return expected state in SmartThings water heater (#146449) 2025-06-10 17:40:53 +00:00
Marc Mueller
6f4029983a Update requests to 2.32.4 (#146445) 2025-06-10 17:40:52 +00:00
J. Diego Rodríguez Royo
b2d25b1883 Improvements for Home Connect application credentials string (#146443) 2025-06-10 17:40:51 +00:00
J. Diego Rodríguez Royo
ba19d4f043 Fix typo at application credentials string at Home Connect integration (#146442)
Fix typos
2025-06-10 17:40:50 +00:00
tronikos
b222fe5afa Handle grpc errors in Google Assistant SDK (#146438) 2025-06-10 17:40:49 +00:00
Franck Nijhof
f945defa2b Reformat Dockerfile to reduce merge conflicts (#146435) 2025-06-10 17:38:37 +00:00
J. Nick Koston
4f0e4bc1ca Bump aiohttp to 3.12.12 (#146426) 2025-06-10 17:28:13 +00:00
J. Nick Koston
41abc8404d Bump yarl to 1.20.1 (#146424) 2025-06-10 17:26:03 +00:00
Jamin
2b08c4c344 Check hangup error in voip (#146423)
Check hangup error

Prevent an error where the call end future may have already been set
when a hangup is detected.
2025-06-10 17:26:02 +00:00
J. Nick Koston
97d91ddddb Bump propcache to 0.3.2 (#146418) 2025-06-10 17:26:01 +00:00
Whitney Young
ec30b12fd1 Fix initial state of UV protection window (#146408)
The `binary_sensor` is created when the config entry is loaded after the
`async_config_entry_first_refresh` has completed (during the forward of
setup to platforms). Therefore, the update coordinator will already have
data and will not trigger the invocation of
`_handle_coordinator_update`.

Fixing this just means performing the same update at initialization.
2025-06-10 17:25:59 +00:00
Erik Montnemery
9997fc11b1 Handle changes to source entity in derivative helper (#146407)
* Handle changes to source entity in derivative helper

* Rename helper function, improve docstring

* Add tests

* Improve derivative tests

* Deduplicate tests

* Rename helpers/helper_entity.py to helpers/helper_integration.py

* Rename tests
2025-06-10 17:25:59 +00:00
wittypluck
c6ff0e6492 Fix CO concentration unit in OpenWeatherMap (#146403) 2025-06-10 17:25:58 +00:00
G Johansson
a3220ecae6 Bump pynordpool to 0.3.0 (#146396) 2025-06-10 17:25:57 +00:00
Erik Montnemery
218864d08c Update switch_as_x to handle wrapped switch moved to another device (#146387)
* Update switch_as_x to handle wrapped switch moved to another device

* Reload switch_as_x config entry after updating device

* Make sure the switch_as_x entity is not removed
2025-06-10 17:25:56 +00:00
Erik Montnemery
3d0d70ece6 Fix switch_as_x entity_id tracking (#146386) 2025-06-10 17:25:55 +00:00
Simone Chemelli
f629731930 Bump aioamazondevices to 3.0.6 (#146385) 2025-06-10 17:25:53 +00:00
J. Nick Koston
e7a7b2417b Bump aioesphomeapi to 32.2.1 (#146375) 2025-06-10 17:03:20 +00:00
Michael Davie
0b24a9abc3 Bump env-canada to v0.11.2 (#146371) 2025-06-10 17:03:19 +00:00
David Knowles
ca77b5210f Bump pydrawise to 2025.6.0 (#146369) 2025-06-10 17:03:18 +00:00
Simon Lamon
0874f1c350 Bump python-linkplay to v0.2.10 (#146359) 2025-06-10 17:03:17 +00:00
Jan-Philipp Benecke
d89b99f42b Improve error logging in trend binary sensor (#146358) 2025-06-10 17:03:16 +00:00
J. Diego Rodríguez Royo
7bd6ec68a8 Explain Home Connect setup (#146356)
* Explain Home Connect setup

* Avoid using "we"

* Fix login spelling

* Fix signup spelling
2025-06-10 17:03:15 +00:00
J. Nick Koston
bfe2eeb833 Shift ESPHome log parsing to the library (#146349) 2025-06-10 17:03:14 +00:00
Klaas Schoute
e97ab1fe3c Change interval for Powerfox integration (#146348) 2025-06-10 17:03:13 +00:00
J. Nick Koston
b3ee2a8885 Bump aioesphomeapi to 32.2.0 (#146344) 2025-06-10 17:03:12 +00:00
Michael
80b09e3212 Bump py-synologydsm-api to 2.7.3 (#146338)
bump py-synologydsm-api to 2.7.3
2025-06-10 17:03:11 +00:00
tronikos
0eb3714abc Allow different manufacturer than Amazon in Amazon Devices (#146333) 2025-06-10 17:03:10 +00:00
Sanjay Govind
7991977443 Fix bosch alarm areas not correctly subscribing to alarms (#146322)
* Fix bosch alarm areas not correctly subscribing to alarms

* add test
2025-06-10 17:03:09 +00:00
J. Nick Koston
5e5431c9f9 Use entity unique id for ESPHome media player formats (#146318) 2025-06-10 17:03:08 +00:00
Simon Lamon
1fc05d1a30 Do not probe linkplay device if another config entry already contains the host (#146305)
* Do not probe if config entry already contains the host

* Add unit test

* Use common fixture
2025-06-10 17:03:07 +00:00
J. Nick Koston
21833e7c31 Bump aiohttp to 3.12.11 (#146298) 2025-06-10 16:59:07 +00:00
G Johansson
79daeb23a9 Bump holidays to 0.74 (#146290) 2025-06-10 16:55:33 +00:00
J. Nick Koston
761c2578fb Bump aiohttp-fast-zlib to 0.3.0 (#146285)
changelog: https://github.com/Bluetooth-Devices/aiohttp-fast-zlib/compare/v0.2.3...v0.3.0

proper aiohttp 3.12 support
2025-06-10 16:48:33 +00:00
Brett Adams
4d3145e559 Add missing write state to Teslemetry (#146267) 2025-06-10 16:47:33 +00:00
Michael
91e29a3bf1 Bump aioimmich to 0.9.1 (#146222)
bump aioimmich to 0.9.1
2025-06-10 16:47:32 +00:00
Joost Lekkerkerker
f6a4486c65 Explain Withings setup (#146216) 2025-06-10 16:47:31 +00:00
Joost Lekkerkerker
fc8b512931 Remove zeroconf discovery from Spotify (#146213) 2025-06-10 16:47:30 +00:00
Brett Adams
e5dd15da82 Fix Export Rule Select Entity in Tessie (#146203)
Fix TessieExportRuleSelectEntity
2025-06-10 16:47:29 +00:00
Brett Adams
e4140d71ab Prevent energy history returning zero in Teslemetry (#146202) 2025-06-10 16:47:28 +00:00
J. Nick Koston
8312780c47 Bump aiohttp to 3.12.9 (#146178) 2025-06-10 16:44:14 +00:00
Raphael Hehl
5accc3dec2 Bump uiprotect to 7.11.0 (#146171)
Bump uiprotect to version 7.11.0
2025-06-10 16:42:40 +00:00
Iskra kranj
d875989866 Bump pyiskra to 0.1.21 (#146156) 2025-06-10 16:42:39 +00:00
Michael
38c92a2338 Bump aioimmich to 0.9.0 (#146154)
bump aioimmich to 0.9.0
2025-06-10 16:42:38 +00:00
J. Nick Koston
ce76b5db16 Bump aiohttp to 3.12.8 (#146153) 2025-06-10 16:39:58 +00:00
Ian
dfc4889d45 Throttle Nextbus if we are reaching the rate limit (#146064)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-06-10 16:32:59 +00:00
Andrea Turri
41431282ee Add evaporate water program id for Miele oven (#145996) 2025-06-10 16:32:58 +00:00
Arie Catsman
5821b2f03c fix possible mac collision in enphase_envoy (#145549)
* fix possible mac collision in enphase_envoy

* remove redundant device registry async_get
2025-06-10 16:32:57 +00:00
starkillerOG
78d2bf736c Reolink conserve battery (#145452) 2025-06-10 16:32:56 +00:00
Franck Nijhof
6c098c3e0a Bump version to 2025.6.0b5 2025-06-04 09:02:53 +00:00
J. Nick Koston
bfb140d2e9 Bump aioesphomeapi to 32.0.0 (#146135) 2025-06-04 09:00:59 +00:00
J. Nick Koston
f71a1a7a89 Bump protobuf to 6.31.1 (#146128)
changelog: https://github.com/protocolbuffers/protobuf/compare/v30.2...v31.1
2025-06-04 09:00:57 +00:00
Erwin Douna
e8aab39620 SMA fix strings (#146112)
* Fix

* Feedback
2025-06-04 09:00:55 +00:00
J. Nick Koston
1d578d8563 Bump habluetooth to 3.49.0 (#146111)
* Bump habluetooth to 3.49.0

changelog: https://github.com/Bluetooth-Devices/habluetooth/compare/v3.48.2...v3.49.0

* update diag

* diag
2025-06-04 09:00:53 +00:00
J. Nick Koston
abfd443541 Bump bleak-esphome to 2.16.0 (#146110) 2025-06-04 09:00:51 +00:00
Brett Adams
81cbb6e5cf Fix BMS and Charge states in Teslemetry (#146091)
Fix BMS and Charge states
2025-06-04 09:00:49 +00:00
Retha Runolfsson
010c5cab87 Fix nightlatch option for all switchbot locks (#146090) 2025-06-04 09:00:47 +00:00
SNoof85
415858119a Add state class measurement to Freebox temperature sensors (#146074) 2025-06-04 09:00:44 +00:00
Simone Chemelli
1838a731d6 Bump aioamazondevices to 3.0.5 (#146073) 2025-06-04 09:00:42 +00:00
Shay Levy
1e304fad65 Fix Shelly BLU TRV calibrate button (#146066) 2025-06-04 09:00:40 +00:00
Michael
999c9b3dc5 Don't use multi-line conditionals in immich (#146062) 2025-06-04 09:00:37 +00:00
epenet
e15edbd54b Adjust SamsungTV on/off logging (#146045)
* Adjust SamsungTV on/off logging

* Update coordinator.py
2025-06-04 09:00:35 +00:00
epenet
e5cb77d168 Adjust ConnectionFailure logging in SamsungTV (#146044) 2025-06-04 09:00:32 +00:00
starkillerOG
cf521d4c7c Improve debug logging Reolink (#146033)
Add debug logging
2025-06-04 09:00:25 +00:00
J. Nick Koston
6f09474193 Bump grpcio to 1.72.1 (#146029) 2025-06-04 09:00:21 +00:00
Robert Resch
7626933352 Bump go2rtc-client to 0.2.1 (#146019)
* Bump go2rtc-client to 0.2.0

* Bump go2rtc-client to 0.2.1

* Clean up hassfest exception

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-06-04 08:56:07 +00:00
starkillerOG
9e1d8c2fc6 Bump reolink-aio to 0.13.5 (#145974)
* Add debug logging

* Bump reolink-aio to 0.13.5

* Revert "Add debug logging"

This reverts commit f96030a6c8.
2025-06-04 08:43:30 +00:00
Simone Chemelli
6defed2915 Bump aioamazondevices to 3.0.4 (#145971) 2025-06-04 08:43:28 +00:00
Simone Chemelli
d729eed7c2 Add diagnostics to Amazon devices (#145964) 2025-06-04 08:43:26 +00:00
Noah Groß
f280032dcf Bump python-picnic-api2 to 1.3.1 (#145962) 2025-06-04 08:43:21 +00:00
Allen Porter
7e85137012 Bump ical to 10.0.0 (#145954) 2025-06-04 08:43:18 +00:00
TimL
88f2c3abd3 Bump pysmlight to v0.2.5 (#145949) 2025-06-04 08:43:15 +00:00
Michael
1a21e01f85 Bump aioimmich to 0.8.0 (#145908) 2025-06-04 08:43:13 +00:00
Ian
d302e817c8 NextBus: Bump py_nextbusnext to 2.2.0 (#145904) 2025-06-04 08:43:09 +00:00
Martin Hjelmare
1e1b0424d7 Fix removal of devices during Z-Wave migration (#145867) 2025-06-04 08:43:07 +00:00
Robert Resch
03f028b7e2 Deprecate hddtemp (#145850) 2025-06-04 08:43:05 +00:00
Robert Resch
b1d35de8e4 Deprecate eddystone temperature integration (#145833) 2025-06-04 08:43:02 +00:00
Erwin Douna
ea6b9e5260 SMA add missing strings for DHCP (#145782) 2025-06-04 08:42:59 +00:00
Bram Kragten
06d869aaa5 Bump version to 2025.6.0b4 2025-05-31 21:25:06 +02:00
Josef Zweck
907cebdd6d Increase update intervals in lamarzocco (#145939) 2025-05-31 21:25:02 +02:00
Josef Zweck
745902bc7e Bump pylamarzocco to 2.0.8 (#145938) 2025-05-31 21:25:01 +02:00
Bram Kragten
ef0b3c9f9c Update frontend to 20250531.0 (#145933) 2025-05-31 21:25:00 +02:00
J. Nick Koston
532c077ddf Bump aiohttp to 3.12.6 (#145919)
* Bump aiohttp to 3.12.5

changelog: https://github.com/aio-libs/aiohttp/compare/v3.12.4...v3.12.5

* .6

* fix mock
2025-05-31 21:24:59 +02:00
tronikos
cd905a6593 Bump opower to 0.12.3 (#145918) 2025-05-31 21:24:59 +02:00
Josef Zweck
d0bf9d9bfb Move server device creation to init in jellyfin (#145910)
* Move server device creation to init in jellyfin

* move device creation to after coordinator refresh
2025-05-31 21:24:58 +02:00
Jordan Harvey
ddc79a631d Bump pyprobeplus to 1.0.1 (#145897) 2025-05-31 21:24:57 +02:00
Simon Lamon
6015f60db4 Bump python-linkplay to v0.2.9 (#145892) 2025-05-31 21:24:57 +02:00
Iskra kranj
a6608bd7ea Bump pyiskra to 0.1.19 (#145889) 2025-05-31 21:24:56 +02:00
Brett Adams
fb2d8c6406 Add streaming to charge cable connected in Teslemetry (#145880) 2025-05-31 21:24:55 +02:00
Brett Adams
c84ffb54d2 Bump tesla-fleet-api to 1.1.1. (#145869)
bump
2025-05-31 21:24:54 +02:00
Samuel Xiao
306bbdc697 Bump switchbot-api to 2.4.0 (#145786)
* update switchbot-api version to 2.4.0

* debug for test code
2025-05-31 21:24:54 +02:00
Robert Resch
9879ecad85 Deprecate snips integration (#145784) 2025-05-31 21:24:53 +02:00
Joost Lekkerkerker
f0fcef5744 Add more Amazon Devices DHCP matches (#145776) 2025-05-31 21:24:52 +02:00
Bram Kragten
aa8a6058b5 Bump version to 2025.6.0b3 2025-05-30 12:56:51 +02:00
J. Diego Rodríguez Royo
48103bd244 Bump aiohomeconnect to 0.17.1 (#145873) 2025-05-30 12:56:45 +02:00
Robert Resch
600ac17a5f Deprecate sms integration (#145847) 2025-05-30 12:56:44 +02:00
Michael
d46f28792c Bump aioimmich to 0.7.0 (#145845) 2025-05-30 12:56:43 +02:00
starkillerOG
0f7379c941 Reolink fallback to download command for playback (#145842) 2025-05-30 12:56:43 +02:00
J. Nick Koston
4317fad798 Bump aiohttp to 3.12.4 (#145838) 2025-05-30 12:56:42 +02:00
J. Nick Koston
5cfccb7e1d Bump aiohttp to 3.12.3 (#145837) 2025-05-30 12:56:41 +02:00
Matthew FitzGerald-Chamberlain
097eecd78a Bump pyaprilaire to 0.9.1 (#145836) 2025-05-30 12:56:40 +02:00
Brett Adams
64b4642c49 Fix Tessie volume max and step (#145835)
* Use fixed volume max and step

* Update snapshot
2025-05-30 12:56:39 +02:00
Michael
0e87d14ca8 Use mime type provided by Immich (#145830)
use mime type from immich instead of guessing it
2025-05-30 12:56:38 +02:00
Josef Zweck
4d22b35a9f Bump aiotedee to 0.2.23 (#145822)
* Bump aiotedee to 0.2.23

* update snapshot
2025-05-30 12:56:37 +02:00
G Johansson
26586b4514 Fix language selections in workday (#145813) 2025-05-30 12:56:36 +02:00
Robert Resch
95fb2a7d7f Deprecate decora integration (#145807) 2025-05-30 12:56:35 +02:00
Robert Resch
fa66ea31d3 Deprecate tensorflow (#145806)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-05-30 12:56:34 +02:00
André Lersveen
e0d3b819e5 Set correct nobo_hub max temperature (#145751)
Max temperature 30°C is implemented upstream in pynobo and the Nobø Energy Hub app also stops at 30°C.
2025-05-30 12:56:34 +02:00
Bram Kragten
17a0b4f3d0 Bump version to 2025.6.0b2 2025-05-28 23:18:38 +02:00
Bram Kragten
d0d228d9f4 Update frontend to 20250528.0 (#145828)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-05-28 23:18:33 +02:00
Michael
309acb961b Fix Immich media source browsing with multiple config entries (#145823)
fix media source browsing with multiple config entries
2025-05-28 23:18:32 +02:00
Michael Hansen
12f8ebb3ea Bump intents to 2025.5.28 (#145816) 2025-05-28 23:18:32 +02:00
David Bonnes
612861061c Fix HOMEASSISTANT_STOP unsubscribe in data update coordinator (#145809)
* initial commit

* a better approach

* Add comment
2025-05-28 23:18:31 +02:00
Robert Resch
83af5ec36b Deprecate keyboard integration (#145805) 2025-05-28 23:18:30 +02:00
starkillerOG
74102d0319 Bump reolink-aio to 0.13.4 (#145799) 2025-05-28 23:18:29 +02:00
Robert Resch
fbd05a0fcf Deprecate lirc integration (#145797) 2025-05-28 23:18:29 +02:00
Robert Resch
a53c786fe0 Deprecate pandora integration (#145785) 2025-05-28 23:18:28 +02:00
Josef Zweck
eb2728e5b9 Fix uom for prebrew numbers in lamarzocco (#145772) 2025-05-28 23:18:27 +02:00
J. Diego Rodríguez Royo
3f17223387 Add more information about possible hostnames at Home Connect (#145770) 2025-05-28 23:18:26 +02:00
Robert Resch
74104cf107 Deprecate GStreamer integration (#145768) 2025-05-28 23:18:25 +02:00
Robert Resch
13b4879723 Deprecate dlib image processing integrations (#145767) 2025-05-28 23:18:25 +02:00
Erik Montnemery
f1ec0b2c59 Handle late abort when creating subentry (#145765)
* Handle late abort when creating subentry

* Move error handling to the base class

* Narrow down expected error in test
2025-05-28 23:18:24 +02:00
Josef Zweck
6d44daf599 Bump pylamarzocco to 2.0.7 (#145763) 2025-05-28 23:18:23 +02:00
Joost Lekkerkerker
644a6f5569 Add more Amazon Devices DHCP matches (#145754) 2025-05-28 23:18:22 +02:00
Abílio Costa
fb83396522 Add Shelly zwave virtual integration (#145749) 2025-05-28 23:18:22 +02:00
Raphael Hehl
e825bd0bdb Bump uiprotect to version 7.10.1 (#145737)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-05-28 23:18:21 +02:00
G Johansson
61823ec7e2 Fix dns resolver error in dnsip config flow validation (#145735)
Fix dns resolver error in dnsip
2025-05-28 23:18:20 +02:00
Michael
cd133cbbe3 Add level of collections in Immich media source tree (#145734)
* add layer for collections in media source tree

* re-arange tests, add test for collection layer

* fix
2025-05-28 23:18:19 +02:00
Erik Montnemery
0e7a1bb76c Make async_remove_stale_devices_links_keep_entity_device move entities (#145719)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-05-28 23:18:18 +02:00
Josef Zweck
f86bf69ebc Update otp description for amazon_devices (#145701)
* Update otp description from amazon_devices

* separate

* Update strings.json
2025-05-28 23:18:18 +02:00
Jan Bouwhuis
adddf330fd Ensure mqtt sensor unit of measurement validation for state class measurement_angle (#145648) 2025-05-28 23:18:17 +02:00
Bram Kragten
10adb57b83 Bump version to 2025.6.0b1 2025-05-27 22:16:13 +02:00
Bram Kragten
3160fe9abc Update frontend to 20250527.0 (#145741) 2025-05-27 22:14:02 +02:00
Erwin Douna
6adb27d173 Tado update mobile devices interval (#145738)
Update the mobile devices interval to five minutes
2025-05-27 22:14:01 +02:00
Joost Lekkerkerker
6e6aae2ea3 Fix unbound local variable in Acmeda config flow (#145729) 2025-05-27 22:14:00 +02:00
Kevin Stillhammer
41a140d16c Debug log the update response in google_travel_time (#145725)
Debug log the update response
2025-05-27 22:14:00 +02:00
Kevin Stillhammer
8880ab6498 Catch PermissionDenied(Route API disabled) in google_travel_time (#145722)
Catch PermissionDenied(Route API disabled)
2025-05-27 22:13:59 +02:00
Martin Hjelmare
389becc4f6 Disable advanced window cover position Matter sensor by default (#145713)
* Disable advanced window cover position Matter sensor by default

* Enanble disabled sensors in snapshot test
2025-05-27 22:13:58 +02:00
Martin Hjelmare
923530972a Remove static pin code length Matter sensors (#145711)
* Remove static Matter sensors

* Clean up translation strings
2025-05-27 22:13:57 +02:00
Martin Hjelmare
b84850df9f Fix error stack trace for HomeAssistantError in websocket service call (#145699)
* Add test

* Fix error stack trace for HomeAssistantError in websocket service call
2025-05-27 22:13:56 +02:00
Joost Lekkerkerker
9e7dc1d11d Use string type for amazon devices OTP code (#145698) 2025-05-27 22:13:56 +02:00
Petar Petrov
2830ed6147 Change description on recommended/custom Z-Wave install step (#145688)
Change description on recommended/custom Z-WaveJS step
2025-05-27 22:13:55 +02:00
Petar Petrov
bfa919d078 Remove confirm screen after Z-Wave usb discovery (#145682)
* Remove confirm screen after Z-Wave usb discovery

* Simplify async_step_usb
2025-05-27 22:13:54 +02:00
Jan Bouwhuis
f09c28e61f Fix justnimbus CI test (#145681) 2025-05-27 22:13:54 +02:00
J. Nick Koston
bfdba7713e Bump aiohttp to 3.12.2 (#145671) 2025-05-27 22:13:53 +02:00
Kevin Stillhammer
d6cadc1e3f Support addresses with comma in google_travel_time (#145663)
Support addresses with comma
2025-05-27 22:13:52 +02:00
Joost Lekkerkerker
20a6a3f195 Handle Google Nest DHCP flows (#145658)
* Handle Google Nest DHCP flows

* Handle Google Nest DHCP flows
2025-05-27 22:13:51 +02:00
Joost Lekkerkerker
f60de45b52 Fix Amazon devices offline handling (#145656) 2025-05-27 22:13:50 +02:00
Joost Lekkerkerker
77031d1ae4 Fix Aquacell snapshot (#145651) 2025-05-27 22:13:49 +02:00
Jan Bouwhuis
9483a88ee1 Fix translation for sensor measurement angle state class (#145649) 2025-05-27 22:13:48 +02:00
Franck Nijhof
3438a4f063 Bump version to 2025.6.0b0 2025-05-26 20:31:18 +00:00
1278 changed files with 73182 additions and 16304 deletions

View File

@@ -1,15 +1,14 @@
name: Report an issue with Home Assistant Core
description: Report an issue with Home Assistant Core.
type: Bug
body:
- type: markdown
attributes:
value: |
This issue form is for reporting bugs only!
If you have a feature or enhancement request, please use the [feature request][fr] section of our [Community Forum][fr].
If you have a feature or enhancement request, please [request them here instead][fr].
[fr]: https://community.home-assistant.io/c/feature-requests
[fr]: https://github.com/orgs/home-assistant/discussions
- type: textarea
validations:
required: true

View File

@@ -10,8 +10,8 @@ contact_links:
url: https://www.home-assistant.io/help
about: We use GitHub for tracking bugs, check our website for resources on getting help.
- name: Feature Request
url: https://community.home-assistant.io/c/feature-requests
about: Please use our Community Forum for making feature requests.
url: https://github.com/orgs/home-assistant/discussions
about: Please use this link to request new features or enhancements to existing features.
- name: I'm unsure where to go
url: https://www.home-assistant.io/join-chat
about: If you are unsure where to go, then joining our chat is recommended; Just ask!

File diff suppressed because it is too large Load Diff

View File

@@ -94,7 +94,7 @@ jobs:
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@v10
uses: dawidd6/action-download-artifact@v11
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/frontend
@@ -105,10 +105,10 @@ jobs:
- name: Download nightly wheels of intents
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@v10
uses: dawidd6/action-download-artifact@v11
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/intents-package
repo: OHF-Voice/intents-package
branch: main
workflow: nightly.yaml
workflow_conclusion: success
@@ -324,7 +324,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Install Cosign
uses: sigstore/cosign-installer@v3.8.2
uses: sigstore/cosign-installer@v3.9.1
with:
cosign-release: "v2.2.3"

View File

@@ -37,10 +37,10 @@ on:
type: boolean
env:
CACHE_VERSION: 2
CACHE_VERSION: 3
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2025.7"
HA_SHORT_VERSION: "2025.8"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['3.13']"
# 10.3 is the oldest supported version

View File

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

6
.gitignore vendored
View File

@@ -137,4 +137,8 @@ tmp_cache
.ropeproject
# Will be created from script/split_tests.py
pytest_buckets.txt
pytest_buckets.txt
# AI tooling
.claude

View File

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

View File

@@ -67,6 +67,7 @@ homeassistant.components.alert.*
homeassistant.components.alexa.*
homeassistant.components.alexa_devices.*
homeassistant.components.alpha_vantage.*
homeassistant.components.altruist.*
homeassistant.components.amazon_polly.*
homeassistant.components.amberelectric.*
homeassistant.components.ambient_network.*
@@ -502,6 +503,7 @@ homeassistant.components.tautulli.*
homeassistant.components.tcp.*
homeassistant.components.technove.*
homeassistant.components.tedee.*
homeassistant.components.telegram_bot.*
homeassistant.components.text.*
homeassistant.components.thethingsnetwork.*
homeassistant.components.threshold.*

1
CLAUDE.md Symbolic link
View File

@@ -0,0 +1 @@
.github/copilot-instructions.md

16
CODEOWNERS generated
View File

@@ -93,6 +93,8 @@ build.json @home-assistant/supervisor
/tests/components/alexa/ @home-assistant/cloud @ochlocracy @jbouwh
/homeassistant/components/alexa_devices/ @chemelli74
/tests/components/alexa_devices/ @chemelli74
/homeassistant/components/altruist/ @airalab @LoSk-p
/tests/components/altruist/ @airalab @LoSk-p
/homeassistant/components/amazon_polly/ @jschlyter
/homeassistant/components/amberelectric/ @madpilot
/tests/components/amberelectric/ @madpilot
@@ -329,8 +331,8 @@ build.json @home-assistant/supervisor
/tests/components/demo/ @home-assistant/core
/homeassistant/components/denonavr/ @ol-iver @starkillerOG
/tests/components/denonavr/ @ol-iver @starkillerOG
/homeassistant/components/derivative/ @afaucogney
/tests/components/derivative/ @afaucogney
/homeassistant/components/derivative/ @afaucogney @karwosts
/tests/components/derivative/ @afaucogney @karwosts
/homeassistant/components/devialet/ @fwestenberg
/tests/components/devialet/ @fwestenberg
/homeassistant/components/device_automation/ @home-assistant/core
@@ -786,8 +788,6 @@ build.json @home-assistant/supervisor
/tests/components/jellyfin/ @RunC0deRun @ctalkington
/homeassistant/components/jewish_calendar/ @tsvi
/tests/components/jewish_calendar/ @tsvi
/homeassistant/components/juicenet/ @jesserockz
/tests/components/juicenet/ @jesserockz
/homeassistant/components/justnimbus/ @kvanzuijlen
/tests/components/justnimbus/ @kvanzuijlen
/homeassistant/components/jvc_projector/ @SteveEasley @msavazzi
@@ -1169,6 +1169,8 @@ build.json @home-assistant/supervisor
/tests/components/ping/ @jpbede
/homeassistant/components/plaato/ @JohNan
/tests/components/plaato/ @JohNan
/homeassistant/components/playstation_network/ @jackjpowell @tr4nt0r
/tests/components/playstation_network/ @jackjpowell @tr4nt0r
/homeassistant/components/plex/ @jjlawren
/tests/components/plex/ @jjlawren
/homeassistant/components/plugwise/ @CoMPaTech @bouwew
@@ -1551,6 +1553,8 @@ build.json @home-assistant/supervisor
/tests/components/technove/ @Moustachauve
/homeassistant/components/tedee/ @patrickhilker @zweckj
/tests/components/tedee/ @patrickhilker @zweckj
/homeassistant/components/telegram_bot/ @hanwg
/tests/components/telegram_bot/ @hanwg
/homeassistant/components/tellduslive/ @fredrike
/tests/components/tellduslive/ @fredrike
/homeassistant/components/template/ @Petro31 @home-assistant/core
@@ -1580,6 +1584,8 @@ build.json @home-assistant/supervisor
/tests/components/tile/ @bachya
/homeassistant/components/tilt_ble/ @apt-itude
/tests/components/tilt_ble/ @apt-itude
/homeassistant/components/tilt_pi/ @michaelheyman
/tests/components/tilt_pi/ @michaelheyman
/homeassistant/components/time/ @home-assistant/core
/tests/components/time/ @home-assistant/core
/homeassistant/components/time_date/ @fabaff
@@ -1668,6 +1674,8 @@ build.json @home-assistant/supervisor
/tests/components/vallox/ @andre-richter @slovdahl @viiru- @yozik04
/homeassistant/components/valve/ @home-assistant/core
/tests/components/valve/ @home-assistant/core
/homeassistant/components/vegehub/ @ghowevege
/tests/components/vegehub/ @ghowevege
/homeassistant/components/velbus/ @Cereal2nd @brefra
/tests/components/velbus/ @Cereal2nd @brefra
/homeassistant/components/velux/ @Julius2342 @DeerMaximum @pawlizio

View File

@@ -38,8 +38,7 @@ def validate_python() -> None:
def ensure_config_path(config_dir: str) -> None:
"""Validate the configuration directory."""
# pylint: disable-next=import-outside-toplevel
from . import config as config_util
from . import config as config_util # noqa: PLC0415
lib_dir = os.path.join(config_dir, "deps")
@@ -80,8 +79,7 @@ def ensure_config_path(config_dir: str) -> None:
def get_arguments() -> argparse.Namespace:
"""Get parsed passed in arguments."""
# pylint: disable-next=import-outside-toplevel
from . import config as config_util
from . import config as config_util # noqa: PLC0415
parser = argparse.ArgumentParser(
description="Home Assistant: Observe, Control, Automate.",
@@ -177,8 +175,7 @@ def main() -> int:
validate_os()
if args.script is not None:
# pylint: disable-next=import-outside-toplevel
from . import scripts
from . import scripts # noqa: PLC0415
return scripts.run(args.script)
@@ -188,8 +185,7 @@ def main() -> int:
ensure_config_path(config_dir)
# pylint: disable-next=import-outside-toplevel
from . import config, runner
from . import config, runner # noqa: PLC0415
safe_mode = config.safe_mode_enabled(config_dir)

View File

@@ -52,28 +52,28 @@ _LOGGER = logging.getLogger(__name__)
def _generate_secret() -> str:
"""Generate a secret."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
return str(pyotp.random_base32())
def _generate_random() -> int:
"""Generate a 32 digit number."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
return int(pyotp.random_base32(length=32, chars=list("1234567890")))
def _generate_otp(secret: str, count: int) -> str:
"""Generate one time password."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
return str(pyotp.HOTP(secret).at(count))
def _verify_otp(secret: str, otp: str, count: int) -> bool:
"""Verify one time password."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
return bool(pyotp.HOTP(secret).verify(otp, count))

View File

@@ -37,7 +37,7 @@ DUMMY_SECRET = "FPPTH34D4E3MI2HG"
def _generate_qr_code(data: str) -> str:
"""Generate a base64 PNG string represent QR Code image of data."""
import pyqrcode # pylint: disable=import-outside-toplevel
import pyqrcode # noqa: PLC0415
qr_code = pyqrcode.create(data)
@@ -59,7 +59,7 @@ def _generate_qr_code(data: str) -> str:
def _generate_secret_and_qr_code(username: str) -> tuple[str, str, str]:
"""Generate a secret, url, and QR code."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
ota_secret = pyotp.random_base32()
url = pyotp.totp.TOTP(ota_secret).provisioning_uri(
@@ -107,7 +107,7 @@ class TotpAuthModule(MultiFactorAuthModule):
def _add_ota_secret(self, user_id: str, secret: str | None = None) -> str:
"""Create a ota_secret for user."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
ota_secret: str = secret or pyotp.random_base32()
@@ -163,7 +163,7 @@ class TotpAuthModule(MultiFactorAuthModule):
def _validate_2fa(self, user_id: str, code: str) -> bool:
"""Validate two factor authentication code."""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
if (ota_secret := self._users.get(user_id)) is None: # type: ignore[union-attr]
# even we cannot find user, we still do verify
@@ -196,7 +196,7 @@ class TotpSetupFlow(SetupFlow[TotpAuthModule]):
Return self.async_show_form(step_id='init') if user_input is None.
Return self.async_create_entry(data={'result': result}) if finish.
"""
import pyotp # pylint: disable=import-outside-toplevel
import pyotp # noqa: PLC0415
errors: dict[str, str] = {}

View File

@@ -75,7 +75,6 @@ from .core_config import async_process_ha_core_config
from .exceptions import HomeAssistantError
from .helpers import (
area_registry,
backup,
category_registry,
config_validation as cv,
device_registry,
@@ -89,6 +88,7 @@ from .helpers import (
restore_state,
template,
translation,
trigger,
)
from .helpers.dispatcher import async_dispatcher_send_internal
from .helpers.storage import get_internal_store_manager
@@ -394,7 +394,7 @@ async def async_setup_hass(
def open_hass_ui(hass: core.HomeAssistant) -> None:
"""Open the UI."""
import webbrowser # pylint: disable=import-outside-toplevel
import webbrowser # noqa: PLC0415
if hass.config.api is None or "frontend" not in hass.config.components:
_LOGGER.warning("Cannot launch the UI because frontend not loaded")
@@ -452,6 +452,7 @@ async def async_load_base_functionality(hass: core.HomeAssistant) -> None:
create_eager_task(restore_state.async_load(hass)),
create_eager_task(hass.config_entries.async_initialize()),
create_eager_task(async_get_system_info(hass)),
create_eager_task(trigger.async_setup(hass)),
)
@@ -561,8 +562,7 @@ async def async_enable_logging(
if not log_no_color:
try:
# pylint: disable-next=import-outside-toplevel
from colorlog import ColoredFormatter
from colorlog import ColoredFormatter # noqa: PLC0415
# basicConfig must be called after importing colorlog in order to
# ensure that the handlers it sets up wraps the correct streams.
@@ -879,10 +879,6 @@ async def _async_set_up_integrations(
if "recorder" in all_domains:
recorder.async_initialize_recorder(hass)
# Initialize backup
if "backup" in all_domains:
backup.async_initialize_backup(hass)
stages: list[tuple[str, set[str], int | None]] = [
*(
(name, domain_group, timeout)

View File

@@ -1,5 +1,11 @@
{
"domain": "sony",
"name": "Sony",
"integrations": ["braviatv", "ps4", "sony_projector", "songpal"]
"integrations": [
"braviatv",
"ps4",
"sony_projector",
"songpal",
"playstation_network"
]
}

View File

@@ -1,5 +1,6 @@
{
"domain": "switchbot",
"name": "SwitchBot",
"integrations": ["switchbot", "switchbot_cloud"]
"integrations": ["switchbot", "switchbot_cloud"],
"iot_standards": ["matter"]
}

View File

@@ -0,0 +1,5 @@
{
"domain": "tilt",
"name": "Tilt",
"integrations": ["tilt_ble", "tilt_pi"]
}

View File

@@ -185,6 +185,7 @@ FORECAST_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_WIND_DIRECTION],
name="Daily forecast wind bearing",
native_unit_of_measurement=DEGREE,
device_class=SensorDeviceClass.WIND_DIRECTION,
),
AemetSensorEntityDescription(
entity_registry_enabled_default=False,
@@ -192,6 +193,7 @@ FORECAST_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_DIRECTION],
name="Hourly forecast wind bearing",
native_unit_of_measurement=DEGREE,
device_class=SensorDeviceClass.WIND_DIRECTION,
),
AemetSensorEntityDescription(
entity_registry_enabled_default=False,
@@ -334,7 +336,8 @@ WEATHER_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
keys=[AOD_WEATHER, AOD_WIND_DIRECTION],
name="Wind bearing",
native_unit_of_measurement=DEGREE,
state_class=SensorStateClass.MEASUREMENT,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
device_class=SensorDeviceClass.WIND_DIRECTION,
),
AemetSensorEntityDescription(
key=ATTR_API_WIND_MAX_SPEED,

View File

@@ -2,23 +2,42 @@
import logging
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import (
HassJobType,
HomeAssistant,
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.helpers import config_validation as cv, storage
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import UNDEFINED, ConfigType, UndefinedType
from .const import DATA_COMPONENT, DATA_PREFERENCES, DOMAIN
from .const import (
ATTR_INSTRUCTIONS,
ATTR_TASK_NAME,
DATA_COMPONENT,
DATA_PREFERENCES,
DOMAIN,
SERVICE_GENERATE_DATA,
AITaskEntityFeature,
)
from .entity import AITaskEntity
from .http import async_setup as async_setup_conversation_http
from .task import GenTextTask, GenTextTaskResult, async_generate_text
from .http import async_setup as async_setup_http
from .task import GenDataTask, GenDataTaskResult, async_generate_data
__all__ = [
"DOMAIN",
"AITaskEntity",
"GenTextTask",
"GenTextTaskResult",
"async_generate_text",
"AITaskEntityFeature",
"GenDataTask",
"GenDataTaskResult",
"async_generate_data",
"async_setup",
"async_setup_entry",
"async_unload_entry",
@@ -35,7 +54,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DATA_COMPONENT] = entity_component
hass.data[DATA_PREFERENCES] = AITaskPreferences(hass)
await hass.data[DATA_PREFERENCES].async_load()
async_setup_conversation_http(hass)
async_setup_http(hass)
hass.services.async_register(
DOMAIN,
SERVICE_GENERATE_DATA,
async_service_generate_data,
schema=vol.Schema(
{
vol.Required(ATTR_TASK_NAME): cv.string,
vol.Optional(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(ATTR_INSTRUCTIONS): cv.string,
}
),
supports_response=SupportsResponse.ONLY,
job_type=HassJobType.Coroutinefunction,
)
return True
@@ -49,10 +82,18 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await hass.data[DATA_COMPONENT].async_unload_entry(entry)
async def async_service_generate_data(call: ServiceCall) -> ServiceResponse:
"""Run the run task service."""
result = await async_generate_data(hass=call.hass, **call.data)
return result.as_dict()
class AITaskPreferences:
"""AI Task preferences."""
gen_text_entity_id: str | None = None
KEYS = ("gen_data_entity_id",)
gen_data_entity_id: str | None = None
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the preferences."""
@@ -65,17 +106,18 @@ class AITaskPreferences:
data = await self._store.async_load()
if data is None:
return
self.gen_text_entity_id = data.get("gen_text_entity_id")
for key in self.KEYS:
setattr(self, key, data[key])
@callback
def async_set_preferences(
self,
*,
gen_text_entity_id: str | None | UndefinedType = UNDEFINED,
gen_data_entity_id: str | None | UndefinedType = UNDEFINED,
) -> None:
"""Set the preferences."""
changed = False
for key, value in (("gen_text_entity_id", gen_text_entity_id),):
for key, value in (("gen_data_entity_id", gen_data_entity_id),):
if value is not UNDEFINED:
if getattr(self, key) != value:
setattr(self, key, value)
@@ -84,16 +126,9 @@ class AITaskPreferences:
if not changed:
return
self._store.async_delay_save(
lambda: {
"gen_text_entity_id": self.gen_text_entity_id,
},
10,
)
self._store.async_delay_save(self.as_dict, 10)
@callback
def as_dict(self) -> dict[str, str | None]:
"""Get the current preferences."""
return {
"gen_text_entity_id": self.gen_text_entity_id,
}
return {key: getattr(self, key) for key in self.KEYS}

View File

@@ -2,7 +2,8 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from enum import IntFlag
from typing import TYPE_CHECKING, Final
from homeassistant.util.hass_dict import HassKey
@@ -16,6 +17,18 @@ DOMAIN = "ai_task"
DATA_COMPONENT: HassKey[EntityComponent[AITaskEntity]] = HassKey(DOMAIN)
DATA_PREFERENCES: HassKey[AITaskPreferences] = HassKey(f"{DOMAIN}_preferences")
SERVICE_GENERATE_DATA = "generate_data"
ATTR_INSTRUCTIONS: Final = "instructions"
ATTR_TASK_NAME: Final = "task_name"
DEFAULT_SYSTEM_PROMPT = (
"You are a Home Assistant expert and help users with their tasks."
)
class AITaskEntityFeature(IntFlag):
"""Supported features of the AI task entity."""
GENERATE_DATA = 1
"""Generate data based on instructions."""

View File

@@ -4,6 +4,8 @@ from collections.abc import AsyncGenerator
import contextlib
from typing import final
from propcache.api import cached_property
from homeassistant.components.conversation import (
ChatLog,
UserContent,
@@ -15,14 +17,15 @@ from homeassistant.helpers.chat_session import async_get_chat_session
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.util import dt as dt_util
from .const import DEFAULT_SYSTEM_PROMPT, DOMAIN
from .task import GenTextTask, GenTextTaskResult
from .const import DEFAULT_SYSTEM_PROMPT, DOMAIN, AITaskEntityFeature
from .task import GenDataTask, GenDataTaskResult
class AITaskEntity(RestoreEntity):
"""Entity that supports conversations."""
_attr_should_poll = False
_attr_supported_features = AITaskEntityFeature(0)
__last_activity: str | None = None
@property
@@ -33,6 +36,11 @@ class AITaskEntity(RestoreEntity):
return None
return self.__last_activity
@cached_property
def supported_features(self) -> AITaskEntityFeature:
"""Flag supported features."""
return self._attr_supported_features
async def async_internal_added_to_hass(self) -> None:
"""Call when the entity is added to hass."""
await super().async_internal_added_to_hass()
@@ -48,7 +56,7 @@ class AITaskEntity(RestoreEntity):
@contextlib.asynccontextmanager
async def _async_get_ai_task_chat_log(
self,
task: GenTextTask,
task: GenDataTask,
) -> AsyncGenerator[ChatLog]:
"""Context manager used to manage the ChatLog used during an AI Task."""
# pylint: disable-next=contextmanager-generator-missing-cleanup
@@ -76,20 +84,20 @@ class AITaskEntity(RestoreEntity):
yield chat_log
@final
async def internal_async_generate_text(
async def internal_async_generate_data(
self,
task: GenTextTask,
) -> GenTextTaskResult:
"""Run a gen text task."""
task: GenDataTask,
) -> GenDataTaskResult:
"""Run a gen data task."""
self.__last_activity = dt_util.utcnow().isoformat()
self.async_write_ha_state()
async with self._async_get_ai_task_chat_log(task) as chat_log:
return await self._async_generate_text(task, chat_log)
return await self._async_generate_data(task, chat_log)
async def _async_generate_text(
async def _async_generate_data(
self,
task: GenTextTask,
task: GenDataTask,
chat_log: ChatLog,
) -> GenTextTaskResult:
"""Handle a gen text task."""
) -> GenDataTaskResult:
"""Handle a gen data task."""
raise NotImplementedError

View File

@@ -8,43 +8,15 @@ from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from .const import DATA_PREFERENCES
from .task import async_generate_text
@callback
def async_setup(hass: HomeAssistant) -> None:
"""Set up the HTTP API for the conversation integration."""
websocket_api.async_register_command(hass, websocket_generate_text)
websocket_api.async_register_command(hass, websocket_get_preferences)
websocket_api.async_register_command(hass, websocket_set_preferences)
@websocket_api.websocket_command(
{
vol.Required("type"): "ai_task/generate_text",
vol.Required("task_name"): str,
vol.Optional("entity_id"): str,
vol.Required("instructions"): str,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_generate_text(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Run a generate text task."""
msg.pop("type")
msg_id = msg.pop("id")
try:
result = await async_generate_text(hass=hass, **msg)
except ValueError as err:
connection.send_error(msg_id, websocket_api.const.ERR_UNKNOWN_ERROR, str(err))
return
connection.send_result(msg_id, result.as_dict())
@websocket_api.websocket_command(
{
vol.Required("type"): "ai_task/preferences/get",
@@ -64,7 +36,7 @@ def websocket_get_preferences(
@websocket_api.websocket_command(
{
vol.Required("type"): "ai_task/preferences/set",
vol.Optional("gen_text_entity_id"): vol.Any(str, None),
vol.Optional("gen_data_entity_id"): vol.Any(str, None),
}
)
@websocket_api.require_admin

View File

@@ -0,0 +1,7 @@
{
"services": {
"generate_data": {
"service": "mdi:file-star-four-points-outline"
}
}
}

View File

@@ -0,0 +1,19 @@
generate_data:
fields:
task_name:
example: "home summary"
required: true
selector:
text:
instructions:
example: "Generate a funny notification that the garage door was left open"
required: true
selector:
text:
entity_id:
required: false
selector:
entity:
domain: ai_task
supported_features:
- ai_task.AITaskEntityFeature.GENERATE_DATA

View File

@@ -0,0 +1,22 @@
{
"services": {
"generate_data": {
"name": "Generate data",
"description": "Uses AI to run a task that generates data.",
"fields": {
"task_name": {
"name": "Task name",
"description": "Name of the task."
},
"instructions": {
"name": "Instructions",
"description": "Instructions on what needs to be done."
},
"entity_id": {
"name": "Entity ID",
"description": "Entity ID to run the task on. If not provided, the preferred entity will be used."
}
}
}
}
}

View File

@@ -3,32 +3,39 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from .const import DATA_COMPONENT, DATA_PREFERENCES
from .const import DATA_COMPONENT, DATA_PREFERENCES, AITaskEntityFeature
async def async_generate_text(
async def async_generate_data(
hass: HomeAssistant,
*,
task_name: str,
entity_id: str | None = None,
instructions: str,
) -> GenTextTaskResult:
) -> GenDataTaskResult:
"""Run a task in the AI Task integration."""
if entity_id is None:
entity_id = hass.data[DATA_PREFERENCES].gen_text_entity_id
entity_id = hass.data[DATA_PREFERENCES].gen_data_entity_id
if entity_id is None:
raise ValueError("No entity_id provided and no preferred entity set")
raise HomeAssistantError("No entity_id provided and no preferred entity set")
entity = hass.data[DATA_COMPONENT].get_entity(entity_id)
if entity is None:
raise ValueError(f"AI Task entity {entity_id} not found")
raise HomeAssistantError(f"AI Task entity {entity_id} not found")
return await entity.internal_async_generate_text(
GenTextTask(
if AITaskEntityFeature.GENERATE_DATA not in entity.supported_features:
raise HomeAssistantError(
f"AI Task entity {entity_id} does not support generating data"
)
return await entity.internal_async_generate_data(
GenDataTask(
name=task_name,
instructions=instructions,
)
@@ -36,8 +43,8 @@ async def async_generate_text(
@dataclass(slots=True)
class GenTextTask:
"""Gen text task to be processed."""
class GenDataTask:
"""Gen data task to be processed."""
name: str
"""Name of the task."""
@@ -47,22 +54,22 @@ class GenTextTask:
def __str__(self) -> str:
"""Return task as a string."""
return f"<GenTextTask {self.name}: {id(self)}>"
return f"<GenDataTask {self.name}: {id(self)}>"
@dataclass(slots=True)
class GenTextTaskResult:
"""Result of gen text task."""
class GenDataTaskResult:
"""Result of gen data task."""
conversation_id: str
"""Unique identifier for the conversation."""
result: str
"""Result of the task."""
data: Any
"""Data generated by the task."""
def as_dict(self) -> dict[str, str]:
def as_dict(self) -> dict[str, Any]:
"""Return result as a dict."""
return {
"conversation_id": self.conversation_id,
"result": self.result,
"data": self.data,
}

View File

@@ -39,14 +39,14 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
)
self._abort_if_unique_id_configured()
try:
location_point_valid = await test_location(
location_point_valid = await check_location(
websession,
user_input["api_key"],
user_input["latitude"],
user_input["longitude"],
)
if not location_point_valid:
location_nearest_valid = await test_location(
location_nearest_valid = await check_location(
websession,
user_input["api_key"],
user_input["latitude"],
@@ -88,7 +88,7 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
)
async def test_location(
async def check_location(
client: ClientSession,
api_key: str,
latitude: float,

View File

@@ -71,7 +71,7 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""
data = {}
data: dict[str, Any] = {}
try:
obs = await self.airnow.observations.latLong(
self.latitude,

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/airnow",
"iot_class": "cloud_polling",
"loggers": ["pyairnow"],
"requirements": ["pyairnow==1.2.1"]
"requirements": ["pyairnow==1.3.1"]
}

View File

@@ -10,7 +10,7 @@ from aioairq.core import AirQ, identify_warming_up_sensors
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@@ -39,7 +39,7 @@ class AirQCoordinator(DataUpdateCoordinator):
name=DOMAIN,
update_interval=timedelta(seconds=UPDATE_INTERVAL),
)
session = async_get_clientsession(hass)
session = async_create_clientsession(hass)
self.airq = AirQ(
entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD], session
)

View File

@@ -8,6 +8,7 @@ from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.NOTIFY,
Platform.SENSOR,
Platform.SWITCH,
]
@@ -28,5 +29,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
async def async_unload_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Unload a config entry."""
await entry.runtime_data.api.close()
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
coordinator = entry.runtime_data
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
await coordinator.api.close()
return unload_ok

View File

@@ -7,6 +7,7 @@ from dataclasses import dataclass
from typing import Final
from aioamazondevices.api import AmazonDevice
from aioamazondevices.const import SENSOR_STATE_OFF
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@@ -28,7 +29,8 @@ PARALLEL_UPDATES = 0
class AmazonBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Alexa Devices binary sensor entity description."""
is_on_fn: Callable[[AmazonDevice], bool]
is_on_fn: Callable[[AmazonDevice, str], bool]
is_supported: Callable[[AmazonDevice, str], bool] = lambda device, key: True
BINARY_SENSORS: Final = (
@@ -36,13 +38,49 @@ BINARY_SENSORS: Final = (
key="online",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
is_on_fn=lambda _device: _device.online,
is_on_fn=lambda device, _: device.online,
),
AmazonBinarySensorEntityDescription(
key="bluetooth",
entity_category=EntityCategory.DIAGNOSTIC,
translation_key="bluetooth",
is_on_fn=lambda _device: _device.bluetooth_state,
is_on_fn=lambda device, _: device.bluetooth_state,
),
AmazonBinarySensorEntityDescription(
key="babyCryDetectionState",
translation_key="baby_cry_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
AmazonBinarySensorEntityDescription(
key="beepingApplianceDetectionState",
translation_key="beeping_appliance_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
AmazonBinarySensorEntityDescription(
key="coughDetectionState",
translation_key="cough_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
AmazonBinarySensorEntityDescription(
key="dogBarkDetectionState",
translation_key="dog_bark_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
AmazonBinarySensorEntityDescription(
key="humanPresenceDetectionState",
device_class=BinarySensorDeviceClass.MOTION,
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
AmazonBinarySensorEntityDescription(
key="waterSoundsDetectionState",
translation_key="water_sounds_detection",
is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF),
is_supported=lambda device, key: device.sensors.get(key) is not None,
),
)
@@ -60,6 +98,7 @@ async def async_setup_entry(
AmazonBinarySensorEntity(coordinator, serial_num, sensor_desc)
for sensor_desc in BINARY_SENSORS
for serial_num in coordinator.data
if sensor_desc.is_supported(coordinator.data[serial_num], sensor_desc.key)
)
@@ -71,4 +110,6 @@ class AmazonBinarySensorEntity(AmazonEntity, BinarySensorEntity):
@property
def is_on(self) -> bool:
"""Return True if the binary sensor is on."""
return self.entity_description.is_on_fn(self.device)
return self.entity_description.is_on_fn(
self.device, self.entity_description.key
)

View File

@@ -2,9 +2,39 @@
"entity": {
"binary_sensor": {
"bluetooth": {
"default": "mdi:bluetooth",
"default": "mdi:bluetooth-off",
"state": {
"off": "mdi:bluetooth-off"
"on": "mdi:bluetooth"
}
},
"baby_cry_detection": {
"default": "mdi:account-voice-off",
"state": {
"on": "mdi:account-voice"
}
},
"beeping_appliance_detection": {
"default": "mdi:bell-off",
"state": {
"on": "mdi:bell-ring"
}
},
"cough_detection": {
"default": "mdi:blur-off",
"state": {
"on": "mdi:blur"
}
},
"dog_bark_detection": {
"default": "mdi:dog-side-off",
"state": {
"on": "mdi:dog-side"
}
},
"water_sounds_detection": {
"default": "mdi:water-pump-off",
"state": {
"on": "mdi:water-pump"
}
}
}

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "bronze",
"requirements": ["aioamazondevices==3.1.4"]
"requirements": ["aioamazondevices==3.1.22"]
}

View File

@@ -15,6 +15,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
from .utils import alexa_api_call
PARALLEL_UPDATES = 1
@@ -70,6 +71,7 @@ class AmazonNotifyEntity(AmazonEntity, NotifyEntity):
entity_description: AmazonNotifyEntityDescription
@alexa_api_call
async def async_send_message(
self, message: str, title: str | None = None, **kwargs: Any
) -> None:

View File

@@ -26,7 +26,7 @@ rules:
unique-config-entry: done
# Silver
action-exceptions: todo
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo

View File

@@ -0,0 +1,88 @@
"""Support for sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Final
from aioamazondevices.api import AmazonDevice
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import LIGHT_LUX, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AmazonSensorEntityDescription(SensorEntityDescription):
"""Amazon Devices sensor entity description."""
native_unit_of_measurement_fn: Callable[[AmazonDevice, str], str] | None = None
SENSORS: Final = (
AmazonSensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement_fn=lambda device, _key: (
UnitOfTemperature.CELSIUS
if device.sensors[_key].scale == "CELSIUS"
else UnitOfTemperature.FAHRENHEIT
),
),
AmazonSensorEntityDescription(
key="illuminance",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Amazon Devices sensors based on a config entry."""
coordinator = entry.runtime_data
async_add_entities(
AmazonSensorEntity(coordinator, serial_num, sensor_desc)
for sensor_desc in SENSORS
for serial_num in coordinator.data
if coordinator.data[serial_num].sensors.get(sensor_desc.key) is not None
)
class AmazonSensorEntity(AmazonEntity, SensorEntity):
"""Sensor device."""
entity_description: AmazonSensorEntityDescription
@property
def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement of the sensor."""
if self.entity_description.native_unit_of_measurement_fn:
return self.entity_description.native_unit_of_measurement_fn(
self.device, self.entity_description.key
)
return super().native_unit_of_measurement
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self.device.sensors[self.entity_description.key].value

View File

@@ -1,8 +1,7 @@
{
"common": {
"data_country": "Country code",
"data_code": "One-time password (OTP code)",
"data_description_country": "The country of your Amazon account.",
"data_description_country": "The country where your Amazon account is registered.",
"data_description_username": "The email address of your Amazon account.",
"data_description_password": "The password of your Amazon account.",
"data_description_code": "The one-time password to log in to your account. Currently, only tokens from OTP applications are supported."
@@ -12,10 +11,10 @@
"step": {
"user": {
"data": {
"country": "[%key:component::alexa_devices::common::data_country%]",
"country": "[%key:common::config_flow::data::country%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
"code": "[%key:component::alexa_devices::common::data_code%]"
},
"data_description": {
"country": "[%key:component::alexa_devices::common::data_description_country%]",
@@ -41,6 +40,21 @@
"binary_sensor": {
"bluetooth": {
"name": "Bluetooth"
},
"baby_cry_detection": {
"name": "Baby crying"
},
"beeping_appliance_detection": {
"name": "Beeping appliance"
},
"cough_detection": {
"name": "Coughing"
},
"dog_bark_detection": {
"name": "Dog barking"
},
"water_sounds_detection": {
"name": "Water sounds"
}
},
"notify": {
@@ -56,5 +70,13 @@
"name": "Do not disturb"
}
}
},
"exceptions": {
"cannot_connect": {
"message": "Error connecting: {error}"
},
"cannot_retrieve_data": {
"message": "Error retrieving data: {error}"
}
}
}

View File

@@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity
from .utils import alexa_api_call
PARALLEL_UPDATES = 1
@@ -60,6 +61,7 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity):
entity_description: AmazonSwitchEntityDescription
@alexa_api_call
async def _switch_set_state(self, state: bool) -> None:
"""Set desired switch state."""
method = getattr(self.coordinator.api, self.entity_description.method)

View File

@@ -0,0 +1,40 @@
"""Utils for Alexa Devices."""
from collections.abc import Awaitable, Callable, Coroutine
from functools import wraps
from typing import Any, Concatenate
from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData
from homeassistant.exceptions import HomeAssistantError
from .const import DOMAIN
from .entity import AmazonEntity
def alexa_api_call[_T: AmazonEntity, **_P](
func: Callable[Concatenate[_T, _P], Awaitable[None]],
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]:
"""Catch Alexa API call exceptions."""
@wraps(func)
async def cmd_wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
"""Wrap all command methods."""
try:
await func(self, *args, **kwargs)
except CannotConnect as err:
self.coordinator.last_update_success = False
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={"error": repr(err)},
) from err
except CannotRetrieveData as err:
self.coordinator.last_update_success = False
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_retrieve_data",
translation_placeholders={"error": repr(err)},
) from err
return cmd_wrapper

View File

@@ -0,0 +1,27 @@
"""The Altruist integration."""
from __future__ import annotations
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from .coordinator import AltruistConfigEntry, AltruistDataUpdateCoordinator
PLATFORMS = [Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: AltruistConfigEntry) -> bool:
"""Set up Altruist from a config entry."""
coordinator = AltruistDataUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AltruistConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -0,0 +1,107 @@
"""Config flow for the Altruist integration."""
import logging
from typing import Any
from altruistclient import AltruistClient, AltruistDeviceModel, AltruistError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_HOST, DOMAIN
_LOGGER = logging.getLogger(__name__)
class AltruistConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Altruist."""
device: AltruistDeviceModel
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
ip_address = ""
if user_input is not None:
ip_address = user_input[CONF_HOST]
try:
client = await AltruistClient.from_ip_address(
async_get_clientsession(self.hass), ip_address
)
except AltruistError:
errors["base"] = "no_device_found"
else:
self.device = client.device
await self.async_set_unique_id(
client.device_id, raise_on_progress=False
)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self.device.id,
data={
CONF_HOST: ip_address,
},
)
data_schema = self.add_suggested_values_to_schema(
vol.Schema({vol.Required(CONF_HOST): str}),
{CONF_HOST: ip_address},
)
return self.async_show_form(
step_id="user",
data_schema=data_schema,
errors=errors,
description_placeholders={
"ip_address": ip_address,
},
)
async def async_step_zeroconf(
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
_LOGGER.debug("Zeroconf discovery: %s", discovery_info)
try:
client = await AltruistClient.from_ip_address(
async_get_clientsession(self.hass), str(discovery_info.ip_address)
)
except AltruistError:
return self.async_abort(reason="no_device_found")
self.device = client.device
_LOGGER.debug("Zeroconf device: %s", client.device)
await self.async_set_unique_id(client.device_id)
self._abort_if_unique_id_configured()
self.context.update(
{
"title_placeholders": {
"name": self.device.id,
}
}
)
return await self.async_step_discovery_confirm()
async def async_step_discovery_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm discovery."""
if user_input is not None:
return self.async_create_entry(
title=self.device.id,
data={
CONF_HOST: self.device.ip_address,
},
)
self._set_confirm_only()
return self.async_show_form(
step_id="discovery_confirm",
description_placeholders={
"model": self.device.id,
},
)

View File

@@ -0,0 +1,5 @@
"""Constants for the Altruist integration."""
DOMAIN = "altruist"
CONF_HOST = "host"

View File

@@ -0,0 +1,64 @@
"""Coordinator module for Altruist integration in Home Assistant.
This module defines the AltruistDataUpdateCoordinator class, which manages
data updates for Altruist sensors using the AltruistClient.
"""
from datetime import timedelta
import logging
from altruistclient import AltruistClient, AltruistError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_HOST
_LOGGER = logging.getLogger(__name__)
UPDATE_INTERVAL = timedelta(seconds=15)
type AltruistConfigEntry = ConfigEntry[AltruistDataUpdateCoordinator]
class AltruistDataUpdateCoordinator(DataUpdateCoordinator[dict[str, str]]):
"""Coordinates data updates for Altruist sensors."""
client: AltruistClient
def __init__(
self,
hass: HomeAssistant,
config_entry: AltruistConfigEntry,
) -> None:
"""Initialize the data update coordinator for Altruist sensors."""
device_id = config_entry.unique_id
super().__init__(
hass,
logger=_LOGGER,
config_entry=config_entry,
name=f"Altruist {device_id}",
update_interval=UPDATE_INTERVAL,
)
self._ip_address = config_entry.data[CONF_HOST]
async def _async_setup(self) -> None:
try:
self.client = await AltruistClient.from_ip_address(
async_get_clientsession(self.hass), self._ip_address
)
await self.client.fetch_data()
except AltruistError as e:
raise ConfigEntryNotReady("Error in Altruist setup") from e
async def _async_update_data(self) -> dict[str, str]:
try:
fetched_data = await self.client.fetch_data()
except AltruistError as ex:
raise UpdateFailed(
f"The Altruist {self.client.device_id} is unavailable: {ex}"
) from ex
return {item["value_type"]: item["value"] for item in fetched_data}

View File

@@ -0,0 +1,15 @@
{
"entity": {
"sensor": {
"pm_10": {
"default": "mdi:thought-bubble"
},
"pm_25": {
"default": "mdi:thought-bubble-outline"
},
"radiation": {
"default": "mdi:radioactive"
}
}
}
}

View File

@@ -0,0 +1,12 @@
{
"domain": "altruist",
"name": "Altruist",
"codeowners": ["@airalab", "@LoSk-p"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/altruist",
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["altruistclient==0.1.1"],
"zeroconf": ["_altruist._tcp.local."]
}

View File

@@ -0,0 +1,83 @@
rules:
# Bronze
action-setup:
status: exempt
comment: |
This integration does not provide additional actions.
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
This integration does not provide additional actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: |
Entities of this integration does not explicitly subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions:
status: exempt
comment: |
This integration does not provide additional actions.
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
comment: |
This integration does not provide options flow.
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: todo
reauthentication-flow: todo
test-coverage: done
# Gold
devices: done
diagnostics: todo
discovery-update-info: todo
discovery: done
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
dynamic-devices:
status: exempt
comment: |
Device type integration
entity-category: todo
entity-device-class: done
entity-disabled-by-default: todo
entity-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues:
status: exempt
comment: No known use cases for repair issues or flows, yet
stale-devices:
status: exempt
comment: |
Device type integration
# Platinum
async-dependency: done
inject-websession: done
strict-typing: done

View File

@@ -0,0 +1,249 @@
"""Defines the Altruist sensor platform."""
from collections.abc import Callable
from dataclasses import dataclass
import logging
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfPressure,
UnitOfSoundPressure,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AltruistConfigEntry
from .coordinator import AltruistDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True)
class AltruistSensorEntityDescription(SensorEntityDescription):
"""Class to describe a Sensor entity."""
native_value_fn: Callable[[str], float] = float
state_class = SensorStateClass.MEASUREMENT
SENSOR_DESCRIPTIONS = [
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
key="BME280_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME280"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PRESSURE,
key="BME280_pressure",
translation_key="pressure",
native_unit_of_measurement=UnitOfPressure.PA,
suggested_unit_of_measurement=UnitOfPressure.MMHG,
suggested_display_precision=0,
translation_placeholders={"sensor_name": "BME280"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key="BME280_temperature",
translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME280"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PRESSURE,
key="BMP_pressure",
translation_key="pressure",
native_unit_of_measurement=UnitOfPressure.PA,
suggested_unit_of_measurement=UnitOfPressure.MMHG,
suggested_display_precision=0,
translation_placeholders={"sensor_name": "BMP"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key="BMP_temperature",
translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BMP"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key="BMP280_temperature",
translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BMP280"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PRESSURE,
key="BMP280_pressure",
translation_key="pressure",
native_unit_of_measurement=UnitOfPressure.PA,
suggested_unit_of_measurement=UnitOfPressure.MMHG,
suggested_display_precision=0,
translation_placeholders={"sensor_name": "BMP280"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
key="HTU21D_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "HTU21D"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key="HTU21D_temperature",
translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "HTU21D"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PM10,
translation_key="pm_10",
key="SDS_P1",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PM25,
translation_key="pm_25",
key="SDS_P2",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
key="SHT3X_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "SHT3X"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key="SHT3X_temperature",
translation_key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "SHT3X"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
key="signal",
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.SOUND_PRESSURE,
key="PCBA_noiseMax",
translation_key="noise_max",
native_unit_of_measurement=UnitOfSoundPressure.DECIBEL,
suggested_display_precision=0,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.SOUND_PRESSURE,
key="PCBA_noiseAvg",
translation_key="noise_avg",
native_unit_of_measurement=UnitOfSoundPressure.DECIBEL,
suggested_display_precision=0,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
translation_key="co2",
key="CCS_CO2",
suggested_display_precision=2,
translation_placeholders={"sensor_name": "CCS"},
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
key="CCS_TVOC",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
key="GC",
native_unit_of_measurement="μR/h",
translation_key="radiation",
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.CO2,
translation_key="co2",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
key="SCD4x_co2",
suggested_display_precision=2,
translation_placeholders={"sensor_name": "SCD4x"},
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AltruistConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Add sensors for passed config_entry in HA."""
coordinator = config_entry.runtime_data
async_add_entities(
AltruistSensor(coordinator, sensor_description)
for sensor_description in SENSOR_DESCRIPTIONS
if sensor_description.key in coordinator.data
)
class AltruistSensor(CoordinatorEntity[AltruistDataUpdateCoordinator], SensorEntity):
"""Implementation of a Altruist sensor."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AltruistDataUpdateCoordinator,
description: AltruistSensorEntityDescription,
) -> None:
"""Initialize the Altruist sensor."""
super().__init__(coordinator)
self._device = coordinator.client.device
self.entity_description: AltruistSensorEntityDescription = description
self._attr_unique_id = f"{self._device.id}-{description.key}"
self._attr_device_info = DeviceInfo(
connections={(CONNECTION_NETWORK_MAC, self._device.id)},
manufacturer="Robonomics",
model="Altruist",
sw_version=self._device.fw_version,
configuration_url=f"http://{self._device.ip_address}",
serial_number=self._device.id,
)
@property
def available(self) -> bool:
"""Return True if entity is available."""
return (
super().available and self.entity_description.key in self.coordinator.data
)
@property
def native_value(self) -> float | int:
"""Return the native value of the sensor."""
string_value = self.coordinator.data[self.entity_description.key]
return self.entity_description.native_value_fn(string_value)

View File

@@ -0,0 +1,51 @@
{
"config": {
"flow_title": "{name}",
"step": {
"discovery_confirm": {
"description": "Do you want to start setup {model}?"
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "Altruist IP address or hostname in the local network"
},
"description": "Fill in Altruist IP address or hostname in your local network"
}
},
"abort": {
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"error": {
"no_device_found": "[%key:common::config_flow::error::cannot_connect%]"
}
},
"entity": {
"sensor": {
"humidity": {
"name": "{sensor_name} humidity"
},
"pressure": {
"name": "{sensor_name} pressure"
},
"temperature": {
"name": "{sensor_name} temperature"
},
"noise_max": {
"name": "Maximum noise"
},
"noise_avg": {
"name": "Average noise"
},
"co2": {
"name": "{sensor_name} CO2"
},
"radiation": {
"name": "Radiation level"
}
}
}
}

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
from homeassistant.components.camera import CameraEntityFeature
from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging
from homeassistant.const import (
CONF_HOST,
@@ -31,6 +32,7 @@ class IPWebcamCamera(MjpegCamera):
"""Representation of a IP Webcam camera."""
_attr_has_entity_name = True
_attr_supported_features = CameraEntityFeature.STREAM
def __init__(self, coordinator: AndroidIPCamDataUpdateCoordinator) -> None:
"""Initialize the camera."""
@@ -46,3 +48,17 @@ class IPWebcamCamera(MjpegCamera):
identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
name=coordinator.config_entry.data[CONF_HOST],
)
self._coordinator = coordinator
async def stream_source(self) -> str:
"""Get the stream source for the Android IP camera."""
return self._coordinator.cam.get_rtsp_url(
video_codec="h264", # most compatible & recommended
# while "opus" is compatible with more devices,
# HA's stream integration requires AAC or MP3,
# and IP webcam doesn't provide MP3 audio.
# aac is supported on select devices >= android 4.1.
# The stream will be quiet on devices that don't support aac,
# but it won't fail.
audio_codec="aac",
)

View File

@@ -6,13 +6,24 @@ from functools import partial
import anthropic
from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import ConfigEntry, ConfigSubentry
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.typing import ConfigType
from .const import CONF_CHAT_MODEL, DOMAIN, LOGGER, RECOMMENDED_CHAT_MODEL
from .const import (
CONF_CHAT_MODEL,
DEFAULT_CONVERSATION_NAME,
DOMAIN,
LOGGER,
RECOMMENDED_CHAT_MODEL,
)
PLATFORMS = (Platform.CONVERSATION,)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
@@ -20,13 +31,24 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
type AnthropicConfigEntry = ConfigEntry[anthropic.AsyncClient]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Anthropic."""
await async_migrate_integration(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: AnthropicConfigEntry) -> bool:
"""Set up Anthropic from a config entry."""
client = await hass.async_add_executor_job(
partial(anthropic.AsyncAnthropic, api_key=entry.data[CONF_API_KEY])
)
try:
model_id = entry.options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL)
# Use model from first conversation subentry for validation
subentries = list(entry.subentries.values())
if subentries:
model_id = subentries[0].data.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL)
else:
model_id = RECOMMENDED_CHAT_MODEL
model = await client.models.retrieve(model_id=model_id, timeout=10.0)
LOGGER.debug("Anthropic model: %s", model.display_name)
except anthropic.AuthenticationError as err:
@@ -45,3 +67,75 @@ async def async_setup_entry(hass: HomeAssistant, entry: AnthropicConfigEntry) ->
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Anthropic."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_integration(hass: HomeAssistant) -> None:
"""Migrate integration entry structure."""
entries = hass.config_entries.async_entries(DOMAIN)
if not any(entry.version == 1 for entry in entries):
return
api_keys_entries: dict[str, ConfigEntry] = {}
entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass)
for entry in entries:
use_existing = False
subentry = ConfigSubentry(
data=entry.options,
subentry_type="conversation",
title=entry.title,
unique_id=None,
)
if entry.data[CONF_API_KEY] not in api_keys_entries:
use_existing = True
api_keys_entries[entry.data[CONF_API_KEY]] = entry
parent_entry = api_keys_entries[entry.data[CONF_API_KEY]]
hass.config_entries.async_add_subentry(parent_entry, subentry)
conversation_entity = entity_registry.async_get_entity_id(
"conversation",
DOMAIN,
entry.entry_id,
)
if conversation_entity is not None:
entity_registry.async_update_entity(
conversation_entity,
config_entry_id=parent_entry.entry_id,
config_subentry_id=subentry.subentry_id,
new_unique_id=subentry.subentry_id,
)
device = device_registry.async_get_device(
identifiers={(DOMAIN, entry.entry_id)}
)
if device is not None:
device_registry.async_update_device(
device.id,
new_identifiers={(DOMAIN, subentry.subentry_id)},
add_config_subentry_id=subentry.subentry_id,
add_config_entry_id=parent_entry.entry_id,
)
if parent_entry.entry_id != entry.entry_id:
device_registry.async_update_device(
device.id,
remove_config_entry_id=entry.entry_id,
)
else:
device_registry.async_update_device(
device.id,
remove_config_entry_id=entry.entry_id,
remove_config_subentry_id=None,
)
if not use_existing:
await hass.config_entries.async_remove(entry.entry_id)
else:
hass.config_entries.async_update_entry(
entry,
title=DEFAULT_CONVERSATION_NAME,
options={},
version=2,
)

View File

@@ -5,20 +5,21 @@ from __future__ import annotations
from collections.abc import Mapping
from functools import partial
import logging
from types import MappingProxyType
from typing import Any
from typing import Any, cast
import anthropic
import voluptuous as vol
from homeassistant.config_entries import (
ConfigEntry,
ConfigEntryState,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
ConfigSubentryFlow,
SubentryFlowResult,
)
from homeassistant.const import CONF_API_KEY, CONF_LLM_HASS_API
from homeassistant.core import HomeAssistant
from homeassistant.const import CONF_API_KEY, CONF_LLM_HASS_API, CONF_NAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import llm
from homeassistant.helpers.selector import (
NumberSelector,
@@ -36,6 +37,7 @@ from .const import (
CONF_RECOMMENDED,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
DEFAULT_CONVERSATION_NAME,
DOMAIN,
RECOMMENDED_CHAT_MODEL,
RECOMMENDED_MAX_TOKENS,
@@ -72,7 +74,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Anthropic."""
VERSION = 1
VERSION = 2
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -81,6 +83,7 @@ class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
errors = {}
if user_input is not None:
self._async_abort_entries_match(user_input)
try:
await validate_input(self.hass, user_input)
except anthropic.APITimeoutError:
@@ -102,57 +105,93 @@ class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_create_entry(
title="Claude",
data=user_input,
options=RECOMMENDED_OPTIONS,
subentries=[
{
"subentry_type": "conversation",
"data": RECOMMENDED_OPTIONS,
"title": DEFAULT_CONVERSATION_NAME,
"unique_id": None,
}
],
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors or None
)
@staticmethod
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlow:
"""Create the options flow."""
return AnthropicOptionsFlow(config_entry)
@classmethod
@callback
def async_get_supported_subentry_types(
cls, config_entry: ConfigEntry
) -> dict[str, type[ConfigSubentryFlow]]:
"""Return subentries supported by this integration."""
return {"conversation": ConversationSubentryFlowHandler}
class AnthropicOptionsFlow(OptionsFlow):
"""Anthropic config flow options handler."""
class ConversationSubentryFlowHandler(ConfigSubentryFlow):
"""Flow for managing conversation subentries."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.last_rendered_recommended = config_entry.options.get(
CONF_RECOMMENDED, False
)
last_rendered_recommended = False
async def async_step_init(
@property
def _is_new(self) -> bool:
"""Return if this is a new subentry."""
return self.source == "user"
async def async_step_set_options(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the options."""
options: dict[str, Any] | MappingProxyType[str, Any] = self.config_entry.options
) -> SubentryFlowResult:
"""Set conversation options."""
# abort if entry is not loaded
if self._get_entry().state != ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
errors: dict[str, str] = {}
if user_input is not None:
if user_input[CONF_RECOMMENDED] == self.last_rendered_recommended:
if not user_input.get(CONF_LLM_HASS_API):
user_input.pop(CONF_LLM_HASS_API, None)
if user_input.get(
CONF_THINKING_BUDGET, RECOMMENDED_THINKING_BUDGET
) >= user_input.get(CONF_MAX_TOKENS, RECOMMENDED_MAX_TOKENS):
errors[CONF_THINKING_BUDGET] = "thinking_budget_too_large"
if not errors:
return self.async_create_entry(title="", data=user_input)
if user_input is None:
if self._is_new:
options = RECOMMENDED_OPTIONS.copy()
else:
# Re-render the options again, now with the recommended options shown/hidden
self.last_rendered_recommended = user_input[CONF_RECOMMENDED]
# If this is a reconfiguration, we need to copy the existing options
# so that we can show the current values in the form.
options = self._get_reconfigure_subentry().data.copy()
options = {
CONF_RECOMMENDED: user_input[CONF_RECOMMENDED],
CONF_PROMPT: user_input[CONF_PROMPT],
CONF_LLM_HASS_API: user_input.get(CONF_LLM_HASS_API),
}
self.last_rendered_recommended = cast(
bool, options.get(CONF_RECOMMENDED, False)
)
elif user_input[CONF_RECOMMENDED] == self.last_rendered_recommended:
if not user_input.get(CONF_LLM_HASS_API):
user_input.pop(CONF_LLM_HASS_API, None)
if user_input.get(
CONF_THINKING_BUDGET, RECOMMENDED_THINKING_BUDGET
) >= user_input.get(CONF_MAX_TOKENS, RECOMMENDED_MAX_TOKENS):
errors[CONF_THINKING_BUDGET] = "thinking_budget_too_large"
if not errors:
if self._is_new:
return self.async_create_entry(
title=user_input.pop(CONF_NAME),
data=user_input,
)
return self.async_update_and_abort(
self._get_entry(),
self._get_reconfigure_subentry(),
data=user_input,
)
options = user_input
self.last_rendered_recommended = user_input[CONF_RECOMMENDED]
else:
# Re-render the options again, now with the recommended options shown/hidden
self.last_rendered_recommended = user_input[CONF_RECOMMENDED]
options = {
CONF_RECOMMENDED: user_input[CONF_RECOMMENDED],
CONF_PROMPT: user_input[CONF_PROMPT],
CONF_LLM_HASS_API: user_input.get(CONF_LLM_HASS_API),
}
suggested_values = options.copy()
if not suggested_values.get(CONF_PROMPT):
@@ -163,19 +202,25 @@ class AnthropicOptionsFlow(OptionsFlow):
suggested_values[CONF_LLM_HASS_API] = [suggested_llm_apis]
schema = self.add_suggested_values_to_schema(
vol.Schema(anthropic_config_option_schema(self.hass, options)),
vol.Schema(
anthropic_config_option_schema(self.hass, self._is_new, options)
),
suggested_values,
)
return self.async_show_form(
step_id="init",
step_id="set_options",
data_schema=schema,
errors=errors or None,
)
async_step_user = async_step_set_options
async_step_reconfigure = async_step_set_options
def anthropic_config_option_schema(
hass: HomeAssistant,
is_new: bool,
options: Mapping[str, Any],
) -> dict:
"""Return a schema for Anthropic completion options."""
@@ -187,15 +232,24 @@ def anthropic_config_option_schema(
for api in llm.async_get_apis(hass)
]
schema = {
vol.Optional(CONF_PROMPT): TemplateSelector(),
vol.Optional(
CONF_LLM_HASS_API,
): SelectSelector(SelectSelectorConfig(options=hass_apis, multiple=True)),
vol.Required(
CONF_RECOMMENDED, default=options.get(CONF_RECOMMENDED, False)
): bool,
}
if is_new:
schema: dict[vol.Required | vol.Optional, Any] = {
vol.Required(CONF_NAME, default=DEFAULT_CONVERSATION_NAME): str,
}
else:
schema = {}
schema.update(
{
vol.Optional(CONF_PROMPT): TemplateSelector(),
vol.Optional(
CONF_LLM_HASS_API,
): SelectSelector(SelectSelectorConfig(options=hass_apis, multiple=True)),
vol.Required(
CONF_RECOMMENDED, default=options.get(CONF_RECOMMENDED, False)
): bool,
}
)
if options.get(CONF_RECOMMENDED):
return schema

View File

@@ -5,6 +5,8 @@ import logging
DOMAIN = "anthropic"
LOGGER = logging.getLogger(__package__)
DEFAULT_CONVERSATION_NAME = "Claude conversation"
CONF_RECOMMENDED = "recommended"
CONF_PROMPT = "prompt"
CONF_CHAT_MODEL = "chat_model"

View File

@@ -38,7 +38,7 @@ from anthropic.types import (
from voluptuous_openapi import convert
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import ConfigEntry, ConfigSubentry
from homeassistant.const import CONF_LLM_HASS_API, MATCH_ALL
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
@@ -72,8 +72,14 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up conversation entities."""
agent = AnthropicConversationEntity(config_entry)
async_add_entities([agent])
for subentry in config_entry.subentries.values():
if subentry.subentry_type != "conversation":
continue
async_add_entities(
[AnthropicConversationEntity(config_entry, subentry)],
config_subentry_id=subentry.subentry_id,
)
def _format_tool(
@@ -326,21 +332,22 @@ class AnthropicConversationEntity(
):
"""Anthropic conversation agent."""
_attr_has_entity_name = True
_attr_name = None
_attr_supports_streaming = True
def __init__(self, entry: AnthropicConfigEntry) -> None:
def __init__(self, entry: AnthropicConfigEntry, subentry: ConfigSubentry) -> None:
"""Initialize the agent."""
self.entry = entry
self._attr_unique_id = entry.entry_id
self.subentry = subentry
self._attr_name = subentry.title
self._attr_unique_id = subentry.subentry_id
self._attr_device_info = dr.DeviceInfo(
identifiers={(DOMAIN, entry.entry_id)},
identifiers={(DOMAIN, subentry.subentry_id)},
name=subentry.title,
manufacturer="Anthropic",
model="Claude",
entry_type=dr.DeviceEntryType.SERVICE,
)
if self.entry.options.get(CONF_LLM_HASS_API):
if self.subentry.data.get(CONF_LLM_HASS_API):
self._attr_supported_features = (
conversation.ConversationEntityFeature.CONTROL
)
@@ -363,7 +370,7 @@ class AnthropicConversationEntity(
chat_log: conversation.ChatLog,
) -> conversation.ConversationResult:
"""Call the API."""
options = self.entry.options
options = self.subentry.data
try:
await chat_log.async_provide_llm_data(
@@ -393,7 +400,7 @@ class AnthropicConversationEntity(
chat_log: conversation.ChatLog,
) -> None:
"""Generate an answer for the chat log."""
options = self.entry.options
options = self.subentry.data
tools: list[ToolParam] | None = None
if chat_log.llm_api:

View File

@@ -12,28 +12,44 @@
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]",
"authentication_error": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
},
"options": {
"step": {
"init": {
"data": {
"prompt": "Instructions",
"chat_model": "[%key:common::generic::model%]",
"max_tokens": "Maximum tokens to return in response",
"temperature": "Temperature",
"llm_hass_api": "[%key:common::config_flow::data::llm_hass_api%]",
"recommended": "Recommended model settings",
"thinking_budget_tokens": "Thinking budget"
},
"data_description": {
"prompt": "Instruct how the LLM should respond. This can be a template.",
"thinking_budget_tokens": "The number of tokens the model can use to think about the response out of the total maximum number of tokens. Set to 1024 or greater to enable extended thinking."
"config_subentries": {
"conversation": {
"initiate_flow": {
"user": "Add conversation agent",
"reconfigure": "Reconfigure conversation agent"
},
"entry_type": "Conversation agent",
"step": {
"set_options": {
"data": {
"name": "[%key:common::config_flow::data::name%]",
"prompt": "Instructions",
"chat_model": "[%key:common::generic::model%]",
"max_tokens": "Maximum tokens to return in response",
"temperature": "Temperature",
"llm_hass_api": "[%key:common::config_flow::data::llm_hass_api%]",
"recommended": "Recommended model settings",
"thinking_budget_tokens": "Thinking budget"
},
"data_description": {
"prompt": "Instruct how the LLM should respond. This can be a template.",
"thinking_budget_tokens": "The number of tokens the model can use to think about the response out of the total maximum number of tokens. Set to 1024 or greater to enable extended thinking."
}
}
},
"abort": {
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"entry_not_loaded": "Cannot add things while the configuration is disabled."
},
"error": {
"thinking_budget_too_large": "Maximum tokens must be greater than the thinking budget."
}
},
"error": {
"thinking_budget_too_large": "Maximum tokens must be greater than the thinking budget."
}
}
}

View File

@@ -12,6 +12,7 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
PERCENTAGE,
EntityCategory,
UnitOfApparentPower,
UnitOfElectricCurrent,
UnitOfElectricPotential,
@@ -35,6 +36,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
"alarmdel": SensorEntityDescription(
key="alarmdel",
translation_key="alarm_delay",
entity_category=EntityCategory.DIAGNOSTIC,
),
"ambtemp": SensorEntityDescription(
key="ambtemp",
@@ -47,15 +49,18 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="apc",
translation_key="apc_status",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"apcmodel": SensorEntityDescription(
key="apcmodel",
translation_key="apc_model",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"badbatts": SensorEntityDescription(
key="badbatts",
translation_key="bad_batteries",
entity_category=EntityCategory.DIAGNOSTIC,
),
"battdate": SensorEntityDescription(
key="battdate",
@@ -82,6 +87,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="cable",
translation_key="cable_type",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"cumonbatt": SensorEntityDescription(
key="cumonbatt",
@@ -94,52 +100,63 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="date",
translation_key="date",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"dipsw": SensorEntityDescription(
key="dipsw",
translation_key="dip_switch_settings",
entity_category=EntityCategory.DIAGNOSTIC,
),
"dlowbatt": SensorEntityDescription(
key="dlowbatt",
translation_key="low_battery_signal",
entity_category=EntityCategory.DIAGNOSTIC,
),
"driver": SensorEntityDescription(
key="driver",
translation_key="driver",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"dshutd": SensorEntityDescription(
key="dshutd",
translation_key="shutdown_delay",
entity_category=EntityCategory.DIAGNOSTIC,
),
"dwake": SensorEntityDescription(
key="dwake",
translation_key="wake_delay",
entity_category=EntityCategory.DIAGNOSTIC,
),
"end apc": SensorEntityDescription(
key="end apc",
translation_key="date_and_time",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"extbatts": SensorEntityDescription(
key="extbatts",
translation_key="external_batteries",
entity_category=EntityCategory.DIAGNOSTIC,
),
"firmware": SensorEntityDescription(
key="firmware",
translation_key="firmware_version",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"hitrans": SensorEntityDescription(
key="hitrans",
translation_key="transfer_high",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"hostname": SensorEntityDescription(
key="hostname",
translation_key="hostname",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"humidity": SensorEntityDescription(
key="humidity",
@@ -163,10 +180,12 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="lastxfer",
translation_key="last_transfer",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"linefail": SensorEntityDescription(
key="linefail",
translation_key="line_failure",
entity_category=EntityCategory.DIAGNOSTIC,
),
"linefreq": SensorEntityDescription(
key="linefreq",
@@ -198,15 +217,18 @@ SENSORS: dict[str, SensorEntityDescription] = {
translation_key="transfer_low",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"mandate": SensorEntityDescription(
key="mandate",
translation_key="manufacture_date",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"masterupd": SensorEntityDescription(
key="masterupd",
translation_key="master_update",
entity_category=EntityCategory.DIAGNOSTIC,
),
"maxlinev": SensorEntityDescription(
key="maxlinev",
@@ -217,11 +239,13 @@ SENSORS: dict[str, SensorEntityDescription] = {
"maxtime": SensorEntityDescription(
key="maxtime",
translation_key="max_time",
entity_category=EntityCategory.DIAGNOSTIC,
),
"mbattchg": SensorEntityDescription(
key="mbattchg",
translation_key="max_battery_charge",
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"minlinev": SensorEntityDescription(
key="minlinev",
@@ -232,41 +256,48 @@ SENSORS: dict[str, SensorEntityDescription] = {
"mintimel": SensorEntityDescription(
key="mintimel",
translation_key="min_time",
entity_category=EntityCategory.DIAGNOSTIC,
),
"model": SensorEntityDescription(
key="model",
translation_key="model",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"nombattv": SensorEntityDescription(
key="nombattv",
translation_key="battery_nominal_voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"nominv": SensorEntityDescription(
key="nominv",
translation_key="nominal_input_voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"nomoutv": SensorEntityDescription(
key="nomoutv",
translation_key="nominal_output_voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"nompower": SensorEntityDescription(
key="nompower",
translation_key="nominal_output_power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
),
"nomapnt": SensorEntityDescription(
key="nomapnt",
translation_key="nominal_apparent_power",
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
device_class=SensorDeviceClass.APPARENT_POWER,
entity_category=EntityCategory.DIAGNOSTIC,
),
"numxfers": SensorEntityDescription(
key="numxfers",
@@ -291,21 +322,25 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="reg1",
translation_key="register_1_fault",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"reg2": SensorEntityDescription(
key="reg2",
translation_key="register_2_fault",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"reg3": SensorEntityDescription(
key="reg3",
translation_key="register_3_fault",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"retpct": SensorEntityDescription(
key="retpct",
translation_key="restore_capacity",
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
"selftest": SensorEntityDescription(
key="selftest",
@@ -315,20 +350,24 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="sense",
translation_key="sensitivity",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"serialno": SensorEntityDescription(
key="serialno",
translation_key="serial_number",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"starttime": SensorEntityDescription(
key="starttime",
translation_key="startup_time",
entity_category=EntityCategory.DIAGNOSTIC,
),
"statflag": SensorEntityDescription(
key="statflag",
translation_key="online_status",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"status": SensorEntityDescription(
key="status",
@@ -337,6 +376,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
"stesti": SensorEntityDescription(
key="stesti",
translation_key="self_test_interval",
entity_category=EntityCategory.DIAGNOSTIC,
),
"timeleft": SensorEntityDescription(
key="timeleft",
@@ -360,23 +400,28 @@ SENSORS: dict[str, SensorEntityDescription] = {
key="upsname",
translation_key="ups_name",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"version": SensorEntityDescription(
key="version",
translation_key="version",
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"xoffbat": SensorEntityDescription(
key="xoffbat",
translation_key="transfer_from_battery",
entity_category=EntityCategory.DIAGNOSTIC,
),
"xoffbatt": SensorEntityDescription(
key="xoffbatt",
translation_key="transfer_from_battery",
entity_category=EntityCategory.DIAGNOSTIC,
),
"xonbatt": SensorEntityDescription(
key="xonbatt",
translation_key="transfer_to_battery",
entity_category=EntityCategory.DIAGNOSTIC,
),
}

View File

@@ -260,11 +260,18 @@ class APIEntityStateView(HomeAssistantView):
if not user.is_admin:
raise Unauthorized(entity_id=entity_id)
hass = request.app[KEY_HASS]
body = await request.text()
try:
data = await request.json()
data: Any = json_loads(body) if body else None
except ValueError:
return self.json_message("Invalid JSON specified.", HTTPStatus.BAD_REQUEST)
if not isinstance(data, dict):
return self.json_message(
"State data should be a JSON object.", HTTPStatus.BAD_REQUEST
)
if (new_state := data.get("state")) is None:
return self.json_message("No state specified.", HTTPStatus.BAD_REQUEST)
@@ -477,9 +484,19 @@ class APITemplateView(HomeAssistantView):
@require_admin
async def post(self, request: web.Request) -> web.Response:
"""Render a template."""
body = await request.text()
try:
data: Any = json_loads(body) if body else None
except ValueError:
return self.json_message("Invalid JSON specified.", HTTPStatus.BAD_REQUEST)
if not isinstance(data, dict):
return self.json_message(
"Template data should be a JSON object.", HTTPStatus.BAD_REQUEST
)
tpl = _cached_template(data["template"], request.app[KEY_HASS])
try:
data = await request.json()
tpl = _cached_template(data["template"], request.app[KEY_HASS])
return tpl.async_render(variables=data.get("variables"), parse_result=False) # type: ignore[no-any-return]
except (ValueError, TemplateError) as ex:
return self.json_message(

View File

@@ -1119,6 +1119,7 @@ class PipelineRun:
) is not None:
# Sentence trigger matched
agent_id = "sentence_trigger"
processed_locally = True
intent_response = intent.IntentResponse(
self.pipeline.conversation_language
)

View File

@@ -1,13 +1,23 @@
"""Base class for assist satellite entities."""
from dataclasses import asdict
import logging
from pathlib import Path
from typing import Any
from hassil.util import (
PUNCTUATION_END,
PUNCTUATION_END_WORD,
PUNCTUATION_START,
PUNCTUATION_START_WORD,
)
import voluptuous as vol
from homeassistant.components.http import StaticPathConfig
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
@@ -23,6 +33,7 @@ from .const import (
)
from .entity import (
AssistSatelliteAnnouncement,
AssistSatelliteAnswer,
AssistSatelliteConfiguration,
AssistSatelliteEntity,
AssistSatelliteEntityDescription,
@@ -34,6 +45,7 @@ from .websocket_api import async_register_websocket_api
__all__ = [
"DOMAIN",
"AssistSatelliteAnnouncement",
"AssistSatelliteAnswer",
"AssistSatelliteConfiguration",
"AssistSatelliteEntity",
"AssistSatelliteEntityDescription",
@@ -86,6 +98,62 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"async_internal_start_conversation",
[AssistSatelliteEntityFeature.START_CONVERSATION],
)
async def handle_ask_question(call: ServiceCall) -> dict[str, Any]:
"""Handle a Show View service call."""
satellite_entity_id: str = call.data[ATTR_ENTITY_ID]
satellite_entity: AssistSatelliteEntity | None = component.get_entity(
satellite_entity_id
)
if satellite_entity is None:
raise HomeAssistantError(
f"Invalid Assist satellite entity id: {satellite_entity_id}"
)
ask_question_args = {
"question": call.data.get("question"),
"question_media_id": call.data.get("question_media_id"),
"preannounce": call.data.get("preannounce", False),
"answers": call.data.get("answers"),
}
if preannounce_media_id := call.data.get("preannounce_media_id"):
ask_question_args["preannounce_media_id"] = preannounce_media_id
answer = await satellite_entity.async_internal_ask_question(**ask_question_args)
if answer is None:
raise HomeAssistantError("No answer from satellite")
return asdict(answer)
hass.services.async_register(
domain=DOMAIN,
service="ask_question",
service_func=handle_ask_question,
schema=vol.All(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(DOMAIN),
vol.Optional("question"): str,
vol.Optional("question_media_id"): str,
vol.Optional("preannounce"): bool,
vol.Optional("preannounce_media_id"): str,
vol.Optional("answers"): [
{
vol.Required("id"): str,
vol.Required("sentences"): vol.All(
cv.ensure_list,
[cv.string],
has_one_non_empty_item,
has_no_punctuation,
),
}
],
},
cv.has_at_least_one_key("question", "question_media_id"),
),
supports_response=SupportsResponse.ONLY,
)
hass.data[CONNECTION_TEST_DATA] = {}
async_register_websocket_api(hass)
hass.http.register_view(ConnectionTestView())
@@ -110,3 +178,29 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.data[DATA_COMPONENT].async_unload_entry(entry)
def has_no_punctuation(value: list[str]) -> list[str]:
"""Validate result does not contain punctuation."""
for sentence in value:
if (
PUNCTUATION_START.search(sentence)
or PUNCTUATION_END.search(sentence)
or PUNCTUATION_START_WORD.search(sentence)
or PUNCTUATION_END_WORD.search(sentence)
):
raise vol.Invalid("sentence should not contain punctuation")
return value
def has_one_non_empty_item(value: list[str]) -> list[str]:
"""Validate result has at least one item."""
if len(value) < 1:
raise vol.Invalid("at least one sentence is required")
for sentence in value:
if not sentence:
raise vol.Invalid("sentences cannot be empty")
return value

View File

@@ -4,12 +4,16 @@ from abc import abstractmethod
import asyncio
from collections.abc import AsyncIterable
import contextlib
from dataclasses import dataclass
from dataclasses import dataclass, field
from enum import StrEnum
import logging
import time
from typing import Any, Literal, final
from hassil import Intents, recognize
from hassil.expression import Expression, ListReference, Sequence
from hassil.intents import WildcardSlotList
from homeassistant.components import conversation, media_source, stt, tts
from homeassistant.components.assist_pipeline import (
OPTION_PREFERRED,
@@ -105,6 +109,20 @@ class AssistSatelliteAnnouncement:
"""Media ID to be played before announcement."""
@dataclass
class AssistSatelliteAnswer:
"""Answer to a question."""
id: str | None
"""Matched answer id or None if no answer was matched."""
sentence: str
"""Raw sentence text from user response."""
slots: dict[str, Any] = field(default_factory=dict)
"""Matched slots from answer."""
class AssistSatelliteEntity(entity.Entity):
"""Entity encapsulating the state and functionality of an Assist satellite."""
@@ -122,6 +140,7 @@ class AssistSatelliteEntity(entity.Entity):
_wake_word_intercept_future: asyncio.Future[str | None] | None = None
_attr_tts_options: dict[str, Any] | None = None
_pipeline_task: asyncio.Task | None = None
_ask_question_future: asyncio.Future[str | None] | None = None
__assist_satellite_state = AssistSatelliteState.IDLE
@@ -309,6 +328,112 @@ class AssistSatelliteEntity(entity.Entity):
"""Start a conversation from the satellite."""
raise NotImplementedError
async def async_internal_ask_question(
self,
question: str | None = None,
question_media_id: str | None = None,
preannounce: bool = True,
preannounce_media_id: str = PREANNOUNCE_URL,
answers: list[dict[str, Any]] | None = None,
) -> AssistSatelliteAnswer | None:
"""Ask a question and get a user's response from the satellite.
If question_media_id is not provided, question is synthesized to audio
with the selected pipeline.
If question_media_id is provided, it is played directly. It is possible
to omit the message and the satellite will not show any text.
If preannounce is True, a sound is played before the start message or media.
If preannounce_media_id is provided, it overrides the default sound.
Calls async_start_conversation.
"""
await self._cancel_running_pipeline()
if question is None:
question = ""
announcement = await self._resolve_announcement_media_id(
question,
question_media_id,
preannounce_media_id=preannounce_media_id if preannounce else None,
)
if self._is_announcing:
raise SatelliteBusyError
self._is_announcing = True
self._set_state(AssistSatelliteState.RESPONDING)
self._ask_question_future = asyncio.Future()
try:
# Wait for announcement to finish
await self.async_start_conversation(announcement)
# Wait for response text
response_text = await self._ask_question_future
if response_text is None:
raise HomeAssistantError("No answer from question")
if not answers:
return AssistSatelliteAnswer(id=None, sentence=response_text)
return self._question_response_to_answer(response_text, answers)
finally:
self._is_announcing = False
self._set_state(AssistSatelliteState.IDLE)
self._ask_question_future = None
def _question_response_to_answer(
self, response_text: str, answers: list[dict[str, Any]]
) -> AssistSatelliteAnswer:
"""Match text to a pre-defined set of answers."""
# Build intents and match
intents = Intents.from_dict(
{
"language": self.hass.config.language,
"intents": {
"QuestionIntent": {
"data": [
{
"sentences": answer["sentences"],
"metadata": {"answer_id": answer["id"]},
}
for answer in answers
]
}
},
}
)
# Assume slot list references are wildcards
wildcard_names: set[str] = set()
for intent in intents.intents.values():
for intent_data in intent.data:
for sentence in intent_data.sentences:
_collect_list_references(sentence, wildcard_names)
for wildcard_name in wildcard_names:
intents.slot_lists[wildcard_name] = WildcardSlotList(wildcard_name)
# Match response text
result = recognize(response_text, intents)
if result is None:
# No match
return AssistSatelliteAnswer(id=None, sentence=response_text)
assert result.intent_metadata
return AssistSatelliteAnswer(
id=result.intent_metadata["answer_id"],
sentence=response_text,
slots={
entity_name: entity.value
for entity_name, entity in result.entities.items()
},
)
async def async_accept_pipeline_from_satellite(
self,
audio_stream: AsyncIterable[bytes],
@@ -351,6 +476,11 @@ class AssistSatelliteEntity(entity.Entity):
self._internal_on_pipeline_event(PipelineEvent(PipelineEventType.RUN_END))
return
if (self._ask_question_future is not None) and (
start_stage == PipelineStage.STT
):
end_stage = PipelineStage.STT
device_id = self.registry_entry.device_id if self.registry_entry else None
# Refresh context if necessary
@@ -433,6 +563,16 @@ class AssistSatelliteEntity(entity.Entity):
self._set_state(AssistSatelliteState.IDLE)
elif event.type is PipelineEventType.STT_START:
self._set_state(AssistSatelliteState.LISTENING)
elif event.type is PipelineEventType.STT_END:
# Intercepting text for ask question
if (
(self._ask_question_future is not None)
and (not self._ask_question_future.done())
and event.data
):
self._ask_question_future.set_result(
event.data.get("stt_output", {}).get("text")
)
elif event.type is PipelineEventType.INTENT_START:
self._set_state(AssistSatelliteState.PROCESSING)
elif event.type is PipelineEventType.TTS_START:
@@ -443,6 +583,12 @@ class AssistSatelliteEntity(entity.Entity):
if not self._run_has_tts:
self._set_state(AssistSatelliteState.IDLE)
if (self._ask_question_future is not None) and (
not self._ask_question_future.done()
):
# No text for ask question
self._ask_question_future.set_result(None)
self.on_pipeline_event(event)
@callback
@@ -577,3 +723,15 @@ class AssistSatelliteEntity(entity.Entity):
media_id_source=media_id_source,
preannounce_media_id=preannounce_media_id,
)
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
"""Collect list reference names recursively."""
if isinstance(expression, Sequence):
seq: Sequence = expression
for item in seq.items:
_collect_list_references(item, list_names)
elif isinstance(expression, ListReference):
# {list}
list_ref: ListReference = expression
list_names.add(list_ref.slot_name)

View File

@@ -10,6 +10,9 @@
},
"start_conversation": {
"service": "mdi:forum"
},
"ask_question": {
"service": "mdi:microphone-question"
}
}
}

View File

@@ -5,5 +5,6 @@
"dependencies": ["assist_pipeline", "http", "stt", "tts"],
"documentation": "https://www.home-assistant.io/integrations/assist_satellite",
"integration_type": "entity",
"quality_scale": "internal"
"quality_scale": "internal",
"requirements": ["hassil==2.2.3"]
}

View File

@@ -54,3 +54,49 @@ start_conversation:
required: false
selector:
text:
ask_question:
fields:
entity_id:
required: true
selector:
entity:
domain: assist_satellite
supported_features:
- assist_satellite.AssistSatelliteEntityFeature.START_CONVERSATION
question:
required: false
example: "What kind of music would you like to play?"
default: ""
selector:
text:
question_media_id:
required: false
selector:
text:
preannounce:
required: false
default: true
selector:
boolean:
preannounce_media_id:
required: false
selector:
text:
answers:
required: false
selector:
object:
label_field: sentences
description_field: id
multiple: true
translation_key: answers
fields:
id:
required: true
selector:
text:
sentences:
required: true
selector:
text:
multiple: true

View File

@@ -59,6 +59,44 @@
"description": "Custom media ID to play before the start message or media."
}
}
},
"ask_question": {
"name": "Ask question",
"description": "Asks a question and gets the user's response.",
"fields": {
"entity_id": {
"name": "Entity",
"description": "Assist satellite entity to ask the question on."
},
"question": {
"name": "Question",
"description": "The question to ask."
},
"question_media_id": {
"name": "Question media ID",
"description": "The media ID of the question to use instead of text-to-speech."
},
"preannounce": {
"name": "Preannounce",
"description": "Play a sound before the start message or media."
},
"preannounce_media_id": {
"name": "Preannounce media ID",
"description": "Custom media ID to play before the start message or media."
},
"answers": {
"name": "Answers",
"description": "Possible answers to the question."
}
}
}
},
"selector": {
"answers": {
"fields": {
"id": "Answer ID",
"sentences": "Sentences"
}
}
}
}

View File

@@ -12,7 +12,7 @@ DATA_BLUEPRINTS = "automation_blueprints"
def _blueprint_in_use(hass: HomeAssistant, blueprint_path: str) -> bool:
"""Return True if any automation references the blueprint."""
from . import automations_with_blueprint # pylint: disable=import-outside-toplevel
from . import automations_with_blueprint # noqa: PLC0415
return len(automations_with_blueprint(hass, blueprint_path)) > 0
@@ -28,8 +28,7 @@ async def _reload_blueprint_automations(
@callback
def async_get_blueprints(hass: HomeAssistant) -> blueprint.DomainBlueprints:
"""Get automation blueprints."""
# pylint: disable-next=import-outside-toplevel
from .config import AUTOMATION_BLUEPRINT_SCHEMA
from .config import AUTOMATION_BLUEPRINT_SCHEMA # noqa: PLC0415
return blueprint.DomainBlueprints(
hass,

View File

@@ -2,9 +2,9 @@
from homeassistant.config_entries import SOURCE_SYSTEM
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, discovery_flow
from homeassistant.helpers.backup import DATA_BACKUP
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.typing import ConfigType
@@ -37,7 +37,6 @@ from .manager import (
IdleEvent,
IncorrectPasswordError,
ManagerBackup,
ManagerStateEvent,
NewBackup,
RestoreBackupEvent,
RestoreBackupStage,
@@ -45,6 +44,7 @@ from .manager import (
WrittenBackup,
)
from .models import AddonInfo, AgentBackup, BackupNotFound, Folder
from .services import async_setup_services
from .util import suggested_filename, suggested_filename_from_name_date
from .websocket import async_register_websocket_handlers
@@ -71,12 +71,12 @@ __all__ = [
"IncorrectPasswordError",
"LocalBackupAgent",
"ManagerBackup",
"ManagerStateEvent",
"NewBackup",
"RestoreBackupEvent",
"RestoreBackupStage",
"RestoreBackupState",
"WrittenBackup",
"async_get_manager",
"suggested_filename",
"suggested_filename_from_name_date",
]
@@ -94,46 +94,20 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if not with_hassio:
reader_writer = CoreBackupReaderWriter(hass)
else:
# pylint: disable-next=import-outside-toplevel, hass-component-root-import
from homeassistant.components.hassio.backup import SupervisorBackupReaderWriter
# pylint: disable-next=hass-component-root-import
from homeassistant.components.hassio.backup import ( # noqa: PLC0415
SupervisorBackupReaderWriter,
)
reader_writer = SupervisorBackupReaderWriter(hass)
backup_manager = BackupManager(hass, reader_writer)
hass.data[DATA_MANAGER] = backup_manager
try:
await backup_manager.async_setup()
except Exception as err:
hass.data[DATA_BACKUP].manager_ready.set_exception(err)
raise
else:
hass.data[DATA_BACKUP].manager_ready.set_result(None)
await backup_manager.async_setup()
async_register_websocket_handlers(hass, with_hassio)
async def async_handle_create_service(call: ServiceCall) -> None:
"""Service handler for creating backups."""
agent_id = list(backup_manager.local_backup_agents)[0]
await backup_manager.async_create_backup(
agent_ids=[agent_id],
include_addons=None,
include_all_addons=False,
include_database=True,
include_folders=None,
include_homeassistant=True,
name=None,
password=None,
)
async def async_handle_create_automatic_service(call: ServiceCall) -> None:
"""Service handler for creating automatic backups."""
await backup_manager.async_create_automatic_backup()
if not with_hassio:
hass.services.async_register(DOMAIN, "create", async_handle_create_service)
hass.services.async_register(
DOMAIN, "create_automatic", async_handle_create_automatic_service
)
async_setup_services(hass)
async_register_http_views(hass)
@@ -162,3 +136,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: BackupConfigEntry) -> bo
async def async_unload_entry(hass: HomeAssistant, entry: BackupConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@callback
def async_get_manager(hass: HomeAssistant) -> BackupManager:
"""Get the backup manager instance.
Raises HomeAssistantError if the backup integration is not available.
"""
if DATA_MANAGER not in hass.data:
raise HomeAssistantError("Backup integration is not available")
return hass.data[DATA_MANAGER]

View File

@@ -1,38 +0,0 @@
"""Websocket commands for the Backup integration."""
from typing import Any
import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.backup import async_subscribe_events
from .const import DATA_MANAGER
from .manager import ManagerStateEvent
@callback
def async_register_websocket_handlers(hass: HomeAssistant) -> None:
"""Register websocket commands."""
websocket_api.async_register_command(hass, handle_subscribe_events)
@websocket_api.require_admin
@websocket_api.websocket_command({vol.Required("type"): "backup/subscribe_events"})
@websocket_api.async_response
async def handle_subscribe_events(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Subscribe to backup events."""
def on_event(event: ManagerStateEvent) -> None:
connection.send_message(websocket_api.event_message(msg["id"], event))
if DATA_MANAGER in hass.data:
manager = hass.data[DATA_MANAGER]
on_event(manager.last_event)
connection.subscriptions[msg["id"]] = async_subscribe_events(hass, on_event)
connection.send_result(msg["id"])

View File

@@ -8,10 +8,6 @@ from datetime import datetime
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.backup import (
async_subscribe_events,
async_subscribe_platform_events,
)
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, LOGGER
@@ -56,8 +52,8 @@ class BackupDataUpdateCoordinator(DataUpdateCoordinator[BackupCoordinatorData]):
update_interval=None,
)
self.unsubscribe: list[Callable[[], None]] = [
async_subscribe_events(hass, self._on_event),
async_subscribe_platform_events(hass, self._on_event),
backup_manager.async_subscribe_events(self._on_event),
backup_manager.async_subscribe_platform_events(self._on_event),
]
self.backup_manager = backup_manager

View File

@@ -36,7 +36,6 @@ from homeassistant.helpers import (
issue_registry as ir,
start,
)
from homeassistant.helpers.backup import DATA_BACKUP
from homeassistant.helpers.json import json_bytes
from homeassistant.util import dt as dt_util, json as json_util
@@ -372,12 +371,10 @@ class BackupManager:
# Latest backup event and backup event subscribers
self.last_event: ManagerStateEvent = BlockedEvent()
self.last_action_event: ManagerStateEvent | None = None
self._backup_event_subscriptions = hass.data[
DATA_BACKUP
].backup_event_subscriptions
self._backup_platform_event_subscriptions = hass.data[
DATA_BACKUP
].backup_platform_event_subscriptions
self._backup_event_subscriptions: list[Callable[[ManagerStateEvent], None]] = []
self._backup_platform_event_subscriptions: list[
Callable[[BackupPlatformEvent], None]
] = []
async def async_setup(self) -> None:
"""Set up the backup manager."""
@@ -1385,6 +1382,32 @@ class BackupManager:
for subscription in self._backup_event_subscriptions:
subscription(event)
@callback
def async_subscribe_events(
self,
on_event: Callable[[ManagerStateEvent], None],
) -> Callable[[], None]:
"""Subscribe events."""
def remove_subscription() -> None:
self._backup_event_subscriptions.remove(on_event)
self._backup_event_subscriptions.append(on_event)
return remove_subscription
@callback
def async_subscribe_platform_events(
self,
on_event: Callable[[BackupPlatformEvent], None],
) -> Callable[[], None]:
"""Subscribe to backup platform events."""
def remove_subscription() -> None:
self._backup_platform_event_subscriptions.remove(on_event)
self._backup_platform_event_subscriptions.append(on_event)
return remove_subscription
def _create_automatic_backup_failed_issue(
self, translation_key: str, translation_placeholders: dict[str, str] | None
) -> None:

View File

@@ -19,9 +19,14 @@ from homeassistant.components.onboarding import (
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.backup import async_get_manager as async_get_backup_manager
from . import BackupManager, Folder, IncorrectPasswordError, http as backup_http
from . import (
BackupManager,
Folder,
IncorrectPasswordError,
async_get_manager,
http as backup_http,
)
if TYPE_CHECKING:
from homeassistant.components.onboarding import OnboardingStoreData
@@ -54,7 +59,7 @@ def with_backup_manager[_ViewT: BaseOnboardingView, **_P](
if self._data["done"]:
raise HTTPUnauthorized
manager = await async_get_backup_manager(request.app[KEY_HASS])
manager = async_get_manager(request.app[KEY_HASS])
return await func(self, manager, request, *args, **kwargs)
return with_backup

View File

@@ -0,0 +1,36 @@
"""The Backup integration."""
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers.hassio import is_hassio
from .const import DATA_MANAGER, DOMAIN
async def _async_handle_create_service(call: ServiceCall) -> None:
"""Service handler for creating backups."""
backup_manager = call.hass.data[DATA_MANAGER]
agent_id = list(backup_manager.local_backup_agents)[0]
await backup_manager.async_create_backup(
agent_ids=[agent_id],
include_addons=None,
include_all_addons=False,
include_database=True,
include_folders=None,
include_homeassistant=True,
name=None,
password=None,
)
async def _async_handle_create_automatic_service(call: ServiceCall) -> None:
"""Service handler for creating automatic backups."""
await call.hass.data[DATA_MANAGER].async_create_automatic_backup()
def async_setup_services(hass: HomeAssistant) -> None:
"""Register services."""
if not is_hassio(hass):
hass.services.async_register(DOMAIN, "create", _async_handle_create_service)
hass.services.async_register(
DOMAIN, "create_automatic", _async_handle_create_automatic_service
)

View File

@@ -10,7 +10,11 @@ from homeassistant.helpers import config_validation as cv
from .config import Day, ScheduleRecurrence
from .const import DATA_MANAGER, LOGGER
from .manager import DecryptOnDowloadNotSupported, IncorrectPasswordError
from .manager import (
DecryptOnDowloadNotSupported,
IncorrectPasswordError,
ManagerStateEvent,
)
from .models import BackupNotFound, Folder
@@ -30,6 +34,7 @@ def async_register_websocket_handlers(hass: HomeAssistant, with_hassio: bool) ->
websocket_api.async_register_command(hass, handle_create_with_automatic_settings)
websocket_api.async_register_command(hass, handle_delete)
websocket_api.async_register_command(hass, handle_restore)
websocket_api.async_register_command(hass, handle_subscribe_events)
websocket_api.async_register_command(hass, handle_config_info)
websocket_api.async_register_command(hass, handle_config_update)
@@ -417,3 +422,22 @@ def handle_config_update(
changes.pop("type")
manager.config.update(**changes)
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command({vol.Required("type"): "backup/subscribe_events"})
@websocket_api.async_response
async def handle_subscribe_events(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Subscribe to backup events."""
def on_event(event: ManagerStateEvent) -> None:
connection.send_message(websocket_api.event_message(msg["id"], event))
manager = hass.data[DATA_MANAGER]
on_event(manager.last_event)
connection.subscriptions[msg["id"]] = manager.async_subscribe_events(on_event)
connection.send_result(msg["id"])

View File

@@ -11,6 +11,6 @@
"documentation": "https://www.home-assistant.io/integrations/bosch_alarm",
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "bronze",
"quality_scale": "platinum",
"requirements": ["bosch-alarm-mode2==0.4.6"]
}

View File

@@ -28,38 +28,41 @@ rules:
# Silver
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo
entity-unavailable: todo
docs-configuration-parameters:
status: exempt
comment: |
No options flow is provided.
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: todo
log-when-unavailable: done
parallel-updates: done
reauthentication-flow: done
test-coverage: done
# Gold
devices: done
diagnostics: todo
diagnostics: done
discovery-update-info: done
discovery: done
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices:
status: exempt
comment: |
Device type integration
entity-category: todo
entity-device-class: todo
entity-disabled-by-default: todo
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: todo
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: |

View File

@@ -20,5 +20,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bthome",
"iot_class": "local_push",
"requirements": ["bthome-ble==3.12.4"]
"requirements": ["bthome-ble==3.13.1"]
}

View File

@@ -168,7 +168,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
key="windazimuth",
translation_key="windazimuth",
native_unit_of_measurement=DEGREE,
icon="mdi:compass-outline",
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),

View File

@@ -240,6 +240,10 @@ async def _async_get_stream_image(
height: int | None = None,
wait_for_next_keyframe: bool = False,
) -> bytes | None:
if (provider := camera._webrtc_provider) and ( # noqa: SLF001
image := await provider.async_get_image(camera, width=width, height=height)
) is not None:
return image
if not camera.stream and CameraEntityFeature.STREAM in camera.supported_features:
camera.stream = await camera.async_create_stream()
if camera.stream:
@@ -494,19 +498,6 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Flag supported features."""
return self._attr_supported_features
@property
def supported_features_compat(self) -> CameraEntityFeature:
"""Return the supported features as CameraEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int:
new_features = CameraEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@cached_property
def is_recording(self) -> bool:
"""Return true if the device is recording."""
@@ -700,9 +691,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
async def async_internal_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_internal_added_to_hass()
self.__supports_stream = (
self.supported_features_compat & CameraEntityFeature.STREAM
)
self.__supports_stream = self.supported_features & CameraEntityFeature.STREAM
await self.async_refresh_providers(write_state=False)
async def async_refresh_providers(self, *, write_state: bool = True) -> None:
@@ -731,7 +720,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
self, fn: Callable[[HomeAssistant, Camera], Coroutine[None, None, _T | None]]
) -> _T | None:
"""Get first provider that supports this camera."""
if CameraEntityFeature.STREAM not in self.supported_features_compat:
if CameraEntityFeature.STREAM not in self.supported_features:
return None
return await fn(self.hass, self)
@@ -781,7 +770,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def camera_capabilities(self) -> CameraCapabilities:
"""Return the camera capabilities."""
frontend_stream_types = set()
if CameraEntityFeature.STREAM in self.supported_features_compat:
if CameraEntityFeature.STREAM in self.supported_features:
if self._supports_native_async_webrtc:
# The camera has a native WebRTC implementation
frontend_stream_types.add(StreamType.WEB_RTC)
@@ -801,8 +790,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""
super().async_write_ha_state()
if self.__supports_stream != (
supports_stream := self.supported_features_compat
& CameraEntityFeature.STREAM
supports_stream := self.supported_features & CameraEntityFeature.STREAM
):
self.__supports_stream = supports_stream
self._invalidate_camera_capabilities_cache()

View File

@@ -156,6 +156,15 @@ class CameraWebRTCProvider(ABC):
"""Close the session."""
return ## This is an optional method so we need a default here.
async def async_get_image(
self,
camera: Camera,
width: int | None = None,
height: int | None = None,
) -> bytes | None:
"""Get an image from the camera."""
return None
@callback
def async_register_webrtc_provider(

View File

@@ -105,11 +105,6 @@ DEFAULT_MAX_HUMIDITY = 99
CONVERTIBLE_ATTRIBUTE = [ATTR_TEMPERATURE, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH]
# Can be removed in 2025.1 after deprecation period of the new feature flags
CHECK_TURN_ON_OFF_FEATURE_FLAG = (
ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
)
SET_TEMPERATURE_SCHEMA = vol.All(
cv.has_at_least_one_key(
ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW

View File

@@ -13,6 +13,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["acme", "hass_nabucasa", "snitun"],
"requirements": ["hass-nabucasa==0.101.0"],
"requirements": ["hass-nabucasa==0.104.0"],
"single_config_entry": true
}

View File

@@ -7,15 +7,18 @@ import numpy as np
import voluptuous as vol
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_ATTRIBUTE,
CONF_MAXIMUM,
CONF_MINIMUM,
CONF_NAME,
CONF_SOURCE,
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.typing import ConfigType
@@ -32,6 +35,7 @@ from .const import (
DEFAULT_DEGREE,
DEFAULT_PRECISION,
DOMAIN,
PLATFORMS,
)
_LOGGER = logging.getLogger(__name__)
@@ -77,59 +81,104 @@ CONFIG_SCHEMA = vol.Schema(
)
async def create_compensation_data(
hass: HomeAssistant, compensation: str, conf: ConfigType, should_raise: bool = False
) -> None:
"""Create compensation data."""
_LOGGER.debug("Setup %s.%s", DOMAIN, compensation)
degree = conf[CONF_DEGREE]
initial_coefficients: list[tuple[float, float]] = conf[CONF_DATAPOINTS]
sorted_coefficients = sorted(initial_coefficients, key=itemgetter(0))
# get x values and y values from the x,y point pairs
x_values, y_values = zip(*initial_coefficients, strict=False)
# try to get valid coefficients for a polynomial
coefficients = None
with np.errstate(all="raise"):
try:
coefficients = np.polyfit(x_values, y_values, degree)
except FloatingPointError as error:
_LOGGER.error(
"Setup of %s encountered an error, %s",
compensation,
error,
)
if should_raise:
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="setup_error",
translation_placeholders={
"title": conf[CONF_NAME],
"error": str(error),
},
) from error
if coefficients is not None:
data = {
k: v for k, v in conf.items() if k not in [CONF_DEGREE, CONF_DATAPOINTS]
}
data[CONF_POLYNOMIAL] = np.poly1d(coefficients)
if data[CONF_LOWER_LIMIT]:
data[CONF_MINIMUM] = sorted_coefficients[0]
else:
data[CONF_MINIMUM] = None
if data[CONF_UPPER_LIMIT]:
data[CONF_MAXIMUM] = sorted_coefficients[-1]
else:
data[CONF_MAXIMUM] = None
hass.data[DATA_COMPENSATION][compensation] = data
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Compensation sensor."""
hass.data[DATA_COMPENSATION] = {}
if DOMAIN not in config:
return True
for compensation, conf in config[DOMAIN].items():
_LOGGER.debug("Setup %s.%s", DOMAIN, compensation)
degree = conf[CONF_DEGREE]
initial_coefficients: list[tuple[float, float]] = conf[CONF_DATAPOINTS]
sorted_coefficients = sorted(initial_coefficients, key=itemgetter(0))
# get x values and y values from the x,y point pairs
x_values, y_values = zip(*initial_coefficients, strict=False)
# try to get valid coefficients for a polynomial
coefficients = None
with np.errstate(all="raise"):
try:
coefficients = np.polyfit(x_values, y_values, degree)
except FloatingPointError as error:
_LOGGER.error(
"Setup of %s encountered an error, %s",
compensation,
error,
)
if coefficients is not None:
data = {
k: v for k, v in conf.items() if k not in [CONF_DEGREE, CONF_DATAPOINTS]
}
data[CONF_POLYNOMIAL] = np.poly1d(coefficients)
if data[CONF_LOWER_LIMIT]:
data[CONF_MINIMUM] = sorted_coefficients[0]
else:
data[CONF_MINIMUM] = None
if data[CONF_UPPER_LIMIT]:
data[CONF_MAXIMUM] = sorted_coefficients[-1]
else:
data[CONF_MAXIMUM] = None
hass.data[DATA_COMPENSATION][compensation] = data
hass.async_create_task(
async_load_platform(
hass,
SENSOR_DOMAIN,
DOMAIN,
{CONF_COMPENSATION: compensation},
config,
)
await create_compensation_data(hass, compensation, conf)
hass.async_create_task(
async_load_platform(
hass,
SENSOR_DOMAIN,
DOMAIN,
{CONF_COMPENSATION: compensation},
config,
)
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Compensation from a config entry."""
config = dict(entry.options)
data_points = config[CONF_DATAPOINTS]
new_data_points = []
for data_point in data_points:
values = data_point.split(",", maxsplit=1)
new_data_points.append([float(values[0]), float(values[1])])
config[CONF_DATAPOINTS] = new_data_points
await create_compensation_data(hass, entry.entry_id, config, True)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(update_listener))
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Compensation config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)

View File

@@ -0,0 +1,147 @@
"""Config flow for statistics."""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any, cast
import voluptuous as vol
from homeassistant.const import (
CONF_ATTRIBUTE,
CONF_ENTITY_ID,
CONF_NAME,
CONF_UNIT_OF_MEASUREMENT,
)
from homeassistant.helpers.schema_config_entry_flow import (
SchemaCommonFlowHandler,
SchemaConfigFlowHandler,
SchemaFlowError,
SchemaFlowFormStep,
)
from homeassistant.helpers.selector import (
AttributeSelector,
AttributeSelectorConfig,
BooleanSelector,
EntitySelector,
NumberSelector,
NumberSelectorConfig,
NumberSelectorMode,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
TextSelector,
)
from .const import (
CONF_DATAPOINTS,
CONF_DEGREE,
CONF_LOWER_LIMIT,
CONF_PRECISION,
CONF_UPPER_LIMIT,
DEFAULT_DEGREE,
DEFAULT_NAME,
DEFAULT_PRECISION,
DOMAIN,
)
async def get_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
"""Get options schema."""
entity_id = handler.options[CONF_ENTITY_ID]
return vol.Schema(
{
vol.Required(CONF_DATAPOINTS): SelectSelector(
SelectSelectorConfig(
options=[],
multiple=True,
custom_value=True,
mode=SelectSelectorMode.DROPDOWN,
)
),
vol.Optional(CONF_ATTRIBUTE): AttributeSelector(
AttributeSelectorConfig(entity_id=entity_id)
),
vol.Optional(CONF_UPPER_LIMIT, default=False): BooleanSelector(),
vol.Optional(CONF_LOWER_LIMIT, default=False): BooleanSelector(),
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
vol.Optional(CONF_DEGREE, default=DEFAULT_DEGREE): NumberSelector(
NumberSelectorConfig(min=0, max=7, step=1, mode=NumberSelectorMode.BOX)
),
vol.Optional(CONF_UNIT_OF_MEASUREMENT): TextSelector(),
}
)
def _is_valid_data_points(check_data_points: list[str]) -> bool:
"""Validate data points."""
result = False
for data_point in check_data_points:
if not data_point.find(",") > 0:
return False
values = data_point.split(",", maxsplit=1)
for value in values:
try:
float(value)
except ValueError:
return False
result = True
return result
async def validate_options(
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Validate options selected."""
user_input[CONF_PRECISION] = int(user_input[CONF_PRECISION])
user_input[CONF_DEGREE] = int(user_input[CONF_DEGREE])
if not _is_valid_data_points(user_input[CONF_DATAPOINTS]):
raise SchemaFlowError("incorrect_datapoints")
if len(user_input[CONF_DATAPOINTS]) <= user_input[CONF_DEGREE]:
raise SchemaFlowError("not_enough_datapoints")
handler.parent_handler._async_abort_entries_match({**handler.options, **user_input}) # noqa: SLF001
return user_input
DATA_SCHEMA_SETUP = vol.Schema(
{
vol.Required(CONF_NAME, default=DEFAULT_NAME): TextSelector(),
vol.Required(CONF_ENTITY_ID): EntitySelector(),
}
)
CONFIG_FLOW = {
"user": SchemaFlowFormStep(
schema=DATA_SCHEMA_SETUP,
next_step="options",
),
"options": SchemaFlowFormStep(
schema=get_options_schema,
validate_user_input=validate_options,
),
}
OPTIONS_FLOW = {
"init": SchemaFlowFormStep(
get_options_schema,
validate_user_input=validate_options,
),
}
class CompensationConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
"""Handle a config flow for Compensation."""
config_flow = CONFIG_FLOW
options_flow = OPTIONS_FLOW
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
"""Return config entry title."""
return cast(str, options[CONF_NAME])

View File

@@ -1,6 +1,9 @@
"""Compensation constants."""
from homeassistant.const import Platform
DOMAIN = "compensation"
PLATFORMS = [Platform.SENSOR]
SENSOR = "compensation"

View File

@@ -2,7 +2,9 @@
"domain": "compensation",
"name": "Compensation",
"codeowners": ["@Petro31"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/compensation",
"integration_type": "helper",
"iot_class": "calculated",
"quality_scale": "legacy",
"requirements": ["numpy==2.3.0"]

View File

@@ -8,9 +8,11 @@ from typing import Any
import numpy as np
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
CONF_ATTRIBUTE,
CONF_ENTITY_ID,
CONF_MAXIMUM,
CONF_MINIMUM,
CONF_SOURCE,
@@ -80,6 +82,36 @@ async def async_setup_platform(
)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Compensation sensor entry."""
compensation = entry.entry_id
conf: dict[str, Any] = hass.data[DATA_COMPENSATION][compensation]
source: str = conf[CONF_ENTITY_ID]
attribute: str | None = conf.get(CONF_ATTRIBUTE)
name = entry.title
async_add_entities(
[
CompensationSensor(
entry.entry_id,
name,
source,
attribute,
conf[CONF_PRECISION],
conf[CONF_POLYNOMIAL],
conf.get(CONF_UNIT_OF_MEASUREMENT),
conf[CONF_MINIMUM],
conf[CONF_MAXIMUM],
)
]
)
class CompensationSensor(SensorEntity):
"""Representation of a Compensation sensor."""

View File

@@ -0,0 +1,82 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"error": {
"incorrect_datapoints": "Datapoints needs to be provided in the right format, ex. '1.0, 0.0'.",
"not_enough_datapoints": "The number of datapoints needs to be more than the configured degree."
},
"step": {
"user": {
"description": "Add a compensation sensor",
"data": {
"name": "[%key:common::config_flow::data::name%]",
"entity_id": "Entity"
},
"data_description": {
"name": "Name for the created entity.",
"entity_id": "Entity to use as source."
}
},
"options": {
"description": "Read the documention for further details on how to configure the statistics sensor using these options.",
"data": {
"data_points": "Data points",
"attribute": "Attribute",
"upper_limit": "Upper limit",
"lower_limit": "Lower limit",
"precision": "Precision",
"degree": "Degree",
"unit_of_measurement": "Unit of measurement"
},
"data_description": {
"data_points": "The collection of data point conversions with the format 'uncompensated_value, compensated_value', ex. '1.0, 0.0'",
"attribute": "Attribute from the source to monitor/compensate.",
"upper_limit": "Enables an upper limit for the sensor. The upper limit is defined by the data collections (data_points) greatest uncompensated value.",
"lower_limit": "Enables a lower limit for the sensor. The lower limit is defined by the data collections (data_points) lowest uncompensated value.",
"precision": "Defines the precision of the calculated values, through the argument of round().",
"degree": "The degree of a polynomial.",
"unit_of_measurement": "Defines the units of measurement of the sensor, if any."
}
}
}
},
"options": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
},
"error": {
"incorrect_datapoints": "[%key:component::compensation::config::error::incorrect_datapoints%]",
"not_enough_datapoints": "[%key:component::compensation::config::error::not_enough_datapoints%]"
},
"step": {
"init": {
"description": "[%key:component::compensation::config::step::options::description%]",
"data": {
"data_points": "[%key:component::compensation::config::step::options::data::data_points%]",
"attribute": "[%key:component::compensation::config::step::options::data::attribute%]",
"upper_limit": "[%key:component::compensation::config::step::options::data::upper_limit%]",
"lower_limit": "[%key:component::compensation::config::step::options::data::lower_limit%]",
"precision": "[%key:component::compensation::config::step::options::data::precision%]",
"degree": "[%key:component::compensation::config::step::options::data::degree%]",
"unit_of_measurement": "[%key:component::compensation::config::step::options::data::unit_of_measurement%]"
},
"data_description": {
"data_points": "[%key:component::compensation::config::step::options::data_description::data_points%]",
"attribute": "[%key:component::compensation::config::step::options::data_description::attribute%]",
"upper_limit": "[%key:component::compensation::config::step::options::data_description::upper_limit%]",
"lower_limit": "[%key:component::compensation::config::step::options::data_description::lower_limit%]",
"precision": "[%key:component::compensation::config::step::options::data_description::precision%]",
"degree": "[%key:component::compensation::config::step::options::data_description::degree%]",
"unit_of_measurement": "[%key:component::compensation::config::step::options::data_description::unit_of_measurement%]"
}
}
}
},
"exceptions": {
"setup_error": {
"message": "Setup of {title} could not be setup due to {error}"
}
}
}

View File

@@ -54,10 +54,10 @@ class Control4RuntimeData:
type Control4ConfigEntry = ConfigEntry[Control4RuntimeData]
async def call_c4_api_retry(func, *func_args):
async def call_c4_api_retry(func, *func_args): # noqa: RET503
"""Call C4 API function and retry on failure."""
# Ruff doesn't understand this loop - the exception is always raised after the retries
for i in range(API_RETRY_TIMES): # noqa: RET503
for i in range(API_RETRY_TIMES):
try:
return await func(*func_args)
except client_exceptions.ClientError as exception:

View File

@@ -271,7 +271,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
)
# Temporary migration. We can remove this in 2024.10
from homeassistant.components.assist_pipeline import ( # pylint: disable=import-outside-toplevel
from homeassistant.components.assist_pipeline import ( # noqa: PLC0415
async_migrate_engine,
)

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.6.10"]
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.6.23"]
}

View File

@@ -300,10 +300,6 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def supported_features(self) -> CoverEntityFeature:
"""Flag supported features."""
if (features := self._attr_supported_features) is not None:
if type(features) is int:
new_features = CoverEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
supported_features = (

View File

@@ -26,6 +26,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
)
from .const import (
CONF_MAX_SUB_INTERVAL,
CONF_ROUND_DIGITS,
CONF_TIME_WINDOW,
CONF_UNIT_PREFIX,
@@ -104,6 +105,9 @@ async def _get_options_dict(handler: SchemaCommonFlowHandler | None) -> dict:
options=TIME_UNITS, translation_key="time_unit"
),
),
vol.Optional(CONF_MAX_SUB_INTERVAL): selector.DurationSelector(
selector.DurationSelectorConfig(allow_negative=False)
),
}

View File

@@ -7,3 +7,4 @@ CONF_TIME_WINDOW = "time_window"
CONF_UNIT = "unit"
CONF_UNIT_PREFIX = "unit_prefix"
CONF_UNIT_TIME = "unit_time"
CONF_MAX_SUB_INTERVAL = "max_sub_interval"

View File

@@ -2,7 +2,7 @@
"domain": "derivative",
"name": "Derivative",
"after_dependencies": ["counter"],
"codeowners": ["@afaucogney"],
"codeowners": ["@afaucogney", "@karwosts"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/derivative",
"integration_type": "helper",

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import datetime, timedelta
from decimal import Decimal, DecimalException
from decimal import Decimal, DecimalException, InvalidOperation
import logging
import voluptuous as vol
@@ -25,6 +25,7 @@ from homeassistant.const import (
UnitOfTime,
)
from homeassistant.core import (
CALLBACK_TYPE,
Event,
EventStateChangedData,
EventStateReportedData,
@@ -40,12 +41,14 @@ from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
)
from homeassistant.helpers.event import (
async_call_later,
async_track_state_change_event,
async_track_state_report_event,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
CONF_MAX_SUB_INTERVAL,
CONF_ROUND_DIGITS,
CONF_TIME_WINDOW,
CONF_UNIT,
@@ -89,10 +92,20 @@ PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
vol.Optional(CONF_UNIT_TIME, default=UnitOfTime.HOURS): vol.In(UNIT_TIME),
vol.Optional(CONF_UNIT): cv.string,
vol.Optional(CONF_TIME_WINDOW, default=DEFAULT_TIME_WINDOW): cv.time_period,
vol.Optional(CONF_MAX_SUB_INTERVAL): cv.positive_time_period,
}
)
def _is_decimal_state(state: str) -> bool:
try:
Decimal(state)
except (InvalidOperation, TypeError):
return False
else:
return True
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
@@ -114,6 +127,11 @@ async def async_setup_entry(
# Before we had support for optional selectors, "none" was used for selecting nothing
unit_prefix = None
if max_sub_interval_dict := config_entry.options.get(CONF_MAX_SUB_INTERVAL, None):
max_sub_interval = cv.time_period(max_sub_interval_dict)
else:
max_sub_interval = None
derivative_sensor = DerivativeSensor(
name=config_entry.title,
round_digits=int(config_entry.options[CONF_ROUND_DIGITS]),
@@ -124,6 +142,7 @@ async def async_setup_entry(
unit_prefix=unit_prefix,
unit_time=config_entry.options[CONF_UNIT_TIME],
device_info=device_info,
max_sub_interval=max_sub_interval,
)
async_add_entities([derivative_sensor])
@@ -145,6 +164,7 @@ async def async_setup_platform(
unit_prefix=config[CONF_UNIT_PREFIX],
unit_time=config[CONF_UNIT_TIME],
unique_id=None,
max_sub_interval=config.get(CONF_MAX_SUB_INTERVAL),
)
async_add_entities([derivative])
@@ -166,6 +186,7 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
unit_of_measurement: str | None,
unit_prefix: str | None,
unit_time: UnitOfTime,
max_sub_interval: timedelta | None,
unique_id: str | None,
device_info: DeviceInfo | None = None,
) -> None:
@@ -192,6 +213,34 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
self._unit_prefix = UNIT_PREFIXES[unit_prefix]
self._unit_time = UNIT_TIME[unit_time]
self._time_window = time_window.total_seconds()
self._max_sub_interval: timedelta | None = (
None # disable time based derivative
if max_sub_interval is None or max_sub_interval.total_seconds() == 0
else max_sub_interval
)
self._cancel_max_sub_interval_exceeded_callback: CALLBACK_TYPE = (
lambda *args: None
)
def _calc_derivative_from_state_list(self, current_time: datetime) -> Decimal:
def calculate_weight(start: datetime, end: datetime, now: datetime) -> float:
window_start = now - timedelta(seconds=self._time_window)
return (end - max(start, window_start)).total_seconds() / self._time_window
derivative = Decimal("0.00")
for start, end, value in self._state_list:
weight = calculate_weight(start, end, current_time)
derivative = derivative + (value * Decimal(weight))
return derivative
def _prune_state_list(self, current_time: datetime) -> None:
# filter out all derivatives older than `time_window` from our window list
self._state_list = [
(time_start, time_end, state)
for time_start, time_end, state in self._state_list
if (current_time - time_end).total_seconds() < self._time_window
]
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
@@ -209,13 +258,52 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
except SyntaxError as err:
_LOGGER.warning("Could not restore last state: %s", err)
def schedule_max_sub_interval_exceeded(source_state: State | None) -> None:
"""Schedule calculation using the source state and max_sub_interval.
The callback reference is stored for possible cancellation if the source state
reports a change before max_sub_interval has passed.
If the callback is executed, meaning there was no state change reported, the
source_state is assumed constant and calculation is done using its value.
"""
if (
self._max_sub_interval is not None
and source_state is not None
and (_is_decimal_state(source_state.state))
):
@callback
def _calc_derivative_on_max_sub_interval_exceeded_callback(
now: datetime,
) -> None:
"""Calculate derivative based on time and reschedule."""
self._prune_state_list(now)
derivative = self._calc_derivative_from_state_list(now)
self._attr_native_value = round(derivative, self._round_digits)
self.async_write_ha_state()
# If derivative is now zero, don't schedule another timeout callback, as it will have no effect
if derivative != 0:
schedule_max_sub_interval_exceeded(source_state)
self._cancel_max_sub_interval_exceeded_callback = async_call_later(
self.hass,
self._max_sub_interval,
_calc_derivative_on_max_sub_interval_exceeded_callback,
)
@callback
def on_state_reported(event: Event[EventStateReportedData]) -> None:
"""Handle constant sensor state."""
self._cancel_max_sub_interval_exceeded_callback()
new_state = event.data["new_state"]
if self._attr_native_value == Decimal(0):
# If the derivative is zero, and the source sensor hasn't
# changed state, then we know it will still be zero.
return
schedule_max_sub_interval_exceeded(new_state)
new_state = event.data["new_state"]
if new_state is not None:
calc_derivative(
@@ -225,7 +313,9 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
@callback
def on_state_changed(event: Event[EventStateChangedData]) -> None:
"""Handle changed sensor state."""
self._cancel_max_sub_interval_exceeded_callback()
new_state = event.data["new_state"]
schedule_max_sub_interval_exceeded(new_state)
old_state = event.data["old_state"]
if new_state is not None and old_state is not None:
calc_derivative(new_state, old_state.state, old_state.last_reported)
@@ -246,13 +336,7 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
"" if unit is None else unit
)
# filter out all derivatives older than `time_window` from our window list
self._state_list = [
(time_start, time_end, state)
for time_start, time_end, state in self._state_list
if (new_state.last_reported - time_end).total_seconds()
< self._time_window
]
self._prune_state_list(new_state.last_reported)
try:
elapsed_time = (
@@ -290,28 +374,27 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
(old_last_reported, new_state.last_reported, new_derivative)
)
def calculate_weight(
start: datetime, end: datetime, now: datetime
) -> float:
window_start = now - timedelta(seconds=self._time_window)
if start < window_start:
weight = (end - window_start).total_seconds() / self._time_window
else:
weight = (end - start).total_seconds() / self._time_window
return weight
# If outside of time window just report derivative (is the same as modeling it in the window),
# otherwise take the weighted average with the previous derivatives
if elapsed_time > self._time_window:
derivative = new_derivative
else:
derivative = Decimal("0.00")
for start, end, value in self._state_list:
weight = calculate_weight(start, end, new_state.last_reported)
derivative = derivative + (value * Decimal(weight))
derivative = self._calc_derivative_from_state_list(
new_state.last_reported
)
self._attr_native_value = round(derivative, self._round_digits)
self.async_write_ha_state()
if self._max_sub_interval is not None:
source_state = self.hass.states.get(self._sensor_source_id)
schedule_max_sub_interval_exceeded(source_state)
@callback
def on_removed() -> None:
self._cancel_max_sub_interval_exceeded_callback()
self.async_on_remove(on_removed)
self.async_on_remove(
async_track_state_change_event(
self.hass, self._sensor_source_id, on_state_changed

View File

@@ -6,6 +6,7 @@
"title": "Create Derivative sensor",
"description": "Create a sensor that estimates the derivative of a sensor.",
"data": {
"max_sub_interval": "Max sub-interval",
"name": "[%key:common::config_flow::data::name%]",
"round": "Precision",
"source": "Input sensor",
@@ -14,6 +15,7 @@
"unit_time": "Time unit"
},
"data_description": {
"max_sub_interval": "If defined, derivative automatically recalculates if the source has not updated for this duration.",
"round": "Controls the number of decimal digits in the output.",
"time_window": "If set, the sensor's value is a time-weighted moving average of derivatives within this window.",
"unit_prefix": "The output will be scaled according to the selected metric prefix and time unit of the derivative."
@@ -25,6 +27,7 @@
"step": {
"init": {
"data": {
"max_sub_interval": "[%key:component::derivative::config::step::user::data::max_sub_interval%]",
"name": "[%key:common::config_flow::data::name%]",
"round": "[%key:component::derivative::config::step::user::data::round%]",
"source": "[%key:component::derivative::config::step::user::data::source%]",
@@ -33,6 +36,7 @@
"unit_time": "[%key:component::derivative::config::step::user::data::unit_time%]"
},
"data_description": {
"max_sub_interval": "[%key:component::derivative::config::step::user::data_description::max_sub_interval%]",
"round": "[%key:component::derivative::config::step::user::data_description::round%]",
"time_window": "[%key:component::derivative::config::step::user::data_description::time_window%]",
"unit_prefix": "[%key:component::derivative::config::step::user::data_description::unit_prefix%]"

View File

@@ -9,7 +9,11 @@ import voluptuous as vol
from homeassistant.const import CONF_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.condition import ConditionProtocol, trace_condition_function
from homeassistant.helpers.condition import (
Condition,
ConditionCheckerType,
trace_condition_function,
)
from homeassistant.helpers.typing import ConfigType
from . import DeviceAutomationType, async_get_device_automation_platform
@@ -19,13 +23,24 @@ if TYPE_CHECKING:
from homeassistant.helpers import condition
class DeviceAutomationConditionProtocol(ConditionProtocol, Protocol):
class DeviceAutomationConditionProtocol(Protocol):
"""Define the format of device_condition modules.
Each module must define either CONDITION_SCHEMA or async_validate_condition_config
from ConditionProtocol.
Each module must define either CONDITION_SCHEMA or async_validate_condition_config.
"""
CONDITION_SCHEMA: vol.Schema
async def async_validate_condition_config(
self, hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
def async_condition_from_config(
self, hass: HomeAssistant, config: ConfigType
) -> ConditionCheckerType:
"""Evaluate state based on configuration."""
async def async_get_condition_capabilities(
self, hass: HomeAssistant, config: ConfigType
) -> dict[str, vol.Schema]:
@@ -37,20 +52,38 @@ class DeviceAutomationConditionProtocol(ConditionProtocol, Protocol):
"""List conditions."""
async def async_validate_condition_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate device condition config."""
return await async_validate_device_automation_config(
hass, config, cv.DEVICE_CONDITION_SCHEMA, DeviceAutomationType.CONDITION
)
class DeviceCondition(Condition):
"""Device condition."""
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
"""Initialize condition."""
self._config = config
self._hass = hass
@classmethod
async def async_validate_condition_config(
cls, hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate device condition config."""
return await async_validate_device_automation_config(
hass, config, cv.DEVICE_CONDITION_SCHEMA, DeviceAutomationType.CONDITION
)
async def async_condition_from_config(self) -> condition.ConditionCheckerType:
"""Test a device condition."""
platform = await async_get_device_automation_platform(
self._hass, self._config[CONF_DOMAIN], DeviceAutomationType.CONDITION
)
return trace_condition_function(
platform.async_condition_from_config(self._hass, self._config)
)
async def async_condition_from_config(
hass: HomeAssistant, config: ConfigType
) -> condition.ConditionCheckerType:
"""Test a device condition."""
platform = await async_get_device_automation_platform(
hass, config[CONF_DOMAIN], DeviceAutomationType.CONDITION
)
return trace_condition_function(platform.async_condition_from_config(hass, config))
CONDITIONS: dict[str, type[Condition]] = {
"device": DeviceCondition,
}
async def async_get_conditions(hass: HomeAssistant) -> dict[str, type[Condition]]:
"""Return the sun conditions."""
return CONDITIONS

View File

@@ -18,7 +18,7 @@ from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.device_registry import DeviceEntry
from .const import GATEWAY_SERIAL_PATTERN, PLATFORMS
from .const import DOMAIN, PLATFORMS
type DevoloHomeControlConfigEntry = ConfigEntry[list[HomeControl]]
@@ -29,19 +29,9 @@ async def async_setup_entry(
"""Set up the devolo account from a config entry."""
mydevolo = configure_mydevolo(entry.data)
credentials_valid = await hass.async_add_executor_job(mydevolo.credentials_valid)
if not credentials_valid:
raise ConfigEntryAuthFailed
if await hass.async_add_executor_job(mydevolo.maintenance):
raise ConfigEntryNotReady
gateway_ids = await hass.async_add_executor_job(mydevolo.get_gateway_ids)
if entry.unique_id and GATEWAY_SERIAL_PATTERN.match(entry.unique_id):
uuid = await hass.async_add_executor_job(mydevolo.uuid)
hass.config_entries.async_update_entry(entry, unique_id=uuid)
gateway_ids = await hass.async_add_executor_job(
check_mydevolo_and_get_gateway_ids, mydevolo
)
def shutdown(event: Event) -> None:
for gateway in entry.runtime_data:
@@ -69,7 +59,11 @@ async def async_setup_entry(
)
)
except GatewayOfflineError as err:
raise ConfigEntryNotReady from err
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="connection_failed",
translation_placeholders={"gateway_id": gateway_id},
) from err
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -91,7 +85,9 @@ async def async_unload_entry(
async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry
hass: HomeAssistant,
config_entry: DevoloHomeControlConfigEntry,
device_entry: DeviceEntry,
) -> bool:
"""Remove a config entry from a device."""
return True
@@ -103,3 +99,19 @@ def configure_mydevolo(conf: Mapping[str, Any]) -> Mydevolo:
mydevolo.user = conf[CONF_USERNAME]
mydevolo.password = conf[CONF_PASSWORD]
return mydevolo
def check_mydevolo_and_get_gateway_ids(mydevolo: Mydevolo) -> list[str]:
"""Check if the credentials are valid and return user's gateway IDs as long as mydevolo is not in maintenance mode."""
if not mydevolo.credentials_valid():
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
)
if mydevolo.maintenance():
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="maintenance",
)
return mydevolo.get_gateway_ids()

View File

@@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import DevoloHomeControlConfigEntry
from .devolo_multi_level_switch import DevoloMultiLevelSwitchDeviceEntity
from .entity import DevoloMultiLevelSwitchDeviceEntity
async def async_setup_entry(

View File

@@ -1,7 +1,5 @@
"""Constants for the devolo_home_control integration."""
import re
from homeassistant.const import Platform
DOMAIN = "devolo_home_control"
@@ -14,5 +12,4 @@ PLATFORMS = [
Platform.SIREN,
Platform.SWITCH,
]
GATEWAY_SERIAL_PATTERN = re.compile(r"\d{16}")
SUPPORTED_MODEL_TYPES = ["2600", "2601"]

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