Compare commits

..

528 Commits

Author SHA1 Message Date
Paulus Schoutsen
cfa4740973 Remove media_id from pipeline TTS-END events 2025-04-30 01:30:07 +00:00
Marc Mueller
97084e9382 Remove redundant typing cast in miele (#143913) 2025-04-30 03:13:23 +02:00
J. Nick Koston
9db34fe232 Bump habluetooth to 3.45.0 (#143909) 2025-04-30 00:05:33 +02:00
Åke Strandberg
c4f0b4ab23 Bump pymiele to 0.4.1 (#143903) 2025-04-30 00:55:23 +03:00
Brian Choromanski
1647afc58a Improve parse_time_expression list comprehension to get interval values (#143488) 2025-04-29 22:20:05 +01:00
Martin Hjelmare
53ea8422f8 Improve Z-Wave hassio confirm form text (#143908) 2025-04-29 22:57:30 +02:00
Jan Bouwhuis
0b988b3fac Bump incomfort-client to v0.6.8 (#143895)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-04-29 22:05:52 +02:00
Thomas55555
5a4abe3ec1 Bump apsystems-ez1 to 2.6.0 (#143897)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-04-29 21:36:13 +02:00
J. Nick Koston
89abc5ac69 Add WebSocket API to ssdp to observe discovery (#143862) 2025-04-29 21:03:53 +02:00
Norbert Rittel
08fe6653bb Add missing hyphen to "self-test" in weheat (#143899) 2025-04-29 22:02:24 +03:00
Norbert Rittel
9aa18c7157 Add missing hyphen to "self-check" in incomfort (#143900) 2025-04-29 20:59:15 +02:00
Retha Runolfsson
cc7929f8fb Add log when device is online and unavailable (#143648) 2025-04-29 20:52:12 +02:00
Josef Zweck
d657298791 Add statistic entities to lamarzocco (#143415)
* Bump pylamarzocco to 2.0.0b2

* Add statistic entities to lamarzocco

* add icons

* Update coordinator.py

* update uom

* Update homeassistant/components/lamarzocco/sensor.py

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

* revert cups

* remove unnecessary call (for now)

---------

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-04-29 20:49:26 +02:00
Jan Bouwhuis
05f393560f Fix mcp_server CI test (#143898) 2025-04-29 20:40:50 +02:00
Abílio Costa
92da640d4c Rename const maps in Whirlpool (#143409) 2025-04-29 20:39:29 +02:00
Manu
ad3fd151aa Add reconfiguration flow to ista EcoTrend integration (#143457) 2025-04-29 20:37:04 +02:00
Simon Lamon
cd104dc08c LinkPlay group members should return the entity ids (#141791) 2025-04-29 20:28:08 +02:00
Norbert Rittel
d3745d2519 Add missing hyphens to "self-…" in imeon_inverter (#143888)
* Add missing hyphens to "self-…" in `imeon_inverter`

* Update test_sensor.ambr
2025-04-29 21:01:49 +03:00
Norbert Rittel
931f3fa41a Fix spelling of "self-consumption" in tessie/tesla_fleet/teslemetry (#143890)
* Fix spelling of "self-consumption" in `tessie`

* Fix spelling of "self-consumption" in `tesla_fleet`

* Fix spelling of "self-consumption" in `teslemetry`
2025-04-29 21:01:41 +03:00
Norbert Rittel
87b5a91212 Add missing hyphen to "self-clean" in roborock (#143893)
Fix four states that contain "self-clean" by adding the missing hyphen.
2025-04-29 21:01:35 +03:00
Norbert Rittel
3b8da62d84 Make spelling of "self-consumption" consistent in growatt_server (#143886)
Also fix one overlooked sentence-casing.
2025-04-29 21:01:08 +03:00
Norbert Rittel
86a48294f4 Change all imap action descriptions to match HA style (#143894)
Change all `imap` action description to match HA style

Change all four descriptions to use third-person singular to ensure proper (machine) translations.
2025-04-29 20:59:06 +03:00
Ville Skyttä
a03884981f Prefer huawei_lte SSDP model name over friendly name (#143725) 2025-04-29 19:25:32 +02:00
Ville Skyttä
ab695f90c7 Upgrade url-normalize to 2.2.1 (#143751) 2025-04-29 19:10:57 +02:00
Allen Porter
efcf8f9555 Improve TurnOn/Off LLM tool descriptions (#143768) 2025-04-29 19:09:05 +02:00
epenet
f71903a563 Simplify device registry checks in renault tests (#143863) 2025-04-29 19:03:14 +02:00
Petro31
95552e9a5b Add trigger based template lights (#140631)
* Add abstract template light class in preparation for trigger based template lights

* add base for trigger entity

* Update more tests

* revert trigger template entity changes and light trigger tests.

* fix merge conflicts

* address comments

* change function name

* nitpick

* fix merge conflict issue

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-04-29 18:02:44 +02:00
Åke Strandberg
5da57271b2 Add 3 duration sensors to miele (#143160)
* Add 3 duration sensors

* Update snapshot

* Address review comments

* Cleanup

* Adjust type hint
2025-04-29 17:53:24 +02:00
Norbert Rittel
62a7139f4d Fix hyphens on "self-consumption"/"serial number" in enphase_envoy (#143887) 2025-04-29 17:29:48 +02:00
Franck Nijhof
a7be26cd95 Merge branch 'master' into dev 2025-04-29 15:17:37 +00:00
J. Nick Koston
9c3b0952e0 Turn off autospec for zeroconf mocks (#143879) 2025-04-29 16:45:58 +02:00
J. Nick Koston
c771f446b4 Bump aioesphomeapi to 30.1.0 (#143881) 2025-04-29 16:13:30 +02:00
Petro31
9a25561017 Fix duplicate code from merge conflict (#143880)
fix conflict
2025-04-29 16:09:08 +02:00
Manuel Stahl
bd870f0537 Remove dependency on modbus for stiebel_eltron (#136482)
* Remove dependency on modbus for stiebel_eltron

The modbus integration changed its setup, so it is
not possible anymore to have an empty hub.

* Add config flow

* Update pystiebeleltron to v0.1.0

* Fix

* Fix

* Add test for non existing modbus hub

* Fix tests

* Add more tests

* Add missing translation string

* Add test for import failure

* Fix issues from review comments

* Fix issues from review comments

* Mock stiebel eltron client instead of setup_entry

* Update homeassistant/components/stiebel_eltron/__init__.py

* Update homeassistant/components/stiebel_eltron/__init__.py

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-04-29 15:34:16 +02:00
Manuel Stahl
d7f43bddfa Remove dependency on modbus for stiebel_eltron (#136482)
* Remove dependency on modbus for stiebel_eltron

The modbus integration changed its setup, so it is
not possible anymore to have an empty hub.

* Add config flow

* Update pystiebeleltron to v0.1.0

* Fix

* Fix

* Add test for non existing modbus hub

* Fix tests

* Add more tests

* Add missing translation string

* Add test for import failure

* Fix issues from review comments

* Fix issues from review comments

* Mock stiebel eltron client instead of setup_entry

* Update homeassistant/components/stiebel_eltron/__init__.py

* Update homeassistant/components/stiebel_eltron/__init__.py

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-04-29 14:57:01 +02:00
Åke Strandberg
87107c5a59 Add log of missing codes to miele diagnostics (#143877)
Add missing code log to diagnostics
2025-04-29 14:56:45 +02:00
Patrick
9ce920b35a Add support for external USB drives to Synology DSM (#138661)
* Add external usb drives

* Add partition percentage used

* Move icons to icons.json

* Add external usb to diagnostics

* Add assert for external usb entity

* Fix reset external_usb

* Update homeassistant/components/synology_dsm/diagnostics.py

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

* Update homeassistant/components/synology_dsm/diagnostics.py

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

* Fix diagnostics

* Make each partition a device

* Add usb sensor tests

* Add diagnostics tests

* It is possible that api.external_usb is None

* Merge upstream into syno_external_usb

* add manufacturer and model to partition

* fix tests

---------

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
Co-authored-by: mib1185 <mail@mib85.de>
2025-04-29 13:32:21 +02:00
J. Diego Rodríguez Royo
15aff9662c Refresh Home Connect program entities possible options when an appliance gets connected (#143213)
Refresh options when an appliance gets connected
2025-04-29 13:12:21 +02:00
Åke Strandberg
da6fb91886 Add some more sensors to miele integration (#142979)
* Add some more sensors

* Add some debug logging and correct spelling

* Address review comments

* Split out duration sensors to separate PR

* Update strings

* Filter program phases by device type

* Update tests

* Fix auto link

* Address som of the comments

* Lint

* Lint

* Remove duplicates from enum sensor options

* Update snapshot

* Sort options in enum sensors
2025-04-29 13:07:55 +02:00
Alex Fuchs
1e880f7406 Bump apsystems-ez1 to 2.5.1 (#143739)
Bump apsystems-ez1 to 2.5.1 to fix debounce problem
2025-04-29 13:04:57 +02:00
Matrix
81153042d3 Bump YoLink Lib to v0.5.2 (#143873)
Bump YoLink API to v0.5.2
2025-04-29 12:57:23 +02:00
Michael
493ca261dc Add strict type checking to SMTP integration (#143698) 2025-04-29 12:56:29 +02:00
Ville Skyttä
7493b340ca Add more huawei_lte sensor descriptions (#143707) 2025-04-29 12:54:36 +02:00
Norbert Rittel
e85e60ed6a Use common state "Fault" in wolflink (#143688) 2025-04-29 12:53:09 +02:00
chammp
8ff4d5dcbf Adapt template sensors to use the same plural trigger/condition/action definitions as automations (#127875)
* Add plurals to template entities

* Ruff

* Ruffy ruff

* Fix linters

* Fix bug introduced after merging dev

* Fix merge mistake

* Revert adding automation helper

* Revert "Fix bug introduced after merging dev"

This reverts commit 098d478f150a06546fb9ec3668865fa5d763c6b2.

* Fix blueprint validation

* Apply suggestions from code review

---------

Co-authored-by: Erik <erik@montnemery.com>
2025-04-29 11:52:58 +02:00
Norbert Rittel
f2838e493b Use common state for "Fault" in peblar (#143708) 2025-04-29 11:39:21 +02:00
Åke Strandberg
a71edcf1a1 Add fan platform to miele integration (#143772)
* Add fan platform

* Fix after review comment

* Address review comments

* Remove commented code

* Update tests

* Use constant
2025-04-29 10:48:56 +02:00
Arie Catsman
47bef74e7c apply for platinum quality scale for enphase_envoy (#143846) 2025-04-29 10:41:22 +02:00
Erwin Douna
b757a7e3fe Replace pymelcloud with python-melcloud (#142120) 2025-04-29 10:38:00 +02:00
dependabot[bot]
362ff5724d Bump actions/attest-build-provenance from 2.2.3 to 2.3.0 (#143865) 2025-04-29 10:31:16 +02:00
Joost Lekkerkerker
4f8363a5c2 Add availability to SmartThings devices (#143836)
* Bump pySmartThings to 3.1.0

* Bump pySmartThings to 3.2.0

* Add availability to SmartThings devices

* Add availability to SmartThings devices

* Add availability to SmartThings devices
2025-04-29 10:29:07 +02:00
Maciej Bieniek
ae3925118c Do not allow to enable BT scanner for Shelly Gen4 device with Zigbee enabled (#143824)
* Bluetooth is not supported when Zigbee is enabled

* Update tests

* Format
2025-04-29 11:12:34 +03:00
Petro31
b2fcab20a6 Add trigger based entities to template switch (#141763)
* Add trigger based entities to template switch platform

* add suggestions
2025-04-29 09:40:16 +02:00
Brett Adams
6423957d29 Add common translations to Sentry in Teslemetry (#143868)
missing translation keys
2025-04-29 09:26:19 +02:00
Brett Adams
835cdad0a9 Add sentry mode sensor to Teslemetry (#143855)
* Add sentry mode sensor

* Fix state handler
2025-04-29 08:37:10 +02:00
Klaas Schoute
d8d6decb38 Bump odp-amsterdam to v6.1.1 (#143854) 2025-04-29 08:35:56 +02:00
Erik Montnemery
16b42cc109 Add cv.renamed (#143834) 2025-04-29 07:36:37 +02:00
tmenguy
a47f27821f Add some tests with an invalid plugStatus and renault twingo iii. (#143838) 2025-04-28 22:31:27 +02:00
Daniel Hjelseth Høyer
c797e7a973 Mill, add statistics (#130406)
* Mill, new features

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

* typo

* tests

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

* mill

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

* Update const.py

* Update sensor.py

* Update sensor.py

* Add test

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

* Add test

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

* mill

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

* mock_setup_entry

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

* after_depencies

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

* Mill

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

* mill stats

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

* mill stats

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

* format

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

* mill

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

* Add test

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

* tests

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

* mill

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

---------

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2025-04-28 21:59:42 +02:00
Norbert Rittel
245eb64405 Fix spelling of "self-test" in apcupsd (#143843) 2025-04-28 22:35:16 +03:00
Martin Hjelmare
a895fcf057 Bump zwave-js-server-python to 0.63.0 (#143844) 2025-04-28 21:34:47 +02:00
Norbert Rittel
5706fb26b8 Make spelling of "self-test" consistent in zha (#143842)
While the "self-test" button already contains the recommended hyphen it's missing in the switch and sensor entity names.
2025-04-28 21:07:50 +03:00
Åke Strandberg
3f82120cdc Add miele core temp sensors (#143785)
Add core temp sensors
2025-04-28 18:47:42 +02:00
Brett Adams
20df183470 Improve energy entities in Teslemetry (#143641)
* Energy fixes

* improvements

* Add more icons
2025-04-28 18:47:12 +02:00
dependabot[bot]
980216795f Bump docker/build-push-action from 6.15.0 to 6.16.0 (#143651)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](471d1dc4e0...14487ce63c)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-28 16:50:07 +02:00
Joost Lekkerkerker
fdfcd841ba Bump pySmartThings to 3.2.0 (#143833)
* Bump pySmartThings to 3.1.0

* Bump pySmartThings to 3.2.0
2025-04-28 16:23:06 +02:00
Josef Zweck
28a09794e9 Bump pylamarzocco to 2.0.0b6 (#143778)
* Bump pylamarzocco to 2.0.0b5

* bump to 6
2025-04-28 16:22:46 +02:00
David Knowles
a0c9217375 Schlage: Source valid auto lock times from pyschlage (#143382)
* Source auto lock times from pyschlage

* Update auto lock strings

* Test all options are translated
2025-04-28 14:45:38 +02:00
Petro31
469176c59b Fix trigger template entity issue when coordinator data is None (#143830)
Fix issue when coordinator data is None
2025-04-28 14:32:16 +02:00
Erik Montnemery
3ece672890 Update rainforest_raven test snapshots (#143829) 2025-04-28 14:04:10 +03:00
Brett Adams
c6ebba8843 Add streaming connectivity binary sensors to Teslemetry (#143443)
* Add connectivity entities

* Add connectivity entities

* Fix Wi-Fi spelling in Teslemetry component
2025-04-28 12:58:40 +02:00
Erik Montnemery
1f047807a4 Update netatmo test snapshots (#143828) 2025-04-28 13:48:50 +03:00
Erik Montnemery
f1b724c49a Update samsungtv test snapshots (#143826) 2025-04-28 13:48:39 +03:00
J. Nick Koston
5ebed2046c Bump bluetooth-data-tools to 1.28.1 (#143817) 2025-04-28 12:05:07 +02:00
Arie Catsman
d1236a53b8 add enphase_envoy interface mac to device registry (#143758)
* add enphase_envoy interface mac to device registry

* Test for capitalized error log entry.

* increase mac collection delay from 17 to 34 sec
2025-04-28 11:20:11 +02:00
Tsvi Mostovicz
84f07ee992 Bump hdate to 1.1.0 (#143759) 2025-04-28 10:38:49 +02:00
J. Nick Koston
d7f5e48626 Bump aioshelly to 13.6.0 (#143814)
changelog: https://github.com/home-assistant-libs/aioshelly/compare/13.5.0...13.6.0
2025-04-28 11:27:50 +03:00
Norbert Rittel
56e07bb1f2 Use common state for "Fault", add recommended hyphen in fronius (#143812)
* Use common state for "Fault" in `fronius`

Also add a recommended hyphen to "self-consumption".

See Wiktionary: "Words derived from self- are usually formed with a hyphen. Using a hyphen is recommended by the U.S. Government Printing Office Style Manual."

* Update test_sensor.ambr
2025-04-28 11:18:07 +03:00
J. Nick Koston
45b2700375 Bump habluetooth to 3.44.0 (#143802) 2025-04-28 03:45:47 -04:00
Erik Montnemery
d860b35f41 Fix flaky test test_async_parallel_updates_with_zero_on_sync_update (#143810) 2025-04-28 09:27:26 +02:00
Martin Hjelmare
5392062edd Add backup agent retention config (#143174) 2025-04-28 09:24:23 +02:00
Åke Strandberg
d9a09a2aea Enable deletion of stale miele devices (#143811)
Enable deletion of stale devices
2025-04-28 08:59:34 +02:00
epenet
3daff73d36 Add renault reconfigure flow (#143449)
* Add renault reconfigure flow

* docstring
2025-04-28 08:43:20 +02:00
J. Nick Koston
e6b88ec087 Bump thermopro-ble to 0.12.0 (#143799)
* Bump thermopro-ble to 0.12.0

changelog: https://github.com/Bluetooth-Devices/thermopro-ble/compare/v0.11.0...v0.12.0

* update tests
2025-04-28 08:37:20 +02:00
J. Nick Koston
592dcec852 Bump govee-ble to 0.44.0 (#143800)
changelog: https://github.com/Bluetooth-Devices/govee-ble/compare/v0.43.1...v0.44.0
2025-04-28 08:36:02 +02:00
J. Nick Koston
2a6b79ec0f Bump bluemaestro-ble to 0.3.0 (#143795)
* Bump bluemaestro-ble to 0.3.0

changelog: https://github.com/Bluetooth-Devices/bluemaestro-ble/compare/v0.2.3...v0.3.0

* update tests
2025-04-28 08:35:22 +02:00
J. Nick Koston
afc1d224a0 Bump sensorpro-ble to 0.6.0 (#143796)
* Bump sensorpro-ble to 0.6.0

changelog: https://github.com/Bluetooth-Devices/sensorpro-ble/compare/v0.5.3...v0.6.0

* update tests
2025-04-28 08:34:34 +02:00
J. Nick Koston
b668acda24 Bump inkbird-ble to 0.14.1 (#143793)
changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.13.0...v0.14.1
2025-04-28 08:33:48 +02:00
J. Nick Koston
c3996d6931 Bump sensorpush-ble to 1.8.0 (#143794)
* Bump sensorpush-ble to 1.8.0

changelog: https://github.com/Bluetooth-Devices/sensorpush-ble/compare/v1.7.1...v1.8.0

* fix tests
2025-04-28 08:33:11 +02:00
J. Nick Koston
9ec174776c Bump leaone-ble to 0.2.0 (#143798)
* Bump leaone-ble to 0.2.0

changelog: https://github.com/Bluetooth-Devices/leaone-ble/compare/v0.1.0...v0.2.0

* update tests
2025-04-28 08:31:58 +02:00
J. Nick Koston
6a8722cf7c Bump thermobeacon-ble to 0.9.0 (#143797)
* Bump thermobeacon-ble to 0.9.0

changelog: https://github.com/Bluetooth-Devices/thermobeacon-ble/compare/v0.8.1...v0.9.0

* update tests
2025-04-28 08:30:57 +02:00
epenet
000b1d80b0 Update docs in renault quality-scale (#143806) 2025-04-28 08:29:28 +02:00
epenet
6d8654610e Remove obsolete code in Renault integration (#143808) 2025-04-28 08:25:03 +02:00
Olivier Douville
5cd4c8e896 Add missing state class in sfr-box (#143773)
* Update sensor.py - Add MEASUREMENT state class on alimvoltage and temperature sensors

This will allow state values to be stored in LTS (long term statistics)

* Update tests accordingly to previous changes in sensors

* Update tests accordingly to previous changes in sensors
2025-04-28 07:55:29 +02:00
Erik Montnemery
cec8db173b Remove redundant entity_id collision check in entity registry (#143660)
* Remove redundant entity_id collision check in entity registry

* Update test
2025-04-28 07:50:26 +02:00
J. Nick Koston
dd9dad80be Bump habluetooth to 3.42.0 and bleak-esphome to 2.14.0 (#143787) 2025-04-27 19:36:58 -05:00
Åke Strandberg
9992ade051 Bump pymiele to 0.4.0 (#143789) 2025-04-27 23:31:10 +01:00
J. Nick Koston
36da4a9b72 Bump bluetooth-data-tools to 1.28.0 (#143782)
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.27.0...v1.28.0

related issue https://github.com/home-assistant/core/issues/143769#issuecomment-2833594159
2025-04-27 16:50:42 -05:00
Mick Vleeshouwer
3fc34244ac Fix hvac_mode property to handle missing CORE_ON_OFF state in Atlantic Electrical Heater in Overkiz (#143330) 2025-04-27 20:42:51 +02:00
tronikos
753c07e911 Bump opower to 0.12.0 (#143748) 2025-04-27 20:40:10 +02:00
Joris Drenth
d0850e2931 Bump Wallbox version to 0.9.0 (#143775)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-04-27 20:36:20 +02:00
Manu
c704df004a Add diagnostics platform to ntfy platform (#143774) 2025-04-27 19:58:15 +02:00
Yuxin Wang
d95c9c496e Make exception messages translatable for APCUPSD (#143747)
Add translation domain and key for UpdateFailed in coordinator
2025-04-27 16:35:55 +02:00
Ville Skyttä
d28f4ed618 Set device class for huawei_lte connectivity binary sensors (#143764) 2025-04-27 16:34:11 +02:00
Jan Bouwhuis
7a0580eff5 Import media player constants at integration level for alexa smart home (#143767) 2025-04-27 15:36:42 +02:00
Sanjay Govind
f94af84f2a Update deprecated const usage in alexa integration (#143741) 2025-04-27 14:33:16 +02:00
Allen Porter
31fb199670 Bump voluptuous-openapi to 0.0.7 (#143742) 2025-04-27 12:10:26 +02:00
Brett Adams
a1ca0a1cb2 Dont add location entities without location scope in Teslemetry (#143497)
* Dont add location entities without location scope

* Fix tests

* simplify logic

* Add test
2025-04-27 11:25:58 +02:00
Allen Porter
2326c23133 Increase Gemini max tokens to avoid failures observed in evaluations (#143728)
* Increase Gemini max tokens to avoid failures observed in evaluations

* Update snapshots
2025-04-26 15:30:47 -07:00
J. Nick Koston
d4c1d1bdb9 Split up SSDP integration into modules (#143732)
* Split up SSDP integration into modules

* Split up SSDP integration into modules

* migrate tests
2025-04-26 18:09:51 -04:00
Allen Porter
8d258871ff Record Anthropic token statistics in conversation trace (#143727)
* Record anthopic token statistics in conversation trace

* Add test coverage for output token parsing
2025-04-26 18:04:12 -04:00
Thomas55555
49299a6bf0 Bump aioautomower to 2025.4.4 (#143533)
* Bump aioautomower to 2025.4.1

* Update split_tests.py

* revert b3222b9be9

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

* aioautomower==2025.4.2

* fix

* aioautomower==2025.4.30b0

* revert

* some try

* aioautomower==2025.4.0

* aioautomower==2025.4.3b0

* aioautomower==2025.4.4

---------

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-04-27 00:07:14 +03:00
Shay Levy
868b8ad318 Move Switcher handle_coordinator_update to base entity (#143738) 2025-04-27 00:01:44 +03:00
Simone Chemelli
40752dcfb6 Translate missing exceptions in SamsungTV (#143628)
* Translate missing exceptions in SamsungTV

* apply review comment
2025-04-26 22:43:07 +02:00
Stefan Agner
18f51abfe6 Remove unnecessary Supervisor info call (#143700) 2025-04-26 15:27:31 -05:00
Maikel Punie
3e2c54dcbd Bump velbusaio to 2025.4.2 (#143675) 2025-04-26 15:22:10 -05:00
Manu
a0cd14b4e8 Add reauth flow to ntfy integration (#143729) 2025-04-26 22:05:13 +02:00
Norbert Rittel
35c6fdbce8 Use common state for "Fault" in shelly (#143730) 2025-04-26 21:08:39 +03:00
sebfortier2288
202addc39d Remove sebfortier2288 from Soma code owners (#143715)
* chore(soma): remove from codeowner

* chore(soma): remove from sebfortier2288 codeowners
2025-04-26 19:56:56 +03:00
Shay Levy
d8cb7c475b Update Switcher temperature sensor device class and state class (#143722)
* Update Switcher temperature sensor device class and state class

* Remove  temperature translation key

* Remove icon
2025-04-26 16:22:44 +02:00
Shay Levy
03bacd747e Use device_registry fixture in Switcher test_remove_device (#143723) 2025-04-26 17:05:51 +03:00
Manu
97b6a68cda Improve device handling for disconnected IronOS devices (#143446)
* Improve device handling for disconnected IronOS devices

* requested changes

* ble_device
2025-04-26 13:34:44 +02:00
Shay Levy
eee18035cf Use value_fn in Switcher sensor platform (#143711) 2025-04-26 13:34:13 +02:00
Maciej Bieniek
f1b3b0c155 Refactor tests for Shelly config flow (#143517)
* Add mock_setup_entry

* Add mock_setup

* Improve test_form_gen1_custom_port

* Improve test_form_errors_get_info

* Improve test_form_errors_test_connection

* Improve test_reconfigure_with_exception

* Improve test_form_auth_errors_test_connection_gen1

* Improve test_form_auth_errors_test_connection_gen2

* Cleaning

* Upate quality scale

* Always use result variable

* Remove unnecessary async_block_till_done
2025-04-26 13:00:45 +03:00
Åke Strandberg
f5d3495c62 Add properties to miele entity class (#143622)
* Add properties to Entity class

* Remove setter and most platform constructors
2025-04-26 09:55:11 +02:00
Martin Hjelmare
e14a356c24 Allow Z-Wave controller migration on USB discovery (#143677)
Allow migration on USB discovery
2025-04-26 07:52:32 +02:00
J. Nick Koston
4e7d396e5b Add WebSocket API to zeroconf to observe discovery (#143540)
* Add WebSocket API to zeroconf to observe discovery

* Add WebSocket API to zeroconf to observe discovery

* increase timeout

* cover

* cover

* cover

* cover

* cover

* cover

* fix lasting side effects

* cleanup merge

* format
2025-04-25 21:18:09 -04:00
J. Nick Koston
34d17ca458 Move state length validation to StateMachine APIs (#143681)
* Move state length validation to StateMachine async_set method

We call validate_state to make sure we do not allow any states
into the state machine that have a length>255 so we do not break
the recorder. Since async_set_internal already requires callers
to pre-validate the state, we can move the check to async_set
instead of at State object creation time to avoid needing to
check it twice in the hot path (entity write state)

* move check in async_set_internal so it only happens on state change

* no need to check if same_state
2025-04-25 21:15:15 -04:00
J. Nick Koston
03950f270a Remove lower call in async_reserve (#143682)
async_reserve is only called from the the entity_platform helper
which already ensures the entity_id is validated and in lower
case.
a783b6a0ab/homeassistant/helpers/entity_platform.py (L936)
2025-04-25 21:12:55 -04:00
Denis Shulyaka
7074331461 Preserve reasoning during tool calls for openai_conversation (#143699)
Preserve reasoning after tool calls for openai_conversation
2025-04-25 21:12:23 -04:00
Maksim Doroshko
4c9cd70f65 Set unique id in ephember (#143180)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-25 23:06:16 +01:00
Ville Skyttä
7a105de969 Add missing huawei_lte sensor translations (#143694) 2025-04-25 22:54:56 +01:00
Åke Strandberg
eec9a28fe8 Add zeroconf discovery to miele (#143259)
* Add zeroconf discovery

* Strip unnecessary code

* Remove one line more

* Remove one more

* Add test for zeroconf flow

* Finish zeroconf flow
2025-04-25 23:18:20 +02:00
Arie Catsman
963f1b1907 bump pyenphase to 1.26.0 (#143686) 2025-04-25 08:50:37 -10:00
dependabot[bot]
dcac9b5f20 Bump actions/download-artifact from 4.2.1 to 4.3.0 (#143650) 2025-04-25 20:40:18 +02:00
Joost Lekkerkerker
765a95c273 Set entities to config category in SmartThings (#143669) 2025-04-25 20:21:35 +02:00
Tomáš Bedřich
6a115d0133 Add S3 integration (#139325)
* Add S3 integration

* Improve translations and error handling

* Test S3 integration

* Update QoS

* Add missing data_description strings

* Fix missing async_initialize_backup in tests

* PR changes

* Remove unique ID, rely on abort_entries_match

* Raise only BackupAgentError (#139754), introduce decorator for error handling

* Switch to metadata-file based solution

* PR changes

* Revert strict typing

* Bump dependency

* Silence mypy

* Pass docs URLs as description_placeholders

* PR changes

* Rename _api to api

* PR Changes

* PR Changes 2

* Remove api abstraction

* Handle S3 multipart upload size limitations

* PR changes
2025-04-25 20:16:44 +02:00
Åke Strandberg
a057effad5 Add miele binary_sensor platform (#142903)
* Add binary_sensor platform

* Address review comments

* Adjust icons and names.

* Change Info to Notification active

* Trigger CI

* Trig CI

* Adjust tests

* Update strings.json

* Update strings.json
2025-04-25 19:32:08 +02:00
Dan
94b0800989 Fix surepetcare sensor error (#143286)
* fix: changed boolean to map to 'online' attribute.

* fix: added catch in case of future changes to prevent complete sensor failure.

* fix: surepetcare - added additional catches in case rssi values aren't included in online status.

* fix: remove hub_rssi when not defined.

* fix: proper code spacing

* fix: use .get for clarity instead of try.

* fix: now written in Python.

* fix: renamed variables for clarity.

* Update homeassistant/components/surepetcare/binary_sensor.py

* fix: update surepetcare test __init__.py mock_feeder with online status.

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-25 19:29:29 +02:00
Åke Strandberg
a783b6a0ab Add climate platform to miele integration (#143333)
* Add climate platform

* Merge

* Address review and improve test

* Address review comments

* Streamline entity naming

* Update tests/components/miele/test_climate.py

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-25 19:18:39 +02:00
Åke Strandberg
5302964eb6 Add button platform to miele (#143508)
* WIP Button platform

* Add button platform

* Disable by default, Address review , update tests

* Follow review comments
2025-04-25 19:10:32 +02:00
Norbert Rittel
261dbd16a6 Add common state "Fault" (#143390) 2025-04-25 18:47:19 +02:00
Guido Schmitz
672dbc03c6 Use coordinator data for devolo Home Network PLC data rate sensor (#143606) 2025-04-25 18:45:16 +02:00
Åke Strandberg
ed0bdf9e5f Add switch platform to miele integration (#142925)
* Add switch platform

* Add a type hint

* Update after review
2025-04-25 18:40:52 +02:00
Simone Chemelli
735e2e4192 Add missing exception translations to Comelit (#142861)
* Add missing exception translations to Comelit

* update quality scale

* remove unwanted placeholder
2025-04-25 18:34:29 +02:00
Martin Hjelmare
0aabb11220 Improve Z-Wave migration flow (#143673) 2025-04-25 18:33:19 +02:00
Jozef Kruszynski
09ad14bc28 Update Music Assistant browse media types (#143249)
* Update Music Assistant browse media types

* changes based on review comments
2025-04-25 18:32:48 +02:00
J. Nick Koston
d61e39743b Reduce ref counting in _async_write_ha_state (#143634)
* Reduce ref counting in _async_write_ha_state

It no longer makes sense to keep a temp reference
to entity_id and hass since the function was
refactored and there are very few accesses now.

* one more place we can reduce ref counts
2025-04-25 18:25:16 +02:00
Paulus Schoutsen
ea90df434b Add an icon to the VoIP assist satellite entities (#143671) 2025-04-25 11:02:53 -05:00
Norbert Rittel
67fc682df2 Sentence-case "webhook" in locative (#143646) 2025-04-25 17:27:32 +02:00
Norbert Rittel
381b495efc Change "webhook (applet)" to lowercase in ifttt (#143642) 2025-04-25 17:27:22 +02:00
Norbert Rittel
812db815f1 Change "webhook" to lowercase and use "webhook service" in dialogflow (#143643)
* Change "webhook" to lowercase and fix grammar in `dialogflow`

* Replace "integration" with "service"
2025-04-25 17:22:12 +02:00
Retha Runolfsson
24ee19f1e2 Update quality scale for switchbot (#143145)
update quality_scale
2025-04-25 17:21:01 +02:00
Everton Leite
f72c5ebb76 Add ratio attribute to Transmission torrent info (#143459) 2025-04-25 17:00:02 +02:00
epenet
1075ea1220 Bump renault-api to 0.3.0 (#143657) 2025-04-25 16:52:23 +02:00
Glenn Waters
ce7edca136 Bump env_canada lib to 0.10.2 (#143664) 2025-04-25 16:44:16 +02:00
Doug Hoffman
3e16857a1e Bump uiprotect to 7.5.5 (#143668)
* Update manifest.json

* Update requirements_all.txt

* Update requirements_test_all.txt
2025-04-25 16:43:52 +02:00
Martin Hjelmare
5b1e32f51d Clean up Z-Wave config flow (#143670) 2025-04-25 16:43:19 +02:00
Ludovic BOUÉ
4adf5ce826 Support for Matter 1.4 Water Heater device type (#131505)
* Create water_heater.json

* Update water_heater.json

* Update water_heater.json

* TankVolume

* TankPercentage

* WaterHeaterMode

WaterHeaterMode

* Update sensor.py

* ruff-format

* Update water_heater.json

 Attributes of WaterHeaterManagement Cluster on Endpoint 2
ClusterId 148 (0x0094)

* Update test_sensor.py

water_heater fixture

* Update test_sensor.py

* SensorDeviceClass=VOLUME_STORAGE for `TankVolume`

* `BoostStateEnum` map

* WaterHeaterManagementBoostState

* Update sensor.py

* WaterHeaterManagementEstimatedHeatRequired

* Fix UnitOfEnergy

* Format

* Add `device_types.WaterHeater` to Climate

* Strings for Tank sensors

* WaterHeater icons

* Update icons.json

* Update strings.json

* Update water_heater.json

* ruff-format

* Fix tests

* Fix sensor.py

* Fix icons

* WaterHeaterManagementEstimatedHeatRequired

* WaterHeaterManagementBoostState

* BoostState as a binary sensor

* ElectricalPowerMeasurement values

* Fix tests

* Create water_heater.py

* Update climate.py from dev branch

* Resolve conflicts

* ruff-format

* Add Platform.WATER_HEATER

* Update water_heater.py

* Update water_heater.py

* Update water_heater.py

* Update water_heater.py

* Add WaterHeaterManagement sensors

* Update tests

* Add select test

* Add strings

* First try with water_heater

* Testing current_operation

* BoostState attribute

* target_temperature attributes

* target_temperature attribute

* set_temperature and set_operation_mode

* turn_on / turn_off

* Trigger Boost command

* Fix WaterHeaterBoostInfoStruct

* Add test file

* Add climate cluster to fixture

* Add climate cluster to fixture

* Add tests

* Add ON_OFF feature

* Update tests

* Update tests

* Translate WaterHeaterMode

* Change description

* Update test and snapshots

* Update snapshots

* Set entity name to None to make the device name be the name of the entity

* Format

* Update water_heater.py

* Fix format

* ruff-format

* Import ServiceValidationError

* Update homeassistant/components/matter/water_heater.py

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

* Update water_heater.py

* Update test_water_heater.py

* Update test_water_heater.ambr

* Update test_water_heater.py

* Update select.py

* Update snapshots

* Rename to boost_info

* Set WaterHeaterMode

* Update snapshots

* Update snapshots

* Fix for warning
W7431: Argument 3 should be of type AddConfigEntryEntitiesCallback in async_setup_entry (hass-argument-type)

* Update strings.json

* Update strings and tests

* Fix missing brace

* Update tests

* fix test

* Updates strings

* Fix async_set_temperature

* Update tests

* Update tests

* Update homeassistant/components/matter/water_heater.py

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

* Sort strings in strings.json

* Update homeassistant/components/matter/water_heater.py

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

* Remove unused line

* Remove min/max target temperatures

* Remove BOOST_STATE_MAP

* Add comment

* Remove SUPPORT_FLAGS_HEATER

* Remove system_mode_value check

* Update homeassistant/components/matter/water_heater.py

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

* Reformat async_set_temperature()

* Update snapshots

* Remove MatterWaterHeaterMode selector

* Update snapshots

* Rename test to test_water_heater_set_temperature

* Add test_water_heater_set_operation_mode

* Remove reset_mock

* Update tests/components/matter/test_water_heater.py

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

* Add test_update_from_water_heater

* Add test_water_heater_turn_on_off

* Add test_water_heater_boostmode

* Fix SystemMode value for STATE_HIGH_DEMAND

* Add disable boost from water heater device side test

* Remove unused lines

* Remove unused lines

* Fix test indentation

* Fix water heater tests

* Check for None

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-04-25 15:28:28 +02:00
Petro31
4a1905a2a2 Update template cover to modern style config (#141878) 2025-04-25 15:22:49 +02:00
Michael
59af3a396c Remove unnecessary mixins from AVM Fritz!SmartHome (#143658)
remove unnecessary mixin
2025-04-25 14:12:59 +02:00
Martin Hjelmare
7c584ece23 Make proper Z-Wave reconfigure flow (#143549)
* Make proper Z-Wave reconfigure flow

* Improve backup_failed string
2025-04-25 14:19:03 +03:00
Petro31
ff2c901930 Update trigger based template entity resolution order (#140660)
* Update trigger based template entity resolution order

* add test

* fix most comments

* Move resolution to base class

* add comment

* remove uncessary if statement

* add more tests

* update availability tests

* update logic stage 1

* phase 2 changes

* fix trigger template entity tests

* fix trigger template entities

* command line tests

* sql tests

* scrape test

* update doc string

* add rest tests

* update sql sensor _update signature

* fix scrape test constructor

* move state check to trigger_entity

* fix comments

* Update homeassistant/components/template/trigger_entity.py

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

* Update homeassistant/helpers/trigger_template_entity.py

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

* Update homeassistant/helpers/trigger_template_entity.py

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

* update command_line and rest

* update scrape

* update sql

* add case to command_line sensor

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-04-25 13:17:25 +02:00
Erik Montnemery
dc8e1773f1 Remove unused defaults from entity_registry.RegistryEntry (#143655) 2025-04-25 12:41:58 +02:00
Stefan Agner
b0d9a2437d Bump aiohasupervisor from version 0.3.b1 to version 0.3.1 (#143585) 2025-04-25 12:20:28 +02:00
Paul Bottein
2be6ecd50f Assign plex update entity to server device (#143654)
* Assign plex update entity to server device

* Fix tests

* Apply suggestions from code review

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-25 11:21:14 +02:00
J. Nick Koston
fa0bb35e6c Avoid creating tasks to add entities when no entities are passed (#143647)
async_add_entities would return early if no entities
were passed but its a bit cleaner to not create the
task in the first place. I noticed in py-spy that
tplink was passing empty lists frequently
which made a task and than did nothing.
2025-04-25 10:16:20 +02:00
Franck Nijhof
360bffa3a9 2025.4.4 (#143653) 2025-04-25 09:47:05 +02:00
Maciej Bieniek
5b503f21d7 Abort Shelly flows if the device is not fully provisioned (#143652)
* Abort flows if the device is not fully provisioned

* Update tests
2025-04-25 10:37:58 +03:00
Franck Nijhof
2214d9b330 Bump version to 2025.4.4 2025-04-25 06:54:02 +00:00
Joost Lekkerkerker
6a2d733d85 Bump pysmartthings to 3.0.5 (#143586) 2025-04-25 06:53:49 +00:00
cnico
7392d5a30a Bump dio-chacon-api to v1.2.2 (#143489)
Bump dio-chacon-api to v1.2.2 to solve https://github.com/home-assistant/core/issues/142808
2025-04-25 06:53:48 +00:00
J. Nick Koston
b3deeca939 Bump aiohomekit to 3.2.14 (#143440) 2025-04-25 06:53:47 +00:00
Simone Chemelli
c38a3a239c Fix Vodafone Station config entry unload (#143371) 2025-04-25 06:53:45 +00:00
Simon Lamon
afa6ed09ef Sync random sensor device classes (#143368) 2025-04-25 06:53:44 +00:00
Simon Lamon
deb966128f Add scan interval and parallel updates to LinkPlay media player (#143324) 2025-04-25 06:53:43 +00:00
Marc Mueller
73707fa231 Fix licenses check for setuptools (#143292) 2025-04-25 06:53:41 +00:00
Marc Mueller
10ac39f6b2 Update setuptools to 78.1.1 (#143275) 2025-04-25 06:53:40 +00:00
Arjan
2e05dc8618 Météo-France: Additional states and change weather condition for "Ciel clair" (#143198)
* Additional new states and change for ciel-clair

* Adding new previously unmapped state

* Adding new forecast state

Adding Brouillard dense, reported after the review
2025-04-25 06:53:39 +00:00
J. Diego Rodríguez Royo
d8233b4de5 Create Home Connect active and selected program entities only when there are programs (#143185)
* Create active and selected program entities only when there are programs

* Test improvements
2025-04-25 06:53:37 +00:00
Arjan
7cbc3ea65f Meteofrance: adding new states provided by MF API since mid April (#143137) 2025-04-25 06:53:36 +00:00
J. Nick Koston
cb0523660d Improve error logging when state is too long (#143636) 2025-04-24 18:37:32 -10:00
Michael
605bf7e287 Add volume flow rate device class to water_flow sensor in PEGELONLINE (#143631)
add SensorDeviceClass.VOLUME_FLOW_RATE to water_flow sensor
2025-04-25 00:42:58 +02:00
Michael
3405b2549b Add new units L/h , L/s and m³/s to volume flow rate sensor device class (#143625)
add new units L/h , L/s and m³/s
2025-04-25 00:17:47 +02:00
Shay Levy
d83c617566 Fix naming consistency in Switcher service strings (#143629) 2025-04-25 01:00:42 +03:00
Brett Adams
7016c19b2f Disable polling for modern vehicles in Teslemetry (#143495) 2025-04-24 23:59:26 +02:00
Simone Chemelli
5cd4a0ced6 Use typed ConfigEntry in SamsungTV (#143627) 2025-04-24 23:55:10 +02:00
J. Nick Koston
347c1a2141 Remove duplicate _attr_should_poll in ESPHome EsphomeAssistSatelliteWakeWordSelect (#143624) 2025-04-25 00:41:51 +03:00
J. Nick Koston
46eae64ef6 Mark ESPHome quality as platinum (#143033) 2025-04-24 11:30:51 -10:00
J. Nick Koston
a74fe60b91 Fix ESPHome async_step_reconfigure signature (#143620) 2025-04-24 11:30:27 -10:00
J. Nick Koston
fab70a80bb Quality improvements for the ESPHome dashboard coordinator (#143619) 2025-04-24 23:20:05 +02:00
J. Nick Koston
2abe2f7d59 Remove unused hass from EsphomeAssistSatelliteWakeWordSelect (#143618) 2025-04-24 11:18:39 -10:00
Abílio Costa
cc970354d7 Add Maytag virtual integration supported by Whirlpool (#143612) 2025-04-24 23:14:39 +02:00
Jan Bouwhuis
e389ff2537 Allow float for device_tracker location accuracy (#143604) 2025-04-24 23:09:18 +02:00
hahn-th
088f0c82bd Bump homematicip to 2.0.1 (#143609) 2025-04-24 23:07:59 +02:00
Norbert Rittel
fa1bb27dd2 Fix sentence-casing of "webhook" in gpslogger and geofency (#143614)
* Fix sentence-casing of "webhook" in `gpslogger`

* Fix sentence-casing of "webhook" in `geofency`
2025-04-25 00:07:42 +03:00
J. Nick Koston
5a6ce34352 Improve ESPHome test typing (#143617) 2025-04-24 10:41:37 -10:00
Paulus Schoutsen
fdcb88977a Add voice styles to HA Cloud (#143605)
* Add voice styles to HA Cloud

* Add seperator and extract util
2025-04-24 16:23:15 -04:00
Stefan Agner
a584ccb8f7 Remove add-on changelog from cached information (#143526) 2025-04-24 22:14:46 +02:00
Mick Vleeshouwer
cc290b15f6 Fix available status of entities in Overkiz (#143538)
* Add availability property to OverkizEntity for device status

* Update homeassistant/components/overkiz/entity.py

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

---------

Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-04-24 21:58:36 +02:00
Shay Levy
575db4665d Fix Switcher review comments (#143607) 2025-04-24 21:54:25 +02:00
J. Nick Koston
a61aff8432 Cleanup duplicate entry data in ESPHome assist_satellite (#143611) 2025-04-24 09:51:58 -10:00
J. Nick Koston
3aa1c60fe3 ESPHome quality improvements round 2 (#143613)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-24 09:51:33 -10:00
Abílio Costa
39f3aa7e78 Mark Whirlpool quality as bronze (#143603) 2025-04-24 20:44:15 +02:00
J. Nick Koston
01e2c3272b Improve error message when ESPHome reconfigure selects an unexpected device (#143608) 2025-04-24 08:44:02 -10:00
J. Nick Koston
5afcd3e54e Remove the previously deprecated ESPHome assist in progress binary sensor (#143536) 2025-04-24 08:43:48 -10:00
Norbert Rittel
b081064954 Use correct singular and lowercase for "webhook" in mailgun (#143595) 2025-04-24 21:38:42 +03:00
Norbert Rittel
11e63ca96a Use correct singular and lowercase for "webhook" in twilio (#143596) 2025-04-24 21:38:03 +03:00
Abílio Costa
6457d46107 Raise ConfigEntryNotReady when fetching Whirlpool appliances fails (#143601) 2025-04-24 21:25:15 +03:00
Norbert Rittel
987bf4d850 Fix spelling of "counterclockwise" in deconz (#143523) 2025-04-24 21:23:40 +03:00
Paulus Schoutsen
fa80c0a88d Bump hass-nabucasa to 0.96.0 (#143542)
* Bump hass-nabucasa to 0.96.0

* Adjust for new voice info format
2025-04-24 13:12:11 -04:00
Norbert Rittel
f69484ba02 Fix missing plural on "Advisories" in environment_canada (#143562) 2025-04-24 19:17:30 +03:00
Norbert Rittel
11f63c7868 Use common strings for "already_in_progress" etc. in music_assistant (#143570)
* Use common string for "already_in_progress" in `music_assistant`

* Use common string for "cannot_connect" as well
2025-04-24 19:16:43 +03:00
Norbert Rittel
3245124553 Use common string for error::unknown in iometer (#143575) 2025-04-24 19:16:33 +03:00
Joost Lekkerkerker
44475967eb Bump pysmartthings to 3.0.5 (#143586) 2025-04-24 19:13:58 +03:00
Norbert Rittel
2d27b5ac53 Use common string for abort::unknown in srp_energy (#143576) 2025-04-24 17:20:53 +02:00
Erik Montnemery
2ae161d8b5 Wait for person integration in onboarding (#143584) 2025-04-24 17:08:53 +02:00
Norbert Rittel
aefe83b1a3 Use common string for "cannot_connect" in imgw_pib (#143574) 2025-04-24 16:54:41 +02:00
Abílio Costa
f86e85b931 Use None for Unknown state in Whirlpool sensor (#143582) 2025-04-24 15:12:45 +01:00
dependabot[bot]
993ebc9eba Bump github/codeql-action from 3.28.15 to 3.28.16 (#143546)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.15 to 3.28.16.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3.28.15...v3.28.16)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.16
  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-04-24 15:34:14 +02:00
dependabot[bot]
1d99bbf22e Bump actions/setup-python from 5.5.0 to 5.6.0 (#143545)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.5.0 to 5.6.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5.5.0...v5.6.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 15:32:11 +02:00
Åke Strandberg
eb4fa635bf Add miele light platform (#143119)
* WIP

* Add light platform

* Address review comments

* Address review and improve tests

* Address review comments in tests
2025-04-24 15:02:39 +02:00
Michael
49522d93df Enable strict type checks for PEGELONLINE (#143563)
enable strict type checks for pegel_online
2025-04-24 14:42:47 +02:00
Norbert Rittel
9e0a7122f5 Fix typos and use a common string in synology_dsm (#143573)
- fix spelling of "Home Assistant", removing wrong hyphen
- remove excessive comma
- fix spelling of "passcode" (single word)
- capitalize "Zeroconf" (name)
- use common string for "reconfigure_successful"
2025-04-24 14:36:49 +02:00
Åke Strandberg
e4fe7ba985 Fix bug in miele diagnostics (#143569)
Fix bug when redacting identifiers in diagnostics
2025-04-24 14:16:31 +02:00
Simone Chemelli
f3ea11bbc1 Bump aiovodafone to 0.10.0 to use async_create_clientsession in Vodafone Station integration (#143537)
* Use async_create_clientsession in Vodafone Station integration

* bump library and rename method
2025-04-24 14:05:42 +02:00
Simone Chemelli
55de91530d Bump aiocomelit to 0.12.0 to use async_create_clientsession in Comelit integration (#143528)
* Use async_create_clientsession in Comelit integration

* bump library and rename method
2025-04-24 14:05:11 +02:00
Maciej Bieniek
290bbcfa3e Improve type annotation in the Shelly text and number platform (#143568)
* Define _id with type

* Define attribute_value with type
2025-04-24 13:55:40 +02:00
Maciej Bieniek
061a1be2bc Use DeviceInfo in the Shelly RPC entity base class (#143565)
Use DeviceInfo
2025-04-24 13:49:43 +02:00
Michael
4bd8c319dd Small fixes to the translation strings in PEGELONLINE (#143567)
small fixes
2025-04-24 13:47:23 +02:00
Michael
367022dd8c Use shorthand attributes in PEGELONLINE (#143564)
use shorthand attributes
2025-04-24 13:39:34 +02:00
ildar170975
f1975d9dbf Elevate Recorder "Error executing ..." from warning to error (#142816) 2025-04-24 11:36:39 +01:00
Retha Runolfsson
0764cf1165 Bump PySwitchbot to 0.60.1 (#143551) 2025-04-23 23:02:41 -10:00
Retha Runolfsson
a55a6e5c48 Add diagnostics for switchbot integration (#143389) 2025-04-23 21:02:44 -10:00
J. Nick Koston
5230aa8917 Increase zeroconf timeout to 10s (#143541) 2025-04-23 19:52:35 -10:00
Retha Runolfsson
f7e3e207b7 Add parallel updates in lock and lock unit tests for switchbot integration (#143391)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-23 19:49:09 -10:00
Erwin Douna
6f0c59f1be Tado bump 0.18.14 & race condition fix (#143531)
* Bump PyTado 0.18.14

* Add test
2025-04-24 08:44:17 +03:00
Josef Zweck
5fcdbd7742 Bump onedrive-personal-sdk to 0.0.14 (#143534) 2025-04-24 08:43:13 +03:00
J. Nick Koston
4173ff5339 Small quality fixes for ESPHome (#143535) 2025-04-23 09:42:27 -10:00
Abílio Costa
e8c4d08b25 Make Whirlpool test check for success after failure (#143525) 2025-04-23 21:00:32 +03:00
J. Nick Koston
36081c69e0 Break apart zeroconf integration to prepare for WebSocket API (#143490) 2025-04-23 07:47:37 -10:00
J. Nick Koston
65db3c1164 Fix display issues with ESPHome encryption key steps (#143483) 2025-04-23 18:39:32 +02:00
Maciej Bieniek
11f02e48d7 Use aioshelly method to set the target temperature for Shelly BLU TRV (#143504) 2025-04-23 18:37:29 +02:00
Stefan Agner
e41283a40a Handle Tailscale hosts without client connectivity details (#143505) 2025-04-23 18:36:55 +02:00
Abílio Costa
738e39413d Fix KeyError in energy websocket (#143519) 2025-04-23 18:34:48 +02:00
Norbert Rittel
8df0a950f7 Make use of "counterclockwise" consistent in hue (#143521) 2025-04-23 18:34:37 +02:00
Robert Resch
199a274c80 Remove deprecated hass.helpers (#143514) 2025-04-23 17:24:25 +02:00
Allen Porter
731d1ab796 Add quality scale for google calendar integration (#131328)
* Add quality scale for google calendar integration

* Update status and comments for the quality scale

* Update based on pr feedback

* Update quality_scale.yaml

* Update quality_scale.yaml

* Update quality_scale.yaml

* Update quality_scale.yaml

* Update quality_scale.yaml

* Update quality_scale.yaml for dependency-transparency

* Score silver, gold, and platinum levels

* Update quality scale

* Update quality scale
2025-04-23 16:32:20 +02:00
Manu
f6d8868eb6 Fix some minor issues and nitpicks in ntfy integration (#143516)
Fix nitpicks
2025-04-23 16:28:58 +02:00
Michael
253cc377b4 Remove boost and off temperature workaround from AVM Fritz!SmartHome (#142863)
* remove workaround

* remove hvacmode from mapping dict
2025-04-23 16:12:35 +02:00
J. Diego Rodríguez Royo
1bfd585f3c Adjust Home Connect max executions parameters (#143509)
Adjust max executions parameters to ensure that 1000 calls per day are not reached
2025-04-23 15:52:53 +02:00
Tobias Sauerwein
839eb0fe14 Bump pyatmo to 9.0.0 (#143512)
* wip

* fix

* fix

* fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-04-23 16:24:03 +03:00
Robert Resch
3dcd06806d Rename Nuki to Nuki Bridge (#143463)
* Rename Nuki to Nuki bridge

* Apply suggestions from code review

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

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-04-23 15:10:26 +02:00
Manu
3c174ce329 Add ntfy (ntfy.sh) integration (#135152)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-04-23 14:52:13 +02:00
Arjan
8a2347539c Météo-France: Additional states and change weather condition for "Ciel clair" (#143198)
* Additional new states and change for ciel-clair

* Adding new previously unmapped state

* Adding new forecast state

Adding Brouillard dense, reported after the review
2025-04-23 14:44:36 +02:00
Jan-Philipp Benecke
f22eca3d9e Remove deprecated hass.components (#141947) 2025-04-23 14:04:36 +02:00
Robert Resch
3cb301214f Fix hassfest type hints for ConfigSubentryFlow (#143502) 2025-04-23 12:14:00 +01:00
Marc Mueller
8215faea0d Replace unnecessary MappingProxyType runtime uses in integrations (#143507) 2025-04-23 12:22:12 +02:00
turakamou
beab4e0d7c Fix device tracker of freebox component to get devices from all interfaces (#142668) 2025-04-23 11:14:37 +01:00
Maciej Bieniek
b785d5297a Use aioshelly methods with Shelly RPC text and select entities (#143464) 2025-04-22 22:07:05 -10:00
Norbert Rittel
d86d7b8843 Fix sentence-casing in two strings of group (#143500)
Make them consistent with the rest of the integration.
2025-04-23 10:32:09 +03:00
cnico
2ca5f05555 Bump dio-chacon-api to v1.2.2 (#143489)
Bump dio-chacon-api to v1.2.2 to solve https://github.com/home-assistant/core/issues/142808
2025-04-23 10:31:43 +03:00
J. Nick Koston
e95188059f Bump fnv-hash-fast to 1.5.0 (#143494)
changelog: https://github.com/Bluetooth-Devices/fnv-hash-fast/compare/v1.4.0...v1.5.0
2025-04-23 10:31:23 +03:00
dependabot[bot]
73e6c8541c Bump sigstore/cosign-installer from 3.8.1 to 3.8.2 (#143501)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-23 09:26:01 +02:00
epenet
6f9c8b2aa0 Add exception translations to Renault (#143452) 2025-04-23 08:40:31 +02:00
Marc Mueller
2d20df37b1 Use runtime data for hyperion (#143461)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-04-22 13:24:47 -10:00
tdfountain
0b2e5cd253 Move device registry into function declaration for tests in NUT (#143349) 2025-04-22 13:11:14 -10:00
Arie Catsman
0208188bb5 Apply for bronze integration quality status for enphase_envoy (#136332)
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-22 11:59:58 -10:00
tdfountain
44ae596929 Add translated UoM for non-standard sensor measures in NUT (#143307) 2025-04-22 11:56:43 -10:00
Maciej Bieniek
db0cbf1ea9 Use rpc_call decorator in the Shelly entity module (#143484) 2025-04-22 11:51:08 -10:00
Josef Zweck
896da4abbd Bump pylamarzocco to 2.0.0b3 (#143477) 2025-04-22 21:26:26 +02:00
Josef Zweck
731e9bbbfc Fix issue in with jellyfin during browsing (#143478) 2025-04-22 19:59:24 +01:00
Manu
bf1c138a3c Fix some mistakes in the Habitica tests (#143465) 2025-04-22 20:53:09 +02:00
Paulus Schoutsen
00fc3e2c29 ESPHome Assist Satellite share TTS url in RUN_START (#143460) 2025-04-22 14:22:31 -04:00
Norbert Rittel
a258aa50a5 Fix inconsistent spelling of "add-ons" in analytics_insights (#143466) 2025-04-22 19:35:29 +02:00
Marc Mueller
3cf12a4792 Replace unnecessary MappingProxyType annotations in integrations (#143451) 2025-04-22 18:14:32 +02:00
Andre Lengwenus
e56f6fafdc Remove redundant parameter from config_entry data of LCN integration (#135912) 2025-04-22 18:00:30 +02:00
Thomas55555
e9789e0b3e Add/remove devices on push in Husqvarna Automower (#142550)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-04-22 17:58:42 +02:00
Franck Nijhof
6f0a9910ea 2025.4.3 (#143253) 2025-04-19 12:22:36 +02:00
Franck Nijhof
b8793760a1 Bump version to 2025.4.3 2025-04-19 09:17:04 +00:00
Joost Lekkerkerker
6264f9c67b Fix missing binary sensor for CoolSelect+ in SmartThings (#143216) 2025-04-19 09:16:46 +00:00
Joost Lekkerkerker
2a74deb84e Fix SmartThings soundbar without media playback (#143170) 2025-04-19 09:16:45 +00:00
puddly
9d1ff37a79 Bump ZHA to 0.0.56 (#143165)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-04-19 09:16:43 +00:00
Franck Nijhof
2f99164781 Reduce jumping Starlink uptime sensor (#143076) 2025-04-19 09:16:42 +00:00
Marc Mueller
80ef32f09d Add Python-2.0 to list of approved licenses (#143052) 2025-04-19 09:16:40 +00:00
G Johansson
63be0e2e1a Bump pysmhi to 1.0.2 (#143007)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-04-19 09:16:39 +00:00
Simone Chemelli
74c4553bb0 Increase uptime deviation for Shelly (#142996)
* Increase uptime deviation for Shelly

* fix test

* make troubleshooting easy

* change deviation interval

* increase deviation to 1m
2025-04-19 09:16:38 +00:00
starkillerOG
e240707b32 Bump reolink-aio to 0.13.2 (#142985) 2025-04-19 09:16:36 +00:00
Simone Chemelli
7c867852a9 Fix switch state for Comelit (#142978) 2025-04-19 09:16:35 +00:00
G Johansson
2d149dc746 Bump holidays to 0.70 (#142954) 2025-04-19 09:16:33 +00:00
Alex L
7edcddd3e4 Update UK Transport Integration URL (#142949) 2025-04-19 09:16:32 +00:00
Tsvi Mostovicz
71f658b560 Don't do I/O while getting Jewish calendar data schema (#142919) 2025-04-19 09:16:31 +00:00
Guido Schmitz
9886db5d6d Bump devolo_plc_api to 1.5.1 (#142908) 2025-04-19 09:16:29 +00:00
Glenn Waters
c236cd070c Bump Environment Canada library to 0.10.1 (#142882) 2025-04-19 09:16:28 +00:00
Kevin Stillhammer
9f1a830d32 Only get tracked pairs for kraken (#142877)
Only get tracked pairs

Getting all available pairs leads to a too long request URL
2025-04-19 09:16:26 +00:00
Allen Porter
1e69ce9111 Fix quality loss for LLM conversation agent question answering (#142873)
* Fix a bug parsing a streaming response with no json

* Remove debug lines

* Fix  quality loss for LLM conversation agent question answering

* Update tests
2025-04-19 09:15:41 +00:00
starkillerOG
389297155d Fix Reolink Home Hub Pro playback (#142871)
Fix Home Hub Pro playback
2025-04-19 09:12:22 +00:00
starkillerOG
c341b86520 Select correct Reolink device uid (#142864)
* Select correct device_uid

* Fix styling

* restructure

* Add test

* Update test_util.py

* Add explanation string
2025-04-19 09:12:21 +00:00
Eric Park
88eef379b2 Keep track of last play status update time in Apple TV (#142838) 2025-04-19 09:12:20 +00:00
peteS-UK
34767d4058 Force Squeezebox item id to string (#142793)
force item_id to string
2025-04-19 09:12:18 +00:00
Dionisis Toulatos
12c3d54a63 Fix MQTT device discovery when using node_id (#142784)
* Fix device discovery when using node_id

* tests

---------

Co-authored-by: jbouwh <jan@jbsoft.nl>
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-04-19 09:12:16 +00:00
Manu
33a185dade Fix error in recurrence calculation of Habitica integration (#142759)
Fix error in rrule calculation of Habitica integration
2025-04-19 09:12:15 +00:00
Erik Montnemery
c1c5776d85 Correct enum member check in home_connect (#142666)
* Correct enum member check in home_connect

* Update homeassistant/components/home_connect/coordinator.py

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

* Add mypy override

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-04-19 09:12:14 +00:00
Brett Adams
eda642554d Check Energy Live API works before creating the coordinator in Tessie (#142510)
* Check live API works before creating the coordinator

* Fix diag

* Fix mypy on entity

* is not None
2025-04-19 09:12:11 +00:00
Chase Mamatey
51f5ce013f Fix duke_energy data retrieval to adhere to service start date (#136054) 2025-04-19 09:12:10 +00:00
Franck Nijhof
f7794ea6b5 2025.4.2 (#142755) 2025-04-12 11:43:50 +02:00
Sanjay Govind
7a1bea7ff5 Add jaraco.itertools license exception as the classifier was removed but no SPDX expression was added (#142439) 2025-04-11 21:35:35 +00:00
Thomas55555
c7c645776d Bump ical to 9.1.0 (#142197) 2025-04-11 21:35:28 +00:00
Franck Nijhof
667cb772e9 Bump version to 2025.4.2 2025-04-11 16:12:45 +00:00
Jeff Rescignano
933d008e52 Upgrade sharkiq depedency to 1.1.0 (#142746) 2025-04-11 16:12:06 +00:00
Allen Porter
d868f39aea Fix Anthropic bug parsing a streaming response with no json (#142745) 2025-04-11 15:42:08 +00:00
Joost Lekkerkerker
28d776a0b0 Fix SmartThings gas meter (#142741) 2025-04-11 15:42:04 +00:00
Joost Lekkerkerker
b5d541b596 Bump pySmartThings to 3.0.4 (#142739) 2025-04-11 15:41:59 +00:00
Bram Kragten
4948499889 Update frontend to 20250411.0 (#142736) 2025-04-11 15:41:55 +00:00
starkillerOG
7696b101f6 Reolink migrate unique ID debugging (#142723)
* Filter out unexpected unique_ids

* correct

* Add test

* fix styling
2025-04-11 15:41:51 +00:00
starkillerOG
fd2987a9fd Bump reolink-aio 0.13.1 (#142719) 2025-04-11 15:41:47 +00:00
Christopher Fenner
4c1d32020a Bump PyViCare to 2.44.0 (#142701)
bump vicare to v2.44.0
2025-04-11 15:41:42 +00:00
Jan Bouwhuis
b40bdab0ae Fix EC certificate key not allowed in MQTT client setup (#142698) 2025-04-11 15:41:38 +00:00
Simone Chemelli
d192aecd3b Comelit config flow timeout error (#142667) 2025-04-11 15:41:34 +00:00
Thomas55555
d1781f5766 Bump livisi to 0.0.25 (#142638) 2025-04-11 15:41:29 +00:00
henryptung
2c4461457a Bump led_ble to 1.1.7 (#142629)
changelog: https://github.com/Bluetooth-Devices/led-ble/compare/v1.1.6...v1.1.7
2025-04-11 15:40:35 +00:00
J. Nick Koston
82959081de Pin multidict to >= 6.4.2 to resolve memory leaks (#142614)
* Pin multidict to >= 6.4.1 to resolve memory leaks

https://github.com/aio-libs/multidict/issues/1134
https://github.com/aio-libs/multidict/issues/1131
https://github.com/aio-libs/multidict/releases/tag/v6.4.1
https://github.com/aio-libs/multidict/releases/tag/v6.4.0

* Apply suggestions from code review
2025-04-11 15:39:38 +00:00
Thimo Seitz
acdac6d5e8 Update growatt server dependency to 1.6.0 (#142606)
* Update GrowattServer Dependency

* Update requirements_test_all.txt
2025-04-11 15:39:34 +00:00
Fredrik Erlandsson
d3d7889883 Fix ssl_cert load from config_flow (#142570)
fix ssl_cert load from config_flow
2025-04-11 15:39:29 +00:00
puddly
60ece3e1c9 Fix Core deadlock by ensuring only one ZHA log queue handler thread is running at a time (#142568)
Ensure only one log queue handler is running at a time
2025-04-11 15:39:25 +00:00
Christopher Fenner
a9f8529460 Fix Quickmode handling in ViCare integration (#142561)
* only check quickmode if supported

* update snapshot

* revert
2025-04-11 15:39:20 +00:00
Andrew Sayre
ec53b61f9e Bump pyheos to v1.0.5 (#142554)
Update pyheos
2025-04-11 15:39:16 +00:00
Thomas55555
e9f02edd8b Fix adding devices in Husqvarna Automower (#142549) 2025-04-11 15:39:12 +00:00
Marcel van der Veldt
d1b7898219 Fix small typo in Music Assistant integration causing unavailable players (#142535)
Fix small typo in Music Assistant integration causing issues with adding players
2025-04-11 15:39:08 +00:00
Jan Bouwhuis
8dc21ef619 Allow max to be equal with min for mqtt number config validation (#142522) 2025-04-11 15:39:03 +00:00
tronikos
d9f91598a5 Fix range of Google Generative AI temperature (#142513) 2025-04-11 15:34:29 +00:00
Ivan Lopez Hernandez
c540acf2bd Handle None on the response candidates in Google Generative AI (#142497)
* Added type checking on the candidates list

* Made error message a constant
2025-04-11 15:34:24 +00:00
Maciej Bieniek
f702f3efcd Fix Shelly initialization if device runs large script (#142487)
* Don't check the whole script to see if it generates events

* Fix tests

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-04-11 15:34:20 +00:00
Wilfred Ketelaar
9410061405 Fixed Renault charge state icon (#142478)
Fixed charge state icon (duplicate mdi prefix)
2025-04-11 15:34:15 +00:00
Maciej Bieniek
485b28d9ea Bump aioshelly to version 13.4.1 (#142477)
* Bymp aioshelly to 13.4.1

* Catch InvalidHostError

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-11 15:34:10 +00:00
epenet
d59200a9f5 Fix kelvin parameter in light action specifications (#142456) 2025-04-11 15:34:05 +00:00
starkillerOG
44a92ca81c Fix Reolink smart AI sensors (#142454) 2025-04-11 15:34:01 +00:00
J. Nick Koston
d39fa39a03 Fix HKC showing hvac_action as idle when fan is active and heat cool target is off (#142443)
* Fix HKC showing hvac_action as idle when fan is active and heat cool target is off

fixes #142442

* comment relocation
2025-04-11 15:33:56 +00:00
Michael
36ec857523 Fix reload of AVM FRITZ!Tools when new connected device is detected (#142430) 2025-04-11 15:33:51 +00:00
Simone Chemelli
fcb8cdc146 Add missing strings to Fritz (#142413)
* Add missing strings to Fritz

* update quality scale

* add common section

this avoids later re-structuring and re-translating

* fix strings

* fix strings

* apply review comment

---------

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
2025-04-11 15:33:46 +00:00
Simone Chemelli
2322b0b65f Add exceptions translation to SamsungTV (#142406)
* Add exceptions translation to SmasungTV

* Update strings.json

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

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-04-11 15:33:42 +00:00
tronikos
87baaf4255 Bump opower to 0.11.1 (#142395)
* Bump opower to 0.10.1

* opower==0.11.0

* opower==0.11.1

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-11 15:33:37 +00:00
J. Nick Koston
b7f0e877f0 Bump aioesphomeapi to 29.9.0 (#142393)
changelog: https://github.com/esphome/aioesphomeapi/compare/v29.8.0...v29.9.0

fixes #142381
2025-04-11 15:33:33 +00:00
Jan-Philipp Benecke
5d92a04732 Only load files ending .metadata.json in WebDAV (#142388) 2025-04-11 15:33:29 +00:00
Álvaro Fernández Rojas
8ff879df22 Update aioairzone to v1.0.0 (#142385)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2025-04-11 15:33:24 +00:00
J. Nick Koston
9fb7ee676e Bump flux_led to 1.2.0 (#142362)
changelog: https://github.com/lightinglibs/flux_led/compare/1.1.3...1.2.0
2025-04-11 15:33:20 +00:00
Jan Bouwhuis
2c855a3986 Limit mqtt info logging for discovery of new components (#142344)
* Limit mqtt info logging for discovery of new component

* Keep in bail out, when debug logging is not enabled
2025-04-11 15:33:14 +00:00
Luke Lashley
cdd4894e30 Check that the current roboorck map exists before updating it. (#142341)
* Check that the current map exists

* Add a few extra checks

* Update coordinator.py

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

* fixlint

---------

Co-authored-by: Allen Porter <allen.porter@gmail.com>
2025-04-11 15:33:09 +00:00
tronikos
5f26226712 Add a description for the enable_google_search_tool option in Google AI (#142322)
* Add a description for the enable_google_search_tool option in Google AI

* Use quotes
2025-04-11 15:33:05 +00:00
tronikos
8baf61031d Bump opower to 0.10.0 (#142321) 2025-04-11 15:33:00 +00:00
Andre Lengwenus
e90ba40553 Add SensorDeviceClass and unit for LCN CO2 sensor. (#142320)
Add SesnorDeviceClass and unit for LCN CO2 sensor.
2025-04-11 15:32:54 +00:00
Luke Lashley
b38016425f Update Roborock map more consistently on state change (#142228)
* update map more consistently on state change

* Makecoordinator keep track of last_updated_state
2025-04-11 15:32:49 +00:00
Thomas55555
ee5e3f7691 Add error details in remote calendar flow (#141753)
* Add error details in remote calendar flow

* no args

* adjust

* json

* Apply suggestions

* remove description placeholder
2025-04-11 15:32:41 +00:00
Franck Nijhof
7af6a4f493 2025.4.1 (#142299)
* Fix blocking event loop - daikin (#141442)

* fix blocking event loop

* create ssl_context directly

* update manifest

* update manifest.json

* Made Google Search enable dependent on Assist availability (#141712)

* Made Google Search enable dependent on Assist availability

* Show error instead of rendering again

* Cleanup test code

* Fix humidifier platform for Comelit (#141854)

* Fix humidifier platform for Comelit

* apply review comment

* Bump evohome-async to 1.0.5 (#141871)

bump client to 1.0.5

* Replace "to log into" with "to log in to" in `incomfort` (#142060)

* Replace "to log into" with "to log in to" in `incomfort`

Also fix one missing sentence-casing of "gateway".

* Replace duplicate "data_description" strings with references

* Avoid unnecessary reload in apple_tv reauth flow (#142079)

* Add translation for hassio update entity name (#142090)

* Bump pyenphase to 1.25.5 (#142107)

* Hide broken ZBT-1 config entries on the hardware page (#142110)

* Hide bad ZBT-1 config entries on the hardware page

* Set up the bad config entry in the unit test

* Roll into a list comprehension

* Remove constant changes

* Fix condition in unit test

* Bump pysmhi to 1.0.1 (#142111)

* Avoid logging a warning when replacing an ignored config entry (#142114)

Replacing an ignored config entry with one from the user
flow should not generate a warning. We should only warn
if we are replacing a usable config entry.

Followup to adjust the warning added in #130567
cc @epenet

* Slow down polling in Tesla Fleet (#142130)

* Slow down polling

* Fix tests

* Bump tesla-fleet-api to v1.0.17 (#142131)

bump

* Tado bump to 0.18.11 (#142175)

* Bump to version 0.18.11

* Adding hassfest files

* Add preset mode to SmartThings climate (#142180)

* Add preset mode to SmartThings climate

* Add preset mode to SmartThings climate

* Do not create a HA mediaplayer for the builtin Music Assistant player (#142192)

Do not create a HA mediaplayer for the builtin Music player

* Do not fetch disconnected Home Connect appliances (#142200)

* Do not fetch disconnected Home Connect appliances

* Apply suggestions

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

* Update docstring

---------

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

* Fix fibaro setup (#142201)

* Fix circular mean by always storing and using the weighted one (#142208)

* Fix circular mean by always storing and using the weighted one

* fix

* Fix test

* Bump pySmartThings to 3.0.2 (#142257)

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

* Update frontend to 20250404.0 (#142274)

* Bump forecast-solar lib to v4.1.0 (#142280)

Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>

* Bump version to 2025.4.1

* Fix skyconnect tests (#142262)

fix tests

* Fix empty actions (#142292)

* Apply fix

* Add tests for alarm button cover lock

* update light

* add number tests

* test select

* add switch tests

* test vacuum

* update lock test

---------

Co-authored-by: Fredrik Erlandsson <fredrik.e@gmail.com>
Co-authored-by: Ivan Lopez Hernandez <ivan.lh.94@outlook.com>
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
Co-authored-by: David Bonnes <zxdavb@bonnes.me>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
Co-authored-by: Arie Catsman <120491684+catsmanac@users.noreply.github.com>
Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: J. Diego Rodríguez Royo <jdrr1998@hotmail.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: rappenze <rappenze@yahoo.com>
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Klaas Schoute <klaas_schoute@hotmail.com>
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Petro31 <35082313+Petro31@users.noreply.github.com>
2025-04-04 22:59:10 +02:00
Petro31
c25f26a290 Fix empty actions (#142292)
* Apply fix

* Add tests for alarm button cover lock

* update light

* add number tests

* test select

* add switch tests

* test vacuum

* update lock test
2025-04-04 20:18:31 +00:00
Josef Zweck
8d62cb60a6 Fix skyconnect tests (#142262)
fix tests
2025-04-04 20:18:27 +00:00
Franck Nijhof
4f799069ea Bump version to 2025.4.1 2025-04-04 19:24:45 +00:00
Klaas Schoute
af708b78e0 Bump forecast-solar lib to v4.1.0 (#142280)
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2025-04-04 19:24:30 +00:00
Bram Kragten
f46e659740 Update frontend to 20250404.0 (#142274) 2025-04-04 19:24:27 +00:00
Joost Lekkerkerker
7bd517e6ff Bump pySmartThings to 3.0.2 (#142257)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-04-04 19:24:23 +00:00
Robert Resch
e9abdab1f5 Fix circular mean by always storing and using the weighted one (#142208)
* Fix circular mean by always storing and using the weighted one

* fix

* Fix test
2025-04-04 19:24:20 +00:00
rappenze
86eee4f041 Fix fibaro setup (#142201) 2025-04-04 19:24:17 +00:00
J. Diego Rodríguez Royo
9db60c830c Do not fetch disconnected Home Connect appliances (#142200)
* Do not fetch disconnected Home Connect appliances

* Apply suggestions

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

* Update docstring

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-04-04 19:24:13 +00:00
Marcel van der Veldt
c43a4682b9 Do not create a HA mediaplayer for the builtin Music Assistant player (#142192)
Do not create a HA mediaplayer for the builtin Music player
2025-04-04 19:24:10 +00:00
Joost Lekkerkerker
2a4996055a Add preset mode to SmartThings climate (#142180)
* Add preset mode to SmartThings climate

* Add preset mode to SmartThings climate
2025-04-04 19:24:07 +00:00
Erwin Douna
4643fc2c14 Tado bump to 0.18.11 (#142175)
* Bump to version 0.18.11

* Adding hassfest files
2025-04-04 19:24:04 +00:00
Brett Adams
6410b90d82 Bump tesla-fleet-api to v1.0.17 (#142131)
bump
2025-04-04 19:24:00 +00:00
Brett Adams
e5c00eceae Slow down polling in Tesla Fleet (#142130)
* Slow down polling

* Fix tests
2025-04-04 19:23:55 +00:00
J. Nick Koston
fe65579df8 Avoid logging a warning when replacing an ignored config entry (#142114)
Replacing an ignored config entry with one from the user
flow should not generate a warning. We should only warn
if we are replacing a usable config entry.

Followup to adjust the warning added in #130567
cc @epenet
2025-04-04 19:23:52 +00:00
G Johansson
281beecb05 Bump pysmhi to 1.0.1 (#142111) 2025-04-04 19:23:48 +00:00
puddly
7546b5d269 Hide broken ZBT-1 config entries on the hardware page (#142110)
* Hide bad ZBT-1 config entries on the hardware page

* Set up the bad config entry in the unit test

* Roll into a list comprehension

* Remove constant changes

* Fix condition in unit test
2025-04-04 19:23:45 +00:00
Arie Catsman
490e3201b9 Bump pyenphase to 1.25.5 (#142107) 2025-04-04 19:23:42 +00:00
Paul Bottein
04be575139 Add translation for hassio update entity name (#142090) 2025-04-04 19:23:39 +00:00
Erik Montnemery
854cae7f12 Avoid unnecessary reload in apple_tv reauth flow (#142079) 2025-04-04 19:23:35 +00:00
Norbert Rittel
109d20978f Replace "to log into" with "to log in to" in incomfort (#142060)
* Replace "to log into" with "to log in to" in `incomfort`

Also fix one missing sentence-casing of "gateway".

* Replace duplicate "data_description" strings with references
2025-04-04 19:23:32 +00:00
David Bonnes
f8d284ec4b Bump evohome-async to 1.0.5 (#141871)
bump client to 1.0.5
2025-04-04 19:23:28 +00:00
Simone Chemelli
06ebe0810f Fix humidifier platform for Comelit (#141854)
* Fix humidifier platform for Comelit

* apply review comment
2025-04-04 19:23:25 +00:00
Ivan Lopez Hernandez
802ad2ff51 Made Google Search enable dependent on Assist availability (#141712)
* Made Google Search enable dependent on Assist availability

* Show error instead of rendering again

* Cleanup test code
2025-04-04 19:23:22 +00:00
Fredrik Erlandsson
9070a8d579 Fix blocking event loop - daikin (#141442)
* fix blocking event loop

* create ssl_context directly

* update manifest

* update manifest.json
2025-04-04 19:23:18 +00:00
Franck Nijhof
e8b2a3de8b 2025.4.0 (#141505) 2025-04-02 18:47:40 +02:00
Joost Lekkerkerker
39549d5dd4 Fix switch name Unknown in SmartThings (#142081)
Fix switch name Unknown
2025-04-02 15:16:50 +00:00
Franck Nijhof
0c19e47bd4 Bump version to 2025.4.0 2025-04-02 15:02:28 +00:00
Michael
05507d77e3 Fix state class for battery sensors in AVM Fritz!SmartHome (#142078)
* set proper state class for battery sensor

* fix tests
2025-04-02 15:02:04 +00:00
Franck Nijhof
94558e2d40 Bump version to 2025.4.0b15 2025-04-02 14:19:49 +00:00
puddly
4f22fe8f7f Translation key for ZBT-1 integration failing due to disconnection (#142077)
Translation key for device disconnected
2025-04-02 14:19:41 +00:00
Marcel van der Veldt
9e7dfbb857 Deprecate None effect instead of breaking it for Hue (#142073)
* Deprecate effect none instead of breaking it for Hue

* add guard for unknown effect value

* revert guard

* Fix

* Add test

* Add test

* Add test

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-04-02 14:19:38 +00:00
Joost Lekkerkerker
02d182239a Improve SmartThings switch deprecation (#142072) 2025-04-02 14:19:35 +00:00
Joost Lekkerkerker
4e0f581747 Improve SmartThings sensor deprecation (#142070)
* Improve SmartThings sensor deprecation

* Improve SmartThings sensor deprecation

* Improve SmartThings sensor deprecation
2025-04-02 14:19:32 +00:00
Joost Lekkerkerker
42d97d348c Add Eve brand (#142067) 2025-04-02 14:19:29 +00:00
Robert Resch
69380c85ca Bump deebot-client to 12.5.0 (#142046) 2025-04-02 14:19:25 +00:00
Abílio Costa
b38c647830 Allow excluding modules from noisy logs check (#142020)
* Allow excluding modules from noisy logs check

* Cache non-excluded modules; hardcode self module name; optimize call

* Address review comments
2025-04-02 14:19:22 +00:00
Petro31
2396fd1090 Fix weather templates using new style configuration (#136677) 2025-04-02 14:19:19 +00:00
Franck Nijhof
aa4eb89eee Bump version to 2025.4.0b14 2025-04-02 09:44:23 +00:00
J. Nick Koston
1b1bc6af95 Bump bluetooth-data-tools to 1.26.5 (#142045)
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.26.1...v1.26.5
2025-04-02 09:36:51 +00:00
J. Nick Koston
f17003a79c Bump aiohttp to 3.11.16 (#142034)
changelog: https://github.com/aio-libs/aiohttp/compare/v3.11.15...v3.11.16
2025-04-02 09:34:14 +00:00
TheJulianJES
ec70e8b0cd Bump ZHA to 0.0.55 (#142031) 2025-04-02 08:29:26 +00:00
puddly
d888c70ff0 Fix entity names for HA hardware firmware update entities (#142029)
* Fix entity names for HA hardware firmware update entities

* Fix unit tests
2025-04-02 08:29:23 +00:00
puddly
f29444002e Skip firmware config flow confirmation if the hardware is in use (#142017)
* Auto-confirm the discovery if we detect that the device is already in use

* Add a unit test
2025-04-02 08:29:20 +00:00
Tomek Wasilczyk
fc66997a36 Fix warning about unfinished oauth tasks on shutdown (#141969)
* Don't wait for OAuth token task on shutdown

To reproduce the warning:
1. Start authentication with integration using OAuth (e.g. SmartThings)
2. When redirected to external login site, just close the page
3. Settings -> Restart Home Assistant

* Clarify comment

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-04-02 08:29:16 +00:00
Erik Montnemery
35513ae072 Remove unused mypy ignore from google_generative_ai_conversation (#141549) 2025-04-02 08:29:13 +00:00
Franck Nijhof
cd363d48c3 Bump version to 2025.4.0b13 2025-04-01 19:12:16 +00:00
G Johansson
d47ef835d7 Fix train to for multiple stations in Trafikverket Train (#142016) 2025-04-01 19:11:51 +00:00
Bram Kragten
00177c699e Update frontend to 20250401.0 (#142010) 2025-04-01 19:11:48 +00:00
Joost Lekkerkerker
11b0086a01 Add LG ThinQ event bus listener to lifecycle hooks (#142006) 2025-04-01 19:11:44 +00:00
J. Nick Koston
ceb177f80e Bump aiohttp to 3.11.15 (#141967)
changelog: https://github.com/aio-libs/aiohttp/compare/v3.11.14...v3.11.15

fixes #141855
fixes #141146
2025-04-01 19:10:28 +00:00
Jan Bouwhuis
fa3832fbd7 Improve error handling and logging on MQTT update entity state updates when template rederings fails (#141960) 2025-04-01 19:07:10 +00:00
puddly
2b9c903429 Fix data in old SkyConnect integration config entries or delete them (#141959)
* Delete old SkyConnect integration config entries

* Try migrating, if possible

* Do not delete config entries, log a failure
2025-04-01 19:07:07 +00:00
puddly
a7c43f9b49 Reload the ZBT-1 integration on USB state changes (#141287)
* Reload the config entry when the ZBT-1 is unplugged

* Register the USB event handler globally to react better to re-plugs

* Fix existing unit tests

* Add an empty `CONFIG_SCHEMA`

* Add a unit test

* Fix unit tests

* Fix unit tests for Linux

* Address most review comments

* Address remaining review comments
2025-04-01 19:07:03 +00:00
Joost Lekkerkerker
b428196149 Improve SmartThings deprecation (#141939)
* Improve SmartThings deprecation

* Improve SmartThings deprecation
2025-04-01 19:01:43 +00:00
Erik Montnemery
e23da1a90f Fix import issues related to onboarding views (#141919)
* Fix import issues related to onboarding views

* Add ha-intents and numpy to pyproject.toml

* Add more requirements to pyproject.toml

* Add more requirements to pyproject.toml
2025-04-01 19:00:24 +00:00
Ben Jones
3951c2ea66 Handle empty or missing state values for MQTT light entities using 'template' schema (#141177)
* check for empty or missing values when processing state messages for MQTT light entities using 'template' schema

* normalise warning logs

* add tests (one is still failing and I can't work out why)

* fix test

* improve test coverage after PR review

* improve test coverage after PR review
2025-04-01 18:32:50 +00:00
Louis Christ
fee152654d Use saved volume when selecting preset in bluesound integration (#141079)
* Use load_preset to select preset as source

* Add tests

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-04-01 18:32:47 +00:00
Mikko Koo
51073c948c Fix nordpool Not to return Unknown if price is exactly 0 (#140647)
* now the price will return even if it is exactly 0

* now the price will return even if it is exactly 0

* now the price will return even if it is exactly 0

* clean code

* clean code

* update testing code coverage

* change zero testing to SE4

* remove row duplicate

* fix date comments

* improve testing

* simplify if-return-0

* remove unnecessary tests

* order testing rows

* restore test_sensor_no_next_price

* remove_average_price_test

* fix test name
2025-04-01 18:32:44 +00:00
aaronburt
91438088a0 Correct unit conversion for OneDrive quota display (#140337)
* Correct unit conversion for OneDrive quota display

* Convert OneDrive quota values from bytes to GiB in coordinator and update strings
2025-04-01 18:32:39 +00:00
Franck Nijhof
427e1abdae Bump version to 2025.4.0b12 2025-03-31 20:12:58 +00:00
Steven Looman
6e7ac45ac0 Bump async-upnp-client to 0.44.0 (#141946) 2025-03-31 20:12:48 +00:00
Bram Kragten
4b3b9ebc29 Update frontend to 20250331.0 (#141943) 2025-03-31 20:12:43 +00:00
Franck Nijhof
649d8638ed Bump version to 2025.4.0b11 2025-03-31 18:34:34 +00:00
Jan-Philipp Benecke
12c4152dbe Bump aiowebdav2 to 0.4.5 (#141934) 2025-03-31 18:34:25 +00:00
Michael Hansen
8f9572bb05 Add preannounce boolean for announce/start conversation (#141930)
* Add preannounce boolean

* Fix disabling preannounce in wizard

* Fix casing

* Fix type of preannounce_media_id

* Adjust description of preannounce_media_id
2025-03-31 18:34:22 +00:00
Erik Montnemery
6d022ff4e0 Revert PR 136314 (Cleanup map references in lovelace) (#141928)
* Revert PR 136314 (Cleanup map references in lovelace)

* Update homeassistant/components/lovelace/__init__.py

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

* Fix dashboard creation

* Update homeassistant/components/lovelace/__init__.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-03-31 18:34:19 +00:00
Josef Zweck
c0c2edb90a Add None check to azure_storage (#141922) 2025-03-31 18:34:16 +00:00
Michael
b014219fdd Correct further sensor categorizations in AVM Fritz!Box tools (#141911)
mark margin and attenuation as diagnostic and disable them by default
2025-03-31 18:34:13 +00:00
Joost Lekkerkerker
216b8ef400 Don't create SmartThings entities for disabled components (#141909) 2025-03-31 18:34:10 +00:00
Joost Lekkerkerker
f2ccd46267 Fix SmartThings being able to understand incomplete DRLC (#141907) 2025-03-31 18:34:06 +00:00
Dan Raper
e16ba27ce8 Bump ohmepy to 1.5.1 (#141879)
* Bump ohmepy to 1.5.1

* Fix types for ohmepy version change
2025-03-31 18:34:03 +00:00
Thomas55555
506526a6a2 Handle 403 error in remote calendar (#141839)
* Handle 403 error in remote calendar

* tests
2025-03-31 18:34:00 +00:00
Franck Nijhof
a88678cf42 Fix SmartThings climate entity missing off HAVC mode (#141700)
* Fix smartthing climate entity missing off HAVC mode:

* Fix tests

* Fix test

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-03-31 18:33:57 +00:00
Retha Runolfsson
d0b61af7ec Add switchbot cover unit tests (#140265)
* add cover unit tests

* Add unit test for SwitchBot cover

* fix: use mock_restore_cache to mock the last state

* modify unit tests

* modify scripts as suggest

* improve readability

* adjust patch target per review comments

* adjust patch target per review comments

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-03-31 18:33:53 +00:00
Franck Nijhof
04f5315ab2 Bump version to 2025.4.0b10 2025-03-31 08:09:39 +00:00
Paulus Schoutsen
7f9e4ba39e Ensure user always has first turn for Google Gen AI (#141893) 2025-03-31 08:09:10 +00:00
J. Nick Koston
06aaf188ea Fix duplicate call to async_write_ha_state when adding elkm1 entities (#141890)
When an entity is added state is always written in
add_to_platform_finish:

7336178e03/homeassistant/helpers/entity.py (L1384)

We should not do it in async_added_to_hass as well
2025-03-31 08:09:06 +00:00
J. Nick Koston
627f994872 Bump aioesphomeapi to 29.8.0 (#141888)
changelog: https://github.com/esphome/aioesphomeapi/compare/v29.7.0...v29.8.0
2025-03-31 08:09:03 +00:00
J. Nick Koston
9e81ec5aae Handle encryption being disabled on an ESPHome device (#141887)
fixes #121442
2025-03-31 08:09:00 +00:00
Franck Nijhof
69753fca1d Update pvo to v2.2.1 (#141847) 2025-03-31 08:08:57 +00:00
Michael
7773cc121e Fix the entity category for max throughput sensors in AVM Fritz!Box Tools (#141838)
correct the entity category for max throughput sensors
2025-03-31 08:08:54 +00:00
Michael
3aa56936ad Move setup messages from info to debug level (#141834)
move info to debug level
2025-03-31 08:08:51 +00:00
Franck Nijhof
e66416c23d Fix hardcoded UoM for total power sensor for Tuya zndb devices (#141822) 2025-03-31 08:08:48 +00:00
Jan Bouwhuis
a592feae3d Correct spelling for 'availability` in MQTT translation strings (#141818) 2025-03-31 08:08:45 +00:00
Aidan Timson
fc0d71e891 Fix System Bridge wait timeout wait condition (#141811)
* Fix System Bridge wait timeout wait condition

* Add DataMissingException as a timeout condition

* Add tests
2025-03-31 08:08:42 +00:00
Thomas55555
d4640f1d24 Bump ical to 9.0.3 (#141805) 2025-03-31 08:08:39 +00:00
Michael
6fe158836e Add boost preset to AVM Fritz!SmartHome climate entities (#141802)
* add boost preset to climate entities

* add set boost preset test
2025-03-31 08:08:36 +00:00
J. Nick Koston
629c0087f4 Bump PyISY to 3.1.15 (#141778)
changelog: https://github.com/automicus/PyISY/compare/v3.1.14...v3.1.15

fixes #141517
fixes #132279
2025-03-31 08:08:33 +00:00
J. Nick Koston
363bd75129 Fix blocking late import of httpcore from httpx (#141771)
There is a late import that blocks the event loop
in newer version
9e8ab40369/httpx/_transports/default.py (L75)
2025-03-31 08:08:30 +00:00
J. Nick Koston
7592d350a8 Bump aiohomekit to 3.2.13 (#141764)
changelog: https://github.com/Jc2k/aiohomekit/compare/3.2.8...3.2.13
2025-03-31 08:08:27 +00:00
puddly
8ac8401b4e Add helper methods to simplify USB integration testing (#141733)
* Add some helper methods to simplify USB integration testing

* Re-export `usb_device_from_port`
2025-03-31 08:08:24 +00:00
Joost Lekkerkerker
eed075dbfa Bump pySmartThings to 3.0.1 (#141722) 2025-03-31 08:08:21 +00:00
Florent Thoumie
23dbdedfb6 Bump iaqualink to 0.5.3 (#141709)
* Update to iaqualink 0.5.3 and silence warning

* Update to iaqualink 0.5.3 and silence warning

* Re-add via_device line
2025-03-31 08:08:18 +00:00
Franck Nijhof
85ad29e28e Ensure EcoNet operation modes are unique (#141689) 2025-03-31 08:08:15 +00:00
Michal Schwarz
35fc81b038 Fix order of palettes, presets and playlists in WLED integration (#132207)
* Fix order of palettes, presets and playlists in WLED integration

* fix tests: update palette items order

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-03-31 08:08:11 +00:00
Lucas Mindêllo de Andrade
5d45b84cd2 Remove sunweg integration (#124230)
* chore(sunweg): remove sunweg integration

* Update homeassistant/components/sunweg/strings.json

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

* Update homeassistant/components/sunweg/manifest.json

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

* feat: added async remove entry

* Clean setup_entry; add tests

---------

Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: abmantis <amfcalt@gmail.com>
2025-03-31 08:06:54 +00:00
Franck Nijhof
7766649304 Bump version to 2025.4.0b9 2025-03-29 17:50:46 +00:00
Simone Chemelli
07e9020dfa Fix immediate state update for Comelit (#141735) 2025-03-29 17:50:36 +00:00
J. Diego Rodríguez Royo
f504a759e0 Set Home Connect program action field as not required (#141729)
* Set Home Connect program action field as not required

* Remove required field

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-03-29 17:50:32 +00:00
Joost Lekkerkerker
9927de4801 Only trigger events on button updates in SmartThings (#141720)
Only trigger events on button updates
2025-03-29 17:50:29 +00:00
Joost Lekkerkerker
1244fc4682 Only link the parent device if known in SmartThings (#141719)
Only link the parent device if we know the parent device
2025-03-29 17:50:26 +00:00
Norbert Rittel
e77a1b12f7 Sentence-case "Medium type" in mopeka (#141718) 2025-03-29 17:50:22 +00:00
J. Nick Koston
5459daaa10 Fix ESPHome entities not being removed when the ESPHome config removes an entire platform (#141708)
* Fix old ESPHome entities not being removed when configuration changes

fixes #140756

* make sure all callbacks fire

* make sure all callbacks fire

* make sure all callbacks fire

* make sure all callbacks fire

* revert

* cover
2025-03-29 17:50:18 +00:00
J. Nick Koston
400131df78 Fix ESPHome update entities being loaded before device_info is available (#141704)
* Fix ESPHome update entities being loaded before device_info is available

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

fixes #135906

* Apply suggestions from code review

* move comment

* Update entry_data.py

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

---------

Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2025-03-29 17:50:15 +00:00
Franck Nijhof
28e1843ff9 Fix Tuya tdq category to pick up temp & humid (#141698) 2025-03-29 17:50:12 +00:00
Franck Nijhof
df777318d1 Handle invalid JSON errors in AirNow (#141695) 2025-03-29 17:50:08 +00:00
Jan Bouwhuis
6ad5e9e89c Improve MQTT translation strings (#141691)
* Improve MQTT options translation string

* more improvements
2025-03-29 17:50:05 +00:00
Norbert Rittel
a0bd8deee9 Replace "country" with common string in holiday (#141687) 2025-03-29 17:50:01 +00:00
Marcel van der Veldt
405cbd6a00 Always set pause feature on Music Assistant mediaplayers (#141686) 2025-03-29 17:49:58 +00:00
Marcel van der Veldt
3e0eb5ab2c Bump music assistant client to 1.2.0 (#141668)
* Bump music assistant client to 1.2.0

* Update test fixtures
2025-03-29 17:49:55 +00:00
Norbert Rittel
fad75a70b6 Add a common string for "country" (#141653) 2025-03-29 17:49:52 +00:00
Josef Zweck
d9720283df Add unkown to uncalibrated state for tedee (#141262) 2025-03-29 17:49:46 +00:00
Franck Nijhof
14eed1778b Bump version to 2025.4.0b8 2025-03-28 20:46:26 +00:00
Norbert Rittel
049aaa7e8b Fix grammar / sentence-casing in workday (#141682)
* Fix grammar / sentence-casing in `workday`

Also replace "country" with common string.

* Add two more references

* Fix second data description reference

* Add "given" to action description for better translations
2025-03-28 20:46:17 +00:00
J. Nick Koston
35717e8216 Increase websocket_api allowed peak time to 10s (#141680)
* Increase websocket_api allowed peak time to 10s

fixes #141624

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

* adjust test
2025-03-28 20:46:13 +00:00
Franck Nijhof
2a081abc18 Fix camera proxy with sole image quality settings (#141676) 2025-03-28 20:46:10 +00:00
puddly
b7f29c7358 Handle all firmware types for ZBT-1 and Yellow update entities (#141674)
Handle other firmware types
2025-03-28 20:46:06 +00:00
Jason Hunter
3bb6373df5 Update Duke Energy package to fix integration (#141669)
* Update Duke Energy package to fix integration

* fix tests
2025-03-28 20:46:03 +00:00
Michael Hansen
e1b4edec50 Bump intents and always prefer more literal text (#141663) 2025-03-28 20:46:00 +00:00
puddly
147bee57e1 Include ZBT-1 and Yellow in device registry (#141623)
* Add the Yellow and ZBT-1 to the device registry

* Unload platforms

* Fix unit tests

* Rename the Yellow update entity to `Radio firmware`

* Rename `EmberZNet` to `EmberZNet Zigbee`

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

* Fix unit tests

* Remove unnecessary `always_update=False` from data update coordinator
2025-03-28 20:45:56 +00:00
Erwin Douna
fcdaea64da Tado add proper off state (#135480)
* Add proper off state

* Remove current temp

* Add default frost temp
2025-03-28 20:45:53 +00:00
Franck Nijhof
d1512d46be Bump version to 2025.4.0b7 2025-03-28 16:00:45 +00:00
Bram Kragten
0be7db6270 Update frontend to 20250328.0 (#141659) 2025-03-28 15:09:56 +00:00
Paulus Schoutsen
2af0282725 Enable the message box on default for satelitte announcement actions (#141654) 2025-03-28 15:09:51 +00:00
Franck Nijhof
ff458c8417 Bump version to 2025.4.0b6 2025-03-28 15:04:34 +00:00
Franck Nijhof
cc93152ff0 Fix ESPHome event entity staying unavailable (#141650) 2025-03-28 14:05:40 +00:00
Paulus Schoutsen
9965f01609 Ensure connection test sound has no preannouncement (#141647) 2025-03-28 14:05:37 +00:00
Jan Bouwhuis
e9c76ce694 Fix duplicate 'device' term in MQTT translation strings (#141646)
* Fix duplicate 'device' from MQTT translation strings

* Update homeassistant/components/mqtt/strings.json
2025-03-28 14:05:34 +00:00
Norbert Rittel
58ab7d350d Fix sentence-casing in airvisual user strings (#141632) 2025-03-28 14:05:30 +00:00
Nick Pesce
e4d6e20ebd Use correct default value for multi press buttons in the Matter integration (#141630)
* Respect the min 2 constraint for the switch MultiPressMax attribute

* Update test_event.py

* Update generic_switch_multi.json

* Fix issue and update tests
2025-03-28 14:05:27 +00:00
Tsvi Mostovicz
45e273897a Jewish calendar match omer service variables requirement to documentation (#141620)
The documentation and the omer schema require a Nusach to be specified, but the YAML misses that requirement
2025-03-28 14:05:23 +00:00
Jan Bouwhuis
d9ec7142d7 Fix volatile_organic_compounds_parts translation string to be referenced for MQTT subentries device class selector (#141618)
* Fix ` volatile_organic_compounds_parts` translation string to be referenced for MQTT subentries device class selector

* Fix tests
2025-03-28 14:05:20 +00:00
Petro31
e162499267 Fix an issue with the switch preview in beta (#141617)
Fix an issue with the switch preview
2025-03-28 14:05:16 +00:00
Jan-Philipp Benecke
67f21429e3 Bump aiowebdav2 to 0.4.4 (#141615) 2025-03-28 14:05:12 +00:00
J. Nick Koston
a0563f06c9 Fix zeroconf logging level not being respected (#141601)
Removes an old logging workaround that is no longer needed

fixes #141558
2025-03-28 14:05:05 +00:00
Luke Lashley
e7c4fdc8bb Bump Python-Snoo to 0.6.5 (#141599)
* Bump Python-Snoo to 0.6.5

* add to event_types
2025-03-28 14:05:00 +00:00
Norbert Rittel
c490e350bc Make names of switch entities in gree consistent with docs (#141580) 2025-03-28 14:04:56 +00:00
Robert Resch
e11409ef99 Reverts #141363 "Deprecate SmartThings machine state sensors" (#141573)
Reverts #141363
2025-03-28 14:04:52 +00:00
Joost Lekkerkerker
5c8e415a76 Add default string and icon for light effect off (#141567) 2025-03-28 14:04:49 +00:00
alorente
e795fb9497 Fix missing response for queued mode scripts (#141460) 2025-03-28 14:04:45 +00:00
Norbert Rittel
d0afabb85c Fix misleading friendly names of pvoutput sensors (#141312)
* Fix misleading friendly names of `pvoutput` sensors

* Update test_sensor.py

* Update test_sensor.py - prettier
2025-03-28 14:04:41 +00:00
Franck Nijhof
4f3e8e9b94 Bump version to 2025.4.0b5 2025-03-27 20:03:14 +00:00
Paul Bottein
46c1cbbc9c Update frontend to 20250327.1 (#141596) 2025-03-27 20:03:01 +00:00
Simon Lamon
8d9a4ea278 Fix typing error in NMBS (#141589)
Fix typing error
2025-03-27 20:02:58 +00:00
Jan-Philipp Benecke
22c83e2393 Bump aiowebdav2 to 0.4.3 (#141586) 2025-03-27 20:02:55 +00:00
Joost Lekkerkerker
c83a75f6f9 Add brand for Bosch (#141561) 2025-03-27 20:02:51 +00:00
Franck Nijhof
841c727112 Bump version to 2025.4.0b4 2025-03-27 16:59:36 +00:00
Bram Kragten
d8c9655bfd Update frontend to 20250327.0 (#141585) 2025-03-27 16:59:29 +00:00
Erik Montnemery
942ed89cc4 Revert "Promote after dependencies in bootstrap" (#141584)
Revert "Promote after dependencies in bootstrap (#140352)"

This reverts commit 3766040960.
2025-03-27 16:59:25 +00:00
Franck Nijhof
a1fe6b9cf3 Bump version to 2025.4.0b3 2025-03-27 15:38:31 +00:00
Luke Lashley
2567181cc2 Better handle Roborock discovery (#141575) 2025-03-27 15:38:24 +00:00
Joost Lekkerkerker
028e4f6029 Also migrate completion time entities in SmartThings (#141572) 2025-03-27 15:38:21 +00:00
Martin Hjelmare
b82e1a9bef Handle cloud subscription expired for backup upload (#141564)
Handle cloud backup subscription expired for upload
2025-03-27 15:38:18 +00:00
Joost Lekkerkerker
438f226c31 Add icons to hue effects (#141559) 2025-03-27 15:38:15 +00:00
Erwin Douna
2f139e3cb1 Tado fix HomeKit flow (#141525)
* Initial commit

* Fix

* Fix

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
2025-03-27 15:38:07 +00:00
Franck Nijhof
5d75e96fbf Bump version to 2025.4.0b2 2025-03-27 10:19:35 +00:00
Norbert Rittel
dcf2ec5c37 Fix sentence-casing in konnected strings, replace "override" with "custom" (#141553)
Fix sentence-casing in `konnected`strings, replace "Override" with "Custom"

Make string consistent with HA standards.

As "Override" can be misunderstood as the verb, replace it with "Custom".
2025-03-27 10:19:22 +00:00
Simon Lamon
2431e1ba98 Bump linkplay to v0.2.2 (#141542)
Bump linkplay
2025-03-27 10:19:18 +00:00
Thomas55555
4ead108c15 Handle webcal prefix in remote calendar (#141541)
Handel webcal prefix in remote calendar
2025-03-27 10:19:14 +00:00
Michael Hansen
ec8363fa49 Add default preannounce sound to Assist satellites (#141522)
* Add default preannounce sound

* Allow None to disable sound

* Register static path instead of HTTP view

* Fix path

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-03-27 10:19:09 +00:00
J. Diego Rodríguez Royo
e7ff0a3f8b Improve some Home Connect deprecations (#141508) 2025-03-27 10:19:06 +00:00
Ivan Lopez Hernandez
f4c0eb4189 Initialize google.genai.Client in the executor (#141432)
* Intialize the client on an executor thread

* Fix MyPy error

* MyPy error

* Exception error

* Fix ruff

* Update __init__.py

---------

Co-authored-by: tronikos <tronikos@users.noreply.github.com>
2025-03-27 10:19:02 +00:00
Manu
b1ee5a76e1 Support for upcoming pyLoad-ng release in pyLoad integration (#141297)
Fix extra key `proxy` in pyLoad
2025-03-27 10:18:58 +00:00
Norbert Rittel
6b9e8c301b Fix wrong friendly name for storage_power in solaredge (#141269)
* Fix wrong friendly name for `storage_power` in `solaredge`

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

* Two additional spelling fixes

* Sentence-case "site"
2025-03-27 10:18:53 +00:00
Franck Nijhof
89c3266c7e Bump version to 2025.4.0b1 2025-03-26 23:21:26 +00:00
Jan Bouwhuis
cff0a632e8 Fix QoS schema issue in MQTT subentries (#141531) 2025-03-26 23:21:17 +00:00
Jan Bouwhuis
e04d8557ae Fix MQTT options flow QoS selector can not serialize (#141528) 2025-03-26 23:21:14 +00:00
Thomas55555
ca6286f241 Fix work area sensor for Husqvarna Automower (#141527)
* Fix work area sensor for Husqvarna Automower

* simplify
2025-03-26 23:21:10 +00:00
Robert Resch
35bcc9d5af Show box for Smartthings rise number entity (#141526) 2025-03-26 23:21:07 +00:00
Joost Lekkerkerker
25b45ce867 Sort SmartThings devices to be created by parent device id (#141515) 2025-03-26 23:21:03 +00:00
Robert Resch
d568209bd5 Bump deebot-client to 12.4.0 (#141501) 2025-03-26 23:21:00 +00:00
Simone Chemelli
8a43e8af9e Fix refresh state for Comelit alarm (#141370) 2025-03-26 23:20:56 +00:00
Franck Nijhof
785e5b2c16 Bump version to 2025.4.0b0 2025-03-26 17:41:03 +00:00
725 changed files with 37101 additions and 11005 deletions

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: 0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -116,7 +116,7 @@ jobs:
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
if: needs.init.outputs.channel == 'dev'
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -175,7 +175,7 @@ jobs:
sed -i "s|pykrakenapi|# pykrakenapi|g" requirements_all.txt
- name: Download translations
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: translations
@@ -324,7 +324,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Install Cosign
uses: sigstore/cosign-installer@v3.8.1
uses: sigstore/cosign-installer@v3.8.2
with:
cosign-release: "v2.2.3"
@@ -457,12 +457,12 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Download translations
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: translations
@@ -509,7 +509,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -522,7 +522,7 @@ jobs:
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -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@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}

View File

@@ -249,7 +249,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -294,7 +294,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -334,7 +334,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -374,7 +374,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -484,7 +484,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
@@ -587,7 +587,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -620,7 +620,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -677,7 +677,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
@@ -720,7 +720,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -767,7 +767,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -812,7 +812,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -889,7 +889,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -949,7 +949,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
@@ -968,7 +968,7 @@ jobs:
run: |
echo "::add-matcher::.github/workflows/matchers/pytest-slow.json"
- name: Download pytest_buckets
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: pytest_buckets
- name: Compile English translations
@@ -1074,7 +1074,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
@@ -1208,7 +1208,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
@@ -1312,7 +1312,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
pattern: coverage-*
- name: Upload coverage to Codecov
@@ -1359,7 +1359,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true
@@ -1454,7 +1454,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
pattern: coverage-*
- name: Upload coverage to Codecov
@@ -1479,7 +1479,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
pattern: test-results-*
- name: Upload test results to Codecov

View File

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

View File

@@ -22,7 +22,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}

View File

@@ -36,7 +36,7 @@ jobs:
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.5.0
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
@@ -138,17 +138,17 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Download env_file
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: requirements_diff
@@ -187,22 +187,22 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Download env_file
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: requirements_diff
- name: Download requirements_all_wheels
uses: actions/download-artifact@v4.2.1
uses: actions/download-artifact@v4.3.0
with:
name: requirements_all_wheels

View File

@@ -363,6 +363,7 @@ homeassistant.components.no_ip.*
homeassistant.components.nordpool.*
homeassistant.components.notify.*
homeassistant.components.notion.*
homeassistant.components.ntfy.*
homeassistant.components.number.*
homeassistant.components.nut.*
homeassistant.components.ohme.*
@@ -385,6 +386,7 @@ homeassistant.components.pandora.*
homeassistant.components.panel_custom.*
homeassistant.components.peblar.*
homeassistant.components.peco.*
homeassistant.components.pegel_online.*
homeassistant.components.persistent_notification.*
homeassistant.components.person.*
homeassistant.components.pi_hole.*
@@ -461,6 +463,7 @@ homeassistant.components.slack.*
homeassistant.components.sleepiq.*
homeassistant.components.smhi.*
homeassistant.components.smlight.*
homeassistant.components.smtp.*
homeassistant.components.snooz.*
homeassistant.components.solarlog.*
homeassistant.components.sonarr.*

11
CODEOWNERS generated
View File

@@ -1051,6 +1051,8 @@ build.json @home-assistant/supervisor
/tests/components/nsw_fuel_station/ @nickw444
/homeassistant/components/nsw_rural_fire_service_feed/ @exxamalte
/tests/components/nsw_rural_fire_service_feed/ @exxamalte
/homeassistant/components/ntfy/ @tr4nt0r
/tests/components/ntfy/ @tr4nt0r
/homeassistant/components/nuheat/ @tstabrawa
/tests/components/nuheat/ @tstabrawa
/homeassistant/components/nuki/ @pschmitt @pvizeli @pree
@@ -1316,6 +1318,8 @@ build.json @home-assistant/supervisor
/tests/components/ruuvitag_ble/ @akx
/homeassistant/components/rympro/ @OnFreund @elad-bar @maorcc
/tests/components/rympro/ @OnFreund @elad-bar @maorcc
/homeassistant/components/s3/ @tomasbedrich
/tests/components/s3/ @tomasbedrich
/homeassistant/components/sabnzbd/ @shaiu @jpbede
/tests/components/sabnzbd/ @shaiu @jpbede
/homeassistant/components/saj/ @fredericvl
@@ -1437,8 +1441,8 @@ build.json @home-assistant/supervisor
/tests/components/solarlog/ @Ernst79 @dontinelli
/homeassistant/components/solax/ @squishykid @Darsstar
/tests/components/solax/ @squishykid @Darsstar
/homeassistant/components/soma/ @ratsept @sebfortier2288
/tests/components/soma/ @ratsept @sebfortier2288
/homeassistant/components/soma/ @ratsept
/tests/components/soma/ @ratsept
/homeassistant/components/sonarr/ @ctalkington
/tests/components/sonarr/ @ctalkington
/homeassistant/components/songpal/ @rytilahti @shenxn
@@ -1470,7 +1474,8 @@ build.json @home-assistant/supervisor
/tests/components/steam_online/ @tkdrob
/homeassistant/components/steamist/ @bdraco
/tests/components/steamist/ @bdraco
/homeassistant/components/stiebel_eltron/ @fucm
/homeassistant/components/stiebel_eltron/ @fucm @ThyMYthOS
/tests/components/stiebel_eltron/ @fucm @ThyMYthOS
/homeassistant/components/stookwijzer/ @fwestenberg
/tests/components/stookwijzer/ @fwestenberg
/homeassistant/components/stream/ @hunterjm @uvjustin @allenporter

View File

@@ -719,7 +719,7 @@ class LockCapabilities(AlexaEntity):
yield Alexa(self.entity)
@ENTITY_ADAPTERS.register(media_player.const.DOMAIN)
@ENTITY_ADAPTERS.register(media_player.DOMAIN)
class MediaPlayerCapabilities(AlexaEntity):
"""Class to represent MediaPlayer capabilities."""
@@ -757,9 +757,7 @@ class MediaPlayerCapabilities(AlexaEntity):
if supported & media_player.MediaPlayerEntityFeature.SELECT_SOURCE:
inputs = AlexaInputController.get_valid_inputs(
self.entity.attributes.get(
media_player.const.ATTR_INPUT_SOURCE_LIST, []
)
self.entity.attributes.get(media_player.ATTR_INPUT_SOURCE_LIST, [])
)
if len(inputs) > 0:
yield AlexaInputController(self.entity)
@@ -776,8 +774,7 @@ class MediaPlayerCapabilities(AlexaEntity):
and domain != "denonavr"
):
inputs = AlexaEqualizerController.get_valid_inputs(
self.entity.attributes.get(media_player.const.ATTR_SOUND_MODE_LIST)
or []
self.entity.attributes.get(media_player.ATTR_SOUND_MODE_LIST) or []
)
if len(inputs) > 0:
yield AlexaEqualizerController(self.entity)

View File

@@ -566,7 +566,7 @@ async def async_api_set_volume(
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_VOLUME_LEVEL: volume,
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
await hass.services.async_call(
@@ -589,7 +589,7 @@ async def async_api_select_input(
# Attempt to map the ALL UPPERCASE payload name to a source.
# Strips trailing 1 to match single input devices.
source_list = entity.attributes.get(media_player.const.ATTR_INPUT_SOURCE_LIST) or []
source_list = entity.attributes.get(media_player.ATTR_INPUT_SOURCE_LIST) or []
for source in source_list:
formatted_source = (
source.lower().replace("-", "").replace("_", "").replace(" ", "")
@@ -611,7 +611,7 @@ async def async_api_select_input(
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_INPUT_SOURCE: media_input,
media_player.ATTR_INPUT_SOURCE: media_input,
}
await hass.services.async_call(
@@ -636,7 +636,7 @@ async def async_api_adjust_volume(
volume_delta = int(directive.payload["volume"])
entity = directive.entity
current_level = entity.attributes[media_player.const.ATTR_MEDIA_VOLUME_LEVEL]
current_level = entity.attributes[media_player.ATTR_MEDIA_VOLUME_LEVEL]
# read current state
try:
@@ -648,7 +648,7 @@ async def async_api_adjust_volume(
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_VOLUME_LEVEL: volume,
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
await hass.services.async_call(
@@ -709,7 +709,7 @@ async def async_api_set_mute(
entity = directive.entity
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_VOLUME_MUTED: mute,
media_player.ATTR_MEDIA_VOLUME_MUTED: mute,
}
await hass.services.async_call(
@@ -1708,15 +1708,13 @@ async def async_api_changechannel(
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_CONTENT_ID: channel,
media_player.const.ATTR_MEDIA_CONTENT_TYPE: (
media_player.const.MEDIA_TYPE_CHANNEL
),
media_player.ATTR_MEDIA_CONTENT_ID: channel,
media_player.ATTR_MEDIA_CONTENT_TYPE: (media_player.MediaType.CHANNEL),
}
await hass.services.async_call(
entity.domain,
media_player.const.SERVICE_PLAY_MEDIA,
media_player.SERVICE_PLAY_MEDIA,
data,
blocking=False,
context=context,
@@ -1825,13 +1823,13 @@ async def async_api_set_eq_mode(
context: ha.Context,
) -> AlexaResponse:
"""Process a SetMode request for EqualizerController."""
mode = directive.payload["mode"]
mode: str = directive.payload["mode"]
entity = directive.entity
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
sound_mode_list = entity.attributes.get(media_player.const.ATTR_SOUND_MODE_LIST)
sound_mode_list = entity.attributes.get(media_player.ATTR_SOUND_MODE_LIST)
if sound_mode_list and mode.lower() in sound_mode_list:
data[media_player.const.ATTR_SOUND_MODE] = mode.lower()
data[media_player.ATTR_SOUND_MODE] = mode.lower()
else:
msg = f"failed to map sound mode {mode} to a mode on {entity.entity_id}"
raise AlexaInvalidValueError(msg)

View File

@@ -3,10 +3,10 @@
from __future__ import annotations
from asyncio import timeout
from collections.abc import Mapping
from http import HTTPStatus
import json
import logging
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, cast
from uuid import uuid4
@@ -260,10 +260,10 @@ async def async_enable_proactive_mode(
def extra_significant_check(
hass: HomeAssistant,
old_state: str,
old_attrs: dict[Any, Any] | MappingProxyType[Any, Any],
old_attrs: Mapping[Any, Any],
old_extra_arg: Any,
new_state: str,
new_attrs: dict[str, Any] | MappingProxyType[Any, Any],
new_attrs: Mapping[Any, Any],
new_extra_arg: Any,
) -> bool:
"""Check if the serialized data has changed."""

View File

@@ -3,12 +3,12 @@
"step": {
"user": {
"data": {
"tracked_addons": "Addons",
"tracked_addons": "Add-ons",
"tracked_integrations": "Integrations",
"tracked_custom_integrations": "Custom integrations"
},
"data_description": {
"tracked_addons": "Select the addons you want to track",
"tracked_addons": "Select the add-ons you want to track",
"tracked_integrations": "Select the integrations you want to track",
"tracked_custom_integrations": "Select the custom integrations you want to track"
}

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
from collections.abc import Mapping
from functools import partial
import logging
from types import MappingProxyType
@@ -175,7 +176,7 @@ class AnthropicOptionsFlow(OptionsFlow):
def anthropic_config_option_schema(
hass: HomeAssistant,
options: dict[str, Any] | MappingProxyType[str, Any],
options: Mapping[str, Any],
) -> dict:
"""Return a schema for Anthropic completion options."""
hass_apis: list[SelectOptionDict] = [

View File

@@ -9,11 +9,13 @@ from anthropic import AsyncStream
from anthropic._types import NOT_GIVEN
from anthropic.types import (
InputJSONDelta,
MessageDeltaUsage,
MessageParam,
MessageStreamEvent,
RawContentBlockDeltaEvent,
RawContentBlockStartEvent,
RawContentBlockStopEvent,
RawMessageDeltaEvent,
RawMessageStartEvent,
RawMessageStopEvent,
RedactedThinkingBlock,
@@ -31,6 +33,7 @@ from anthropic.types import (
ToolResultBlockParam,
ToolUseBlock,
ToolUseBlockParam,
Usage,
)
from voluptuous_openapi import convert
@@ -162,7 +165,8 @@ def _convert_content(
return messages
async def _transform_stream(
async def _transform_stream( # noqa: C901 - This is complex, but better to have it in one place
chat_log: conversation.ChatLog,
result: AsyncStream[MessageStreamEvent],
messages: list[MessageParam],
) -> AsyncGenerator[conversation.AssistantContentDeltaDict]:
@@ -207,6 +211,7 @@ async def _transform_stream(
| None
) = None
current_tool_args: str
input_usage: Usage | None = None
async for response in result:
LOGGER.debug("Received response: %s", response)
@@ -215,6 +220,7 @@ async def _transform_stream(
if response.message.role != "assistant":
raise ValueError("Unexpected message role")
current_message = MessageParam(role=response.message.role, content=[])
input_usage = response.message.usage
elif isinstance(response, RawContentBlockStartEvent):
if isinstance(response.content_block, ToolUseBlock):
current_block = ToolUseBlockParam(
@@ -285,12 +291,34 @@ async def _transform_stream(
raise ValueError("Unexpected stop event without a current message")
current_message["content"].append(current_block) # type: ignore[union-attr]
current_block = None
elif isinstance(response, RawMessageDeltaEvent):
if (usage := response.usage) is not None:
chat_log.async_trace(_create_token_stats(input_usage, usage))
elif isinstance(response, RawMessageStopEvent):
if current_message is not None:
messages.append(current_message)
current_message = None
def _create_token_stats(
input_usage: Usage | None, response_usage: MessageDeltaUsage
) -> dict[str, Any]:
"""Create token stats for conversation agent tracing."""
input_tokens = 0
cached_input_tokens = 0
if input_usage:
input_tokens = input_usage.input_tokens
cached_input_tokens = input_usage.cache_creation_input_tokens or 0
output_tokens = response_usage.output_tokens
return {
"stats": {
"input_tokens": input_tokens,
"cached_input_tokens": cached_input_tokens,
"output_tokens": output_tokens,
}
}
class AnthropicConversationEntity(
conversation.ConversationEntity, conversation.AbstractConversationAgent
):
@@ -393,7 +421,8 @@ class AnthropicConversationEntity(
[
content
async for content in chat_log.async_add_delta_content_stream(
user_input.agent_id, _transform_stream(stream, messages)
user_input.agent_id,
_transform_stream(chat_log, stream, messages),
)
if not isinstance(content, conversation.AssistantContent)
]

View File

@@ -113,4 +113,7 @@ class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):
data = await aioapcaccess.request_status(self._host, self._port)
return APCUPSdData(data)
except (OSError, asyncio.IncompleteReadError) as error:
raise UpdateFailed(error) from error
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from error

View File

@@ -93,7 +93,7 @@
"name": "Internal temperature"
},
"last_self_test": {
"name": "Last self test"
"name": "Last self-test"
},
"last_transfer": {
"name": "Last transfer"
@@ -177,7 +177,7 @@
"name": "Restore requirement"
},
"self_test_result": {
"name": "Self test result"
"name": "Self-test result"
},
"sensitivity": {
"name": "Sensitivity"
@@ -195,7 +195,7 @@
"name": "Status"
},
"self_test_interval": {
"name": "Self test interval"
"name": "Self-test interval"
},
"time_left": {
"name": "Time left"
@@ -219,5 +219,10 @@
"name": "Transfer to battery"
}
}
},
"exceptions": {
"cannot_connect": {
"message": "Cannot connect to APC UPS Daemon."
}
}
}

View File

@@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/apsystems",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["apsystems-ez1==2.5.0"]
"loggers": ["APsystemsEZ1"],
"requirements": ["apsystems-ez1==2.6.0"]
}

View File

@@ -92,6 +92,9 @@ class AssistSatelliteAnnouncement:
media_id: str
"""Media ID to be played."""
original_media_id: str
"""The raw media ID before processing."""
tts_token: str | None
"""The TTS token of the media."""
@@ -498,7 +501,9 @@ class AssistSatelliteEntity(entity.Entity):
media_id_source: Literal["url", "media_id", "tts"] | None = None
tts_token: str | None = None
if not media_id:
if media_id:
original_media_id = media_id
else:
media_id_source = "tts"
# Synthesize audio and get URL
pipeline_id = self._resolve_pipeline()
@@ -525,6 +530,13 @@ class AssistSatelliteEntity(entity.Entity):
tts_token = stream.token
media_id = stream.url
original_media_id = tts.generate_media_source_id(
self.hass,
message,
engine=engine,
language=pipeline.tts_language,
options=tts_options,
)
if media_source.is_media_source_id(media_id):
if not media_id_source:
@@ -560,6 +572,7 @@ class AssistSatelliteEntity(entity.Entity):
return AssistSatelliteAnnouncement(
message=message,
media_id=media_id,
original_media_id=original_media_id,
tts_token=tts_token,
media_id_source=media_id_source,
preannounce_media_id=preannounce_media_id,

View File

@@ -2,10 +2,9 @@
from __future__ import annotations
from collections.abc import Callable
from collections.abc import Callable, Mapping
from datetime import datetime, timedelta
import logging
from types import MappingProxyType
from typing import Any
from pyasuswrt import AsusWrtError
@@ -363,7 +362,7 @@ class AsusWrtRouter:
"""Add a function to call when router is closed."""
self._on_close.append(func)
def update_options(self, new_options: MappingProxyType[str, Any]) -> bool:
def update_options(self, new_options: Mapping[str, Any]) -> bool:
"""Update router options."""
req_reload = False
for name, new_opt in new_options.items():

View File

@@ -18,6 +18,7 @@ from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_MODE,
ATTR_NAME,
CONF_ACTIONS,
CONF_ALIAS,
CONF_CONDITIONS,
CONF_DEVICE_ID,
@@ -27,6 +28,7 @@ from homeassistant.const import (
CONF_MODE,
CONF_PATH,
CONF_PLATFORM,
CONF_TRIGGERS,
CONF_VARIABLES,
CONF_ZONE,
EVENT_HOMEASSISTANT_STARTED,
@@ -86,11 +88,9 @@ from homeassistant.util.hass_dict import HassKey
from .config import AutomationConfig, ValidationStatus
from .const import (
CONF_ACTIONS,
CONF_INITIAL_STATE,
CONF_TRACE,
CONF_TRIGGER_VARIABLES,
CONF_TRIGGERS,
DEFAULT_INITIAL_STATE,
DOMAIN,
LOGGER,

View File

@@ -14,11 +14,15 @@ from homeassistant.components import blueprint
from homeassistant.components.trace import TRACE_CONFIG_SCHEMA
from homeassistant.config import config_per_platform, config_without_domain
from homeassistant.const import (
CONF_ACTION,
CONF_ACTIONS,
CONF_ALIAS,
CONF_CONDITION,
CONF_CONDITIONS,
CONF_DESCRIPTION,
CONF_ID,
CONF_TRIGGER,
CONF_TRIGGERS,
CONF_VARIABLES,
)
from homeassistant.core import HomeAssistant
@@ -30,14 +34,10 @@ from homeassistant.helpers.typing import ConfigType
from homeassistant.util.yaml.input import UndefinedSubstitution
from .const import (
CONF_ACTION,
CONF_ACTIONS,
CONF_HIDE_ENTITY,
CONF_INITIAL_STATE,
CONF_TRACE,
CONF_TRIGGER,
CONF_TRIGGER_VARIABLES,
CONF_TRIGGERS,
DOMAIN,
LOGGER,
)
@@ -58,34 +58,9 @@ _MINIMAL_PLATFORM_SCHEMA = vol.Schema(
def _backward_compat_schema(value: Any | None) -> Any:
"""Backward compatibility for automations."""
if not isinstance(value, dict):
return value
# `trigger` has been renamed to `triggers`
if CONF_TRIGGER in value:
if CONF_TRIGGERS in value:
raise vol.Invalid(
"Cannot specify both 'trigger' and 'triggers'. Please use 'triggers' only."
)
value[CONF_TRIGGERS] = value.pop(CONF_TRIGGER)
# `condition` has been renamed to `conditions`
if CONF_CONDITION in value:
if CONF_CONDITIONS in value:
raise vol.Invalid(
"Cannot specify both 'condition' and 'conditions'. Please use 'conditions' only."
)
value[CONF_CONDITIONS] = value.pop(CONF_CONDITION)
# `action` has been renamed to `actions`
if CONF_ACTION in value:
if CONF_ACTIONS in value:
raise vol.Invalid(
"Cannot specify both 'action' and 'actions'. Please use 'actions' only."
)
value[CONF_ACTIONS] = value.pop(CONF_ACTION)
return value
value = cv.renamed(CONF_TRIGGER, CONF_TRIGGERS)(value)
value = cv.renamed(CONF_ACTION, CONF_ACTIONS)(value)
return cv.renamed(CONF_CONDITION, CONF_CONDITIONS)(value)
PLATFORM_SCHEMA = vol.All(

View File

@@ -2,10 +2,6 @@
import logging
CONF_ACTION = "action"
CONF_ACTIONS = "actions"
CONF_TRIGGER = "trigger"
CONF_TRIGGERS = "triggers"
CONF_TRIGGER_VARIABLES = "trigger_variables"
DOMAIN = "automation"

View File

@@ -4,7 +4,6 @@ from __future__ import annotations
from collections.abc import Mapping
from ipaddress import ip_address
from types import MappingProxyType
from typing import Any
from urllib.parse import urlsplit
@@ -88,7 +87,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
if user_input is not None:
try:
api = await get_axis_api(self.hass, MappingProxyType(user_input))
api = await get_axis_api(self.hass, user_input)
except AuthenticationRequired:
errors["base"] = "invalid_auth"

View File

@@ -1,7 +1,7 @@
"""Axis network device abstraction."""
from asyncio import timeout
from types import MappingProxyType
from collections.abc import Mapping
from typing import Any
import axis
@@ -23,7 +23,7 @@ from ..errors import AuthenticationRequired, CannotConnect
async def get_axis_api(
hass: HomeAssistant,
config: MappingProxyType[str, Any],
config: Mapping[str, Any],
) -> axis.AxisDevice:
"""Create a Axis device API."""
session = get_async_client(hass, verify_ssl=False)

View File

@@ -3,11 +3,10 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable
from collections.abc import Callable, Mapping
from datetime import datetime
import json
import logging
from types import MappingProxyType
from typing import Any
from azure.eventhub import EventData, EventDataBatch
@@ -179,7 +178,7 @@ class AzureEventHub:
await self.async_send(None)
await self._queue.join()
def update_options(self, new_options: MappingProxyType[str, Any]) -> None:
def update_options(self, new_options: Mapping[str, Any]) -> None:
"""Update options."""
self._send_interval = new_options[CONF_SEND_INTERVAL]

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
from collections import defaultdict
from dataclasses import dataclass, field, replace
import datetime as dt
from datetime import datetime, timedelta
@@ -87,12 +88,26 @@ class BackupConfigData:
else:
time = None
days = [Day(day) for day in data["schedule"]["days"]]
agents = {}
for agent_id, agent_data in data["agents"].items():
protected = agent_data["protected"]
stored_retention = agent_data["retention"]
agent_retention: AgentRetentionConfig | None
if stored_retention:
agent_retention = AgentRetentionConfig(
copies=stored_retention["copies"],
days=stored_retention["days"],
)
else:
agent_retention = None
agent_config = AgentConfig(
protected=protected,
retention=agent_retention,
)
agents[agent_id] = agent_config
return cls(
agents={
agent_id: AgentConfig(protected=agent_data["protected"])
for agent_id, agent_data in data["agents"].items()
},
agents=agents,
automatic_backups_configured=data["automatic_backups_configured"],
create_backup=CreateBackupConfig(
agent_ids=data["create_backup"]["agent_ids"],
@@ -176,12 +191,36 @@ class BackupConfig:
"""Update config."""
if agents is not UNDEFINED:
for agent_id, agent_config in agents.items():
if agent_id not in self.data.agents:
self.data.agents[agent_id] = AgentConfig(**agent_config)
agent_retention = agent_config.get("retention")
if agent_retention is None:
new_agent_retention = None
else:
self.data.agents[agent_id] = replace(
self.data.agents[agent_id], **agent_config
new_agent_retention = AgentRetentionConfig(
copies=agent_retention.get("copies"),
days=agent_retention.get("days"),
)
if agent_id not in self.data.agents:
old_agent_retention = None
self.data.agents[agent_id] = AgentConfig(
protected=agent_config.get("protected", False),
retention=new_agent_retention,
)
else:
new_agent_config = self.data.agents[agent_id]
old_agent_retention = new_agent_config.retention
if "protected" in agent_config:
new_agent_config = replace(
new_agent_config, protected=agent_config["protected"]
)
if "retention" in agent_config:
new_agent_config = replace(
new_agent_config, retention=new_agent_retention
)
self.data.agents[agent_id] = new_agent_config
if new_agent_retention != old_agent_retention:
# There's a single retention application method
# for both global and agent retention settings.
self.data.retention.apply(self._manager)
if automatic_backups_configured is not UNDEFINED:
self.data.automatic_backups_configured = automatic_backups_configured
if create_backup is not UNDEFINED:
@@ -207,11 +246,24 @@ class AgentConfig:
"""Represent the config for an agent."""
protected: bool
"""Agent protected configuration.
If True, the agent backups are password protected.
"""
retention: AgentRetentionConfig | None = None
"""Agent retention configuration.
If None, the global retention configuration is used.
If not None, the global retention configuration is ignored for this agent.
If an agent retention configuration is set and both copies and days are None,
backups will be kept forever for that agent.
"""
def to_dict(self) -> StoredAgentConfig:
"""Convert agent config to a dict."""
return {
"protected": self.protected,
"retention": self.retention.to_dict() if self.retention else None,
}
@@ -219,24 +271,46 @@ class StoredAgentConfig(TypedDict):
"""Represent the stored config for an agent."""
protected: bool
retention: StoredRetentionConfig | None
class AgentParametersDict(TypedDict, total=False):
"""Represent the parameters for an agent."""
protected: bool
retention: RetentionParametersDict | None
@dataclass(kw_only=True)
class RetentionConfig:
"""Represent the backup retention configuration."""
class BaseRetentionConfig:
"""Represent the base backup retention configuration."""
copies: int | None = None
days: int | None = None
def to_dict(self) -> StoredRetentionConfig:
"""Convert backup retention configuration to a dict."""
return StoredRetentionConfig(
copies=self.copies,
days=self.days,
)
@dataclass(kw_only=True)
class RetentionConfig(BaseRetentionConfig):
"""Represent the backup retention configuration."""
def apply(self, manager: BackupManager) -> None:
"""Apply backup retention configuration."""
if self.days is not None:
agents_retention = {
agent_id: agent_config.retention
for agent_id, agent_config in manager.config.data.agents.items()
}
if self.days is not None or any(
agent_retention and agent_retention.days is not None
for agent_retention in agents_retention.values()
):
LOGGER.debug(
"Scheduling next automatic delete of backups older than %s in 1 day",
self.days,
@@ -246,13 +320,6 @@ class RetentionConfig:
LOGGER.debug("Unscheduling next automatic delete")
self._unschedule_next(manager)
def to_dict(self) -> StoredRetentionConfig:
"""Convert backup retention configuration to a dict."""
return StoredRetentionConfig(
copies=self.copies,
days=self.days,
)
@callback
def _schedule_next(
self,
@@ -271,16 +338,81 @@ class RetentionConfig:
"""Return backups older than days to delete."""
# we need to check here since we await before
# this filter is applied
if self.days is None:
return {}
now = dt_util.utcnow()
return {
backup_id: backup
for backup_id, backup in backups.items()
if dt_util.parse_datetime(backup.date, raise_on_error=True)
+ timedelta(days=self.days)
< now
agents_retention = {
agent_id: agent_config.retention
for agent_id, agent_config in manager.config.data.agents.items()
}
has_agents_retention = any(
agent_retention for agent_retention in agents_retention.values()
)
has_agents_retention_days = any(
agent_retention and agent_retention.days is not None
for agent_retention in agents_retention.values()
)
if (global_days := self.days) is None and not has_agents_retention_days:
# No global retention days and no agent retention days
return {}
now = dt_util.utcnow()
if global_days is not None and not has_agents_retention:
# Return early to avoid the longer filtering below.
return {
backup_id: backup
for backup_id, backup in backups.items()
if dt_util.parse_datetime(backup.date, raise_on_error=True)
+ timedelta(days=global_days)
< now
}
# If there are any agent retention settings, we need to check
# the retention settings, for every backup and agent combination.
backups_to_delete = {}
for backup_id, backup in backups.items():
backup_date = dt_util.parse_datetime(
backup.date, raise_on_error=True
)
delete_from_agents = set(backup.agents)
for agent_id in backup.agents:
agent_retention = agents_retention.get(agent_id)
if agent_retention is None:
# This agent does not have a retention setting,
# so the global retention setting should be used.
if global_days is None:
# This agent does not have a retention setting
# and the global retention days setting is None,
# so this backup should not be deleted.
delete_from_agents.discard(agent_id)
continue
days = global_days
elif (agent_days := agent_retention.days) is None:
# This agent has a retention setting
# where days is set to None,
# so the backup should not be deleted.
delete_from_agents.discard(agent_id)
continue
else:
# This agent has a retention setting
# where days is set to a number,
# so that setting should be used.
days = agent_days
if backup_date + timedelta(days=days) >= now:
# This backup is not older than the retention days,
# so this agent should not be deleted.
delete_from_agents.discard(agent_id)
filtered_backup = replace(
backup,
agents={
agent_id: agent_backup_status
for agent_id, agent_backup_status in backup.agents.items()
if agent_id in delete_from_agents
},
)
backups_to_delete[backup_id] = filtered_backup
return backups_to_delete
await manager.async_delete_filtered_backups(
include_filter=_automatic_backups_filter, delete_filter=_delete_filter
@@ -312,6 +444,10 @@ class RetentionParametersDict(TypedDict, total=False):
days: int | None
class AgentRetentionConfig(BaseRetentionConfig):
"""Represent an agent retention configuration."""
class StoredBackupSchedule(TypedDict):
"""Represent the stored backup schedule configuration."""
@@ -554,16 +690,87 @@ async def delete_backups_exceeding_configured_count(manager: BackupManager) -> N
backups: dict[str, ManagerBackup],
) -> dict[str, ManagerBackup]:
"""Return oldest backups more numerous than copies to delete."""
agents_retention = {
agent_id: agent_config.retention
for agent_id, agent_config in manager.config.data.agents.items()
}
has_agents_retention = any(
agent_retention for agent_retention in agents_retention.values()
)
has_agents_retention_copies = any(
agent_retention and agent_retention.copies is not None
for agent_retention in agents_retention.values()
)
# we need to check here since we await before
# this filter is applied
if manager.config.data.retention.copies is None:
if (
global_copies := manager.config.data.retention.copies
) is None and not has_agents_retention_copies:
# No global retention copies and no agent retention copies
return {}
return dict(
sorted(
backups.items(),
key=lambda backup_item: backup_item[1].date,
)[: max(len(backups) - manager.config.data.retention.copies, 0)]
if global_copies is not None and not has_agents_retention:
# Return early to avoid the longer filtering below.
return dict(
sorted(
backups.items(),
key=lambda backup_item: backup_item[1].date,
)[: max(len(backups) - global_copies, 0)]
)
backups_by_agent: dict[str, dict[str, ManagerBackup]] = defaultdict(dict)
for backup_id, backup in backups.items():
for agent_id in backup.agents:
backups_by_agent[agent_id][backup_id] = backup
backups_to_delete_by_agent: dict[str, dict[str, ManagerBackup]] = defaultdict(
dict
)
for agent_id, agent_backups in backups_by_agent.items():
agent_retention = agents_retention.get(agent_id)
if agent_retention is None:
# This agent does not have a retention setting,
# so the global retention setting should be used.
if global_copies is None:
# This agent does not have a retention setting
# and the global retention copies setting is None,
# so backups should not be deleted.
continue
# The global retention setting will be used.
copies = global_copies
elif (agent_copies := agent_retention.copies) is None:
# This agent has a retention setting
# where copies is set to None,
# so backups should not be deleted.
continue
else:
# This agent retention setting will be used.
copies = agent_copies
backups_to_delete_by_agent[agent_id] = dict(
sorted(
agent_backups.items(),
key=lambda backup_item: backup_item[1].date,
)[: max(len(agent_backups) - copies, 0)]
)
backup_ids_to_delete: dict[str, set[str]] = defaultdict(set)
for agent_id, to_delete in backups_to_delete_by_agent.items():
for backup_id in to_delete:
backup_ids_to_delete[backup_id].add(agent_id)
backups_to_delete: dict[str, ManagerBackup] = {}
for backup_id, agent_ids in backup_ids_to_delete.items():
backup = backups[backup_id]
# filter the backup to only include the agents that should be deleted
filtered_backup = replace(
backup,
agents={
agent_id: agent_backup_status
for agent_id, agent_backup_status in backup.agents.items()
if agent_id in agent_ids
},
)
backups_to_delete[backup_id] = filtered_backup
return backups_to_delete
await manager.async_delete_filtered_backups(
include_filter=_automatic_backups_filter, delete_filter=_delete_filter

View File

@@ -16,7 +16,7 @@ if TYPE_CHECKING:
STORE_DELAY_SAVE = 30
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
STORAGE_VERSION_MINOR = 5
STORAGE_VERSION_MINOR = 6
class StoredBackupData(TypedDict):
@@ -72,6 +72,10 @@ class _BackupStore(Store[StoredBackupData]):
data["config"]["automatic_backups_configured"] = (
data["config"]["create_backup"]["password"] is not None
)
if old_minor_version < 6:
# Version 1.6 adds agent retention settings
for agent in data["config"]["agents"]:
data["config"]["agents"][agent]["retention"] = None
# Note: We allow reading data with major version 2.
# Reject if major version is higher than 2.

View File

@@ -346,7 +346,28 @@ async def handle_config_info(
@websocket_api.websocket_command(
{
vol.Required("type"): "backup/config/update",
vol.Optional("agents"): vol.Schema({str: {"protected": bool}}),
vol.Optional("agents"): vol.Schema(
{
str: {
vol.Optional("protected"): bool,
vol.Optional("retention"): vol.Any(
vol.Schema(
{
# Note: We can't use cv.positive_int because it allows 0 even
# though 0 is not positive.
vol.Optional("copies"): vol.Any(
vol.All(int, vol.Range(min=1)), None
),
vol.Optional("days"): vol.Any(
vol.All(int, vol.Range(min=1)), None
),
},
),
None,
),
}
}
),
vol.Optional("automatic_backups_configured"): bool,
vol.Optional("create_backup"): vol.Schema(
{

View File

@@ -12,5 +12,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bluemaestro",
"iot_class": "local_push",
"requirements": ["bluemaestro-ble==0.2.3"]
"requirements": ["bluemaestro-ble==0.3.0"]
}

View File

@@ -19,8 +19,8 @@
"bleak-retry-connector==3.9.0",
"bluetooth-adapters==0.21.4",
"bluetooth-auto-recovery==1.4.5",
"bluetooth-data-tools==1.27.0",
"bluetooth-data-tools==1.28.1",
"dbus-fast==2.43.0",
"habluetooth==3.39.0"
"habluetooth==3.45.0"
]
}

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from bluetooth_adapters import (
from habluetooth import (
DiscoveredDeviceAdvertisementData,
DiscoveredDeviceAdvertisementDataDict,
DiscoveryStorageType,

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/chacon_dio",
"iot_class": "cloud_push",
"loggers": ["dio_chacon_api"],
"requirements": ["dio-chacon-wifi-api==1.2.1"]
"requirements": ["dio-chacon-wifi-api==1.2.2"]
}

View File

@@ -93,3 +93,5 @@ STT_ENTITY_UNIQUE_ID = "cloud-speech-to-text"
TTS_ENTITY_UNIQUE_ID = "cloud-text-to-speech"
LOGIN_MFA_TIMEOUT = 60
VOICE_STYLE_SEPERATOR = "||"

View File

@@ -18,7 +18,7 @@ from aiohttp import web
import attr
from hass_nabucasa import AlreadyConnectedError, Cloud, auth, thingtalk
from hass_nabucasa.const import STATE_DISCONNECTED
from hass_nabucasa.voice import TTS_VOICES
from hass_nabucasa.voice_data import TTS_VOICES
import voluptuous as vol
from homeassistant.components import websocket_api
@@ -57,6 +57,7 @@ from .const import (
PREF_REMOTE_ALLOW_REMOTE_ENABLE,
PREF_TTS_DEFAULT_VOICE,
REQUEST_TIMEOUT,
VOICE_STYLE_SEPERATOR,
)
from .google_config import CLOUD_GOOGLE
from .repairs import async_manage_legacy_subscription_issue
@@ -591,10 +592,21 @@ async def websocket_subscription(
def validate_language_voice(value: tuple[str, str]) -> tuple[str, str]:
"""Validate language and voice."""
language, voice = value
style: str | None
voice, _, style = voice.partition(VOICE_STYLE_SEPERATOR)
if not style:
style = None
if language not in TTS_VOICES:
raise vol.Invalid(f"Invalid language {language}")
if voice not in TTS_VOICES[language]:
if voice not in (language_info := TTS_VOICES[language]):
raise vol.Invalid(f"Invalid voice {voice} for language {language}")
voice_info = language_info[voice]
if style and (
isinstance(voice_info, str) or style not in voice_info.get("variants", [])
):
raise vol.Invalid(
f"Invalid style {style} for voice {voice} in language {language}"
)
return value
@@ -1012,13 +1024,24 @@ def tts_info(
msg: dict[str, Any],
) -> None:
"""Fetch available tts info."""
connection.send_result(
msg["id"],
{
"languages": [
(language, voice)
for language, voices in TTS_VOICES.items()
for voice in voices
]
},
)
result = []
for language, voices in TTS_VOICES.items():
for voice_id, voice_info in voices.items():
if isinstance(voice_info, str):
result.append((language, voice_id, voice_info))
continue
name = voice_info["name"]
result.append((language, voice_id, name))
result.extend(
[
(
language,
f"{voice_id}{VOICE_STYLE_SEPERATOR}{variant}",
f"{name} ({variant})",
)
for variant in voice_info.get("variants", [])
]
)
connection.send_result(msg["id"], {"languages": result})

View File

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

View File

@@ -6,7 +6,8 @@ import logging
from typing import Any
from hass_nabucasa import Cloud
from hass_nabucasa.voice import MAP_VOICE, TTS_VOICES, AudioOutput, Gender, VoiceError
from hass_nabucasa.voice import MAP_VOICE, AudioOutput, Gender, VoiceError
from hass_nabucasa.voice_data import TTS_VOICES
import voluptuous as vol
from homeassistant.components.tts import (
@@ -30,7 +31,13 @@ from homeassistant.setup import async_when_setup
from .assist_pipeline import async_migrate_cloud_pipeline_engine
from .client import CloudClient
from .const import DATA_CLOUD, DATA_PLATFORMS_SETUP, DOMAIN, TTS_ENTITY_UNIQUE_ID
from .const import (
DATA_CLOUD,
DATA_PLATFORMS_SETUP,
DOMAIN,
TTS_ENTITY_UNIQUE_ID,
VOICE_STYLE_SEPERATOR,
)
from .prefs import CloudPreferences
ATTR_GENDER = "gender"
@@ -57,6 +64,7 @@ DEFAULT_VOICES = {
"ar-SY": "AmanyNeural",
"ar-TN": "ReemNeural",
"ar-YE": "MaryamNeural",
"as-IN": "PriyomNeural",
"az-AZ": "BabekNeural",
"bg-BG": "KalinaNeural",
"bn-BD": "NabanitaNeural",
@@ -126,6 +134,8 @@ DEFAULT_VOICES = {
"id-ID": "GadisNeural",
"is-IS": "GudrunNeural",
"it-IT": "ElsaNeural",
"iu-Cans-CA": "SiqiniqNeural",
"iu-Latn-CA": "SiqiniqNeural",
"ja-JP": "NanamiNeural",
"jv-ID": "SitiNeural",
"ka-GE": "EkaNeural",
@@ -147,6 +157,8 @@ DEFAULT_VOICES = {
"ne-NP": "HemkalaNeural",
"nl-BE": "DenaNeural",
"nl-NL": "ColetteNeural",
"or-IN": "SubhasiniNeural",
"pa-IN": "OjasNeural",
"pl-PL": "AgnieszkaNeural",
"ps-AF": "LatifaNeural",
"pt-BR": "FranciscaNeural",
@@ -158,6 +170,7 @@ DEFAULT_VOICES = {
"sl-SI": "PetraNeural",
"so-SO": "UbaxNeural",
"sq-AL": "AnilaNeural",
"sr-Latn-RS": "NicholasNeural",
"sr-RS": "SophieNeural",
"su-ID": "TutiNeural",
"sv-SE": "SofieNeural",
@@ -177,12 +190,9 @@ DEFAULT_VOICES = {
"vi-VN": "HoaiMyNeural",
"wuu-CN": "XiaotongNeural",
"yue-CN": "XiaoMinNeural",
"zh-CN": "XiaoxiaoNeural",
"zh-CN-henan": "YundengNeural",
"zh-CN-liaoning": "XiaobeiNeural",
"zh-CN-shaanxi": "XiaoniNeural",
"zh-CN-shandong": "YunxiangNeural",
"zh-CN-sichuan": "YunxiNeural",
"zh-CN": "XiaoxiaoNeural",
"zh-HK": "HiuMaanNeural",
"zh-TW": "HsiaoChenNeural",
"zu-ZA": "ThandoNeural",
@@ -191,6 +201,39 @@ DEFAULT_VOICES = {
_LOGGER = logging.getLogger(__name__)
@callback
def _prepare_voice_args(
*,
hass: HomeAssistant,
language: str,
voice: str,
gender: str | None,
) -> dict:
"""Prepare voice arguments."""
gender = handle_deprecated_gender(hass, gender)
style: str | None
original_voice, _, style = voice.partition(VOICE_STYLE_SEPERATOR)
if not style:
style = None
updated_voice = handle_deprecated_voice(hass, original_voice)
if updated_voice not in TTS_VOICES[language]:
default_voice = DEFAULT_VOICES[language]
_LOGGER.debug(
"Unsupported voice %s detected, falling back to default %s for %s",
voice,
default_voice,
language,
)
updated_voice = default_voice
return {
"language": language,
"voice": updated_voice,
"gender": gender,
"style": style,
}
def _deprecated_platform(value: str) -> str:
"""Validate if platform is deprecated."""
if value == DOMAIN:
@@ -328,36 +371,59 @@ class CloudTTSEntity(TextToSpeechEntity):
"""Return a list of supported voices for a language."""
if not (voices := TTS_VOICES.get(language)):
return None
return [Voice(voice, voice) for voice in voices]
result = []
for voice_id, voice_info in voices.items():
if isinstance(voice_info, str):
result.append(
Voice(
voice_id,
voice_info,
)
)
continue
name = voice_info["name"]
result.append(
Voice(
voice_id,
name,
)
)
result.extend(
[
Voice(
f"{voice_id}{VOICE_STYLE_SEPERATOR}{variant}",
f"{name} ({variant})",
)
for variant in voice_info.get("variants", [])
]
)
return result
async def async_get_tts_audio(
self, message: str, language: str, options: dict[str, Any]
) -> TtsAudioType:
"""Load TTS from Home Assistant Cloud."""
gender: Gender | str | None = options.get(ATTR_GENDER)
gender = handle_deprecated_gender(self.hass, gender)
original_voice: str = options.get(
ATTR_VOICE,
self._voice if language == self._language else DEFAULT_VOICES[language],
)
voice = handle_deprecated_voice(self.hass, original_voice)
if voice not in TTS_VOICES[language]:
default_voice = DEFAULT_VOICES[language]
_LOGGER.debug(
"Unsupported voice %s detected, falling back to default %s for %s",
voice,
default_voice,
language,
)
voice = default_voice
# Process TTS
try:
data = await self.cloud.voice.process_tts(
text=message,
language=language,
gender=gender,
voice=voice,
output=options[ATTR_AUDIO_OUTPUT],
**_prepare_voice_args(
hass=self.hass,
language=language,
voice=options.get(
ATTR_VOICE,
self._voice
if language == self._language
else DEFAULT_VOICES[language],
),
gender=options.get(ATTR_GENDER),
),
)
except VoiceError as err:
_LOGGER.error("Voice error: %s", err)
@@ -401,7 +467,38 @@ class CloudProvider(Provider):
"""Return a list of supported voices for a language."""
if not (voices := TTS_VOICES.get(language)):
return None
return [Voice(voice, voice) for voice in voices]
result = []
for voice_id, voice_info in voices.items():
if isinstance(voice_info, str):
result.append(
Voice(
voice_id,
voice_info,
)
)
continue
name = voice_info["name"]
result.append(
Voice(
voice_id,
name,
)
)
result.extend(
[
Voice(
f"{voice_id}{VOICE_STYLE_SEPERATOR}{variant}",
f"{name} ({variant})",
)
for variant in voice_info.get("variants", [])
]
)
return result
@property
def default_options(self) -> dict[str, str]:
@@ -415,30 +512,22 @@ class CloudProvider(Provider):
) -> TtsAudioType:
"""Load TTS from Home Assistant Cloud."""
assert self.hass is not None
gender: Gender | str | None = options.get(ATTR_GENDER)
gender = handle_deprecated_gender(self.hass, gender)
original_voice: str = options.get(
ATTR_VOICE,
self._voice if language == self._language else DEFAULT_VOICES[language],
)
voice = handle_deprecated_voice(self.hass, original_voice)
if voice not in TTS_VOICES[language]:
default_voice = DEFAULT_VOICES[language]
_LOGGER.debug(
"Unsupported voice %s detected, falling back to default %s for %s",
voice,
default_voice,
language,
)
voice = default_voice
# Process TTS
try:
data = await self.cloud.voice.process_tts(
text=message,
language=language,
gender=gender,
voice=voice,
output=options[ATTR_AUDIO_OUTPUT],
**_prepare_voice_args(
hass=self.hass,
language=language,
voice=options.get(
ATTR_VOICE,
self._voice
if language == self._language
else DEFAULT_VOICES[language],
),
gender=options.get(ATTR_GENDER),
),
)
except VoiceError as err:
_LOGGER.error("Voice error: %s", err)

View File

@@ -12,6 +12,7 @@ from .coordinator import (
ComelitSerialBridge,
ComelitVedoSystem,
)
from .utils import async_client_session
BRIDGE_PLATFORMS = [
Platform.CLIMATE,
@@ -32,6 +33,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ComelitConfigEntry) -> b
"""Set up Comelit platform."""
coordinator: ComelitBaseCoordinator
session = await async_client_session(hass)
if entry.data.get(CONF_TYPE, BRIDGE) == BRIDGE:
coordinator = ComelitSerialBridge(
hass,
@@ -39,6 +43,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ComelitConfigEntry) -> b
entry.data[CONF_HOST],
entry.data.get(CONF_PORT, DEFAULT_PORT),
entry.data[CONF_PIN],
session,
)
platforms = BRIDGE_PLATFORMS
else:
@@ -48,6 +53,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ComelitConfigEntry) -> b
entry.data[CONF_HOST],
entry.data.get(CONF_PORT, DEFAULT_PORT),
entry.data[CONF_PIN],
session,
)
platforms = VEDO_PLATFORMS

View File

@@ -22,6 +22,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from .const import _LOGGER, DEFAULT_PORT, DEVICE_TYPE_LIST, DOMAIN
from .utils import async_client_session
DEFAULT_HOST = "192.168.1.252"
DEFAULT_PIN = 111111
@@ -47,10 +48,14 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
"""Validate the user input allows us to connect."""
api: ComelitCommonApi
session = await async_client_session(hass)
if data.get(CONF_TYPE, BRIDGE) == BRIDGE:
api = ComeliteSerialBridgeApi(data[CONF_HOST], data[CONF_PORT], data[CONF_PIN])
api = ComeliteSerialBridgeApi(
data[CONF_HOST], data[CONF_PORT], data[CONF_PIN], session
)
else:
api = ComelitVedoApi(data[CONF_HOST], data[CONF_PORT], data[CONF_PIN])
api = ComelitVedoApi(data[CONF_HOST], data[CONF_PORT], data[CONF_PIN], session)
try:
await api.login()

View File

@@ -15,6 +15,7 @@ from aiocomelit.api import (
)
from aiocomelit.const import BRIDGE, VEDO
from aiocomelit.exceptions import CannotAuthenticate, CannotConnect, CannotRetrieveData
from aiohttp import ClientSession
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -95,9 +96,16 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[T]):
await self.api.login()
return await self._async_update_system_data()
except (CannotConnect, CannotRetrieveData) as err:
raise UpdateFailed(repr(err)) from err
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_failed",
translation_placeholders={"error": repr(err)},
) from err
except CannotAuthenticate as err:
raise ConfigEntryAuthFailed from err
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="cannot_authenticate",
) from err
@abstractmethod
async def _async_update_system_data(self) -> T:
@@ -119,9 +127,10 @@ class ComelitSerialBridge(
host: str,
port: int,
pin: int,
session: ClientSession,
) -> None:
"""Initialize the scanner."""
self.api = ComeliteSerialBridgeApi(host, port, pin)
self.api = ComeliteSerialBridgeApi(host, port, pin, session)
super().__init__(hass, entry, BRIDGE, host)
async def _async_update_system_data(
@@ -144,9 +153,10 @@ class ComelitVedoSystem(ComelitBaseCoordinator[AlarmDataObject]):
host: str,
port: int,
pin: int,
session: ClientSession,
) -> None:
"""Initialize the scanner."""
self.api = ComelitVedoApi(host, port, pin)
self.api = ComelitVedoApi(host, port, pin, session)
super().__init__(hass, entry, VEDO, host)
async def _async_update_system_data(

View File

@@ -8,5 +8,5 @@
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"quality_scale": "bronze",
"requirements": ["aiocomelit==0.11.3"]
"requirements": ["aiocomelit==0.12.0"]
}

View File

@@ -70,9 +70,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations:
status: todo
comment: PR in progress
exception-translations: done
icon-translations: done
reconfiguration-flow:
status: todo
@@ -86,7 +84,5 @@ rules:
# Platinum
async-dependency: done
inject-websession:
status: todo
comment: implement aiohttp_client.async_create_clientsession
inject-websession: done
strict-typing: done

View File

@@ -74,7 +74,10 @@
"message": "Error connecting: {error}"
},
"cannot_authenticate": {
"message": "Error authenticating: {error}"
"message": "Error authenticating"
},
"updated_failed": {
"message": "Failed to update data: {error}"
}
}
}

View File

@@ -0,0 +1,13 @@
"""Utils for Comelit."""
from aiohttp import ClientSession, CookieJar
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
async def async_client_session(hass: HomeAssistant) -> ClientSession:
"""Return a new aiohttp session."""
return aiohttp_client.async_create_clientsession(
hass, verify_ssl=False, cookie_jar=CookieJar(unsafe=True)
)

View File

@@ -56,7 +56,10 @@ from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.helpers.reload import async_integration_yaml_config
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.helpers.trigger_template_entity import CONF_AVAILABILITY
from homeassistant.helpers.trigger_template_entity import (
CONF_AVAILABILITY,
ValueTemplate,
)
from homeassistant.helpers.typing import ConfigType
from .const import (
@@ -91,7 +94,9 @@ BINARY_SENSOR_SCHEMA = vol.Schema(
vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
vol.Optional(CONF_DEVICE_CLASS): BINARY_SENSOR_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_VALUE_TEMPLATE): vol.All(
cv.template, ValueTemplate.from_template
),
vol.Optional(CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(
@@ -108,7 +113,9 @@ COVER_SCHEMA = vol.Schema(
vol.Optional(CONF_COMMAND_STOP, default="true"): cv.string,
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_ICON): cv.template,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_VALUE_TEMPLATE): vol.All(
cv.template, ValueTemplate.from_template
),
vol.Optional(CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_DEVICE_CLASS): COVER_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_UNIQUE_ID): cv.string,
@@ -134,7 +141,9 @@ SENSOR_SCHEMA = vol.Schema(
vol.Optional(CONF_NAME, default=SENSOR_DEFAULT_NAME): cv.string,
vol.Optional(CONF_ICON): cv.template,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_VALUE_TEMPLATE): vol.All(
cv.template, ValueTemplate.from_template
),
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_DEVICE_CLASS): SENSOR_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_STATE_CLASS): SENSOR_STATE_CLASSES_SCHEMA,
@@ -150,7 +159,9 @@ SWITCH_SCHEMA = vol.Schema(
vol.Optional(CONF_COMMAND_ON, default="true"): cv.string,
vol.Optional(CONF_COMMAND_STATE): cv.string,
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_VALUE_TEMPLATE): vol.All(
cv.template, ValueTemplate.from_template
),
vol.Optional(CONF_ICON): cv.template,
vol.Optional(CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_UNIQUE_ID): cv.string,

View File

@@ -18,7 +18,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerEntity
from homeassistant.helpers.trigger_template_entity import (
ManualTriggerEntity,
ValueTemplate,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util
@@ -50,7 +53,7 @@ async def async_setup_platform(
scan_interval: timedelta = binary_sensor_config.get(
CONF_SCAN_INTERVAL, SCAN_INTERVAL
)
value_template: Template | None = binary_sensor_config.get(CONF_VALUE_TEMPLATE)
value_template: ValueTemplate | None = binary_sensor_config.get(CONF_VALUE_TEMPLATE)
data = CommandSensorData(hass, command, command_timeout)
@@ -86,7 +89,7 @@ class CommandBinarySensor(ManualTriggerEntity, BinarySensorEntity):
config: ConfigType,
payload_on: str,
payload_off: str,
value_template: Template | None,
value_template: ValueTemplate | None,
scan_interval: timedelta,
) -> None:
"""Initialize the Command line binary sensor."""
@@ -133,9 +136,14 @@ class CommandBinarySensor(ManualTriggerEntity, BinarySensorEntity):
await self.data.async_update()
value = self.data.value
variables = self._template_variables_with_value(value)
if not self._render_availability_template(variables):
self.async_write_ha_state()
return
if self._value_template is not None:
value = self._value_template.async_render_with_possible_json_value(
value, None
value = self._value_template.async_render_as_value_template(
self.entity_id, variables, None
)
self._attr_is_on = None
if value == self._payload_on:
@@ -143,7 +151,7 @@ class CommandBinarySensor(ManualTriggerEntity, BinarySensorEntity):
elif value == self._payload_off:
self._attr_is_on = False
self._process_manual_data(value)
self._process_manual_data(variables)
self.async_write_ha_state()
async def async_update(self) -> None:

View File

@@ -20,7 +20,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerEntity
from homeassistant.helpers.trigger_template_entity import (
ManualTriggerEntity,
ValueTemplate,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util, slugify
@@ -79,7 +82,7 @@ class CommandCover(ManualTriggerEntity, CoverEntity):
command_close: str,
command_stop: str,
command_state: str | None,
value_template: Template | None,
value_template: ValueTemplate | None,
timeout: int,
scan_interval: timedelta,
) -> None:
@@ -164,14 +167,20 @@ class CommandCover(ManualTriggerEntity, CoverEntity):
"""Update device state."""
if self._command_state:
payload = str(await self._async_query_state())
variables = self._template_variables_with_value(payload)
if not self._render_availability_template(variables):
self.async_write_ha_state()
return
if self._value_template:
payload = self._value_template.async_render_with_possible_json_value(
payload, None
payload = self._value_template.async_render_as_value_template(
self.entity_id, variables, None
)
self._state = None
if payload:
self._state = int(payload)
self._process_manual_data(payload)
self._process_manual_data(variables)
self.async_write_ha_state()
async def async_update(self) -> None:

View File

@@ -23,7 +23,10 @@ from homeassistant.exceptions import TemplateError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerSensorEntity
from homeassistant.helpers.trigger_template_entity import (
ManualTriggerSensorEntity,
ValueTemplate,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util
@@ -57,7 +60,7 @@ async def async_setup_platform(
json_attributes: list[str] | None = sensor_config.get(CONF_JSON_ATTRIBUTES)
json_attributes_path: str | None = sensor_config.get(CONF_JSON_ATTRIBUTES_PATH)
scan_interval: timedelta = sensor_config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
value_template: Template | None = sensor_config.get(CONF_VALUE_TEMPLATE)
value_template: ValueTemplate | None = sensor_config.get(CONF_VALUE_TEMPLATE)
data = CommandSensorData(hass, command, command_timeout)
trigger_entity_config = {
@@ -88,7 +91,7 @@ class CommandSensor(ManualTriggerSensorEntity):
self,
data: CommandSensorData,
config: ConfigType,
value_template: Template | None,
value_template: ValueTemplate | None,
json_attributes: list[str] | None,
json_attributes_path: str | None,
scan_interval: timedelta,
@@ -144,6 +147,11 @@ class CommandSensor(ManualTriggerSensorEntity):
await self.data.async_update()
value = self.data.value
variables = self._template_variables_with_value(self.data.value)
if not self._render_availability_template(variables):
self.async_write_ha_state()
return
if self._json_attributes:
self._attr_extra_state_attributes = {}
if value:
@@ -168,16 +176,17 @@ class CommandSensor(ManualTriggerSensorEntity):
LOGGER.warning("Unable to parse output as JSON: %s", value)
else:
LOGGER.warning("Empty reply found when expecting JSON data")
if self._value_template is None:
self._attr_native_value = None
self._process_manual_data(value)
self._process_manual_data(variables)
self.async_write_ha_state()
return
self._attr_native_value = None
if self._value_template is not None and value is not None:
value = self._value_template.async_render_with_possible_json_value(
value,
None,
value = self._value_template.async_render_as_value_template(
self.entity_id, variables, None
)
if self.device_class not in {
@@ -190,7 +199,7 @@ class CommandSensor(ManualTriggerSensorEntity):
value, self.entity_id, self.device_class
)
self._process_manual_data(value)
self._process_manual_data(variables)
self.async_write_ha_state()
async def async_update(self) -> None:

View File

@@ -19,7 +19,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerEntity
from homeassistant.helpers.trigger_template_entity import (
ManualTriggerEntity,
ValueTemplate,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util, slugify
@@ -78,7 +81,7 @@ class CommandSwitch(ManualTriggerEntity, SwitchEntity):
command_on: str,
command_off: str,
command_state: str | None,
value_template: Template | None,
value_template: ValueTemplate | None,
timeout: int,
scan_interval: timedelta,
) -> None:
@@ -166,15 +169,21 @@ class CommandSwitch(ManualTriggerEntity, SwitchEntity):
"""Update device state."""
if self._command_state:
payload = str(await self._async_query_state())
variables = self._template_variables_with_value(payload)
if not self._render_availability_template(variables):
self.async_write_ha_state()
return
value = None
if self._value_template:
value = self._value_template.async_render_with_possible_json_value(
payload, None
value = self._value_template.async_render_as_value_template(
self.entity_id, variables, None
)
self._attr_is_on = None
if payload or value:
self._attr_is_on = (value or payload).lower() == "true"
self._process_manual_data(payload)
self._process_manual_data(variables)
self.async_write_ha_state()
async def async_update(self) -> None:

View File

@@ -73,7 +73,7 @@
"remote_moved_any_side": "Device moved with any side up",
"remote_double_tap_any_side": "Device double tapped on any side",
"remote_turned_clockwise": "Device turned clockwise",
"remote_turned_counter_clockwise": "Device turned counter clockwise",
"remote_turned_counter_clockwise": "Device turned counterclockwise",
"remote_rotate_from_side_1": "Device rotated from \"side 1\" to \"{subtype}\"",
"remote_rotate_from_side_2": "Device rotated from \"side 2\" to \"{subtype}\"",
"remote_rotate_from_side_3": "Device rotated from \"side 3\" to \"{subtype}\"",

View File

@@ -218,7 +218,7 @@ class TrackerEntity(
entity_description: TrackerEntityDescription
_attr_latitude: float | None = None
_attr_location_accuracy: int = 0
_attr_location_accuracy: float = 0
_attr_location_name: str | None = None
_attr_longitude: float | None = None
_attr_source_type: SourceType = SourceType.GPS
@@ -234,7 +234,7 @@ class TrackerEntity(
return not self.should_poll
@cached_property
def location_accuracy(self) -> int:
def location_accuracy(self) -> float:
"""Return the location accuracy of the device.
Value in meters.

View File

@@ -3,8 +3,8 @@
from __future__ import annotations
import asyncio
from collections.abc import Mapping
from functools import partial
from types import MappingProxyType
from typing import Any
from devolo_home_control_api.exceptions.gateway import GatewayOfflineError
@@ -97,7 +97,7 @@ async def async_remove_config_entry_device(
return True
def configure_mydevolo(conf: dict[str, Any] | MappingProxyType[str, Any]) -> Mydevolo:
def configure_mydevolo(conf: Mapping[str, Any]) -> Mydevolo:
"""Configure mydevolo."""
mydevolo = Mydevolo()
mydevolo.user = conf[CONF_USERNAME]

View File

@@ -138,7 +138,7 @@ async def async_setup_entry(
SENSOR_TYPES[CONNECTED_PLC_DEVICES],
)
)
network = await device.plcnet.async_get_network_overview()
network: LogicalNetwork = coordinators[CONNECTED_PLC_DEVICES].data
peers = [
peer.mac_address for peer in network.devices if peer.topology == REMOTE
]

View File

@@ -2,7 +2,7 @@
"config": {
"step": {
"user": {
"title": "Set up the Dialogflow Webhook",
"title": "Set up the Dialogflow webhook",
"description": "Are you sure you want to set up Dialogflow?"
}
},
@@ -12,7 +12,7 @@
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
},
"create_entry": {
"default": "To send events to Home Assistant, you will need to set up [webhook integration of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details."
"default": "To send events to Home Assistant, you will need to set up the [webhook service of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details."
}
}
}

View File

@@ -2,8 +2,7 @@
from __future__ import annotations
from collections.abc import Callable
from types import MappingProxyType
from collections.abc import Callable, Mapping
from typing import Any
from dynalite_devices_lib.dynalite_devices import (
@@ -50,7 +49,7 @@ class DynaliteBridge:
LOGGER.debug("Setting up bridge - host %s", self.host)
return await self.dynalite_devices.async_setup()
def reload_config(self, config: MappingProxyType[str, Any]) -> None:
def reload_config(self, config: Mapping[str, Any]) -> None:
"""Reconfigure a bridge when config changes."""
LOGGER.debug("Reloading bridge - host %s, config %s", self.host, config)
self.dynalite_devices.configure(convert_config(config))

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from types import MappingProxyType
from collections.abc import Mapping
from typing import Any
from dynalite_devices_lib import const as dyn_const
@@ -138,9 +138,7 @@ def convert_template(config: dict[str, Any]) -> dict[str, Any]:
return convert_with_map(config, my_map)
def convert_config(
config: dict[str, Any] | MappingProxyType[str, Any],
) -> dict[str, Any]:
def convert_config(config: Mapping[str, Any]) -> dict[str, Any]:
"""Convert a config dict by replacing component consts with library consts."""
my_map = {
CONF_NAME: dyn_const.CONF_NAME,

View File

@@ -2,8 +2,8 @@
from __future__ import annotations
from collections.abc import Mapping
import logging
from types import MappingProxyType
from typing import Any
from elevenlabs import AsyncElevenLabs
@@ -43,7 +43,7 @@ _LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
def to_voice_settings(options: MappingProxyType[str, Any]) -> VoiceSettings:
def to_voice_settings(options: Mapping[str, Any]) -> VoiceSettings:
"""Return voice settings."""
return VoiceSettings(
stability=options.get(CONF_STABILITY, DEFAULT_STABILITY),

View File

@@ -5,7 +5,6 @@ from __future__ import annotations
import asyncio
import logging
import re
from types import MappingProxyType
from typing import Any
from elkm1_lib.elements import Element
@@ -235,7 +234,7 @@ def _async_find_matching_config_entry(
async def async_setup_entry(hass: HomeAssistant, entry: ElkM1ConfigEntry) -> bool:
"""Set up Elk-M1 Control from a config entry."""
conf: MappingProxyType[str, Any] = entry.data
conf = entry.data
host = hostname_from_url(entry.data[CONF_HOST])

View File

@@ -293,9 +293,9 @@ async def ws_get_fossil_energy_consumption(
if statistics_id not in statistic_ids:
continue
for period in stat:
if period["change"] is None:
if (change := period.get("change")) is None:
continue
result[period["start"]] += period["change"]
result[period["start"]] += change
return {key: result[key] for key in sorted(result)}

View File

@@ -9,12 +9,14 @@ import logging
from typing import Any
from pyenphase import Envoy, EnvoyError, EnvoyTokenAuth
from pyenphase.models.home import EnvoyInterfaceInformation
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.event import async_call_later, async_track_time_interval
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
@@ -26,7 +28,7 @@ TOKEN_REFRESH_CHECK_INTERVAL = timedelta(days=1)
STALE_TOKEN_THRESHOLD = timedelta(days=30).total_seconds()
NOTIFICATION_ID = "enphase_envoy_notification"
FIRMWARE_REFRESH_INTERVAL = timedelta(hours=4)
MAC_VERIFICATION_DELAY = timedelta(seconds=34)
_LOGGER = logging.getLogger(__name__)
@@ -39,6 +41,7 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
envoy_serial_number: str
envoy_firmware: str
config_entry: EnphaseConfigEntry
interface: EnvoyInterfaceInformation | None
def __init__(
self, hass: HomeAssistant, envoy: Envoy, entry: EnphaseConfigEntry
@@ -50,8 +53,10 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
self.password = entry_data[CONF_PASSWORD]
self._setup_complete = False
self.envoy_firmware = ""
self.interface = None
self._cancel_token_refresh: CALLBACK_TYPE | None = None
self._cancel_firmware_refresh: CALLBACK_TYPE | None = None
self._cancel_mac_verification: CALLBACK_TYPE | None = None
super().__init__(
hass,
_LOGGER,
@@ -121,6 +126,66 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
self.hass.config_entries.async_reload(self.config_entry.entry_id)
)
def _schedule_mac_verification(
self, delay: timedelta = MAC_VERIFICATION_DELAY
) -> None:
"""Schedule one time job to verify envoy mac address."""
self.async_cancel_mac_verification()
self._cancel_mac_verification = async_call_later(
self.hass,
delay,
self._async_verify_mac,
)
@callback
def _async_verify_mac(self, now: datetime.datetime) -> None:
"""Verify Envoy active interface mac address in background."""
self.hass.async_create_background_task(
self._async_fetch_and_compare_mac(), "{name} verify envoy mac address"
)
async def _async_fetch_and_compare_mac(self) -> None:
"""Get Envoy interface information and update mac in device connections."""
interface: (
EnvoyInterfaceInformation | None
) = await self.envoy.interface_settings()
if interface is None:
_LOGGER.debug("%s: interface information returned None", self.name)
return
# remember interface information so diagnostics can include in report
self.interface = interface
# Add to or update device registry connections as needed
device_registry = dr.async_get(self.hass)
envoy_device = device_registry.async_get_device(
identifiers={
(
DOMAIN,
self.envoy_serial_number,
)
}
)
if envoy_device is None:
_LOGGER.error(
"No envoy device found in device registry: %s %s",
DOMAIN,
self.envoy_serial_number,
)
return
connection = (dr.CONNECTION_NETWORK_MAC, interface.mac)
if connection in envoy_device.connections:
_LOGGER.debug(
"connection verified as existing: %s in %s", connection, self.name
)
return
device_registry.async_update_device(
device_id=envoy_device.id,
new_connections={connection},
)
_LOGGER.debug("added connection: %s to %s", connection, self.name)
@callback
def _async_mark_setup_complete(self) -> None:
"""Mark setup as complete and setup firmware checks and token refresh if needed."""
@@ -132,6 +197,7 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
FIRMWARE_REFRESH_INTERVAL,
cancel_on_shutdown=True,
)
self._schedule_mac_verification()
self.async_cancel_token_refresh()
if not isinstance(self.envoy.auth, EnvoyTokenAuth):
return
@@ -252,3 +318,10 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
if self._cancel_firmware_refresh:
self._cancel_firmware_refresh()
self._cancel_firmware_refresh = None
@callback
def async_cancel_mac_verification(self) -> None:
"""Cancel mac verification."""
if self._cancel_mac_verification:
self._cancel_mac_verification()
self._cancel_mac_verification = None

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
import copy
from datetime import datetime
from typing import TYPE_CHECKING, Any
from attr import asdict
@@ -63,6 +64,7 @@ async def _get_fixture_collection(envoy: Envoy, serial: str) -> dict[str, Any]:
"/ivp/ensemble/generator",
"/ivp/meters",
"/ivp/meters/readings",
"/home,",
]
for end_point in end_points:
@@ -146,11 +148,25 @@ async def async_get_config_entry_diagnostics(
"inverters": envoy_data.inverters,
"tariff": envoy_data.tariff,
}
# Add Envoy active interface information to report
active_interface: dict[str, Any] = {}
if coordinator.interface:
active_interface = {
"name": (interface := coordinator.interface).primary_interface,
"interface type": interface.interface_type,
"mac": interface.mac,
"uses dhcp": interface.dhcp,
"firmware build date": datetime.fromtimestamp(
interface.software_build_epoch
).strftime("%Y-%m-%d %H:%M:%S"),
"envoy timezone": interface.timezone,
}
envoy_properties: dict[str, Any] = {
"envoy_firmware": envoy.firmware,
"part_number": envoy.part_number,
"envoy_model": envoy.envoy_model,
"active interface": active_interface,
"supported_features": [feature.name for feature in envoy.supported_features],
"phase_mode": envoy.phase_mode,
"phase_count": envoy.phase_count,

View File

@@ -6,7 +6,8 @@
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"requirements": ["pyenphase==1.25.5"],
"quality_scale": "platinum",
"requirements": ["pyenphase==1.26.0"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."

View File

@@ -1,31 +1,19 @@
rules:
# Bronze
action-setup:
status: done
status: exempt
comment: only actions implemented are platform native ones.
appropriate-polling:
status: done
comment: fixed 1 minute cycle based on Enphase Envoy device characteristics
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy/#actions
docs-high-level-description:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy
docs-installation-instructions:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#prerequisites
docs-removal-instructions:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#removing-the-integration
entity-event-setup:
status: done
comment: no events used.
docs-actions: done
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
runtime-data: done
@@ -34,24 +22,14 @@ rules:
unique-config-entry: done
# Silver
action-exceptions:
status: todo
comment: |
needs to raise appropriate error when exception occurs.
Pending https://github.com/pyenphase/pyenphase/pull/194
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#configuration
docs-installation-parameters:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#required-manual-input
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates:
status: done
comment: pending https://github.com/home-assistant/core/pull/132373
parallel-updates: done
reauthentication-flow: done
test-coverage: done
@@ -60,22 +38,14 @@ rules:
diagnostics: done
discovery-update-info: done
discovery: done
docs-data-update:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#data-updates
docs-examples:
status: todo
comment: add blue-print examples, if any
docs-known-limitations: todo
docs-supported-devices:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#supported-devices
docs-supported-functions: todo
docs-troubleshooting:
status: done
comment: https://www.home-assistant.io/integrations/enphase_envoy#troubleshooting
docs-use-cases: todo
dynamic-devices: todo
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: done
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
@@ -86,7 +56,7 @@ rules:
repair-issues:
status: exempt
comment: no general issues or repair.py
stale-devices: todo
stale-devices: done
# Platinum
async-dependency: done

View File

@@ -128,7 +128,7 @@
"storage_mode": {
"name": "Storage mode",
"state": {
"self_consumption": "Self consumption",
"self_consumption": "Self-consumption",
"backup": "Full backup",
"savings": "Savings mode"
}
@@ -393,7 +393,7 @@
},
"exceptions": {
"unexpected_device": {
"message": "Unexpected Envoy serial-number found at {host}; expected {expected_serial}, found {actual_serial}"
"message": "Unexpected Envoy serial number found at {host}; expected {expected_serial}, found {actual_serial}"
},
"authentication_error": {
"message": "Envoy authentication failure on {host}: {args}"

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
"iot_class": "cloud_polling",
"loggers": ["env_canada"],
"requirements": ["env-canada==0.10.1"]
"requirements": ["env-canada==0.10.2"]
}

View File

@@ -86,7 +86,7 @@
"name": "AQHI"
},
"advisories": {
"name": "Advisory"
"name": "Advisories"
},
"endings": {
"name": "Endings"

View File

@@ -94,6 +94,7 @@ class EphEmberThermostat(ClimateEntity):
self._ember = ember
self._zone_name = zone_name(zone)
self._zone = zone
self._attr_unique_id = zone["zoneid"]
# hot water = true, is immersive device without target temperature control.
self._hot_water = zone_is_hotwater(zone)

View File

@@ -22,5 +22,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["eq3btsmart"],
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.13.1"]
"requirements": ["eq3btsmart==1.4.1", "bleak-esphome==2.14.0"]
}

View File

@@ -73,7 +73,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ESPHomeConfigEntry) -> b
async def async_unload_entry(hass: HomeAssistant, entry: ESPHomeConfigEntry) -> bool:
"""Unload an esphome config entry."""
entry_data = await cleanup_instance(hass, entry)
entry_data = await cleanup_instance(entry)
return await hass.config_entries.async_unload_platforms(
entry, entry_data.loaded_platforms
)

View File

@@ -50,7 +50,7 @@ _ESPHOME_ACP_STATE_TO_HASS_STATE: EsphomeEnumMapper[
class EspHomeACPFeatures(APIIntEnum):
"""ESPHome AlarmCintolPanel feature numbers."""
"""ESPHome AlarmControlPanel feature numbers."""
ARM_HOME = 1
ARM_AWAY = 2

View File

@@ -35,15 +35,14 @@ from homeassistant.components.intent import (
async_register_timer_handler,
)
from homeassistant.components.media_player import async_process_play_media_url
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .entity import EsphomeAssistEntity
from .entry_data import ESPHomeConfigEntry, RuntimeEntryData
from .entity import EsphomeAssistEntity, convert_api_error_ha_error
from .entry_data import ESPHomeConfigEntry
from .enum_mapper import EsphomeEnumMapper
from .ffmpeg_proxy import async_create_proxy_url
@@ -97,7 +96,7 @@ async def async_setup_entry(
if entry_data.device_info.voice_assistant_feature_flags_compat(
entry_data.api_version
):
async_add_entities([EsphomeAssistSatellite(entry, entry_data)])
async_add_entities([EsphomeAssistSatellite(entry)])
class EsphomeAssistSatellite(
@@ -109,17 +108,12 @@ class EsphomeAssistSatellite(
key="assist_satellite", translation_key="assist_satellite"
)
def __init__(
self,
config_entry: ConfigEntry,
entry_data: RuntimeEntryData,
) -> None:
def __init__(self, entry: ESPHomeConfigEntry) -> None:
"""Initialize satellite."""
super().__init__(entry_data)
super().__init__(entry.runtime_data)
self.config_entry = config_entry
self.entry_data = entry_data
self.cli = self.entry_data.client
self.config_entry = entry
self.cli = self._entry_data.client
self._is_running: bool = True
self._pipeline_task: asyncio.Task | None = None
@@ -135,23 +129,23 @@ class EsphomeAssistSatellite(
@property
def pipeline_entity_id(self) -> str | None:
"""Return the entity ID of the pipeline to use for the next conversation."""
assert self.entry_data.device_info is not None
assert self._entry_data.device_info is not None
ent_reg = er.async_get(self.hass)
return ent_reg.async_get_entity_id(
Platform.SELECT,
DOMAIN,
f"{self.entry_data.device_info.mac_address}-pipeline",
f"{self._entry_data.device_info.mac_address}-pipeline",
)
@property
def vad_sensitivity_entity_id(self) -> str | None:
"""Return the entity ID of the VAD sensitivity to use for the next conversation."""
assert self.entry_data.device_info is not None
assert self._entry_data.device_info is not None
ent_reg = er.async_get(self.hass)
return ent_reg.async_get_entity_id(
Platform.SELECT,
DOMAIN,
f"{self.entry_data.device_info.mac_address}-vad_sensitivity",
f"{self._entry_data.device_info.mac_address}-vad_sensitivity",
)
@callback
@@ -197,16 +191,16 @@ class EsphomeAssistSatellite(
_LOGGER.debug("Received satellite configuration: %s", self._satellite_config)
# Inform listeners that config has been updated
self.entry_data.async_assist_satellite_config_updated(self._satellite_config)
self._entry_data.async_assist_satellite_config_updated(self._satellite_config)
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_added_to_hass()
assert self.entry_data.device_info is not None
assert self._entry_data.device_info is not None
feature_flags = (
self.entry_data.device_info.voice_assistant_feature_flags_compat(
self.entry_data.api_version
self._entry_data.device_info.voice_assistant_feature_flags_compat(
self._entry_data.api_version
)
)
if feature_flags & VoiceAssistantFeature.API_AUDIO:
@@ -262,7 +256,7 @@ class EsphomeAssistSatellite(
# Update wake word select when config is updated
self.async_on_remove(
self.entry_data.async_register_assist_satellite_set_wake_word_callback(
self._entry_data.async_register_assist_satellite_set_wake_word_callback(
self.async_set_wake_word
)
)
@@ -284,7 +278,7 @@ class EsphomeAssistSatellite(
data_to_send: dict[str, Any] = {}
if event_type == VoiceAssistantEventType.VOICE_ASSISTANT_STT_START:
self.entry_data.async_set_assist_pipeline_state(True)
self._entry_data.async_set_assist_pipeline_state(True)
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_STT_END:
assert event.data is not None
data_to_send = {"text": event.data["stt_output"]["text"]}
@@ -306,10 +300,10 @@ class EsphomeAssistSatellite(
url = async_process_play_media_url(self.hass, path)
data_to_send = {"url": url}
assert self.entry_data.device_info is not None
assert self._entry_data.device_info is not None
feature_flags = (
self.entry_data.device_info.voice_assistant_feature_flags_compat(
self.entry_data.api_version
self._entry_data.device_info.voice_assistant_feature_flags_compat(
self._entry_data.api_version
)
)
if feature_flags & VoiceAssistantFeature.SPEAKER and (
@@ -336,13 +330,20 @@ class EsphomeAssistSatellite(
"code": event.data["code"],
"message": event.data["message"],
}
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_RUN_START:
assert event.data is not None
if tts_output := event.data["tts_output"]:
path = tts_output["url"]
url = async_process_play_media_url(self.hass, path)
data_to_send = {"url": url}
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_RUN_END:
if self._tts_streaming_task is None:
# No TTS
self.entry_data.async_set_assist_pipeline_state(False)
self._entry_data.async_set_assist_pipeline_state(False)
self.cli.send_voice_assistant_event(event_type, data_to_send)
@convert_api_error_ha_error
async def async_announce(
self, announcement: assist_satellite.AssistSatelliteAnnouncement
) -> None:
@@ -352,6 +353,7 @@ class EsphomeAssistSatellite(
"""
await self._do_announce(announcement, run_pipeline_after=False)
@convert_api_error_ha_error
async def async_start_conversation(
self, start_announcement: assist_satellite.AssistSatelliteAnnouncement
) -> None:
@@ -379,7 +381,7 @@ class EsphomeAssistSatellite(
# Route media through the proxy
format_to_use: MediaPlayerSupportedFormat | None = None
for supported_format in chain(
*self.entry_data.media_player_formats.values()
*self._entry_data.media_player_formats.values()
):
if supported_format.purpose == MediaPlayerFormatPurpose.ANNOUNCEMENT:
format_to_use = supported_format
@@ -437,10 +439,10 @@ class EsphomeAssistSatellite(
# API or UDP output audio
port: int = 0
assert self.entry_data.device_info is not None
assert self._entry_data.device_info is not None
feature_flags = (
self.entry_data.device_info.voice_assistant_feature_flags_compat(
self.entry_data.api_version
self._entry_data.device_info.voice_assistant_feature_flags_compat(
self._entry_data.api_version
)
)
if (feature_flags & VoiceAssistantFeature.SPEAKER) and not (
@@ -541,7 +543,7 @@ class EsphomeAssistSatellite(
def _update_tts_format(self) -> None:
"""Update the TTS format from the first media player."""
for supported_format in chain(*self.entry_data.media_player_formats.values()):
for supported_format in chain(*self._entry_data.media_player_formats.values()):
# Find first announcement format
if supported_format.purpose == MediaPlayerFormatPurpose.ANNOUNCEMENT:
self._attr_tts_options = {
@@ -627,7 +629,7 @@ class EsphomeAssistSatellite(
# State change
self.tts_response_finished()
self.entry_data.async_set_assist_pipeline_state(False)
self._entry_data.async_set_assist_pipeline_state(False)
async def _wrap_audio_stream(self) -> AsyncIterable[bytes]:
"""Yield audio chunks from the queue until None."""

View File

@@ -2,50 +2,22 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from functools import partial
from aioesphomeapi import BinarySensorInfo, BinarySensorState, EntityInfo
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.core import callback
from homeassistant.util.enum import try_parse_enum
from .const import DOMAIN
from .entity import EsphomeAssistEntity, EsphomeEntity, platform_async_setup_entry
from .entry_data import ESPHomeConfigEntry
from .entity import EsphomeEntity, platform_async_setup_entry
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
entry: ESPHomeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up ESPHome binary sensors based on a config entry."""
await platform_async_setup_entry(
hass,
entry,
async_add_entities,
info_type=BinarySensorInfo,
entity_type=EsphomeBinarySensor,
state_type=BinarySensorState,
)
entry_data = entry.runtime_data
assert entry_data.device_info is not None
if entry_data.device_info.voice_assistant_feature_flags_compat(
entry_data.api_version
):
async_add_entities([EsphomeAssistInProgressBinarySensor(entry_data)])
class EsphomeBinarySensor(
EsphomeEntity[BinarySensorInfo, BinarySensorState], BinarySensorEntity
):
@@ -76,50 +48,9 @@ class EsphomeBinarySensor(
return self._static_info.is_status_binary_sensor or super().available
class EsphomeAssistInProgressBinarySensor(EsphomeAssistEntity, BinarySensorEntity):
"""A binary sensor implementation for ESPHome for use with assist_pipeline."""
entity_description = BinarySensorEntityDescription(
entity_registry_enabled_default=False,
key="assist_in_progress",
translation_key="assist_in_progress",
)
async def async_added_to_hass(self) -> None:
"""Create issue."""
await super().async_added_to_hass()
if TYPE_CHECKING:
assert self.registry_entry is not None
ir.async_create_issue(
self.hass,
DOMAIN,
f"assist_in_progress_deprecated_{self.registry_entry.id}",
breaks_in_ha_version="2025.4",
data={
"entity_id": self.entity_id,
"entity_uuid": self.registry_entry.id,
"integration_name": "ESPHome",
},
is_fixable=True,
severity=ir.IssueSeverity.WARNING,
translation_key="assist_in_progress_deprecated",
translation_placeholders={
"integration_name": "ESPHome",
},
)
async def async_will_remove_from_hass(self) -> None:
"""Remove issue."""
await super().async_will_remove_from_hass()
if TYPE_CHECKING:
assert self.registry_entry is not None
ir.async_delete_issue(
self.hass,
DOMAIN,
f"assist_in_progress_deprecated_{self.registry_entry.id}",
)
@property
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
return self._entry_data.assist_pipeline_state
async_setup_entry = partial(
platform_async_setup_entry,
info_type=BinarySensorInfo,
entity_type=EsphomeBinarySensor,
state_type=BinarySensorState,
)

View File

@@ -180,13 +180,13 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
def _get_precision(self) -> float:
"""Return the precision of the climate device."""
precicions = [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
precisions = [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
static_info = self._static_info
if static_info.visual_current_temperature_step != 0:
step = static_info.visual_current_temperature_step
else:
step = static_info.visual_target_temperature_step
for prec in precicions:
for prec in precisions:
if step >= prec:
return prec
# Fall back to highest precision, tenths

View File

@@ -177,7 +177,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
)
async def async_step_reconfigure(
self, entry_data: Mapping[str, Any]
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by a reconfig request."""
self._reconfig_entry = self._get_reconfigure_entry()
@@ -323,7 +323,9 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
):
return
assert conflict_entry.unique_id is not None
if updates:
if self.source == SOURCE_RECONFIGURE:
error = "reconfigure_already_configured"
elif updates:
error = "already_configured_updates"
else:
error = "already_configured_detailed"
@@ -662,10 +664,12 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
return ERROR_REQUIRES_ENCRYPTION_KEY
except InvalidEncryptionKeyAPIError as ex:
if ex.received_name:
device_name_changed = self._device_name != ex.received_name
self._device_name = ex.received_name
if ex.received_mac:
self._device_mac = format_mac(ex.received_mac)
self._name = ex.received_name
if not self._name or device_name_changed:
self._name = ex.received_name
return ERROR_INVALID_ENCRYPTION_KEY
except ResolveAPIError:
return "resolve_error"

View File

@@ -5,43 +5,38 @@ from __future__ import annotations
from datetime import timedelta
import logging
import aiohttp
from awesomeversion import AwesomeVersion
from esphome_dashboard_api import ConfiguredDevice, ESPHomeDashboardAPI
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
MIN_VERSION_SUPPORTS_UPDATE = AwesomeVersion("2023.1.0")
REFRESH_INTERVAL = timedelta(minutes=5)
class ESPHomeDashboardCoordinator(DataUpdateCoordinator[dict[str, ConfiguredDevice]]):
"""Class to interact with the ESPHome dashboard."""
def __init__(
self,
hass: HomeAssistant,
addon_slug: str,
url: str,
session: aiohttp.ClientSession,
) -> None:
"""Initialize."""
def __init__(self, hass: HomeAssistant, addon_slug: str, url: str) -> None:
"""Initialize the dashboard coordinator."""
super().__init__(
hass,
_LOGGER,
config_entry=None,
name="ESPHome Dashboard",
update_interval=timedelta(minutes=5),
update_interval=REFRESH_INTERVAL,
always_update=False,
)
self.addon_slug = addon_slug
self.url = url
self.api = ESPHomeDashboardAPI(url, session)
self.api = ESPHomeDashboardAPI(url, async_get_clientsession(hass))
self.supports_update: bool | None = None
async def _async_update_data(self) -> dict:
async def _async_update_data(self) -> dict[str, ConfiguredDevice]:
"""Fetch device data."""
devices = await self.api.get_devices()
configured_devices = devices["configured"]

View File

@@ -9,7 +9,6 @@ from typing import Any
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.singleton import singleton
from homeassistant.helpers.storage import Store
@@ -104,9 +103,7 @@ class ESPHomeDashboardManager:
self._cancel_shutdown = None
self._current_dashboard = None
dashboard = ESPHomeDashboardCoordinator(
hass, addon_slug, url, async_get_clientsession(hass)
)
dashboard = ESPHomeDashboardCoordinator(hass, addon_slug, url)
await dashboard.async_request_refresh()
self._current_dashboard = dashboard

View File

@@ -17,15 +17,12 @@ STORAGE_VERSION = 1
@dataclass(slots=True)
class DomainData:
"""Define a class that stores global esphome data in hass.data[DOMAIN]."""
"""Define a class that stores global esphome data."""
_stores: dict[str, ESPHomeStorage] = field(default_factory=dict)
def get_entry_data(self, entry: ESPHomeConfigEntry) -> RuntimeEntryData:
"""Return the runtime entry data associated with this config entry.
Raises KeyError if the entry isn't loaded yet.
"""
"""Return the runtime entry data associated with this config entry."""
return entry.runtime_data
def get_or_create_store(

View File

@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Concatenate, Generic, TypeVar, cast
from aioesphomeapi import (
APIConnectionError,
DeviceInfo as EsphomeDeviceInfo,
EntityCategory as EsphomeEntityCategory,
EntityInfo,
EntityState,
@@ -155,7 +156,7 @@ def esphome_float_state_property[_EntityT: EsphomeEntity[Any, Any]](
return _wrapper
def convert_api_error_ha_error[**_P, _R, _EntityT: EsphomeEntity[Any, Any]](
def convert_api_error_ha_error[**_P, _R, _EntityT: EsphomeBaseEntity](
func: Callable[Concatenate[_EntityT, _P], Awaitable[None]],
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
"""Decorate ESPHome command calls that send commands/make changes to the device.
@@ -194,15 +195,21 @@ ENTITY_CATEGORIES: EsphomeEnumMapper[EsphomeEntityCategory, EntityCategory | Non
)
class EsphomeEntity(Entity, Generic[_InfoT, _StateT]):
class EsphomeBaseEntity(Entity):
"""Define a base esphome entity."""
_attr_should_poll = False
_attr_has_entity_name = True
_attr_should_poll = False
_device_info: EsphomeDeviceInfo
device_entry: dr.DeviceEntry
class EsphomeEntity(EsphomeBaseEntity, Generic[_InfoT, _StateT]):
"""Define an esphome entity."""
_static_info: _InfoT
_state: _StateT
_has_state: bool
device_entry: dr.DeviceEntry
def __init__(
self,
@@ -325,15 +332,12 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]):
self.async_write_ha_state()
class EsphomeAssistEntity(Entity):
class EsphomeAssistEntity(EsphomeBaseEntity):
"""Define a base entity for Assist Pipeline entities."""
_attr_has_entity_name = True
_attr_should_poll = False
def __init__(self, entry_data: RuntimeEntryData) -> None:
"""Initialize the binary sensor."""
self._entry_data: RuntimeEntryData = entry_data
self._entry_data = entry_data
assert entry_data.device_info is not None
device_info = entry_data.device_info
self._device_info = device_info

View File

@@ -106,7 +106,7 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
@property
@esphome_state_property
def is_on(self) -> bool | None:
def is_on(self) -> bool:
"""Return true if the entity is on."""
return self._state.state
@@ -126,7 +126,7 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
@property
@esphome_state_property
def oscillating(self) -> bool | None:
def oscillating(self) -> bool:
"""Return the oscillation state."""
return self._state.oscillating
@@ -138,7 +138,7 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
@property
@esphome_state_property
def preset_mode(self) -> str | None:
def preset_mode(self) -> str:
"""Return the current fan preset mode."""
return self._state.preset_mode

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
from functools import lru_cache, partial
from operator import methodcaller
from typing import TYPE_CHECKING, Any, cast
from aioesphomeapi import (
@@ -108,7 +109,7 @@ def _mired_to_kelvin(mired_temperature: float) -> int:
def _color_mode_to_ha(mode: int) -> str:
"""Convert an esphome color mode to a HA color mode constant.
Choses the color mode that best matches the feature-set.
Choose the color mode that best matches the feature-set.
"""
candidates = []
for ha_mode, cap_lists in _COLOR_MODE_MAPPING.items():
@@ -148,7 +149,7 @@ def _least_complex_color_mode(color_modes: tuple[int, ...]) -> int:
# popcount with bin() function because it appears
# to be the best way: https://stackoverflow.com/a/9831671
color_modes_list = list(color_modes)
color_modes_list.sort(key=lambda mode: (mode).bit_count())
color_modes_list.sort(key=methodcaller("bit_count"))
return color_modes_list[0]
@@ -160,7 +161,7 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
@property
@esphome_state_property
def is_on(self) -> bool | None:
def is_on(self) -> bool:
"""Return true if the light is on."""
return self._state.state
@@ -292,13 +293,13 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
@property
@esphome_state_property
def brightness(self) -> int | None:
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return round(self._state.brightness * 255)
@property
@esphome_state_property
def color_mode(self) -> str | None:
def color_mode(self) -> str:
"""Return the color mode of the light."""
if not self._supports_color_mode:
supported_color_modes = self.supported_color_modes
@@ -310,7 +311,7 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
@property
@esphome_state_property
def rgb_color(self) -> tuple[int, int, int] | None:
def rgb_color(self) -> tuple[int, int, int]:
"""Return the rgb color value [int, int, int]."""
state = self._state
if not self._supports_color_mode:
@@ -328,7 +329,7 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
@property
@esphome_state_property
def rgbw_color(self) -> tuple[int, int, int, int] | None:
def rgbw_color(self) -> tuple[int, int, int, int]:
"""Return the rgbw color value [int, int, int, int]."""
white = round(self._state.white * 255)
rgb = cast("tuple[int, int, int]", self.rgb_color)
@@ -336,7 +337,7 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
@property
@esphome_state_property
def rgbww_color(self) -> tuple[int, int, int, int, int] | None:
def rgbww_color(self) -> tuple[int, int, int, int, int]:
"""Return the rgbww color value [int, int, int, int, int]."""
state = self._state
rgb = cast("tuple[int, int, int]", self.rgb_color)
@@ -372,7 +373,7 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity):
@property
@esphome_state_property
def effect(self) -> str | None:
def effect(self) -> str:
"""Return the current effect."""
return self._state.effect

View File

@@ -40,25 +40,25 @@ class EsphomeLock(EsphomeEntity[LockInfo, LockEntityState], LockEntity):
@property
@esphome_state_property
def is_locked(self) -> bool | None:
def is_locked(self) -> bool:
"""Return true if the lock is locked."""
return self._state.state is LockState.LOCKED
@property
@esphome_state_property
def is_locking(self) -> bool | None:
def is_locking(self) -> bool:
"""Return true if the lock is locking."""
return self._state.state is LockState.LOCKING
@property
@esphome_state_property
def is_unlocking(self) -> bool | None:
def is_unlocking(self) -> bool:
"""Return true if the lock is unlocking."""
return self._state.state is LockState.UNLOCKING
@property
@esphome_state_property
def is_jammed(self) -> bool | None:
def is_jammed(self) -> bool:
"""Return true if the lock is jammed (incomplete locking)."""
return self._state.state is LockState.JAMMED

View File

@@ -49,6 +49,7 @@ from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
issue_registry as ir,
template,
)
from homeassistant.helpers.device_registry import format_mac
@@ -215,7 +216,7 @@ class ESPHomeManager:
async def on_stop(self, event: Event) -> None:
"""Cleanup the socket client on HA close."""
await cleanup_instance(self.hass, self.entry)
await cleanup_instance(self.entry)
@property
def services_issue(self) -> str:
@@ -376,7 +377,7 @@ class ESPHomeManager:
async def on_connect(self) -> None:
"""Subscribe to states and list entities on successful API login."""
try:
await self._on_connnect()
await self._on_connect()
except APIConnectionError as err:
_LOGGER.warning(
"Error getting setting up connection for %s: %s", self.host, err
@@ -412,7 +413,7 @@ class ESPHomeManager:
self._async_on_log, self._log_level
)
async def _on_connnect(self) -> None:
async def _on_connect(self) -> None:
"""Subscribe to states and list entities on successful API login."""
entry = self.entry
unique_id = entry.unique_id
@@ -654,6 +655,30 @@ class ESPHomeManager:
):
self._async_subscribe_logs(new_log_level)
@callback
def _async_cleanup(self) -> None:
"""Cleanup stale issues and entities."""
assert self.entry_data.device_info is not None
ent_reg = er.async_get(self.hass)
# Cleanup stale assist_in_progress entity and issue,
# Remove this after 2026.4
if not (
stale_entry_entity_id := ent_reg.async_get_entity_id(
DOMAIN,
Platform.BINARY_SENSOR,
f"{self.entry_data.device_info.mac_address}-assist_in_progress",
)
):
return
stale_entry = ent_reg.async_get(stale_entry_entity_id)
assert stale_entry is not None
ent_reg.async_remove(stale_entry_entity_id)
issue_reg = ir.async_get(self.hass)
if issue := issue_reg.async_get_issue(
DOMAIN, f"assist_in_progress_deprecated_{stale_entry.id}"
):
issue_reg.async_delete(DOMAIN, issue.issue_id)
async def async_start(self) -> None:
"""Start the esphome connection manager."""
hass = self.hass
@@ -696,6 +721,7 @@ class ESPHomeManager:
_setup_services(hass, entry_data, services)
if (device_info := entry_data.device_info) is not None:
self._async_cleanup()
if device_info.name:
reconnect_logic.name = device_info.name
if (
@@ -939,9 +965,7 @@ def _setup_services(
_async_register_service(hass, entry_data, device_info, service)
async def cleanup_instance(
hass: HomeAssistant, entry: ESPHomeConfigEntry
) -> RuntimeEntryData:
async def cleanup_instance(entry: ESPHomeConfigEntry) -> RuntimeEntryData:
"""Cleanup the esphome client if it exists."""
data = entry.runtime_data
data.async_on_disconnect()

View File

@@ -15,10 +15,11 @@
"iot_class": "local_push",
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==30.0.1",
"aioesphomeapi==30.1.0",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==2.13.1"
"bleak-esphome==2.14.0"
],
"zeroconf": ["_esphomelib._tcp.local."]
}

View File

@@ -96,7 +96,7 @@ class EsphomeMediaPlayer(
@property
@esphome_float_state_property
def volume_level(self) -> float | None:
def volume_level(self) -> float:
"""Volume level of the media player (0..1)."""
return self._state.volume

View File

@@ -0,0 +1,85 @@
rules:
# Bronze
action-setup:
status: exempt
comment: |
Since actions are defined per device, rather than per integration,
they are specific to the device's YAML configuration. Additionally,
ESPHome allows for user-defined actions, making it impossible to
set them up until the device is connected as they vary by device. For more
information, see: https://esphome.io/components/api.html#user-defined-actions
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
Since actions are defined per device, rather than per integration,
they are specific to the device's YAML configuration. Additionally,
ESPHome allows for user-defined actions, making it difficult to provide
standard documentation since these actions vary by device. For more
information, see: https://esphome.io/components/api.html#user-defined-actions
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup:
status: exempt
comment: |
ESPHome relies on sleepy devices and fast reconnect logic, so we
can't raise `ConfigEntryNotReady`. Instead, we need to utilize the
reconnect logic in `aioesphomeapi` to determine the right moment
to trigger the connection.
unique-config-entry: done
# Silver
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: done
reauthentication-flow: done
test-coverage: done
# Gold
devices: done
diagnostics: done
discovery-update-info: done
discovery: done
docs-data-update: done
docs-examples:
status: exempt
comment: |
Since ESPHome is a framework for creating custom devices, the
possibilities are virtually limitless. As a result, example
automations would likely only be relevant to the specific user
of the device and not generally useful to others.
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: done
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow: done
repair-issues: done
stale-devices: done
# Platinum
async-dependency: done
inject-websession: done
strict-typing: done

View File

@@ -7,9 +7,6 @@ from typing import cast
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.assist_pipeline.repair_flows import (
AssistInProgressDeprecatedRepairFlow,
)
from homeassistant.components.repairs import RepairsFlow
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import issue_registry as ir
@@ -99,8 +96,6 @@ async def async_create_fix_flow(
data: dict[str, str | int | float | None] | None,
) -> RepairsFlow:
"""Create flow."""
if issue_id.startswith("assist_in_progress_deprecated"):
return AssistInProgressDeprecatedRepairFlow(data)
if issue_id.startswith("device_conflict"):
return DeviceConflictRepair(data)
# If ESPHome adds confirm-only repairs in the future, this should be changed

View File

@@ -52,7 +52,7 @@ async def async_setup_entry(
[
EsphomeAssistPipelineSelect(hass, entry_data),
EsphomeVadSensitivitySelect(hass, entry_data),
EsphomeAssistSatelliteWakeWordSelect(hass, entry_data),
EsphomeAssistSatelliteWakeWordSelect(entry_data),
]
)
@@ -107,11 +107,10 @@ class EsphomeAssistSatelliteWakeWordSelect(
translation_key="wake_word",
entity_category=EntityCategory.CONFIG,
)
_attr_should_poll = False
_attr_current_option: str | None = None
_attr_options: list[str] = []
def __init__(self, hass: HomeAssistant, entry_data: RuntimeEntryData) -> None:
def __init__(self, entry_data: RuntimeEntryData) -> None:
"""Initialize a wake word selector."""
EsphomeAssistEntity.__init__(self, entry_data)

View File

@@ -4,6 +4,7 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_configured_detailed": "A device `{name}`, with MAC address `{mac}` is already configured as `{title}`.",
"already_configured_updates": "A device `{name}`, with MAC address `{mac}` is already configured as `{title}`; the existing configuration will be updated with the validated data.",
"reconfigure_already_configured": "A device `{name}` with MAC address `{mac}` is already configured as `{title}`. Reconfiguration was aborted because the new configuration appears to refer to a different device.",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"mdns_missing_mac": "Missing MAC address in mDNS properties.",
@@ -19,10 +20,11 @@
"reconfigure_unique_id_changed": "**Reconfiguration of `{name}` was aborted** because the address `{host}` points to a different device: `{unexpected_device_name}` (MAC: `{unexpected_mac}`) instead of the expected one (MAC: `{expected_mac}`)."
},
"error": {
"resolve_error": "Can't resolve address of the ESP. If this error persists, please set a static IP address",
"connection_error": "Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
"resolve_error": "Unable to resolve the address of the ESPHome device. If this issue continues, consider setting a static IP address.",
"connection_error": "Unable to connect to the ESPHome device. Make sure the devices YAML configuration includes an `api` section.",
"requires_encryption_key": "The ESPHome device requires an encryption key. Enter the key defined in the devices YAML configuration under `api -> encryption -> key`.",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_psk": "The transport encryption key is invalid. Please ensure it matches what you have in your configuration"
"invalid_psk": "The encryption key is invalid. Make sure it matches the value in the devices YAML configuration under `api -> encryption -> key`."
},
"step": {
"user": {
@@ -101,11 +103,6 @@
"name": "[%key:component::assist_satellite::entity_component::_::name%]"
}
},
"binary_sensor": {
"assist_in_progress": {
"name": "[%key:component::assist_pipeline::entity::binary_sensor::assist_in_progress::name%]"
}
},
"select": {
"pipeline": {
"name": "[%key:component::assist_pipeline::entity::select::pipeline::name%]",

View File

@@ -36,7 +36,7 @@ class EsphomeSwitch(EsphomeEntity[SwitchInfo, SwitchState], SwitchEntity):
@property
@esphome_state_property
def is_on(self) -> bool | None:
def is_on(self) -> bool:
"""Return true if the switch is on."""
return self._state.state

View File

@@ -70,7 +70,6 @@ async def async_setup_entry(
@callback
def _async_setup_update_entity() -> None:
"""Set up the update entity."""
nonlocal unsubs
assert dashboard is not None
# Keep listening until device is available
if not entry_data.available or not dashboard.last_update_success:
@@ -95,10 +94,12 @@ async def async_setup_entry(
_async_setup_update_entity()
return
unsubs = [
entry_data.async_subscribe_device_updated(_async_setup_update_entity),
dashboard.async_add_listener(_async_setup_update_entity),
]
unsubs.extend(
[
entry_data.async_subscribe_device_updated(_async_setup_update_entity),
dashboard.async_add_listener(_async_setup_update_entity),
]
)
class ESPHomeDashboardUpdateEntity(
@@ -109,7 +110,6 @@ class ESPHomeDashboardUpdateEntity(
_attr_has_entity_name = True
_attr_device_class = UpdateDeviceClass.FIRMWARE
_attr_title = "ESPHome"
_attr_name = "Firmware"
_attr_release_url = "https://esphome.io/changelog/"
_attr_entity_registry_enabled_default = False
@@ -242,7 +242,7 @@ class ESPHomeUpdateEntity(EsphomeEntity[UpdateInfo, UpdateState], UpdateEntity):
@property
@esphome_state_property
def installed_version(self) -> str | None:
def installed_version(self) -> str:
"""Return the installed version."""
return self._state.current_version
@@ -260,19 +260,19 @@ class ESPHomeUpdateEntity(EsphomeEntity[UpdateInfo, UpdateState], UpdateEntity):
@property
@esphome_state_property
def release_summary(self) -> str | None:
def release_summary(self) -> str:
"""Return the release summary."""
return self._state.release_summary
@property
@esphome_state_property
def release_url(self) -> str | None:
def release_url(self) -> str:
"""Return the release URL."""
return self._state.release_url
@property
@esphome_state_property
def title(self) -> str | None:
def title(self) -> str:
"""Return the title of the update."""
return self._state.title

View File

@@ -65,7 +65,7 @@ class EsphomeValve(EsphomeEntity[ValveInfo, ValveState], ValveEntity):
@property
@esphome_state_property
def current_valve_position(self) -> int | None:
def current_valve_position(self) -> int:
"""Return current position of valve. 0 is closed, 100 is open."""
return round(self._state.position * 100.0)

View File

@@ -72,7 +72,11 @@ async def get_hosts_list_if_supported(
supports_hosts: bool = True
fbx_devices: list[dict[str, Any]] = []
try:
fbx_devices = await fbx_api.lan.get_hosts_list() or []
fbx_interfaces = await fbx_api.lan.get_interfaces() or []
for interface in fbx_interfaces:
fbx_devices.extend(
await fbx_api.lan.get_hosts_list(interface["name"]) or []
)
except HttpRequestError as err:
if (
(matcher := re.search(r"Request failed \(APIResponse: (.+)\)", str(err)))

View File

@@ -2,13 +2,12 @@
from __future__ import annotations
from collections.abc import Callable, ValuesView
from collections.abc import Callable, Mapping, ValuesView
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from functools import partial
import logging
import re
from types import MappingProxyType
from typing import Any, TypedDict, cast
from fritzconnection import FritzConnection
@@ -187,7 +186,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
)
self._devices: dict[str, FritzDevice] = {}
self._options: MappingProxyType[str, Any] | None = None
self._options: Mapping[str, Any] | None = None
self._unique_id: str | None = None
self.connection: FritzConnection = None
self.fritz_guest_wifi: FritzGuestWLAN = None
@@ -213,9 +212,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
str, Callable[[FritzStatus, StateType], Any]
] = {}
async def async_setup(
self, options: MappingProxyType[str, Any] | None = None
) -> None:
async def async_setup(self, options: Mapping[str, Any] | None = None) -> None:
"""Wrap up FritzboxTools class setup."""
self._options = options
await self.hass.async_add_executor_job(self.setup)

View File

@@ -22,19 +22,14 @@ from .entity import FritzBoxDeviceEntity
from .model import FritzEntityDescriptionMixinBase
@dataclass(frozen=True)
class FritzEntityDescriptionMixinBinarySensor(FritzEntityDescriptionMixinBase):
"""BinarySensor description mixin for Fritz!Smarthome entities."""
is_on: Callable[[FritzhomeDevice], bool | None]
@dataclass(frozen=True)
@dataclass(frozen=True, kw_only=True)
class FritzBinarySensorEntityDescription(
BinarySensorEntityDescription, FritzEntityDescriptionMixinBinarySensor
BinarySensorEntityDescription, FritzEntityDescriptionMixinBase
):
"""Description for Fritz!Smarthome binary sensor entities."""
is_on: Callable[[FritzhomeDevice], bool | None]
BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = (
FritzBinarySensorEntityDescription(

View File

@@ -53,8 +53,11 @@ MAX_TEMPERATURE = 28
# special temperatures for on/off in Fritz!Box API (modified by pyfritzhome)
ON_API_TEMPERATURE = 127.0
OFF_API_TEMPERATURE = 126.5
ON_REPORT_SET_TEMPERATURE = 30.0
OFF_REPORT_SET_TEMPERATURE = 0.0
PRESET_API_HKR_STATE_MAPPING = {
PRESET_COMFORT: "comfort",
PRESET_BOOST: "on",
PRESET_ECO: "eco",
}
async def async_setup_entry(
@@ -128,29 +131,28 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
return self.data.actual_temperature # type: ignore [no-any-return]
@property
def target_temperature(self) -> float:
def target_temperature(self) -> float | None:
"""Return the temperature we try to reach."""
if self.data.target_temperature == ON_API_TEMPERATURE:
return ON_REPORT_SET_TEMPERATURE
if self.data.target_temperature == OFF_API_TEMPERATURE:
return OFF_REPORT_SET_TEMPERATURE
if self.data.target_temperature in [ON_API_TEMPERATURE, OFF_API_TEMPERATURE]:
return None
return self.data.target_temperature # type: ignore [no-any-return]
async def async_set_hkr_state(self, hkr_state: str) -> None:
"""Set the state of the climate."""
await self.hass.async_add_executor_job(self.data.set_hkr_state, hkr_state, True)
await self.coordinator.async_refresh()
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (hvac_mode := kwargs.get(ATTR_HVAC_MODE)) is HVACMode.OFF:
await self.async_set_hvac_mode(hvac_mode)
if kwargs.get(ATTR_HVAC_MODE) is HVACMode.OFF:
await self.async_set_hkr_state("off")
elif (target_temp := kwargs.get(ATTR_TEMPERATURE)) is not None:
if target_temp == OFF_API_TEMPERATURE:
target_temp = OFF_REPORT_SET_TEMPERATURE
elif target_temp == ON_API_TEMPERATURE:
target_temp = ON_REPORT_SET_TEMPERATURE
await self.hass.async_add_executor_job(
self.data.set_target_temperature, target_temp, True
)
await self.coordinator.async_refresh()
else:
return
await self.coordinator.async_refresh()
@property
def hvac_mode(self) -> HVACMode:
@@ -159,10 +161,7 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
return HVACMode.HEAT
if self.data.summer_active:
return HVACMode.OFF
if self.data.target_temperature in (
OFF_REPORT_SET_TEMPERATURE,
OFF_API_TEMPERATURE,
):
if self.data.target_temperature == OFF_API_TEMPERATURE:
return HVACMode.OFF
return HVACMode.HEAT
@@ -180,7 +179,7 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
)
return
if hvac_mode is HVACMode.OFF:
await self.async_set_temperature(temperature=OFF_REPORT_SET_TEMPERATURE)
await self.async_set_hkr_state("off")
else:
if value_scheduled_preset(self.data) == PRESET_ECO:
target_temp = self.data.eco_temperature
@@ -210,12 +209,7 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
translation_domain=DOMAIN,
translation_key="change_preset_while_active_mode",
)
if preset_mode == PRESET_COMFORT:
await self.async_set_temperature(temperature=self.data.comfort_temperature)
elif preset_mode == PRESET_ECO:
await self.async_set_temperature(temperature=self.data.eco_temperature)
elif preset_mode == PRESET_BOOST:
await self.async_set_temperature(temperature=ON_REPORT_SET_TEMPERATURE)
await self.async_set_hkr_state(PRESET_API_HKR_STATE_MAPPING[preset_mode])
@property
def extra_state_attributes(self) -> ClimateExtraAttributes:

View File

@@ -35,20 +35,14 @@ from .entity import FritzBoxDeviceEntity
from .model import FritzEntityDescriptionMixinBase
@dataclass(frozen=True)
class FritzEntityDescriptionMixinSensor(FritzEntityDescriptionMixinBase):
"""Sensor description mixin for Fritz!Smarthome entities."""
native_value: Callable[[FritzhomeDevice], StateType | datetime]
@dataclass(frozen=True)
@dataclass(frozen=True, kw_only=True)
class FritzSensorEntityDescription(
SensorEntityDescription, FritzEntityDescriptionMixinSensor
SensorEntityDescription, FritzEntityDescriptionMixinBase
):
"""Description for Fritz!Smarthome sensor entities."""
entity_category_fn: Callable[[FritzhomeDevice], EntityCategory | None] | None = None
native_value: Callable[[FritzhomeDevice], StateType | datetime]
def suitable_eco_temperature(device: FritzhomeDevice) -> bool:

View File

@@ -317,11 +317,11 @@
"state_message": {
"name": "State message",
"state": {
"fault": "[%key:common::state::fault%]",
"critical_fault": "Critical fault",
"up_and_running": "Up and running",
"keep_minimum_temperature": "Keep minimum temperature",
"legionella_protection": "Legionella protection",
"critical_fault": "Critical fault",
"fault": "Fault",
"boost_mode": "Boost mode"
}
},
@@ -362,7 +362,7 @@
"name": "Relative autonomy"
},
"relative_self_consumption": {
"name": "Relative self consumption"
"name": "Relative self-consumption"
},
"capacity_maximum": {
"name": "Maximum capacity"

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/garages_amsterdam",
"iot_class": "cloud_polling",
"requirements": ["odp-amsterdam==6.0.2"]
"requirements": ["odp-amsterdam==6.1.1"]
}

View File

@@ -2,8 +2,8 @@
"config": {
"step": {
"user": {
"title": "Set up the Geofency Webhook",
"description": "Are you sure you want to set up the Geofency Webhook?"
"title": "Set up the Geofency webhook",
"description": "Are you sure you want to set up the Geofency webhook?"
}
},
"abort": {

View File

@@ -0,0 +1,119 @@
rules:
# Bronze
config-flow:
status: todo
comment: Some fields missing data_description in the option flow.
brands: done
dependency-transparency:
status: todo
comment: |
This depends on the legacy (deprecated) oauth libraries for device
auth (no longer recommended auth). Google publishes to pypi using
an internal build system. We need to either revisit approach or
revisit our stance on this.
common-modules: done
has-entity-name: done
action-setup:
status: todo
comment: |
Actions are current setup in `async_setup_entry` and need to be moved
to `async_setup`.
appropriate-polling: done
test-before-configure: done
entity-event-setup:
status: exempt
comment: Integration does not subscribe to events.
unique-config-entry: done
entity-unique-id: done
docs-installation-instructions: done
docs-removal-instructions: todo
test-before-setup:
status: todo
comment: |
The integration does not test the connection in `async_setup_entry` but
instead does this in the calendar platform only, which can be improved.
docs-high-level-description: done
config-flow-test-coverage:
status: todo
comment: |
The config flow has 100% test coverage, however there are opportunities
to increase functionality such as checking for the specific contents
of a unique id assigned to a config entry.
docs-actions: done
runtime-data:
status: todo
comment: |
The integration stores config entry data in `hass.data` and should be
updated to use `runtime_data`.
# Silver
log-when-unavailable: done
config-entry-unloading: done
reauthentication-flow:
status: todo
comment: |
The integration supports reauthentication, however the config flow test
coverage can be improved on reauth corner cases.
action-exceptions: done
docs-installation-parameters: todo
integration-owner: done
parallel-updates: todo
test-coverage:
status: todo
comment: One module needs an additional line of coverage to be above the bar
docs-configuration-parameters: todo
entity-unavailable: done
# Gold
docs-examples: done
discovery-update-info:
status: exempt
comment: Google calendar does not support discovery
entity-device-class: todo
entity-translations: todo
docs-data-update: todo
entity-disabled-by-default: done
discovery:
status: exempt
comment: Google calendar does not support discovery
exception-translations: todo
devices: todo
docs-supported-devices: done
icon-translations:
status: exempt
comment: Google calendar does not have any icons
docs-known-limitations: todo
stale-devices:
status: exempt
comment: Google calendar does not have devices
docs-supported-functions: done
repair-issues:
status: todo
comment: There are some warnings/deprecations that should be repair issues
reconfiguration-flow:
status: exempt
comment: There is nothing to configure in the configuration flow
entity-category:
status: exempt
comment: The entities in google calendar do not support categories
dynamic-devices:
status: exempt
comment: Google calendar does not have devices
docs-troubleshooting: todo
diagnostics: todo
docs-use-cases: todo
# Platinum
async-dependency:
status: done
comment: |
The main client `gcal_sync` library is async. The primary authentication
used in config flow is handled by built in async OAuth code. The
integration still supports legacy OAuth credentials setup in the
configuration flow, which is no longer recommended or described in the
documentation for new users. This legacy config flow uses oauth2client
which is not natively async.
strict-typing:
status: todo
comment: Dependency oauth2client does not confirm to PEP 561
inject-websession: done

View File

@@ -208,7 +208,7 @@ class GoogleGenerativeAIOptionsFlow(OptionsFlow):
async def google_generative_ai_config_option_schema(
hass: HomeAssistant,
options: dict[str, Any] | MappingProxyType[str, Any],
options: Mapping[str, Any],
genai_client: genai.Client,
) -> dict:
"""Return a schema for Google Generative AI completion options."""

View File

@@ -16,7 +16,7 @@ RECOMMENDED_TOP_P = 0.95
CONF_TOP_K = "top_k"
RECOMMENDED_TOP_K = 64
CONF_MAX_TOKENS = "max_tokens"
RECOMMENDED_MAX_TOKENS = 150
RECOMMENDED_MAX_TOKENS = 1500
CONF_HARASSMENT_BLOCK_THRESHOLD = "harassment_block_threshold"
CONF_HATE_BLOCK_THRESHOLD = "hate_block_threshold"
CONF_SEXUAL_BLOCK_THRESHOLD = "sexual_block_threshold"

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