Compare commits

..

293 Commits

Author SHA1 Message Date
G Johansson b27ef88be7 Scrape sub config entry 2025-03-25 16:08:30 +00:00
Joost Lekkerkerker 0aa09a2d51 Only keep valid powerConsumptionReports in SmartThings (#140049)
* power consumption report

* Only keep valid powerConsumptionReports in SmartThings
2025-03-07 15:04:46 +01:00
Joost Lekkerkerker 62e45e393d Fix SmartThings thermostat climate check (#140046)
* Fix SmartThings thermostat climate check

* Add tests
2025-03-07 14:56:31 +01:00
Joost Lekkerkerker eadff2938f Bump pysmartthings to 2.7.0 (#140047) 2025-03-07 14:26:43 +01:00
peteS-UK 354cd90c92 Fix Unit of Measurement for Squeezebox duration sensor entity on LMS service (#139861)
UOM Fix
2025-03-07 13:53:24 +01:00
Norbert Rittel 11348959ca Make descriptions of keymitt_ble.calibrate action UI-friendly (#139866)
* Make descriptions of `keymitt_ble.calibrate` action UI-friendly

Update the action and field descriptions to better work within the graphical UI (selector / units shown) and for translations.

* Change to "press or release" to cover the 'Invert' mode
2025-03-07 13:39:48 +01:00
Brett Adams 935890e4e0 Fix shift state default in Teslemetry and Tessie (#140018)
* Fix again

* Fix Tessie

* Update snap
2025-03-07 13:28:21 +01:00
Abílio Costa 82d5304b45 Update whirlpool-sixth-sense to 0.19.1 (#139987) 2025-03-07 13:13:35 +01:00
hahn-th 2401d8900a Add description for HomematicIP HCU1 in homematicip_cloud setup config flow (#140025)
add description for hcu1
2025-03-07 13:11:45 +01:00
David Bonnes c834944ee7 Fix evohome to gracefully handle null schedules (#140036)
* extend tests to catch null schedules

* add fixture with null schedule

* remove null schedules for now

* fic the typing for _schedule attr (is list, not dict)

* add valid schedule to fixture

* update ssetpoints only if there is a schedule

* snapshot to match last change

* refactor: dont update switchpoints if no schedule

* add in warnings for null schedules

* add fixture for DHW without schedule
2025-03-07 13:04:04 +01:00
J. Nick Koston 9a90e1e410 Bump ulid-transform to 1.4.0 (#140037)
changelog: https://github.com/Bluetooth-Devices/ulid-transform/compare/v1.3.0...v1.4.0
2025-03-07 13:01:31 +01:00
Joost Lekkerkerker 73ef240921 Fix SmartThings disabling working capabilities (#140039) 2025-03-07 12:55:32 +01:00
J. Nick Koston 2985f08054 Bump dbus-fast to 2.39.3 (#140015)
* Bump dbus-fast to 2.39.2

changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v2.37.0...v2.39.2

* bump again for more fixes
2025-03-07 12:56:00 +02:00
Jan-Philipp Benecke 8780bc99eb Set content length when uploading files to WebDAV (#139950) 2025-03-07 10:44:17 +01:00
David Bonnes 452fbbe61c Fix regression to evohome debug logging (#140000)
* fix regression in debug logging

* lint
2025-03-07 09:12:21 +00:00
Brett Adams 6be8370eb3 Fix powerwall 0% in Tessie and Tesla Fleet (#140017)
Fix powerwall zero
2025-03-07 08:45:25 +01:00
Martin Hjelmare 9682d3b313 Bump aiohomeconnect to 0.16.3 (#140014) 2025-03-06 20:50:34 -10:00
Paulus Schoutsen d47481a30e Track when an LLM expects to continue a conversation (#139810)
* Track when an LLM expects to continue a conversation

* Strip content

* Address comments
2025-03-06 22:52:29 -05:00
J. Diego Rodríguez Royo 3dd1fadc7d Check operation state on Home Connect program sensor update (#140011)
Check operation state on program sensor update
2025-03-07 01:50:06 +01:00
Jan-Philipp Benecke fd1044dcba Bump aiowebdav2 to 0.4.1 (#139988) 2025-03-06 22:06:47 +00:00
Ivan Lopez Hernandez 2aa584ce39 Correctly retrieve only loaded Google Generative AI config_entries (#139999)
* Correctly retrieve only loaded config_entries

* Ruff
2025-03-06 13:17:33 -08:00
J. Nick Koston e78139edf1 Bump nexia to 2.2.2 (#139986)
changelog: https://github.com/bdraco/nexia/compare/2.2.1...2.2.2
2025-03-06 10:10:07 -10:00
Allen Porter eb49e596f9 Add a roborock quality_scale.yaml (#139849)
* Add a roborock quality_scale.yaml

* Update wording in polling

* Update event listening

* Update quality scale based on feedback
2025-03-06 19:00:40 +00:00
Markus Adrario eaad8ec49d Add Homee select platform (#139534)
* homee select initial

* Finish select tests

* Add motor rotation

* fix snapshot after translation compilation

* string improvement

* last fixes

* fix review comments

* remove restore last known state

* readd wind monitoring state

* fix strings

* remove problematic selects

* remove motor rotation from strings

* fix review comments

* Update tests/components/homee/test_select.py

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

* add PARALLEL_UPDATES

* parallel updates for select, not light.

---------

Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-03-06 18:56:17 +00:00
Jan Bouwhuis 99e1a7a676 Check if the unit of measurement is valid before creating the entity (#139932) 2025-03-06 18:52:46 +01:00
Martin Hjelmare 4ff2309a90 Use mysensors config entry async_on_unload (#139978)
* Use config entry on unload in mysensors

* Test mysensors config entry load and unload

* Fix docstring
2025-03-06 18:50:47 +01:00
Joost Lekkerkerker 4bafdf5e4b Add config entry level diagnostics to SmartThings (#139939)
* Add config entry level diagnostics to SmartThings

* Add config entry level diagnostics to SmartThings

* Add config entry level diagnostics to SmartThings
2025-03-06 18:48:39 +01:00
Joost Lekkerkerker f38a32477e Fix SmartThings fan (#139962) 2025-03-06 18:47:37 +01:00
Luke Lashley 59073d47a1 Bump to python-snoo 0.6.1 (#139954) 2025-03-06 18:44:13 +01:00
Regev Brody df1563daaf Add Roborock buttons for starting routines (#139845) 2025-03-06 17:18:37 +00:00
Bram Kragten 93dfbb4166 Update frontend to 20250306.0 (#139965) 2025-03-06 17:52:45 +01:00
Joost Lekkerkerker 9549b1488e Fix SmartThings dust sensor UoM (#139977) 2025-03-06 17:52:05 +01:00
Erik Montnemery 6ba45a32c0 Update typing of BackupAgent.async_get_backup (#139923)
* Update typing of BackupAgent.async_get_backup

* Remove manual reset of frame helper
2025-03-06 17:25:34 +01:00
Artur Pragacz 88f18fdfdc Improve loader dependency tests (#139916) 2025-03-06 15:20:08 +01:00
epenet 377e0a64d1 Reset helpers.frame._REPORTED_INTEGRATIONS in between tests (#139924)
* Reset helpers.frame._REPORTED_INTEGRATIONS in between tests

* Rename

* Apply suggestions from code review

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

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-03-06 13:57:13 +01:00
epenet 1a4a3a0f08 Use runtime_data in forked_daapd (#138284)
* Use runtime_data in forked_daapd

* Adjust
2025-03-06 13:42:35 +01:00
Ishima 485da61d3c Check support for demand load control in SmartThings AC (#139616)
* Check support for demand load control in SmartThings AC

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-03-06 13:42:23 +01:00
Jan-Philipp Benecke 5d7b60e4c8 Bump aiowebdav2 to 0.4.0 (#139938) 2025-03-06 13:30:02 +01:00
marc7s 5d8e03c124 Update geocachingapi to v0.3.0 (#139878)
Bump Geocaching API version

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-06 13:29:30 +01:00
Joost Lekkerkerker edc763b7d2 Bump pysmartthings to 2.6.1 (#139936)
* Bump pysmartthings to 2.6.1

* Bump pysmartthings to 2.6.1
2025-03-06 13:22:49 +01:00
Erik Montnemery c51e644203 Prioritize integration_domain passed to helper.frame.report_usage (#139819)
* Prioritize integration_domain passed to helper.frame.report_usage

* Update tests

* Update tests

* Improve docstring

* Rename according to suggestion
2025-03-06 13:16:50 +01:00
Franck Nijhof dc4464a347 Merge branch 'master' into dev 2025-03-06 12:10:27 +00:00
Marc Mueller 47919fe7e9 Simplify lint-only config (2) [ci] (#139933) 2025-03-06 12:56:46 +01:00
Martin Hjelmare 6455daf092 Set Ondilo ICO diagnostic sensors (#139934) 2025-03-06 12:30:42 +01:00
Norbert Rittel e06af94a1a Improve description of tibber.get_prices action (#139863)
Replace with the description from the online docs which add the information that a price level is included.

This also makes it consistent with the standard descriptive style in Home Assistant.
2025-03-06 12:22:36 +01:00
Joost Lekkerkerker 052eed6bb3 Deduplicate climate modes in SmartThings (#139930)
* Deduplicate climate modes in SmartThings

* Deduplicate climate modes in SmartThings
2025-03-06 12:20:53 +01:00
Markus Adrario 095b04caf9 Homee parallel updates (#139926)
* set parallel updates to 0

* add platforms
2025-03-06 12:20:22 +01:00
epenet 83dd1af6d2 Drop report method from frame helper (#139920)
* Drop report method from frame helper

* Adjust test_prevent_flooding

* Adjust test_report_missing_integration_frame

* Adjust test_report_error_if_integration

* Remove test_report
2025-03-06 11:25:22 +01:00
J. Nick Koston 8bfffcbd29 Bump thermobeacon-ble to 0.8.1 (#139919)
changelog: https://github.com/Bluetooth-Devices/thermobeacon-ble/compare/v0.8.0...v0.8.1

fixes #139917
2025-03-06 11:24:56 +01:00
Manu f2b07ea886 Add support for IronOS v2.23 (#139903)
Add support for IronOS 2.23
2025-03-06 11:23:10 +01:00
Norbert Rittel 4f255439eb Fix sentence-casing in music_assistant.get_library action (#139901)
- make the casing of several words consistent
- make the action's description consistent with HA style using "Retrieves …" instead of "Get …"
2025-03-06 11:11:22 +01:00
Avi Miller b44c26d324 Bump aiolifx to 1.1.4 to enable new LIFX product support (#139897)
Signed-off-by: Avi Miller <me@dje.li>
2025-03-06 11:10:49 +01:00
dependabot[bot] 46f4bc3434 Bump actions/attest-build-provenance from 2.2.2 to 2.2.3 (#139896)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/bd77c077858b8d561b7a36cbe48ef4cc642ca39d...c074443f1aee8d4aeeae555aebba3282517141b2)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 10:02:16 +01:00
J. Nick Koston b280874dc0 Small cleanups for HomeKit (#139889)
* Small cleanups for HomeKit

- Add some missing typing
- Break out some duplicate code

* Small cleanups for HomeKit

- Add some missing typing
- Break out some duplicate code
2025-03-05 22:54:48 -10:00
Petro31 aec6868af1 Add abstract class to trigger based template entities (#139650)
* add abstract class to trigger based template entities

* updates after merge of parent PR

* add comments

* add tests
2025-03-06 08:00:11 +01:00
Manu 48865e00b6 Bump pynecil to v4.1.0 (#139881) 2025-03-06 07:42:09 +01:00
J. Nick Koston a5002018e0 Bump dbus-fast to 2.37.0 (#139877) 2025-03-06 07:38:23 +01:00
Ivan Lopez Hernandez 8e35783164 Trim the Schema allowed keys to match the Public Gemini API docs. (#139876)
* Trim the Schema allowed types to match the Public API docs, not the SDK types as those do not match

* Testing
2025-03-05 18:34:11 -08:00
Norbert Rittel f8e3f2a94f Improve descriptions in overseerr.get_requests action (#139781)
Make the action description consistent with HA style adding a bit more info from the online docs.

Fix spelling of "ID"
2025-03-05 23:00:12 +00:00
pglab-electronics cc30823726 Reimplement PGLab sensor to use a coordinator (#139789)
* Reimplement PGLab sensor to use a coordinator

* fix spelling mistake on coordinator name

* rename createDiscoverDeviceInfo function in snake_case

* adding suffix pglab_ to PGLabBaseEntity/PGLabEntity constructor parameters

* Fix docs of PGLabEntity::async_added_to_hass

* make coordinator able to return the sensor native value

* renaming PGLABConfigEntry in PGLabConfigEntry to be consistent with the integration naming

* renamed entry function arguments to config_entry to be less confusing

* pass config_entry to constructor of base class of PGLabSensorsCoordinator

* set the return value type of get_sensor_value

* store coordinator as regular instance attribute

* Avoid to access directly entity from discovery module

* Rearrange get_sensor_value return types
2025-03-05 20:33:59 +01:00
Franck Nijhof 97cc3984c5 2025.3.0 (#139859)
* Add vesync debug mode in library (#134571)

* Debug mode pass through

* Correct code, shouldn't have been lambda

* listener for change

* ruff

* Update manifest.json

* Reflect correct logger title

* Ruff fix from merge

* Fix return value for DataUpdateCoordinator._async setup (#139181)

Fix return value for coodinator async setup

* Fix race in WS command recorder/info (#139177)

* Fix race in WS command recorder/info

* Add comment

* Remove unnecessary local import

* Bump aiohttp to 3.11.13 (#139197)

changelog: https://github.com/aio-libs/aiohttp/compare/v3.11.12...v3.11.13

* Update Linkplay constants for Arylic S10+ and Arylic Up2Stream Amp 2.1 (#138198)

* Add support for Apps and Radios to Squeezebox Media Browser (#135009)

* Add azure_storage as backup agent (#134085)

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

* Bump onedrive quality scale to platinum (#137451)

* Bump pyloadapi to v1.4.2 (#139140)

* Add missing translations to switchbot (#139212)

* Fix bug in check_translations fixture (#139206)

* Fix bug in check_translations fixture

* Fix check for ignored translation errors

* Fix websocket_api test

* Add missing exception translation to Home Connect (#139218)

Add missing exception translation

* Configure trusted publishing for PyPI file upload (#137607)

* Bump aiostreammagic to 2.11.0 (#139213)

* Add missing exception translation to Home Connect (#139223)

* Bump ohmepy to 1.3.2 (#139013)

* Fix kitchen_sink statistic issues (#139228)

* Bump aiowebdav2 to 0.3.0 (#139202)

* Bump pylamarzocco to 1.4.7 (#139231)

* Add backup helper (#139199)

* Add backup helper

* Add hassio to stage 1

* Apply same changes to newly merged `webdav` and `azure_storage` to fix inflight conflict

* Address comments, add tests

---------

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

* Reduce requests made by webdav (#139238)

* Reduce requests made by webdav

* Update homeassistant/components/webdav/backup.py

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

---------

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

* Add Homee valve platform (#139188)

* Fix units for LCN sensor (#138940)

* Add Ohme voltage and slot list sensor (#139203)

* Bump ohmepy to 1.3.1

* Bump ohmepy to 1.3.2

* Add voltage and slot list sensor

* CI fixes

* Change slot list sensor name

* Fix snapshot tests

* Initiate source list as instance variable in Volumio (#139243)

* `logbook.log` action: Make description of  `name` field UI-friendly (#139200)

* Treat "Twist Assist" & "Block to Block" as feature names and add descriptions in Z-Wave (#139239)

Treat "Twist Assist" & "Block to Block" as feature names and add descriptions

- name-case both "Twist Assist" and "Block to Block" so those feature names don't get translated
- for proper translation of both features add useful descriptions of what they actually do
- fix sentence-casing on "Operation type"

* Add climate's swing mode to LG ThinQ (#137619)

Co-authored-by: yunseon.park <yunseon.park@lge.com>

* Bump aiowithings to 3.1.6 (#139242)

* Add update reward action to Habitica integration (#139157)

* Add Re-Auth Flow to vesync (#137398)

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

* Rework the velbus configflow to make it more user-friendly (#135609)

* Add missing ATTR_HVAC_MODE of async_set_temperature to LG ThinQ (#137621)

Co-authored-by: yunseon.park <yunseon.park@lge.com>

* Make Radarr units translatable (#139250)

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

* Improve Minecraft Server config flow tests (#139251)

* Revert "Bump Stookwijzer to 1.5.7" (#139253)

* Add parallel updates to Home Connect (#139255)

* Bump fnv-hash-fast to 1.2.6 (#139246)

* Make default dim level configurable in Lutron (#137127)

* Set PARALLEL_UPDATES in all Minecraft Server platforms (#139259)

* Bump aiowebostv to 0.7.1 (#139244)

* Consistently capitalize "Velbus" brand name, camel-case "VelServ" (#139257)

* Bump cached-ipaddress to 0.9.2 (#139245)

* Make Sonarr component's units translatable (#139254)

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

* Bump stookwijzer to 1.5.8 (#139258)

* Bump Velbus to bronze quality scale (#139256)

* Add Homee number platform (#138962)

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

* Fix yolink lock v2 state update (#138710)

* Set Minecraft Server quality scale to silver (#139265)

* Add OpenWeatherMap Minute forecast action (#128799)

* Fix Ezviz entity state for cameras that are offline (#136003)

* Use proper camel-case for "VeSync", fix sentence-casing in title (#139252)

Just a quick follow-up PR to fix these two spelling mistakes.

* Add request made by `rest_command` to debug log (#139266)

* Create repair for configured unavailable backup agents (#137382)

* Create repair for configured not loaded agents

* Rework to repair issue

* Extract logic to config function

* Update test

* Handle empty agend ids config update

* Address review comment

* Update tests

* Address comment

* Improve description of `openweathermap.get_minute_forecast` action (#139267)

* Use right import in ezviz (#139272)

* Change touchline dependency to pytouchline_extended (#136362)

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

* Rename description field to notes in Habitica action (#139271)

* Add support for effects in Govee lights (#137846)

* Add Burbank Water and Power (BWP) virtual integration (#139027)

* Update adext to 0.4.4 (#139151)

* Add sound mode support to Onkyo (#133531)

* Use new python library for picnic component (#139111)

* Bump securetar to 2025.2.1 (#139273)

* Fix race in async_get_integrations with multiple calls when an integration is not found (#139270)

* Fix race in async_get_integrations with multiple calls when an integration is not found

* Fix race in async_get_integrations with multiple calls when an integration is not found

* Fix race in async_get_integrations with multiple calls when an integration is not found

* tweaks

* tweaks

* tweaks

* restore lost comment

* tweak test

* comment cache

* improve test

* improve comment

* Bump python-overseerr to 0.7.1 (#139263)

Co-authored-by: Shay Levy <levyshay1@gmail.com>

* Add coordinator to SMHI (#139052)

* Add coordinator to SMHI

* Remove not needed logging

* docstrings

* Make Radarr unit translation lowercase (#139261)

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

* Add common state translation string for charging and discharging (#139074)

add common state translation string for charging and discharging

* Add test fixture ignore_translations_for_mock_domains (#139235)

* Add test fixture ignore_translations_for_mock_domains

* Fix fixture

* Avoid unnecessary attempt to get integration

* Really fix fixture

* Add forgotten parameter

* Address review comment

* Fix grammar in loader comments (#139276)

https://github.com/home-assistant/core/pull/139270#discussion_r1970315129

* Bump aiohomeconnect to 0.15.0 (#139277)

* Add current cavity temperature sensor to Home Connect (#139282)

* Bump anthropic to 0.47.2 (#139283)

* Adjust recorder validate_statistics handler (#139229)

* Fix re-connect logic in Apple TV integration (#139289)

* Revert "Bump stookwijzer==1.5.8" (#139287)

* Add option to ESPHome to subscribe to logs (#139073)

* Remove not used constants in smhi (#139298)

* Bump `aioshelly` to version `13.0.0` (#139294)

* Bump aioshelly to version 13.0.0

* MODEL_BLU_GATEWAY_GEN3 -> MODEL_BLU_GATEWAY_G3

* Remove timeout from vscode test launch configuration (#139288)

* Add missing Home Connect context at event listener registration for appliance options (#139292)

* Add missing context at event listener registration for appliance options

* Add tests

* Sort common translation strings (#139300)

sort common strings

* Add album artist media browser category to Squeezebox (#139210)

* Bump aioesphomeapi to 29.2.0 (#139309)

* Bump actions/download-artifact from 4.1.8 to 4.1.9 (#139317)

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.8 to 4.1.9.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4.1.8...v4.1.9)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump home-assistant/builder from 2024.08.2 to 2025.02.0 (#139316)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Adjust remote ESPHome log subscription level on logging change (#139308)

* Fix homeassistant/expose_entity/list (#138872)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* Bump `accuweather` to version `4.1.0` (#139320)

* Bump ZHA to 0.0.50 (#139318)

* Bump pytechnove to 2.0.0 (#139314)

* Update python-smarttub dependency to 0.0.39 (#139313)

* Fix anthropic blocking call (#139299)

* Bump pybotvac to 0.0.26 (#139330)

* Bump stookwijzer==1.6.0 (#139332)

* Improve error message when failing to create backups (#139262)

* Improve error message when failing to create backups

* Check for expected error message in tests

* Add translations and icon for Twinkly select entity (#139336)

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

* Bump recommended ESPHome Bluetooth proxy version to 2025.2.1 (#139196)

* Add default_db_url flag to WS command recorder/info (#139333)

* Improve action descriptions of LIFX integration (#139329)

Improve action description of lifx integration

- fix sentence-casing on two action names
- change "Kelvin" unit name to proper uppercase
- reference 'Theme' and 'Palette' fields by their friendly names for matching translations
- change paint_theme action description to match HA style

* Bump Music Assistant client to 1.1.1 (#139331)

* Refactor SmartThings (#137940)

* Add keys initiate_flow and entry_type to data entry translations (#138882)

* Add support for swing horizontal mode for mqtt climate (#139303)

* Add support for swing horizontal mode for mqtt climate

* Fix import

* Add entity translations to SmartThings (#139342)

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* Refactor SmartThings

* fix

* fix

* Add AC tests

* Add thermostat tests

* Add cover tests

* Add device tests

* Add light tests

* Add rest of the tests

* Add oauth

* Add oauth tests

* Add oauth tests

* Add oauth tests

* Add oauth tests

* Bump version

* Add rest of the tests

* Finalize

* Finalize

* Finalize

* Finalize

* Finalize

* Finalize

* Finalize

* Finalize

* Finalize

* Finalize

* Iterate over entities instead

* use set

* use const

* uncomment

* fix handler

* Fix device info

* Fix device info

* Fix lib

* Fix lib

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix

* Fix

* Add fake fan

* Fix

* Add entity translations to SmartThings

* Fix

* Improve logging for selected options in Onkyo (#139279)

Different error for not selected option

* Change no fixtures comment in SmartThings (#139344)

* Set options for carbon monoxide detector sensor in SmartThings (#139346)

* Improve calculating supported features in template light (#139339)

* Update frontend to 20250226.0 (#139340)

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

* Use particulate matter device class in SmartThings (#139351)

Use particule matter device class in SmartThings

* Set options for dishwasher job state sensor in SmartThings (#139349)

* Set options for dishwasher machine state sensor in SmartThings (#139347)

* Set options for dishwasher machine state sensor in SmartThings

* Fix

* Set options for alarm sensor in SmartThings (#139345)

* Set options for alarm sensor in SmartThings

* Set options for alarm sensor in SmartThings

* Fix

* Fix variable scopes in scripts (#138883)

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

* Add translatable states to SmartThings media source input (#139353)

Add translatable states to media source input

* Add translatable states to SmartThings media playback (#139354)

Add translatable states to media playback

* Add translatable states to oven mode in SmartThings (#139356)

* Add translatable states to oven job state in SmartThings (#139361)

* Add translatable states to oven machine state (#139358)

* Add translatable states to robot cleaner movement in SmartThings (#139363)

* Add translatable states to robot cleaner cleaning mode in SmartThings (#139362)

* Add translatable states to robot cleaner cleaning mode in SmartThings

* Update homeassistant/components/smartthings/strings.json

* Update homeassistant/components/smartthings/strings.json

---------

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

* Add translatable states to washer machine state in SmartThings (#139366)

* Add translatable states to smoke detector in SmartThings (#139365)

* Add translatable states to robot cleaner turbo mode in SmartThings (#139364)

* Add translatable states to washer job state in SmartThings (#139368)

* Add translatable states to washer job state in SmartThings

* fix

* Update homeassistant/components/smartthings/sensor.py

* Improve Home Connect oven cavity temperature sensor (#139355)

* Improve oven cavity temperature translation

* Fetch cavity temperature unit

* Handle generic Home Connect error

* Improve test clarity

* Add translatable states to dryer machine state in Smartthings (#139369)

* Add translatable states to dryer job state in SmartThings (#139370)

* Add translatable states to washer job state in SmartThings

* Add translatable states to dryer job state in Smartthings

* fix

* fix

* Don't create entities for disabled capabilities in SmartThings (#139343)

* Don't create entities for disabled capabilities in SmartThings

* Fix

* fix

* fix

* Fix typo in SmartThing string (#139373)

* Bump version to 2025.3.0b0

* Bump stookwijzer==1.6.1 (#139380)

* Bump ZHA to 0.0.51 (#139383)

* Bump ZHA to 0.0.51

* Fix unit tests not accounting for primary entities

* Bump intents to 2025.2.26 (#139387)

* Fix fetch options error for Home connect (#139392)

* Handle errors when obtaining options definitions

* Don't fetch program options if the program key is unknown

* Test to ensure that available program endpoint is not called on unknown program

* Bump onedrive to 0.0.12 (#139410)

* Bump onedrive to 0.0.12

* Add alternative name

* Bump pysmartthings to 2.0.0 (#139418)

* Bump pysmartthings to 2.0.0

* Fix

* Fix

* Fix

* Fix

* Bump habluetooth to 3.24.1 (#139420)

* Fix conversation agent fallback (#139421)

* Add diagnostics to SmartThings (#139423)

* Bump bleak-esphome to 2.8.0 (#139426)

* Bump reolink-aio to 0.12.1 (#139427)

* Fix Music Assistant media player entity features (#139428)

* Fix Music Assistant supported media player features

* Update supported features when player config changes

* Add tests

* Update frontend to 20250227.0 (#139437)

* Bump version to 2025.3.0b1

* Bump weatherflow4py to 1.3.1 (#135529)

* version bump of dep

* update requirements

* Add new mediatypes to Music Assistant integration (#139338)

* Bump Music Assistant client to 1.1.0

* Add some casts to help mypy

* Add handling of the new media types in Music Assistant

* mypy cleanup

* lint

* update snapshot

* Adjust tests

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>

* Move climate intent to homeassistant integration (#139371)

* Move climate intent to homeassistant integration

* Move get temperature intent to intent integration

* Clean up old test

* Bump aiohomeconnect to 0.15.1 (#139445)

* Fix SmartThings diagnostics (#139447)

* Bump pysmartthings to 2.0.1 (#139454)

* Change webdav namespace to absolut URI (#139456)

* Change webdav namespace to absolut URI

* Add const file

* Improve onedrive migration (#139458)

* Bump pysmartthings to 2.1.0 (#139460)

* Only lowercase SmartThings media input source if we have it (#139468)

* Set SmartThings suggested display precision (#139470)

* Fix Gemini Schema validation for #139416 (#139478)

Fixed Schema validation for issue #139477

* Fail recorder.backup.async_pre_backup if Home Assistant is not running (#139491)

Fail recorder.backup.async_pre_backup if hass is not running

* Fix shift state in Teslemetry (#139505)

* Fix shift state

* Different fix

* Improve error handling in CoreBackupReaderWriter (#139508)

* Add diagnostics to onedrive (#139516)

* Add diagnostics to onedrive

* redact PII

* add raw data

* Make the Tuya backend library compatible with the newer paho mqtt client. (#139518)

* Make the Tuya backend library compatible with the newer paho mqtt client.

* Improve classnames and docstrings

* Suppress unsupported event 'EVT_USP_RpsPowerDeniedByPsuOverload' by bumping aiounifi to v83 (#139519)

Bump aiounifi to v83

* Don't split wheels builder anymore (#139522)

* Bump yt-dlp to 2025.02.19 (#139526)

* Update frontend to 20250228.0 (#139531)

* Bump version to 2025.3.0b2

* Add missing 'state_class' attribute for Growatt plant sensors (#132145)

* Add missing 'state_class' attribute for Growatt plant sensors

* Update total.py

* Update total.py 'TOTAL_INCREASING'

* Update total.py "maximum_output" -> 'TOTAL_INCREASING'

* Update homeassistant/components/growatt_server/sensor/total.py

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Bump env_canada to 0.8.0 (#138237)

* Bump env_canada to 0.8.0

* Fix requirements*.txt

* Grepped more

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>

* Fix Nederlandse Spoorwegen to ignore trains in the past (#138331)

* Update NS integration to show first next train instead of just the first.

* Handle no first or next trip.

* Remove debug statement.

* Remove seconds and revert back to minutes.

* Make use of dt_util.now().

* Fix issue with next train if no first train.

* Use multiple indexed group-by queries to get start time states for MySQL (#138786)

* tweaks

* mysql

* mysql

* Update homeassistant/components/recorder/history/modern.py

* Update homeassistant/components/recorder/history/modern.py

* Update homeassistant/components/recorder/const.py

* Update homeassistant/components/recorder/statistics.py

* Apply suggestions from code review

* mysql

* mysql

* cover

* make sure db is fully init on old schema

* fixes

* fixes

* coverage

* coverage

* coverage

* s/slow_dependant_subquery/slow_dependent_subquery/g

* reword

* comment that callers are responsible for staying under the limit

* comment that callers are responsible for staying under the limit

* switch to kwargs

* reduce branching complexity

* split stats query

* preen

* split tests

* split tests

* Specify recorder as after dependency in sql integration (#139037)

* Specify recorder as after dependency in sql integration

* Remove hassfest exception

---------

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

* Handle IPv6 URLs in devolo Home Network (#139191)

* Handle IPv6 URLs in devolo Home Network

* Use yarl

* Fix bug in derivative sensor when source sensor's state is constant (#139230)

Previously, when the source sensor's state remains constant, the derivative
sensor repeats its latest value indefinitely.

This patch fixes this bug by consuming the state_reported event and updating
the sensor's output even when the source sensor doesn't change its state.

* Ensure Hue bridge is added first to the device registry (#139438)

* Fix update data for multiple Gree devices (#139469)

fix sync date for multiple devices

do not use handler for explicit update devices as internal communication lib do not provide which device is updated
use ha update loop

copy data object to prevent rewrite data from internal lib

allow more time to process response before log warning about long wait for response and make log message more clear

* Use last event as color mode in SmartThings (#139473)

* Use last event as color mode in SmartThings

* Use last event as color mode in SmartThings

* Fix

* Set SmartThings delta energy to Total (#139474)

* Fix alert not respecting can_acknowledge setting (#139483)

* fix(alert): check can_ack prior to acking

* fix(alert): add test for when can_acknowledge=False

* fix(alert): warn on can_ack blocking an ack

* Raise error when trying to acknowledge alert with can_acknowledge set to False

* Rewrite can_ack check as guard

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Make can_ack service error msg human readable because it will show up in the UI

* format with ruff

* Make pytest aware of service error when acking an unackable alert

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Bump pysmartthings to 2.2.0 (#139539)

* Remove orphan devices on startup in SmartThings (#139541)

* Bump PySwitchBot to 0.56.1 (#139544)

changelog: https://github.com/sblibs/pySwitchbot/compare/0.56.0...0.56.1

* Bump pysmartthings to 2.3.0 (#139546)

* Improve SmartThings OCF device info (#139547)

* Add SmartThings Viper device info (#139548)

* Revert polling changes to HomeKit Controller (#139550)

This reverts #116200

We changed the polling logic to avoid polling if all chars are marked as watchable
to avoid crashing the firmware on a very limited set of devices as it was
more in line with what iOS does. In the end, the user ended up replacing
the device in #116143 because it turned out to be unreliable in other
ways. The vendor has since issued a firmware update that may resolve
the problem with all of these devices.

In practice it turns out many more devices
report that chars are evented and never send events. After a few months
of data and reports the trade-off does not seem worth it since
users are having to set up manual polling on a wide range of
devices. The amount of devices with evented chars that do not
actually send state vastly exceeds the number of devices that
might crash if they are polled too often so restore the previous
behavior

fixes #138561
fixes #100331
fixes #124529
fixes #123456
fixes #130763
fixes #124099
fixes #124916
fixes #135434
fixes #125273
fixes #124099
fixes #119617

* Bump pysmartthings to 2.4.0 (#139564)

* Bump Tesla Fleet API to v0.9.12 (#139565)

* bump

* Update manifest.json

* Fix versions

* remove tesla_bluetooth

* Remove mistake

* Bump aiowebdav2 to 0.3.1 (#139567)

* Validate scopes in SmartThings config flow (#139569)

* Only determine SmartThings swing modes if we support it (#139571)

Only determine swing modes if we support it

* Don't require not needed scopes in SmartThings (#139576)

* Don't require not needed scopes

* Don't require not needed scopes

* Homee: fix watchdog icon (#139577)

fix watchdog icon

* Bump aiohomekit to 3.2.8 (#139579)

changelog: https://github.com/Jc2k/aiohomekit/compare/3.2.7...3.2.8

* Fix duplicate unique id issue in Sensibo (#139582)

* Fix duplicate unique id issue in Sensibo

* Fixes

* Mods

* Improve field descriptions of `zha.permit` action (#139584)

Make the field descriptions of `source_ieee` and `install_code` UI-friendly by cross-referencing them using their friendly names to allow matching translations.

Better explain the alternative of using the `qr_code` field by adding that this contains both the IEEE address and the Install code of the joining device.

* Fix - Allow brightness only light MQTT json light to be set up using the `brightness` flag or via  `supported_color_modes` (#139585)

* Fix - Allow brightness only light MQTT json light to be set up using the `brightness` flag or via  `supported_color_modes`

* Improve comment

* Fix Manufacturer naming for Squeezelite model name for Squeezebox (#139586)

Squeezelite Manufacturer Fix

* Bump deebot-client to 12.3.1 (#139598)

* Fix handling of NaN float values for current humidity in ESPHome (#139600)

fixes #131837

* Bump aioshelly to 13.1.0 (#139601)

Co-authored-by: Franck Nijhof <git@frenck.dev>

* Bump inkbird-ble to 0.7.1 (#139603)

changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.7.0...v0.7.1

* Fix body text of imap message not available in custom event data template (#139609)

* Fix arm vacation mode showing as armed away in elkm1 (#139613)

Add native arm vacation mode support to elkm1

Vacation mode is currently implemented as a custom
service which will be deprecated in a future PR.

Note that the custom service was added long before
HA had a native vacation mode which was added
in #45980

* Still request scopes in SmartThings (#139626)

Still request scopes

* Bump pysmartthings to 2.4.1 (#139627)

* Bump version to 2025.3.0b3

* Fix unique identifiers where multiple IKEA Tradfri gateways are in use (#136060)

* Create unique identifiers where multiple gateways are in use

Resolving issue https://github.com/home-assistant/core/issues/134497

* Added migration function to __init__.py

Added migration function to execute upon initialisation, to:
a) remove the erroneously-added config)_entry added to the device (gateway B gets added as a config_entry to a device associated to gateway A), and
b) swap out the non-unique identifiers for genuinely unique identifiers.

* Added tests to simulate migration from bad data scenario (i.e. explicitly executing migrate_entity_unique_ids() from __init__.py)

* Ammendments suggested in first review

* Changes after second review

* Rewrite of test_migrate_config_entry_and_identifiers after feedback

* Converted migrate function into major version, updated tests

* Finalised variable naming convention per feedback, added test to validate config entry migrated to v2

* Hopefully final changes for cosmetic / comment stucture

* Further code-coverage in test_migrate_config_entry_and_identifiers()

* Minor test corrections

* Added test for non-tradfri identifiers

* Fix vicare exception for specific ventilation device type (#138343)

* fix for exception for specific ventilation device type + tests

* fix for exception for specific ventilation device type + tests

* New Testset just for fan

* update test_sensor.ambr

* Prevent zero interval in Calendar get_events service (#139378)

* Prevent zero interval in Calendar get_events service

* Fix holiday calendar tests

* Remove redundant entity_id

* Use translation for exception

* Replace check with voluptuous validator

* Revert strings.xml

* Fix Homee brightness sensors reporting in percent (#139409)

* fix brigtness sensor having percent as unit.

* add test for percent-brightness-sensor

* remove valve position and update tests

* Removed test, because covered by Snapshots

* fix review comments

* move device calss to init.

* fix test

* fix review comments

* add battery sensor back to test fixture

* fix

* Fix ability to remove orphan device in Music Assistant integration (#139431)

* Fix ability to remove orphan device in Music Assistant integration

* Add test

* Remove orphaned device entries at startup as well

* adjust mocked client

* Fix broken link in ESPHome BLE repair (#139639)

ESPHome always uses .0 in the URL for the changelog,
and we never had a patch version in the stable
BLE version field so we need to switch it to
.0 for the URL.

* Fix scope comparison in SmartThings (#139652)

* Avoid duplicate chat log content (#139679)

* Add additional roborock debug logging (#139680)

* Improve failure handling and logging for invalid map responses (#139681)

* Abort SmartThings flow if default_config is not enabled (#139700)

* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled

* Bump ESPHome stable BLE version to 2025.2.2 (#139704)

ensure proxies have https://github.com/esphome/esphome/pull/8328
so they do not reboot themselves if disconnecting takes
too long

* Bump holidays to 0.68 (#139711)

* Bump aiowebostv to 0.7.2 (#139712)

* Bump sense-energy to 0.13.6 (#139714)

changes: https://github.com/scottbonline/sense/releases/tag/0.13.6

* Add nest translation string for `already_in_progress` (#139727)

* Bump google-nest-sdm to 7.1.4 (#139728)

* Delete refresh after a non-breaking error at event stream at Home Connect (#139740)

* Delete refresh after non-breaking error

And improve how many time does it take to retry to open stream

* Update tests

* Bump version to 2025.3.0b4

* Bump aiohomeconnect to 0.16.2 (#139750)

* Add Apollo Automation virtual integration (#139751)

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

* Fix incorrect weather state returned by HKO (#139757)

* Fix incorrect weather state

* Clean up unused import

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Franck Nijhof <git@frenck.dev>

* Bump pysmartthings to 2.5.0 (#139758)

* Bump pysmartthings to 2.5.0

* Bump pysmartthings to 2.5.0

* Fix home connect available (#139760)

* Fix home connect available

* Extend and clarify test

* Do not change connected state on stream interrupted

* Bump nexia to 2.1.1 (#139772)

changelog: https://github.com/bdraco/nexia/compare/2.0.9...2.1.1

fixes #133368

* Bump version to 2025.3.0b5

* Bump aiowebostv to 0.7.3 (#139788)

* Drop BETA postfix from Matter integration's title (#139816)

Drop BETA postfix from Matter title

Now that the whole Matter stack of Home Assistant is officially certified, we can drop the beta flag.

* Split the energy and data retrieval in WeHeat (#139211)

* Split the energy and data logs

* Make sure that pump_info name is set to device name, bump weheat

* Adding config entry

* Fixed circular import

* parallelisation of awaits

* Update homeassistant/components/weheat/binary_sensor.py

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

* Fix undefined weheatdata

---------

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

* Bump version to 2025.3.0b6

* Update frontend to 20250305.0 (#139829)

* Bump version to 2025.3.0b7

* Get temperature data appropriate for hass.config.unit in LG ThinQ (#137626)

* Get temperature data appropriate for hass.config.unit

* Modify temperature_unit for init

* Modify unit's map

* Fix ruff error

---------

Co-authored-by: yunseon.park <yunseon.park@lge.com>

* Bump nexia to 2.2.1 (#139786)

* Bump nexia to 2.2.0

changelog: https://github.com/bdraco/nexia/compare/2.1.1...2.2.0

* Apply suggestions from code review

* Revert "Add scene support to roborock (#137203)" (#139840)

This reverts commit 379bf10675.

* Bump aioecowitt to 2025.3.1 (#139841)

* Bump aioecowitt to 2025.3.1

* Bump aioecowitt to 2025.3.1

* Bump onedrive-personal-sdk to 0.0.13 (#139846)

* Bump intents to 2025.3.5 (#139851)

Co-authored-by: Franck Nijhof <git@frenck.dev>

* Bump version to 2025.3.0b8

* Bump version to 2025.3.0

* Fix no disabled capabilities in SmartThings (#139860)

Fix no disabled capabilities

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: cdnninja <jaydenaphillips@gmail.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Tristan <tristan.steele@gmail.com>
Co-authored-by: peteS-UK <64092177+peteS-UK@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Manu <4445816+tr4nt0r@users.noreply.github.com>
Co-authored-by: J. Diego Rodríguez Royo <jdrr1998@hotmail.com>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Co-authored-by: Noah Husby <32528627+noahhusby@users.noreply.github.com>
Co-authored-by: Dan Raper <me@danr.uk>
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
Co-authored-by: Markus Adrario <Mozilla@adrario.de>
Co-authored-by: Andre Lengwenus <alengwenus@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: LG-ThinQ-Integration <LG-ThinQ-Integration@lge.com>
Co-authored-by: yunseon.park <yunseon.park@lge.com>
Co-authored-by: Maikel Punie <maikel.punie@gmail.com>
Co-authored-by: Dan Bishop <d@nbishop.uk>
Co-authored-by: elmurato <1382097+elmurato@users.noreply.github.com>
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Cameron Ring <cameron@cs.stanford.edu>
Co-authored-by: Shay Levy <levyshay1@gmail.com>
Co-authored-by: fwestenberg <47930023+fwestenberg@users.noreply.github.com>
Co-authored-by: Matrix <justin@yosmart.com>
Co-authored-by: Andrew <34544450+10100011@users.noreply.github.com>
Co-authored-by: Renier Moorcroft <66512715+RenierM26@users.noreply.github.com>
Co-authored-by: Peter Brøndum <34370407+brondum@users.noreply.github.com>
Co-authored-by: Galorhallen <12990764+Galorhallen@users.noreply.github.com>
Co-authored-by: tronikos <tronikos@users.noreply.github.com>
Co-authored-by: Paul Traina <pleasantone@users.noreply.github.com>
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Co-authored-by: Noah Groß <me@codesalat.dev>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
Co-authored-by: Denis Shulyaka <Shulyaka@gmail.com>
Co-authored-by: Pierre Ståhl <pierre.staahl@gmail.com>
Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
Co-authored-by: Christophe Gagnier <Moustachauve@users.noreply.github.com>
Co-authored-by: Matt Zimmerman <mdz@users.noreply.github.com>
Co-authored-by: Ben Bridts <ben.bridts@gmail.com>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
Co-authored-by: Michael Hansen <mike@rhasspy.org>
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
Co-authored-by: Jeef <jeeftor@users.noreply.github.com>
Co-authored-by: Ivan Lopez Hernandez <ivan.lh.94@outlook.com>
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
Co-authored-by: LaithBudairi <69572447+LaithBudairi@users.noreply.github.com>
Co-authored-by: M-A <maruel@gmail.com>
Co-authored-by: Martreides <8385298+Martreides@users.noreply.github.com>
Co-authored-by: Guido Schmitz <Shutgun@users.noreply.github.com>
Co-authored-by: Juan Grande <juan.grande@gmail.com>
Co-authored-by: Filip Agh <filip11agh@gmail.com>
Co-authored-by: StaleLoafOfBread <45444205+StaleLoafOfBread@users.noreply.github.com>
Co-authored-by: cs12ag <70966712+cs12ag@users.noreply.github.com>
Co-authored-by: Niklas Neesen <n.neesen@me.com>
Co-authored-by: Allen Porter <allen@thebends.org>
Co-authored-by: Anthony Hou <anthony.tr.hou@gmail.com>
Co-authored-by: SteveDiks <126147459+SteveDiks@users.noreply.github.com>
2025-03-05 20:00:41 +01:00
Erik Montnemery cc5c8bf5e3 Make helpers.frame.report_usage work when called from any thread (#139836)
* Make helpers.frame.report_usage work when called from any thread

* Address review comments, update tests

* Add test

* Update test

* Update recorder test

* Update tests
2025-03-05 19:37:34 +01:00
Erik Montnemery cfaf18f942 Improve the mock_integration_frame test fixture (#139850)
* Improve the mock_integration_frame test fixture

* Update test
2025-03-05 18:42:34 +01:00
Joost Lekkerkerker 98e317dd55 Fix no disabled capabilities in SmartThings (#139860)
Fix no disabled capabilities
2025-03-05 17:42:31 +00:00
Joost Lekkerkerker 1f24e5aec4 Fix no disabled capabilities in SmartThings (#139860)
Fix no disabled capabilities
2025-03-05 18:41:21 +01:00
Franck Nijhof ed088aa72f Bump version to 2025.3.0 2025-03-05 17:39:36 +00:00
Franck Nijhof 51162320cb Bump version to 2025.3.0b8 2025-03-05 17:25:33 +00:00
Michael Hansen b88eab8ba3 Bump intents to 2025.3.5 (#139851)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-05 17:23:04 +00:00
Josef Zweck 6c080ee650 Bump onedrive-personal-sdk to 0.0.13 (#139846) 2025-03-05 17:22:17 +00:00
Joost Lekkerkerker 8056b0df2b Bump aioecowitt to 2025.3.1 (#139841)
* Bump aioecowitt to 2025.3.1

* Bump aioecowitt to 2025.3.1
2025-03-05 17:22:14 +00:00
Allen Porter 3f94b7a61c Revert "Add scene support to roborock (#137203)" (#139840)
This reverts commit 379bf10675.
2025-03-05 17:22:11 +00:00
J. Nick Koston 1484e46317 Bump nexia to 2.2.1 (#139786)
* Bump nexia to 2.2.0

changelog: https://github.com/bdraco/nexia/compare/2.1.1...2.2.0

* Apply suggestions from code review
2025-03-05 17:22:08 +00:00
LG-ThinQ-Integration 2812c8a993 Get temperature data appropriate for hass.config.unit in LG ThinQ (#137626)
* Get temperature data appropriate for hass.config.unit

* Modify temperature_unit for init

* Modify unit's map

* Fix ruff error

---------

Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-03-05 17:22:04 +00:00
Michael Hansen cfe102f274 Bump intents to 2025.3.5 (#139851)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-05 18:20:48 +01:00
jb101010-2 b225a7f370 Bump pysuezV2 to 2.0.4 (#139824) 2025-03-05 17:12:34 +00:00
Erik Montnemery 0f3409bd09 Fix stale test name in vacuum (#139853) 2025-03-05 17:07:43 +01:00
Joost Lekkerkerker e7d371cddc Bump aioecowitt to 2025.3.1 (#139841)
* Bump aioecowitt to 2025.3.1

* Bump aioecowitt to 2025.3.1
2025-03-05 17:04:41 +01:00
Josef Zweck fffb414ba9 Bump onedrive-personal-sdk to 0.0.13 (#139846) 2025-03-05 16:19:15 +01:00
Erik Montnemery 1552aec416 Improve frame helper tests (#139843) 2025-03-05 16:13:09 +01:00
Maciej Bieniek c69cec28fe Bump gios to version 6.0.0 (#139832)
* Fix the code

* Fix tests

* Bump version

* Use https for configuration URL
2025-03-05 15:04:56 +01:00
Simone Chemelli 61e0b938ae Convert Shelly block switches to EntityDescription (#106985) 2025-03-05 14:56:30 +01:00
Allen Porter c0e5a549b6 Revert "Add scene support to roborock (#137203)" (#139840)
This reverts commit 379bf10675.
2025-03-05 14:36:20 +01:00
Robert Resch f0bba1d6d4 Fix disable test results uploads properly (#139827)
* Fix disable test results uploads properly

* use dedicated variable

* fix pushes
2025-03-05 13:52:29 +01:00
tdfountain 1c1a950c05 Add conditional support for ambient sensors in NUT (#139675)
* Conditionally remove ambient sensors if not present

* Create ambient sensors list and use list comprehension

* Update homeassistant/components/nut/sensor.py

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

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-03-05 12:12:56 +00:00
LG-ThinQ-Integration df2248bb82 Get temperature data appropriate for hass.config.unit in LG ThinQ (#137626)
* Get temperature data appropriate for hass.config.unit

* Modify temperature_unit for init

* Modify unit's map

* Fix ruff error

---------

Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-03-05 12:13:11 +01:00
Franck Nijhof 5043e2ad10 Bump version to 2025.3.0b7 2025-03-05 11:01:06 +00:00
Bram Kragten 2c2fd76270 Update frontend to 20250305.0 (#139829) 2025-03-05 11:00:56 +00:00
Bram Kragten 7fe75a959f Update frontend to 20250305.0 (#139829) 2025-03-05 11:54:58 +01:00
Dan Raper 5f88354cb3 Add vehicle select to Ohme (#139795)
* Add vehicle select to Ohme

* mypy fixes

* Update homeassistant/components/ohme/select.py

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

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-03-05 10:59:47 +01:00
Erik Montnemery 09561aeb39 Improve frame helper tests (#139821) 2025-03-05 10:43:29 +01:00
Franck Nijhof 7001f8daaf Bump version to 2025.3.0b6 2025-03-05 09:39:26 +00:00
SteveDiks b41fc932c5 Split the energy and data retrieval in WeHeat (#139211)
* Split the energy and data logs

* Make sure that pump_info name is set to device name, bump weheat

* Adding config entry

* Fixed circular import

* parallelisation of awaits

* Update homeassistant/components/weheat/binary_sensor.py

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

* Fix undefined weheatdata

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-03-05 09:39:13 +00:00
SteveDiks 2a11c413c7 Split the energy and data retrieval in WeHeat (#139211)
* Split the energy and data logs

* Make sure that pump_info name is set to device name, bump weheat

* Adding config entry

* Fixed circular import

* parallelisation of awaits

* Update homeassistant/components/weheat/binary_sensor.py

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

* Fix undefined weheatdata

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-03-05 10:11:59 +01:00
Marcel van der Veldt 0872243297 Drop BETA postfix from Matter integration's title (#139816)
Drop BETA postfix from Matter title

Now that the whole Matter stack of Home Assistant is officially certified, we can drop the beta flag.
2025-03-05 08:44:44 +00:00
Shay Levy bba889975a Bump aiowebostv to 0.7.3 (#139788) 2025-03-05 08:44:39 +00:00
Dan Raper 36412a034d Bump ohmepy to 1.4.0 (#139791) 2025-03-05 09:27:10 +01:00
Martin Hjelmare 13dfd27b7e Clean Home Connect error handling (#139817) 2025-03-05 09:07:45 +01:00
Marcel van der Veldt 1fb02944b7 Drop BETA postfix from Matter integration's title (#139816)
Drop BETA postfix from Matter title

Now that the whole Matter stack of Home Assistant is officially certified, we can drop the beta flag.
2025-03-05 09:04:55 +01:00
dependabot[bot] 1c045ab222 Bump actions/download-artifact from 4.1.8 to 4.1.9 (#139814)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.8 to 4.1.9.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4.1.8...v4.1.9)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 08:16:42 +01:00
dependabot[bot] 0d329bd83d Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#139813)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 07:49:18 +01:00
J. Nick Koston d1995086cc Bump habluetooth to 3.25.0 (#139811)
changelog: https://github.com/Bluetooth-Devices/habluetooth/compare/v3.24.1...v3.25.0
2025-03-05 00:15:00 -05:00
J. Nick Koston f0ad0e6eae Bump cached-ipaddress to 0.10.0 (#139807) 2025-03-04 17:51:46 -10:00
J. Nick Koston 457a7216ff Bump dbus-fast to 2.35.1 (#139809) 2025-03-04 17:51:31 -10:00
tdfountain 782f504522 Add common PDU sensors to NUT (#139669)
* Add common PDU sensors and alphabetize sensors list

* Back out code quality improvements

* Change voltage and current status to diagnostic and disabled by default
2025-03-04 17:26:43 -10:00
J. Nick Koston e60a284354 Bump aioesphomeapi to 29.4.0 (#139806)
changelog: https://github.com/esphome/aioesphomeapi/compare/v29.3.2...v29.4.0
2025-03-04 17:25:43 -10:00
J. Nick Koston d5d9bc1df6 Bump ulid-transform to 1.3.0 (#139808)
changelog: https://github.com/Bluetooth-Devices/ulid-transform/compare/v1.2.1...v1.3.0
2025-03-04 17:25:11 -10:00
J. Nick Koston 27fd0a88f4 Bump bleak-esphome to 2.11.0 (#139803)
changelog: https://github.com/Bluetooth-Devices/bleak-esphome/compare/v2.10.2...v2.11.0
2025-03-04 17:12:45 -10:00
J. Nick Koston 24188ffb31 Bump zeroconf to 0.146.0 (#139804)
changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.145.1...0.146.0
2025-03-04 17:10:07 -10:00
Petro31 e51d9bd6f4 Remove redundant is not None checks in Template integration (#139790)
Remove redundant is not None checks
2025-03-04 21:58:41 -05:00
J. Nick Koston 3eb7302fde Bump fnv-hash-fast to 1.4.0 (#139801)
changelog: https://github.com/Bluetooth-Devices/fnv-hash-fast/compare/v1.2.6...v1.4.0
2025-03-04 21:57:43 -05:00
J. Nick Koston 49b2f8fd7f Bump bluetooth-data-tools to 1.25.0 (#139802)
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.23.4...v1.25.0
2025-03-04 21:57:27 -05:00
Shay Levy 0143a71e97 Bump aiowebostv to 0.7.3 (#139788) 2025-03-04 16:45:23 -10:00
J. Nick Koston 9bc806ab21 Bump nexia to 2.2.1 (#139786)
* Bump nexia to 2.2.0

changelog: https://github.com/bdraco/nexia/compare/2.1.1...2.2.0

* Apply suggestions from code review
2025-03-05 01:57:03 +01:00
Simone Chemelli 366c5c3f10 Improve unique_id tests for Shelly block devices (#139778)
* Improve unique_id tests for Shelly block devices

* type test

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-03-04 14:03:38 -10:00
peteS-UK 3ee5262a8d Clean up squeezebox build_item_response part 2 (#139595) 2025-03-04 17:48:13 -06:00
Martin Hjelmare c671862d3f Improve Home Connect appliances test fixture (#139787)
Improve Home Connect appliances fixture
2025-03-05 00:45:58 +01:00
Manu 50ba93042b Add create_habit action to Habitica integration (#139673) 2025-03-04 21:43:49 +00:00
Franck Nijhof 01e8ca6495 Bump version to 2025.3.0b5 2025-03-04 20:25:14 +00:00
J. Nick Koston 7d82375f81 Bump nexia to 2.1.1 (#139772)
changelog: https://github.com/bdraco/nexia/compare/2.0.9...2.1.1

fixes #133368
2025-03-04 20:24:56 +00:00
Martin Hjelmare 47033e587b Fix home connect available (#139760)
* Fix home connect available

* Extend and clarify test

* Do not change connected state on stream interrupted
2025-03-04 20:24:47 +00:00
Joost Lekkerkerker e73b08b269 Bump pysmartthings to 2.5.0 (#139758)
* Bump pysmartthings to 2.5.0

* Bump pysmartthings to 2.5.0
2025-03-04 20:23:45 +00:00
Anthony Hou a195a9107b Fix incorrect weather state returned by HKO (#139757)
* Fix incorrect weather state

* Clean up unused import

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-04 20:12:25 +00:00
Joost Lekkerkerker 185949cc18 Add Apollo Automation virtual integration (#139751)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-03-04 20:12:22 +00:00
J. Diego Rodríguez Royo c129f27c95 Bump aiohomeconnect to 0.16.2 (#139750) 2025-03-04 20:12:16 +00:00
Norbert Rittel 1456d9d800 Capitalize "Suez Water" and "ID" in user-facing strings (#139782) 2025-03-04 21:00:51 +01:00
Erik Montnemery 3b9bb96784 Align google_drive with changes in BackupAgent (#139767) 2025-03-04 11:45:10 -08:00
epenet 7359013db0 Move ForkedDaapdUpdater setup to __init__ module (#139733)
* Move ForkedDaapdUpdater setup to __init__ module

* Adjust tests

* One more
2025-03-04 20:24:36 +01:00
Erik Montnemery be3d678f23 Align hassio with changes in BackupAgent (#139780) 2025-03-04 20:20:49 +01:00
Martin Hjelmare e8099fd3b2 Fix home connect available (#139760)
* Fix home connect available

* Extend and clarify test

* Do not change connected state on stream interrupted
2025-03-04 19:26:20 +01:00
Erik Montnemery 344cfedd6f Align synology_dsm with changes in BackupAgent (#139770) 2025-03-04 19:22:18 +01:00
Robert Resch c0d882e305 Upload test result artifacts always (#139776)
Upload test results artificats always
2025-03-04 19:19:38 +01:00
Erik Montnemery e86fc88631 Minor improvement of hassio backup tests (#139775) 2025-03-04 18:20:55 +01:00
J. Nick Koston e1127fc78c Bump nexia to 2.1.1 (#139772)
changelog: https://github.com/bdraco/nexia/compare/2.0.9...2.1.1

fixes #133368
2025-03-04 18:01:40 +01:00
Erik Montnemery 95fbba1d74 Align cloud with changes in BackupAgent (#139766) 2025-03-04 17:46:13 +01:00
Erik Montnemery 46ac44c248 Align webdav with changes in BackupAgent (#139771) 2025-03-04 17:44:26 +01:00
Erik Montnemery 0ebdb1c2a8 Align kitchen_sink with changes in BackupAgent (#139768) 2025-03-04 16:38:03 +01:00
Erik Montnemery e3a90831bf Align onedrive with changes in BackupAgent (#139769) 2025-03-04 16:32:47 +01:00
Erik Montnemery ec100e5a6c Align azure_storage with changes in BackupAgent (#139765) 2025-03-04 16:10:33 +01:00
Joost Lekkerkerker 0eb087ba3f Bump pysmartthings to 2.5.0 (#139758)
* Bump pysmartthings to 2.5.0

* Bump pysmartthings to 2.5.0
2025-03-04 15:59:38 +01:00
Erik Montnemery e55757dc82 Simplify error handling in BackupAgent when a backup is not found (#139754)
Simplify error handling in BackupAgent when backup is not found
2025-03-04 15:56:12 +01:00
Paulus Schoutsen c51a2317e1 Add timer support to VoIP (#139763) 2025-03-04 15:48:10 +01:00
Anthony Hou 7fb949dff7 Fix incorrect weather state returned by HKO (#139757)
* Fix incorrect weather state

* Clean up unused import

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-04 15:25:47 +01:00
J. Diego Rodríguez Royo 74ea553b63 Bump aiohomeconnect to 0.16.2 (#139750) 2025-03-04 15:17:05 +01:00
Joost Lekkerkerker d9690507a4 Add Apollo Automation virtual integration (#139751)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-03-04 15:08:14 +01:00
Marc Mueller e69b4f389f Simplify lint-only job config [ci] (#139748) 2025-03-04 14:07:27 +01:00
Paulus Schoutsen 8a97c2bfca VoIP block non-TTS announcements (#139658)
* VoIP block non-TTS announcements

* Migrate VoIP to use pipeline token
2025-03-04 08:02:58 -05:00
Robert Resch d5ba55d2fc Disable test results upload on forks (#139749)
Disable test result uploads on forks
2025-03-04 13:27:51 +01:00
J. Nick Koston d38e046494 Bump bleak-esphome to 2.10.2 (#139731)
* Bump bleak-esphome to 2.10.0

changelog: https://github.com/Bluetooth-Devices/bleak-esphome/compare/v2.9.0...v2.10.0

* again for wheel fix

* disable name check since its a binary now
2025-03-04 11:49:44 +01:00
Franck Nijhof 6a5a66e2f9 Bump version to 2025.3.0b4 2025-03-04 10:46:11 +00:00
J. Diego Rodríguez Royo db63d9fcbf Delete refresh after a non-breaking error at event stream at Home Connect (#139740)
* Delete refresh after non-breaking error

And improve how many time does it take to retry to open stream

* Update tests
2025-03-04 10:45:10 +00:00
Allen Porter 5b3d798eca Bump google-nest-sdm to 7.1.4 (#139728) 2025-03-04 10:45:06 +00:00
Allen Porter a0dde2a7d6 Add nest translation string for already_in_progress (#139727) 2025-03-04 10:45:00 +00:00
J. Nick Koston 1bdc33d52d Bump sense-energy to 0.13.6 (#139714)
changes: https://github.com/scottbonline/sense/releases/tag/0.13.6
2025-03-04 10:44:57 +00:00
Shay Levy f1d332da5a Bump aiowebostv to 0.7.2 (#139712) 2025-03-04 10:44:51 +00:00
G Johansson 304c13261a Bump holidays to 0.68 (#139711) 2025-03-04 10:44:48 +00:00
J. Nick Koston c58cbfd6f4 Bump ESPHome stable BLE version to 2025.2.2 (#139704)
ensure proxies have https://github.com/esphome/esphome/pull/8328
so they do not reboot themselves if disconnecting takes
too long
2025-03-04 10:44:44 +00:00
Joost Lekkerkerker b890d3e15a Abort SmartThings flow if default_config is not enabled (#139700)
* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled
2025-03-04 10:44:41 +00:00
Allen Porter 2c9b8b6835 Improve failure handling and logging for invalid map responses (#139681) 2025-03-04 10:44:37 +00:00
Allen Porter 73cc1f51ca Add additional roborock debug logging (#139680) 2025-03-04 10:44:33 +00:00
Paulus Schoutsen dca77e8232 Avoid duplicate chat log content (#139679) 2025-03-04 10:44:30 +00:00
Joost Lekkerkerker 03cb177e7c Fix scope comparison in SmartThings (#139652) 2025-03-04 10:44:26 +00:00
J. Nick Koston ad04b53615 Fix broken link in ESPHome BLE repair (#139639)
ESPHome always uses .0 in the URL for the changelog,
and we never had a patch version in the stable
BLE version field so we need to switch it to
.0 for the URL.
2025-03-04 10:44:23 +00:00
Marcel van der Veldt 46bcb307f6 Fix ability to remove orphan device in Music Assistant integration (#139431)
* Fix ability to remove orphan device in Music Assistant integration

* Add test

* Remove orphaned device entries at startup as well

* adjust mocked client
2025-03-04 10:44:19 +00:00
Markus Adrario b816625028 Fix Homee brightness sensors reporting in percent (#139409)
* fix brigtness sensor having percent as unit.

* add test for percent-brightness-sensor

* remove valve position and update tests

* Removed test, because covered by Snapshots

* fix review comments

* move device calss to init.

* fix test

* fix review comments

* add battery sensor back to test fixture

* fix
2025-03-04 10:44:15 +00:00
Abílio Costa 0940fc7806 Prevent zero interval in Calendar get_events service (#139378)
* Prevent zero interval in Calendar get_events service

* Fix holiday calendar tests

* Remove redundant entity_id

* Use translation for exception

* Replace check with voluptuous validator

* Revert strings.xml
2025-03-04 10:44:12 +00:00
Niklas Neesen 50aefc3653 Fix vicare exception for specific ventilation device type (#138343)
* fix for exception for specific ventilation device type + tests

* fix for exception for specific ventilation device type + tests

* New Testset just for fan

* update test_sensor.ambr
2025-03-04 10:44:09 +00:00
cs12ag c0dc83cbc0 Fix unique identifiers where multiple IKEA Tradfri gateways are in use (#136060)
* Create unique identifiers where multiple gateways are in use

Resolving issue https://github.com/home-assistant/core/issues/134497

* Added migration function to __init__.py

Added migration function to execute upon initialisation, to:
a) remove the erroneously-added config)_entry added to the device (gateway B gets added as a config_entry to a device associated to gateway A), and
b) swap out the non-unique identifiers for genuinely unique identifiers.

* Added tests to simulate migration from bad data scenario (i.e. explicitly executing migrate_entity_unique_ids() from __init__.py)

* Ammendments suggested in first review

* Changes after second review

* Rewrite of test_migrate_config_entry_and_identifiers after feedback

* Converted migrate function into major version, updated tests

* Finalised variable naming convention per feedback, added test to validate config entry migrated to v2

* Hopefully final changes for cosmetic / comment stucture

* Further code-coverage in test_migrate_config_entry_and_identifiers()

* Minor test corrections

* Added test for non-tradfri identifiers
2025-03-04 10:44:01 +00:00
Robert Resch 50cec420ef Upload test results to codecov (#138512)
* Upload test results to codecov

* Upload tests results in single job
2025-03-04 11:43:41 +01:00
Markus Adrario 23dac3933f Fix Homee brightness sensors reporting in percent (#139409)
* fix brigtness sensor having percent as unit.

* add test for percent-brightness-sensor

* remove valve position and update tests

* Removed test, because covered by Snapshots

* fix review comments

* move device calss to init.

* fix test

* fix review comments

* add battery sensor back to test fixture

* fix
2025-03-04 11:40:36 +01:00
Erik Montnemery 32f59bfd25 Remove unused constant from recorder (#139741) 2025-03-04 11:39:35 +01:00
Erik Montnemery 4f36bbdfe6 Fix regression in template flag introduced by #139645 (#139742) 2025-03-04 11:33:27 +01:00
J. Diego Rodríguez Royo 973fee9fe1 Delete refresh after a non-breaking error at event stream at Home Connect (#139740)
* Delete refresh after non-breaking error

And improve how many time does it take to retry to open stream

* Update tests
2025-03-04 11:07:44 +01:00
Norbert Rittel 13001faf51 Improve strings in openai_conversation.generate_image action (#139736)
Use descriptive wording, fix sentence-casing.
2025-03-04 09:57:38 +01:00
Marcel van der Veldt 9f780a5308 Fix ability to remove orphan device in Music Assistant integration (#139431)
* Fix ability to remove orphan device in Music Assistant integration

* Add test

* Remove orphaned device entries at startup as well

* adjust mocked client
2025-03-04 09:56:42 +01:00
Abílio Costa d87c963db5 Prevent zero interval in Calendar get_events service (#139378)
* Prevent zero interval in Calendar get_events service

* Fix holiday calendar tests

* Remove redundant entity_id

* Use translation for exception

* Replace check with voluptuous validator

* Revert strings.xml
2025-03-04 09:52:29 +01:00
Allen Porter c6a9472fdb Add nest translation string for already_in_progress (#139727) 2025-03-04 09:46:56 +01:00
Allen Porter cd0a983850 Bump google-nest-sdm to 7.1.4 (#139728) 2025-03-04 09:28:10 +01:00
Petro31 890d3f4af4 Add a base class for template entities to inherit from (#139645)
* add-abstract-template-entity-base-class

* review 1 changes
2025-03-04 07:23:05 +01:00
Joshua Leaper a778092941 Support up to 8 AUX outputs in Ness Alarm (#139718)
Support up to 8 AUX outputs
2025-03-03 23:35:20 +00:00
J. Nick Koston 9ea582de26 Bump sense-energy to 0.13.6 (#139714)
changes: https://github.com/scottbonline/sense/releases/tag/0.13.6
2025-03-03 11:20:25 -10:00
Shay Levy b6f2d8f30b Bump aiowebostv to 0.7.2 (#139712) 2025-03-03 10:26:16 -10:00
G Johansson 139072bb59 Bump holidays to 0.68 (#139711) 2025-03-03 21:47:38 +02:00
Paul Bottein 07a93dade2 Add translations for switch state by device class (#139693) 2025-03-03 20:24:36 +01:00
Allen Porter 9dc04cb088 Improve failure handling and logging for invalid map responses (#139681) 2025-03-03 20:23:29 +01:00
StaleLoafOfBread 890c672f8c Add charging binary_sensor so front end can render battery icon properly (#139684)
* Add charging binary sensor

* Add charging binary sensor test
2025-03-03 20:21:05 +01:00
Simone Chemelli e28e4d210f Bump aiocomelit to 0.11.2 (#139707) 2025-03-03 20:19:09 +01:00
Joost Lekkerkerker dcd2d42894 Abort SmartThings flow if default_config is not enabled (#139700)
* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled
2025-03-03 20:07:07 +01:00
Allen Porter e47e151259 Add additional roborock debug logging (#139680) 2025-03-03 21:02:45 +02:00
Andrew Jackson 2c44043e6a Bump mastodon.py to 2.0.1 (#139701)
* Bump mastodon to 2.0.1

* Fix mypy
2025-03-03 20:57:30 +02:00
Norbert Rittel f248901ea8 Grammar fixes in user-facing strings of the LinkPlay integration (#139709)
Grammar fixes in user-facing string of the LinkPlay integration

Fix spelling of "set up", "media player", "ID" and improve the descriptions of the `play_preset` action.
2025-03-03 20:55:47 +02:00
Elias Wernicke 62b6be900f Add complete item intent function for todo component (#127806)
* add complete item intent

* fix error and add tests

* fix merge conflict

* improve error tests

* improve error tests

* add response_key

* add check for non completed

---------

Co-authored-by: Michael Hansen <mike@rhasspy.org>
2025-03-03 12:16:43 -06:00
J. Nick Koston 1b15df3075 Bump ESPHome stable BLE version to 2025.2.2 (#139704)
ensure proxies have https://github.com/esphome/esphome/pull/8328
so they do not reboot themselves if disconnecting takes
too long
2025-03-03 12:44:49 -05:00
Norbert Rittel 229407d685 Fix missing sentence-casing in three Fully Kiosk Browser strings (#139705)
Fix missing sentence-casing in Fully Kiosk Browser strings
2025-03-03 18:25:18 +01:00
Simone Chemelli aaecb47125 Add strict typing to Comelit (#139455)
* Add quality scale and strict typing to Comelit

* mypy

* fix strings

* remove quality scale

* revert quality scale changes

* improve typing

* letfover

* update typing based on new lib

* align to platform

* cleanup

* apply review comments (part 1)

* apply review comment ( part 2)

* apply review comments

* align

* align test data

* TypedDict

* better casting
2025-03-03 17:57:42 +01:00
Joakim Sørensen b17ee78dec Bump hass-nabucasa from 0.92.0 to 0.94.0 (#139697) 2025-03-03 15:51:04 +00:00
Erik Montnemery 20e48054cf Fix stale docstrings in onboarding tests (#139696) 2025-03-03 15:08:39 +00:00
cs12ag ee486c269c Fix unique identifiers where multiple IKEA Tradfri gateways are in use (#136060)
* Create unique identifiers where multiple gateways are in use

Resolving issue https://github.com/home-assistant/core/issues/134497

* Added migration function to __init__.py

Added migration function to execute upon initialisation, to:
a) remove the erroneously-added config)_entry added to the device (gateway B gets added as a config_entry to a device associated to gateway A), and
b) swap out the non-unique identifiers for genuinely unique identifiers.

* Added tests to simulate migration from bad data scenario (i.e. explicitly executing migrate_entity_unique_ids() from __init__.py)

* Ammendments suggested in first review

* Changes after second review

* Rewrite of test_migrate_config_entry_and_identifiers after feedback

* Converted migrate function into major version, updated tests

* Finalised variable naming convention per feedback, added test to validate config entry migrated to v2

* Hopefully final changes for cosmetic / comment stucture

* Further code-coverage in test_migrate_config_entry_and_identifiers()

* Minor test corrections

* Added test for non-tradfri identifiers
2025-03-03 14:06:25 +01:00
Paulus Schoutsen aee891434f Avoid duplicate chat log content (#139679) 2025-03-03 11:46:40 +01:00
Brett Adams 5472345f45 Add additional garage door code to Advantage Air (#139687)
add Garage door
2025-03-03 11:45:04 +01:00
Norbert Rittel 572534b306 Fix missing camel-case in one "ElevenLabs" string (#139686) 2025-03-03 10:18:30 +01:00
Erik Montnemery 3c363eb5ce Adjust type hints in update entity (#129387)
* Adjust type hints in update entity

* Update allowed return type of update_percentage

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-03 10:17:13 +01:00
andresb5555 7e4432e321 Do not force logfile to roll over when using TimedRotatingFileHandler (#128301)
Do not force log file to roll over when using TimedRotatingFileHandler
2025-03-02 22:07:35 +01:00
Elias Wernicke 5ae7109561 Increase test coverage for todo intent (#135960)
* move intent tests to file

* add tests for errors
2025-03-02 22:04:25 +01:00
hydazz 4602c0a1c3 Add Night mode and HVACAction to Advantage Air (#137475)
* add night mode toggle

* populate AC's action

* set hvac action on zones

* update tests

* show zones as off if AC is off

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-02 21:59:44 +01:00
Michel van de Wetering 53bc5ff029 Keep entered values in form when connecting to Epson projector fails (#135402)
Add suggested values to form
2025-03-02 21:41:38 +01:00
martin12as c782a6ab63 Improve outlet constant naming for NUT (#139660)
* Update const.py

Fixed to match string.json

* Update const.py
2025-03-02 21:38:12 +01:00
Trevor Warwick 23644a60ac Improve Linkplay device unavailability detection (#138457)
* Dampen reachability changes

Retry a few times before declaring player is unavailable

* Fix ruff-format complaint

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>

* Fix ruff-format complaint

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>

* Fix ruff-format complaint

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>

* Fix duplicated change

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>

---------

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-03-02 21:26:54 +01:00
Joost Lekkerkerker 14e66ffef4 Fetch integration list from next branch for analytics insights (#137250)
Fetch integration list from next branch
2025-03-02 21:21:47 +01:00
karwosts fa40d02a07 Add model_id filter to device selector (#135646)
* Add model_id filter to device selector

* Rerun CI
2025-03-02 21:15:37 +01:00
Niklas Neesen 8536f2b4cb Fix vicare exception for specific ventilation device type (#138343)
* fix for exception for specific ventilation device type + tests

* fix for exception for specific ventilation device type + tests

* New Testset just for fan

* update test_sensor.ambr
2025-03-02 20:57:13 +01:00
J. Nick Koston 387bf83ba8 Bump aioesphomeapi to 29.3.2 (#139653)
changelog: https://github.com/esphome/aioesphomeapi/compare/v29.3.1...v29.3.2
2025-03-02 20:53:45 +01:00
Norbert Rittel 18b0f54a3e Fix typo in outlet_2_load_off of NUT integration (#139656)
Fix typo in `outlet_2_load_off`

Fix small copy & paste error in https://github.com/home-assistant/core/pull/139044
2025-03-02 20:49:19 +01:00
Nathan Spencer f76e295204 Add fault event to balboa (#138623)
* Add fault sensor to balboa

* Use an event instead of sensor for faults

* Don't set fault initially in conftest

* Use event type per fault message code

* Set fault to None in conftest
2025-03-02 20:24:27 +01:00
Norbert Rittel e63b17cd58 Make spelling of "All-Link" consistent in Insteon integration (#139651)
"All-Link" is a fixed term in the Insteon integration that should be kept in translations. To clarify that this commit makes all occurrences in the Insteon integration consistent (plus fixing one typo).

On the other end the word "database" is sentence-cased as this can be translated, just as "record" etc.

Finally the description of the "Load All-Link database" action is made consistent using descriptive third-person singular as all other actions do.
2025-03-02 20:04:53 +01:00
martin12as 05e23f0fc7 Add nut commands to turn off/on outlet 1 & 2 (#139044)
* Update const.py

* Update strings.json

* Update homeassistant/components/nut/strings.json

Co-authored-by: tdfountain <174762217+tdfountain@users.noreply.github.com>

* Update homeassistant/components/nut/strings.json

Co-authored-by: tdfountain <174762217+tdfountain@users.noreply.github.com>

---------

Co-authored-by: tdfountain <174762217+tdfountain@users.noreply.github.com>
2025-03-02 20:00:05 +01:00
Joost Lekkerkerker fca4ef3b1e Fix scope comparison in SmartThings (#139652) 2025-03-02 19:52:37 +01:00
Bram Kragten 8382663be4 Bump version to 2025.3.0b3 2025-03-02 16:15:38 +01:00
Joost Lekkerkerker 7e1309d874 Bump pysmartthings to 2.4.1 (#139627) 2025-03-02 16:15:16 +01:00
Joost Lekkerkerker 1d0cba1a43 Still request scopes in SmartThings (#139626)
Still request scopes
2025-03-02 16:15:15 +01:00
J. Nick Koston 7d9a6ceb6b Fix arm vacation mode showing as armed away in elkm1 (#139613)
Add native arm vacation mode support to elkm1

Vacation mode is currently implemented as a custom
service which will be deprecated in a future PR.

Note that the custom service was added long before
HA had a native vacation mode which was added
in #45980
2025-03-02 16:15:14 +01:00
Jan Bouwhuis 6abdb28a03 Fix body text of imap message not available in custom event data template (#139609) 2025-03-02 16:15:13 +01:00
J. Nick Koston 3690e03951 Bump inkbird-ble to 0.7.1 (#139603)
changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.7.0...v0.7.1
2025-03-02 16:15:13 +01:00
Shay Levy 4fe4d14f16 Bump aioshelly to 13.1.0 (#139601)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-02 16:15:12 +01:00
J. Nick Koston 74e8ffa555 Fix handling of NaN float values for current humidity in ESPHome (#139600)
fixes #131837
2025-03-02 16:15:11 +01:00
Robert Resch c257b228f1 Bump deebot-client to 12.3.1 (#139598) 2025-03-02 16:15:10 +01:00
peteS-UK 6ff0f67d03 Fix Manufacturer naming for Squeezelite model name for Squeezebox (#139586)
Squeezelite Manufacturer Fix
2025-03-02 16:15:10 +01:00
Jan Bouwhuis 8fdff9ca37 Fix - Allow brightness only light MQTT json light to be set up using the brightness flag or via supported_color_modes (#139585)
* Fix - Allow brightness only light MQTT json light to be set up using the `brightness` flag or via  `supported_color_modes`

* Improve comment
2025-03-02 16:15:09 +01:00
Norbert Rittel 9055dff9bd Improve field descriptions of zha.permit action (#139584)
Make the field descriptions of `source_ieee` and `install_code` UI-friendly by cross-referencing them using their friendly names to allow matching translations.

Better explain the alternative of using the `qr_code` field by adding that this contains both the IEEE address and the Install code of the joining device.
2025-03-02 16:15:08 +01:00
G Johansson e766d681b5 Fix duplicate unique id issue in Sensibo (#139582)
* Fix duplicate unique id issue in Sensibo

* Fixes

* Mods
2025-03-02 16:15:07 +01:00
J. Nick Koston 511e57d0b3 Bump aiohomekit to 3.2.8 (#139579)
changelog: https://github.com/Jc2k/aiohomekit/compare/3.2.7...3.2.8
2025-03-02 16:15:04 +01:00
Markus Adrario 74be49d00d Homee: fix watchdog icon (#139577)
fix watchdog icon
2025-03-02 16:15:04 +01:00
Joost Lekkerkerker 684c3aac6b Don't require not needed scopes in SmartThings (#139576)
* Don't require not needed scopes

* Don't require not needed scopes
2025-03-02 16:15:03 +01:00
Joost Lekkerkerker a718b6ebff Only determine SmartThings swing modes if we support it (#139571)
Only determine swing modes if we support it
2025-03-02 16:15:02 +01:00
Joost Lekkerkerker f17274d417 Validate scopes in SmartThings config flow (#139569) 2025-03-02 16:15:01 +01:00
Jan-Philipp Benecke 1530139a61 Bump aiowebdav2 to 0.3.1 (#139567) 2025-03-02 16:15:01 +01:00
Brett Adams f56d65b2ec Bump Tesla Fleet API to v0.9.12 (#139565)
* bump

* Update manifest.json

* Fix versions

* remove tesla_bluetooth

* Remove mistake
2025-03-02 16:15:00 +01:00
Joost Lekkerkerker 21277a81d3 Bump pysmartthings to 2.4.0 (#139564) 2025-03-02 16:14:59 +01:00
J. Nick Koston e1ce5b8c69 Revert polling changes to HomeKit Controller (#139550)
This reverts #116200

We changed the polling logic to avoid polling if all chars are marked as watchable
to avoid crashing the firmware on a very limited set of devices as it was
more in line with what iOS does. In the end, the user ended up replacing
the device in #116143 because it turned out to be unreliable in other
ways. The vendor has since issued a firmware update that may resolve
the problem with all of these devices.

In practice it turns out many more devices
report that chars are evented and never send events. After a few months
of data and reports the trade-off does not seem worth it since
users are having to set up manual polling on a wide range of
devices. The amount of devices with evented chars that do not
actually send state vastly exceeds the number of devices that
might crash if they are polled too often so restore the previous
behavior

fixes #138561
fixes #100331
fixes #124529
fixes #123456
fixes #130763
fixes #124099
fixes #124916
fixes #135434
fixes #125273
fixes #124099
fixes #119617
2025-03-02 16:14:59 +01:00
Joost Lekkerkerker 0323a9c4e6 Add SmartThings Viper device info (#139548) 2025-03-02 16:14:58 +01:00
Joost Lekkerkerker c7d89398a0 Improve SmartThings OCF device info (#139547) 2025-03-02 16:14:57 +01:00
Joost Lekkerkerker 8cc587d3a7 Bump pysmartthings to 2.3.0 (#139546) 2025-03-02 16:14:56 +01:00
J. Nick Koston 5ad156767a Bump PySwitchBot to 0.56.1 (#139544)
changelog: https://github.com/sblibs/pySwitchbot/compare/0.56.0...0.56.1
2025-03-02 16:14:56 +01:00
Joost Lekkerkerker f54b3f4de2 Remove orphan devices on startup in SmartThings (#139541) 2025-03-02 16:14:55 +01:00
Joost Lekkerkerker 6f0c62dc9d Bump pysmartthings to 2.2.0 (#139539) 2025-03-02 16:14:54 +01:00
StaleLoafOfBread dce8bca103 Fix alert not respecting can_acknowledge setting (#139483)
* fix(alert): check can_ack prior to acking

* fix(alert): add test for when can_acknowledge=False

* fix(alert): warn on can_ack blocking an ack

* Raise error when trying to acknowledge alert with can_acknowledge set to False

* Rewrite can_ack check as guard

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Make can_ack service error msg human readable because it will show up in the UI

* format with ruff

* Make pytest aware of service error when acking an unackable alert

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-03-02 16:14:53 +01:00
Joost Lekkerkerker 22af8af132 Set SmartThings delta energy to Total (#139474) 2025-03-02 16:14:52 +01:00
Joost Lekkerkerker 8a62b882bf Use last event as color mode in SmartThings (#139473)
* Use last event as color mode in SmartThings

* Use last event as color mode in SmartThings

* Fix
2025-03-02 16:14:52 +01:00
Filip Agh 708f22fe6f Fix update data for multiple Gree devices (#139469)
fix sync date for multiple devices

do not use handler for explicit update devices as internal communication lib do not provide which device is updated
use ha update loop

copy data object to prevent rewrite data from internal lib

allow more time to process response before log warning about long wait for response and make log message more clear
2025-03-02 16:14:51 +01:00
Marcel van der Veldt a4e71e2055 Ensure Hue bridge is added first to the device registry (#139438) 2025-03-02 16:14:50 +01:00
Juan Grande 61a3cc37e0 Fix bug in derivative sensor when source sensor's state is constant (#139230)
Previously, when the source sensor's state remains constant, the derivative
sensor repeats its latest value indefinitely.

This patch fixes this bug by consuming the state_reported event and updating
the sensor's output even when the source sensor doesn't change its state.
2025-03-02 16:11:06 +01:00
Guido Schmitz a0668e5a5b Handle IPv6 URLs in devolo Home Network (#139191)
* Handle IPv6 URLs in devolo Home Network

* Use yarl
2025-03-02 16:11:06 +01:00
G Johansson b4b7142b55 Specify recorder as after dependency in sql integration (#139037)
* Specify recorder as after dependency in sql integration

* Remove hassfest exception

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-03-02 16:11:05 +01:00
J. Nick Koston 108b71d33c Use multiple indexed group-by queries to get start time states for MySQL (#138786)
* tweaks

* mysql

* mysql

* Update homeassistant/components/recorder/history/modern.py

* Update homeassistant/components/recorder/history/modern.py

* Update homeassistant/components/recorder/const.py

* Update homeassistant/components/recorder/statistics.py

* Apply suggestions from code review

* mysql

* mysql

* cover

* make sure db is fully init on old schema

* fixes

* fixes

* coverage

* coverage

* coverage

* s/slow_dependant_subquery/slow_dependent_subquery/g

* reword

* comment that callers are responsible for staying under the limit

* comment that callers are responsible for staying under the limit

* switch to kwargs

* reduce branching complexity

* split stats query

* preen

* split tests

* split tests
2025-03-02 16:11:04 +01:00
Martreides 2636a47333 Fix Nederlandse Spoorwegen to ignore trains in the past (#138331)
* Update NS integration to show first next train instead of just the first.

* Handle no first or next trip.

* Remove debug statement.

* Remove seconds and revert back to minutes.

* Make use of dt_util.now().

* Fix issue with next train if no first train.
2025-03-02 16:11:03 +01:00
M-A 17116fcd6c Bump env_canada to 0.8.0 (#138237)
* Bump env_canada to 0.8.0

* Fix requirements*.txt

* Grepped more

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-02 16:11:03 +01:00
LaithBudairi 17c16144d1 Add missing 'state_class' attribute for Growatt plant sensors (#132145)
* Add missing 'state_class' attribute for Growatt plant sensors

* Update total.py

* Update total.py 'TOTAL_INCREASING'

* Update total.py "maximum_output" -> 'TOTAL_INCREASING'

* Update homeassistant/components/growatt_server/sensor/total.py

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-03-02 16:11:02 +01:00
Bram Kragten 178d509d56 Bump version to 2025.3.0b2 2025-02-28 17:06:59 +01:00
Bram Kragten 09c129de40 Update frontend to 20250228.0 (#139531) 2025-02-28 17:06:51 +01:00
Joost Lekkerkerker 07128ba063 Bump yt-dlp to 2025.02.19 (#139526) 2025-02-28 17:06:50 +01:00
Robert Resch a786ff53ff Don't split wheels builder anymore (#139522) 2025-02-28 17:06:50 +01:00
Robert Svensson d2e19c829d Suppress unsupported event 'EVT_USP_RpsPowerDeniedByPsuOverload' by bumping aiounifi to v83 (#139519)
Bump aiounifi to v83
2025-02-28 17:06:49 +01:00
Jan Bouwhuis 94b342f26a Make the Tuya backend library compatible with the newer paho mqtt client. (#139518)
* Make the Tuya backend library compatible with the newer paho mqtt client.

* Improve classnames and docstrings
2025-02-28 17:06:48 +01:00
Josef Zweck 9e3e6b3f43 Add diagnostics to onedrive (#139516)
* Add diagnostics to onedrive

* redact PII

* add raw data
2025-02-28 17:06:47 +01:00
Erik Montnemery 4300900322 Improve error handling in CoreBackupReaderWriter (#139508) 2025-02-28 17:06:46 +01:00
Brett Adams 342e04974d Fix shift state in Teslemetry (#139505)
* Fix shift state

* Different fix
2025-02-28 17:06:46 +01:00
Erik Montnemery fdb4c0a81f Fail recorder.backup.async_pre_backup if Home Assistant is not running (#139491)
Fail recorder.backup.async_pre_backup if hass is not running
2025-02-28 17:06:45 +01:00
Ivan Lopez Hernandez 6de878ffe4 Fix Gemini Schema validation for #139416 (#139478)
Fixed Schema validation for issue #139477
2025-02-28 17:06:44 +01:00
Joost Lekkerkerker c63aaec09e Set SmartThings suggested display precision (#139470) 2025-02-28 17:06:43 +01:00
Joost Lekkerkerker d8bf47c101 Only lowercase SmartThings media input source if we have it (#139468) 2025-02-28 17:06:42 +01:00
Joost Lekkerkerker 736ff8828d Bump pysmartthings to 2.1.0 (#139460) 2025-02-28 17:06:41 +01:00
Josef Zweck b501999a4c Improve onedrive migration (#139458) 2025-02-28 17:06:40 +01:00
Jan-Philipp Benecke 3985f1c6c8 Change webdav namespace to absolut URI (#139456)
* Change webdav namespace to absolut URI

* Add const file
2025-02-28 17:06:39 +01:00
Joost Lekkerkerker 46ec3987a8 Bump pysmartthings to 2.0.1 (#139454) 2025-02-28 17:06:39 +01:00
Joost Lekkerkerker df4e5a54e3 Fix SmartThings diagnostics (#139447) 2025-02-28 17:06:38 +01:00
J. Diego Rodríguez Royo d8a259044f Bump aiohomeconnect to 0.15.1 (#139445) 2025-02-28 17:06:37 +01:00
Michael Hansen 0891669aee Move climate intent to homeassistant integration (#139371)
* Move climate intent to homeassistant integration

* Move get temperature intent to intent integration

* Clean up old test
2025-02-28 17:06:36 +01:00
Marcel van der Veldt 83c0351338 Add new mediatypes to Music Assistant integration (#139338)
* Bump Music Assistant client to 1.1.0

* Add some casts to help mypy

* Add handling of the new media types in Music Assistant

* mypy cleanup

* lint

* update snapshot

* Adjust tests

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-02-28 17:06:35 +01:00
Jeef c5e5fe555d Bump weatherflow4py to 1.3.1 (#135529)
* version bump of dep

* update requirements
2025-02-28 17:06:34 +01:00
Bram Kragten 345ba73777 Bump version to 2025.3.0b1 2025-02-27 16:48:00 +01:00
Bram Kragten e4200a79a2 Update frontend to 20250227.0 (#139437) 2025-02-27 16:47:52 +01:00
Marcel van der Veldt 381fa65ba0 Fix Music Assistant media player entity features (#139428)
* Fix Music Assistant supported media player features

* Update supported features when player config changes

* Add tests
2025-02-27 16:47:51 +01:00
starkillerOG 16314711b8 Bump reolink-aio to 0.12.1 (#139427) 2025-02-27 16:47:50 +01:00
J. Nick Koston 553abe4a4a Bump bleak-esphome to 2.8.0 (#139426) 2025-02-27 16:47:49 +01:00
Joost Lekkerkerker 6a1bbdb3a7 Add diagnostics to SmartThings (#139423) 2025-02-27 16:47:48 +01:00
Paulus Schoutsen 59d92c75bd Fix conversation agent fallback (#139421) 2025-02-27 16:47:47 +01:00
J. Nick Koston 7732e6878e Bump habluetooth to 3.24.1 (#139420) 2025-02-27 16:47:46 +01:00
Joost Lekkerkerker 2cde317d59 Bump pysmartthings to 2.0.0 (#139418)
* Bump pysmartthings to 2.0.0

* Fix

* Fix

* Fix

* Fix
2025-02-27 16:47:45 +01:00
Josef Zweck 0c08430507 Bump onedrive to 0.0.12 (#139410)
* Bump onedrive to 0.0.12

* Add alternative name
2025-02-27 16:47:45 +01:00
J. Diego Rodríguez Royo fa6d7d5e3c Fix fetch options error for Home connect (#139392)
* Handle errors when obtaining options definitions

* Don't fetch program options if the program key is unknown

* Test to ensure that available program endpoint is not called on unknown program
2025-02-27 16:47:43 +01:00
Michael Hansen 585b950a46 Bump intents to 2025.2.26 (#139387) 2025-02-27 16:47:42 +01:00
puddly 3effc2e182 Bump ZHA to 0.0.51 (#139383)
* Bump ZHA to 0.0.51

* Fix unit tests not accounting for primary entities
2025-02-27 16:47:42 +01:00
fwestenberg 0e1602ff71 Bump stookwijzer==1.6.1 (#139380) 2025-02-27 16:47:41 +01:00
Bram Kragten 693584ce29 Bump version to 2025.3.0b0 2025-02-26 18:23:01 +01:00
392 changed files with 16027 additions and 4320 deletions
+1 -1
View File
@@ -531,7 +531,7 @@ jobs:
- name: Generate artifact attestation
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
uses: actions/attest-build-provenance@bd77c077858b8d561b7a36cbe48ef4cc642ca39d # v2.2.2
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
+75 -25
View File
@@ -89,6 +89,7 @@ jobs:
test_groups: ${{ steps.info.outputs.test_groups }}
tests_glob: ${{ steps.info.outputs.tests_glob }}
tests: ${{ steps.info.outputs.tests }}
lint_only: ${{ steps.info.outputs.lint_only }}
skip_coverage: ${{ steps.info.outputs.skip_coverage }}
runs-on: ubuntu-24.04
steps:
@@ -142,6 +143,7 @@ jobs:
test_group_count=10
tests="[]"
tests_glob=""
lint_only=""
skip_coverage=""
if [[ "${{ steps.integrations.outputs.changes }}" != "[]" ]];
@@ -192,6 +194,17 @@ jobs:
test_full_suite="true"
fi
if [[ "${{ github.event.inputs.lint-only }}" == "true" ]] \
|| [[ "${{ github.event.inputs.pylint-only }}" == "true" ]] \
|| [[ "${{ github.event.inputs.mypy-only }}" == "true" ]] \
|| [[ "${{ github.event.inputs.audit-licenses-only }}" == "true" ]] \
|| [[ "${{ github.event_name }}" == "push" \
&& "${{ github.event.repository.full_name }}" != "home-assistant/core" ]];
then
lint_only="true"
skip_coverage="true"
fi
if [[ "${{ github.event.inputs.skip-coverage }}" == "true" ]] \
|| [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci-skip-coverage') }}" == "true" ]];
then
@@ -217,6 +230,8 @@ jobs:
echo "tests=${tests}" >> $GITHUB_OUTPUT
echo "tests_glob: ${tests_glob}"
echo "tests_glob=${tests_glob}" >> $GITHUB_OUTPUT
echo "lint_only": ${lint_only}
echo "lint_only=${lint_only}" >> $GITHUB_OUTPUT
echo "skip_coverage: ${skip_coverage}"
echo "skip_coverage=${skip_coverage}" >> $GITHUB_OUTPUT
@@ -829,11 +844,7 @@ jobs:
prepare-pytest-full:
runs-on: ubuntu-24.04
if: |
(github.event_name != 'push' || github.event.repository.full_name == 'home-assistant/core')
&& github.event.inputs.lint-only != 'true'
&& github.event.inputs.pylint-only != 'true'
&& github.event.inputs.mypy-only != 'true'
&& github.event.inputs.audit-licenses-only != 'true'
needs.info.outputs.lint_only != 'true'
&& needs.info.outputs.test_full_suite == 'true'
needs:
- info
@@ -886,11 +897,7 @@ jobs:
pytest-full:
runs-on: ubuntu-24.04
if: |
(github.event_name != 'push' || github.event.repository.full_name == 'home-assistant/core')
&& github.event.inputs.lint-only != 'true'
&& github.event.inputs.pylint-only != 'true'
&& github.event.inputs.mypy-only != 'true'
&& github.event.inputs.audit-licenses-only != 'true'
needs.info.outputs.lint_only != 'true'
&& needs.info.outputs.test_full_suite == 'true'
needs:
- info
@@ -962,6 +969,7 @@ jobs:
if [[ "${{ needs.info.outputs.skip_coverage }}" != "true" ]]; then
cov_params+=(--cov="homeassistant")
cov_params+=(--cov-report=xml)
cov_params+=(--junitxml=junit.xml -o junit_family=legacy)
fi
echo "Test group ${{ matrix.group }}: $(sed -n "${{ matrix.group }},1p" pytest_buckets.txt)"
@@ -992,6 +1000,12 @@ jobs:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
overwrite: true
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@v4.6.1
with:
name: test-results-full-${{ matrix.python-version }}-${{ matrix.group }}
path: junit.xml
- name: Remove pytest_buckets
run: rm pytest_buckets.txt
- name: Check dirty
@@ -1009,11 +1023,7 @@ jobs:
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping -uroot -ppassword" --health-interval=5s --health-timeout=2s --health-retries=3
if: |
(github.event_name != 'push' || github.event.repository.full_name == 'home-assistant/core')
&& github.event.inputs.lint-only != 'true'
&& github.event.inputs.pylint-only != 'true'
&& github.event.inputs.mypy-only != 'true'
&& github.event.inputs.audit-licenses-only != 'true'
needs.info.outputs.lint_only != 'true'
&& needs.info.outputs.mariadb_groups != '[]'
needs:
- info
@@ -1088,6 +1098,7 @@ jobs:
cov_params+=(--cov="homeassistant.components.recorder")
cov_params+=(--cov-report=xml)
cov_params+=(--cov-report=term-missing)
cov_params+=(--junitxml=junit.xml -o junit_family=legacy)
fi
python3 -b -X dev -m pytest \
@@ -1122,6 +1133,13 @@ jobs:
steps.pytest-partial.outputs.mariadb }}
path: coverage.xml
overwrite: true
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@v4.6.1
with:
name: test-results-mariadb-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
path: junit.xml
- name: Check dirty
run: |
./script/check_dirty
@@ -1137,11 +1155,7 @@ jobs:
POSTGRES_PASSWORD: password
options: --health-cmd="pg_isready -hlocalhost -Upostgres" --health-interval=5s --health-timeout=2s --health-retries=3
if: |
(github.event_name != 'push' || github.event.repository.full_name == 'home-assistant/core')
&& github.event.inputs.lint-only != 'true'
&& github.event.inputs.pylint-only != 'true'
&& github.event.inputs.mypy-only != 'true'
&& github.event.inputs.audit-licenses-only != 'true'
needs.info.outputs.lint_only != 'true'
&& needs.info.outputs.postgresql_groups != '[]'
needs:
- info
@@ -1218,6 +1232,7 @@ jobs:
cov_params+=(--cov="homeassistant.components.recorder")
cov_params+=(--cov-report=xml)
cov_params+=(--cov-report=term-missing)
cov_params+=(--junitxml=junit.xml -o junit_family=legacy)
fi
python3 -b -X dev -m pytest \
@@ -1253,6 +1268,13 @@ jobs:
steps.pytest-partial.outputs.postgresql }}
path: coverage.xml
overwrite: true
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@v4.6.1
with:
name: test-results-postgres-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
path: junit.xml
- name: Check dirty
run: |
./script/check_dirty
@@ -1285,11 +1307,7 @@ jobs:
pytest-partial:
runs-on: ubuntu-24.04
if: |
(github.event_name != 'push' || github.event.repository.full_name == 'home-assistant/core')
&& github.event.inputs.lint-only != 'true'
&& github.event.inputs.pylint-only != 'true'
&& github.event.inputs.mypy-only != 'true'
&& github.event.inputs.audit-licenses-only != 'true'
needs.info.outputs.lint_only != 'true'
&& needs.info.outputs.tests_glob
&& needs.info.outputs.test_full_suite == 'false'
needs:
@@ -1365,6 +1383,7 @@ jobs:
cov_params+=(--cov="homeassistant.components.${{ matrix.group }}")
cov_params+=(--cov-report=xml)
cov_params+=(--cov-report=term-missing)
cov_params+=(--junitxml=junit.xml -o junit_family=legacy)
fi
python3 -b -X dev -m pytest \
@@ -1394,6 +1413,12 @@ jobs:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
overwrite: true
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@v4.6.1
with:
name: test-results-partial-${{ matrix.python-version }}-${{ matrix.group }}
path: junit.xml
- name: Check dirty
run: |
./script/check_dirty
@@ -1419,3 +1444,28 @@ jobs:
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
upload-test-results:
name: Upload test results to Codecov
# codecov/test-results-action currently doesn't support tokenless uploads
# therefore we can't run it on forks
if: ${{ (github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork) && needs.info.outputs.skip_coverage != 'true' && !cancelled() }}
runs-on: ubuntu-24.04
needs:
- info
- pytest-partial
- pytest-full
- pytest-postgres
- pytest-mariadb
timeout-minutes: 10
steps:
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.1.9
with:
pattern: test-results-*
- name: Upload test results to Codecov
uses: codecov/test-results-action@v1
with:
fail_ci_if_error: true
verbose: true
token: ${{ secrets.CODECOV_TOKEN }}
+1
View File
@@ -69,6 +69,7 @@ test-reports/
test-results.xml
test-output.xml
pytest-*.txt
junit.xml
# Translations
*.mo
+1
View File
@@ -136,6 +136,7 @@ homeassistant.components.clicksend.*
homeassistant.components.climate.*
homeassistant.components.cloud.*
homeassistant.components.co2signal.*
homeassistant.components.comelit.*
homeassistant.components.command_line.*
homeassistant.components.config.*
homeassistant.components.configurator.*
Generated
+2 -2
View File
@@ -1529,8 +1529,8 @@ build.json @home-assistant/supervisor
/tests/components/tedee/ @patrickhilker @zweckj
/homeassistant/components/tellduslive/ @fredrike
/tests/components/tellduslive/ @fredrike
/homeassistant/components/template/ @PhracturedBlue @home-assistant/core
/tests/components/template/ @PhracturedBlue @home-assistant/core
/homeassistant/components/template/ @Petro31 @PhracturedBlue @home-assistant/core
/tests/components/template/ @Petro31 @PhracturedBlue @home-assistant/core
/homeassistant/components/tesla_fleet/ @Bre77
/tests/components/tesla_fleet/ @Bre77
/homeassistant/components/tesla_wall_connector/ @einarhauks
+7 -6
View File
@@ -81,6 +81,7 @@ from .helpers import (
entity,
entity_registry,
floor_registry,
frame,
issue_registry,
label_registry,
recorder,
@@ -441,9 +442,10 @@ async def async_load_base_functionality(hass: core.HomeAssistant) -> None:
if DATA_REGISTRIES_LOADED in hass.data:
return
hass.data[DATA_REGISTRIES_LOADED] = None
translation.async_setup(hass)
entity.async_setup(hass)
frame.async_setup(hass)
template.async_setup(hass)
translation.async_setup(hass)
await asyncio.gather(
create_eager_task(get_internal_store_manager(hass).async_initialize()),
create_eager_task(area_registry.async_load(hass)),
@@ -664,11 +666,10 @@ def _create_log_file(
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
err_log_path, backupCount=1
)
try:
err_handler.doRollover()
except OSError as err:
_LOGGER.error("Error rolling over log file: %s", err)
try:
err_handler.doRollover()
except OSError as err:
_LOGGER.error("Error rolling over log file: %s", err)
return err_handler
@@ -2,6 +2,7 @@
from __future__ import annotations
from decimal import Decimal
import logging
from typing import Any
@@ -14,6 +15,7 @@ from homeassistant.components.climate import (
FAN_MEDIUM,
ClimateEntity,
ClimateEntityFeature,
HVACAction,
HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
@@ -49,6 +51,14 @@ ADVANTAGE_AIR_MYTEMP_ENABLED = "climateControlModeEnabled"
ADVANTAGE_AIR_HEAT_TARGET = "myAutoHeatTargetTemp"
ADVANTAGE_AIR_COOL_TARGET = "myAutoCoolTargetTemp"
ADVANTAGE_AIR_MYFAN = "autoAA"
ADVANTAGE_AIR_MYAUTO_MODE_SET = "myAutoModeCurrentSetMode"
HVAC_ACTIONS = {
"cool": HVACAction.COOLING,
"heat": HVACAction.HEATING,
"vent": HVACAction.FAN,
"dry": HVACAction.DRYING,
}
HVAC_MODES = [
HVACMode.OFF,
@@ -175,6 +185,17 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
return ADVANTAGE_AIR_HVAC_MODES.get(self._ac["mode"])
return HVACMode.OFF
@property
def hvac_action(self) -> HVACAction | None:
"""Return the current running HVAC action."""
if self._ac["state"] == ADVANTAGE_AIR_STATE_OFF:
return HVACAction.OFF
if self._ac["mode"] == "myauto":
return HVAC_ACTIONS.get(
self._ac.get(ADVANTAGE_AIR_MYAUTO_MODE_SET, HVACAction.OFF)
)
return HVAC_ACTIONS.get(self._ac["mode"])
@property
def fan_mode(self) -> str | None:
"""Return the current fan modes."""
@@ -273,6 +294,22 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
return HVACMode.HEAT_COOL
return HVACMode.OFF
@property
def hvac_action(self) -> HVACAction | None:
"""Return the HVAC action, inheriting from master AC if zone is open but idle if air is <= 5%."""
if self._ac["state"] == ADVANTAGE_AIR_STATE_OFF:
return HVACAction.OFF
master_action = HVAC_ACTIONS.get(self._ac["mode"], HVACAction.OFF)
if self._ac["mode"] == "myauto":
master_action = HVAC_ACTIONS.get(
str(self._ac.get(ADVANTAGE_AIR_MYAUTO_MODE_SET)), HVACAction.OFF
)
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
if self._zone["value"] <= Decimal(5):
return HVACAction.IDLE
return master_action
return HVACAction.OFF
@property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
@@ -7,3 +7,4 @@ ADVANTAGE_AIR_STATE_CLOSE = "close"
ADVANTAGE_AIR_STATE_ON = "on"
ADVANTAGE_AIR_STATE_OFF = "off"
ADVANTAGE_AIR_AUTOFAN_ENABLED = "aaAutoFanModeEnabled"
ADVANTAGE_AIR_NIGHT_MODE_ENABLED = "quietNightModeEnabled"
@@ -41,7 +41,7 @@ async def async_setup_entry(
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.BLIND)
)
elif thing["channelDipState"] == 3: # 3 = "Garage door"
elif thing["channelDipState"] in [3, 10]: # 3 & 10 = "Garage door"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.GARAGE)
)
@@ -9,6 +9,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .const import (
ADVANTAGE_AIR_AUTOFAN_ENABLED,
ADVANTAGE_AIR_NIGHT_MODE_ENABLED,
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
)
@@ -32,6 +33,8 @@ async def async_setup_entry(
entities.append(AdvantageAirFreshAir(instance, ac_key))
if ADVANTAGE_AIR_AUTOFAN_ENABLED in ac_device["info"]:
entities.append(AdvantageAirMyFan(instance, ac_key))
if ADVANTAGE_AIR_NIGHT_MODE_ENABLED in ac_device["info"]:
entities.append(AdvantageAirNightMode(instance, ac_key))
if things := instance.coordinator.data.get("myThings"):
entities.extend(
AdvantageAirRelay(instance, thing)
@@ -93,6 +96,32 @@ class AdvantageAirMyFan(AdvantageAirAcEntity, SwitchEntity):
await self.async_update_ac({ADVANTAGE_AIR_AUTOFAN_ENABLED: False})
class AdvantageAirNightMode(AdvantageAirAcEntity, SwitchEntity):
"""Representation of Advantage 'MySleep$aver' Mode control."""
_attr_icon = "mdi:weather-night"
_attr_name = "MySleep$aver"
_attr_device_class = SwitchDeviceClass.SWITCH
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
"""Initialize an Advantage Air Night Mode control."""
super().__init__(instance, ac_key)
self._attr_unique_id += "-nightmode"
@property
def is_on(self) -> bool:
"""Return the Night Mode status."""
return self._ac[ADVANTAGE_AIR_NIGHT_MODE_ENABLED]
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn Night Mode on."""
await self.async_update_ac({ADVANTAGE_AIR_NIGHT_MODE_ENABLED: True})
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn Night Mode off."""
await self.async_update_ac({ADVANTAGE_AIR_NIGHT_MODE_ENABLED: False})
class AdvantageAirRelay(AdvantageAirThingEntity, SwitchEntity):
"""Representation of Advantage Air Thing."""
@@ -8,7 +8,7 @@ from python_homeassistant_analytics import (
HomeassistantAnalyticsClient,
HomeassistantAnalyticsConnectionError,
)
from python_homeassistant_analytics.models import IntegrationType
from python_homeassistant_analytics.models import Environment, IntegrationType
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
@@ -81,7 +81,7 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
)
try:
addons = await client.get_addons()
integrations = await client.get_integrations()
integrations = await client.get_integrations(Environment.NEXT)
custom_integrations = await client.get_custom_integrations()
except HomeassistantAnalyticsConnectionError:
LOGGER.exception("Error connecting to Home Assistant analytics")
@@ -165,7 +165,7 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlow):
)
try:
addons = await client.get_addons()
integrations = await client.get_integrations()
integrations = await client.get_integrations(Environment.NEXT)
custom_integrations = await client.get_custom_integrations()
except HomeassistantAnalyticsConnectionError:
LOGGER.exception("Error connecting to Home Assistant analytics")
@@ -305,7 +305,9 @@ class AnthropicConversationEntity(
intent_response = intent.IntentResponse(language=user_input.language)
intent_response.async_set_speech(response_content.content or "")
return conversation.ConversationResult(
response=intent_response, conversation_id=chat_log.conversation_id
response=intent_response,
conversation_id=chat_log.conversation_id,
continue_conversation=chat_log.continue_conversation,
)
async def _async_entry_update_listener(
@@ -0,0 +1 @@
"""Virtual integration: Apollo Automation."""
@@ -0,0 +1,6 @@
{
"domain": "apollo_automation",
"name": "Apollo Automation",
"integration_type": "virtual",
"supported_by": "esphome"
}
@@ -20,6 +20,9 @@ import hass_nabucasa
import voluptuous as vol
from homeassistant.components import conversation, stt, tts, wake_word, websocket_api
from homeassistant.components.tts import (
generate_media_source_id as tts_generate_media_source_id,
)
from homeassistant.const import ATTR_SUPPORTED_FEATURES, MATCH_ALL
from homeassistant.core import Context, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
@@ -1272,10 +1275,26 @@ class PipelineRun:
)
)
try:
# Synthesize audio and get URL
tts_media_id = tts_generate_media_source_id(
self.hass,
tts_input,
engine=self.tts_stream.engine,
language=self.tts_stream.language,
options=self.tts_stream.options,
)
except Exception as src_error:
_LOGGER.exception("Unexpected error during text-to-speech")
raise TextToSpeechError(
code="tts-failed",
message="Unexpected error during text-to-speech",
) from src_error
self.tts_stream.async_set_message(tts_input)
tts_output = {
"media_id": self.tts_stream.media_source_id,
"media_id": tts_media_id,
"url": self.tts_stream.url,
"mime_type": self.tts_stream.content_type,
}
@@ -23,6 +23,9 @@ from homeassistant.components.assist_pipeline import (
vad,
)
from homeassistant.components.media_player import async_process_play_media_url
from homeassistant.components.tts import (
generate_media_source_id as tts_generate_media_source_id,
)
from homeassistant.core import Context, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import chat_session, entity
@@ -481,9 +484,6 @@ class AssistSatelliteEntity(entity.Entity):
pipeline_id = self._resolve_pipeline()
pipeline = async_get_pipeline(self.hass, pipeline_id)
if pipeline.tts_engine is None:
raise HomeAssistantError("Pipeline has no TTS engine configured")
tts_options: dict[str, Any] = {}
if pipeline.tts_voice is not None:
tts_options[tts.ATTR_VOICE] = pipeline.tts_voice
@@ -491,15 +491,14 @@ class AssistSatelliteEntity(entity.Entity):
if self.tts_options is not None:
tts_options.update(self.tts_options)
stream = tts.async_create_stream(
media_id = tts_generate_media_source_id(
self.hass,
message,
engine=pipeline.tts_engine,
language=pipeline.tts_language,
options=tts_options,
)
stream.async_set_message(message)
media_id = stream.url
original_media_id = stream.media_source_id
original_media_id = media_id
if media_source.is_media_source_id(media_id):
if not media_id_source:
@@ -141,7 +141,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Delete a backup file."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
return
raise BackupNotFound(f"Backup {backup_id} not found")
await self._client.delete_blob(blob.name)
@handle_backup_errors
@@ -163,11 +163,11 @@ class AzureStorageBackupAgent(BackupAgent):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
) -> AgentBackup:
"""Return a backup."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
return None
raise BackupNotFound(f"Backup {backup_id} not found")
return AgentBackup.from_dict(json.loads(blob.metadata["backup_metadata"]))
+9 -2
View File
@@ -41,6 +41,8 @@ class BackupAgent(abc.ABC):
) -> AsyncIterator[bytes]:
"""Download a backup file.
Raises BackupNotFound if the backup does not exist.
:param backup_id: The ID of the backup that was returned in async_list_backups.
:return: An async iterator that yields bytes.
"""
@@ -67,6 +69,8 @@ class BackupAgent(abc.ABC):
) -> None:
"""Delete a backup file.
Raises BackupNotFound if the backup does not exist.
:param backup_id: The ID of the backup that was returned in async_list_backups.
"""
@@ -79,8 +83,11 @@ class BackupAgent(abc.ABC):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
"""Return a backup."""
) -> AgentBackup:
"""Return a backup.
Raises BackupNotFound if the backup does not exist.
"""
class LocalBackupAgent(BackupAgent):
+4 -7
View File
@@ -88,13 +88,13 @@ class CoreLocalBackupAgent(LocalBackupAgent):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
) -> AgentBackup:
"""Return a backup."""
if not self._loaded_backups:
await self._load_backups()
if backup_id not in self._backups:
return None
raise BackupNotFound(f"Backup {backup_id} not found")
backup, backup_path = self._backups[backup_id]
if not await self._hass.async_add_executor_job(backup_path.exists):
@@ -107,7 +107,7 @@ class CoreLocalBackupAgent(LocalBackupAgent):
backup_path,
)
self._backups.pop(backup_id)
return None
raise BackupNotFound(f"Backup {backup_id} not found")
return backup
@@ -130,10 +130,7 @@ class CoreLocalBackupAgent(LocalBackupAgent):
if not self._loaded_backups:
await self._load_backups()
try:
backup_path = self.get_backup_path(backup_id)
except BackupNotFound:
return
backup_path = self.get_backup_path(backup_id)
await self._hass.async_add_executor_job(backup_path.unlink, True)
LOGGER.debug("Deleted backup located at %s", backup_path)
self._backups.pop(backup_id)
+15 -4
View File
@@ -15,6 +15,7 @@ from multidict import istr
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import frame
from homeassistant.util import slugify
from . import util
@@ -59,11 +60,19 @@ class DownloadBackupView(HomeAssistantView):
if agent_id not in manager.backup_agents:
return Response(status=HTTPStatus.BAD_REQUEST)
agent = manager.backup_agents[agent_id]
backup = await agent.async_get_backup(backup_id)
try:
backup = await agent.async_get_backup(backup_id)
except BackupNotFound:
return Response(status=HTTPStatus.NOT_FOUND)
# We don't need to check if the path exists, aiohttp.FileResponse will handle
# that
if backup is None:
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not backup:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
return Response(status=HTTPStatus.NOT_FOUND)
headers = {
@@ -92,6 +101,8 @@ class DownloadBackupView(HomeAssistantView):
) -> StreamResponse | FileResponse | Response:
if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id]
# We don't need to check if the path exists, aiohttp.FileResponse will
# handle that
path = local_agent.get_backup_path(backup_id)
return FileResponse(path=path.as_posix(), headers=headers)
+42 -3
View File
@@ -30,6 +30,7 @@ from homeassistant.backup_restore import (
from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
frame,
instance_id,
integration_platform,
issue_registry as ir,
@@ -64,6 +65,7 @@ from .models import (
AgentBackup,
BackupError,
BackupManagerError,
BackupNotFound,
BackupReaderWriterError,
BaseBackup,
Folder,
@@ -648,6 +650,8 @@ class BackupManager:
)
for idx, result in enumerate(get_backup_results):
agent_id = agent_ids[idx]
if isinstance(result, BackupNotFound):
continue
if isinstance(result, BackupAgentError):
agent_errors[agent_id] = result
continue
@@ -659,7 +663,14 @@ class BackupManager:
continue
if isinstance(result, BaseException):
raise result # unexpected error
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not result:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
continue
if backup is None:
if known_backup := self.known_backups.get(backup_id):
@@ -723,6 +734,8 @@ class BackupManager:
)
for idx, result in enumerate(delete_backup_results):
agent_id = agent_ids[idx]
if isinstance(result, BackupNotFound):
continue
if isinstance(result, BackupAgentError):
agent_errors[agent_id] = result
continue
@@ -832,7 +845,7 @@ class BackupManager:
agent_errors = {
backup_id: error
for backup_id, error in zip(backup_ids, delete_results, strict=True)
if error
if error and not isinstance(error, BackupNotFound)
}
if agent_errors:
LOGGER.error(
@@ -1264,7 +1277,20 @@ class BackupManager:
) -> None:
"""Initiate restoring a backup."""
agent = self.backup_agents[agent_id]
if not await agent.async_get_backup(backup_id):
try:
backup = await agent.async_get_backup(backup_id)
except BackupNotFound as err:
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
) from err
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not backup:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
)
@@ -1352,7 +1378,20 @@ class BackupManager:
agent = self.backup_agents[agent_id]
except KeyError as err:
raise BackupManagerError(f"Invalid agent selected: {agent_id}") from err
if not await agent.async_get_backup(backup_id):
try:
backup = await agent.async_get_backup(backup_id)
except BackupNotFound as err:
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
) from err
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not backup:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
)
+1 -1
View File
@@ -21,6 +21,7 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.CLIMATE,
Platform.EVENT,
Platform.FAN,
Platform.LIGHT,
Platform.SELECT,
@@ -28,7 +29,6 @@ PLATFORMS = [
Platform.TIME,
]
KEEP_ALIVE_INTERVAL = timedelta(minutes=1)
SYNC_TIME_INTERVAL = timedelta(hours=1)
+91
View File
@@ -0,0 +1,91 @@
"""Support for Balboa events."""
from __future__ import annotations
from datetime import datetime, timedelta
from pybalboa import EVENT_UPDATE, SpaClient
from homeassistant.components.event import EventEntity
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from . import BalboaConfigEntry
from .entity import BalboaEntity
FAULT = "fault"
FAULT_DATE = "fault_date"
REQUEST_FAULT_LOG_INTERVAL = timedelta(minutes=5)
FAULT_MESSAGE_CODE_MAP: dict[int, str] = {
15: "sensor_out_of_sync",
16: "low_flow",
17: "flow_failed",
18: "settings_reset",
19: "priming_mode",
20: "clock_failed",
21: "settings_reset",
22: "memory_failure",
26: "service_sensor_sync",
27: "heater_dry",
28: "heater_may_be_dry",
29: "water_too_hot",
30: "heater_too_hot",
31: "sensor_a_fault",
32: "sensor_b_fault",
34: "pump_stuck",
35: "hot_fault",
36: "gfci_test_failed",
37: "standby_mode",
}
FAULT_EVENT_TYPES = sorted(set(FAULT_MESSAGE_CODE_MAP.values()))
async def async_setup_entry(
hass: HomeAssistant,
entry: BalboaConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the spa's events."""
async_add_entities([BalboaEventEntity(entry.runtime_data)])
class BalboaEventEntity(BalboaEntity, EventEntity):
"""Representation of a Balboa event entity."""
_attr_event_types = FAULT_EVENT_TYPES
_attr_translation_key = FAULT
def __init__(self, spa: SpaClient) -> None:
"""Initialize a Balboa event entity."""
super().__init__(spa, FAULT)
@callback
def _async_handle_event(self) -> None:
"""Handle the fault event."""
if not (fault := self._client.fault):
return
fault_date = fault.fault_datetime.isoformat()
if self.state_attributes.get(FAULT_DATE) != fault_date:
self._trigger_event(
FAULT_MESSAGE_CODE_MAP.get(fault.message_code, fault.message),
{FAULT_DATE: fault_date, "code": fault.message_code},
)
self.async_write_ha_state()
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_added_to_hass()
self.async_on_remove(self._client.on(EVENT_UPDATE, self._async_handle_event))
async def request_fault_log(now: datetime | None = None) -> None:
"""Request the most recent fault log."""
await self._client.request_fault_log()
await request_fault_log()
self.async_on_remove(
async_track_time_interval(
self.hass, request_fault_log, REQUEST_FAULT_LOG_INTERVAL
)
)
@@ -57,6 +57,35 @@
}
}
},
"event": {
"fault": {
"name": "Fault",
"state_attributes": {
"event_type": {
"state": {
"sensor_out_of_sync": "Sensors are out of sync",
"low_flow": "The water flow is low",
"flow_failed": "The water flow has failed",
"settings_reset": "The settings have been reset",
"priming_mode": "Priming mode",
"clock_failed": "The clock has failed",
"memory_failure": "Program memory failure",
"service_sensor_sync": "Sensors are out of sync -- call for service",
"heater_dry": "The heater is dry",
"heater_may_be_dry": "The heater may be dry",
"water_too_hot": "The water is too hot",
"heater_too_hot": "The heater is too hot",
"sensor_a_fault": "Sensor A fault",
"sensor_b_fault": "Sensor B fault",
"pump_stuck": "A pump may be stuck on",
"hot_fault": "Hot fault",
"gfci_test_failed": "The GFCI test failed",
"standby_mode": "Standby mode (hold mode)"
}
}
}
}
},
"fan": {
"pump": {
"name": "Pump {index}"
@@ -19,8 +19,8 @@
"bleak-retry-connector==3.9.0",
"bluetooth-adapters==0.21.4",
"bluetooth-auto-recovery==1.4.4",
"bluetooth-data-tools==1.23.4",
"dbus-fast==2.33.0",
"habluetooth==3.24.1"
"bluetooth-data-tools==1.25.0",
"dbus-fast==2.39.3",
"habluetooth==3.25.0"
]
}
@@ -153,6 +153,27 @@ def _has_min_duration(
return validate
def _has_positive_interval(
start_key: str, end_key: str, duration_key: str
) -> Callable[[dict[str, Any]], dict[str, Any]]:
"""Verify that the time span between start and end is greater than zero."""
def validate(obj: dict[str, Any]) -> dict[str, Any]:
if (duration := obj.get(duration_key)) is not None:
if duration <= datetime.timedelta(seconds=0):
raise vol.Invalid(f"Expected positive duration ({duration})")
return obj
if (start := obj.get(start_key)) and (end := obj.get(end_key)):
if start >= end:
raise vol.Invalid(
f"Expected end time to be after start time ({start}, {end})"
)
return obj
return validate
def _has_same_type(*keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]:
"""Verify that all values are of the same type."""
@@ -281,6 +302,7 @@ SERVICE_GET_EVENTS_SCHEMA: Final = vol.All(
),
}
),
_has_positive_interval(EVENT_START_DATETIME, EVENT_END_DATETIME, EVENT_DURATION),
)
@@ -870,6 +892,7 @@ async def async_get_events_service(
end = start + service_call.data[EVENT_DURATION]
else:
end = service_call.data[EVENT_END_DATETIME]
calendar_event_list = await calendar.async_get_events(
calendar.hass, dt_util.as_local(start), dt_util.as_local(end)
)
+12 -15
View File
@@ -18,7 +18,12 @@ from hass_nabucasa.cloud_api import (
)
from hass_nabucasa.files import FilesError, StorageType, calculate_b64md5
from homeassistant.components.backup import AgentBackup, BackupAgent, BackupAgentError
from homeassistant.components.backup import (
AgentBackup,
BackupAgent,
BackupAgentError,
BackupNotFound,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import ChunkAsyncStreamIterator
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -90,9 +95,7 @@ class CloudBackupAgent(BackupAgent):
:param backup_id: The ID of the backup that was returned in async_list_backups.
:return: An async iterator that yields bytes.
"""
if not (backup := await self._async_get_backup(backup_id)):
raise BackupAgentError("Backup not found")
backup = await self._async_get_backup(backup_id)
try:
content = await self._cloud.files.download(
storage_type=StorageType.BACKUP,
@@ -171,9 +174,7 @@ class CloudBackupAgent(BackupAgent):
:param backup_id: The ID of the backup that was returned in async_list_backups.
"""
if not (backup := await self._async_get_backup(backup_id)):
return
backup = await self._async_get_backup(backup_id)
try:
await async_files_delete_file(
self._cloud,
@@ -204,16 +205,12 @@ class CloudBackupAgent(BackupAgent):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
) -> AgentBackup:
"""Return a backup."""
if not (backup := await self._async_get_backup(backup_id)):
return None
backup = await self._async_get_backup(backup_id)
return AgentBackup.from_dict(backup["Metadata"])
async def _async_get_backup(
self,
backup_id: str,
) -> FilesHandlerListEntry | None:
async def _async_get_backup(self, backup_id: str) -> FilesHandlerListEntry:
"""Return a backup."""
backups = await self._async_list_backups()
@@ -221,4 +218,4 @@ class CloudBackupAgent(BackupAgent):
if backup["Metadata"]["backup_id"] == backup_id:
return backup
return None
raise BackupNotFound(f"Backup {backup_id} not found")
+1 -1
View File
@@ -13,6 +13,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["acme", "hass_nabucasa", "snitun"],
"requirements": ["hass-nabucasa==0.92.0"],
"requirements": ["hass-nabucasa==0.94.0"],
"single_config_entry": true
}
@@ -6,7 +6,7 @@ import logging
from typing import cast
from aiocomelit.api import ComelitVedoAreaObject
from aiocomelit.const import ALARM_AREAS, AlarmAreaState
from aiocomelit.const import AlarmAreaState
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
@@ -56,7 +56,7 @@ async def async_setup_entry(
async_add_entities(
ComelitAlarmEntity(coordinator, device, config_entry.entry_id)
for device in coordinator.data[ALARM_AREAS].values()
for device in coordinator.data["alarm_areas"].values()
)
@@ -92,7 +92,7 @@ class ComelitAlarmEntity(CoordinatorEntity[ComelitVedoSystem], AlarmControlPanel
@property
def _area(self) -> ComelitVedoAreaObject:
"""Return area object."""
return self.coordinator.data[ALARM_AREAS][self._area_index]
return self.coordinator.data["alarm_areas"][self._area_index]
@property
def available(self) -> bool:
@@ -5,7 +5,6 @@ from __future__ import annotations
from typing import cast
from aiocomelit import ComelitVedoZoneObject
from aiocomelit.const import ALARM_ZONES
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@@ -29,7 +28,7 @@ async def async_setup_entry(
async_add_entities(
ComelitVedoBinarySensorEntity(coordinator, device, config_entry.entry_id)
for device in coordinator.data[ALARM_ZONES].values()
for device in coordinator.data["alarm_zones"].values()
)
@@ -49,7 +48,7 @@ class ComelitVedoBinarySensorEntity(
) -> None:
"""Init sensor entity."""
self._api = coordinator.api
self._zone = zone
self._zone_index = zone.index
super().__init__(coordinator)
# Use config_entry.entry_id as base for unique_id
# because no serial number or mac is available
@@ -59,4 +58,6 @@ class ComelitVedoBinarySensorEntity(
@property
def is_on(self) -> bool:
"""Presence detected."""
return self.coordinator.data[ALARM_ZONES][self._zone.index].status_api == "0001"
return (
self.coordinator.data["alarm_zones"][self._zone_index].status_api == "0001"
)
+47 -73
View File
@@ -3,7 +3,7 @@
from __future__ import annotations
from enum import StrEnum
from typing import Any, cast
from typing import Any, TypedDict, cast
from aiocomelit import ComelitSerialBridgeObject
from aiocomelit.const import CLIMATE
@@ -16,7 +16,8 @@ from homeassistant.components.climate import (
UnitOfTemperature,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -42,22 +43,23 @@ class ClimaComelitCommand(StrEnum):
AUTO = "auto"
API_STATUS: dict[str, dict[str, Any]] = {
ClimaComelitMode.OFF: {
"action": "off",
"hvac_mode": HVACMode.OFF,
"hvac_action": HVACAction.OFF,
},
ClimaComelitMode.LOWER: {
"action": "lower",
"hvac_mode": HVACMode.COOL,
"hvac_action": HVACAction.COOLING,
},
ClimaComelitMode.UPPER: {
"action": "upper",
"hvac_mode": HVACMode.HEAT,
"hvac_action": HVACAction.HEATING,
},
class ClimaComelitApiStatus(TypedDict):
"""Comelit Clima API status."""
hvac_mode: HVACMode
hvac_action: HVACAction
API_STATUS: dict[str, ClimaComelitApiStatus] = {
ClimaComelitMode.OFF: ClimaComelitApiStatus(
hvac_mode=HVACMode.OFF, hvac_action=HVACAction.OFF
),
ClimaComelitMode.LOWER: ClimaComelitApiStatus(
hvac_mode=HVACMode.COOL, hvac_action=HVACAction.COOLING
),
ClimaComelitMode.UPPER: ClimaComelitApiStatus(
hvac_mode=HVACMode.HEAT, hvac_action=HVACAction.HEATING
),
}
MODE_TO_ACTION: dict[HVACMode, ClimaComelitCommand] = {
@@ -114,69 +116,41 @@ class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity
self._attr_unique_id = f"{config_entry_entry_id}-{device.index}"
self._attr_device_info = coordinator.platform_device_info(device, device.type)
@property
def _clima(self) -> list[Any]:
"""Return clima device data."""
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
device = self.coordinator.data[CLIMATE][self._device.index]
if not isinstance(device.val, list):
raise HomeAssistantError("Invalid clima data")
# CLIMATE has a 2 item tuple:
# - first for Clima
# - second for Humidifier
return self.coordinator.data[CLIMATE][self._device.index].val[0]
values = device.val[0]
@property
def _api_mode(self) -> str:
"""Return device mode."""
# Values from API: "O", "L", "U"
return self._clima[2]
_active = values[1]
_mode = values[2] # Values from API: "O", "L", "U"
_automatic = values[3] == ClimaComelitMode.AUTO
@property
def _api_active(self) -> bool:
"Return device active/idle."
return self._clima[1]
self._attr_current_temperature = values[0] / 10
@property
def _api_automatic(self) -> bool:
"""Return device in automatic/manual mode."""
return self._clima[3] == ClimaComelitMode.AUTO
self._attr_hvac_action = None
if _mode == ClimaComelitMode.OFF:
self._attr_hvac_action = HVACAction.OFF
if not _active:
self._attr_hvac_action = HVACAction.IDLE
if _mode in API_STATUS:
self._attr_hvac_action = API_STATUS[_mode]["hvac_action"]
@property
def target_temperature(self) -> float:
"""Set target temperature."""
return self._clima[4] / 10
self._attr_hvac_mode = None
if _mode == ClimaComelitMode.OFF:
self._attr_hvac_mode = HVACMode.OFF
if _automatic:
self._attr_hvac_mode = HVACMode.AUTO
if _mode in API_STATUS:
self._attr_hvac_mode = API_STATUS[_mode]["hvac_mode"]
@property
def current_temperature(self) -> float:
"""Return current temperature."""
return self._clima[0] / 10
@property
def hvac_mode(self) -> HVACMode | None:
"""HVAC current mode."""
if self._api_mode == ClimaComelitMode.OFF:
return HVACMode.OFF
if self._api_automatic:
return HVACMode.AUTO
if self._api_mode in API_STATUS:
return API_STATUS[self._api_mode]["hvac_mode"]
return None
@property
def hvac_action(self) -> HVACAction | None:
"""HVAC current action."""
if self._api_mode == ClimaComelitMode.OFF:
return HVACAction.OFF
if not self._api_active:
return HVACAction.IDLE
if self._api_mode in API_STATUS:
return API_STATUS[self._api_mode]["hvac_action"]
return None
self._attr_target_temperature = values[4] / 10
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
+26 -13
View File
@@ -2,18 +2,19 @@
from abc import abstractmethod
from datetime import timedelta
from typing import Any
from typing import TypeVar
from aiocomelit import (
from aiocomelit.api import (
AlarmDataObject,
ComelitCommonApi,
ComeliteSerialBridgeApi,
ComelitSerialBridgeObject,
ComelitVedoApi,
ComelitVedoAreaObject,
ComelitVedoZoneObject,
exceptions,
)
from aiocomelit.api import ComelitCommonApi
from aiocomelit.const import BRIDGE, VEDO
from aiocomelit.exceptions import CannotAuthenticate, CannotConnect, CannotRetrieveData
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -26,7 +27,13 @@ from .const import _LOGGER, DOMAIN
type ComelitConfigEntry = ConfigEntry[ComelitBaseCoordinator]
class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
T = TypeVar(
"T",
bound=dict[str, dict[int, ComelitSerialBridgeObject]] | AlarmDataObject,
)
class ComelitBaseCoordinator(DataUpdateCoordinator[T]):
"""Base coordinator for Comelit Devices."""
_hw_version: str
@@ -81,23 +88,25 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[dict[str, Any]]):
hw_version=self._hw_version,
)
async def _async_update_data(self) -> dict[str, Any]:
async def _async_update_data(self) -> T:
"""Update device data."""
_LOGGER.debug("Polling Comelit %s host: %s", self._device, self._host)
try:
await self.api.login()
return await self._async_update_system_data()
except (exceptions.CannotConnect, exceptions.CannotRetrieveData) as err:
except (CannotConnect, CannotRetrieveData) as err:
raise UpdateFailed(repr(err)) from err
except exceptions.CannotAuthenticate as err:
except CannotAuthenticate as err:
raise ConfigEntryAuthFailed from err
@abstractmethod
async def _async_update_system_data(self) -> dict[str, Any]:
async def _async_update_system_data(self) -> T:
"""Class method for updating data."""
class ComelitSerialBridge(ComelitBaseCoordinator):
class ComelitSerialBridge(
ComelitBaseCoordinator[dict[str, dict[int, ComelitSerialBridgeObject]]]
):
"""Queries Comelit Serial Bridge."""
_hw_version = "20003101"
@@ -115,12 +124,14 @@ class ComelitSerialBridge(ComelitBaseCoordinator):
self.api = ComeliteSerialBridgeApi(host, port, pin)
super().__init__(hass, entry, BRIDGE, host)
async def _async_update_system_data(self) -> dict[str, Any]:
async def _async_update_system_data(
self,
) -> dict[str, dict[int, ComelitSerialBridgeObject]]:
"""Specific method for updating data."""
return await self.api.get_all_devices()
class ComelitVedoSystem(ComelitBaseCoordinator):
class ComelitVedoSystem(ComelitBaseCoordinator[AlarmDataObject]):
"""Queries Comelit VEDO system."""
_hw_version = "VEDO IP"
@@ -138,6 +149,8 @@ class ComelitVedoSystem(ComelitBaseCoordinator):
self.api = ComelitVedoApi(host, port, pin)
super().__init__(hass, entry, VEDO, host)
async def _async_update_system_data(self) -> dict[str, Any]:
async def _async_update_system_data(
self,
) -> AlarmDataObject:
"""Specific method for updating data."""
return await self.api.get_all_areas_and_zones()
+22 -51
View File
@@ -16,8 +16,8 @@ from homeassistant.components.humidifier import (
HumidifierEntity,
HumidifierEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -122,61 +122,32 @@ class ComelitHumidifierEntity(CoordinatorEntity[ComelitSerialBridge], Humidifier
self._active_action = active_action
self._set_command = set_command
@property
def _humidifier(self) -> list[Any]:
"""Return humidifier device data."""
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
device = self.coordinator.data[CLIMATE][self._device.index]
if not isinstance(device.val, list):
raise HomeAssistantError("Invalid clima data")
# CLIMATE has a 2 item tuple:
# - first for Clima
# - second for Humidifier
return self.coordinator.data[CLIMATE][self._device.index].val[1]
values = device.val[1]
@property
def _api_mode(self) -> str:
"""Return device mode."""
# Values from API: "O", "L", "U"
return self._humidifier[2]
_active = values[1]
_mode = values[2] # Values from API: "O", "L", "U"
_automatic = values[3] == HumidifierComelitMode.AUTO
@property
def _api_active(self) -> bool:
"Return device active/idle."
return self._humidifier[1]
self._attr_action = HumidifierAction.IDLE
if _mode == HumidifierComelitMode.OFF:
self._attr_action = HumidifierAction.OFF
if _active and _mode == self._active_mode:
self._attr_action = self._active_action
@property
def _api_automatic(self) -> bool:
"""Return device in automatic/manual mode."""
return self._humidifier[3] == HumidifierComelitMode.AUTO
@property
def target_humidity(self) -> float:
"""Set target humidity."""
return self._humidifier[4] / 10
@property
def current_humidity(self) -> float:
"""Return current humidity."""
return self._humidifier[0] / 10
@property
def is_on(self) -> bool | None:
"""Return true is humidifier is on."""
return self._api_mode == self._active_mode
@property
def mode(self) -> str | None:
"""Return current mode."""
return MODE_AUTO if self._api_automatic else MODE_NORMAL
@property
def action(self) -> HumidifierAction | None:
"""Return current action."""
if self._api_mode == HumidifierComelitMode.OFF:
return HumidifierAction.OFF
if self._api_active and self._api_mode == self._active_mode:
return self._active_action
return HumidifierAction.IDLE
self._attr_current_humidity = values[0] / 10
self._attr_is_on = _mode == self._active_mode
self._attr_mode = MODE_AUTO if _automatic else MODE_NORMAL
self._attr_target_humidity = values[4] / 10
async def async_set_humidity(self, humidity: int) -> None:
"""Set new target humidity."""
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"requirements": ["aiocomelit==0.11.1"]
"requirements": ["aiocomelit==0.11.2"]
}
+11 -8
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
from typing import Final, cast
from aiocomelit import ComelitSerialBridgeObject, ComelitVedoZoneObject
from aiocomelit.const import ALARM_ZONES, BRIDGE, OTHER, AlarmZoneState
from aiocomelit.const import BRIDGE, OTHER, AlarmZoneState
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -82,7 +82,7 @@ async def async_setup_vedo_entry(
coordinator = cast(ComelitVedoSystem, config_entry.runtime_data)
entities: list[ComelitVedoSensorEntity] = []
for device in coordinator.data[ALARM_ZONES].values():
for device in coordinator.data["alarm_zones"].values():
entities.extend(
ComelitVedoSensorEntity(
coordinator, device, config_entry.entry_id, sensor_desc
@@ -119,9 +119,12 @@ class ComelitBridgeSensorEntity(CoordinatorEntity[ComelitSerialBridge], SensorEn
@property
def native_value(self) -> StateType:
"""Sensor value."""
return getattr(
self.coordinator.data[OTHER][self._device.index],
self.entity_description.key,
return cast(
StateType,
getattr(
self.coordinator.data[OTHER][self._device.index],
self.entity_description.key,
),
)
@@ -139,7 +142,7 @@ class ComelitVedoSensorEntity(CoordinatorEntity[ComelitVedoSystem], SensorEntity
) -> None:
"""Init sensor entity."""
self._api = coordinator.api
self._zone = zone
self._zone_index = zone.index
super().__init__(coordinator)
# Use config_entry.entry_id as base for unique_id
# because no serial number or mac is available
@@ -151,7 +154,7 @@ class ComelitVedoSensorEntity(CoordinatorEntity[ComelitVedoSystem], SensorEntity
@property
def _zone_object(self) -> ComelitVedoZoneObject:
"""Zone object."""
return self.coordinator.data[ALARM_ZONES][self._zone.index]
return self.coordinator.data["alarm_zones"][self._zone_index]
@property
def available(self) -> bool:
@@ -164,4 +167,4 @@ class ComelitVedoSensorEntity(CoordinatorEntity[ComelitVedoSystem], SensorEntity
if (status := self._zone_object.human_status) == AlarmZoneState.UNKNOWN:
return None
return status.value
return cast(str, status.value)
+1 -4
View File
@@ -77,7 +77,4 @@ class ComelitSwitchEntity(CoordinatorEntity[ComelitSerialBridge], SwitchEntity):
@property
def is_on(self) -> bool:
"""Return True if switch is on."""
return (
self.coordinator.data[self._device.type][self._device.index].status
== STATE_ON
)
return self.coordinator.data[OTHER][self._device.index].status == STATE_ON
@@ -49,7 +49,11 @@ def async_get_chat_log(
raise RuntimeError(
"Cannot attach chat log delta listener unless initial caller"
)
if user_input is not None:
if user_input is not None and (
(content := chat_log.content[-1]).role != "user"
# MyPy doesn't understand that content is a UserContent here
or content.content != user_input.text # type: ignore[union-attr]
):
chat_log.async_add_user_content(UserContent(content=user_input.text))
yield chat_log
@@ -179,6 +183,25 @@ class ChatLog:
llm_api: llm.APIInstance | None = None
delta_listener: Callable[[ChatLog, dict], None] | None = None
@property
def continue_conversation(self) -> bool:
"""Return whether the conversation should continue."""
if not self.content:
return False
last_msg = self.content[-1]
return (
last_msg.role == "assistant"
and last_msg.content is not None # type: ignore[union-attr]
and last_msg.content.strip().endswith( # type: ignore[union-attr]
(
"?",
";", # Greek question mark
)
)
)
@property
def unresponded_tool_results(self) -> bool:
"""Return if there are unresponded tool results."""
@@ -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.2.26"]
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.3.5"]
}
+1 -1
View File
@@ -16,6 +16,6 @@
"requirements": [
"aiodhcpwatcher==1.1.1",
"aiodiscover==2.6.1",
"cached-ipaddress==0.9.2"
"cached-ipaddress==0.10.0"
]
}
@@ -6,5 +6,5 @@
"dependencies": ["webhook"],
"documentation": "https://www.home-assistant.io/integrations/ecowitt",
"iot_class": "local_push",
"requirements": ["aioecowitt==2024.2.1"]
"requirements": ["aioecowitt==2025.3.1"]
}
@@ -6,7 +6,7 @@
"api_key": "[%key:common::config_flow::data::api_key%]"
},
"data_description": {
"api_key": "Your Elevenlabs API key."
"api_key": "Your ElevenLabs API key."
}
}
},
@@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense-energy==0.13.5"]
"requirements": ["sense-energy==0.13.6"]
}
@@ -72,5 +72,7 @@ class EpsonConfigFlow(ConfigFlow, domain=DOMAIN):
if projector:
projector.close()
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
step_id="user",
data_schema=self.add_suggested_values_to_schema(DATA_SCHEMA, user_input),
errors=errors,
)
@@ -22,5 +22,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["eq3btsmart"],
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.9.0"]
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.11.0"]
}
+1 -1
View File
@@ -14,7 +14,7 @@ DEFAULT_ALLOW_SERVICE_CALLS = True
DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS = False
STABLE_BLE_VERSION_STR = "2025.2.1"
STABLE_BLE_VERSION_STR = "2025.2.2"
STABLE_BLE_VERSION = AwesomeVersion(STABLE_BLE_VERSION_STR)
PROJECT_URLS = {
"esphome.bluetooth-proxy": "https://esphome.github.io/bluetooth-proxies/",
@@ -16,9 +16,9 @@
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
"mqtt": ["esphome/discover/#"],
"requirements": [
"aioesphomeapi==29.3.1",
"aioesphomeapi==29.4.0",
"esphome-dashboard-api==1.2.3",
"bleak-esphome==2.9.0"
"bleak-esphome==2.11.0"
],
"zeroconf": ["_esphomelib._tcp.local."]
}
@@ -11,6 +11,7 @@ from typing import Any
import evohomeasync as ec1
import evohomeasync2 as ec2
from evohomeasync2.const import (
SZ_DHW,
SZ_GATEWAY_ID,
SZ_GATEWAY_INFO,
SZ_GATEWAYS,
@@ -19,8 +20,9 @@ from evohomeasync2.const import (
SZ_TEMPERATURE_CONTROL_SYSTEMS,
SZ_TIME_ZONE,
SZ_USE_DAYLIGHT_SAVE_SWITCHING,
SZ_ZONES,
)
from evohomeasync2.schemas.typedefs import EvoLocStatusResponseT
from evohomeasync2.schemas.typedefs import EvoLocStatusResponseT, EvoTcsConfigResponseT
from homeassistant.const import CONF_SCAN_INTERVAL
from homeassistant.core import HomeAssistant
@@ -113,17 +115,19 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
SZ_USE_DAYLIGHT_SAVE_SWITCHING
],
}
tcs_info: EvoTcsConfigResponseT = self.tcs.config # type: ignore[assignment]
tcs_info[SZ_ZONES] = [zone.config for zone in self.tcs.zones]
if self.tcs.hotwater:
tcs_info[SZ_DHW] = self.tcs.hotwater.config
gwy_info = {
SZ_GATEWAY_ID: self.loc.gateways[0].id,
SZ_TEMPERATURE_CONTROL_SYSTEMS: [
self.loc.gateways[0].systems[0].config
],
SZ_TEMPERATURE_CONTROL_SYSTEMS: [tcs_info],
}
config = {
SZ_LOCATION_INFO: loc_info,
SZ_GATEWAYS: [{SZ_GATEWAY_INFO: gwy_info}],
}
self.logger.debug("Config = %s", config)
self.logger.debug("Config = %s", [config])
async def call_client_api(
self,
@@ -203,10 +207,18 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
async def _update_v2_schedules(self) -> None:
for zone in self.tcs.zones:
await zone.get_schedule()
try:
await zone.get_schedule()
except ec2.InvalidScheduleError as err:
self.logger.warning(
"Zone '%s' has an invalid/missing schedule: %r", zone.name, err
)
if dhw := self.tcs.hotwater:
await dhw.get_schedule()
try:
await dhw.get_schedule()
except ec2.InvalidScheduleError as err:
self.logger.warning("DHW has an invalid/missing schedule: %r", err)
async def _async_update_data(self) -> EvoLocStatusResponseT: # type: ignore[override]
"""Fetch the latest state of an entire TCC Location.
+7 -3
View File
@@ -6,6 +6,7 @@ import logging
from typing import Any
import evohomeasync2 as evo
from evohomeasync2.schemas.typedefs import DayOfWeekDhwT
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -102,7 +103,7 @@ class EvoChild(EvoEntity):
self._evo_tcs = evo_device.tcs
self._schedule: dict[str, Any] | None = None
self._schedule: list[DayOfWeekDhwT] | None = None
self._setpoints: dict[str, Any] = {}
@property
@@ -123,6 +124,9 @@ class EvoChild(EvoEntity):
Only Zones & DHW controllers (but not the TCS) can have schedules.
"""
if not self._schedule:
return self._setpoints
this_sp_dtm, this_sp_val = self._evo_device.this_switchpoint
next_sp_dtm, next_sp_val = self._evo_device.next_switchpoint
@@ -152,10 +156,10 @@ class EvoChild(EvoEntity):
self._evo_device,
err,
)
self._schedule = {}
self._schedule = []
return
else:
self._schedule = schedule or {} # mypy hint
self._schedule = schedule # type: ignore[assignment]
_LOGGER.debug("Schedule['%s'] = %s", self.name, schedule)
@@ -1,29 +1,36 @@
"""The forked_daapd component."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from pyforked_daapd import ForkedDaapdAPI
from .const import DOMAIN, HASS_DATA_UPDATER_KEY
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import ForkedDaapdConfigEntry, ForkedDaapdUpdater
PLATFORMS = [Platform.MEDIA_PLAYER]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ForkedDaapdConfigEntry) -> bool:
"""Set up forked-daapd from a config entry by forwarding to platform."""
host: str = entry.data[CONF_HOST]
port: int = entry.data[CONF_PORT]
password: str = entry.data[CONF_PASSWORD]
forked_daapd_api = ForkedDaapdAPI(
async_get_clientsession(hass), host, port, password
)
forked_daapd_updater = ForkedDaapdUpdater(hass, forked_daapd_api, entry.entry_id)
entry.runtime_data = forked_daapd_updater
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(
hass: HomeAssistant, entry: ForkedDaapdConfigEntry
) -> bool:
"""Remove forked-daapd component."""
status = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if status and hass.data.get(DOMAIN) and hass.data[DOMAIN].get(entry.entry_id):
if websocket_handler := hass.data[DOMAIN][entry.entry_id][
HASS_DATA_UPDATER_KEY
].websocket_handler:
if status:
if websocket_handler := entry.runtime_data.websocket_handler:
websocket_handler.cancel()
del hass.data[DOMAIN][entry.entry_id]
if not hass.data[DOMAIN]:
del hass.data[DOMAIN]
return status
@@ -7,12 +7,7 @@ from typing import Any
from pyforked_daapd import ForkedDaapdAPI
import voluptuous as vol
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -28,6 +23,7 @@ from .const import (
DEFAULT_TTS_VOLUME,
DOMAIN,
)
from .coordinator import ForkedDaapdConfigEntry
_LOGGER = logging.getLogger(__name__)
@@ -115,7 +111,7 @@ class ForkedDaapdFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
config_entry: ForkedDaapdConfigEntry,
) -> ForkedDaapdOptionsFlowHandler:
"""Return options flow handler."""
return ForkedDaapdOptionsFlowHandler()
@@ -30,9 +30,8 @@ DEFAULT_SERVER_NAME = "My Server"
DEFAULT_TTS_PAUSE_TIME = 1.2
DEFAULT_TTS_VOLUME = 0.8
DEFAULT_UNMUTE_VOLUME = 0.6
DOMAIN = "forked_daapd" # key for hass.data
DOMAIN = "forked_daapd"
FD_NAME = "OwnTone"
HASS_DATA_UPDATER_KEY = "UPDATER"
KNOWN_PIPES = {"librespot-java"}
PIPE_FUNCTION_MAP = {
"librespot-java": {
@@ -9,6 +9,7 @@ from typing import Any
from pyforked_daapd import ForkedDaapdAPI
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.dispatcher import async_dispatcher_send
@@ -22,6 +23,8 @@ from .const import (
SIGNAL_UPDATE_QUEUE,
)
type ForkedDaapdConfigEntry = ConfigEntry[ForkedDaapdUpdater]
_LOGGER = logging.getLogger(__name__)
WS_NOTIFY_EVENT_TYPES = ["player", "outputs", "volume", "options", "queue", "database"]
@@ -39,6 +42,11 @@ class ForkedDaapdUpdater:
self._all_output_ids: set[str] = set()
self._entry_id = entry_id
@property
def api(self) -> ForkedDaapdAPI:
"""Return the API object."""
return self._api
async def async_init(self) -> None:
"""Perform async portion of class initialization."""
if not (server_config := await self._api.get_request("config")):
@@ -7,7 +7,6 @@ from collections import defaultdict
import logging
from typing import Any
from pyforked_daapd import ForkedDaapdAPI
from pylibrespot_java import LibrespotJavaAPI
from homeassistant.components import media_source
@@ -28,8 +27,7 @@ from homeassistant.components.spotify import (
resolve_spotify_media_type,
spotify_uri_from_media_browser_url,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import (
@@ -55,9 +53,7 @@ from .const import (
DEFAULT_TTS_PAUSE_TIME,
DEFAULT_TTS_VOLUME,
DEFAULT_UNMUTE_VOLUME,
DOMAIN,
FD_NAME,
HASS_DATA_UPDATER_KEY,
KNOWN_PIPES,
PIPE_FUNCTION_MAP,
SIGNAL_ADD_ZONES,
@@ -74,23 +70,21 @@ from .const import (
SUPPORTED_FEATURES_ZONE,
TTS_TIMEOUT,
)
from .coordinator import ForkedDaapdUpdater
from .coordinator import ForkedDaapdConfigEntry
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: ForkedDaapdConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up forked-daapd from a config entry."""
forked_daapd_updater = config_entry.runtime_data
host: str = config_entry.data[CONF_HOST]
port: int = config_entry.data[CONF_PORT]
password: str = config_entry.data[CONF_PASSWORD]
forked_daapd_api = ForkedDaapdAPI(
async_get_clientsession(hass), host, port, password
)
forked_daapd_api = forked_daapd_updater.api
forked_daapd_master = ForkedDaapdMaster(
clientsession=async_get_clientsession(hass),
api=forked_daapd_api,
@@ -111,20 +105,12 @@ async def async_setup_entry(
)
config_entry.async_on_unload(config_entry.add_update_listener(update_listener))
if not hass.data.get(DOMAIN):
hass.data[DOMAIN] = {config_entry.entry_id: {}}
async_add_entities([forked_daapd_master], False)
forked_daapd_updater = ForkedDaapdUpdater(
hass, forked_daapd_api, config_entry.entry_id
)
hass.data[DOMAIN][config_entry.entry_id][HASS_DATA_UPDATER_KEY] = (
forked_daapd_updater
)
await forked_daapd_updater.async_init()
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def update_listener(hass: HomeAssistant, entry: ForkedDaapdConfigEntry) -> None:
"""Handle options update."""
async_dispatcher_send(
hass, SIGNAL_CONFIG_OPTIONS_UPDATE.format(entry.entry_id), entry.options
@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250228.0"]
"requirements": ["home-assistant-frontend==20250306.0"]
}
@@ -1,6 +1,6 @@
{
"common": {
"data_description_password": "The Remote Admin Password from the Fully Kiosk Browser app settings.",
"data_description_password": "The Remote Admin password from the Fully Kiosk Browser app settings.",
"data_description_ssl": "Is the Fully Kiosk app configured to require SSL for the connection?",
"data_description_verify_ssl": "Should SSL certificartes be verified? This should be off for self-signed certificates."
},
@@ -151,7 +151,7 @@
}
},
"set_config": {
"name": "Set Configuration",
"name": "Set configuration",
"description": "Sets a configuration parameter on Fully Kiosk Browser.",
"fields": {
"key": {
@@ -165,7 +165,7 @@
}
},
"start_application": {
"name": "Start Application",
"name": "Start application",
"description": "Starts an application on the device running Fully Kiosk Browser.",
"fields": {
"application": {
@@ -6,5 +6,5 @@
"dependencies": ["application_credentials"],
"documentation": "https://www.home-assistant.io/integrations/geocaching",
"iot_class": "cloud_polling",
"requirements": ["geocachingapi==0.2.1"]
"requirements": ["geocachingapi==0.3.0"]
}
+10 -1
View File
@@ -4,9 +4,14 @@ from __future__ import annotations
import logging
from aiohttp.client_exceptions import ClientConnectorError
from gios import Gios
from gios.exceptions import GiosError
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -36,8 +41,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: GiosConfigEntry) -> bool
device_registry.async_update_device(device_entry.id, new_identifiers={new_ids})
websession = async_get_clientsession(hass)
try:
gios = await Gios.create(websession, station_id)
except (GiosError, ConnectionError, ClientConnectorError) as err:
raise ConfigEntryNotReady from err
coordinator = GiosDataUpdateCoordinator(hass, entry, websession, station_id)
coordinator = GiosDataUpdateCoordinator(hass, entry, gios)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = GiosData(coordinator)
+1 -1
View File
@@ -37,7 +37,7 @@ class GiosFlowHandler(ConfigFlow, domain=DOMAIN):
websession = async_get_clientsession(self.hass)
async with asyncio.timeout(API_TIMEOUT):
gios = Gios(user_input[CONF_STATION_ID], websession)
gios = await Gios.create(websession, user_input[CONF_STATION_ID])
await gios.async_update()
assert gios.station_name is not None
+1 -1
View File
@@ -13,7 +13,7 @@ SCAN_INTERVAL: Final = timedelta(minutes=30)
DOMAIN: Final = "gios"
MANUFACTURER: Final = "Główny Inspektorat Ochrony Środowiska"
URL = "http://powietrze.gios.gov.pl/pjp/current/station_details/info/{station_id}"
URL = "https://powietrze.gios.gov.pl/pjp/current/station_details/info/{station_id}"
API_TIMEOUT: Final = 30
+2 -4
View File
@@ -6,7 +6,6 @@ import asyncio
from dataclasses import dataclass
import logging
from aiohttp import ClientSession
from aiohttp.client_exceptions import ClientConnectorError
from gios import Gios
from gios.exceptions import GiosError
@@ -39,11 +38,10 @@ class GiosDataUpdateCoordinator(DataUpdateCoordinator[GiosSensors]):
self,
hass: HomeAssistant,
config_entry: GiosConfigEntry,
session: ClientSession,
station_id: int,
gios: Gios,
) -> None:
"""Class to manage fetching GIOS data API."""
self.gios = Gios(station_id, session)
self.gios = gios
super().__init__(
hass,
+1 -1
View File
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["dacite", "gios"],
"requirements": ["gios==5.0.0"]
"requirements": ["gios==6.0.0"]
}
@@ -8,7 +8,12 @@ from typing import Any
from google_drive_api.exceptions import GoogleDriveApiError
from homeassistant.components.backup import AgentBackup, BackupAgent, BackupAgentError
from homeassistant.components.backup import (
AgentBackup,
BackupAgent,
BackupAgentError,
BackupNotFound,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import ChunkAsyncStreamIterator
@@ -93,13 +98,13 @@ class GoogleDriveBackupAgent(BackupAgent):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
) -> AgentBackup:
"""Return a backup."""
backups = await self.async_list_backups()
for backup in backups:
if backup.backup_id == backup_id:
return backup
return None
raise BackupNotFound(f"Backup {backup_id} not found")
async def async_download_backup(
self,
@@ -120,7 +125,7 @@ class GoogleDriveBackupAgent(BackupAgent):
return ChunkAsyncStreamIterator(stream)
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
raise BackupAgentError(f"Failed to download backup: {err}") from err
raise BackupAgentError("Backup not found")
raise BackupNotFound(f"Backup {backup_id} not found")
async def async_delete_backup(
self,
@@ -138,5 +143,7 @@ class GoogleDriveBackupAgent(BackupAgent):
_LOGGER.debug("Deleting file_id: %s", file_id)
await self._client.async_delete(file_id)
_LOGGER.debug("Deleted backup_id: %s", backup_id)
return
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
raise BackupAgentError(f"Failed to delete backup: {err}") from err
raise BackupNotFound(f"Backup {backup_id} not found")
@@ -65,9 +65,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
prompt_parts = [call.data[CONF_PROMPT]]
config_entry: GoogleGenerativeAIConfigEntry = hass.config_entries.async_entries(
DOMAIN
)[0]
config_entry: GoogleGenerativeAIConfigEntry = (
hass.config_entries.async_loaded_entries(DOMAIN)[0]
)
client = config_entry.runtime_data
@@ -64,28 +64,18 @@ async def async_setup_entry(
SUPPORTED_SCHEMA_KEYS = {
"min_items",
"example",
"property_ordering",
"pattern",
"minimum",
"default",
"any_of",
"max_length",
"title",
"min_properties",
"min_length",
"max_items",
"maximum",
"nullable",
"max_properties",
# Gemini API does not support all of the OpenAPI schema
# SoT: https://ai.google.dev/api/caching#Schema
"type",
"description",
"enum",
"format",
"items",
"description",
"nullable",
"enum",
"max_items",
"min_items",
"properties",
"required",
"items",
}
@@ -109,9 +99,7 @@ def _format_schema(schema: dict[str, Any]) -> Schema:
key = _camel_to_snake(key)
if key not in SUPPORTED_SCHEMA_KEYS:
continue
if key == "any_of":
val = [_format_schema(subschema) for subschema in val]
elif key == "type":
if key == "type":
val = val.upper()
elif key == "format":
# Gemini API does not support all formats, see: https://ai.google.dev/api/caching#Schema
@@ -471,7 +459,9 @@ class GoogleGenerativeAIConversationEntity(
" ".join([part.text.strip() for part in response_parts if part.text])
)
return conversation.ConversationResult(
response=response, conversation_id=chat_log.conversation_id
response=response,
conversation_id=chat_log.conversation_id,
continue_conversation=chat_log.continue_conversation,
)
async def _async_entry_update_listener(
@@ -62,6 +62,7 @@ SERVICE_TRANSFORMATION = "transformation"
SERVICE_UPDATE_REWARD = "update_reward"
SERVICE_CREATE_REWARD = "create_reward"
SERVICE_UPDATE_HABIT = "update_habit"
SERVICE_CREATE_HABIT = "create_habit"
DEVELOPER_ID = "4c4ca53f-c059-4ffa-966e-9d29dd405daf"
X_CLIENT = f"{DEVELOPER_ID} - {APPLICATION_NAME} {__version__}"
@@ -237,6 +237,12 @@
"tag_options": "mdi:tag",
"developer_options": "mdi:test-tube"
}
},
"create_habit": {
"service": "mdi:contrast-box",
"sections": {
"developer_options": "mdi:test-tube"
}
}
}
}
+10 -1
View File
@@ -66,6 +66,7 @@ from .const import (
SERVICE_API_CALL,
SERVICE_CANCEL_QUEST,
SERVICE_CAST_SKILL,
SERVICE_CREATE_HABIT,
SERVICE_CREATE_REWARD,
SERVICE_GET_TASKS,
SERVICE_LEAVE_QUEST,
@@ -190,6 +191,7 @@ SERVICE_TASK_TYPE_MAP = {
SERVICE_UPDATE_REWARD: TaskType.REWARD,
SERVICE_CREATE_REWARD: TaskType.REWARD,
SERVICE_UPDATE_HABIT: TaskType.HABIT,
SERVICE_CREATE_HABIT: TaskType.HABIT,
}
@@ -596,7 +598,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
data = Task()
if not is_update:
data["type"] = TaskType.REWARD
data["type"] = SERVICE_TASK_TYPE_MAP[call.service]
if (text := call.data.get(ATTR_RENAME)) or (text := call.data.get(ATTR_NAME)):
data["text"] = text
@@ -733,6 +735,13 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
schema=SERVICE_CREATE_TASK_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_CREATE_HABIT,
create_or_update_task,
schema=SERVICE_CREATE_TASK_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_API_CALL,
@@ -183,7 +183,7 @@ update_reward:
create_reward:
fields:
config_entry: *config_entry
name:
name: &name
required: true
selector:
text:
@@ -199,7 +199,7 @@ update_habit:
task: *task
rename: *rename
notes: *notes
up_down:
up_down: &up_down
required: false
selector:
select:
@@ -210,7 +210,7 @@ update_habit:
label: ""
multiple: true
mode: list
priority:
priority: &priority
required: false
selector:
select:
@@ -221,7 +221,7 @@ update_habit:
- "hard"
mode: dropdown
translation_key: "priority"
frequency:
frequency: &frequency
required: false
selector:
select:
@@ -252,3 +252,13 @@ update_habit:
unit_of_measurement: ""
mode: box
alias: *alias
create_habit:
fields:
config_entry: *config_entry
name: *name
notes: *notes
up_down: *up_down
priority: *priority
frequency: *frequency
tag: *tag
developer_options: *developer_options
+60 -10
View File
@@ -11,9 +11,9 @@
"config_entry_description": "Select the Habitica account to update a task.",
"task_description": "The name (or task ID) of the task you want to update.",
"rename_name": "Rename",
"rename_description": "The new title for the Habitica task.",
"notes_name": "Update notes",
"notes_description": "The new notes for the Habitica task.",
"rename_description": "The title for the Habitica task.",
"notes_name": "Notes",
"notes_description": "The notes for the Habitica task.",
"tag_name": "Add tags",
"tag_description": "Add tags to the Habitica task. If a tag does not already exist, a new one will be created.",
"remove_tag_name": "Remove tags",
@@ -25,7 +25,13 @@
"tag_options_name": "Tags",
"tag_options_description": "Add or remove tags from a task.",
"name_description": "The title for the Habitica task.",
"cost_name": "Cost"
"cost_name": "Cost",
"difficulty_name": "Difficulty",
"difficulty_description": "The difficulty of the task.",
"frequency_name": "Counter reset",
"frequency_description": "The frequency at which the habit's counter resets: daily at the start of a new day, weekly after Sunday night, or monthly at the beginning of a new month.",
"up_down_name": "Rewards or losses",
"up_down_description": "Whether the habit is good and rewarding (positive), bad and penalizing (negative), or both."
},
"config": {
"abort": {
@@ -793,16 +799,16 @@
"description": "[%key:component::habitica::common::alias_description%]"
},
"priority": {
"name": "Difficulty",
"description": "Update the difficulty of a task."
"name": "[%key:component::habitica::common::difficulty_name%]",
"description": "[%key:component::habitica::common::difficulty_description%]"
},
"frequency": {
"name": "Counter reset",
"description": "Update when a habit's counter resets: daily resets at the start of a new day, weekly after Sunday night, and monthly at the beginning of a new month."
"name": "[%key:component::habitica::common::frequency_name%]",
"description": "[%key:component::habitica::common::frequency_description%]"
},
"up_down": {
"name": "Rewards or losses",
"description": "Update if the habit is good and rewarding (positive), bad and penalizing (negative), or both."
"name": "[%key:component::habitica::common::up_down_name%]",
"description": "[%key:component::habitica::common::up_down_description%]"
},
"counter_up": {
"name": "Adjust positive counter",
@@ -823,6 +829,50 @@
"description": "[%key:component::habitica::common::developer_options_description%]"
}
}
},
"create_habit": {
"name": "Create habit",
"description": "Adds a new habit.",
"fields": {
"config_entry": {
"name": "[%key:component::habitica::common::config_entry_name%]",
"description": "Select the Habitica account to create a habit."
},
"name": {
"name": "[%key:component::habitica::common::task_name%]",
"description": "[%key:component::habitica::common::name_description%]"
},
"notes": {
"name": "[%key:component::habitica::common::notes_name%]",
"description": "[%key:component::habitica::common::notes_description%]"
},
"tag": {
"name": "[%key:component::habitica::common::tag_name%]",
"description": "[%key:component::habitica::common::tag_description%]"
},
"alias": {
"name": "[%key:component::habitica::common::alias_name%]",
"description": "[%key:component::habitica::common::alias_description%]"
},
"priority": {
"name": "[%key:component::habitica::common::difficulty_name%]",
"description": "[%key:component::habitica::common::difficulty_description%]"
},
"frequency": {
"name": "[%key:component::habitica::common::frequency_name%]",
"description": "[%key:component::habitica::common::frequency_description%]"
},
"up_down": {
"name": "[%key:component::habitica::common::up_down_name%]",
"description": "[%key:component::habitica::common::up_down_description%]"
}
},
"sections": {
"developer_options": {
"name": "[%key:component::habitica::common::developer_options_name%]",
"description": "[%key:component::habitica::common::developer_options_description%]"
}
}
}
},
"selector": {
+20 -16
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio
from collections.abc import AsyncIterator, Callable, Coroutine, Mapping
from contextlib import suppress
import logging
import os
from pathlib import Path, PurePath
@@ -173,7 +174,7 @@ class SupervisorBackupAgent(BackupAgent):
),
)
except SupervisorNotFoundError as err:
raise BackupNotFound from err
raise BackupNotFound(f"Backup {backup_id} not found") from err
async def async_upload_backup(
self,
@@ -186,13 +187,14 @@ class SupervisorBackupAgent(BackupAgent):
The upload will be skipped if the backup already exists in the agent's location.
"""
if await self.async_get_backup(backup.backup_id):
_LOGGER.debug(
"Backup %s already exists in location %s",
backup.backup_id,
self.location,
)
return
with suppress(BackupNotFound):
if await self.async_get_backup(backup.backup_id):
_LOGGER.debug(
"Backup %s already exists in location %s",
backup.backup_id,
self.location,
)
return
stream = await open_stream()
upload_options = supervisor_backups.UploadBackupOptions(
location={self.location},
@@ -218,14 +220,14 @@ class SupervisorBackupAgent(BackupAgent):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
) -> AgentBackup:
"""Return a backup."""
try:
details = await self._client.backups.backup_info(backup_id)
except SupervisorNotFoundError:
return None
except SupervisorNotFoundError as err:
raise BackupNotFound(f"Backup {backup_id} not found") from err
if self.location not in details.location_attributes:
return None
raise BackupNotFound(f"Backup {backup_id} not found")
return _backup_details_to_agent_backup(details, self.location)
async def async_delete_backup(self, backup_id: str, **kwargs: Any) -> None:
@@ -237,8 +239,8 @@ class SupervisorBackupAgent(BackupAgent):
location={self.location}
),
)
except SupervisorNotFoundError:
_LOGGER.debug("Backup %s does not exist", backup_id)
except SupervisorNotFoundError as err:
raise BackupNotFound(f"Backup {backup_id} not found") from err
class SupervisorBackupReaderWriter(BackupReaderWriter):
@@ -492,10 +494,12 @@ class SupervisorBackupReaderWriter(BackupReaderWriter):
) -> None:
"""Restore a backup."""
manager = self._hass.data[DATA_MANAGER]
# The backup manager has already checked that the backup exists so we don't need to
# check that here.
# The backup manager has already checked that the backup exists so we don't
# need to catch BackupNotFound here.
backup = await manager.backup_agents[agent_id].async_get_backup(backup_id)
if (
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
backup
and restore_homeassistant
and restore_database != backup.database_included
+1 -2
View File
@@ -11,7 +11,6 @@ from hko import HKO, HKOError
from homeassistant.components.weather import (
ATTR_CONDITION_CLOUDY,
ATTR_CONDITION_FOG,
ATTR_CONDITION_HAIL,
ATTR_CONDITION_LIGHTNING_RAINY,
ATTR_CONDITION_PARTLYCLOUDY,
ATTR_CONDITION_POURING,
@@ -145,7 +144,7 @@ class HKOUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Return the condition corresponding to the weather info."""
info = info.lower()
if WEATHER_INFO_RAIN in info:
return ATTR_CONDITION_HAIL
return ATTR_CONDITION_RAINY
if WEATHER_INFO_SNOW in info and WEATHER_INFO_RAIN in info:
return ATTR_CONDITION_SNOWY_RAINY
if WEATHER_INFO_SNOW in info:
@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.67", "babel==2.15.0"]
"requirements": ["holidays==0.68", "babel==2.15.0"]
}
@@ -47,8 +47,6 @@ _LOGGER = logging.getLogger(__name__)
type HomeConnectConfigEntry = ConfigEntry[HomeConnectCoordinator]
EVENT_STREAM_RECONNECT_DELAY = 30
@dataclass(frozen=True, kw_only=True)
class HomeConnectApplianceData:
@@ -100,6 +98,7 @@ class HomeConnectCoordinator(
CALLBACK_TYPE, tuple[CALLBACK_TYPE, tuple[EventKey, ...]]
] = {}
self.device_registry = dr.async_get(self.hass)
self.data = {}
@cached_property
def context_listeners(self) -> dict[tuple[str, EventKey], list[CALLBACK_TYPE]]:
@@ -157,10 +156,20 @@ class HomeConnectCoordinator(
async def _event_listener(self) -> None:
"""Match event with listener for event type."""
retry_time = 10
while True:
try:
async for event_message in self.client.stream_all_events():
retry_time = 10
event_message_ha_id = event_message.ha_id
if (
event_message_ha_id in self.data
and not self.data[event_message_ha_id].info.connected
):
self.data[event_message_ha_id].info.connected = True
self._call_all_event_listeners_for_appliance(
event_message_ha_id
)
match event_message.type:
case EventType.STATUS:
statuses = self.data[event_message_ha_id].status
@@ -256,20 +265,18 @@ class HomeConnectCoordinator(
except (EventStreamInterruptedError, HomeConnectRequestError) as error:
_LOGGER.debug(
"Non-breaking error (%s) while listening for events,"
" continuing in 30 seconds",
type(error).__name__,
" continuing in %s seconds",
error,
retry_time,
)
await asyncio.sleep(EVENT_STREAM_RECONNECT_DELAY)
await asyncio.sleep(retry_time)
retry_time = min(retry_time * 2, 3600)
except HomeConnectApiError as error:
_LOGGER.error("Error while listening for events: %s", error)
self.hass.config_entries.async_schedule_reload(
self.config_entry.entry_id
)
break
# if there was a non-breaking error, we continue listening
# but we need to refresh the data to get the possible changes
# that happened while the event stream was interrupted
await self.async_refresh()
@callback
def _call_event_listener(self, event_message: EventMessage) -> None:
@@ -297,6 +304,8 @@ class HomeConnectCoordinator(
translation_placeholders=get_dict_from_home_connect_error(error),
) from error
except HomeConnectError as error:
for appliance_data in self.data.values():
appliance_data.info.connected = False
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="fetch_api_error",
@@ -305,7 +314,7 @@ class HomeConnectCoordinator(
return {
appliance.ha_id: await self._get_appliance_data(
appliance, self.data.get(appliance.ha_id) if self.data else None
appliance, self.data.get(appliance.ha_id)
)
for appliance in appliances.homeappliances
}
@@ -334,9 +343,7 @@ class HomeConnectCoordinator(
_LOGGER.debug(
"Error fetching settings for %s: %s",
appliance.ha_id,
error
if isinstance(error, HomeConnectApiError)
else type(error).__name__,
error,
)
settings = {}
try:
@@ -348,9 +355,7 @@ class HomeConnectCoordinator(
_LOGGER.debug(
"Error fetching status for %s: %s",
appliance.ha_id,
error
if isinstance(error, HomeConnectApiError)
else type(error).__name__,
error,
)
status = {}
@@ -364,9 +369,7 @@ class HomeConnectCoordinator(
_LOGGER.debug(
"Error fetching programs for %s: %s",
appliance.ha_id,
error
if isinstance(error, HomeConnectApiError)
else type(error).__name__,
error,
)
else:
programs.extend(all_programs.programs)
@@ -456,9 +459,7 @@ class HomeConnectCoordinator(
_LOGGER.debug(
"Error fetching options for %s: %s",
ha_id,
error
if isinstance(error, HomeConnectApiError)
else type(error).__name__,
error,
)
return {}
@@ -8,6 +8,7 @@ from typing import cast
from aiohomeconnect.model import EventKey, OptionKey
from aiohomeconnect.model.error import ActiveProgramNotSetError, HomeConnectError
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
@@ -51,8 +52,10 @@ class HomeConnectEntity(CoordinatorEntity[HomeConnectCoordinator]):
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self.update_native_value()
available = self._attr_available = self.appliance.info.connected
self.async_write_ha_state()
_LOGGER.debug("Updated %s, new state: %s", self.entity_id, self.state)
state = STATE_UNAVAILABLE if not available else self.state
_LOGGER.debug("Updated %s, new state: %s", self.entity_id, state)
@property
def bsh_key(self) -> str:
@@ -61,10 +64,13 @@ class HomeConnectEntity(CoordinatorEntity[HomeConnectCoordinator]):
@property
def available(self) -> bool:
"""Return True if entity is available."""
return (
self.appliance.info.connected and self._attr_available and super().available
)
"""Return True if entity is available.
Do not use self.last_update_success for available state
as event updates should take precedence over the coordinator
refresh.
"""
return self._attr_available
class HomeConnectOptionEntity(HomeConnectEntity):
@@ -7,6 +7,6 @@
"documentation": "https://www.home-assistant.io/integrations/home_connect",
"iot_class": "cloud_push",
"loggers": ["aiohomeconnect"],
"requirements": ["aiohomeconnect==0.15.1"],
"requirements": ["aiohomeconnect==0.16.3"],
"single_config_entry": true
}
@@ -386,6 +386,13 @@ class HomeConnectProgramSensor(HomeConnectSensor):
def update_native_value(self) -> None:
"""Update the program sensor's status."""
self.program_running = (
status := self.appliance.status.get(StatusKey.BSH_COMMON_OPERATION_STATE)
) is not None and status.value in [
BSH_OPERATION_STATE_RUN,
BSH_OPERATION_STATE_PAUSE,
BSH_OPERATION_STATE_FINISHED,
]
event = self.appliance.events.get(cast(EventKey, self.bsh_key))
if event:
self._update_native_value(event.value)
@@ -2,7 +2,7 @@
import re
from aiohomeconnect.model.error import HomeConnectApiError, HomeConnectError
from aiohomeconnect.model.error import HomeConnectError
RE_CAMEL_CASE = re.compile(r"(?<!^)(?=[A-Z])|(?=\d)(?<=\D)")
@@ -11,11 +11,7 @@ def get_dict_from_home_connect_error(
err: HomeConnectError,
) -> dict[str, str]:
"""Return a translation string from a Home Connect error."""
return {
"error": str(err)
if isinstance(err, HomeConnectApiError)
else type(err).__name__
}
return {"error": str(err)}
def bsh_key_to_translation_key(bsh_key: str) -> str:
@@ -19,6 +19,7 @@ PLATFORMS = [
Platform.COVER,
Platform.LIGHT,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.VALVE,
+2
View File
@@ -15,6 +15,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import HomeeConfigEntry
from .entity import HomeeEntity
PARALLEL_UPDATES = 0
BUTTON_DESCRIPTIONS: dict[AttributeType, ButtonEntityDescription] = {
AttributeType.AUTOMATIC_MODE_IMPULSE: ButtonEntityDescription(key="automatic_mode"),
AttributeType.BRIEFLY_OPEN_IMPULSE: ButtonEntityDescription(key="briefly_open"),
+2
View File
@@ -21,6 +21,8 @@ from .entity import HomeeNodeEntity
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
OPEN_CLOSE_ATTRIBUTES = [
AttributeType.OPEN_CLOSE,
AttributeType.SLAT_ROTATION_IMPULSE,
@@ -1,6 +1,12 @@
{
"entity": {
"sensor": {
"brightness": {
"default": "mdi:brightness-5"
},
"brightness_instance": {
"default": "mdi:brightness-5"
},
"link_quality": {
"default": "mdi:signal"
},
+2
View File
@@ -32,6 +32,8 @@ LIGHT_ATTRIBUTES = [
AttributeType.DIMMING_LEVEL,
]
PARALLEL_UPDATES = 0
def is_light_node(node: HomeeNode) -> bool:
"""Determine if a node is controllable as a homee light based on its profile and attributes."""
+2
View File
@@ -16,6 +16,8 @@ from . import HomeeConfigEntry
from .const import HOMEE_UNIT_TO_HA_UNIT
from .entity import HomeeEntity
PARALLEL_UPDATES = 0
NUMBER_DESCRIPTIONS = {
AttributeType.DOWN_POSITION: NumberEntityDescription(
key="down_position",
@@ -35,7 +35,7 @@ rules:
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: todo
parallel-updates: done
reauthentication-flow: todo
test-coverage: todo
+63
View File
@@ -0,0 +1,63 @@
"""The Homee select platform."""
from pyHomee.const import AttributeType
from pyHomee.model import HomeeAttribute
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import HomeeConfigEntry
from .entity import HomeeEntity
PARALLEL_UPDATES = 0
SELECT_DESCRIPTIONS: dict[AttributeType, SelectEntityDescription] = {
AttributeType.REPEATER_MODE: SelectEntityDescription(
key="repeater_mode",
options=["off", "level1", "level2"],
entity_category=EntityCategory.CONFIG,
),
}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: HomeeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Add the Homee platform for the select component."""
async_add_entities(
HomeeSelect(attribute, config_entry, SELECT_DESCRIPTIONS[attribute.type])
for node in config_entry.runtime_data.nodes
for attribute in node.attributes
if attribute.type in SELECT_DESCRIPTIONS and attribute.editable
)
class HomeeSelect(HomeeEntity, SelectEntity):
"""Representation of a Homee select entity."""
def __init__(
self,
attribute: HomeeAttribute,
entry: HomeeConfigEntry,
description: SelectEntityDescription,
) -> None:
"""Initialize a Homee select entity."""
super().__init__(attribute, entry)
self.entity_description = description
assert description.options is not None
self._attr_options = description.options
self._attr_translation_key = description.key
@property
def current_option(self) -> str:
"""Return the current selected option."""
return self.options[int(self._attribute.current_value)]
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.async_set_homee_value(self.options.index(option))
+18
View File
@@ -27,6 +27,8 @@ from .const import (
from .entity import HomeeEntity, HomeeNodeEntity
from .helpers import get_name_for_enum
PARALLEL_UPDATES = 0
def get_open_close_value(attribute: HomeeAttribute) -> str | None:
"""Return the open/close value."""
@@ -40,10 +42,22 @@ def get_window_value(attribute: HomeeAttribute) -> str | None:
return vals.get(attribute.current_value)
def get_brightness_device_class(
attribute: HomeeAttribute, device_class: SensorDeviceClass | None
) -> SensorDeviceClass | None:
"""Return the device class for a brightness sensor."""
if attribute.unit == "%":
return None
return device_class
@dataclass(frozen=True, kw_only=True)
class HomeeSensorEntityDescription(SensorEntityDescription):
"""A class that describes Homee sensor entities."""
device_class_fn: Callable[
[HomeeAttribute, SensorDeviceClass | None], SensorDeviceClass | None
] = lambda attribute, device_class: device_class
value_fn: Callable[[HomeeAttribute], str | float | None] = (
lambda value: value.current_value
)
@@ -67,6 +81,7 @@ SENSOR_DESCRIPTIONS: dict[AttributeType, HomeeSensorEntityDescription] = {
AttributeType.BRIGHTNESS: HomeeSensorEntityDescription(
key="brightness",
device_class=SensorDeviceClass.ILLUMINANCE,
device_class_fn=get_brightness_device_class,
state_class=SensorStateClass.MEASUREMENT,
value_fn=(
lambda attribute: attribute.current_value * 1000
@@ -303,6 +318,9 @@ class HomeeSensor(HomeeEntity, SensorEntity):
if attribute.instance > 0:
self._attr_translation_key = f"{self._attr_translation_key}_instance"
self._attr_translation_placeholders = {"instance": str(attribute.instance)}
self._attr_device_class = description.device_class_fn(
attribute, description.device_class
)
@property
def native_value(self) -> float | str | None:
@@ -110,7 +110,20 @@
"name": "Wake-up interval"
}
},
"select": {
"repeater_mode": {
"name": "Repeater mode",
"state": {
"off": "[%key:common::state::off%]",
"level1": "Level 1",
"level2": "Level 2"
}
}
},
"sensor": {
"brightness": {
"name": "Illuminance"
},
"brightness_instance": {
"name": "Illuminance {instance}"
},
+2
View File
@@ -20,6 +20,8 @@ from . import HomeeConfigEntry
from .const import CLIMATE_PROFILES, LIGHT_PROFILES
from .entity import HomeeEntity
PARALLEL_UPDATES = 0
def get_device_class(
attribute: HomeeAttribute, config_entry: HomeeConfigEntry
+2
View File
@@ -15,6 +15,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import HomeeConfigEntry
from .entity import HomeeEntity
PARALLEL_UPDATES = 0
VALVE_DESCRIPTIONS = {
AttributeType.CURRENT_VALVE_POSITION: ValveEntityDescription(
key="valve_position",
+46 -51
View File
@@ -221,6 +221,34 @@ UNPAIR_SERVICE_SCHEMA = vol.All(
)
@callback
def _async_update_entries_from_yaml(
hass: HomeAssistant, config: ConfigType, start_import_flow: bool
) -> None:
current_entries = hass.config_entries.async_entries(DOMAIN)
entries_by_name, entries_by_port = _async_get_imported_entries_indices(
current_entries
)
hk_config: list[dict[str, Any]] = config[DOMAIN]
for index, conf in enumerate(hk_config):
if _async_update_config_entry_from_yaml(
hass, entries_by_name, entries_by_port, conf
):
continue
if start_import_flow:
conf[CONF_ENTRY_INDEX] = index
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=conf,
),
eager_start=True,
)
def _async_all_homekit_instances(hass: HomeAssistant) -> list[HomeKit]:
"""All active HomeKit instances."""
hk_data: HomeKitEntryData | None
@@ -258,31 +286,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
await hass.async_add_executor_job(get_loader)
_async_register_events_and_services(hass)
if DOMAIN not in config:
return True
current_entries = hass.config_entries.async_entries(DOMAIN)
entries_by_name, entries_by_port = _async_get_imported_entries_indices(
current_entries
)
for index, conf in enumerate(config[DOMAIN]):
if _async_update_config_entry_from_yaml(
hass, entries_by_name, entries_by_port, conf
):
continue
conf[CONF_ENTRY_INDEX] = index
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=conf,
),
eager_start=True,
)
_async_update_entries_from_yaml(hass, config, start_import_flow=True)
return True
@@ -326,13 +333,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeKitConfigEntry) -> b
conf = entry.data
options = entry.options
name = conf[CONF_NAME]
port = conf[CONF_PORT]
_LOGGER.debug("Begin setup HomeKit for %s", name)
name: str = conf[CONF_NAME]
port: int = conf[CONF_PORT]
# ip_address and advertise_ip are yaml only
ip_address = conf.get(CONF_IP_ADDRESS, _DEFAULT_BIND)
advertise_ips: list[str] = conf.get(
ip_address: str | list[str] | None = conf.get(CONF_IP_ADDRESS, _DEFAULT_BIND)
advertise_ips: list[str]
advertise_ips = conf.get(
CONF_ADVERTISE_IP
) or await network.async_get_announce_addresses(hass)
@@ -344,13 +350,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeKitConfigEntry) -> b
# with users who have not migrated yet we do not do exclude
# these entities by default as we cannot migrate automatically
# since it requires a re-pairing.
exclude_accessory_mode = conf.get(
exclude_accessory_mode: bool = conf.get(
CONF_EXCLUDE_ACCESSORY_MODE, DEFAULT_EXCLUDE_ACCESSORY_MODE
)
homekit_mode = options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
entity_config = options.get(CONF_ENTITY_CONFIG, {}).copy()
entity_filter = FILTER_SCHEMA(options.get(CONF_FILTER, {}))
devices = options.get(CONF_DEVICES, [])
homekit_mode: str = options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
entity_config: dict[str, Any] = options.get(CONF_ENTITY_CONFIG, {}).copy()
entity_filter: EntityFilter = FILTER_SCHEMA(options.get(CONF_FILTER, {}))
devices: list[str] = options.get(CONF_DEVICES, [])
homekit = HomeKit(
hass,
@@ -500,26 +506,15 @@ def _async_register_events_and_services(hass: HomeAssistant) -> None:
async def _handle_homekit_reload(service: ServiceCall) -> None:
"""Handle start HomeKit service call."""
config = await async_integration_yaml_config(hass, DOMAIN)
if not config or DOMAIN not in config:
return
current_entries = hass.config_entries.async_entries(DOMAIN)
entries_by_name, entries_by_port = _async_get_imported_entries_indices(
current_entries
)
for conf in config[DOMAIN]:
_async_update_config_entry_from_yaml(
hass, entries_by_name, entries_by_port, conf
_async_update_entries_from_yaml(hass, config, start_import_flow=False)
await asyncio.gather(
*(
create_eager_task(hass.config_entries.async_reload(entry.entry_id))
for entry in hass.config_entries.async_entries(DOMAIN)
)
reload_tasks = [
create_eager_task(hass.config_entries.async_reload(entry.entry_id))
for entry in current_entries
]
await asyncio.gather(*reload_tasks)
)
async_register_admin_service(
hass,
@@ -537,7 +532,7 @@ class HomeKit:
hass: HomeAssistant,
name: str,
port: int,
ip_address: str | None,
ip_address: list[str] | str | None,
entity_filter: EntityFilter,
exclude_accessory_mode: bool,
entity_config: dict[str, Any],
@@ -10,7 +10,7 @@
"loggers": ["pyhap"],
"requirements": [
"HAP-python==4.9.2",
"fnv-hash-fast==1.2.6",
"fnv-hash-fast==1.4.0",
"PyQRCode==1.2.1",
"base36==0.1.1"
],
@@ -3,6 +3,7 @@
"step": {
"init": {
"title": "Pick Homematic IP access point",
"description": "If you are about to register a **Homematic IP HCU1**, please press the button on top of the device before you continue.\n\nThe registration process must be completed within 5 minutes.",
"data": {
"hapid": "Access point ID (SGTIN)",
"pin": "[%key:common::config_flow::data::pin%]",
+10 -10
View File
@@ -111,7 +111,7 @@
},
"services": {
"add_all_link": {
"name": "Add all link",
"name": "Add All-Link",
"description": "Tells the Insteon Modem (IM) start All-Linking mode. Once the IM is in All-Linking mode, press the link button on the device to complete All-Linking.",
"fields": {
"group": {
@@ -120,13 +120,13 @@
},
"mode": {
"name": "[%key:common::config_flow::data::mode%]",
"description": "Linking mode controller - IM is controller responder - IM is responder."
"description": "Linking mode of the Insteon Modem."
}
}
},
"delete_all_link": {
"name": "Delete all link",
"description": "Tells the Insteon Modem (IM) to remove an All-Link record from the All-Link Database of the IM and a device. Once the IM is set to delete the link, press the link button on the corresponding device to complete the process.",
"name": "Delete All-Link",
"description": "Tells the Insteon Modem (IM) to remove an All-Link record from the All-Link database of the IM and a device. Once the IM is set to delete the link, press the link button on the corresponding device to complete the process.",
"fields": {
"group": {
"name": "Group",
@@ -135,8 +135,8 @@
}
},
"load_all_link_database": {
"name": "Load all link database",
"description": "Load the All-Link Database for a device. WARNING - Loading a device All-LInk database is very time consuming and inconsistent. This may take a LONG time and may need to be repeated to obtain all records.",
"name": "Load All-Link database",
"description": "Loads the All-Link database for a device. WARNING - Loading a device All-Link database is very time consuming and inconsistent. This may take a LONG time and may need to be repeated to obtain all records.",
"fields": {
"entity_id": {
"name": "Entity",
@@ -149,8 +149,8 @@
}
},
"print_all_link_database": {
"name": "Print all link database",
"description": "Prints the All-Link Database for a device. Requires that the All-Link Database is loaded into memory.",
"name": "Print All-Link database",
"description": "Prints the All-Link database for a device. Requires that the All-Link database is loaded into memory.",
"fields": {
"entity_id": {
"name": "Entity",
@@ -159,8 +159,8 @@
}
},
"print_im_all_link_database": {
"name": "Print IM all link database",
"description": "Prints the All-Link Database for the INSTEON Modem (IM)."
"name": "Print IM All-Link database",
"description": "Prints the All-Link database for the INSTEON Modem (IM)."
},
"x10_all_units_off": {
"name": "X10 all units off",
@@ -8,6 +8,7 @@ from enum import Enum
import logging
from typing import cast
from awesomeversion import AwesomeVersion
from pynecil import (
CharSetting,
CommunicationError,
@@ -34,6 +35,8 @@ SCAN_INTERVAL = timedelta(seconds=5)
SCAN_INTERVAL_GITHUB = timedelta(hours=3)
SCAN_INTERVAL_SETTINGS = timedelta(seconds=60)
V223 = AwesomeVersion("v2.23")
@dataclass
class IronOSCoordinators:
@@ -72,6 +75,7 @@ class IronOSBaseCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
),
)
self.device = device
self.v223_features = False
async def _async_setup(self) -> None:
"""Set up the coordinator."""
@@ -81,6 +85,8 @@ class IronOSBaseCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
except CommunicationError as e:
raise UpdateFailed("Cannot connect to device") from e
self.v223_features = AwesomeVersion(self.device_info.build) >= V223
class IronOSLiveDataCoordinator(IronOSBaseCoordinator[LiveDataResponse]):
"""IronOS coordinator."""
+16 -1
View File
@@ -73,6 +73,9 @@
},
"power_limit": {
"default": "mdi:flash-alert"
},
"hall_effect_sleep_time": {
"default": "mdi:timer-sand"
}
},
"select": {
@@ -105,6 +108,9 @@
},
"usb_pd_mode": {
"default": "mdi:meter-electric-outline"
},
"tip_type": {
"default": "mdi:pencil-outline"
}
},
"sensor": {
@@ -154,7 +160,16 @@
"soldering": "mdi:soldering-iron",
"sleeping": "mdi:sleep",
"settings": "mdi:menu-open",
"debug": "mdi:bug-play"
"debug": "mdi:bug-play",
"soldering_profile": "mdi:chart-box-outline",
"temperature_adjust": "mdi:thermostat-box",
"usb_pd_debug": "mdi:bug-play",
"thermal_runaway": "mdi:fire-alert",
"startup_logo": "mdi:dots-circle",
"cjc_calibration": "mdi:tune-vertical",
"startup_warnings": "mdi:alert",
"initialisation_done": "mdi:check-circle",
"hibernating": "mdi:sleep"
}
},
"estimated_power": {
@@ -14,5 +14,5 @@
"iot_class": "local_polling",
"loggers": ["pynecil"],
"quality_scale": "platinum",
"requirements": ["pynecil==4.0.1"]
"requirements": ["pynecil==4.1.0"]
}
+23 -2
View File
@@ -65,6 +65,7 @@ class PinecilNumber(StrEnum):
VOLTAGE_DIV = "voltage_div"
TEMP_INCREMENT_SHORT = "temp_increment_short"
TEMP_INCREMENT_LONG = "temp_increment_long"
HALL_EFFECT_SLEEP_TIME = "hall_effect_sleep_time"
def multiply(value: float | None, multiplier: float) -> float | None:
@@ -323,6 +324,23 @@ PINECIL_NUMBER_DESCRIPTIONS: tuple[IronOSNumberEntityDescription, ...] = (
),
)
PINECIL_NUMBER_DESCRIPTIONS_V223: tuple[IronOSNumberEntityDescription, ...] = (
IronOSNumberEntityDescription(
key=PinecilNumber.HALL_EFFECT_SLEEP_TIME,
translation_key=PinecilNumber.HALL_EFFECT_SLEEP_TIME,
value_fn=(lambda _, settings: settings.get("hall_sleep_time")),
characteristic=CharSetting.HALL_SLEEP_TIME,
raw_value_fn=lambda value: value,
mode=NumberMode.BOX,
native_min_value=0,
native_max_value=60,
native_step=5,
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfTime.SECONDS,
entity_registry_enabled_default=False,
),
)
async def async_setup_entry(
hass: HomeAssistant,
@@ -331,10 +349,13 @@ async def async_setup_entry(
) -> None:
"""Set up number entities from a config entry."""
coordinators = entry.runtime_data
descriptions = PINECIL_NUMBER_DESCRIPTIONS
if coordinators.live_data.v223_features:
descriptions += PINECIL_NUMBER_DESCRIPTIONS_V223
async_add_entities(
IronOSNumberEntity(coordinators, description)
for description in PINECIL_NUMBER_DESCRIPTIONS
IronOSNumberEntity(coordinators, description) for description in descriptions
)
+34 -3
View File
@@ -17,6 +17,7 @@ from pynecil import (
ScrollSpeed,
SettingsDataResponse,
TempUnit,
TipType,
USBPDMode,
)
@@ -53,6 +54,7 @@ class PinecilSelect(StrEnum):
LOCKING_MODE = "locking_mode"
LOGO_DURATION = "logo_duration"
USB_PD_MODE = "usb_pd_mode"
TIP_TYPE = "tip_type"
def enum_to_str(enum: Enum | None) -> str | None:
@@ -138,6 +140,8 @@ PINECIL_SELECT_DESCRIPTIONS: tuple[IronOSSelectEntityDescription, ...] = (
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
)
PINECIL_SELECT_DESCRIPTIONS_V222: tuple[IronOSSelectEntityDescription, ...] = (
IronOSSelectEntityDescription(
key=PinecilSelect.USB_PD_MODE,
translation_key=PinecilSelect.USB_PD_MODE,
@@ -149,6 +153,27 @@ PINECIL_SELECT_DESCRIPTIONS: tuple[IronOSSelectEntityDescription, ...] = (
entity_registry_enabled_default=False,
),
)
PINECIL_SELECT_DESCRIPTIONS_V223: tuple[IronOSSelectEntityDescription, ...] = (
IronOSSelectEntityDescription(
key=PinecilSelect.USB_PD_MODE,
translation_key=PinecilSelect.USB_PD_MODE,
characteristic=CharSetting.USB_PD_MODE,
value_fn=lambda x: enum_to_str(x.get("usb_pd_mode")),
raw_value_fn=lambda value: USBPDMode[value.upper()],
options=[x.name.lower() for x in USBPDMode],
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
IronOSSelectEntityDescription(
key=PinecilSelect.TIP_TYPE,
translation_key=PinecilSelect.TIP_TYPE,
characteristic=CharSetting.TIP_TYPE,
value_fn=lambda x: enum_to_str(x.get("tip_type")),
raw_value_fn=lambda value: TipType[value.upper()],
options=[x.name.lower() for x in TipType],
entity_category=EntityCategory.CONFIG,
),
)
async def async_setup_entry(
@@ -157,11 +182,17 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up select entities from a config entry."""
coordinator = entry.runtime_data
coordinators = entry.runtime_data
descriptions = PINECIL_SELECT_DESCRIPTIONS
descriptions += (
PINECIL_SELECT_DESCRIPTIONS_V223
if coordinators.live_data.v223_features
else PINECIL_SELECT_DESCRIPTIONS_V222
)
async_add_entities(
IronOSSelectEntity(coordinator, description)
for description in PINECIL_SELECT_DESCRIPTIONS
IronOSSelectEntity(coordinators, description) for description in descriptions
)
+22 -1
View File
@@ -94,6 +94,9 @@
},
"temp_increment_long": {
"name": "Long-press temperature step"
},
"hall_effect_sleep_time": {
"name": "Hall sensor sleep timeout"
}
},
"select": {
@@ -173,6 +176,15 @@
"off": "[%key:common::state::off%]",
"on": "[%key:common::state::on%]"
}
},
"tip_type": {
"name": "Soldering tip type",
"state": {
"auto": "Auto sense",
"ts100_long": "TS100 long/Hakko T12 tip",
"pine_short": "Pinecil short tip",
"pts200": "PTS200 short tip"
}
}
},
"sensor": {
@@ -223,7 +235,16 @@
"sleeping": "Sleeping",
"settings": "Settings",
"debug": "Debug",
"boost": "Boost"
"boost": "Boost",
"soldering_profile": "Soldering profile",
"temperature_adjust": "Temperature adjust",
"usb_pd_debug": "USB PD debug",
"thermal_runaway": "Thermal runaway",
"startup_logo": "Booting",
"cjc_calibration": "CJC calibration",
"startup_warnings": "Startup warnings",
"initialisation_done": "Initialisation done",
"hibernating": "Hibernating"
}
},
"estimated_power": {
@@ -34,7 +34,7 @@
"services": {
"calibrate": {
"name": "Calibrate",
"description": "Calibration - Set depth, press & hold duration, and operation mode. Warning - this will send a push command to the device.",
"description": "Sets the depth, press or release duration, and operation mode. Warning - this will send a push command to the device.",
"fields": {
"entity_id": {
"name": "Entity",
@@ -42,15 +42,15 @@
},
"depth": {
"name": "Depth",
"description": "Depth in percent."
"description": "How far to extend the push arm."
},
"duration": {
"name": "Duration",
"description": "Duration in seconds."
"description": "How long to press or release."
},
"mode": {
"name": "[%key:common::config_flow::data::mode%]",
"description": "Normal | invert | toggle."
"description": "The operation mode of the arm."
}
}
}

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