Compare commits

..

347 Commits

Author SHA1 Message Date
Franck Nijhof 1ee39275fc 2024.2.3 (#111133) 2024-02-22 16:08:18 +01:00
Franck Nijhof 728399c09a Revert "Fix Shelly RPC RSSI sensor removal (#111035)"
This reverts commit 4eb1bac859.
2024-02-22 12:05:24 +01:00
Franck Nijhof c76c66c466 Bump version to 2024.2.3 2024-02-22 11:48:04 +01:00
Joost Lekkerkerker 2f8d8eed84 Ignore cloudhook already removed in mobile app (#111122) 2024-02-22 11:46:29 +01:00
Robert Resch a372878aed Bump deebot-client to 5.2.2 (#111112) 2024-02-22 11:46:26 +01:00
Álvaro Fernández Rojas f5d7e6f506 Bump aioairzone to v0.7.4 (#111105)
* Update aioairzone to v0.7.3

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

* Update aioairzone to v0.7.4

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

---------

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2024-02-22 11:46:23 +01:00
Shay Levy 2cf87b8536 Bump pywebpush to 1.14.1 (#111082) 2024-02-22 11:46:20 +01:00
Christopher Bailey 68450bb594 Fixes UniFi Protect light state check (#111058) 2024-02-22 11:46:17 +01:00
G Johansson 4f5c808b40 Bump holidays to 0.43 (#111039) 2024-02-22 11:46:14 +01:00
Shay Levy 4eb1bac859 Fix Shelly RPC RSSI sensor removal (#111035) 2024-02-22 11:46:10 +01:00
starkillerOG f6bef0081b Bump motionblinds to 0.6.21 (#110970) 2024-02-22 11:45:18 +01:00
Michael f590f86cdd Reset error state when Ecovacs bot is operational again (#110962) 2024-02-22 11:45:15 +01:00
starkillerOG 85ad0a0e1e Bump reolink-aio to 0.8.8 (#110959) 2024-02-22 11:45:12 +01:00
Dominik Sander bd08807b53 Bump deluge-client to 1.10.2 (#110905)
This mainly addresses the client hanging and not reconnecting whenever
the deluge server is restarted. See https://github.com/JohnDoee/deluge-client/pull/42

https://github.com/JohnDoee/deluge-client/compare/1.10.0...1.10.2
2024-02-22 11:45:09 +01:00
Robert Hillis 715c6a554c Bump deluge-client to 1.10.0 (#110663) 2024-02-22 11:45:04 +01:00
starkillerOG e3074c6148 Reolink continue setup when internet blocked (#110888) 2024-02-22 11:43:20 +01:00
Chris Caron b7c97880e9 Allow loading of more then 1 defined Apprise URL (#110868) 2024-02-22 11:37:35 +01:00
Galorhallen 6745814e59 Update govee-local-api library to 1.4.4 (#110854) 2024-02-22 11:37:32 +01:00
Jan-Philipp Benecke adf24c970c Bump aiotankerkoenig to 0.4.1 (#110840) 2024-02-22 11:37:29 +01:00
Michael f13052f9aa Detect reached API rate limit in Tankerkoenig (#110432) 2024-02-22 11:37:23 +01:00
Aaron Bach 2a12369e31 Ensure Tile timestamps are reported as UTC (#110773) 2024-02-22 11:35:56 +01:00
Michael 76ec26b23d Bump roombapy to 1.6.12 (#110762)
* bump roombapy to 1.6.11

* Apply suggestions from code review

* "fix" tests (make them pydantic fiendly?)

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-02-22 11:35:52 +01:00
Steven Barth d85a92f5f1 Remove matplotlib pinning due to Python 3.12 incompatibility (#110706) 2024-02-22 11:35:49 +01:00
Michael ac74eb7854 Fix scene activation with climate entities with None attribute values (#110684)
don't call service with attribute None
2024-02-22 11:35:46 +01:00
Chris Talkington 8769921d70 Update rokuecp to 0.19.1 (#110670) 2024-02-22 11:35:43 +01:00
wilburCforce 54c5c94f97 Fix uuid issue in Lutron (#110524) 2024-02-22 11:35:39 +01:00
Brett Adams 9cb425ae8b Fix set_temperature in Tessie climate platform (#110445)
* HVAC_MODE support for set_temperature

* Fix import
2024-02-22 11:35:36 +01:00
Brett Adams eff82ba82c Add wake up timeout to Teslemetry (#109037) 2024-02-22 11:35:33 +01:00
Sid f21313b622 Handle deep standby and poweroffs of enigma2 devices gracefully (#107462)
* handle deep standby and poweroff of enigma2 devices gracefully

* address review comments

* remove warning on deep standby

* use contextlib.suppress
2024-02-22 11:35:29 +01:00
Mick Vleeshouwer bba7641c77 Fix reauth in Overkiz for config entries created prior to 2022.12 (#106251)
* Fix #106204

* Use api type variable
2024-02-22 11:35:23 +01:00
Franck Nijhof 7aa14e20d1 2024.2.2 (#110720) 2024-02-16 15:47:38 +01:00
Franck Nijhof b55b2c8da3 Bump version to 2024.2.2 2024-02-16 14:13:26 +01:00
Robert Resch 8c05ebd031 Bump deebot-client to 5.2.1 (#110683)
* Bump deebot-client to 5.2.0

* Bumb again

* Fix tests
2024-02-16 14:13:18 +01:00
Robert Svensson 34a3e88e0d Bump aiounifi to v71 (#110658) 2024-02-16 14:13:15 +01:00
J. Nick Koston bf002ac0b0 Fix elkm1 service calls running in the executor (#110655)
fixes
```
  File "/usr/src/homeassistant/homeassistant/components/elkm1/__init__.py", line 416, in _set_time_service
    _getelk(service).panel.set_time(dt_util.now())
  File "/usr/local/lib/python3.11/site-packages/elkm1_lib/panel.py", line 55, in set_time
    self._connection.send(rw_encode(datetime))
  File "/usr/local/lib/python3.11/site-packages/elkm1_lib/connection.py", line 152, in send
    self._send(QueuedWrite(msg.message, msg.response_command), priority_send)
  File "/usr/local/lib/python3.11/site-packages/elkm1_lib/connection.py", line 148, in _send
    self._check_write_queue.set()
  File "/usr/local/lib/python3.11/asyncio/locks.py", line 192, in set
    fut.set_result(True)
  File "/usr/local/lib/python3.11/asyncio/base_events.py", line 763, in call_soon
    self._check_thread()
  File "/usr/local/lib/python3.11/asyncio/base_events.py", line 800, in _check_thread
    raise RuntimeError(
RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
```
2024-02-16 14:13:12 +01:00
jan iversen 6f529a2c77 Modbus, allow received int to be a float. (#110648) 2024-02-16 14:13:09 +01:00
J. Nick Koston e5db7278e1 Fix tplink not updating IP from DHCP discovery and discovering twice (#110557)
We only called format_mac on the mac address if we connected
to the device during entry creation. Since the format of the
mac address from DHCP discovery did not match the format saved
in the unique id, the IP would not get updated and a second
discovery would appear

Thankfully the creation path does format the mac so we did not
create any entries with an inconsistantly formatted unique id

fixes #110460
2024-02-16 14:13:05 +01:00
J. Nick Koston cdf67e9bb5 Bump orjson to 3.9.14 (#110552)
changelog: https://github.com/ijl/orjson/compare/3.9.13...3.9.14

fixes a crasher due to buffer overread (was only partially fixed in 3.9.13)
2024-02-16 14:13:03 +01:00
G Johansson 393359a546 Coerce to float in Sensibo climate react custom service (#110508) 2024-02-16 14:12:59 +01:00
Stefan Agner 9309e38302 Fix Raspberry Pi utilities installation on Alpine 3.19 (#110463) 2024-02-16 14:12:56 +01:00
wilburCforce 479ecc8b94 Update pylutron to 0.2.12 (#110414) 2024-02-16 14:12:53 +01:00
wilburCforce ec7950aeda Update pylutron to 0.2.11 (#109853) 2024-02-16 14:12:48 +01:00
Robert Hillis c763483049 Mitigate session closed error in Netgear LTE (#110412) 2024-02-16 14:10:34 +01:00
Steven Looman fe84e7a576 Bump async-upnp-client to 0.38.2 (#110411) 2024-02-16 14:10:31 +01:00
Michael 5ba31290b8 Bump py-sucks to 0.9.9 (#110397)
bump py-sucks to 0.9.9
2024-02-16 14:10:28 +01:00
J. Nick Koston de619e4ddc Fix zone radius calculation when radius is not 0 (#110354) 2024-02-16 14:10:25 +01:00
Nikolay Vasilchuk 56ceadaeeb Fix Starline GPS count sensor (#110348) 2024-02-16 14:10:22 +01:00
IceBotYT da61564f82 Bump linear-garage-door to 0.2.9 (#110298) 2024-02-16 14:10:19 +01:00
starkillerOG 003673cd29 Fix TDBU naming in Motionblinds (#110283)
fix TDBU naming
2024-02-16 14:10:16 +01:00
Matthias Alphart da6c571e65 Update xknxproject to 3.6.0 (#110282) 2024-02-16 14:10:13 +01:00
J. Nick Koston 159fab7025 Bump PySwitchbot to 0.45.0 (#110275) 2024-02-16 14:10:10 +01:00
Michael 96a10e76b8 Bump aiopegelonline to 0.0.8 (#110274) 2024-02-16 14:10:08 +01:00
G Johansson e7068ae134 Fix cpu percentage in System Monitor (#110268)
* Fix cpu percentage in System Monitor

* Tests
2024-02-16 14:10:05 +01:00
Jan-Philipp Benecke 6a0c3f1b4f Handle no data error in Electricity Maps config flow (#110259)
Co-authored-by: Viktor Andersson <30777521+VIKTORVAV99@users.noreply.github.com>
2024-02-16 14:10:02 +01:00
Simon Lamon a0ae18a1b6 Fix state classes issue translation in Group (#110238)
Fix state classes translation
2024-02-16 14:09:59 +01:00
David Bonnes ad761bb2de Bump evohome-async to 0.4.19 (#110225)
bump client to 0.4.19
2024-02-16 14:09:56 +01:00
DustyArmstrong edb69fb095 Bump datapoint to 0.9.9 + re-enable Met Office Integration (#110206) 2024-02-16 14:09:53 +01:00
Simon Lamon 58b28e6df1 Fix device class repairs issues placeholders in Group (#110181)
fix translation placeholders
2024-02-16 14:09:50 +01:00
Adam Goode 973a13abfa Properly report cover positions to prometheus (#110157) 2024-02-16 14:09:47 +01:00
J. Nick Koston 2a51377cef Bump yalexs to 1.11.2 (#110144)
changelog: https://github.com/bdraco/yalexs/compare/v1.11.1...v1.11.2
2024-02-16 14:09:44 +01:00
J. Nick Koston 87bd67656b Only schedule august activity update when a new activity is seen (#110141) 2024-02-16 14:09:41 +01:00
Maciej Bieniek c79bc17d17 Fix typo in sensor icons configuration (#110133)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-02-16 14:09:38 +01:00
A Björck 54270df217 Bump yalexs to 1.11.1, fixing camera snapshots from Yale Home (#110089) 2024-02-16 14:09:35 +01:00
Malte Franken 5a87cde71e Bump aio-geojson-usgs-earthquakes to 0.3 (#110084) 2024-02-16 14:09:32 +01:00
Christophe Gagnier e825bcc282 Update pytechnove to 1.2.2 (#110074) 2024-02-16 14:09:29 +01:00
Aurélien Grenotton b54a3170f0 Fix freebox pairing in bridge mode (#106131) 2024-02-16 14:09:25 +01:00
Luke Lashley 349d8f5c28 Better teardown and setup of Roborock connections (#106092)
Co-authored-by: Robert Resch <robert@resch.dev>
2024-02-16 14:08:20 +01:00
Franck Nijhof cfd1f7809f 2024.2.1 (#110078) 2024-02-09 11:04:19 +01:00
Erik Montnemery 5f9cc2fec1 Prevent network access in emulated_hue tests (#109991) 2024-02-09 10:16:49 +01:00
Franck Nijhof 58d46f6dec Bump version to 2024.2.1 2024-02-09 09:02:01 +01:00
Brandon Rothweiler 74ea9e24df Bump py-aosmith to 1.0.8 (#110061) 2024-02-09 09:01:49 +01:00
David Bonnes 437a2a829f Bump evohome-async to 0.4.18 (#110056) 2024-02-09 09:01:46 +01:00
Michael Hansen f5884c6279 Matching duplicate named entities is now an error in Assist (#110050)
* Matching duplicate named entities is now an error

* Update snapshot

* Only use area id
2024-02-09 09:01:43 +01:00
Michael e4382a494c Log error and continue on parsing issues of translated strings (#110046) 2024-02-09 09:00:19 +01:00
Bram Kragten 56ff767969 Update frontend to 20240207.1 (#110039) 2024-02-09 09:00:17 +01:00
jan iversen 4a18f592c6 Avoid key_error in modbus climate with non-defined fan_mode. (#110017) 2024-02-09 09:00:14 +01:00
Robert Resch 7ff2f376d4 Bump aioecowitt to 2024.2.1 (#109999) 2024-02-09 09:00:10 +01:00
jan iversen a18918bb73 Allow modbus negative min/max value. (#109995) 2024-02-09 09:00:06 +01:00
Robert Resch 49e5709826 Bump deebot-client to 5.1.1 (#109994) 2024-02-09 09:00:00 +01:00
jan iversen c665903f9d Allow modbus min/max temperature to be negative. (#109977) 2024-02-09 08:59:58 +01:00
spycle de44af2948 Bump pyMicrobot to 0.0.12 (#109970) 2024-02-09 08:59:55 +01:00
Erik Montnemery 95a800b6bc Don't blow up if config entries have unhashable unique IDs (#109966)
* Don't blow up if config entries have unhashable unique IDs

* Add test

* Add comment on when we remove the guard

* Don't stringify hashable non string unique_id
2024-02-09 08:59:52 +01:00
jan iversen a9e9ec2c3d Allow modbus "scale" to be negative. (#109965) 2024-02-09 08:59:49 +01:00
Marcel van der Veldt 7309c3c290 Handle Matter nodes that become available after startup is done (#109956) 2024-02-09 08:59:46 +01:00
Malte Franken f48d70654b Bump aio-geojson-geonetnz-volcano to 0.9 (#109940) 2024-02-09 08:59:43 +01:00
Marcel van der Veldt a9b3c2e2b5 Skip polling of unavailable Matter nodes (#109917) 2024-02-09 08:59:41 +01:00
Jan-Philipp Benecke 19349e1779 Bump aioelectricitymaps to 0.4.0 (#109895) 2024-02-09 08:59:38 +01:00
Marcel van der Veldt e320d715c7 Bump Python matter server to 5.5.0 (#109894) 2024-02-09 08:59:35 +01:00
Michael Hansen 44c9ea68eb Assist fixes (#109889)
* Don't pass entity ids in hassil slot lists

* Use first completed response

* Add more tests
2024-02-09 08:59:32 +01:00
Mike Degatano dbfee24eb7 Allow disabling home assistant watchdog (#109818) 2024-02-09 08:59:27 +01:00
mkmer 3b7271d597 Catch APIRateLimit in Honeywell (#107806) 2024-02-09 08:58:44 +01:00
Franck Nijhof 9dbf84228e 2024.2.0 (#109883) 2024-02-07 18:31:28 +01:00
Joost Lekkerkerker 9e47d03086 Fix kitchen sink tests (#109243) 2024-02-07 17:40:10 +01:00
Franck Nijhof f63aaf8b5a Bump version to 2024.2.0 2024-02-07 16:28:11 +01:00
Malte Franken 8375fc235d Bump aio-geojson-geonetnz-quakes to 0.16 (#109873) 2024-02-07 16:27:47 +01:00
Åke Strandberg 3030870de0 Remove soft hyphens from myuplink sensor names (#109845)
Remove soft hyphens from sensor names
2024-02-07 16:27:44 +01:00
Matrix f61c70b686 Fix YoLink SpeakerHub support (#107925)
* improve

* Fix when hub offline/online message pushing

* fix as suggestion

* check config entry load state

* Add exception translation
2024-02-07 16:27:40 +01:00
Franck Nijhof e720b398d2 Bump version to 2024.2.0b11 2024-02-07 12:44:50 +01:00
Bram Kragten bd21490a57 Update frontend to 20240207.0 (#109871) 2024-02-07 12:34:36 +01:00
Malte Franken 75b308c1aa Bump aio-georss-gdacs to 0.9 (#109859) 2024-02-07 12:34:33 +01:00
Joakim Plate 881707e1fe Update nibe to 2.8.0 with LOG.SET fixes (#109825)
Update nibe to 2.8.0
2024-02-07 12:34:30 +01:00
Jiayi Chen 2ca3bbaea5 Update Growatt server URLs (#109122) 2024-02-07 12:34:24 +01:00
Franck Nijhof ea4bdbb3a0 Bump version to 2024.2.0b10 2024-02-07 08:48:17 +01:00
puddly 40cfc31dcb Bump ZHA dependency zigpy to 0.62.3 (#109848) 2024-02-07 08:48:07 +01:00
starkillerOG 031aadff00 Bump motionblinds to 0.6.20 (#109837) 2024-02-07 08:48:04 +01:00
Vilppu Vuorinen 27691b7d48 Disable energy report based operations with API lib upgrade (#109832)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-02-07 08:48:01 +01:00
Joost Lekkerkerker fe94107af7 Make integration fields in Analytics Insights optional (#109789) 2024-02-07 08:47:58 +01:00
Teemu R d784a76d32 Add tapo virtual integration (#109765) 2024-02-07 08:47:55 +01:00
Joost Lekkerkerker ebb1912617 Show domain in oauth2 error log (#109708)
* Show token url in oauth2 error log

* Fix tests

* Use domain
2024-02-07 08:47:50 +01:00
Franck Nijhof 8c605c29c3 Bump version to 2024.2.0b9 2024-02-06 22:49:53 +01:00
Joakim Sørensen 74a75e709f Bump awesomeversion from 23.11.0 to 24.2.0 (#109830) 2024-02-06 22:49:41 +01:00
J. Nick Koston 2103875ff7 Bump aioesphomeapi to 21.0.2 (#109824) 2024-02-06 22:49:38 +01:00
Erik Montnemery 5c83b774bb Bump python-otbr-api to 2.6.0 (#109823) 2024-02-06 22:49:34 +01:00
Joost Lekkerkerker 2c870f9da9 Bump aioecowitt to 2024.2.0 (#109817) 2024-02-06 22:49:31 +01:00
Maciej Bieniek 40adb3809f Ignore trackable without details in Tractive integration (#109814)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-02-06 22:49:28 +01:00
wittypluck 8aa1242221 Mark Unifi bandwidth sensors as unavailable when client disconnects (#109812)
* Set sensor as unavailable instead of resetting value to 0 on disconnect

* Update unit test on unavailable bandwidth sensor
2024-02-06 22:49:25 +01:00
J. Nick Koston 8569ddc5f9 Fix entity services targeting entities outside the platform when using areas/devices (#109810) 2024-02-06 22:49:22 +01:00
Franck Nijhof 7032415528 Don't block Supervisor entry setup with refreshing updates (#109809) 2024-02-06 22:49:19 +01:00
puddly d099fb2a26 Pin chacha20poly1305-reuseable>=0.12.1 (#109807)
* Pin `chacha20poly1305-reuseable`
Prevents a runtime `assert isinstance(cipher, AESGCM)` error

* Update `gen_requirements_all.py` as well
2024-02-06 22:49:16 +01:00
Jan-Philipp Benecke 35fad52913 Bump aioelectricitymaps to 0.3.1 (#109797) 2024-02-06 22:49:13 +01:00
Vilppu Vuorinen c170132827 Update MELCloud codeowners (#109793)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-02-06 22:49:10 +01:00
Matthias Alphart 439f82a4ec Update xknx to 2.12.0 and xknxproject to 3.5.0 (#109787) 2024-02-06 22:49:07 +01:00
Steven B 2481d14632 Bump ring_doorbell to 0.8.7 (#109783) 2024-02-06 22:49:04 +01:00
Steven B 3cf826dc93 Bump ring_doorbell to 0.8.6 (#109199) 2024-02-06 22:48:59 +01:00
Jan-Philipp Benecke e25ddf9650 Change state class of Tesla wall connector session energy entity (#109778) 2024-02-06 22:46:34 +01:00
puddly 8d79ac67f5 Bump ZHA dependencies (#109770)
* Bump ZHA dependencies

* Bump universal-silabs-flasher to 0.0.18

* Flip `Server_to_Client` enum in ZHA unit test

* Bump zigpy to 0.62.2
2024-02-06 22:46:31 +01:00
David F. Mulcahey 5025c15165 Buffer JsonDecodeError in Flo (#109767) 2024-02-06 22:46:28 +01:00
Joost Lekkerkerker ffd5e04a29 Fix Radarr health check singularity (#109762)
* Fix Radarr health check singularity

* Fix comment
2024-02-06 22:46:25 +01:00
G Johansson 9fcdfd1b16 Bump holidays to 0.42 (#109760) 2024-02-06 22:46:21 +01:00
Vilppu Vuorinen c1e5b2e6cc Fix compatibility issues with older pymelcloud version (#109757) 2024-02-06 22:42:58 +01:00
suaveolent 31c0d21204 Improve lupusec code quality (#109727)
* renamed async_add_devices

* fixed typo

* patch class instead of __init__

* ensure non blocking get_alarm

* exception handling

* added test case for json decode error

* avoid blockign calls

---------

Co-authored-by: suaveolent <suaveolent@users.noreply.github.com>
2024-02-06 22:42:54 +01:00
spycle 3ba63fc78f Fix keymitt_ble config-flow (#109644)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-02-06 22:42:51 +01:00
spycle 0395315267 Bump pyMicrobot to 0.0.10 (#109628) 2024-02-06 22:42:48 +01:00
TheJulianJES 6b354457c2 Fix ZHA creating unnecessary "summ received" entity after upgrade (#109268)
* Do not create `current_summ_received` entity until initialized once

* Update zha_devices_list.py to not expect summation received entities

The attribute isn't initialized for these devices in the test (which our check now expects it to be), hence we need to remove them from this list.

* Update sensor tests to have initial state for current_summ_received entity

The attribute needs to be initialized for it to be created which we do by plugging the attribute read.
The test expects the initial state to be "unknown", but hence we plugged the attribute (to create the entity), the state is whatever we plug the attribute read as.

* Update sensor tests to expect not updating current_summ_received entity if it doesn't exist
2024-02-06 22:42:43 +01:00
Franck Nijhof df88335370 Bump version to 2024.2.0b8 2024-02-05 20:27:40 +01:00
Joost Lekkerkerker 4c6c5ee63d Handle startup error in Analytics insights (#109755) 2024-02-05 20:27:29 +01:00
Vilppu Vuorinen 65476914ed Reduce MELCloud poll frequency to avoid throttling (#109750) 2024-02-05 20:27:26 +01:00
Bouwe Westerdijk d30a2e3611 Fix incorrectly assigning supported features for plugwise climates (#109749) 2024-02-05 20:27:23 +01:00
G Johansson eb510e3630 Add missing new climate feature flags to Mill (#109748) 2024-02-05 20:27:20 +01:00
Michael 532df5b5f1 Use tracked entity friendly name for proximity sensors (#109744)
user tracked entity friendly name
2024-02-05 20:27:17 +01:00
Jan Bouwhuis 1534f99c80 Fix generic camera error when template renders to an invalid URL (#109737) 2024-02-05 20:27:14 +01:00
Cyrill Raccaud a19aa9595a Bump python-bring-api to 3.0.0 (#109720) 2024-02-05 20:27:11 +01:00
Joost Lekkerkerker e3191d098f Add strings to Ruuvitag BLE (#109717) 2024-02-05 20:27:08 +01:00
Bram Kragten cc36071612 Update frontend to 20240205.0 (#109716) 2024-02-05 20:27:04 +01:00
Joakim Sørensen 2d90ee8237 Fix log string in Traccar Server Coordinator (#109709) 2024-02-05 20:27:01 +01:00
Simone Chemelli 16266703df Queue climate calls for Comelit SimpleHome (#109707) 2024-02-05 20:26:58 +01:00
Joost Lekkerkerker dd2cc52119 Set Analytics Insights as diagnostic (#109702)
* Set Analytics Insights as diagnostic

* Set Analytics Insights as diagnostic
2024-02-05 20:26:54 +01:00
Joost Lekkerkerker c48c8c25fa Remove obsolete check from Proximity (#109701) 2024-02-05 20:26:51 +01:00
Joost Lekkerkerker 83a5659d57 Set shorthand attribute in Epion (#109695) 2024-02-05 20:26:48 +01:00
Joost Lekkerkerker 3183cd346d Add data descriptions to analytics insights (#109694) 2024-02-05 20:26:45 +01:00
Marcel van der Veldt 5930c841d7 Bump python matter server to 5.4.1 (#109692) 2024-02-05 20:26:42 +01:00
Myles Eftos f05ba22b5c Show site state in Amberelectric config flow (#104702) 2024-02-05 20:26:38 +01:00
Franck Nijhof ecc6cc280a Bump version to 2024.2.0b7 2024-02-05 09:41:55 +01:00
David F. Mulcahey f50afd6004 Buffer TImeoutError in Flo (#109675) 2024-02-05 09:41:45 +01:00
Michael 44ecaa740b Add missing translation string to Home Assistant Analytics Insights (#109666)
add missing string
2024-02-05 09:41:42 +01:00
Michael 91b1a8e962 Add icon translation to proximity (#109664)
add icon translations
2024-02-05 09:41:39 +01:00
Malte Franken 74f1b18b73 Bump georss-generic-client to 0.8 (#109658) 2024-02-05 09:41:36 +01:00
Michael 8ab1c044bd Add zone related sensors in proximity (#109630)
* move legacy needed convertions into legacy entity

* add zone related sensors

* fix test coverage

* fix typing

* fix entity name translations

* rename placeholder to tracked_entity
2024-02-05 09:41:33 +01:00
Leah Oswald 66d8856033 Fix home connect remaining progress time (#109525)
* fix remaining progress time for home connect component

The home connect API is sending some default values (on dishwashers) for
the remaining progress time after the program finished. This is a problem
because this value is stored and on every API event, for example opening
the door of a dishwasher, the value for remaining progress time is
updated with this wrong value. So I see a wrong value the whole time the
dishwasher is not running and therefore has no remaining progress time.
This coming fixes this problem and adds a check if the appliance is in
running, pause or finished state, because there we have valid data. In
the other states the new code just returns none like on other edge
cases. Now there is no value if there is no program running.

* fix some formating according to the ruff rules

* fix some formating according to the ruff rules again

* fix alphabetic order of imports

* add check if keys exist in dict before accessing them

check if BSH_OPERATION_STATE and ATTR_VALUE key values exist before
accessing them later in the elif statement

* fix formating because forgotten local ruff run
2024-02-05 09:41:30 +01:00
Kevin Worrel 5c1e4379a9 Screenlogic service refactor (#109041) 2024-02-05 09:41:27 +01:00
ollo69 bca9826e18 Don't create AsusWRT loadavg sensors when unavailable (#106790) 2024-02-05 09:41:23 +01:00
Franck Nijhof 3a067d445d Bump version to 2024.2.0b6 2024-02-04 23:25:49 +01:00
Franck Nijhof 4d7c96205d Fix Tuya reauth_successful translation string (#109659) 2024-02-04 23:25:41 +01:00
Jan-Philipp Benecke 3934524d4a Add icon translations to Utility meter helper (#109656) 2024-02-04 23:25:37 +01:00
Franck Nijhof 5747f8ce9d Improve Tuya token/reauth handling (#109653) 2024-02-04 23:25:34 +01:00
Joost Lekkerkerker 02ebf1d7f8 Add icon translations to Random (#109652) 2024-02-04 23:25:31 +01:00
Joost Lekkerkerker d789e83879 Add icon translations to Counter (#109651) 2024-02-04 23:25:28 +01:00
Joost Lekkerkerker ce29b4a7e3 Add icon translations to derivative (#109650) 2024-02-04 23:25:25 +01:00
Joost Lekkerkerker e2695ba88f Allow the helper integrations to omit icon translation field (#109648) 2024-02-04 23:25:22 +01:00
G Johansson 7ca83a7648 Add debug logger for cpu temp in System Monitor (#109627) 2024-02-04 23:25:19 +01:00
G Johansson c0efec4a84 Fix repairs for remove dates in Workday (#109626) 2024-02-04 23:25:16 +01:00
Franck Nijhof f766fbfb98 Fix Tuya QR code expiry, use native QR selector (#109615)
* Fix Tuya QR code expiry, use native QR selector

* Adjust tests
2024-02-04 23:25:14 +01:00
Franck Nijhof bd78c44ac5 Update orjson to 3.9.13 (#109614) 2024-02-04 23:25:11 +01:00
Jan Bouwhuis fd2469e2a7 Fix imap message part decoding (#109523) 2024-02-04 23:25:08 +01:00
Cody C a7010e3e80 Handle GeoJSON int to str conversion when the name is an int (#108937)
Co-authored-by: Chris Roberts <NasaGeek@users.noreply.github.com>
2024-02-04 23:25:04 +01:00
Franck Nijhof d379a9aaae Bump version to 2024.2.0b5 2024-02-04 15:59:46 +01:00
Michael 2dc630a4af Redact location names in proximity diagnostics (#109600) 2024-02-04 15:59:34 +01:00
Josef Zweck ba0c065750 Bugfix lamarzocco issue (#109596) 2024-02-04 15:59:31 +01:00
tronikos 4fca06256b Fix Google generative AI service example (#109594)
Update strings.json
2024-02-04 15:59:28 +01:00
J. Nick Koston 0a25788822 Bump yalexs-ble to 2.4.1 (#109585)
changelog: https://github.com/bdraco/yalexs-ble/compare/v2.4.0...v2.4.1
2024-02-04 15:59:25 +01:00
G Johansson 94e1eaa15d Fix overkiz climate feature flags for valve heating (#109582)
* Fix overkiz climate feature flags for valve heating

* Update homeassistant/components/overkiz/climate_entities/valve_heating_temperature_interface.py
2024-02-04 15:59:22 +01:00
G Johansson dfc26e4509 Fix group sensor uom's in not convertable device classes (#109580) 2024-02-04 15:59:20 +01:00
Matrix b14add5914 Fix yolink abnormal status when LeakSensor detection mode changes to "no water detect" (#109575)
Add no water detect support
2024-02-04 15:59:17 +01:00
G Johansson 7be6aa455e Add back logging for core for feature flags in climate (#109572) 2024-02-04 15:59:14 +01:00
G Johansson 40636f2273 Add new climate feature flags to lyric (#109571) 2024-02-04 15:59:11 +01:00
G Johansson f91f98e309 Add new climate feature flags to lookin (#109570) 2024-02-04 15:59:08 +01:00
G Johansson 838b1338b8 Add migrated climate feature flags to livisi (#109569) 2024-02-04 15:59:05 +01:00
G Johansson d3aa7375f0 Add new climate feature flags to lightwave (#109568) 2024-02-04 15:59:02 +01:00
G Johansson 514ce59a8f Add new climate feature flags to lcn (#109566) 2024-02-04 15:58:59 +01:00
G Johansson 25063821e1 Add new climate feature flags to izone (#109565) 2024-02-04 15:58:56 +01:00
G Johansson b3c257fb79 Add new climate feature flags to isy994 (#109564) 2024-02-04 15:58:53 +01:00
G Johansson cb02c2e6d0 Fix new climate feature flags in intesishome (#109563) 2024-02-04 15:58:50 +01:00
G Johansson 1000fae905 Add new climate feature flags to intellifire (#109562) 2024-02-04 15:58:47 +01:00
G Johansson 30b9a28502 Add new climate feature flags to insteon (#109560) 2024-02-04 15:58:44 +01:00
G Johansson 42bf086c97 Add migrated climate feature flags to incomfort (#109559) 2024-02-04 15:58:41 +01:00
G Johansson 064f412da4 Add new climate feature flags to iaqualink (#109558) 2024-02-04 15:58:38 +01:00
G Johansson 122652b396 Add new climate feature flags to huum (#109557)
Adds new climate feature flags to huum
2024-02-04 15:58:36 +01:00
G Johansson 32b25c7e53 Add new climate feature flags to honeywell (#109556)
Adds new climate feature flags to honeywell
2024-02-04 15:58:33 +01:00
G Johansson 83c487a319 Add migrated climate feature flags to homematicip_cloud (#109555)
Adds migrated climate feature flags to homematicip_cloud
2024-02-04 15:58:30 +01:00
G Johansson 75c0c7bda0 Add new climate feature flags to homematic (#109554)
Adds new climate feature flags to homematic
2024-02-04 15:58:27 +01:00
G Johansson 4d7abbf8c5 Add new climate feature flags to hive (#109553)
Adds new climate feature flags to hive
2024-02-04 15:58:24 +01:00
G Johansson bf4bc9d935 Add new climate feature flags to hisense (#109552)
Adds new climate feature flags to hisense
2024-02-04 15:58:21 +01:00
G Johansson e1699b4d65 Add new climate feature flags to heatmiser (#109551)
Adds new climate feature flags to heatmiser
2024-02-04 15:58:18 +01:00
G Johansson 67362db547 Add new climate feature flags to gree (#109550)
Adds new climate feature flags to gree
2024-02-04 15:58:15 +01:00
G Johansson dafdcd369c Add new climate feature flags to geniushub (#109549)
Adds new climate feature flags to geniushub
2024-02-04 15:58:12 +01:00
G Johansson 384070c158 Add new climate feature flags to generic_thermostat (#109548)
Adds new climate feature flags to generic_thermostat
2024-02-04 15:58:09 +01:00
G Johansson 46016004fa Add migrated climate feature flags to fritzbox (#109547)
Adds migrated climate feature flags to fritzbox
2024-02-04 15:58:07 +01:00
G Johansson 38fcc88c57 Add new climate feature flags to freedompro (#109546)
Adds new climate feature flags to freedompro
2024-02-04 15:58:04 +01:00
G Johansson 9cde864224 Add new climate feature flags to escea (#109545)
Adds new climate feature flags to escea
2024-02-04 15:58:01 +01:00
G Johansson 439f1766a0 Add new climate feature flags to ephember (#109544)
Adds new climate feature flags to ephember
2024-02-04 15:57:58 +01:00
G Johansson 12e32fb799 Adds new climate feature flags to elkm1 (#109543) 2024-02-04 15:57:55 +01:00
G Johansson 14ad2e91f3 Add new climate feature flags to electrasmart (#109542)
Adds new climate feature flags to electrasmart
2024-02-04 15:57:52 +01:00
G Johansson 8c0cd6bbab Add new climate feature flags to econet (#109541)
Adds new climate feature flags to econet
2024-02-04 15:57:49 +01:00
G Johansson c02e96c5c0 Add new climate feature flags to ecobee (#109540)
Adds new climate feature flags to ecobee
2024-02-04 15:57:46 +01:00
G Johansson 3a08e3bec6 Add new climate feature flags to duotecno (#109539)
Adds new climate feature flags to duotecno
2024-02-04 15:57:43 +01:00
G Johansson a62c05b983 Add migrated climate feature flags to devolo home control (#109538)
Adds migrated climate feature flags to devolo home control
2024-02-04 15:57:40 +01:00
G Johansson 79846d5668 Add migrated climate feature flags to daikin (#109537)
Adds migrated climate feature flags to daikin
2024-02-04 15:57:38 +01:00
G Johansson 482032cb87 Add migrated climate feature flags to coolmaster (#109536)
Adds migrated climate feature flags to coolmaster
2024-02-04 15:57:35 +01:00
G Johansson 9ebf985010 Add new climate feature flags to comelit (#109535)
Adds new climate feature flags to comelit
2024-02-04 15:57:32 +01:00
G Johansson f16c0bd559 Add new climate feature flags to ccm15 (#109534)
Adds new climate feature flags to ccm15
2024-02-04 15:57:29 +01:00
Cody C 1df5ad23ef Fix empty error modal when adding duplicate Thread integration (#109530) 2024-02-04 15:57:26 +01:00
David F. Mulcahey d99ba75ed8 Prevent Flo devices and entities from going unavailable when a single refresh fails (#109522)
* Prevent Flo devices and entities from going unavailable when a single refresh fails

* review comment
2024-02-04 15:57:23 +01:00
TheJulianJES ceeef1eacc Move climate feature flags to child classes for airzone_cloud (#109515) 2024-02-04 15:57:20 +01:00
Klaas Schoute 856780ed30 Bump easyenergy lib to v2.1.1 (#109510) 2024-02-04 15:57:17 +01:00
Joakim Plate 500b0a9b52 Correct flow rate conversion review after merge (#109501) 2024-02-04 15:57:14 +01:00
G Johansson 855edba3a2 Add new climate feature flags for plugwise (#109464) 2024-02-04 15:57:11 +01:00
G Johansson e7203d6015 Add new climate feature flags to switcher_kis (#109459) 2024-02-04 15:57:08 +01:00
G Johansson 97446a5af3 Add migrated climate feature flag to switchbee (#109458) 2024-02-04 15:57:05 +01:00
G Johansson ac2e05b5c0 Add climate feature flags to spider (#109456) 2024-02-04 15:57:02 +01:00
G Johansson af07ac120e Add new climate feature flags to tuya (#109434) 2024-02-04 15:57:00 +01:00
G Johansson f2de666c54 Add new climate feature flags to esphome (#109428) 2024-02-04 15:56:57 +01:00
G Johansson 38288dd68e Add new climate feature flags for airtouch5 (#109422)
* Add new climate feature flags for airtouch5

* adapt
2024-02-04 15:56:54 +01:00
G Johansson 3e2f97d105 Add ClimateEntityFeatures to airtouch4 (#109421)
* Add ClimateEntityFeatures to airtouch4

* adapt
2024-02-04 15:56:51 +01:00
Michael 5f014f42ac Avoid duplicate entity names in proximity (#109413)
* avoid duplicate config entry title

* consecutive range 2..10

* use existing logic
2024-02-04 15:56:48 +01:00
Josef Zweck 4b2adab24d Revert "Add webhook support to tedee integration (#106846)" (#109408) 2024-02-04 15:56:45 +01:00
Franck Nijhof ef6fed5067 Bump version to 2024.2.0b4 2024-02-03 14:42:00 +01:00
Jan-Philipp Benecke 0a627aed6d Fix Tankerkoenig diagnostics file to use right format (#109494)
Fix tankerkoenig diagnostics file
2024-02-03 14:40:26 +01:00
Joakim Sørensen cb03a6e29b Change IoT class for Traccar Client (#109493) 2024-02-03 14:40:23 +01:00
Joakim Sørensen e630027455 Extend the history of Elvia history to 3 years (#109490)
Extend the history of Elvia data to 3 years
2024-02-03 14:40:21 +01:00
G Johansson 280d7ef4ec Add new climate feature flags to deconz (#109482) 2024-02-03 14:40:18 +01:00
G Johansson 93b7ffa807 Add new climate feature flags to demo (#109481) 2024-02-03 14:40:15 +01:00
G Johansson cc8e9ac141 Adds new climate feature flags to bsblan (#109480) 2024-02-03 14:40:12 +01:00
G Johansson 2724b115da Adds new climate feature flags to broadlink (#109479) 2024-02-03 14:40:09 +01:00
G Johansson 150fb151fa Add new climate feature flags to blebox (#109478) 2024-02-03 14:40:06 +01:00
G Johansson 4b8cb35ba0 Adds migrated climate feature flags in balboa (#109477) 2024-02-03 14:40:03 +01:00
G Johansson 5cce878b85 Adds new climate feature flags in baf (#109476) 2024-02-03 14:40:00 +01:00
G Johansson 92ebc5b436 Adds new climate feature flags to ambiclimate (#109475) 2024-02-03 14:39:57 +01:00
G Johansson 463320c8ee Adds migrated climate feature flags in nuheat (#109474) 2024-02-03 14:39:54 +01:00
G Johansson 3bf5fa9302 Adds migrated climate feature flags to nobo_hub (#109473) 2024-02-03 14:39:51 +01:00
G Johansson 650ab70444 Add migrated climate feature flags to nexia (#109472) 2024-02-03 14:39:48 +01:00
G Johansson bb8a74a3f4 Add new climate feature flags to mysensors (#109471)
Adds new climate feature flags to mysensors
2024-02-03 14:39:45 +01:00
G Johansson 49445c46a0 Add migrated climate feature flags to moehlenhoff (#109470) 2024-02-03 14:39:42 +01:00
G Johansson 490101fa92 Adds new climate feature flags to melissa (#109469) 2024-02-03 14:39:39 +01:00
G Johansson 2ac4bb8e9f Add new feature flags to melcloud (#109468) 2024-02-03 14:39:36 +01:00
G Johansson 1c0a6970e2 Adds new climate feature flags to maxcube (#109467) 2024-02-03 14:39:33 +01:00
G Johansson f3f69a8107 Add new climate feature flags to radiotherm (#109466) 2024-02-03 14:39:30 +01:00
G Johansson a77bcccbbb Adds migrated climate feature flags for proliphix (#109465) 2024-02-03 14:39:28 +01:00
G Johansson e98da8596a Add migrated climate feature flags to overkiz (#109463) 2024-02-03 14:39:25 +01:00
G Johansson 5843c93371 Add migrated climate feature flags to opentherm_gw (#109462) 2024-02-03 14:39:22 +01:00
G Johansson 5991b06574 Add new climate feature flags to oem (#109461) 2024-02-03 14:39:19 +01:00
G Johansson fecdfbfb9f Add new climate feature flags to stiebel_eltron (#109457) 2024-02-03 14:39:16 +01:00
G Johansson 8cfe3821da Add migrated climate feature flags to senz (#109455) 2024-02-03 14:39:13 +01:00
G Johansson cba67d1525 Add new climate feature flags to screenlogic (#109454) 2024-02-03 14:39:10 +01:00
G Johansson 39df394414 Add migrated climate feature flags to schluter (#109452) 2024-02-03 14:39:07 +01:00
G Johansson db91a40b55 Add migrated climate feature flags to touchline (#109451) 2024-02-03 14:39:04 +01:00
G Johansson 3181358484 Add migrated climate feature flags to toon (#109450) 2024-02-03 14:39:01 +01:00
G Johansson 776e2da4e6 Add migrated climate feature flags to tolo (#109449) 2024-02-03 14:38:58 +01:00
G Johansson 72ffdf4f4b Add new climate feature flags to tfiac (#109448) 2024-02-03 14:38:55 +01:00
G Johansson 36bba95fd0 Add migrated climate feature flags for tessie (#109447) 2024-02-03 14:38:52 +01:00
G Johansson 13fc69d8a8 Add migrated climate feature flags to teslemetry (#109446) 2024-02-03 14:38:49 +01:00
G Johansson 94464f220c Add migrated climate feature flags to zwave_me (#109445) 2024-02-03 14:38:46 +01:00
G Johansson 13decb9b10 Add new climate feature flags to zhong_hong (#109444) 2024-02-03 14:38:44 +01:00
G Johansson b6226acd2b Add migrated climate feature flags to zha (#109443) 2024-02-03 14:38:41 +01:00
G Johansson d04282a41c Add new climate feature flags to yolink (#109442) 2024-02-03 14:38:38 +01:00
G Johansson a671d0bc6c Add migrated climate feature flags to xs1 (#109441) 2024-02-03 14:38:35 +01:00
G Johansson 6c84e8dff0 Add new climate feature flags to whirlpool (#109440) 2024-02-03 14:38:32 +01:00
G Johansson 57279b1c7b Add migrated climate feature flags to vicare (#109439) 2024-02-03 14:38:29 +01:00
G Johansson a5646e0df2 Add migrated feature flags to vera (#109438) 2024-02-03 14:38:26 +01:00
Teemu R 63ad3ebdf4 Add new OUIs for tplink (#109437) 2024-02-03 14:38:23 +01:00
G Johansson f2a9ef6591 Add new climate feature flags to venstar (#109436) 2024-02-03 14:38:21 +01:00
G Johansson 2a5bb66c06 Adds migrated climate entity feature for velbus (#109435) 2024-02-03 14:38:18 +01:00
G Johansson 0c0be6d6a1 Add migrated climate feature flags to homekit_controller (#109433) 2024-02-03 14:38:15 +01:00
G Johansson e99a58ad53 Add new climate feature flags to flexit_bacnet (#109431) 2024-02-03 14:38:12 +01:00
G Johansson 6c4b773bc1 Add migrated climate entity features to flexit (#109430) 2024-02-03 14:38:09 +01:00
G Johansson c571f36c6c Add new climate feature flags to evohome (#109429) 2024-02-03 14:38:06 +01:00
G Johansson f1b041afbe Add migrated climate feature flags to smarttub (#109427) 2024-02-03 14:38:03 +01:00
G Johansson 183af92658 Add migrated climate feature flags to smartthings (#109426) 2024-02-03 14:38:00 +01:00
G Johansson d71dd12263 Add migrated climate feature flags to shelly (#109425) 2024-02-03 14:37:57 +01:00
G Johansson 18a7aa20b4 Adds new climate feature flags for airzone_cloud (#109424) 2024-02-03 14:37:54 +01:00
G Johansson f8fde71ef3 Add new climate feature flags to airzone (#109423) 2024-02-03 14:37:51 +01:00
G Johansson 966798b588 Add migrated ClimateEntityFeatures to advantage_air (#109420)
* Add migrated ClimateEntityFeatures to advantage_air

* AdvantageAirZone
2024-02-03 14:37:48 +01:00
G Johansson 66b6f81996 Add migrated ClimateEntityFeature to MQTT (#109419) 2024-02-03 14:37:45 +01:00
G Johansson 4a1a5b9e87 Adds migrated ClimateEntityFeature to Netatmo (#109418) 2024-02-03 14:37:42 +01:00
G Johansson f01f033b3f Add ClimateEntityFeatures to Nest (#109417) 2024-02-03 14:37:39 +01:00
G Johansson cd884de79e Add new ClimateEntityFeature for Tado (#109416) 2024-02-03 14:37:36 +01:00
G Johansson 14be7e4a72 Add Mill migrated ClimateEntityFeatures (#109415) 2024-02-03 14:37:32 +01:00
Jc2k 2be71d53c5 Bump aiohomekit to 3.1.4 (#109414) 2024-02-03 14:37:25 +01:00
Michael Hansen 1c960d300d Bump intents to 2024.2.2 (#109412)
Bump intents to 2024.2.2
2024-02-03 14:35:31 +01:00
Michael Hansen 87a1482e4d Pass slots to error messages instead of IDs [rework] (#109410)
Co-authored-by: tetele <tm.sandu@gmail.com>
2024-02-03 14:33:14 +01:00
Jan-Philipp Benecke 24b8e60978 Bump aiotankerkoenig to 0.3.0 (#109404) 2024-02-03 14:23:42 +01:00
Jurriaan Pruis 404e30911b Bump matrix-nio to 0.24.0 (#109403)
Update matrix-nio to 0.24.0
2024-02-03 14:23:38 +01:00
wilburCforce 9c599f7513 Fix device type in Lutron (#109401)
remove testing code
2024-02-03 14:23:35 +01:00
Jan-Philipp Benecke f3e8360949 Bump aioelectricitymaps to 0.3.0 (#109399)
* Bump aioelectricitymaps to 0.3.0

* Fix tests
2024-02-03 14:23:32 +01:00
Teemu R 43f8731f8a Bump python-kasa to 0.6.2.1 (#109397) 2024-02-03 14:23:29 +01:00
Michael Hansen 4a60d36216 More thorough checks in ESPHome voice assistant UDP server (#109394)
* More thorough checks in UDP server

* Simplify and change to stop_requested

* Check transport
2024-02-03 14:23:26 +01:00
Michael 07a1ee0baa Add diagnostics to proximity (#109393) 2024-02-03 14:23:23 +01:00
Franck Nijhof a57c30be88 Update elgato to 5.1.2 (#109391) 2024-02-03 14:23:20 +01:00
jjlawren b1bf69689e Do not suggest area for portable Sonos speakers (#109350)
* Do not suggest area for portable speakers

* Update tests

* Improve readability, update tests
2024-02-03 14:23:17 +01:00
Christopher Fenner 4735aadaa8 Ignore gateway devices in ViCare integration (#106477)
* filter unsupported devices

* Update __init__.py

* use debug

* remove dead code
2024-02-03 14:23:13 +01:00
Franck Nijhof f18f161efa Bump version to 2024.2.0b3 2024-02-02 17:36:51 +01:00
Bram Kragten 3c90e3d83f Update frontend to 20240202.0 (#109388) 2024-02-02 17:36:43 +01:00
Robert Resch 57a43ef151 Improve Ecovacs naming (#109372) 2024-02-02 17:36:40 +01:00
Franck Nijhof 6afc6ca126 Remove suggested area from Verisure (#109364) 2024-02-02 17:36:37 +01:00
Joakim Sørensen 92a3edc536 Specify end_time when importing Elvia data to deal with drift (#109361) 2024-02-02 17:36:34 +01:00
Robert Resch e91e67b400 Bump deebot_client to 5.1.0 (#109360) 2024-02-02 17:36:31 +01:00
Franck Nijhof 7a7bcf1a92 Update cryptography to 42.0.2 (#109359) 2024-02-02 17:36:28 +01:00
karwosts 1383a0c13a Missing template helper translation keys (#109347) 2024-02-02 17:36:24 +01:00
J. Nick Koston 12e3077895 Ensure the purge entities service cleans up the states_meta table (#109344) 2024-02-02 17:36:21 +01:00
G Johansson 05c0973937 Add Adax migrated ClimateEntityFeatures (#109341) 2024-02-02 17:36:18 +01:00
G Johansson 7467d588c8 Add sensibo migrated ClimateEntityFeatures (#109340)
Adds sensibo migrated ClimateEntityFeatures
2024-02-02 17:36:15 +01:00
jan iversen 0c82e0a618 Correct modbus commit validation, too strict on integers (#109338) 2024-02-02 17:36:12 +01:00
J. Nick Koston 5a4f88349a Fix stale camera error message in img_util (#109325) 2024-02-02 17:36:09 +01:00
G Johansson e720e82fd6 Add migrated ClimateEntityFeature for Nibe Heat Pump (#109140) 2024-02-02 17:36:05 +01:00
G Johansson 8ed0af2fb7 Add TURN_ON/OFF ClimateEntityFeature for KNX (#109138) 2024-02-02 17:36:02 +01:00
G Johansson 550c0bf3c3 Add migrated ClimateEntityFeature for SwitchBot Cloud (#109136) 2024-02-02 17:35:59 +01:00
G Johansson 8bb98b4146 Add TURN_ON/OFF ClimateEntityFeature for Modbus (#109133) 2024-02-02 17:35:56 +01:00
G Johansson bd5bc6b83d Add TURN_ON/OFF ClimateEntityFeature for Matter (#108974)
* Add TURN_ON/OFF ClimateEntityFeature for Matter

* Adjust matter
2024-02-02 17:35:53 +01:00
G Johansson a2b6b0a0bc Add TURN_ON/OFF ClimateEntityFeature for Fibaro (#108963) 2024-02-02 17:35:50 +01:00
G Johansson 41ad3d8987 Add migrated ClimateEntityFeature for Atag (#108961) 2024-02-02 17:35:45 +01:00
mkmer 3e7dc3588d Add independent session in honeywell (#108435) 2024-02-02 17:35:42 +01:00
Cyrill Raccaud 66d802b5e5 Follow up swiss_public_transport migration fix of unique ids (#107873)
improve migration fix of unique ids
- follow up to #107087
2024-02-02 17:35:38 +01:00
Franck Nijhof f77bd13cc0 Bump version to 2024.2.0b2 2024-02-01 22:29:58 +01:00
Paul Bottein fe4ad30ade Add device class to tesla wall connector session energy (#109333) 2024-02-01 22:29:49 +01:00
Joakim Sørensen 15a1a4bfdf Fix custom attribute lookup in Traccar Server (#109331) 2024-02-01 22:29:46 +01:00
Franck Nijhof 3d80c4f7f6 Update Home Assistant base image to 2024.02.0 (#109329) 2024-02-01 22:29:42 +01:00
Michael Hansen 0015af0b3c Move default response out of sentence trigger registration and into agent (#109317)
* Move default response out of trigger and into agent

* Add test
2024-02-01 22:29:39 +01:00
J. Nick Koston a535bda821 Fix race in loading service descriptions (#109316) 2024-02-01 22:29:36 +01:00
Maciej Bieniek ca539630a6 Do not use a battery device class for Shelly analog input sensor (#109311)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-02-01 22:29:32 +01:00
Josef Zweck faf2a90cd1 Bump pytedee_async to 0.2.13 (#109307)
bump
2024-02-01 22:29:30 +01:00
Robert Resch 6aba79d7b9 Verify Ecovacs mqtt config (#109306) 2024-02-01 22:29:26 +01:00
Ståle Storø Hauknes b464e77112 Bump airthings-ble to 0.6.1 (#109302)
Bump airthings-ble
2024-02-01 22:29:23 +01:00
Josh Pettersen a8b39ce332 Remove battery charge sensor from powerwall (#109271) 2024-02-01 22:29:19 +01:00
Michael Hansen 77b25553e3 Migrate to new intent error response keys (#109269) 2024-02-01 22:29:16 +01:00
G Johansson c31dfd6d00 Don't log warning for core integrations on new feature flags in Climate (#109250)
* Don't log for core integration on Climate new feature flags

* Add test

* Fix test
2024-02-01 22:29:12 +01:00
Brett Adams 647ac10dd9 Add climate turn on/off feature to Teslemetry (#109241) 2024-02-01 22:29:06 +01:00
Brett Adams 50dfe4dec0 Add climate on/off feature to Tessie (#109239) 2024-02-01 22:29:03 +01:00
Raman Gupta 52a8216150 Add translations for zwave_js entities and services (#109188) 2024-02-01 22:28:59 +01:00
473 changed files with 6392 additions and 2451 deletions
+1
View File
@@ -485,6 +485,7 @@ omit =
homeassistant/components/gpsd/sensor.py
homeassistant/components/greenwave/light.py
homeassistant/components/growatt_server/__init__.py
homeassistant/components/growatt_server/const.py
homeassistant/components/growatt_server/sensor.py
homeassistant/components/growatt_server/sensor_types/*
homeassistant/components/gstreamer/media_player.py
-2
View File
@@ -786,8 +786,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/media_source/ @hunterjm
/tests/components/media_source/ @hunterjm
/homeassistant/components/mediaroom/ @dgomes
/homeassistant/components/melcloud/ @vilppuvuorinen
/tests/components/melcloud/ @vilppuvuorinen
/homeassistant/components/melissa/ @kennedyshead
/tests/components/melissa/ @kennedyshead
/homeassistant/components/melnor/ @vanstinator
+5 -5
View File
@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2024.01.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2024.01.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2024.01.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2024.01.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2024.01.0
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2024.02.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2024.02.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2024.02.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2024.02.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2024.02.0
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io
+1 -1
View File
@@ -1,6 +1,6 @@
{
"domain": "tplink",
"name": "TP-Link",
"integrations": ["tplink", "tplink_omada", "tplink_lte"],
"integrations": ["tplink", "tplink_omada", "tplink_lte", "tplink_tapo"],
"iot_standards": ["matter"]
}
+1
View File
@@ -74,6 +74,7 @@ class AdaxDevice(ClimateEntity):
)
_attr_target_temperature_step = PRECISION_WHOLE
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, heater_data: dict[str, Any], adax_data_handler: Adax) -> None:
"""Initialize the heater."""
@@ -83,6 +83,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
_attr_max_temp = 32
_attr_min_temp = 16
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
"""Initialize an AdvantageAir AC unit."""
@@ -202,11 +203,16 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
"""AdvantageAir MyTemp Zone control."""
_attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT_COOL]
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_target_temperature_step = PRECISION_WHOLE
_attr_max_temp = 32
_attr_min_temp = 16
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
"""Initialize an AdvantageAir Zone control."""
@@ -24,5 +24,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"iot_class": "local_polling",
"requirements": ["airthings-ble==0.6.0"]
"requirements": ["airthings-ble==0.6.1"]
}
+11 -2
View File
@@ -88,9 +88,13 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
_attr_name = None
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, coordinator, ac_number, info):
"""Initialize the climate device."""
@@ -192,9 +196,14 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
_attr_has_entity_name = True
_attr_name = None
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_hvac_modes = AT_GROUP_MODES
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, coordinator, group_number, info):
"""Initialize the climate device."""
+13 -5
View File
@@ -120,15 +120,12 @@ class Airtouch5ClimateEntity(ClimateEntity, Airtouch5Entity):
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_target_temperature_step = 1
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
class Airtouch5AC(Airtouch5ClimateEntity):
"""Representation of the AC unit. Used to control the overall HVAC Mode."""
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
)
def __init__(self, client: Airtouch5SimpleClient, ability: AcAbility) -> None:
"""Initialise the Climate Entity."""
super().__init__(client)
@@ -152,6 +149,14 @@ class Airtouch5AC(Airtouch5ClimateEntity):
if ability.supports_mode_heat:
self._attr_hvac_modes.append(HVACMode.HEAT)
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
)
if len(self.hvac_modes) > 1:
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
self._attr_fan_modes = []
if ability.supports_fan_speed_quiet:
self._attr_fan_modes.append(FAN_DIFFUSE)
@@ -262,7 +267,10 @@ class Airtouch5Zone(Airtouch5ClimateEntity):
_attr_hvac_modes = [HVACMode.OFF, HVACMode.FAN_ONLY]
_attr_preset_modes = [PRESET_NONE, PRESET_BOOST]
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
def __init__(
+6 -1
View File
@@ -117,6 +117,7 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
_attr_name = None
_speeds: dict[int, str] = {}
_speeds_reverse: dict[str, int] = {}
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
@@ -129,7 +130,11 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
super().__init__(coordinator, entry, system_zone_id, zone_data)
self._attr_unique_id = f"{self._attr_unique_id}_{system_zone_id}"
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
self._attr_target_temperature_step = API_TEMPERATURE_STEP
self._attr_temperature_unit = TEMP_UNIT_LIB_TO_HASS[
self.get_airzone_value(AZD_TEMP_UNIT)
@@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone",
"iot_class": "local_polling",
"loggers": ["aioairzone"],
"requirements": ["aioairzone==0.7.2"]
"requirements": ["aioairzone==0.7.4"]
}
@@ -144,8 +144,8 @@ class AirzoneClimate(AirzoneEntity, ClimateEntity):
"""Define an Airzone Cloud climate."""
_attr_name = None
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
@callback
def _handle_coordinator_update(self) -> None:
@@ -175,6 +175,12 @@ class AirzoneClimate(AirzoneEntity, ClimateEntity):
class AirzoneDeviceClimate(AirzoneClimate):
"""Define an Airzone Cloud Device base class."""
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
async def async_turn_on(self) -> None:
"""Turn the entity on."""
params = {
@@ -212,6 +218,12 @@ class AirzoneDeviceClimate(AirzoneClimate):
class AirzoneDeviceGroupClimate(AirzoneClimate):
"""Define an Airzone Cloud DeviceGroup base class."""
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
async def async_turn_on(self) -> None:
"""Turn the entity on."""
params = {
@@ -3,18 +3,46 @@ from __future__ import annotations
import amberelectric
from amberelectric.api import amber_api
from amberelectric.model.site import Site
from amberelectric.model.site import Site, SiteStatus
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_API_TOKEN
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)
from .const import CONF_SITE_ID, CONF_SITE_NAME, CONF_SITE_NMI, DOMAIN
from .const import CONF_SITE_ID, CONF_SITE_NAME, DOMAIN
API_URL = "https://app.amber.com.au/developers"
def generate_site_selector_name(site: Site) -> str:
"""Generate the name to show in the site drop down in the configuration flow."""
if site.status == SiteStatus.CLOSED:
return site.nmi + " (Closed: " + site.closed_on.isoformat() + ")" # type: ignore[no-any-return]
if site.status == SiteStatus.PENDING:
return site.nmi + " (Pending)" # type: ignore[no-any-return]
return site.nmi # type: ignore[no-any-return]
def filter_sites(sites: list[Site]) -> list[Site]:
"""Deduplicates the list of sites."""
filtered: list[Site] = []
filtered_nmi: set[str] = set()
for site in sorted(sites, key=lambda site: site.status.value):
if site.status == SiteStatus.ACTIVE or site.nmi not in filtered_nmi:
filtered.append(site)
filtered_nmi.add(site.nmi)
return filtered
class AmberElectricConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
@@ -31,7 +59,7 @@ class AmberElectricConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
api: amber_api.AmberApi = amber_api.AmberApi.create(configuration)
try:
sites: list[Site] = api.get_sites()
sites: list[Site] = filter_sites(api.get_sites())
if len(sites) == 0:
self._errors[CONF_API_TOKEN] = "no_site"
return None
@@ -86,38 +114,31 @@ class AmberElectricConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
assert self._sites is not None
assert self._api_token is not None
api_token = self._api_token
if user_input is not None:
site_nmi = user_input[CONF_SITE_NMI]
sites = [site for site in self._sites if site.nmi == site_nmi]
site = sites[0]
site_id = site.id
site_id = user_input[CONF_SITE_ID]
name = user_input.get(CONF_SITE_NAME, site_id)
return self.async_create_entry(
title=name,
data={
CONF_SITE_ID: site_id,
CONF_API_TOKEN: api_token,
CONF_SITE_NMI: site.nmi,
},
data={CONF_SITE_ID: site_id, CONF_API_TOKEN: self._api_token},
)
user_input = {
CONF_API_TOKEN: api_token,
CONF_SITE_NMI: "",
CONF_SITE_NAME: "",
}
return self.async_show_form(
step_id="site",
data_schema=vol.Schema(
{
vol.Required(
CONF_SITE_NMI, default=user_input[CONF_SITE_NMI]
): vol.In([site.nmi for site in self._sites]),
vol.Optional(
CONF_SITE_NAME, default=user_input[CONF_SITE_NAME]
): str,
vol.Required(CONF_SITE_ID): SelectSelector(
SelectSelectorConfig(
options=[
SelectOptionDict(
value=site.id,
label=generate_site_selector_name(site),
)
for site in self._sites
],
mode=SelectSelectorMode.DROPDOWN,
)
),
vol.Optional(CONF_SITE_NAME): str,
}
),
errors=self._errors,
@@ -6,7 +6,6 @@ from homeassistant.const import Platform
DOMAIN = "amberelectric"
CONF_SITE_NAME = "site_name"
CONF_SITE_ID = "site_id"
CONF_SITE_NMI = "site_nmi"
ATTRIBUTION = "Data provided by Amber Electric"
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/amberelectric",
"iot_class": "cloud_polling",
"loggers": ["amberelectric"],
"requirements": ["amberelectric==1.0.4"]
"requirements": ["amberelectric==1.1.0"]
}
@@ -153,10 +153,15 @@ class AmbiclimateEntity(ClimateEntity):
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_target_temperature_step = 1
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
_attr_has_entity_name = True
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, heater: AmbiclimateDevice, store: Store[dict[str, Any]]) -> None:
"""Initialize the thermostat."""
@@ -3,11 +3,15 @@ from __future__ import annotations
from dataclasses import dataclass
from python_homeassistant_analytics import HomeassistantAnalyticsClient
from python_homeassistant_analytics import (
HomeassistantAnalyticsClient,
HomeassistantAnalyticsConnectionError,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_TRACKED_INTEGRATIONS, DOMAIN
@@ -28,7 +32,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Homeassistant Analytics from a config entry."""
client = HomeassistantAnalyticsClient(session=async_get_clientsession(hass))
integrations = await client.get_integrations()
try:
integrations = await client.get_integrations()
except HomeassistantAnalyticsConnectionError as ex:
raise ConfigEntryNotReady("Could not fetch integration list") from ex
names = {}
for integration in entry.options[CONF_TRACKED_INTEGRATIONS]:
@@ -53,10 +53,25 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
) -> FlowResult:
"""Handle the initial step."""
self._async_abort_entries_match()
if user_input:
return self.async_create_entry(
title="Home Assistant Analytics Insights", data={}, options=user_input
)
errors: dict[str, str] = {}
if user_input is not None:
if not user_input.get(CONF_TRACKED_INTEGRATIONS) and not user_input.get(
CONF_TRACKED_CUSTOM_INTEGRATIONS
):
errors["base"] = "no_integrations_selected"
else:
return self.async_create_entry(
title="Home Assistant Analytics Insights",
data={},
options={
CONF_TRACKED_INTEGRATIONS: user_input.get(
CONF_TRACKED_INTEGRATIONS, []
),
CONF_TRACKED_CUSTOM_INTEGRATIONS: user_input.get(
CONF_TRACKED_CUSTOM_INTEGRATIONS, []
),
},
)
client = HomeassistantAnalyticsClient(
session=async_get_clientsession(self.hass)
@@ -78,16 +93,17 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
]
return self.async_show_form(
step_id="user",
errors=errors,
data_schema=vol.Schema(
{
vol.Required(CONF_TRACKED_INTEGRATIONS): SelectSelector(
vol.Optional(CONF_TRACKED_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=options,
multiple=True,
sort=True,
)
),
vol.Required(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
vol.Optional(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=list(custom_integrations),
multiple=True,
@@ -106,8 +122,24 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage the options."""
if user_input:
return self.async_create_entry(title="", data=user_input)
errors: dict[str, str] = {}
if user_input is not None:
if not user_input.get(CONF_TRACKED_INTEGRATIONS) and not user_input.get(
CONF_TRACKED_CUSTOM_INTEGRATIONS
):
errors["base"] = "no_integrations_selected"
else:
return self.async_create_entry(
title="",
data={
CONF_TRACKED_INTEGRATIONS: user_input.get(
CONF_TRACKED_INTEGRATIONS, []
),
CONF_TRACKED_CUSTOM_INTEGRATIONS: user_input.get(
CONF_TRACKED_CUSTOM_INTEGRATIONS, []
),
},
)
client = HomeassistantAnalyticsClient(
session=async_get_clientsession(self.hass)
@@ -129,17 +161,18 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
]
return self.async_show_form(
step_id="init",
errors=errors,
data_schema=self.add_suggested_values_to_schema(
vol.Schema(
{
vol.Required(CONF_TRACKED_INTEGRATIONS): SelectSelector(
vol.Optional(CONF_TRACKED_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=options,
multiple=True,
sort=True,
)
),
vol.Required(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
vol.Optional(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
SelectSelectorConfig(
options=list(custom_integrations),
multiple=True,
@@ -10,6 +10,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -93,6 +94,7 @@ class HomeassistantAnalyticsSensor(
"""Home Assistant Analytics Sensor."""
_attr_has_entity_name = True
_attr_entity_category = EntityCategory.DIAGNOSTIC
entity_description: AnalyticsSensorEntityDescription
@@ -3,25 +3,41 @@
"step": {
"user": {
"data": {
"tracked_integrations": "Integrations"
"tracked_integrations": "Integrations",
"tracked_custom_integrations": "Custom integrations"
},
"data_description": {
"tracked_integrations": "Select the integrations you want to track",
"tracked_custom_integrations": "Select the custom integrations you want to track"
}
}
},
"abort": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
},
"error": {
"no_integration_selected": "You must select at least one integration to track"
}
},
"options": {
"step": {
"init": {
"data": {
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_integrations%]"
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_integrations%]",
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_custom_integrations%]"
},
"data_description": {
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_integrations%]",
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_custom_integrations%]"
}
}
},
"abort": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"error": {
"no_integration_selected": "[%key:component::analytics_insights::config::error::no_integration_selected%]"
}
},
"entity": {
@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aosmith",
"iot_class": "cloud_polling",
"requirements": ["py-aosmith==1.0.6"]
"requirements": ["py-aosmith==1.0.8"]
}
+5 -3
View File
@@ -52,9 +52,11 @@ def get_service(
return None
# Ordered list of URLs
if config.get(CONF_URL) and not a_obj.add(config[CONF_URL]):
_LOGGER.error("Invalid Apprise URL(s) supplied")
return None
if urls := config.get(CONF_URL):
for entry in urls:
if not a_obj.add(entry):
_LOGGER.error("One or more specified Apprise URL(s) are invalid")
return None
return AppriseNotificationService(a_obj)
+13 -1
View File
@@ -11,6 +11,7 @@ from typing import Any, TypeVar, cast
from aioasuswrt.asuswrt import AsusWrt as AsusWrtLegacy
from aiohttp import ClientSession
from pyasuswrt import AsusWrtError, AsusWrtHttp
from pyasuswrt.exceptions import AsusWrtNotAvailableInfoError
from homeassistant.const import (
CONF_HOST,
@@ -354,13 +355,14 @@ class AsusWrtHttpBridge(AsusWrtBridge):
async def async_get_available_sensors(self) -> dict[str, dict[str, Any]]:
"""Return a dictionary of available sensors for this bridge."""
sensors_temperatures = await self._get_available_temperature_sensors()
sensors_loadavg = await self._get_loadavg_sensors_availability()
sensors_types = {
SENSORS_TYPE_BYTES: {
KEY_SENSORS: SENSORS_BYTES,
KEY_METHOD: self._get_bytes,
},
SENSORS_TYPE_LOAD_AVG: {
KEY_SENSORS: SENSORS_LOAD_AVG,
KEY_SENSORS: sensors_loadavg,
KEY_METHOD: self._get_load_avg,
},
SENSORS_TYPE_RATES: {
@@ -393,6 +395,16 @@ class AsusWrtHttpBridge(AsusWrtBridge):
return []
return available_sensors
async def _get_loadavg_sensors_availability(self) -> list[str]:
"""Check if load avg is available on the router."""
try:
await self._api.async_get_loadavg()
except AsusWrtNotAvailableInfoError:
return []
except AsusWrtError:
pass
return SENSORS_LOAD_AVG
@handle_errors_and_zip(AsusWrtError, SENSORS_BYTES)
async def _get_bytes(self) -> Any:
"""Fetch byte information from the router."""
+1
View File
@@ -46,6 +46,7 @@ class AtagThermostat(AtagEntity, ClimateEntity):
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
)
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, coordinator, atag_id):
"""Initialize an Atag climate device."""
+4 -3
View File
@@ -249,10 +249,11 @@ class AugustData(AugustSubscriberMixin):
device = self.get_device_detail(device_id)
activities = activities_from_pubnub_message(device, date_time, message)
activity_stream = self.activity_stream
if activities:
activity_stream.async_process_newer_device_activities(activities)
if activities and activity_stream.async_process_newer_device_activities(
activities
):
self.async_signal_device_id_update(device.device_id)
activity_stream.async_schedule_house_id_refresh(device.house_id)
activity_stream.async_schedule_house_id_refresh(device.house_id)
@callback
def async_stop(self) -> None:
@@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==1.10.0", "yalexs-ble==2.4.0"]
"requirements": ["yalexs==1.11.2", "yalexs-ble==2.4.1"]
}
+6 -1
View File
@@ -33,10 +33,15 @@ async def async_setup_entry(
class BAFAutoComfort(BAFEntity, ClimateEntity):
"""BAF climate auto comfort."""
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_hvac_modes = [HVACMode.OFF, HVACMode.FAN_ONLY]
_attr_translation_key = "auto_comfort"
_enable_turn_on_off_backwards_compatibility = False
@callback
def _async_update_attrs(self) -> None:
@@ -63,6 +63,7 @@ class BalboaClimateEntity(BalboaEntity, ClimateEntity):
)
_attr_translation_key = DOMAIN
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, client: SpaClient) -> None:
"""Initialize the climate entity."""
+6 -1
View File
@@ -53,8 +53,13 @@ async def async_setup_entry(
class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEntity):
"""Representation of a BleBox climate feature (saunaBox)."""
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
@property
def hvac_modes(self):
+5 -6
View File
@@ -14,6 +14,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .coordinator import BringDataUpdateCoordinator
@@ -29,14 +30,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
email = entry.data[CONF_EMAIL]
password = entry.data[CONF_PASSWORD]
bring = Bring(email, password)
def login_and_load_lists() -> None:
bring.login()
bring.loadLists()
session = async_get_clientsession(hass)
bring = Bring(email, password, sessionAsync=session)
try:
await hass.async_add_executor_job(login_and_load_lists)
await bring.loginAsync()
await bring.loadListsAsync()
except BringRequestException as e:
raise ConfigEntryNotReady(
f"Timeout while connecting for email '{email}'"
@@ -11,6 +11,7 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
@@ -48,14 +49,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
bring = Bring(user_input[CONF_EMAIL], user_input[CONF_PASSWORD])
def login_and_load_lists() -> None:
bring.login()
bring.loadLists()
session = async_get_clientsession(self.hass)
bring = Bring(
user_input[CONF_EMAIL], user_input[CONF_PASSWORD], sessionAsync=session
)
try:
await self.hass.async_add_executor_job(login_and_load_lists)
await bring.loginAsync()
await bring.loadListsAsync()
except BringRequestException:
errors["base"] = "cannot_connect"
except BringAuthException:
@@ -40,9 +40,7 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
async def _async_update_data(self) -> dict[str, BringData]:
try:
lists_response = await self.hass.async_add_executor_job(
self.bring.loadLists
)
lists_response = await self.bring.loadListsAsync()
except BringRequestException as e:
raise UpdateFailed("Unable to connect and retrieve data from bring") from e
except BringParseException as e:
@@ -51,9 +49,7 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
list_dict = {}
for lst in lists_response["lists"]:
try:
items = await self.hass.async_add_executor_job(
self.bring.getItems, lst["listUuid"]
)
items = await self.bring.getItemsAsync(lst["listUuid"])
except BringRequestException as e:
raise UpdateFailed(
"Unable to connect and retrieve data from bring"
+1 -1
View File
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/bring",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["python-bring-api==2.0.0"]
"requirements": ["python-bring-api==3.0.0"]
}
+8 -15
View File
@@ -91,11 +91,8 @@ class BringTodoListEntity(
async def async_create_todo_item(self, item: TodoItem) -> None:
"""Add an item to the To-do list."""
try:
await self.hass.async_add_executor_job(
self.coordinator.bring.saveItem,
self.bring_list["listUuid"],
item.summary,
item.description or "",
await self.coordinator.bring.saveItemAsync(
self.bring_list["listUuid"], item.summary, item.description or ""
)
except BringRequestException as e:
raise HomeAssistantError("Unable to save todo item for bring") from e
@@ -126,16 +123,14 @@ class BringTodoListEntity(
assert item.uid
if item.status == TodoItemStatus.COMPLETED:
await self.hass.async_add_executor_job(
self.coordinator.bring.removeItem,
await self.coordinator.bring.removeItemAsync(
bring_list["listUuid"],
item.uid,
)
elif item.summary == item.uid:
try:
await self.hass.async_add_executor_job(
self.coordinator.bring.updateItem,
await self.coordinator.bring.updateItemAsync(
bring_list["listUuid"],
item.uid,
item.description or "",
@@ -144,13 +139,11 @@ class BringTodoListEntity(
raise HomeAssistantError("Unable to update todo item for bring") from e
else:
try:
await self.hass.async_add_executor_job(
self.coordinator.bring.removeItem,
await self.coordinator.bring.removeItemAsync(
bring_list["listUuid"],
item.uid,
)
await self.hass.async_add_executor_job(
self.coordinator.bring.saveItem,
await self.coordinator.bring.saveItemAsync(
bring_list["listUuid"],
item.summary,
item.description or "",
@@ -164,8 +157,8 @@ class BringTodoListEntity(
"""Delete an item from the To-do list."""
for uid in uids:
try:
await self.hass.async_add_executor_job(
self.coordinator.bring.removeItem, self.bring_list["listUuid"], uid
await self.coordinator.bring.removeItemAsync(
self.bring_list["listUuid"], uid
)
except BringRequestException as e:
raise HomeAssistantError("Unable to delete todo item for bring") from e
@@ -35,9 +35,14 @@ class BroadlinkThermostat(ClimateEntity, BroadlinkEntity):
_attr_has_entity_name = True
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF, HVACMode.AUTO]
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_target_temperature_step = PRECISION_HALVES
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, device: BroadlinkDevice) -> None:
"""Initialize the climate entity."""
+5 -1
View File
@@ -73,12 +73,16 @@ class BSBLANClimate(
_attr_name = None
# Determine preset modes
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_preset_modes = PRESET_MODES
# Determine hvac modes
_attr_hvac_modes = HVAC_MODES
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
+1 -1
View File
@@ -98,6 +98,6 @@ class TurboJPEGSingleton:
TurboJPEGSingleton.__instance = TurboJPEG()
except Exception: # pylint: disable=broad-except
_LOGGER.exception(
"Error loading libturbojpeg; Cameras may impact HomeKit performance"
"Error loading libturbojpeg; Camera snapshot performance will be sub-optimal"
)
TurboJPEGSingleton.__instance = False
@@ -64,8 +64,11 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self, ac_host: str, ac_index: int, coordinator: CCM15Coordinator
+25 -9
View File
@@ -1,4 +1,5 @@
"""Intents for the client integration."""
from __future__ import annotations
import voluptuous as vol
@@ -36,24 +37,34 @@ class GetTemperatureIntent(intent.IntentHandler):
if not entities:
raise intent.IntentHandleError("No climate entities")
if "area" in slots:
# Filter by area
area_name = slots["area"]["value"]
name_slot = slots.get("name", {})
entity_name: str | None = name_slot.get("value")
entity_text: str | None = name_slot.get("text")
area_slot = slots.get("area", {})
area_id = area_slot.get("value")
if area_id:
# Filter by area and optionally name
area_name = area_slot.get("text")
for maybe_climate in intent.async_match_states(
hass, area_name=area_name, domains=[DOMAIN]
hass, name=entity_name, area_name=area_id, domains=[DOMAIN]
):
climate_state = maybe_climate
break
if climate_state is None:
raise intent.IntentHandleError(f"No climate entity in area {area_name}")
raise intent.NoStatesMatchedError(
name=entity_text or entity_name,
area=area_name or area_id,
domains={DOMAIN},
device_classes=None,
)
climate_entity = component.get_entity(climate_state.entity_id)
elif "name" in slots:
elif entity_name:
# Filter by name
entity_name = slots["name"]["value"]
for maybe_climate in intent.async_match_states(
hass, name=entity_name, domains=[DOMAIN]
):
@@ -61,7 +72,12 @@ class GetTemperatureIntent(intent.IntentHandler):
break
if climate_state is None:
raise intent.IntentHandleError(f"No climate entity named {entity_name}")
raise intent.NoStatesMatchedError(
name=entity_name,
area=None,
domains={DOMAIN},
device_classes=None,
)
climate_entity = component.get_entity(climate_state.entity_id)
else:
@@ -68,13 +68,22 @@ async def _async_reproduce_states(
[ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW],
)
if ATTR_PRESET_MODE in state.attributes:
if (
ATTR_PRESET_MODE in state.attributes
and state.attributes[ATTR_PRESET_MODE] is not None
):
await call_service(SERVICE_SET_PRESET_MODE, [ATTR_PRESET_MODE])
if ATTR_SWING_MODE in state.attributes:
if (
ATTR_SWING_MODE in state.attributes
and state.attributes[ATTR_SWING_MODE] is not None
):
await call_service(SERVICE_SET_SWING_MODE, [ATTR_SWING_MODE])
if ATTR_FAN_MODE in state.attributes:
if (
ATTR_FAN_MODE in state.attributes
and state.attributes[ATTR_FAN_MODE] is not None
):
await call_service(SERVICE_SET_FAN_MODE, [ATTR_FAN_MODE])
if ATTR_HUMIDITY in state.attributes:
@@ -4,8 +4,12 @@ from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from aioelectricitymaps import ElectricityMaps
from aioelectricitymaps.exceptions import ElectricityMapsError, InvalidToken
from aioelectricitymaps import (
ElectricityMaps,
ElectricityMapsError,
ElectricityMapsInvalidTokenError,
ElectricityMapsNoDataError,
)
import voluptuous as vol
from homeassistant import config_entries
@@ -146,8 +150,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
try:
await fetch_latest_carbon_intensity(self.hass, em, data)
except InvalidToken:
except ElectricityMapsInvalidTokenError:
errors["base"] = "invalid_auth"
except ElectricityMapsNoDataError:
errors["base"] = "no_data"
except ElectricityMapsError:
errors["base"] = "unknown"
else:
@@ -4,9 +4,12 @@ from __future__ import annotations
from datetime import timedelta
import logging
from aioelectricitymaps import ElectricityMaps
from aioelectricitymaps.exceptions import ElectricityMapsError, InvalidToken
from aioelectricitymaps.models import CarbonIntensityResponse
from aioelectricitymaps import (
CarbonIntensityResponse,
ElectricityMaps,
ElectricityMapsError,
ElectricityMapsInvalidTokenError,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -43,7 +46,7 @@ class CO2SignalCoordinator(DataUpdateCoordinator[CarbonIntensityResponse]):
return await fetch_latest_carbon_intensity(
self.hass, self.client, self.config_entry.data
)
except InvalidToken as err:
except ElectricityMapsInvalidTokenError as err:
raise ConfigEntryAuthFailed from err
except ElectricityMapsError as err:
raise UpdateFailed(str(err)) from err
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["aioelectricitymaps"],
"requirements": ["aioelectricitymaps==0.2.0"]
"requirements": ["aioelectricitymaps==0.4.0"]
}
@@ -28,12 +28,9 @@
"error": {
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]",
"api_ratelimit": "API Ratelimit exceeded"
"no_data": "No data is available for the location you have selected."
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"unknown": "[%key:common::config_flow::error::unknown%]",
"api_ratelimit": "[%key:component::co2signal::config::error::api_ratelimit%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
+7 -5
View File
@@ -1,12 +1,11 @@
"""Support for climates."""
from __future__ import annotations
import asyncio
from enum import StrEnum
from typing import Any
from aiocomelit import ComelitSerialBridgeObject
from aiocomelit.const import CLIMATE, SLEEP_BETWEEN_CALLS
from aiocomelit.const import CLIMATE
from homeassistant.components.climate import (
ClimateEntity,
@@ -91,11 +90,16 @@ class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity
_attr_hvac_modes = [HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT, HVACMode.OFF]
_attr_max_temp = 30
_attr_min_temp = 5
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_target_temperature_step = PRECISION_TENTHS
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_has_entity_name = True
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
@@ -186,7 +190,6 @@ class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity
await self.coordinator.api.set_clima_status(
self._device.index, ClimaAction.MANUAL
)
await asyncio.sleep(SLEEP_BETWEEN_CALLS)
await self.coordinator.api.set_clima_status(
self._device.index, ClimaAction.SET, target_temp
)
@@ -198,7 +201,6 @@ class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity
await self.coordinator.api.set_clima_status(
self._device.index, ClimaAction.ON
)
await asyncio.sleep(SLEEP_BETWEEN_CALLS)
await self.coordinator.api.set_clima_status(
self._device.index, MODE_TO_ACTION[hvac_mode]
)
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/comelit",
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"requirements": ["aiocomelit==0.8.2"]
"requirements": ["aiocomelit==0.8.3"]
}
@@ -349,7 +349,7 @@ async def websocket_hass_agent_debug(
},
# Slot values that would be received by the intent
"slots": { # direct access to values
entity_key: entity.value
entity_key: entity.text or entity.value
for entity_key, entity in result.entities.items()
},
# Extra slot details, such as the originally matched text
@@ -1,4 +1,5 @@
"""Standard conversation implementation for Home Assistant."""
from __future__ import annotations
import asyncio
@@ -12,22 +13,15 @@ import re
from typing import IO, Any
from hassil.expression import Expression, ListReference, Sequence
from hassil.intents import (
Intents,
ResponseType,
SlotList,
TextSlotList,
WildcardSlotList,
)
from hassil.intents import Intents, SlotList, TextSlotList, WildcardSlotList
from hassil.recognize import (
MISSING_ENTITY,
RecognizeResult,
UnmatchedEntity,
UnmatchedTextEntity,
recognize_all,
)
from hassil.util import merge_dict
from home_assistant_intents import get_intents, get_languages
from home_assistant_intents import ErrorKey, get_intents, get_languages
import yaml
from homeassistant import core, setup
@@ -229,24 +223,27 @@ class DefaultAgent(AbstractConversationAgent):
# Check if a trigger matched
if isinstance(result, SentenceTriggerResult):
# Gather callback responses in parallel
trigger_responses = await asyncio.gather(
*(
self._trigger_sentences[trigger_id].callback(
result.sentence, trigger_result
)
for trigger_id, trigger_result in result.matched_triggers.items()
trigger_callbacks = [
self._trigger_sentences[trigger_id].callback(
result.sentence, trigger_result
)
)
for trigger_id, trigger_result in result.matched_triggers.items()
]
# Use last non-empty result as response
# Use last non-empty result as response.
#
# There may be multiple copies of a trigger running when editing in
# the UI, so it's critical that we filter out empty responses here.
response_text: str | None = None
for trigger_response in trigger_responses:
response_text = response_text or trigger_response
for trigger_future in asyncio.as_completed(trigger_callbacks):
if trigger_response := await trigger_future:
response_text = trigger_response
break
# Convert to conversation result
response = intent.IntentResponse(language=language)
response.response_type = intent.IntentResponseType.ACTION_DONE
response.async_set_speech(response_text or "")
response.async_set_speech(response_text or "Done")
return ConversationResult(response=response)
@@ -259,7 +256,7 @@ class DefaultAgent(AbstractConversationAgent):
return _make_error_result(
language,
intent.IntentResponseErrorCode.NO_INTENT_MATCH,
self._get_error_text(ResponseType.NO_INTENT, lang_intents),
self._get_error_text(ErrorKey.NO_INTENT, lang_intents),
conversation_id,
)
@@ -268,14 +265,14 @@ class DefaultAgent(AbstractConversationAgent):
_LOGGER.debug(
"Recognized intent '%s' for template '%s' but had unmatched: %s",
result.intent.name,
result.intent_sentence.text
if result.intent_sentence is not None
else "",
(
result.intent_sentence.text
if result.intent_sentence is not None
else ""
),
result.unmatched_entities_list,
)
error_response_type, error_response_args = _get_unmatched_response(
result.unmatched_entities
)
error_response_type, error_response_args = _get_unmatched_response(result)
return _make_error_result(
language,
intent.IntentResponseErrorCode.NO_VALID_TARGETS,
@@ -291,7 +288,8 @@ class DefaultAgent(AbstractConversationAgent):
# Slot values to pass to the intent
slots = {
entity.name: {"value": entity.value} for entity in result.entities_list
entity.name: {"value": entity.value, "text": entity.text or entity.value}
for entity in result.entities_list
}
try:
@@ -318,6 +316,20 @@ class DefaultAgent(AbstractConversationAgent):
),
conversation_id,
)
except intent.DuplicateNamesMatchedError as duplicate_names_error:
# Intent was valid, but two or more entities with the same name matched.
(
error_response_type,
error_response_args,
) = _get_duplicate_names_matched_response(duplicate_names_error)
return _make_error_result(
language,
intent.IntentResponseErrorCode.NO_VALID_TARGETS,
self._get_error_text(
error_response_type, lang_intents, **error_response_args
),
conversation_id,
)
except intent.IntentHandleError:
# Intent was valid and entities matched constraints, but an error
# occurred during handling.
@@ -325,7 +337,7 @@ class DefaultAgent(AbstractConversationAgent):
return _make_error_result(
language,
intent.IntentResponseErrorCode.FAILED_TO_HANDLE,
self._get_error_text(ResponseType.HANDLE_ERROR, lang_intents),
self._get_error_text(ErrorKey.HANDLE_ERROR, lang_intents),
conversation_id,
)
except intent.IntentUnexpectedError:
@@ -333,7 +345,7 @@ class DefaultAgent(AbstractConversationAgent):
return _make_error_result(
language,
intent.IntentResponseErrorCode.UNKNOWN,
self._get_error_text(ResponseType.HANDLE_ERROR, lang_intents),
self._get_error_text(ErrorKey.HANDLE_ERROR, lang_intents),
conversation_id,
)
@@ -480,9 +492,11 @@ class DefaultAgent(AbstractConversationAgent):
for entity_name, entity_value in recognize_result.entities.items()
},
# First matched or unmatched state
"state": template.TemplateState(self.hass, state1)
if state1 is not None
else None,
"state": (
template.TemplateState(self.hass, state1)
if state1 is not None
else None
),
"query": {
# Entity states that matched the query (e.g, "on")
"matched": [
@@ -724,7 +738,12 @@ class DefaultAgent(AbstractConversationAgent):
if async_should_expose(self.hass, DOMAIN, state.entity_id)
]
# Gather exposed entity names
# Gather exposed entity names.
#
# NOTE: We do not pass entity ids in here because multiple entities may
# have the same name. The intent matcher doesn't gather all matching
# values for a list, just the first. So we will need to match by name no
# matter what.
entity_names = []
for state in states:
# Checked against "requires_context" and "excludes_context" in hassil
@@ -753,7 +772,10 @@ class DefaultAgent(AbstractConversationAgent):
# Default name
entity_names.append((state.name, state.name, context))
# Expose all areas
# Expose all areas.
#
# We pass in area id here with the expectation that no two areas will
# share the same name or alias.
areas = ar.async_get(self.hass)
area_names = []
for area in areas.async_list_areas():
@@ -791,11 +813,11 @@ class DefaultAgent(AbstractConversationAgent):
if device_area is None:
return None
return {"area": device_area.id}
return {"area": {"value": device_area.id, "text": device_area.name}}
def _get_error_text(
self,
response_type: ResponseType,
error_key: ErrorKey,
lang_intents: LanguageIntents | None,
**response_args,
) -> str:
@@ -803,7 +825,7 @@ class DefaultAgent(AbstractConversationAgent):
if lang_intents is None:
return _DEFAULT_ERROR_TEXT
response_key = response_type.value
response_key = error_key.value
response_str = (
lang_intents.error_responses.get(response_key) or _DEFAULT_ERROR_TEXT
)
@@ -916,59 +938,86 @@ def _make_error_result(
return ConversationResult(response, conversation_id)
def _get_unmatched_response(
unmatched_entities: dict[str, UnmatchedEntity],
) -> tuple[ResponseType, dict[str, Any]]:
error_response_type = ResponseType.NO_INTENT
error_response_args: dict[str, Any] = {}
def _get_unmatched_response(result: RecognizeResult) -> tuple[ErrorKey, dict[str, Any]]:
"""Get key and template arguments for error when there are unmatched intent entities/slots."""
if unmatched_name := unmatched_entities.get("name"):
# Unmatched device or entity
assert isinstance(unmatched_name, UnmatchedTextEntity)
error_response_type = ResponseType.NO_ENTITY
error_response_args["entity"] = unmatched_name.text
# Filter out non-text and missing context entities
unmatched_text: dict[str, str] = {
key: entity.text.strip()
for key, entity in result.unmatched_entities.items()
if isinstance(entity, UnmatchedTextEntity) and entity.text != MISSING_ENTITY
}
elif unmatched_area := unmatched_entities.get("area"):
# Unmatched area
assert isinstance(unmatched_area, UnmatchedTextEntity)
error_response_type = ResponseType.NO_AREA
error_response_args["area"] = unmatched_area.text
if unmatched_area := unmatched_text.get("area"):
# area only
return ErrorKey.NO_AREA, {"area": unmatched_area}
return error_response_type, error_response_args
# Area may still have matched
matched_area: str | None = None
if matched_area_entity := result.entities.get("area"):
matched_area = matched_area_entity.text.strip()
if unmatched_name := unmatched_text.get("name"):
if matched_area:
# device in area
return ErrorKey.NO_ENTITY_IN_AREA, {
"entity": unmatched_name,
"area": matched_area,
}
# device only
return ErrorKey.NO_ENTITY, {"entity": unmatched_name}
# Default error
return ErrorKey.NO_INTENT, {}
def _get_no_states_matched_response(
no_states_error: intent.NoStatesMatchedError,
) -> tuple[ResponseType, dict[str, Any]]:
"""Return error response type and template arguments for error."""
if not (
no_states_error.area
and (no_states_error.device_classes or no_states_error.domains)
):
# Device class and domain must be paired with an area for the error
# message.
return ResponseType.NO_INTENT, {}
) -> tuple[ErrorKey, dict[str, Any]]:
"""Return key and template arguments for error when intent returns no matching states."""
error_response_args: dict[str, Any] = {"area": no_states_error.area}
# Check device classes first, since it's more specific than domain
# Device classes should be checked before domains
if no_states_error.device_classes:
# No exposed entities of a particular class in an area.
# Example: "close the bedroom windows"
#
# Only use the first device class for the error message
error_response_args["device_class"] = next(iter(no_states_error.device_classes))
device_class = next(iter(no_states_error.device_classes)) # first device class
if no_states_error.area:
# device_class in area
return ErrorKey.NO_DEVICE_CLASS_IN_AREA, {
"device_class": device_class,
"area": no_states_error.area,
}
return ResponseType.NO_DEVICE_CLASS, error_response_args
# device_class only
return ErrorKey.NO_DEVICE_CLASS, {"device_class": device_class}
# No exposed entities of a domain in an area.
# Example: "turn on lights in kitchen"
assert no_states_error.domains
#
# Only use the first domain for the error message
error_response_args["domain"] = next(iter(no_states_error.domains))
if no_states_error.domains:
domain = next(iter(no_states_error.domains)) # first domain
if no_states_error.area:
# domain in area
return ErrorKey.NO_DOMAIN_IN_AREA, {
"domain": domain,
"area": no_states_error.area,
}
return ResponseType.NO_DOMAIN, error_response_args
# domain only
return ErrorKey.NO_DOMAIN, {"domain": domain}
# Default error
return ErrorKey.NO_INTENT, {}
def _get_duplicate_names_matched_response(
duplicate_names_error: intent.DuplicateNamesMatchedError,
) -> tuple[ErrorKey, dict[str, Any]]:
"""Return key and template arguments for error when intent returns duplicate matches."""
if duplicate_names_error.area:
return ErrorKey.DUPLICATE_ENTITIES_IN_AREA, {
"entity": duplicate_names_error.name,
"area": duplicate_names_error.area,
}
return ErrorKey.DUPLICATE_ENTITIES, {"entity": duplicate_names_error.name}
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
@@ -7,5 +7,5 @@
"integration_type": "system",
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": ["hassil==1.6.0", "home-assistant-intents==2024.1.29"]
"requirements": ["hassil==1.6.1", "home-assistant-intents==2024.2.2"]
}
@@ -98,7 +98,12 @@ async def async_attach_trigger(
# mypy does not understand the type narrowing, unclear why
return automation_result.conversation_response # type: ignore[return-value]
return "Done"
# It's important to return None here instead of a string.
#
# When editing in the UI, a copy of this trigger is registered.
# If we return a string from here, there is a race condition between the
# two trigger copies for who will provide a response.
return None
default_agent = await _get_agent_manager(hass).async_get_agent(HOME_ASSISTANT_AGENT)
assert isinstance(default_agent, DefaultAgent)
@@ -54,6 +54,7 @@ class CoolmasterClimate(CoolmasterEntity, ClimateEntity):
"""Representation of a coolmaster climate device."""
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, coordinator, unit_id, info, supported_modes):
"""Initialize the climate device."""
@@ -0,0 +1,8 @@
{
"services": {
"decrement": "mdi:numeric-negative-1",
"increment": "mdi:numeric-positive-1",
"reset": "mdi:refresh",
"set_value": "mdi:counter"
}
}
@@ -128,6 +128,7 @@ class DaikinClimate(ClimateEntity):
_attr_target_temperature_step = 1
_attr_fan_modes: list[str]
_attr_swing_modes: list[str]
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, api: DaikinApi) -> None:
"""Initialize the climate device."""
+6 -1
View File
@@ -100,6 +100,7 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
TYPE = DOMAIN
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, device: Thermostat, gateway: DeconzGateway) -> None:
"""Set up thermostat device."""
@@ -119,7 +120,11 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
HVAC_MODE_TO_DECONZ[item]: item for item in self._attr_hvac_modes
}
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
if device.fan_mode:
self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"loggers": ["deluge_client"],
"requirements": ["deluge-client==1.7.1"]
"requirements": ["deluge-client==1.10.2"]
}
+4
View File
@@ -97,6 +97,7 @@ class DemoClimate(ClimateEntity):
_attr_name = None
_attr_should_poll = False
_attr_translation_key = "ubercool"
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
@@ -137,6 +138,9 @@ class DemoClimate(ClimateEntity):
self._attr_supported_features |= (
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
self._target_temperature = target_temperature
self._target_humidity = target_humidity
self._unit_of_measurement = unit_of_measurement
@@ -0,0 +1,9 @@
{
"entity": {
"sensor": {
"derivative": {
"default": "mdi:chart-line"
}
}
}
}
@@ -64,8 +64,6 @@ UNIT_TIME = {
UnitOfTime.DAYS: 24 * 60 * 60,
}
ICON = "mdi:chart-line"
DEFAULT_ROUND = 3
DEFAULT_TIME_WINDOW = 0
@@ -157,9 +155,9 @@ async def async_setup_platform(
class DerivativeSensor(RestoreSensor, SensorEntity):
"""Representation of an derivative sensor."""
"""Representation of a derivative sensor."""
_attr_icon = ICON
_attr_translation_key = "derivative"
_attr_should_poll = False
def __init__(
@@ -56,6 +56,7 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit
_attr_precision = PRECISION_TENTHS
_attr_hvac_mode = HVACMode.HEAT
_attr_hvac_modes = [HVACMode.HEAT]
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
@@ -8,7 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
"iot_class": "local_push",
"loggers": ["async_upnp_client"],
"requirements": ["async-upnp-client==0.38.1", "getmac==0.9.4"],
"requirements": ["async-upnp-client==0.38.2", "getmac==0.9.4"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
@@ -8,7 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["async-upnp-client==0.38.1"],
"requirements": ["async-upnp-client==0.38.2"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",
+5 -1
View File
@@ -47,12 +47,16 @@ class DuotecnoClimate(DuotecnoEntity, ClimateEntity):
_unit: SensUnit
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_hvac_modes = list(HVACMODE_REVERSE)
_attr_preset_modes = list(PRESETMODES)
_attr_translation_key = "duotecno"
_enable_turn_on_off_backwards_compatibility = False
@property
def current_temperature(self) -> float | None:
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
"iot_class": "cloud_polling",
"quality_scale": "platinum",
"requirements": ["easyenergy==2.1.0"]
"requirements": ["easyenergy==2.1.1"]
}
@@ -323,6 +323,7 @@ class Thermostat(ClimateEntity):
_attr_fan_modes = [FAN_AUTO, FAN_ON]
_attr_name = None
_attr_has_entity_name = True
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self, data: EcobeeData, thermostat_index: int, thermostat: dict
@@ -375,6 +376,10 @@ class Thermostat(ClimateEntity):
supported = supported | ClimateEntityFeature.TARGET_HUMIDITY
if self.has_aux_heat:
supported = supported | ClimateEntityFeature.AUX_HEAT
if len(self.hvac_modes) > 1 and HVACMode.OFF in self.hvac_modes:
supported = (
supported | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
return supported
@property
+8 -6
View File
@@ -66,6 +66,7 @@ class EcoNetThermostat(EcoNetEntity, ClimateEntity):
_attr_should_poll = True
_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, thermostat):
"""Initialize."""
@@ -79,12 +80,13 @@ class EcoNetThermostat(EcoNetEntity, ClimateEntity):
ha_mode = ECONET_STATE_TO_HA[mode]
self._attr_hvac_modes.append(ha_mode)
@property
def supported_features(self) -> ClimateEntityFeature:
"""Return the list of supported features."""
if self._econet.supports_humidifier:
return SUPPORT_FLAGS_THERMOSTAT | ClimateEntityFeature.TARGET_HUMIDITY
return SUPPORT_FLAGS_THERMOSTAT
self._attr_supported_features |= SUPPORT_FLAGS_THERMOSTAT
if thermostat.supports_humidifier:
self._attr_supported_features |= ClimateEntityFeature.TARGET_HUMIDITY
if len(self.hvac_modes) > 1 and HVACMode.OFF in self.hvac_modes:
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
@property
def current_temperature(self):
@@ -75,7 +75,7 @@ async def _validate_input(
rest_config = create_rest_config(
aiohttp_client.async_get_clientsession(hass),
device_id=device_id,
country=country,
alpha_2_country=country,
override_rest_url=rest_url,
)
@@ -266,6 +266,10 @@ class EcovacsConfigFlow(ConfigFlow, domain=DOMAIN):
# If not we will inform the user about the mismatch.
error = None
placeholders = None
# Convert the country to upper case as ISO 3166-1 alpha-2 country codes are upper case
user_input[CONF_COUNTRY] = user_input[CONF_COUNTRY].upper()
if len(user_input[CONF_COUNTRY]) != 2:
error = "invalid_country_length"
placeholders = {"countries_url": "https://www.iso.org/obp/ui/#search/code/"}
@@ -49,7 +49,7 @@ class EcovacsController:
create_rest_config(
aiohttp_client.async_get_clientsession(self._hass),
device_id=self._device_id,
country=country,
alpha_2_country=country,
override_rest_url=config.get(CONF_OVERRIDE_REST_URL),
),
config[CONF_USERNAME],
@@ -74,11 +74,16 @@ class EcovacsController:
async def initialize(self) -> None:
"""Init controller."""
mqtt_config_verfied = False
try:
devices = await self._api_client.get_devices()
credentials = await self._authenticator.authenticate()
for device_config in devices:
if isinstance(device_config, DeviceInfo):
# MQTT device
if not mqtt_config_verfied:
await self._mqtt.verify_config()
mqtt_config_verfied = True
device = Device(device_config, self._authenticator)
await device.initialize(self._mqtt)
self.devices.append(device)
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.8", "deebot-client==5.0.0"]
"requirements": ["py-sucks==0.9.9", "deebot-client==5.2.2"]
}
@@ -47,13 +47,13 @@
"name": "Relocate"
},
"reset_lifespan_brush": {
"name": "Reset brush lifespan"
"name": "Reset main brush lifespan"
},
"reset_lifespan_filter": {
"name": "Reset filter lifespan"
},
"reset_lifespan_side_brush": {
"name": "Reset side brush lifespan"
"name": "Reset side brushes lifespan"
}
},
"image": {
@@ -79,13 +79,13 @@
}
},
"lifespan_brush": {
"name": "Brush lifespan"
"name": "Main brush lifespan"
},
"lifespan_filter": {
"name": "Filter lifespan"
},
"lifespan_side_brush": {
"name": "Side brush lifespan"
"name": "Side brushes lifespan"
},
"network_ip": {
"name": "IP address"
@@ -100,7 +100,7 @@
"name": "Area cleaned"
},
"stats_time": {
"name": "Time cleaned"
"name": "Cleaning duration"
},
"total_stats_area": {
"name": "Total area cleaned"
@@ -109,12 +109,12 @@
"name": "Total cleanings"
},
"total_stats_time": {
"name": "Total time cleaned"
"name": "Total cleaning duration"
}
},
"select": {
"water_amount": {
"name": "Water amount",
"name": "Water flow level",
"state": {
"high": "High",
"low": "Low",
@@ -137,7 +137,7 @@
"name": "Advanced mode"
},
"carpet_auto_fan_boost": {
"name": "Carpet auto fan speed boost"
"name": "Carpet auto-boost suction"
},
"clean_preference": {
"name": "Clean preference"
+1 -1
View File
@@ -95,7 +95,7 @@ class EcovacsLegacyVacuum(StateVacuumEntity):
This will not change the entity's state. If the error caused the state
to change, that will come through as a separate on_status event
"""
if error == "no_error":
if error in ["no_error", sucks.ERROR_CODES["100"]]:
self.error = None
else:
self.error = error
+2 -2
View File
@@ -38,8 +38,8 @@ class EcowittEntity(Entity):
"""Update the state on callback."""
self.async_write_ha_state()
self.ecowitt.update_cb.append(_update_state) # type: ignore[arg-type] # upstream bug
self.async_on_remove(lambda: self.ecowitt.update_cb.remove(_update_state)) # type: ignore[arg-type] # upstream bug
self.ecowitt.update_cb.append(_update_state)
self.async_on_remove(lambda: self.ecowitt.update_cb.remove(_update_state))
@property
def available(self) -> bool:
@@ -6,5 +6,5 @@
"dependencies": ["webhook"],
"documentation": "https://www.home-assistant.io/integrations/ecowitt",
"iot_class": "local_push",
"requirements": ["aioecowitt==2023.5.0"]
"requirements": ["aioecowitt==2024.2.1"]
}
@@ -111,6 +111,7 @@ class ElectraClimateEntity(ClimateEntity):
_attr_hvac_modes = ELECTRA_MODES
_attr_has_entity_name = True
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, device: ElectraAirConditioner, api: ElectraAPI) -> None:
"""Initialize Electra climate entity."""
@@ -121,6 +122,8 @@ class ElectraClimateEntity(ClimateEntity):
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
swing_modes: list = []
@@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["elgato==5.1.1"],
"requirements": ["elgato==5.1.2"],
"zeroconf": ["_elg._tcp.local."]
}
+18 -10
View File
@@ -10,7 +10,7 @@ from types import MappingProxyType
from typing import Any
from elkm1_lib.elements import Element
from elkm1_lib.elk import Elk
from elkm1_lib.elk import Elk, Panel
from elkm1_lib.util import parse_url
import voluptuous as vol
@@ -398,22 +398,30 @@ async def async_wait_for_elk_to_sync(
return success
@callback
def _async_get_elk_panel(hass: HomeAssistant, service: ServiceCall) -> Panel:
"""Get the ElkM1 panel from a service call."""
prefix = service.data["prefix"]
elk = _find_elk_by_prefix(hass, prefix)
if elk is None:
raise HomeAssistantError(f"No ElkM1 with prefix '{prefix}' found")
return elk.panel
def _create_elk_services(hass: HomeAssistant) -> None:
def _getelk(service: ServiceCall) -> Elk:
prefix = service.data["prefix"]
elk = _find_elk_by_prefix(hass, prefix)
if elk is None:
raise HomeAssistantError(f"No ElkM1 with prefix '{prefix}' found")
return elk
"""Create ElkM1 services."""
@callback
def _speak_word_service(service: ServiceCall) -> None:
_getelk(service).panel.speak_word(service.data["number"])
_async_get_elk_panel(hass, service).speak_word(service.data["number"])
@callback
def _speak_phrase_service(service: ServiceCall) -> None:
_getelk(service).panel.speak_phrase(service.data["number"])
_async_get_elk_panel(hass, service).speak_phrase(service.data["number"])
@callback
def _set_time_service(service: ServiceCall) -> None:
_getelk(service).panel.set_time(dt_util.now())
_async_get_elk_panel(hass, service).set_time(dt_util.now())
hass.services.async_register(
DOMAIN, "speak_word", _speak_word_service, SPEAK_SERVICE_SCHEMA
@@ -79,6 +79,8 @@ class ElkThermostat(ElkEntity, ClimateEntity):
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.AUX_HEAT
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_min_temp = 1
_attr_max_temp = 99
@@ -87,6 +89,7 @@ class ElkThermostat(ElkEntity, ClimateEntity):
_attr_target_temperature_step = 1
_attr_fan_modes = [FAN_AUTO, FAN_ON]
_element: Thermostat
_enable_turn_on_off_backwards_compatibility = False
@property
def temperature_unit(self) -> str:
@@ -35,8 +35,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._api_token = api_token = user_input[CONF_API_TOKEN]
client = Elvia(meter_value_token=api_token).meter_value()
try:
end_time = dt_util.utcnow()
results = await client.get_meter_values(
start_time=(dt_util.now() - timedelta(hours=1)).isoformat()
start_time=(end_time - timedelta(hours=1)).isoformat(),
end_time=end_time.isoformat(),
)
except ElviaError.AuthError as exception:
+37 -11
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, cast
from elvia import Elvia
from elvia import Elvia, error as ElviaError
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
from homeassistant.components.recorder.statistics import (
@@ -38,11 +38,18 @@ class ElviaImporter:
self.client = Elvia(meter_value_token=api_token).meter_value()
self.metering_point_id = metering_point_id
async def _fetch_hourly_data(self, since: datetime) -> list[MeterValueTimeSeries]:
async def _fetch_hourly_data(
self,
since: datetime,
until: datetime,
) -> list[MeterValueTimeSeries]:
"""Fetch hourly data."""
LOGGER.debug("Fetching hourly data since %s", since)
start_time = since.isoformat()
end_time = until.isoformat()
LOGGER.debug("Fetching hourly data %s - %s", start_time, end_time)
all_data = await self.client.get_meter_values(
start_time=since.isoformat(),
start_time=start_time,
end_time=end_time,
metering_point_ids=[self.metering_point_id],
)
return all_data["meteringpoints"][0]["metervalue"]["timeSeries"]
@@ -61,18 +68,37 @@ class ElviaImporter:
)
if not last_stats:
# First time we insert 1 years of data (if available)
hourly_data = await self._fetch_hourly_data(
since=dt_util.now() - timedelta(days=365)
)
# First time we insert 3 years of data (if available)
hourly_data: list[MeterValueTimeSeries] = []
until = dt_util.utcnow()
for year in (3, 2, 1):
try:
year_hours = await self._fetch_hourly_data(
since=until - timedelta(days=365 * year),
until=until - timedelta(days=365 * (year - 1)),
)
except ElviaError.ElviaException:
# This will raise if the contract have no data for the
# year, we can safely ignore this
continue
hourly_data.extend(year_hours)
if hourly_data is None or len(hourly_data) == 0:
LOGGER.error("No data available for the metering point")
return
last_stats_time = None
_sum = 0.0
else:
hourly_data = await self._fetch_hourly_data(
since=dt_util.utc_from_timestamp(last_stats[statistic_id][0]["end"])
)
try:
hourly_data = await self._fetch_hourly_data(
since=dt_util.utc_from_timestamp(
last_stats[statistic_id][0]["end"]
),
until=dt_util.utcnow(),
)
except ElviaError.ElviaException as err:
LOGGER.error("Error fetching data: %s", err)
return
if (
hourly_data is None
@@ -1,9 +1,12 @@
"""Support for Enigma2 media players."""
from __future__ import annotations
from aiohttp.client_exceptions import ClientConnectorError
import contextlib
from logging import getLogger
from aiohttp.client_exceptions import ClientConnectorError, ServerDisconnectedError
from openwebif.api import OpenWebIfDevice
from openwebif.enums import RemoteControlCodes, SetVolumeOption
from openwebif.enums import PowerState, RemoteControlCodes, SetVolumeOption
import voluptuous as vol
from yarl import URL
@@ -50,6 +53,8 @@ ATTR_MEDIA_DESCRIPTION = "media_description"
ATTR_MEDIA_END_TIME = "media_end_time"
ATTR_MEDIA_START_TIME = "media_start_time"
_LOGGER = getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
@@ -143,7 +148,12 @@ class Enigma2Device(MediaPlayerEntity):
async def async_turn_off(self) -> None:
"""Turn off media player."""
await self._device.turn_off()
if self._device.turn_off_to_deep:
with contextlib.suppress(ServerDisconnectedError):
await self._device.set_powerstate(PowerState.DEEP_STANDBY)
self._attr_available = False
else:
await self._device.set_powerstate(PowerState.STANDBY)
async def async_turn_on(self) -> None:
"""Turn the media player on."""
@@ -191,8 +201,19 @@ class Enigma2Device(MediaPlayerEntity):
async def async_update(self) -> None:
"""Update state of the media_player."""
await self._device.update()
self._attr_available = not self._device.is_offline
try:
await self._device.update()
except ClientConnectorError as err:
if self._attr_available:
_LOGGER.warning(
"%s is unavailable. Error: %s", self._device.base.host, err
)
self._attr_available = False
return
if not self._attr_available:
_LOGGER.debug("%s is available", self._device.base.host)
self._attr_available = True
if not self._device.status.in_standby:
self._attr_extra_state_attributes = {
@@ -83,6 +83,7 @@ class EphEmberThermostat(ClimateEntity):
_attr_hvac_modes = OPERATION_LIST
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, ember, zone):
"""Initialize the thermostat."""
@@ -100,6 +101,9 @@ class EphEmberThermostat(ClimateEntity):
if self._hot_water:
self._attr_supported_features = ClimateEntityFeature.AUX_HEAT
self._attr_target_temperature_step = None
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
@property
def current_temperature(self):
+2 -2
View File
@@ -88,9 +88,9 @@ class EpionSensor(CoordinatorEntity[EpionCoordinator], SensorEntity):
super().__init__(coordinator)
self._epion_device_id = epion_device_id
self.entity_description = description
self.unique_id = f"{epion_device_id}_{description.key}"
self._attr_unique_id = f"{epion_device_id}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._epion_device_id)},
identifiers={(DOMAIN, epion_device_id)},
manufacturer="Epion",
name=self.device.get("deviceName"),
sw_version=self.device.get("fwVersion"),
+5 -1
View File
@@ -82,10 +82,14 @@ class ControllerEntity(ClimateEntity):
_attr_precision = PRECISION_WHOLE
_attr_should_poll = False
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_target_temperature_step = PRECISION_WHOLE
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, controller: Controller) -> None:
"""Initialise ControllerDevice."""
@@ -137,6 +137,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_translation_key = "climate"
_enable_turn_on_off_backwards_compatibility = False
@callback
def _on_static_info_update(self, static_info: EntityInfo) -> None:
@@ -179,6 +180,8 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
features |= ClimateEntityFeature.FAN_MODE
if self.swing_modes:
features |= ClimateEntityFeature.SWING_MODE
if len(self.hvac_modes) > 1 and HVACMode.OFF in self.hvac_modes:
features |= ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
self._attr_supported_features = features
def _get_precision(self) -> float:
@@ -352,7 +352,6 @@ class ESPHomeManager:
if self.voice_assistant_udp_server is not None:
_LOGGER.warning("Voice assistant UDP server was not stopped")
self.voice_assistant_udp_server.stop()
self.voice_assistant_udp_server.close()
self.voice_assistant_udp_server = None
hass = self.hass
@@ -15,7 +15,7 @@
"iot_class": "local_push",
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
"requirements": [
"aioesphomeapi==21.0.1",
"aioesphomeapi==21.0.2",
"esphome-dashboard-api==1.2.3",
"bleak-esphome==0.4.1"
],
@@ -1,4 +1,5 @@
"""ESPHome voice assistant support."""
from __future__ import annotations
import asyncio
@@ -67,7 +68,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
"""Receive UDP packets and forward them to the voice assistant."""
started = False
stopped = False
stop_requested = False
transport: asyncio.DatagramTransport | None = None
remote_addr: tuple[str, int] | None = None
@@ -92,6 +93,11 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
self._tts_done = asyncio.Event()
self._tts_task: asyncio.Task | None = None
@property
def is_running(self) -> bool:
"""True if the the UDP server is started and hasn't been asked to stop."""
return self.started and (not self.stop_requested)
async def start_server(self) -> int:
"""Start accepting connections."""
@@ -99,7 +105,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
"""Accept connection."""
if self.started:
raise RuntimeError("Can only start once")
if self.stopped:
if self.stop_requested:
raise RuntimeError("No longer accepting connections")
self.started = True
@@ -124,7 +130,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
@callback
def datagram_received(self, data: bytes, addr: tuple[str, int]) -> None:
"""Handle incoming UDP packet."""
if not self.started or self.stopped:
if not self.is_running:
return
if self.remote_addr is None:
self.remote_addr = addr
@@ -142,19 +148,19 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
def stop(self) -> None:
"""Stop the receiver."""
self.queue.put_nowait(b"")
self.started = False
self.stopped = True
self.close()
def close(self) -> None:
"""Close the receiver."""
self.started = False
self.stopped = True
self.stop_requested = True
if self.transport is not None:
self.transport.close()
async def _iterate_packets(self) -> AsyncIterable[bytes]:
"""Iterate over incoming packets."""
if not self.started or self.stopped:
if not self.is_running:
raise RuntimeError("Not running")
while data := await self.queue.get():
@@ -303,8 +309,11 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
async def _send_tts(self, media_id: str) -> None:
"""Send TTS audio to device via UDP."""
# Always send stream start/end events
self.handle_event(VoiceAssistantEventType.VOICE_ASSISTANT_TTS_STREAM_START, {})
try:
if self.transport is None:
if (not self.is_running) or (self.transport is None):
return
extension, data = await tts.async_get_media_source_audio(
@@ -337,15 +346,11 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
_LOGGER.debug("Sending %d bytes of audio", audio_bytes_size)
self.handle_event(
VoiceAssistantEventType.VOICE_ASSISTANT_TTS_STREAM_START, {}
)
bytes_per_sample = stt.AudioBitRates.BITRATE_16 // 8
sample_offset = 0
samples_left = audio_bytes_size // bytes_per_sample
while samples_left > 0:
while (samples_left > 0) and self.is_running:
bytes_offset = sample_offset * bytes_per_sample
chunk: bytes = audio_bytes[bytes_offset : bytes_offset + 1024]
samples_in_chunk = len(chunk) // bytes_per_sample
+8 -1
View File
@@ -156,6 +156,7 @@ class EvoClimateEntity(EvoDevice, ClimateEntity):
"""Base for an evohome Climate device."""
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
@property
def hvac_modes(self) -> list[HVACMode]:
@@ -190,7 +191,10 @@ class EvoZone(EvoChild, EvoClimateEntity):
]
self._attr_supported_features = (
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
async def async_zone_svc_request(self, service: str, data: dict[str, Any]) -> None:
@@ -372,6 +376,9 @@ class EvoController(EvoClimateEntity):
]
if self._attr_preset_modes:
self._attr_supported_features = ClimateEntityFeature.PRESET_MODE
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
async def async_tcs_svc_request(self, service: str, data: dict[str, Any]) -> None:
"""Process a service request (system mode) for a controller.
@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/evohome",
"iot_class": "cloud_polling",
"loggers": ["evohomeasync", "evohomeasync2"],
"requirements": ["evohome-async==0.4.17"]
"requirements": ["evohome-async==0.4.19"]
}
@@ -126,6 +126,8 @@ async def async_setup_entry(
class FibaroThermostat(FibaroDevice, ClimateEntity):
"""Representation of a Fibaro Thermostat."""
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, fibaro_device: DeviceModel) -> None:
"""Initialize the Fibaro device."""
super().__init__(fibaro_device)
@@ -209,6 +211,11 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
if mode in OPMODES_PRESET:
self._attr_preset_modes.append(OPMODES_PRESET[mode])
if HVACMode.OFF in self._attr_hvac_modes and len(self._attr_hvac_modes) > 1:
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass."""
_LOGGER.debug(
@@ -69,6 +69,7 @@ class Flexit(ClimateEntity):
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self, hub: ModbusHub, modbus_slave: int | None, name: str | None
@@ -62,13 +62,17 @@ class FlexitClimateEntity(FlexitEntity, ClimateEntity):
]
_attr_supported_features = (
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_target_temperature_step = PRECISION_HALVES
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_max_temp = MAX_TEMP
_attr_min_temp = MIN_TEMP
_enable_turn_on_off_backwards_compatibility = False
def __init__(self, coordinator: FlexitCoordinator) -> None:
"""Initialize the Flexit unit."""
+8 -2
View File
@@ -7,6 +7,7 @@ from typing import Any
from aioflo.api import API
from aioflo.errors import RequestError
from orjson import JSONDecodeError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -18,6 +19,8 @@ from .const import DOMAIN as FLO_DOMAIN, LOGGER
class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Flo device object."""
_failure_count: int = 0
def __init__(
self, hass: HomeAssistant, api_client: API, location_id: str, device_id: str
) -> None:
@@ -43,8 +46,11 @@ class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=
await self.send_presence_ping()
await self._update_device()
await self._update_consumption_data()
except RequestError as error:
raise UpdateFailed(error) from error
self._failure_count = 0
except (RequestError, TimeoutError, JSONDecodeError) as error:
self._failure_count += 1
if self._failure_count > 3:
raise UpdateFailed(error) from error
@property
def location_id(self) -> str:
@@ -11,7 +11,7 @@ from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.data_entry_flow import FlowResult
from .const import DOMAIN
from .router import get_api
from .router import get_api, get_hosts_list_if_supported
_LOGGER = logging.getLogger(__name__)
@@ -69,7 +69,7 @@ class FreeboxFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# Check permissions
await fbx.system.get_config()
await fbx.lan.get_hosts_list()
await get_hosts_list_if_supported(fbx)
# Close connection
await fbx.close()
+30 -21
View File
@@ -64,6 +64,33 @@ async def get_api(hass: HomeAssistant, host: str) -> Freepybox:
return Freepybox(APP_DESC, token_file, API_VERSION)
async def get_hosts_list_if_supported(
fbx_api: Freepybox,
) -> tuple[bool, list[dict[str, Any]]]:
"""Hosts list is not supported when freebox is configured in bridge mode."""
supports_hosts: bool = True
fbx_devices: list[dict[str, Any]] = []
try:
fbx_devices = await fbx_api.lan.get_hosts_list() or []
except HttpRequestError as err:
if (
(matcher := re.search(r"Request failed \(APIResponse: (.+)\)", str(err)))
and is_json(json_str := matcher.group(1))
and (json_resp := json.loads(json_str)).get("error_code") == "nodev"
):
# No need to retry, Host list not available
supports_hosts = False
_LOGGER.debug(
"Host list is not available using bridge mode (%s)",
json_resp.get("msg"),
)
else:
raise err
return supports_hosts, fbx_devices
class FreeboxRouter:
"""Representation of a Freebox router."""
@@ -111,27 +138,9 @@ class FreeboxRouter:
# Access to Host list not available in bridge mode, API return error_code 'nodev'
if self.supports_hosts:
try:
fbx_devices = await self._api.lan.get_hosts_list()
except HttpRequestError as err:
if (
(
matcher := re.search(
r"Request failed \(APIResponse: (.+)\)", str(err)
)
)
and is_json(json_str := matcher.group(1))
and (json_resp := json.loads(json_str)).get("error_code") == "nodev"
):
# No need to retry, Host list not available
self.supports_hosts = False
_LOGGER.debug(
"Host list is not available using bridge mode (%s)",
json_resp.get("msg"),
)
else:
raise err
self.supports_hosts, fbx_devices = await get_hosts_list_if_supported(
self._api
)
# Adds the Freebox itself
fbx_devices.append(
@@ -64,10 +64,15 @@ class Device(CoordinatorEntity[FreedomproDataUpdateCoordinator], ClimateEntity):
_attr_hvac_modes = SUPPORTED_HVAC_MODES
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_name = None
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_current_temperature = 0
_attr_target_temperature = 0
_attr_hvac_mode = HVACMode.OFF
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
@@ -80,6 +80,7 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
| ClimateEntityFeature.TURN_ON
)
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False
@property
def current_temperature(self) -> float:
@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20240131.0"]
"requirements": ["home-assistant-frontend==20240207.1"]
}
+1 -1
View File
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aio_georss_gdacs", "aio_georss_client"],
"quality_scale": "platinum",
"requirements": ["aio-georss-gdacs==0.8"]
"requirements": ["aio-georss-gdacs==0.9"]
}
@@ -8,6 +8,7 @@ import logging
from typing import Any
import httpx
import voluptuous as vol
import yarl
from homeassistant.components.camera import Camera, CameraEntityFeature
@@ -140,6 +141,12 @@ class GenericCamera(Camera):
_LOGGER.error("Error parsing template %s: %s", self._still_image_url, err)
return self._last_image
try:
vol.Schema(vol.Url())(url)
except vol.Invalid as err:
_LOGGER.warning("Invalid URL '%s': %s, returning last image", url, err)
return self._last_image
if url == self._last_url and self._limit_refetch:
return self._last_image
@@ -178,6 +178,7 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
"""Representation of a Generic Thermostat device."""
_attr_should_poll = False
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
@@ -225,7 +226,11 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
self._target_temp = target_temp
self._attr_temperature_unit = unit
self._attr_unique_id = unique_id
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
if len(presets):
self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
self._attr_preset_modes = [PRESET_NONE] + list(presets.keys())

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