Compare commits

..

130 Commits

Author SHA1 Message Date
Abílio Costa
32308649a0 Merge branch 'dev' into whirlpool_sensor_door_remove 2025-05-05 16:50:34 +01:00
Luke Lashley
aa8dfa760d Bump Roborock Map Parser to 0.1.4 (#144260)
Bump to 0.1.4
2025-05-05 07:40:48 -07:00
tronikos
0043b18135 Use names instead of statistic IDs in the Opower repair issue (#144018)
* Use names instead of statistic IDs in the Opower repair issue

* target_ids
2025-05-05 08:36:58 -04:00
Luca De Petrillo
c14ddedfae Fix message corruption in picotts component (#141182) 2025-05-05 14:30:36 +02:00
Markus Jacobsen
a073a6b01e Fix hassfest expecting strings file for custom components (#135789)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-05-05 14:23:02 +02:00
epenet
0713ac4977 Cleanup invalid CONF_ID from samsungtv tests (#144252) 2025-05-05 13:47:07 +02:00
Allen Porter
3390dc0dbb Fix Office 365 calendars to be compatible with rfc5545 (#144230) 2025-05-05 13:13:08 +02:00
dependabot[bot]
445b38f25d Bump github/codeql-action from 3.28.16 to 3.28.17 (#144245)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.16 to 3.28.17.
- [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.16...v3.28.17)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.17
  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-05-05 12:51:19 +02:00
John Hillery
9e4a20c267 Bump nexia to 2.9.0 (#144153)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-05-05 11:40:37 +01:00
epenet
d88cd72d13 Move more SamsungTV test constants to fixture files (#144249)
* Add SSDP fixtures to SamsungTV

* Adjust

* Improve

* Improve
2025-05-05 11:58:24 +02:00
tronikos
66b2e06cd3 Fix Invalid statistic_id for Opower: National Grid (#144243)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-05-05 11:35:32 +02:00
Michael
58906008b9 Add last attempted automatic backup sensor (#144194)
add last_attempted_automatic_backup sensor
2025-05-05 10:39:06 +02:00
Ville Skyttä
aa062515b8 Remove unused huawei_lte YAML schemas, error out on YAML config (#144217) 2025-05-05 10:46:30 +03:00
Norbert Rittel
65da1e79b9 Change some strings to international English in fronius (#144244)
Change to international English in `fronius`
2025-05-05 10:39:23 +03:00
Brett Adams
41ecb24135 Set api type more specifically in Teslemetry (#144178)
* Set api type more specifically

* remove extra spacing

* Fix class after rebase

---------

Co-authored-by: Allen Porter <allen@thebends.org>
2025-05-04 21:54:00 -07:00
Brett Adams
e3b3c32751 Add valet switch to Teslemetry (#144167)
* Add valet switch

* Add snapshot
2025-05-05 06:28:01 +02:00
Allen Porter
e2a8137140 Bump ical to 9.2.0 (#144240) 2025-05-04 23:40:49 -04:00
Allen Porter
fa6a2f08ab Update remote calendar to do all event handling in an executor (#144232) 2025-05-04 23:07:02 -04:00
Allen Porter
68d62ab58e Update local calendar to process calendar events in the executor (#144233) 2025-05-04 23:06:27 -04:00
Allen Porter
c6b9a40234 Increase the local calendar update interval to avoid re-parsing the calendar state unnecessarily (#144234) 2025-05-04 23:05:33 -04:00
Luke Lashley
e0916fdd26 Change roborock to use home_data_v3 (#144238) 2025-05-04 20:02:32 -07:00
Luke Lashley
cad2d72ed9 Bump python-roborock to 2.18.2 (#144235) 2025-05-04 16:59:49 -07:00
Norbert Rittel
8eaddbf2b2 Replace "log-in" with "log in" in zwave_me (#144223) 2025-05-04 21:56:33 +03:00
Norbert Rittel
9b30f32cad Replace "Sign-in …" with "Sign in …" in ring (#144222)
The config flow titles in Home Assistant use verbs by default ("Set up …" / "Configure …" / "Select …" etc.

Therefore "Sign-in …" (noun) for the initial setup in `ring` is replaced with "Sign in …" (verb).
2025-05-04 21:12:01 +03:00
Norbert Rittel
c2a69bcb20 Improve user-facing strings of blink (#144219)
- treat "sign in" as verb for consistency, removing the hyphen
- fix sentence-casing of two strings
2025-05-04 20:36:57 +03:00
Norbert Rittel
2e7b60c3ca Fix spelling of "sign in" and "setup" in verisure (#144214)
- use "sign in" for the verb
- use "setup" for the noun
- fix sentence-casing of "Verification code"
2025-05-04 20:36:24 +03:00
Norbert Rittel
eca811d0d4 Fix sentence-casing in user-facing strings of tami4 (#144212)
Fix sentence-casing in user-facing string of `tami4`
2025-05-04 20:35:59 +03:00
Norbert Rittel
8e202bc202 Improve the user-facing strings of heos (#144218) 2025-05-04 12:13:53 -05:00
J. Nick Koston
429682cecd Remove unnecessary intermediate functions in entry_data for ESPHome (#144173) 2025-05-04 11:42:07 -05:00
Pete Sage
9cd2080de2 Avoid delaying HA startup in Rehlko (#144202) 2025-05-04 11:41:39 -05:00
Pete Sage
2960271b81 bump aiokem to 0.5.10 (#144203) 2025-05-04 11:15:01 -05:00
Paulus Schoutsen
8048d2bfb8 Fix intent TurnOn creating stack trace for buttons (#144205) 2025-05-04 09:00:40 -07:00
Norbert Rittel
490bb46a82 Make spelling of "Auto-charge" switch consistent in TechnoVE (#144206)
* Make spelling of "Auto-charge" switch consistent in TechnoVE

Also fix sentence-casing in "Charging enabled" switch.

* Update test_switch.ambr
2025-05-04 18:56:25 +03:00
Norbert Rittel
1199353204 Fix sentence-casing of "Phone number" in peco (#144208) 2025-05-04 18:55:58 +03:00
Oliver
2c368c79d1 Update denonavr to 1.1.0 (#144199) 2025-05-04 16:41:44 +02:00
Michael Hannon
095318114b Add Zimi Cloud Connect Integration (#129876)
* Give entry unique id with MAC, strings.json tweaks

* Update codeowners

* Add config_flow tests

* Update requirements

* Update homeassistant/components/zimi/__init__.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Store controller reference in entry.runtime_data instead of hass.data

* Add typing

* Removed hass data pop on unload.  (No longer needed when hass data moved for runtime_data)

* Refactor config_flow based on feedback from @zweckj with inline validation, simpler defaults, better description data

* Add Michael to codeowners

* Remove manual debug override in entity

* Populate via_device

* remove empty keys from manifest.json

* Refactor with DataUpdateCoordinator
Device Entities use existing push update method

* set via_device to match zcc identifier

* Changed logger to use debug level

* Define the zimi constants

* Move extraaneous code out from try

* Move __del__ to async_wil_remove_from_hass

* Use zcc device for name

* Print debug if mac mismatch
Add final exception if api is not ready after connect

* Re-work configuration flow:
1. Remove unused CONF_TIMEOUT, CONF_VERBOSITY and CONF_WATCHDOG
2. Move connect() logic out of ZimiCoordinator
3. Add fast connect check during ConfigFlow to check mac matches
4. Use zcc version 3.2.3 with default watchdog time value (and remove this from HA)

* Add error detail to mac mismatch

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/const.py

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

* Update homeassistant/components/zimi/coordinator.py

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

* Update homeassistant/components/zimi/coordinator.py

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

* Update homeassistant/components/zimi/light.py

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

* Remove coordinator and move setup to __init__

* Set name in _attr_name

* Use _light directly for status etc; Remove _state and _brightness; SImplify update()

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* No need to delete device, fix return

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

* Remove non-failing items from try
Abort duplicate configurations

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

* Move attr change to notify

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

* Update homeassistant/components/zimi/__init__.py

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

* Remove superflous defalt

* Update homeassistant/components/zimi/light.py

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

* Update homeassistant/components/zimi/light.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Move aysnc_connect_to_controller to helpers.py

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

* Invert if api

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

* Update homeassistant/components/zimi/config_flow.py

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

* Added ZimiConfigEntry to type runtime_data correctly.
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>

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

* Invert error logic for cleaner flow
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>

* Add ZimiDimmer class

* Set colour_mode only in ZimiDimmer

* Use device name instead of entity name
Update deviceinfo for zcc
Update deviceinfo for lights
More ZimiDimmer and ZimiLight cleanup

* Update homeassistant/components/zimi/__init__.py

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

* Update homeassistant/components/zimi/__init__.py

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

* Add missing import for CONNECTION_NETWORK_MAC

* @mhannon11 Fixed some minor style changes BUT these tests need re-working now that the config_flow has a second call to the zcc helper to check the API.   The tests as written now fail with connect_fail

* Remove some code from try

* Moved static items from initialiser

* Remove superflous assert when unloading entry

* refactor - move title out of data

* One call to async_add_entities
Update ZimiDimmer to initialise color_modes after calling super()

* Create ZimiEntity base class
(as ToggleEntity)

* Updated test of config_flow

* Move api_mock parameters to test cases

* Much improved tests

* Test for input value mismatch and then recovery of flow

* Import FlowResultType

* Implement Entities event setup correctly

* Initial quality_scale.yml

* Update homeassistant/components/zimi/quality_scale.yml

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

* Update homeassistant/components/zimi/manifest.json

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

* Add link to zcc repo

* Update homeassistant/components/zimi/entity.py

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

* Update homeassistant/components/zimi/entity.py

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

* Removed unecessary f-strings

* Filled in all of the quality scale

* Updated in line with latest documentation improvements

* FIx missing import for Entity

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/strings.json

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

* Simplify logger and throw

* Update homeassistant/components/zimi/helpers.py

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

* Re-factor config_flow with multi-stage steps

* Add comments to notify

* Don't set hw_version

* Update homeassistant/components/zimi/light.py

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

* Update homeassistant/components/zimi/light.py

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

* Update homeassistant/components/zimi/quality_scale.yml

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

* mark docs-troubleshooting done

* Update with zcc-helper version supporting PEP 625 sdist rules on PyPi

* Comment re characteristic ID

* Pulls in latest zcc that closes UDP listening port correctly after discovery timeout

* Re-factored config_flow

1. Try discovery and auto-populate
2. Try manual configuration (with optional values for port and mac)

In most cases, auto-discovery does it all.

Discovery will only fail if UDP broadcast is not possible to/from zcc.

* Do not show error message if discovery fails

* Refactor with self.data and async_show_step_finish()

* Update homeassistant/components/zimi/light.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/quality_scale.yml

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

* Update homeassistant/components/zimi/quality_scale.yml

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

* Update homeassistant/components/zimi/entity.py

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

* Update homeassistant/components/zimi/light.py

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

* refactor import to use ConfigFlow

* Change status for discovery

* Add dynamic title to config flow

* string

* Revert title from form but add IP:port to static title

* Automatically finish configuration if possible, if not show form

* Use StrEnum instead of Exception class

* Remove MAC from user forms

* Disconnect api before form completion

* Assign to self.mac instead of returning as detail

* Updated test suite

* Update test status

* mark action exemptions todo

* Remove mac related error cases from flow completely

* Remove unused MAC error strings

* Moved error details to logs
Removed _error_tuple
Removed error details

* Update homeassistant/components/zimi/config_flow.py

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

* rename check_errors

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update zcc-helper and support HA devices via zcc manufacter_info fields

* Partial implementation - Use updated zcc-helper to discover multiple controllers

* Config_flow with support for auto-discovery of one or more zcc or fallback to manual configuration.

* Don't re-connect to api if validate_connection already did

* Make fast=False is used for creation

* Pull in improved zcc_helper version to address data completeness after machine_info implementation

* Update homeassistant/components/zimi/config_flow.py

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

* Import and use ConfigFlowResult

* Latest zcc to fix discovers() return value bug

* Update config_flow.py

* Update homeassistant/components/zimi/manifest.json

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

* Use latest release version of 3.3 (no changes to rc4)

* Improved sentence casing

* Update strings.json

* Update homeassistant/components/zimi/entity.py

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

* Remove superflous logging
Use Zimi network_name as ZCC name
Cleanup device info inputs

* Remove __del__

* Rename arguments

* Update homeassistant/components/zimi/config_flow.py

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

* Move PLATFORMS to init

* Update homeassistant/components/zimi/light.py

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

* Remove debug at init

* Update homeassistant/components/zimi/helpers.py

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

* Remove _attr_has_entity = False

* More naming changes

* Revised config_flow to use zcc-helper for validation using new zcc-helper version

* Update homeassistant/components/zimi/__init__.py

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

* Update homeassistant/components/zimi/__init__.py

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

* Removed commented enum

* s/_entity/_device/g

* Update homeassistant/components/zimi/entity.py

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

* Update homeassistant/components/zimi/helpers.py

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

* Don't log error when raising exception

* Updated tests for new config_flow

* Refactor with new zcc that uses Exception classes to pass errors

* Updated tests for config_flow to use Exceptions

* Device name is based on model

* Device name is None

Maps better to ZCC concept where devices do not have a name but the individual entities have names.

* Fix quality filename

* Bump zcc-helper to 3.4 release version

* Remove name override

* Bump zcc-helper to 3.4.1 with new device_name attribute used to populate devinfo

* Update homeassistant/components/zimi/light.py

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

* Add missing transalation picked up by CI

* Update homeassistant/components/zimi/light.py

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

* Bump zcc-helper to only classify light and dimmer controlPointType as lights

* Bump to non dev version of zcc-helper

* Ruff fixes

* Add missing data description for pytest

* Remove confusing comment

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/const.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/config_flow.py

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

* Update homeassistant/components/zimi/const.py

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

* Update homeassistant/components/zimi/const.py

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

* Update homeassistant/components/zimi/strings.json

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

* Update homeassistant/components/zimi/strings.json

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

* Update tests/components/zimi/test_config_flow.py

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

* Update homeassistant/components/zimi/light.py

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

* f-strings

* Update tests/components/zimi/test_config_flow.py

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

* Update tests/components/zimi/test_config_flow.py

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

* Assert result type, step and errors between each step

* test for duplicate entry

* Update tests/components/zimi/test_config_flow.py

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

* Update tests/components/zimi/test_config_flow.py

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

* Update tests/components/zimi/test_config_flow.py

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

* Update tests/components/zimi/test_config_flow.py

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

* Update tests/components/zimi/test_config_flow.py

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

* Remove duplicate test for discovery failure

* Calculate brightness

* Don't re-raise Exception in helper

* Fix ruff and mypi errors

* Add tests for missing connection exceptions

* Added standard invalid_host and timeout strings

* Explain limitations in discovery.

* Update quality_scale.yaml

* Update quality_scale.yaml

* Removed duplicate strings with reference

---------

Co-authored-by: markhannon <mark.hannon@gmail.com>
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-05-04 15:58:32 +02:00
Norbert Rittel
9e388f5b13 Fix spelling of "comma-separated (network addresses)" in nmap_tracker (#144197) 2025-05-04 15:21:06 +03:00
Brett Adams
87fab1fa14 Rename classes in Teslemetry (#144179) 2025-05-04 14:18:39 +02:00
Brett Adams
8046684179 Update models const in Teslemetry (#144175) 2025-05-04 13:44:56 +02:00
Brett Adams
5a475ec7ea Improve typing of binary sensors in Teslemetry (#144169) 2025-05-04 13:42:25 +02:00
Brett Adams
8c6edd8b81 Add better typing to Teslemetry switch platform (#144168) 2025-05-04 13:41:45 +02:00
Brett Adams
de496c693e Add hazard lights binary sensor to Teslemetry (#144166) 2025-05-04 13:36:13 +02:00
Norbert Rittel
cb37d4d36a Fix spelling of "comma-separated (list / event name)" in doorbird (#144190) 2025-05-04 13:09:31 +03:00
Norbert Rittel
2aa82da615 Fix spelling of "comma-separated (list)" in huawei_lte (#144189) 2025-05-04 13:09:09 +03:00
Maciej Bieniek
04982f5e12 Add missing pollen category to AccuWeather (#144185)
* Add extreme level to pollen map

* Sort

* Sort
2025-05-04 13:08:07 +03:00
Norbert Rittel
b9e11b0f45 Fix spelling of "comma-separated" and "IP address" in cast (#144188) 2025-05-04 13:07:30 +03:00
hahn-th
1e0d1c46ab Bump homematicip to 2.0.1.1 (#144182)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-05-04 12:07:18 +02:00
Norbert Rittel
b5d499dda8 Fix spelling of "comma-separated (list)" in fritzbox_callmonitor (#144191)
Also fix one missing sentence-casing in corresponding "title" string.
2025-05-04 13:06:46 +03:00
Åke Strandberg
d1615f9a6e Bump pymiele to 0.4.3 (#144176)
* Use device class transation

* Bump pymiele to 0.4.3

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-05-04 11:30:37 +02:00
Marc Mueller
516a3c0504 Fix licenses check for setuptools (#144181) 2025-05-04 11:02:11 +03:00
J. Nick Koston
2a5c0d9b88 Add support for updating ESPHome deep sleep devices (#144161)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-05-03 20:50:17 -05:00
J. Nick Koston
a15a3c12d5 Pass requestor_uuid to bond API calls (#144128) 2025-05-03 20:40:28 -04:00
J. Nick Koston
a6131b3ebf Bump habluetooth to 3.48.2 (#144157) 2025-05-03 18:22:48 -05:00
Paulus Schoutsen
b9aadb252f Point thumbnail TTS media source to right logo (#144162) 2025-05-03 17:21:22 -04:00
J. Nick Koston
1264c2cbfa Bump zeroconf to 0.147.0 (#144158) 2025-05-03 14:21:03 -05:00
tronikos
716b559e5d Skip the update right after the migration in Opower (#144088)
* Wait for the migration to finish in Opower

* Don't call async_block_till_done since this can timeout and seems to meant for tests

* Don't call async_block_till_done since this can timeout and seems to meant for tests
2025-05-03 15:12:01 -04:00
Charlie Rusbridger
30e4264aa9 Use kodi posters, fall back to thumbnails if unavailable. (#144066) 2025-05-03 15:10:33 -04:00
Michael
fb94f8ea18 Make the network device tracking feature optional in AVM Fritz!Tools (#144149)
* make the network device tracking feature optional

* fix doc strings

* Apply suggestions from code review

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

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-05-03 21:04:59 +02:00
Florian Sabonchi
aea5760424 Fix check for locked device in AVM Fritz!SmartHome (#141697)
* feat: raise execption on hvac mode while device is locked

* fix: test for setting hvac mode while device is locked.

* feat: update translation

* feat: add separate translations for HVAC and temperature

* fix: test cases

* fix: test cases for test_set_preset_mode_boost

* rev: code review

* rev: exception string

* feat: updated  error message and added helper function

* Update homeassistant/components/fritzbox/strings.json

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

* fix: translation key

* remove check_active_or_lock_mode from async_set_preset_mode

---------

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
2025-05-03 20:25:27 +02:00
Jan Bouwhuis
debec3bfbc Improve supported color modes description (#144144) 2025-05-03 17:13:43 +01:00
J. Diego Rodríguez Royo
4122f94fb6 Add DHCP discovery to Home Connect (#144095)
* Add DHCP discovery to Home Connect

* Added tests

* Use enums

* Use more enums
2025-05-03 17:16:02 +02:00
J. Nick Koston
b48a2cf2b5 Add tests to ensure ESPHome entity_ids are preserved on upgrade (#144116) 2025-05-03 10:12:37 -05:00
Shay Levy
0ca9ad1cc0 Mark Shelly docs-data-update as done (#144151) 2025-05-03 16:17:37 +02:00
Shay Levy
ee555a3700 Mark Shelly icon-translations as done (#144148) 2025-05-03 17:08:34 +03:00
Josef Zweck
a2bc3e3908 Switch to common clientsession for lamarzocco (#144137) 2025-05-03 14:44:18 +02:00
Thomas55555
64b7f2c285 Improve select platform in Husqvarna Automower (#144117) 2025-05-03 15:39:46 +03:00
Marc Mueller
db2435dc36 Fix litterrobot entity typing (#144147) 2025-05-03 14:35:17 +02:00
Marc Mueller
1d500fda67 Fix fritz coordinator typing (#144146) 2025-05-03 14:35:04 +02:00
Jan Bouwhuis
558b0ec3b1 Fix small issues with mqtt translations and improve readability (#144091) 2025-05-03 11:51:26 +02:00
J. Nick Koston
9780db1c22 Bump Bluetooth deps to improve auto recovery process (#144133) 2025-05-03 10:09:28 +02:00
J. Nick Koston
5e39fb6da1 Bump bleak-esphome to 2.15.1 (#144129) 2025-05-03 10:08:56 +02:00
J. Nick Koston
4450f919c3 Bump PyISY to 3.4.1 (#144127) 2025-05-02 17:46:59 -05:00
Marc Hörsken
3183bb78ff Update pywmspro to 0.2.2 to make error handling more robust (#144124) 2025-05-03 00:16:49 +02:00
J. Nick Koston
e74f918382 Bump aiodns to 3.3.0 (#144115) 2025-05-02 15:53:19 -05:00
Thomas55555
247d2e7efd Bump aioautomower to 2025.5.1 (#144118) 2025-05-02 22:35:34 +02:00
Bram Kragten
32b7edb608 Update frontend to 20250502.0 (#144114) 2025-05-02 22:33:39 +02:00
Josef Zweck
df4297be62 Fix intermittent unavailability for lamarzocco brew active sensor (#144120)
* Fix brew active intermittent unavailability for lamarzocco

* Whitespaces
2025-05-02 22:29:54 +02:00
Brett Adams
4c2e9fc759 Bump teslemetry-stream to 0.7.7 (#144085) 2025-05-02 21:13:12 +02:00
J. Nick Koston
2890fc7dd2 Only create a single resolver object if there are multiple aiohttp sessions (#144090) 2025-05-02 13:43:06 -05:00
Ian
97be2c4ac9 Bump py-nextbusnext to 2.1.2 (#144081)r
Bump py-nextbusnext version

Fixes #144059
2025-05-02 20:17:58 +02:00
Åke Strandberg
762d284102 Improve naming of miele freezers and fridges (#144062)
* Use device class transation

* Improve naming of miele freezers and fridges

* Address review

* Address review comment

* Simplify
2025-05-02 19:31:56 +02:00
Joost Lekkerkerker
4967c287f8 Add DHCP discovery to Knocki (#144048)
* Add DHCP discovery to Knocki

* Update homeassistant/components/knocki/quality_scale.yaml

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

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-05-02 18:34:09 +02:00
Pete Sage
5e463d6af4 bump aiokem to 0.5.9 (#144098)
fix: bump aiokem to 0.5.9
2025-05-02 17:34:58 +02:00
Åke Strandberg
cbf4676ae4 Improve handling of missing miele program codes (#144093)
* Use device class transation

* Improve handling of unknown program codes

* Address review comment
2025-05-02 17:31:11 +02:00
Tomáš Bedřich
81444c8f4a Disable S3 checksums (#144092)
Disable S3 checksums (#143995)
2025-05-02 13:49:33 +02:00
J. Nick Koston
9861bd88b9 Avoid working out suggested id in entity_platform when already registered (#144079)
If the entity is already registered, avoid trying to work
out the suggested_entity_id and suggested_object_id as
async_get_or_create will discard them anyways.
2025-05-02 11:44:38 +02:00
Simone Chemelli
b0f1c71129 Handle missing action exceptions in SamsungTV (#143630)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-05-02 11:39:28 +02:00
Shay Levy
86b845f04a Mark exception-translations done in Shelly (#144073) 2025-05-02 12:32:41 +03:00
J. Diego Rodríguez Royo
3af0d6e484 Use is instead of == on check against enum value at Home Connect (#144083)
* Use `is` instead of `==` on check against enum value at Home Connect

* Revert HTTP status checks
2025-05-02 10:08:46 +02:00
epenet
fca62f1ae8 Move SamsungTV test constants to fixture files (#144086) 2025-05-02 08:32:44 +02:00
Andreas Kölsch
4e8d68a2ef Fix brightness calculation when using brightness_step_pct (#143786) 2025-05-01 23:07:52 +01:00
abmantis
c469720166 Remove door state from Whirlpool machine state sensor
Breaking change: The door state is now reported as a binary sensor instead of being part of the main machine state sensor, which now reports only the cycle states. Users relying on this state in automations or scripts will need to update their configurations to use the new binary sensor.
2025-05-01 22:54:04 +01:00
J. Diego Rodríguez Royo
883ab44437 Move Home Connect entry state assertion at tests (#144027) 2025-05-01 23:04:03 +02:00
tronikos
abd17d9af9 Pass empty set instead of empty dict to get_last_statistics (#144022) 2025-05-01 16:47:48 -04:00
J. Nick Koston
a906a1754e Avoid DomainData lookup in ESPHome update platform (#144072)
We can get this from entry.runtime_data
2025-05-01 16:45:44 -04:00
Josef Zweck
255beafe08 Add connect/disconnect callbacks to lamarzocco (#144011) 2025-05-01 16:29:44 -04:00
Josef Zweck
e2679004a1 Add bluetooth connection availability to diagnostics for lamarzocco (#144012)
* Add bluetooth connection availability to diagnostics for lamarzocco

* make even more detailed
2025-05-01 16:26:50 -04:00
J. Nick Koston
06bb692522 Bump inkbird-ble to 0.16.1 (#144074)
I made a mistake in one of the data lengths as I forgot to add
the length of the id which is 2 bytes. I really wish vendors
would stop putting raw data in this field.

changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.16.0...v0.16.1
2025-05-01 23:23:56 +03:00
Shay Levy
71599b8e75 Set Shelly PARALLEL_UPDATES (#144070) 2025-05-01 21:20:50 +02:00
J. Nick Koston
79f8bea48d Avoid validation of ESPHome MAC when discovered entry is ignored or unchanged (#144071)
fixes #144033
fixes #143991
2025-05-01 14:51:38 -04:00
Åke Strandberg
82b335a2c1 Flag strict typing for miele (#144060) 2025-05-01 19:10:24 +02:00
Thomas55555
361d93eb96 Remove deprecated binary sensor in Husqvarna Automower (#144064)
* Remove deprecated binary sensor in Husqvarna Automower

* snapshot
2025-05-01 18:35:48 +02:00
Ludovic BOUÉ
bab699eb0c Matter Solar power fixture (#144058) 2025-05-01 17:06:08 +02:00
Thomas55555
b8881ed85b Fix test in Husqvarna Automower (#144055) 2025-05-01 16:36:05 +02:00
Åke Strandberg
4013b418dd Use device class transation for door in miele (#144053) 2025-05-01 16:33:30 +02:00
Åke Strandberg
80d714b865 Use action property defined in MieleEntity (#144052) 2025-05-01 16:06:49 +02:00
Åke Strandberg
7fcad580cb Update miele program codes and strings (#144049) 2025-05-01 15:14:11 +02:00
Ludovic BOUÉ
60b6ff4064 Matter Laundry Dryer fixture (#144043)
* Create laundry_dryer.json

* Add snapshots

* Format fixture

* Set CurrentPhase attribute

* Set OperationalState attribute

* Update snapshot
2025-05-01 14:52:32 +02:00
Josef Zweck
24252edf38 Handle TimeoutError for lamarzocco (#144042) 2025-05-01 14:02:39 +02:00
J. Diego Rodríguez Royo
79aa7aacec Sort Home Connect test params (#144035) 2025-05-01 12:04:25 +02:00
OzGav
92944fa509 Media Player strings adjust grammar (#144030) 2025-05-01 11:40:04 +02:00
J. Diego Rodríguez Royo
c0f0a4a1ac Listen for an event just once at Home Connect test (#144031) 2025-05-01 11:24:29 +02:00
J. Diego Rodríguez Royo
a084b9fdde Set autouse to setup_credentials Home Connect fixture (#144028) 2025-05-01 11:24:05 +02:00
Andrea Turri
83b9b8b032 Fix state of fan entity for Miele hobs with extractor when turned off (#144025) 2025-05-01 10:42:27 +02:00
J. Diego Rodríguez Royo
bc47049d42 Remove non required Home Connect tests (#144024) 2025-05-01 10:18:32 +02:00
J. Diego Rodríguez Royo
17360ede28 Use common percentage const at Home Connect (#144021) 2025-05-01 09:57:42 +02:00
J. Diego Rodríguez Royo
f441f4d7c0 Remove translation key for battery level in Home Connect sensor (#144020) 2025-05-01 09:57:30 +02:00
J. Diego Rodríguez Royo
5ddc449247 Remove default brightness values from Home Connect light entities (#144019) 2025-05-01 09:57:17 +02:00
J. Diego Rodríguez Royo
dd8d714c94 Remove _attr_should_poll from Home Connect base entity (#144016) 2025-05-01 09:49:49 +02:00
J. Diego Rodríguez Royo
c2079ddf6f Remove unused client param at Home Connect diagnostics (#144017) 2025-05-01 09:49:25 +02:00
Manu
5250590b17 Remove deprecated action api_call from Habitica integration (#143978) 2025-05-01 09:28:25 +02:00
Jan-Philipp Benecke
93f4f14b2a Default backup encryption to true when updating only location retention (#143997) 2025-04-30 22:45:41 +01:00
Ville Skyttä
ba712ed514 Move huawei_lte sensor icons to icons.json where applicable (#143999) 2025-04-30 23:26:10 +02:00
Norbert Rittel
6e76ca0fb3 Add translations for "energy_distance" and "wind_direction" in random (#143994)
* Add translations for "energy_distance" and "wind_direction" in `random`

* Comma
2025-05-01 00:13:04 +03:00
Megamind
b0345cce68 Bump pushover-complete to 1.2.0 (#143966)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-04-30 23:04:56 +02:00
Paulus Schoutsen
c4eddc8d11 Ensure legacy TTS providers are hidden if entity exists (#143992) 2025-04-30 16:57:02 -04:00
Josef Zweck
7d89804a87 Bump pylamarzocco to 2.0.0b7 (#143989) 2025-04-30 22:56:04 +02:00
Ludovic BOUÉ
b92f718e08 Matter Cooktop fixture (#143984) 2025-04-30 22:09:29 +02:00
Franck Nijhof
ad0209a4a0 Bump version to 2025.6.0dev0 (#143983) 2025-04-30 21:44:19 +02:00
J. Diego Rodríguez Royo
d23d25c6b7 Add units of measurement for Home Connect counter entities (#143982) 2025-04-30 21:03:17 +02:00
235 changed files with 6325 additions and 3737 deletions

View File

@@ -40,7 +40,7 @@ env:
CACHE_VERSION: 12
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 9
HA_SHORT_VERSION: "2025.5"
HA_SHORT_VERSION: "2025.6"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['3.13']"
# 10.3 is the oldest supported version

View File

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

View File

@@ -332,6 +332,7 @@ homeassistant.components.media_player.*
homeassistant.components.media_source.*
homeassistant.components.met_eireann.*
homeassistant.components.metoffice.*
homeassistant.components.miele.*
homeassistant.components.mikrotik.*
homeassistant.components.min_max.*
homeassistant.components.minecraft_server.*

10
CODEOWNERS generated
View File

@@ -171,8 +171,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/avea/ @pattyland
/homeassistant/components/awair/ @ahayworth @danielsjf
/tests/components/awair/ @ahayworth @danielsjf
/homeassistant/components/aws_s3/ @tomasbedrich
/tests/components/aws_s3/ @tomasbedrich
/homeassistant/components/axis/ @Kane610
/tests/components/axis/ @Kane610
/homeassistant/components/azure_data_explorer/ @kaareseras
@@ -1320,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
@@ -1678,8 +1678,8 @@ build.json @home-assistant/supervisor
/tests/components/vlc_telnet/ @rodripf @MartinHjelmare
/homeassistant/components/vodafone_station/ @paoloantinori @chemelli74
/tests/components/vodafone_station/ @paoloantinori @chemelli74
/homeassistant/components/voip/ @balloob @synesthesiam @jaminh
/tests/components/voip/ @balloob @synesthesiam @jaminh
/homeassistant/components/voip/ @balloob @synesthesiam
/tests/components/voip/ @balloob @synesthesiam
/homeassistant/components/volumio/ @OnFreund
/tests/components/volumio/ @OnFreund
/homeassistant/components/volvooncall/ @molobrakos
@@ -1796,6 +1796,8 @@ build.json @home-assistant/supervisor
/tests/components/zeversolar/ @kvanzuijlen
/homeassistant/components/zha/ @dmulcahey @adminiuga @puddly @TheJulianJES
/tests/components/zha/ @dmulcahey @adminiuga @puddly @TheJulianJES
/homeassistant/components/zimi/ @markhannon
/tests/components/zimi/ @markhannon
/homeassistant/components/zodiac/ @JulienTant
/tests/components/zodiac/ @JulienTant
/homeassistant/components/zone/ @home-assistant/core

View File

@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.05.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.05.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.05.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.05.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.05.0
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.02.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.02.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.02.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.02.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.02.1
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io

View File

@@ -1,12 +1,5 @@
{
"domain": "amazon",
"name": "Amazon",
"integrations": [
"alexa",
"amazon_polly",
"aws",
"aws_s3",
"fire_tv",
"route53"
]
"integrations": ["alexa", "amazon_polly", "aws", "fire_tv", "route53"]
}

View File

@@ -30,6 +30,7 @@ class BackupCoordinatorData:
"""Class to hold backup data."""
backup_manager_state: BackupManagerState
last_attempted_automatic_backup: datetime | None
last_successful_automatic_backup: datetime | None
next_scheduled_automatic_backup: datetime | None
@@ -70,6 +71,7 @@ class BackupDataUpdateCoordinator(DataUpdateCoordinator[BackupCoordinatorData]):
"""Update backup manager data."""
return BackupCoordinatorData(
self.backup_manager.state,
self.backup_manager.config.data.last_attempted_automatic_backup,
self.backup_manager.config.data.last_completed_automatic_backup,
self.backup_manager.config.data.schedule.next_automatic_backup,
)

View File

@@ -46,6 +46,12 @@ BACKUP_MANAGER_DESCRIPTIONS = (
device_class=SensorDeviceClass.TIMESTAMP,
value_fn=lambda data: data.last_successful_automatic_backup,
),
BackupSensorEntityDescription(
key="last_attempted_automatic_backup",
translation_key="last_attempted_automatic_backup",
device_class=SensorDeviceClass.TIMESTAMP,
value_fn=lambda data: data.last_attempted_automatic_backup,
),
)

View File

@@ -37,6 +37,9 @@
"next_scheduled_automatic_backup": {
"name": "Next scheduled automatic backup"
},
"last_attempted_automatic_backup": {
"name": "Last attempted automatic backup"
},
"last_successful_automatic_backup": {
"name": "Last successful automatic backup"
}

View File

@@ -2,7 +2,7 @@
"config": {
"step": {
"user": {
"title": "Sign-in with Blink account",
"title": "Sign in with Blink account",
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
@@ -30,7 +30,7 @@
"step": {
"simple_options": {
"data": {
"scan_interval": "Scan Interval (seconds)"
"scan_interval": "Scan interval (seconds)"
},
"title": "Blink options",
"description": "Configure Blink integration"
@@ -93,7 +93,7 @@
},
"config_entry_id": {
"name": "Integration ID",
"description": "The Blink Integration ID."
"description": "The Blink integration ID."
}
}
}

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.4.1"]
"requirements": ["bluemaestro-ble==0.4.0"]
}

View File

@@ -5,7 +5,7 @@ import logging
from typing import Any
from aiohttp import ClientError, ClientResponseError, ClientTimeout
from bond_async import Bond, BPUPSubscriptions, start_bpup
from bond_async import Bond, BPUPSubscriptions, RequestorUUID, start_bpup
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -49,6 +49,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BondConfigEntry) -> bool
token=token,
timeout=ClientTimeout(total=_API_TIMEOUT),
session=async_get_clientsession(hass),
requestor_uuid=RequestorUUID.HOME_ASSISTANT,
)
hub = BondHub(bond, host)
try:

View File

@@ -8,7 +8,7 @@ import logging
from typing import Any
from aiohttp import ClientConnectionError, ClientResponseError
from bond_async import Bond
from bond_async import Bond, RequestorUUID
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState, ConfigFlow, ConfigFlowResult
@@ -34,7 +34,12 @@ TOKEN_SCHEMA = vol.Schema({})
async def async_get_token(hass: HomeAssistant, host: str) -> str | None:
"""Try to fetch the token from the bond device."""
bond = Bond(host, "", session=async_get_clientsession(hass))
bond = Bond(
host,
"",
session=async_get_clientsession(hass),
requestor_uuid=RequestorUUID.HOME_ASSISTANT,
)
response: dict[str, str] = {}
with contextlib.suppress(ClientConnectionError):
response = await bond.token()
@@ -45,7 +50,10 @@ async def _validate_input(hass: HomeAssistant, data: dict[str, Any]) -> tuple[st
"""Validate the user input allows us to connect."""
bond = Bond(
data[CONF_HOST], data[CONF_ACCESS_TOKEN], session=async_get_clientsession(hass)
data[CONF_HOST],
data[CONF_ACCESS_TOKEN],
session=async_get_clientsession(hass),
requestor_uuid=RequestorUUID.HOME_ASSISTANT,
)
try:
hub = BondHub(bond, data[CONF_HOST])

View File

@@ -10,12 +10,12 @@
"known_hosts": "Add known host"
},
"data_description": {
"known_hosts": "Hostnames or IP-addresses of cast devices, use if mDNS discovery is not working"
"known_hosts": "Hostnames or IP addresses of cast devices, use if mDNS discovery is not working"
}
}
},
"error": {
"invalid_known_hosts": "Known hosts must be a comma separated list of hosts."
"invalid_known_hosts": "Known hosts must be a comma-separated list of hosts."
}
},
"options": {

View File

@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/denonavr",
"iot_class": "local_push",
"loggers": ["denonavr"],
"requirements": ["denonavr==1.0.1"],
"requirements": ["denonavr==1.1.0"],
"ssdp": [
{
"manufacturer": "Denon",

View File

@@ -3,10 +3,10 @@
"step": {
"init": {
"data": {
"events": "Comma separated list of events."
"events": "Comma-separated list of events."
},
"data_description": {
"events": "Add a comma separated event name for each event you wish to track. After entering them here, use the DoorBird app to assign them to a specific event.\n\nExample: somebody_pressed_the_button, motion"
"events": "Add a comma-separated event name for each event you wish to track. After entering them here, use the DoorBird app to assign them to a specific event.\n\nExample: somebody_pressed_the_button, motion"
}
}
}

View File

@@ -8,6 +8,7 @@ from collections.abc import Callable, Iterable
from dataclasses import dataclass, field
from functools import partial
import logging
from operator import delitem
from typing import TYPE_CHECKING, Any, Final, TypedDict, cast
from aioesphomeapi import (
@@ -183,18 +184,7 @@ class RuntimeEntryData:
"""Register to receive callbacks when static info changes for an EntityInfo type."""
callbacks = self.entity_info_callbacks.setdefault(entity_info_type, [])
callbacks.append(callback_)
return partial(
self._async_unsubscribe_register_static_info, callbacks, callback_
)
@callback
def _async_unsubscribe_register_static_info(
self,
callbacks: list[Callable[[list[EntityInfo]], None]],
callback_: Callable[[list[EntityInfo]], None],
) -> None:
"""Unsubscribe to when static info is registered."""
callbacks.remove(callback_)
return partial(callbacks.remove, callback_)
@callback
def async_register_key_static_info_updated_callback(
@@ -206,18 +196,7 @@ class RuntimeEntryData:
callback_key = (type(static_info), static_info.key)
callbacks = self.entity_info_key_updated_callbacks.setdefault(callback_key, [])
callbacks.append(callback_)
return partial(
self._async_unsubscribe_static_key_info_updated, callbacks, callback_
)
@callback
def _async_unsubscribe_static_key_info_updated(
self,
callbacks: list[Callable[[EntityInfo], None]],
callback_: Callable[[EntityInfo], None],
) -> None:
"""Unsubscribe to when static info is updated ."""
callbacks.remove(callback_)
return partial(callbacks.remove, callback_)
@callback
def async_set_assist_pipeline_state(self, state: bool) -> None:
@@ -232,14 +211,7 @@ class RuntimeEntryData:
) -> CALLBACK_TYPE:
"""Subscribe to assist pipeline updates."""
self.assist_pipeline_update_callbacks.append(update_callback)
return partial(self._async_unsubscribe_assist_pipeline_update, update_callback)
@callback
def _async_unsubscribe_assist_pipeline_update(
self, update_callback: CALLBACK_TYPE
) -> None:
"""Unsubscribe to assist pipeline updates."""
self.assist_pipeline_update_callbacks.remove(update_callback)
return partial(self.assist_pipeline_update_callbacks.remove, update_callback)
@callback
def async_remove_entities(
@@ -337,12 +309,7 @@ class RuntimeEntryData:
def async_subscribe_device_updated(self, callback_: CALLBACK_TYPE) -> CALLBACK_TYPE:
"""Subscribe to state updates."""
self.device_update_subscriptions.add(callback_)
return partial(self._async_unsubscribe_device_update, callback_)
@callback
def _async_unsubscribe_device_update(self, callback_: CALLBACK_TYPE) -> None:
"""Unsubscribe to device updates."""
self.device_update_subscriptions.remove(callback_)
return partial(self.device_update_subscriptions.remove, callback_)
@callback
def async_subscribe_static_info_updated(
@@ -350,14 +317,7 @@ class RuntimeEntryData:
) -> CALLBACK_TYPE:
"""Subscribe to static info updates."""
self.static_info_update_subscriptions.add(callback_)
return partial(self._async_unsubscribe_static_info_updated, callback_)
@callback
def _async_unsubscribe_static_info_updated(
self, callback_: Callable[[list[EntityInfo]], None]
) -> None:
"""Unsubscribe to static info updates."""
self.static_info_update_subscriptions.remove(callback_)
return partial(self.static_info_update_subscriptions.remove, callback_)
@callback
def async_subscribe_state_update(
@@ -369,14 +329,7 @@ class RuntimeEntryData:
"""Subscribe to state updates."""
subscription_key = (state_type, state_key)
self.state_subscriptions[subscription_key] = entity_callback
return partial(self._async_unsubscribe_state_update, subscription_key)
@callback
def _async_unsubscribe_state_update(
self, subscription_key: tuple[type[EntityState], int]
) -> None:
"""Unsubscribe to state updates."""
self.state_subscriptions.pop(subscription_key)
return partial(delitem, self.state_subscriptions, subscription_key)
@callback
def async_update_state(self, state: EntityState) -> None:
@@ -523,7 +476,7 @@ class RuntimeEntryData:
) -> CALLBACK_TYPE:
"""Register to receive callbacks when the Assist satellite's configuration is updated."""
self.assist_satellite_config_update_callbacks.append(callback_)
return lambda: self.assist_satellite_config_update_callbacks.remove(callback_)
return partial(self.assist_satellite_config_update_callbacks.remove, callback_)
@callback
def async_assist_satellite_config_updated(
@@ -540,7 +493,7 @@ class RuntimeEntryData:
) -> CALLBACK_TYPE:
"""Register to receive callbacks when the Assist satellite's wake word is set."""
self.assist_satellite_set_wake_word_callbacks.append(callback_)
return lambda: self.assist_satellite_set_wake_word_callbacks.remove(callback_)
return partial(self.assist_satellite_set_wake_word_callbacks.remove, callback_)
@callback
def async_assist_satellite_set_wake_word(self, wake_word_id: str) -> None:

View File

@@ -195,7 +195,10 @@
"message": "Error compiling {configuration}; Try again in ESPHome dashboard for more information."
},
"error_uploading": {
"message": "Error during OTA of {configuration}; Try again in ESPHome dashboard for more information."
"message": "Error during OTA (Over-The-Air) of {configuration}; Try again in ESPHome dashboard for more information."
},
"ota_in_progress": {
"message": "An OTA (Over-The-Air) update is already in progress for {configuration}."
}
}
}

View File

@@ -125,21 +125,17 @@ class ESPHomeDashboardUpdateEntity(
(dr.CONNECTION_NETWORK_MAC, entry_data.device_info.mac_address)
}
)
self._install_lock = asyncio.Lock()
self._available_future: asyncio.Future[None] | None = None
self._update_attrs()
@callback
def _update_attrs(self) -> None:
"""Update the supported features."""
# If the device has deep sleep, we can't assume we can install updates
# as the ESP will not be connectable (by design).
coordinator = self.coordinator
device_info = self._device_info
# Install support can change at run time
if (
coordinator.last_update_success
and coordinator.supports_update
and not device_info.has_deep_sleep
):
if coordinator.last_update_success and coordinator.supports_update:
self._attr_supported_features = UpdateEntityFeature.INSTALL
else:
self._attr_supported_features = NO_FEATURES
@@ -178,6 +174,13 @@ class ESPHomeDashboardUpdateEntity(
self, static_info: list[EntityInfo] | None = None
) -> None:
"""Handle updated data from the device."""
if (
self._entry_data.available
and self._available_future
and not self._available_future.done()
):
self._available_future.set_result(None)
self._available_future = None
self._update_attrs()
self.async_write_ha_state()
@@ -192,17 +195,46 @@ class ESPHomeDashboardUpdateEntity(
entry_data.async_subscribe_device_updated(self._handle_device_update)
)
async def async_will_remove_from_hass(self) -> None:
"""Handle entity about to be removed from Home Assistant."""
if self._available_future and not self._available_future.done():
self._available_future.cancel()
self._available_future = None
async def _async_wait_available(self) -> None:
"""Wait until the device is available."""
# If the device has deep sleep, we need to wait for it to wake up
# and connect to the network to be able to install the update.
if self._entry_data.available:
return
self._available_future = self.hass.loop.create_future()
try:
await self._available_future
finally:
self._available_future = None
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
async with self.hass.data.setdefault(KEY_UPDATE_LOCK, asyncio.Lock()):
coordinator = self.coordinator
api = coordinator.api
device = coordinator.data.get(self._device_info.name)
assert device is not None
configuration = device["configuration"]
try:
if self._install_lock.locked():
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="ota_in_progress",
translation_placeholders={
"configuration": self._device_info.name,
},
)
# Ensure only one OTA per device at a time
async with self._install_lock:
# Ensure only one compile at a time for ALL devices
async with self.hass.data.setdefault(KEY_UPDATE_LOCK, asyncio.Lock()):
coordinator = self.coordinator
api = coordinator.api
device = coordinator.data.get(self._device_info.name)
assert device is not None
configuration = device["configuration"]
if not await api.compile(configuration):
raise HomeAssistantError(
translation_domain=DOMAIN,
@@ -211,14 +243,25 @@ class ESPHomeDashboardUpdateEntity(
"configuration": configuration,
},
)
if not await api.upload(configuration, "OTA"):
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="error_uploading",
translation_placeholders={
"configuration": configuration,
},
)
# If the device uses deep sleep, there's a small chance it goes
# to sleep right after the dashboard connects but before the OTA
# starts. In that case, the update won't go through, so we try
# again to catch it on its next wakeup.
attempts = 2 if self._device_info.has_deep_sleep else 1
try:
for attempt in range(1, attempts + 1):
await self._async_wait_available()
if await api.upload(configuration, "OTA"):
break
if attempt == attempts:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="error_uploading",
translation_placeholders={
"configuration": configuration,
},
)
finally:
await self.coordinator.async_request_refresh()

View File

@@ -45,7 +45,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: FeedReaderConfigEntry)
# if this is the last entry, remove the storage
if len(entries) == 1:
hass.data.pop(MY_KEY)
return await hass.config_entries.async_unload_platforms(entry, [Platform.EVENT])
return await hass.config_entries.async_unload_platforms(entry, Platform.EVENT)
async def _async_update_listener(

View File

@@ -15,6 +15,8 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import (
CONF_FEATURE_DEVICE_TRACKING,
DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
DEFAULT_SSL,
DOMAIN,
FRITZ_AUTH_EXCEPTIONS,
@@ -38,6 +40,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: FritzConfigEntry) -> bool:
"""Set up fritzboxtools from config entry."""
_LOGGER.debug("Setting up FRITZ!Box Tools component")
avm_wrapper = AvmWrapper(
hass=hass,
config_entry=entry,
@@ -46,6 +49,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: FritzConfigEntry) -> boo
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
use_tls=entry.data.get(CONF_SSL, DEFAULT_SSL),
device_discovery_enabled=entry.options.get(
CONF_FEATURE_DEVICE_TRACKING, DEFAULT_CONF_FEATURE_DEVICE_TRACKING
),
)
try:
@@ -62,6 +68,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: FritzConfigEntry) -> boo
raise ConfigEntryAuthFailed("Missing UPnP configuration")
await avm_wrapper.async_config_entry_first_refresh()
await avm_wrapper.async_trigger_cleanup()
entry.runtime_data = avm_wrapper

View File

@@ -35,7 +35,9 @@ from homeassistant.helpers.service_info.ssdp import (
from homeassistant.helpers.typing import VolDictType
from .const import (
CONF_FEATURE_DEVICE_TRACKING,
CONF_OLD_DISCOVERY,
DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
DEFAULT_CONF_OLD_DISCOVERY,
DEFAULT_HOST,
DEFAULT_HTTP_PORT,
@@ -72,7 +74,8 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
"""Initialize FRITZ!Box Tools flow."""
self._name: str = ""
self._password: str = ""
self._use_tls: bool = False
self._use_tls: bool = DEFAULT_SSL
self._feature_device_discovery: bool = DEFAULT_CONF_FEATURE_DEVICE_TRACKING
self._port: int | None = None
self._username: str = ""
self._model: str = ""
@@ -141,6 +144,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
options={
CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds(),
CONF_OLD_DISCOVERY: DEFAULT_CONF_OLD_DISCOVERY,
CONF_FEATURE_DEVICE_TRACKING: self._feature_device_discovery,
},
)
@@ -204,6 +208,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD]
self._use_tls = user_input[CONF_SSL]
self._feature_device_discovery = user_input[CONF_FEATURE_DEVICE_TRACKING]
self._port = self._determine_port(user_input)
error = await self.async_fritz_tools_init()
@@ -234,6 +239,10 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): bool,
vol.Required(
CONF_FEATURE_DEVICE_TRACKING,
default=DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
): bool,
}
),
errors=errors or {},
@@ -250,6 +259,10 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): bool,
vol.Required(
CONF_FEATURE_DEVICE_TRACKING,
default=DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
): bool,
}
),
description_placeholders={"name": self._name},
@@ -405,7 +418,7 @@ class FritzBoxToolsOptionsFlowHandler(OptionsFlow):
"""Handle options flow."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_create_entry(data=user_input)
options = self.config_entry.options
data_schema = vol.Schema(
@@ -420,6 +433,13 @@ class FritzBoxToolsOptionsFlowHandler(OptionsFlow):
CONF_OLD_DISCOVERY,
default=options.get(CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY),
): bool,
vol.Optional(
CONF_FEATURE_DEVICE_TRACKING,
default=options.get(
CONF_FEATURE_DEVICE_TRACKING,
DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
),
): bool,
}
)
return self.async_show_form(step_id="init", data_schema=data_schema)

View File

@@ -40,6 +40,9 @@ PLATFORMS = [
CONF_OLD_DISCOVERY = "old_discovery"
DEFAULT_CONF_OLD_DISCOVERY = False
CONF_FEATURE_DEVICE_TRACKING = "feature_device_tracking"
DEFAULT_CONF_FEATURE_DEVICE_TRACKING = True
DSL_CONNECTION: Literal["dsl"] = "dsl"
DEFAULT_DEVICE_NAME = "Unknown device"

View File

@@ -39,6 +39,7 @@ from homeassistant.util.hass_dict import HassKey
from .const import (
CONF_OLD_DISCOVERY,
DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
DEFAULT_CONF_OLD_DISCOVERY,
DEFAULT_HOST,
DEFAULT_SSL,
@@ -175,6 +176,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
username: str = DEFAULT_USERNAME,
host: str = DEFAULT_HOST,
use_tls: bool = DEFAULT_SSL,
device_discovery_enabled: bool = DEFAULT_CONF_FEATURE_DEVICE_TRACKING,
) -> None:
"""Initialize FritzboxTools class."""
super().__init__(
@@ -202,6 +204,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
self.port = port
self.username = username
self.use_tls = use_tls
self.device_discovery_enabled = device_discovery_enabled
self.has_call_deflections: bool = False
self._model: str | None = None
self._current_firmware: str | None = None
@@ -332,10 +335,15 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
"entity_states": {},
}
try:
await self.async_scan_devices()
await self.async_update_device_info()
if self.device_discovery_enabled:
await self.async_scan_devices()
entity_data["entity_states"] = await self.hass.async_add_executor_job(
self._entity_states_update
)
if self.has_call_deflections:
entity_data[
"call_deflections"
@@ -521,7 +529,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
return {}
def manage_device_info(
self, dev_info: Device, dev_mac: str, consider_home: bool
self, dev_info: Device, dev_mac: str, consider_home: float
) -> bool:
"""Update device lists and return if device is new."""
_LOGGER.debug("Client dev_info: %s", dev_info)
@@ -551,12 +559,8 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
if new_device:
async_dispatcher_send(self.hass, self.signal_device_new)
async def async_scan_devices(self, now: datetime | None = None) -> None:
"""Scan for new devices and return a list of found device ids."""
if self.hass.is_stopping:
_ha_is_stopping("scan devices")
return
async def async_update_device_info(self, now: datetime | None = None) -> None:
"""Update own device information."""
_LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host)
(
@@ -565,6 +569,13 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
self._release_url,
) = await self._async_update_device_info()
async def async_scan_devices(self, now: datetime | None = None) -> None:
"""Scan for new network devices."""
if self.hass.is_stopping:
_ha_is_stopping("scan devices")
return
_LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host)
_default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds()
if self._options:
@@ -683,7 +694,10 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
async def async_trigger_cleanup(self) -> None:
"""Trigger device trackers cleanup."""
device_hosts = await self._async_update_hosts_info()
_LOGGER.debug("Device tracker cleanup triggered")
device_hosts = {self.mac: Device(True, "", "", "", "", None)}
if self.device_discovery_enabled:
device_hosts = await self._async_update_hosts_info()
entity_reg: er.EntityRegistry = er.async_get(self.hass)
config_entry = self.config_entry

View File

@@ -4,7 +4,9 @@
"data_description_port": "Leave empty to use the default port.",
"data_description_username": "Username for the FRITZ!Box.",
"data_description_password": "Password for the FRITZ!Box.",
"data_description_ssl": "Use SSL to connect to the FRITZ!Box."
"data_description_ssl": "Use SSL to connect to the FRITZ!Box.",
"data_description_feature_device_tracking": "Enable or disable the network device tracking feature.",
"data_feature_device_tracking": "Enable network device tracking"
},
"config": {
"flow_title": "{name}",
@@ -15,12 +17,14 @@
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"ssl": "[%key:common::config_flow::data::ssl%]"
"ssl": "[%key:common::config_flow::data::ssl%]",
"feature_device_tracking": "[%key:component::fritz::common::data_feature_device_tracking%]"
},
"data_description": {
"username": "[%key:component::fritz::common::data_description_username%]",
"password": "[%key:component::fritz::common::data_description_password%]",
"ssl": "[%key:component::fritz::common::data_description_ssl%]"
"ssl": "[%key:component::fritz::common::data_description_ssl%]",
"feature_device_tracking": "[%key:component::fritz::common::data_description_feature_device_tracking%]"
}
},
"reauth_confirm": {
@@ -57,14 +61,16 @@
"port": "[%key:common::config_flow::data::port%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"ssl": "[%key:common::config_flow::data::ssl%]"
"ssl": "[%key:common::config_flow::data::ssl%]",
"feature_device_tracking": "[%key:component::fritz::common::data_feature_device_tracking%]"
},
"data_description": {
"host": "[%key:component::fritz::common::data_description_host%]",
"port": "[%key:component::fritz::common::data_description_port%]",
"username": "[%key:component::fritz::common::data_description_username%]",
"password": "[%key:component::fritz::common::data_description_password%]",
"ssl": "[%key:component::fritz::common::data_description_ssl%]"
"ssl": "[%key:component::fritz::common::data_description_ssl%]",
"feature_device_tracking": "[%key:component::fritz::common::data_description_feature_device_tracking%]"
}
}
},
@@ -89,11 +95,13 @@
"init": {
"data": {
"consider_home": "Seconds to consider a device at 'home'",
"old_discovery": "Enable old discovery method"
"old_discovery": "Enable old discovery method",
"feature_device_tracking": "[%key:component::fritz::common::data_feature_device_tracking%]"
},
"data_description": {
"consider_home": "Time in seconds to consider a device at home. Default is 180 seconds.",
"old_discovery": "Enable old discovery method. This is needed for some scenarios."
"old_discovery": "Enable old discovery method. This is needed for some scenarios.",
"feature_device_tracking": "[%key:component::fritz::common::data_description_feature_device_tracking%]"
}
}
}

View File

@@ -39,9 +39,9 @@
"options": {
"step": {
"init": {
"title": "Configure Prefixes",
"title": "Configure prefixes",
"data": {
"prefixes": "Prefixes (comma separated list)"
"prefixes": "Prefixes (comma-separated list)"
}
}
},

View File

@@ -140,16 +140,16 @@
"ac_module_temperature_sensor_faulty_l3": "AC module temperature sensor faulty (L3)",
"dc_module_temperature_sensor_faulty": "DC module temperature sensor faulty",
"internal_processor_status": "Warning about the internal processor status. See status code for more information",
"eeprom_reinitialised": "EEPROM has been re-initialised",
"initialisation_error_usb_flash_drive_not_supported": "Initialisation error USB flash drive is not supported",
"initialisation_error_usb_stick_over_current": "Initialisation error Overcurrent on USB stick",
"eeprom_reinitialised": "EEPROM has been re-initialized",
"initialisation_error_usb_flash_drive_not_supported": "Initialization error USB flash drive is not supported",
"initialisation_error_usb_stick_over_current": "Initialization error Overcurrent on USB stick",
"no_usb_flash_drive_connected": "No USB flash drive connected",
"update_file_not_recognised_or_missing": "Update file not recognised or not present",
"update_file_not_recognised_or_missing": "Update file not recognized or not present",
"update_file_does_not_match_device": "Update file does not match the device, update file too old",
"write_or_read_error_occurred": "Write or read error occurred",
"file_could_not_be_opened": "File could not be opened",
"log_file_cannot_be_saved": "Log file cannot be saved (e.g. USB flash drive is write protected or full)",
"initialisation_error_file_system_error_on_usb": "Initialisation error in file system on USB flash drive",
"initialisation_error_file_system_error_on_usb": "Initialization error in file system on USB flash drive",
"error_during_logging_data_recording": "Error during recording of logging data",
"error_during_update_process": "Error occurred during update process",
"update_file_corrupt": "Update file corrupt",

View File

@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250506.0"]
"requirements": ["home-assistant-frontend==20250502.0"]
}

View File

@@ -2,13 +2,11 @@
from __future__ import annotations
import asyncio
import mimetypes
from pathlib import Path
from google.genai import Client
from google.genai.errors import APIError, ClientError
from google.genai.types import File, FileState
from requests.exceptions import Timeout
import voluptuous as vol
@@ -34,8 +32,6 @@ from .const import (
CONF_CHAT_MODEL,
CONF_PROMPT,
DOMAIN,
FILE_POLLING_INTERVAL_SECONDS,
LOGGER,
RECOMMENDED_CHAT_MODEL,
TIMEOUT_MILLIS,
)
@@ -95,40 +91,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
)
prompt_parts.append(uploaded_file)
async def wait_for_file_processing(uploaded_file: File) -> None:
"""Wait for file processing to complete."""
while True:
uploaded_file = await client.aio.files.get(
name=uploaded_file.name,
config={"http_options": {"timeout": TIMEOUT_MILLIS}},
)
if uploaded_file.state not in (
FileState.STATE_UNSPECIFIED,
FileState.PROCESSING,
):
break
LOGGER.debug(
"Waiting for file `%s` to be processed, current state: %s",
uploaded_file.name,
uploaded_file.state,
)
await asyncio.sleep(FILE_POLLING_INTERVAL_SECONDS)
if uploaded_file.state == FileState.FAILED:
raise HomeAssistantError(
f"File `{uploaded_file.name}` processing failed, reason: {uploaded_file.error.message}"
)
await hass.async_add_executor_job(append_files_to_prompt)
tasks = [
asyncio.create_task(wait_for_file_processing(part))
for part in prompt_parts
if isinstance(part, File) and part.state != FileState.ACTIVE
]
async with asyncio.timeout(TIMEOUT_MILLIS / 1000):
await asyncio.gather(*tasks)
try:
response = await client.aio.models.generate_content(
model=RECOMMENDED_CHAT_MODEL, contents=prompt_parts

View File

@@ -26,4 +26,3 @@ CONF_USE_GOOGLE_SEARCH_TOOL = "enable_google_search_tool"
RECOMMENDED_USE_GOOGLE_SEARCH_TOOL = False
TIMEOUT_MILLIS = 10000
FILE_POLLING_INTERVAL_SECONDS = 0.05

View File

@@ -1,6 +1,6 @@
"""Constants for the habitica integration."""
from homeassistant.const import APPLICATION_NAME, CONF_PATH, __version__
from homeassistant.const import APPLICATION_NAME, __version__
CONF_API_USER = "api_user"
@@ -13,15 +13,6 @@ HABITICANS_URL = "https://habitica.com/static/img/home-main@3x.ffc32b12.png"
DOMAIN = "habitica"
# service constants
SERVICE_API_CALL = "api_call"
ATTR_PATH = CONF_PATH
ATTR_ARGS = "args"
# event constants
EVENT_API_CALL_SUCCESS = f"{DOMAIN}_{SERVICE_API_CALL}_success"
ATTR_DATA = "data"
MANUFACTURER = "HabitRPG, Inc."
NAME = "Habitica"

View File

@@ -29,7 +29,7 @@ import voluptuous as vol
from homeassistant.components.todo import ATTR_RENAME
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_DATE, ATTR_NAME, CONF_NAME
from homeassistant.const import ATTR_DATE, ATTR_NAME
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@@ -38,28 +38,24 @@ from homeassistant.core import (
)
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.selector import ConfigEntrySelector
from homeassistant.util import dt as dt_util
from .const import (
ATTR_ADD_CHECKLIST_ITEM,
ATTR_ALIAS,
ATTR_ARGS,
ATTR_CLEAR_DATE,
ATTR_CLEAR_REMINDER,
ATTR_CONFIG_ENTRY,
ATTR_COST,
ATTR_COUNTER_DOWN,
ATTR_COUNTER_UP,
ATTR_DATA,
ATTR_DIRECTION,
ATTR_FREQUENCY,
ATTR_INTERVAL,
ATTR_ITEM,
ATTR_KEYWORD,
ATTR_NOTES,
ATTR_PATH,
ATTR_PRIORITY,
ATTR_REMINDER,
ATTR_REMOVE_CHECKLIST_ITEM,
@@ -78,10 +74,8 @@ from .const import (
ATTR_UNSCORE_CHECKLIST_ITEM,
ATTR_UP_DOWN,
DOMAIN,
EVENT_API_CALL_SUCCESS,
SERVICE_ABORT_QUEST,
SERVICE_ACCEPT_QUEST,
SERVICE_API_CALL,
SERVICE_CANCEL_QUEST,
SERVICE_CAST_SKILL,
SERVICE_CREATE_DAILY,
@@ -106,14 +100,6 @@ from .coordinator import HabiticaConfigEntry
_LOGGER = logging.getLogger(__name__)
SERVICE_API_CALL_SCHEMA = vol.Schema(
{
vol.Required(ATTR_NAME): str,
vol.Required(ATTR_PATH): vol.All(cv.ensure_list, [str]),
vol.Optional(ATTR_ARGS): dict,
}
)
SERVICE_CAST_SKILL_SCHEMA = vol.Schema(
{
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector({"integration": DOMAIN}),
@@ -266,46 +252,6 @@ def get_config_entry(hass: HomeAssistant, entry_id: str) -> HabiticaConfigEntry:
def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
"""Set up services for Habitica integration."""
async def handle_api_call(call: ServiceCall) -> None:
async_create_issue(
hass,
DOMAIN,
"deprecated_api_call",
breaks_in_ha_version="2025.6.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_api_call",
)
_LOGGER.warning(
"Deprecated action called: 'habitica.api_call' is deprecated and will be removed in Home Assistant version 2025.6.0"
)
name = call.data[ATTR_NAME]
path = call.data[ATTR_PATH]
entries: list[HabiticaConfigEntry] = hass.config_entries.async_entries(DOMAIN)
api = None
for entry in entries:
if entry.data[CONF_NAME] == name:
api = await entry.runtime_data.habitica.habitipy()
break
if api is None:
_LOGGER.error("API_CALL: User '%s' not configured", name)
return
try:
for element in path:
api = api[element]
except KeyError:
_LOGGER.error(
"API_CALL: Path %s is invalid for API on '{%s}' element", path, element
)
return
kwargs = call.data.get(ATTR_ARGS, {})
data = await api(**kwargs)
hass.bus.async_fire(
EVENT_API_CALL_SUCCESS, {ATTR_NAME: name, ATTR_PATH: path, ATTR_DATA: data}
)
async def cast_skill(call: ServiceCall) -> ServiceResponse:
"""Skill action."""
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
@@ -928,12 +874,6 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
schema=SERVICE_CREATE_TASK_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_API_CALL,
handle_api_call,
schema=SERVICE_API_CALL_SCHEMA,
)
hass.services.async_register(
DOMAIN,

View File

@@ -1,20 +1,4 @@
# Describes the format for Habitica service
api_call:
fields:
name:
required: true
example: "xxxNotAValidNickxxx"
selector:
text:
path:
required: true
example: '["tasks", "user", "post"]'
selector:
object:
args:
example: '{"text": "Use API from Home Assistant", "type": "todo"}'
selector:
object:
cast_skill:
fields:
config_entry: &config_entry

View File

@@ -526,31 +526,9 @@
"deprecated_entity": {
"title": "The Habitica {name} entity is deprecated",
"description": "The Habitica entity `{entity}` is deprecated and will be removed in a future release.\nPlease update your automations and scripts, disable `{entity}` and reload the integration/restart Home Assistant to fix this issue."
},
"deprecated_api_call": {
"title": "The Habitica action habitica.api_call is deprecated",
"description": "The Habitica action `habitica.api_call` is deprecated and will be removed in Home Assistant 2025.5.0.\n\nPlease update your automations and scripts to use other Habitica actions and entities."
}
},
"services": {
"api_call": {
"name": "API name",
"description": "Calls Habitica API.",
"fields": {
"name": {
"name": "[%key:common::config_flow::data::name%]",
"description": "Habitica's username to call for."
},
"path": {
"name": "[%key:common::config_flow::data::path%]",
"description": "Items from API URL in form of an array with method attached at the end. Consult https://habitica.com/apidoc/. Example uses https://habitica.com/apidoc/#api-Task-CreateUserTasks."
},
"args": {
"name": "Args",
"description": "Any additional JSON or URL parameter arguments. See apidoc mentioned for path. Example uses same API endpoint."
}
}
},
"cast_skill": {
"name": "Cast a skill",
"description": "Uses a skill or spell from your Habitica character on a specific task to affect its progress or status.",

View File

@@ -109,7 +109,6 @@ class HassIOIngress(HomeAssistantView):
delete = _handle
patch = _handle
options = _handle
head = _handle
async def _handle_websocket(
self, request: web.Request, token: str, path: str

View File

@@ -56,8 +56,8 @@
"options": {
"step": {
"init": {
"title": "HEOS Options",
"description": "You can sign-in to your HEOS Account to access favorites, streaming services, and other features. Clearing the credentials will sign-out of your account.",
"title": "HEOS options",
"description": "You can sign in to your HEOS Account to access favorites, streaming services, and other features. Clearing the credentials will sign out of your account.",
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
@@ -102,7 +102,7 @@
},
"move_queue_item": {
"name": "Move queue item",
"description": "Move one or more items within the play queue.",
"description": "Moves one or more items within the play queue.",
"fields": {
"queue_ids": {
"name": "Queue IDs",

View File

@@ -4,8 +4,6 @@ from __future__ import annotations
from typing import Any
from aiohomeconnect.client import Client as HomeConnectClient
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntry
@@ -14,7 +12,7 @@ from .coordinator import HomeConnectApplianceData, HomeConnectConfigEntry
async def _generate_appliance_diagnostics(
client: HomeConnectClient, appliance: HomeConnectApplianceData
appliance: HomeConnectApplianceData,
) -> dict[str, Any]:
return {
**appliance.info.to_dict(),
@@ -31,9 +29,7 @@ async def async_get_config_entry_diagnostics(
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return {
appliance.info.ha_id: await _generate_appliance_diagnostics(
entry.runtime_data.client, appliance
)
appliance.info.ha_id: await _generate_appliance_diagnostics(appliance)
for appliance in entry.runtime_data.data.values()
}
@@ -45,6 +41,4 @@ async def async_get_device_diagnostics(
ha_id = next(
(identifier[1] for identifier in device.identifiers if identifier[0] == DOMAIN),
)
return await _generate_appliance_diagnostics(
entry.runtime_data.client, entry.runtime_data.data[ha_id]
)
return await _generate_appliance_diagnostics(entry.runtime_data.data[ha_id])

View File

@@ -32,7 +32,6 @@ _LOGGER = logging.getLogger(__name__)
class HomeConnectEntity(CoordinatorEntity[HomeConnectCoordinator]):
"""Generic Home Connect entity (base class)."""
_attr_should_poll = False
_attr_has_entity_name = True
def __init__(

View File

@@ -39,11 +39,11 @@ PARALLEL_UPDATES = 1
class HomeConnectLightEntityDescription(LightEntityDescription):
"""Light entity description."""
brightness_key: SettingKey | None = None
brightness_key: SettingKey
brightness_scale: tuple[float, float]
color_key: SettingKey | None = None
enable_custom_color_value_key: str | None = None
custom_color_key: SettingKey | None = None
brightness_scale: tuple[float, float] = (0.0, 100.0)
LIGHTS: tuple[HomeConnectLightEntityDescription, ...] = (

View File

@@ -4,6 +4,20 @@
"codeowners": ["@DavidMStraub", "@Diegorro98", "@MartinHjelmare"],
"config_flow": true,
"dependencies": ["application_credentials", "repairs"],
"dhcp": [
{
"hostname": "balay-*",
"macaddress": "C8D778*"
},
{
"hostname": "(bosch|siemens)-*",
"macaddress": "68A40E*"
},
{
"hostname": "siemens-*",
"macaddress": "38B4D3*"
}
],
"documentation": "https://www.home-assistant.io/integrations/home_connect",
"iot_class": "cloud_push",
"loggers": ["aiohomeconnect"],

View File

@@ -11,6 +11,7 @@ from homeassistant.components.number import (
NumberEntity,
NumberEntityDescription,
)
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -79,7 +80,7 @@ NUMBERS = (
NumberEntityDescription(
key=SettingKey.COOKING_HOOD_COLOR_TEMPERATURE_PERCENT,
translation_key="color_temperature_percent",
native_unit_of_measurement="%",
native_unit_of_measurement=PERCENTAGE,
),
NumberEntityDescription(
key=SettingKey.LAUNDRY_CARE_WASHER_I_DOS_1_BASE_LEVEL,

View File

@@ -159,7 +159,6 @@ SENSORS = (
HomeConnectSensorEntityDescription(
key=StatusKey.BSH_COMMON_BATTERY_LEVEL,
device_class=SensorDeviceClass.BATTERY,
translation_key="battery_level",
),
HomeConnectSensorEntityDescription(
key=StatusKey.BSH_COMMON_VIDEO_CAMERA_STATE,

View File

@@ -1585,9 +1585,6 @@
"name": "Ristretto espresso cups",
"unit_of_measurement": "[%key:component::home_connect::entity::sensor::hot_water_cups_counter::unit_of_measurement%]"
},
"battery_level": {
"name": "Battery level"
},
"camera_state": {
"name": "Camera state",
"state": {

View File

@@ -79,7 +79,7 @@ class HomeConnectTimeEntity(HomeConnectEntity, TimeEntity):
async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass."""
await super().async_added_to_hass()
if self.bsh_key == SettingKey.BSH_COMMON_ALARM_CLOCK:
if self.bsh_key is SettingKey.BSH_COMMON_ALARM_CLOCK:
automations = automations_with_entity(self.hass, self.entity_id)
scripts = scripts_with_entity(self.hass, self.entity_id)
items = automations + scripts
@@ -123,7 +123,7 @@ class HomeConnectTimeEntity(HomeConnectEntity, TimeEntity):
async def async_will_remove_from_hass(self) -> None:
"""Call when entity will be removed from hass."""
if self.bsh_key == SettingKey.BSH_COMMON_ALARM_CLOCK:
if self.bsh_key is SettingKey.BSH_COMMON_ALARM_CLOCK:
async_delete_issue(
self.hass,
DOMAIN,

View File

@@ -23,7 +23,6 @@ from huawei_lte_api.exceptions import (
from requests.exceptions import Timeout
import voluptuous as vol
from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_HW_VERSION,
@@ -90,36 +89,7 @@ _LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=30)
NOTIFY_SCHEMA = vol.Any(
None,
vol.Schema(
{
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_RECIPIENT): vol.Any(
None, vol.All(cv.ensure_list, [cv.string])
),
}
),
)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.All(
cv.ensure_list,
[
vol.Schema(
{
vol.Required(CONF_URL): cv.url,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(NOTIFY_DOMAIN): NOTIFY_SCHEMA,
}
)
],
)
},
extra=vol.ALLOW_EXTRA,
)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
SERVICE_SCHEMA = vol.Schema({vol.Optional(CONF_URL): cv.url})

View File

@@ -37,6 +37,137 @@
"default": "mdi:antenna"
}
},
"sensor": {
"uptime": {
"default": "mdi:timer-outline"
},
"wan_ip_address": {
"default": "mdi:ip"
},
"wan_ipv6_address": {
"default": "mdi:ip"
},
"cell_id": {
"default": "mdi:antenna"
},
"cqi0": {
"default": "mdi:speedometer"
},
"cqi1": {
"default": "mdi:speedometer"
},
"enodeb_id": {
"default": "mdi:antenna"
},
"lac": {
"default": "mdi:map-marker"
},
"nei_cellid": {
"default": "mdi:antenna"
},
"nrcqi0": {
"default": "mdi:speedometer"
},
"nrcqi1": {
"default": "mdi:speedometer"
},
"pci": {
"default": "mdi:antenna"
},
"rac": {
"default": "mdi:map-marker"
},
"tac": {
"default": "mdi:map-marker"
},
"sms_unread": {
"default": "mdi:email-arrow-left"
},
"current_day_transfer": {
"default": "mdi:arrow-up-down-bold"
},
"current_month_download": {
"default": "mdi:download"
},
"current_month_upload": {
"default": "mdi:upload"
},
"wifi_clients_connected": {
"default": "mdi:wifi"
},
"primary_dns_server": {
"default": "mdi:ip"
},
"primary_ipv6_dns_server": {
"default": "mdi:ip"
},
"secondary_dns_server": {
"default": "mdi:ip"
},
"secondary_ipv6_dns_server": {
"default": "mdi:ip"
},
"current_connection_duration": {
"default": "mdi:timer-outline"
},
"current_connection_download": {
"default": "mdi:download"
},
"current_download_rate": {
"default": "mdi:download"
},
"current_connection_upload": {
"default": "mdi:upload"
},
"current_upload_rate": {
"default": "mdi:upload"
},
"total_connected_duration": {
"default": "mdi:timer-outline"
},
"total_download": {
"default": "mdi:download"
},
"total_upload": {
"default": "mdi:upload"
},
"sms_deleted_device": {
"default": "mdi:email-minus"
},
"sms_drafts_device": {
"default": "mdi:email-arrow-right-outline"
},
"sms_inbox_device": {
"default": "mdi:email"
},
"sms_capacity_device": {
"default": "mdi:email"
},
"sms_outbox_device": {
"default": "mdi:email-arrow-right"
},
"sms_unread_device": {
"default": "mdi:email-arrow-left"
},
"sms_drafts_sim": {
"default": "mdi:email-arrow-right-outline"
},
"sms_inbox_sim": {
"default": "mdi:email"
},
"sms_capacity_sim": {
"default": "mdi:email"
},
"sms_outbox_sim": {
"default": "mdi:email-arrow-right"
},
"sms_unread_sim": {
"default": "mdi:email-arrow-left"
},
"sms_messages_sim": {
"default": "mdi:email-arrow-left"
}
},
"switch": {
"mobile_data": {
"default": "mdi:signal-off",

View File

@@ -138,7 +138,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"uptime": HuaweiSensorEntityDescription(
key="uptime",
translation_key="uptime",
icon="mdi:timer-outline",
native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -146,14 +145,12 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"WanIPAddress": HuaweiSensorEntityDescription(
key="WanIPAddress",
translation_key="wan_ip_address",
icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True,
),
"WanIPv6Address": HuaweiSensorEntityDescription(
key="WanIPv6Address",
translation_key="wan_ipv6_address",
icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC,
),
},
@@ -181,19 +178,16 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"cell_id": HuaweiSensorEntityDescription(
key="cell_id",
translation_key="cell_id",
icon="mdi:antenna",
entity_category=EntityCategory.DIAGNOSTIC,
),
"cqi0": HuaweiSensorEntityDescription(
key="cqi0",
translation_key="cqi0",
icon="mdi:speedometer",
entity_category=EntityCategory.DIAGNOSTIC,
),
"cqi1": HuaweiSensorEntityDescription(
key="cqi1",
translation_key="cqi1",
icon="mdi:speedometer",
entity_category=EntityCategory.DIAGNOSTIC,
),
"dl_mcs": HuaweiSensorEntityDescription(
@@ -230,7 +224,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"enodeb_id": HuaweiSensorEntityDescription(
key="enodeb_id",
translation_key="enodeb_id",
icon="mdi:antenna",
entity_category=EntityCategory.DIAGNOSTIC,
),
"ims": HuaweiSensorEntityDescription(
@@ -241,7 +234,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"lac": HuaweiSensorEntityDescription(
key="lac",
translation_key="lac",
icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC,
),
"ltedlfreq": HuaweiSensorEntityDescription(
@@ -279,7 +271,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"nei_cellid": HuaweiSensorEntityDescription(
key="nei_cellid",
translation_key="nei_cellid",
icon="mdi:antenna",
entity_category=EntityCategory.DIAGNOSTIC,
),
"nrbler": HuaweiSensorEntityDescription(
@@ -290,13 +281,11 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"nrcqi0": HuaweiSensorEntityDescription(
key="nrcqi0",
translation_key="nrcqi0",
icon="mdi:speedometer",
entity_category=EntityCategory.DIAGNOSTIC,
),
"nrcqi1": HuaweiSensorEntityDescription(
key="nrcqi1",
translation_key="nrcqi1",
icon="mdi:speedometer",
entity_category=EntityCategory.DIAGNOSTIC,
),
"nrdlbandwidth": HuaweiSensorEntityDescription(
@@ -376,7 +365,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"pci": HuaweiSensorEntityDescription(
key="pci",
translation_key="pci",
icon="mdi:antenna",
entity_category=EntityCategory.DIAGNOSTIC,
),
"plmn": HuaweiSensorEntityDescription(
@@ -387,7 +375,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"rac": HuaweiSensorEntityDescription(
key="rac",
translation_key="rac",
icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC,
),
"rrc_status": HuaweiSensorEntityDescription(
@@ -458,7 +445,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"tac": HuaweiSensorEntityDescription(
key="tac",
translation_key="tac",
icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC,
),
"tdd": HuaweiSensorEntityDescription(
@@ -522,7 +508,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"UnreadMessage": HuaweiSensorEntityDescription(
key="UnreadMessage",
translation_key="sms_unread",
icon="mdi:email-arrow-left",
),
},
),
@@ -536,7 +521,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_day_transfer",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:arrow-up-down-bold",
state_class=SensorStateClass.TOTAL,
last_reset_item="CurrentDayDuration",
last_reset_format_fn=format_last_reset_elapsed_seconds,
@@ -546,7 +530,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_month_download",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:download",
state_class=SensorStateClass.TOTAL,
last_reset_item="MonthDuration",
last_reset_format_fn=format_last_reset_elapsed_seconds,
@@ -556,7 +539,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_month_upload",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:upload",
state_class=SensorStateClass.TOTAL,
last_reset_item="MonthDuration",
last_reset_format_fn=format_last_reset_elapsed_seconds,
@@ -580,32 +562,27 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"CurrentWifiUser": HuaweiSensorEntityDescription(
key="CurrentWifiUser",
translation_key="wifi_clients_connected",
icon="mdi:wifi",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
"PrimaryDns": HuaweiSensorEntityDescription(
key="PrimaryDns",
translation_key="primary_dns_server",
icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC,
),
"PrimaryIPv6Dns": HuaweiSensorEntityDescription(
key="PrimaryIPv6Dns",
translation_key="primary_ipv6_dns_server",
icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC,
),
"SecondaryDns": HuaweiSensorEntityDescription(
key="SecondaryDns",
translation_key="secondary_dns_server",
icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC,
),
"SecondaryIPv6Dns": HuaweiSensorEntityDescription(
key="SecondaryIPv6Dns",
translation_key="secondary_ipv6_dns_server",
icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC,
),
},
@@ -618,14 +595,12 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_connection_duration",
native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
icon="mdi:timer-outline",
),
"CurrentDownload": HuaweiSensorEntityDescription(
key="CurrentDownload",
translation_key="current_connection_download",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING,
),
"CurrentDownloadRate": HuaweiSensorEntityDescription(
@@ -633,7 +608,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_download_rate",
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
icon="mdi:download",
state_class=SensorStateClass.MEASUREMENT,
),
"CurrentUpload": HuaweiSensorEntityDescription(
@@ -641,7 +615,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_connection_upload",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING,
),
"CurrentUploadRate": HuaweiSensorEntityDescription(
@@ -649,7 +622,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="current_upload_rate",
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
icon="mdi:upload",
state_class=SensorStateClass.MEASUREMENT,
),
"TotalConnectTime": HuaweiSensorEntityDescription(
@@ -657,7 +629,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="total_connected_duration",
native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
icon="mdi:timer-outline",
state_class=SensorStateClass.TOTAL_INCREASING,
),
"TotalDownload": HuaweiSensorEntityDescription(
@@ -665,7 +636,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="total_download",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING,
),
"TotalUpload": HuaweiSensorEntityDescription(
@@ -673,7 +643,6 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
translation_key="total_upload",
native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING,
),
},
@@ -719,62 +688,50 @@ SENSOR_META: dict[str, HuaweiSensorGroup] = {
"LocalDeleted": HuaweiSensorEntityDescription(
key="LocalDeleted",
translation_key="sms_deleted_device",
icon="mdi:email-minus",
),
"LocalDraft": HuaweiSensorEntityDescription(
key="LocalDraft",
translation_key="sms_drafts_device",
icon="mdi:email-arrow-right-outline",
),
"LocalInbox": HuaweiSensorEntityDescription(
key="LocalInbox",
translation_key="sms_inbox_device",
icon="mdi:email",
),
"LocalMax": HuaweiSensorEntityDescription(
key="LocalMax",
translation_key="sms_capacity_device",
icon="mdi:email",
),
"LocalOutbox": HuaweiSensorEntityDescription(
key="LocalOutbox",
translation_key="sms_outbox_device",
icon="mdi:email-arrow-right",
),
"LocalUnread": HuaweiSensorEntityDescription(
key="LocalUnread",
translation_key="sms_unread_device",
icon="mdi:email-arrow-left",
),
"SimDraft": HuaweiSensorEntityDescription(
key="SimDraft",
translation_key="sms_drafts_sim",
icon="mdi:email-arrow-right-outline",
),
"SimInbox": HuaweiSensorEntityDescription(
key="SimInbox",
translation_key="sms_inbox_sim",
icon="mdi:email",
),
"SimMax": HuaweiSensorEntityDescription(
key="SimMax",
translation_key="sms_capacity_sim",
icon="mdi:email",
),
"SimOutbox": HuaweiSensorEntityDescription(
key="SimOutbox",
translation_key="sms_outbox_sim",
icon="mdi:email-arrow-right",
),
"SimUnread": HuaweiSensorEntityDescription(
key="SimUnread",
translation_key="sms_unread_sim",
icon="mdi:email-arrow-left",
),
"SimUsed": HuaweiSensorEntityDescription(
key="SimUsed",
translation_key="sms_messages_sim",
icon="mdi:email-arrow-left",
),
},
),
@@ -870,7 +827,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity):
"""Return icon for sensor."""
if self.entity_description.icon_fn:
return self.entity_description.icon_fn(self.state)
return self.entity_description.icon
return super().icon
@property
def device_class(self) -> SensorDeviceClass | None:

View File

@@ -61,7 +61,7 @@
},
"data_description": {
"name": "Used to distinguish between notification services in case there are multiple Huawei LTE devices configured. Changes to this option value take effect after Home Assistant restart.",
"recipient": "Comma separated list of default recipient SMS phone numbers for the notification service, used in case the notification sender does not specify any.",
"recipient": "Comma-separated list of default recipient SMS phone numbers for the notification service, used in case the notification sender does not specify any.",
"track_wired_clients": "Whether the device tracker entities track also clients attached to the router's wired Ethernet network, in addition to wireless clients.",
"unauthenticated_mode": "Whether to run in unauthenticated mode. Unauthenticated mode provides a limited set of features, but may help in case there are problems accessing the router's web interface from a browser while the integration is active. Changes to this option value take effect after integration reload."
}

View File

@@ -3,29 +3,18 @@
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING
from aioautomower.model import MowerActivities, MowerAttributes
from homeassistant.components.automation import automations_with_entity
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.components.script import scripts_with_entity
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from . import AutomowerConfigEntry
from .const import DOMAIN
from .coordinator import AutomowerDataUpdateCoordinator
from .entity import AutomowerBaseEntity
@@ -34,13 +23,6 @@ _LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
def entity_used_in(hass: HomeAssistant, entity_id: str) -> list[str]:
"""Get list of related automations and scripts."""
used_in = automations_with_entity(hass, entity_id)
used_in += scripts_with_entity(hass, entity_id)
return used_in
@dataclass(frozen=True, kw_only=True)
class AutomowerBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes Automower binary sensor entity."""
@@ -59,12 +41,6 @@ MOWER_BINARY_SENSOR_TYPES: tuple[AutomowerBinarySensorEntityDescription, ...] =
translation_key="leaving_dock",
value_fn=lambda data: data.mower.activity == MowerActivities.LEAVING,
),
AutomowerBinarySensorEntityDescription(
key="returning_to_dock",
translation_key="returning_to_dock",
value_fn=lambda data: data.mower.activity == MowerActivities.GOING_HOME,
entity_registry_enabled_default=False,
),
)
@@ -107,39 +83,3 @@ class AutomowerBinarySensorEntity(AutomowerBaseEntity, BinarySensorEntity):
def is_on(self) -> bool:
"""Return the state of the binary sensor."""
return self.entity_description.value_fn(self.mower_attributes)
async def async_added_to_hass(self) -> None:
"""Raise issue when entity is registered and was not disabled."""
if TYPE_CHECKING:
assert self.unique_id
if not (
entity_id := er.async_get(self.hass).async_get_entity_id(
BINARY_SENSOR_DOMAIN, DOMAIN, self.unique_id
)
):
return
if (
self.enabled
and self.entity_description.key == "returning_to_dock"
and entity_used_in(self.hass, entity_id)
):
async_create_issue(
self.hass,
DOMAIN,
f"deprecated_entity_{self.entity_description.key}",
breaks_in_ha_version="2025.6.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_entity",
translation_placeholders={
"entity_name": str(self.name),
"entity": entity_id,
},
)
else:
async_delete_issue(
self.hass,
DOMAIN,
f"deprecated_task_entity_{self.entity_description.key}",
)
await super().async_added_to_hass()

View File

@@ -67,7 +67,9 @@ rules:
reconfiguration-flow:
status: exempt
comment: no configuration possible
repair-issues: done
repair-issues:
status: exempt
comment: no issues available
stale-devices: done
# Platinum

View File

@@ -19,10 +19,10 @@ _LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 1
HEADLIGHT_MODES: list = [
HeadlightModes.ALWAYS_OFF.lower(),
HeadlightModes.ALWAYS_ON.lower(),
HeadlightModes.EVENING_AND_NIGHT.lower(),
HeadlightModes.EVENING_ONLY.lower(),
HeadlightModes.ALWAYS_OFF,
HeadlightModes.ALWAYS_ON,
HeadlightModes.EVENING_AND_NIGHT,
HeadlightModes.EVENING_ONLY,
]
@@ -65,13 +65,11 @@ class AutomowerSelectEntity(AutomowerControlEntity, SelectEntity):
@property
def current_option(self) -> str:
"""Return the current option for the entity."""
return cast(
HeadlightModes, self.mower_attributes.settings.headlight.mode
).lower()
return cast(HeadlightModes, self.mower_attributes.settings.headlight.mode)
@handle_sending_exception()
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.coordinator.api.commands.set_headlight_mode(
self.mower_id, cast(HeadlightModes, option.upper())
self.mower_id, HeadlightModes(option)
)

View File

@@ -39,9 +39,6 @@
"binary_sensor": {
"leaving_dock": {
"name": "Leaving dock"
},
"returning_to_dock": {
"name": "Returning to dock"
}
},
"button": {
@@ -323,12 +320,6 @@
}
}
},
"issues": {
"deprecated_entity": {
"title": "The Husqvarna Automower {entity_name} sensor is deprecated",
"description": "The Husqvarna Automower entity `{entity}` is deprecated and will be removed in a future release.\nYou can use the new returning state of the lawn mower entity instead.\nPlease update your automations and scripts to replace the sensor entity with the newly added lawn mower entity.\nWhen you are done migrating you can disable `{entity}`."
}
},
"services": {
"override_schedule": {
"name": "Override schedule",

View File

@@ -10,7 +10,9 @@ import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import DOMAIN, LOGGER
@@ -62,3 +64,19 @@ class KnockiConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
data_schema=DATA_SCHEMA,
)
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle a DHCP discovery."""
device_registry = dr.async_get(self.hass)
if device_entry := device_registry.async_get_device(
identifiers={(DOMAIN, discovery_info.hostname)}
):
device_registry.async_update_device(
device_entry.id,
new_connections={
(dr.CONNECTION_NETWORK_MAC, discovery_info.macaddress)
},
)
return await super().async_step_dhcp(discovery_info)

View File

@@ -3,6 +3,11 @@
"name": "Knocki",
"codeowners": ["@joostlek", "@jgatto1", "@JakeBosh"],
"config_flow": true,
"dhcp": [
{
"hostname": "knc*"
}
],
"documentation": "https://www.home-assistant.io/integrations/knocki",
"integration_type": "hub",
"iot_class": "cloud_push",

View File

@@ -50,10 +50,8 @@ rules:
# Gold
devices: done
diagnostics: todo
discovery-update-info:
status: exempt
comment: This is a cloud service and does not benefit from device updates.
discovery: todo
discovery-update-info: done
discovery: done
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo

View File

@@ -37,5 +37,5 @@
"iot_class": "cloud_push",
"loggers": ["pylamarzocco"],
"quality_scale": "platinum",
"requirements": ["pylamarzocco==2.0.0"]
"requirements": ["pylamarzocco==2.0.0b7"]
}

View File

@@ -17,7 +17,7 @@ from .coordinator import LitterRobotDataUpdateCoordinator
_WhiskerEntityT = TypeVar("_WhiskerEntityT", bound=Robot | Pet)
def get_device_info(whisker_entity: _WhiskerEntityT) -> DeviceInfo:
def get_device_info(whisker_entity: Robot | Pet) -> DeviceInfo:
"""Get device info for a robot or pet."""
if isinstance(whisker_entity, Robot):
return DeviceInfo(

View File

@@ -36,6 +36,11 @@ _LOGGER = logging.getLogger(__name__)
PRODID = "-//homeassistant.io//local_calendar 1.0//EN"
# The calendar on disk is only changed when this entity is updated, so there
# is no need to poll for changes. The calendar enttiy base class will handle
# refreshing the entity state based on the start or end time of the event.
SCAN_INTERVAL = timedelta(days=1)
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -93,6 +93,7 @@ async def async_setup_intents(hass: HomeAssistant) -> None:
DOMAIN,
SERVICE_VOLUME_SET,
required_domains={DOMAIN},
required_states={MediaPlayerState.PLAYING},
required_features=MediaPlayerEntityFeature.VOLUME_SET,
required_slots={
ATTR_MEDIA_VOLUME_LEVEL: intent.IntentSlotInfo(
@@ -158,6 +159,7 @@ class MediaUnpauseHandler(intent.ServiceIntentHandler):
DOMAIN,
SERVICE_MEDIA_PLAY,
required_domains={DOMAIN},
required_states={MediaPlayerState.PAUSED},
description="Resumes a media player",
platforms={DOMAIN},
device_classes={MediaPlayerDeviceClass},

View File

@@ -57,8 +57,8 @@ ATA_HVAC_MODE_REVERSE_LOOKUP = {v: k for k, v in ATA_HVAC_MODE_LOOKUP.items()}
ATW_ZONE_HVAC_MODE_LOOKUP = {
atw.ZONE_STATUS_HEAT: HVACMode.HEAT,
atw.ZONE_STATUS_COOL: HVACMode.COOL,
atw.ZONE_OPERATION_MODE_HEAT: HVACMode.HEAT,
atw.ZONE_OPERATION_MODE_COOL: HVACMode.COOL,
}
ATW_ZONE_HVAC_MODE_REVERSE_LOOKUP = {v: k for k, v in ATW_ZONE_HVAC_MODE_LOOKUP.items()}

View File

@@ -2,6 +2,8 @@
from enum import IntEnum
from pymiele import MieleEnum
DOMAIN = "miele"
MANUFACTURER = "Miele"
@@ -246,6 +248,7 @@ STATE_PROGRAM_PHASE_OVEN = {
}
STATE_PROGRAM_PHASE_WARMING_DRAWER = {
0: "not_running",
3073: "heating_up",
3075: "door_open",
3094: "keeping_warm",
3088: "cooling_down",
@@ -324,13 +327,17 @@ STATE_PROGRAM_PHASE: dict[int, dict[int, str]] = {
MieleAppliance.ROBOT_VACUUM_CLEANER: STATE_PROGRAM_PHASE_ROBOT_VACUUM_CLEANER,
}
STATE_PROGRAM_TYPE = {
0: "normal_operation_mode",
1: "own_program",
2: "automatic_program",
3: "cleaning_care_program",
4: "maintenance_program",
}
class StateProgramType(MieleEnum):
"""Defines program types."""
normal_operation_mode = 0
own_program = 1
automatic_program = 2
cleaning_care_program = 3
maintenance_program = 4
unknown = -9999
WASHING_MACHINE_PROGRAM_ID: dict[int, str] = {
-1: "no_program", # Extrapolated from other device types.
@@ -404,14 +411,21 @@ DISHWASHER_PROGRAM_ID: dict[int, str] = {
TUMBLE_DRYER_PROGRAM_ID: dict[int, str] = {
-1: "no_program", # Extrapolated from other device types.
0: "no_program", # Extrapolated from other device types
2: "cottons",
3: "minimum_iron",
4: "woollens_handcare",
5: "delicates",
6: "warm_air",
8: "express",
10: "automatic_plus",
20: "cottons",
23: "cottons_hygiene",
30: "minimum_iron",
31: "gentle_minimum_iron",
31: "bed_linen",
40: "woollens_handcare",
50: "delicates",
60: "warm_air",
66: "eco",
70: "cool_air",
80: "express",
90: "cottons",
@@ -449,17 +463,29 @@ OVEN_PROGRAM_ID: dict[int, str] = {
31: "bottom_heat",
35: "moisture_plus_auto_roast",
40: "moisture_plus_fan_plus",
48: "moisture_plus_auto_roast",
49: "moisture_plus_fan_plus",
50: "moisture_plus_intensive_bake",
51: "moisture_plus_conventional_heat",
74: "moisture_plus_intensive_bake",
76: "moisture_plus_conventional_heat",
49: "moisture_plus_fan_plus",
323: "pyrolytic",
326: "descale",
335: "shabbat_program",
336: "yom_tov",
356: "defrost",
357: "drying",
358: "heat_crockery",
360: "low_temperature_cooking",
361: "steam_cooking",
362: "keeping_warm",
512: "1_tray",
513: "2_trays",
529: "baking_tray",
554: "baiser_one_large",
555: "baiser_several_small",
556: "lemon_meringue_pie",
557: "viennese_apple_strudel",
621: "prove_15_min",
622: "prove_30_min",
623: "prove_45_min",
@@ -673,7 +699,7 @@ STEAM_OVEN_MICRO_PROGRAM_ID: dict[int, str] = {
2019: "defrosting_with_steam",
2020: "blanching",
2021: "bottling",
2022: "heat_crockery",
2022: "sterilize_crockery",
2023: "prove_dough",
2027: "soak",
2029: "reheating_with_microwave",
@@ -745,7 +771,7 @@ STEAM_OVEN_MICRO_PROGRAM_ID: dict[int, str] = {
2129: "potatoes_floury_diced",
2130: "german_turnip_sliced",
2131: "german_turnip_cut_into_batons",
2132: "german_turnip_sliced",
2132: "german_turnip_diced",
2133: "pumpkin_diced",
2134: "corn_on_the_cob",
2135: "mangel_cut",

View File

@@ -30,9 +30,9 @@ from homeassistant.helpers.typing import StateType
from .const import (
STATE_PROGRAM_ID,
STATE_PROGRAM_PHASE,
STATE_PROGRAM_TYPE,
STATE_STATUS_TAGS,
MieleAppliance,
StateProgramType,
StateStatus,
)
from .coordinator import MieleConfigEntry, MieleDataUpdateCoordinator
@@ -144,6 +144,7 @@ SENSOR_TYPES: Final[tuple[MieleSensorDefinition, ...]] = (
MieleAppliance.STEAM_OVEN,
MieleAppliance.MICROWAVE,
MieleAppliance.COFFEE_SYSTEM,
MieleAppliance.ROBOT_VACUUM_CLEANER,
MieleAppliance.WASHER_DRYER,
MieleAppliance.STEAM_OVEN_COMBI,
MieleAppliance.STEAM_OVEN_MICRO,
@@ -180,10 +181,10 @@ SENSOR_TYPES: Final[tuple[MieleSensorDefinition, ...]] = (
description=MieleSensorDescription(
key="state_program_type",
translation_key="program_type",
value_fn=lambda value: value.state_program_type,
value_fn=lambda value: StateProgramType(value.state_program_type).name,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=sorted(set(STATE_PROGRAM_TYPE.values())),
options=sorted(set(StateProgramType.keys())),
),
),
MieleSensorDefinition(
@@ -439,8 +440,6 @@ async def async_setup_entry(
entity_class = MieleProgramIdSensor
case "state_program_phase":
entity_class = MielePhaseSensor
case "state_program_type":
entity_class = MieleTypeSensor
case _:
entity_class = MieleSensor
if (
@@ -552,22 +551,6 @@ class MielePhaseSensor(MieleSensor):
)
class MieleTypeSensor(MieleSensor):
"""Representation of the program type sensor."""
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
ret_val = STATE_PROGRAM_TYPE.get(int(self.device.state_program_type))
if ret_val is None:
_LOGGER.debug(
"Unknown program type: %s on device type: %s",
self.device.state_program_type,
self.device.device_type,
)
return ret_val
class MieleProgramIdSensor(MieleSensor):
"""Representation of the program id sensor."""

View File

@@ -313,6 +313,8 @@
"automatic_plus": "Automatic plus",
"baking_tray": "Baking tray",
"barista_assistant": "BaristaAssistant",
"baser_one_large": "Baiser one large",
"baser_severall_small": "Baiser several small",
"basket_program": "Basket program",
"basmati_rice_rapid_steam_cooking": "Basmati rice (rapid steam cooking)",
"basmati_rice_steam_cooking": "Basmati rice (steam cooking)",
@@ -468,7 +470,7 @@
"gentle_minimum_iron": "Gentle minimum iron",
"gentle_smoothing": "Gentle smoothing",
"german_turnip_cut_into_batons": "German turnip (cut into batons)",
"german_turnip_sliced": "German turnip (sliced)",
"german_turnip_diced": "German turnip (diced)",
"gilt_head_bream_fillet": "Gilt-head bream (fillet)",
"gilt_head_bream_whole": "Gilt-head bream (whole)",
"glasses_warm": "Glasses warm",
@@ -489,7 +491,6 @@
"greenage_plums": "Greenage plums",
"halibut_fillet_2_cm": "Halibut (fillet, 2 cm)",
"halibut_fillet_3_cm": "Halibut (fillet, 3 cm)",
"heat_crockery": "Heat crockery",
"heating_damp_flannels": "Heating damp flannels",
"hens_eggs_size_l_hard": "Hens eggs (size „L“, hard)",
"hens_eggs_size_l_medium": "Hens eggs (size „L“, medium)",
@@ -529,9 +530,11 @@
"latte_macchiato": "Latte macchiato",
"leek_pieces": "Leek (pieces)",
"leek_rings": "Leek (rings)",
"lemon_meringue_pie": "Lemon meringue pie",
"long_coffee": "Long coffee",
"long_grain_rice_general_rapid_steam_cooking": "Long grain rice (general, rapid steam cooking)",
"long_grain_rice_general_steam_cooking": "Long grain rice (general, steam cooking)",
"low_temperature_cooking": "Low temperature cooking",
"maintenance": "Maintenance program",
"make_yoghurt": "Make yoghurt",
"mangel_cut": "Mangel (cut)",
@@ -670,6 +673,7 @@
"prove_dough": "Prove dough",
"pumpkin_diced": "Pumpkin (diced)",
"pumpkin_soup": "Pumpkin soup",
"pyrolytic": "Pyrolytic",
"quick_mw": "Quick MW",
"quick_power_wash": "QuickPowerWash",
"quinces_diced": "Quinces (diced)",
@@ -722,6 +726,7 @@
"sea_devil_fillet_3_cm": "Sea devil (fillet, 3 cm)",
"sea_devil_fillet_4_cm": "Sea devil (fillet, 4 cm)",
"separate_rinse_starch": "Separate rinse/starch",
"shabbat_program": "Shabbat program",
"sheyang_rapid_steam_cooking": "Sheyang (rapid steam cooking)",
"sheyang_steam_cooking": "Sheyang (steam cooking)",
"shirts": "Shirts",
@@ -752,6 +757,7 @@
"steam_care": "Steam care",
"steam_cooking": "Steam cooking",
"steam_smoothing": "Steam smoothing",
"sterilize_crockery": "Sterilize crockery",
"stuffed_cabbage": "Stuffed cabbage",
"sweat_onions": "Sweat onions",
"swede_cut_into_batons": "Swede (cut into batons)",
@@ -790,6 +796,7 @@
"veal_sausages": "Veal sausages",
"venus_clams": "Venus clams",
"very_hot_water": "Very hot water",
"viennese_apple_strudel": "Viennese apple strudel",
"viennese_silverside": "Viennese silverside",
"warm_air": "Warm air",
"wheat_cracked": "Wheat (cracked)",
@@ -814,6 +821,7 @@
"yellow_beans_cut": "Yellow beans (cut)",
"yellow_beans_whole": "Yellow beans (whole)",
"yellow_split_peas": "Yellow split peas",
"yom_tov": "Yom tov",
"zander_fillet": "Zander (fillet)"
}
},

View File

@@ -465,7 +465,7 @@ class PlatformField:
required: bool
validator: Callable[..., Any]
error: str | None = None
default: str | int | bool | None | vol.Undefined = vol.UNDEFINED
default: str | int | bool | vol.Undefined = vol.UNDEFINED
is_schema_default: bool = False
exclude_from_reconfig: bool = False
conditions: tuple[dict[str, Any], ...] | None = None
@@ -498,7 +498,8 @@ def validate_light_platform_config(user_data: dict[str, Any]) -> dict[str, str]:
if user_data.get(CONF_MIN_KELVIN, DEFAULT_MIN_KELVIN) >= user_data.get(
CONF_MAX_KELVIN, DEFAULT_MAX_KELVIN
):
errors["advanced_settings"] = "max_below_min_kelvin"
errors[CONF_MAX_KELVIN] = "max_below_min_kelvin"
errors[CONF_MIN_KELVIN] = "max_below_min_kelvin"
return errors
@@ -514,7 +515,6 @@ COMMON_ENTITY_FIELDS = {
required=False,
validator=str,
exclude_from_reconfig=True,
default=None,
),
CONF_ENTITY_PICTURE: PlatformField(
selector=TEXT_SELECTOR, required=False, validator=cv.url, error="invalid_url"
@@ -1150,7 +1150,7 @@ ENTITY_CONFIG_VALIDATOR: dict[
}
MQTT_DEVICE_PLATFORM_FIELDS = {
ATTR_NAME: PlatformField(selector=TEXT_SELECTOR, required=True, validator=str),
ATTR_NAME: PlatformField(selector=TEXT_SELECTOR, required=False, validator=str),
ATTR_SW_VERSION: PlatformField(
selector=TEXT_SELECTOR, required=False, validator=str
),
@@ -1275,10 +1275,7 @@ def validate_user_input(
try:
validator(value)
except (ValueError, vol.Error, vol.Invalid):
data_schema_field = data_schema_fields[field]
errors[data_schema_field.section or field] = (
data_schema_field.error or "invalid_input"
)
errors[field] = data_schema_fields[field].error or "invalid_input"
if config_validator is not None:
if TYPE_CHECKING:
@@ -1327,10 +1324,7 @@ def data_schema_from_fields(
vol.Required(field_name, default=field_details.default)
if field_details.required
else vol.Optional(
field_name,
default=field_details.default
if field_details.default is not None
else vol.UNDEFINED,
field_name, default=field_details.default
): field_details.selector(component_data_with_user_input) # type: ignore[operator]
if field_details.custom_filtering
else field_details.selector
@@ -1381,17 +1375,12 @@ def data_schema_from_fields(
@callback
def subentry_schema_default_data_from_fields(
data_schema_fields: dict[str, PlatformField],
component_data: dict[str, Any],
) -> dict[str, Any]:
"""Generate custom data schema from platform fields or device data."""
return {
key: field.default
for key, field in data_schema_fields.items()
if _check_conditions(field, component_data)
and (
field.is_schema_default
or (field.default is not vol.UNDEFINED and key not in component_data)
)
if field.is_schema_default
}
@@ -2217,10 +2206,7 @@ class MQTTSubentryFlowHandler(ConfigSubentryFlow):
for component_data in self._subentry_data["components"].values():
platform = component_data[CONF_PLATFORM]
subentry_default_data = subentry_schema_default_data_from_fields(
COMMON_ENTITY_FIELDS
| PLATFORM_ENTITY_FIELDS[platform]
| PLATFORM_MQTT_FIELDS[platform],
component_data,
PLATFORM_ENTITY_FIELDS[platform]
)
component_data.update(subentry_default_data)

View File

@@ -244,7 +244,6 @@
"title": "Configure MQTT device \"{mqtt_device}\"",
"description": "Please configure MQTT specific details for {platform} entity \"{entity}\":",
"data": {
"on_command_type": "ON command type",
"blue_template": "Blue template",
"brightness_template": "Brightness template",
"command_template": "Command template",
@@ -255,6 +254,7 @@
"force_update": "Force update",
"green_template": "Green template",
"last_reset_value_template": "Last reset value template",
"on_command_type": "ON command type",
"optimistic": "Optimistic",
"payload_off": "Payload \"off\"",
"payload_on": "Payload \"on\"",
@@ -275,19 +275,19 @@
"command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to render the payload to be published at the command topic.",
"command_topic": "The publishing topic that will be used to control the {platform} entity. [Learn more.]({url}#command_topic)",
"color_temp_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract color temperature in Kelvin from the state payload value. Expected result of the template is an integer.",
"force_update": "Sends update events even if the value hasnt changed. Useful if you want to have meaningful value graphs in history. [Learn more.]({url}#force_update)",
"green_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract green color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"last_reset_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the last reset. When Last reset template is set, the State class option must be Total. [Learn more.]({url}#last_reset_value_template)",
"force_update": "Sends update events even if the value hasnt changed. Useful if you want to have meaningful value graphs in history. [Learn more.]({url}#force_update)",
"on_command_type": "Defines when the payload \"on\" is sent. Using \"Last\" (the default) will send any style (brightness, color, etc) topics first and then a payload \"on\" to the command topic. Using \"First\" will send the payload \"on\" and then any style topics. Using \"Brightness\" will only send brightness commands instead of the payload \"on\" to turn the light on.",
"optimistic": "Flag that defines if the {platform} entity works in optimistic mode. [Learn more.]({url}#optimistic)",
"payload_off": "The payload that represents the off state.",
"payload_on": "The payload that represents the on state.",
"payload_off": "The payload that represents the \"off\" state.",
"payload_on": "The payload that represents the \"on\" state.",
"qos": "The QoS value a {platform} entity should use.",
"red_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract red color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker.",
"state_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract state from the state payload value.",
"state_topic": "The MQTT topic subscribed to receive {platform} state values. [Learn more.]({url}#state_topic)",
"supported_color_modes": "A list of color modes supported by the list. Possible color modes are On/Off, Brightness, Color temperature, HS, XY, RGB, RGBW, RGBWW, White. Note that if On/Off or Brightness are used, that must be the only value in the list. [Learn more.]({url}#supported_color_modes)",
"supported_color_modes": "A list of color modes supported by the light. Possible color modes are On/Off, Brightness, Color temperature, HS, XY, RGB, RGBW, RGBWW, White. Note that if On/Off or Brightness are used, that must be the only value in the list. [Learn more.]({url}#supported_color_modes)",
"value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the {platform} entity value. [Learn more.]({url}#value_template)"
},
"sections": {

View File

@@ -12,5 +12,5 @@
"documentation": "https://www.home-assistant.io/integrations/nexia",
"iot_class": "cloud_polling",
"loggers": ["nexia"],
"requirements": ["nexia==2.7.0"]
"requirements": ["nexia==2.9.0"]
}

View File

@@ -23,9 +23,9 @@
"user": {
"description": "Configure hosts to be scanned by Nmap. Network address and excludes can be IP addresses (192.168.1.1), IP networks (192.168.0.0/24) or IP ranges (192.168.1.0-32).",
"data": {
"hosts": "Network addresses (comma separated) to scan",
"hosts": "Network addresses (comma-separated) to scan",
"home_interval": "Minimum number of minutes between scans of active devices (preserve battery)",
"exclude": "Network addresses (comma separated) to exclude from scanning",
"exclude": "Network addresses (comma-separated) to exclude from scanning",
"scan_options": "Raw configurable scan options for Nmap"
}
}

View File

@@ -4,7 +4,7 @@
"user": {
"data": {
"county": "County",
"phone_number": "Phone Number"
"phone_number": "Phone number"
},
"data_description": {
"county": "County used for outage number retrieval",

View File

@@ -29,8 +29,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: RehlkoConfigEntry) -> bo
"""Set up Rehlko from a config entry."""
websession = async_get_clientsession(hass)
rehlko = AioKem(session=websession)
# If requests take more than 20 seconds; timeout and let the setup retry.
rehlko.set_timeout(20)
async def async_refresh_token_update(refresh_token: str) -> None:
"""Handle refresh token update."""
@@ -89,8 +87,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: RehlkoConfigEntry) -> bo
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Retrys enabled after successful connection to prevent blocking startup
rehlko.set_retry_policy(retry_count=3, retry_delays=[5, 10, 20])
# Rehlko service can be slow to respond, increase timeout for polls.
rehlko.set_timeout(100)
return True

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["renault_api"],
"quality_scale": "silver",
"requirements": ["renault-api==0.3.1"]
"requirements": ["renault-api==0.3.0"]
}

View File

@@ -2,7 +2,7 @@
"config": {
"step": {
"user": {
"title": "Sign-in with Ring account",
"title": "Sign in with Ring account",
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"

View File

@@ -53,7 +53,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) ->
)
_LOGGER.debug("Getting home data")
try:
home_data = await api_client.get_home_data_v2(user_data)
home_data = await api_client.get_home_data_v3(user_data)
except RoborockInvalidCredentials as err:
raise ConfigEntryAuthFailed(
"Invalid credentials",

View File

@@ -1,4 +1,4 @@
"""The AWS S3 integration."""
"""The S3 integration."""
from __future__ import annotations
@@ -7,6 +7,7 @@ from typing import cast
from aiobotocore.client import AioBaseClient as S3Client
from aiobotocore.session import AioSession
from botocore.config import Config
from botocore.exceptions import ClientError, ConnectionError, ParamValidationError
from homeassistant.config_entries import ConfigEntry
@@ -32,6 +33,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
"""Set up S3 from a config entry."""
data = cast(dict, entry.data)
# due to https://github.com/home-assistant/core/issues/143995
config = Config(
request_checksum_calculation="when_required",
response_checksum_validation="when_required",
)
try:
session = AioSession()
# pylint: disable-next=unnecessary-dunder-call
@@ -40,6 +46,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
endpoint_url=data.get(CONF_ENDPOINT_URL),
aws_secret_access_key=data[CONF_SECRET_ACCESS_KEY],
aws_access_key_id=data[CONF_ACCESS_KEY_ID],
config=config,
).__aenter__()
await client.head_bucket(Bucket=data[CONF_BUCKET])
except ClientError as err:

View File

@@ -1,4 +1,4 @@
"""Backup platform for the AWS S3 integration."""
"""Backup platform for the S3 integration."""
from collections.abc import AsyncIterator, Callable, Coroutine
import functools

View File

@@ -1,9 +1,8 @@
"""Config flow for the AWS S3 integration."""
"""Config flow for the S3 integration."""
from __future__ import annotations
from typing import Any
from urllib.parse import urlparse
from aiobotocore.session import AioSession
from botocore.exceptions import ClientError, ConnectionError, ParamValidationError
@@ -18,7 +17,6 @@ from homeassistant.helpers.selector import (
)
from .const import (
AWS_DOMAIN,
CONF_ACCESS_KEY_ID,
CONF_BUCKET,
CONF_ENDPOINT_URL,
@@ -59,34 +57,28 @@ class S3ConfigFlow(ConfigFlow, domain=DOMAIN):
CONF_ENDPOINT_URL: user_input[CONF_ENDPOINT_URL],
}
)
if not urlparse(user_input[CONF_ENDPOINT_URL]).hostname.endswith(
AWS_DOMAIN
):
try:
session = AioSession()
async with session.create_client(
"s3",
endpoint_url=user_input.get(CONF_ENDPOINT_URL),
aws_secret_access_key=user_input[CONF_SECRET_ACCESS_KEY],
aws_access_key_id=user_input[CONF_ACCESS_KEY_ID],
) as client:
await client.head_bucket(Bucket=user_input[CONF_BUCKET])
except ClientError:
errors["base"] = "invalid_credentials"
except ParamValidationError as err:
if "Invalid bucket name" in str(err):
errors[CONF_BUCKET] = "invalid_bucket_name"
except ValueError:
errors[CONF_ENDPOINT_URL] = "invalid_endpoint_url"
except ConnectionError:
errors[CONF_ENDPOINT_URL] = "cannot_connect"
else:
try:
session = AioSession()
async with session.create_client(
"s3",
endpoint_url=user_input.get(CONF_ENDPOINT_URL),
aws_secret_access_key=user_input[CONF_SECRET_ACCESS_KEY],
aws_access_key_id=user_input[CONF_ACCESS_KEY_ID],
) as client:
await client.head_bucket(Bucket=user_input[CONF_BUCKET])
except ClientError:
errors["base"] = "invalid_credentials"
except ParamValidationError as err:
if "Invalid bucket name" in str(err):
errors[CONF_BUCKET] = "invalid_bucket_name"
except ValueError:
errors[CONF_ENDPOINT_URL] = "invalid_endpoint_url"
except ConnectionError:
errors[CONF_ENDPOINT_URL] = "cannot_connect"
else:
return self.async_create_entry(
title=user_input[CONF_BUCKET], data=user_input
)
return self.async_create_entry(
title=user_input[CONF_BUCKET], data=user_input
)
return self.async_show_form(
step_id="user",

View File

@@ -1,19 +1,18 @@
"""Constants for the AWS S3 integration."""
"""Constants for the S3 integration."""
from collections.abc import Callable
from typing import Final
from homeassistant.util.hass_dict import HassKey
DOMAIN: Final = "aws_s3"
DOMAIN: Final = "s3"
CONF_ACCESS_KEY_ID = "access_key_id"
CONF_SECRET_ACCESS_KEY = "secret_access_key"
CONF_ENDPOINT_URL = "endpoint_url"
CONF_BUCKET = "bucket"
AWS_DOMAIN = "amazonaws.com"
DEFAULT_ENDPOINT_URL = f"https://s3.eu-central-1.{AWS_DOMAIN}/"
DEFAULT_ENDPOINT_URL = "https://s3.eu-central-1.amazonaws.com/"
DATA_BACKUP_AGENT_LISTENERS: HassKey[list[Callable[[], None]]] = HassKey(
f"{DOMAIN}.backup_agent_listeners"

View File

@@ -1,9 +1,9 @@
{
"domain": "aws_s3",
"name": "AWS S3",
"domain": "s3",
"name": "S3",
"codeowners": ["@tomasbedrich"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aws_s3",
"documentation": "https://www.home-assistant.io/integrations/s3",
"integration_type": "service",
"iot_class": "cloud_push",
"loggers": ["aiobotocore"],

View File

@@ -9,19 +9,19 @@
"endpoint_url": "Endpoint URL"
},
"data_description": {
"access_key_id": "Access key ID to connect to AWS S3 API",
"secret_access_key": "Secret access key to connect to AWS S3 API",
"access_key_id": "Access key ID to connect to S3 API",
"secret_access_key": "Secret access key to connect to S3 API",
"bucket": "Bucket must already exist and be writable by the provided credentials.",
"endpoint_url": "Endpoint URL provided to [Boto3 Session]({boto3_docs_url}). Region-specific [AWS S3 endpoints]({aws_s3_docs_url}) are available in their docs."
},
"title": "Add AWS S3 bucket"
"title": "Add S3 bucket"
}
},
"error": {
"cannot_connect": "[%key:component::aws_s3::exceptions::cannot_connect::message%]",
"invalid_bucket_name": "[%key:component::aws_s3::exceptions::invalid_bucket_name::message%]",
"invalid_credentials": "[%key:component::aws_s3::exceptions::invalid_credentials::message%]",
"invalid_endpoint_url": "Invalid endpoint URL. Please make sure it's a valid AWS S3 endpoint URL."
"cannot_connect": "[%key:component::s3::exceptions::cannot_connect::message%]",
"invalid_bucket_name": "[%key:component::s3::exceptions::invalid_bucket_name::message%]",
"invalid_credentials": "[%key:component::s3::exceptions::invalid_credentials::message%]",
"invalid_endpoint_url": "Invalid endpoint URL"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"

View File

@@ -46,6 +46,7 @@ from homeassistant.const import (
CONF_TOKEN,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_component
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import format_mac
@@ -53,6 +54,7 @@ from homeassistant.util import dt as dt_util
from .const import (
CONF_SESSION_ID,
DOMAIN,
ENCRYPTED_WEBSOCKET_PORT,
LEGACY_PORT,
LOGGER,
@@ -371,9 +373,13 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
except (ConnectionClosed, BrokenPipeError):
# BrokenPipe can occur when the commands is sent to fast
self._remote = None
except (UnhandledResponse, AccessDenied):
except (UnhandledResponse, AccessDenied) as err:
# We got a response so it's on.
LOGGER.debug("Failed sending command %s", key, exc_info=True)
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="error_sending_command",
translation_placeholders={"error": repr(err), "host": self.host},
) from err
except OSError:
# Different reasons, e.g. hostname not resolveable
pass

View File

@@ -29,13 +29,14 @@ from homeassistant.components.media_player import (
MediaType,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.async_ import create_eager_task
from .bridge import SamsungTVWSBridge
from .const import CONF_SSDP_RENDERING_CONTROL_LOCATION, LOGGER
from .const import CONF_SSDP_RENDERING_CONTROL_LOCATION, DOMAIN, LOGGER
from .coordinator import SamsungTVConfigEntry, SamsungTVDataUpdateCoordinator
from .entity import SamsungTVEntity
@@ -308,7 +309,12 @@ class SamsungTVDevice(SamsungTVEntity, MediaPlayerEntity):
try:
await dmr_device.async_set_volume_level(volume)
except UpnpActionResponseError as err:
LOGGER.warning("Unable to set volume level on %s: %r", self._host, err)
assert self._host
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="error_set_volume",
translation_placeholders={"error": repr(err), "host": self._host},
) from err
async def async_volume_up(self) -> None:
"""Volume up the media player."""

View File

@@ -68,6 +68,12 @@
"service_unsupported": {
"message": "Entity {entity} does not support this action."
},
"error_set_volume": {
"message": "Unable to set volume level on {host}: {error}"
},
"error_sending_command": {
"message": "Unable to send command to {host}: {error}"
},
"encrypted_mode_auth_failed": {
"message": "Token and session ID are required in encrypted mode."
},

View File

@@ -42,6 +42,8 @@ from .utils import (
is_rpc_momentary_input,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class BlockBinarySensorDescription(

View File

@@ -32,6 +32,8 @@ from .const import DOMAIN, LOGGER, SHELLY_GAS_MODELS
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
from .utils import get_device_entry_gen, get_rpc_key_ids
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class ShellyButtonDescription[

View File

@@ -51,6 +51,8 @@ from .utils import (
is_rpc_thermostat_internal_actuator,
)
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -21,6 +21,8 @@ from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoo
from .entity import ShellyBlockEntity, ShellyRpcEntity
from .utils import get_device_entry_gen, get_rpc_key_ids
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -38,6 +38,8 @@ from .utils import (
is_rpc_momentary_input,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class ShellyBlockEventDescription(EventEntityDescription):

View File

@@ -49,6 +49,8 @@ from .utils import (
percentage_to_brightness,
)
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,

View File

@@ -42,6 +42,8 @@ from .utils import (
get_virtual_component_ids,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class BlockNumberDescription(BlockEntityDescription, NumberEntityDescription):

View File

@@ -33,7 +33,7 @@ rules:
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: todo
parallel-updates: done
reauthentication-flow: done
test-coverage: done
@@ -42,7 +42,7 @@ rules:
diagnostics: done
discovery-update-info: done
discovery: done
docs-data-update: todo
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
@@ -56,8 +56,8 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: todo
exception-translations: todo
icon-translations: todo
exception-translations: done
icon-translations: done
reconfiguration-flow: done
repair-issues: done
stale-devices:

View File

@@ -28,6 +28,8 @@ from .utils import (
get_virtual_component_ids,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class RpcSelectDescription(RpcEntityDescription, SelectEntityDescription):

View File

@@ -63,6 +63,8 @@ from .utils import (
is_rpc_wifi_stations_disabled,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class BlockSensorDescription(BlockEntityDescription, SensorEntityDescription):

View File

@@ -39,6 +39,8 @@ from .utils import (
is_rpc_exclude_from_relay,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class BlockSwitchDescription(BlockEntityDescription, SwitchEntityDescription):

View File

@@ -28,6 +28,8 @@ from .utils import (
get_virtual_component_ids,
)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class RpcTextDescription(RpcEntityDescription, TextEntityDescription):

View File

@@ -47,6 +47,8 @@ from .utils import get_device_entry_gen, get_release_url
LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class RpcUpdateDescription(RpcEntityDescription, UpdateEntityDescription):

View File

@@ -25,6 +25,8 @@ from .entity import (
)
from .utils import async_remove_shelly_entity, get_device_entry_gen
PARALLEL_UPDATES = 0
@dataclass(kw_only=True, frozen=True)
class BlockValveDescription(BlockEntityDescription, ValveEntityDescription):

View File

@@ -30,5 +30,5 @@
"iot_class": "cloud_push",
"loggers": ["pysmartthings"],
"quality_scale": "bronze",
"requirements": ["pysmartthings==3.2.1"]
"requirements": ["pysmartthings==3.2.0"]
}

View File

@@ -26,7 +26,6 @@ class SmartThingsSelectDescription(SelectEntityDescription):
options_attribute: Attribute
status_attribute: Attribute
command: Command
default_options: list[str] | None = None
CAPABILITIES_TO_SELECT: dict[Capability | str, SmartThingsSelectDescription] = {
@@ -47,7 +46,6 @@ CAPABILITIES_TO_SELECT: dict[Capability | str, SmartThingsSelectDescription] = {
options_attribute=Attribute.SUPPORTED_MACHINE_STATES,
status_attribute=Attribute.MACHINE_STATE,
command=Command.SET_MACHINE_STATE,
default_options=["run", "pause", "stop"],
),
Capability.WASHER_OPERATING_STATE: SmartThingsSelectDescription(
key=Capability.WASHER_OPERATING_STATE,
@@ -57,7 +55,6 @@ CAPABILITIES_TO_SELECT: dict[Capability | str, SmartThingsSelectDescription] = {
options_attribute=Attribute.SUPPORTED_MACHINE_STATES,
status_attribute=Attribute.MACHINE_STATE,
command=Command.SET_MACHINE_STATE,
default_options=["run", "pause", "stop"],
),
Capability.SAMSUNG_CE_AUTO_DISPENSE_DETERGENT: SmartThingsSelectDescription(
key=Capability.SAMSUNG_CE_AUTO_DISPENSE_DETERGENT,
@@ -117,12 +114,8 @@ class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity):
@property
def options(self) -> list[str]:
"""Return the list of options."""
return (
self.get_attribute_value(
self.entity_description.key, self.entity_description.options_attribute
)
or self.entity_description.default_options
or []
return self.get_attribute_value(
self.entity_description.key, self.entity_description.options_attribute
)
@property

View File

@@ -631,7 +631,7 @@ CAPABILITY_TO_SENSORS: dict[
SmartThingsSensorEntityDescription(
key="powerEnergy_meter",
translation_key="power_energy",
state_class=SensorStateClass.TOTAL,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["powerEnergy"] / 1000,

View File

@@ -29,17 +29,17 @@
"config": {
"step": {
"user": {
"title": "SMS Verification",
"title": "SMS verification",
"description": "Enter your phone number (same as what you used to register to the tami4 app)",
"data": {
"phone": "Phone Number"
"phone": "Phone number"
}
},
"otp": {
"title": "[%key:component::tami4::config::step::user::title%]",
"description": "Enter the code you received via SMS",
"data": {
"otp": "SMS Code"
"otp": "SMS code"
}
}
},

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