Compare commits

...

287 Commits

Author SHA1 Message Date
Erik 6d082a87a4 Include pending_components in WS command get_config 2025-04-01 07:50:27 +02:00
pglab-electronics c0e8f14745 Update support to external library pypglab to version 0.0.5 (#141876)
update support to external library pypglab to version 0.0.5
2025-03-31 10:25:48 +02:00
elmurato 0488012c77 Add sensor platform to Pterodactyl (#141428)
* Add sensor platform

* Correct CPU Limit state attribute translation

* Remove calculated util entitites, add usage and limit entities

* Use suggested_unit_of_measurement instead of converters

* Start only first word of sensor names in upper case, improve suggested units of sensors

* Simplify update of native_value, set uptime as timestamp

* Add paranthesis around multi-line lambda
2025-03-31 10:23:40 +02:00
J. Nick Koston f247183e11 Bump SQLAlchemy to 2.0.40 (#141898)
changelog: https://docs.sqlalchemy.org/en/20/changelog/changelog_20.html#change-2.0.40
2025-03-31 10:11:13 +02:00
Norbert Rittel c662b94d06 Replace "Away" in climate with common state string, matching "Home" (#141897)
* Replace "Away" in `climate` with common state string

Also reordered the states a bit to group the two presence-based options at the top and order the rest alphabetically.

* Prettier
2025-03-31 09:56:10 +02:00
Norbert Rittel ee4bf165b5 Use common state for "Away" in nobo_hub (#141895) 2025-03-31 08:45:19 +02:00
Norbert Rittel 92ac396d19 Use common state for "Away" in honeywell (#141894) 2025-03-31 08:44:42 +02:00
Norbert Rittel 03366038ce Define "Away" state in plugwise using common string (#141875) 2025-03-31 07:35:03 +02:00
Noah Husby 0b91aa9202 Bump aiorussound to 4.5.0 (#141892) 2025-03-31 07:32:14 +02:00
Norbert Rittel ffc4fa1c2a Replace "Away" in humidifier with common string (#141872) 2025-03-31 07:29:17 +02:00
Norbert Rittel 15e03957a9 Replace "Away" in generic_thermostat with common string (#141880) 2025-03-31 07:25:19 +02:00
Marc Mueller 0be881bca6 Fix test RuntimeWarnings for homeassistant_hardware (#141884) 2025-03-31 07:24:02 +02:00
Paulus Schoutsen e88b321741 Ensure user always has first turn for Google Gen AI (#141893) 2025-03-30 23:31:45 -04:00
Allen Porter 0c4cb27fe9 Add OAuth support for Model Context Protocol (mcp) integration (#141874)
* Add authentication support for Model Context Protocol (mcp) integration

* Update homeassistant/components/mcp/application_credentials.py

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

* Handle MCP servers with ports

---------

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2025-03-30 23:14:52 -04:00
J. Nick Koston 1639163c2e Handle encryption being disabled on an ESPHome device (#141887)
fixes #121442
2025-03-30 21:25:24 -04:00
J. Nick Koston f043404cd9 Fix duplicate call to async_write_ha_state when adding elkm1 entities (#141890)
When an entity is added state is always written in
add_to_platform_finish:

https://github.com/home-assistant/core/blob/7336178e03a80be11f54eadd6833b9a2a40bae30/homeassistant/helpers/entity.py#L1384

We should not do it in async_added_to_hass as well
2025-03-30 21:23:54 -04:00
J. Nick Koston 018651ff1d Improve handling of empty iterable in async_add_entities (#141889)
* Improve handling of empty iterable in async_add_entities

We had two checks here because we were doing an empty
iterable check. If its a list we can check it directly
but if its not we need to convert it to a list to know
if its empty.

* tweaks

* tasks never used
2025-03-30 21:22:47 -04:00
J. Nick Koston 704d7a037c Bump aioesphomeapi to 29.8.0 (#141888)
changelog: https://github.com/esphome/aioesphomeapi/compare/v29.7.0...v29.8.0
2025-03-30 21:14:17 -04:00
Marc Mueller 7336178e03 Fix test RuntimeWarnings for hassio (#141883) 2025-03-30 12:00:48 -10:00
tdfountain 1c16fb8e42 Set and check unique id of config in NUT (#141783)
* Set and check unique id in config

* Update homeassistant/components/nut/config_flow.py

Set unique ID and abort only if value is defined

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

* Add duplicate ID test case for multiple devices

* Add unique ID check to config flow step for UPS

* Update homeassistant/components/nut/__init__.py

Fix to only set config_entries unique ID if not None

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

* Remove duplicate config flow call

---------

Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-03-30 11:41:56 -10:00
tdfountain 3ab2cd3fb7 Set device connection MAC address for networked devices in NUT (#141856)
* Set device connection MAC address for networked devices

* Change variable name for consistency
2025-03-30 11:21:11 -10:00
Norbert Rittel 5057343b6a Replace "Home" and "Away" in vallox with common strings (#141870) 2025-03-30 22:49:52 +02:00
Norbert Rittel 6c3e85fd5e Replace "Home" and "Away" in reolink with common strings (#141869) 2025-03-30 22:44:48 +02:00
Norbert Rittel f046456445 Replace "Home" and "Away" in opentherm_gw with common strings (#141867) 2025-03-30 22:36:46 +02:00
tdfountain e81a08916a Remove scan interval option from NUT (#141845)
Remove scan interval option and test case, migrate config and add migration test case
2025-03-30 22:34:45 +02:00
John Karabudak 85d2e3d006 Fix LLM to speed up prefill (#141156)
* fix: two minor LLM changes to speed up prefill

- moved the current date/time to the end of the prompt
- started sorting all entities by last_changed

* addressed PR comments

* fixed tests

* reduced scope of try/catch in LLM prompt

* addressed more PR comments

* fixed Anthropic test

* addressed another PR comment

* fixed remainder of tests
2025-03-30 13:30:40 -07:00
Norbert Rittel 936b0b32ed Replace "Home" and "Away" in drop_connect with common strings (#141864) 2025-03-30 22:30:08 +02:00
J. Nick Koston 0d511c697c Improve performance of as_compressed_state (#141800)
We have to build all of these at startup.

Its a lot faster to compare floats instead
of datetime objects. Since we already have to
fetch last_changed_timestamp, use it to compare
with last_updated_timestamp since we already know
we will have last_updated_timestamp
2025-03-30 22:20:24 +02:00
Norbert Rittel 5bfe034b4d Replace "Country" with common and pollutant labels with sensor strings (#141863)
* Replace "Country" with common and pollutant labels with `sensor` strings

* Fix copy & paste error for "ozone"
2025-03-30 22:17:51 +02:00
J. Nick Koston cf786b3b04 Bump google_cloud deps (#141861)
speech: https://github.com/googleapis/google-cloud-python/compare/google-cloud-speech-v2.27.0...google-cloud-speech-v2.31.1
texttospeech: https://github.com/googleapis/google-cloud-python/compare/google-cloud-texttospeech-v2.17.2...google-cloud-texttospeech-v2.25.1
2025-03-30 22:15:19 +02:00
Joost Lekkerkerker 0f9f090db2 Bump pySmartThings to 3.0.1 (#141722) 2025-03-30 21:34:49 +02:00
J. Nick Koston 302eea7418 Bump PyISY to 3.4.0 (#141851)
* Bump PyISY to 3.3.0

changelog: https://github.com/automicus/PyISY/compare/v3.2.0...v3.3.0

* Apply suggestions from code review

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-03-30 22:29:51 +03:00
J. Nick Koston b5e1f7e03e Cleanup some typing in isy994 (#141859)
Now that pyisy is mostly typed there were some obvious
issues. We are still a long way away from being able
to add py.typed to pyisy, but we can now see some
obvious things in an IDE
2025-03-30 09:18:30 -10:00
Norbert Rittel 02397a8d2d Replace "Off" state in selectors of home_connect with common state (#141857)
* Replace "Off" state in selectors of `home_connect` with common state

* Replace internal with common references
2025-03-30 21:03:46 +02:00
Norbert Rittel ea9437eab2 Use common state for "Off" in climate selector (#141850)
* Use common states for "Away" and "Off" in `climate`

* Revert common state for "Away"

Four other integrations are referencing this instead of the common state. Needs to be addressed first.
2025-03-30 21:02:54 +02:00
Norbert Rittel aaea30bee0 Replace "Off" in selector of media_player with common state (#141853) 2025-03-30 21:01:03 +02:00
Joost Lekkerkerker 9c869fa701 Add a coordinator to Point (#126775)
* Add a coordinator to Point

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix
2025-03-30 20:58:40 +02:00
Eli Sand 5106548f2c Fix generic_thermostat so it doesn't turn on when current temp is within target temp range (#138209)
* Don't turn on thermostat if temp is equal to target temp.

* Update strings to reflect logic change.

* Fix logic and add zero tolerance tests.

* Include tests for cool mode

* Removed unnecessary async_block_till_done calls
2025-03-30 19:43:13 +01:00
Franck Nijhof 506d485c0d Ensure EcoNet operation modes are unique (#141689) 2025-03-30 20:31:08 +02:00
Bouwe Westerdijk da190ec96f Bump plugwise to v1.7.3 (#141843) 2025-03-30 20:24:13 +02:00
Franck Nijhof 9567929484 Update pvo to v2.2.1 (#141847) 2025-03-30 21:12:42 +03:00
Norbert Rittel dc16494332 Replace "Disabled" with common state in schlage, fix sentence-case (#141849)
Replace "Disabled" with common state in `lamarzocco`, fix sentence-case

- replace "Disabled" with with common state reference
- fix sentence-casing of "Auto-lock"
2025-03-30 21:12:15 +03:00
Norbert Rittel 933f422588 Replace "Disabled" with common state in lamarzocco (#141848) 2025-03-30 20:00:18 +02:00
Michael 663d0691a7 Move setup messages from info to debug level (#141834)
move info to debug level
2025-03-30 19:49:41 +02:00
Norbert Rittel 97a0b9272e Resolve state mismatches in wolflink (#141846) 2025-03-30 19:42:39 +02:00
Lucas Mindêllo de Andrade 3d49000c75 Remove sunweg integration (#124230)
* chore(sunweg): remove sunweg integration

* Update homeassistant/components/sunweg/strings.json

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

* Update homeassistant/components/sunweg/manifest.json

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

* feat: added async remove entry

* Clean setup_entry; add tests

---------

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: abmantis <amfcalt@gmail.com>
2025-03-30 18:11:09 +01:00
Norbert Rittel 68d1a3c0a2 Replace "Off" and references with common state in tesla_fleet (#141840) 2025-03-30 19:06:28 +02:00
Norbert Rittel b06de7a687 Replace "Off" and references with common state in teslemetry (#141841) 2025-03-30 18:50:58 +02:00
Michael 963ea6141c Fix the entity category for max throughput sensors in AVM Fritz!Box Tools (#141838)
correct the entity category for max throughput sensors
2025-03-30 17:46:03 +02:00
Franck Nijhof 7232d36494 Fix hardcoded UoM for total power sensor for Tuya zndb devices (#141822) 2025-03-30 17:44:48 +02:00
Norbert Rittel 12eb071e8a Replace "Off" with common state in plugwise (#141828) 2025-03-30 17:31:12 +02:00
Paulus Schoutsen 86be626c69 Migrate ESPHome to use token instead of media source ID for legacy Assist Pipelines (#139665)
Migrate legacy ESPHome devices to use TTS token

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-30 16:53:49 +02:00
Mauricio Bonani ec20e41836 Improve the readability of status messages in NUT (#141335)
Improve the readability of status messages
2025-03-30 16:26:44 +02:00
Michal Schwarz b3564b6cff Fix order of palettes, presets and playlists in WLED integration (#132207)
* Fix order of palettes, presets and playlists in WLED integration

* fix tests: update palette items order

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-30 16:14:56 +02:00
Marlon acbee815be Update apsystems library to support battery inverter (#140086)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-30 16:11:22 +02:00
Norbert Rittel 5a1aeff85c Replace "On" and "Off" with common states in rfxtrx (#141835)
Also fix sentence-casing on other "… on" and "… off" states.
2025-03-30 16:10:17 +02:00
Allen Porter 4463e4c42b Move roborock unique id to be based on roborock userid instead of email (#141337)
* Move roborock unique id to be based on roborock userid instead of email

* Remove unnecessary data update

* Update tests

* Add tests coverage for removal of config entry

* Use config entry migration

* Remove unused fixtues

* Remove unnecessary logging
2025-03-30 16:04:28 +02:00
Norbert Rittel 4103ef71c9 Replace "Off" with common state in wyoming (#141832) 2025-03-30 16:02:09 +02:00
Norbert Rittel dccaa2dd2d Replace "Off" with common state in sleepiq (#141831) 2025-03-30 16:01:15 +02:00
Norbert Rittel 5ac6096e08 Replace "Off" with common state in osoenergy (#141830) 2025-03-30 16:00:50 +02:00
Norbert Rittel 4734a82f99 Replace "Off" with common state in airgradient (#141829)
* Replace "Off" with common state in `airgradient`

Also reference the name for CO2 from the `sensor` integration.

* Replace indirect with direct references
2025-03-30 16:00:11 +02:00
Norbert Rittel c6c2309dee Replace "Idle" with common state in zha (#141825) 2025-03-30 15:21:13 +02:00
Simone Chemelli 5e1bbd8bff Add full test coverage for Comelit climate platform (#140460)
* Add climate tests for Comelit

* fix climate and humidifier

* fix code and tests

* fix humidifier

* apply review comment

* align post merge

* add more tests

* typo

* apply review comment

* ruff
2025-03-30 15:15:26 +02:00
Michael 4761207097 Add boost preset to AVM Fritz!SmartHome climate entities (#141802)
* add boost preset to climate entities

* add set boost preset test
2025-03-30 15:03:26 +02:00
Simone Chemelli b4a6ca63b3 Add full test coverage for Comelit sensor platform (#141813) 2025-03-30 15:02:15 +02:00
Jan Bouwhuis a5b320180a Correct spelling for 'availability` in MQTT translation strings (#141818) 2025-03-30 15:01:06 +02:00
Aidan Timson bcc767136c Add System Bridge suggested sensor precisions (#141815) 2025-03-30 15:00:38 +02:00
Simone Chemelli d3257d96d0 Add full test coverage for Comelit light platform (#141736)
* Add full test coverage for Comelit light platform

* cleanup
2025-03-30 14:59:56 +02:00
Martin Hjelmare 89df6a82b0 Bump pydroid-ipcam to 3.0.0 (#141739) 2025-03-30 14:59:13 +02:00
Thomas55555 e725ba403b Bump ical to 9.0.3 (#141805) 2025-03-30 14:58:47 +02:00
Aidan Timson 578fece13e Fix System Bridge wait timeout wait condition (#141811)
* Fix System Bridge wait timeout wait condition

* Add DataMissingException as a timeout condition

* Add tests
2025-03-30 14:57:53 +02:00
Norbert Rittel 0eeb6b5fd5 Replace "Idle" with common state in backup, fix sentence-case (#141814)
* Replace "Idle" with common state in `backup`, fix sentence-case

* Update test_sensors.ambr
2025-03-30 14:56:36 +02:00
Norbert Rittel ad3f7f041f Replace "Idle" with common state in homekit_controller (#141820) 2025-03-30 15:34:36 +03:00
Norbert Rittel dce9bfd359 Replace "Idle" with common state in venstar, fix sentence-case (#141819) 2025-03-30 15:34:23 +03:00
Norbert Rittel 8f96ccc835 Fix sentence-casing in a few strings of bmw_connected_drive (#141816)
* Fix sentence-casing in a few strings of `bmw_connected_drive`

Also replace "Standby" state with common string reference.

* Update test_select.ambr
2025-03-30 15:13:35 +03:00
Andrew Sayre a48dd05035 Refactor registration of HEOS media player entity services (#141666)
Refactor entity service registration
2025-03-30 15:10:05 +03:00
Norbert Rittel eb90958341 Replace "Stand-by" and "Off" with common states in palazzetti (#141809)
Also fixes the  wrong spelling of "Stand-by" by using "Standby" from the common string.
2025-03-30 15:08:03 +03:00
Norbert Rittel 31ed6a48cb Replace "Standby" with common state in roborock (#141810) 2025-03-30 15:07:43 +03:00
Norbert Rittel dfa80f0787 Replace "Standby" with common state in knx (#141817)
Also reordered the states alphabetically to improve code readability.
2025-03-30 15:07:08 +03:00
Norbert Rittel 73acfa6a8e Replace "Stand-by" with common state in incomfort (#141807)
Also fixes the  wrong spelling of "Stand-by" by using "Standby" from the common string.
2025-03-30 13:55:06 +02:00
Norbert Rittel efad20cdff Replace "Standby" and "Idle" with common states in fronius (#141812) 2025-03-30 13:43:33 +02:00
Norbert Rittel 11d68cef54 Replace "Standby" with common state in blue_current (#141806) 2025-03-30 14:14:17 +03:00
J. Nick Koston 9c28e60475 Bump pyisy to 3.2.0 (#141798)
changelog: https://github.com/automicus/PyISY/compare/v3.1.15...v3.2.0

Fixes some tasks missing a strong reference
https://github.com/automicus/PyISY/pull/425

There is a bit of refactoring so I did not tag it for
beta.
2025-03-30 14:06:07 +03:00
Simone Chemelli 9ee79b87ee Add full test coverage for Comelit switch platform (#141738)
* Add full test coverage for Comelit switch platform

* cleanup
2025-03-30 12:10:41 +02:00
Simone Chemelli 600aedc9a1 Add tests for Comelit cover platform (#141740)
* Add  tests for Comelit cover platform

* cleanup up
2025-03-30 12:04:00 +02:00
Norbert Rittel 5b5efb5aaa Replace "Stopped" with common state in smartthings (#141789)
* Replace "Stopped" with common state in `smartthings`

* Replace internal references with common ones
2025-03-30 11:58:17 +02:00
Norbert Rittel 391b3ed1e7 Replace "Stopped" with common state in snoo (#141788)
* Replace "Stopped" with common state in `snoo`

* Replace internal reference with common one
2025-03-30 11:57:15 +02:00
Norbert Rittel 24277259ad Use more common states for ESS and PV in vicare (#141792) 2025-03-30 11:56:50 +02:00
J. Nick Koston 65261de7cc Migrate emulated_roku to use runtime_data to fix flakey tests (#141795) 2025-03-30 11:55:58 +02:00
Norbert Rittel beb92a7f9c Replace "Charging" state for binary_sensor with common string (#141796) 2025-03-30 11:41:01 +02:00
J. Nick Koston f1b059c75d Bump PyISY to 3.1.15 (#141778)
changelog: https://github.com/automicus/PyISY/compare/v3.1.14...v3.1.15

fixes #141517
fixes #132279
2025-03-30 11:40:25 +02:00
J. Nick Koston d4970f81aa Cleanup ESPHome update tests to avoid accessing integration internals (#141786)
We should not access DomainData directly in the test
2025-03-29 22:30:06 -10:00
Norbert Rittel 29219afb7f Replace "Charging" state in renault with common string (#141787) 2025-03-30 10:16:42 +02:00
tdfountain 7fbf15edc9 Add ambient state translations in NUT (#141772)
Add ambient state translations
2025-03-29 21:00:53 -10:00
J. Nick Koston c8d3fa6768 Small cleanups to the device registry (#141773)
Remove some calls to internal functions that
are now available directly on the devices
and deleted_devices objects

Remove internal functions that are no
longer used
2025-03-29 21:00:13 -10:00
J. Nick Koston ea5cf3d854 Bump aiohomekit to 3.2.13 (#141764)
changelog: https://github.com/Jc2k/aiohomekit/compare/3.2.8...3.2.13
2025-03-29 20:59:56 -10:00
J. Nick Koston 4a833fb489 Fix blocking late import of httpcore from httpx (#141771)
There is a late import that blocks the event loop
in newer version
https://github.com/encode/httpx/blob/9e8ab40369bd3ec2cc8bff37ab79bf5769c8b00f/httpx/_transports/default.py#L75
2025-03-29 20:59:40 -10:00
Norbert Rittel 02aa823d25 Replace "Stopped" with common state in matter (#141768) 2025-03-30 09:42:48 +03:00
Norbert Rittel 92034aeecc Replace "Opening" / "Closing" with common states in homee (#141766) 2025-03-30 09:42:28 +03:00
Florent Thoumie 9f2232fad1 Bump iaqualink to 0.5.3 (#141709)
* Update to iaqualink 0.5.3 and silence warning

* Update to iaqualink 0.5.3 and silence warning

* Re-add via_device line
2025-03-30 06:49:18 +02:00
tdfountain 2be2d54a5c Replace hard coded attributes with constants for test cases in NUT (#141774)
Replace hard coded attributes with constants
2025-03-29 15:19:41 -10:00
J. Nick Koston ed99686cc1 Bump propcache to 0.3.1 (#141770)
* Bump propcache to 0.3.1

changelog: https://github.com/aio-libs/propcache/compare/v0.3.0...v0.3.1

* revert
2025-03-29 23:10:08 +00:00
Norbert Rittel a6c1f1e485 Replace "Opening" / "Closing" with common states in shelly (#141767) 2025-03-30 00:48:28 +03:00
puddly a219445751 Add helper methods to simplify USB integration testing (#141733)
* Add some helper methods to simplify USB integration testing

* Re-export `usb_device_from_port`
2025-03-29 11:26:37 -10:00
Norbert Rittel b65b5aacb6 Add common state references to cover, valve and lock (#141754)
* Add common states to `cover`

* @NoRi2909 Add common states to `valve`

* Add common states to `lock`
2025-03-29 17:06:15 -04:00
Norbert Rittel bcead72265 Replace "Stopped" with common state in traccar_server (#141751) 2025-03-29 17:05:34 -04:00
Benjamin Bender 35b9564ed4 Show external cover art in music-assistant-integration (#141716)
* fix: handling of external album-art in music-assistant-integration

* chore: refinements

* make the image-logic more readable

* fix code comment to be accurate
2025-03-29 17:04:57 -04:00
Florent Thoumie aba01d4361 Remove iaqualink warning caused by via_device (#141761)
Remove warning caused by via_device
2025-03-29 17:03:35 -04:00
Franck Nijhof 4398af51c8 Fix spamming log message in QNAP (#141752) 2025-03-29 16:57:43 -04:00
Norbert Rittel 83f4f4cc96 Replace "Stopped" with common state in ipp (#141750) 2025-03-29 19:19:56 +01:00
Norbert Rittel 1800e6fb8e Add common states for "Opening" and "Closing" (#141747) 2025-03-29 19:19:30 +01:00
Simone Chemelli 43b83c855f Align code styling in Vodafone Station tests (#141745) 2025-03-29 18:42:12 +01:00
Marcel van der Veldt 20e2de200f Always set pause feature on Music Assistant mediaplayers (#141686) 2025-03-29 18:39:59 +01:00
Josef Zweck ed4ebe1222 Add unkown to uncalibrated state for tedee (#141262) 2025-03-29 18:38:19 +01:00
Simone Chemelli 4e4446cef4 Fix immediate state update for Comelit (#141735) 2025-03-29 18:22:03 +01:00
Norbert Rittel e2ff0b265d Replace "Stopped" with common state in prusalink (#141743)
* Replace "Stopped" with common state in `prusalink`

* Sentence-case "Nozzle diameter"
2025-03-29 18:07:38 +01:00
J. Nick Koston 6d48fc183a Fix ESPHome update entities being loaded before device_info is available (#141704)
* Fix ESPHome update entities being loaded before device_info is available

Since we load platforms when restoring config, the update
platform could be loaded before the connection to the
device was finished which meant device_info could still
be empty. Wait until device_info is available to
load the update platform.

fixes #135906

* Apply suggestions from code review

* move comment

* Update entry_data.py

Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>

---------

Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2025-03-29 17:53:01 +01:00
J. Nick Koston ea8392a4a1 Fix ESPHome entities not being removed when the ESPHome config removes an entire platform (#141708)
* Fix old ESPHome entities not being removed when configuration changes

fixes #140756

* make sure all callbacks fire

* make sure all callbacks fire

* make sure all callbacks fire

* make sure all callbacks fire

* revert

* cover
2025-03-29 17:48:51 +01:00
Simone Chemelli bcd296822d Add full test coverage for Comelit alarm control panel (#141371)
* Add full test coverage for Comelit alarm control panel

* fix methods description

* revert unwanted change

* apply review comment
2025-03-29 17:29:37 +01:00
Jan Bouwhuis 6ee97f341d Improve MQTT translation strings (#141691)
* Improve MQTT options translation string

* more improvements
2025-03-29 17:29:04 +01:00
Norbert Rittel aa2ab74ee9 Replace "On" and "Off" in airzone_cloud with common states (#141711)
* Replace "On", "Off" and "Stop(ped)" in `airzone_cloud` with common strings

* Revert to "Stop" as mode name by manufacturer

Co-authored-by: acidcoke <acidcoke@acidic.codes>

---------

Co-authored-by: acidcoke <acidcoke@acidic.codes>
2025-03-29 18:05:41 +03:00
Norbert Rittel 49b2ab9889 Replace "Stopped" etc. with common state in teslemetry/tessie/tesla_fleet (#141714)
* Replace "Stopped" with common state in `teslemetry`

* Replace "Disconnected" with common state in `teslemetry`

* Replace "Stopped"/"Disconnected" with common state in `tessie`

* Replace "Stopped", "Connected", "Disconnected" with common state in `tesla_fleet`
2025-03-29 18:03:48 +03:00
Martin Hjelmare 2549e2cc0f Patch Z-Wave platforms in humidifier tests (#141732) 2025-03-29 17:59:13 +03:00
J. Diego Rodríguez Royo b15fa81a44 Set Home Connect program action field as not required (#141729)
* Set Home Connect program action field as not required

* Remove required field

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-03-29 15:02:54 +01:00
J. Diego Rodríguez Royo 09f6246d1b Dynamically add Home Connect event sensors (#141198)
* Dynamically add Home Connect event sensors to HA

* Add and remove listeners on paired and depaired events

* Apply suggestion

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

* Update test

* Adjust English

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-03-29 12:53:34 +01:00
Norbert Rittel 96ff389fd1 Sentence-case "Medium type" in mopeka (#141718) 2025-03-29 10:19:25 +01:00
Joost Lekkerkerker b55f1df297 Only link the parent device if known in SmartThings (#141719)
Only link the parent device if we know the parent device
2025-03-29 10:18:27 +01:00
Joost Lekkerkerker d88f7b8600 Only trigger events on button updates in SmartThings (#141720)
Only trigger events on button updates
2025-03-29 10:17:38 +01:00
Norbert Rittel df2a94bb5b Replace "country" with common string in lg_thinq (#141690) 2025-03-29 00:27:10 +00:00
Norbert Rittel c4ac492c6e Add common state "Stopped" (#141701) 2025-03-29 00:25:22 +00:00
Franck Nijhof fcd4d3e2df Add ability to subscribe to own YouTube channels (#141693) 2025-03-28 19:59:24 -04:00
Franck Nijhof 42d6bd3839 Handle invalid JSON errors in AirNow (#141695) 2025-03-28 19:58:41 -04:00
J. Nick Koston d6b48003b6 Improve performance of websocket_api _state_diff_event (#141696)
We can use last_updated_timestamp for the compare since its always
calculated when the state is created and comparing floats is
much faster than datetime objects
2025-03-28 19:58:12 -04:00
Franck Nijhof ba8f69d956 Fix Tuya tdq category to pick up temp & humid (#141698) 2025-03-28 19:57:56 -04:00
Norbert Rittel f22bb72d18 Replace 4 occurrences of "Enable" in teslemetry with common string (#141699) 2025-03-28 23:47:44 +01:00
Marcel van der Veldt f7a0a9fa41 Bump music assistant client to 1.2.0 (#141668)
* Bump music assistant client to 1.2.0

* Update test fixtures
2025-03-28 22:43:31 +01:00
Norbert Rittel 3795d653c5 Replace "country" with common string in holiday (#141687) 2025-03-28 22:43:00 +01:00
Norbert Rittel 8ee014b855 Fix grammar / sentence-casing in workday (#141682)
* Fix grammar / sentence-casing in `workday`

Also replace "country" with common string.

* Add two more references

* Fix second data description reference

* Add "given" to action description for better translations
2025-03-28 21:22:53 +01:00
Erwin Douna 1ab5bdf85f Tado add proper off state (#135480)
* Add proper off state

* Remove current temp

* Add default frost temp
2025-03-28 20:54:36 +01:00
puddly 5283e1a39f Handle all firmware types for ZBT-1 and Yellow update entities (#141674)
Handle other firmware types
2025-03-28 20:38:16 +01:00
Franck Nijhof 17c56208ee Fix camera proxy with sole image quality settings (#141676) 2025-03-28 20:36:15 +01:00
Norbert Rittel 8474d9fefe Replace "country" with common string in ecovacs (#141677) 2025-03-28 20:32:32 +01:00
J. Nick Koston fd9f002e9f Increase websocket_api allowed peak time to 10s (#141680)
* Increase websocket_api allowed peak time to 10s

fixes #141624

During integration reload or startup, we can end up sending a message for
each entity being created for integrations that create them from an external
source (ie MQTT) because the messages come in one at a time. This can overload
the loop and/or client for more than 5s. While we have done significant work
to optimize for this path, we are at the limit at what we can expect clients
to be able to process in the time window, so increase the time window.

* adjust test
2025-03-28 20:32:00 +01:00
Norbert Rittel 26268357a0 Replace "country" with common string in prosegur (#141678) 2025-03-28 20:19:20 +01:00
Dan Raper 82b463b22f Get Ohme to gold quality (#140617)
* Add reconfigure step, diagnostics and default disabled entities to Ohme

* Formatting

* Update tests

* Bugfixes and add tests for diagnostics and reconfigure

* Remove diagnostics changes

* Remove reconfigure changes

* Pull upstream strings.json
2025-03-28 18:41:00 +00:00
Jason Hunter 7ae397a211 Update Duke Energy package to fix integration (#141669)
* Update Duke Energy package to fix integration

* fix tests
2025-03-28 18:33:59 +00:00
Norbert Rittel ea4ad681e4 Replace "country" with common string in cookidoo (#141670) 2025-03-28 18:29:31 +00:00
Michael Hansen a150f9d5ad Bump intents and always prefer more literal text (#141663) 2025-03-28 13:03:42 -04:00
puddly afb7fe0d40 Include ZBT-1 and Yellow in device registry (#141623)
* Add the Yellow and ZBT-1 to the device registry

* Unload platforms

* Fix unit tests

* Rename the Yellow update entity to `Radio firmware`

* Rename `EmberZNet` to `EmberZNet Zigbee`

* Prefix the `sw_version` with the firmware type and clean up

* Fix unit tests

* Remove unnecessary `always_update=False` from data update coordinator
2025-03-28 18:00:05 +01:00
Norbert Rittel 8bf42b9d3e Replace "language" and "country" with common strings in epic_games_store (#141665) 2025-03-28 17:50:36 +01:00
Andrew Sayre ba00707d89 Add HEOS entity service to remove queue items (#141495)
* Add remove queue items service

* Tests

* Correct casing of ID

* Match docs
2025-03-28 11:09:01 -05:00
tdfountain 2121b943a3 Add exception translation to NUT (#141629)
* Add exception translation and test cases

* Capitalize ID in error string

* Test translation placeholders, simplify test cases
2025-03-28 16:43:16 +01:00
Bram Kragten ef06d2c06e Update frontend to 20250328.0 (#141659) 2025-03-28 16:08:14 +01:00
Norbert Rittel cc1fac5776 Add a common string for "country" (#141653) 2025-03-28 15:52:44 +01:00
Norbert Rittel 6cb3430c60 Fix sentence-casing of "sea level" in matter (#141655)
* Fix sentence-casing of "sea level" in `matter`

* Update test_number.ambr
2025-03-28 15:30:21 +01:00
Paulus Schoutsen 4cea90f773 Enable the message box on default for satelitte announcement actions (#141654) 2025-03-28 15:07:09 +01:00
Paulus Schoutsen 4da5f6188d Ensure connection test sound has no preannouncement (#141647) 2025-03-28 14:01:12 +01:00
Norbert Rittel e7f8b9ad92 Fix typo and sentence-casing in jewish_calendar (#141651)
Also replace "Language" with common string.
2025-03-28 15:55:52 +03:00
Jan Bouwhuis 473a28c5f2 Fix duplicate 'device' term in MQTT translation strings (#141646)
* Fix duplicate 'device' from MQTT translation strings

* Update homeassistant/components/mqtt/strings.json
2025-03-28 13:55:36 +01:00
Franck Nijhof d765936be3 Fix ESPHome event entity staying unavailable (#141650) 2025-03-28 13:55:11 +01:00
Norbert Rittel 0db643d9d1 Replace "connect" / "disconnect" with common strings in idasen_desk (#141649) 2025-03-28 08:46:13 -04:00
Norbert Rittel af29159e2f Remove "meter" from entity names of rainforest_raven sensors (#141487)
* Fix misleading friendly names of `rainforest_raven` sensors

The three sensors
- power_demand
- total_energy_delivered
- total_energy_received

currently add "meter" in their friendly names.

This does not provide any useful information and is rather irritating instead – it sounds like these are the power demands or consumption of the meter itself. But they are the measured values.

This commit removes "meter" from the names making them simpler and more precise, too.

In addition the sentence-casing of "MAC addresses" is fixed.

* Update test_sensor.ambr

* Update test_sensor.ambr (2)

* Also remove "meter" from Signal strength

* Update test_sensor.ambr (3)

* Change `meter_price` to `energy_price` in strings.json

* Change `meter_price` to `energy_price` in test_sensor.ambr

* Change `meter_price` to `energy_price` in sensor.py
2025-03-28 11:26:51 +00:00
Tsvi Mostovicz 65c38d8e31 Jewish calendar match omer service variables requirement to documentation (#141620)
The documentation and the omer schema require a Nusach to be specified, but the YAML misses that requirement
2025-03-28 13:59:04 +03:00
LG-ThinQ-Integration adb7aa237b Add number for ventilator's sleepTimer (#140972)
Add sleepTimer for ventilator

Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-03-28 11:54:18 +01:00
Norbert Rittel 577f86b83a Remove "meter" from entity names of rainforest_eagle sensors (#141641)
* Remove "meter" from entity names in strings.json

* Replace `meter_price`with `energy_price`in sensor.py

* Update test_sensor.py
2025-03-28 13:52:15 +03:00
J. Diego Rodríguez Royo 01169e9184 Add Pitsos virtual integration (#141610) 2025-03-28 13:50:48 +03:00
J. Diego Rodríguez Royo dde037291a Add Neff virtual integration (#141609) 2025-03-28 13:50:29 +03:00
J. Diego Rodríguez Royo 6971a189f9 Add Gaggenau virtual integration (#141608) 2025-03-28 13:50:12 +03:00
J. Diego Rodríguez Royo c860686138 Add Constructa virtual integration (#141607) 2025-03-28 13:49:52 +03:00
J. Diego Rodríguez Royo 2eb507863f Add Balay virtual integration (#141606) 2025-03-28 13:49:38 +03:00
Joost Lekkerkerker 54ee5c6998 Add default string and icon for light effect off (#141567) 2025-03-28 11:48:34 +01:00
alorente 63df2474a9 Fix missing response for queued mode scripts (#141460) 2025-03-28 10:47:41 +00:00
Robert Resch 93f12fb7c6 Reverts #141363 "Deprecate SmartThings machine state sensors" (#141573)
Reverts #141363
2025-03-28 11:40:24 +01:00
Nick Pesce b7a995ac53 Use correct default value for multi press buttons in the Matter integration (#141630)
* Respect the min 2 constraint for the switch MultiPressMax attribute

* Update test_event.py

* Update generic_switch_multi.json

* Fix issue and update tests
2025-03-28 11:11:18 +01:00
Solmath 267a80e70c Show internet radio station if no artist is available in Cambridge Audio (#140716)
* Add media_channel property to cambridge audio

* Return channel instead of artist when playing internet radio to mimick behaviour of CXN100 and StreamMagic app

* Add test for media_artist attribute

* Add test that media_artist is not set in certain cases

* Update homeassistant/components/cambridge_audio/media_player.py

Co-authored-by: Noah Husby <32528627+noahhusby@users.noreply.github.com>

---------

Co-authored-by: Noah Husby <32528627+noahhusby@users.noreply.github.com>
2025-03-28 09:49:20 +01:00
Norbert Rittel 078be3b8df Replace already_configured in teslemetry with common string (#141637) 2025-03-28 09:44:34 +01:00
J. Diego Rodríguez Royo 7b6c967c3a Add Profilo virtual integration (#141611) 2025-03-28 09:42:51 +01:00
J. Diego Rodríguez Royo a405ccd044 Add Siemens virtual integration (#141612) 2025-03-28 09:37:27 +01:00
J. Diego Rodríguez Royo f6c55ebf05 Add Thermador virtual integration (#141613) 2025-03-28 09:35:05 +01:00
Shay Levy 6b3b4cce4b Record Shelly quality scale (#141062)
* Record Shelly quality scale

* Update

* change stale-devices status to todo

* Update homeassistant/components/shelly/quality_scale.yaml

Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>

---------

Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
2025-03-28 09:30:29 +01:00
Jan Bouwhuis 8887c979b4 Fix volatile_organic_compounds_parts translation string to be referenced for MQTT subentries device class selector (#141618)
* Fix ` volatile_organic_compounds_parts` translation string to be referenced for MQTT subentries device class selector

* Fix tests
2025-03-28 08:48:23 +01:00
Norbert Rittel 665541409a Fix sentence-casing in airvisual user strings (#141632) 2025-03-28 10:37:55 +03:00
Denis Shulyaka 195919b5fb Add PDF support for openai_conversation.generate_content service (#141588)
Add PDF support for openai_conversation.generate_content service
2025-03-27 20:05:54 -04:00
Petro31 31479056ed Fix an issue with the switch preview in beta (#141617)
Fix an issue with the switch preview
2025-03-28 00:43:17 +02:00
Jan-Philipp Benecke 9f0976d94a Bump aiowebdav2 to 0.4.4 (#141615) 2025-03-27 23:19:04 +01:00
Norbert Rittel a049d2b7db Make names of switch entities in gree consistent with docs (#141580) 2025-03-27 21:51:11 +00:00
Norbert Rittel 6959017d55 Use official camel-cased spelling "FullTopic" in tasmota (#141604)
* Use camel-cased spelling "FullTopic" in `tasmota`

This  should ensure that this fixed term is kept in translations.

In addition an excessive space character is removed.

* Fix wrong plural in second sentence
2025-03-27 23:12:42 +02:00
Luke Lashley 4ff5a04a72 Bump Python-Snoo to 0.6.5 (#141599)
* Bump Python-Snoo to 0.6.5

* add to event_types
2025-03-27 21:56:11 +01:00
Norbert Rittel ea0c4a7263 Fix misleading friendly names of pvoutput sensors (#141312)
* Fix misleading friendly names of `pvoutput` sensors

* Update test_sensor.py

* Update test_sensor.py - prettier
2025-03-27 21:49:16 +01:00
J. Nick Koston 9633f03ddc Fix zeroconf logging level not being respected (#141601)
Removes an old logging workaround that is no longer needed

fixes #141558
2025-03-27 21:45:48 +01:00
Artur Pragacz 4c0d8ce87c Remove deprecated YAML import in Onkyo (#141600) 2025-03-27 21:13:23 +01:00
Joost Lekkerkerker d92728e533 Add brand for Bosch (#141561) 2025-03-27 21:01:52 +01:00
Paul Bottein 799962ef0e Update frontend to 20250327.1 (#141596) 2025-03-27 20:58:59 +01:00
Stephan Traub 631f817f11 Wiz - update dependency to support new light features and bugfixes (#141529)
* Bump pywizlight and fix deprecation issue

* Removed workaround for color_mode; update pywizlight
2025-03-27 21:51:42 +02:00
Martin Hjelmare 52f7bdeb5d Patch Z-Wave platforms in fan tests (#141591) 2025-03-27 21:40:39 +02:00
Martin Hjelmare 51db140aed Clean up Z-Wave config flow (#141595) 2025-03-27 21:30:16 +02:00
Jan-Philipp Benecke 1ad12d5945 Bump aiowebdav2 to 0.4.3 (#141586) 2025-03-27 19:44:33 +02:00
Simon Lamon 9f5d94046d Fix typing error in NMBS (#141589)
Fix typing error
2025-03-27 19:39:33 +02:00
Erik Montnemery de1e06c39b Revert "Promote after dependencies in bootstrap" (#141584)
Revert "Promote after dependencies in bootstrap (#140352)"

This reverts commit 3766040960.
2025-03-27 12:57:58 -04:00
Bram Kragten abbabc11d2 Update frontend to 20250327.0 (#141585) 2025-03-27 17:51:52 +01:00
Joost Lekkerkerker 62be82fd3c Also migrate completion time entities in SmartThings (#141572) 2025-03-27 16:36:45 +01:00
Luke Lashley f0fd5a639a Better handle Roborock discovery (#141575) 2025-03-27 11:17:56 -04:00
Andrii Mitnovych dea00fac3f Get area and floor by alias (#126150)
* Add possibility to get area by alias

* Add ability to get floor by alias

* Moved alias lookup to separate function, adjusted templates.

* Changed registry to return all areas/floors with given alias

* Use normalize_name from normalized_name_base_registry
2025-03-27 16:02:47 +01:00
Erwin Douna c30f17f592 Tado fix HomeKit flow (#141525)
* Initial commit

* Fix

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-03-27 16:01:54 +01:00
Martin Hjelmare e9e95f45d8 Handle cloud subscription expired for backup upload (#141564)
Handle cloud backup subscription expired for upload
2025-03-27 15:29:11 +01:00
Joost Lekkerkerker e8aa3e6d34 Add icons to hue effects (#141559) 2025-03-27 12:05:45 +01:00
Norbert Rittel 3646884d79 Replace "controller_id" with friendly name in homeworks error message (#141550) 2025-03-27 12:29:53 +02:00
Norbert Rittel 5747c6b1a8 Fix sentence-casing in konnected strings, replace "override" with "custom" (#141553)
Fix sentence-casing in `konnected`strings, replace "Override" with "Custom"

Make string consistent with HA standards.

As "Override" can be misunderstood as the verb, replace it with "Custom".
2025-03-27 10:59:19 +01:00
Thomas55555 43a5c7ddc8 Handle webcal prefix in remote calendar (#141541)
Handel webcal prefix in remote calendar
2025-03-27 10:22:25 +01:00
J. Diego Rodríguez Royo d9d74107fe Improve some Home Connect deprecations (#141508) 2025-03-27 10:18:30 +01:00
Erik Montnemery 373cca9857 Remove unused mypy ignore from google_generative_ai_conversation (#141549) 2025-03-27 10:03:07 +01:00
Martin Hjelmare 284b3f444d Remove leftover cloudflare persistent notification dismiss (#141548) 2025-03-27 09:53:47 +01:00
Simon Lamon dfb088e524 Bump linkplay to v0.2.2 (#141542)
Bump linkplay
2025-03-27 08:51:12 +01:00
Manu 5546f1d73d Support for upcoming pyLoad-ng release in pyLoad integration (#141297)
Fix extra key `proxy` in pyLoad
2025-03-27 07:46:58 +01:00
Jan Bouwhuis 13fc871806 Use kwargs only for MQTT subentry PlatformField helper (#141498) 2025-03-27 07:46:08 +01:00
J. Nick Koston 0f9fd78656 Bump pyserial-asyncio-fast to 0.16 (#141540)
changelog: https://github.com/home-assistant-libs/pyserial-asyncio-fast/compare/0.15...0.16
2025-03-27 07:32:59 +01:00
Ivan Lopez Hernandez 4f318c0be3 Initialize google.genai.Client in the executor (#141432)
* Intialize the client on an executor thread

* Fix MyPy error

* MyPy error

* Exception error

* Fix ruff

* Update __init__.py

---------

Co-authored-by: tronikos <tronikos@users.noreply.github.com>
2025-03-26 22:05:22 -07:00
Michael Hansen 5eb1d0a28e Add default preannounce sound to Assist satellites (#141522)
* Add default preannounce sound

* Allow None to disable sound

* Register static path instead of HTTP view

* Fix path

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-03-26 22:45:28 -05:00
Brett Adams 66c03713b7 Fix Auto Seat Heater in Tesla Fleet (#141539)
Fix Auto Seat Heater
2025-03-26 20:55:34 -04:00
Marc Mueller d51070c99b Update boto3 to 1.37.1 and aiobotocore to 2.21.1 (#141499) 2025-03-27 01:38:34 +01:00
Marc Mueller 50d050e63e Update pyserial-asyncio-fast to 0.15 (#141537) 2025-03-27 01:33:01 +01:00
Norbert Rittel 89bf426163 Fix wrong friendly name for storage_power in solaredge (#141269)
* Fix wrong friendly name for `storage_power` in `solaredge`

"Stored power" is a contradiction in itself.
You can only store energy.

* Two additional spelling fixes

* Sentence-case "site"
2025-03-27 00:24:14 +01:00
Jan Bouwhuis 377548e3a1 Fix QoS schema issue in MQTT subentries (#141531) 2025-03-26 23:35:28 +01:00
Simone Chemelli 543c6929e6 Fix refresh state for Comelit alarm (#141370) 2025-03-26 22:34:53 +00:00
Jan Bouwhuis 42ae572948 Fix MQTT options flow QoS selector can not serialize (#141528) 2025-03-26 22:56:57 +01:00
Thomas55555 c3f8b7e200 Fix work area sensor for Husqvarna Automower (#141527)
* Fix work area sensor for Husqvarna Automower

* simplify
2025-03-26 23:16:26 +02:00
Robert Resch 3a207e2571 Show box for Smartthings rise number entity (#141526) 2025-03-26 22:03:24 +01:00
Andrew Sayre 6bfd39f094 Add play queue item to HEOS (#141480)
Add ability to play specific queue item
2025-03-26 15:47:10 -05:00
Robert Resch 002ca9611d Add test for invalid mean type in StatisticsMeta (#141475) 2025-03-26 21:40:02 +01:00
Joost Lekkerkerker 46ee3d2b26 Sort SmartThings devices to be created by parent device id (#141515) 2025-03-26 20:52:39 +01:00
Franck Nijhof eb901bcf3a Bump version to 2025.5.0dev0 (#141507) 2025-03-26 20:30:03 +01:00
Norbert Rittel 930b4a2c81 Capitalize "Ethernet" in roku sensor name (#141509)
* Capitalize "Ethernet" in `roku` sensor name

* Update test_binary_sensor.py
2025-03-26 21:18:52 +02:00
Robert Resch 22d1b8e1cd Bump deebot-client to 12.4.0 (#141501) 2025-03-26 19:36:04 +01:00
Joost Lekkerkerker 2e3853dd7d Deprecate SmartThings media player switch (#141467)
* Deprecate SmartThings media player switch

* Fix

* Fix

* Update homeassistant/components/smartthings/strings.json

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

* Fix

---------

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-03-26 18:40:11 +01:00
Joost Lekkerkerker fe99c39e25 Deprecate media player sensors for SmartThings (#141469)
* Deprecate media player sensors for SmartThings

* Deprecate media player sensors
2025-03-26 18:21:49 +01:00
Maciej Bieniek c8ab5bc796 Bump IMGW-PIB library to 1.0.10 (#141491) 2025-03-26 17:57:27 +01:00
Álvaro Fernández Rojas 4f3b36c2e1 Update aioairzone-cloud to v0.6.11 (#141488)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2025-03-26 17:57:15 +01:00
Marc Mueller 222d89a84c Update meteofrance-api to 1.4.0 (#141490) 2025-03-26 17:56:45 +01:00
Joost Lekkerkerker eb3cb0e0c7 Bump yt-dlp to 2025.03.26 (#141484) 2025-03-26 11:49:29 -05:00
Joost Lekkerkerker 69c8f4fbb6 Add button to reset the water filter in SmartThings (#141493)
* Add button to reset the water filter in SmartThings

* Add button to reset the water filter in SmartThings
2025-03-26 11:48:03 -05:00
Jan Bouwhuis 3bcf1c942c Cleanup missed QoS translation string for MQTT subentries (#141485) 2025-03-26 17:40:22 +01:00
Michael Hansen 220aaf93c6 Add preannounce media id support for ESPHome (#141474)
* Working on preannounce media id support for ESPHome

* Fix test

* Update tests
2025-03-26 11:31:05 -05:00
Jan Bouwhuis febc455bc5 Add switch as entity platform on MQTT subentries (#140658) 2025-03-26 16:46:44 +01:00
Marc Mueller 57f65c205e Use SPDX identifier for container license (#141477) 2025-03-26 16:31:28 +01:00
Erik Montnemery 6e56486294 Bump pychromecast to 14.0.7 (#141479) 2025-03-26 16:30:37 +01:00
Joost Lekkerkerker 3a1e1684ea Add power binary sensor for Cooktop in SmartThings (#141482) 2025-03-26 16:29:02 +01:00
Bram Kragten 9d63a49812 Update frontend to 20250326.0 (#141481) 2025-03-26 16:27:43 +01:00
Markus Adrario 7a4ca6dcdc Add Homee lock platform (#140893)
* Add homee lock platform

* finish tests

* add locking & unlocking

* add PARALLEL_UPDATES

* fix review comments

* fix test review comment.

* fix another review comment
2025-03-26 09:46:21 -05:00
Marc Mueller 1622638f10 Update mypy-dev to 1.16.0a7 (#141472) 2025-03-26 15:21:38 +01:00
Jan Bouwhuis 0de3549e6e Move QoS setting to shared device properties in MQTT device subentries configuration (#141369)
* Move QoS setting to shared device properties in MQTT device subentries configuration

* Use kwargs for validate_user_input helper
2025-03-26 15:20:08 +01:00
Joost Lekkerkerker 63d4efda2e Deprecate switch entity for airdresser (#141470)
* Deprecate switch entity for airdresser

* Deprecate switch entity for airdresser
2025-03-26 15:06:13 +01:00
J. Diego Rodríguez Royo b5910dd7d6 Move Home Connect alarm clock entity from time platform to number platform (#141400)
* Move alarm clock entity from time platform to number platform

* Deprecate alarm clock time entity

* Don't update unique id

* Fix tests

* Fixable issues

* improvement

* Make the issues persistent
2025-03-26 14:46:07 +01:00
Denis Shulyaka c974285490 Add Web search to OpenAI Conversation integration (#141426)
* Add Web search to OpenAI Conversation integration

* Limit search for gpt-4o models

* Add more tests
2025-03-26 09:36:05 -04:00
Michael Hansen 8db91623ec Add language scores websocket command (#140480)
* Add language scores websocket command

* Don't store language scores in snapshot

* Add language/country args for preferred lang

* Bump intents to 2025.3.24 for dash lang code
2025-03-26 14:07:15 +01:00
Michael Hansen 3eda5333b0 Add info websocket command to wyoming integration (#139982)
* Add info websocket command to wyoming integration

* Add snapshot

* Add config schema

* Remove snapshots because of changing config entry ids
2025-03-26 14:06:51 +01:00
Robert Resch 3aaf859985 Add state class MEASUREMENT_ANGLE to wind direction sensor (#141392)
* Add state class MEASUREMENT_ANGLE to wind direction sensor

* Update snapshots

* Add some more
2025-03-26 13:58:23 +01:00
Sanjay Govind dba4c197c8 Add bosch_alarm integration (#138497)
* Add bosch_alarm integration

* Remove other platforms for now

* update some strings not being consistant

* fix sentence-casing for strings

* remove options flow and versioning

* clean up config flow

* Add OSI license + tagged releases + ci to bosch-alarm-mode2

* Apply suggestions from code review

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

* apply changes from review

* apply changes from review

* remove options flow

* work on fixtures

* work on fixtures

* fix errors and complete flow

* use fixtures for alarm config

* Update homeassistant/components/bosch_alarm/manifest.json

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

* fix missing type

* mock setup entry

* remove use of patch in config flow test

* Use coordinator for managing panel data

* Use coordinator for managing panel data

* Coordinator cleanup

* remove unnecessary observers

* update listeners when error state changes

* Update homeassistant/components/bosch_alarm/coordinator.py

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

* Update homeassistant/components/bosch_alarm/quality_scale.yaml

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

* Update homeassistant/components/bosch_alarm/config_flow.py

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

* rename config flow

* Update homeassistant/components/bosch_alarm/quality_scale.yaml

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

* add missing types

* fix quality_scale.yaml

* enable strict typing

* enable strict typing

* Add test for alarm control panel

* add more tests

* add more tests

* Update homeassistant/components/bosch_alarm/coordinator.py

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

* Update homeassistant/components/bosch_alarm/coordinator.py

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

* Update homeassistant/components/bosch_alarm/alarm_control_panel.py

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

* Update homeassistant/components/bosch_alarm/alarm_control_panel.py

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

* Update homeassistant/components/bosch_alarm/alarm_control_panel.py

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

* Add snapshot test

* add snapshot test

* add snapshot test

* update quality scale

* update quality scale

* update quality scale

* update quality scale

* Apply suggestions from code review

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

* apply changes from code review

* apply changes from code review

* apply changes from code review

* Apply suggestions from code review

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

* apply changes from code review

* apply changes from code review

* Fix alarm control panel device name

* Fix

* Fix

* Fix

* Fix

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-03-26 13:56:44 +01:00
Robert Resch f842640249 Add check that sensor state classes are used only with valid unit of measurements (#141444) 2025-03-26 13:52:00 +01:00
Robert Resch aa493ff97d Correct device class and state class for wind direction sensors (#141393)
* Fix state class on wind direction sensors

* Update snapshots
2025-03-26 13:48:08 +01:00
Joost Lekkerkerker 21d5885ded Add select entity for dishwasher operating state in SmartThings (#141468)
* Add select entity for dishwasher operating state in SmartThings

* Add select entity for dishwasher operating state in SmartThings
2025-03-26 13:39:36 +01:00
Tsvi Mostovicz 054b3bb26c Add service for counting the omer (#141008)
* Add service for counting the omer

* Add description and strings. Expect string from user

* Fix constraints on nusach and language + Make independent of config_entry

* Provide config schema

* Fix services.yaml and strings.json to match updated service.py

* Use LanguageSelector and some constants

* Action description -> third-person singular

* Use built-in language selector in yaml

* Fix schema

* Show the hebrew date in the correct language in the response

* Revert "Show the hebrew date in the correct language in the response"

This reverts commit 59442d16c5.

Requires a bugfix in the original library

* Don't return the hebrew date as it doesn't return correctly
2025-03-26 13:38:58 +01:00
Jan Bouwhuis 77bf977d63 Add sensor as entity platform on MQTT subentries (#139899)
* Add sensor as entity platform on MQTT subentries

* Fix typo

* Improve device class data description

* Tweak

* Rework reconfig calculation

* Filter out last_reset_value_template if state class is not total

* Collapse expire after as advanced setting

* Update suggested_display_precision translation strings

* Make options and last_reset_template conditional, use sections for advanced settings

* Ensure options are removed properly

* Improve sensor options label, ensure UOM is set when device class has units

* Use helper to apply suggested values from component config

* Rename to `Add option`

* Fix schema builder not hiding empty sections and removing fields excluded from reconfig

* Do not hide advanced settings if values are available or are defaults

* Improve spelling and Learn more links

* Improve unit of measurement validation

* Fix UOM selector and translation strings

* Address comments from code review

* Remove stale comment

* Rename selector constant, split validator

* Simplify config validator

* Return tuple with config and errors for config validation
2025-03-26 13:34:24 +01:00
Robert Resch 3f68e327f3 Bump uv to 0.6.10 (#141464) 2025-03-26 13:30:57 +01:00
Marc Mueller 82db1ffd12 Update typing-extensions to 4.13.0 (#141465) 2025-03-26 13:28:46 +01:00
Allen Porter 06f6c86ba5 Simplify roborock map storage test fixture (#141430) 2025-03-26 08:19:48 -04:00
Robert Resch e3f2f30395 Add circular mean statistics and sensor state class MEASUREMENT_ANGLE (#138453)
* Add circular mean statistics

* fixes

* Add has_circular_mean and fix tests

* Fix mypy

* Rename to MEASUREMENT_ANGLE

* Fix kitchen_sink tests

* Fix sensor tests

* for testing only

* Revert ws command change

* Apply suggestions

* test only

* add custom handling for postgres

* fix recursion limit

* Check if column is already available

* Set default false and not nullable for has_circular_mean

* Proper fix to be backwards compatible

* Fix value is None

* Align with schema

* Remove has_circular_mean from test schemas as it's not required anymore

* fix wrong column type

* Use correct variable to reduce stats

* Add guard that the uom is matching a valid one from the state class

* Add some tests

* Fix tests again

* Use mean_type in StatisticsMetato difference between different mean type algorithms

* Fix leftovers

* Fix kitchen_sink tests

* Fix postgres

* Add circular mean test

* Add mean_type_changed stats issue

* Align the attributes with unit_changed

* Fix mean_type_change stats issue

* Add missing sensor recorder tests

* Add test_statistic_during_period_circular_mean

* Add mean_weight

* Add test_statistic_during_period_hole_circular_mean

* Use seperate migration step to null has_mean

* Typo ARITHMETIC

* Implement requested changes

* Implement requested changes

* Split into #141444

* Add StatisticMeanType.NONE and forbid that mean_type can be None

* Fix mean_type

* Implement requested changes

* Small leftover of latest StatisticMeanType changes
2025-03-26 13:15:58 +01:00
Brett Adams 4a6d2c91da Bump tesla-fleet-api to v1.0.16 (#140869)
* Add streaming climate

* fixes

* Add missing changes

* Fix restore

* Update homeassistant/components/teslemetry/climate.py

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

* Use dict

* Add fan mode translations

* Infer side

* WIP

* fix deps

* Migration in progress

* Working

* tesla-fleet-api==1.0.15

* tesla-fleet-api==1.0.16

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-03-26 12:28:16 +01:00
Simone Chemelli d7de8c5f68 Add full test coverage for Comelit coordinator (#141321)
* Add full test coverage for Comelit coordinator

* add common const

* apply review comment
2025-03-26 13:21:58 +02:00
Norbert Rittel 7bcba2b639 Fix online docs URL in motionblinds plus gateway naming (#141453)
* Fix online docs URL in `motionblinds` plus gateway naming

- add missing "api" to the online docs URL to make it work
- fix sentence-casing of "API key"
- replace "Motion Gateway" with "Motionblinds gateway" as there is no brand "Motion" and the list of compatible bridges cover a lot more brands

* Replace comma with period to improve readability
2025-03-26 13:11:49 +02:00
Maciej Bieniek 53990f8fad Do not show the firmware changelog for Shelly Wall Display X2 update entities (#141457)
There is no firmware changelog for Wall Display X2
2025-03-26 12:11:09 +01:00
Joost Lekkerkerker ed7c864869 Add switch for icemaker in SmartThings (#141313)
* Add switch for icemaker in SmartThings

* Fix
2025-03-26 12:10:44 +01:00
Joost Lekkerkerker 74ff40e253 Deprecate SmartThings machine state sensors (#141363)
* Deprecate SmartThings machine state sensors

* Fix
2025-03-26 11:46:50 +01:00
TimL 57d02d7a17 Cleanups related to improved typing on radios objects (#141455)
* Improved handling of radio objects

* Drop get_radio helper

* Remove mock of get_radio in tests
2025-03-26 11:45:07 +01:00
TimL 043603c9be Add SMLIGHT sensor entities for second radio (#137403)
* Add sensors for second radio

* Add test for zigbee2 sensor

* Update homeassistant/components/smlight/sensor.py

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

* drop useless replace

* Fix test failure

* Fix code coverage in config flow

* Update homeassistant/components/smlight/sensor.py

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

* fix conversion of iterator to list

* Remove assert on radios

* simplify handling of radios further

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-26 11:34:44 +01:00
TimL e10801af80 Bump pysmlight to v0.2.4 (#141450) 2025-03-26 11:28:25 +01:00
Simone Chemelli f4fa4056ac Make BT support detection dynamic for Shelly RPC devices (#137323) 2025-03-26 11:17:54 +01:00
Joost Lekkerkerker 208e8ae451 Deprecate SmartThings switch entity (#141360)
* Deprecate SmartThings switch entity

* Apply suggestions from code review

Co-authored-by: Robert Resch <robert@resch.dev>

* Fix

* Revert "Apply suggestions from code review"

This reverts commit c6d39d38de.

* Revert "Revert "Apply suggestions from code review""

This reverts commit d92411c156.

* Fix

* Fix

---------

Co-authored-by: Robert Resch <robert@resch.dev>
2025-03-26 11:05:31 +01:00
TheJulianJES 02f8322ac1 Bump ZHA to 0.0.54 (#141447)
* Bump ZHA to 0.0.54

* Add strings for v2 quirk entities

* Adjust cover tests for new ZHA behavior

* Improve cover tests further
2025-03-26 11:55:18 +02:00
Norbert Rittel e8158234a9 Fix grammar in spotify reauthentication error (#141451) 2025-03-26 11:45:55 +02:00
Norbert Rittel 7848c3cd79 Fixes to user-facing strings of cloudflare integration (#141452)
- fix sentence-casing of a few strings
- fix grammar of action description
2025-03-26 11:45:05 +02:00
Norbert Rittel 2d8420b656 Fix spelling of "serial number" in smappee (#141449) 2025-03-26 10:25:12 +01:00
Joost Lekkerkerker 63a86763b1 Migrate unique ids in SmartThings (#141308)
* Migrate unique ids in SmartThings

* Migrate

* Migrate

* Migrate

* Fix

* Fix
2025-03-26 10:23:20 +01:00
Michael b5117eb071 Proper handling of unavailable Synology DSM nas during backup (#140721)
* raise BackupAgentUnreachableError when NAS is unavailable

* also raise BackupAgentUnreachableError during upload when nas unavailable

* Revert "also raise BackupAgentUnreachableError during upload when nas unavailable"

This reverts commit 38877d8540.

* Revert "raise BackupAgentUnreachableError when NAS is unavailable"

This reverts commit 4d8cfae396.

* check last_update_success of  coordinator_central to get backup agents

* consider last_update_success before notify backup listeners

* add test

* use walrus :=  :)
2025-03-26 10:22:43 +01:00
Norbert Rittel f0c774a4bd Small grammar fixes in hue user strings (#141446)
… including proper sentence-casing
2025-03-26 11:16:10 +02:00
Simone Chemelli 8bedf97382 Remove helpers and align coding style in Shelly tests (#140080)
* Cleanup hass.states method in Shelly tests (part 1)

* remove helper functions and align coding style

* missed

* revert unwanted changes

* apply review comment

* apply review comment

* apply review comment

* apply ATTR where missing

* apply walrus

* add missed walrus

* add walrus to entity_registry.async_get

* minor tweak

* align after merge
2025-03-26 10:05:42 +01:00
Robert Resch 65c05d66c0 Use a constant for sensor statistics issues (#141441) 2025-03-26 09:43:09 +01:00
Norbert Rittel 1cb4332a3c Fix sentence-case and naming of "Security code" in tradfri (#141440) 2025-03-26 10:07:30 +02:00
650 changed files with 20269 additions and 7080 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ env:
CACHE_VERSION: 12
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 9
HA_SHORT_VERSION: "2025.4"
HA_SHORT_VERSION: "2025.5"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['3.13']"
# 10.3 is the oldest supported version
+1
View File
@@ -119,6 +119,7 @@ homeassistant.components.bluetooth_adapters.*
homeassistant.components.bluetooth_tracker.*
homeassistant.components.bmw_connected_drive.*
homeassistant.components.bond.*
homeassistant.components.bosch_alarm.*
homeassistant.components.braviatv.*
homeassistant.components.bring.*
homeassistant.components.brother.*
Generated
+2 -2
View File
@@ -216,6 +216,8 @@ build.json @home-assistant/supervisor
/tests/components/bmw_connected_drive/ @gerard33 @rikroe
/homeassistant/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/tests/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/homeassistant/components/bosch_alarm/ @mag1024 @sanjay900
/tests/components/bosch_alarm/ @mag1024 @sanjay900
/homeassistant/components/bosch_shc/ @tschamm
/tests/components/bosch_shc/ @tschamm
/homeassistant/components/braviatv/ @bieniu @Drafteed
@@ -1478,8 +1480,6 @@ build.json @home-assistant/supervisor
/tests/components/suez_water/ @ooii @jb101010-2
/homeassistant/components/sun/ @Swamp-Ig
/tests/components/sun/ @Swamp-Ig
/homeassistant/components/sunweg/ @rokam
/tests/components/sunweg/ @rokam
/homeassistant/components/supla/ @mwegrzynek
/homeassistant/components/surepetcare/ @benleb @danielhiversen
/tests/components/surepetcare/ @benleb @danielhiversen
Generated
+1 -1
View File
@@ -31,7 +31,7 @@ RUN \
&& go2rtc --version
# Install uv
RUN pip3 install uv==0.6.8
RUN pip3 install uv==0.6.10
WORKDIR /usr/src
+1 -1
View File
@@ -19,4 +19,4 @@ labels:
org.opencontainers.image.authors: The Home Assistant Authors
org.opencontainers.image.url: https://www.home-assistant.io/
org.opencontainers.image.documentation: https://www.home-assistant.io/docs/
org.opencontainers.image.licenses: Apache License 2.0
org.opencontainers.image.licenses: Apache-2.0
+17 -11
View File
@@ -859,14 +859,8 @@ async def _async_set_up_integrations(
integrations, all_integrations = await _async_resolve_domains_and_preload(
hass, config
)
# Detect all cycles
integrations_after_dependencies = (
await loader.resolve_integrations_after_dependencies(
hass, all_integrations.values(), set(all_integrations)
)
)
all_domains = set(integrations_after_dependencies)
domains = set(integrations) & all_domains
all_domains = set(all_integrations)
domains = set(integrations)
_LOGGER.info(
"Domains to be set up: %s | %s",
@@ -874,8 +868,6 @@ async def _async_set_up_integrations(
all_domains - domains,
)
async_set_domains_to_be_loaded(hass, all_domains)
# Initialize recorder
if "recorder" in all_domains:
recorder.async_initialize_recorder(hass)
@@ -908,12 +900,24 @@ async def _async_set_up_integrations(
stage_dep_domains_unfiltered = {
dep
for domain in stage_domains
for dep in integrations_after_dependencies[domain]
for dep in all_integrations[domain].all_dependencies
if dep not in stage_domains
}
stage_dep_domains = stage_dep_domains_unfiltered - hass.config.components
stage_all_domains = stage_domains | stage_dep_domains
stage_all_integrations = {
domain: all_integrations[domain] for domain in stage_all_domains
}
# Detect all cycles
stage_integrations_after_dependencies = (
await loader.resolve_integrations_after_dependencies(
hass, stage_all_integrations.values(), stage_all_domains
)
)
stage_all_domains = set(stage_integrations_after_dependencies)
stage_domains &= stage_all_domains
stage_dep_domains &= stage_all_domains
_LOGGER.info(
"Setting up stage %s: %s | %s\nDependencies: %s | %s",
@@ -924,6 +928,8 @@ async def _async_set_up_integrations(
stage_dep_domains_unfiltered - stage_dep_domains,
)
async_set_domains_to_be_loaded(hass, stage_all_domains)
if timeout is None:
await _async_setup_multi_components(hass, stage_all_domains, config)
continue
+5
View File
@@ -0,0 +1,5 @@
{
"domain": "bosch",
"name": "Bosch",
"integrations": ["bosch_alarm", "bosch_shc", "home_connect"]
}
@@ -68,8 +68,8 @@
"led_bar_mode": {
"name": "LED bar mode",
"state": {
"off": "Off",
"co2": "Carbon dioxide",
"off": "[%key:common::state::off%]",
"co2": "[%key:component::sensor::entity_component::carbon_dioxide::name%]",
"pm": "Particulate matter"
}
},
@@ -143,8 +143,8 @@
"led_bar_mode": {
"name": "[%key:component::airgradient::entity::select::led_bar_mode::name%]",
"state": {
"off": "[%key:component::airgradient::entity::select::led_bar_mode::state::off%]",
"co2": "[%key:component::airgradient::entity::select::led_bar_mode::state::co2%]",
"off": "[%key:common::state::off%]",
"co2": "[%key:component::sensor::entity_component::carbon_dioxide::name%]",
"pm": "[%key:component::airgradient::entity::select::led_bar_mode::state::pm%]"
}
},
@@ -8,7 +8,7 @@ from aiohttp import ClientSession
from aiohttp.client_exceptions import ClientConnectorError
from pyairnow import WebServiceAPI
from pyairnow.conv import aqi_to_concentration
from pyairnow.errors import AirNowError
from pyairnow.errors import AirNowError, InvalidJsonError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -79,7 +79,7 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
distance=self.distance,
)
except (AirNowError, ClientConnectorError) as error:
except (AirNowError, ClientConnectorError, InvalidJsonError) as error:
raise UpdateFailed(error) from error
if not obs:
@@ -2,7 +2,7 @@
"config": {
"step": {
"geography_by_coords": {
"title": "Configure a Geography",
"title": "Configure a geography",
"description": "Use the AirVisual cloud API to monitor a latitude/longitude.",
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
@@ -16,8 +16,8 @@
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"city": "City",
"country": "Country",
"state": "State"
"state": "State",
"country": "[%key:common::config_flow::data::country%]"
}
},
"reauth_confirm": {
@@ -56,12 +56,12 @@
"sensor": {
"pollutant_label": {
"state": {
"co": "Carbon Monoxide",
"n2": "Nitrogen Dioxide",
"o3": "Ozone",
"p1": "PM10",
"p2": "PM2.5",
"s2": "Sulfur Dioxide"
"co": "[%key:component::sensor::entity_component::carbon_monoxide::name%]",
"n2": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]",
"o3": "[%key:component::sensor::entity_component::ozone::name%]",
"p1": "[%key:component::sensor::entity_component::pm10::name%]",
"p2": "[%key:component::sensor::entity_component::pm25::name%]",
"s2": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]"
}
},
"pollutant_level": {
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
"iot_class": "cloud_push",
"loggers": ["aioairzone_cloud"],
"requirements": ["aioairzone-cloud==0.6.10"]
"requirements": ["aioairzone-cloud==0.6.11"]
}
@@ -32,8 +32,8 @@
"air_quality": {
"name": "Air Quality mode",
"state": {
"off": "Off",
"on": "On",
"off": "[%key:common::state::off%]",
"on": "[%key:common::state::on%]",
"auto": "Auto"
}
},
@@ -1438,7 +1438,7 @@ class AlexaModeController(AlexaCapability):
# Fan preset_mode
if self.instance == f"{fan.DOMAIN}.{fan.ATTR_PRESET_MODE}":
mode = self.entity.attributes.get(fan.ATTR_PRESET_MODE, None)
if mode in self.entity.attributes.get(fan.ATTR_PRESET_MODES, None):
if mode in self.entity.attributes.get(fan.ATTR_PRESET_MODES, ()):
return f"{fan.ATTR_PRESET_MODE}.{mode}"
# Humidifier mode
@@ -6,5 +6,5 @@
"iot_class": "cloud_push",
"loggers": ["boto3", "botocore", "s3transfer"],
"quality_scale": "legacy",
"requirements": ["boto3==1.34.131"]
"requirements": ["boto3==1.37.1"]
}
@@ -240,6 +240,7 @@ SENSOR_DESCRIPTIONS = (
suggested_display_precision=0,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),
SensorEntityDescription(
key=TYPE_WINDGUSTMPH,
@@ -609,6 +609,7 @@ SENSOR_DESCRIPTIONS = (
translation_key="wind_direction",
native_unit_of_measurement=DEGREE,
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),
SensorEntityDescription(
key=TYPE_WINDDIR_AVG10M,
@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/android_ip_webcam",
"iot_class": "local_polling",
"requirements": ["pydroid-ipcam==2.0.0"]
"requirements": ["pydroid-ipcam==3.0.0"]
}
@@ -43,6 +43,7 @@ class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
config_entry: ApSystemsConfigEntry
device_version: str
battery_system: bool
def __init__(
self,
@@ -68,6 +69,7 @@ class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
self.api.max_power = device_info.maxPower
self.api.min_power = device_info.minPower
self.device_version = device_info.devVer
self.battery_system = device_info.isBatterySystem
async def _async_update_data(self) -> ApSystemsSensorData:
try:
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/apsystems",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["apsystems-ez1==2.4.0"]
"requirements": ["apsystems-ez1==2.5.0"]
}
@@ -36,6 +36,8 @@ class ApSystemsInverterSwitch(ApSystemsEntity, SwitchEntity):
super().__init__(data)
self._api = data.coordinator.api
self._attr_unique_id = f"{data.device_id}_inverter_status"
if data.coordinator.battery_system:
self._attr_available = False
async def async_update(self) -> None:
"""Update switch status and availability."""
+8 -1
View File
@@ -6,7 +6,11 @@ import logging
from typing import Any
from homeassistant.components import mqtt
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.const import DEGREE, UnitOfPrecipitationDepth, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -98,6 +102,7 @@ def discover_sensors(topic: str, payload: dict[str, Any]) -> list[ArwnSensor] |
DEGREE,
"mdi:compass",
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),
]
return None
@@ -178,6 +183,7 @@ class ArwnSensor(SensorEntity):
units: str,
icon: str | None = None,
device_class: SensorDeviceClass | None = None,
state_class: SensorStateClass | None = None,
) -> None:
"""Initialize the sensor."""
self.entity_id = _slug(name)
@@ -188,6 +194,7 @@ class ArwnSensor(SensorEntity):
self._attr_native_unit_of_measurement = units
self._attr_icon = icon
self._attr_device_class = device_class
self._attr_state_class = state_class
def set_event(self, event: dict[str, Any]) -> None:
"""Update the sensor with the most recent event."""
@@ -1,9 +1,11 @@
"""Base class for assist satellite entities."""
import logging
from pathlib import Path
import voluptuous as vol
from homeassistant.components.http import StaticPathConfig
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
@@ -15,6 +17,8 @@ from .const import (
CONNECTION_TEST_DATA,
DATA_COMPONENT,
DOMAIN,
PREANNOUNCE_FILENAME,
PREANNOUNCE_URL,
AssistSatelliteEntityFeature,
)
from .entity import (
@@ -56,7 +60,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
{
vol.Optional("message"): str,
vol.Optional("media_id"): str,
vol.Optional("preannounce_media_id"): str,
vol.Optional("preannounce_media_id"): vol.Any(str, None),
}
),
cv.has_at_least_one_key("message", "media_id"),
@@ -71,7 +75,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
{
vol.Optional("start_message"): str,
vol.Optional("start_media_id"): str,
vol.Optional("preannounce_media_id"): str,
vol.Optional("preannounce_media_id"): vol.Any(str, None),
vol.Optional("extra_system_prompt"): str,
}
),
@@ -84,6 +88,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async_register_websocket_api(hass)
hass.http.register_view(ConnectionTestView())
# Default preannounce sound
await hass.http.async_register_static_paths(
[
StaticPathConfig(
PREANNOUNCE_URL, str(Path(__file__).parent / PREANNOUNCE_FILENAME)
)
]
)
return True
@@ -20,6 +20,9 @@ CONNECTION_TEST_DATA: HassKey[dict[str, asyncio.Event]] = HassKey(
f"{DOMAIN}_connection_tests"
)
PREANNOUNCE_FILENAME = "preannounce.mp3"
PREANNOUNCE_URL = f"/api/assist_satellite/static/{PREANNOUNCE_FILENAME}"
class AssistSatelliteEntityFeature(IntFlag):
"""Supported features of Assist satellite entity."""
@@ -28,7 +28,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import chat_session, entity
from homeassistant.helpers.entity import EntityDescription
from .const import AssistSatelliteEntityFeature
from .const import PREANNOUNCE_URL, AssistSatelliteEntityFeature
from .errors import AssistSatelliteError, SatelliteBusyError
_LOGGER = logging.getLogger(__name__)
@@ -180,7 +180,7 @@ class AssistSatelliteEntity(entity.Entity):
self,
message: str | None = None,
media_id: str | None = None,
preannounce_media_id: str | None = None,
preannounce_media_id: str | None = PREANNOUNCE_URL,
) -> None:
"""Play and show an announcement on the satellite.
@@ -190,7 +190,8 @@ class AssistSatelliteEntity(entity.Entity):
If 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_media_id is provided, it is played before the announcement.
If preannounce_media_id is provided, it overrides the default sound.
If preannounce_media_id is None, no sound is played.
Calls async_announce with message and media id.
"""
@@ -228,7 +229,7 @@ class AssistSatelliteEntity(entity.Entity):
start_message: str | None = None,
start_media_id: str | None = None,
extra_system_prompt: str | None = None,
preannounce_media_id: str | None = None,
preannounce_media_id: str | None = PREANNOUNCE_URL,
) -> None:
"""Start a conversation from the satellite.
@@ -239,6 +240,7 @@ class AssistSatelliteEntity(entity.Entity):
to omit the message and the satellite will not show any text.
If preannounce_media_id is provided, it is played before the announcement.
If preannounce_media_id is None, no sound is played.
Calls async_start_conversation.
"""
@@ -8,6 +8,7 @@ announce:
message:
required: false
example: "Time to wake up!"
default: ""
selector:
text:
media_id:
@@ -28,6 +29,7 @@ start_conversation:
start_message:
required: false
example: "You left the lights on in the living room. Turn them off?"
default: ""
selector:
text:
start_media_id:
@@ -198,7 +198,8 @@ async def websocket_test_connection(
hass.async_create_background_task(
satellite.async_internal_announce(
media_id=f"{CONNECTION_TEST_URL_BASE}/{connection_id}"
media_id=f"{CONNECTION_TEST_URL_BASE}/{connection_id}",
preannounce_media_id=None,
),
f"assist_satellite_connection_test_{msg['entity_id']}",
)
+1 -1
View File
@@ -6,5 +6,5 @@
"iot_class": "cloud_push",
"loggers": ["aiobotocore", "botocore"],
"quality_scale": "legacy",
"requirements": ["aiobotocore==2.13.1", "botocore==1.34.131"]
"requirements": ["aiobotocore==2.21.1", "botocore==1.37.1"]
}
+2 -2
View File
@@ -26,9 +26,9 @@
"entity": {
"sensor": {
"backup_manager_state": {
"name": "Backup Manager State",
"name": "Backup Manager state",
"state": {
"idle": "Idle",
"idle": "[%key:common::state::idle%]",
"create_backup": "Creating a backup",
"receive_backup": "Receiving a backup",
"restore_backup": "Restoring a backup"
@@ -0,0 +1 @@
"""Balay virtual integration."""
@@ -0,0 +1,6 @@
{
"domain": "balay",
"name": "Balay",
"integration_type": "virtual",
"supported_by": "home_connect"
}
@@ -132,7 +132,7 @@
"name": "Charging",
"state": {
"off": "Not charging",
"on": "Charging"
"on": "[%key:common::state::charging%]"
}
},
"carbon_monoxide": {
@@ -37,7 +37,7 @@
"vehicle_status": {
"name": "Vehicle status",
"state": {
"standby": "Standby",
"standby": "[%key:common::state::standby%]",
"vehicle_detected": "Detected",
"ready": "Ready",
"no_power": "No power",
@@ -6,7 +6,7 @@
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"region": "ConnectedDrive Region"
"region": "ConnectedDrive region"
},
"data_description": {
"username": "The email address of your MyBMW/MINI Connected account.",
@@ -113,10 +113,10 @@
},
"select": {
"ac_limit": {
"name": "AC Charging Limit"
"name": "AC charging limit"
},
"charging_mode": {
"name": "Charging Mode",
"name": "Charging mode",
"state": {
"immediate_charging": "Immediate charging",
"delayed_charging": "Delayed charging",
@@ -181,7 +181,7 @@
"cooling": "Cooling",
"heating": "Heating",
"inactive": "Inactive",
"standby": "Standby",
"standby": "[%key:common::state::standby%]",
"ventilation": "Ventilation"
}
},
@@ -0,0 +1,62 @@
"""The Bosch Alarm integration."""
from __future__ import annotations
from ssl import SSLError
from bosch_alarm_mode2 import Panel
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from .const import CONF_INSTALLER_CODE, CONF_USER_CODE, DOMAIN
PLATFORMS: list[Platform] = [Platform.ALARM_CONTROL_PANEL]
type BoschAlarmConfigEntry = ConfigEntry[Panel]
async def async_setup_entry(hass: HomeAssistant, entry: BoschAlarmConfigEntry) -> bool:
"""Set up Bosch Alarm from a config entry."""
panel = Panel(
host=entry.data[CONF_HOST],
port=entry.data[CONF_PORT],
automation_code=entry.data.get(CONF_PASSWORD),
installer_or_user_code=entry.data.get(
CONF_INSTALLER_CODE, entry.data.get(CONF_USER_CODE)
),
)
try:
await panel.connect()
except (PermissionError, ValueError) as err:
await panel.disconnect()
raise ConfigEntryNotReady from err
except (TimeoutError, OSError, ConnectionRefusedError, SSLError) as err:
await panel.disconnect()
raise ConfigEntryNotReady("Connection failed") from err
entry.runtime_data = panel
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, entry.unique_id or entry.entry_id)},
name=f"Bosch {panel.model}",
manufacturer="Bosch Security Systems",
model=panel.model,
sw_version=panel.firmware_version,
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: BoschAlarmConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
await entry.runtime_data.disconnect()
return unload_ok
@@ -0,0 +1,109 @@
"""Support for Bosch Alarm Panel."""
from __future__ import annotations
from bosch_alarm_mode2 import Panel
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
AlarmControlPanelEntityFeature,
AlarmControlPanelState,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import BoschAlarmConfigEntry
from .const import DOMAIN
async def async_setup_entry(
hass: HomeAssistant,
config_entry: BoschAlarmConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up control panels for each area."""
panel = config_entry.runtime_data
async_add_entities(
AreaAlarmControlPanel(
panel,
area_id,
config_entry.unique_id or config_entry.entry_id,
)
for area_id in panel.areas
)
class AreaAlarmControlPanel(AlarmControlPanelEntity):
"""An alarm control panel entity for a bosch alarm panel."""
_attr_has_entity_name = True
_attr_supported_features = (
AlarmControlPanelEntityFeature.ARM_HOME
| AlarmControlPanelEntityFeature.ARM_AWAY
)
_attr_code_arm_required = False
_attr_name = None
def __init__(self, panel: Panel, area_id: int, unique_id: str) -> None:
"""Initialise a Bosch Alarm control panel entity."""
self.panel = panel
self._area = panel.areas[area_id]
self._area_id = area_id
self._attr_unique_id = f"{unique_id}_area_{area_id}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._attr_unique_id)},
name=self._area.name,
manufacturer="Bosch Security Systems",
via_device=(
DOMAIN,
unique_id,
),
)
@property
def alarm_state(self) -> AlarmControlPanelState | None:
"""Return the state of the alarm."""
if self._area.is_triggered():
return AlarmControlPanelState.TRIGGERED
if self._area.is_disarmed():
return AlarmControlPanelState.DISARMED
if self._area.is_arming():
return AlarmControlPanelState.ARMING
if self._area.is_pending():
return AlarmControlPanelState.PENDING
if self._area.is_part_armed():
return AlarmControlPanelState.ARMED_HOME
if self._area.is_all_armed():
return AlarmControlPanelState.ARMED_AWAY
return None
async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Disarm this panel."""
await self.panel.area_disarm(self._area_id)
async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
await self.panel.area_arm_part(self._area_id)
async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
await self.panel.area_arm_all(self._area_id)
@property
def available(self) -> bool:
"""Return True if entity is available."""
return self.panel.connection_status()
async def async_added_to_hass(self) -> None:
"""Run when entity attached to hass."""
await super().async_added_to_hass()
self._area.status_observer.attach(self.schedule_update_ha_state)
self.panel.connection_status_observer.attach(self.schedule_update_ha_state)
async def async_will_remove_from_hass(self) -> None:
"""Run when entity removed from hass."""
await super().async_will_remove_from_hass()
self._area.status_observer.detach(self.schedule_update_ha_state)
self.panel.connection_status_observer.detach(self.schedule_update_ha_state)
@@ -0,0 +1,165 @@
"""Config flow for Bosch Alarm integration."""
from __future__ import annotations
import asyncio
import logging
import ssl
from typing import Any
from bosch_alarm_mode2 import Panel
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_CODE,
CONF_HOST,
CONF_MODEL,
CONF_PASSWORD,
CONF_PORT,
)
import homeassistant.helpers.config_validation as cv
from .const import CONF_INSTALLER_CODE, CONF_USER_CODE, DOMAIN
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=7700): cv.positive_int,
}
)
STEP_AUTH_DATA_SCHEMA_SOLUTION = vol.Schema(
{
vol.Required(CONF_USER_CODE): str,
}
)
STEP_AUTH_DATA_SCHEMA_AMAX = vol.Schema(
{
vol.Required(CONF_INSTALLER_CODE): str,
vol.Required(CONF_PASSWORD): str,
}
)
STEP_AUTH_DATA_SCHEMA_BG = vol.Schema(
{
vol.Required(CONF_PASSWORD): str,
}
)
STEP_INIT_DATA_SCHEMA = vol.Schema({vol.Optional(CONF_CODE): str})
async def try_connect(
data: dict[str, Any], load_selector: int = 0
) -> tuple[str, int | None]:
"""Validate the user input allows us to connect.
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
"""
panel = Panel(
host=data[CONF_HOST],
port=data[CONF_PORT],
automation_code=data.get(CONF_PASSWORD),
installer_or_user_code=data.get(CONF_INSTALLER_CODE, data.get(CONF_USER_CODE)),
)
try:
await panel.connect(load_selector)
finally:
await panel.disconnect()
return (panel.model, panel.serial_number)
class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Bosch Alarm."""
def __init__(self) -> None:
"""Init config flow."""
self._data: dict[str, Any] = {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
try:
# Use load_selector = 0 to fetch the panel model without authentication.
(model, serial) = await try_connect(user_input, 0)
except (
OSError,
ConnectionRefusedError,
ssl.SSLError,
asyncio.exceptions.TimeoutError,
) as e:
_LOGGER.error("Connection Error: %s", e)
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
self._data = user_input
self._data[CONF_MODEL] = model
return await self.async_step_auth()
return self.async_show_form(
step_id="user",
data_schema=self.add_suggested_values_to_schema(
STEP_USER_DATA_SCHEMA, user_input
),
errors=errors,
)
async def async_step_auth(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the auth step."""
errors: dict[str, str] = {}
# Each model variant requires a different authentication flow
if "Solution" in self._data[CONF_MODEL]:
schema = STEP_AUTH_DATA_SCHEMA_SOLUTION
elif "AMAX" in self._data[CONF_MODEL]:
schema = STEP_AUTH_DATA_SCHEMA_AMAX
else:
schema = STEP_AUTH_DATA_SCHEMA_BG
if user_input is not None:
self._data.update(user_input)
try:
(model, serial_number) = await try_connect(
self._data, Panel.LOAD_EXTENDED_INFO
)
except (PermissionError, ValueError) as e:
errors["base"] = "invalid_auth"
_LOGGER.error("Authentication Error: %s", e)
except (
OSError,
ConnectionRefusedError,
ssl.SSLError,
TimeoutError,
) as e:
_LOGGER.error("Connection Error: %s", e)
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
if serial_number:
await self.async_set_unique_id(str(serial_number))
self._abort_if_unique_id_configured()
else:
self._async_abort_entries_match({CONF_HOST: self._data[CONF_HOST]})
return self.async_create_entry(title=f"Bosch {model}", data=self._data)
return self.async_show_form(
step_id="auth",
data_schema=self.add_suggested_values_to_schema(schema, user_input),
errors=errors,
)
@@ -0,0 +1,6 @@
"""Constants for the Bosch Alarm integration."""
DOMAIN = "bosch_alarm"
HISTORY_ATTR = "history"
CONF_INSTALLER_CODE = "installer_code"
CONF_USER_CODE = "user_code"
@@ -0,0 +1,11 @@
{
"domain": "bosch_alarm",
"name": "Bosch Alarm",
"codeowners": ["@mag1024", "@sanjay900"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bosch_alarm",
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "bronze",
"requirements": ["bosch-alarm-mode2==0.4.3"]
}
@@ -0,0 +1,84 @@
rules:
# Bronze
action-setup:
status: exempt
comment: |
No custom actions defined
appropriate-polling:
status: exempt
comment: |
No polling
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
No custom actions are defined.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup: done
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: |
No custom actions are defined.
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo
entity-unavailable: todo
integration-owner: done
log-when-unavailable: todo
parallel-updates: todo
reauthentication-flow: todo
test-coverage: done
# Gold
devices: done
diagnostics: todo
discovery-update-info: todo
discovery: todo
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: todo
entity-disabled-by-default: todo
entity-translations: todo
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
repair-issues:
status: exempt
comment: |
No repairs
stale-devices:
status: exempt
comment: |
Device type integration
# Platinum
async-dependency: done
inject-websession:
status: exempt
comment: |
Integration does not make any HTTP requests.
strict-typing: done
@@ -0,0 +1,36 @@
{
"config": {
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of your Bosch alarm panel",
"port": "The port used to connect to your Bosch alarm panel. This is usually 7700"
}
},
"auth": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
"installer_code": "Installer code",
"user_code": "User code"
},
"data_description": {
"password": "The Mode 2 automation code from your panel",
"installer_code": "The installer code from your panel",
"user_code": "The user code from your panel"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
}
@@ -170,6 +170,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
native_unit_of_measurement=DEGREE,
icon="mdi:compass-outline",
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),
SensorEntityDescription(
key="pressure",
@@ -142,6 +142,12 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
@property
def media_artist(self) -> str | None:
"""Artist of current playing media, music track only."""
if (
not self.client.play_state.metadata.artist
and self.client.state.source == "IR"
):
# Return channel instead of artist when playing internet radio
return self.client.play_state.metadata.station
return self.client.play_state.metadata.artist
@property
@@ -169,6 +175,11 @@ class CambridgeAudioDevice(CambridgeAudioEntity, MediaPlayerEntity):
"""Last time the media position was updated."""
return self.client.position_last_updated
@property
def media_channel(self) -> str | None:
"""Channel currently playing."""
return self.client.play_state.metadata.station
@property
def is_volume_muted(self) -> bool | None:
"""Volume mute status."""
+1 -1
View File
@@ -14,7 +14,7 @@
"documentation": "https://www.home-assistant.io/integrations/cast",
"iot_class": "local_polling",
"loggers": ["casttube", "pychromecast"],
"requirements": ["PyChromecast==14.0.6"],
"requirements": ["PyChromecast==14.0.7"],
"single_config_entry": true,
"zeroconf": ["_googlecast._tcp.local."]
}
@@ -98,13 +98,13 @@
"name": "Preset",
"state": {
"none": "None",
"eco": "Eco",
"away": "Away",
"home": "[%key:common::state::home%]",
"away": "[%key:common::state::not_home%]",
"activity": "Activity",
"boost": "Boost",
"comfort": "Comfort",
"home": "[%key:common::state::home%]",
"sleep": "Sleep",
"activity": "Activity"
"eco": "Eco",
"sleep": "Sleep"
}
},
"preset_modes": {
@@ -257,7 +257,7 @@
"selector": {
"hvac_mode": {
"options": {
"off": "Off",
"off": "[%key:common::state::off%]",
"auto": "Auto",
"cool": "Cool",
"dry": "Dry",
+12 -2
View File
@@ -4,13 +4,14 @@ from __future__ import annotations
import asyncio
from collections.abc import AsyncIterator, Callable, Coroutine, Mapping
from http import HTTPStatus
import logging
import random
from typing import Any
from aiohttp import ClientError
from aiohttp import ClientError, ClientResponseError
from hass_nabucasa import Cloud, CloudError
from hass_nabucasa.api import CloudApiNonRetryableError
from hass_nabucasa.api import CloudApiError, CloudApiNonRetryableError
from hass_nabucasa.cloud_api import (
FilesHandlerListEntry,
async_files_delete_file,
@@ -120,6 +121,8 @@ class CloudBackupAgent(BackupAgent):
"""
if not backup.protected:
raise BackupAgentError("Cloud backups must be protected")
if self._cloud.subscription_expired:
raise BackupAgentError("Cloud subscription has expired")
size = backup.size
try:
@@ -152,6 +155,13 @@ class CloudBackupAgent(BackupAgent):
) from err
raise BackupAgentError(f"Failed to upload backup {err}") from err
except CloudError as err:
if (
isinstance(err, CloudApiError)
and isinstance(err.orig_exc, ClientResponseError)
and err.orig_exc.status == HTTPStatus.FORBIDDEN
and self._cloud.subscription_expired
):
raise BackupAgentError("Cloud subscription has expired") from err
if tries == _RETRY_LIMIT:
raise BackupAgentError(f"Failed to upload backup {err}") from err
tries += 1
@@ -9,7 +9,6 @@ from typing import Any
import pycfdns
import voluptuous as vol
from homeassistant.components import persistent_notification
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_TOKEN, CONF_ZONE
from homeassistant.core import HomeAssistant
@@ -118,8 +117,6 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
persistent_notification.async_dismiss(self.hass, "cloudflare_setup")
errors: dict[str, str] = {}
if user_input is not None:
@@ -4,19 +4,19 @@
"step": {
"user": {
"title": "Connect to Cloudflare",
"description": "This integration requires an API Token created with Zone:Zone:Read and Zone:DNS:Edit permissions for all zones in your account.",
"description": "This integration requires an API token created with Zone:Zone:Read and Zone:DNS:Edit permissions for all zones in your account.",
"data": {
"api_token": "[%key:common::config_flow::data::api_token%]"
}
},
"zone": {
"title": "Choose the Zone to Update",
"title": "Choose the zone to update",
"data": {
"zone": "Zone"
}
},
"records": {
"title": "Choose the Records to Update",
"title": "Choose the records to update",
"data": {
"records": "Records"
}
@@ -40,7 +40,7 @@
"services": {
"update_records": {
"name": "Update records",
"description": "Manually trigger update to Cloudflare records."
"description": "Manually triggers an update of Cloudflare records."
}
}
}
@@ -41,6 +41,7 @@ ALARM_ACTIONS: dict[str, str] = {
ALARM_AREA_ARMED_STATUS: dict[str, int] = {
DISABLE: 0,
HOME_P1: 1,
HOME_P2: 2,
NIGHT: 3,
@@ -128,20 +129,38 @@ class ComelitAlarmEntity(CoordinatorEntity[ComelitVedoSystem], AlarmControlPanel
AlarmAreaState.TRIGGERED: AlarmControlPanelState.TRIGGERED,
}.get(self._area.human_status)
async def _async_update_state(self, area_state: AlarmAreaState, armed: int) -> None:
"""Update state after action."""
self._area.human_status = area_state
self._area.armed = armed
await self.async_update_ha_state()
async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
if code != str(self._api.device_pin):
return
await self._api.set_zone_status(self._area.index, ALARM_ACTIONS[DISABLE])
await self._async_update_state(
AlarmAreaState.DISARMED, ALARM_AREA_ARMED_STATUS[DISABLE]
)
async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
await self._api.set_zone_status(self._area.index, ALARM_ACTIONS[AWAY])
await self._async_update_state(
AlarmAreaState.ARMED, ALARM_AREA_ARMED_STATUS[AWAY]
)
async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
await self._api.set_zone_status(self._area.index, ALARM_ACTIONS[HOME])
await self._async_update_state(
AlarmAreaState.ARMED, ALARM_AREA_ARMED_STATUS[HOME_P1]
)
async def async_alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command."""
await self._api.set_zone_status(self._area.index, ALARM_ACTIONS[NIGHT])
await self._async_update_state(
AlarmAreaState.ARMED, ALARM_AREA_ARMED_STATUS[NIGHT]
)
@@ -9,3 +9,5 @@ _LOGGER = logging.getLogger(__package__)
DOMAIN = "comelit"
DEFAULT_PORT = 80
DEVICE_TYPE_LIST = [BRIDGE, VEDO]
SCAN_INTERVAL = 5
@@ -22,7 +22,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import _LOGGER, DOMAIN
from .const import _LOGGER, DOMAIN, SCAN_INTERVAL
type ComelitConfigEntry = ConfigEntry[ComelitBaseCoordinator]
@@ -53,7 +53,7 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[T]):
logger=_LOGGER,
config_entry=entry,
name=f"{DOMAIN}-{host}-coordinator",
update_interval=timedelta(seconds=5),
update_interval=timedelta(seconds=SCAN_INTERVAL),
)
device_registry = dr.async_get(self.hass)
device_registry.async_get_or_create(
+11 -10
View File
@@ -8,7 +8,7 @@ from aiocomelit import ComelitSerialBridgeObject
from aiocomelit.const import COVER, STATE_COVER, STATE_OFF, STATE_ON
from homeassistant.components.cover import CoverDeviceClass, CoverEntity, CoverState
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -98,13 +98,20 @@ class ComelitCoverEntity(
"""Return if the cover is opening."""
return self._current_action("opening")
async def _cover_set_state(self, action: int, state: int) -> None:
"""Set desired cover state."""
self._last_state = self.state
await self._api.set_device_status(COVER, self._device.index, action)
self.coordinator.data[COVER][self._device.index].status = state
self.async_write_ha_state()
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close cover."""
await self._api.set_device_status(COVER, self._device.index, STATE_OFF)
await self._cover_set_state(STATE_OFF, 2)
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open cover."""
await self._api.set_device_status(COVER, self._device.index, STATE_ON)
await self._cover_set_state(STATE_ON, 1)
async def async_stop_cover(self, **_kwargs: Any) -> None:
"""Stop the cover."""
@@ -112,13 +119,7 @@ class ComelitCoverEntity(
return
action = STATE_ON if self.is_closing else STATE_OFF
await self._api.set_device_status(COVER, self._device.index, action)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle device update."""
self._last_state = self.state
self.async_write_ha_state()
await self._cover_set_state(action, 0)
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
+2 -1
View File
@@ -59,7 +59,8 @@ class ComelitLightEntity(CoordinatorEntity[ComelitSerialBridge], LightEntity):
async def _light_set_state(self, state: int) -> None:
"""Set desired light state."""
await self.coordinator.api.set_device_status(LIGHT, self._device.index, state)
await self.coordinator.async_request_refresh()
self.coordinator.data[LIGHT][self._device.index].status = state
self.async_write_ha_state()
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
+2 -1
View File
@@ -67,7 +67,8 @@ class ComelitSwitchEntity(CoordinatorEntity[ComelitSerialBridge], SwitchEntity):
await self.coordinator.api.set_device_status(
self._device.type, self._device.index, state
)
await self.coordinator.async_request_refresh()
self.coordinator.data[self._device.type][self._device.index].status = state
self.async_write_ha_state()
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
@@ -0,0 +1 @@
"""Constructa virtual integration."""
@@ -0,0 +1,6 @@
{
"domain": "constructa",
"name": "Constructa",
"integration_type": "virtual",
"supported_by": "home_connect"
}
@@ -354,6 +354,35 @@ class ChatLog:
if self.delta_listener:
self.delta_listener(self, asdict(tool_result))
async def _async_expand_prompt_template(
self,
llm_context: llm.LLMContext,
prompt: str,
language: str,
user_name: str | None = None,
) -> str:
try:
return template.Template(prompt, self.hass).async_render(
{
"ha_name": self.hass.config.location_name,
"user_name": user_name,
"llm_context": llm_context,
},
parse_result=False,
)
except TemplateError as err:
LOGGER.error("Error rendering prompt: %s", err)
intent_response = intent.IntentResponse(language=language)
intent_response.async_set_error(
intent.IntentResponseErrorCode.UNKNOWN,
"Sorry, I had a problem with my template",
)
raise ConverseError(
"Error rendering prompt",
conversation_id=self.conversation_id,
response=intent_response,
) from err
async def async_update_llm_data(
self,
conversing_domain: str,
@@ -409,38 +438,28 @@ class ChatLog:
):
user_name = user.name
try:
prompt_parts = [
template.Template(
llm.BASE_PROMPT
+ (user_llm_prompt or llm.DEFAULT_INSTRUCTIONS_PROMPT),
self.hass,
).async_render(
{
"ha_name": self.hass.config.location_name,
"user_name": user_name,
"llm_context": llm_context,
},
parse_result=False,
)
]
except TemplateError as err:
LOGGER.error("Error rendering prompt: %s", err)
intent_response = intent.IntentResponse(language=user_input.language)
intent_response.async_set_error(
intent.IntentResponseErrorCode.UNKNOWN,
"Sorry, I had a problem with my template",
prompt_parts = []
prompt_parts.append(
await self._async_expand_prompt_template(
llm_context,
(user_llm_prompt or llm.DEFAULT_INSTRUCTIONS_PROMPT),
user_input.language,
user_name,
)
raise ConverseError(
"Error rendering prompt",
conversation_id=self.conversation_id,
response=intent_response,
) from err
)
if llm_api:
prompt_parts.append(llm_api.api_prompt)
prompt_parts.append(
await self._async_expand_prompt_template(
llm_context,
llm.BASE_PROMPT,
user_input.language,
user_name,
)
)
if extra_system_prompt := (
# Take new system prompt if one was given
user_input.extra_system_prompt or self.extra_system_prompt
@@ -650,7 +650,14 @@ class DefaultAgent(ConversationEntity):
if (
(maybe_result is None) # first result
or (num_matched_entities > best_num_matched_entities)
or (
# More literal text matched
result.text_chunks_matched > maybe_result.text_chunks_matched
)
or (
# More entities matched
num_matched_entities > best_num_matched_entities
)
or (
# Fewer unmatched entities
(num_matched_entities == best_num_matched_entities)
@@ -662,16 +669,6 @@ class DefaultAgent(ConversationEntity):
and (num_unmatched_entities == best_num_unmatched_entities)
and (num_unmatched_ranges > best_num_unmatched_ranges)
)
or (
# More literal text matched
(num_matched_entities == best_num_matched_entities)
and (num_unmatched_entities == best_num_unmatched_entities)
and (num_unmatched_ranges == best_num_unmatched_ranges)
and (
result.text_chunks_matched
> maybe_result.text_chunks_matched
)
)
or (
# Prefer match failures with entities
(result.text_chunks_matched == maybe_result.text_chunks_matched)
@@ -3,11 +3,13 @@
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import asdict
from typing import Any
from aiohttp import web
from hassil.recognize import MISSING_ENTITY, RecognizeResult
from hassil.string_matcher import UnmatchedRangeEntity, UnmatchedTextEntity
from home_assistant_intents import get_language_scores
import voluptuous as vol
from homeassistant.components import http, websocket_api
@@ -38,6 +40,7 @@ def async_setup(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, websocket_list_agents)
websocket_api.async_register_command(hass, websocket_list_sentences)
websocket_api.async_register_command(hass, websocket_hass_agent_debug)
websocket_api.async_register_command(hass, websocket_hass_agent_language_scores)
@websocket_api.websocket_command(
@@ -336,6 +339,36 @@ def _get_unmatched_slots(
return unmatched_slots
@websocket_api.websocket_command(
{
vol.Required("type"): "conversation/agent/homeassistant/language_scores",
vol.Optional("language"): str,
vol.Optional("country"): str,
}
)
@websocket_api.async_response
async def websocket_hass_agent_language_scores(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Get support scores per language."""
language = msg.get("language", hass.config.language)
country = msg.get("country", hass.config.country)
scores = await hass.async_add_executor_job(get_language_scores)
matching_langs = language_util.matches(language, scores.keys(), country=country)
preferred_lang = matching_langs[0] if matching_langs else language
result = {
"languages": {
lang_key: asdict(lang_scores) for lang_key, lang_scores in scores.items()
},
"preferred_language": preferred_lang,
}
connection.send_result(msg["id"], result)
class ConversationProcessView(http.HomeAssistantView):
"""View to process text."""
@@ -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.3.23"]
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.3.28"]
}
@@ -6,7 +6,7 @@
"data": {
"email": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]",
"country": "Country"
"country": "[%key:common::config_flow::data::country%]"
},
"data_description": {
"email": "Email used to access your {cookidoo} account.",
+3 -3
View File
@@ -38,10 +38,10 @@
"name": "[%key:component::cover::title%]",
"state": {
"open": "[%key:common::state::open%]",
"opening": "Opening",
"opening": "[%key:common::state::opening%]",
"closed": "[%key:common::state::closed%]",
"closing": "Closing",
"stopped": "Stopped"
"closing": "[%key:common::state::closing%]",
"stopped": "[%key:common::state::stopped%]"
},
"state_attributes": {
"current_position": {
@@ -38,8 +38,8 @@
"protect_mode": {
"name": "Protect mode",
"state": {
"away": "Away",
"home": "Home",
"away": "[%key:common::state::not_home%]",
"home": "[%key:common::state::home%]",
"schedule": "Schedule"
}
}
@@ -50,10 +50,10 @@ class DukeEnergyConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
username = auth["cdp_internal_user_id"].lower()
username = auth["internalUserID"].lower()
await self.async_set_unique_id(username)
self._abort_if_unique_id_configured()
email = auth["email"].lower()
email = auth["loginEmailAddress"].lower()
data = {
CONF_EMAIL: email,
CONF_USERNAME: username,
@@ -8,7 +8,11 @@ from aiodukeenergy import DukeEnergy
from aiohttp import ClientError
from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
from homeassistant.components.recorder.models import (
StatisticData,
StatisticMeanType,
StatisticMetaData,
)
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
@@ -137,7 +141,7 @@ class DukeEnergyCoordinator(DataUpdateCoordinator[None]):
f"Duke Energy {meter['serviceType'].capitalize()} {serial_number}"
)
consumption_metadata = StatisticMetaData(
has_mean=False,
mean_type=StatisticMeanType.NONE,
has_sum=True,
name=f"{name_prefix} Consumption",
source=DOMAIN,
@@ -6,5 +6,5 @@
"dependencies": ["recorder"],
"documentation": "https://www.home-assistant.io/integrations/duke_energy",
"iot_class": "cloud_polling",
"requirements": ["aiodukeenergy==0.2.2"]
"requirements": ["aiodukeenergy==0.3.0"]
}
@@ -91,15 +91,15 @@ class EcoNetWaterHeater(EcoNetEntity[WaterHeater], WaterHeaterEntity):
def operation_list(self) -> list[str]:
"""List of available operation modes."""
econet_modes = self.water_heater.modes
op_list = []
operation_modes = set()
for mode in econet_modes:
if (
mode is not WaterHeaterOperationMode.UNKNOWN
and mode is not WaterHeaterOperationMode.VACATION
):
ha_mode = ECONET_STATE_TO_HA[mode]
op_list.append(ha_mode)
return op_list
operation_modes.add(ha_mode)
return list(operation_modes)
@property
def supported_features(self) -> WaterHeaterEntityFeature:
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.10", "deebot-client==12.3.1"]
"requirements": ["py-sucks==0.9.10", "deebot-client==12.4.0"]
}
@@ -14,7 +14,7 @@
"step": {
"auth": {
"data": {
"country": "Country",
"country": "[%key:common::config_flow::data::country%]",
"override_rest_url": "REST URL",
"override_mqtt_url": "MQTT URL",
"password": "[%key:common::config_flow::data::password%]",
@@ -68,6 +68,7 @@ ECOWITT_SENSORS_MAPPING: Final = {
key="DEGREE",
native_unit_of_measurement=DEGREE,
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),
EcoWittSensorTypes.WATT_METERS_SQUARED: SensorEntityDescription(
key="WATT_METERS_SQUARED",
+6 -2
View File
@@ -100,7 +100,11 @@ class ElkEntity(Entity):
return {"index": self._element.index + 1}
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
pass
"""Handle changes to the element.
This method is called when the element changes. It should be
overridden by subclasses to handle the changes.
"""
@callback
def _element_callback(self, element: Element, changeset: dict[str, Any]) -> None:
@@ -111,7 +115,7 @@ class ElkEntity(Entity):
async def async_added_to_hass(self) -> None:
"""Register callback for ElkM1 changes and update entity state."""
self._element.add_callback(self._element_callback)
self._element_callback(self._element, {})
self._element_changed(self._element, {})
@property
def device_info(self) -> DeviceInfo:
+6 -2
View File
@@ -7,7 +7,11 @@ from typing import TYPE_CHECKING, cast
from elvia import Elvia, error as ElviaError
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
from homeassistant.components.recorder.models import (
StatisticData,
StatisticMeanType,
StatisticMetaData,
)
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
@@ -144,7 +148,7 @@ class ElviaImporter:
async_add_external_statistics(
hass=self.hass,
metadata=StatisticMetaData(
has_mean=False,
mean_type=StatisticMeanType.NONE,
has_sum=True,
name=f"{self.metering_point_id} Consumption",
source=DOMAIN,
@@ -46,6 +46,8 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
type EmulatedRokuConfigEntry = ConfigEntry[EmulatedRoku]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the emulated roku component."""
@@ -65,22 +67,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
async def async_setup_entry(
hass: HomeAssistant, entry: EmulatedRokuConfigEntry
) -> bool:
"""Set up an emulated roku server from a config entry."""
config = config_entry.data
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
name = config[CONF_NAME]
listen_port = config[CONF_LISTEN_PORT]
host_ip = config.get(CONF_HOST_IP) or await async_get_source_ip(hass)
advertise_ip = config.get(CONF_ADVERTISE_IP)
advertise_port = config.get(CONF_ADVERTISE_PORT)
upnp_bind_multicast = config.get(CONF_UPNP_BIND_MULTICAST)
config = entry.data
name: str = config[CONF_NAME]
listen_port: int = config[CONF_LISTEN_PORT]
host_ip: str = config.get(CONF_HOST_IP) or await async_get_source_ip(hass)
advertise_ip: str | None = config.get(CONF_ADVERTISE_IP)
advertise_port: int | None = config.get(CONF_ADVERTISE_PORT)
upnp_bind_multicast: bool | None = config.get(CONF_UPNP_BIND_MULTICAST)
server = EmulatedRoku(
hass,
entry.entry_id,
name,
host_ip,
listen_port,
@@ -88,14 +89,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
advertise_port,
upnp_bind_multicast,
)
hass.data[DOMAIN][name] = server
entry.runtime_data = server
return await server.setup()
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(
hass: HomeAssistant, entry: EmulatedRokuConfigEntry
) -> bool:
"""Unload a config entry."""
name = entry.data[CONF_NAME]
server = hass.data[DOMAIN].pop(name)
return await server.unload()
return await entry.runtime_data.unload()
@@ -5,7 +5,13 @@ import logging
from emulated_roku import EmulatedRokuCommandHandler, EmulatedRokuServer
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CoreState, EventOrigin
from homeassistant.core import (
CALLBACK_TYPE,
CoreState,
Event,
EventOrigin,
HomeAssistant,
)
LOGGER = logging.getLogger(__package__)
@@ -27,16 +33,18 @@ class EmulatedRoku:
def __init__(
self,
hass,
name,
host_ip,
listen_port,
advertise_ip,
advertise_port,
upnp_bind_multicast,
):
hass: HomeAssistant,
entry_id: str,
name: str,
host_ip: str,
listen_port: int,
advertise_ip: str | None,
advertise_port: int | None,
upnp_bind_multicast: bool | None,
) -> None:
"""Initialize the properties."""
self.hass = hass
self.entry_id = entry_id
self.roku_usn = name
self.host_ip = host_ip
@@ -47,21 +55,21 @@ class EmulatedRoku:
self.bind_multicast = upnp_bind_multicast
self._api_server = None
self._api_server: EmulatedRokuServer | None = None
self._unsub_start_listener = None
self._unsub_stop_listener = None
self._unsub_start_listener: CALLBACK_TYPE | None = None
self._unsub_stop_listener: CALLBACK_TYPE | None = None
async def setup(self):
async def setup(self) -> bool:
"""Start the emulated_roku server."""
class EventCommandHandler(EmulatedRokuCommandHandler):
"""emulated_roku command handler to turn commands into events."""
def __init__(self, hass):
def __init__(self, hass: HomeAssistant) -> None:
self.hass = hass
def on_keydown(self, roku_usn, key):
def on_keydown(self, roku_usn: str, key: str) -> None:
"""Handle keydown event."""
self.hass.bus.async_fire(
EVENT_ROKU_COMMAND,
@@ -73,7 +81,7 @@ class EmulatedRoku:
EventOrigin.local,
)
def on_keyup(self, roku_usn, key):
def on_keyup(self, roku_usn: str, key: str) -> None:
"""Handle keyup event."""
self.hass.bus.async_fire(
EVENT_ROKU_COMMAND,
@@ -85,7 +93,7 @@ class EmulatedRoku:
EventOrigin.local,
)
def on_keypress(self, roku_usn, key):
def on_keypress(self, roku_usn: str, key: str) -> None:
"""Handle keypress event."""
self.hass.bus.async_fire(
EVENT_ROKU_COMMAND,
@@ -97,7 +105,7 @@ class EmulatedRoku:
EventOrigin.local,
)
def launch(self, roku_usn, app_id):
def launch(self, roku_usn: str, app_id: str) -> None:
"""Handle launch event."""
self.hass.bus.async_fire(
EVENT_ROKU_COMMAND,
@@ -129,17 +137,19 @@ class EmulatedRoku:
bind_multicast=self.bind_multicast,
)
async def emulated_roku_stop(event):
async def emulated_roku_stop(event: Event | None) -> None:
"""Wrap the call to emulated_roku.close."""
LOGGER.debug("Stopping emulated_roku %s", self.roku_usn)
self._unsub_stop_listener = None
assert self._api_server is not None
await self._api_server.close()
async def emulated_roku_start(event):
async def emulated_roku_start(event: Event | None) -> None:
"""Wrap the call to emulated_roku.start."""
try:
LOGGER.debug("Starting emulated_roku %s", self.roku_usn)
self._unsub_start_listener = None
assert self._api_server is not None
await self._api_server.start()
except OSError:
LOGGER.exception(
@@ -165,7 +175,7 @@ class EmulatedRoku:
return True
async def unload(self):
async def unload(self) -> bool:
"""Unload the emulated_roku server."""
LOGGER.debug("Unloading emulated_roku %s", self.roku_usn)
@@ -177,6 +187,7 @@ class EmulatedRoku:
self._unsub_stop_listener()
self._unsub_stop_listener = None
assert self._api_server is not None
await self._api_server.close()
return True
@@ -168,6 +168,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = (
native_unit_of_measurement=DEGREE,
value_fn=lambda data: data.conditions.get("wind_bearing", {}).get("value"),
device_class=SensorDeviceClass.WIND_DIRECTION,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
),
ECSensorEntityDescription(
key="wind_chill",
@@ -3,8 +3,8 @@
"step": {
"user": {
"data": {
"language": "Language",
"country": "Country"
"language": "[%key:common::config_flow::data::language%]",
"country": "[%key:common::config_flow::data::country%]"
}
}
},
@@ -310,12 +310,13 @@ class EsphomeAssistSatellite(
self.entry_data.api_version
)
)
if feature_flags & VoiceAssistantFeature.SPEAKER:
media_id = tts_output["media_id"]
if feature_flags & VoiceAssistantFeature.SPEAKER and (
stream := tts.async_get_stream(self.hass, tts_output["token"])
):
self._tts_streaming_task = (
self.config_entry.async_create_background_task(
self.hass,
self._stream_tts_audio(media_id),
self._stream_tts_audio(stream),
"esphome_voice_assistant_tts",
)
)
@@ -370,8 +371,10 @@ class EsphomeAssistSatellite(
announcement.media_id,
)
media_id = announcement.media_id
if announcement.media_id_source != "tts":
# Route non-TTS media through the proxy
is_media_tts = announcement.media_id_source == "tts"
preannounce_media_id = announcement.preannounce_media_id
if (not is_media_tts) or preannounce_media_id:
# Route media through the proxy
format_to_use: MediaPlayerSupportedFormat | None = None
for supported_format in chain(
*self.entry_data.media_player_formats.values()
@@ -384,22 +387,33 @@ class EsphomeAssistSatellite(
assert (self.registry_entry is not None) and (
self.registry_entry.device_id is not None
)
proxy_url = async_create_proxy_url(
self.hass,
self.registry_entry.device_id,
media_id,
make_proxy_url = partial(
async_create_proxy_url,
hass=self.hass,
device_id=self.registry_entry.device_id,
media_format=format_to_use.format,
rate=format_to_use.sample_rate or None,
channels=format_to_use.num_channels or None,
width=format_to_use.sample_bytes or None,
)
media_id = async_process_play_media_url(self.hass, proxy_url)
if not is_media_tts:
media_id = async_process_play_media_url(
self.hass, make_proxy_url(media_url=media_id)
)
if preannounce_media_id:
preannounce_media_id = async_process_play_media_url(
self.hass, make_proxy_url(media_url=preannounce_media_id)
)
await self.cli.send_voice_assistant_announcement_await_response(
media_id,
_ANNOUNCEMENT_TIMEOUT_SEC,
announcement.message,
start_conversation=run_pipeline_after,
preannounce_media_id=preannounce_media_id or "",
)
async def handle_pipeline_start(
@@ -551,7 +565,7 @@ class EsphomeAssistSatellite(
async def _stream_tts_audio(
self,
media_id: str,
tts_result: tts.ResultStream,
sample_rate: int = 16000,
sample_width: int = 2,
sample_channels: int = 1,
@@ -566,15 +580,14 @@ class EsphomeAssistSatellite(
if not self._is_running:
return
extension, data = await tts.async_get_media_source_audio(
self.hass,
media_id,
)
if extension != "wav":
_LOGGER.error("Only WAV audio can be streamed, got %s", extension)
if tts_result.extension != "wav":
_LOGGER.error(
"Only WAV audio can be streamed, got %s", tts_result.extension
)
return
data = b"".join([chunk async for chunk in tts_result.async_stream_result()])
with io.BytesIO(data) as wav_io, wave.open(wav_io, "rb") as wav_file:
if (
(wav_file.getframerate() != sample_rate)
@@ -128,8 +128,23 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
self._password = ""
return await self._async_authenticate_or_add()
if error is None and entry_data.get(CONF_NOISE_PSK):
return await self.async_step_reauth_encryption_removed_confirm()
return await self.async_step_reauth_confirm()
async def async_step_reauth_encryption_removed_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reauthorization flow when encryption was removed."""
if user_input is not None:
self._noise_psk = None
return self._async_get_entry()
return self.async_show_form(
step_id="reauth_encryption_removed_confirm",
description_placeholders={"name": self._name},
)
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+22 -18
View File
@@ -282,15 +282,18 @@ class RuntimeEntryData:
) -> None:
"""Distribute an update of static infos to all platforms."""
# First, load all platforms
needed_platforms = set()
if async_get_dashboard(hass):
needed_platforms.add(Platform.UPDATE)
needed_platforms: set[Platform] = set()
if self.device_info and self.device_info.voice_assistant_feature_flags_compat(
self.api_version
):
needed_platforms.add(Platform.BINARY_SENSOR)
needed_platforms.add(Platform.SELECT)
if self.device_info:
if async_get_dashboard(hass):
# Only load the update platform if the device_info is set
# When we restore the entry, the device_info may not be set yet
# and we don't want to load the update platform since it needs
# a complete device_info.
needed_platforms.add(Platform.UPDATE)
if self.device_info.voice_assistant_feature_flags_compat(self.api_version):
needed_platforms.add(Platform.BINARY_SENSOR)
needed_platforms.add(Platform.SELECT)
ent_reg = er.async_get(hass)
registry_get_entity = ent_reg.async_get_entity_id
@@ -312,18 +315,19 @@ class RuntimeEntryData:
# Make a dict of the EntityInfo by type and send
# them to the listeners for each specific EntityInfo type
infos_by_type: dict[type[EntityInfo], list[EntityInfo]] = {}
infos_by_type: defaultdict[type[EntityInfo], list[EntityInfo]] = defaultdict(
list
)
for info in infos:
info_type = type(info)
if info_type not in infos_by_type:
infos_by_type[info_type] = []
infos_by_type[info_type].append(info)
infos_by_type[type(info)].append(info)
callbacks_by_type = self.entity_info_callbacks
for type_, entity_infos in infos_by_type.items():
if callbacks_ := callbacks_by_type.get(type_):
for callback_ in callbacks_:
callback_(entity_infos)
for type_, callbacks in self.entity_info_callbacks.items():
# If all entities for a type are removed, we
# still need to call the callbacks with an empty list
# to make sure the entities are removed.
entity_infos = infos_by_type.get(type_, [])
for callback_ in callbacks:
callback_(entity_infos)
# Finally update static info subscriptions
for callback_ in self.static_info_update_subscriptions:
+10
View File
@@ -33,6 +33,16 @@ class EsphomeEvent(EsphomeEntity[EventInfo, Event], EventEntity):
self._trigger_event(self._state.event_type)
self.async_write_ha_state()
@callback
def _on_device_update(self) -> None:
"""Call when device updates or entry data changes."""
super()._on_device_update()
if self._entry_data.available:
# Event entities should go available directly
# when the device comes online and not wait
# for the next data push.
self.async_write_ha_state()
async_setup_entry = partial(
platform_async_setup_entry,
@@ -13,6 +13,7 @@ from aioesphomeapi import (
APIConnectionError,
APIVersion,
DeviceInfo as EsphomeDeviceInfo,
EncryptionHelloAPIError,
EntityInfo,
HomeassistantServiceCall,
InvalidAuthAPIError,
@@ -570,6 +571,7 @@ class ESPHomeManager:
if isinstance(
err,
(
EncryptionHelloAPIError,
RequiresEncryptionAPIError,
InvalidEncryptionKeyAPIError,
InvalidAuthAPIError,
@@ -16,7 +16,7 @@
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
"mqtt": ["esphome/discover/#"],
"requirements": [
"aioesphomeapi==29.7.0",
"aioesphomeapi==29.8.0",
"esphome-dashboard-api==1.2.3",
"bleak-esphome==2.12.0"
],
@@ -43,6 +43,9 @@
},
"description": "The ESPHome device {name} enabled transport encryption or changed the encryption key. Please enter the updated key. You can find it in the ESPHome Dashboard or in your device configuration."
},
"reauth_encryption_removed_confirm": {
"description": "The ESPHome device {name} disabled transport encryption. Please confirm that you want to remove the encryption key and allow unencrypted connections."
},
"discovery_confirm": {
"description": "Do you want to add the ESPHome node `{name}` to Home Assistant?",
"title": "Discovered ESPHome node"
+7 -5
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from typing import Any, cast
import pyeverlights
import voluptuous as vol
@@ -84,7 +84,7 @@ class EverLightsLight(LightEntity):
api: pyeverlights.EverLights,
channel: int,
status: dict[str, Any],
effects,
effects: list[str],
) -> None:
"""Initialize the light."""
self._api = api
@@ -106,8 +106,10 @@ class EverLightsLight(LightEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
hs_color = kwargs.get(ATTR_HS_COLOR, self._attr_hs_color)
brightness = kwargs.get(ATTR_BRIGHTNESS, self._attr_brightness)
hs_color = cast(
tuple[float, float], kwargs.get(ATTR_HS_COLOR, self._attr_hs_color)
)
brightness = cast(int, kwargs.get(ATTR_BRIGHTNESS, self._attr_brightness))
effect = kwargs.get(ATTR_EFFECT)
if effect is not None:
@@ -116,7 +118,7 @@ class EverLightsLight(LightEntity):
rgb = color_int_to_rgb(colors[0])
hsv = color_util.color_RGB_to_hsv(*rgb)
hs_color = hsv[:2]
brightness = hsv[2] / 100 * 255
brightness = round(hsv[2] / 100 * 255)
else:
rgb = color_util.color_hsv_to_RGB(
+2 -2
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
from collections import namedtuple
from datetime import timedelta
import logging
from typing import Any
from typing import Any, cast
from fints.client import FinTS3PinTanClient
from fints.models import SEPAAccount
@@ -73,7 +73,7 @@ def setup_platform(
credentials = BankCredentials(
config[CONF_BIN], config[CONF_USERNAME], config[CONF_PIN], config[CONF_URL]
)
fints_name = config.get(CONF_NAME, config[CONF_BIN])
fints_name = cast(str, config.get(CONF_NAME, config[CONF_BIN]))
account_config = {
acc[CONF_ACCOUNT]: acc[CONF_NAME] for acc in config[CONF_ACCOUNTS]
+2 -2
View File
@@ -193,7 +193,6 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
translation_key="max_kb_s_sent",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=_retrieve_max_kb_s_sent_state,
),
FritzSensorEntityDescription(
@@ -201,7 +200,6 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
translation_key="max_kb_s_received",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=_retrieve_max_kb_s_received_state,
),
FritzSensorEntityDescription(
@@ -225,6 +223,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
translation_key="link_kb_s_sent",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=_retrieve_link_kb_s_sent_state,
),
FritzSensorEntityDescription(
@@ -232,6 +231,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
translation_key="link_kb_s_received",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=_retrieve_link_kb_s_received_state,
),
FritzSensorEntityDescription(
+6 -1
View File
@@ -6,6 +6,7 @@ from typing import Any
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
PRESET_BOOST,
PRESET_COMFORT,
PRESET_ECO,
ClimateEntity,
@@ -38,7 +39,7 @@ from .sensor import value_scheduled_preset
HVAC_MODES = [HVACMode.HEAT, HVACMode.OFF]
PRESET_HOLIDAY = "holiday"
PRESET_SUMMER = "summer"
PRESET_MODES = [PRESET_ECO, PRESET_COMFORT]
PRESET_MODES = [PRESET_ECO, PRESET_COMFORT, PRESET_BOOST]
SUPPORTED_FEATURES = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
@@ -194,6 +195,8 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
return PRESET_HOLIDAY
if self.data.summer_active:
return PRESET_SUMMER
if self.data.target_temperature == ON_API_TEMPERATURE:
return PRESET_BOOST
if self.data.target_temperature == self.data.comfort_temperature:
return PRESET_COMFORT
if self.data.target_temperature == self.data.eco_temperature:
@@ -211,6 +214,8 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
await self.async_set_temperature(temperature=self.data.comfort_temperature)
elif preset_mode == PRESET_ECO:
await self.async_set_temperature(temperature=self.data.eco_temperature)
elif preset_mode == PRESET_BOOST:
await self.async_set_temperature(temperature=ON_REPORT_SET_TEMPERATURE)
@property
def extra_state_attributes(self) -> ClimateExtraAttributes:
@@ -182,10 +182,10 @@
"state": {
"startup": "Startup",
"running": "Running",
"standby": "Standby",
"standby": "[%key:common::state::standby%]",
"bootloading": "Bootloading",
"error": "Error",
"idle": "Idle",
"idle": "[%key:common::state::idle%]",
"ready": "Ready",
"sleeping": "Sleeping"
}
@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250306.0"]
"requirements": ["home-assistant-frontend==20250328.0"]
}
@@ -0,0 +1 @@
"""Gaggenau virtual integration."""
@@ -0,0 +1,6 @@
{
"domain": "gaggenau",
"name": "Gaggenau",
"integration_type": "virtual",
"supported_by": "home_connect"
}
@@ -539,10 +539,14 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
return
assert self._cur_temp is not None and self._target_temp is not None
too_cold = self._target_temp >= self._cur_temp + self._cold_tolerance
too_hot = self._cur_temp >= self._target_temp + self._hot_tolerance
min_temp = self._target_temp - self._cold_tolerance
max_temp = self._target_temp + self._hot_tolerance
if self._is_device_active:
if (self.ac_mode and too_cold) or (not self.ac_mode and too_hot):
if (self.ac_mode and self._cur_temp <= min_temp) or (
not self.ac_mode and self._cur_temp >= max_temp
):
_LOGGER.debug("Turning off heater %s", self.heater_entity_id)
await self._async_heater_turn_off()
elif time is not None:
@@ -552,7 +556,9 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
self.heater_entity_id,
)
await self._async_heater_turn_on()
elif (self.ac_mode and too_hot) or (not self.ac_mode and too_cold):
elif (self.ac_mode and self._cur_temp > max_temp) or (
not self.ac_mode and self._cur_temp < min_temp
):
_LOGGER.debug("Turning on heater %s", self.heater_entity_id)
await self._async_heater_turn_on()
elif time is not None:
@@ -21,17 +21,17 @@
"heater": "Switch entity used to cool or heat depending on A/C mode.",
"target_sensor": "Temperature sensor that reflects the current temperature.",
"min_cycle_duration": "Set a minimum amount of time that the switch specified must be in its current state prior to being switched either off or on.",
"cold_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched on. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor equals or goes below 24.5.",
"cold_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched on. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor goes below 24.5.",
"hot_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched off. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will stop when the sensor equals or goes above 25.5."
}
},
"presets": {
"title": "Temperature presets",
"data": {
"away_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::away%]",
"home_temp": "[%key:common::state::home%]",
"away_temp": "[%key:common::state::not_home%]",
"comfort_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::comfort%]",
"eco_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::eco%]",
"home_temp": "[%key:common::state::home%]",
"sleep_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::sleep%]",
"activity_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::activity%]"
}
@@ -63,10 +63,10 @@
"presets": {
"title": "[%key:component::generic_thermostat::config::step::presets::title%]",
"data": {
"away_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::away%]",
"home_temp": "[%key:common::state::home%]",
"away_temp": "[%key:common::state::not_home%]",
"comfort_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::comfort%]",
"eco_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::eco%]",
"home_temp": "[%key:common::state::home%]",
"sleep_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::sleep%]",
"activity_temp": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::activity%]"
}
@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/google",
"iot_class": "cloud_polling",
"loggers": ["googleapiclient"],
"requirements": ["gcal-sync==7.0.0", "oauth2client==4.1.3", "ical==9.0.1"]
"requirements": ["gcal-sync==7.0.0", "oauth2client==4.1.3", "ical==9.0.3"]
}
@@ -8,7 +8,7 @@
"integration_type": "service",
"iot_class": "cloud_push",
"requirements": [
"google-cloud-texttospeech==2.17.2",
"google-cloud-speech==2.27.0"
"google-cloud-texttospeech==2.25.1",
"google-cloud-speech==2.31.1"
]
}
@@ -5,7 +5,7 @@ from __future__ import annotations
import mimetypes
from pathlib import Path
from google import genai # type: ignore[attr-defined]
from google.genai import Client
from google.genai.errors import APIError, ClientError
from requests.exceptions import Timeout
import voluptuous as vol
@@ -43,7 +43,7 @@ CONF_FILENAMES = "filenames"
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
PLATFORMS = (Platform.CONVERSATION,)
type GoogleGenerativeAIConfigEntry = ConfigEntry[genai.Client]
type GoogleGenerativeAIConfigEntry = ConfigEntry[Client]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
@@ -139,7 +139,11 @@ async def async_setup_entry(
"""Set up Google Generative AI Conversation from a config entry."""
try:
client = genai.Client(api_key=entry.data[CONF_API_KEY])
def _init_client() -> Client:
return Client(api_key=entry.data[CONF_API_KEY])
client = await hass.async_add_executor_job(_init_client)
await client.aio.models.get(
model=entry.options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL),
config={"http_options": {"timeout": TIMEOUT_MILLIS}},
@@ -7,7 +7,7 @@ import logging
from types import MappingProxyType
from typing import Any
from google import genai # type: ignore[attr-defined]
from google import genai
from google.genai.errors import APIError, ClientError
from requests.exceptions import Timeout
import voluptuous as vol
@@ -356,6 +356,15 @@ class GoogleGenerativeAIConversationEntity(
messages.append(_convert_content(chat_content))
# The SDK requires the first message to be a user message
# This is not the case if user used `start_conversation`
# Workaround from https://github.com/googleapis/python-genai/issues/529#issuecomment-2740964537
if messages and messages[0].role != "user":
messages.insert(
0,
Content(role="user", parts=[Part.from_text(text=" ")]),
)
if tool_results:
messages.append(_create_google_tool_response_content(tool_results))
generateContentConfig = GenerateContentConfig(
+2 -2
View File
@@ -16,13 +16,13 @@
"name": "Panel light"
},
"quiet": {
"name": "Quiet"
"name": "Quiet mode"
},
"fresh_air": {
"name": "Fresh air"
},
"xfan": {
"name": "XFan"
"name": "Xtra fan"
},
"health_mode": {
"name": "Health mode"
+2
View File
@@ -2,11 +2,13 @@
ATTR_PASSWORD = "password"
ATTR_USERNAME = "username"
ATTR_QUEUE_IDS = "queue_ids"
DOMAIN = "heos"
ENTRY_TITLE = "HEOS System"
SERVICE_GET_QUEUE = "get_queue"
SERVICE_GROUP_VOLUME_SET = "group_volume_set"
SERVICE_GROUP_VOLUME_DOWN = "group_volume_down"
SERVICE_GROUP_VOLUME_UP = "group_volume_up"
SERVICE_REMOVE_FROM_QUEUE = "remove_from_queue"
SERVICE_SIGN_IN = "sign_in"
SERVICE_SIGN_OUT = "sign_out"

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