Compare commits

..

974 Commits

Author SHA1 Message Date
Abílio Costa 66c3981ff5 Merge branch 'zha_3phase_remaining_meetering' into zha_3ph_energy 2025-01-26 00:26:38 +00:00
Abílio Costa 8ca9608a7e Merge branch 'dev' into zha_3phase_remaining_meetering 2025-01-26 00:22:47 +00:00
Martin Hjelmare 1a57992e78 Add restore backup tests (#136538)
* Test restore backup with busy manager

* Test restore backup with agent error

* Test restore backup with file error
2025-01-25 18:20:41 -06:00
Norbert Rittel 733e1feba3 Fix wrong plural on tado.add_meter_reading action (#136524)
As this action can only take a single argument the plural introduced in the descriptions is misleading.

This also makes the friendly name of the action consistent with its key name.
2025-01-25 18:20:05 -06:00
Robin Wohlers-Reichel cffb0a03d2 Add Darsstar as codeowner for solax integration (#136528)
* Add Darsstar as codeowner for solax integration

* Update manifest.json
2025-01-25 18:18:20 -06:00
Michael cf8409dcd2 Add backup agent to Synology DSM (#135227)
* pre-alpha state

* small type

* use ChunkAsyncStreamIterator from aiohttp_client helper

* create parent folders during upload if none exists

* check file station permissionsduring setup

* ensure backup-agents are reloaded

* adjust config flow

* fix check for availability of file station

* fix possible unbound

* add config flow tests

* fix existing tests

* add backup tests

* backup listeners are not async

* some more tests

* migrate existing config entries

* fix migration

* notify backup listeners only when needed during setup

* add backup settings to options flow

* switch back to the listener approach from the dev docs example

* add negative tests

* fix tests

* use HassKey

* fix tests

* Revert "use HassKey"

This reverts commit 71c5a4d6fa9c04b4907ff5f8df6ef7bd1737aa85.

* use hass loop call_soon instead of non-eager-start tasks

* use HassKey for backup-agent-listeners

* delete empty backup-agent-listener list from hass.data

* don't handle single file download errors

* Apply suggestions from code review

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

* add more tests

* we don't have entities related to file_station api

* add more backup tests

* test unload backup agent

* revert sorting of properties

* additional use hass config location for default backup path

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-25 22:31:30 +01:00
Christopher Fenner 5e6f624938 Add heat pump heating rod sensors in ViCare integration (#136467)
* add heating rod sensors

* add labels

* update snapshot
2025-01-25 21:42:49 +01:00
Keith 34e8595d19 Updated igloohome-api dependency to 0.1.0 (#136516)
- Updated igloohome-api to 0.1.0
2025-01-25 21:38:27 +01:00
J. Nick Koston 412636a198 Remove unneeded call active check in modbus (#136487)
We have an asyncio.Lock in place to prevent polling collisions
now so this is no longer needed

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-01-25 19:31:49 +02:00
Indu Prakash 1bf97e3f45 Bump pyvesync to 2.1.16 (#136493)
Update use pyvesync 2.1.16

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-01-25 19:31:16 +02:00
Abílio Costa 42f7bd0a8f Reuse fixtures in config flow tests for Whirlpool (#136489)
* Use fixtures in config flow tests for Whirlpool

* Keep old tests; new one will go to separate PR
2025-01-25 19:30:52 +02:00
mkmer 821abc8c53 Bump AIOSomeComfort to 0.0.30 in Honeywell (#136523) 2025-01-25 19:22:03 +02:00
J. Nick Koston 772f61cf77 Reduce boilerplate code to setup modbus platform entities (#136491) 2025-01-25 07:14:06 -10:00
Andrew Sayre 2fb85aab8e Incorporate GroupManager into HEOS Coordinator (#136462)
* Incorporate GroupManager

* Update quality scale

* Fix group params

* Revert quality scale change

* Rename varaible

* Move group action implementaton out of coordinator

* Fix get_group_members hass access

* entity -> entity_id
2025-01-25 18:04:33 +01:00
Joost Lekkerkerker 2db301fab9 Fix Spotify flaky test (#136529) 2025-01-25 18:53:27 +02:00
TimL 05bdfe7aa6 Abort config flow is device is unsupported (#136505)
* Abort config flow if device is not yet supported

* Abort on user step for unsupported device

* Add string for unsupported device

* fix tests due to extra get_info calls

* add tests for unsupported devices to abort flow
2025-01-25 13:17:38 +01:00
Ludovic BOUÉ 71d63bac8d Add TemperatureLevel feature from Matter TemperatureControl cluster (#134532) 2025-01-25 12:22:45 +01:00
starkillerOG 8b24bac1d1 Bump reolink_aio to 0.11.8 (#136504) 2025-01-25 11:28:52 +01:00
Joost Lekkerkerker fb04c256a8 Refactor EZVIZ config flow tests (#136434) 2025-01-25 10:43:22 +01:00
Steven B. 28951096a8 Update tplink climate platform to use thermostat module (#136166) 2025-01-25 10:38:06 +01:00
TimL b25b97b6b6 Bump pysmlight to v0.1.6 (#136496) 2025-01-25 09:22:26 +01:00
J. Nick Koston d84fa1fcfb Fix httpx late import of trio doing blocking I/O in the event loop (#136409)
httpx 0.28.1 moved the trio import to happen a bit later

```
2025-01-23 19:53:12.370 WARNING (MainThread) [homeassistant.util.loop] Detected blocking call to open with args (/lib/c, rb) inside the event loop by integration rest at homeassistant/components/rest/data.py, line 88: self._async_client = create_async_httpx_client( (offender: /usr/local/lib/python3.13/ctypes/util.py, line 285: with open(filepath, rb) as fh:), please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+rest%22
For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#open
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 227, in <module>
    sys.exit(main())
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 213, in main
    exit_code = runner.run(runtime_conf)
  File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run
    return loop.run_until_complete(setup_and_run_hass(runtime_config))
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 707, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 678, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2033, in _run_once
    handle._run()
  File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 2360, in _async_forward_entry_setup
    result = await async_setup_component(
  File "/usr/src/homeassistant/homeassistant/setup.py", line 165, in async_setup_component
    result = await _async_setup_component(hass, domain, config)
  File "/usr/src/homeassistant/homeassistant/setup.py", line 420, in _async_setup_component
    result = await task
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 90, in async_setup
    await component.async_setup(config)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 146, in async_setup
    self.hass.async_create_task_internal(
  File "/usr/src/homeassistant/homeassistant/core.py", line 832, in async_create_task_internal
    task = create_eager_task(target, name=name, loop=self.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 307, in async_setup_platform
    await self._platforms[key].async_setup(platform_config, discovery_info)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 303, in async_setup
    await self._async_setup_platform(async_create_setup_awaitable)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 363, in _async_setup_platform
    awaitable = create_eager_task(awaitable, loop=hass.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/components/rest/sensor.py", line 85, in async_setup_platform
    await rest.async_update(log_errors=False)
  File "/usr/src/homeassistant/homeassistant/components/rest/data.py", line 88, in async_update
    self._async_client = create_async_httpx_client(

2025-01-23 19:53:12.371 WARNING (MainThread) [homeassistant.util.loop] Detected blocking call to glob with args (/lib/libc.so,) inside the event loop by integration rest at homeassistant/components/rest/data.py, line 88: self._async_client = create_async_httpx_client( (offender: /usr/local/lib/python3.13/ctypes/util.py, line 311: for f in glob({0}{1}.format(prefix, suffix)):), please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+rest%22
For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#glob
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 227, in <module>
    sys.exit(main())
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 213, in main
    exit_code = runner.run(runtime_conf)
  File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run
    return loop.run_until_complete(setup_and_run_hass(runtime_config))
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 707, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 678, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2033, in _run_once
    handle._run()
  File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 2360, in _async_forward_entry_setup
    result = await async_setup_component(
  File "/usr/src/homeassistant/homeassistant/setup.py", line 165, in async_setup_component
    result = await _async_setup_component(hass, domain, config)
  File "/usr/src/homeassistant/homeassistant/setup.py", line 420, in _async_setup_component
    result = await task
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 90, in async_setup
    await component.async_setup(config)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 146, in async_setup
    self.hass.async_create_task_internal(
  File "/usr/src/homeassistant/homeassistant/core.py", line 832, in async_create_task_internal
    task = create_eager_task(target, name=name, loop=self.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 307, in async_setup_platform
    await self._platforms[key].async_setup(platform_config, discovery_info)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 303, in async_setup
    await self._async_setup_platform(async_create_setup_awaitable)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 363, in _async_setup_platform
    awaitable = create_eager_task(awaitable, loop=hass.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/components/rest/sensor.py", line 85, in async_setup_platform
    await rest.async_update(log_errors=False)
  File "/usr/src/homeassistant/homeassistant/components/rest/data.py", line 88, in async_update
    self._async_client = create_async_httpx_client(

2025-01-23 19:53:12.372 WARNING (MainThread) [homeassistant.util.loop] Detected blocking call to iglob with args (/lib/libc.so,) inside the event loop by integration rest at homeassistant/components/rest/data.py, line 88: self._async_client = create_async_httpx_client( (offender: /usr/local/lib/python3.13/glob.py, line 31: return list(iglob(pathname, root_dir=root_dir, dir_fd=dir_fd, recursive=recursive,), please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+rest%22
For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#iglob
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 227, in <module>
    sys.exit(main())
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 213, in main
    exit_code = runner.run(runtime_conf)
  File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run
    return loop.run_until_complete(setup_and_run_hass(runtime_config))
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 707, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 678, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2033, in _run_once
    handle._run()
  File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 2360, in _async_forward_entry_setup
    result = await async_setup_component(
  File "/usr/src/homeassistant/homeassistant/setup.py", line 165, in async_setup_component
    result = await _async_setup_component(hass, domain, config)
  File "/usr/src/homeassistant/homeassistant/setup.py", line 420, in _async_setup_component
    result = await task
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 90, in async_setup
    await component.async_setup(config)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 146, in async_setup
    self.hass.async_create_task_internal(
  File "/usr/src/homeassistant/homeassistant/core.py", line 832, in async_create_task_internal
    task = create_eager_task(target, name=name, loop=self.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 307, in async_setup_platform
    await self._platforms[key].async_setup(platform_config, discovery_info)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 303, in async_setup
    await self._async_setup_platform(async_create_setup_awaitable)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 363, in _async_setup_platform
    awaitable = create_eager_task(awaitable, loop=hass.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/components/rest/sensor.py", line 85, in async_setup_platform
    await rest.async_update(log_errors=False)
  File "/usr/src/homeassistant/homeassistant/components/rest/data.py", line 88, in async_update
    self._async_client = create_async_httpx_client(

2025-01-23 19:53:12.374 WARNING (MainThread) [homeassistant.util.loop] Detected blocking call to scandir with args (/lib,) inside the event loop by integration rest at homeassistant/components/rest/data.py, line 88: self._async_client = create_async_httpx_client( (offender: /usr/local/lib/python3.13/glob.py, line 170: with os.scandir(arg) as it:), please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+rest%22
For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#scandir
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 227, in <module>
    sys.exit(main())
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 213, in main
    exit_code = runner.run(runtime_conf)
  File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run
    return loop.run_until_complete(setup_and_run_hass(runtime_config))
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 707, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 678, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2033, in _run_once
    handle._run()
  File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 2360, in _async_forward_entry_setup
    result = await async_setup_component(
  File "/usr/src/homeassistant/homeassistant/setup.py", line 165, in async_setup_component
    result = await _async_setup_component(hass, domain, config)
  File "/usr/src/homeassistant/homeassistant/setup.py", line 420, in _async_setup_component
    result = await task
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 90, in async_setup
    await component.async_setup(config)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 146, in async_setup
    self.hass.async_create_task_internal(
  File "/usr/src/homeassistant/homeassistant/core.py", line 832, in async_create_task_internal
    task = create_eager_task(target, name=name, loop=self.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 307, in async_setup_platform
    await self._platforms[key].async_setup(platform_config, discovery_info)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 303, in async_setup
    await self._async_setup_platform(async_create_setup_awaitable)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 363, in _async_setup_platform
    awaitable = create_eager_task(awaitable, loop=hass.loop)
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/components/rest/sensor.py", line 85, in async_setup_platform
    await rest.async_update(log_errors=False)
  File "/usr/src/homeassistant/homeassistant/components/rest/data.py", line 88, in async_update
    self._async_client = create_async_httpx_client(
```
2025-01-25 09:15:05 +01:00
epenet ddf071c80e Move deconz function to util.py (#136414) 2025-01-25 08:41:54 +01:00
epenet 829fab5371 Cleanup update_listener in deconz (#136416) 2025-01-25 08:40:22 +01:00
J. Nick Koston 891485f306 Bump pydantic to 2.10.6 (#136483) 2025-01-24 12:17:52 -10:00
Steven Looman 8622beb8a7 Bump async-upnp-client to 0.43.0 (#136481) 2025-01-24 12:05:31 -10:00
Christian 9993a68a55 Powerwall: Reuse authentication cookie (#136147)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-24 11:52:24 -10:00
Joost Lekkerkerker f5fc46a7be Make Spotify polling interval dynamic (#136461) 2025-01-24 22:03:46 +01:00
Norbert Rittel 7363413d3d Fix sentence-casing in strings of Vizio integration (#136465) 2025-01-24 22:00:46 +02:00
Raphael Hehl c25ffd3e66 Bump uiprotect to version 7.5.0 (#136475) 2025-01-24 13:57:19 -06:00
Steven B. 1697e24068 Add PARALLEL_UPDATES constant to ring integration platforms (#136470) 2025-01-24 20:48:55 +01:00
Josef Zweck b0188772bc Bump aioacaia to 0.1.14 (#136453) 2025-01-24 17:01:44 +01:00
Markus Adrario a56c37a508 Add more sensors to homee (#136445) 2025-01-24 16:02:14 +01:00
epenet 728d381eb3 Move dynalite service definitions to separate module (#136446) 2025-01-24 15:55:53 +01:00
Artur Pragacz fc9ad40ac8 Reorganize input sources in Onkyo options (#133511) 2025-01-24 15:45:53 +01:00
epenet 51bc56929b Use runtime_data in dunehd (#136443) 2025-01-24 15:45:34 +01:00
J. Nick Koston 98e59f01b7 Bump aioharmony to 0.4.1 (#136413)
changelog: https://github.com/Harmony-Libs/aioharmony/compare/v0.4.0...v0.4.1
2025-01-24 16:23:22 +02:00
epenet 4dc873416f Use runtime_data in dexcom (#136441) 2025-01-24 15:14:05 +01:00
epenet f3e13f4662 Use runtime_data in duotecno (#136444) 2025-01-24 15:13:53 +01:00
epenet 2e78ab620f Use runtime_data in dormakaba_dkey (#136440) 2025-01-24 14:52:22 +01:00
epenet f6b1786b13 Move dexcom coordinator to separate module (#136433) 2025-01-24 14:20:23 +01:00
epenet 384c173ab3 Use runtime_data in directv (#136435) 2025-01-24 14:14:42 +01:00
epenet c991d4dac5 Move dormakaba_dkey coordinator to separate module (#136437) 2025-01-24 14:14:03 +01:00
David Knowles 7050dbb66d Refactor the Hydrawise config flow (#135886)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-24 14:13:54 +01:00
Shay Levy 47efb68780 Add missing translations for LG webOS TV and fix names (#136438) 2025-01-24 14:13:10 +01:00
Erwin Douna 5d353a9833 Tado change to async and add Data Update Coordinator (#134175)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-24 13:05:54 +01:00
Jan Bouwhuis 09559a43ad Rename incomfort exceptions classes to fix typo and assign correct translation domain (#136426) 2025-01-24 12:17:23 +01:00
Andrew Sayre a3ba3bbb1d Incorporate SourceManager into HEOS Coordinator (#136377)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-24 11:56:41 +01:00
Indu Prakash 50cf94ca9b Fix humidifier mode for Vesync (#135746)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-24 11:50:23 +01:00
Glenn Vandeuren (aka Iondependent) 72d1ac9f92 Bump nhc to 0.3.9 (#136418) 2025-01-24 11:44:15 +01:00
epenet c2fe7230b5 Use runtime_data in denonavr (#136424) 2025-01-24 11:38:24 +01:00
Franck Nijhof 20e936c7b9 Omit Peblar update entities for most white label devices (#136374) 2025-01-24 11:33:25 +01:00
epenet 4e89c2322b Simplify update listener in denonavr (#136422) 2025-01-24 11:26:09 +01:00
epenet 6fde10ef9e Move denonavr shared constants to central location (#136421) 2025-01-24 11:23:23 +01:00
J. Nick Koston 0abdda7abb Bump WSDiscovery to 2.1.2 (#136363) 2025-01-23 23:30:49 -10:00
Thomas55555 5a30156372 Bump aioautomower to 2025.1.1 (#136365) 2025-01-23 22:38:38 -10:00
J. Nick Koston f3074dc218 Bump aioharmony to 0.4.0 (#136398) 2025-01-23 22:24:12 -10:00
epenet 8b08cb9bc1 Use runtime_data in coolmaster (#136405)
* Use runtime_data in coolmaster

* Adjust test
2025-01-24 08:58:35 +01:00
Makrit 6a1279611d Handle width and height placeholders in the thumbnail URL (#136227) 2025-01-24 08:49:33 +01:00
G Johansson e44cfa00af Remove deprecated 17track package sensor (#136389) 2025-01-24 08:43:18 +01:00
dependabot[bot] 90d95d935e Bump codecov/codecov-action from 5.2.0 to 5.3.0 (#136402)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-24 08:42:58 +01:00
dependabot[bot] 6854feeb40 Bump github/codeql-action from 3.28.3 to 3.28.4 (#136401)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-24 08:34:48 +01:00
Shay Levy fe67069c91 Add translated action exceptions to LG webOS TV (#136397)
* Add translated action exceptions to LG webOS TV

* Apply suggestions from code review

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-24 02:07:24 +02:00
epenet 3bbcd37ec8 Use runtime_data in ccm15 (#136378) 2025-01-24 02:02:38 +02:00
epenet c691f8cc1e Use runtime_data in comelit (#136384) 2025-01-24 01:50:36 +02:00
epenet 1593b40f52 Use runtime_data in daikin (#136376) 2025-01-24 01:49:31 +02:00
epenet a70a9d2f76 Use runtime_data in coinbase (#136381) 2025-01-24 00:12:08 +01:00
Paulus Schoutsen 005ae3ace6 Allow LLMs to get calendar events from exposed calendars (#136304) 2025-01-23 17:54:04 -05:00
Michael Hansen 414fa4125e Don't translate state names in default agent responses (#136382)
Don't translate state names in responses
2025-01-23 16:03:48 -06:00
J. Nick Koston a12255ea5d Migrate modbus to use HassKey (#136379) 2025-01-23 22:56:31 +01:00
Claudio Ruggeri - CR-Tech 5e34babc39 Fix slave id equal to 0 (#136263)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-23 11:12:02 -10:00
Hervé Cauwelier 0cd87cf3e9 holiday: asynchronously generate the entity name (#136354)
Asking the country translation was trigerring Babel to open a file, and
thus a blocking I/O.
2025-01-23 21:51:01 +01:00
Klaas Schoute cd16a57e04 Bump powerfox to v1.2.1 (#136366) 2025-01-23 20:52:54 +01:00
Simon Lamon b682495fda Handle LinkPlay devices with no mac (#136272)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-23 20:36:59 +01:00
Markus Lanthaler 2617575e18 Set Netgear device entities to unavailable when the device is not connected (#135362) 2025-01-23 20:23:03 +01:00
Andrew Sayre 507239c661 Incorporate ControllerManager into HEOS Coordinator (#136302)
* Integrate ControllerManager

* Test for uncovered

* Correct test docstring

* Cast entry before graph access

* Assert config_entry state in reauth

* Use implicit casting
2025-01-23 12:52:56 -06:00
J. Nick Koston 9d83bbfec6 Refactor modbus polling to prevent dupe updates and memory leak (#136211) 2025-01-23 19:52:40 +01:00
Steven B. 2466df2b78 Fix tplink deprecated entity cleanup (#136160) 2025-01-23 19:51:56 +01:00
Matt Doran b2624e6274 Update Hydrawise maximum watering duration to meet the app limits (#136050)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-23 19:50:56 +01:00
Jan Bouwhuis 59d677ba3e Enable strict typing for incomfort integration (#136291)
* Enable strict typing for incomfort integration

* Comply to strict typing

* Wrap in bool
2025-01-23 19:21:39 +01:00
Chris ac7b9d7639 Properly parse AirNow API data in coordinator (#136198) 2025-01-23 19:09:03 +01:00
Arie Catsman c98df36b75 Bump pyenphase to 1.23.1 (#136200) 2025-01-23 19:05:57 +01:00
epenet 5803d44443 Cleanup hass.data in cloudflare (#136358) 2025-01-23 19:04:10 +01:00
Norbert Rittel 61694648fc Several fixes in user-facing strings of Renson integration actions (#136279) 2025-01-23 18:56:08 +01:00
epenet 21a83c4875 Use runtime_data in canary (#136357) 2025-01-23 18:53:04 +01:00
epenet 29c528ee54 Use runtime_data in bosch_shc (#136356) 2025-01-23 18:52:10 +01:00
epenet 8dba4affa9 Move single-use lovelace function (#136336) 2025-01-23 18:48:48 +01:00
Norbert Rittel dae4b53cb7 Fix sentence-casing in isy994 integration strings, reword "lock user code" (#136316) 2025-01-23 18:38:56 +01:00
epenet 83e826219a Enable strict-typing in lovelace (#136327) 2025-01-23 18:37:58 +01:00
Jan Bouwhuis 33ce795695 Improve error handling for incomfort gateway (#136317) 2025-01-23 18:26:28 +01:00
Åke Strandberg 3da9c599dc Avoid keyerror on incomplete api data in myuplink (#136333)
* Avoid keyerror

* Inject erroneous value in device point fixture

* Update diagnostics snapshot
2025-01-23 19:04:00 +02:00
Åke Strandberg 025f70445b Bump myuplink lib to 0.7.0 (#136343) 2025-01-23 19:01:50 +02:00
Paul Bottein d29572f3d0 Update frontend to 20250109.2 (#136348) 2025-01-23 17:18:00 +01:00
Joost Lekkerkerker d8223a1771 Bump aiowithings to 3.1.5 (#136350) 2025-01-23 17:17:07 +01:00
Joost Lekkerkerker 132f418f92 Add reconfigure flow to Airgradient (#136324)
* Add reconfigure flow to Airgradient

* Update homeassistant/components/airgradient/strings.json

---------

Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-23 16:53:31 +02:00
Paul Bottein 093c41cd83 Update frontend to 20250109.1 (#136339) 2025-01-23 15:49:18 +01:00
Martin Hjelmare dabcc6d55a Clean up remaining backup manager tests (#136335) 2025-01-23 15:23:44 +01:00
Norbert Rittel 5dfafd9f2e Replace key names with translatable friendly names in zwave_js (#136318)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-01-23 15:15:08 +01:00
Franck Nijhof f6a040d598 Update peblar to v0.4.0 (#136329)
* Update peblar to v0.4.0

* Update snapshots
2025-01-23 15:02:30 +02:00
Shay Levy 66f945e852 Bump aiowebostv to 0.6.0 (#136206) 2025-01-23 13:51:24 +01:00
Joost Lekkerkerker 40ed0562bc Add translated action exceptions to Airgradient (#136322)
* Add translated action exceptions to Airgradient

* Add translated action exceptions to Airgradient
2025-01-23 14:48:46 +02:00
Gerben Jongerius d6f6961674 Restructure the youless integration internals (#135842) 2025-01-23 13:35:21 +01:00
Joost Lekkerkerker e57dafee6c Add parallel updates to Airgradient (#136323) 2025-01-23 13:35:53 +02:00
Abílio Costa 75738f2105 Add system_health the to Network component (#135514) 2025-01-23 12:30:46 +01:00
epenet 73bd21e0ab Use HassKey in lovelace (#136313)
* Use HassKey in lovelace

* Improve type hints

* docstring

* Rename constant
2025-01-23 11:26:18 +01:00
Joost Lekkerkerker ae65a81188 Update Overseerr quality scale (#136260)
* Update Overseerr quality scale

* Update Overseerr quality scale

* Update Overseerr quality scale
2025-01-23 10:24:26 +01:00
epenet 10cfef1f3e Cleanup map references in lovelace (#136314)
* Cleanup map references in lovelace

* Cleanup fixtures
2025-01-23 10:10:37 +01:00
Fábio Domingues 8172afd9f4 Auto select thermostat preset when selecting temperature (#134146) 2025-01-23 09:41:29 +01:00
Christopher Fenner 40348890da Add heat pump supply pressure sensor in ViCare integration (#136265) 2025-01-23 09:15:24 +01:00
J. Nick Koston b839a2e2bd Fix handling of non-supported devices in led-ble (#136300) 2025-01-23 09:09:11 +01:00
dependabot[bot] f5542450c4 Bump codecov/codecov-action from 5.1.2 to 5.2.0 (#136306)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 09:06:13 +01:00
dependabot[bot] be0a344642 Bump actions/attest-build-provenance from 2.1.0 to 2.2.0 (#136307)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 09:05:32 +01:00
dependabot[bot] df036d3091 Bump dawidd6/action-download-artifact from 7 to 8 (#136309)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 09:05:10 +01:00
dependabot[bot] 9fc21c389a Bump github/codeql-action from 3.28.2 to 3.28.3 (#136308)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 09:01:39 +01:00
Norbert Rittel 595a7fbcd7 Fix grammar of OSO auth and action descriptions (#136312) 2025-01-23 08:58:33 +01:00
Dan Raper 95b49fd2bc Add time platform to ohme (#136289) 2025-01-23 08:20:03 +01:00
Nathan Spencer 90bd783fff Standardize DOMAIN usage in litterrobot tests (#136290)
* Standardize DOMAIN usage in litterrobot tests

* Fix additional DOMAIN references in tests

* Make platform domain usage more clear in tests
2025-01-23 08:17:59 +01:00
J. Nick Koston 75bdcee3e4 Bump led-ble to 1.1.4 (#136301) 2025-01-22 18:45:44 -10:00
J. Nick Koston 29ce89ee4f Bump zeroconf to 0.141.0 (#136292)
changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.140.1...0.141.0
2025-01-22 22:25:03 -06:00
J. Nick Koston 7afd1f8cf8 Avoid useless data conversion in sonos config flow (#136294)
We would convert the zeroconf data to a dict and pass
it to async_step_discovery which does nothing with it
2025-01-22 22:24:12 -06:00
J. Nick Koston ce792f6fe9 Bump onvif-zeep-async to 3.2.5 (#136299) 2025-01-22 18:19:56 -10:00
Jeff Terrace 68b6a7c987 Add TP-Link Tapo pet detection to onvif parsers (#136303) 2025-01-22 18:19:09 -10:00
Yuxin Wang 43d8c0bb6e Fallback to None for literal "Blank" serial number for APCUPSD integration (#136297)
* Fallback to None for Blank serial number

* Fix comments
2025-01-22 22:10:52 -06:00
rwalker777 6fa4cbd3e1 Revert "Add Tuya based bluetooth lights" (#133386)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-22 18:04:39 -10:00
Jan Bouwhuis ff7601e676 Bump incomfort-client to v0.6.7 (#136285)
* Bump incomfort-client to v0.6.7

* Fix mypy
2025-01-22 23:30:10 +01:00
Nathan Spencer 544c4a0583 Cleanup litterrobot sensor entity (#136287) 2025-01-22 23:03:50 +01:00
Petro31 4a7e009f27 Allow time triggers with offsets to use input_datetimes (#131550) 2025-01-22 21:57:13 +00:00
Thomas Lake cad49453eb ping: Suppress ProcessLookupError on timeout (#134281) 2025-01-22 22:30:04 +01:00
epenet 3a493bb6c0 Improve type hints in benchmark script (#136259) 2025-01-22 22:29:00 +01:00
Nathan Spencer 33f966a12e Convert LitterRobotHub to a DataUpdateCoordinator (#136283) 2025-01-22 22:20:13 +01:00
Dan Raper e3c836aa7d Add number platform to ohme (#136271)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-01-22 22:19:54 +01:00
Andrew Sayre 52f77626f7 Implement Coordinator for HEOS (initial plumbing) (#136205) 2025-01-22 22:12:05 +01:00
Nathan Spencer dc24f83407 Cleanup litterrobot select entity (#136282) 2025-01-22 21:27:28 +01:00
J. Nick Koston f8dc3d6624 Bump habluetooth to 3.12.0 (#136281) 2025-01-22 10:14:19 -10:00
Markus Adrario ea1cec2525 Bump pyHomee to 1.2.3 (#136213)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-22 20:55:52 +01:00
Nathan Spencer 208805a930 Move brightness icon map to icons.json (#136201) 2025-01-22 20:49:11 +01:00
J. Nick Koston 66115ce695 Remove myself from ibeacon codeowners (#136280) 2025-01-22 09:37:07 -10:00
J. Nick Koston dcb17d03af Bump bleak-esphome to 2.1.1 (#136277) 2025-01-22 20:36:31 +01:00
Simon Lamon 4203345550 Bump python-linkplay to v0.1.3 (#136267) 2025-01-22 09:02:01 -10:00
Maciej Bieniek 5f67461c26 Provide beta release note for Shelly RPC devices (#136154)
* Return beta release note for Shelly RPC devices

* Cleaning

* Fix test

* Move release note check
2025-01-22 21:00:42 +02:00
Arie Catsman 8c0515aff2 Set enphase_envoy CT Status flags entity_category to diagnostics. (#136241) 2025-01-22 21:00:12 +02:00
Joost Lekkerkerker 9f2a6af1ec Only add Overseerr event if we are push based (#136258) 2025-01-22 20:58:48 +02:00
Dan Raper ad205aeea3 Bump ohmepy to 1.2.4 (#136270) 2025-01-22 19:29:08 +01:00
Erik Montnemery ea9be01c7c Indicate in WS API when scheduling additional automatic backup (#136155) 2025-01-22 18:01:46 +00:00
Álvaro Fernández Rojas 4e494aa393 Allow multiple Airzone entries with different System IDs (#135397)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-22 17:41:58 +00:00
epenet 3bbd7daa7f Improve type hints in template helper (#136253) 2025-01-22 15:27:01 +00:00
Joost Lekkerkerker 7a78f87fa6 Clean up attributes of Overseerr event entity (#136251) 2025-01-22 15:17:57 +01:00
Joost Lekkerkerker eb20a00aa2 Add reconfigure flow to Overseerr (#136248) 2025-01-22 14:55:17 +01:00
Huyuwei 4c8b4b36e5 Record IQS for Switchbot (#136058)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-22 14:27:13 +01:00
Joost Lekkerkerker 194d59df03 Add reauth flow to Overseerr (#136247) 2025-01-22 14:23:00 +01:00
Christopher Fenner b90e3917a3 Bump PyViCare to 2.41.0 (#136231) 2025-01-22 07:08:32 -06:00
Norbert Rittel 06dc88f7b5 Replace field keys in descriptions with translatable friendly names (#136230)
Replace field keys in description with translatable names
2025-01-22 07:05:55 -06:00
Joost Lekkerkerker 5e63e02ebc Handle invalid auth in Overseerr (#136243) 2025-01-22 13:47:13 +01:00
Nathan Spencer 0b7ed7dcbd Add quality_scale file to litterrobot (#135904) 2025-01-22 13:17:59 +01:00
J. Nick Koston a150e39922 Bump httpx to 0.28.1, httpcore to 1.0.7 along with required deps (#133840) 2025-01-22 12:50:00 +01:00
Shay Levy 2ca4c8aacf Update LG webOS TV IQS (#135509) 2025-01-22 12:42:18 +01:00
Thijs W. 99d1c51a3b Fix passing value to pymodbus low level function (#135108) 2025-01-22 12:33:21 +01:00
Ludovic BOUÉ 1ea6cba1f5 Handle empty string BatReplacementDescription from Matter attribute value (#134457) 2025-01-22 12:28:18 +01:00
Andrew Sayre f4d6cb45e5 Add repeat feature to HEOS media player (#136180) 2025-01-22 12:25:56 +01:00
Norbert Rittel a3cc68754f Make description of hdmi_cec.select_device action consistent (#136228)
The hdmi_cec.select_device action has an inconsistent description that causes wrong (machine) translations.

This commit brings it in line with all other actions in the integration.
2025-01-22 10:18:41 +01:00
Nathan Spencer 67ca9e45b5 Use kw_only attribute for remaining entity descriptions in litterrobot (#136202)
* Use kw_only attribute for binary sensor descriptions in litterrobot

* Update time.py with kw_only for litterrobot

* Wrap multiline lambda
2025-01-22 10:14:48 +01:00
J. Nick Koston 29f9c88041 Bump habluetooth to 3.11.2 (#136221) 2025-01-22 09:59:15 +01:00
J. Nick Koston 6ee4eb2280 Bump bluetooth-adapters to 0.21.1 (#136220) 2025-01-22 09:56:41 +01:00
Arie Catsman 03be8a039c Use icon translations for enphase_envoy. (#136190) 2025-01-22 09:54:45 +01:00
Jan Bouwhuis b8632063f5 Add dhcp discovery to incomfort integration (#136027)
* Add dhcp discovery to incomfort integration

* Remove duplicate code

* Ensure confirmation when discovered via DHCP

* Validate hostname is not changed

* Fix test

* Create gateway device with unique_id

* Add tests for assertion on via device

* Add registered devices to allow dhcp updates

* Migrate existing entry with host match

* Always load gatewate device an check if exising entry is loaded

* Make isolated flow step for dhcp auth

* Suggestions from code review
2025-01-22 07:55:55 +01:00
dependabot[bot] a511610f24 Bump github/codeql-action from 3.28.1 to 3.28.2 (#136225) 2025-01-22 07:53:32 +01:00
Marc Mueller f822fd82bb Fix recorder fixture typing (#136174) 2025-01-21 18:18:05 -10:00
krakonos1602 ffcb4d676b Add Eve Thermo TRV Matter features (#135635)
* Add Eve Thermo Matter features

* Update homeassistant/components/matter/switch.py

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

* Update homeassistant/components/matter/switch.py

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

* Update homeassistant/components/matter/switch.py

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

* Add Eve Thermo Child lock test

* Update homeassistant/components/matter/switch.py

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

* Update homeassistant/components/matter/switch.py

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

* Implement thorough Child lock testing

* Apply suggestions from code review

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
2025-01-22 03:42:07 +01:00
J. Nick Koston 18ab882536 Bump bleak-esphome to 2.1.0 (#136214) 2025-01-21 14:58:20 -10:00
J. Diego Rodríguez Royo 386357d9bd Bump ollama to 0.4.7 (#136212) 2025-01-21 14:16:26 -10:00
J. Nick Koston 561e027dee Bump habluetooth to 3.10.0 (#136210) 2025-01-21 13:27:09 -10:00
Nathan Spencer e7345dd44a Remove extra_state_attributes from Litter-Robot vacuum entities (#136196) 2025-01-22 00:49:43 +02:00
Norbert Rittel 940a0f85e9 Remove excessive newline codes from strings of nissan_leaf (#136197)
Just three occurrences of `\n." to remove.
2025-01-21 15:37:02 -06:00
Norbert Rittel 6130c2f676 Remove excessive newlines from envisalink strings (#136194)
Remove excessive newline codes from user-facing strings

Delete two occurrences of `\n.` from the strings.json file.
2025-01-21 15:35:45 -06:00
Nathan Spencer b9537466fd Add button to reset Litter-Robot 4 (#136191) 2025-01-21 15:31:59 -06:00
J. Nick Koston 3bcef79562 Bump bleak-retry-connector to 3.8.0 (#136203) 2025-01-21 11:28:11 -10:00
Nathan Spencer 69900ed8cb Cleanup litterrobot switch entity (#136199) 2025-01-21 11:12:15 -10:00
Norbert Rittel f274a3eb37 Fix sentence-casing in user-facing strings of nmap_tracker (#136195) 2025-01-21 21:33:11 +01:00
Abílio Costa cbf040a79d Merge branch 'dev' into zha_3phase_remaining_meetering 2025-01-21 19:15:55 +00:00
Abílio Costa baf5061fba Add strings and state attrs for ZHA 3 Phase current (#132871)
* Add strings and state attrs for ZHA 3 Phase current

* Use lower case
2025-01-21 14:04:41 -05:00
Norbert Rittel e4d19a41fd Fix casing and spelling in user-facing strings of homematicip_cloud (#136188)
- change all occurrences of "HomematicIP" to "Homematic IP" for consistency
- use sentence-casing for "access point" and "configuration"
- write all occurrences of "access point" in two words
- change "id" to uppercase "ID"
- Change abbreviation "hap" to "HAP" (Homematic access point)
- make one action description consistent with HA standard
- Reword config_output_path description to avoid starting with brackets
- change one occurrence of "home-assistant" to "Home Assistant"
2025-01-21 11:36:23 -06:00
Paulus Schoutsen 22e0b0e9a7 Voip migrate entities (#136140)
* Migrate VoIP entities

* Revert device name to host again
2025-01-21 11:12:30 -06:00
Andrew Sayre dd31c2c832 Set PARALLEL_UPDATES for HEOS media_player (#136178)
Set PARALLEL_UPDATES
2025-01-21 16:18:34 +01:00
Andrew Sayre 9bf2996ea0 Update HEOS tests to not interact directly with integration internals (#136177) 2025-01-21 16:00:34 +01:00
epenet b11b36b523 Add more util aliases to import conventions (#136153) 2025-01-21 15:58:23 +01:00
Erik Montnemery 3b79ded0b0 Use HassKey for hassio component data (#136172) 2025-01-21 15:52:46 +01:00
Duco Sebel 380c2ac600 Bumb python-homewizard-energy to 8.1.1 (#136170) 2025-01-21 08:47:42 -06:00
Huyuwei b93907ab02 Add data_description to switchbot translations (#136148)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-21 15:12:09 +01:00
Markus Adrario fb96ef99d0 Homee sensor (#135447)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-21 15:02:42 +01:00
Erik Montnemery 032940f1a9 Gate update.install backup parameter by supported feature (#136169) 2025-01-21 14:41:37 +01:00
Erik Montnemery a2cbaef264 Prepare backup store to read version 2 (#136149) 2025-01-21 14:37:44 +01:00
Paulus Schoutsen 5b49ba563e Satellite announcement to track original media id (#136141) 2025-01-21 14:33:37 +01:00
Erik Montnemery a60d2b69e3 Add service backup.create_automatic (#136152) 2025-01-21 12:40:54 +00:00
Mick Montorier-Aberman 33a2fa2c85 Add support for Bot in SwitchBot Cloud (#135606) 2025-01-21 12:08:38 +00:00
Arie Catsman e822f5de6e Fix typo in enphase_envoy data description (#136164) 2025-01-21 12:43:17 +01:00
Norbert Rittel e4219b617c Capitalize "Homematic" brand name and 2 more user string fixes (#136113)
Capitalize "Homematic" brand name and more user string fixes
2025-01-21 12:28:00 +02:00
Ville Skyttä 40eb8b91cc Adjust to recommended propcache.api import paths (#136150) 2025-01-21 10:58:22 +01:00
Maciej Bieniek 57b17472d7 Clean up entity registry imports in Shelly tests (#136159) 2025-01-21 11:47:15 +02:00
Franck Nijhof 6e4a21987b Merge branch 'master' into dev 2025-01-21 09:41:47 +00:00
Marcel van der Veldt f422ad22c4 Add value is not to Matter discovery schema logic (#136157) 2025-01-21 10:15:32 +01:00
epenet 364556a7dd Prefer from...import...as over import...as in core tests (#136146) 2025-01-21 09:28:17 +01:00
fwestenberg 0254be78d6 Bump Devialet to 1.5.7 (#136114) 2025-01-21 08:46:32 +01:00
Erik Montnemery fb4df00e3c Add support for custom weekly backup schedule (#136079)
* Add support for custom weekly backup schedule

* Rename the new flag to custom_days

* Make the store change backwards compatible

* Improve comments
2025-01-21 08:27:41 +01:00
Andrew Sayre 79a43b8a50 Update HEOS tests to not patch internals (#136136) 2025-01-21 08:26:34 +01:00
Brett Adams f6b444b24b Fix buttons in Teslemetry (#136142) 2025-01-21 08:06:18 +01:00
dependabot[bot] a73ab4145a Bump actions/stale from 9.0.0 to 9.1.0 (#136145) 2025-01-21 08:02:31 +01:00
cdnninja ac59203279 Remove not needed warning in Z-Wave (#136006)
* Remove unneeded logging

* ruff correction
2025-01-21 02:25:53 +01:00
Arie Catsman 24e6441806 Add data descriptions for enphase_envoy config flows. (#136120) 2025-01-20 18:47:33 -06:00
Allen Porter 0035c7b1fe Add myself to Roborock codeowners (#136134) 2025-01-21 01:22:50 +01:00
Joris Pelgröm 09ef4d9b05 Bump letpot to 0.3.0 (#136133) 2025-01-21 00:52:21 +01:00
G Johansson b8ed80328a Bump holidays to 0.65 (#136122) 2025-01-20 16:59:12 -06:00
Arie Catsman ba2c8646e9 Add scheduled envoy firmware checks to enphase_envoy coordinator (#136102)
* Add scheduled envoy firmware checks to enphase_envoy coordinator

* Set firmware scantime to 4 hours and split test in 2
2025-01-20 16:58:10 -06:00
Maciej Bieniek 11d44e608b Add additional entities for Shelly BLU TRV (#135244)
* Add valve position sensor

* Add valve position and external sensor temperature numbers

* Fix method name

* Better name

* Add remove condition

* Add calibration binary sensor

* Add battery and signal strength sensors

* Remove condition from ShellyRpcEntity

* Typo

* Add get_entity_class helper

* Add tests

* Use snapshots in tests
2025-01-21 00:11:20 +02:00
Andrew Sayre d7ec99de7d Remove yaml config fixture from HEOS tests (#136123) 2025-01-20 22:18:46 +01:00
Sid 24610e4b9f Enable Ruff B035 (#135883) 2025-01-20 21:09:28 +01:00
Andrew Sayre dde6dc0421 Raise exceptions in HEOS service actions (#136049)
* Raise errors instead of log

* Correct docstring typo
2025-01-20 13:29:57 -06:00
Joost Lekkerkerker a4d2fe2d89 Bump python-overseerr to 0.6.0 (#136104) 2025-01-20 13:17:03 -06:00
Shay Levy ad6d54dfd2 Bump ayla-iot-unofficial to 1.4.5 (#136099) 2025-01-20 13:13:32 -06:00
Joost Lekkerkerker d404d619d0 Add icon to overseerr (#136110) 2025-01-20 13:00:59 -06:00
Sid 4c008a5cb5 Fix upload service response for google_photos (#136106) 2025-01-20 13:00:02 -06:00
Jan Bouwhuis e7a635abc8 Fix index in incomfort diagnostics generator (#136108) 2025-01-20 19:53:04 +01:00
Andrew Sayre 45e00eb13d Add integration_type to HEOS (#136105) 2025-01-20 19:51:26 +01:00
Shay Levy 8d99a54656 Bump aiowebostv to 0.5.0 (#136097) 2025-01-20 08:31:45 -10:00
Steven B. a84335ae6d Enable dynamic child devices for tplink module entities (#135822)
Add dynamic child device handling to tplink integration for module based entities. For child devices that could be added/removed to hubs.

This address the module based platforms. #135229 addressed feature based platforms.
2025-01-20 19:13:14 +01:00
Maikel Punie cf33671718 Bump velbusaio to 2025.1.1 (#136089) 2025-01-20 19:41:49 +02:00
Sid 83b0d5a0b9 Enable Ruff B024 (#136088) 2025-01-20 19:14:50 +02:00
Joost Lekkerkerker 3f8f206c53 Add diagnostics to Overseerr (#136094) 2025-01-20 19:13:33 +02:00
Joost Lekkerkerker 63f14b9487 Fix Overseerr event types translations (#136096) 2025-01-20 19:12:13 +02:00
Franck Nijhof 3e1d13b6ad 2025.1.3 (#136092) 2025-01-20 18:04:03 +01:00
Marc Mueller af02dbf0cb Update pylint to 3.3.3 and astroid to 3.3.8 (#136090) 2025-01-20 06:52:18 -10:00
Robert Resch 05c7cb5f32 Bump uv to 0.5.21 (#136086) 2025-01-20 17:21:17 +01:00
Franck Nijhof d9e6549ad5 Bump version to 2025.1.3 2025-01-20 16:03:47 +00:00
Erik Montnemery 3c534a73f5 Always include SSL folder in backups (#136080) 2025-01-20 16:03:35 +00:00
Robert Resch 92b786e8cf Bump deebot-client to 11.0.0 (#136073) 2025-01-20 16:03:32 +00:00
Joost Lekkerkerker 4ed027b1cc Bump yt-dlp to 2025.01.15 (#136072) 2025-01-20 16:03:29 +00:00
J. Nick Koston b9b9322c91 Bump onvif-zeep-async to 3.2.3 (#136022) 2025-01-20 16:03:26 +00:00
Scott K Logan 3922b8eb80 Bump aioraven to 0.7.1 (#136017) 2025-01-20 16:03:23 +00:00
J. Nick Koston 5d1e2d17da Handle invalid datetime in onvif (#136014) 2025-01-20 16:03:20 +00:00
Joakim Plate b1445e5926 Correct type for off delay in rfxtrx (#135994) 2025-01-20 16:03:17 +00:00
Joost Lekkerkerker 8101fee9bb Fix switchbot cloud library logger (#135987) 2025-01-20 16:03:13 +00:00
J. Nick Koston 670371ff38 Bump aiooui to 0.1.9 (#135956) 2025-01-20 16:02:24 +00:00
J. Nick Koston f8eb42a094 Bump aiooui to 0.1.8 (#135945) 2025-01-20 16:00:39 +00:00
Matthias Alphart ca891bfc3e Update knx-frontend to 2025.1.18.164225 (#135941) 2025-01-20 15:58:44 +00:00
Glenn Vandeuren (aka Iondependent) 6da6de6a35 Update NHC lib to v0.3.4 (#135923)
Update NHC to v0.3.4
2025-01-20 15:58:40 +00:00
Glenn Vandeuren (aka Iondependent) 1bf1804492 Round brightness in Niko Home Control (#135920) 2025-01-20 15:58:37 +00:00
J. Nick Koston 11205f1c9d Bump onvif-zeep-async to 3.2.2 (#135898) 2025-01-20 15:58:34 +00:00
J. Nick Koston 84b3db1674 Prevent HomeKit from going unavailable when min/max is reversed (#135892) 2025-01-20 15:58:30 +00:00
Raphael Hehl a42c2b2986 Remove device_class from NFC and fingerprint event descriptions (#135867) 2025-01-20 15:58:27 +00:00
Álvaro Fernández Rojas 480045887a Update aioairzone to v0.9.9 (#135866)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-20 15:58:23 +00:00
J. Nick Koston 4f5235cbd4 Handle invalid HS color values in HomeKit Bridge (#135739) 2025-01-20 15:58:20 +00:00
Joost Lekkerkerker 83ab6b8ea2 Add reauthentication to SmartThings (#135673)
* Add reauthentication to SmartThings

* Add reauthentication to SmartThings

* Add reauthentication to SmartThings

* Add reauthentication to SmartThings
2025-01-20 15:58:16 +00:00
Jan Bouwhuis cc0989b50e Fix mqtt number state validation (#135621) 2025-01-20 15:58:12 +00:00
Glenn Waters 44046c5f83 Bump elkm1-lib to 2.2.11 (#135616) 2025-01-20 15:58:09 +00:00
Joost Lekkerkerker 0bd03346e8 Use device supplied ranges in LaMetric (#135590) 2025-01-20 15:58:05 +00:00
Joost Lekkerkerker c6cde13615 Bump demetriek to 1.2.0 (#135580) 2025-01-20 15:58:02 +00:00
Michael Hansen 0e37e04928 Use STT/TTS languages for LLM fallback (#135533) 2025-01-20 15:57:59 +00:00
Artur Pragacz bef545259e Fix referenced objects in script sequences (#135499) 2025-01-20 15:57:55 +00:00
Khole d77ec8ffbe Replace pyhiveapi with pyhive-integration (#135482) 2025-01-20 15:57:52 +00:00
Mick Vleeshouwer 75a1a46a49 Fix incorrect cast in HitachiAirToWaterHeatingZone in Overkiz (#135468) 2025-01-20 15:57:48 +00:00
Ravaka Razafimanantsoa 2b636423d9 Bump switchbot-api to 2.3.1 (#135451) 2025-01-20 15:57:45 +00:00
Norbert Rittel ed4c54a700 Fix descriptions of send_message action of Bring! integration (#135446)
* Make "Urgent message" selector consistent, use "Bring!" as name

- Replace one occurrence of "bring" with the brand name "Bring!"
- Change description of action to third-person singular for consistency in Home Assistant
- Make all occurrences of the selector "Urgent message" consistent (in sentence case) so they all get consistent translations, too
- Change one related error message to refer to the UI name of the required "Article" field

* Changed ` to '  to avoid Regex problems

* Reverted change to notify_missing_argument_item

Reverted to avoid failing test

* Reverted change to "bring"

* Add "is" to description of "Article"

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

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-01-20 15:57:42 +00:00
Joost Lekkerkerker 1d22fa9b45 Actually use translated entity names in Lametric (#135381) 2025-01-20 15:57:38 +00:00
Quentame 5356ffa539 Bump Freebox to 1.2.2 (#135313) 2025-01-20 15:57:35 +00:00
epenet 0660eae6f4 Fix missing comma in ollama MODEL_NAMES (#135262) 2025-01-20 15:57:32 +00:00
adam-the-hero 56f54cdccf Fix Watergate Power supply mode description and MQTT/Wifi uptimes (#135085) 2025-01-20 15:57:28 +00:00
Brett Adams 48c23c2e79 Bump pyaussiebb to 0.1.5 (#134943)
Bump
2025-01-20 15:57:25 +00:00
Renier Moorcroft 93c5915faa Image entity key error when camera is ignored in EZVIZ (#134343) 2025-01-20 15:57:22 +00:00
dcmeglio 8865fc0c33 Gracefully handle webhook unsubscription if error occurs while contacting Withings (#134271) 2025-01-20 15:57:19 +00:00
Matthew FitzGerald-Chamberlain 9680abf51e Aprilaire - Fix humidifier showing when it is not available (#133984) 2025-01-20 15:57:15 +00:00
Konrad Vité c687a6f669 Fix DiscoveryFlowHandler when discovery_function returns bool (#133563)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-20 15:57:02 +00:00
Joost Lekkerkerker 3630c8b8ed Set configuration url to overseerr instance (#136085) 2025-01-20 16:25:06 +01:00
Marc Mueller 29b7d5c2e4 Improve conversation typing (#136084) 2025-01-20 15:32:18 +01:00
Erik Montnemery a7d5e52ffe Always include SSL folder in backups (#136080) 2025-01-20 15:21:34 +01:00
Abílio Costa 3e716a1308 Use fixtures for Network component tests (#135220) 2025-01-20 15:19:17 +01:00
Paul Donohue 63d294e58e Prevent pylint out-of-memory failures (#136020) 2025-01-20 15:00:32 +01:00
Norbert Rittel 9730ac4e72 Replace targets key with UI name 'Targets' in media_player.join action (#136063) 2025-01-20 14:58:53 +01:00
Norbert Rittel ea82c4974e Fix spelling of "ID" in hyperion user strings (#136082) 2025-01-20 14:53:41 +01:00
epenet 3342904330 Use new ServiceInfo location in core tests (#136067) 2025-01-20 14:04:58 +01:00
Norbert Rittel 077fbb91c0 Improve user interface strings in opentherm_gw (#136078) 2025-01-20 12:28:30 +00:00
epenet c5efad3a2d Use new ServiceInfo location in component tests (part 4) (#136065) 2025-01-20 13:19:17 +01:00
epenet af40b6524e Use new ServiceInfo location in component tests (part 3) (#136064) 2025-01-20 13:16:59 +01:00
epenet fe010289b4 Use new ServiceInfo location in component tests (part 2) (#136062) 2025-01-20 13:13:45 +01:00
epenet 64500e837f Use new ServiceInfo location in component tests (part 1) (#136057) 2025-01-20 13:09:34 +01:00
Erik Montnemery 760168de83 Allow backup writer to update progress during restore (#135975)
* Allow backup writer to update progress during restore

* Clarify comment
2025-01-20 12:58:17 +01:00
Erik Montnemery 43da828a51 Make the time for automated backups configurable (#135825)
* Make the time for automated backups configurable

* Store time as a string, use None to indicate default time

* Don't add jitter if the time is set by user

* Include time of next automatic backup in response to backup/info

* Update tests

* Rename recurrence to state

* Include scheduled backup time in backup/config/info response

* Address review comments

* Update cloud test

* Add test for store migration

* Address review comments
2025-01-20 12:57:46 +01:00
Robert Resch 8020bec47b Bump deebot-client to 11.0.0 (#136073) 2025-01-20 12:55:09 +01:00
Norbert Rittel 9e40b7f7f4 Fix casing of "client" and "ID" in transmission integration (#136071) 2025-01-20 12:50:53 +01:00
Joost Lekkerkerker e27a259541 Bump yt-dlp to 2025.01.15 (#136072) 2025-01-20 12:50:15 +01:00
epenet f7f6c1163d Use new SsdpServiceInfo location in remaining components (#136053) 2025-01-20 11:40:00 +01:00
Artur Pragacz 877e44e3c9 Remove redundant device update code (#134100)
Remove redundant device update steps
2025-01-20 09:37:32 +01:00
Manu ff80a7c5bc Add reconfiguration flow to Habitica (#136038) 2025-01-20 09:25:45 +01:00
Manu 9e37c0dc8f Add diagnostics platform to IronOS integration (#136040) 2025-01-20 08:12:42 +01:00
Paulus Schoutsen 85f10cf60a Use LLM fallback when local matching matches intent but not targets (#136045)
LLM fallback to be used when local matching matches intent but finds no targets
2025-01-20 02:06:06 -05:00
Joost Lekkerkerker 53ad02a1eb Enable RUF032 (#135836) 2025-01-20 08:05:33 +01:00
J. Nick Koston be2c592b17 Bump habluetooth to 3.9.2 (#136042) 2025-01-20 08:01:44 +01:00
J. Nick Koston bf56583385 Bump thermopro-ble to 0.10.1 (#136041) 2025-01-19 16:50:30 -10:00
dependabot[bot] fd0b57a357 Bump docker/build-push-action from 6.11.0 to 6.12.0 (#135749)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-19 22:57:51 +01:00
J. Nick Koston 38c709aa1b Bump onvif-zeep-async to 3.2.3 (#136022) 2025-01-19 11:12:08 -10:00
G Johansson a98bb96325 Add reconfigure flow to Trafikverket Train (#136000) 2025-01-19 21:33:15 +01:00
G Johansson 2295e3779a Ensure entity platform in cover tests (#135917) 2025-01-19 21:29:28 +01:00
G Johansson 53f80e9759 Ensure entity platform in camera tests (#135918) 2025-01-19 21:28:50 +01:00
Joost Lekkerkerker f5d35bca72 Implement cloudhooks for Overseerr (#134680) 2025-01-19 21:28:08 +01:00
Norbert Rittel 77221f53b3 Fix sentence-casing in PurpleAir integration strings (#135981) 2025-01-19 21:27:01 +01:00
jsuar a2d76cac5a Fix Slack file upload (#135818)
* pgrade Slack integration to use AsyncWebClient and support files_upload_v2

- Replaced deprecated WebClient with AsyncWebClient throughout the integration.
- Removed the unsupported `run_async` parameter.
- Added a helper function to resolve channel names to channel IDs.
- Updated `_async_send_local_file_message` and `_async_send_remote_file_message` to handle Slack's new API requirements, including per-channel uploads.
- Updated dependency from slackclient==2.5.0 to slack-sdk>=3.0.0.
- Improved error handling and logging for channel resolution and file uploads.

* Fix test to use AsyncWebClient for Slack authentication flow

* Fix Slack authentication URL by removing the www subdomain

* Refactor Slack file upload functionality and add utility for file uploads
2025-01-19 21:09:04 +01:00
Joakim Plate a69786f64f Set friendly name for PT2262 sensors to masked name (#135988) 2025-01-19 21:07:05 +01:00
Scott K Logan 2900baac04 Bump aioraven to 0.7.1 (#136017) 2025-01-19 21:05:34 +01:00
Duco Sebel 2092456c7e Bumb python-homewizard-energy to 8.1.0 (#136016) 2025-01-19 21:03:30 +01:00
Joakim Plate 2bedb2cadb Correct translation key for data bits in rfxtrx (#135990) 2025-01-19 20:43:47 +01:00
Marc Mueller 5329356f20 Update numpy to 2.2.2 (#135982) 2025-01-19 20:35:32 +01:00
Paulus Schoutsen 0c68854fdf Migrate tests from OpenAI to conversation integration (#135963) 2025-01-19 20:32:59 +01:00
David Knowles 8777dd9065 Bump pydrawise to 2025.1.0 (#135998) 2025-01-19 20:31:30 +01:00
J. Diego Rodríguez Royo 57294fa461 Do not base power switch state on appliance's operation state at Home Connect (#135932) 2025-01-19 20:24:48 +01:00
J. Nick Koston 3a078d5414 Handle invalid datetime in onvif (#136014) 2025-01-19 20:16:40 +01:00
Joakim Plate 568a27000d Correct type for off delay in rfxtrx (#135994) 2025-01-19 20:09:05 +01:00
Maikel Punie 4612f4da19 Fix velbus via devices (#135986) 2025-01-19 20:07:32 +01:00
Manu ec45cb4939 Improve exception handling in Habitica integration (#135950) 2025-01-19 19:51:55 +01:00
Jan Bouwhuis ccd7b1c21a Add incomfort heater serialnr to device info (#136012) 2025-01-19 19:51:04 +01:00
Norbert Rittel 3ee2dc9790 Make strings of create_scene action UI- and translation-friendly (#136004) 2025-01-19 19:43:47 +01:00
Jan Bouwhuis 889f699e5d Disable noisy diagnostic incomfort sensors by default (#135992) 2025-01-19 19:28:19 +01:00
Jan Bouwhuis 5ffae140af Add diagnostics feature to incomfort integration (#136009) 2025-01-19 19:27:36 +01:00
Jan Bouwhuis 04eb86e5a0 Cleanup incomfort translation strings (#135991) 2025-01-19 15:30:03 +01:00
Jan Bouwhuis 3077a4cdee Add re-configure flow incomfort integration (#135887)
* Add re-configure flow incomfort integration

* End with abort flow in reconfigure failure flow

* Apply parenthesis
2025-01-19 15:16:26 +01:00
Joost Lekkerkerker 02bf8447b3 Fix unset coordinator in Switchbot cloud (#135985) 2025-01-19 15:15:32 +01:00
Joost Lekkerkerker cf29ef91ee Fix switchbot cloud library logger (#135987) 2025-01-19 15:15:21 +01:00
Andrew Sayre 439f22f584 Fix HEOS device information (#135940) 2025-01-19 15:07:00 +01:00
Jan Bouwhuis b17c36eeff Add re-authentication flow to incomfort integration (#135861) 2025-01-19 14:26:21 +01:00
Mick Montorier-Aberman 41fe863b72 Refactor SwitchBot Cloud make_device_data (#135698) 2025-01-19 14:22:21 +01:00
Norbert Rittel dfc4cdf785 Improve descriptions in list_notifications action, fix casing (#135838) 2025-01-19 13:43:35 +01:00
Christopher Fenner 654e111c23 Fix fan speed in auto mode in ViCare integration (#134256) 2025-01-19 13:39:38 +01:00
Norbert Rittel 9d5fe77b71 Remove unnecessary "title" keys to use default setup flow instead (#135512) 2025-01-19 13:34:22 +01:00
Manu 958b1e7759 Move integration setup to coordinator _async_setup in Bring (#135711) 2025-01-19 13:29:21 +01:00
Norbert Rittel 2f5545e7b8 Fix name and descriptions of actions in EZVIZ integration etc. (#135858) 2025-01-19 13:28:49 +01:00
Erwin Douna 15d57692d9 SMA add diagnostics (#135852) 2025-01-19 13:28:15 +01:00
Sid a55bd593af Rework enigma2 tests (#135475) 2025-01-19 13:24:47 +01:00
Manuel Stahl 3978c4cdb3 Add type annotations to stiebel eltron component (#135228) 2025-01-19 13:21:59 +01:00
Norbert Rittel 4690aef8b8 Further clarify the meaning of Sensibo's Climate React mode (#135833)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-19 13:21:37 +01:00
Brett Adams 6292d6c0dc Add streaming to device tracker platform in Teslemetry (#135962)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-19 13:20:40 +01:00
Matthias Alphart af0f416497 Fix KNX default state updater option (#135611) 2025-01-19 12:53:09 +01:00
Manu acbb15a496 Set dependency-transparency and async-dependency in Habitica IQS (#135902) 2025-01-19 12:51:49 +01:00
Glenn Vandeuren (aka Iondependent) 9f3b39a2d2 Round brightness in Niko Home Control (#135920) 2025-01-19 12:51:05 +01:00
Norbert Rittel 5a91562d1d Fix grammar and plural handling in action descriptions (#135654) 2025-01-19 12:37:28 +01:00
J. Diego Rodríguez Royo ac58494b55 Improve program related sensors at Home Connect (#135929) 2025-01-19 12:02:23 +01:00
J. Diego Rodríguez Royo 33d552e3f7 Add power switch only if it is available at Home Connect (#135930) 2025-01-19 11:58:38 +01:00
Norbert Rittel f3222045ae Change 'device_id' to translatable 'device ID', fix typos in LCN (#135978) 2025-01-19 11:56:34 +01:00
J. Nick Koston 0d968267a2 Improve remote Bluetooth scanner manufacturer data (#135961)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-19 11:55:13 +01:00
cdnninja 85bea5b70e Vesync switch humidifier to property (#135949) 2025-01-19 11:43:16 +01:00
Erik Montnemery 02347d5d36 Improve backup store in tests (#135974) 2025-01-19 11:13:37 +01:00
Paulus Schoutsen 754de6f998 Add shared history for conversation agents (#135903)
* Add shared history for conversation agents

* Remove unused code

* Add support for native history items

* Store all assistant responses as assistant in history

* Add history support to DefaultAgent.async_handle_intents

* Make local fallback work

* Add default agent history

* Add history cleanup

* Add tests

* ChatHistory -> ChatSession

* Address comments

* Update snapshots
2025-01-18 22:33:03 -05:00
Norbert Rittel 32d7a23bff Fix duplicated "effect" in Speed field descriptions of flux_led (#135948) 2025-01-18 15:13:28 -10:00
J. Nick Koston fe4e001fa5 Bump bluetooth-adapters to 0.21.0 (#135957) 2025-01-18 15:10:15 -10:00
J. Nick Koston 725d835fab Bump aiooui to 0.1.9 (#135956) 2025-01-18 15:01:55 -10:00
J. Nick Koston 640da1cc67 Bump aiooui to 0.1.8 (#135945) 2025-01-19 00:53:59 +01:00
Marc Mueller 6690b121c0 Fix unicode chars in zha tests (#135954) 2025-01-18 13:47:30 -10:00
Marc Mueller 8a3ef101e6 Replace additional deprecated USBServiceInfo imports (#135953) 2025-01-18 17:43:07 -06:00
J. Nick Koston 09ae388f4e Bump bleak-retry-connector to 3.7.0 (#135939) 2025-01-18 12:02:18 -10:00
Matthias Alphart 659450dac9 Update knx-frontend to 2025.1.18.164225 (#135941) 2025-01-18 22:33:41 +01:00
Álvaro Fernández Rojas 37c3a9546c Update aioairzone to v0.9.9 (#135866)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-18 10:57:54 -10:00
Norbert Rittel b32c401c24 Fix inconsistently spelled occurrences of "ID" in telegram_bot integration (#135928)
* Make all occurrences of "ID" in telegram_bot consistent

- change all remaining occurrences of "id" or "Id" to the correct spelling "ID"
- change "chat_id" to the UI-friedly "chat ID"
- use "ID of the chat …" in descriptions, matching "ID of the message …"
- fix the edit_replymarkup action's description to also use "Edits …", matching all other descriptions with "Sends …" or "Edits …"

* Use translatable descriptions for the Timeout fields

Uses the description from the online documentation that can be translated while the current ones use the action name which makes it difficult to handle in other languages.
2025-01-18 14:54:44 -06:00
Marc Mueller 19e5b091c5 Use HassKey for assist_pipeline singleton (#135875) 2025-01-18 09:52:13 -10:00
Marc Mueller 24c50e0988 Fix aiodns DeprecationWarning in tests (#135921) 2025-01-18 08:04:01 -10:00
Joost Lekkerkerker fe8a93d62f Add reauthentication to SmartThings (#135673)
* Add reauthentication to SmartThings

* Add reauthentication to SmartThings

* Add reauthentication to SmartThings

* Add reauthentication to SmartThings
2025-01-18 18:41:24 +01:00
Glenn Vandeuren (aka Iondependent) b39c2719d7 Update NHC lib to v0.3.4 (#135923)
Update NHC to v0.3.4
2025-01-18 18:47:20 +02:00
Marc Mueller 0c9fd7c482 Fix DeprecationWarnings in mcp_server (#135927)
* Fix DeprecationWarnings in mcp_server

* Spelling
2025-01-18 18:43:35 +02:00
Marc Mueller dedcef7230 Fix acmeda pytest usefixtures spelling (#135919) 2025-01-18 17:08:07 +01:00
Manu 595f49ee9f Set strict-typing in Habitica quality scale record (#135899)
* Set strict-typing in Habitica quality scale record

* cast
2025-01-18 16:35:35 +01:00
G Johansson 5a7b6cd7a0 Remove asserting name in tts test (no entity platform) (#135726)
* Ensure entity platform in tts tests

* Correct placement

* Remove name test

* Remove hass
2025-01-18 14:47:53 +01:00
Maciej Bieniek f0c6b47522 Increase test coverage for IMGW-PIB (#135915) 2025-01-18 13:31:17 +01:00
Joris Pelgröm d349c47694 Add reauth flow to LetPot integration (#135734) 2025-01-18 06:11:35 -06:00
Josef Zweck f878465a9a Fix imgw_pib tests (#135913) 2025-01-18 06:07:28 -06:00
Nathan Spencer 81b7d01a7d Bump pylitterbot to 2024.0.0 (#135891) 2025-01-18 13:01:09 +01:00
Manu f5dd3ef530 Increase test coverage in Habitica integration (#135896)
Add tests to Habitica integration
2025-01-18 12:59:23 +01:00
Brett Adams 88f16807a0 Bump Teslemetry Stream to 0.6.6 (#135905)
bump66
2025-01-18 12:38:20 +01:00
Manu 76d9bcbdfb Set parallel-updates in Habitica quality scale record (#135901) 2025-01-18 11:17:58 +01:00
tronikos f01598aadd Use runtime_data in Opower (#135910)
* Use runtime_data in Opower

* Fix async_unload_entry

* Fix async_unload_entry

* fix
2025-01-18 11:14:31 +01:00
Josef Zweck c56eee3639 Fix bmw_connected_drive tests (#135911) 2025-01-18 11:10:52 +01:00
tronikos 06d8bc658f Fix typo in Opower log message (#135909) 2025-01-18 10:39:40 +01:00
Noah Husby f724ae9a01 Record IQS for Russound RNET (#134692) 2025-01-18 08:33:49 +01:00
J. Nick Koston bbe897745e Bump onvif-zeep-async to 3.2.2 (#135898) 2025-01-17 19:30:21 -10:00
Ernst Klamer 089c9c41ba Add BThome hold press event (#135871)
* add hold_press

* add hold_press

* add hold_press

* add hold_press
2025-01-18 02:23:25 +02:00
J. Nick Koston 43fe4ebbbe Prevent HomeKit from going unavailable when min/max is reversed (#135892) 2025-01-17 14:08:17 -10:00
J. Nick Koston fc1b6292cd Bump dbus-fast to 2.30.2 (#135874) 2025-01-17 13:05:18 -10:00
J. Nick Koston 174f3ca755 Bump ulid-transform to 1.2.0 (#135882) 2025-01-17 12:06:28 -10:00
J. Nick Koston 51d277fc0c Bump bluetooth-data-tools to 1.22.0 (#135879) 2025-01-17 12:06:01 -10:00
J. Nick Koston b98e1a1d2f Bump habluetooth to 3.9.0 (#135877) 2025-01-17 12:05:41 -10:00
J. Nick Koston a08e42399d Bump fnv-hash-fast to 1.2.2 (#135872) 2025-01-17 12:04:53 -10:00
epenet 2b0e383b2e Use new ServiceInfo location in zha (#135703) 2025-01-17 22:56:59 +01:00
J. Nick Koston 9868138fc4 Bump aioesphomeapi to 28.0.1 (#135869) 2025-01-17 11:53:29 -10:00
epenet c601170b1d Use new ServiceInfo location in devolo_home_network (#135690) 2025-01-17 21:01:05 +01:00
Raphael Hehl 5ea5413064 Remove device_class from NFC and fingerprint event descriptions (#135867) 2025-01-17 09:49:01 -10:00
Marc Mueller abc256fb3e Add overload for async singleton call with HassKey (#134059) 2025-01-17 19:22:48 +01:00
G Johansson 2ec971ad9d Remove not needed name from config flow in SMHI (#134841) 2025-01-17 19:21:13 +01:00
Erik Montnemery 235fda55fe Validate config entry when adding or updating entity registry entry (#135067) 2025-01-17 19:18:13 +01:00
epenet 028a0d4eec Remove call to get_serial_by_id in homeassistant_sky_connect (#135751) 2025-01-17 19:10:56 +01:00
epenet 14f3868c26 Fix flaky test in acmeda (#135846) 2025-01-17 19:08:32 +01:00
Indu Prakash 54e4e8a7bb Fix humidifier on off status update (#135743) 2025-01-17 18:49:33 +01:00
Andre Lengwenus a8cb618f96 Add missing data_descriptions to strings.json for LCN (#135674) 2025-01-17 18:09:19 +01:00
Norbert Rittel ca5aca4ab9 Fix "set" / "sets" in action names and descriptions, spelling of "dB" (#135659) 2025-01-17 18:08:48 +01:00
Guido Schmitz ea7e53d10d Add zeroconf dependency to devolo Home Network manifest (#135708) 2025-01-17 18:08:26 +01:00
Norbert Rittel c7de3112fb Fix several issues in a string of IHC integration (#135618) 2025-01-17 18:02:33 +01:00
hahn-th 4a64c797d4 Add doorbell event to homematicip_cloud (#133269) 2025-01-17 17:54:15 +01:00
Max R 2a514ebc3f Update yolink "play on speaker hub" action to allow optional values (to match YoLink API) (#133099) 2025-01-17 17:43:47 +01:00
jesperraemaekers 44b577cadb Bump Weheat to 2025.1.15 (#135626) 2025-01-17 17:43:15 +01:00
Brett Adams 24bb623567 Add streaming to Teslemetry cover platform (#135660) 2025-01-17 17:38:03 +01:00
tronikos 9e0df89bee Log errors in opower (#135497) 2025-01-17 17:33:48 +01:00
Norbert Rittel fb309a3f98 Fix description of "x10_all_units_off" action (#135000) 2025-01-17 17:18:38 +01:00
Mick Vleeshouwer 829d3bf621 Add support for EvoHomeController in Overkiz (#133777) 2025-01-17 17:13:25 +01:00
Bouwe Westerdijk a2afc1b670 Plugwise test-code improvements (#134193) 2025-01-17 17:12:09 +01:00
Norbert Rittel 7b413b5faf Clarify action descriptions regarding Lost device sound and state (#134277) 2025-01-17 16:56:14 +01:00
Markus Adrario 734d1898cf Homee: fix cover if it has no up/down attribute (#135563) 2025-01-17 15:51:18 +01:00
Joost Lekkerkerker c651e2b3c3 Enable RUF101 (#135835) 2025-01-17 13:01:07 +01:00
Joost Lekkerkerker ef8b8fbbaa Enable RUF023 (#135830) 2025-01-17 12:28:27 +01:00
Renier Moorcroft 23e04ced9c Image entity key error when camera is ignored in EZVIZ (#134343) 2025-01-17 12:27:44 +01:00
Noah Husby 13a7ad759c Add media position & seek to Russound RIO (#134372) 2025-01-17 12:03:52 +01:00
Andre Lengwenus 99d250f222 Set target value on LCN regulator lock (#133870) 2025-01-17 11:15:42 +01:00
Brett Adams 689d7d3cd9 Add Energy History to Tesla Fleet (#126878)
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: JEMcats <hurst-status09@icloud.com>
Co-authored-by: JEMcats <jakobmattheis@icloud.com>
2025-01-17 10:34:35 +01:00
Joost Lekkerkerker b4f4b06f29 Enable RUF021 (#135832) 2025-01-17 10:20:45 +01:00
Simon 85b4be2f16 Add model option to speak action for ElevenLabs (#133902) 2025-01-17 10:18:07 +01:00
dcmeglio 5e0bbf65e4 Gracefully handle webhook unsubscription if error occurs while contacting Withings (#134271) 2025-01-17 10:14:41 +01:00
Richard Kroegel 514b74096a Improve BMW test quality (#133704) 2025-01-17 09:58:46 +01:00
Richard Kroegel b1d8994751 Add BMW quality scale details (#132017)
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2025-01-17 09:49:58 +01:00
J. Nick Koston c215aee940 Reduce duplicate code in the Bluetooth WebSocket API (#135808) 2025-01-17 10:30:14 +02:00
Manu 5f9457ab6e Update quality scale docs-installation-parameters rule for IronOS integration (#133318) 2025-01-17 09:23:04 +01:00
Redge 76cdfe861c Add 'silent' to HTML5_SHOWNOTIFICATION_PARAMETERS (#135709) 2025-01-17 09:16:45 +01:00
epenet bd91cc4bdc Use new ServiceInfo location in bosch_shc (#135689) 2025-01-17 09:15:20 +01:00
G Johansson cde3ba5504 Ensure entity platform in dsmr_reader tests (#135718) 2025-01-17 09:14:40 +01:00
G Johansson 21256cab85 Ensure entity platform in google_assistant tests (#135719) 2025-01-17 09:14:25 +01:00
G Johansson d62a66eaf2 Ensure entity platform in google_wifi tests (#135720) 2025-01-17 09:14:08 +01:00
Marc Mueller 46b17b539c Use new syntax for TypeVar defaults (#135780) 2025-01-17 09:12:52 +01:00
G Johansson 6aed2dcc0f Ensure entity platform in homeassistant tests (#135721) 2025-01-17 09:11:07 +01:00
G Johansson 7430238c0a Ensure entity platform in kira tests (#135723) 2025-01-17 09:10:47 +01:00
G Johansson cd88913daf Ensure entity platform in mochad tests (#135725) 2025-01-17 09:10:29 +01:00
G Johansson 8e39c65759 Ensure entity platform in universal tests (#135727) 2025-01-17 09:10:09 +01:00
G Johansson 0f8785d8bc Ensure entity platform in alert tests (#135714) 2025-01-17 08:44:40 +01:00
Petro31 566f514a75 Allow is_state_attr to check attributes for None (#132879) 2025-01-17 08:41:10 +01:00
G Johansson f3683f0b5e Ensure entity platform in blackbird tests (#135715) 2025-01-17 08:34:47 +01:00
J. Nick Koston a39137c3fc Bump zeroconf to 0.140.1 (#135815) 2025-01-17 08:29:44 +01:00
J. Nick Koston c2b6c4b4fc Small cleanups to lifx services to reduce code (#135817) 2025-01-16 19:39:48 -10:00
J. Nick Koston daac986e00 Bump dbus-fast to 2.29.0 (#135804) 2025-01-16 15:10:01 -10:00
Avi Miller 02ec1d1b71 New paint_theme service added to the LIFX integration (#135667)
* New paint_theme service added to the LIFX integration

Signed-off-by: Avi Miller <me@dje.li>
Co-authored-by: J. Nick Koston <nick@koston.org>

* Move effect selection into a dispatch table

Signed-off-by: Avi Miller <me@dje.li>

---------

Signed-off-by: Avi Miller <me@dje.li>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-16 14:41:09 -10:00
Erwin Douna 632c166201 SMA update code owners (#135812)
Update code owners
2025-01-16 23:48:40 +01:00
Joost Lekkerkerker 8b12f5270e Enable more RUF rules (#135770)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
2025-01-16 23:43:14 +01:00
Maciej Bieniek b0d3aa1c34 Bump imgw_pib to version 1.0.9 and remove hydrological detail entities (#134668) 2025-01-16 23:42:03 +01:00
Konrad Vité e6c696933f Fix DiscoveryFlowHandler when discovery_function returns bool (#133563)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-16 12:31:16 -10:00
G Johansson e5164496cf Ensure entity platform in vacuum tests (#135786) 2025-01-16 23:27:54 +01:00
G Johansson 88c3be4ecf Ensure entity platform in light tests (#135787) 2025-01-16 23:26:50 +01:00
G Johansson 619917c679 Ensure entity platform in media_player tests (#135788) 2025-01-16 23:26:18 +01:00
Norbert Rittel e433c2250c Several strings fixes in the emoncms integration (#135792) 2025-01-16 23:22:28 +01:00
Erwin Douna 59429dea39 Bump SMA to 0.7.5 (#135799) 2025-01-16 23:20:36 +01:00
Sid 3e4d92f6a7 Bump eheimdigital to 1.0.5 (#135802) 2025-01-16 23:19:41 +01:00
Norbert Rittel a3d24f2472 Fix spelling of "API" and use consistent term "API token" (#135795) 2025-01-16 23:18:54 +01:00
Erwin Douna 46c5591336 SMA add serial number in DeviceInfo (#135809)
SSIA
2025-01-16 23:17:42 +01:00
Norbert Rittel 99f24ca59c Fix service description to match HA style, fix casing (#135797) 2025-01-16 23:15:07 +01:00
Erik Montnemery 1fee0a5aa2 Improve backup store in tests (#135798) 2025-01-16 23:14:19 +01:00
Shay Levy ef34a33a7b Remove misleading "Current" in NUT power sensor names (#135800) 2025-01-16 23:07:43 +01:00
G Johansson bb505baae7 Ensure entity platform in core config tests (#135729) 2025-01-16 12:06:20 -10:00
Jan Bouwhuis b446eaf2d0 Improve incomfort test coverage (#135806) 2025-01-16 23:04:57 +01:00
Jan Bouwhuis 60d51bf4ad Assign entity_category for incomfort entities (#135807) 2025-01-16 23:03:48 +01:00
puddly 9b66ba61a8 USB device add/remove callbacks (#131224) 2025-01-16 11:53:15 -10:00
J. Nick Koston eb651a8a71 Bump govee-ble to 0.42.0 (#135801) 2025-01-16 11:37:12 -10:00
Avi Miller 1b520e37e2 Update aiolifx-themes to 0.6.4 (#135805)
* Restore support for Python 3.12

Signed-off-by: Avi Miller <me@dje.li>

* Bump aiolifx-themes to 0.6.4

Signed-off-by: Avi Miller <me@dje.li>

---------

Signed-off-by: Avi Miller <me@dje.li>
2025-01-16 23:33:54 +02:00
puddly 9331b1572c Implement a polling fallback for USB monitor (#130918) 2025-01-16 11:14:53 -10:00
Paulus Schoutsen 762bc7b8d1 Add broadcast intent (#135337) 2025-01-16 14:41:53 -06:00
J. Nick Koston 6e255060c6 Add Bluetooth config entries for remote scanners (#135543) 2025-01-16 09:52:52 -10:00
Steve HOLWEG 93b3d76ee2 Add button to move netatmo cover to preferred position (#134722) 2025-01-16 18:34:30 +00:00
Markus Jacobsen e188d9a00c Fix Bang & Olufsen event testing (#135707)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-01-16 18:06:18 +00:00
Gerben Jongerius d908d2ab55 Bump youless-api to 2.2.0 (#135781)
Bump version youless 2.2.0
2025-01-16 17:44:09 +02:00
Duco Sebel 55bde60f1a Move HomeWizard config options to class (#135778) 2025-01-16 15:44:59 +01:00
Jan Bouwhuis 5ca68cb273 Improve incomfort coordinator logging (#135777) 2025-01-16 15:24:40 +01:00
Erik Montnemery 2e189480a5 Improve backup decrypt exceptions (#135765) 2025-01-16 16:07:13 +02:00
Joost Lekkerkerker eb98f110d3 Fix Vicare patch (#135773) 2025-01-16 15:41:24 +02:00
Paulus Schoutsen 5cf56207fe Add temperature and humidity entities to area registry (#135423)
* Add temperature and humidity entities to area registry

* Fix service test

* Add validation

* ABC

* More ABC

* More ABC 2

* Fix tests

* ABC 3

* ABC 4
2025-01-16 08:25:26 -05:00
Joost Lekkerkerker 9f7a38f189 Enable RUF022 (#135767) 2025-01-16 13:48:24 +01:00
epenet 476935050a Use new ServiceInfo location in dlna_dmr (#135691) 2025-01-16 13:41:09 +01:00
epenet 27c2f2333e Use new ServiceInfo location in esphome (#135692) 2025-01-16 13:40:13 +01:00
Christopher Fenner 40a3e19ce5 Add further ventilation-related sensors to ViCare (#131496) 2025-01-16 13:38:40 +01:00
Matthew FitzGerald-Chamberlain 9d7706c9be Aprilaire - Fix humidifier showing when it is not available (#133984) 2025-01-16 13:37:44 +01:00
Max Cabrajac a67bc12bb8 Change AdGuard Home URL field validator to accept paths (#127957) 2025-01-16 13:34:30 +01:00
Erik Montnemery 6cbe18ebbd Bump securetar to 2025.1.3 (#135762)
* Bump securetar to 2025.1.3

* Remove outdated fixture
2025-01-16 13:26:52 +01:00
epenet 1cff45b8b7 Use new ServiceInfo location in apple_tv (#135688) 2025-01-16 13:20:46 +01:00
Joost Lekkerkerker fc39b6792c Enable RUF100 (#135760) 2025-01-16 13:06:33 +01:00
DrDonoso 3638d25f6a Add message_thread_id to telegram_text and telegram_command events (#130738)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-16 13:03:42 +01:00
Martin Hjelmare 421f9aa638 Avoid using the backup manager in restore tests (#135757)
* Fix typing

* Refactor test restore backup

* Refactor test restore backup wrong password

* Refactor test restore backup wrong parameters

* Update manager state after rebase

* Remove not needed patch
2025-01-16 12:49:27 +01:00
Simone Rescio 9a1b965c7f Fix rmtree in translation script on MacOS (#129352) 2025-01-16 12:39:37 +01:00
Erik Montnemery 9db6be11f7 Support decrypting backups when downloading (#135728)
* Support decrypting backups when downloading

* Close stream

* Use test helper

* Wait for worker to finish

* Simplify

* Update backup.json

* Simplify

* Revert change from the future
2025-01-16 12:36:12 +01:00
Tyron 6fdccda225 Return Chat IDs on Telegram Bot (#131274)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-16 12:27:19 +01:00
epenet 1172887c80 Use new ServiceInfo location in zwave_js (#135704) 2025-01-16 10:55:48 +01:00
epenet f3b7317373 Use new ServiceInfo location in homeassistant_sky_connect (#135693) 2025-01-16 10:55:14 +01:00
puddly edddd6edfb Reduce USB rescan cooldown from 1 minute to 10 seconds (#135712)
* Reduce USB rescan cooldown from 1 minute to 1 second

* Increase cooldown to 10s as a middle ground
2025-01-16 11:08:38 +02:00
J. Nick Koston 016a274698 Bump govee-ble to 0.41.0 (#135750)
Adds support for the H5130 pressure/presence sensor

changelog: https://github.com/Bluetooth-Devices/govee-ble/compare/v0.40.0...v0.41.0
2025-01-16 10:48:29 +02:00
G Johansson c89d60fb5d Ensure entity platform in light tests (#135724) 2025-01-16 09:21:49 +01:00
dotvav b5a7d0258a Palazzetti integration: Update integration quality scale (#135752)
Update integration quality scale
2025-01-16 09:19:37 +01:00
Norbert Rittel 137666982d Reword action descriptions to match Home Assistant style (#135733)
* Reword action descriptions to match Home Assistant style

This commit changes the two action descriptions of the Husqvarna Automower integration to use the descriptive language that is standard in Home Assistant.

This helps in fixing or preventing wrong (machine) translations.

This is done using the wording from the online documentation by using "Lets the mower … ", moving the actual result more into focus.

* Re-add "either" to first description
2025-01-16 09:18:23 +01:00
Martin Hjelmare 77a351f992 Add receive backup tests (#135680)
* Clean up test_receive_backup_busy_manager

* Test receive backup agent error

* Test file write error during backup receive

* Test read tar error during backup receive

* Test non agent upload error during backup receive

* Test file read error during backup receive
2025-01-16 08:41:59 +01:00
G Johansson a8645ea4ed Ensure entity platform in bluetooth tests (#135716) 2025-01-15 21:24:37 -10:00
Brett Adams e886c9e054 Slow down polling for Tesla Fleet (#135747)
Slow down polling
2025-01-15 23:28:15 -05:00
Jamin 79ee2e954b Use SIP URI for VoIP device identifier (#135603)
* Use SIP URI for VoIP device identifier

Use the SIP URI instead of just host/IP address to identify VoIP
devices. This will allow calls initiating from Home Assistant to the
device as well as allows devices connecting through a PBX to be uniquely
identified.

* Add tests

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-01-15 20:59:58 -05:00
J. Nick Koston e736ca72f0 Handle invalid HS color values in HomeKit Bridge (#135739) 2025-01-15 13:33:58 -10:00
epenet be06ef46c1 Use new ServiceInfo location in wmspro (#135702)
* Use new ServiceInfo location in wmspro

* Fix self.source
2025-01-15 21:22:05 +01:00
Steven B. 51e3bf42f2 Add dynamic child device handling to tplink integration (#135229)
Add dynamic child device handling to tplink integration. For child devices that could be added/removed to hubs.
2025-01-15 20:45:06 +01:00
Ik-12 c6cab3259c Create switches for controlling policy-based routes (#134473)
Create switches for controlling policy-based routes (aka "traffic routes" in the Unifi API).
2025-01-15 20:37:33 +01:00
Sid 146d6bbc68 Bump eheimdigital to 1.0.4 (#135722) 2025-01-15 21:29:29 +02:00
Erik Montnemery f36a10126c Add WS command backup/can_decrypt_on_download (#135662)
* Add WS command backup/can_decrypt_on_download

* Wrap errors

* Add default messages to exceptions

* Improve test coverage
2025-01-15 19:40:29 +01:00
Steven B. 3622e8331b Update tplink quality_scale.yaml (#135705) 2025-01-15 16:53:57 +01:00
epenet 241fc2af67 Use new ServiceInfo location in insteon (#135694) 2025-01-15 16:35:27 +01:00
epenet 9d7c917771 Use new ServiceInfo location in modem_callerid (#135695) 2025-01-15 16:17:43 +01:00
epenet d3bedd693a Use new ServiceInfo location in rabbitair (#135696) 2025-01-15 16:17:09 +01:00
epenet 082ef3f85f Use new ServiceInfo location in rainforest_raven (#135697) 2025-01-15 16:15:51 +01:00
epenet 5e648ebb5c Use new ServiceInfo location in tplink (#135700) 2025-01-15 16:14:55 +01:00
epenet 7a442af9fa Use new ServiceInfo location in sonos (#135699) 2025-01-15 16:14:21 +01:00
epenet 406c00997f Use new ServiceInfo location in components (part 3) (#135687) 2025-01-15 15:49:45 +01:00
epenet 19a89ebcf3 Use new ServiceInfo location in components (part 2) (#135685) 2025-01-15 15:49:01 +01:00
epenet bc8a2b58d3 Use new ServiceInfo location in components (part 1) (#135682) 2025-01-15 15:43:46 +01:00
Mick Montorier-Aberman 6a50648223 Call async_forward_setup_entry after the first refresh in SwitchBot Cloud (#135625) 2025-01-15 14:33:21 +00:00
epenet e83ee00af8 Move UsbServiceInfo to service_info helpers (#135663)
* Move UsbServiceInfo to service_info helpers

* Adjust components
2025-01-15 15:10:25 +01:00
Robert Resch 8ae02aaba0 Add missing camera functions to pylint type hints plugin (#135676) 2025-01-15 14:53:08 +01:00
puddly 0eea265415 Bump python-otbr-api to 2.7.0 (#135638)
Bump OTBR API to 2.7.0

Bump `python-otbr-api` to 2.7.0 in `thread` as well
2025-01-15 15:04:42 +02:00
epenet 8c13daf6d9 Move SsdpServiceInfo to service_info helpers (#135661)
* Move SsdpServiceInfo to service_info helpers

* docstring

* Move string constants

* Adjust components
2025-01-15 15:00:27 +02:00
Martin Hjelmare 4ccc686295 Improve logging of backup upload errors (#135672)
Improve logging for upload errors
2025-01-15 14:59:42 +02:00
epenet 31c36beb2e Move DhcpServiceInfo to service_info helpers (#135658)
* Move DhcpServiceInfo to service_info helpers

* Fix mypy/pylint
2025-01-15 13:09:18 +01:00
Norbert Rittel 9c5c1a35a4 Fix descriptions of send_command action for consistency (#135670)
Three small fixes for the description keys of the send_command action of the Homeworks integration:
- use third-person singular for descriptive wording
- Change to "the command" to match "the controller" in two strings

Both ensure better and more consistent machine and human translations.
2025-01-15 14:00:40 +02:00
epenet b046ca9abe Move ZeroconfServiceInfo to service_info helpers (#135653)
* Move ZeroconfServiceInfo to service_info helpers

* Adjust deprecation date

* Fix mypy/pylint

* Fix DeprecatedConstant

* Add deprecation test

* Adjust

* Also deprecate ATTR_PROPERTIES_ID
2025-01-15 12:25:42 +01:00
Avi Miller 650e14379c Bump aiolifx-themes to v0.6.2 (#135645)
* Bump aiolifx-themes to v0.6.1

Signed-off-by: Avi Miller <me@dje.li>

* Bump aiolifx-themes to 0.6.2 to fix deps issue with 0.6.1

Signed-off-by: Avi Miller <me@dje.li>

---------

Signed-off-by: Avi Miller <me@dje.li>
2025-01-15 12:59:15 +02:00
Jan Bouwhuis 1421f4c124 Set MQTT quality scale to platinum (#135612)
* Set MQTT quality scale to platinum

* Add  test for type stub
2025-01-15 10:51:41 +01:00
Jan Bouwhuis f0257fec88 Fix mqtt number state validation (#135621) 2025-01-15 10:13:27 +01:00
Joost Lekkerkerker 8a35261fd8 Remove unused noqas (#135583) 2025-01-15 10:02:18 +01:00
hahn-th f57640c2cd Bump homematicip to 1.1.6 (#135649) 2025-01-15 09:31:48 +01:00
TimL 23a2b19ca0 Bump pysmlight v0.1.5 (#135647) 2025-01-15 09:58:38 +02:00
TimL 6cbbfec5f5 Reduce scan interval on SMLIGHT firmware updates (#135650)
Reduce scan interval on firmware updates
2025-01-15 09:56:01 +02:00
Brett Adams 65df8b946f Update buttons in Teslemetry (#135631)
* Update button

* tests
2025-01-15 08:32:46 +01:00
Thomas55555 4b37b367de Dynamic devices for Husqvarna Automower (#133227)
* Dynamic devices for Husqvarna Automower

* callbacks

* add stayout-zones together

* add alltogether on init

* fix stale lock names

* also for workareas

* separate "normal" vs callback entity adding

* mark quality scale

* Apply suggestions from code review

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

* Apply suggestions from code review

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

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-01-15 08:31:24 +01:00
Norbert Rittel c1520a9b20 Fix spelling of EnOcean in strings file of the integration (#135622) 2025-01-15 01:49:10 +01:00
Marc Mueller 239aa94b6f Update Python version for mypy to 3.13 (#135636) 2025-01-15 01:43:13 +01:00
Marc Mueller c4d8cda92b Update mypy-dev to 1.15.0a2 (#135633) 2025-01-15 00:54:54 +01:00
mkmer 6e88c6570e Return OFF in hvac_action for Honeywell climate (#135620) 2025-01-14 23:15:49 +00:00
Marc Mueller ecc89fd9a9 Fix spotify typing for Python 3.13 (#135628) 2025-01-15 00:02:22 +01:00
Ville Skyttä 18de735619 More UpCloud config entry refactors (#135548) 2025-01-14 22:49:00 +01:00
Jordan Sitkin f80f6d9e3d Add PaddleSwitchPico (Pico Paddle Remote) device trigger to Lutron Caseta (#135615) 2025-01-14 10:28:10 -10:00
Erik Montnemery c408bd6aad Bump securetar to 2025.1.2 (#135614) 2025-01-14 20:39:58 +01:00
Glenn Waters faf2c64cc4 Bump elkm1-lib to 2.2.11 (#135616) 2025-01-14 09:14:41 -10:00
Franck Nijhof 60bdc13c94 Drop Python 3.12 support (#135589) 2025-01-14 16:23:15 +01:00
epenet fa96168488 Rename onewire entity classes (#135601) 2025-01-14 15:44:18 +01:00
Manu 526277da0f Add entity pictures to Habitica integration (#134179) 2025-01-14 15:23:22 +01:00
Jan Bouwhuis 934f59449d Make mqtt integration exports explicit (#135595) 2025-01-14 15:17:28 +01:00
adam-the-hero 026df07451 Fix Watergate Power supply mode description and MQTT/Wifi uptimes (#135085) 2025-01-14 14:40:01 +01:00
Indu Prakash 38d008bb66 Add vesync number platform (#135564) 2025-01-14 14:33:48 +01:00
Rob 406c3b5925 Adding support for new Lutron RGB tape light (#130731) 2025-01-14 14:07:20 +01:00
Manu 7cc61d1b86 Skip fetching deactivated shopping lists in Bring integration (#135336)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-14 14:07:07 +01:00
Joost Lekkerkerker 421c4889bf Use device supplied ranges in LaMetric (#135590) 2025-01-14 14:02:17 +01:00
Joost Lekkerkerker d6ee7a2c1e Add serial number to LaMetric (#135591) 2025-01-14 13:54:08 +01:00
Brett Adams 6a032baa48 Add streaming binary sensors to Teslemetry (#135248)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-14 13:46:10 +01:00
Erik Montnemery edc7c0ff2f Bump securetar to 2025.1.1 (#135582) 2025-01-14 13:28:43 +01:00
jesperraemaekers 8109efe810 Reverted async-dependency to todo for Weheat (#135588) 2025-01-14 13:27:47 +01:00
Joost Lekkerkerker 5e50b11114 Avoid core documentation url hosted elsewhere (#130513)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-01-14 13:17:25 +01:00
jiriappl 4f796174fd Match the upstream alt id of the new Levoit air purifier (#135426) 2025-01-14 13:17:09 +01:00
Joost Lekkerkerker 5fc3618b4a Bump demetriek to 1.2.0 (#135580) 2025-01-14 12:56:31 +01:00
Steven B. d970b728ce Update tplink quality_scale.yaml (#135209) 2025-01-14 12:41:48 +01:00
Joost Lekkerkerker c66176cfa5 Unignore ruff rule ISC001 (#135581) 2025-01-14 12:40:43 +01:00
Indu Prakash 6f138c71b4 Remove incorrect logging about Unknown device (#135585) 2025-01-14 12:38:31 +01:00
Krisjanis Lejejs 6e80ad505b Bump hass-nabucasa from 0.87.0 to 0.88.1 (#135521)
* Bump hass-nabucasa from 0.87.0 to 0.88.0

* Bump hass-nabucasa from 0.88.0 to 0.88.1

* Fix Alexa breaking changes
2025-01-14 12:17:22 +01:00
Joost Lekkerkerker 8db63adc11 Bump ruff to 0.9.1 (#135197) 2025-01-14 11:46:12 +01:00
Jan Bouwhuis 2b51ab1c75 Set MQTT quality scale to gold (#135579) 2025-01-14 11:45:07 +01:00
jesperraemaekers f4e7c9d6c3 Bump Weheat to 2025.1.14 (#135578) 2025-01-14 11:36:26 +01:00
Josef Zweck 6359a75977 Cleanup tedee callbacks (#135577) 2025-01-14 11:34:37 +01:00
Maikel Punie 096c6b8575 Mark Velbus test coverage as done (#135571) 2025-01-14 11:32:33 +01:00
Maikel Punie 959cea45b8 Migrate Velbus to have Entity name (#135520) 2025-01-14 11:30:10 +01:00
Jan-Philipp Benecke e3f03c9da1 Set inexogy quality scale to silver (#135547) 2025-01-14 11:20:35 +01:00
Ville Skyttä 1426c421f3 Use percent formatting in logging per guidelines (#135550) 2025-01-14 11:15:38 +01:00
Indu Prakash 58df5f2394 Add iprak to to vesync code owners (#135562) 2025-01-14 10:51:13 +01:00
Erik Montnemery d333fa320f Fix nmbs sensor unique_id (#135576) 2025-01-14 10:24:48 +01:00
jesperraemaekers 6d7e9f10d9 Set PARALLEL_UPDATES for Weheat (#135574)
Add PARALLEL_UPDATES
2025-01-14 11:19:28 +02:00
Manu 0c144092c6 Bump habiticalib to v.0.3.3 (#135551) 2025-01-14 10:07:23 +01:00
Artur Pragacz 1de4d0efda Fix deprecated enums (#134824) 2025-01-14 10:04:48 +01:00
Erik Montnemery 440cd5bee0 Improve improv via BLE log messages (#135575) 2025-01-14 10:00:21 +01:00
Master-Guy 09e2168f72 Changed json.schemas.url for devcontainers (#135281) 2025-01-13 21:46:32 +01:00
Michael Hansen b897e6a85f Use STT/TTS languages for LLM fallback (#135533) 2025-01-13 14:17:12 -06:00
Norbert Rittel 3e9b410b7c Fix grammar issue in 'invalid_auth' string (#135546)
Remove that wrong comma and add a "that" to clarify the meaning of the error message.
2025-01-13 20:56:10 +01:00
Jan-Philipp Benecke 3c825bb826 Set PARALLEL_UPDATES for inexogy (#135545) 2025-01-13 20:48:24 +01:00
Jan-Philipp Benecke e8ad391df2 Add data_descriptions to inexogy config flow (#135536) 2025-01-13 20:31:13 +01:00
JJ 504ed83ffb Add person component to strict type checking (#132754) 2025-01-13 20:11:17 +01:00
Maikel Punie eaaab4ccfe Velbus add subdevices for din-rail modules (#131371) 2025-01-13 20:10:45 +01:00
jesperraemaekers 4ddb72314d Add quality scale for weheat (#135384) 2025-01-13 20:09:19 +01:00
Maikel Punie c489f94026 Velbus unsubscribe to the status updates on removal (#135530) 2025-01-13 20:08:04 +01:00
Maikel Punie 38dcc782d1 Velbus update unique-config-entry quality score (#135524) 2025-01-13 20:07:47 +01:00
qbus-iot 2d2f4f5cec Add new integration Qbus (#127280)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Thomas D <11554546+thomasddn@users.noreply.github.com>
2025-01-13 19:06:52 +00:00
Álvaro Fernández Rojas ca34541b04 Register Airzone WebServer device (#135538) 2025-01-13 20:06:19 +01:00
Jan Bouwhuis 984c380e13 Add option to allow to use setpoint instead of override for legacy incomfort RF gateway (#135143)
* Add option to allow to use setpoint in stead of override for legacy incomfort RF gateway

* Add test to assert state with legacy_setpoint_status option

* Use selector

* Update homeassistant/components/incomfort/strings.json

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

* Follow up on code review

* Rephrase data_description

* Rephrase

* Use async_schedule_reload helper

* Move option flow after config flow

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-13 19:50:06 +01:00
G Johansson 1c053485a9 Bump smhi-pkg to 1.0.19 (#135537) 2025-01-13 20:40:01 +02:00
Steven B. ab28115d2b Cleanup tplink test framework (#135205) 2025-01-13 19:32:22 +01:00
Indu Prakash d986fe7a07 Add humidifier entity for Vesync devices (#134333) 2025-01-13 19:26:18 +01:00
Jan Bouwhuis 6fd73730cc Bump aioimaplib to 2.0.0 (#135448) 2025-01-13 19:19:06 +01:00
Jan Bouwhuis b93aa760c5 Refactor the MQTT option and reconfigure flow (#133342)
* Move entry options to entry.options en remove broker setup from mqtt option flow

* UPdate diagnostics to export both entry data and options

* Parameterize entry options directly not depending on migration

* Update tests to use v2 entry and add separate migration test

* use start_reconfigure_flow helper

* Update quality scale comment

* Do minor entry upgrade, and do not force to upgrade entry

* Ensure options are read from older entries

* Add comment

* Follow up on code review

* Assert config entry version checking the broker connection

* Update comment
2025-01-13 19:00:18 +01:00
epenet b84a4dc120 Add zeroconf discovery to onewire (#135295) 2025-01-13 17:52:37 +00:00
Manu cdcc7dbbe8 Deprecate sensors in Habitica integration (#134036)
* Deprecate sensors

* move to setup, remove disabled

* changes

* add breaking version to string

* fixes

* fix entity id in tests
2025-01-13 18:35:14 +01:00
Maikel Punie 8d38279993 Bump velbusaio to 2025.1.0 (#135525) 2025-01-13 17:18:46 +01:00
dotvav 153496b5f4 Palazzetti integration: Add support for additional fans (#135377)
* Add support for second and third fans

* Update test mock and snapshot

* Test coverage and error message

* Rename fans left and right instead of 2 and 3
2025-01-13 17:17:46 +01:00
jesperraemaekers 1fa3d90d73 Removing unused API file form weheat (#135518) 2025-01-13 17:14:10 +01:00
Robert Resch 1e4c7e832d Bump go2rtc recommended version to 1.9.8 (#135523) 2025-01-13 17:02:23 +01:00
Maxim Mikityanskiy 275365a9d3 Expose raw PM2.5 in Airgradient (#135457) 2025-01-13 14:42:53 +00:00
G Johansson 4709a3162c Change Trafikverket Train to use station signatures (#131416)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-13 15:38:02 +01:00
Franck Nijhof 157548609b Revert "Make all three numbered lists consistent, using 1. 1. 1. for the syntax" (#135510) 2025-01-13 14:18:47 +00:00
Manu fc0a6c2ff3 Refactor number/select to use common method in IronOS (#134173) 2025-01-13 14:50:55 +01:00
Klaas Schoute 0d116ec6a2 Improve tests of energyzero integration (#133452)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-01-13 14:49:01 +01:00
Marc Mueller 6060f637a8 Update getmac to 0.9.5 (#135506) 2025-01-13 14:47:32 +01:00
Shay Levy ba9ad009e9 Fix LG webOS TV trigger validation (#135312)
* Fix LG webOS TV trigger validation

* Raise if not loaded
2025-01-13 15:37:40 +02:00
Norbert Rittel ec5759d3b9 Fix typos "Login" > "Log in" and "Setup" > "Set up" (#135306) 2025-01-13 14:16:25 +01:00
Norbert Rittel c7a5c49a03 Small fixes in the strings file of the azure_data_explorer integration (#135309) 2025-01-13 14:16:00 +01:00
Dave T 9b55faa879 Refactor config flow tests in generic camera (#134385)
Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
Co-authored-by: Allen Porter <allen.porter@gmail.com>
2025-01-13 14:15:21 +01:00
jesperraemaekers 6fd9476bb9 Refresh token before setting up weheat (#135264) 2025-01-13 14:01:57 +01:00
dotvav d33ee130bc Bump pypalazzetti to 0.1.19 (#135465) 2025-01-13 13:59:34 +01:00
dotvav e1ffd9380d Replace climate fan speed 'silent' with a button (#135075) 2025-01-13 13:51:20 +01:00
Norbert Rittel fc6695b05c Use proper sentence-case for all strings in azure_event_hub (#135328) 2025-01-13 13:47:40 +01:00
Duco Sebel 8f71d7a6f3 Move HomeWizard API initialisation to async_setup_entry (#135315) 2025-01-13 13:35:50 +01:00
Manu 4dbf2b0320 Fix grey dailies with weekly frequency and no weekdays selected in Habitica (#135419) 2025-01-13 13:20:15 +01:00
Austin Mroczek 3aa466806e TotalConnect update quality_scale with documentation updates (#134049) 2025-01-13 13:11:56 +01:00
Lukas Schlötterer 7b63c17101 Add kV and MV unit conversion for voltages (#135396) 2025-01-13 13:00:35 +01:00
Brett Adams dae87db244 Fix when live status is blank in Telsemetry (#130408) 2025-01-13 12:44:36 +01:00
Norbert Rittel fba1b4be5b Replace "click" with "select" to fit for mobile app (#135382) 2025-01-13 12:32:07 +01:00
Mick Vleeshouwer c15073cc27 Fix incorrect cast in HitachiAirToWaterHeatingZone in Overkiz (#135468) 2025-01-13 12:11:01 +01:00
Paul Daumlechner 25041aa02d Add dhcp discovery to velux (#135138)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-13 12:01:04 +01:00
Norbert Rittel 96ad2b6ed8 Replace "Login …" with "Log in …" in two strings of Habitica integration (#135383) 2025-01-13 11:55:55 +01:00
epenet a649ff4a91 Add hassio discovery to onewire (#135294) 2025-01-13 11:55:18 +01:00
Joost Lekkerkerker 1ceebd92a9 Change icon ID name in Lametric (#135368) 2025-01-13 11:48:00 +01:00
Artur Pragacz b009f11013 Fix referenced objects in script sequences (#135499) 2025-01-13 11:40:53 +01:00
Maikel Punie 2d67aca550 Rework velbus services to deprecated the interface parameter (#134816)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-13 11:36:20 +01:00
LG-ThinQ-Integration 98ef32c668 Add remain, running, schedule time sensors to LG ThinQ (#131133)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-01-13 11:29:09 +01:00
Erik Montnemery 3a0072d42d Fix typing in zha update entity (#135500) 2025-01-13 11:27:20 +01:00
Maciej Bieniek 86ea68eaec Add missing total active returned energy sensor for Shelly Mini PM Gen3 (#135433)
Add missing total active returned energy sensor for Mini PM Gen3
2025-01-13 11:12:04 +01:00
Michael e67a131bd9 Bump uv to 0.5.18 (#135454) 2025-01-13 09:11:46 +01:00
dependabot[bot] c36d73e469 Bump github/codeql-action from 3.28.0 to 3.28.1 (#135492)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 08:54:42 +01:00
Khole ac279d9794 Replace pyhiveapi with pyhive-integration (#135482) 2025-01-13 08:50:25 +01:00
J. Nick Koston 4e5bf5ac22 Ensure ESPHome cleanups Bluetooth scanner data upon removal (#135470)
* Add bluetooth API to remove scanners that are no longer used

- Cleanup the advertisment history right away when a scanner is removed

In the future we will do some additional cleanup

* coverage

* finish tests

* Ensure ESPHome cleanups Bluetooth scanner data upon removal

needs https://github.com/home-assistant/core/pull/135408
2025-01-12 22:41:49 -05:00
J. Nick Koston 2e5e2c50dd Ensure Shelly cleanups Bluetooth scanner data upon removal (#135472)
* Add bluetooth API to remove scanners that are no longer used

- Cleanup the advertisment history right away when a scanner is removed

In the future we will do some additional cleanup

* coverage

* finish tests

* Ensure Shelly cleanups Bluetooth scanner data upon removal

needs https://github.com/home-assistant/core/pull/135408
2025-01-12 22:41:21 -05:00
J. Nick Koston c9a7afe439 Add bluetooth API to remove scanners that are no longer used (#135408) 2025-01-12 14:03:05 -10:00
Ville Skyttä 0a444de39c Refactor upcloud to use config entry runtime data (#135449) 2025-01-13 01:43:37 +02:00
Ravaka Razafimanantsoa 559c411dd2 Add current and voltage for plugs to switchbot_cloud (#135458)
SwitchBot Cloud: Adding current and voltage for plugs
2025-01-12 18:42:06 +01:00
tronikos 61ea732caa Fix strings for the Google integrations (#135445) 2025-01-12 09:15:33 -08:00
Ravaka Razafimanantsoa 11ebc27bfe Bump switchbot-api to 2.3.1 (#135451) 2025-01-12 17:29:01 +01:00
Manu ccb94ac6a6 Update translations and error messages in Bring! integration (#135455)
* Update translations and error messages

* use placeholder for field name

* change key for translation string
2025-01-12 16:27:31 +01:00
WaterInTheLake ab0dfe304c Fix translation string: numbering in list (#135441) 2025-01-12 14:42:45 +01:00
Norbert Rittel 8b0be70fdd Fix descriptions of send_message action of Bring! integration (#135446)
* Make "Urgent message" selector consistent, use "Bring!" as name

- Replace one occurrence of "bring" with the brand name "Bring!"
- Change description of action to third-person singular for consistency in Home Assistant
- Make all occurrences of the selector "Urgent message" consistent (in sentence case) so they all get consistent translations, too
- Change one related error message to refer to the UI name of the required "Article" field

* Changed ` to '  to avoid Regex problems

* Reverted change to notify_missing_argument_item

Reverted to avoid failing test

* Reverted change to "bring"

* Add "is" to description of "Article"

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

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-01-12 14:36:23 +01:00
tronikos f7df214dd8 Fix config entries typo s/entruis/entries/ (#135431)
Fix typo s/entruis/entries/
2025-01-12 11:07:45 +01:00
Andrew Sayre 11fa6b2e4e Bump pyheos to 1.0.0 (#135415) 2025-01-11 23:06:06 -06:00
Joost Lekkerkerker 52c57eb2e5 Actually use translated entity names in Lametric (#135381) 2025-01-11 23:15:49 +01:00
Manu 0d85f54e76 Add sensors for inventory items to Habitica (#135331)
Add sensors for inventory items
2025-01-11 21:31:36 +01:00
Norbert Rittel b3af12c9b1 Reword action descriptions for better translations in Teslemetry (#135370)
Slightly reword action descriptions for better translations

Currently only one of the action descriptions in the Teslemetry integration uses the descriptive form of third person plural.

This commit changes the remaining descriptions to adopt the same language and changes "the" to "a" as the actual action target is defined below that in the UI.
2025-01-11 20:15:41 +00:00
Jeff Terrace 6571ebf15b Add additional Tapo ONVIF Person/Vehicle/Line/Tamper/Intrusion events (#135399) 2025-01-11 09:52:46 -10:00
Norbert Rittel 2237ed9af7 Make all three numbered lists consistent, using 1. 1. 1. for the syntax (#135400)
Make all three numbered lists use 1. 1. 1. for the syntax

Currently only two of the setup descriptions of the Nest integration use automatic syntax for a numbered list.

This commit makes the third one consistent, using 1. 1. 1. as well.

This helps translators in Lokalise understand that this is the expected format for all numbered lists in Home Assistant.
2025-01-11 21:44:59 +02:00
YogevBokobza c442935fdd Switcher runner child lock support (#133270)
* Switcher runner child lock support

* fix based on requested changes

* Update homeassistant/components/switcher_kis/switch.py

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

* Fix

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-11 21:01:10 +02:00
Jeff Terrace 6dc9c6819f Add @jterrace to onvif integration owners (#135398) 2025-01-11 19:30:51 +01:00
Shay Levy a745e079e9 Add reconfigure to LG webOS TV (#135360)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-11 16:16:35 +01:00
Markus Lanthaler 19f460614e Enable slowly-changing, important diagnostics for connected devices by default (#134776) 2025-01-11 15:29:31 +01:00
Brett Adams 20d6ba4286 Bump Teslemetry Stream (#135344)
bump
2025-01-11 16:09:53 +02:00
dotvav 4cf7a51a05 Palazzetti Quality Scale update after doc improvement (#135277) 2025-01-11 13:24:00 +01:00
Simon Lamon 8e2b284a7f Add more typings to nmbs sensor (#135359) 2025-01-11 13:04:37 +01:00
Erwin Douna 74c3e9629f Fix Tado config flow (#135353) 2025-01-11 12:52:40 +01:00
Norbert Rittel 907f1e062a Fix spelling of "Log in …" and "API key" in LOQED integration (#135347) 2025-01-11 12:51:56 +01:00
Simon Lamon fd169affd7 Remove code owner for nmbs (#135357) 2025-01-11 12:49:10 +01:00
dependabot[bot] 81c390d3b8 Bump docker/build-push-action from 6.10.0 to 6.11.0 (#135254) 2025-01-11 12:32:30 +01:00
dependabot[bot] d356d4bb82 Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#135255) 2025-01-11 12:31:46 +01:00
Joost Lekkerkerker 4d93fbcb52 Fix backup formatting (#135350) 2025-01-11 11:15:00 +01:00
Simon Lamon b9259b6f77 Add config flow to NMBS (#121548)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-01-11 10:31:47 +01:00
Norbert Rittel 22b84450e8 Small fixes in setup flow strings, correct sentence-case (#135349) 2025-01-11 10:10:40 +01:00
Norbert Rittel 9ef93517e7 Fix spelling of "Log in", fix "outdated student" (#135348) 2025-01-11 10:00:59 +01:00
J. Nick Koston cdc96fdf6f Add bluetooth subscribe_advertisements WebSocket API (#134291) 2025-01-10 16:49:53 -10:00
Paulus Schoutsen ab8af033c0 Extract resolve announcement media ID for AssistSatelliteEntity (#134917) 2025-01-10 18:33:49 -08:00
J. Nick Koston 619dee5d93 Bump habluetooth to 3.8.0 (#135322)
changelog: https://github.com/Bluetooth-Devices/habluetooth/compare/v3.7.0...v3.8.0
2025-01-10 23:50:03 +02:00
Duco Sebel 00c3b8cc3e Use LOGGER from homewizard.const instead per-file loggers (#135320) 2025-01-10 23:49:36 +02:00
Manu bf747bb733 Fix Habitica gems/hourglass sensors (#135323) 2025-01-10 23:47:05 +02:00
Robert Resch 560d15effb Don't store uv's lockfile in hassfest image (#135214) 2025-01-10 21:15:44 +01:00
Quentame 39aa0339ac Bump Freebox to 1.2.2 (#135313) 2025-01-10 21:47:48 +02:00
Norbert Rittel 675cc32534 Fix typos, replace duplicated strings with references (#135303) 2025-01-10 18:21:39 +01:00
Norbert Rittel 31b45e6d3f Fix typos and inconsistent spelling of "tedee" brand name (#135305)
- Change "Setup your tedee locks" to "Set up …"
- Remove two excessive commas
- Change one occurrence of "Tedee" to "tedee".
2025-01-10 18:20:50 +01:00
Shay Levy 6fd4d7acaa Use runtime_data in LG webOS TV (#135301) 2025-01-10 19:16:25 +02:00
Maciej Bieniek c4b4cad335 Bump aioshelly to version 12.3.1 (#135299) 2025-01-10 17:18:00 +01:00
Norbert Rittel 32d3fe714f Grammar and consistency fixes in hdmi_cec strings (#135292) 2025-01-10 16:15:14 +01:00
Manu 6fd0760f25 Add USB-PD Mode select entity to IronOS integration (#134901)
Add USB-PD Mode select entity
2025-01-10 14:07:14 +01:00
Norbert Rittel 59d61104d1 Replace 'entity_id' with UI-friendly, localizable 'entity ID' (#135232)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-10 14:06:58 +01:00
dotvav 028c5349ac Bump pypalazzetti to 0.1.16 (#135269) 2025-01-10 14:06:17 +01:00
Antoine Reversat 9388879b78 Mark FGLAir entities unavailable if they are reporting to be offline (#135202) 2025-01-10 13:24:33 +01:00
Norbert Rittel 246a9f95a3 Smaller grammar fixes, replace 'entity_id' with UI-friendly 'ID' (#135236)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-10 13:23:33 +01:00
Duco Sebel f31f6d7ed0 Adjust HomeWizard to use updated python-homewizard-energy library (#135046) 2025-01-10 13:19:55 +01:00
Shay Levy 1f0eda8e47 Move LG webOS TV actions to entitiy services (#135285) 2025-01-10 13:02:03 +01:00
cdnninja bce7e9ba5e Simplify vesync init loading (#135052) 2025-01-10 12:30:29 +01:00
epenet 475a2fb828 Discover new devices at runtime in onewire (#135199) 2025-01-10 11:53:31 +01:00
epenet 24c70caf33 Improve formatting in component files (#135261)
* Improve formatting in component files

* Apply suggestions from code review
2025-01-10 11:46:15 +01:00
Jan Bouwhuis eba090c9ef Allow to process kelvin as color_temp for mqtt template light (#133957) 2025-01-10 11:43:36 +01:00
epenet b5971ec55d Add model_id and serial_number to onewire device info (#135279) 2025-01-10 11:18:50 +01:00
epenet ad84490541 Fix incorrect test in test_core_config (#135260) 2025-01-10 11:10:23 +01:00
Maikel Punie 033064f832 Velbus light platform code cleanup (#134482) 2025-01-10 11:10:09 +01:00
epenet a2d9920aa9 Fix missing comma in ollama MODEL_NAMES (#135262) 2025-01-10 11:09:49 +01:00
epenet 8386eaa92b Split long strings in stream hls tests (#135271) 2025-01-10 11:09:20 +01:00
epenet aa741a9207 Combine short strings in components (#135265) 2025-01-10 11:07:51 +01:00
Norbert Rittel 024b9ae414 Change 'entity_id' to UI-friendly 'Entity ID', fix spelling of "setpoint" (#135234)
In addition this makes the description of the first action consistent, using third-person singular like the other two and adhering to the HA standard.
2025-01-10 11:06:08 +01:00
epenet 02956f9a83 Improve formatting in component test files (#135267)
Improve formatting in test files
2025-01-10 10:53:45 +01:00
Shay Levy 9d1989125f Fix LG webOS TV media player test coverage (#135225)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-10 10:44:23 +01:00
epenet 04d5cc8f79 Combine short byte strings in xiaomi_ble tests (#135268) 2025-01-10 10:19:28 +01:00
epenet e29ead2a36 Split long strings in components (#135263) 2025-01-10 10:13:36 +01:00
epenet 5df7092f41 Improve formatting in core files (#135256)
* Adjust core files formatting

* Adjust translations script
2025-01-10 09:08:37 +01:00
Norbert Rittel 823feae0f9 Make description of alarm_arm_vacation consistent (#135257)
Small fix to also use "Arms …" in the description of the alarm_arm_vacation action, making it consistent with the other two alarm_arm_… actions.
2025-01-10 08:45:06 +01:00
J. Nick Koston 3c6113e37c Remove per engine max bind vars (#135153) 2025-01-09 22:50:13 +00:00
peteS-UK 139b747a70 Expand Squeezebox auth test for config_flow to finish on create_entry (#133612)
Expand auth test to create_entry
2025-01-09 23:47:53 +01:00
Bram Kragten bceccd85ee 2025.1.2 (#135241) 2025-01-09 23:25:42 +01:00
J. Nick Koston da30dbcfe4 Bump fnv-hash-fast to 1.1.0 (#135237) 2025-01-09 12:03:08 -10:00
Bram Kragten 0027d907a4 Bump version to 2025.1.2 2025-01-09 22:25:42 +01:00
Bram Kragten 5d201406cb Update frontend to 20250109.0 (#135235) 2025-01-09 22:24:43 +01:00
Brynley McDonald 30924b561a Fix Flick Electric Pricing (#135154) 2025-01-09 22:24:42 +01:00
jb101010-2 1eddb4a21b Bump pysuezV2 to 2.0.3 (#135080) 2025-01-09 22:24:41 +01:00
Erik Montnemery 42cdd25d90 Add jitter to backup start time to avoid thundering herd (#135065) 2025-01-09 22:24:41 +01:00
Bram Kragten b8b7daff5a Implement upload retry logic in CloudBackupAgent (#135062)
* Implement upload retry logic in CloudBackupAgent

* Update backup.py

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

* nit

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-01-09 22:23:53 +01:00
Simone Chemelli 0deb46295d Refactor Vodafone Station tests (#134956) 2025-01-09 22:22:37 +01:00
Cyrill Raccaud 7f3f550b7b Bump cookidoo-api to 0.12.2 (#135045)
fix cookidoo .co.uk countries and group api endpoint
2025-01-09 22:14:35 +01:00
Thomas55555 3c14e2f0a8 Bump aioautomower to 2025.1.0 (#135039) 2025-01-09 22:14:34 +01:00
starkillerOG 9601455d9f Fix channel retrieval for Reolink DUO V1 connected to a NVR (#135035)
fix channel retrieval for DUO V1 connected to a NVR
2025-01-09 22:14:33 +01:00
Mick Vleeshouwer 902bd57b4b Catch errors in automation (instead of raise unexpected error) in Overkiz (#135026)
Catch errors in automation (instead of raise unexpected error)
2025-01-09 22:14:32 +01:00
puddly ab071d1c1b Fix ZHA "referencing a non existing via_device" warning (#135008) 2025-01-09 22:14:31 +01:00
Joakim Sørensen 2c02eefa11 Increase cloud backup download timeout (#134961)
Increese download timeout
2025-01-09 22:14:31 +01:00
Quentame 44808c02f9 Fix Météo-France setup in non French cities (because of failed next rain sensor) (#134782) 2025-01-09 22:14:30 +01:00
Bram Kragten 1abcac5fb5 Update frontend to 20250109.0 (#135235) 2025-01-09 22:13:39 +01:00
J. Nick Koston 3b6f47e438 Bump anyio to 4.8.0 (#135224) 2025-01-09 22:12:34 +01:00
Simone Chemelli 6e1a13f878 Add support for Shelly BLU TRV (#128439)
* feat: add support for Shelly BLU TRV

* chore: apply some fixes

* make BLUTRV a separate device

* apply review comment

* review comments and small optimization

* add HVACMode.OFF

* a couple of fixes

* 2 more fixes

* better approach

* cleanup

* small optimization

* remove cooling as not supported by firmware

* tweaks

* humidity and entity name

* fix naming

* allign async_set_hvac_mode

* align settings

* restore temp

* fix

* remove OFF

* cleanup

* hvac_mode

* add tests

* typo

* more tests

* bump aioshelly
2025-01-09 21:28:36 +01:00
epenet ee865d2f0f Add exception-translations rule to quality_scale pytest validation (#131914)
* Add exception-translations rule to quality_scale pytest validation

* Adjust

* Return empty dict if file is missing

* Fix

* Improve typing

* Address comments

* Update tests/components/conftest.py

* Update tests/components/conftest.py

* Update tests/components/conftest.py

---------

Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-09 21:21:47 +01:00
epenet dd57c75e64 Use remove-prefix/suffix introduced in Python 3.9 (#135206)
Use removeprefix/removesuffix
2025-01-09 21:15:22 +01:00
J. Nick Koston 0cc586a3ac Bump zeroconf to 0.139.0 (#135213) 2025-01-09 19:01:49 +01:00
Brett Adams b6c0257c43 Add streaming sensors to Teslemetry (#132783)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-09 18:58:12 +01:00
Jan Bouwhuis cabdae98e8 Allow to process kelvin as color_temp for mqtt json light (#133955) 2025-01-09 18:34:42 +01:00
Shay Levy 07482de4ab Fix LG webOS TV init test coverage (#135194) 2025-01-09 18:29:17 +01:00
epenet 31719bc84c Refactor onewire hub (#135186)
* Improve type hints in onewire hub

* More cleanups

* Improve

* Get host/port from entry data

* Use DeviceInfo object
2025-01-09 18:17:21 +01:00
Steven B. 1ca5f79708 Use typed config entry in tplink coordinator (#135182) 2025-01-09 17:43:38 +01:00
Ruslan Sayfutdinov a5f70dec96 Make generated files appear as generated (#134991) 2025-01-09 17:26:46 +01:00
Allen Porter 6e111d18ec Allow unregistering LLM APIs (#135162) 2025-01-09 08:18:25 -08:00
Jan Bouwhuis ec37e1ff8d Allow to process kelvin as color_temp for mqtt basic light (#133953) 2025-01-09 16:31:09 +01:00
epenet 8705fd8546 Avoid unnecessary executor calls in onewire (#135187) 2025-01-09 16:11:33 +02:00
Norbert Rittel 050a17db4d Use friendly names in add_to_playlist action, fix "ID" (#134978) 2025-01-09 13:45:32 +01:00
Andre Lengwenus 9dc4597f59 Update module properties on module scan for LCN (#135018) 2025-01-09 13:44:57 +01:00
Arie Catsman 9dd7021d63 No need to set unique_id in enphase_envoy reauth step (#133615)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-01-09 13:31:29 +01:00
Kerey Roper 6a4160bcc4 add support for dimming/brightening X10 lamps (#130196)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-09 13:07:24 +01:00
Norbert Rittel 411d14c2ce Update title and description for setup dialog of thethingsnetwork (#134954) 2025-01-09 13:07:03 +01:00
Joost Lekkerkerker d7315f4500 Add event entities to Overseerr (#134975) 2025-01-09 12:48:09 +01:00
epenet c4ac648a2b Add select platform to onewire (#135181)
* Add select platform to onewire

* Add tests

* Apply suggestions from code review
2025-01-09 12:45:49 +01:00
epenet e9616f38d8 Update scaffold to use internal _PLATFORM constant (#135177) 2025-01-09 12:41:29 +01:00
Steven B. 1550086dd6 Fix stale docstrings in tplink integration (#135183) 2025-01-09 12:37:32 +01:00
beginner2047 8e28b7b49b Add yue language support to Google Translate TTS (#134480) 2025-01-09 12:16:54 +01:00
epenet 4a33b1d936 Set PARALLEL_UPDATES to 0 in onewire (#135178) 2025-01-09 12:15:32 +01:00
epenet 8bfdbc173a Use snapshot_platform helper in onewire tests (#135176)
* Use snapshot_platform helper in onewire tests

* Snapshot device registry
2025-01-09 11:45:29 +01:00
Cyrill Raccaud 3ce4c47cfc Add uuid as unique_id to config entries for Cookidoo (#134831) 2025-01-09 11:28:28 +01:00
Steven B. 0d9ac25257 Add and cleanup tplink translations (#135120) 2025-01-09 11:28:10 +01:00
epenet 15e785b974 Move OneWire PLATFORM constant back to init (#135172) 2025-01-09 11:22:08 +01:00
Antoine Reversat 13527768cc Add outside temperature sensor to fujitsu_fglair (#130717) 2025-01-09 11:21:27 +01:00
Jan-Philipp Benecke 071e675d9d Mark docs-installation-parameters and docs-removal-instructions for inexogy as done (#135126) 2025-01-09 11:21:07 +01:00
epenet 316a61fcde Deprecate raw_value attribute in onewire entity (#135171)
* Drop raw_value attribute in onewire entity

* Deprecate only
2025-01-09 11:20:08 +01:00
Erik Montnemery 9901f3c3dd Add jitter to backup start time to avoid thundering herd (#135065) 2025-01-09 10:53:33 +01:00
J. Nick Koston c9d8c59b45 Bump zeroconf to 0.138.1 (#135148) 2025-01-09 10:33:24 +01:00
Nikolay Vasilchuk 0184d8e954 Deprecate StarLine engine switch attributes (#133958)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-09 10:24:04 +01:00
Brynley McDonald 2f892678f6 Fix Flick Electric Pricing (#135154) 2025-01-09 10:09:04 +01:00
G Johansson fe8cae8eb5 Make devices dynamic in Sensibo (#134935) 2025-01-09 09:02:14 +01:00
G Johansson 64752af4c2 Change minimum SQLite version to 3.40.1 (#135042)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-01-08 16:34:36 -10:00
G Johansson c5f80dd01d Render select entity unavailable when active feature is missing in Sensibo (#135031) 2025-01-08 22:55:31 +01:00
Quentame 2704090418 Fix Météo-France setup in non French cities (because of failed next rain sensor) (#134782) 2025-01-08 22:51:37 +01:00
Tomer Shemesh f01c860c44 Add support for Lutron Wood Tilt Blinds (#135057) 2025-01-08 22:40:13 +01:00
Shay Levy bb4a497247 Impove LG webOS TV tests quality (#135130)
* Impove LG webOS TV tests quality

* Review comments
2025-01-08 23:12:09 +02:00
Joris Pelgröm 488c5a6b9f Use is in FlowResultType enum comparison in integration scaffold tests (#135133) 2025-01-08 22:10:29 +01:00
Louis Christ acbd501ede Add DataUpdateCoordinator to bluesound integration (#135125) 2025-01-08 22:09:59 +01:00
Shay Levy d06cd1ad3b Set PARALLEL_UPDATES in LG webOS TV (#135135) 2025-01-08 22:08:13 +01:00
Joris Pelgröm 4129697dd9 Add LetPot integration (#134925) 2025-01-08 21:38:52 +01:00
Ståle Storø Hauknes 4086d092ff Add suggested precision for Airthings BLE integration (#134985)
Add suggested precision
2025-01-08 21:05:42 +01:00
Arie Catsman 988a0639f4 Remove enphase_envoy config flow tests that make no sense (#133833) 2025-01-08 20:09:06 +01:00
Steven B. c9c553047c Add quality scale file to tplink integration (#135017) 2025-01-08 20:08:04 +01:00
Arie Catsman f05cffea17 Update enphase_envoy test_init to use str for unique_id and test for loaded config entry (#133810) 2025-01-08 20:06:51 +01:00
Hervé Cauwelier d2a188ad3c Improve holidays config form and naming (#133663)
The holidays library is improving over time, let's make use of their
data for a more user-friendly experience.

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-08 17:19:28 +01:00
epenet 02e30edc6c Improve onewire options flow tests (#135109) 2025-01-08 17:00:35 +01:00
G Johansson 0e52ea482f Fix hvac_modes never empty in Sensibo (#135029) 2025-01-08 15:27:26 +01:00
epenet d46be61b6f Split simple and recovery in onewire config-flow user tests (#135102) 2025-01-08 15:25:39 +01:00
epenet f05e234c30 Refactor patching in onewire tests (#135070) 2025-01-08 15:14:51 +01:00
jb101010-2 bc09e825a9 Bump pysuezV2 to 2.0.3 (#135080) 2025-01-08 15:12:56 +01:00
Steven B. 6f6d485530 Raise HomeAssistantError from tplink light effect service (#135081) 2025-01-08 15:12:21 +01:00
Steven B. 63eb27df7b Add PARALLEL_UPDATES constant to tplink integration platforms (#135083) 2025-01-08 15:11:06 +01:00
elmurato da29b2f711 Add quality_scale.yaml to Minecraft Server (#132551) 2025-01-08 15:00:33 +01:00
farkasdi c2f6f93f1d Update addition logger string in fan.py (#135098) 2025-01-08 14:58:50 +01:00
Dawid Pietryga 39143a2e79 Add satel integra switches and alarm control panels unique_id (#129636) 2025-01-08 14:49:43 +01:00
dontinelli 99e65c38b0 Add binary sensors to fyta (#134900)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-08 14:41:33 +01:00
epenet ec7d2f3731 Add quality_scale file to onewire (#134951) 2025-01-08 14:41:20 +01:00
epenet d43187327f Remove rounding from onewire sensors (#135095) 2025-01-08 14:25:05 +01:00
Austin Mroczek 8be01ac9d6 TotalConnect improved config flow and test before setup (#133852)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-08 12:37:04 +01:00
Erik Montnemery e052ab27f2 Fix DSMR migration (#135068) 2025-01-08 11:20:35 +00:00
Shay Levy 43ec63eabc Cleanup LG webOS TV name (#135028) 2025-01-08 12:06:02 +01:00
starkillerOG 7a2a6cf7d8 Add Reolink unexpected error translation (#134807) 2025-01-08 10:58:28 +01:00
puddly eff440d2a8 Fix ZHA "referencing a non existing via_device" warning (#135008) 2025-01-08 10:51:57 +01:00
Andrew Sayre 3fea4efb9f Update pyheos to 0.9.0 (#134947)
Bump pyheos
2025-01-08 10:36:02 +02:00
Matthias Alphart dc1928f3eb Delete KNX config storage when removing the integration (#135071) 2025-01-08 09:35:44 +01:00
epenet f8618e65f6 Improve type hints in onewire tests (#134993) 2025-01-08 09:33:04 +01:00
G Johansson e99aaed7fa Fix climate react type (#135030) 2025-01-08 10:30:14 +02:00
starkillerOG d000558227 Fix channel retrieval for Reolink DUO V1 connected to a NVR (#135035)
fix channel retrieval for DUO V1 connected to a NVR
2025-01-08 10:28:01 +02:00
Thomas55555 7daf442271 Bump aioautomower to 2025.1.0 (#135039) 2025-01-08 10:26:48 +02:00
Cyrill Raccaud b8f458458b Bump cookidoo-api to 0.12.2 (#135045)
fix cookidoo .co.uk countries and group api endpoint
2025-01-08 10:24:09 +02:00
J. Nick Koston 85ecb04abf Bump dbus-fast to 2.28.0 (#135049)
changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v2.24.3...v2.28.0
2025-01-08 10:19:03 +02:00
Joakim Sørensen 20db7fdc96 Implement upload retry logic in CloudBackupAgent (#135062)
* Implement upload retry logic in CloudBackupAgent

* Update backup.py

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

* nit

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-01-08 08:16:18 +01:00
Diogo Gomes a1d43b9387 Add weather warning sensor to IPMA (#134054)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-01-07 22:11:24 +00:00
Franck Nijhof d59a91a905 2025.1.1 (#134940) 2025-01-07 08:43:32 +01:00
Franck Nijhof 298f059488 Revert "Remove deprecated supported features warning in ..." (multiple) (#134933) 2025-01-07 06:53:14 +00:00
Franck Nijhof 7a5525951d Bump version to 2025.1.1 2025-01-06 23:42:21 +00:00
Artur Pragacz 9a9514d53b Revert "Remove deprecated supported features warning in LightEntity" (#134927) 2025-01-06 23:42:00 +00:00
G Johansson 5337ab2e72 Bump holidays to 0.64 (#134922) 2025-01-06 23:41:55 +00:00
Klaas Schoute b815899fdc Bump powerfox to v1.2.0 (#134908) 2025-01-06 23:41:51 +00:00
Klaas Schoute 81a669c163 Bump powerfox to v1.1.0 (#134730) 2025-01-06 23:41:45 +00:00
Bram Kragten 188def51c6 Update frontend to 20250106.0 (#134905) 2025-01-06 23:40:07 +00:00
Manu eb345971b4 Fix wrong power limit decimal place in IronOS (#134902) 2025-01-06 23:40:03 +00:00
Manu 9288dce7ed Add bring_api to loggers in Bring integration (#134897)
Add bring-api to loggers
2025-01-06 23:39:59 +00:00
Steven B. 4867d3a187 Bump python-kasa to 0.9.1 (#134893)
Bump tplink python-kasa dependency to 0.9.1
2025-01-06 23:39:55 +00:00
Norbert Rittel c40771ba6a Use uppercase for "ID" and sentence-case for "name" / "icon" (#134890) 2025-01-06 23:39:51 +00:00
Luke Lashley 2fc489d17d Add extra failure exceptions during roborock setup (#134889)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-01-06 23:39:47 +00:00
Robin Wohlers-Reichel 279785b22e Bump solax to 3.2.3 (#134876) 2025-01-06 23:39:42 +00:00
Joakim Sørensen e5c986171b Log cloud backup upload response status (#134871)
Log the status of the upload response
2025-01-06 23:39:38 +00:00
Joakim Sørensen 58805f721c Log upload BackupAgentError (#134865)
* Log out BackupAgentError

* Update homeassistant/components/backup/manager.py

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

* Update homeassistant/components/backup/manager.py

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

* Format

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-01-06 23:39:33 +00:00
Allen Porter 29989e9034 Update Roborock config flow message when an account is already configured (#134854) 2025-01-06 23:39:28 +00:00
Avi Miller fbd031a03d Bump aiolifx-themes to update colors (#134846) 2025-01-06 23:39:23 +00:00
J. Diego Rodríguez Royo fe1ce39831 Fix how function arguments are passed on actions at Home Connect (#134845) 2025-01-06 23:39:19 +00:00
J. Nick Koston 914c6459dc Bump habluetooth to 3.7.0 (#134833) 2025-01-06 23:39:14 +00:00
Raphael Hehl 43ffdd0eef Bump uiprotect to version 7.4.1 (#134829) 2025-01-06 23:39:10 +00:00
Norbert Rittel 39d16ed5ce Fix a few typos or grammar issues in asus_wrt (#134813) 2025-01-06 23:39:06 +00:00
Norbert Rittel 07f3d939e3 Replace "id" with "ID" for consistency across HA (#134798) 2025-01-06 23:39:01 +00:00
G Johansson eda60073ee Raise ImportError in python_script (#134792) 2025-01-06 23:38:57 +00:00
Norbert Rittel 09ffa38ddf Fix missing sentence-casing etc. in several strings (#134775) 2025-01-06 23:38:53 +00:00
jb101010-2 b32a791ea4 Bump pysuezV2 to 2.0.1 (#134769) 2025-01-06 23:38:48 +00:00
Michael a4ea25631a Register base device entry during coordinator setup in AVM Fritz!Tools integration (#134764)
* register base device entry during coordinator setup

* make mypy happy
2025-01-06 23:38:44 +00:00
Duco Sebel bd8ea646a9 Bumb python-homewizard-energy to 7.0.1 (#134753) 2025-01-06 23:38:38 +00:00
Norbert Rittel 538a2ea057 Fix swapped letter order in "°F" and "°C" temperature units (#134750)
Fixes the wrong order "F°" and "C°" for the temperature units.
2025-01-06 23:38:34 +00:00
Sid b461bc2fb5 Bump openwebifpy to 4.3.1 (#134746) 2025-01-06 23:38:29 +00:00
TheJulianJES 103960e0a7 Bump ZHA to 0.0.45 (#134726) 2025-01-06 23:37:24 +00:00
dontinelli 1c4273ce91 Change from host to ip in zeroconf discovery for slide_local (#134709) 2025-01-06 23:34:17 +00:00
J. Diego Rodríguez Royo 0f0209d4bb Iterate over a copy of the list of programs at Home Connect select setup entry (#134684) 2025-01-06 23:34:13 +00:00
Cyrill Raccaud 27b8b8458b Cookidoo exotic domains (#134676) 2025-01-06 23:34:08 +00:00
Franck Nijhof c022d91baa Update demetriek to 1.1.1 (#134663) 2025-01-06 23:34:02 +00:00
Cyrill Raccaud 0daac09008 Bump cookidoo-api library to 0.11.1 of for Cookidoo (#134661) 2025-01-06 23:33:56 +00:00
Franck Nijhof ca8416fe50 Update peblar to 0.3.3 (#134658) 2025-01-06 23:33:50 +00:00
starkillerOG a14f6faaaf Fix Reolink playback of recodings (#134652) 2025-01-06 23:33:45 +00:00
Franck Nijhof a9a14381d3 Update twentemilieu to 2.2.1 (#134651) 2025-01-06 23:33:39 +00:00
Joost Lekkerkerker a4d0794fe4 Remove call to remove slide (#134647) 2025-01-06 23:33:33 +00:00
Cyrill Raccaud 9ead6fe362 Set logging in manifest for Cookidoo (#134645) 2025-01-06 23:33:28 +00:00
epenet 017679abe1 Fix hive color tunable light (#134628) 2025-01-06 23:33:23 +00:00
Brynley McDonald 0bd7b793fe Fix Flick Electric authentication (#134611) 2025-01-06 23:33:19 +00:00
Teemu R. c46a70fdcf Mention case-sensitivity in tplink credentials prompt (#134606) 2025-01-06 23:33:13 +00:00
Raphael Hehl 8c2ec5e7c8 Bump uiprotect to version 7.2.0 (#134587) 2025-01-06 23:33:09 +00:00
J. Nick Koston 3063f0b565 Bump bleak-esphome to 2.0.0 (#134580) 2025-01-06 23:33:04 +00:00
peteS-UK aafc1ff074 Small fix to allow playing of expandable favorites on Squeezebox (#134572) 2025-01-06 23:33:00 +00:00
Ludovic BOUÉ 45142b0cc0 Matter Battery replacement icon (#134460) 2025-01-06 23:32:54 +00:00
Franck Nijhof a412acec0e 2025.1.0 (#134529)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: VandeurenGlenn <8685280+VandeurenGlenn@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Allen Porter <allen.porter@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Teemu R. <tpr@iki.fi>
Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: OzGav <gavnosp@hotmail.com>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>
Co-authored-by: Simon Sorg <simon.sorg@student.hpi.de>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Sander Hoentjen <sander@hoentjen.eu>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Richard Kroegel <42204099+rikroe@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Álvaro Fernández Rojas <noltari@gmail.com>
Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: Manu <4445816+tr4nt0r@users.noreply.github.com>
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Arie Catsman <120491684+catsmanac@users.noreply.github.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Matthias Alphart <farmio@alphart.net>
Co-authored-by: Tom <CoMPaTech@users.noreply.github.com>
Co-authored-by: Steven B. <51370195+sdb9696@users.noreply.github.com>
Co-authored-by: Glenn Vandeuren (aka Iondependent) <vandeurenglenn@gmail.com>
Co-authored-by: Austin Mroczek <austin@mroczek.org>
Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
Co-authored-by: PierreAronnax <pierre@trionax.com>
Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
Co-authored-by: jesperraemaekers <146726232+jesperraemaekers@users.noreply.github.com>
Co-authored-by: Steven Looman <steven.looman@gmail.com>
Co-authored-by: Barry vd. Heuvel <barry@fruitcake.nl>
Co-authored-by: Raphael Hehl <7577984+RaHehl@users.noreply.github.com>
Co-authored-by: Andre Lengwenus <alengwenus@gmail.com>
Co-authored-by: dontinelli <73341522+dontinelli@users.noreply.github.com>
Co-authored-by: Noah Husby <32528627+noahhusby@users.noreply.github.com>
Co-authored-by: Lucas Gasenzer <lucasgasenzer@mac.com>
Co-authored-by: jb101010-2 <168106462+jb101010-2@users.noreply.github.com>
Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com>
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
Co-authored-by: Martin Weinelt <mweinelt@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: jon6fingrs <53415122+jon6fingrs@users.noreply.github.com>
Co-authored-by: mrtlhfr <10065880+mrtlhfr@users.noreply.github.com>
Co-authored-by: Matrix <justin@yosmart.com>
Co-authored-by: Duco Sebel <74970928+DCSBL@users.noreply.github.com>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Omni Flux <omni.hyper.flux@gmail.com>
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Thomas55555 <59625598+Thomas55555@users.noreply.github.com>
Co-authored-by: karwosts <32912880+karwosts@users.noreply.github.com>
Co-authored-by: Jordi <Jordi1990@users.noreply.github.com>
Co-authored-by: Martin Mrazik <mmrazik@users.noreply.github.com>
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
Co-authored-by: G-Two <7310260+G-Two@users.noreply.github.com>
Co-authored-by: Claudio Ruggeri - CR-Tech <41435902+crug80@users.noreply.github.com>
Co-authored-by: Khole <29937485+KJonline@users.noreply.github.com>
Co-authored-by: Philipp Danner <philipp@danner-web.de>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Co-authored-by: Cyrill Raccaud <miaucl@users.noreply.github.com>
Co-authored-by: Allen Porter <allen@thebends.org>
Co-authored-by: Aaron Bach <bachya1208@gmail.com>
Co-authored-by: Michael Hansen <mike@rhasspy.org>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Paul Daumlechner <paul.daumlechner@live.de>
Co-authored-by: Adam Goode <agoode@google.com>
Co-authored-by: Alberto Geniola <albertogeniola@users.noreply.github.com>
Co-authored-by: tronikos <tronikos@users.noreply.github.com>
Co-authored-by: Arne Keller <arne.keller@posteo.de>
Co-authored-by: Andrew Jackson <andrew@codechimp.org>
Co-authored-by: Brynley McDonald <brynley+github@zephire.nz>
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
Co-authored-by: Niels Mündler <niels.muendler@inf.ethz.ch>
Co-authored-by: Craig Andrews <candrews@integralblue.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Matthew FitzGerald-Chamberlain <mattfitzgeraldchamberlain@proton.me>
Co-authored-by: Adam Štrauch <cx@initd.cz>
Co-authored-by: cdnninja <jaydenaphillips@gmail.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Kenny Root <kenny@the-b.org>
Co-authored-by: Krzysztof Dąbrowski <krzysdabro@live.com>
Co-authored-by: Andrea Arcangeli <aagit@users.noreply.github.com>
Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
Fix section translations check (#133683)
Fix test coverage in workday (#133616)
Fix spelling of "Gateway PIN" and remove two excessive spaces (#133716)
Fix Peblar current limit user setting value (#133753)
Fix binary_sensor typing in Overkiz (#133782)
Fix errors in HitachiDHW in Overkiz (#133765)
Fix typo in ElevenLabs (#133819)
fixture from LCN tests (#133821)
fix yesterday sensor extra_state invalid typing (#133425)
Fix TypeError in maxcube climate action inference logic (#133853)
Fix tplink camera entity unique id (#133880)
Fix a history stats bug when window and tracked state change simultaneously (#133770)
fixes #133904
Fix duplicate call to async_register_preload_platform (#133909)
Fix missing % in string for generic camera (#133925)
Fix Peblar import in data coordinator (#133926)
Fix reload modbus component issue (#133820)
Fix error when device goes offline (#133848)
fix "Slow" response leads to "Could not find a charging station" #124129 (#133889)
fix #124129
Fix swiss public transport line field none (#133964)
fix #133116
Fix Nord Pool empty response (#134033)
Fix KNX config flow translations and add data descriptions (#134078)
Fix Wake on LAN Port input as Box instead of Slider (#134216)
Fix duplicate sensor disk entities in Systemmonitor (#134139)
Fix Onkyo volume rounding (#134157)
Fix 400 This voice does not support speaking rate or pitch parameters at this time for Google Cloud Journey voices (#134255)
Fix SQL sensor name (#134414)
Fix a few small typos in peblar (#134481)
Fix input_datetime.set_datetime not accepting 0 timestamp value (#134489)
Fix backup dir not existing (#134506)
Fix activating backup retention config on startup (#134523)
fix generic component tests (#134569)
2025-01-03 19:19:01 +01:00
Franck Nijhof ac4bd32137 Bump version to 2025.1.0 2025-01-03 17:31:21 +00:00
Abílio Costa 7e1e63374f Bump whirlpool-sixth-sense to 0.18.11 (#134562) 2025-01-03 17:31:05 +00:00
Robert Resch 03fd6a901b Cherry pick single file from #134020 to fix generic component tests (#134569) 2025-01-03 18:24:46 +01:00
Franck Nijhof 46b2830699 Bump version to 2025.1.0b9 2025-01-03 15:41:14 +00:00
Bram Kragten b416ae1387 Update frontend to 20250103.0 (#134561) 2025-01-03 15:41:06 +00:00
Erik Montnemery 962b880146 Log cloud backup agent file list (#134556) 2025-01-03 15:41:03 +00:00
Erik Montnemery 9c98125d20 Avoid early COMPLETED event when restoring backup (#134546) 2025-01-03 15:41:00 +00:00
Joost Lekkerkerker c9f1fee6bb Set Ituran to silver (#134538) 2025-01-03 15:40:57 +00:00
Erik Montnemery 9b8ed9643f Add backup as after_dependency of frontend (#134534) 2025-01-03 15:40:54 +00:00
Erik Montnemery 7ea7178aa9 Simplify error handling when creating backup (#134528) 2025-01-03 15:40:51 +00:00
starkillerOG c5746291cc Add Reolink proxy for playback (#133916) 2025-01-03 15:40:46 +00:00
Franck Nijhof 1af384bc0a Bump version to 2025.1.0b8 2025-01-03 09:56:51 +00:00
Franck Nijhof ea82c1b73e Only load Peblar customization update entity when present (#134526) 2025-01-03 09:56:39 +00:00
Franck Nijhof 96936f5f4a Update peblar to v0.3.2 (#134524) 2025-01-03 09:56:36 +00:00
Erik Montnemery 316f93f208 Fix activating backup retention config on startup (#134523) 2025-01-03 09:56:33 +00:00
Robert Svensson f719a14537 Handle deCONZ color temp 0 is never used when calculating kelvin CT (#134521) 2025-01-03 09:56:30 +00:00
Erik Montnemery a830a14342 Improve recorder schema migration error test (#134518) 2025-01-03 09:56:27 +00:00
Erik Montnemery 1b67d51e24 Add error prints for recorder fatal errors (#134517) 2025-01-03 09:56:23 +00:00
Paulus Schoutsen e1f6475623 Fix backup dir not existing (#134506) 2025-01-03 09:56:20 +00:00
Josef Zweck 59a3fe857b Bump aioacaia to 0.1.13 (#134496) 2025-01-03 09:56:17 +00:00
Franck Nijhof f364e29148 Fix input_datetime.set_datetime not accepting 0 timestamp value (#134489) 2025-01-03 09:56:13 +00:00
Franck Nijhof 47190e4ac1 Bump version to 2025.1.0b7 2025-01-02 22:23:54 +00:00
Franck Nijhof 7fa1983da0 Update peblar to 0.3.1 (#134486) 2025-01-02 22:21:44 +00:00
Norbert Rittel 9b906e94c7 Fix a few small typos in peblar (#134481) 2025-01-02 22:21:16 +00:00
Robert Resch 5ac4d5bef7 Bump deebot-client to 10.1.0 (#134470) 2025-01-02 21:36:44 +00:00
Erik Montnemery 995e222959 Don't start recorder if a database from the future is used (#134467) 2025-01-02 21:36:41 +00:00
Duco Sebel 61ac8e7e8c Include host in Peblar EV-Charger discovery setup description (#133954)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-01-02 21:36:38 +00:00
Andrea Arcangeli 67ec71031d open_meteo: correct UTC timezone handling in hourly forecast (#129664)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-01-02 21:36:33 +00:00
Franck Nijhof 59f866bcf7 Bump version to 2025.1.0b6 2025-01-02 17:21:58 +00:00
Bram Kragten d75d970fc7 Update frontend to 20250102.0 (#134462) 2025-01-02 17:21:47 +00:00
Josef Zweck 0a13516ddd Bump aioacaia to 0.1.12 (#134454) 2025-01-02 17:21:43 +00:00
Erik Montnemery 21aca3c146 Initialize AppleTVConfigFlow.identifiers (#134443) 2025-01-02 17:21:40 +00:00
Erik Montnemery faf9c2ee40 Adjust language in backup integration (#134440)
* Adjust language in backup integration

* Update tests
2025-01-02 17:21:37 +00:00
Erik Montnemery e89a1da462 Export IncorrectPasswordError from backup integration (#134436) 2025-01-02 17:21:34 +00:00
Erik Montnemery 8ace126d9f Improve hassio backup create and restore parameter checks (#134434) 2025-01-02 17:21:31 +00:00
TheJulianJES ca6bae6b15 Bump ZHA to 0.0.44 (#134427) 2025-01-02 17:21:28 +00:00
Michael Hansen c9ba267fec Bump intents to 2025.1.1 (#134424) 2025-01-02 17:21:24 +00:00
G Johansson 0e79c17cb8 Fix SQL sensor name (#134414) 2025-01-02 17:21:21 +00:00
Krzysztof Dąbrowski 4cb413521d Add state attributes translations to GIOS (#134390) 2025-01-02 17:21:18 +00:00
Brett Adams f97439eaab Check vehicle metadata (#134381) 2025-01-02 17:21:15 +00:00
Kenny Root 568b637dc5 Bump zabbix-utils to 2.0.2 (#134373) 2025-01-02 17:21:12 +00:00
Stefan Agner 3a8f71a64a Improve Supervisor backup error handling (#134346)
* Raise Home Assistant error in case backup restore fails

This change raises a Home Assistant error in case the backup restore
fails. The Supervisor is checking some common issues before starting
the actual restore in background. This early checks raise an exception
(represented by a HTTP 400 error). This change catches such errors and
raises a Home Assistant error with the message from the Supervisor
exception.

* Add test coverage
2025-01-02 17:21:09 +00:00
cdnninja fea3dfda94 Vesync unload error when not all platforms used (#134166) 2025-01-02 17:21:05 +00:00
Adam Štrauch 554cdd1784 Add new ID LAP-V201S-AEUR for Vital200S AirPurifier in Vesync integration (#133999) 2025-01-02 17:21:02 +00:00
Matthew FitzGerald-Chamberlain ce7a0650e4 Improve support for Aprilaire S86WMUPR (#133974) 2025-01-02 17:20:59 +00:00
Martin Hjelmare 5895aa4cde Handle backup errors more consistently (#133522)
* Add backup manager and read writer errors

* Clean up not needed default argument

* Clean up todo comment

* Trap agent bugs during upload

* Always release stream

* Clean up leftover

* Update test for backup with automatic settings

* Fix use of vol.Any

* Refactor test helper

* Only update successful timestamp if completed event is sent

* Always delete surplus copies

* Fix after rebase

* Fix after rebase

* Revert "Fix use of vol.Any"

This reverts commit 28fd7a544899bb6ed05f771e9e608bc5b41d2b5e.

* Inherit BackupReaderWriterError in IncorrectPasswordError

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-01-02 17:20:52 +00:00
Craig Andrews bd5477729a Improve is docker env checks (#132404)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Sander Hoentjen <sander@hoentjen.eu>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-01-02 17:20:36 +00:00
Paulus Schoutsen 2e21ac7001 Bump version to 2025.1.0b5 2024-12-31 22:10:20 +00:00
Josef Zweck ab6394b26c Bump pylamarzocco to 1.4.6 (#134367) 2024-12-31 22:10:09 +00:00
Bram Kragten 0ae4a9a911 Update frontend to 20241231.0 (#134363) 2024-12-31 22:10:08 +00:00
Michael Hansen f709989717 Revert speech seconds to 0.3 (#134360) 2024-12-31 22:10:05 +00:00
Michael Hansen 952363eca3 Bump hassil to 2.1.0 (#134359) 2024-12-31 22:10:05 +00:00
Simone Chemelli a7995e0093 Bump aioshelly to 12.2.0 (#134352) 2024-12-31 22:10:04 +00:00
Niels Mündler 1064ef9dc6 Bump pysynthru version to 0.8.0 (#134294) 2024-12-31 22:10:03 +00:00
starkillerOG c2f06fbd47 Bump reolink-aio to 0.11.6 (#134286) 2024-12-31 22:10:02 +00:00
Bram Kragten a36fd09644 Set backup manager state to completed when restore is finished (#134283) 2024-12-31 22:10:01 +00:00
tronikos b89995a79f Allow automations to pass any conversation_id for Google Generative AI (#134251) 2024-12-31 22:10:00 +00:00
Brett Adams c908f823c5 Handle missing application credentials in Tesla Fleet (#134237)
* Handle missing application credentials

* Add tests

* Test reauth starts

* Only catch ValueError
2024-12-31 22:09:59 +00:00
Simone Chemelli 229c32b0da Bump aiocomelit to 0.10.1 (#134214) 2024-12-31 22:09:59 +00:00
Dave T e303a9a2b5 Add stream preview to options flow in generic camera (#133927)
* Add stream preview to options flow

* Increase test coverage

* Code review: use correct flow handler type in cast

* Restore test coverage to 100%

* Remove error and test that can't be triggered yet
2024-12-31 22:09:58 +00:00
Brynley McDonald 54fa30c2b8 Update Flick Electric API (#133475) 2024-12-31 22:09:57 +00:00
Joost Lekkerkerker fbd6cf7244 Improve Mealie set mealplan service (#130606)
* Improve Mealie set mealplan service

* Fix

* Fix
2024-12-31 22:09:56 +00:00
Bram Kragten c10175e25c Bump version to 2025.1.0b4 2024-12-30 20:06:44 +01:00
Bram Kragten 82f0e8cc19 Update frontend to 20241230.0 (#134284) 2024-12-30 20:06:32 +01:00
Andrew Jackson 623e1b08b8 Bump aiomealie to 0.9.5 (#134274) 2024-12-30 20:06:31 +01:00
Norbert Rittel 0c73251004 Remove excessive period at end of action name (#134272) 2024-12-30 20:06:30 +01:00
Arne Keller d9057fc43e ollama: update to 0.4.5 (#134265) 2024-12-30 20:06:29 +01:00
Josef Zweck 077c9e62b4 Bump pylamarzocco to 1.4.5 (#134259)
* Bump pylamarzocco to 1.4.4

* Bump pylamarzocco to 1.4.5

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-12-30 20:06:28 +01:00
tronikos 7456ce1c01 Fix 400 This voice does not support speaking rate or pitch parameters at this time for Google Cloud Journey voices (#134255) 2024-12-30 20:06:28 +01:00
tronikos a627fa70a7 Avoid KeyError for ignored entries in async_step_zeroconf of Android TV Remote (#134250) 2024-12-30 20:06:27 +01:00
Michael c402eaec3f Bump aiopegelonline to 0.1.1 (#134230)
bump aiopegelonline to 0.1.1
2024-12-30 20:06:26 +01:00
tronikos ea51ecd384 Bump opower to 0.8.7 (#134228)
* Bump opower to 0.8.7

* update deps
2024-12-30 20:06:25 +01:00
Artur Pragacz 0873d27d7b Fix Onkyo volume rounding (#134157) 2024-12-30 20:06:23 +01:00
G Johansson 45fd7fb6d5 Fix duplicate sensor disk entities in Systemmonitor (#134139) 2024-12-30 20:06:23 +01:00
Alberto Geniola e22685640c Bump elmax-api (#133845) 2024-12-30 20:06:22 +01:00
Adam Goode 5756166545 Quickly process unavailable metrics in Prometheus (#133219) 2024-12-30 20:06:21 +01:00
Norbert Rittel 2f8a92c725 Make triggers and condition for monetary sensor consistent (#131184) 2024-12-30 20:06:20 +01:00
Paul Daumlechner cf9ccc6fb4 Bump pyvlx to 0.2.26 (#115483) 2024-12-30 20:06:19 +01:00
Paulus Schoutsen b05b9b9a33 Bump version to 2025.1.0b3 2024-12-29 18:37:17 +00:00
Paulus Schoutsen 352d5d14a3 Bump frontend to 20241229.0 (#134225) 2024-12-29 18:37:04 +00:00
Michael Hansen 52e47f55c8 Bump VoIP utils to 0.2.2 (#134219) 2024-12-29 18:37:03 +00:00
Lucas Gasenzer 0470bff9a2 Fix Wake on LAN Port input as Box instead of Slider (#134216) 2024-12-29 18:37:02 +00:00
Michael a38839b420 Make feedreader recoverable (#134202)
raise ConfigEntryNotReady on connection errors during setup
2024-12-29 18:37:01 +00:00
Michael 394b2be40a Make PEGELONLINE recoverable (#134199) 2024-12-29 18:37:00 +00:00
Matthias Alphart 291dd6dc66 Update knx-frontend to 2024.12.26.233449 (#134184) 2024-12-29 18:36:59 +00:00
G Johansson ef87366346 Add missing device classes in scrape (#134141) 2024-12-29 18:36:57 +00:00
Joost Lekkerkerker bd243f68a4 Bump yt-dlp to 2024.12.23 (#134131) 2024-12-29 18:36:57 +00:00
Aaron Bach 951baa3972 Bump pytile to 2024.12.0 (#134103) 2024-12-29 18:36:56 +00:00
Joost Lekkerkerker 1874eec8b3 Bump python-homeassistant-analytics to 0.8.1 (#134101) 2024-12-29 18:36:55 +00:00
Joost Lekkerkerker 3120a90f26 Make elevenlabs recoverable (#134094)
* Make elevenlabs recoverable

* Add tests for entry setup

* Use the same fixtures for setup and config flow

* Update tests/components/elevenlabs/test_setup.py

Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>

---------

Co-authored-by: Simon Sorg <simon.sorg@student.hpi.de>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>
2024-12-29 18:36:54 +00:00
Joost Lekkerkerker 7032361bf5 Make google tasks recoverable (#134092) 2024-12-29 18:36:53 +00:00
Matthias Alphart bd786b53ee Fix KNX config flow translations and add data descriptions (#134078)
* Fix KNX config flow translations and add data descriptions

* Update strings.json

* typo
2024-12-29 18:36:53 +00:00
Noah Husby f6a9cd38c0 Remove timeout from Russound RIO initialization (#134070) 2024-12-29 18:36:51 +00:00
Aaron Bach 1a909d3a8a Change SimpliSafe websocket reconnection log to DEBUG-level (#134063)
* Change SimpliSafe websocket reconnection log to `DEBUG`-level

* revert
2024-12-29 18:36:51 +00:00
Noah Husby b84ae2abc3 Bump aiorussound to 4.1.1 (#134058)
* Bump aiorussound to 4.1.1

* Trigger Build

* Trigger Build
2024-12-29 18:36:50 +00:00
G Johansson 15b80c59fc Cleanup devices in Nord Pool from reconfiguration (#134043)
* Cleanup devices in Nord Pool from reconfiguration

* Mods

* Mod
2024-12-29 18:36:49 +00:00
G Johansson c11bdcc949 Fix Nord Pool empty response (#134033)
* Fix Nord Pool empty response

* Mods

* reset validate prices
2024-12-29 18:36:48 +00:00
Allen Porter 1957ab1ccf Improve Google Tasks error messages (#134023) 2024-12-29 18:36:47 +00:00
Josef Zweck ef2af44795 Bump pylamarzocco to 1.4.3 (#134008) 2024-12-29 18:36:47 +00:00
J. Nick Koston f0e8360401 Ensure all states have been migrated to use timestamps (#134007) 2024-12-29 18:36:46 +00:00
Cyrill Raccaud 03fb136218 Fix swiss public transport line field none (#133964)
* fix #133116

The line can theoretically be none, when no line info is available (lets say walking sections first?)

* fix line field

* add unit test with missing line field
2024-12-29 18:36:45 +00:00
abmantis 4315651187 Add strings for 3 phase energy in ZHA 2024-12-24 18:09:06 +00:00
abmantis dda1404716 Add remaining strings and state attrs for ZHA 3 phase 2024-12-24 17:11:31 +00:00
Bram Kragten d415b7bc8d Bump version to 2025.1.0b2 2024-12-24 16:42:54 +01:00
Bram Kragten 9242b67e0d Update frontend to 20241224.0 (#133963) 2024-12-24 16:42:36 +01:00
Marc Mueller 6e7d095831 Update Jinja2 to 3.1.5 (#133951) 2024-12-24 16:42:35 +01:00
Joost Lekkerkerker ef05133a66 Use SignedSession in Xbox (#133938) 2024-12-24 16:42:34 +01:00
Franck Nijhof 7b2fc282e5 Update apprise to v1.9.1 (#133936) 2024-12-24 16:42:33 +01:00
Philipp Danner 4ca17dbb9e fix "Slow" response leads to "Could not find a charging station" #124129 (#133889)
fix #124129
2024-12-24 16:42:32 +01:00
Khole 5d7a22fa76 Hive: Fix error when device goes offline (#133848) 2024-12-24 16:42:31 +01:00
Claudio Ruggeri - CR-Tech 502fbe65ee Fix reload modbus component issue (#133820)
fix issue 116675
2024-12-24 16:42:31 +01:00
abmantis 109a6a428d Merge branch 'dev' of github.com:home-assistant/core into zha_3phase_current_entities 2024-12-24 15:36:29 +00:00
Franck Nijhof ce83071900 Bump version to 2025.1.0b1 2024-12-24 08:24:58 +00:00
G-Two 4f1e9b2338 Stop using shared aiohttp client session for Subaru integration (#133931) 2024-12-24 08:24:37 +00:00
Franck Nijhof f23bc51b88 Fix Peblar import in data coordinator (#133926) 2024-12-24 08:24:34 +00:00
Dave T 44150e9fd7 Fix missing % in string for generic camera (#133925)
Fix missing % in generic camera string
2024-12-24 08:24:31 +00:00
Brett Adams cf9686a802 Slow down polling in Teslemetry (#133924) 2024-12-24 08:24:27 +00:00
Abílio Costa 657e5b73b6 Add cronsim to default dependencies (#133913) 2024-12-24 08:24:24 +00:00
J. Nick Koston d3666ecf8a Fix duplicate call to async_register_preload_platform (#133909) 2024-12-24 08:24:21 +00:00
J. Nick Koston bed186cce4 Ensure cloud and recorder backup platforms do not have to wait for the import executor (#133907)
* Ensure cloud and recorder backup platforms do not have to wait for the import executor

partially fixes #133904

* backup.backup as well
2024-12-24 08:24:18 +00:00
J. Nick Koston 2b8240746a Sort integration platforms preload list (#133905)
* Sort integration platforms preload list

https://github.com/home-assistant/core/pull/133856#discussion_r1895385026

* sort

* Sort them all

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2024-12-24 08:24:14 +00:00
Martin Mrazik efabb82cb6 Map RGB+CCT to RGB for WLED (#133900) 2024-12-24 08:24:11 +00:00
Jordi 80955ba821 Add Harvey virtual integration (#133874)
Add harvey virtual integration
2024-12-24 08:24:07 +00:00
karwosts bb371c87d5 Fix a history stats bug when window and tracked state change simultaneously (#133770) 2024-12-24 08:24:04 +00:00
Thomas55555 7ce563b0b4 Catch ClientConnectorError and TimeOutError in APSystems (#132027) 2024-12-24 08:24:00 +00:00
Franck Nijhof c2f6e5036e Bump version to 2025.1.0b0 2024-12-23 15:56:12 +00:00
Abílio Costa 9883c54725 Merge branch 'dev' into zha_3phase_current_entities 2024-12-23 11:57:39 +00:00
abmantis ed4c49cb21 Add strings and state attrs for ZHA 3 Phase current 2024-12-10 20:00:42 +00:00
2165 changed files with 72857 additions and 30081 deletions
+1 -1
View File
@@ -62,7 +62,7 @@
"json.schemas": [
{
"fileMatch": ["homeassistant/components/*/manifest.json"],
"url": "./script/json_schemas/manifest_schema.json"
"url": "${containerWorkspaceFolder}/script/json_schemas/manifest_schema.json"
}
]
}
+11
View File
@@ -11,3 +11,14 @@
*.pcm binary
Dockerfile.dev linguist-language=Dockerfile
# Generated files
CODEOWNERS linguist-generated=true
Dockerfile linguist-generated=true
homeassistant/generated/*.py linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
requirements_all.txt linguist-generated=true
requirements_test_all.txt linguist-generated=true
requirements_test_pre_commit.txt linguist-generated=true
script/hassfest/docker/Dockerfile linguist-generated=true
+6 -6
View File
@@ -69,7 +69,7 @@ jobs:
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
- name: Upload translations
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: translations
path: translations.tar.gz
@@ -94,7 +94,7 @@ jobs:
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@v7
uses: dawidd6/action-download-artifact@v8
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/frontend
@@ -105,7 +105,7 @@ jobs:
- name: Download nightly wheels of intents
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@v7
uses: dawidd6/action-download-artifact@v8
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/intents-package
@@ -509,7 +509,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -522,7 +522,7 @@ jobs:
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
@@ -531,7 +531,7 @@ jobs:
- name: Generate artifact attestation
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
+15 -15
View File
@@ -41,8 +41,8 @@ env:
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 9
HA_SHORT_VERSION: "2025.2"
DEFAULT_PYTHON: "3.12"
ALL_PYTHON_VERSIONS: "['3.12', '3.13']"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['3.13']"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support
@@ -537,7 +537,7 @@ jobs:
python --version
uv pip freeze >> pip_freeze.txt
- name: Upload pip_freeze artifact
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pip-freeze-${{ matrix.python-version }}
path: pip_freeze.txt
@@ -661,7 +661,7 @@ jobs:
. venv/bin/activate
python -m script.licenses extract --output-file=licenses-${{ matrix.python-version }}.json
- name: Upload licenses
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
path: licenses-${{ matrix.python-version }}.json
@@ -877,7 +877,7 @@ jobs:
. venv/bin/activate
python -m script.split_tests ${{ needs.info.outputs.test_group_count }} tests
- name: Upload pytest_buckets
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest_buckets
path: pytest_buckets.txt
@@ -979,14 +979,14 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1106,7 +1106,7 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${mariadb}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1114,7 +1114,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1236,7 +1236,7 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${postgresql}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1244,7 +1244,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1273,7 +1273,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@v5.1.2
uses: codecov/codecov-action@v5.3.0
with:
fail_ci_if_error: true
flags: full-suite
@@ -1378,14 +1378,14 @@ jobs:
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1411,7 +1411,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@v5.1.2
uses: codecov/codecov-action@v5.3.0
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
+2 -2
View File
@@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.28.0
uses: github/codeql-action/init@v3.28.4
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.28.0
uses: github/codeql-action/analyze@v3.28.4
with:
category: "/language:python"
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
# - No PRs marked as no-stale
# - No issues (-1)
- name: 60 days stale PRs policy
uses: actions/stale@v9.0.0
uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60
@@ -57,7 +57,7 @@ jobs:
# - No issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: 90 days stale issues
uses: actions/stale@v9.0.0
uses: actions/stale@v9.1.0
with:
repo-token: ${{ steps.token.outputs.token }}
days-before-stale: 90
@@ -87,7 +87,7 @@ jobs:
# - No Issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: Needs more information stale issues policy
uses: actions/stale@v9.0.0
uses: actions/stale@v9.1.0
with:
repo-token: ${{ steps.token.outputs.token }}
only-labels: "needs-more-information"
+1 -1
View File
@@ -10,7 +10,7 @@ on:
- "**strings.json"
env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.13"
jobs:
upload:
+5 -5
View File
@@ -17,7 +17,7 @@ on:
- "script/gen_requirements_all.py"
env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.13"
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name}}
@@ -91,7 +91,7 @@ jobs:
) > build_constraints.txt
- name: Upload env_file
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: env_file
path: ./.env_file
@@ -99,14 +99,14 @@ jobs:
overwrite: true
- name: Upload build_constraints
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: build_constraints
path: ./build_constraints.txt
overwrite: true
- name: Upload requirements_diff
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: requirements_diff
path: ./requirements_diff.txt
@@ -118,7 +118,7 @@ jobs:
python -m script.gen_requirements_all ci
- name: Upload requirements_all_wheels
uses: actions/upload-artifact@v4.5.0
uses: actions/upload-artifact@v4.6.0
with:
name: requirements_all_wheels
path: ./requirements_all_wheels_*.txt
+4 -3
View File
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.6
rev: v0.9.1
hooks:
- id: ruff
args:
@@ -61,13 +61,14 @@ repos:
name: mypy
entry: script/run-in-env.sh mypy
language: script
types_or: [python, pyi]
require_serial: true
types_or: [python, pyi]
files: ^(homeassistant|pylint)/.+\.(py|pyi)$
- id: pylint
name: pylint
entry: script/run-in-env.sh pylint -j 0 --ignore-missing-annotations=y
entry: script/run-in-env.sh pylint --ignore-missing-annotations=y
language: script
require_serial: true
types_or: [python, pyi]
files: ^(homeassistant|tests)/.+\.(py|pyi)$
- id: gen_requirements_all
+7
View File
@@ -224,6 +224,7 @@ homeassistant.components.gpsd.*
homeassistant.components.greeneye_monitor.*
homeassistant.components.group.*
homeassistant.components.guardian.*
homeassistant.components.habitica.*
homeassistant.components.hardkernel.*
homeassistant.components.hardware.*
homeassistant.components.here_travel_time.*
@@ -236,6 +237,7 @@ homeassistant.components.homeassistant_green.*
homeassistant.components.homeassistant_hardware.*
homeassistant.components.homeassistant_sky_connect.*
homeassistant.components.homeassistant_yellow.*
homeassistant.components.homee.*
homeassistant.components.homekit.*
homeassistant.components.homekit_controller
homeassistant.components.homekit_controller.alarm_control_panel
@@ -261,6 +263,7 @@ homeassistant.components.image_processing.*
homeassistant.components.image_upload.*
homeassistant.components.imap.*
homeassistant.components.imgw_pib.*
homeassistant.components.incomfort.*
homeassistant.components.input_button.*
homeassistant.components.input_select.*
homeassistant.components.input_text.*
@@ -291,6 +294,7 @@ homeassistant.components.lcn.*
homeassistant.components.ld2410_ble.*
homeassistant.components.led_ble.*
homeassistant.components.lektrico.*
homeassistant.components.letpot.*
homeassistant.components.lidarr.*
homeassistant.components.lifx.*
homeassistant.components.light.*
@@ -305,6 +309,7 @@ homeassistant.components.logbook.*
homeassistant.components.logger.*
homeassistant.components.london_underground.*
homeassistant.components.lookin.*
homeassistant.components.lovelace.*
homeassistant.components.luftdaten.*
homeassistant.components.madvr.*
homeassistant.components.manual.*
@@ -370,6 +375,7 @@ homeassistant.components.panel_custom.*
homeassistant.components.peblar.*
homeassistant.components.peco.*
homeassistant.components.persistent_notification.*
homeassistant.components.person.*
homeassistant.components.pi_hole.*
homeassistant.components.ping.*
homeassistant.components.plugwise.*
@@ -384,6 +390,7 @@ homeassistant.components.purpleair.*
homeassistant.components.pushbullet.*
homeassistant.components.pvoutput.*
homeassistant.components.python_script.*
homeassistant.components.qbus.*
homeassistant.components.qnap_qsw.*
homeassistant.components.rabbitair.*
homeassistant.components.radarr.*
+2 -1
View File
@@ -1,5 +1,5 @@
{
// Please keep this file in sync with settings in home-assistant/.devcontainer/devcontainer.json
// Please keep this file (mostly!) in sync with settings in home-assistant/.devcontainer/devcontainer.json
// Added --no-cov to work around TypeError: message must be set
// https://github.com/microsoft/vscode-python/issues/14067
"python.testing.pytestArgs": ["--no-cov"],
@@ -12,6 +12,7 @@
"fileMatch": [
"homeassistant/components/*/manifest.json"
],
// This value differs between working with devcontainer and locally, therefor this value should NOT be in sync!
"url": "./script/json_schemas/manifest_schema.json"
}
]
Generated
+17 -15
View File
@@ -682,8 +682,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/iammeter/ @lewei50
/homeassistant/components/iaqualink/ @flz
/tests/components/iaqualink/ @flz
/homeassistant/components/ibeacon/ @bdraco
/tests/components/ibeacon/ @bdraco
/homeassistant/components/icloud/ @Quentame @nzapponi
/tests/components/icloud/ @Quentame @nzapponi
/homeassistant/components/idasen_desk/ @abmantis
@@ -831,6 +829,8 @@ build.json @home-assistant/supervisor
/tests/components/led_ble/ @bdraco
/homeassistant/components/lektrico/ @lektrico
/tests/components/lektrico/ @lektrico
/homeassistant/components/letpot/ @jpelgrom
/tests/components/letpot/ @jpelgrom
/homeassistant/components/lg_netcast/ @Drafteed @splinter98
/tests/components/lg_netcast/ @Drafteed @splinter98
/homeassistant/components/lg_thinq/ @LG-ThinQ-Integration
@@ -1022,7 +1022,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/nina/ @DeerMaximum
/tests/components/nina/ @DeerMaximum
/homeassistant/components/nissan_leaf/ @filcole
/homeassistant/components/nmbs/ @thibmaek
/homeassistant/components/noaa_tides/ @jdelaney72
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
/tests/components/nobo_hub/ @echoromeo @oyvindwe
@@ -1074,8 +1073,8 @@ build.json @home-assistant/supervisor
/tests/components/onewire/ @garbled1 @epenet
/homeassistant/components/onkyo/ @arturpragacz @eclair4151
/tests/components/onkyo/ @arturpragacz @eclair4151
/homeassistant/components/onvif/ @hunterjm
/tests/components/onvif/ @hunterjm
/homeassistant/components/onvif/ @hunterjm @jterrace
/tests/components/onvif/ @hunterjm @jterrace
/homeassistant/components/open_meteo/ @frenck
/tests/components/open_meteo/ @frenck
/homeassistant/components/openai_conversation/ @balloob
@@ -1190,6 +1189,8 @@ build.json @home-assistant/supervisor
/tests/components/pyload/ @tr4nt0r
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
/tests/components/qbittorrent/ @geoffreylagaisse @finder39
/homeassistant/components/qbus/ @Qbus-iot @thomasddn
/tests/components/qbus/ @Qbus-iot @thomasddn
/homeassistant/components/qingping/ @bdraco
/tests/components/qingping/ @bdraco
/homeassistant/components/qld_bushfire/ @exxamalte
@@ -1266,8 +1267,8 @@ build.json @home-assistant/supervisor
/tests/components/rituals_perfume_genie/ @milanmeu @frenck
/homeassistant/components/rmvtransport/ @cgtobi
/tests/components/rmvtransport/ @cgtobi
/homeassistant/components/roborock/ @Lash-L
/tests/components/roborock/ @Lash-L
/homeassistant/components/roborock/ @Lash-L @allenporter
/tests/components/roborock/ @Lash-L @allenporter
/homeassistant/components/roku/ @ctalkington
/tests/components/roku/ @ctalkington
/homeassistant/components/romy/ @xeniter
@@ -1286,6 +1287,7 @@ build.json @home-assistant/supervisor
/tests/components/ruckus_unleashed/ @lanrat @ms264556 @gabe565
/homeassistant/components/russound_rio/ @noahhusby
/tests/components/russound_rio/ @noahhusby
/homeassistant/components/russound_rnet/ @noahhusby
/homeassistant/components/ruuvi_gateway/ @akx
/tests/components/ruuvi_gateway/ @akx
/homeassistant/components/ruuvitag_ble/ @akx
@@ -1379,8 +1381,8 @@ build.json @home-assistant/supervisor
/tests/components/slide_local/ @dontinelli
/homeassistant/components/slimproto/ @marcelveldt
/tests/components/slimproto/ @marcelveldt
/homeassistant/components/sma/ @kellerza @rklomp
/tests/components/sma/ @kellerza @rklomp
/homeassistant/components/sma/ @kellerza @rklomp @erwindouna
/tests/components/sma/ @kellerza @rklomp @erwindouna
/homeassistant/components/smappee/ @bsmappee
/tests/components/smappee/ @bsmappee
/homeassistant/components/smart_meter_texas/ @grahamwetzler
@@ -1406,8 +1408,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/solaredge_local/ @drobtravels @scheric
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
/tests/components/solarlog/ @Ernst79 @dontinelli
/homeassistant/components/solax/ @squishykid
/tests/components/solax/ @squishykid
/homeassistant/components/solax/ @squishykid @Darsstar
/tests/components/solax/ @squishykid @Darsstar
/homeassistant/components/soma/ @ratsept @sebfortier2288
/tests/components/soma/ @ratsept @sebfortier2288
/homeassistant/components/sonarr/ @ctalkington
@@ -1626,15 +1628,15 @@ build.json @home-assistant/supervisor
/tests/components/valve/ @home-assistant/core
/homeassistant/components/velbus/ @Cereal2nd @brefra
/tests/components/velbus/ @Cereal2nd @brefra
/homeassistant/components/velux/ @Julius2342 @DeerMaximum
/tests/components/velux/ @Julius2342 @DeerMaximum
/homeassistant/components/velux/ @Julius2342 @DeerMaximum @pawlizio
/tests/components/velux/ @Julius2342 @DeerMaximum @pawlizio
/homeassistant/components/venstar/ @garbled1 @jhollowe
/tests/components/venstar/ @garbled1 @jhollowe
/homeassistant/components/versasense/ @imstevenxyz
/homeassistant/components/version/ @ludeeus
/tests/components/version/ @ludeeus
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak
/homeassistant/components/vicare/ @CFenner
/tests/components/vicare/ @CFenner
/homeassistant/components/vilfo/ @ManneW
Generated
+2 -2
View File
@@ -13,7 +13,7 @@ ENV \
ARG QEMU_CPU
# Install uv
RUN pip3 install uv==0.5.8
RUN pip3 install uv==0.5.21
WORKDIR /usr/src
@@ -55,7 +55,7 @@ RUN \
"armv7") go2rtc_suffix='arm' ;; \
*) go2rtc_suffix=${BUILD_ARCH} ;; \
esac \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.7/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.8/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& chmod +x /bin/go2rtc \
# Verify go2rtc can be executed
&& go2rtc --version
+1 -1
View File
@@ -308,7 +308,7 @@ class AuthStore:
credentials.data = data
self._async_schedule_save()
async def async_load(self) -> None: # noqa: C901
async def async_load(self) -> None:
"""Load the users."""
if self._loaded:
raise RuntimeError("Auth storage is already loaded")
+4 -9
View File
@@ -4,9 +4,8 @@ from __future__ import annotations
import logging
import types
from typing import Any, Generic
from typing import Any
from typing_extensions import TypeVar
import voluptuous as vol
from voluptuous.humanize import humanize_error
@@ -35,12 +34,6 @@ DATA_REQS: HassKey[set[str]] = HassKey("mfa_auth_module_reqs_processed")
_LOGGER = logging.getLogger(__name__)
_MultiFactorAuthModuleT = TypeVar(
"_MultiFactorAuthModuleT",
bound="MultiFactorAuthModule",
default="MultiFactorAuthModule",
)
class MultiFactorAuthModule:
"""Multi-factor Auth Module of validation function."""
@@ -102,7 +95,9 @@ class MultiFactorAuthModule:
raise NotImplementedError
class SetupFlow(data_entry_flow.FlowHandler, Generic[_MultiFactorAuthModuleT]):
class SetupFlow[_MultiFactorAuthModuleT: MultiFactorAuthModule = MultiFactorAuthModule](
data_entry_flow.FlowHandler
):
"""Handler for the setup flow."""
def __init__(
+1 -1
View File
@@ -11,7 +11,7 @@ import uuid
import attr
from attr import Attribute
from attr.setters import validate
from propcache import cached_property
from propcache.api import cached_property
from homeassistant.const import __version__
from homeassistant.data_entry_flow import FlowContext, FlowResult
+4 -4
View File
@@ -17,12 +17,12 @@ POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
__all__ = [
"POLICY_SCHEMA",
"merge_policies",
"PermissionLookup",
"PolicyType",
"AbstractPermissions",
"PolicyPermissions",
"OwnerPermissions",
"PermissionLookup",
"PolicyPermissions",
"PolicyType",
"merge_policies",
]
+2 -6
View File
@@ -5,9 +5,8 @@ from __future__ import annotations
from collections.abc import Mapping
import logging
import types
from typing import Any, Generic
from typing import Any
from typing_extensions import TypeVar
import voluptuous as vol
from voluptuous.humanize import humanize_error
@@ -47,8 +46,6 @@ AUTH_PROVIDER_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
_AuthProviderT = TypeVar("_AuthProviderT", bound="AuthProvider", default="AuthProvider")
class AuthProvider:
"""Provider of user authentication."""
@@ -195,9 +192,8 @@ async def load_auth_provider_module(
return module
class LoginFlow(
class LoginFlow[_AuthProviderT: AuthProvider = AuthProvider](
FlowHandler[AuthFlowContext, AuthFlowResult, tuple[str, str]],
Generic[_AuthProviderT],
):
"""Handler for the login flow."""
+1 -1
View File
@@ -119,7 +119,7 @@ def _extract_backup(
Path(
tempdir,
"extracted",
f"homeassistant.tar{'.gz' if backup_meta["compressed"] else ''}",
f"homeassistant.tar{'.gz' if backup_meta['compressed'] else ''}",
),
gzip=backup_meta["compressed"],
key=password_to_key(restore_content.password)
+1 -1
View File
@@ -31,7 +31,7 @@ def _check_import_call_allowed(mapped_args: dict[str, Any]) -> bool:
def _check_file_allowed(mapped_args: dict[str, Any]) -> bool:
# If the file is in /proc we can ignore it.
args = mapped_args["args"]
path = args[0] if type(args[0]) is str else str(args[0]) # noqa: E721
path = args[0] if type(args[0]) is str else str(args[0])
return path.startswith(ALLOWED_FILE_PREFIXES)
+5
View File
@@ -112,6 +112,11 @@ with contextlib.suppress(ImportError):
# Ensure anyio backend is imported to avoid it being imported in the event loop
from anyio._backends import _asyncio # noqa: F401
with contextlib.suppress(ImportError):
# httpx will import trio if it is installed which does
# blocking I/O in the event loop. We want to avoid that.
import trio # noqa: F401
if TYPE_CHECKING:
from .runner import RuntimeConfig
+1 -1
View File
@@ -26,5 +26,5 @@
"iot_class": "local_push",
"loggers": ["aioacaia"],
"quality_scale": "platinum",
"requirements": ["aioacaia==0.1.13"]
"requirements": ["aioacaia==0.1.14"]
}
+1 -1
View File
@@ -70,7 +70,7 @@ class PulseHub:
async def async_notify_update(self, update_type: aiopulse.UpdateType) -> None:
"""Evaluate entities when hub reports that update has occurred."""
LOGGER.debug("Hub {update_type.name} updated")
LOGGER.debug("Hub %s updated", update_type.name)
if update_type == aiopulse.UpdateType.rollers:
await update_devices(self.hass, self.config_entry, self.api.rollers)
@@ -3,9 +3,9 @@
from __future__ import annotations
import logging
import telnetlib # pylint: disable=deprecated-module
from typing import Final
import telnetlib # pylint: disable=deprecated-module
import voluptuous as vol
from homeassistant.components.device_tracker import (
+5 -2
View File
@@ -34,9 +34,12 @@ from .const import (
SERVICE_REMOVE_URL,
)
SERVICE_URL_SCHEMA = vol.Schema({vol.Required(CONF_URL): cv.url})
SERVICE_URL_SCHEMA = vol.Schema({vol.Required(CONF_URL): vol.Any(cv.url, cv.path)})
SERVICE_ADD_URL_SCHEMA = vol.Schema(
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_URL): cv.url}
{
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_URL): vol.Any(cv.url, cv.path),
}
)
SERVICE_REFRESH_SCHEMA = vol.Schema(
{vol.Optional(CONF_FORCE, default=False): cv.boolean}
@@ -66,7 +66,7 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Motion sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} motion'
self._attr_name = f"{self._zone['name']} motion"
self._attr_unique_id += "-motion"
@property
@@ -84,7 +84,7 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone MyZone sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} myZone'
self._attr_name = f"{self._zone['name']} myZone"
self._attr_unique_id += "-myzone"
@property
@@ -103,7 +103,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Vent Sensor."""
super().__init__(instance, ac_key, zone_key=zone_key)
self._attr_name = f'{self._zone["name"]} vent'
self._attr_name = f"{self._zone['name']} vent"
self._attr_unique_id += "-vent"
@property
@@ -131,7 +131,7 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone wireless signal sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} signal'
self._attr_name = f"{self._zone['name']} signal"
self._attr_unique_id += "-signal"
@property
@@ -165,7 +165,7 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Temp Sensor."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} temperature'
self._attr_name = f"{self._zone['name']} temperature"
self._attr_unique_id += "-temp"
@property
@@ -18,7 +18,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirGradientConfigEntry
from .const import DOMAIN
from .coordinator import AirGradientCoordinator
from .entity import AirGradientEntity
from .entity import AirGradientEntity, exception_handler
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
@@ -100,6 +102,7 @@ class AirGradientButton(AirGradientEntity, ButtonEntity):
self.entity_description = description
self._attr_unique_id = f"{coordinator.serial_number}-{description.key}"
@exception_handler
async def async_press(self) -> None:
"""Press the button."""
await self.entity_description.press_fn(self.coordinator.client)
@@ -1,5 +1,6 @@
"""Config flow for Airgradient."""
from collections.abc import Mapping
from typing import Any
from airgradient import (
@@ -11,10 +12,15 @@ from airgradient import (
from awesomeversion import AwesomeVersion
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
SOURCE_RECONFIGURE,
SOURCE_USER,
ConfigFlow,
ConfigFlowResult,
)
from homeassistant.const import CONF_HOST, CONF_MODEL
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN
@@ -37,7 +43,7 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
await self.client.set_configuration_control(ConfigurationControl.LOCAL)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
self.data[CONF_HOST] = host = discovery_info.host
@@ -95,10 +101,18 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(
current_measures.serial_number, raise_on_progress=False
)
self._abort_if_unique_id_configured()
if self.source == SOURCE_USER:
self._abort_if_unique_id_configured()
if self.source == SOURCE_RECONFIGURE:
self._abort_if_unique_id_mismatch()
await self.set_configuration_source()
return self.async_create_entry(
title=current_measures.model,
if self.source == SOURCE_USER:
return self.async_create_entry(
title=current_measures.model,
data={CONF_HOST: user_input[CONF_HOST]},
)
return self.async_update_reload_and_abort(
self._get_reconfigure_entry(),
data={CONF_HOST: user_input[CONF_HOST]},
)
return self.async_show_form(
@@ -106,3 +120,9 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
errors=errors,
)
async def async_step_reconfigure(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle reconfiguration."""
return await self.async_step_user()
@@ -55,7 +55,11 @@ class AirGradientCoordinator(DataUpdateCoordinator[AirGradientData]):
measures = await self.client.get_current_measures()
config = await self.client.get_config()
except AirGradientError as error:
raise UpdateFailed(error) from error
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_error",
translation_placeholders={"error": str(error)},
) from error
if measures.firmware_version != self._current_version:
device_registry = dr.async_get(self.hass)
device_entry = device_registry.async_get_device(
+33 -1
View File
@@ -1,7 +1,11 @@
"""Base class for AirGradient entities."""
from airgradient import get_model_name
from collections.abc import Callable, Coroutine
from typing import Any, Concatenate
from airgradient import AirGradientConnectionError, AirGradientError, get_model_name
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -26,3 +30,31 @@ class AirGradientEntity(CoordinatorEntity[AirGradientCoordinator]):
serial_number=coordinator.serial_number,
sw_version=measures.firmware_version,
)
def exception_handler[_EntityT: AirGradientEntity, **_P](
func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]],
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
"""Decorate AirGradient calls to handle exceptions.
A decorator that wraps the passed in function, catches AirGradient errors.
"""
async def handler(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None:
try:
await func(self, *args, **kwargs)
except AirGradientConnectionError as error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="communication_error",
translation_placeholders={"error": str(error)},
) from error
except AirGradientError as error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unknown_error",
translation_placeholders={"error": str(error)},
) from error
return handler
@@ -19,7 +19,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirGradientConfigEntry
from .const import DOMAIN
from .coordinator import AirGradientCoordinator
from .entity import AirGradientEntity
from .entity import AirGradientEntity, exception_handler
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
@@ -121,6 +123,7 @@ class AirGradientNumber(AirGradientEntity, NumberEntity):
"""Return the state of the number."""
return self.entity_description.value_fn(self.coordinator.data.config)
@exception_handler
async def async_set_native_value(self, value: float) -> None:
"""Set the selected value."""
await self.entity_description.set_value_fn(self.coordinator.client, int(value))
@@ -29,7 +29,7 @@ rules:
unique-config-entry: done
# Silver
action-exceptions: todo
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
@@ -38,7 +38,7 @@ rules:
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: todo
parallel-updates: done
reauthentication-flow:
status: exempt
comment: |
@@ -68,9 +68,9 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: todo
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: |
@@ -19,7 +19,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirGradientConfigEntry
from .const import DOMAIN, PM_STANDARD, PM_STANDARD_REVERSE
from .coordinator import AirGradientCoordinator
from .entity import AirGradientEntity
from .entity import AirGradientEntity, exception_handler
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
@@ -216,6 +218,7 @@ class AirGradientSelect(AirGradientEntity, SelectEntity):
"""Return the state of the select."""
return self.entity_description.value_fn(self.coordinator.data.config)
@exception_handler
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.entity_description.set_value_fn(self.coordinator.client, option)
@@ -35,6 +35,8 @@ from .const import PM_STANDARD, PM_STANDARD_REVERSE
from .coordinator import AirGradientCoordinator
from .entity import AirGradientEntity
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AirGradientMeasurementSensorEntityDescription(SensorEntityDescription):
@@ -137,6 +139,15 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
entity_registry_enabled_default=False,
value_fn=lambda status: status.raw_total_volatile_organic_component,
),
AirGradientMeasurementSensorEntityDescription(
key="pm02_raw",
translation_key="raw_pm02",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.raw_pm02,
),
)
CONFIG_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (
@@ -17,7 +17,9 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1.",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unique_id_mismatch": "Please ensure you reconfigure against the same device."
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -119,6 +121,9 @@
"raw_nitrogen": {
"name": "Raw NOx"
},
"raw_pm02": {
"name": "Raw PM2.5"
},
"display_pm_standard": {
"name": "[%key:component::airgradient::entity::select::display_pm_standard::name%]",
"state": {
@@ -162,5 +167,16 @@
"name": "Post data to Airgradient"
}
}
},
"exceptions": {
"communication_error": {
"message": "An error occurred while communicating with the Airgradient device: {error}"
},
"unknown_error": {
"message": "An unknown error occurred while communicating with the Airgradient device: {error}"
},
"update_error": {
"message": "An error occurred while communicating with the Airgradient device: {error}"
}
}
}
@@ -20,7 +20,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirGradientConfigEntry
from .const import DOMAIN
from .coordinator import AirGradientCoordinator
from .entity import AirGradientEntity
from .entity import AirGradientEntity, exception_handler
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
@@ -99,11 +101,13 @@ class AirGradientSwitch(AirGradientEntity, SwitchEntity):
"""Return the state of the switch."""
return self.entity_description.value_fn(self.coordinator.data.config)
@exception_handler
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self.entity_description.set_value_fn(self.coordinator.client, True)
await self.coordinator.async_request_refresh()
@exception_handler
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self.entity_description.set_value_fn(self.coordinator.client, False)
@@ -2,7 +2,7 @@
from datetime import timedelta
from propcache import cached_property
from propcache.api import cached_property
from homeassistant.components.update import UpdateDeviceClass, UpdateEntity
from homeassistant.core import HomeAssistant
@@ -11,6 +11,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirGradientConfigEntry, AirGradientCoordinator
from .entity import AirGradientEntity
PARALLEL_UPDATES = 1
SCAN_INTERVAL = timedelta(hours=1)
+9 -12
View File
@@ -21,7 +21,6 @@ from .const import (
ATTR_API_CAT_DESCRIPTION,
ATTR_API_CAT_LEVEL,
ATTR_API_CATEGORY,
ATTR_API_PM25,
ATTR_API_POLLUTANT,
ATTR_API_REPORT_DATE,
ATTR_API_REPORT_HOUR,
@@ -91,18 +90,16 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
max_aqi_desc = obv[ATTR_API_CATEGORY][ATTR_API_CAT_DESCRIPTION]
max_aqi_poll = pollutant
# Copy other data from PM2.5 Value
if obv[ATTR_API_AQI_PARAM] == ATTR_API_PM25:
# Copy Report Details
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
# Copy Report Details
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
# Copy Station Details
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
# Copy Station Details
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
# Store Overall AQI
data[ATTR_API_AQI] = max_aqi
+1 -2
View File
@@ -155,8 +155,7 @@ class AirthingsHeaterEnergySensor(
self._id = airthings_device.device_id
self._attr_device_info = DeviceInfo(
configuration_url=(
"https://dashboard.airthings.com/devices/"
f"{airthings_device.device_id}"
f"https://dashboard.airthings.com/devices/{airthings_device.device_id}"
),
identifiers={(DOMAIN, airthings_device.device_id)},
name=airthings_device.name,
@@ -67,18 +67,21 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"humidity": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"pressure": SensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"battery": SensorEntityDescription(
key="battery",
@@ -86,24 +89,28 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
),
"co2": SensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"voc": SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"illuminance": SensorEntityDescription(
key="illuminance",
translation_key="illuminance",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
}
@@ -50,7 +50,7 @@ SENSOR_DESCRIPTIONS = (
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: int(
history.get(
f'Outdoor {"AQI(US)" if settings["is_aqi_usa"] else "AQI(CN)"}', -1
f"Outdoor {'AQI(US)' if settings['is_aqi_usa'] else 'AQI(CN)'}", -1
)
),
translation_key="outdoor_air_quality_index",
+48 -2
View File
@@ -5,7 +5,14 @@ from __future__ import annotations
import logging
from typing import Any
from aioairzone.const import AZD_MAC, AZD_WEBSERVER, DEFAULT_SYSTEM_ID
from aioairzone.const import (
AZD_FIRMWARE,
AZD_FULL_NAME,
AZD_MAC,
AZD_MODEL,
AZD_WEBSERVER,
DEFAULT_SYSTEM_ID,
)
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
from homeassistant.config_entries import ConfigEntry
@@ -17,6 +24,7 @@ from homeassistant.helpers import (
entity_registry as er,
)
from .const import DOMAIN, MANUFACTURER
from .coordinator import AirzoneUpdateCoordinator
PLATFORMS: list[Platform] = [
@@ -78,7 +86,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
options = ConnectionOptions(
entry.data[CONF_HOST],
entry.data[CONF_PORT],
entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID),
entry.data[CONF_ID],
)
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
@@ -88,6 +96,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
entry.runtime_data = coordinator
device_registry = dr.async_get(hass)
ws_data: dict[str, Any] | None = coordinator.data.get(AZD_WEBSERVER)
if ws_data is not None:
mac = ws_data.get(AZD_MAC, "")
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, mac)},
identifiers={(DOMAIN, f"{entry.entry_id}_ws")},
manufacturer=MANUFACTURER,
model=ws_data.get(AZD_MODEL),
name=ws_data.get(AZD_FULL_NAME),
sw_version=ws_data.get(AZD_FIRMWARE),
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@@ -96,3 +120,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
"""Migrate an old entry."""
if entry.version == 1 and entry.minor_version < 2:
# Add missing CONF_ID
system_id = entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID)
new_data = entry.data.copy()
new_data[CONF_ID] = system_id
hass.config_entries.async_update_entry(
entry,
data=new_data,
minor_version=2,
)
_LOGGER.info(
"Migration to configuration version %s.%s successful",
entry.version,
entry.minor_version,
)
return True
@@ -10,12 +10,12 @@ from aioairzone.exceptions import AirzoneError, InvalidSystem
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
import voluptuous as vol
from homeassistant.components import dhcp
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import DOMAIN
@@ -44,6 +44,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
_discovered_ip: str | None = None
_discovered_mac: str | None = None
MINOR_VERSION = 2
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -53,6 +54,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
errors = {}
if user_input is not None:
if CONF_ID not in user_input:
user_input[CONF_ID] = DEFAULT_SYSTEM_ID
self._async_abort_entries_match(user_input)
airzone = AirzoneLocalApi(
@@ -60,7 +64,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
ConnectionOptions(
user_input[CONF_HOST],
user_input[CONF_PORT],
user_input.get(CONF_ID, DEFAULT_SYSTEM_ID),
user_input[CONF_ID],
),
)
@@ -84,6 +88,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
)
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
if user_input[CONF_ID] != DEFAULT_SYSTEM_ID:
title += f" #{user_input[CONF_ID]}"
return self.async_create_entry(title=title, data=user_input)
return self.async_show_form(
@@ -93,7 +100,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_dhcp(
self, discovery_info: dhcp.DhcpServiceInfo
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle DHCP discovery."""
self._discovered_ip = discovery_info.ip
+4 -2
View File
@@ -68,8 +68,9 @@ class AirzoneSystemEntity(AirzoneEntity):
model=self.get_airzone_value(AZD_MODEL),
name=f"System {self.system_id}",
sw_version=self.get_airzone_value(AZD_FIRMWARE),
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
)
if AZD_WEBSERVER in self.coordinator.data:
self._attr_device_info["via_device"] = (DOMAIN, f"{entry.entry_id}_ws")
self._attr_unique_id = entry.unique_id or entry.entry_id
@property
@@ -102,8 +103,9 @@ class AirzoneHotWaterEntity(AirzoneEntity):
manufacturer=MANUFACTURER,
model="DHW",
name=self.get_airzone_value(AZD_NAME),
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
)
if AZD_WEBSERVER in self.coordinator.data:
self._attr_device_info["via_device"] = (DOMAIN, f"{entry.entry_id}_ws")
self._attr_unique_id = entry.unique_id or entry.entry_id
def get_airzone_value(self, key: str) -> Any:
@@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone",
"iot_class": "local_polling",
"loggers": ["aioairzone"],
"requirements": ["aioairzone==0.9.7"]
"requirements": ["aioairzone==0.9.9"]
}
@@ -7,7 +7,7 @@ from datetime import timedelta
import logging
from typing import TYPE_CHECKING, Any, Final, final
from propcache import cached_property
from propcache.api import cached_property
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
+19 -14
View File
@@ -474,25 +474,30 @@ class ClimateCapabilities(AlexaEntity):
# If we support two modes, one being off, we allow turning on too.
supported_features = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if (
self.entity.domain == climate.DOMAIN
and climate.HVACMode.OFF
in (self.entity.attributes.get(climate.ATTR_HVAC_MODES) or [])
or self.entity.domain == climate.DOMAIN
and (
supported_features
& (
climate.ClimateEntityFeature.TURN_ON
| climate.ClimateEntityFeature.TURN_OFF
(
self.entity.domain == climate.DOMAIN
and climate.HVACMode.OFF
in (self.entity.attributes.get(climate.ATTR_HVAC_MODES) or [])
)
or (
self.entity.domain == climate.DOMAIN
and (
supported_features
& (
climate.ClimateEntityFeature.TURN_ON
| climate.ClimateEntityFeature.TURN_OFF
)
)
)
or self.entity.domain == water_heater.DOMAIN
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
or (
self.entity.domain == water_heater.DOMAIN
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
)
):
yield AlexaPowerController(self.entity)
if (
self.entity.domain == climate.DOMAIN
or self.entity.domain == water_heater.DOMAIN
if self.entity.domain == climate.DOMAIN or (
self.entity.domain == water_heater.DOMAIN
and (
supported_features
& water_heater.WaterHeaterEntityFeature.OPERATION_MODE
@@ -317,9 +317,8 @@ async def async_enable_proactive_mode(
if should_doorbell:
old_state = data["old_state"]
if (
new_state.domain == event.DOMAIN
or new_state.state == STATE_ON
if new_state.domain == event.DOMAIN or (
new_state.state == STATE_ON
and (old_state is None or old_state.state != STATE_ON)
):
await async_send_doorbell_event_message(
@@ -14,7 +14,6 @@ from androidtvremote2 import (
)
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import (
SOURCE_REAUTH,
ConfigEntry,
@@ -31,6 +30,7 @@ from homeassistant.helpers.selector import (
SelectSelectorConfig,
SelectSelectorMode,
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_APP_ICON, CONF_APP_NAME, CONF_APPS, CONF_ENABLE_IME, DOMAIN
from .helpers import create_api, get_enable_ime
@@ -142,7 +142,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
_LOGGER.debug("Android TV device found via zeroconf: %s", discovery_info)
@@ -27,7 +27,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, TemplateError
from homeassistant.helpers import device_registry as dr, intent, llm, template
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import ulid
from homeassistant.util import ulid as ulid_util
from . import AnthropicConfigEntry
from .const import (
@@ -164,7 +164,7 @@ class AnthropicConversationEntity(
]
if user_input.conversation_id is None:
conversation_id = ulid.ulid_now()
conversation_id = ulid_util.ulid_now()
messages = []
elif user_input.conversation_id in self.history:
@@ -177,8 +177,8 @@ class AnthropicConversationEntity(
# a new conversation was started. If the user picks their own, they
# want to track a conversation and we respect it.
try:
ulid.ulid_to_bytes(user_input.conversation_id)
conversation_id = ulid.ulid_now()
ulid_util.ulid_to_bytes(user_input.conversation_id)
conversation_id = ulid_util.ulid_now()
except ValueError:
conversation_id = user_input.conversation_id
@@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/anthropic",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["anthropic==0.31.2"]
"requirements": ["anthropic==0.44.0"]
}
@@ -44,7 +44,10 @@ class APCUPSdData(dict[str, str]):
@property
def serial_no(self) -> str | None:
"""Return the unique serial number of the UPS, if available."""
return self.get("SERIALNO")
sn = self.get("SERIALNO")
# We had user reports that some UPS models simply return "Blank" as serial number, in
# which case we fall back to `None` to indicate that it is actually not available.
return None if sn == "Blank" else sn
class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):
@@ -34,6 +34,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
SchemaFlowFormStep,
SchemaOptionsFlowHandler,
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_CREDENTIALS, CONF_IDENTIFIERS, CONF_START_OFF, DOMAIN
@@ -204,7 +205,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle device found via zeroconf."""
if discovery_info.ip_address.version == 6:
@@ -38,7 +38,7 @@ from homeassistant.loader import (
from homeassistant.util import slugify
from homeassistant.util.hass_dict import HassKey
__all__ = ["ClientCredential", "AuthorizationServer", "async_import_client_credential"]
__all__ = ["AuthorizationServer", "ClientCredential", "async_import_client_credential"]
_LOGGER = logging.getLogger(__name__)
@@ -50,7 +50,7 @@ async def async_setup_entry(
descriptions: list[AprilaireHumidifierDescription] = []
if coordinator.data.get(Attribute.HUMIDIFICATION_AVAILABLE) in (0, 1, 2):
if coordinator.data.get(Attribute.HUMIDIFICATION_AVAILABLE) in (1, 2):
descriptions.append(
AprilaireHumidifierDescription(
key="humidifier",
@@ -67,7 +67,7 @@ async def async_setup_entry(
)
)
if coordinator.data.get(Attribute.DEHUMIDIFICATION_AVAILABLE) in (0, 1):
if coordinator.data.get(Attribute.DEHUMIDIFICATION_AVAILABLE) == 1:
descriptions.append(
AprilaireHumidifierDescription(
key="dehumidifier",
@@ -9,10 +9,10 @@ from arcam.fmj.client import Client, ConnectionFailed
from arcam.fmj.utils import get_uniqueid_from_host, get_uniqueid_from_udn
import voluptuous as vol
from homeassistant.components import ssdp
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_UDN, SsdpServiceInfo
from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN
@@ -88,12 +88,12 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
)
async def async_step_ssdp(
self, discovery_info: ssdp.SsdpServiceInfo
self, discovery_info: SsdpServiceInfo
) -> ConfigFlowResult:
"""Handle a discovered device."""
host = str(urlparse(discovery_info.ssdp_location).hostname)
port = DEFAULT_PORT
uuid = get_uniqueid_from_udn(discovery_info.upnp[ssdp.ATTR_UPNP_UDN])
uuid = get_uniqueid_from_udn(discovery_info.upnp[ATTR_UPNP_UDN])
if not uuid:
return self.async_abort(reason="cannot_connect")
@@ -46,24 +46,24 @@ from .websocket_api import async_register_websocket_api
__all__ = (
"DOMAIN",
"async_create_default_pipeline",
"async_get_pipelines",
"async_migrate_engine",
"async_setup",
"async_pipeline_from_audio_stream",
"async_update_pipeline",
"EVENT_RECORDING",
"OPTION_PREFERRED",
"SAMPLES_PER_CHUNK",
"SAMPLE_CHANNELS",
"SAMPLE_RATE",
"SAMPLE_WIDTH",
"AudioSettings",
"Pipeline",
"PipelineEvent",
"PipelineEventType",
"PipelineNotFound",
"WakeWordSettings",
"EVENT_RECORDING",
"OPTION_PREFERRED",
"SAMPLES_PER_CHUNK",
"SAMPLE_RATE",
"SAMPLE_WIDTH",
"SAMPLE_CHANNELS",
"async_create_default_pipeline",
"async_get_pipelines",
"async_migrate_engine",
"async_pipeline_from_audio_stream",
"async_setup",
"async_update_pipeline",
)
CONFIG_SCHEMA = vol.Schema(
@@ -50,6 +50,7 @@ from homeassistant.util import (
language as language_util,
ulid as ulid_util,
)
from homeassistant.util.hass_dict import HassKey
from homeassistant.util.limited_size_dict import LimitedSizeDict
from .audio_enhancer import AudioEnhancer, EnhancedAudioChunk, MicroVadSpeexEnhancer
@@ -91,6 +92,8 @@ ENGINE_LANGUAGE_PAIRS = (
("tts_engine", "tts_language"),
)
KEY_ASSIST_PIPELINE: HassKey[PipelineData] = HassKey(DOMAIN)
def validate_language(data: dict[str, Any]) -> Any:
"""Validate language settings."""
@@ -248,7 +251,7 @@ async def async_create_default_pipeline(
The default pipeline will use the homeassistant conversation agent and the
specified stt / tts engines.
"""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
pipeline_store = pipeline_data.pipeline_store
pipeline_settings = _async_resolve_default_pipeline_settings(
hass,
@@ -283,7 +286,7 @@ def _async_get_pipeline_from_conversation_entity(
@callback
def async_get_pipeline(hass: HomeAssistant, pipeline_id: str | None = None) -> Pipeline:
"""Get a pipeline by id or the preferred pipeline."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
if pipeline_id is None:
# A pipeline was not specified, use the preferred one
@@ -306,7 +309,7 @@ def async_get_pipeline(hass: HomeAssistant, pipeline_id: str | None = None) -> P
@callback
def async_get_pipelines(hass: HomeAssistant) -> list[Pipeline]:
"""Get all pipelines."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
return list(pipeline_data.pipeline_store.data.values())
@@ -329,7 +332,7 @@ async def async_update_pipeline(
prefer_local_intents: bool | UndefinedType = UNDEFINED,
) -> None:
"""Update a pipeline."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
updates: dict[str, Any] = pipeline.to_json()
updates.pop("id")
@@ -587,7 +590,7 @@ class PipelineRun:
):
raise InvalidPipelineStagesError(self.start_stage, self.end_stage)
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
if self.pipeline.id not in pipeline_data.pipeline_debug:
pipeline_data.pipeline_debug[self.pipeline.id] = LimitedSizeDict(
size_limit=STORED_PIPELINE_RUNS
@@ -615,7 +618,7 @@ class PipelineRun:
def process_event(self, event: PipelineEvent) -> None:
"""Log an event and call listener."""
self.event_callback(event)
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
if self.id not in pipeline_data.pipeline_debug[self.pipeline.id]:
# This run has been evicted from the logged pipeline runs already
return
@@ -650,7 +653,7 @@ class PipelineRun:
)
)
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
pipeline_data.pipeline_runs.remove_run(self)
async def prepare_wake_word_detection(self) -> None:
@@ -1021,9 +1024,18 @@ class PipelineRun:
raise RuntimeError("Recognize intent was not prepared")
if self.pipeline.conversation_language == MATCH_ALL:
# LLMs support all languages ('*') so use pipeline language for
# intent fallback.
input_language = self.pipeline.language
# LLMs support all languages ('*') so use languages from the
# pipeline for intent fallback.
#
# We prioritize the STT and TTS languages because they may be more
# specific, such as "zh-CN" instead of just "zh". This is necessary
# for languages whose intents are split out by region when
# preferring local intent matching.
input_language = (
self.pipeline.stt_language
or self.pipeline.tts_language
or self.pipeline.language
)
else:
input_language = self.pipeline.conversation_language
@@ -1053,7 +1065,8 @@ class PipelineRun:
)
processed_locally = self.intent_agent == conversation.HOME_ASSISTANT_AGENT
conversation_result: conversation.ConversationResult | None = None
agent_id = user_input.agent_id
intent_response: intent.IntentResponse | None = None
if user_input.agent_id != conversation.HOME_ASSISTANT_AGENT:
# Sentence triggers override conversation agent
if (
@@ -1063,14 +1076,12 @@ class PipelineRun:
)
) is not None:
# Sentence trigger matched
trigger_response = intent.IntentResponse(
agent_id = "sentence_trigger"
intent_response = intent.IntentResponse(
self.pipeline.conversation_language
)
trigger_response.async_set_speech(trigger_response_text)
conversation_result = conversation.ConversationResult(
response=trigger_response,
conversation_id=user_input.conversation_id,
)
intent_response.async_set_speech(trigger_response_text)
# Try local intents first, if preferred.
elif self.pipeline.prefer_local_intents and (
intent_response := await conversation.async_handle_intents(
@@ -1078,13 +1089,31 @@ class PipelineRun:
)
):
# Local intent matched
conversation_result = conversation.ConversationResult(
response=intent_response,
conversation_id=user_input.conversation_id,
)
agent_id = conversation.HOME_ASSISTANT_AGENT
processed_locally = True
if conversation_result is None:
# It was already handled, create response and add to chat history
if intent_response is not None:
async with conversation.async_get_chat_session(
self.hass, user_input
) as chat_session:
speech: str = intent_response.speech.get("plain", {}).get(
"speech", ""
)
chat_session.async_add_message(
conversation.ChatMessage(
role="assistant",
agent_id=agent_id,
content=speech,
native=intent_response,
)
)
conversation_result = conversation.ConversationResult(
response=intent_response,
conversation_id=chat_session.conversation_id,
)
else:
# Fall back to pipeline conversation agent
conversation_result = await conversation.async_converse(
hass=self.hass,
@@ -1095,6 +1124,10 @@ class PipelineRun:
language=user_input.language,
agent_id=user_input.agent_id,
)
speech = conversation_result.response.speech.get("plain", {}).get(
"speech", ""
)
except Exception as src_error:
_LOGGER.exception("Unexpected error during intent recognition")
raise IntentRecognitionError(
@@ -1114,10 +1147,6 @@ class PipelineRun:
)
)
speech: str = conversation_result.response.speech.get("plain", {}).get(
"speech", ""
)
return speech
async def prepare_text_to_speech(self) -> None:
@@ -1218,7 +1247,7 @@ class PipelineRun:
return
# Forward to device audio capture
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
audio_queue = pipeline_data.device_audio_queues.get(self._device_id)
if audio_queue is None:
return
@@ -1463,9 +1492,9 @@ class PipelineInput:
if stt_audio_buffer:
# Send audio in the buffer first to speech-to-text, then move on to stt_stream.
# This is basically an async itertools.chain.
async def buffer_then_audio_stream() -> (
AsyncGenerator[EnhancedAudioChunk]
):
async def buffer_then_audio_stream() -> AsyncGenerator[
EnhancedAudioChunk
]:
# Buffered audio
for chunk in stt_audio_buffer:
yield chunk
@@ -1875,7 +1904,7 @@ class PipelineStore(Store[SerializedPipelineStorageCollection]):
return old_data
@singleton(DOMAIN)
@singleton(KEY_ASSIST_PIPELINE, async_=True)
async def async_setup_pipeline_store(hass: HomeAssistant) -> PipelineData:
"""Set up the pipeline storage collection."""
pipeline_store = PipelineStorageCollection(
@@ -9,8 +9,8 @@ from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import collection, entity_registry as er, restore_state
from .const import DOMAIN, OPTION_PREFERRED
from .pipeline import AssistDevice, PipelineData, PipelineStorageCollection
from .const import OPTION_PREFERRED
from .pipeline import KEY_ASSIST_PIPELINE, AssistDevice
from .vad import VadSensitivity
@@ -30,7 +30,7 @@ def get_chosen_pipeline(
if state is None or state.state == OPTION_PREFERRED:
return None
pipeline_store: PipelineStorageCollection = hass.data[DOMAIN].pipeline_store
pipeline_store = hass.data[KEY_ASSIST_PIPELINE].pipeline_store
return next(
(item.id for item in pipeline_store.async_items() if item.name == state.state),
None,
@@ -80,7 +80,7 @@ class AssistPipelineSelect(SelectEntity, restore_state.RestoreEntity):
"""When entity is added to Home Assistant."""
await super().async_added_to_hass()
pipeline_data: PipelineData = self.hass.data[DOMAIN]
pipeline_data = self.hass.data[KEY_ASSIST_PIPELINE]
pipeline_store = pipeline_data.pipeline_store
self.async_on_remove(
pipeline_store.async_add_change_set_listener(self._pipelines_updated)
@@ -116,9 +116,7 @@ class AssistPipelineSelect(SelectEntity, restore_state.RestoreEntity):
@callback
def _update_options(self) -> None:
"""Handle pipeline update."""
pipeline_store: PipelineStorageCollection = self.hass.data[
DOMAIN
].pipeline_store
pipeline_store = self.hass.data[KEY_ASSIST_PIPELINE].pipeline_store
options = [OPTION_PREFERRED]
options.extend(sorted(item.name for item in pipeline_store.async_items()))
self._attr_options = options
@@ -1,9 +1,6 @@
"""Assist pipeline Websocket API."""
import asyncio
# Suppressing disable=deprecated-module is needed for Python 3.11
import audioop # pylint: disable=deprecated-module
import base64
from collections.abc import AsyncGenerator, Callable
import contextlib
@@ -11,6 +8,7 @@ import logging
import math
from typing import Any, Final
import audioop # pylint: disable=deprecated-module
import voluptuous as vol
from homeassistant.components import conversation, stt, tts, websocket_api
@@ -22,7 +20,6 @@ from homeassistant.util import language as language_util
from .const import (
DEFAULT_PIPELINE_TIMEOUT,
DEFAULT_WAKE_WORD_TIMEOUT,
DOMAIN,
EVENT_RECORDING,
SAMPLE_CHANNELS,
SAMPLE_RATE,
@@ -30,9 +27,9 @@ from .const import (
)
from .error import PipelineNotFound
from .pipeline import (
KEY_ASSIST_PIPELINE,
AudioSettings,
DeviceAudioQueue,
PipelineData,
PipelineError,
PipelineEvent,
PipelineEventType,
@@ -284,7 +281,7 @@ def websocket_list_runs(
msg: dict[str, Any],
) -> None:
"""List pipeline runs for which debug data is available."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
pipeline_id = msg["pipeline_id"]
if pipeline_id not in pipeline_data.pipeline_debug:
@@ -320,7 +317,7 @@ def websocket_list_devices(
msg: dict[str, Any],
) -> None:
"""List assist devices."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
ent_reg = er.async_get(hass)
connection.send_result(
msg["id"],
@@ -351,7 +348,7 @@ def websocket_get_run(
msg: dict[str, Any],
) -> None:
"""Get debug data for a pipeline run."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
pipeline_id = msg["pipeline_id"]
pipeline_run_id = msg["pipeline_run_id"]
@@ -456,7 +453,7 @@ async def websocket_device_capture(
msg: dict[str, Any],
) -> None:
"""Capture raw audio from a satellite device and forward to client."""
pipeline_data: PipelineData = hass.data[DOMAIN]
pipeline_data = hass.data[KEY_ASSIST_PIPELINE]
device_id = msg["device_id"]
# Number of seconds to record audio in wall clock time
@@ -30,8 +30,8 @@ from .websocket_api import async_register_websocket_api
__all__ = [
"DOMAIN",
"AssistSatelliteAnnouncement",
"AssistSatelliteEntity",
"AssistSatelliteConfiguration",
"AssistSatelliteEntity",
"AssistSatelliteEntityDescription",
"AssistSatelliteEntityFeature",
"AssistSatelliteWakeWord",
@@ -96,7 +96,11 @@ class AssistSatelliteAnnouncement:
media_id: str
"""Media ID to be played."""
original_media_id: str
"""The raw media ID before processing."""
media_id_source: Literal["url", "media_id", "tts"]
"""Source of the media ID."""
class AssistSatelliteEntity(entity.Entity):
@@ -187,47 +191,10 @@ class AssistSatelliteEntity(entity.Entity):
"""
await self._cancel_running_pipeline()
media_id_source: Literal["url", "media_id", "tts"] | None = None
if message is None:
message = ""
if not media_id:
media_id_source = "tts"
# Synthesize audio and get URL
pipeline_id = self._resolve_pipeline()
pipeline = async_get_pipeline(self.hass, pipeline_id)
tts_options: dict[str, Any] = {}
if pipeline.tts_voice is not None:
tts_options[tts.ATTR_VOICE] = pipeline.tts_voice
if self.tts_options is not None:
tts_options.update(self.tts_options)
media_id = tts_generate_media_source_id(
self.hass,
message,
engine=pipeline.tts_engine,
language=pipeline.tts_language,
options=tts_options,
)
if media_source.is_media_source_id(media_id):
if not media_id_source:
media_id_source = "media_id"
media = await media_source.async_resolve_media(
self.hass,
media_id,
None,
)
media_id = media.url
if not media_id_source:
media_id_source = "url"
# Resolve to full URL
media_id = async_process_play_media_url(self.hass, media_id)
announcement = await self._resolve_announcement_media_id(message, media_id)
if self._is_announcing:
raise SatelliteBusyError
@@ -237,9 +204,7 @@ class AssistSatelliteEntity(entity.Entity):
try:
# Block until announcement is finished
await self.async_announce(
AssistSatelliteAnnouncement(message, media_id, media_id_source)
)
await self.async_announce(announcement)
finally:
self._is_announcing = False
self._set_state(AssistSatelliteState.IDLE)
@@ -428,3 +393,54 @@ class AssistSatelliteEntity(entity.Entity):
vad_sensitivity = vad.VadSensitivity(vad_sensitivity_state.state)
return vad.VadSensitivity.to_seconds(vad_sensitivity)
async def _resolve_announcement_media_id(
self, message: str, media_id: str | None
) -> AssistSatelliteAnnouncement:
"""Resolve the media ID."""
media_id_source: Literal["url", "media_id", "tts"] | None = None
if media_id:
original_media_id = media_id
else:
media_id_source = "tts"
# Synthesize audio and get URL
pipeline_id = self._resolve_pipeline()
pipeline = async_get_pipeline(self.hass, pipeline_id)
tts_options: dict[str, Any] = {}
if pipeline.tts_voice is not None:
tts_options[tts.ATTR_VOICE] = pipeline.tts_voice
if self.tts_options is not None:
tts_options.update(self.tts_options)
media_id = tts_generate_media_source_id(
self.hass,
message,
engine=pipeline.tts_engine,
language=pipeline.tts_language,
options=tts_options,
)
original_media_id = media_id
if media_source.is_media_source_id(media_id):
if not media_id_source:
media_id_source = "media_id"
media = await media_source.async_resolve_media(
self.hass,
media_id,
None,
)
media_id = media.url
if not media_id_source:
media_id_source = "url"
# Resolve to full URL
media_id = async_process_play_media_url(self.hass, media_id)
return AssistSatelliteAnnouncement(
message, media_id, original_media_id, media_id_source
)
@@ -0,0 +1,69 @@
"""Assist Satellite intents."""
import voluptuous as vol
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, intent
from .const import DOMAIN, AssistSatelliteEntityFeature
async def async_setup_intents(hass: HomeAssistant) -> None:
"""Set up the intents."""
intent.async_register(hass, BroadcastIntentHandler())
class BroadcastIntentHandler(intent.IntentHandler):
"""Broadcast a message."""
intent_type = intent.INTENT_BROADCAST
description = "Broadcast a message through the home"
@property
def slot_schema(self) -> dict | None:
"""Return a slot schema."""
return {vol.Required("message"): str}
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
"""Broadcast a message."""
hass = intent_obj.hass
ent_reg = er.async_get(hass)
# Find all assist satellite entities that are not the one invoking the intent
entities = {
entity: entry
for entity in hass.states.async_entity_ids(DOMAIN)
if (entry := ent_reg.async_get(entity))
and entry.supported_features & AssistSatelliteEntityFeature.ANNOUNCE
}
if intent_obj.device_id:
entities = {
entity: entry
for entity, entry in entities.items()
if entry.device_id != intent_obj.device_id
}
await hass.services.async_call(
DOMAIN,
"announce",
{"message": intent_obj.slots["message"]["value"]},
blocking=True,
context=intent_obj.context,
target={"entity_id": list(entities)},
)
response = intent_obj.create_response()
response.async_set_speech("Done")
response.response_type = intent.IntentResponseType.ACTION_DONE
response.async_set_results(
success_results=[
intent.IntentResponseTarget(
type=intent.IntentResponseTargetType.ENTITY,
id=entity,
name=state.name if (state := hass.states.get(entity)) else entity,
)
for entity in entities
]
)
return response
@@ -9,7 +9,7 @@ from dataclasses import dataclass
import logging
from typing import Any, Protocol, cast
from propcache import cached_property
from propcache.api import cached_property
import voluptuous as vol
from homeassistant.components import websocket_api
@@ -636,9 +636,9 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
alias = ""
if "trigger" in run_variables:
if "description" in run_variables["trigger"]:
reason = f' by {run_variables["trigger"]["description"]}'
reason = f" by {run_variables['trigger']['description']}"
if "alias" in run_variables["trigger"]:
alias = f' trigger \'{run_variables["trigger"]["alias"]}\''
alias = f" trigger '{run_variables['trigger']['alias']}'"
self._logger.debug("Automation%s triggered%s", alias, reason)
# Create a new context referring to the old context.
@@ -11,11 +11,12 @@ from python_awair.exceptions import AuthError, AwairError
from python_awair.user import AwairUser
import voluptuous as vol
from homeassistant.components import onboarding, zeroconf
from homeassistant.components import onboarding
from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_DEVICE, CONF_HOST
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN, LOGGER
@@ -29,7 +30,7 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
host: str
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
+14 -7
View File
@@ -10,7 +10,6 @@ from urllib.parse import urlsplit
import voluptuous as vol
from homeassistant.components import dhcp, ssdp, zeroconf
from homeassistant.config_entries import (
SOURCE_IGNORE,
SOURCE_REAUTH,
@@ -32,6 +31,14 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from homeassistant.helpers.service_info.ssdp import (
ATTR_UPNP_FRIENDLY_NAME,
ATTR_UPNP_PRESENTATION_URL,
ATTR_UPNP_SERIAL,
SsdpServiceInfo,
)
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from homeassistant.helpers.typing import VolDictType
from homeassistant.util.network import is_link_local
@@ -190,7 +197,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
return await self.async_step_user()
async def async_step_dhcp(
self, discovery_info: dhcp.DhcpServiceInfo
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for a DHCP discovered Axis device."""
return await self._process_discovered_device(
@@ -203,21 +210,21 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
)
async def async_step_ssdp(
self, discovery_info: ssdp.SsdpServiceInfo
self, discovery_info: SsdpServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for a SSDP discovered Axis device."""
url = urlsplit(discovery_info.upnp[ssdp.ATTR_UPNP_PRESENTATION_URL])
url = urlsplit(discovery_info.upnp[ATTR_UPNP_PRESENTATION_URL])
return await self._process_discovered_device(
{
CONF_HOST: url.hostname,
CONF_MAC: format_mac(discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL]),
CONF_NAME: f"{discovery_info.upnp[ssdp.ATTR_UPNP_FRIENDLY_NAME]}",
CONF_MAC: format_mac(discovery_info.upnp[ATTR_UPNP_SERIAL]),
CONF_NAME: f"{discovery_info.upnp[ATTR_UPNP_FRIENDLY_NAME]}",
CONF_PORT: url.port,
}
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for a Zeroconf discovered Axis device."""
return await self._process_discovered_device(
@@ -2,10 +2,10 @@
"config": {
"step": {
"user": {
"title": "Setup your Azure Data Explorer integration",
"title": "Set up Azure Data Explorer",
"description": "Enter connection details",
"data": {
"cluster_ingest_uri": "Cluster Ingest URI",
"cluster_ingest_uri": "Cluster ingestion URI",
"authority_id": "Authority ID",
"client_id": "Client ID",
"client_secret": "Client secret",
@@ -14,7 +14,7 @@
"use_queued_ingestion": "Use queued ingestion"
},
"data_description": {
"cluster_ingest_uri": "Ingest-URI of the cluster",
"cluster_ingest_uri": "Ingestion URI of the cluster",
"use_queued_ingestion": "Must be enabled when using ADX free cluster"
}
}
@@ -2,26 +2,26 @@
"config": {
"step": {
"user": {
"title": "Set up your Azure Event Hub integration",
"title": "Set up Azure Event Hub",
"data": {
"event_hub_instance_name": "Event Hub Instance Name",
"use_connection_string": "Use Connection String"
"event_hub_instance_name": "Event Hub instance name",
"use_connection_string": "Use connection string"
}
},
"conn_string": {
"title": "Connection String method",
"title": "Connection string method",
"description": "Please enter the connection string for: {event_hub_instance_name}",
"data": {
"event_hub_connection_string": "Event Hub Connection String"
"event_hub_connection_string": "Event Hub connection string"
}
},
"sas": {
"title": "SAS Credentials method",
"title": "SAS credentials method",
"description": "Please enter the SAS (shared access signature) credentials for: {event_hub_instance_name}",
"data": {
"event_hub_namespace": "Event Hub Namespace",
"event_hub_sas_policy": "Event Hub SAS Policy",
"event_hub_sas_key": "Event Hub SAS Key"
"event_hub_namespace": "Event Hub namespace",
"event_hub_sas_policy": "Event Hub SAS policy",
"event_hub_sas_key": "Event Hub SAS key"
}
}
},
@@ -38,7 +38,7 @@
"options": {
"step": {
"init": {
"title": "Options for the Azure Event Hub.",
"title": "Options for Azure Event Hub.",
"data": {
"send_interval": "Interval between sending batches to the hub."
}
+21 -1
View File
@@ -27,6 +27,7 @@ from .manager import (
IncorrectPasswordError,
ManagerBackup,
NewBackup,
RestoreBackupEvent,
WrittenBackup,
)
from .models import AddonInfo, AgentBackup, Folder
@@ -35,7 +36,6 @@ from .websocket import async_register_websocket_handlers
__all__ = [
"AddonInfo",
"AgentBackup",
"ManagerBackup",
"BackupAgent",
"BackupAgentError",
"BackupAgentPlatformProtocol",
@@ -46,7 +46,9 @@ __all__ = [
"Folder",
"IncorrectPasswordError",
"LocalBackupAgent",
"ManagerBackup",
"NewBackup",
"RestoreBackupEvent",
"WrittenBackup",
]
@@ -86,8 +88,26 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
password=None,
)
async def async_handle_create_automatic_service(call: ServiceCall) -> None:
"""Service handler for creating automatic backups."""
config_data = backup_manager.config.data
await backup_manager.async_create_backup(
agent_ids=config_data.create_backup.agent_ids,
include_addons=config_data.create_backup.include_addons,
include_all_addons=config_data.create_backup.include_all_addons,
include_database=config_data.create_backup.include_database,
include_folders=config_data.create_backup.include_folders,
include_homeassistant=True, # always include HA
name=config_data.create_backup.name,
password=config_data.create_backup.password,
with_automatic_settings=True,
)
if not with_hassio:
hass.services.async_register(DOMAIN, "create", async_handle_create_service)
hass.services.async_register(
DOMAIN, "create_automatic", async_handle_create_automatic_service
)
async_register_http_views(hass)
+1 -1
View File
@@ -7,7 +7,7 @@ from collections.abc import AsyncIterator, Callable, Coroutine
from pathlib import Path
from typing import Any, Protocol
from propcache import cached_property
from propcache.api import cached_property
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
+111 -17
View File
@@ -5,8 +5,10 @@ from __future__ import annotations
import asyncio
from collections.abc import Callable
from dataclasses import dataclass, field, replace
import datetime as dt
from datetime import datetime, timedelta
from enum import StrEnum
import random
from typing import TYPE_CHECKING, Self, TypedDict
from cronsim import CronSim
@@ -22,11 +24,17 @@ from .models import BackupManagerError, Folder
if TYPE_CHECKING:
from .manager import BackupManager, ManagerBackup
# The time of the automatic backup event should be compatible with
# the time of the recorder's nightly job which runs at 04:12.
# Run the backup at 04:45.
CRON_PATTERN_DAILY = "45 4 * * *"
CRON_PATTERN_WEEKLY = "45 4 * * {}"
CRON_PATTERN_DAILY = "{m} {h} * * *"
CRON_PATTERN_WEEKLY = "{m} {h} * * {d}"
# The default time for automatic backups to run is at 04:45.
# This time is chosen to be compatible with the time of the recorder's
# nightly job which runs at 04:12.
DEFAULT_BACKUP_TIME = dt.time(4, 45)
# Randomize the start time of the backup by up to 60 minutes to avoid
# all backups running at the same time.
BACKUP_START_TIME_JITTER = 60 * 60
class StoredBackupConfig(TypedDict):
@@ -69,6 +77,12 @@ class BackupConfigData:
else:
last_completed = None
if time_str := data["schedule"]["time"]:
time = dt_util.parse_time(time_str)
else:
time = None
days = [Day(day) for day in data["schedule"]["days"]]
return cls(
create_backup=CreateBackupConfig(
agent_ids=data["create_backup"]["agent_ids"],
@@ -85,7 +99,12 @@ class BackupConfigData:
copies=retention["copies"],
days=retention["days"],
),
schedule=BackupSchedule(state=ScheduleState(data["schedule"]["state"])),
schedule=BackupSchedule(
days=days,
recurrence=ScheduleRecurrence(data["schedule"]["recurrence"]),
state=ScheduleState(data["schedule"].get("state", ScheduleState.NEVER)),
time=time,
),
)
def to_dict(self) -> StoredBackupConfig:
@@ -132,7 +151,7 @@ class BackupConfig:
*,
create_backup: CreateBackupParametersDict | UndefinedType = UNDEFINED,
retention: RetentionParametersDict | UndefinedType = UNDEFINED,
schedule: ScheduleState | UndefinedType = UNDEFINED,
schedule: ScheduleParametersDict | UndefinedType = UNDEFINED,
) -> None:
"""Update config."""
if create_backup is not UNDEFINED:
@@ -143,7 +162,7 @@ class BackupConfig:
self.data.retention = new_retention
self.data.retention.apply(self._manager)
if schedule is not UNDEFINED:
new_schedule = BackupSchedule(state=schedule)
new_schedule = BackupSchedule(**schedule)
if new_schedule.to_dict() != self.data.schedule.to_dict():
self.data.schedule = new_schedule
self.data.schedule.apply(self._manager)
@@ -237,11 +256,46 @@ class RetentionParametersDict(TypedDict, total=False):
class StoredBackupSchedule(TypedDict):
"""Represent the stored backup schedule configuration."""
days: list[Day]
recurrence: ScheduleRecurrence
state: ScheduleState
time: str | None
class ScheduleParametersDict(TypedDict, total=False):
"""Represent parameters for backup schedule."""
days: list[Day]
recurrence: ScheduleRecurrence
state: ScheduleState
time: dt.time | None
class Day(StrEnum):
"""Represent the day(s) in a custom schedule recurrence."""
MONDAY = "mon"
TUESDAY = "tue"
WEDNESDAY = "wed"
THURSDAY = "thu"
FRIDAY = "fri"
SATURDAY = "sat"
SUNDAY = "sun"
class ScheduleRecurrence(StrEnum):
"""Represent the schedule recurrence."""
NEVER = "never"
DAILY = "daily"
CUSTOM_DAYS = "custom_days"
class ScheduleState(StrEnum):
"""Represent the schedule state."""
"""Represent the schedule recurrence.
This is deprecated and can be remove in HA Core 2025.8.
"""
NEVER = "never"
DAILY = "daily"
@@ -258,8 +312,15 @@ class ScheduleState(StrEnum):
class BackupSchedule:
"""Represent the backup schedule."""
days: list[Day] = field(default_factory=list)
recurrence: ScheduleRecurrence = ScheduleRecurrence.NEVER
# Although no longer used, state is kept for backwards compatibility.
# It can be removed in HA Core 2025.8.
state: ScheduleState = ScheduleState.NEVER
time: dt.time | None = None
cron_event: CronSim | None = field(init=False, default=None)
next_automatic_backup: datetime | None = field(init=False, default=None)
next_automatic_backup_additional = False
@callback
def apply(
@@ -268,17 +329,27 @@ class BackupSchedule:
) -> None:
"""Apply a new schedule.
There are only three possible state types: never, daily, or weekly.
There are only three possible recurrence types: never, daily, or custom_days
"""
if self.state is ScheduleState.NEVER:
if self.recurrence is ScheduleRecurrence.NEVER or (
self.recurrence is ScheduleRecurrence.CUSTOM_DAYS and not self.days
):
self._unschedule_next(manager)
return
if self.state is ScheduleState.DAILY:
self._schedule_next(CRON_PATTERN_DAILY, manager)
else:
time = self.time if self.time is not None else DEFAULT_BACKUP_TIME
if self.recurrence is ScheduleRecurrence.DAILY:
self._schedule_next(
CRON_PATTERN_WEEKLY.format(self.state.value),
CRON_PATTERN_DAILY.format(m=time.minute, h=time.hour),
manager,
)
else: # ScheduleRecurrence.CUSTOM_DAYS
self._schedule_next(
CRON_PATTERN_WEEKLY.format(
m=time.minute,
h=time.hour,
d=",".join(day.value for day in self.days),
),
manager,
)
@@ -299,12 +370,23 @@ class BackupSchedule:
if next_time < now:
# schedule a backup at next daily time once
# if we missed the last scheduled backup
cron_event = CronSim(CRON_PATTERN_DAILY, now)
time = self.time if self.time is not None else DEFAULT_BACKUP_TIME
cron_event = CronSim(
CRON_PATTERN_DAILY.format(m=time.minute, h=time.hour), now
)
next_time = next(cron_event)
# reseed the cron event attribute
# add a day to the next time to avoid scheduling at the same time again
self.cron_event = CronSim(cron_pattern, now + timedelta(days=1))
# Compare the computed next time with the next time from the cron pattern
# to determine if an additional backup has been scheduled
cron_event_configured = CronSim(cron_pattern, now)
next_configured_time = next(cron_event_configured)
self.next_automatic_backup_additional = next_time < next_configured_time
else:
self.next_automatic_backup_additional = False
async def _create_backup(now: datetime) -> None:
"""Create backup."""
manager.remove_next_backup_event = None
@@ -329,17 +411,29 @@ class BackupSchedule:
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected error creating automatic backup")
if self.time is None:
# randomize the start time of the backup by up to 60 minutes if the time is
# not set to avoid all backups running at the same time
next_time += timedelta(seconds=random.randint(0, BACKUP_START_TIME_JITTER))
LOGGER.debug("Scheduling next automatic backup at %s", next_time)
self.next_automatic_backup = next_time
manager.remove_next_backup_event = async_track_point_in_time(
manager.hass, _create_backup, next_time
)
def to_dict(self) -> StoredBackupSchedule:
"""Convert backup schedule to a dict."""
return StoredBackupSchedule(state=self.state)
return StoredBackupSchedule(
days=self.days,
recurrence=self.recurrence,
state=self.state,
time=self.time.isoformat() if self.time else None,
)
@callback
def _unschedule_next(self, manager: BackupManager) -> None:
"""Unschedule the next backup."""
self.next_automatic_backup = None
if (remove_next_event := manager.remove_next_backup_event) is not None:
remove_next_event()
manager.remove_next_backup_event = None
+74 -2
View File
@@ -4,18 +4,23 @@ from __future__ import annotations
import asyncio
from http import HTTPStatus
from typing import cast
import threading
from typing import IO, cast
from aiohttp import BodyPartReader
from aiohttp.hdrs import CONTENT_DISPOSITION
from aiohttp.web import FileResponse, Request, Response, StreamResponse
from multidict import istr
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import slugify
from . import util
from .agent import BackupAgent
from .const import DATA_MANAGER
from .manager import BackupManager
@callback
@@ -43,8 +48,13 @@ class DownloadBackupView(HomeAssistantView):
agent_id = request.query.getone("agent_id")
except KeyError:
return Response(status=HTTPStatus.BAD_REQUEST)
try:
password = request.query.getone("password")
except KeyError:
password = None
manager = request.app[KEY_HASS].data[DATA_MANAGER]
hass = request.app[KEY_HASS]
manager = hass.data[DATA_MANAGER]
if agent_id not in manager.backup_agents:
return Response(status=HTTPStatus.BAD_REQUEST)
agent = manager.backup_agents[agent_id]
@@ -58,6 +68,24 @@ class DownloadBackupView(HomeAssistantView):
headers = {
CONTENT_DISPOSITION: f"attachment; filename={slugify(backup.name)}.tar"
}
if not password:
return await self._send_backup_no_password(
request, headers, backup_id, agent_id, agent, manager
)
return await self._send_backup_with_password(
hass, request, headers, backup_id, agent_id, password, agent, manager
)
async def _send_backup_no_password(
self,
request: Request,
headers: dict[istr, str],
backup_id: str,
agent_id: str,
agent: BackupAgent,
manager: BackupManager,
) -> StreamResponse | FileResponse | Response:
if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
@@ -70,6 +98,50 @@ class DownloadBackupView(HomeAssistantView):
await response.write(chunk)
return response
async def _send_backup_with_password(
self,
hass: HomeAssistant,
request: Request,
headers: dict[istr, str],
backup_id: str,
agent_id: str,
password: str,
agent: BackupAgent,
manager: BackupManager,
) -> StreamResponse | FileResponse | Response:
reader: IO[bytes]
if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
try:
reader = await hass.async_add_executor_job(open, path.as_posix(), "rb")
except FileNotFoundError:
return Response(status=HTTPStatus.NOT_FOUND)
else:
stream = await agent.async_download_backup(backup_id)
reader = cast(IO[bytes], util.AsyncIteratorReader(hass, stream))
worker_done_event = asyncio.Event()
def on_done() -> None:
"""Call by the worker thread when it's done."""
hass.loop.call_soon_threadsafe(worker_done_event.set)
stream = util.AsyncIteratorWriter(hass)
worker = threading.Thread(
target=util.decrypt_backup, args=[reader, stream, password, on_done]
)
try:
worker.start()
response = StreamResponse(status=HTTPStatus.OK, headers=headers)
await response.prepare(request)
async for chunk in stream:
await response.write(chunk)
return response
finally:
reader.close()
await worker_done_event.wait()
class UploadBackupView(HomeAssistantView):
"""Generate backup view."""
@@ -2,6 +2,9 @@
"services": {
"create": {
"service": "mdi:cloud-upload"
},
"create_automatic": {
"service": "mdi:cloud-upload"
}
}
}
+84 -13
View File
@@ -10,11 +10,11 @@ from enum import StrEnum
import hashlib
import io
import json
from pathlib import Path
from pathlib import Path, PurePath
import shutil
import tarfile
import time
from typing import TYPE_CHECKING, Any, Protocol, TypedDict
from typing import IO, TYPE_CHECKING, Any, Protocol, TypedDict, cast
import aiohttp
from securetar import SecureTarFile, atomic_contents_add
@@ -31,6 +31,7 @@ from homeassistant.helpers import (
from homeassistant.helpers.json import json_bytes
from homeassistant.util import dt as dt_util
from . import util as backup_util
from .agent import (
BackupAgent,
BackupAgentError,
@@ -48,7 +49,13 @@ from .const import (
)
from .models import AgentBackup, BackupManagerError, Folder
from .store import BackupStore
from .util import make_backup_dir, read_backup, validate_password
from .util import (
AsyncIteratorReader,
make_backup_dir,
read_backup,
validate_password,
validate_password_stream,
)
@dataclass(frozen=True, kw_only=True, slots=True)
@@ -140,6 +147,7 @@ class RestoreBackupState(StrEnum):
"""Receive backup state enum."""
COMPLETED = "completed"
CORE_RESTART = "core_restart"
FAILED = "failed"
IN_PROGRESS = "in_progress"
@@ -210,7 +218,7 @@ class BackupReaderWriter(abc.ABC):
include_database: bool,
include_folders: list[Folder] | None,
include_homeassistant: bool,
on_progress: Callable[[ManagerStateEvent], None],
on_progress: Callable[[CreateBackupEvent], None],
password: str | None,
) -> tuple[NewBackup, asyncio.Task[WrittenBackup]]:
"""Create a backup."""
@@ -231,6 +239,7 @@ class BackupReaderWriter(abc.ABC):
backup_id: str,
*,
agent_id: str,
on_progress: Callable[[RestoreBackupEvent], None],
open_stream: Callable[[], Coroutine[Any, Any, AsyncIterator[bytes]]],
password: str | None,
restore_addons: list[str] | None,
@@ -248,6 +257,14 @@ class BackupReaderWriterError(HomeAssistantError):
class IncorrectPasswordError(BackupReaderWriterError):
"""Raised when the password is incorrect."""
_message = "The password provided is incorrect."
class DecryptOnDowloadNotSupported(BackupManagerError):
"""Raised when on-the-fly decryption is not supported."""
_message = "On-the-fly decryption is not supported for this backup."
class BackupManager:
"""Define the format that backup managers can have."""
@@ -430,18 +447,21 @@ class BackupManager:
return_exceptions=True,
)
for idx, result in enumerate(sync_backup_results):
agent_id = agent_ids[idx]
if isinstance(result, BackupReaderWriterError):
# writer errors will affect all agents
# no point in continuing
raise BackupManagerError(str(result)) from result
if isinstance(result, BackupAgentError):
LOGGER.error("Error uploading to %s: %s", agent_ids[idx], result)
agent_errors[agent_ids[idx]] = result
agent_errors[agent_id] = result
LOGGER.error("Upload failed for %s: %s", agent_id, result)
continue
if isinstance(result, Exception):
# trap bugs from agents
agent_errors[agent_ids[idx]] = result
LOGGER.error("Unexpected error: %s", result, exc_info=result)
agent_errors[agent_id] = result
LOGGER.error(
"Unexpected error for %s: %s", agent_id, result, exc_info=result
)
continue
if isinstance(result, BaseException):
raise result
@@ -753,7 +773,7 @@ class BackupManager:
backup_name = (
name
or f"{"Automatic" if with_automatic_settings else "Custom"} backup {HAVERSION}"
or f"{'Automatic' if with_automatic_settings else 'Custom'} backup {HAVERSION}"
)
try:
@@ -923,6 +943,7 @@ class BackupManager:
backup_id=backup_id,
open_stream=open_backup,
agent_id=agent_id,
on_progress=self.async_on_backup_event,
password=password,
restore_addons=restore_addons,
restore_database=restore_database,
@@ -987,6 +1008,41 @@ class BackupManager:
translation_placeholders={"failed_agents": ", ".join(agent_errors)},
)
async def async_can_decrypt_on_download(
self,
backup_id: str,
*,
agent_id: str,
password: str | None,
) -> None:
"""Check if we are able to decrypt the backup on download."""
try:
agent = self.backup_agents[agent_id]
except KeyError as err:
raise BackupManagerError(f"Invalid agent selected: {agent_id}") from err
if not await agent.async_get_backup(backup_id):
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
)
reader: IO[bytes]
if agent_id in self.local_backup_agents:
local_agent = self.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
reader = await self.hass.async_add_executor_job(open, path.as_posix(), "rb")
else:
backup_stream = await agent.async_download_backup(backup_id)
reader = cast(IO[bytes], AsyncIteratorReader(self.hass, backup_stream))
try:
validate_password_stream(reader, password)
except backup_util.IncorrectPassword as err:
raise IncorrectPasswordError from err
except backup_util.UnsupportedSecureTarVersion as err:
raise DecryptOnDowloadNotSupported from err
except backup_util.DecryptError as err:
raise BackupManagerError(str(err)) from err
finally:
reader.close()
class KnownBackups:
"""Track known backups."""
@@ -1077,7 +1133,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
include_database: bool,
include_folders: list[Folder] | None,
include_homeassistant: bool,
on_progress: Callable[[ManagerStateEvent], None],
on_progress: Callable[[CreateBackupEvent], None],
password: str | None,
) -> tuple[NewBackup, asyncio.Task[WrittenBackup]]:
"""Initiate generating a backup."""
@@ -1117,7 +1173,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
date_str: str,
extra_metadata: dict[str, bool | str],
include_database: bool,
on_progress: Callable[[ManagerStateEvent], None],
on_progress: Callable[[CreateBackupEvent], None],
password: str | None,
) -> WrittenBackup:
"""Generate a backup."""
@@ -1231,6 +1287,17 @@ class CoreBackupReaderWriter(BackupReaderWriter):
if not database_included:
excludes = excludes + EXCLUDE_DATABASE_FROM_BACKUP
def is_excluded_by_filter(path: PurePath) -> bool:
"""Filter to filter excludes."""
for exclude in excludes:
if not path.match(exclude):
continue
LOGGER.debug("Ignoring %s because of %s", path, exclude)
return True
return False
outer_secure_tarfile = SecureTarFile(
tar_file_path, "w", gzip=False, bufsize=BUF_SIZE
)
@@ -1249,7 +1316,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
atomic_contents_add(
tar_file=core_tar,
origin_path=Path(self._hass.config.path()),
excludes=excludes,
file_filter=is_excluded_by_filter,
arcname="data",
)
return (tar_file_path, tar_file_path.stat().st_size)
@@ -1314,6 +1381,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
open_stream: Callable[[], Coroutine[Any, Any, AsyncIterator[bytes]]],
*,
agent_id: str,
on_progress: Callable[[RestoreBackupEvent], None],
password: str | None,
restore_addons: list[str] | None,
restore_database: bool,
@@ -1358,7 +1426,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
validate_password, path, password
)
if not password_valid:
raise IncorrectPasswordError("The password provided is incorrect.")
raise IncorrectPasswordError
def _write_restore_file() -> None:
"""Write the restore file."""
@@ -1376,6 +1444,9 @@ class CoreBackupReaderWriter(BackupReaderWriter):
)
await self._hass.async_add_executor_job(_write_restore_file)
on_progress(
RestoreBackupEvent(stage=None, state=RestoreBackupState.CORE_RESTART)
)
await self._hass.services.async_call("homeassistant", "restart", blocking=True)
@@ -8,5 +8,5 @@
"integration_type": "system",
"iot_class": "calculated",
"quality_scale": "internal",
"requirements": ["cronsim==2.6", "securetar==2024.11.0"]
"requirements": ["cronsim==2.6", "securetar==2025.1.3"]
}
@@ -1 +1,2 @@
create:
create_automatic:
+42 -3
View File
@@ -2,7 +2,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, TypedDict
from typing import TYPE_CHECKING, Any, TypedDict
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.storage import Store
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
STORE_DELAY_SAVE = 30
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
STORAGE_VERSION_MINOR = 2
class StoredBackupData(TypedDict):
@@ -25,14 +26,52 @@ class StoredBackupData(TypedDict):
config: StoredBackupConfig
class _BackupStore(Store[StoredBackupData]):
"""Class to help storing backup data."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize storage class."""
super().__init__(
hass,
STORAGE_VERSION,
STORAGE_KEY,
minor_version=STORAGE_VERSION_MINOR,
)
async def _async_migrate_func(
self,
old_major_version: int,
old_minor_version: int,
old_data: dict[str, Any],
) -> dict[str, Any]:
"""Migrate to the new version."""
data = old_data
if old_major_version == 1:
if old_minor_version < 2:
# Version 1.2 adds configurable backup time and custom days
data["config"]["schedule"]["time"] = None
if (state := data["config"]["schedule"]["state"]) in ("daily", "never"):
data["config"]["schedule"]["days"] = []
data["config"]["schedule"]["recurrence"] = state
else:
data["config"]["schedule"]["days"] = [state]
data["config"]["schedule"]["recurrence"] = "custom_days"
# Note: We allow reading data with major version 2.
# Reject if major version is higher than 2.
if old_major_version > 2:
raise NotImplementedError
return data
class BackupStore:
"""Store backup config."""
def __init__(self, hass: HomeAssistant, manager: BackupManager) -> None:
"""Initialize the backup manager."""
"""Initialize the backup store."""
self._hass = hass
self._manager = manager
self._store: Store[StoredBackupData] = Store(hass, STORAGE_VERSION, STORAGE_KEY)
self._store = _BackupStore(hass)
async def load(self) -> StoredBackupData | None:
"""Load the store."""
@@ -13,6 +13,10 @@
"create": {
"name": "Create backup",
"description": "Creates a new backup."
},
"create_automatic": {
"name": "Create automatic backup",
"description": "Creates a new backup with automatic backup settings."
}
}
}
+185 -3
View File
@@ -3,22 +3,51 @@
from __future__ import annotations
import asyncio
from pathlib import Path
from collections.abc import AsyncIterator, Callable
import copy
from io import BytesIO
import json
from pathlib import Path, PurePath
from queue import SimpleQueue
import tarfile
from typing import cast
from typing import IO, Self, cast
import aiohttp
from securetar import SecureTarFile
from securetar import SecureTarError, SecureTarFile, SecureTarReadError
from homeassistant.backup_restore import password_to_key
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.json import JsonObjectType, json_loads_object
from .const import BUF_SIZE, LOGGER
from .models import AddonInfo, AgentBackup, Folder
class DecryptError(HomeAssistantError):
"""Error during decryption."""
_message = "Unexpected error during decryption."
class UnsupportedSecureTarVersion(DecryptError):
"""Unsupported securetar version."""
_message = "Unsupported securetar version."
class IncorrectPassword(DecryptError):
"""Invalid password or corrupted backup."""
_message = "Invalid password or corrupted backup."
class BackupEmpty(DecryptError):
"""No tar files found in the backup."""
_message = "No tar files found in the backup."
def make_backup_dir(path: Path) -> None:
"""Create a backup directory if it does not exist."""
path.mkdir(exist_ok=True)
@@ -106,6 +135,159 @@ def validate_password(path: Path, password: str | None) -> bool:
return False
class AsyncIteratorReader:
"""Wrap an AsyncIterator."""
def __init__(self, hass: HomeAssistant, stream: AsyncIterator[bytes]) -> None:
"""Initialize the wrapper."""
self._hass = hass
self._stream = stream
self._buffer: bytes | None = None
self._pos: int = 0
async def _next(self) -> bytes | None:
"""Get the next chunk from the iterator."""
return await anext(self._stream, None)
def read(self, n: int = -1, /) -> bytes:
"""Read data from the iterator."""
result = bytearray()
while n < 0 or len(result) < n:
if not self._buffer:
self._buffer = asyncio.run_coroutine_threadsafe(
self._next(), self._hass.loop
).result()
self._pos = 0
if not self._buffer:
# The stream is exhausted
break
chunk = self._buffer[self._pos : self._pos + n]
result.extend(chunk)
n -= len(chunk)
self._pos += len(chunk)
if self._pos == len(self._buffer):
self._buffer = None
return bytes(result)
def close(self) -> None:
"""Close the iterator."""
class AsyncIteratorWriter:
"""Wrap an AsyncIterator."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the wrapper."""
self._hass = hass
self._queue: asyncio.Queue[bytes | None] = asyncio.Queue(maxsize=1)
def __aiter__(self) -> Self:
"""Return the iterator."""
return self
async def __anext__(self) -> bytes:
"""Get the next chunk from the iterator."""
if data := await self._queue.get():
return data
raise StopAsyncIteration
def write(self, s: bytes, /) -> int:
"""Write data to the iterator."""
asyncio.run_coroutine_threadsafe(self._queue.put(s), self._hass.loop).result()
return len(s)
def validate_password_stream(
input_stream: IO[bytes],
password: str | None,
) -> None:
"""Decrypt a backup."""
with (
tarfile.open(fileobj=input_stream, mode="r|", bufsize=BUF_SIZE) as input_tar,
):
for obj in input_tar:
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
continue
istf = SecureTarFile(
None, # Not used
gzip=False,
key=password_to_key(password) if password is not None else None,
mode="r",
fileobj=input_tar.extractfile(obj),
)
with istf.decrypt(obj) as decrypted:
if istf.securetar_header.plaintext_size is None:
raise UnsupportedSecureTarVersion
try:
decrypted.read(1) # Read a single byte to trigger the decryption
except SecureTarReadError as err:
raise IncorrectPassword from err
return
raise BackupEmpty
def decrypt_backup(
input_stream: IO[bytes],
output_stream: IO[bytes],
password: str | None,
on_done: Callable[[], None],
) -> None:
"""Decrypt a backup."""
try:
with (
tarfile.open(
fileobj=input_stream, mode="r|", bufsize=BUF_SIZE
) as input_tar,
tarfile.open(
fileobj=output_stream, mode="w|", bufsize=BUF_SIZE
) as output_tar,
):
_decrypt_backup(input_tar, output_tar, password)
except (DecryptError, SecureTarError, tarfile.TarError) as err:
LOGGER.warning("Error decrypting backup: %s", err)
finally:
output_stream.write(b"") # Write an empty chunk to signal the end of the stream
on_done()
def _decrypt_backup(
input_tar: tarfile.TarFile,
output_tar: tarfile.TarFile,
password: str | None,
) -> None:
"""Decrypt a backup."""
for obj in input_tar:
# We compare with PurePath to avoid issues with different path separators,
# for example when backup.json is added as "./backup.json"
if PurePath(obj.name) == PurePath("backup.json"):
# Rewrite the backup.json file to indicate that the backup is decrypted
if not (reader := input_tar.extractfile(obj)):
raise DecryptError
metadata = json_loads_object(reader.read())
metadata["protected"] = False
updated_metadata_b = json.dumps(metadata).encode()
metadata_obj = copy.deepcopy(obj)
metadata_obj.size = len(updated_metadata_b)
output_tar.addfile(metadata_obj, BytesIO(updated_metadata_b))
continue
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
output_tar.addfile(obj, input_tar.extractfile(obj))
continue
istf = SecureTarFile(
None, # Not used
gzip=False,
key=password_to_key(password) if password is not None else None,
mode="r",
fileobj=input_tar.extractfile(obj),
)
with istf.decrypt(obj) as decrypted:
if (plaintext_size := istf.securetar_header.plaintext_size) is None:
raise UnsupportedSecureTarVersion
decrypted_obj = copy.deepcopy(obj)
decrypted_obj.size = plaintext_size
output_tar.addfile(decrypted_obj, decrypted)
async def receive_file(
hass: HomeAssistant, contents: aiohttp.BodyPartReader, path: Path
) -> None:
+62 -4
View File
@@ -6,10 +6,15 @@ import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
from .config import ScheduleState
from .config import Day, ScheduleRecurrence
from .const import DATA_MANAGER, LOGGER
from .manager import IncorrectPasswordError, ManagerStateEvent
from .manager import (
DecryptOnDowloadNotSupported,
IncorrectPasswordError,
ManagerStateEvent,
)
from .models import Folder
@@ -24,6 +29,7 @@ def async_register_websocket_handlers(hass: HomeAssistant, with_hassio: bool) ->
websocket_api.async_register_command(hass, handle_details)
websocket_api.async_register_command(hass, handle_info)
websocket_api.async_register_command(hass, handle_can_decrypt_on_download)
websocket_api.async_register_command(hass, handle_create)
websocket_api.async_register_command(hass, handle_create_with_automatic_settings)
websocket_api.async_register_command(hass, handle_delete)
@@ -54,6 +60,8 @@ async def handle_info(
"backups": [backup.as_frontend_json() for backup in backups.values()],
"last_attempted_automatic_backup": manager.config.data.last_attempted_automatic_backup,
"last_completed_automatic_backup": manager.config.data.last_completed_automatic_backup,
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup,
"next_automatic_backup_additional": manager.config.data.schedule.next_automatic_backup_additional,
},
)
@@ -147,6 +155,38 @@ async def handle_restore(
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required("type"): "backup/can_decrypt_on_download",
vol.Required("backup_id"): str,
vol.Required("agent_id"): str,
vol.Required("password"): str,
}
)
@websocket_api.async_response
async def handle_can_decrypt_on_download(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Check if the supplied password is correct."""
try:
await hass.data[DATA_MANAGER].async_can_decrypt_on_download(
msg["backup_id"],
agent_id=msg["agent_id"],
password=msg.get("password"),
)
except IncorrectPasswordError:
connection.send_error(msg["id"], "password_incorrect", "Incorrect password")
except DecryptOnDowloadNotSupported:
connection.send_error(
msg["id"], "decrypt_not_supported", "Decrypt on download not supported"
)
else:
connection.send_result(msg["id"])
@websocket_api.require_admin
@websocket_api.websocket_command(
{
@@ -281,10 +321,18 @@ async def handle_config_info(
) -> None:
"""Send the stored backup config."""
manager = hass.data[DATA_MANAGER]
config = manager.config.data.to_dict()
# Remove state from schedule, it's not needed in the frontend
# mypy doesn't like deleting from TypedDict, ignore it
del config["schedule"]["state"] # type: ignore[misc]
connection.send_result(
msg["id"],
{
"config": manager.config.data.to_dict(),
"config": config
| {
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup,
"next_automatic_backup_additional": manager.config.data.schedule.next_automatic_backup_additional,
}
},
)
@@ -314,7 +362,17 @@ async def handle_config_info(
vol.Optional("days"): vol.Any(int, None),
},
),
vol.Optional("schedule"): vol.All(str, vol.Coerce(ScheduleState)),
vol.Optional("schedule"): vol.Schema(
{
vol.Optional("days"): vol.Any(
vol.All([vol.Coerce(Day)], vol.Unique()),
),
vol.Optional("recurrence"): vol.All(
str, vol.Coerce(ScheduleRecurrence)
),
vol.Optional("time"): vol.Any(cv.time, None),
}
),
}
)
@websocket_api.async_response
+2 -2
View File
@@ -10,9 +10,9 @@ from aiobafi6 import Device, Service
from aiobafi6.discovery import PORT
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_IP_ADDRESS
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN, RUN_TIMEOUT
from .models import BAFDiscovery
@@ -44,7 +44,7 @@ class BAFFlowHandler(ConfigFlow, domain=DOMAIN):
self.discovery: BAFDiscovery | None = None
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
if discovery_info.ip_address.version == 6:
+1 -1
View File
@@ -20,7 +20,7 @@ class BalboaEntity(Entity):
"""Initialize the control."""
mac = client.mac_address
model = client.model
self._attr_unique_id = f'{model}-{key}-{mac.replace(":","")[-6:]}'
self._attr_unique_id = f"{model}-{key}-{mac.replace(':', '')[-6:]}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, mac)},
name=model,
@@ -10,10 +10,10 @@ from mozart_api.exceptions import ApiException
from mozart_api.mozart_client import MozartClient
import voluptuous as vol
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_MODEL
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from homeassistant.util.ssl import get_default_context
from .const import (
@@ -131,7 +131,10 @@ def _no_overlapping(configs: list[dict]) -> list[dict]:
for i, tup in enumerate(intervals):
if len(intervals) > i + 1 and tup.below > intervals[i + 1].above:
raise vol.Invalid(
f"Ranges for bayesian numeric state entities must not overlap, but {ent_id} has overlapping ranges, above:{tup.above}, below:{tup.below} overlaps with above:{intervals[i+1].above}, below:{intervals[i+1].below}."
"Ranges for bayesian numeric state entities must not overlap, "
f"but {ent_id} has overlapping ranges, above:{tup.above}, "
f"below:{tup.below} overlaps with above:{intervals[i + 1].above}, "
f"below:{intervals[i + 1].below}."
)
return configs
@@ -206,7 +209,10 @@ async def async_setup_platform(
broken_observations: list[dict[str, Any]] = []
for observation in observations:
if CONF_P_GIVEN_F not in observation:
text: str = f"{name}/{observation.get(CONF_ENTITY_ID,'')}{observation.get(CONF_VALUE_TEMPLATE,'')}"
text = (
f"{name}/{observation.get(CONF_ENTITY_ID, '')}"
f"{observation.get(CONF_VALUE_TEMPLATE, '')}"
)
raise_no_prob_given_false(hass, text)
_LOGGER.error("Missing prob_given_false YAML entry for %s", text)
broken_observations.append(observation)
@@ -7,7 +7,7 @@ from enum import StrEnum
import logging
from typing import Literal, final
from propcache import cached_property
from propcache.api import cached_property
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
@@ -15,10 +15,10 @@ from blebox_uniapi.error import (
from blebox_uniapi.session import ApiHost
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from . import get_maybe_authenticated_session
from .const import (
@@ -84,7 +84,7 @@ class BleBoxConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
hass = self.hass
@@ -5,7 +5,7 @@
"data": {
"api_token": "[%key:common::config_flow::data::api_token%]"
},
"description": "Enter your Blue Current api token",
"description": "Enter your Blue Current API token",
"title": "Authentication"
}
},
@@ -19,7 +19,7 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"wrong_account": "Wrong account: Please authenticate with the api key for {email}."
"wrong_account": "Wrong account: Please authenticate with the API token for {email}."
}
},
"entity": {
@@ -13,7 +13,7 @@ import yarl
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.util import yaml
from homeassistant.util import yaml as yaml_util
from .models import Blueprint
from .schemas import BLUEPRINT_SCHEMA, is_blueprint_config
@@ -115,7 +115,7 @@ def _extract_blueprint_from_community_topic(
block_content = html.unescape(block_content.strip())
try:
data = yaml.parse_yaml(block_content)
data = yaml_util.parse_yaml(block_content)
except HomeAssistantError:
if block_syntax == "yaml":
raise
@@ -136,7 +136,7 @@ def _extract_blueprint_from_community_topic(
)
return ImportedBlueprint(
f'{post["username"]}/{topic["slug"]}', block_content, blueprint
f"{post['username']}/{topic['slug']}", block_content, blueprint
)
@@ -167,14 +167,13 @@ async def fetch_blueprint_from_github_url(
resp = await session.get(import_url, raise_for_status=True)
raw_yaml = await resp.text()
data = yaml.parse_yaml(raw_yaml)
data = yaml_util.parse_yaml(raw_yaml)
assert isinstance(data, dict)
blueprint = Blueprint(data, schema=BLUEPRINT_SCHEMA)
parsed_import_url = yarl.URL(import_url)
suggested_filename = f"{parsed_import_url.parts[1]}/{parsed_import_url.parts[-1]}"
if suggested_filename.endswith(".yaml"):
suggested_filename = suggested_filename[:-5]
suggested_filename = suggested_filename.removesuffix(".yaml")
return ImportedBlueprint(suggested_filename, raw_yaml, blueprint)
@@ -205,7 +204,7 @@ async def fetch_blueprint_from_github_gist_url(
continue
content = info["content"]
data = yaml.parse_yaml(content)
data = yaml_util.parse_yaml(content)
if not is_blueprint_config(data):
continue
@@ -236,7 +235,7 @@ async def fetch_blueprint_from_website_url(
resp = await session.get(url, raise_for_status=True)
raw_yaml = await resp.text()
data = yaml.parse_yaml(raw_yaml)
data = yaml_util.parse_yaml(raw_yaml)
assert isinstance(data, dict)
blueprint = Blueprint(data, schema=BLUEPRINT_SCHEMA)
@@ -253,7 +252,7 @@ async def fetch_blueprint_from_generic_url(
resp = await session.get(url, raise_for_status=True)
raw_yaml = await resp.text()
data = yaml.parse_yaml(raw_yaml)
data = yaml_util.parse_yaml(raw_yaml)
assert isinstance(data, dict)
blueprint = Blueprint(data, schema=BLUEPRINT_SCHEMA)
+7 -5
View File
@@ -23,7 +23,7 @@ from homeassistant.const import (
)
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import yaml
from homeassistant.util import yaml as yaml_util
from .const import (
BLUEPRINT_FOLDER,
@@ -79,7 +79,7 @@ class Blueprint:
self.domain = data_domain
missing = yaml.extract_inputs(data) - set(self.inputs)
missing = yaml_util.extract_inputs(data) - set(self.inputs)
if missing:
raise InvalidBlueprint(
@@ -117,7 +117,7 @@ class Blueprint:
def yaml(self) -> str:
"""Dump blueprint as YAML."""
return yaml.dump(self.data)
return yaml_util.dump(self.data)
@callback
def validate(self) -> list[str] | None:
@@ -179,7 +179,7 @@ class BlueprintInputs:
@callback
def async_substitute(self) -> dict:
"""Get the blueprint value with the inputs substituted."""
processed = yaml.substitute(self.blueprint.data, self.inputs_with_default)
processed = yaml_util.substitute(self.blueprint.data, self.inputs_with_default)
combined = {**processed, **self.config_with_inputs}
# From config_with_inputs
combined.pop(CONF_USE_BLUEPRINT)
@@ -225,7 +225,9 @@ class DomainBlueprints:
def _load_blueprint(self, blueprint_path: str) -> Blueprint:
"""Load a blueprint."""
try:
blueprint_data = yaml.load_yaml_dict(self.blueprint_folder / blueprint_path)
blueprint_data = yaml_util.load_yaml_dict(
self.blueprint_folder / blueprint_path
)
except FileNotFoundError as err:
raise FailedToLoad(
self.domain,
@@ -13,7 +13,7 @@ from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.util import yaml
from homeassistant.util import yaml as yaml_util
from . import importer, models
from .const import DOMAIN
@@ -174,7 +174,7 @@ async def ws_save_blueprint(
domain = msg["domain"]
try:
yaml_data = cast(dict[str, Any], yaml.parse_yaml(msg["yaml"]))
yaml_data = cast(dict[str, Any], yaml_util.parse_yaml(msg["yaml"]))
blueprint = models.Blueprint(
yaml_data, expected_domain=domain, schema=BLUEPRINT_SCHEMA
)
@@ -263,7 +263,7 @@ async def ws_substitute_blueprint(
try:
config = blueprint_inputs.async_substitute()
except yaml.UndefinedSubstitution as err:
except yaml_util.UndefinedSubstitution as err:
connection.send_error(msg["id"], websocket_api.ERR_UNKNOWN_ERROR, str(err))
return
+14 -10
View File
@@ -14,10 +14,13 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
from .coordinator import BluesoundCoordinator
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
PLATFORMS = [Platform.MEDIA_PLAYER]
PLATFORMS = [
Platform.MEDIA_PLAYER,
]
@dataclass
@@ -26,6 +29,7 @@ class BluesoundRuntimeData:
player: Player
sync_status: SyncStatus
coordinator: BluesoundCoordinator
type BluesoundConfigEntry = ConfigEntry[BluesoundRuntimeData]
@@ -33,9 +37,6 @@ type BluesoundConfigEntry = ConfigEntry[BluesoundRuntimeData]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Bluesound."""
if DOMAIN not in hass.data:
hass.data[DOMAIN] = []
return True
@@ -46,13 +47,16 @@ async def async_setup_entry(
host = config_entry.data[CONF_HOST]
port = config_entry.data[CONF_PORT]
session = async_get_clientsession(hass)
async with Player(host, port, session=session, default_timeout=10) as player:
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError as ex:
raise ConfigEntryNotReady(f"Error connecting to {host}:{port}") from ex
player = Player(host, port, session=session, default_timeout=10)
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError as ex:
raise ConfigEntryNotReady(f"Error connecting to {host}:{port}") from ex
config_entry.runtime_data = BluesoundRuntimeData(player, sync_status)
coordinator = BluesoundCoordinator(hass, player, sync_status)
await coordinator.async_config_entry_first_refresh()
config_entry.runtime_data = BluesoundRuntimeData(player, sync_status, coordinator)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
@@ -7,10 +7,10 @@ from pyblu import Player, SyncStatus
from pyblu.errors import PlayerUnreachableError
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import DOMAIN
from .media_player import DEFAULT_PORT
@@ -72,7 +72,7 @@ class BluesoundConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle a flow initialized by zeroconf discovery."""
if discovery_info.port is not None:
@@ -0,0 +1,160 @@
"""Define a base coordinator for Bluesound entities."""
from __future__ import annotations
import asyncio
from collections.abc import Callable, Coroutine
import contextlib
from dataclasses import dataclass, replace
from datetime import timedelta
import logging
from pyblu import Input, Player, Preset, Status, SyncStatus
from pyblu.errors import PlayerUnreachableError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
NODE_OFFLINE_CHECK_TIMEOUT = timedelta(minutes=3)
PRESET_AND_INPUTS_INTERVAL = timedelta(minutes=15)
@dataclass
class BluesoundData:
"""Define a class to hold Bluesound data."""
sync_status: SyncStatus
status: Status
presets: list[Preset]
inputs: list[Input]
def cancel_task(task: asyncio.Task) -> Callable[[], Coroutine[None, None, None]]:
"""Cancel a task."""
async def _cancel_task() -> None:
task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await task
return _cancel_task
class BluesoundCoordinator(DataUpdateCoordinator[BluesoundData]):
"""Define an object to hold Bluesound data."""
def __init__(
self, hass: HomeAssistant, player: Player, sync_status: SyncStatus
) -> None:
"""Initialize."""
self.player = player
self._inital_sync_status = sync_status
super().__init__(
hass,
logger=_LOGGER,
name=sync_status.name,
)
async def _async_setup(self) -> None:
assert self.config_entry is not None
preset = await self.player.presets()
inputs = await self.player.inputs()
status = await self.player.status()
self.async_set_updated_data(
BluesoundData(
sync_status=self._inital_sync_status,
status=status,
presets=preset,
inputs=inputs,
)
)
status_loop_task = self.hass.async_create_background_task(
self._poll_status_loop(),
name=f"bluesound.poll_status_loop_{self.data.sync_status.id}",
)
self.config_entry.async_on_unload(cancel_task(status_loop_task))
sync_status_loop_task = self.hass.async_create_background_task(
self._poll_sync_status_loop(),
name=f"bluesound.poll_sync_status_loop_{self.data.sync_status.id}",
)
self.config_entry.async_on_unload(cancel_task(sync_status_loop_task))
presets_and_inputs_loop_task = self.hass.async_create_background_task(
self._poll_presets_and_inputs_loop(),
name=f"bluesound.poll_presets_and_inputs_loop_{self.data.sync_status.id}",
)
self.config_entry.async_on_unload(cancel_task(presets_and_inputs_loop_task))
async def _async_update_data(self) -> BluesoundData:
return self.data
async def _poll_presets_and_inputs_loop(self) -> None:
while True:
await asyncio.sleep(PRESET_AND_INPUTS_INTERVAL.total_seconds())
try:
preset = await self.player.presets()
inputs = await self.player.inputs()
self.async_set_updated_data(
replace(
self.data,
presets=preset,
inputs=inputs,
)
)
except PlayerUnreachableError as ex:
self.async_set_update_error(ex)
except asyncio.CancelledError:
return
except Exception as ex: # noqa: BLE001 - this loop should never stop
self.async_set_update_error(ex)
async def _poll_status_loop(self) -> None:
"""Loop which polls the status of the player."""
while True:
try:
status = await self.player.status(
etag=self.data.status.etag, poll_timeout=120, timeout=125
)
self.async_set_updated_data(
replace(
self.data,
status=status,
)
)
except PlayerUnreachableError as ex:
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
except asyncio.CancelledError:
return
except Exception as ex: # noqa: BLE001 - this loop should never stop
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
async def _poll_sync_status_loop(self) -> None:
"""Loop which polls the sync status of the player."""
while True:
try:
sync_status = await self.player.sync_status(
etag=self.data.sync_status.etag, poll_timeout=120, timeout=125
)
self.async_set_updated_data(
replace(
self.data,
sync_status=sync_status,
)
)
except PlayerUnreachableError as ex:
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
except asyncio.CancelledError:
raise
except Exception as ex: # noqa: BLE001 - this loop should never stop
self.async_set_update_error(ex)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT.total_seconds())
@@ -2,15 +2,12 @@
from __future__ import annotations
import asyncio
from asyncio import CancelledError, Task
from contextlib import suppress
from asyncio import Task
from datetime import datetime, timedelta
import logging
from typing import TYPE_CHECKING, Any
from pyblu import Input, Player, Preset, Status, SyncStatus
from pyblu.errors import PlayerUnreachableError
import voluptuous as vol
from homeassistant.components import media_source
@@ -23,7 +20,7 @@ from homeassistant.components.media_player import (
async_process_play_media_url,
)
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.device_registry import (
@@ -36,9 +33,11 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
)
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
import homeassistant.util.dt as dt_util
from .const import ATTR_BLUESOUND_GROUP, ATTR_MASTER, DOMAIN
from .coordinator import BluesoundCoordinator
from .utils import dispatcher_join_signal, dispatcher_unjoin_signal, format_unique_id
if TYPE_CHECKING:
@@ -56,11 +55,6 @@ SERVICE_JOIN = "join"
SERVICE_SET_TIMER = "set_sleep_timer"
SERVICE_UNJOIN = "unjoin"
NODE_OFFLINE_CHECK_TIMEOUT = 180
NODE_RETRY_INITIATION = timedelta(minutes=3)
SYNC_STATUS_INTERVAL = timedelta(minutes=5)
POLL_TIMEOUT = 120
@@ -71,10 +65,10 @@ async def async_setup_entry(
) -> None:
"""Set up the Bluesound entry."""
bluesound_player = BluesoundPlayer(
config_entry.runtime_data.coordinator,
config_entry.data[CONF_HOST],
config_entry.data[CONF_PORT],
config_entry.runtime_data.player,
config_entry.runtime_data.sync_status,
)
platform = entity_platform.async_get_current_platform()
@@ -89,11 +83,10 @@ async def async_setup_entry(
)
platform.async_register_entity_service(SERVICE_UNJOIN, None, "async_unjoin")
hass.data[DATA_BLUESOUND].append(bluesound_player)
async_add_entities([bluesound_player], update_before_add=True)
class BluesoundPlayer(MediaPlayerEntity):
class BluesoundPlayer(CoordinatorEntity[BluesoundCoordinator], MediaPlayerEntity):
"""Representation of a Bluesound Player."""
_attr_media_content_type = MediaType.MUSIC
@@ -102,12 +95,15 @@ class BluesoundPlayer(MediaPlayerEntity):
def __init__(
self,
coordinator: BluesoundCoordinator,
host: str,
port: int,
player: Player,
sync_status: SyncStatus,
) -> None:
"""Initialize the media player."""
super().__init__(coordinator)
sync_status = coordinator.data.sync_status
self.host = host
self.port = port
self._poll_status_loop_task: Task[None] | None = None
@@ -115,15 +111,14 @@ class BluesoundPlayer(MediaPlayerEntity):
self._id = sync_status.id
self._last_status_update: datetime | None = None
self._sync_status = sync_status
self._status: Status | None = None
self._inputs: list[Input] = []
self._presets: list[Preset] = []
self._status: Status = coordinator.data.status
self._inputs: list[Input] = coordinator.data.inputs
self._presets: list[Preset] = coordinator.data.presets
self._group_name: str | None = None
self._group_list: list[str] = []
self._bluesound_device_name = sync_status.name
self._player = player
self._is_leader = False
self._leader: BluesoundPlayer | None = None
self._last_status_update = dt_util.utcnow()
self._attr_unique_id = format_unique_id(sync_status.mac, port)
# there should always be one player with the default port per mac
@@ -146,52 +141,10 @@ class BluesoundPlayer(MediaPlayerEntity):
via_device=(DOMAIN, format_mac(sync_status.mac)),
)
async def _poll_status_loop(self) -> None:
"""Loop which polls the status of the player."""
while True:
try:
await self.async_update_status()
except PlayerUnreachableError:
_LOGGER.error(
"Node %s:%s is offline, retrying later", self.host, self.port
)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
except CancelledError:
_LOGGER.debug(
"Stopping the polling of node %s:%s", self.host, self.port
)
return
except: # noqa: E722 - this loop should never stop
_LOGGER.exception(
"Unexpected error for %s:%s, retrying later", self.host, self.port
)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
async def _poll_sync_status_loop(self) -> None:
"""Loop which polls the sync status of the player."""
while True:
try:
await self.update_sync_status()
except PlayerUnreachableError:
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
except CancelledError:
raise
except: # noqa: E722 - all errors must be caught for this loop
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
async def async_added_to_hass(self) -> None:
"""Start the polling task."""
await super().async_added_to_hass()
self._poll_status_loop_task = self.hass.async_create_background_task(
self._poll_status_loop(),
name=f"bluesound.poll_status_loop_{self.host}:{self.port}",
)
self._poll_sync_status_loop_task = self.hass.async_create_background_task(
self._poll_sync_status_loop(),
name=f"bluesound.poll_sync_status_loop_{self.host}:{self.port}",
)
assert self._sync_status.id is not None
self.async_on_remove(
async_dispatcher_connect(
@@ -212,105 +165,24 @@ class BluesoundPlayer(MediaPlayerEntity):
"""Stop the polling task."""
await super().async_will_remove_from_hass()
assert self._poll_status_loop_task is not None
if self._poll_status_loop_task.cancel():
# the sleeps in _poll_loop will raise CancelledError
with suppress(CancelledError):
await self._poll_status_loop_task
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._sync_status = self.coordinator.data.sync_status
self._status = self.coordinator.data.status
self._inputs = self.coordinator.data.inputs
self._presets = self.coordinator.data.presets
assert self._poll_sync_status_loop_task is not None
if self._poll_sync_status_loop_task.cancel():
# the sleeps in _poll_sync_status_loop will raise CancelledError
with suppress(CancelledError):
await self._poll_sync_status_loop_task
self.hass.data[DATA_BLUESOUND].remove(self)
async def async_update(self) -> None:
"""Update internal status of the entity."""
if not self.available:
return
with suppress(PlayerUnreachableError):
await self.async_update_presets()
await self.async_update_captures()
async def async_update_status(self) -> None:
"""Use the poll session to always get the status of the player."""
etag = None
if self._status is not None:
etag = self._status.etag
try:
status = await self._player.status(
etag=etag, poll_timeout=POLL_TIMEOUT, timeout=POLL_TIMEOUT + 5
)
self._attr_available = True
self._last_status_update = dt_util.utcnow()
self._status = status
self.async_write_ha_state()
except PlayerUnreachableError:
self._attr_available = False
self._last_status_update = None
self._status = None
self.async_write_ha_state()
_LOGGER.error(
"Client connection error, marking %s as offline",
self._bluesound_device_name,
)
raise
async def update_sync_status(self) -> None:
"""Update the internal status."""
etag = None
if self._sync_status:
etag = self._sync_status.etag
sync_status = await self._player.sync_status(
etag=etag, poll_timeout=POLL_TIMEOUT, timeout=POLL_TIMEOUT + 5
)
self._sync_status = sync_status
self._last_status_update = dt_util.utcnow()
self._group_list = self.rebuild_bluesound_group()
if sync_status.leader is not None:
self._is_leader = False
leader_id = f"{sync_status.leader.ip}:{sync_status.leader.port}"
leader_device = [
device
for device in self.hass.data[DATA_BLUESOUND]
if device.id == leader_id
]
if leader_device and leader_id != self.id:
self._leader = leader_device[0]
else:
self._leader = None
_LOGGER.error("Leader not found %s", leader_id)
else:
if self._leader is not None:
self._leader = None
followers = self._sync_status.followers
self._is_leader = followers is not None
self.async_write_ha_state()
async def async_update_captures(self) -> None:
"""Update Capture sources."""
inputs = await self._player.inputs()
self._inputs = inputs
async def async_update_presets(self) -> None:
"""Update Presets."""
presets = await self._player.presets()
self._presets = presets
@property
def state(self) -> MediaPlayerState:
"""Return the state of the device."""
if self._status is None:
if self.available is False:
return MediaPlayerState.OFF
if self.is_grouped and not self.is_leader:
@@ -327,7 +199,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_title(self) -> str | None:
"""Title of current playing media."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
return self._status.name
@@ -335,7 +207,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_artist(self) -> str | None:
"""Artist of current playing media (Music track only)."""
if self._status is None:
if self.available is False:
return None
if self.is_grouped and not self.is_leader:
@@ -346,7 +218,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_album_name(self) -> str | None:
"""Artist of current playing media (Music track only)."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
return self._status.album
@@ -354,7 +226,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_image_url(self) -> str | None:
"""Image url of current playing media."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
url = self._status.image
@@ -369,7 +241,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_position(self) -> int | None:
"""Position of current playing media in seconds."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
mediastate = self.state
@@ -388,7 +260,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def media_duration(self) -> int | None:
"""Duration of current playing media in seconds."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
duration = self._status.total_seconds
@@ -405,16 +277,11 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def volume_level(self) -> float | None:
"""Volume level of the media player (0..1)."""
volume = None
volume = self._status.volume
if self._status is not None:
volume = self._status.volume
if self.is_grouped:
volume = self._sync_status.volume
if volume is None:
return None
return volume / 100
@property
@@ -447,7 +314,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def source_list(self) -> list[str] | None:
"""List of available input sources."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
sources = [x.text for x in self._inputs]
@@ -458,7 +325,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def source(self) -> str | None:
"""Name of the current input source."""
if self._status is None or (self.is_grouped and not self.is_leader):
if self.available is False or (self.is_grouped and not self.is_leader):
return None
if self._status.input_id is not None:
@@ -475,7 +342,7 @@ class BluesoundPlayer(MediaPlayerEntity):
@property
def supported_features(self) -> MediaPlayerEntityFeature:
"""Flag of media commands that are supported."""
if self._status is None:
if self.available is False:
return MediaPlayerEntityFeature(0)
if self.is_grouped and not self.is_leader:
@@ -577,16 +444,21 @@ class BluesoundPlayer(MediaPlayerEntity):
if self.sync_status.leader is None and self.sync_status.followers is None:
return []
player_entities: list[BluesoundPlayer] = self.hass.data[DATA_BLUESOUND]
config_entries: list[BluesoundConfigEntry] = (
self.hass.config_entries.async_entries(DOMAIN)
)
sync_status_list = [
x.runtime_data.coordinator.data.sync_status for x in config_entries
]
leader_sync_status: SyncStatus | None = None
if self.sync_status.leader is None:
leader_sync_status = self.sync_status
else:
required_id = f"{self.sync_status.leader.ip}:{self.sync_status.leader.port}"
for x in player_entities:
if x.sync_status.id == required_id:
leader_sync_status = x.sync_status
for sync_status in sync_status_list:
if sync_status.id == required_id:
leader_sync_status = sync_status
break
if leader_sync_status is None or leader_sync_status.followers is None:
@@ -594,9 +466,9 @@ class BluesoundPlayer(MediaPlayerEntity):
follower_ids = [f"{x.ip}:{x.port}" for x in leader_sync_status.followers]
follower_names = [
x.sync_status.name
for x in player_entities
if x.sync_status.id in follower_ids
sync_status.name
for sync_status in sync_status_list
if sync_status.id in follower_ids
]
follower_names.insert(0, leader_sync_status.name)
return follower_names
+57 -18
View File
@@ -22,6 +22,7 @@ from bluetooth_adapters import (
adapter_model,
adapter_unique_name,
get_adapters,
get_manufacturer_from_mac,
)
from bluetooth_data_tools import monotonic_time_coarse as MONOTONIC_TIME
from habluetooth import (
@@ -51,7 +52,7 @@ from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.issue_registry import async_delete_issue
from homeassistant.loader import async_get_bluetooth
from . import passive_update_processor
from . import passive_update_processor, websocket_api
from .api import (
_get_manager,
async_address_present,
@@ -66,6 +67,7 @@ from .api import (
async_rediscover_address,
async_register_callback,
async_register_scanner,
async_remove_scanner,
async_scanner_by_source,
async_scanner_count,
async_scanner_devices_by_address,
@@ -77,6 +79,9 @@ from .const import (
CONF_ADAPTER,
CONF_DETAILS,
CONF_PASSIVE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DOMAIN,
CONF_SOURCE_MODEL,
DOMAIN,
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS,
@@ -92,9 +97,24 @@ if TYPE_CHECKING:
from homeassistant.helpers.typing import ConfigType
__all__ = [
"FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS",
"MONOTONIC_TIME",
"SOURCE_LOCAL",
"BaseHaRemoteScanner",
"BaseHaScanner",
"BluetoothCallback",
"BluetoothCallbackMatcher",
"BluetoothChange",
"BluetoothScannerDevice",
"BluetoothScanningMode",
"BluetoothServiceInfo",
"BluetoothServiceInfoBleak",
"HaBluetoothConnector",
"HomeAssistantRemoteScanner",
"async_address_present",
"async_ble_device_from_address",
"async_discovered_service_info",
"async_get_advertisement_callback",
"async_get_fallback_availability_interval",
"async_get_learned_advertising_interval",
"async_get_scanner",
@@ -103,26 +123,12 @@ __all__ = [
"async_rediscover_address",
"async_register_callback",
"async_register_scanner",
"async_set_fallback_availability_interval",
"async_track_unavailable",
"async_remove_scanner",
"async_scanner_by_source",
"async_scanner_count",
"async_scanner_devices_by_address",
"async_get_advertisement_callback",
"BaseHaScanner",
"HomeAssistantRemoteScanner",
"BluetoothCallbackMatcher",
"BluetoothChange",
"BluetoothServiceInfo",
"BluetoothServiceInfoBleak",
"BluetoothScanningMode",
"BluetoothCallback",
"BluetoothScannerDevice",
"HaBluetoothConnector",
"BaseHaRemoteScanner",
"SOURCE_LOCAL",
"FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS",
"MONOTONIC_TIME",
"async_set_fallback_availability_interval",
"async_track_unavailable",
]
_LOGGER = logging.getLogger(__name__)
@@ -232,6 +238,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
set_manager(manager)
await storage_setup_task
await manager.async_setup()
websocket_api.async_setup(hass)
hass.async_create_background_task(
_async_start_adapter_discovery(hass, manager, bluetooth_adapters),
@@ -312,6 +319,38 @@ async def async_update_device(
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry for a bluetooth scanner."""
if source_entry_id := entry.data.get(CONF_SOURCE_CONFIG_ENTRY_ID):
if not (source_entry := hass.config_entries.async_get_entry(source_entry_id)):
# Cleanup the orphaned entry using a call_soon to ensure
# we can return before the entry is removed
hass.loop.call_soon(
hass_callback(
lambda: hass.async_create_task(
hass.config_entries.async_remove(entry.entry_id),
"remove orphaned bluetooth entry {entry.entry_id}",
)
)
)
address = entry.unique_id
assert address is not None
assert source_entry is not None
source_domain = entry.data[CONF_SOURCE_DOMAIN]
if mac_manufacturer := await get_manufacturer_from_mac(address):
manufacturer = f"{mac_manufacturer} ({source_domain})"
else:
manufacturer = source_domain
details = AdapterDetails(
address=address,
product=entry.data.get(CONF_SOURCE_MODEL),
manufacturer=manufacturer,
)
await async_update_device(
hass,
entry,
source_entry.title,
details,
)
return True
manager = _get_manager(hass)
address = entry.unique_id
assert address is not None
@@ -132,7 +132,7 @@ class ActiveBluetoothDataUpdateCoordinator[_T](PassiveBluetoothDataUpdateCoordin
)
self.last_poll_successful = False
return
except Exception: # noqa: BLE001
except Exception:
if self.last_poll_successful:
self.logger.exception("%s: Failure while polling", self.address)
self.last_poll_successful = False
@@ -127,7 +127,7 @@ class ActiveBluetoothProcessorCoordinator[_DataT](
)
self.last_poll_successful = False
return
except Exception: # noqa: BLE001
except Exception:
if self.last_poll_successful:
self.logger.exception("%s: Failure while polling", self.address)
self.last_poll_successful = False
+12 -1
View File
@@ -178,9 +178,20 @@ def async_register_scanner(
hass: HomeAssistant,
scanner: BaseHaScanner,
connection_slots: int | None = None,
source_domain: str | None = None,
source_model: str | None = None,
source_config_entry_id: str | None = None,
) -> CALLBACK_TYPE:
"""Register a BleakScanner."""
return _get_manager(hass).async_register_scanner(scanner, connection_slots)
return _get_manager(hass).async_register_hass_scanner(
scanner, connection_slots, source_domain, source_model, source_config_entry_id
)
@hass_callback
def async_remove_scanner(hass: HomeAssistant, source: str) -> None:
"""Permanently remove a BleakScanner by source address."""
return _get_manager(hass).async_remove_scanner(source)
@hass_callback
@@ -18,7 +18,12 @@ from habluetooth import get_manager
import voluptuous as vol
from homeassistant.components import onboarding
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.core import callback
from homeassistant.helpers.schema_config_entry_flow import (
SchemaFlowFormStep,
@@ -26,7 +31,16 @@ from homeassistant.helpers.schema_config_entry_flow import (
)
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN
from .const import (
CONF_ADAPTER,
CONF_DETAILS,
CONF_PASSIVE,
CONF_SOURCE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DOMAIN,
CONF_SOURCE_MODEL,
DOMAIN,
)
from .util import adapter_title
OPTIONS_SCHEMA = vol.Schema(
@@ -63,6 +77,8 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
self, discovery_info: DiscoveryInfoType
) -> ConfigFlowResult:
"""Handle a flow initialized by discovery."""
if discovery_info and CONF_SOURCE in discovery_info:
return await self.async_step_external_scanner(discovery_info)
self._adapter = cast(str, discovery_info[CONF_ADAPTER])
self._details = cast(AdapterDetails, discovery_info[CONF_DETAILS])
await self.async_set_unique_id(self._details[ADAPTER_ADDRESS])
@@ -167,6 +183,24 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
),
)
async def async_step_external_scanner(
self, user_input: dict[str, Any]
) -> ConfigFlowResult:
"""Handle a flow initialized by an external scanner."""
source = user_input[CONF_SOURCE]
await self.async_set_unique_id(source)
data = {
CONF_SOURCE: source,
CONF_SOURCE_MODEL: user_input[CONF_SOURCE_MODEL],
CONF_SOURCE_DOMAIN: user_input[CONF_SOURCE_DOMAIN],
CONF_SOURCE_CONFIG_ENTRY_ID: user_input[CONF_SOURCE_CONFIG_ENTRY_ID],
}
self._abort_if_unique_id_configured(updates=data)
manager = get_manager()
scanner = manager.async_scanner_by_source(source)
assert scanner is not None
return self.async_create_entry(title=scanner.name, data=data)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -177,8 +211,10 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> SchemaOptionsFlowHandler:
) -> SchemaOptionsFlowHandler | RemoteAdapterOptionsFlowHandler:
"""Get the options flow for this handler."""
if CONF_SOURCE in config_entry.data:
return RemoteAdapterOptionsFlowHandler()
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
@classmethod
@@ -186,3 +222,13 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
"""Return options flow support for this handler."""
return bool((manager := get_manager()) and manager.supports_passive_scan)
class RemoteAdapterOptionsFlowHandler(OptionsFlow):
"""Handle a option flow for remote adapters."""
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle options flow."""
return self.async_abort(reason="remote_adapters_not_supported")

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