Compare commits

...

414 Commits

Author SHA1 Message Date
Franck Nijhof
9b10af612a 2023.12.1 (#105324) 2023-12-08 22:13:24 +01:00
Bouwe Westerdijk
47dc48ca66 Bump plugwise to v0.34.5 (#105330) 2023-12-08 21:20:02 +01:00
Jan Bouwhuis
5220afa856 Workaround to_json template filter in parsing dict key (#105327)
* Work-a-round orjson for `to_json` fiter in case dict key is str subclass

* Add option instead

* Remove json.dumps work-a-round

* Update homeassistant/helpers/template.py

* Fix test

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-12-08 21:19:59 +01:00
G Johansson
629731e2dd Add rollback on exception that needs rollback in SQL (#104948) 2023-12-08 21:19:56 +01:00
Franck Nijhof
9aaff618e2 Bump version to 2023.12.1 2023-12-08 19:08:47 +01:00
Erik Montnemery
35954128ad Add workaround for orjson not handling subclasses of str (#105314)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-12-08 19:08:02 +01:00
Michael Hansen
d9b31e9841 Use area id for context instead of name (#105313) 2023-12-08 19:07:58 +01:00
Erik Montnemery
1e3c154fdf Add test for energy cost sensor for late price sensor (#105312) 2023-12-08 19:07:24 +01:00
Paul Bottein
1777f6b935 Update frontend to 20231208.2 (#105299) 2023-12-08 19:06:25 +01:00
Øyvind Matheson Wergeland
c24af97514 Always set _attr_current_option in Nobø Hub select entities (#105289)
Always set _attr_current_option in select entities.
2023-12-08 19:06:08 +01:00
Jan Bouwhuis
892a7c36ca Fix mqtt json light state updates using deprecated color handling (#105283) 2023-12-08 19:06:03 +01:00
Michael Hansen
53cbde8dca Set device id and forward errors to Wyoming satellites (#105266)
* Set device id and forward errors

* Fix tests
2023-12-08 19:05:23 +01:00
J. Nick Koston
f3bb832b19 Bump pyunifiprotect to 4.22.0 (#105265) 2023-12-08 19:02:43 +01:00
On Freund
d89f6b5eb0 Fix update of uncategorized OurGroceries items (#105255)
* Fix update of uncategorized OurGroceries items

* Address code review comments
2023-12-08 19:02:40 +01:00
Duco Sebel
4953a36da8 Add migration for old HomeWizard sensors (#105251)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-12-08 19:02:37 +01:00
Jan-Philipp Benecke
38e01b248f Explicit check for None in Discovergy entity if condition (#105248)
Fix checking for None in Discovergy
2023-12-08 19:02:34 +01:00
ollo69
f8d9c4c3ad Fix AsusWrt invalid data type with tuple type (#105247) 2023-12-08 19:02:31 +01:00
G Johansson
3a10ea1892 Fix check_date service in workday (#105241)
* Fix check_date service in workday

* Add test
2023-12-08 19:02:28 +01:00
Lars
c6187ed914 Fix Fritzbox light setup (#105232) 2023-12-08 19:02:24 +01:00
Erik Montnemery
d679764d3b Disable config flow progress in peco config flow (#105222) 2023-12-08 19:02:21 +01:00
haimn
f1169e0a0d fix supportedFanOscillationModes is null (#105205)
* fix supportedFanOscillationModes is null

* set default supported_swings to None

* return None if no fan oscillation modes listed
2023-12-08 19:02:18 +01:00
Greg Dowling
119c9c3a6b Fix bug in roon incremental volume control. (#105201) 2023-12-08 19:02:15 +01:00
Jan-Philipp Benecke
c5d1a0fbe1 Increase ping update interval to 30 seconds (#105199) 2023-12-08 19:02:12 +01:00
Sebastian Nohn
8ffb147926 Set ping interval to 15 seconds instead of 5 minutes (#105191)
set ping interval to a more sane value of 15 seconds instead of 5 minutes. fixes home-assistant/core#105163
2023-12-08 19:02:04 +01:00
lunmay
688fab49c3 Fix missing apostrophe in smtp (#105189)
Fix missing apostrophe
2023-12-08 19:01:20 +01:00
TheJulianJES
c035ffb06e Fix ZHA quirk ID custom entities matching all devices (#105184) 2023-12-08 19:00:39 +01:00
Quentame
054ede9663 Bump Météo-France to 1.3.0 (#105170) 2023-12-08 19:00:36 +01:00
Michael Hansen
614e9069c2 Don't return TTS URL in Assist pipeline (#105164)
* Don't return TTS URL

* Add test for empty queue
2023-12-08 19:00:33 +01:00
starkillerOG
b832a692d9 Bump reolink_aio to 0.8.2 (#105157) 2023-12-08 19:00:30 +01:00
Robert Svensson
cfa85956e1 Improve LIDL christmas light detection in deCONZ (#105155) 2023-12-08 19:00:25 +01:00
Mick Vleeshouwer
a2f9ffe50f Disable scenarios (scenes) for local API in Overkiz (#105153) 2023-12-08 19:00:22 +01:00
Michael Hansen
47032d055c Expose todo entities to Assist by default (#105150) 2023-12-08 19:00:14 +01:00
Jan Bouwhuis
b977fd6ab2 Correct smtp error message string (#105148) 2023-12-08 18:59:36 +01:00
Jan Bouwhuis
3972d8fc00 Correct smtp error message string (#105148) 2023-12-08 18:58:00 +01:00
TJ Horner
53497e3fad Bump apple_weatherkit to 1.1.2 (#105140) 2023-12-08 18:57:16 +01:00
Matrix
24f0e927f3 Bump yolink-api to 0.3.4 (#105124)
* Bump yolink-api to 0.3.3

* bump yolink api to 0.3.4
2023-12-08 18:57:10 +01:00
Excentyl
9527548207 Initialize energy_state without price (#97031)
Co-authored-by: Erik <erik@montnemery.com>
2023-12-08 18:56:05 +01:00
Franck Nijhof
ea1222bff3 2023.12.0 (#105135) 2023-12-06 17:44:26 +01:00
Franck Nijhof
af23580530 Bump version to 2023.12.0 2023-12-06 16:31:24 +01:00
Paul Bottein
d8b056b340 Update frontend to 20231206.0 (#105132) 2023-12-06 16:31:06 +01:00
Franck Nijhof
0958e8fadf Fix missing target in todo.remove_completed_items service (#105127) 2023-12-06 16:30:59 +01:00
Xidorn Quan
e165d6741e Bump thermopro-ble to 0.5.0 (#105126) 2023-12-06 16:30:52 +01:00
Tobias Perschon
6b3e9904c8 Add missing services and strings entries for reply_to_message_id (#105072) 2023-12-06 16:27:06 +01:00
Franck Nijhof
9fcb722381 Bump version to 2023.12.0b5 2023-12-06 08:31:21 +01:00
Michael Hansen
da766bc7c5 Bump intents to 2023.12.05 (#105116) 2023-12-06 08:31:13 +01:00
lunmay
681a3fd271 Fix typo in v2c strings.json (#105104)
fo -> of
2023-12-06 08:31:10 +01:00
Robert Svensson
990fd31e84 Bump aiounifi to v67 (#105099)
* Bump aiounifi to v67

* Fix mypy
2023-12-06 08:31:06 +01:00
Jan Bouwhuis
f7c9d20472 Fix overkiz measurement sensor returns None if 0 (#105090) 2023-12-06 08:31:03 +01:00
TJ Horner
ae4811b776 Update apple_weatherkit to 1.1.1 (#105079) 2023-12-06 08:30:46 +01:00
Franck Nijhof
b0367d3d74 Bump version to 2023.12.0b4 2023-12-05 19:09:24 +01:00
Bram Kragten
30d529aab0 Update frontend to 20231205.0 (#105081) 2023-12-05 19:09:16 +01:00
Jan-Philipp Benecke
4018a28510 Remove device from known_devices upon import in ping device tracker (#105009)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-12-05 19:09:12 +01:00
Franck Nijhof
a076b7d992 Bump version to 2023.12.0b3 2023-12-05 15:57:40 +01:00
Erik Montnemery
55c686ad03 Don't use deprecated_class decorator on deprecated YAML classes (#105063) 2023-12-05 15:55:27 +01:00
Robert Svensson
7cb383146a Make UniFi WiFi clients numerical (#105032) 2023-12-05 15:55:23 +01:00
Robert Svensson
2f727d5fe1 Fix stuck clients in UniFi options (#105028) 2023-12-05 15:55:18 +01:00
Robert Svensson
65c8aa3249 Make unifi RX-/TX-sensors diagnostic entities (#105022) 2023-12-05 15:55:15 +01:00
Bartosz Dokurno
c62c002657 Update Todoist config flow URL (#104992) 2023-12-05 15:55:12 +01:00
Erik Montnemery
fd4a05fc7a Minor improvements of deprecation helper (#104980) 2023-12-05 15:55:08 +01:00
Marco
56e325a2b1 Fix Mikrotik rename from wifiwave2 to wifi for upcoming RouterOS 7.13 (#104966)
Co-authored-by: Marco98 <Marco98@users.noreply.github.com>
2023-12-05 15:55:05 +01:00
Aaron Godfrey
48cce1a854 Exclude Todoist sub-tasks for the todo platform (#104914) 2023-12-05 15:55:01 +01:00
Marcel van der Veldt
99401c60c7 Add Matter custom cluster sensors (Eve Energy Plug energy measurements) (#104830)
* Support for sensors from custom clusters in Matter

* lint

* no need to write state twice

* Add test for eve energy plug

* Update homeassistant/components/matter/entity.py

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

* adjust comment

* debounce extra poll timer

* use async_call_later helper

* Update homeassistant/components/matter/entity.py

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

* wip extend test

* Update test_sensor.py

* fix state class for sensors

* trigger (fake) event callback on all subscribers

* Update eve-energy-plug.json

* add test for additionally polled value

* adjust delay to 3 seconds

* Adjust subscribe_events to always use kwargs

* Update tests/components/matter/common.py

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

* Update test_sensor.py

* remove redundant code

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-12-05 15:54:58 +01:00
Michael Hansen
5a49e1dd5c Add Wyoming satellite (#104759)
* First draft of Wyoming satellite

* Set up homeassistant in tests

* Move satellite

* Add devices with binary sensor and select

* Add more events

* Add satellite enabled switch

* Fix mistake

* Only set up necessary platforms for satellites

* Lots of fixes

* Add tests

* Use config entry id as satellite id

* Initial satellite test

* Add satellite pipeline test

* More tests

* More satellite tests

* Only support single device per config entry

* Address comments

* Make a copy of platforms
2023-12-05 15:54:55 +01:00
Allen Porter
db6b804298 Add due date and description fields to Todoist To-do entity (#104655)
* Add Todoist Due date and description fields

* Update entity features with new names

* Make items into walrus

* Update due_datetime field

* Add additional tests for adding new fields to items

* Fix call args in todoist test
2023-12-05 15:54:52 +01:00
Allen Porter
655b067277 Add due date and description to Google Tasks (#104654)
* Add tests for config validation function

* Add Google Tasks due date and description

* Revert test timezone

* Update changes after upstream

* Update homeassistant/components/google_tasks/todo.py

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

* Add google tasks tests for creating

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-12-05 15:54:48 +01:00
GeoffAtHome
55bafc260d Fix geniushub smart plug state at start-up (#102110)
* Smart plug did state wrong at start-up

* Update docstring to reflect code
2023-12-05 15:54:44 +01:00
Franck Nijhof
ca147060d9 Bump version to 2023.12.0b2 2023-12-04 15:00:20 +01:00
c0ffeeca7
8fd2e6451a W-Z: add host field description (#104996) 2023-12-04 15:00:09 +01:00
Bram Kragten
df8f462370 Update frontend to 20231204.0 (#104990) 2023-12-04 15:00:06 +01:00
Jan Bouwhuis
64f7855b94 Raise on smtp notification if attachment is not allowed (#104981)
* Raise smtp notification if attachment not allowed

* Pass url as placeholder

* Use variable in err message

* Add allow_list as placeholder
2023-12-04 15:00:02 +01:00
Jan Bouwhuis
204cc20bc2 Do not allow smtp to access insecure files (#104972) 2023-12-04 14:59:59 +01:00
Richard Kroegel
63ed4b0769 Bump bimmer-connected to 0.14.6 (#104961)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-12-04 14:59:56 +01:00
starkillerOG
cd86318b4b Do not fail if Reolink ONVIF cannot be connected (#104947) 2023-12-04 14:59:53 +01:00
G Johansson
214f214122 Only raise issue if switch used in Logitech Harmony Hub (#104941) 2023-12-04 14:59:49 +01:00
Alex Thompson
b53b1ab614 Fix Lyric HVAC mode reset on temperature change (#104910)
* Fix Lyric HVAC mode reset on temperature change

* Reduce code duplication

* Revert additional bugfix

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

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2023-12-04 14:59:46 +01:00
G Johansson
f5fae54c32 Fix get_events name in calendar strings (#104902) 2023-12-04 14:59:43 +01:00
Jan Bouwhuis
e1142e2ad8 Fix dsmr zero reconnect interval option could crash HA (#104900)
* Fix dsmr zero interval option could crash HA

* No change change the options
2023-12-04 14:59:40 +01:00
Patrick Decat
380e71d1b2 Fix incompatible 'measurement' state and 'volume' device class warnings in Overkiz (#104896) 2023-12-04 14:59:36 +01:00
Jan Bouwhuis
cda7863a45 Link second Hue host field description (#104885) 2023-12-04 14:59:33 +01:00
c0ffeeca7
9181d655f9 U-V add host field description (#104872)
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
2023-12-04 14:59:30 +01:00
c0ffeeca7
555e413edb T-add host field description (#104871) 2023-12-04 14:59:26 +01:00
starkillerOG
39026e3b53 Reolink schedule update after firmware update (#104867) 2023-12-04 14:59:23 +01:00
c0ffeeca7
8fd9761e7d Solar-Log to Soundtouch: add host field description (#104863) 2023-12-04 14:59:20 +01:00
c0ffeeca7
0cf4c6e568 SamsungTV to Snapcast: add host field description (#104862) 2023-12-04 14:59:17 +01:00
c0ffeeca7
0dc157dc31 Reolink to Ruckus: add host field description (#104861)
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
2023-12-04 14:59:14 +01:00
c0ffeeca7
9827ba7e60 Radio Thermostat to Renson: add host field description (#104860) 2023-12-04 14:59:10 +01:00
c0ffeeca7
f194ffcd52 Ping to Qnap: add host field description (#104859) 2023-12-04 14:59:07 +01:00
c0ffeeca7
42982de223 Obihai to OpenGarage: add host field description (#104858)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2023-12-04 14:59:04 +01:00
c0ffeeca7
1d04fcc485 Nanoleaf to Nut: add host field description (#104857)
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
2023-12-04 14:59:01 +01:00
c0ffeeca7
1378abab35 Modern Forms to MyStrom: add host field description (#104856) 2023-12-04 14:58:58 +01:00
c0ffeeca7
78cf9f2a01 Lifx, Lutron: add host field description (#104855) 2023-12-04 14:58:55 +01:00
Michael
074bcc8adc Fix handling of unrecognized mimetypes in Synology DSM photos integration (#104848) 2023-12-04 14:58:52 +01:00
Jan Bouwhuis
d67d2d9566 Filter out zero readings for DSMR enery sensors (#104843) 2023-12-04 14:58:49 +01:00
Robert Hillis
262e59f293 Fix Harmony switch removal version (#104838) 2023-12-04 14:58:46 +01:00
Daniel Hjelseth Høyer
11db0ab1e1 Bump Mill library (#104836) 2023-12-04 14:58:43 +01:00
Erik Montnemery
367bbf5709 Use deprecated_class decorator in deprecated YAML loader classes (#104835) 2023-12-04 14:58:40 +01:00
Marcel van der Veldt
0d318da9aa Update Matter test fixtures to schema version 5 (#104829) 2023-12-04 14:58:37 +01:00
mkmer
7ea4e15ff2 Late review updates for Blink (#104755) 2023-12-04 14:58:32 +01:00
Eric Härtel
cc0326548e Add CB3 descriptor to ZHA manifest (#104071) 2023-12-04 14:51:43 +01:00
Franck Nijhof
93c8618f8a Bump version to 2023.12.0b1 2023-11-30 19:48:24 +01:00
Franck Nijhof
208622e8a7 Revert "Add Komfovent (#95722)" (#104819) 2023-11-30 19:48:08 +01:00
Erik Montnemery
45f79ee1ba Restore renamed yaml loader classes and warn when used (#104818) 2023-11-30 19:48:04 +01:00
Bram Kragten
7739f99233 Update frontend to 20231130.0 (#104816) 2023-11-30 19:48:01 +01:00
Maciej Bieniek
43e0ddc74e Address late review for the host field description in Shelly integration (#104815)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2023-11-30 19:47:57 +01:00
Mappenhei
7e012183da Add Humidity device class to LaCross humidity sensor (#104814) 2023-11-30 19:47:54 +01:00
Maciej Bieniek
7a36bdb052 Make Shelly Wall Display thermostat implementation compatible with firmware 1.2.5 (#104812) 2023-11-30 19:47:51 +01:00
Erik Montnemery
83d881459a Add NodeStrClass.__voluptuous_compile__ (#104808) 2023-11-30 19:47:47 +01:00
Marcel van der Veldt
9d53d6811a Bump python-matter-server to version 5.0.0 (#104805) 2023-11-30 19:47:44 +01:00
Charles Garwood
847fd4c653 Use .get for Fully Kiosk SSL settings in coordinator (#104801) 2023-11-30 19:47:40 +01:00
Michael
0eefc98b33 Fix runtime error in CalDAV (#104800) 2023-11-30 19:47:36 +01:00
Marcel van der Veldt
ea8a47d0e9 Fix device sync to Google Assistant if Matter integration is active (#104796)
* Only get Matter device info if device is an actual Matter device

* Return None if matter device does not exist

* lint

* fix test

* adjust google assistant test
2023-11-30 19:47:30 +01:00
c0ffeeca7
75d2ea9c57 KMtronic to LG Soundbar: add host field description (#104792) 2023-11-30 19:45:02 +01:00
c0ffeeca7
cf63cd33c5 iAlarm to Keenetic: add host field description (#104791)
Co-authored-by: Andrey Kupreychik <foxel@quickfox.ru>
2023-11-30 19:44:58 +01:00
c0ffeeca7
fd442fadf8 HLK to Hyperion: add host field description (#104789)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-30 19:44:52 +01:00
c0ffeeca7
62537aa63a Frontier to Glances: add host field description (#104787)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-30 19:44:48 +01:00
c0ffeeca7
d7de9c13fd Goalzero to HEOS: add host field description (#104786) 2023-11-30 19:44:45 +01:00
Erwin Douna
04b72953e6 Fix Fastdotcom no entity (#104785)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-11-30 19:44:41 +01:00
c0ffeeca7
ddba7d8ed8 Freebox to FRITZ!Box add host field description (#104784)
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
2023-11-30 19:44:38 +01:00
c0ffeeca7
40c7432e8a FiveM to Foscam: add host field description (#104782) 2023-11-30 19:44:35 +01:00
c0ffeeca7
e1504759fe Enphase to Evil: add host field description (#104779)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-30 19:44:31 +01:00
c0ffeeca7
b6b2cf194d Ecoforest to Emonitor: add host field description (#104778) 2023-11-30 19:44:28 +01:00
c0ffeeca7
c3566db339 Dremel to Duotecno: add host field description (#104776)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-30 19:44:25 +01:00
c0ffeeca7
4eec48de51 Deconz to DoorBird: add host field description (#104772)
* Deconz to DoorBird: add host field description

* Update homeassistant/components/deconz/strings.json

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>

---------

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
2023-11-30 19:44:21 +01:00
c0ffeeca7
fe544f670f Comelit, Coolmaster: add host field description (#104771) 2023-11-30 19:44:18 +01:00
c0ffeeca7
816e524457 Broadlink, BSB-Lan: add host field description (#104770) 2023-11-30 19:44:15 +01:00
Allen Porter
4b22551af1 Fix bug in rainbird device ids that are int serial numbers (#104768) 2023-11-30 19:44:12 +01:00
Jan Bouwhuis
b4907800a9 Debug level logging for DSMR migration code (#104757) 2023-11-30 19:44:08 +01:00
Daniel Gangl
f366b37c52 Bump zamg to 0.3.3 (#104756) 2023-11-30 19:44:05 +01:00
Michael Hansen
90bcad31b5 Skip TTS when text is empty (#104741)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-11-30 19:44:01 +01:00
Franck Nijhof
34c65749e2 Revert "Add proj dependency to our wheels builder (#104699)" (#104704) 2023-11-30 19:43:58 +01:00
Christopher Fenner
5f549649de Update initial translation for ViCare water heater entity (#104696) 2023-11-30 19:43:55 +01:00
c0ffeeca7
78f1c0cb80 Axis: add host and user name field description (#104693) 2023-11-30 19:43:51 +01:00
Charles Garwood
6f45fafc11 Bump pynws to 1.6.0 (#104679) 2023-11-30 19:43:48 +01:00
Florian
4acea82ca1 Fix Philips TV none recordings_list (#104665)
Correct for missing recordings list in api client.
---------

Co-authored-by: Joakim Plate <elupus@ecce.se>
2023-11-30 19:43:44 +01:00
Sergiy Maysak
f1e8c1c7ee Fix wirelesstag unique_id to use uuid instead of tag_id (#104394)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-11-30 19:43:40 +01:00
Franck Nijhof
19f543214f Bump version to 2023.12.0b0 2023-11-29 20:40:14 +01:00
Allen Porter
af2f8699b7 Add due date and description to CalDAV To-do (#104656)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2023-11-29 19:35:36 +01:00
J. Nick Koston
1522118453 Remove aiohttp cancel on disconnect workaround (#104175) 2023-11-29 11:20:36 -07:00
J. Nick Koston
50f2c41145 Avoid db hit and executor job for impossible history queries (#104724) 2023-11-29 11:20:22 -07:00
Joost Lekkerkerker
1fefa93648 Use config entry callbacks in Gree (#104740) 2023-11-29 19:03:58 +01:00
Michael Hansen
e10d58ef3e Bump intents to 2023.11.29 (#104738) 2023-11-29 18:52:27 +01:00
J. Nick Koston
1b048ff388 Remove HomeAssistantAccessLogger (#104173) 2023-11-29 10:40:19 -07:00
Duco Sebel
1727c19e0d Address review comments for Picnic (#104732) 2023-11-29 18:35:55 +01:00
Paul Bottein
38eda9f46e Add multiple option to text selector (#104635)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-11-29 18:32:32 +01:00
J. Nick Koston
dfed10420c Remove aiohttp enable_compression helper (#104174) 2023-11-29 10:24:34 -07:00
Richard Kroegel
2287c45afc Bump bimmer-connected to 0.14.5 (#104715)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-11-29 18:11:04 +01:00
Michael Hansen
a894146cee Fix TTS streaming for VoIP (#104620)
* Use wav instead of raw tts audio in voip

* More tests

* Use mock TTS dir
2023-11-29 12:07:22 -05:00
Jan Bouwhuis
47426a3ddc Remove redundant websocket_api exception handler (#104727) 2023-11-29 16:56:26 +01:00
Jan Bouwhuis
4bf88b1690 Improve MQTT json light brightness scaling (#104510)
* Improve MQTT json light brightness scaling

* Revert unrelated changes

* Format
2023-11-29 16:42:10 +01:00
Joost Lekkerkerker
b36ddaa15c Change super class order in Withings Calendar (#104721) 2023-11-29 16:30:23 +01:00
Jan Bouwhuis
82264a0d6b Fix mqtt cover state is open after receiving stopped payload (#104726) 2023-11-29 16:29:42 +01:00
Bram Kragten
4628b03677 Update frontend to 20231129.1 (#104723) 2023-11-29 16:29:20 +01:00
Jan-Philipp Benecke
e2bab699b5 Avoid double refresh when adding entities in bsblan (#104647) 2023-11-29 16:24:30 +01:00
J. Nick Koston
608f4f7c52 Bump aiohttp to 3.9.1 (#104176) 2023-11-29 16:13:54 +01:00
dupondje
ba481001c3 Add support for multiple mbus devices in dsmr (#84097)
* Add support for multiple mbus devices in dsmr

A dsmr meter can have 4 mbus devices.
Support them all and also add support for a water meter on the mbus
device.

* Apply suggestions from code review

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

* Rewrite old gas sensor to new mbus sensor

* No force updates + fix mbus entity unique_id

* Remove old gas device

* Add additional tests

* Fix remarks from last review + move migrated 5b gas meter to new device_id

* Fix ruff error

* Last fixes

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2023-11-29 15:41:58 +01:00
Joost Lekkerkerker
36eb858d0a Rename variable in Epson tests (#104722) 2023-11-29 15:22:21 +01:00
Stefan Rado
c6c8bb6970 Bump aioesphomeapi to 19.2.1 (#104703) 2023-11-29 15:20:40 +01:00
Erik Montnemery
61d82ae9ab Tweak dockerfile generation (#104717) 2023-11-29 15:20:21 +01:00
Franck Nijhof
8f2e69fdb7 Revert "Update stookwijzer api to atlas leefomgeving (#103323)" (#104705) 2023-11-29 15:18:25 +01:00
Martin Hjelmare
e884933dbd Remove rest api service call timeout (#104709) 2023-11-29 14:46:19 +01:00
stegm
09d7679818 Add new sensors of Kostal Plenticore integration (#103802) 2023-11-29 14:24:09 +01:00
Marc Mueller
0a13968209 Improve devialet coordinator typing (#104707) 2023-11-29 14:17:15 +01:00
Jan Bouwhuis
953a212dd6 Use ServiceValidationError for invalid fan preset_mode and move check to fan entity component (#104560)
* Use ServiceValidationError for fan preset_mode

* Use _valid_preset_mode_or_raise to raise

* Move preset_mode validation to entity component

* Fix bond fan and comments

* Fixes baf, fjaraskupan and template

* More integration adjustments

* Add custom components mock and test code

* Make NotValidPresetModeError subclass

* Update homeassistant/components/fan/strings.json

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

* Keep bond has_action validation

* Move demo test asserts outside context block

* Follow up comment

* Update homeassistant/components/fan/strings.json

Co-authored-by: G Johansson <goran.johansson@shiftit.se>

* Fix demo tests

* Remove pylint disable

* Remove unreachable code

* Update homeassistant/components/fan/__init__.py

Co-authored-by: G Johansson <goran.johansson@shiftit.se>

* Use NotValidPresetModeError, Final methods

* Address comments

* Correct docst

* Follow up comments

* Update homeassistant/components/fan/__init__.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-11-29 13:56:51 +01:00
Bram Kragten
49381cefa3 Update frontend to 20231129.0 (#104710) 2023-11-29 13:37:23 +01:00
G Johansson
e5a7446afe Use id as location data in Trafikverket Camera (#104473) 2023-11-29 13:35:32 +01:00
Franck Nijhof
cf23de1c48 Add proj-dev dependency to our wheels builder (#104711) 2023-11-29 13:15:37 +01:00
Martin Hjelmare
5f44dadb66 Rename todo due_date_time parameter to due_datetime (#104698)
* Rename todo due_date_time parameter to due_datetime

* Apply suggestions from code review

---------

Co-authored-by: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com>
2023-11-29 13:11:15 +01:00
Jan Bouwhuis
861bb48ab6 Assign specific error code for HomeAssistantError on websocket_api connection exceptions (#104700)
Assign specific error code for HomeAssistantError
2023-11-29 13:07:52 +01:00
Franck Nijhof
9741380cc0 Add proj-util dependency to our wheels builder (#104708) 2023-11-29 12:41:47 +01:00
Simone Chemelli
fc7b17d35b Handle server disconnection for Comelit devices (#104583) 2023-11-29 12:33:25 +01:00
schelv
31cab5803c Add Option For Kelvin Unit To Color Temperature Selector (#103799) 2023-11-29 12:25:06 +01:00
Stefan Rado
d9c0acc1d2 Partially revert #103807: Remove deprecated aux heat support from ESPHome climate entities (#104694) 2023-11-29 04:45:15 -06:00
Franck Nijhof
6dc818b682 Add proj dependency to our wheels builder (#104699) 2023-11-29 11:38:23 +01:00
puddly
bd8f01bd35 Bump ZHA dependencies (#104335) 2023-11-29 11:30:15 +01:00
Erik Montnemery
999875d0e4 Autogenerate Dockerfile (#104669) 2023-11-29 11:26:50 +01:00
c0ffeeca7
bcfb5307f5 Balboa, Bond, Bosch: add host field description (#104695) 2023-11-29 11:06:50 +01:00
Jan Bouwhuis
efd330f182 Send localization info on websocket_api script errors (#104638)
* Send localization info on script errors

* Use connection exception hander

* Keep HomeAssistantError is unknown_error

* Move specific exception handling
2023-11-29 10:47:23 +01:00
Franck Nijhof
7dbaf40f48 Merge branch 'master' into dev 2023-11-29 10:13:51 +01:00
Ville Skyttä
afc3f1d933 Make huawei_lte operator search and preferred network modes translatable (#104673) 2023-11-29 10:07:54 +01:00
c0ffeeca7
634785a2d8 Atag: add host field description (#104691) 2023-11-29 10:02:49 +01:00
Sebastian YEPES
a3bad54583 Add Tuya Smart Water Timer (#95053)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-29 09:48:45 +01:00
Simone Chemelli
6a87876729 Handle server disconnection for Vodafone devices (#104650) 2023-11-29 09:39:30 +01:00
Maciej Bieniek
8c56b5ef82 Add a host field description for Bravia, Brother and NAM (#104689) 2023-11-29 09:35:38 +01:00
c0ffeeca7
4d00767081 ASUSWRT: add description of host field. Fix title (#104690)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-29 09:35:08 +01:00
Christopher Fenner
c4e3ae84f4 Optimize async executor use in ViCare integration (#104645)
* use one async executor

* use list comprehension

* simplify

* simplify

* simplify

* simplify

* simplify

* simplify

* simplify

* simplify

* add type

* Apply suggestions from code review

* fix ruff findings
2023-11-29 09:28:56 +01:00
TheJulianJES
2663a4d617 Bump zha-quirks to 0.0.107 (#104683) 2023-11-29 09:19:02 +01:00
Christopher Fenner
5dc64dd6b9 Fix HA state update in ViCare number platform (#104687)
use sync update fn
2023-11-29 09:16:58 +01:00
c0ffeeca7
8e8e8077a0 Agent DVR and Android IP webcam: Add description of host field (#104688) 2023-11-29 09:13:35 +01:00
IceBotYT
526180a8af Add PECO smart meter binary_sensor (#71034)
* Add support for PECO smart meter

* Add support for PECO smart meter

* Conform to black

* Fix tests and additional clean-up

* Return init file to original state

* Move to FlowResultType

* Catch up to upstream

* Remove commented code

* isort

* Merge smart meter and outage count into one entry

* Test coverage

* Remove logging exceptions from config flow verification

* Fix comments from @emontnemery

* Revert "Add support for PECO smart meter"

This reverts commit 36ca90856684f328e71bc3778fa7aa52a6bde5ca.

* More fixes
2023-11-29 09:08:27 +01:00
Maciej Bieniek
3aa9066a50 Add field description for Shelly host (#104686) 2023-11-29 08:45:47 +01:00
c0ffeeca7
4b667cff26 Host field description: implement review from #104658 (#104685) 2023-11-29 08:44:28 +01:00
Renat Sibgatulin
68722ce662 Bump aioairq to 0.3.1 (#104659) 2023-11-29 08:43:48 +01:00
Jan-Philipp Benecke
a9a95ad881 Revert "Introduce base entity for ping" (#104682) 2023-11-29 08:42:02 +01:00
Stefan Rado
017d05c03e Add humidity and aux heat support to ESPHome climate entities (#103807)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-11-29 17:57:30 +13:00
J. Nick Koston
3c25d95481 Bump aioesphomeapi to 19.2.0 (#104677) 2023-11-29 16:57:39 +13:00
J. Nick Koston
de3b608e78 Remove BLE connection state unsubscribe workaround from ESPHome (#104674)
aioesphomeapi now has explict coverage to ensure calling
the unsubscribe function after the connection drops is safe
and will not raise anymore
2023-11-28 23:38:00 +01:00
Alex Hermann
bdef0ba6e5 Significantly improve performance for some cases of the history start time state query (#99450)
* recorder: Apply filter in the outer query too

Function _get_start_time_state_for_entities_stmt() produced a query which
is dead-slow in my installation. On analysis, the outer query produced
millions of rows which had to be joined to the subquery. The subquery has
a filter which would eliminate almost all of the outer rows.

To speed up the query, apply the same filter to the outer query, so way
less rows have to be joined.

This reduced the query time on my system from more than half an hour to
mere milliseconds.

* lint

* merge filter

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2023-11-28 16:23:49 -06:00
Ville Skyttä
21d842cb58 Fix human readable huawei_lte sensor names (#104672)
Regression from 7c85d84133, #98631
2023-11-28 23:44:35 +02:00
Adrian Huber
2c196baa7a Add DeviceInfo to Wolf SmartSet Entities (#104642)
* Fix await warning

* Add DeviceInfo to Wolflink sensors

* Remove comment

* Don't pass device name to DeviceInfo

* Use _attr_device_info instead of property
2023-11-28 22:24:25 +01:00
J. Nick Koston
93aa31c835 Bump aioesphomeapi to 19.1.7 (#104644)
* Bump aioesphomeapi to 19.1.5

changelog: https://github.com/esphome/aioesphomeapi/compare/v19.1.4...v19.1.5

- Removes the need to watch for BLE connection drops with a seperate
  future as the library now raises BluetoothConnectionDroppedError when
  the connection drops during a BLE operation

* reduce stack

* .6

* tweak

* 19.1.7
2023-11-28 21:51:35 +01:00
Daniel Hjelseth Høyer
63ef9efa26 Bump pyAdax to 0.4.0 (#104660)
Updata Adax lib

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2023-11-28 21:22:48 +01:00
mkmer
595663778c Bump aiosomecomfort to 0.0.24 (#104649)
* Bump aiosomecomfort to 0.0.24

* PascalCase change
2023-11-28 19:34:30 +01:00
c0ffeeca7
9bdf82eb32 Add info what to enter into host field (#104658)
* Add info what to enter into host field

* Fix style
2023-11-28 19:32:11 +01:00
mkmer
56f2f17ed1 Bump aiosomecomfort to 0.0.23 (#104641) 2023-11-28 15:51:47 +01:00
glanch
7533895a3d Add tag name to tag_scanned event data (#97553)
* Add tag name to tag scanned event data

* Make name in event data optional, add test cases for events

* Simplify sanity None check of tag data

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

* Apply suggestions from code review

---------

Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-11-28 13:58:40 +01:00
fwestenberg
d3b04a5a58 Add Devialet integration (#86551)
* Add Devialet

* Bump Devialet==1.4.0

* Bump Devialet==1.4.1

* Sort manifest and add shorthand

* Black formatting

* Fix incompatible type

* Add type guarding for name

* Rename host keywork in tests

* Fix Devialet tests

* Add update coordinator

* Update devialet tests

* Create unique_id from entry data
2023-11-28 13:56:17 +01:00
Jan-Philipp Benecke
61a5c0de5e Use shorthand attributes in HVV departures (#104637)
* Use shorthand attributes in HVV departures

* Apply code review suggestion

Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com>

* Apply code review sugesstion

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

---------

Co-authored-by: Christopher Fenner <9592452+CFenner@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-11-28 13:44:40 +01:00
fwestenberg
9dc5d4a1bb Update stookwijzer api to atlas leefomgeving (#103323)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-11-28 13:23:51 +01:00
Allen Porter
b8cc3349be Add To-do due date and description fields (#104128)
* Add To-do due date and description fields

* Fix due date schema

* Revert devcontainer change

* Split due date and due date time

* Add tests for config validation function

* Add timezone converstion tests

* Add local todo due date/time and description implementation

* Revert configuration

* Revert test changes

* Add comments for the todo item field description

* Rename function _validate_supported_features

* Fix issues in items factory

* Readability improvements

* Apply suggestions from code review

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

* Rename CONF to ATTR usages

* Simplify local time validator

* Rename TodoListEntityFeature fields for setting extended fields

* Remove duplicate validations

* Update subscribe test

* Fix local_todo tests

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-11-28 13:01:12 +01:00
Franck Nijhof
ef89d1cd3d 2023.11.3 (#104348) 2023-11-22 12:51:36 +01:00
Allen Porter
9c4fd88a3d Bump ical to 6.1.0 (#103759) 2023-11-22 09:45:45 +01:00
Allen Porter
f5783cd3b5 Bump ical to 6.0.0 (#103482) 2023-11-22 09:45:40 +01:00
Franck Nijhof
1200ded24c Bumped version to 2023.11.3 2023-11-22 09:12:24 +01:00
Erik Montnemery
da992e9f45 Bump pychromecast to 13.0.8 (#104320) 2023-11-22 09:11:21 +01:00
Allen Porter
40326385ae Bump pyrainbird to 4.0.1 (#104293) 2023-11-22 09:11:17 +01:00
Richard Kroegel
da04c32893 Bump bimmer_connected to 0.14.3 (#104282)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-11-22 09:11:13 +01:00
Jan Bouwhuis
ae2ff926c1 Restore removed guard for non-string inputs in Alexa (#104263) 2023-11-22 09:11:10 +01:00
epenet
a5d48da07a Catch ClientOSError in renault integration (#104248) 2023-11-22 09:11:06 +01:00
Anton Tolchanov
669daabfdb Handle attributes set to None in prometheus (#104247)
Better handle attributes set to None
2023-11-22 09:11:02 +01:00
Jan Bouwhuis
b64ef24f20 Fix mqtt json light allows to set brightness value >255 (#104220) 2023-11-22 09:10:58 +01:00
Jan Bouwhuis
86beb9d135 Fix imap does not decode text body correctly (#104217) 2023-11-22 09:10:06 +01:00
Rene Nemec
64297aeb8f Increase Tomato request timeout (#104203)
* tomato integration timeout fixed

* update tests in tomato integration
2023-11-22 09:10:01 +01:00
mkmer
5650df5cfb Bump aiosomecomfort to 0.0.22 (#104202)
* Bump aiosomecomfort to 0.0.20

* Bump aiosomecomfort to 0.0.22
2023-11-22 09:09:56 +01:00
Allen Porter
83c59d4154 Fix Local To-do list bug renaming items (#104182)
* Fix Local To-do bug renaming items

* Fix renaming
2023-11-22 09:09:53 +01:00
Thomas Schamm
4680ac0cbf Bump boschshcpy to 0.2.75 (#104159)
Bumped to boschshcpy==0.2.75
2023-11-22 09:08:37 +01:00
J. Nick Koston
8b79d38497 Prevent Bluetooth reconnects from blocking shutdown (#104150) 2023-11-22 09:08:33 +01:00
J. Nick Koston
35b1051c67 Add debug logging for which adapter is used to connect bluetooth devices (#103264)
Log which adapter is used to connect bluetooth devices

This is a debug logging improvement to help users find problems
with their setup
2023-11-22 09:08:28 +01:00
J. Nick Koston
fcc7020946 Fix memory leak in ESPHome disconnect callbacks (#104149) 2023-11-22 08:35:58 +01:00
J. Nick Koston
d69d9863b5 Fix ESPHome BLE client raising confusing error when not connected (#104146) 2023-11-22 08:35:55 +01:00
Arie Catsman
885152df81 Bump pyenphase to 1.14.3 (#104101)
fix(101354):update pyenphase to 1.14.3
2023-11-22 08:35:51 +01:00
Martin Hjelmare
7ff1bdb098 Fix device tracker see gps accuracy selector (#104022) 2023-11-22 08:35:48 +01:00
deosrc
399299c13c Fix netatmo authentication when using cloud authentication credentials (#104021)
* Fix netatmo authentication loop

* Update unit tests

* Move logic to determine api scopes

* Add unit tests for new method

* Use pyatmo scope list (#1)

* Exclude scopes not working with cloud

* Fix linting error

---------

Co-authored-by: Tobias Sauerwein <cgtobi@users.noreply.github.com>
2023-11-22 08:35:44 +01:00
J. Nick Koston
c241c2f79c Fix emulated_hue with None values (#104020) 2023-11-22 08:35:40 +01:00
Martin Hjelmare
b010c6b793 Fix openexchangerates form data description (#103974) 2023-11-22 08:35:36 +01:00
Chuck Foster
2f380d4b75 Fix duplicate Ban file entries (#103953) 2023-11-22 08:35:33 +01:00
Matt Zimmerman
19f268a1e1 Update smarttub to 0.0.36 (#103948) 2023-11-22 08:35:29 +01:00
Raman Gupta
bcd371ac2b Bump zwave-js-server-python to 0.54.0 (#103943) 2023-11-22 08:35:25 +01:00
Tom Brien
a5a8d38d08 Fix Coinbase for new API Structure (#103930) 2023-11-22 08:35:22 +01:00
Simone Chemelli
56298b2c88 fix Comelit cover stop (#103911) 2023-11-22 08:35:18 +01:00
Allen Porter
cf35e9b154 Update Fitbit to avoid a KeyError when restingHeartRate is not present (#103872)
* Update Fitbit to avoid a KeyError when `restingHeartRate` is not present

* Explicitly handle none response values
2023-11-22 08:35:15 +01:00
Allen Porter
29a65d5620 Fix for Google Calendar API returning invalid RRULE:DATE rules (#103870) 2023-11-22 08:35:11 +01:00
Allen Porter
c352cf0bd8 Fix bug in Fitbit config flow, and switch to prefer display name (#103869) 2023-11-22 08:35:08 +01:00
Allen Porter
e89b47138d Bump gcal_sync to 6.0.1 (#103861) 2023-11-22 08:35:04 +01:00
suaveolent
339e9e7b48 Bump lupupy to 0.3.1 (#103835)
Co-authored-by: suaveolent <suaveolent@users.noreply.github.com>
2023-11-22 08:35:00 +01:00
J. Nick Koston
92780dd217 Bump pyunifiprotect to 4.21.0 (#103832)
changelog: https://github.com/AngellusMortis/pyunifiprotect/compare/v4.20.0...v4.21.0
2023-11-22 08:34:56 +01:00
Maikel Punie
6133ce0258 Bump velbusaio to 2023.11.0 (#103798) 2023-11-22 08:34:53 +01:00
Simone Chemelli
57c76b2ea3 Bump aiocomelit to 0.5.2 (#103791)
* Bump aoicomelit to 0.5.0

* bump to 0.5.2
2023-11-22 08:34:49 +01:00
Mick Vleeshouwer
149aef9a12 Bump pyOverkiz to 1.13.2 (#103790) 2023-11-22 08:34:45 +01:00
Mick Vleeshouwer
3dddf6b9f6 Bump pyOverkiz to 1.13.0 (#103582) 2023-11-22 08:34:41 +01:00
Allen Porter
2a26dea587 Fix Rainbird unique to use a more reliable source (mac address) (#101603)
* Fix rainbird unique id to use a mac address for new entries

* Fix typo

* Normalize mac address before using as unique id

* Apply suggestions from code review

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

* Update test check and remove dead code

* Update all config entries to the new format

* Update config entry tests for migration

* Fix rainbird entity unique ids

* Add test coverage for repair failure

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* Remove unnecessary migration failure checks

* Remove invalid config entries

* Update entry when entering a different hostname for an existing host.

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-11-22 08:34:36 +01:00
Jan Rieger
31ac03fe50 Fix typo in calendar translation (#103789) 2023-11-22 08:31:14 +01:00
Marcel van der Veldt
fb1dfb016e Fix race condition in Matter unsubscribe method (#103770) 2023-11-22 08:31:10 +01:00
Jan Bouwhuis
8a152a68d8 Fix raising vol.Invalid during mqtt config validation instead of ValueError (#103764) 2023-11-22 08:31:06 +01:00
Marcel van der Veldt
df3e49b24f Fix discovery schema for Matter switches (#103762)
* Fix discovery schema for matter switches

* fix typo in function that generates device name

* add test for switchunit
2023-11-22 08:31:02 +01:00
G-Two
db604170ba Bump subarulink to 0.7.9 (#103761) 2023-11-22 08:30:58 +01:00
J. Nick Koston
d8a6d3e1bc Bump python-matter-server to 4.0.2 (#103760)
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
2023-11-22 08:30:54 +01:00
Maciej Bieniek
6f086a27d4 Bump accuweather to version 2.1.0 (#103744) 2023-11-22 08:30:51 +01:00
Joost Lekkerkerker
3993c14f1d Lock Withings token refresh (#103688)
Lock Withings refresh
2023-11-22 08:30:44 +01:00
Allen Porter
d63d7841c3 Remove rainbird yaml config test fixtures (#103607) 2023-11-22 08:30:38 +01:00
Maciej Bieniek
e555671765 Bump accuweather to version 2.0.1 (#103532) 2023-11-22 08:30:33 +01:00
Franck Nijhof
a3319262ac 2023.11.2 (#103737) 2023-11-10 12:41:51 +01:00
Franck Nijhof
eaf711335d Bumped version to 2023.11.2 2023-11-10 10:04:50 +01:00
Bram Kragten
f120558750 Update frontend to 20231030.2 (#103706) 2023-11-10 10:04:33 +01:00
Joost Lekkerkerker
30dc05cdd7 Add name to Withings coordinator (#103692) 2023-11-10 10:04:29 +01:00
J. Nick Koston
8ce746972f Incease tplink setup timeout (#103671)
It can take longer than 5s to do the first update of the device
especially when the device is overloaded as seen in #103668

Wait 10 seconds for the discovery since when the power strips are loaded they cannot respond in time
2023-11-10 10:04:26 +01:00
starkillerOG
f946ed9e16 Fix Reolink DHCP IP update (#103654) 2023-11-10 10:04:23 +01:00
Joost Lekkerkerker
0ffc1bae76 Bump yt-dlp to 2023.10.13 (#103616) 2023-11-10 10:04:20 +01:00
Joost Lekkerkerker
d1a3a5895b Raise exception when data can't be fetched in Opensky (#103596) 2023-11-10 10:04:17 +01:00
suaveolent
f9c70fd3c8 fix: get_devices only checks for the first type (#103583) 2023-11-10 10:04:14 +01:00
dupondje
70f0ee81c9 Update dsmr-parser to 1.3.1 to fix parsing issues (#103572) 2023-11-10 10:04:10 +01:00
Charles Garwood
95d4254074 Bump pyenphase to 1.14.2 (#103553) 2023-11-10 10:04:07 +01:00
J. Nick Koston
c8d3e377f0 Bump aioesphomeapi to 18.2.4 (#103552) 2023-11-10 10:04:04 +01:00
epenet
da1c282c1b Fix invalid MAC in samsungtv (#103512)
* Fix invalid MAC in samsungtv

* Also adjust __init__
2023-11-10 10:04:01 +01:00
dupondje
35c0c9958d Fix 5B Gas meter in dsmr (#103506)
* Fix 5B Gas meter in dsmr

In commit 1b73219 the gas meter broke for 5B.
As the change can't be reverted easily without removing the peak usage
sensors, we implement a workaround.

The first MBUS_METER_READING2 value will contain the gas meter data just
like the previous BELGIUM_5MIN_GAS_METER_READING did.
But this without the need to touch dsmr_parser (version).

Fixes: #103306, #103293

* Use parametrize

* Apply suggestions from code review

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

* Add additional tests + typo fix

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2023-11-10 10:03:57 +01:00
mkmer
93a0bd351a Bump blinkpy to 0.22.3 (#103438) 2023-11-10 10:00:11 +01:00
J. Nick Koston
dbdd9d74cf Bump bluetooth-data-tools to 0.14.0 (#103413)
changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v1.13.0...v1.14.0
2023-11-10 10:00:08 +01:00
J. Nick Koston
3cac87cf30 Bump aioesphomeapi to 18.2.1 (#103156) 2023-11-10 10:00:05 +01:00
Jesse Hills
d019045199 ESPHome: Add suggested_area from device info (#102834) 2023-11-10 10:00:00 +01:00
Robert Resch
8f684ab102 Revert binary_sensor part of #103210 (#103499) 2023-11-06 17:56:53 +01:00
Matt Zimmerman
c17def27fc Fix litterrobot test failure due to time zone dependence (#103444)
* fix litterrobot test

* use a date in northern hemisehpere summer
2023-11-06 17:03:07 +01:00
dupondje
27d8d1011e Use right equipment identifier in DSMR setup (#103494) 2023-11-06 14:51:33 +01:00
Joost Lekkerkerker
e2270a305d Sort Withings sleep data on end date (#103454)
* Sort Withings sleep data on end date

* Sort Withings sleep data on end date
2023-11-06 14:51:30 +01:00
Matthias Alphart
6fd8973a00 Fix KNX expose default value when attribute is None (#103446)
Fix KNX expose default value when attribute is `null`
2023-11-06 14:51:27 +01:00
jan iversen
9a37868244 Modbus set device_class in slaves (#103442) 2023-11-06 14:51:24 +01:00
jan iversen
9327c51115 modbus Allow swap: byte for datatype: string. (#103441) 2023-11-06 14:51:21 +01:00
Michael
e56e75114a Fix serial in Flo device information (#103427) 2023-11-06 14:51:17 +01:00
Tobias Sauerwein
f45114371e Bump pyatmo to v7.6.0 (#103410)
Signed-off-by: Tobias Sauerwein <cgtobi@gmail.com>
2023-11-06 14:51:14 +01:00
Franck Nijhof
7e2c12b0a9 Update tailscale to 0.6.0 (#103409) 2023-11-06 14:51:10 +01:00
J. Nick Koston
050f1085d0 Pin jaraco.functools to fix builds and CI (#103406) 2023-11-06 14:51:07 +01:00
Matt Zimmerman
334a02bc2b Handle smarttub sensor values being None (#103385)
* [smarttub] handle sensor values being None

* empty commit to rerun CI

* lint

* use const in test

* reorder checks

* use None instead of STATE_UNKNOWN

* empty commit to rerun CI

* check for STATE_UNKNOWN in test

* empty commit to rerun CI
2023-11-06 14:51:04 +01:00
Nathan Spencer
412fa4c65a Handle null data in WeatherFlow sensors (#103349)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-11-06 14:51:01 +01:00
Álvaro Fernández Rojas
2b36befe95 Update aioairzone-cloud to v0.3.5 (#103315)
* Update aioairzone-cloud to v0.3.3

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Update aioairzone-cloud to v0.3.4

Reverts accidental TaskGroup changes.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Update aioairzone-cloud to v0.3.5

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Trigger Github CI

---------

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-11-06 14:50:56 +01:00
Álvaro Fernández Rojas
aa623cc15c Update aioairzone-cloud to v0.3.2 (#103258)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-11-06 14:50:51 +01:00
Maciej Bieniek
b0bb91ec08 Don't assume that the sleep value is a dictionary in Tractive integration (#103138)
* Sleep value can be null

* Catch TypeError
2023-11-06 14:49:10 +01:00
Franck Nijhof
ce12d82624 2023.11.1 (#103301) 2023-11-04 14:27:14 +01:00
Raman Gupta
9eff9ee374 Fix zwave_js cover bug for Window Covering CC values (#103289)
* Fix cover bug for Window Covering CC values

* update test

* Fix fixture

* Remove no-op line from test
2023-11-04 12:53:50 +01:00
Rami Mosleh
1ef460cffe Fix sensor unique id in Islamic prayer times (#103356) 2023-11-04 12:53:24 +01:00
Robert Svensson
42243f1433 Handle UniFi traffic rules not supported on older versions (#103346) 2023-11-04 12:53:21 +01:00
Jan Rieger
8a07c10d88 Report correct weather condition at night for Met (#103334)
* Report correct weather condition at night for Met, fixes #68369, fixes #89001

* Update homeassistant/components/met/weather.py

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

---------

Co-authored-by: Jan-Philipp Benecke <github@bnck.me>
2023-11-04 12:53:18 +01:00
Joakim Sørensen
730a3f7870 Remove extra from traccar webhook (#103319) 2023-11-04 12:53:15 +01:00
Jan-Philipp Benecke
718901d2ad Fix typo in Todoist config flow (#103317) 2023-11-04 12:53:12 +01:00
Raman Gupta
d95d4d0184 Add script to convert zwave_js device diagnostics to fixture (#102799) 2023-11-04 12:53:06 +01:00
Ian
67ce51899f Bump py_nextbusnext to v1.0.2 to fix TypeError (#103214)
* Bump py_nextbusnext to v1.0.1 to fix TypeError

Currently throwing an error as a set is passed into the method that is currently
expecting a Sequence. That method is technically compatible with Iterable, so the
latest patch relaxes that restriction.

* Bump version to v1.0.2 to fix error message
2023-11-04 12:46:05 +01:00
starkillerOG
810681b357 Bump reolink-aio to 0.7.14 and improve typing of Reolink (#103129)
* Improve typing

* fix mypy

* Further improve typing

* Restore Literal typing

* Bump reolink_aio to 0.7.13

* Bump reolink-aio to 0.7.14
2023-11-04 12:46:01 +01:00
Franck Nijhof
0b0f099d27 Bumped version to 2023.11.1 2023-11-03 13:02:22 +01:00
tronikos
4a56d0ec1d Bump opower to 0.0.39 (#103292) 2023-11-03 13:02:04 +01:00
Raman Gupta
910654bf78 Fix firmware update failure (#103277) 2023-11-03 13:02:01 +01:00
Marcel van der Veldt
1a823376d8 Fix Matter 1.2 locks with specific unlatch/unbolt support (#103275) 2023-11-03 13:01:56 +01:00
Pedro Januário
ba634ac346 add library logger info on ecoforest integration manifest (#103274) 2023-11-03 13:01:24 +01:00
J. Nick Koston
92486b1ff0 Bump yalexs-ble to 2.3.2 (#103267) 2023-11-03 12:59:19 +01:00
Tom
06d26b7c7f Fix Plugwise Schedule selection (#103262) 2023-11-03 12:59:16 +01:00
Joakim Plate
1dcd66d75c Remove measurement flag from timestamp in gardena bluetooth (#103245)
Remove measurement flag from timestamp
2023-11-03 12:59:13 +01:00
Charles Garwood
c811e0db49 Bump pyenphase to 1.14.1 (#103239) 2023-11-03 12:59:09 +01:00
Matthias Alphart
dc30ddc24b Fix Fronius entity initialisation (#103211)
* Use None instead of raising ValueError if value invalid

* use async_dispatcher_send
2023-11-03 12:59:06 +01:00
Jan Bouwhuis
239fa04d02 Fix mqtt config validation error handling (#103210)
* Fix MQTT config check

* Fix handling invalid enity_category for sensors

* Improve docstr

* Update comment

* Use correct util for yaml dump
2023-11-03 12:59:03 +01:00
Xitee
2be229c5b5 Fix roomba error if battery stats are not available (#103196) 2023-11-03 12:59:00 +01:00
Xitee
5b4df0f7ff Fix roomba translation key mismatch (#103191) 2023-11-03 12:58:57 +01:00
mkmer
355b51d4c8 Catch unexpected response in Honeywell (#103169)
catch unexpected response
2023-11-03 12:58:54 +01:00
Joost Lekkerkerker
0c8074bab4 Bump aiowaqi to 3.0.0 (#103166) 2023-11-03 12:58:51 +01:00
G Johansson
acd98e9b40 Bump python-holidays to 0.35 (#103092) 2023-11-03 12:58:45 +01:00
Franck Nijhof
0b8d4235c3 2023.11.0 (#103164) 2023-11-01 15:59:51 +01:00
Franck Nijhof
4ce859b4e4 Bumped version to 2023.11.0 2023-11-01 11:24:41 +01:00
Franck Nijhof
18acec32b8 Bumped version to 2023.11.0b6 2023-11-01 11:22:25 +01:00
Bram Kragten
cfa2f2ce61 Update frontend to 20231030.1 (#103163) 2023-11-01 11:22:16 +01:00
Jan Bouwhuis
aa5ea5ebc3 Fix mqtt is not reloading without yaml config (#103159) 2023-11-01 11:22:13 +01:00
J. Nick Koston
bcea021c14 Allow non-admins to subscribe to the issue registry updated event (#103145) 2023-11-01 11:22:10 +01:00
Allen Porter
ea2d2ba7b7 Improve fitbit oauth token error handling in config flow (#103131)
* Improve fitbit oauth token error handling in config flow

* Apply suggestions from code review

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

* Update tests with updated error reason

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-11-01 11:22:07 +01:00
Teemu R
c5f21fefbe Bump python-kasa to 0.5.4 for tplink (#103038) 2023-11-01 11:22:03 +01:00
Franck Nijhof
9910f9e0ae Bumped version to 2023.11.0b5 2023-10-31 19:43:21 +01:00
J. Nick Koston
f0a06efa1f Fix race in starting reauth flows (#103130) 2023-10-31 19:43:05 +01:00
J. Nick Koston
8992d15ffc Bump aiohomekit to 3.0.9 (#103123) 2023-10-31 19:43:02 +01:00
Paul Bottein
e097dc02dd Don't try to load resources in safe mode (#103122) 2023-10-31 19:42:59 +01:00
starkillerOG
bfae1468d6 Bump reolink-aio to 0.7.12 (#103120) 2023-10-31 19:42:52 +01:00
Christopher Fenner
09ed6e9f9b Handle exception introduced with recent PyViCare update (#103110) 2023-10-31 19:42:48 +01:00
Erik Montnemery
040ecb74e0 Add todo to core files (#103102) 2023-10-31 19:42:45 +01:00
Erik Montnemery
a48e63aa28 Fix todoist todo tests (#103101) 2023-10-31 19:42:41 +01:00
Erik Montnemery
19479b2a68 Fix local_todo todo tests (#103099) 2023-10-31 19:42:36 +01:00
Franck Nijhof
9ae29e243d Bumped version to 2023.11.0b4 2023-10-31 13:30:10 +01:00
Joost Lekkerkerker
e309bd764b Abort config flow if Google Tasks API is not enabled (#103114)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-10-31 13:29:52 +01:00
Christopher Fenner
777ffe6946 Fix client id label in ViCare integration (#103111) 2023-10-31 13:29:49 +01:00
Robert Resch
fa0f679a9a Fix todo.remove_item frontend (#103108) 2023-10-31 13:29:46 +01:00
Erik Montnemery
26b7e94c4f Fix shopping_list todo tests (#103100) 2023-10-31 13:29:43 +01:00
Erik Montnemery
957998ea8d Fix google_tasks todo tests (#103098) 2023-10-31 13:29:40 +01:00
Erik Montnemery
abaeacbd6b Fix restore state for light when saved attribute is None (#103096) 2023-10-31 13:29:36 +01:00
Bram Kragten
d76c16fa3a Update frontend to 20231030.0 (#103086) 2023-10-31 13:29:33 +01:00
G Johansson
67edb98e59 Fix Met Device Info (#103082) 2023-10-31 13:29:30 +01:00
Robert Resch
376a79eb42 Refactor todo services and their schema (#103079) 2023-10-31 13:29:27 +01:00
Rami Mosleh
41500cbe9b Code cleanup for transmission integration (#103078) 2023-10-31 13:29:24 +01:00
Paul Manzotti
06f27e7e74 Update geniushub-client to v0.7.1 (#103071) 2023-10-31 13:29:19 +01:00
Franck Nijhof
a3ebfaebe7 Bumped version to 2023.11.0b3 2023-10-30 19:59:32 +01:00
Joost Lekkerkerker
8d781ff063 Add 2 properties to Withings diagnostics (#103067) 2023-10-30 19:59:21 +01:00
Joost Lekkerkerker
bac39f0061 Show a warning when no Withings data found (#103066) 2023-10-30 19:59:17 +01:00
David Knowles
c7b702f3c2 Bump pyschlage to 2023.10.0 (#103065) 2023-10-30 19:59:14 +01:00
Christopher Fenner
3728f3da69 Update PyViCare to v2.28.1 for ViCare integration (#103064) 2023-10-30 19:59:11 +01:00
tronikos
31d8f4b35d Fix Opower not refreshing statistics when there are no forecast entities (#103058)
Ensure _insert_statistics is periodically called
2023-10-30 19:59:08 +01:00
Mike Woudenberg
f113d9aa71 Use correct config entry field to update when IP changes in loqed (#103051) 2023-10-30 19:59:05 +01:00
Jack Boswell
891ad0b1be Bump starlink-grpc-core to 1.1.3 (#103043) 2023-10-30 19:59:02 +01:00
Jirka
5c16a8247a Update MQTT QoS description string (#103036)
Update strings.json
2023-10-30 19:58:58 +01:00
Allen Porter
483671bf9f Bump google-nest-sdm to 3.0.3 (#103035) 2023-10-30 19:58:54 +01:00
G-Two
6f73d2aac5 Bump to subarulink 0.7.8 (#103033) 2023-10-30 19:58:50 +01:00
Allen Porter
f5b3661836 Fix bug in fitbit credential import for expired tokens (#103024)
* Fix bug in fitbit credential import on token refresh

* Use stable test ids

* Update homeassistant/components/fitbit/sensor.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-10-30 19:58:47 +01:00
kpine
f70c13214c Revert "Fix temperature setting for multi-setpoint z-wave device (#102395)" (#103022)
This reverts commit 2d6dc2bccc.
2023-10-30 19:58:43 +01:00
Raman Gupta
70e8978123 Fix zwave_js siren name (#103016)
* Fix zwave_js.siren name

* Fix test
2023-10-30 19:58:40 +01:00
Diogo Gomes
031b1c26ce Fix utility_meter reset when DST change occurs (#103012) 2023-10-30 19:58:37 +01:00
Nortonko
13580a334f Bump python-androidtv to 0.0.73 (#102999)
* Update manifest.json

Bump python-androidtv to version 0.0.73

* bump androidtv 0.0.73

* bump androidtv 0.0.73
2023-10-30 19:58:34 +01:00
Michael
e81bfb959e Fix proximity entity id (#102992)
* fix proximity entity id

* extend test to cover entity id
2023-10-30 19:58:31 +01:00
Tom Puttemans
fefe930506 DSMR Gas currently delivered device state class conflict (#102991)
Fixes #102985
2023-10-30 19:58:28 +01:00
David Bonnes
5ac7e8b1ac Harden evohome against failures to retrieve high-precision temps (#102989)
fix hass-logger-period
2023-10-30 19:58:24 +01:00
tronikos
36512f7157 Bump opower to 0.0.38 (#102983) 2023-10-30 19:58:21 +01:00
Bouwe Westerdijk
cc3ae9e103 Correct total state_class of huisbaasje sensors (#102945)
* Change all cumulative-interval sensors to TOTAL
2023-10-30 19:58:18 +01:00
Robert Hillis
12482216f6 Fix Google Mail expired authorization (#102735)
* Fix Google Mail expired authorization

* add test

* raise HomeAssistantError

* handle in api module

* uno mas
2023-10-30 19:58:14 +01:00
David Knowles
20409d0124 Make Hydrawise initialize data immediately (#101936) 2023-10-30 19:58:11 +01:00
mkmer
a741bc9951 Add retry before unavailable to Honeywell (#101702)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-10-30 19:58:08 +01:00
Erwin Douna
59d2bce369 Enable dry mode for Tado AC's V3 (#99568) 2023-10-30 19:58:04 +01:00
Paulus Schoutsen
eef318f63c Bumped version to 2023.11.0b2 2023-10-28 23:29:03 -04:00
Paulus Schoutsen
9c8a4bb4eb Fix proximity zone handling (#102971)
* fix proximity zone

* fix test
2023-10-28 23:29:03 -04:00
Paulus Schoutsen
9c9f1ea685 Fix error message strings for Todoist configuration flow (#102968)
* Fix error message strings for Todoist configuration flow

* Update error code in test
2023-10-28 23:29:03 -04:00
Paulus Schoutsen
85d999b020 Add gas device class to dsmr_reader sensor (#102953)
DSMR reader integration - can't configure gas meter in energy dashboard posible due to missing device_class
Fixes #102367
2023-10-28 23:29:03 -04:00
Paulus Schoutsen
bcddf52364 Update xknxproject to 3.4.0 (#102946) 2023-10-28 23:29:03 -04:00
Paulus Schoutsen
07e4e1379a Improve diagnostic handling in HomeWizard Energy (#102935) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
f9f010643a Handle/extend number entity availability property in HomeWizard Energy (#102934) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
974c34e2b6 Small base entity cleanup for HomeWizard Energy entities (#102933) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
1c3de76b04 Move HomeWizard Energy identify button to config entity category (#102932) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
bee63ca654 Hide mac address from HomeWizard Energy config entry/discovery titles (#102931) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
29c99f419f Bump velbusaio to 2023.10.2 (#102919) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
3d321c5ca7 Update frontend to 20231027.0 (#102913) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
4617c16a96 Update aioairzone-cloud to v0.3.1 (#102899) 2023-10-28 23:29:02 -04:00
Paulus Schoutsen
a60656bf29 Improve fitbit oauth import robustness (#102833)
* Improve fitbit oauth import robustness

* Improve sensor tests and remove unnecessary client check

* Fix oauth client id/secret config key checks

* Add executor for sync call
2023-10-28 23:29:02 -04:00
Paulus Schoutsen
2eb2a65197 Use new API for Vasttrafik (#102570) 2023-10-28 23:29:02 -04:00
Franck Nijhof
867aaf10ee Bumped version to 2023.11.0b1 2023-10-27 14:02:42 +02:00
Franck Nijhof
7fe1ac901f Some textual fixes for todo (#102895) 2023-10-27 14:02:24 +02:00
Bram Kragten
5dca3844ef Add redirect from shopping list to todo (#102894) 2023-10-27 14:02:20 +02:00
Erik Montnemery
b5c75a2f2f Allow missing components in safe mode (#102891) 2023-10-27 14:02:17 +02:00
Erik Montnemery
62fc9dfd6c Allow missing components in safe mode (#102888) 2023-10-27 14:02:14 +02:00
Jan Bouwhuis
0573981d6f Fix mqtt schema import not available for mqtt_room (#102866) 2023-10-27 14:02:09 +02:00
Paul Bottein
cc7a4d01e3 Don't return resources in safe mode (#102865) 2023-10-27 14:02:06 +02:00
Paul Bottein
293025ab6c Update frontend to 20231026.0 (#102857) 2023-10-27 14:02:02 +02:00
Jan-Philipp Benecke
a490b5e286 Add connections to PassiveBluetoothProcessorEntity (#102854) 2023-10-27 14:01:58 +02:00
Joost Lekkerkerker
7e4da1d03b Bump aiowithings to 1.0.2 (#102852) 2023-10-27 14:01:53 +02:00
Ravaka Razafimanantsoa
9e140864eb Address late review of switchbot cloud (#102842)
For Martin's review
2023-10-27 14:01:47 +02:00
Kevin Worrel
a6f88fb123 Bump screenlogicpy to v0.9.4 (#102836) 2023-10-27 14:01:43 +02:00
J. Nick Koston
386c5ecc3e Bump bleak-retry-connector to 3.3.0 (#102825)
changelog: https://github.com/Bluetooth-Devices/bleak-retry-connector/compare/v3.2.1...v3.3.0
2023-10-27 14:01:39 +02:00
Erik Montnemery
0d7fb5b026 Use real devices in automation blueprint tests (#102824) 2023-10-27 14:01:35 +02:00
Erik Montnemery
767b7ba4d6 Correct logic for picking bluetooth local name (#102823)
* Correct logic for picking bluetooth local name

* make test more robust

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2023-10-27 14:01:31 +02:00
William Scanlon
f2cef7245a Bump pyeconet to 0.1.22 to handle breaking API change (#102820) 2023-10-27 14:01:27 +02:00
J. Nick Koston
701a5d7758 Bump HAP-python 4.9.1 (#102811) 2023-10-27 14:01:23 +02:00
mkmer
244fccdae6 Move coordinator first refresh in Blink (#102805)
Move coordinator first refresh
2023-10-27 14:01:18 +02:00
Erik Montnemery
10e6a26717 Fix fan device actions (#102797) 2023-10-27 14:01:15 +02:00
Allen Porter
5fe5013198 Change todo move API to reference previous uid (#102795) 2023-10-27 14:01:11 +02:00
Marc Mueller
0a0584b053 Fix velbus import (#102780) 2023-10-27 14:01:07 +02:00
Erik Montnemery
62733e830f Improve validation of device automations (#102766)
* Improve validation of device automations

* Improve comments

* Address review comment
2023-10-27 14:01:02 +02:00
Simone Chemelli
bbcfb5f30e Improve exception handling for Vodafone Station (#102761)
* improve exception handling for Vodafone Station

* address review comment

* apply review comment

* better except handling (bump library)

* cleanup
2023-10-27 14:00:55 +02:00
Amit Finkelstein
5b0e0b07b3 Apple TV: Use replacement commands for deprecated ones (#102056)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-10-27 14:00:45 +02:00
Franck Nijhof
05fd64fe80 Bumped version to 2023.11.0b0 2023-10-25 17:41:53 +02:00
539 changed files with 11846 additions and 3697 deletions

View File

@@ -633,8 +633,6 @@ omit =
homeassistant/components/kodi/browse_media.py
homeassistant/components/kodi/media_player.py
homeassistant/components/kodi/notify.py
homeassistant/components/komfovent/__init__.py
homeassistant/components/komfovent/climate.py
homeassistant/components/konnected/__init__.py
homeassistant/components/konnected/panel.py
homeassistant/components/konnected/switch.py

View File

@@ -259,6 +259,8 @@ build.json @home-assistant/supervisor
/tests/components/denonavr/ @ol-iver @starkillerOG
/homeassistant/components/derivative/ @afaucogney
/tests/components/derivative/ @afaucogney
/homeassistant/components/devialet/ @fwestenberg
/tests/components/devialet/ @fwestenberg
/homeassistant/components/device_automation/ @home-assistant/core
/tests/components/device_automation/ @home-assistant/core
/homeassistant/components/device_tracker/ @home-assistant/core
@@ -661,8 +663,6 @@ build.json @home-assistant/supervisor
/tests/components/knx/ @Julius2342 @farmio @marvin-w
/homeassistant/components/kodi/ @OnFreund
/tests/components/kodi/ @OnFreund
/homeassistant/components/komfovent/ @ProstoSanja
/tests/components/komfovent/ @ProstoSanja
/homeassistant/components/konnected/ @heythisisnate
/tests/components/konnected/ @heythisisnate
/homeassistant/components/kostal_plenticore/ @stegm

View File

@@ -1,3 +1,6 @@
# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
ARG BUILD_FROM
FROM ${BUILD_FROM}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/adax",
"iot_class": "local_polling",
"loggers": ["adax", "adax_local"],
"requirements": ["adax==0.3.0", "Adax-local==0.1.5"]
"requirements": ["adax==0.4.0", "Adax-local==0.1.5"]
}

View File

@@ -10,6 +10,9 @@
"username": "[%key:common::config_flow::data::username%]",
"ssl": "[%key:common::config_flow::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"host": "The hostname or IP address of the device running your AdGuard Home."
}
},
"hassio_confirm": {

View File

@@ -6,6 +6,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The IP address of the Agent DVR server."
}
}
},

View File

@@ -3,7 +3,6 @@ from typing import Final
DOMAIN: Final = "airq"
MANUFACTURER: Final = "CorantGmbH"
TARGET_ROUTE: Final = "average"
CONCENTRATION_GRAMS_PER_CUBIC_METER: Final = "g/m³"
ACTIVITY_BECQUEREL_PER_CUBIC_METER: Final = "Bq/m³"
UPDATE_INTERVAL: float = 10.0

View File

@@ -13,7 +13,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, MANUFACTURER, TARGET_ROUTE, UPDATE_INTERVAL
from .const import DOMAIN, MANUFACTURER, UPDATE_INTERVAL
_LOGGER = logging.getLogger(__name__)
@@ -56,6 +56,4 @@ class AirQCoordinator(DataUpdateCoordinator):
hw_version=info["hw_version"],
)
)
data = await self.airq.get(TARGET_ROUTE)
return self.airq.drop_uncertainties_from_data(data)
return await self.airq.get_latest_data()

View File

@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioairq"],
"requirements": ["aioairq==0.2.4"]
"requirements": ["aioairq==0.3.1"]
}

View File

@@ -12,6 +12,9 @@
"title": "Set up your AirTouch 4 connection details.",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your AirTouch controller."
}
}
}

View File

@@ -12,6 +12,9 @@
"data": {
"ip_address": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"ip_address": "The hostname or IP address of your AirVisual Pro device."
}
}
},

View File

@@ -14,6 +14,10 @@
"port": "[%key:common::config_flow::data::port%]",
"device_baudrate": "Device Baud Rate",
"device_path": "Device Path"
},
"data_description": {
"host": "The hostname or IP address of the AlarmDecoder device that is connected to your alarm panel.",
"port": "The port on which AlarmDecoder is accessible (for example, 10000)"
}
}
},

View File

@@ -7,6 +7,9 @@
"port": "[%key:common::config_flow::data::port%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The IP address of the device running the Android IP Webcam app. The IP address is shown in the app once you start the server."
}
}
},

View File

@@ -41,7 +41,6 @@ from homeassistant.exceptions import (
Unauthorized,
)
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.aiohttp_compat import enable_compression
from homeassistant.helpers.event import EventStateChangedData
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.service import async_get_all_descriptions
@@ -218,9 +217,11 @@ class APIStatesView(HomeAssistantView):
if entity_perm(state.entity_id, "read")
)
response = web.Response(
body=f'[{",".join(states)}]', content_type=CONTENT_TYPE_JSON
body=f'[{",".join(states)}]',
content_type=CONTENT_TYPE_JSON,
zlib_executor_size=32768,
)
enable_compression(response)
response.enable_compression()
return response
@@ -390,17 +391,14 @@ class APIDomainServicesView(HomeAssistantView):
)
try:
async with timeout(SERVICE_WAIT_TIMEOUT):
# shield the service call from cancellation on connection drop
await shield(
hass.services.async_call(
domain, service, data, blocking=True, context=context
)
# shield the service call from cancellation on connection drop
await shield(
hass.services.async_call(
domain, service, data, blocking=True, context=context
)
)
except (vol.Invalid, ServiceNotFound) as ex:
raise HTTPBadRequest() from ex
except TimeoutError:
pass
finally:
cancel_listen()

View File

@@ -9,7 +9,7 @@ from dataclasses import asdict, dataclass, field
from enum import StrEnum
import logging
from pathlib import Path
from queue import Queue
from queue import Empty, Queue
from threading import Thread
import time
from typing import TYPE_CHECKING, Any, Final, cast
@@ -1010,8 +1010,8 @@ class PipelineRun:
self.tts_engine = engine
self.tts_options = tts_options
async def text_to_speech(self, tts_input: str) -> str:
"""Run text-to-speech portion of pipeline. Returns URL of TTS audio."""
async def text_to_speech(self, tts_input: str) -> None:
"""Run text-to-speech portion of pipeline."""
self.process_event(
PipelineEvent(
PipelineEventType.TTS_START,
@@ -1024,43 +1024,40 @@ class PipelineRun:
)
)
try:
# Synthesize audio and get URL
tts_media_id = tts_generate_media_source_id(
self.hass,
tts_input,
engine=self.tts_engine,
language=self.pipeline.tts_language,
options=self.tts_options,
)
tts_media = await media_source.async_resolve_media(
self.hass,
tts_media_id,
None,
)
except Exception as src_error:
_LOGGER.exception("Unexpected error during text-to-speech")
raise TextToSpeechError(
code="tts-failed",
message="Unexpected error during text-to-speech",
) from src_error
if tts_input := tts_input.strip():
try:
# Synthesize audio and get URL
tts_media_id = tts_generate_media_source_id(
self.hass,
tts_input,
engine=self.tts_engine,
language=self.pipeline.tts_language,
options=self.tts_options,
)
tts_media = await media_source.async_resolve_media(
self.hass,
tts_media_id,
None,
)
except Exception as src_error:
_LOGGER.exception("Unexpected error during text-to-speech")
raise TextToSpeechError(
code="tts-failed",
message="Unexpected error during text-to-speech",
) from src_error
_LOGGER.debug("TTS result %s", tts_media)
_LOGGER.debug("TTS result %s", tts_media)
tts_output = {
"media_id": tts_media_id,
**asdict(tts_media),
}
else:
tts_output = {}
self.process_event(
PipelineEvent(
PipelineEventType.TTS_END,
{
"tts_output": {
"media_id": tts_media_id,
**asdict(tts_media),
}
},
)
PipelineEvent(PipelineEventType.TTS_END, {"tts_output": tts_output})
)
return tts_media.url
def _capture_chunk(self, audio_bytes: bytes | None) -> None:
"""Forward audio chunk to various capturing mechanisms."""
if self.debug_recording_queue is not None:
@@ -1247,6 +1244,8 @@ def _pipeline_debug_recording_thread_proc(
# Chunk of 16-bit mono audio at 16Khz
if wav_writer is not None:
wav_writer.writeframes(message)
except Empty:
pass # occurs when pipeline has unexpected error
except Exception: # pylint: disable=broad-exception-caught
_LOGGER.exception("Unexpected error in debug recording thread")
finally:

View File

@@ -55,7 +55,9 @@ _LOGGER = logging.getLogger(__name__)
_AsusWrtBridgeT = TypeVar("_AsusWrtBridgeT", bound="AsusWrtBridge")
_FuncType = Callable[[_AsusWrtBridgeT], Awaitable[list[Any] | dict[str, Any]]]
_FuncType = Callable[
[_AsusWrtBridgeT], Awaitable[list[Any] | tuple[Any] | dict[str, Any]]
]
_ReturnFuncType = Callable[[_AsusWrtBridgeT], Coroutine[Any, Any, dict[str, Any]]]
@@ -81,7 +83,7 @@ def handle_errors_and_zip(
if isinstance(data, dict):
return dict(zip(keys, list(data.values())))
if not isinstance(data, list):
if not isinstance(data, (list, tuple)):
raise UpdateFailed("Received invalid data type")
return dict(zip(keys, data))

View File

@@ -2,7 +2,6 @@
"config": {
"step": {
"user": {
"title": "AsusWRT",
"description": "Set required parameter to connect to your router",
"data": {
"host": "[%key:common::config_flow::data::host%]",
@@ -11,10 +10,12 @@
"ssh_key": "Path to your SSH key file (instead of password)",
"protocol": "Communication protocol to use",
"port": "Port (leave empty for protocol default)"
},
"data_description": {
"host": "The hostname or IP address of your ASUSWRT router."
}
},
"legacy": {
"title": "AsusWRT",
"description": "Set required parameters to connect to your router",
"data": {
"mode": "Router operating mode"
@@ -37,7 +38,6 @@
"options": {
"step": {
"init": {
"title": "AsusWRT Options",
"data": {
"consider_home": "Seconds to wait before considering a device away",
"track_unknown": "Track unknown / unnamed devices",

View File

@@ -2,10 +2,13 @@
"config": {
"step": {
"user": {
"title": "Connect to the device",
"description": "Connect to the device",
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of the Atag device."
}
}
},

View File

@@ -3,12 +3,16 @@
"flow_title": "{name} ({host})",
"step": {
"user": {
"title": "Set up Axis device",
"description": "Set up an Axis device",
"data": {
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of the Axis device.",
"username": "The user name you set up on your Axis device. It is recommended to create a user specifically for Home Assistant."
}
}
},

View File

@@ -93,8 +93,6 @@ class BAFFan(BAFEntity, FanEntity):
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan."""
if preset_mode != PRESET_MODE_AUTO:
raise ValueError(f"Invalid preset mode: {preset_mode}")
self._device.fan_mode = OffOnAuto.AUTO
async def async_set_direction(self, direction: str) -> None:

View File

@@ -2,9 +2,12 @@
"config": {
"step": {
"user": {
"title": "Connect to the Balboa Wi-Fi device",
"description": "Connect to the Balboa Wi-Fi device",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "Hostname or IP address of your Balboa Spa Wifi Device. For example, 192.168.1.58."
}
}
},

View File

@@ -25,7 +25,7 @@ from homeassistant.helpers.typing import ConfigType
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, PLATFORMS
from .coordinator import BlinkUpdateCoordinator
from .services import async_setup_services
from .services import setup_services
_LOGGER = logging.getLogger(__name__)
@@ -74,7 +74,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Blink."""
await async_setup_services(hass)
setup_services(hass)
return True

View File

@@ -1,8 +1,6 @@
"""Services for the Blink integration."""
from __future__ import annotations
import logging
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
@@ -14,7 +12,7 @@ from homeassistant.const import (
CONF_PIN,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
import homeassistant.helpers.config_validation as cv
import homeassistant.helpers.device_registry as dr
@@ -27,56 +25,67 @@ from .const import (
)
from .coordinator import BlinkUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
SERVICE_SAVE_VIDEO_SCHEMA = vol.Schema(
{
vol.Required(ATTR_DEVICE_ID): cv.ensure_list,
vol.Required(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_FILENAME): cv.string,
}
)
SERVICE_SEND_PIN_SCHEMA = vol.Schema(
{vol.Required(ATTR_DEVICE_ID): cv.ensure_list, vol.Optional(CONF_PIN): cv.string}
{
vol.Required(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_PIN): cv.string,
}
)
SERVICE_SAVE_RECENT_CLIPS_SCHEMA = vol.Schema(
{
vol.Required(ATTR_DEVICE_ID): cv.ensure_list,
vol.Required(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_FILE_PATH): cv.string,
}
)
async def async_setup_services(hass: HomeAssistant) -> None:
def setup_services(hass: HomeAssistant) -> None:
"""Set up the services for the Blink integration."""
async def collect_coordinators(
def collect_coordinators(
device_ids: list[str],
) -> list[BlinkUpdateCoordinator]:
config_entries = list[ConfigEntry]()
config_entries: list[ConfigEntry] = []
registry = dr.async_get(hass)
for target in device_ids:
device = registry.async_get(target)
if device:
device_entries = list[ConfigEntry]()
device_entries: list[ConfigEntry] = []
for entry_id in device.config_entries:
entry = hass.config_entries.async_get_entry(entry_id)
if entry and entry.domain == DOMAIN:
device_entries.append(entry)
if not device_entries:
raise HomeAssistantError(
f"Device '{target}' is not a {DOMAIN} device"
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_device",
translation_placeholders={"target": target, "domain": DOMAIN},
)
config_entries.extend(device_entries)
else:
raise HomeAssistantError(
f"Device '{target}' not found in device registry"
translation_domain=DOMAIN,
translation_key="device_not_found",
translation_placeholders={"target": target},
)
coordinators = list[BlinkUpdateCoordinator]()
coordinators: list[BlinkUpdateCoordinator] = []
for config_entry in config_entries:
if config_entry.state != ConfigEntryState.LOADED:
raise HomeAssistantError(f"{config_entry.title} is not loaded")
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="not_loaded",
translation_placeholders={"target": config_entry.title},
)
coordinators.append(hass.data[DOMAIN][config_entry.entry_id])
return coordinators
@@ -85,24 +94,36 @@ async def async_setup_services(hass: HomeAssistant) -> None:
camera_name = call.data[CONF_NAME]
video_path = call.data[CONF_FILENAME]
if not hass.config.is_allowed_path(video_path):
_LOGGER.error("Can't write %s, no access to path!", video_path)
return
for coordinator in await collect_coordinators(call.data[ATTR_DEVICE_ID]):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="no_path",
translation_placeholders={"target": video_path},
)
for coordinator in collect_coordinators(call.data[ATTR_DEVICE_ID]):
all_cameras = coordinator.api.cameras
if camera_name in all_cameras:
try:
await all_cameras[camera_name].video_to_file(video_path)
except OSError as err:
_LOGGER.error("Can't write image to file: %s", err)
raise ServiceValidationError(
str(err),
translation_domain=DOMAIN,
translation_key="cant_write",
) from err
async def async_handle_save_recent_clips_service(call: ServiceCall) -> None:
"""Save multiple recent clips to output directory."""
camera_name = call.data[CONF_NAME]
clips_dir = call.data[CONF_FILE_PATH]
if not hass.config.is_allowed_path(clips_dir):
_LOGGER.error("Can't write to directory %s, no access to path!", clips_dir)
return
for coordinator in await collect_coordinators(call.data[ATTR_DEVICE_ID]):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="no_path",
translation_placeholders={"target": clips_dir},
)
for coordinator in collect_coordinators(call.data[ATTR_DEVICE_ID]):
all_cameras = coordinator.api.cameras
if camera_name in all_cameras:
try:
@@ -110,11 +131,15 @@ async def async_setup_services(hass: HomeAssistant) -> None:
output_dir=clips_dir
)
except OSError as err:
_LOGGER.error("Can't write recent clips to directory: %s", err)
raise ServiceValidationError(
str(err),
translation_domain=DOMAIN,
translation_key="cant_write",
) from err
async def send_pin(call: ServiceCall):
"""Call blink to send new pin."""
for coordinator in await collect_coordinators(call.data[ATTR_DEVICE_ID]):
for coordinator in collect_coordinators(call.data[ATTR_DEVICE_ID]):
await coordinator.api.auth.send_auth_key(
coordinator.api,
call.data[CONF_PIN],
@@ -122,7 +147,7 @@ async def async_setup_services(hass: HomeAssistant) -> None:
async def blink_refresh(call: ServiceCall):
"""Call blink to refresh info."""
for coordinator in await collect_coordinators(call.data[ATTR_DEVICE_ID]):
for coordinator in collect_coordinators(call.data[ATTR_DEVICE_ID]):
await coordinator.api.refresh(force_cache=True)
# Register all the above services

View File

@@ -101,5 +101,22 @@
}
}
}
},
"exceptions": {
"invalid_device": {
"message": "Device '{target}' is not a {domain} device"
},
"device_not_found": {
"message": "Device '{target}' not found in device registry"
},
"no_path": {
"message": "Can't write to directory {target}, no access to path!"
},
"cant_write": {
"message": "Can't write to file"
},
"not_loaded": {
"message": "{target} is not loaded"
}
}
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"iot_class": "cloud_polling",
"loggers": ["bimmer_connected"],
"requirements": ["bimmer-connected==0.14.3"]
"requirements": ["bimmer-connected[china]==0.14.6"]
}

View File

@@ -199,10 +199,6 @@ class BondFan(BondEntity, FanEntity):
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan."""
if preset_mode != PRESET_MODE_BREEZE or not self._device.has_action(
Action.BREEZE_ON
):
raise ValueError(f"Invalid preset mode: {preset_mode}")
await self._hub.bond.action(self._device.device_id, Action(Action.BREEZE_ON))
async def async_turn_off(self, **kwargs: Any) -> None:

View File

@@ -12,6 +12,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"access_token": "[%key:common::config_flow::data::access_token%]"
},
"data_description": {
"host": "The IP address of your Bond hub."
}
}
},

View File

@@ -6,6 +6,9 @@
"title": "SHC authentication parameters",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your Bosch Smart Home Controller."
}
},
"credentials": {

View File

@@ -5,6 +5,9 @@
"description": "Ensure that your TV is turned on before trying to set it up.",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of the Sony Bravia TV to control."
}
},
"authorize": {

View File

@@ -3,10 +3,13 @@
"flow_title": "{name} ({model} at {host})",
"step": {
"user": {
"title": "Connect to the device",
"description": "Connect to the device",
"data": {
"host": "[%key:common::config_flow::data::host%]",
"timeout": "Timeout"
},
"data_description": {
"host": "The hostname or IP address of your Broadlink device."
}
},
"auth": {

View File

@@ -6,6 +6,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"type": "Type of the printer"
},
"data_description": {
"host": "The hostname or IP address of the Brother printer to control."
}
},
"zeroconf_confirm": {

View File

@@ -60,8 +60,7 @@ async def async_setup_entry(
data.static,
entry,
)
],
True,
]
)

View File

@@ -11,6 +11,9 @@
"passkey": "Passkey string",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your BSB-Lan device."
}
}
},

View File

@@ -11,7 +11,11 @@ async def async_get_calendars(
hass: HomeAssistant, client: caldav.DAVClient, component: str
) -> list[caldav.Calendar]:
"""Get all calendars that support the specified component."""
calendars = await hass.async_add_executor_job(client.principal().calendars)
def _get_calendars() -> list[caldav.Calendar]:
return client.principal().calendars()
calendars = await hass.async_add_executor_job(_get_calendars)
components_results = await asyncio.gather(
*[
hass.async_add_executor_job(calendar.get_supported_components)

View File

@@ -2,10 +2,10 @@
from __future__ import annotations
import asyncio
from datetime import timedelta
from datetime import date, datetime, timedelta
from functools import partial
import logging
from typing import cast
from typing import Any, cast
import caldav
from caldav.lib.error import DAVError, NotFoundError
@@ -21,6 +21,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util
from .api import async_get_calendars, get_attr_value
from .const import DOMAIN
@@ -71,6 +72,12 @@ def _todo_item(resource: caldav.CalendarObjectResource) -> TodoItem | None:
or (summary := get_attr_value(todo, "summary")) is None
):
return None
due: date | datetime | None = None
if due_value := get_attr_value(todo, "due"):
if isinstance(due_value, datetime):
due = dt_util.as_local(due_value)
elif isinstance(due_value, date):
due = due_value
return TodoItem(
uid=uid,
summary=summary,
@@ -78,9 +85,28 @@ def _todo_item(resource: caldav.CalendarObjectResource) -> TodoItem | None:
get_attr_value(todo, "status") or "",
TodoItemStatus.NEEDS_ACTION,
),
due=due,
description=get_attr_value(todo, "description"),
)
def _to_ics_fields(item: TodoItem) -> dict[str, Any]:
"""Convert a TodoItem to the set of add or update arguments."""
item_data: dict[str, Any] = {}
if summary := item.summary:
item_data["summary"] = summary
if status := item.status:
item_data["status"] = TODO_STATUS_MAP_INV.get(status, "NEEDS-ACTION")
if due := item.due:
if isinstance(due, datetime):
item_data["due"] = dt_util.as_utc(due).strftime("%Y%m%dT%H%M%SZ")
else:
item_data["due"] = due.strftime("%Y%m%d")
if description := item.description:
item_data["description"] = description
return item_data
class WebDavTodoListEntity(TodoListEntity):
"""CalDAV To-do list entity."""
@@ -89,6 +115,9 @@ class WebDavTodoListEntity(TodoListEntity):
TodoListEntityFeature.CREATE_TODO_ITEM
| TodoListEntityFeature.UPDATE_TODO_ITEM
| TodoListEntityFeature.DELETE_TODO_ITEM
| TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
| TodoListEntityFeature.SET_DUE_DATETIME_ON_ITEM
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
)
def __init__(self, calendar: caldav.Calendar, config_entry_id: str) -> None:
@@ -116,13 +145,7 @@ class WebDavTodoListEntity(TodoListEntity):
"""Add an item to the To-do list."""
try:
await self.hass.async_add_executor_job(
partial(
self._calendar.save_todo,
summary=item.summary,
status=TODO_STATUS_MAP_INV.get(
item.status or TodoItemStatus.NEEDS_ACTION, "NEEDS-ACTION"
),
),
partial(self._calendar.save_todo, **_to_ics_fields(item)),
)
except (requests.ConnectionError, DAVError) as err:
raise HomeAssistantError(f"CalDAV save error: {err}") from err
@@ -139,10 +162,7 @@ class WebDavTodoListEntity(TodoListEntity):
except (requests.ConnectionError, DAVError) as err:
raise HomeAssistantError(f"CalDAV lookup error: {err}") from err
vtodo = todo.icalendar_component # type: ignore[attr-defined]
if item.summary:
vtodo["summary"] = item.summary
if item.status:
vtodo["status"] = TODO_STATUS_MAP_INV.get(item.status, "NEEDS-ACTION")
vtodo.update(**_to_ics_fields(item))
try:
await self.hass.async_add_executor_job(
partial(

View File

@@ -73,7 +73,7 @@
}
},
"get_events": {
"name": "Get event",
"name": "Get events",
"description": "Get events on a calendar within a time range.",
"fields": {
"start_date_time": {

View File

@@ -68,13 +68,13 @@ class ComelitSerialBridge(DataUpdateCoordinator):
async def _async_update_data(self) -> dict[str, Any]:
"""Update device data."""
_LOGGER.debug("Polling Comelit Serial Bridge host: %s", self._host)
try:
await self.api.login()
return await self.api.get_all_devices()
except exceptions.CannotConnect as err:
_LOGGER.warning("Connection error for %s", self._host)
await self.api.close()
raise UpdateFailed(f"Error fetching data: {repr(err)}") from err
except exceptions.CannotAuthenticate:
raise ConfigEntryAuthFailed
return await self.api.get_all_devices()

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/comelit",
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"requirements": ["aiocomelit==0.5.2"]
"requirements": ["aiocomelit==0.6.2"]
}

View File

@@ -13,6 +13,9 @@
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]",
"pin": "[%key:common::config_flow::data::pin%]"
},
"data_description": {
"host": "The hostname or IP address of your Comelit device."
}
}
},

View File

@@ -649,7 +649,7 @@ class DefaultAgent(AbstractConversationAgent):
if device_area is None:
return None
return {"area": device_area.name}
return {"area": device_area.id}
def _get_error_text(
self, response_type: ResponseType, lang_intents: LanguageIntents | None

View File

@@ -7,5 +7,5 @@
"integration_type": "system",
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": ["hassil==1.5.1", "home-assistant-intents==2023.11.17"]
"requirements": ["hassil==1.5.1", "home-assistant-intents==2023.12.05"]
}

View File

@@ -2,7 +2,7 @@
"config": {
"step": {
"user": {
"title": "Set up your CoolMasterNet connection details.",
"description": "Set up your CoolMasterNet connection details.",
"data": {
"host": "[%key:common::config_flow::data::host%]",
"off": "Can be turned off",
@@ -12,6 +12,9 @@
"dry": "Support dry mode",
"fan_only": "Support fan only mode",
"swing_support": "Control swing mode"
},
"data_description": {
"host": "The hostname or IP address of your CoolMasterNet device."
}
}
},

View File

@@ -67,7 +67,7 @@ DECONZ_TO_COLOR_MODE = {
LightColorMode.XY: ColorMode.XY,
}
TS0601_EFFECTS = [
XMAS_LIGHT_EFFECTS = [
"carnival",
"collide",
"fading",
@@ -200,8 +200,8 @@ class DeconzBaseLight(DeconzDevice[_LightDeviceT], LightEntity):
if device.effect is not None:
self._attr_supported_features |= LightEntityFeature.EFFECT
self._attr_effect_list = [EFFECT_COLORLOOP]
if device.model_id == "TS0601":
self._attr_effect_list += TS0601_EFFECTS
if device.model_id in ("HG06467", "TS0601"):
self._attr_effect_list = XMAS_LIGHT_EFFECTS
@property
def color_mode(self) -> str | None:

View File

@@ -11,11 +11,14 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of your deCONZ host."
}
},
"link": {
"title": "Link with deCONZ",
"description": "Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ Settings -> Gateway -> Advanced\n2. Press \"Authenticate app\" button"
"description": "Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ Settings > Gateway > Advanced\n2. Press \"Authenticate app\" button"
},
"hassio_confirm": {
"title": "deCONZ Zigbee gateway via Home Assistant add-on",

View File

@@ -9,6 +9,9 @@
"password": "[%key:common::config_flow::data::password%]",
"port": "[%key:common::config_flow::data::port%]",
"web_port": "Web port (for visiting service)"
},
"data_description": {
"host": "The hostname or IP address of your Deluge device."
}
}
},

View File

@@ -161,12 +161,9 @@ class DemoPercentageFan(BaseDemoFan, FanEntity):
def set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
if self.preset_modes and preset_mode in self.preset_modes:
self._preset_mode = preset_mode
self._percentage = None
self.schedule_update_ha_state()
else:
raise ValueError(f"Invalid preset mode: {preset_mode}")
self._preset_mode = preset_mode
self._percentage = None
self.schedule_update_ha_state()
def turn_on(
self,
@@ -230,10 +227,6 @@ class AsyncDemoPercentageFan(BaseDemoFan, FanEntity):
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
if self.preset_modes is None or preset_mode not in self.preset_modes:
raise ValueError(
f"{preset_mode} is not a valid preset_mode: {self.preset_modes}"
)
self._preset_mode = preset_mode
self._percentage = None
self.async_write_ha_state()

View File

@@ -0,0 +1,31 @@
"""The Devialet integration."""
from __future__ import annotations
from devialet import DevialetApi
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
PLATFORMS = [Platform.MEDIA_PLAYER]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Devialet from a config entry."""
session = async_get_clientsession(hass)
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = DevialetApi(
entry.data[CONF_HOST], session
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Devialet config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
del hass.data[DOMAIN][entry.entry_id]
return unload_ok

View File

@@ -0,0 +1,104 @@
"""Support for Devialet Phantom speakers."""
from __future__ import annotations
import logging
from typing import Any
from devialet.devialet_api import DevialetApi
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
LOGGER = logging.getLogger(__package__)
class DevialetFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Devialet."""
VERSION = 1
def __init__(self) -> None:
"""Initialize flow."""
self._host: str | None = None
self._name: str | None = None
self._model: str | None = None
self._serial: str | None = None
self._errors: dict[str, str] = {}
async def async_validate_input(self) -> FlowResult | None:
"""Validate the input using the Devialet API."""
self._errors.clear()
session = async_get_clientsession(self.hass)
client = DevialetApi(self._host, session)
if not await client.async_update() or client.serial is None:
self._errors["base"] = "cannot_connect"
LOGGER.error("Cannot connect")
return None
await self.async_set_unique_id(client.serial)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=client.device_name,
data={CONF_HOST: self._host, CONF_NAME: client.device_name},
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user or zeroconf."""
if user_input is not None:
self._host = user_input[CONF_HOST]
result = await self.async_validate_input()
if result is not None:
return result
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
errors=self._errors,
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
) -> FlowResult:
"""Handle a flow initialized by zeroconf discovery."""
LOGGER.info("Devialet device found via ZEROCONF: %s", discovery_info)
self._host = discovery_info.host
self._name = discovery_info.name.split(".", 1)[0]
self._model = discovery_info.properties["model"]
self._serial = discovery_info.properties["serialNumber"]
await self.async_set_unique_id(self._serial)
self._abort_if_unique_id_configured()
self.context["title_placeholders"] = {"title": self._name}
return await self.async_step_confirm()
async def async_step_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle user-confirmation of discovered node."""
title = f"{self._name} ({self._model})"
if user_input is not None:
result = await self.async_validate_input()
if result is not None:
return result
return self.async_show_form(
step_id="confirm",
description_placeholders={"device": self._model, "title": title},
errors=self._errors,
last_step=True,
)

View File

@@ -0,0 +1,12 @@
"""Constants for the Devialet integration."""
from typing import Final
DOMAIN: Final = "devialet"
MANUFACTURER: Final = "Devialet"
SOUND_MODES = {
"Custom": "custom",
"Flat": "flat",
"Night mode": "night mode",
"Voice": "voice",
}

View File

@@ -0,0 +1,32 @@
"""Class representing a Devialet update coordinator."""
from datetime import timedelta
import logging
from devialet import DevialetApi
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=5)
class DevialetCoordinator(DataUpdateCoordinator[None]):
"""Devialet update coordinator."""
def __init__(self, hass: HomeAssistant, client: DevialetApi) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
self.client = client
async def _async_update_data(self) -> None:
"""Fetch data from API endpoint."""
await self.client.async_update()

View File

@@ -0,0 +1,20 @@
"""Diagnostics support for Devialet."""
from __future__ import annotations
from typing import Any
from devialet import DevialetApi
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
client: DevialetApi = hass.data[DOMAIN][entry.entry_id]
return await client.async_get_diagnostics()

View File

@@ -0,0 +1,12 @@
{
"domain": "devialet",
"name": "Devialet",
"after_dependencies": ["zeroconf"],
"codeowners": ["@fwestenberg"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/devialet",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["devialet==1.4.3"],
"zeroconf": ["_devialet-http._tcp.local."]
}

View File

@@ -0,0 +1,212 @@
"""Support for Devialet speakers."""
from __future__ import annotations
from devialet.const import NORMAL_INPUTS
from homeassistant.components.media_player import (
MediaPlayerEntity,
MediaPlayerEntityFeature,
MediaPlayerState,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER, SOUND_MODES
from .coordinator import DevialetCoordinator
SUPPORT_DEVIALET = (
MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.SELECT_SOURCE
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
)
DEVIALET_TO_HA_FEATURE_MAP = {
"play": MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.STOP,
"pause": MediaPlayerEntityFeature.PAUSE,
"previous": MediaPlayerEntityFeature.PREVIOUS_TRACK,
"next": MediaPlayerEntityFeature.NEXT_TRACK,
"seek": MediaPlayerEntityFeature.SEEK,
}
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the Devialet entry."""
client = hass.data[DOMAIN][entry.entry_id]
coordinator = DevialetCoordinator(hass, client)
await coordinator.async_config_entry_first_refresh()
async_add_entities([DevialetMediaPlayerEntity(coordinator, entry)])
class DevialetMediaPlayerEntity(
CoordinatorEntity[DevialetCoordinator], MediaPlayerEntity
):
"""Devialet media player."""
_attr_has_entity_name = True
_attr_name = None
def __init__(self, coordinator: DevialetCoordinator, entry: ConfigEntry) -> None:
"""Initialize the Devialet device."""
self.coordinator = coordinator
super().__init__(coordinator)
self._attr_unique_id = str(entry.unique_id)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._attr_unique_id)},
manufacturer=MANUFACTURER,
model=self.coordinator.client.model,
name=entry.data[CONF_NAME],
sw_version=self.coordinator.client.version,
)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if not self.coordinator.client.is_available:
self.async_write_ha_state()
return
self._attr_volume_level = self.coordinator.client.volume_level
self._attr_is_volume_muted = self.coordinator.client.is_volume_muted
self._attr_source_list = self.coordinator.client.source_list
self._attr_sound_mode_list = sorted(SOUND_MODES)
self._attr_media_artist = self.coordinator.client.media_artist
self._attr_media_album_name = self.coordinator.client.media_album_name
self._attr_media_artist = self.coordinator.client.media_artist
self._attr_media_image_url = self.coordinator.client.media_image_url
self._attr_media_duration = self.coordinator.client.media_duration
self._attr_media_position = self.coordinator.client.current_position
self._attr_media_position_updated_at = (
self.coordinator.client.position_updated_at
)
self._attr_media_title = (
self.coordinator.client.media_title
if self.coordinator.client.media_title
else self.source
)
self.async_write_ha_state()
@property
def state(self) -> MediaPlayerState | None:
"""Return the state of the device."""
playing_state = self.coordinator.client.playing_state
if not playing_state:
return MediaPlayerState.IDLE
if playing_state == "playing":
return MediaPlayerState.PLAYING
if playing_state == "paused":
return MediaPlayerState.PAUSED
return MediaPlayerState.ON
@property
def available(self) -> bool:
"""Return if the media player is available."""
return self.coordinator.client.is_available
@property
def supported_features(self) -> MediaPlayerEntityFeature:
"""Flag media player features that are supported."""
features = SUPPORT_DEVIALET
if self.coordinator.client.source_state is None:
return features
if not self.coordinator.client.available_options:
return features
for option in self.coordinator.client.available_options:
features |= DEVIALET_TO_HA_FEATURE_MAP.get(option, 0)
return features
@property
def source(self) -> str | None:
"""Return the current input source."""
source = self.coordinator.client.source
for pretty_name, name in NORMAL_INPUTS.items():
if source == name:
return pretty_name
return None
@property
def sound_mode(self) -> str | None:
"""Return the current sound mode."""
if self.coordinator.client.equalizer is not None:
sound_mode = self.coordinator.client.equalizer
elif self.coordinator.client.night_mode:
sound_mode = "night mode"
else:
return None
for pretty_name, mode in SOUND_MODES.items():
if sound_mode == mode:
return pretty_name
return None
async def async_volume_up(self) -> None:
"""Volume up media player."""
await self.coordinator.client.async_volume_up()
async def async_volume_down(self) -> None:
"""Volume down media player."""
await self.coordinator.client.async_volume_down()
async def async_set_volume_level(self, volume: float) -> None:
"""Set volume level, range 0..1."""
await self.coordinator.client.async_set_volume_level(volume)
async def async_mute_volume(self, mute: bool) -> None:
"""Mute (true) or unmute (false) media player."""
await self.coordinator.client.async_mute_volume(mute)
async def async_media_play(self) -> None:
"""Play media player."""
await self.coordinator.client.async_media_play()
async def async_media_pause(self) -> None:
"""Pause media player."""
await self.coordinator.client.async_media_pause()
async def async_media_stop(self) -> None:
"""Pause media player."""
await self.coordinator.client.async_media_stop()
async def async_media_next_track(self) -> None:
"""Send the next track command."""
await self.coordinator.client.async_media_next_track()
async def async_media_previous_track(self) -> None:
"""Send the previous track command."""
await self.coordinator.client.async_media_previous_track()
async def async_media_seek(self, position: float) -> None:
"""Send seek command."""
await self.coordinator.client.async_media_seek(position)
async def async_select_sound_mode(self, sound_mode: str) -> None:
"""Send sound mode command."""
for pretty_name, mode in SOUND_MODES.items():
if sound_mode == pretty_name:
if mode == "night mode":
await self.coordinator.client.async_set_night_mode(True)
else:
await self.coordinator.client.async_set_night_mode(False)
await self.coordinator.client.async_set_equalizer(mode)
async def async_turn_off(self) -> None:
"""Turn off media player."""
await self.coordinator.client.async_turn_off()
async def async_select_source(self, source: str) -> None:
"""Select input source."""
await self.coordinator.client.async_select_source(source)

View File

@@ -0,0 +1,22 @@
{
"config": {
"flow_title": "{title}",
"step": {
"user": {
"description": "Please enter the host name or IP address of the Devialet device.",
"data": {
"host": "Host"
}
},
"confirm": {
"description": "Do you want to set up Devialet device {device}?"
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}
}

View File

@@ -0,0 +1,22 @@
{
"config": {
"abort": {
"already_configured": "Service is already configured"
},
"error": {
"cannot_connect": "Failed to connect"
},
"flow_title": "{title}",
"step": {
"confirm": {
"description": "Do you want to set up Devialet device {device}?"
},
"user": {
"data": {
"host": "Host"
},
"description": "Please enter the host name or IP address of the Devialet device."
}
}
}
}

View File

@@ -1033,6 +1033,19 @@ def update_config(path: str, dev_id: str, device: Device) -> None:
out.write(dump(device_config))
def remove_device_from_config(hass: HomeAssistant, device_id: str) -> None:
"""Remove device from YAML configuration file."""
path = hass.config.path(YAML_DEVICES)
devices = load_yaml_config_file(path)
devices.pop(device_id)
dumped = dump(devices)
with open(path, "r+", encoding="utf8") as out:
out.seek(0)
out.truncate()
out.write(dumped)
def get_gravatar_for_email(email: str) -> str:
"""Return an 80px Gravatar for the given email address.

View File

@@ -8,6 +8,9 @@
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your DirectTV device."
}
}
},

View File

@@ -183,6 +183,7 @@ async def async_setup_entry(
for description in sensors
for value_key in {description.key, *description.alternative_keys}
if description.value_fn(coordinator.data, value_key, description.scale)
is not None
)
async_add_entities(entities)

View File

@@ -9,6 +9,7 @@
"use_legacy_protocol": "Use legacy protocol"
},
"data_description": {
"host": "The hostname or IP address of your D-Link device",
"password": "Default: PIN code on the back."
}
},

View File

@@ -17,8 +17,11 @@
"data": {
"password": "[%key:common::config_flow::data::password%]",
"host": "[%key:common::config_flow::data::host%]",
"name": "Device Name",
"name": "Device name",
"username": "[%key:common::config_flow::data::username%]"
},
"data_description": {
"host": "The hostname or IP address of your DoorBird device."
}
}
},

View File

@@ -4,6 +4,9 @@
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your Dremel 3D printer."
}
}
},

View File

@@ -12,7 +12,6 @@ LOGGER = logging.getLogger(__package__)
PLATFORMS = [Platform.SENSOR]
CONF_DSMR_VERSION = "dsmr_version"
CONF_PROTOCOL = "protocol"
CONF_RECONNECT_INTERVAL = "reconnect_interval"
CONF_PRECISION = "precision"
CONF_TIME_BETWEEN_UPDATE = "time_between_update"
@@ -29,6 +28,7 @@ DATA_TASK = "task"
DEVICE_NAME_ELECTRICITY = "Electricity Meter"
DEVICE_NAME_GAS = "Gas Meter"
DEVICE_NAME_WATER = "Water Meter"
DSMR_VERSIONS = {"2.2", "4", "5", "5B", "5L", "5S", "Q3D"}

View File

@@ -34,6 +34,7 @@ from homeassistant.const import (
UnitOfVolume,
)
from homeassistant.core import CoreState, Event, HomeAssistant, callback
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
@@ -47,7 +48,6 @@ from .const import (
CONF_DSMR_VERSION,
CONF_PRECISION,
CONF_PROTOCOL,
CONF_RECONNECT_INTERVAL,
CONF_SERIAL_ID,
CONF_SERIAL_ID_GAS,
CONF_TIME_BETWEEN_UPDATE,
@@ -57,6 +57,7 @@ from .const import (
DEFAULT_TIME_BETWEEN_UPDATE,
DEVICE_NAME_ELECTRICITY,
DEVICE_NAME_GAS,
DEVICE_NAME_WATER,
DOMAIN,
DSMR_PROTOCOL,
LOGGER,
@@ -73,6 +74,7 @@ class DSMRSensorEntityDescription(SensorEntityDescription):
dsmr_versions: set[str] | None = None
is_gas: bool = False
is_water: bool = False
obis_reference: str
@@ -374,28 +376,138 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = (
)
def add_gas_sensor_5B(telegram: dict[str, DSMRObject]) -> DSMRSensorEntityDescription:
"""Return correct entity for 5B Gas meter."""
ref = None
if obis_references.BELGIUM_MBUS1_METER_READING2 in telegram:
ref = obis_references.BELGIUM_MBUS1_METER_READING2
elif obis_references.BELGIUM_MBUS2_METER_READING2 in telegram:
ref = obis_references.BELGIUM_MBUS2_METER_READING2
elif obis_references.BELGIUM_MBUS3_METER_READING2 in telegram:
ref = obis_references.BELGIUM_MBUS3_METER_READING2
elif obis_references.BELGIUM_MBUS4_METER_READING2 in telegram:
ref = obis_references.BELGIUM_MBUS4_METER_READING2
elif ref is None:
ref = obis_references.BELGIUM_MBUS1_METER_READING2
return DSMRSensorEntityDescription(
key="belgium_5min_gas_meter_reading",
translation_key="gas_meter_reading",
obis_reference=ref,
dsmr_versions={"5B"},
is_gas=True,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.TOTAL_INCREASING,
)
def create_mbus_entity(
mbus: int, mtype: int, telegram: dict[str, DSMRObject]
) -> DSMRSensorEntityDescription | None:
"""Create a new MBUS Entity."""
if (
mtype == 3
and (
obis_reference := getattr(
obis_references, f"BELGIUM_MBUS{mbus}_METER_READING2"
)
)
in telegram
):
return DSMRSensorEntityDescription(
key=f"mbus{mbus}_gas_reading",
translation_key="gas_meter_reading",
obis_reference=obis_reference,
is_gas=True,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.TOTAL_INCREASING,
)
if (
mtype == 7
and (
obis_reference := getattr(
obis_references, f"BELGIUM_MBUS{mbus}_METER_READING1"
)
)
in telegram
):
return DSMRSensorEntityDescription(
key=f"mbus{mbus}_water_reading",
translation_key="water_meter_reading",
obis_reference=obis_reference,
is_water=True,
device_class=SensorDeviceClass.WATER,
state_class=SensorStateClass.TOTAL_INCREASING,
)
return None
def device_class_and_uom(
telegram: dict[str, DSMRObject],
entity_description: DSMRSensorEntityDescription,
) -> tuple[SensorDeviceClass | None, str | None]:
"""Get native unit of measurement from telegram,."""
dsmr_object = telegram[entity_description.obis_reference]
uom: str | None = getattr(dsmr_object, "unit") or None
with suppress(ValueError):
if entity_description.device_class == SensorDeviceClass.GAS and (
enery_uom := UnitOfEnergy(str(uom))
):
return (SensorDeviceClass.ENERGY, enery_uom)
if uom in UNIT_CONVERSION:
return (entity_description.device_class, UNIT_CONVERSION[uom])
return (entity_description.device_class, uom)
def rename_old_gas_to_mbus(
hass: HomeAssistant, entry: ConfigEntry, mbus_device_id: str
) -> None:
"""Rename old gas sensor to mbus variant."""
dev_reg = dr.async_get(hass)
device_entry_v1 = dev_reg.async_get_device(identifiers={(DOMAIN, entry.entry_id)})
if device_entry_v1 is not None:
device_id = device_entry_v1.id
ent_reg = er.async_get(hass)
entries = er.async_entries_for_device(ent_reg, device_id)
for entity in entries:
if entity.unique_id.endswith("belgium_5min_gas_meter_reading"):
try:
ent_reg.async_update_entity(
entity.entity_id,
new_unique_id=mbus_device_id,
device_id=mbus_device_id,
)
except ValueError:
LOGGER.debug(
"Skip migration of %s because it already exists",
entity.entity_id,
)
else:
LOGGER.debug(
"Migrated entity %s from unique id %s to %s",
entity.entity_id,
entity.unique_id,
mbus_device_id,
)
# Cleanup old device
dev_entities = er.async_entries_for_device(
ent_reg, device_id, include_disabled_entities=True
)
if not dev_entities:
dev_reg.async_remove_device(device_id)
def create_mbus_entities(
hass: HomeAssistant, telegram: dict[str, DSMRObject], entry: ConfigEntry
) -> list[DSMREntity]:
"""Create MBUS Entities."""
entities = []
for idx in range(1, 5):
if (
device_type := getattr(obis_references, f"BELGIUM_MBUS{idx}_DEVICE_TYPE")
) not in telegram:
continue
if (type_ := int(telegram[device_type].value)) not in (3, 7):
continue
if (
identifier := getattr(
obis_references,
f"BELGIUM_MBUS{idx}_EQUIPMENT_IDENTIFIER",
)
) in telegram:
serial_ = telegram[identifier].value
rename_old_gas_to_mbus(hass, entry, serial_)
else:
serial_ = ""
if description := create_mbus_entity(idx, type_, telegram):
entities.append(
DSMREntity(
description,
entry,
telegram,
*device_class_and_uom(telegram, description), # type: ignore[arg-type]
serial_,
idx,
)
)
return entities
async def async_setup_entry(
@@ -415,25 +527,10 @@ async def async_setup_entry(
add_entities_handler()
add_entities_handler = None
def device_class_and_uom(
telegram: dict[str, DSMRObject],
entity_description: DSMRSensorEntityDescription,
) -> tuple[SensorDeviceClass | None, str | None]:
"""Get native unit of measurement from telegram,."""
dsmr_object = telegram[entity_description.obis_reference]
uom: str | None = getattr(dsmr_object, "unit") or None
with suppress(ValueError):
if entity_description.device_class == SensorDeviceClass.GAS and (
enery_uom := UnitOfEnergy(str(uom))
):
return (SensorDeviceClass.ENERGY, enery_uom)
if uom in UNIT_CONVERSION:
return (entity_description.device_class, UNIT_CONVERSION[uom])
return (entity_description.device_class, uom)
all_sensors = SENSORS
if dsmr_version == "5B":
all_sensors += (add_gas_sensor_5B(telegram),)
mbus_entities = create_mbus_entities(hass, telegram, entry)
for mbus_entity in mbus_entities:
entities.append(mbus_entity)
entities.extend(
[
@@ -443,7 +540,7 @@ async def async_setup_entry(
telegram,
*device_class_and_uom(telegram, description), # type: ignore[arg-type]
)
for description in all_sensors
for description in SENSORS
if (
description.dsmr_versions is None
or dsmr_version in description.dsmr_versions
@@ -549,9 +646,7 @@ async def async_setup_entry(
update_entities_telegram(None)
# throttle reconnect attempts
await asyncio.sleep(
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
)
await asyncio.sleep(DEFAULT_RECONNECT_INTERVAL)
except (serial.serialutil.SerialException, OSError):
# Log any error while establishing connection and drop to retry
@@ -565,9 +660,7 @@ async def async_setup_entry(
update_entities_telegram(None)
# throttle reconnect attempts
await asyncio.sleep(
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
)
await asyncio.sleep(DEFAULT_RECONNECT_INTERVAL)
except CancelledError:
# Reflect disconnect state in devices state by setting an
# None telegram resulting in `unavailable` states
@@ -618,6 +711,8 @@ class DSMREntity(SensorEntity):
telegram: dict[str, DSMRObject],
device_class: SensorDeviceClass,
native_unit_of_measurement: str | None,
serial_id: str = "",
mbus_id: int = 0,
) -> None:
"""Initialize entity."""
self.entity_description = entity_description
@@ -629,8 +724,15 @@ class DSMREntity(SensorEntity):
device_serial = entry.data[CONF_SERIAL_ID]
device_name = DEVICE_NAME_ELECTRICITY
if entity_description.is_gas:
device_serial = entry.data[CONF_SERIAL_ID_GAS]
if serial_id:
device_serial = serial_id
else:
device_serial = entry.data[CONF_SERIAL_ID_GAS]
device_name = DEVICE_NAME_GAS
if entity_description.is_water:
if serial_id:
device_serial = serial_id
device_name = DEVICE_NAME_WATER
if device_serial is None:
device_serial = entry.entry_id
@@ -638,7 +740,13 @@ class DSMREntity(SensorEntity):
identifiers={(DOMAIN, device_serial)},
name=device_name,
)
self._attr_unique_id = f"{device_serial}_{entity_description.key}"
if mbus_id != 0:
if serial_id:
self._attr_unique_id = f"{device_serial}"
else:
self._attr_unique_id = f"{device_serial}_{mbus_id}"
else:
self._attr_unique_id = f"{device_serial}_{entity_description.key}"
@callback
def update_data(self, telegram: dict[str, DSMRObject] | None) -> None:
@@ -686,6 +794,10 @@ class DSMREntity(SensorEntity):
float(value), self._entry.data.get(CONF_PRECISION, DEFAULT_PRECISION)
)
# Make sure we do not return a zero value for an energy sensor
if not value and self.state_class == SensorStateClass.TOTAL_INCREASING:
return None
return value
@staticmethod

View File

@@ -147,6 +147,9 @@
},
"voltage_swell_l3_count": {
"name": "Voltage swells phase L3"
},
"water_meter_reading": {
"name": "Water consumption"
}
}
},

View File

@@ -5,6 +5,9 @@
"description": "Ensure that your player is turned on.",
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your Dune HD device."
}
}
},

View File

@@ -6,6 +6,9 @@
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your Duotecno device."
}
}
},

View File

@@ -6,6 +6,9 @@
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your Ecoforest device."
}
}
},

View File

@@ -7,6 +7,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of your Elgato device."
}
},
"zeroconf_confirm": {

View File

@@ -5,6 +5,9 @@
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your SiteSage Emonitor device."
}
},
"confirm": {

View File

@@ -6,7 +6,6 @@ import logging
from aiohttp import web
import voluptuous as vol
from homeassistant.components.http import HomeAssistantAccessLogger
from homeassistant.components.network import async_get_source_ip
from homeassistant.const import (
CONF_ENTITIES,
@@ -101,7 +100,7 @@ async def start_emulated_hue_bridge(
config.advertise_port or config.listen_port,
)
runner = web.AppRunner(app, access_log_class=HomeAssistantAccessLogger)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, config.host_ip_addr, config.listen_port)

View File

@@ -317,6 +317,11 @@ class EnergyCostSensor(SensorEntity):
try:
energy_price = float(energy_price_state.state)
except ValueError:
if self._last_energy_sensor_state is None:
# Initialize as it's the first time all required entities except
# price are in place. This means that the cost will update the first
# time the energy is updated after the price entity is in place.
self._reset(energy_state)
return
energy_price_unit: str | None = energy_price_state.attributes.get(

View File

@@ -8,6 +8,9 @@
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your Enphase Envoy gateway."
}
}
},

View File

@@ -5,6 +5,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"name": "[%key:common::config_flow::data::name%]"
},
"data_description": {
"host": "The hostname or IP address of your Epson projector."
}
}
},

View File

@@ -22,6 +22,7 @@ from aioesphomeapi import (
APIClient,
APIVersion,
BLEConnectionError,
BluetoothConnectionDroppedError,
BluetoothProxyFeature,
DeviceInfo,
)
@@ -30,7 +31,6 @@ from aioesphomeapi.core import (
BluetoothGATTAPIError,
TimeoutAPIError,
)
from async_interrupt import interrupt
from bleak.backends.characteristic import BleakGATTCharacteristic
from bleak.backends.client import BaseBleakClient, NotifyCallback
from bleak.backends.device import BLEDevice
@@ -68,39 +68,25 @@ def mac_to_int(address: str) -> int:
return int(address.replace(":", ""), 16)
def verify_connected(func: _WrapFuncType) -> _WrapFuncType:
"""Define a wrapper throw BleakError if not connected."""
async def _async_wrap_bluetooth_connected_operation(
self: ESPHomeClient, *args: Any, **kwargs: Any
) -> Any:
# pylint: disable=protected-access
if not self._is_connected:
raise BleakError(f"{self._description} is not connected")
loop = self._loop
disconnected_futures = self._disconnected_futures
disconnected_future = loop.create_future()
disconnected_futures.add(disconnected_future)
disconnect_message = f"{self._description}: Disconnected during operation"
try:
async with interrupt(disconnected_future, BleakError, disconnect_message):
return await func(self, *args, **kwargs)
finally:
disconnected_futures.discard(disconnected_future)
return cast(_WrapFuncType, _async_wrap_bluetooth_connected_operation)
def api_error_as_bleak_error(func: _WrapFuncType) -> _WrapFuncType:
"""Define a wrapper throw esphome api errors as BleakErrors."""
async def _async_wrap_bluetooth_operation(
self: ESPHomeClient, *args: Any, **kwargs: Any
) -> Any:
# pylint: disable=protected-access
try:
return await func(self, *args, **kwargs)
except TimeoutAPIError as err:
raise asyncio.TimeoutError(str(err)) from err
except BluetoothConnectionDroppedError as ex:
_LOGGER.debug(
"%s: BLE device disconnected during %s operation",
self._description,
func.__name__,
)
self._async_ble_device_disconnected()
raise BleakError(str(ex)) from ex
except BluetoothGATTAPIError as ex:
# If the device disconnects in the middle of an operation
# be sure to mark it as disconnected so any library using
@@ -111,7 +97,6 @@ def api_error_as_bleak_error(func: _WrapFuncType) -> _WrapFuncType:
# before the callback is delivered.
if ex.error.error == -1:
# pylint: disable=protected-access
_LOGGER.debug(
"%s: BLE device disconnected during %s operation",
self._description,
@@ -169,7 +154,6 @@ class ESPHomeClient(BaseBleakClient):
self._notify_cancels: dict[
int, tuple[Callable[[], Coroutine[Any, Any, None]], Callable[[], None]]
] = {}
self._disconnected_futures: set[asyncio.Future[None]] = set()
self._device_info = client_data.device_info
self._feature_flags = device_info.bluetooth_proxy_feature_flags_compat(
client_data.api_version
@@ -185,24 +169,7 @@ class ESPHomeClient(BaseBleakClient):
def __str__(self) -> str:
"""Return the string representation of the client."""
return f"ESPHomeClient ({self.address})"
def _unsubscribe_connection_state(self) -> None:
"""Unsubscribe from connection state updates."""
if not self._cancel_connection_state:
return
try:
self._cancel_connection_state()
except (AssertionError, ValueError) as ex:
_LOGGER.debug(
(
"%s: Failed to unsubscribe from connection state (likely"
" connection dropped): %s"
),
self._description,
ex,
)
self._cancel_connection_state = None
return f"ESPHomeClient ({self._description})"
def _async_disconnected_cleanup(self) -> None:
"""Clean up on disconnect."""
@@ -211,12 +178,10 @@ class ESPHomeClient(BaseBleakClient):
for _, notify_abort in self._notify_cancels.values():
notify_abort()
self._notify_cancels.clear()
for future in self._disconnected_futures:
if not future.done():
future.set_result(None)
self._disconnected_futures.clear()
self._disconnect_callbacks.discard(self._async_esp_disconnected)
self._unsubscribe_connection_state()
if self._cancel_connection_state:
self._cancel_connection_state()
self._cancel_connection_state = None
def _async_ble_device_disconnected(self) -> None:
"""Handle the BLE device disconnecting from the ESP."""
@@ -406,7 +371,6 @@ class ESPHomeClient(BaseBleakClient):
"""Get ATT MTU size for active connection."""
return self._mtu or DEFAULT_MTU
@verify_connected
@api_error_as_bleak_error
async def pair(self, *args: Any, **kwargs: Any) -> bool:
"""Attempt to pair."""
@@ -415,6 +379,7 @@ class ESPHomeClient(BaseBleakClient):
"Pairing is not available in this version ESPHome; "
f"Upgrade the ESPHome version on the {self._device_info.name} device."
)
self._raise_if_not_connected()
response = await self._client.bluetooth_device_pair(self._address_as_int)
if response.paired:
return True
@@ -423,7 +388,6 @@ class ESPHomeClient(BaseBleakClient):
)
return False
@verify_connected
@api_error_as_bleak_error
async def unpair(self) -> bool:
"""Attempt to unpair."""
@@ -432,6 +396,7 @@ class ESPHomeClient(BaseBleakClient):
"Unpairing is not available in this version ESPHome; "
f"Upgrade the ESPHome version on the {self._device_info.name} device."
)
self._raise_if_not_connected()
response = await self._client.bluetooth_device_unpair(self._address_as_int)
if response.success:
return True
@@ -454,7 +419,6 @@ class ESPHomeClient(BaseBleakClient):
dangerous_use_bleak_cache=dangerous_use_bleak_cache, **kwargs
)
@verify_connected
async def _get_services(
self, dangerous_use_bleak_cache: bool = False, **kwargs: Any
) -> BleakGATTServiceCollection:
@@ -462,6 +426,7 @@ class ESPHomeClient(BaseBleakClient):
Must only be called from get_services or connected
"""
self._raise_if_not_connected()
address_as_int = self._address_as_int
cache = self._cache
# If the connection version >= 3, we must use the cache
@@ -527,7 +492,6 @@ class ESPHomeClient(BaseBleakClient):
)
return characteristic
@verify_connected
@api_error_as_bleak_error
async def clear_cache(self) -> bool:
"""Clear the GATT cache."""
@@ -541,6 +505,7 @@ class ESPHomeClient(BaseBleakClient):
self._device_info.name,
)
return True
self._raise_if_not_connected()
response = await self._client.bluetooth_device_clear_cache(self._address_as_int)
if response.success:
return True
@@ -551,7 +516,6 @@ class ESPHomeClient(BaseBleakClient):
)
return False
@verify_connected
@api_error_as_bleak_error
async def read_gatt_char(
self,
@@ -570,12 +534,12 @@ class ESPHomeClient(BaseBleakClient):
Returns:
(bytearray) The read data.
"""
self._raise_if_not_connected()
characteristic = self._resolve_characteristic(char_specifier)
return await self._client.bluetooth_gatt_read(
self._address_as_int, characteristic.handle, GATT_READ_TIMEOUT
)
@verify_connected
@api_error_as_bleak_error
async def read_gatt_descriptor(self, handle: int, **kwargs: Any) -> bytearray:
"""Perform read operation on the specified GATT descriptor.
@@ -587,11 +551,11 @@ class ESPHomeClient(BaseBleakClient):
Returns:
(bytearray) The read data.
"""
self._raise_if_not_connected()
return await self._client.bluetooth_gatt_read_descriptor(
self._address_as_int, handle, GATT_READ_TIMEOUT
)
@verify_connected
@api_error_as_bleak_error
async def write_gatt_char(
self,
@@ -610,12 +574,12 @@ class ESPHomeClient(BaseBleakClient):
response (bool): If write-with-response operation should be done.
Defaults to `False`.
"""
self._raise_if_not_connected()
characteristic = self._resolve_characteristic(characteristic)
await self._client.bluetooth_gatt_write(
self._address_as_int, characteristic.handle, bytes(data), response
)
@verify_connected
@api_error_as_bleak_error
async def write_gatt_descriptor(self, handle: int, data: Buffer) -> None:
"""Perform a write operation on the specified GATT descriptor.
@@ -624,11 +588,11 @@ class ESPHomeClient(BaseBleakClient):
handle (int): The handle of the descriptor to read from.
data (bytes or bytearray): The data to send.
"""
self._raise_if_not_connected()
await self._client.bluetooth_gatt_write_descriptor(
self._address_as_int, handle, bytes(data)
)
@verify_connected
@api_error_as_bleak_error
async def start_notify(
self,
@@ -655,6 +619,7 @@ class ESPHomeClient(BaseBleakClient):
callback (function): The function to be called on notification.
kwargs: Unused.
"""
self._raise_if_not_connected()
ble_handle = characteristic.handle
if ble_handle in self._notify_cancels:
raise BleakError(
@@ -709,7 +674,6 @@ class ESPHomeClient(BaseBleakClient):
wait_for_response=False,
)
@verify_connected
@api_error_as_bleak_error
async def stop_notify(
self,
@@ -723,6 +687,7 @@ class ESPHomeClient(BaseBleakClient):
specified by either integer handle, UUID or directly by the
BleakGATTCharacteristic object representing it.
"""
self._raise_if_not_connected()
characteristic = self._resolve_characteristic(char_specifier)
# Do not raise KeyError if notifications are not enabled on this characteristic
# to be consistent with the behavior of the BlueZ backend
@@ -730,6 +695,11 @@ class ESPHomeClient(BaseBleakClient):
notify_stop, _ = notify_cancel
await notify_stop()
def _raise_if_not_connected(self) -> None:
"""Raise a BleakError if not connected."""
if not self._is_connected:
raise BleakError(f"{self._description} is not connected")
def __del__(self) -> None:
"""Destructor to make sure the connection state is unsubscribed."""
if self._cancel_connection_state:

View File

@@ -164,11 +164,15 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
)
self._attr_min_temp = static_info.visual_min_temperature
self._attr_max_temp = static_info.visual_max_temperature
self._attr_min_humidity = round(static_info.visual_min_humidity)
self._attr_max_humidity = round(static_info.visual_max_humidity)
features = ClimateEntityFeature(0)
if self._static_info.supports_two_point_target_temperature:
features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
else:
features |= ClimateEntityFeature.TARGET_TEMPERATURE
if self._static_info.supports_target_humidity:
features |= ClimateEntityFeature.TARGET_HUMIDITY
if self.preset_modes:
features |= ClimateEntityFeature.PRESET_MODE
if self.fan_modes:
@@ -234,6 +238,14 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
"""Return the current temperature."""
return self._state.current_temperature
@property
@esphome_state_property
def current_humidity(self) -> int | None:
"""Return the current humidity."""
if not self._static_info.supports_current_humidity:
return None
return round(self._state.current_humidity)
@property
@esphome_state_property
def target_temperature(self) -> float | None:
@@ -252,6 +264,12 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
"""Return the highbound target temperature we try to reach."""
return self._state.target_temperature_high
@property
@esphome_state_property
def target_humidity(self) -> int:
"""Return the humidity we try to reach."""
return round(self._state.target_humidity)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature (and operation mode if set)."""
data: dict[str, Any] = {"key": self._key}
@@ -267,6 +285,10 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
data["target_temperature_high"] = kwargs[ATTR_TARGET_TEMP_HIGH]
await self._client.climate_command(**data)
async def async_set_humidity(self, humidity: int) -> None:
"""Set new target humidity."""
await self._client.climate_command(key=self._key, target_humidity=humidity)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target operation mode."""
await self._client.climate_command(

View File

@@ -15,8 +15,7 @@
"iot_class": "local_push",
"loggers": ["aioesphomeapi", "noiseprotocol"],
"requirements": [
"async-interrupt==1.1.1",
"aioesphomeapi==19.1.4",
"aioesphomeapi==19.2.1",
"bluetooth-data-tools==1.15.0",
"esphome-dashboard-api==1.2.3"
],

View File

@@ -186,16 +186,22 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
data_to_send = {"text": event.data["tts_input"]}
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_TTS_END:
assert event.data is not None
path = event.data["tts_output"]["url"]
url = async_process_play_media_url(self.hass, path)
data_to_send = {"url": url}
tts_output = event.data["tts_output"]
if tts_output:
path = tts_output["url"]
url = async_process_play_media_url(self.hass, path)
data_to_send = {"url": url}
if self.device_info.voice_assistant_version >= 2:
media_id = event.data["tts_output"]["media_id"]
self._tts_task = self.hass.async_create_background_task(
self._send_tts(media_id), "esphome_voice_assistant_tts"
)
if self.device_info.voice_assistant_version >= 2:
media_id = tts_output["media_id"]
self._tts_task = self.hass.async_create_background_task(
self._send_tts(media_id), "esphome_voice_assistant_tts"
)
else:
self._tts_done.set()
else:
# Empty TTS response
data_to_send = {}
self._tts_done.set()
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_WAKE_WORD_END:
assert event.data is not None

View File

@@ -4,6 +4,9 @@
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "The hostname or IP address of your Evil Genius Labs device."
}
}
},

View File

@@ -18,7 +18,8 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA,
@@ -77,8 +78,19 @@ ATTR_PRESET_MODES = "preset_modes"
# mypy: disallow-any-generics
class NotValidPresetModeError(ValueError):
"""Exception class when the preset_mode in not in the preset_modes list."""
class NotValidPresetModeError(ServiceValidationError):
"""Raised when the preset_mode is not in the preset_modes list."""
def __init__(
self, *args: object, translation_placeholders: dict[str, str] | None = None
) -> None:
"""Initialize the exception."""
super().__init__(
*args,
translation_domain=DOMAIN,
translation_key="not_valid_preset_mode",
translation_placeholders=translation_placeholders,
)
@bind_hass
@@ -107,7 +119,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
),
vol.Optional(ATTR_PRESET_MODE): cv.string,
},
"async_turn_on",
"async_handle_turn_on_service",
)
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
@@ -156,7 +168,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
component.async_register_entity_service(
SERVICE_SET_PRESET_MODE,
{vol.Required(ATTR_PRESET_MODE): cv.string},
"async_set_preset_mode",
"async_handle_set_preset_mode_service",
[FanEntityFeature.SET_SPEED, FanEntityFeature.PRESET_MODE],
)
@@ -237,17 +249,30 @@ class FanEntity(ToggleEntity):
"""Set new preset mode."""
raise NotImplementedError()
@final
async def async_handle_set_preset_mode_service(self, preset_mode: str) -> None:
"""Validate and set new preset mode."""
self._valid_preset_mode_or_raise(preset_mode)
await self.async_set_preset_mode(preset_mode)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
await self.hass.async_add_executor_job(self.set_preset_mode, preset_mode)
@final
@callback
def _valid_preset_mode_or_raise(self, preset_mode: str) -> None:
"""Raise NotValidPresetModeError on invalid preset_mode."""
preset_modes = self.preset_modes
if not preset_modes or preset_mode not in preset_modes:
preset_modes_str: str = ", ".join(preset_modes or [])
raise NotValidPresetModeError(
f"The preset_mode {preset_mode} is not a valid preset_mode:"
f" {preset_modes}"
f" {preset_modes}",
translation_placeholders={
"preset_mode": preset_mode,
"preset_modes": preset_modes_str,
},
)
def set_direction(self, direction: str) -> None:
@@ -267,6 +292,18 @@ class FanEntity(ToggleEntity):
"""Turn on the fan."""
raise NotImplementedError()
@final
async def async_handle_turn_on_service(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any,
) -> None:
"""Validate and turn on the fan."""
if preset_mode is not None:
self._valid_preset_mode_or_raise(preset_mode)
await self.async_turn_on(percentage, preset_mode, **kwargs)
async def async_turn_on(
self,
percentage: int | None = None,

View File

@@ -144,5 +144,10 @@
"reverse": "Reverse"
}
}
},
"exceptions": {
"not_valid_preset_mode": {
"message": "Preset mode {preset_mode} is not valid, valid preset modes are: {preset_modes}."
}
}
}

View File

@@ -24,7 +24,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Fast.com sensor."""
async_add_entities([SpeedtestSensor(hass.data[DOMAIN])])
async_add_entities([SpeedtestSensor(entry.entry_id, hass.data[DOMAIN])])
# pylint: disable-next=hass-invalid-inheritance # needs fixing
@@ -38,9 +38,10 @@ class SpeedtestSensor(RestoreEntity, SensorEntity):
_attr_icon = "mdi:speedometer"
_attr_should_poll = False
def __init__(self, speedtest_data: dict[str, Any]) -> None:
def __init__(self, entry_id: str, speedtest_data: dict[str, Any]) -> None:
"""Initialize the sensor."""
self._speedtest_data = speedtest_data
self._attr_unique_id = entry_id
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""

View File

@@ -6,6 +6,9 @@
"name": "[%key:common::config_flow::data::name%]",
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of your FiveM server."
}
}
},

View File

@@ -131,11 +131,9 @@ class Fan(CoordinatorEntity[FjaraskupanCoordinator], FanEntity):
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
if command := PRESET_TO_COMMAND.get(preset_mode):
async with self.coordinator.async_connect_and_update() as device:
await device.send_command(command)
else:
raise UnsupportedPreset(f"The preset {preset_mode} is unsupported")
command = PRESET_TO_COMMAND[preset_mode]
async with self.coordinator.async_connect_and_update() as device:
await device.send_command(command)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""

View File

@@ -6,6 +6,9 @@
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your Flo device."
}
}
},

View File

@@ -9,6 +9,9 @@
"password": "[%key:common::config_flow::data::password%]",
"rtsp_port": "RTSP port",
"stream": "Stream"
},
"data_description": {
"host": "The hostname or IP address of your Foscam camera."
}
}
},

View File

@@ -5,6 +5,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of your Freebox router."
}
},
"link": {

View File

@@ -26,6 +26,9 @@
"port": "[%key:common::config_flow::data::port%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your FRITZ!Box router."
}
}
},

View File

@@ -38,11 +38,9 @@ async def async_setup_entry(
FritzboxLight(
coordinator,
ain,
device.get_colors(),
device.get_color_temps(),
)
for ain in coordinator.new_devices
if (device := coordinator.data.devices[ain]).has_lightbulb
if (coordinator.data.devices[ain]).has_lightbulb
)
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
@@ -57,27 +55,10 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
self,
coordinator: FritzboxDataUpdateCoordinator,
ain: str,
supported_colors: dict,
supported_color_temps: list[int],
) -> None:
"""Initialize the FritzboxLight entity."""
super().__init__(coordinator, ain, None)
if supported_color_temps:
# only available for color bulbs
self._attr_max_color_temp_kelvin = int(max(supported_color_temps))
self._attr_min_color_temp_kelvin = int(min(supported_color_temps))
# Fritz!DECT 500 only supports 12 values for hue, with 3 saturations each.
# Map supported colors to dict {hue: [sat1, sat2, sat3]} for easier lookup
self._supported_hs: dict[int, list[int]] = {}
for values in supported_colors.values():
hue = int(values[0][0])
self._supported_hs[hue] = [
int(values[0][1]),
int(values[1][1]),
int(values[2][1]),
]
@property
def is_on(self) -> bool:
@@ -173,3 +154,28 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
"""Turn the light off."""
await self.hass.async_add_executor_job(self.data.set_state_off)
await self.coordinator.async_refresh()
async def async_added_to_hass(self) -> None:
"""Get light attributes from device after entity is added to hass."""
await super().async_added_to_hass()
supported_colors = await self.hass.async_add_executor_job(
self.coordinator.data.devices[self.ain].get_colors
)
supported_color_temps = await self.hass.async_add_executor_job(
self.coordinator.data.devices[self.ain].get_color_temps
)
if supported_color_temps:
# only available for color bulbs
self._attr_max_color_temp_kelvin = int(max(supported_color_temps))
self._attr_min_color_temp_kelvin = int(min(supported_color_temps))
# Fritz!DECT 500 only supports 12 values for hue, with 3 saturations each.
# Map supported colors to dict {hue: [sat1, sat2, sat3]} for easier lookup
for values in supported_colors.values():
hue = int(values[0][0])
self._supported_hs[hue] = [
int(values[0][1]),
int(values[1][1]),
int(values[2][1]),
]

View File

@@ -8,6 +8,9 @@
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your FRITZ!Box router."
}
},
"confirm": {

View File

@@ -8,6 +8,9 @@
"port": "[%key:common::config_flow::data::port%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "The hostname or IP address of your FRITZ!Box router."
}
},
"phonebook": {

View File

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

View File

@@ -5,10 +5,13 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of your Frontier Silicon device."
}
},
"device_config": {
"title": "Device Configuration",
"title": "Device configuration",
"description": "The pin can be found via 'MENU button > Main Menu > System setting > Network > NetRemote PIN setup'",
"data": {
"pin": "[%key:common::config_flow::data::pin%]"

View File

@@ -19,13 +19,14 @@ class FullyKioskDataUpdateCoordinator(DataUpdateCoordinator):
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize."""
self.use_ssl = entry.data.get(CONF_SSL, False)
self.fully = FullyKiosk(
async_get_clientsession(hass),
entry.data[CONF_HOST],
DEFAULT_PORT,
entry.data[CONF_PASSWORD],
use_ssl=entry.data[CONF_SSL],
verify_ssl=entry.data[CONF_VERIFY_SSL],
use_ssl=self.use_ssl,
verify_ssl=entry.data.get(CONF_VERIFY_SSL, False),
)
super().__init__(
hass,
@@ -33,7 +34,6 @@ class FullyKioskDataUpdateCoordinator(DataUpdateCoordinator):
name=entry.data[CONF_HOST],
update_interval=UPDATE_INTERVAL,
)
self.use_ssl = entry.data[CONF_SSL]
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""

View File

@@ -13,6 +13,9 @@
"password": "[%key:common::config_flow::data::password%]",
"ssl": "[%key:common::config_flow::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"host": "The hostname or IP address of the device running your Fully Kiosk Browser application."
}
}
},

View File

@@ -68,9 +68,12 @@ class GeniusSwitch(GeniusZone, SwitchEntity):
def is_on(self) -> bool:
"""Return the current state of the on/off zone.
The zone is considered 'on' if & only if it is override/on (e.g. timer/on is 'off').
The zone is considered 'on' if the mode is either 'override' or 'timer'.
"""
return self._zone.data["mode"] == "override" and self._zone.data["setpoint"]
return (
self._zone.data["mode"] in ["override", "timer"]
and self._zone.data["setpoint"]
)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Send the zone to Timer mode.

View File

@@ -10,6 +10,9 @@
"version": "Glances API Version (2 or 3)",
"ssl": "[%key:common::config_flow::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"host": "The hostname or IP address of the system running your Glances system monitor."
}
},
"reauth_confirm": {

View File

@@ -6,6 +6,9 @@
"data": {
"host": "[%key:common::config_flow::data::host%]",
"name": "[%key:common::config_flow::data::name%]"
},
"data_description": {
"host": "The hostname or IP address of your Goal Zero Yeti."
}
},
"confirm_discovery": {

View File

@@ -686,8 +686,12 @@ class GoogleEntity:
return device
# Add Matter info
if "matter" in self.hass.config.components and (
matter_info := matter.get_matter_device_info(self.hass, device_entry.id)
if (
"matter" in self.hass.config.components
and any(x for x in device_entry.identifiers if x[0] == "matter")
and (
matter_info := matter.get_matter_device_info(self.hass, device_entry.id)
)
):
device["matterUniqueId"] = matter_info["unique_id"]
device["matterOriginalVendorId"] = matter_info["vendor_id"]

View File

@@ -1,7 +1,7 @@
"""Google Tasks todo platform."""
from __future__ import annotations
from datetime import timedelta
from datetime import date, datetime, timedelta
from typing import Any, cast
from homeassistant.components.todo import (
@@ -14,6 +14,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt as dt_util
from .api import AsyncConfigEntryAuth
from .const import DOMAIN
@@ -35,9 +36,31 @@ def _convert_todo_item(item: TodoItem) -> dict[str, str]:
result["title"] = item.summary
if item.status is not None:
result["status"] = TODO_STATUS_MAP_INV[item.status]
if (due := item.due) is not None:
# due API field is a timestamp string, but with only date resolution
result["due"] = dt_util.start_of_local_day(due).isoformat()
if (description := item.description) is not None:
result["notes"] = description
return result
def _convert_api_item(item: dict[str, str]) -> TodoItem:
"""Convert tasks API items into a TodoItem."""
due: date | None = None
if (due_str := item.get("due")) is not None:
due = datetime.fromisoformat(due_str).date()
return TodoItem(
summary=item["title"],
uid=item["id"],
status=TODO_STATUS_MAP.get(
item.get("status", ""),
TodoItemStatus.NEEDS_ACTION,
),
due=due,
description=item.get("notes"),
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
@@ -68,6 +91,8 @@ class GoogleTaskTodoListEntity(
TodoListEntityFeature.CREATE_TODO_ITEM
| TodoListEntityFeature.UPDATE_TODO_ITEM
| TodoListEntityFeature.DELETE_TODO_ITEM
| TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
)
def __init__(
@@ -88,17 +113,7 @@ class GoogleTaskTodoListEntity(
"""Get the current set of To-do items."""
if self.coordinator.data is None:
return None
return [
TodoItem(
summary=item["title"],
uid=item["id"],
status=TODO_STATUS_MAP.get(
item.get("status"), # type: ignore[arg-type]
TodoItemStatus.NEEDS_ACTION,
),
)
for item in _order_tasks(self.coordinator.data)
]
return [_convert_api_item(item) for item in _order_tasks(self.coordinator.data)]
async def async_create_todo_item(self, item: TodoItem) -> None:
"""Add an item to the To-do list."""

View File

@@ -11,7 +11,6 @@ from homeassistant.helpers.event import async_track_time_interval
from .bridge import DiscoveryService
from .const import (
COORDINATORS,
DATA_DISCOVERY_INTERVAL,
DATA_DISCOVERY_SERVICE,
DISCOVERY_SCAN_INTERVAL,
DISPATCHERS,
@@ -29,7 +28,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
gree_discovery = DiscoveryService(hass)
hass.data[DATA_DISCOVERY_SERVICE] = gree_discovery
hass.data[DOMAIN].setdefault(DISPATCHERS, [])
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async def _async_scan_update(_=None):
@@ -39,8 +37,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.debug("Scanning network for Gree devices")
await _async_scan_update()
hass.data[DOMAIN][DATA_DISCOVERY_INTERVAL] = async_track_time_interval(
hass, _async_scan_update, timedelta(seconds=DISCOVERY_SCAN_INTERVAL)
entry.async_on_unload(
async_track_time_interval(
hass, _async_scan_update, timedelta(seconds=DISCOVERY_SCAN_INTERVAL)
)
)
return True
@@ -48,13 +48,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if hass.data[DOMAIN].get(DISPATCHERS) is not None:
for cleanup in hass.data[DOMAIN][DISPATCHERS]:
cleanup()
if hass.data[DOMAIN].get(DATA_DISCOVERY_INTERVAL) is not None:
hass.data[DOMAIN].pop(DATA_DISCOVERY_INTERVAL)()
if hass.data.get(DATA_DISCOVERY_SERVICE) is not None:
hass.data.pop(DATA_DISCOVERY_SERVICE)

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