Compare commits

...

133 Commits

Author SHA1 Message Date
Franck Nijhof
2b64f6f2ab 2024.7.0 (#120579) 2024-07-03 18:52:01 +02:00
Franck Nijhof
1080a4ef1e Bump version to 2024.7.0 2024-07-03 17:55:58 +02:00
Franck Nijhof
d94b36cfbb Bump version to 2024.7.0b11 2024-07-03 17:29:08 +02:00
Marcel van der Veldt
85168239cd Matter fix Energy sensor discovery schemas (#121080) 2024-07-03 17:28:59 +02:00
Robert Resch
547b24ce58 Bump deebot-client to 8.1.0 (#121078) 2024-07-03 17:28:56 +02:00
Michael Hansen
e8bcb3e11e Bump intents to 2024.7.3 (#121076) 2024-07-03 17:28:53 +02:00
Marcel van der Veldt
c89a9b5ce0 Bump python-matter-server to 6.2.2 (#121072) 2024-07-03 17:28:49 +02:00
Robert Svensson
13631250b4 Bump axis to v62 (#121070) 2024-07-03 17:28:46 +02:00
Bram Kragten
6621cf475a Update frontend to 20240703.0 (#121063) 2024-07-03 17:28:43 +02:00
Anton Tolchanov
36e74cd9a6 Generate Prometheus metrics in an executor job (#121058) 2024-07-03 17:28:38 +02:00
Kevin Stillhammer
16827ea09e Bump here-transit to 1.2.1 (#120900) 2024-07-03 17:27:10 +02:00
Kevin Stillhammer
c4956b66b0 Bump here-routing to 1.0.1 (#120877) 2024-07-03 17:27:06 +02:00
Franck Nijhof
84204c38be Bump version to 2024.7.0b10 2024-07-03 08:59:52 +02:00
J. Nick Koston
febd1a3772 Bump inkbird-ble to 0.5.7 (#121039)
changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.5.6...v0.5.7
2024-07-03 08:59:45 +02:00
Allen Porter
1665cb40ac Bump gcal_sync to 6.1.4 (#120941) 2024-07-03 08:59:41 +02:00
Franck Nijhof
1b9f27fab7 Bump version to 2024.7.0b9 2024-07-02 22:15:17 +02:00
Bram Kragten
d1e76d5c3c Update frontend to 20240702.0 (#121032) 2024-07-02 22:15:09 +02:00
Jan-Philipp Benecke
4377f4cbea Temporarily set apprise log level to debug in tests (#121029)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-07-02 22:15:05 +02:00
Franck Nijhof
6b045a7d7b Bump version to 2024.7.0b8 2024-07-02 21:09:55 +02:00
Marcel van der Veldt
1fa6972a66 Handle mains power for Matter appliances (#121023) 2024-07-02 21:09:39 +02:00
Marcel van der Veldt
b3e833f677 Fix setting target temperature for single setpoint Matter thermostat (#121011) 2024-07-02 21:09:36 +02:00
starkillerOG
807ed0ce10 Do not hold core startup with reolink firmware check task (#120985) 2024-07-02 21:09:32 +02:00
starkillerOG
5cb41106b5 Reolink replace automatic removal of devices by manual removal (#120981)
Co-authored-by: Robert Resch <robert@resch.dev>
2024-07-02 21:09:28 +02:00
Joost Lekkerkerker
98a2e46d4a Remove Aladdin Connect integration (#120980) 2024-07-02 21:08:14 +02:00
Joost Lekkerkerker
24afbde79e Bump yt-dlp to 2024.07.01 (#120978) 2024-07-02 21:05:52 +02:00
starkillerOG
65d2ca53cb Bump reolink-aio to 0.9.4 (#120964) 2024-07-02 21:05:49 +02:00
Joost Lekkerkerker
23b905b422 Bump airgradient to 0.6.1 (#120962) 2024-07-02 21:05:46 +02:00
Joost Lekkerkerker
de458493f8 Fix missing airgradient string (#120957) 2024-07-02 21:05:42 +02:00
Erik Montnemery
efd3252849 Create log files in an executor thread (#120912) 2024-07-02 21:05:39 +02:00
Jesse Hills
3b6acd5380 [ESPHome] Disable dashboard based update entities by default (#120907)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2024-07-02 21:05:36 +02:00
Teemu R
1e6dc74812 Minor polishing for tplink (#120868) 2024-07-02 21:05:22 +02:00
Franck Nijhof
74687f3b60 Bump version to 2024.7.0b7 2024-07-01 19:44:51 +02:00
Markus Jacobsen
2f307d6a8a Fix Bang & Olufsen jumping volume bar (#120946) 2024-07-01 19:44:37 +02:00
J. Nick Koston
d8f55763c5 Downgrade logging previously reported asyncio block to debug (#120942) 2024-07-01 19:44:34 +02:00
Steven B
4b2be448f0 Bump python-kasa to 0.7.0.2 (#120940) 2024-07-01 19:44:31 +02:00
Marcel van der Veldt
8a7e2c05a5 Mark dry/fan-only climate modes as supported for Panasonic room air conditioner (#120939) 2024-07-01 19:44:28 +02:00
Paulus Schoutsen
887ab1dc58 Bump openai to 1.35.1 (#120926)
Bump openai to 1.35.7
2024-07-01 19:44:25 +02:00
Jan Bouwhuis
a787ce8633 Bump incomfort-client dependency to 0.6.3 (#120913) 2024-07-01 19:44:22 +02:00
Robert Resch
88ed43c779 Improve add user error messages (#120909) 2024-07-01 19:44:19 +02:00
dougiteixeira
16d7764f18 Add missing translations for device class in Template (#120893) 2024-07-01 19:44:15 +02:00
dougiteixeira
a0f8012f48 Add missing translations for device class in SQL (#120892) 2024-07-01 19:44:12 +02:00
dougiteixeira
5a052feb87 Add missing translations for device class in Scrape (#120891) 2024-07-01 19:44:09 +02:00
Allen Porter
779a7ddaa2 Bump ical to 8.1.1 (#120888) 2024-07-01 19:44:06 +02:00
Shay Levy
a9740faeda Fix Shelly device shutdown (#120881) 2024-07-01 19:44:03 +02:00
Thomas55555
3a0e85beb8 Bump aioautomower to 2024.6.4 (#120875) 2024-07-01 19:43:59 +02:00
Yuxin Wang
c19fb35d02 Add handling for different STATFLAG formats in APCUPSD (#120870)
* Add handling for different STATFLAG formats

* Just use removesuffix
2024-07-01 19:43:56 +02:00
J. Nick Koston
6f716c1753 Fix publish cancellation handling in MQTT (#120826) 2024-07-01 19:43:53 +02:00
Jan Bouwhuis
40384b9acd Split mqtt client tests (#120636) 2024-07-01 19:43:50 +02:00
Jan Bouwhuis
3bbf8df6d6 Cleanup mqtt platform tests part 4 (init) (#120574) 2024-07-01 19:43:46 +02:00
Franck Nijhof
14af3661f3 Bump version to 2024.7.0b6 2024-06-30 20:42:10 +02:00
Michael
af733425c2 Bump pyfritzhome to 0.6.12 (#120861) 2024-06-30 20:41:51 +02:00
Allen Porter
4fc89e8861 Rollback PyFlume to 0.6.5 (#120846) 2024-06-30 20:39:47 +02:00
Tsvi Mostovicz
bcec268c04 Fix Jewish calendar unique id move to entity (#120842) 2024-06-30 20:39:44 +02:00
Shay Levy
becf9fcce2 Bump aiowebostv to 0.4.1 (#120838) 2024-06-30 20:39:41 +02:00
Etienne Soufflet
ad9e0ef8e4 Fix Tado fan mode (#120809) 2024-06-30 20:39:38 +02:00
Simon Lamon
f58eafe8fc Fix routes with transfer in nmbs integration (#120808) 2024-06-30 20:39:35 +02:00
mkmer
a7246400b3 Allow EM heat on from any mode in Honeywell (#120750) 2024-06-30 20:39:32 +02:00
Joost Lekkerkerker
38a30b343d Bump pizzapi to 0.0.6 (#120691) 2024-06-30 20:39:28 +02:00
Franck Nijhof
08a0eaf184 Bump version to 2024.7.0b5 2024-06-29 17:51:45 +02:00
Joost Lekkerkerker
3ee8f6edba Use meal note as fallback in Mealie (#120828) 2024-06-29 17:51:34 +02:00
Joost Lekkerkerker
e866417c01 Add icons to Airgradient (#120820) 2024-06-29 17:51:31 +02:00
Joost Lekkerkerker
05c63eb884 Bump python-opensky to 1.0.1 (#120818) 2024-06-29 17:51:28 +02:00
Joost Lekkerkerker
bb52bfd73d Add unique id to Mealie config entry (#120816) 2024-06-29 17:51:25 +02:00
Joost Lekkerkerker
7319492bf3 Bump aiomealie to 0.5.0 (#120815) 2024-06-29 17:51:21 +02:00
J. Nick Koston
66932e3d9a Fix unneeded dict values for MATCH_ALL recorder attrs exclude (#120804)
* Small cleanup to handling MATCH_ALL recorder attrs exclude

* Fix unneeded dict values for MATCH_ALL recorder attrs exclude

The exclude is a set so the dict values were not needed

* Fix unneeded dict values for MATCH_ALL recorder attrs exclude

The exclude is a set so the dict values were not needed

* Fix unneeded dict values for MATCH_ALL recorder attrs exclude

The exclude is a set so the dict values were not needed
2024-06-29 17:51:18 +02:00
J. Nick Koston
0ec07001bd Fix blocking I/O in xmpp notify to read uploaded files (#120801)
detected by ruff in https://github.com/home-assistant/core/pull/120799
2024-06-29 17:51:15 +02:00
J. Nick Koston
0dcfd38cdc Fix missing f-string in loop util (#120800) 2024-06-29 17:51:12 +02:00
Klaas Schoute
b45eff9a2b Bump gridnet lib to v5.0.1 (#120793) 2024-06-29 17:51:09 +02:00
Klaas Schoute
ec577c7bd3 Bump odp-amsterdam lib to v6.0.2 (#120788) 2024-06-29 17:51:06 +02:00
Paul Bottein
723c4a1eb5 Update frontend to 20240628.0 (#120785)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-06-29 17:51:02 +02:00
Klaas Schoute
b30b4d5a3a Bump energyzero lib to v2.1.1 (#120783) 2024-06-29 17:50:59 +02:00
Matthew FitzGerald-Chamberlain
8165acddeb Bump pyaprilaire to 0.7.4 (#120782) 2024-06-29 17:50:56 +02:00
Joost Lekkerkerker
0f3ed3bb67 Bump aiowithings to 3.0.2 (#120765) 2024-06-29 17:50:53 +02:00
Maciej Bieniek
d1a96ef362 Do not call async_delete_issue() if there is no issue to delete in Shelly integration (#120762)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-06-29 17:50:50 +02:00
J. Nick Koston
917eeba984 Increase mqtt availablity timeout to 50s (#120760) 2024-06-29 17:50:46 +02:00
Clifford Roche
59bb8b360e Bump greeclimate to 1.4.6 (#120758) 2024-06-29 17:50:43 +02:00
Klaas Schoute
6028e5b77a Bump p1monitor lib to v3.0.1 (#120756) 2024-06-29 17:50:40 +02:00
Klaas Schoute
83df470307 Bump easyenergy lib to v2.1.2 (#120753) 2024-06-29 17:50:37 +02:00
Joost Lekkerkerker
20ac0aa7b1 Bump govee-local-api to 1.5.1 (#120747) 2024-06-29 17:50:34 +02:00
Joost Lekkerkerker
f57c942901 Bump sense-energy to 0.12.4 (#120744)
* Bump sense-energy to 0.12.4

* Fix
2024-06-29 17:50:31 +02:00
Alexey ALERT Rubashёff
8994ab1686 Add warm water remaining volume sensor to Overkiz (#120718)
* warm water remaining volume sensor

* Update homeassistant/components/overkiz/sensor.py

Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>

---------

Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
2024-06-29 17:50:28 +02:00
Alexey ALERT Rubashёff
b350ba9657 Add electrical consumption sensor to Overkiz (#120717)
electrical consumption sensor
2024-06-29 17:50:25 +02:00
wittypluck
5fd589053a Reject small uptime updates for Unifi clients (#120398)
Extend logic to reject small uptime updates to Unifi clients + add unit tests
2024-06-29 17:50:22 +02:00
Allen Porter
2d5961fa4f Bump gcal_sync to 6.1.3 (#120278) 2024-06-29 17:50:18 +02:00
Franck Nijhof
d7a59748cf Bump version to 2024.7.0b4 2024-06-28 13:38:24 +02:00
tronikos
cada78496b Fix Google Generative AI: 400 Request contains an invalid argument (#120741) 2024-06-28 13:31:00 +02:00
Illia
c5fa9ad272 Bump asyncarve to 0.1.1 (#120740) 2024-06-28 13:30:51 +02:00
epenet
fe8b5656dd Separate renault strings (#120737) 2024-06-28 13:30:48 +02:00
epenet
0ae11b0335 Bump renault-api to 0.2.4 (#120727) 2024-06-28 13:30:45 +02:00
Dave Leaver
76780ca04e Bump airtouch5py to 1.2.0 (#120715)
* Bump airtouch5py to fix console 1.2.0

* Bump airtouch5py again
2024-06-28 13:30:38 +02:00
Brett Adams
3932ce57b9 Check Tessie scopes to fix startup bug (#120710)
* Add scope check

* Add tests

* Bump Teslemetry
2024-06-28 13:30:35 +02:00
dougiteixeira
35d145d3bc Link the Statistics helper entity to the source entity device (#120705) 2024-06-28 13:30:30 +02:00
Maciej Bieniek
1227d56aa2 Bump nextdns to version 3.1.0 (#120703)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-06-28 13:30:27 +02:00
Joost Lekkerkerker
ef3ecb6183 Bump apsystems-ez1 to 1.3.3 (#120702) 2024-06-28 13:30:18 +02:00
Joost Lekkerkerker
ca515f740e Bump panasonic_viera to 0.4.2 (#120692)
* Bump panasonic_viera to 0.4.2

* Bump panasonic_viera to 0.4.2

* Bump panasonic_viera to 0.4.2

* Fix Keys
2024-06-28 13:30:15 +02:00
Erik Montnemery
876fb234ce Bump hatasmota to 0.9.2 (#120670) 2024-06-28 13:30:10 +02:00
Erik Montnemery
f28cbf1909 Set stateclass on unknown numeric Tasmota sensors (#120650) 2024-06-28 13:30:06 +02:00
Franck Nijhof
9b5d0f72dc Bump version to 2024.7.0b3 2024-06-27 22:20:25 +02:00
Steven B
23056f839b Update tplink unlink identifiers to deal with ids from other domains (#120596) 2024-06-27 22:20:02 +02:00
Joost Lekkerkerker
0b8dd738f1 Bump ttls to 1.8.3 (#120700) 2024-06-27 22:19:25 +02:00
Glenn Waters
411633d3b3 Bump Environment Canada to 0.7.1 (#120699) 2024-06-27 22:19:22 +02:00
Thomas55555
f3ab3bd5cb Bump aioautomower to 2024.6.3 (#120697) 2024-06-27 22:19:19 +02:00
Bram Kragten
476b9909ac Update frontend to 20240627.0 (#120693) 2024-06-27 22:19:16 +02:00
Glenn Waters
e756328d52 Bump upb-lib to 0.5.7 (#120689) 2024-06-27 22:19:13 +02:00
J. Nick Koston
b9c9921847 Add newer models to unifi integrations discovery (#120688) 2024-06-27 22:19:10 +02:00
MatthewFlamm
09dbd8e7eb Use more observations in NWS (#120687)
Use more observations
2024-06-27 22:19:07 +02:00
Glenn Waters
07dd832c58 Bump Environment Canada to 0.7.0 (#120686) 2024-06-27 22:19:04 +02:00
J. Nick Koston
f9c5661c66 Bump unifi-discovery to 1.2.0 (#120684) 2024-06-27 22:19:01 +02:00
J. Nick Koston
94f8f8281f Bump uiprotect to 4.2.0 (#120669) 2024-06-27 22:18:58 +02:00
Erik Montnemery
f6aa25c717 Fix docstring for EventStateEventData (#120662) 2024-06-27 22:18:55 +02:00
Jesse Hills
f9ca85735d [esphome] Add more tests to bring integration to 100% coverage (#120661) 2024-06-27 22:18:52 +02:00
Joost Lekkerkerker
be086c581c Fix Airgradient ABC days name (#120659) 2024-06-27 22:18:49 +02:00
Joost Lekkerkerker
03d198dd64 Fix unknown attribute in MPD (#120657) 2024-06-27 22:18:47 +02:00
Joost Lekkerkerker
a8d6866f9f Disable polling for Knocki (#120656) 2024-06-27 22:18:44 +02:00
Brett Adams
0e1dc9878f Fix values at startup for Tessie (#120652) 2024-06-27 22:18:41 +02:00
Erik Montnemery
6849597764 Bump hatasmota to 0.9.1 (#120649) 2024-06-27 22:18:38 +02:00
Josef Zweck
3022d3bfa0 Move Auto On/off switches to Config EntityCategory (#120648) 2024-06-27 22:18:35 +02:00
Erik Montnemery
4836d6620b Add snapshots to tasmota sensor test (#120647) 2024-06-27 22:18:32 +02:00
Erik Montnemery
b290e95350 Improve typing of state event helpers (#120639) 2024-06-27 22:18:29 +02:00
Josef Zweck
89ac3ce832 Fix the version that raises the issue (#120638) 2024-06-27 22:18:26 +02:00
Erik Montnemery
1933454b76 Rename async_track_state_reported_event to async_track_state_report_event (#120637)
* Rename async_track_state_reported_event to async_track_state_report_event

* Update tests
2024-06-27 22:18:23 +02:00
J. Nick Koston
38601d48ff Add async_track_state_reported_event to fix integration performance regression (#120622)
split from https://github.com/home-assistant/core/pull/120621
2024-06-27 22:18:20 +02:00
J. Nick Koston
7256f23376 Fix performance regression in integration from state_reported (#120621)
* Fix performance regression in integration from state_reported

Because the callbacks were no longer indexed by entity id, users
saw upwards of 1M calls/min

https://github.com/home-assistant/core/pull/113869/files#r1655580523

* Update homeassistant/helpers/event.py

* coverage

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2024-06-27 22:18:17 +02:00
J. Nick Koston
7519603bf5 Bump uiprotect to 4.0.0 (#120617) 2024-06-27 22:18:14 +02:00
Luke Lashley
ef47daad9d Bump anova_wifi to 0.14.0 (#120616) 2024-06-27 22:18:11 +02:00
Erik Montnemery
18d283bed6 Don't allow updating a device to have no connections or identifiers (#120603)
* Don't allow updating a device to have no connections or identifiers

* Move check to the top of the function
2024-06-27 22:18:08 +02:00
Steven B
210e906a4d Store tplink credentials_hash outside of device_config (#120597) 2024-06-27 22:17:51 +02:00
J. Nick Koston
dcffd6bd7a Remove unused fields from unifiprotect event sensors (#120568) 2024-06-27 22:14:15 +02:00
Alexey ALERT Rubashёff
2c2261254b Improve AtlanticDomesticHotWaterProductionMBLComponent support in Overkiz (#114178)
* add overkiz AtlanticDHW support

Adds support of Overkiz water heater entity selection based on device controllable_name
Adds support of Atlantic water heater based on Atlantic Steatite Cube WI-FI VM 150 S4CS 2400W
Adds more Overkiz water heater binary_sensors, numbers, and sensors

* Changed class annotation

* min_temp and max_temp as properties

* reverted binary_sensors, number, sensor to make separate PRs

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_dhw.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_dhw.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_dhw.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* Update homeassistant/components/overkiz/water_heater.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_dhw.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_dhw.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* review fixes, typos, and pylint

* review fix

* review fix

* ruff

* temperature properties changed to constructor attributes

* logger removed

* constants usage consistency

* redundant mapping removed

* Update homeassistant/components/overkiz/water_heater_entities/atlantic_dhw.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* boost mode method annotation typo

* removed away mode for atlantic dwh

* absence and boost mode attributes now support 'prog' state

* heating status bugfix

* electrical consumption sensor

* warm water remaining volume sensor

* away mode reintroduced

* mypy check

* boost plus state support

* Update homeassistant/components/overkiz/sensor.py

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>

* sensors reverted to separate them into their own PR

* check away and boost modes on before switching them off

* atlantic_dhw renamed to atlantic_domestic_hot_water_production

* annotation changed

* AtlanticDomesticHotWaterProductionMBLComponent file renamed, annotation change reverted

---------

Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
2024-06-27 22:14:13 +02:00
Jesse Hills
53e49861a1 Mark esphome integration as platinum (#112565)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-06-27 22:14:08 +02:00
Franck Nijhof
3da8d0a741 Bump version to 2024.7.0b2 2024-06-26 23:55:20 +02:00
Paul Bottein
0701b0daa9 Update frontend to 20240626.2 (#120614) 2024-06-26 23:55:11 +02:00
Luca Angemi
bea6fe30b8 Fix telegram bot thread_id key error (#120613) 2024-06-26 23:55:08 +02:00
212 changed files with 7333 additions and 3962 deletions

View File

@@ -58,11 +58,6 @@ omit =
homeassistant/components/airvisual/sensor.py
homeassistant/components/airvisual_pro/__init__.py
homeassistant/components/airvisual_pro/sensor.py
homeassistant/components/aladdin_connect/__init__.py
homeassistant/components/aladdin_connect/api.py
homeassistant/components/aladdin_connect/application_credentials.py
homeassistant/components/aladdin_connect/cover.py
homeassistant/components/aladdin_connect/sensor.py
homeassistant/components/alarmdecoder/__init__.py
homeassistant/components/alarmdecoder/alarm_control_panel.py
homeassistant/components/alarmdecoder/binary_sensor.py

View File

@@ -80,8 +80,6 @@ build.json @home-assistant/supervisor
/tests/components/airzone/ @Noltari
/homeassistant/components/airzone_cloud/ @Noltari
/tests/components/airzone_cloud/ @Noltari
/homeassistant/components/aladdin_connect/ @swcloudgenie
/tests/components/aladdin_connect/ @swcloudgenie
/homeassistant/components/alarm_control_panel/ @home-assistant/core
/tests/components/alarm_control_panel/ @home-assistant/core
/homeassistant/components/alert/ @home-assistant/core @frenck

View File

@@ -55,13 +55,6 @@ class InvalidUser(HomeAssistantError):
Will not be raised when validating authentication.
"""
class InvalidUsername(InvalidUser):
"""Raised when invalid username is specified.
Will not be raised when validating authentication.
"""
def __init__(
self,
*args: object,
@@ -77,6 +70,13 @@ class InvalidUsername(InvalidUser):
)
class InvalidUsername(InvalidUser):
"""Raised when invalid username is specified.
Will not be raised when validating authentication.
"""
class Data:
"""Hold the user data."""
@@ -216,7 +216,7 @@ class Data:
break
if index is None:
raise InvalidUser
raise InvalidUser(translation_key="user_not_found")
self.users.pop(index)
@@ -232,7 +232,7 @@ class Data:
user["password"] = self.hash_password(new_password, True).decode()
break
else:
raise InvalidUser
raise InvalidUser(translation_key="user_not_found")
@callback
def _validate_new_username(self, new_username: str) -> None:
@@ -275,7 +275,7 @@ class Data:
self._async_check_for_not_normalized_usernames(self._data)
break
else:
raise InvalidUser
raise InvalidUser(translation_key="user_not_found")
async def async_save(self) -> None:
"""Save data."""

View File

@@ -8,7 +8,7 @@ import contextlib
from functools import partial
from itertools import chain
import logging
import logging.handlers
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
import mimetypes
from operator import contains, itemgetter
import os
@@ -257,12 +257,12 @@ async def async_setup_hass(
) -> core.HomeAssistant | None:
"""Set up Home Assistant."""
def create_hass() -> core.HomeAssistant:
async def create_hass() -> core.HomeAssistant:
"""Create the hass object and do basic setup."""
hass = core.HomeAssistant(runtime_config.config_dir)
loader.async_setup(hass)
async_enable_logging(
await async_enable_logging(
hass,
runtime_config.verbose,
runtime_config.log_rotate_days,
@@ -287,7 +287,7 @@ async def async_setup_hass(
async with hass.timeout.async_timeout(10):
await hass.async_stop()
hass = create_hass()
hass = await create_hass()
if runtime_config.skip_pip or runtime_config.skip_pip_packages:
_LOGGER.warning(
@@ -326,13 +326,13 @@ async def async_setup_hass(
if config_dict is None:
recovery_mode = True
await stop_hass(hass)
hass = create_hass()
hass = await create_hass()
elif not basic_setup_success:
_LOGGER.warning("Unable to set up core integrations. Activating recovery mode")
recovery_mode = True
await stop_hass(hass)
hass = create_hass()
hass = await create_hass()
elif any(domain not in hass.config.components for domain in CRITICAL_INTEGRATIONS):
_LOGGER.warning(
@@ -345,7 +345,7 @@ async def async_setup_hass(
recovery_mode = True
await stop_hass(hass)
hass = create_hass()
hass = await create_hass()
if old_logging:
hass.data[DATA_LOGGING] = old_logging
@@ -523,8 +523,7 @@ async def async_from_config_dict(
return hass
@core.callback
def async_enable_logging(
async def async_enable_logging(
hass: core.HomeAssistant,
verbose: bool = False,
log_rotate_days: int | None = None,
@@ -607,23 +606,9 @@ def async_enable_logging(
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
not err_path_exists and os.access(err_dir, os.W_OK)
):
err_handler: (
logging.handlers.RotatingFileHandler
| logging.handlers.TimedRotatingFileHandler
err_handler = await hass.async_add_executor_job(
_create_log_file, err_log_path, log_rotate_days
)
if log_rotate_days:
err_handler = logging.handlers.TimedRotatingFileHandler(
err_log_path, when="midnight", backupCount=log_rotate_days
)
else:
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
err_log_path, backupCount=1
)
try:
err_handler.doRollover()
except OSError as err:
_LOGGER.error("Error rolling over log file: %s", err)
err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
@@ -640,7 +625,29 @@ def async_enable_logging(
async_activate_log_queue_handler(hass)
class _RotatingFileHandlerWithoutShouldRollOver(logging.handlers.RotatingFileHandler):
def _create_log_file(
err_log_path: str, log_rotate_days: int | None
) -> RotatingFileHandler | TimedRotatingFileHandler:
"""Create log file and do roll over."""
err_handler: RotatingFileHandler | TimedRotatingFileHandler
if log_rotate_days:
err_handler = TimedRotatingFileHandler(
err_log_path, when="midnight", backupCount=log_rotate_days
)
else:
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
err_log_path, backupCount=1
)
try:
err_handler.doRollover()
except OSError as err:
_LOGGER.error("Error rolling over log file: %s", err)
return err_handler
class _RotatingFileHandlerWithoutShouldRollOver(RotatingFileHandler):
"""RotatingFileHandler that does not check if it should roll over on every log."""
def shouldRollover(self, record: logging.LogRecord) -> bool:

View File

@@ -8,6 +8,34 @@
"default": "mdi:lightbulb-on-outline"
}
},
"number": {
"led_bar_brightness": {
"default": "mdi:brightness-percent"
},
"display_brightness": {
"default": "mdi:brightness-percent"
}
},
"select": {
"configuration_control": {
"default": "mdi:cloud-cog"
},
"display_temperature_unit": {
"default": "mdi:thermometer-lines"
},
"led_bar_mode": {
"default": "mdi:led-strip"
},
"nox_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"voc_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"co2_automatic_baseline_calibration": {
"default": "mdi:molecule-co2"
}
},
"sensor": {
"total_volatile_organic_component_index": {
"default": "mdi:molecule"
@@ -17,6 +45,32 @@
},
"pm003_count": {
"default": "mdi:blur"
},
"led_bar_brightness": {
"default": "mdi:brightness-percent"
},
"display_brightness": {
"default": "mdi:brightness-percent"
},
"display_temperature_unit": {
"default": "mdi:thermometer-lines"
},
"led_bar_mode": {
"default": "mdi:led-strip"
},
"nox_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"voc_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"co2_automatic_baseline_calibration": {
"default": "mdi:molecule-co2"
}
},
"switch": {
"post_data_to_airgradient": {
"default": "mdi:cogs"
}
}
}

View File

@@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/airgradient",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["airgradient==0.6.0"],
"requirements": ["airgradient==0.6.1"],
"zeroconf": ["_airgradient._tcp.local."]
}

View File

@@ -88,6 +88,7 @@ LEARNING_TIME_OFFSET_OPTIONS = [
]
ABC_DAYS = [
"1",
"8",
"30",
"90",

View File

@@ -16,6 +16,7 @@
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
},
"error": {
@@ -91,8 +92,9 @@
}
},
"co2_automatic_baseline_calibration": {
"name": "CO2 automatic baseline calibration",
"name": "CO2 automatic baseline duration",
"state": {
"1": "1 day",
"8": "8 days",
"30": "30 days",
"90": "90 days",

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/airtouch5",
"iot_class": "local_push",
"loggers": ["airtouch5py"],
"requirements": ["airtouch5py==0.2.8"]
"requirements": ["airtouch5py==0.2.10"]
}

View File

@@ -1,94 +1,38 @@
"""The Aladdin Connect Genie integration."""
# mypy: ignore-errors
from __future__ import annotations
# from genie_partner_sdk.client import AladdinConnectClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import (
OAuth2Session,
async_get_config_entry_implementation,
)
from homeassistant.helpers import issue_registry as ir
from .api import AsyncConfigEntryAuth
from .const import DOMAIN
from .coordinator import AladdinConnectCoordinator
PLATFORMS: list[Platform] = [Platform.COVER, Platform.SENSOR]
type AladdinConnectConfigEntry = ConfigEntry[AladdinConnectCoordinator]
DOMAIN = "aladdin_connect"
async def async_setup_entry(
hass: HomeAssistant, entry: AladdinConnectConfigEntry
) -> bool:
"""Set up Aladdin Connect Genie from a config entry."""
implementation = await async_get_config_entry_implementation(hass, entry)
session = OAuth2Session(hass, entry, implementation)
auth = AsyncConfigEntryAuth(async_get_clientsession(hass), session)
coordinator = AladdinConnectCoordinator(hass, AladdinConnectClient(auth))
await coordinator.async_setup()
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_remove_stale_devices(hass, entry)
return True
async def async_unload_entry(
hass: HomeAssistant, entry: AladdinConnectConfigEntry
) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_entry(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> bool:
"""Migrate old config."""
if config_entry.version < 2:
config_entry.async_start_reauth(hass)
hass.config_entries.async_update_entry(
config_entry,
version=2,
minor_version=1,
)
return True
def async_remove_stale_devices(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> None:
"""Remove stale devices from device registry."""
device_registry = dr.async_get(hass)
device_entries = dr.async_entries_for_config_entry(
device_registry, config_entry.entry_id
async def async_setup_entry(hass: HomeAssistant, _: ConfigEntry) -> bool:
"""Set up Aladdin Connect from a config entry."""
ir.async_create_issue(
hass,
DOMAIN,
DOMAIN,
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="integration_removed",
translation_placeholders={
"entries": "/config/integrations/integration/aladdin_connect",
},
)
all_device_ids = {door.unique_id for door in config_entry.runtime_data.doors}
for device_entry in device_entries:
device_id: str | None = None
return True
for identifier in device_entry.identifiers:
if identifier[0] == DOMAIN:
device_id = identifier[1]
break
if device_id is None or device_id not in all_device_ids:
# If device_id is None an invalid device entry was found for this config entry.
# If the device_id is not in existing device ids it's a stale device entry.
# Remove config entry from this device entry in either case.
device_registry.async_update_device(
device_entry.id, remove_config_entry_id=config_entry.entry_id
)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if all(
config_entry.state is ConfigEntryState.NOT_LOADED
for config_entry in hass.config_entries.async_entries(DOMAIN)
if config_entry.entry_id != entry.entry_id
):
ir.async_delete_issue(hass, DOMAIN, DOMAIN)
return True

View File

@@ -1,33 +0,0 @@
"""API for Aladdin Connect Genie bound to Home Assistant OAuth."""
# mypy: ignore-errors
from typing import cast
from aiohttp import ClientSession
# from genie_partner_sdk.auth import Auth
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
API_URL = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1"
API_KEY = "k6QaiQmcTm2zfaNns5L1Z8duBtJmhDOW8JawlCC3"
class AsyncConfigEntryAuth(Auth): # type: ignore[misc]
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""
def __init__(
self,
websession: ClientSession,
oauth_session: OAuth2Session,
) -> None:
"""Initialize Aladdin Connect Genie auth."""
super().__init__(
websession, API_URL, oauth_session.token["access_token"], API_KEY
)
self._oauth_session = oauth_session
async def async_get_access_token(self) -> str:
"""Return a valid access token."""
await self._oauth_session.async_ensure_token_valid()
return cast(str, self._oauth_session.token["access_token"])

View File

@@ -1,14 +0,0 @@
"""application_credentials platform the Aladdin Connect Genie integration."""
from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant
from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN
async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer:
"""Return authorization server."""
return AuthorizationServer(
authorize_url=OAUTH2_AUTHORIZE,
token_url=OAUTH2_TOKEN,
)

View File

@@ -1,70 +1,11 @@
"""Config flow for Aladdin Connect Genie."""
"""Config flow for Aladdin Connect integration."""
from collections.abc import Mapping
import logging
from typing import Any
from homeassistant.config_entries import ConfigFlow
import jwt
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
from .const import DOMAIN
from . import DOMAIN
class AladdinConnectOAuth2FlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Config flow to handle Aladdin Connect Genie OAuth2 authentication."""
class AladdinConnectConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Aladdin Connect."""
DOMAIN = DOMAIN
VERSION = 2
MINOR_VERSION = 1
reauth_entry: ConfigEntry | None = None
async def async_step_reauth(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon API auth error or upgrade from v1 to v2."""
self.reauth_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: Mapping[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
if user_input is None:
return self.async_show_form(step_id="reauth_confirm")
return await self.async_step_user()
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Create an oauth config entry or update existing entry for reauth."""
token_payload = jwt.decode(
data[CONF_TOKEN][CONF_ACCESS_TOKEN], options={"verify_signature": False}
)
if not self.reauth_entry:
await self.async_set_unique_id(token_payload["sub"])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=token_payload["username"],
data=data,
)
if self.reauth_entry.unique_id == token_payload["username"]:
return self.async_update_reload_and_abort(
self.reauth_entry,
data=data,
unique_id=token_payload["sub"],
)
if self.reauth_entry.unique_id == token_payload["sub"]:
return self.async_update_reload_and_abort(self.reauth_entry, data=data)
return self.async_abort(reason="wrong_account")
@property
def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)
VERSION = 1

View File

@@ -1,6 +0,0 @@
"""Constants for the Aladdin Connect Genie integration."""
DOMAIN = "aladdin_connect"
OAUTH2_AUTHORIZE = "https://app.aladdinconnect.net/login.html"
OAUTH2_TOKEN = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1/oauth2/token"

View File

@@ -1,38 +0,0 @@
"""Define an object to coordinate fetching Aladdin Connect data."""
# mypy: ignore-errors
from datetime import timedelta
import logging
# from genie_partner_sdk.client import AladdinConnectClient
# from genie_partner_sdk.model import GarageDoor
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class AladdinConnectCoordinator(DataUpdateCoordinator[None]):
"""Aladdin Connect Data Update Coordinator."""
def __init__(self, hass: HomeAssistant, acc: AladdinConnectClient) -> None:
"""Initialize."""
super().__init__(
hass,
logger=_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=15),
)
self.acc = acc
self.doors: list[GarageDoor] = []
async def async_setup(self) -> None:
"""Fetch initial data."""
self.doors = await self.acc.get_doors()
async def _async_update_data(self) -> None:
"""Fetch data from API endpoint."""
for door in self.doors:
await self.acc.update_door(door.device_id, door.door_number)

View File

@@ -1,84 +0,0 @@
"""Cover Entity for Genie Garage Door."""
# mypy: ignore-errors
from typing import Any
# from genie_partner_sdk.model import GarageDoor
from homeassistant.components.cover import (
CoverDeviceClass,
CoverEntity,
CoverEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AladdinConnectConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Aladdin Connect platform."""
coordinator = config_entry.runtime_data
async_add_entities(AladdinDevice(coordinator, door) for door in coordinator.doors)
class AladdinDevice(AladdinConnectEntity, CoverEntity):
"""Representation of Aladdin Connect cover."""
_attr_device_class = CoverDeviceClass.GARAGE
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
_attr_name = None
def __init__(
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
) -> None:
"""Initialize the Aladdin Connect cover."""
super().__init__(coordinator, device)
self._attr_unique_id = device.unique_id
async def async_open_cover(self, **kwargs: Any) -> None:
"""Issue open command to cover."""
await self.coordinator.acc.open_door(
self._device.device_id, self._device.door_number
)
async def async_close_cover(self, **kwargs: Any) -> None:
"""Issue close command to cover."""
await self.coordinator.acc.close_door(
self._device.device_id, self._device.door_number
)
@property
def is_closed(self) -> bool | None:
"""Update is closed attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "closed")
@property
def is_closing(self) -> bool | None:
"""Update is closing attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "closing")
@property
def is_opening(self) -> bool | None:
"""Update is opening attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "opening")

View File

@@ -1,27 +0,0 @@
"""Defines a base Aladdin Connect entity."""
# mypy: ignore-errors
# from genie_partner_sdk.model import GarageDoor
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AladdinConnectCoordinator
class AladdinConnectEntity(CoordinatorEntity[AladdinConnectCoordinator]):
"""Defines a base Aladdin Connect entity."""
_attr_has_entity_name = True
def __init__(
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
) -> None:
"""Initialize the entity."""
super().__init__(coordinator)
self._device = device
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device.unique_id)},
name=device.name,
manufacturer="Overhead Door",
)

View File

@@ -1,11 +1,9 @@
{
"domain": "aladdin_connect",
"name": "Aladdin Connect",
"codeowners": ["@swcloudgenie"],
"config_flow": true,
"dependencies": ["application_credentials"],
"disabled": "This integration is disabled because it uses non-open source code to operate.",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"integration_type": "system",
"iot_class": "cloud_polling",
"requirements": ["genie-partner-sdk==1.0.2"]
"requirements": []
}

View File

@@ -1,5 +0,0 @@
extend = "../../../pyproject.toml"
lint.extend-ignore = [
"F821"
]

View File

@@ -1,80 +0,0 @@
"""Support for Aladdin Connect Garage Door sensors."""
# mypy: ignore-errors
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
# from genie_partner_sdk.client import AladdinConnectClient
# from genie_partner_sdk.model import GarageDoor
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
@dataclass(frozen=True, kw_only=True)
class AccSensorEntityDescription(SensorEntityDescription):
"""Describes AladdinConnect sensor entity."""
value_fn: Callable[[AladdinConnectClient, str, int], float | None]
SENSORS: tuple[AccSensorEntityDescription, ...] = (
AccSensorEntityDescription(
key="battery_level",
device_class=SensorDeviceClass.BATTERY,
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=AladdinConnectClient.get_battery_status,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AladdinConnectConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Aladdin Connect sensor devices."""
coordinator = entry.runtime_data
async_add_entities(
AladdinConnectSensor(coordinator, door, description)
for description in SENSORS
for door in coordinator.doors
)
class AladdinConnectSensor(AladdinConnectEntity, SensorEntity):
"""A sensor implementation for Aladdin Connect devices."""
entity_description: AccSensorEntityDescription
def __init__(
self,
coordinator: AladdinConnectCoordinator,
device: GarageDoor,
description: AccSensorEntityDescription,
) -> None:
"""Initialize a sensor for an Aladdin Connect device."""
super().__init__(coordinator, device)
self.entity_description = description
self._attr_unique_id = f"{device.unique_id}-{description.key}"
@property
def native_value(self) -> float | None:
"""Return the state of the sensor."""
return self.entity_description.value_fn(
self.coordinator.acc, self._device.device_id, self._device.door_number
)

View File

@@ -1,29 +1,8 @@
{
"config": {
"step": {
"pick_implementation": {
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Aladdin Connect needs to re-authenticate your account"
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
"issues": {
"integration_removed": {
"title": "The Aladdin Connect integration has been removed",
"description": "The Aladdin Connect integration has been removed from Home Assistant.\n\nTo resolve this issue, please remove the (now defunct) integration entries from your Home Assistant setup. [Click here to see your existing Aladdin Connect integration entries]({entries})."
}
}
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/anova",
"iot_class": "cloud_push",
"loggers": ["anova_wifi"],
"requirements": ["anova-wifi==0.12.0"]
"requirements": ["anova-wifi==0.14.0"]
}

View File

@@ -68,4 +68,8 @@ class OnlineStatus(CoordinatorEntity[APCUPSdCoordinator], BinarySensorEntity):
"""Returns true if the UPS is online."""
# Check if ONLINE bit is set in STATFLAG.
key = self.entity_description.key.upper()
return int(self.coordinator.data[key], 16) & _VALUE_ONLINE_MASK != 0
# The daemon could either report just a hex ("0x05000008"), or a hex with a "Status Flag"
# suffix ("0x05000008 Status Flag") in older versions.
# Here we trim the suffix if it exists to support both.
flag = self.coordinator.data[key].removesuffix(" Status Flag")
return int(flag, 16) & _VALUE_ONLINE_MASK != 0

View File

@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["pyaprilaire"],
"requirements": ["pyaprilaire==0.7.0"]
"requirements": ["pyaprilaire==0.7.4"]
}

View File

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

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/arve",
"iot_class": "cloud_polling",
"requirements": ["asyncarve==0.0.9"]
"requirements": ["asyncarve==0.1.1"]
}

View File

@@ -37,7 +37,10 @@
"message": "Username \"{username}\" already exists"
},
"username_not_normalized": {
"message": "Username \"{new_username}\" is not normalized"
"message": "Username \"{new_username}\" is not normalized. Please make sure the username is lowercase and does not contain any whitespace."
},
"user_not_found": {
"message": "User not found"
}
},
"issues": {

View File

@@ -30,7 +30,7 @@
"iot_class": "local_push",
"loggers": ["axis"],
"quality_scale": "platinum",
"requirements": ["axis==61"],
"requirements": ["axis==62"],
"ssdp": [
{
"manufacturer": "AXIS"

View File

@@ -366,7 +366,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
@property
def volume_level(self) -> float | None:
"""Volume level of the media player (0..1)."""
if self._volume.level and self._volume.level.level:
if self._volume.level and self._volume.level.level is not None:
return float(self._volume.level.level / 100)
return None

View File

@@ -53,11 +53,7 @@ async def websocket_create(
)
return
try:
await provider.async_add_auth(msg["username"], msg["password"])
except auth_ha.InvalidUser:
connection.send_error(msg["id"], "username_exists", "Username already exists")
return
await provider.async_add_auth(msg["username"], msg["password"])
credentials = await provider.async_get_or_create_credentials(
{"username": msg["username"]}
@@ -94,13 +90,7 @@ async def websocket_delete(
connection.send_result(msg["id"])
return
try:
await provider.async_remove_auth(msg["username"])
except auth_ha.InvalidUser:
connection.send_error(
msg["id"], "auth_not_found", "Given username was not found."
)
return
await provider.async_remove_auth(msg["username"])
connection.send_result(msg["id"])
@@ -187,14 +177,8 @@ async def websocket_admin_change_password(
)
return
try:
await provider.async_change_password(username, msg["password"])
connection.send_result(msg["id"])
except auth_ha.InvalidUser:
connection.send_error(
msg["id"], "credentials_not_found", "Credentials not found"
)
return
await provider.async_change_password(username, msg["password"])
connection.send_result(msg["id"])
@websocket_api.websocket_command(

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.6.26"]
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.7.3"]
}

View File

@@ -4,11 +4,11 @@ from datetime import timedelta
import logging
from pizzapi import Address, Customer, Order
from pizzapi.address import StoreException
import voluptuous as vol
from homeassistant.components import http
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@@ -118,7 +118,7 @@ class Dominos:
self.country = conf.get(ATTR_COUNTRY)
try:
self.closest_store = self.address.closest_store()
except StoreException:
except Exception: # noqa: BLE001
self.closest_store = None
def handle_order(self, call: ServiceCall) -> None:
@@ -139,7 +139,7 @@ class Dominos:
"""Update the shared closest store (if open)."""
try:
self.closest_store = self.address.closest_store()
except StoreException:
except Exception: # noqa: BLE001
self.closest_store = None
return False
return True
@@ -219,7 +219,7 @@ class DominosOrder(Entity):
"""Update the order state and refreshes the store."""
try:
self.dominos.update_closest_store()
except StoreException:
except Exception: # noqa: BLE001
self._orderable = False
return
@@ -227,13 +227,13 @@ class DominosOrder(Entity):
order = self.order()
order.pay_with()
self._orderable = True
except StoreException:
except Exception: # noqa: BLE001
self._orderable = False
def order(self):
"""Create the order object."""
if self.dominos.closest_store is None:
raise StoreException
raise HomeAssistantError("No store available")
order = Order(
self.dominos.closest_store,
@@ -252,7 +252,7 @@ class DominosOrder(Entity):
try:
order = self.order()
order.place()
except StoreException:
except Exception: # noqa: BLE001
self._orderable = False
_LOGGER.warning(
"Attempted to order Dominos - Order invalid or store closed"

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/dominos",
"iot_class": "cloud_polling",
"loggers": ["pizzapi"],
"requirements": ["pizzapi==0.0.3"]
"requirements": ["pizzapi==0.0.6"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
"iot_class": "cloud_polling",
"quality_scale": "platinum",
"requirements": ["easyenergy==2.1.1"]
"requirements": ["easyenergy==2.1.2"]
}

View File

@@ -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.10", "deebot-client==8.0.0"]
"requirements": ["py-sucks==0.9.10", "deebot-client==8.1.0"]
}

View File

@@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense-energy==0.12.2"]
"requirements": ["sense-energy==0.12.4"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/energyzero",
"iot_class": "cloud_polling",
"quality_scale": "platinum",
"requirements": ["energyzero==2.1.0"]
"requirements": ["energyzero==2.1.1"]
}

View File

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

View File

@@ -19,7 +19,6 @@ from homeassistant.const import (
PERCENTAGE,
UV_INDEX,
UnitOfLength,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfSpeed,
UnitOfTemperature,
@@ -114,14 +113,6 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = (
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: data.conditions.get("pop", {}).get("value"),
),
ECSensorEntityDescription(
key="precip_yesterday",
translation_key="precip_yesterday",
device_class=SensorDeviceClass.PRECIPITATION,
native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda data: data.conditions.get("precip_yesterday", {}).get("value"),
),
ECSensorEntityDescription(
key="pressure",
translation_key="pressure",

View File

@@ -52,9 +52,6 @@
"pop": {
"name": "Chance of precipitation"
},
"precip_yesterday": {
"name": "Precipitation yesterday"
},
"pressure": {
"name": "Barometric pressure"
},

View File

@@ -15,6 +15,7 @@
"iot_class": "local_push",
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==24.6.1",
"esphome-dashboard-api==1.2.3",

View File

@@ -97,6 +97,7 @@ class ESPHomeDashboardUpdateEntity(
_attr_title = "ESPHome"
_attr_name = "Firmware"
_attr_release_url = "https://esphome.io/changelog/"
_attr_entity_registry_enabled_default = False
def __init__(
self, entry_data: RuntimeEntryData, coordinator: ESPHomeDashboardCoordinator

View File

@@ -98,7 +98,7 @@ class FlumeNotificationDataUpdateCoordinator(DataUpdateCoordinator[None]):
# The related binary sensors (leak detected, high flow, low battery)
# will be active until the notification is deleted in the Flume app.
self.notifications = pyflume.FlumeNotificationList(
self.auth, read=None, sort_direction="DESC"
self.auth, read=None
).notification_list
_LOGGER.debug("Notifications %s", self.notifications)

View File

@@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/flume",
"iot_class": "cloud_polling",
"loggers": ["pyflume"],
"requirements": ["PyFlume==0.8.7"]
"requirements": ["PyFlume==0.6.5"]
}

View File

@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"loggers": ["pyfritzhome"],
"quality_scale": "gold",
"requirements": ["pyfritzhome==0.6.11"],
"requirements": ["pyfritzhome==0.6.12"],
"ssdp": [
{
"st": "urn:schemas-upnp-org:device:fritzbox:1"

View File

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

View File

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

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/calendar.google",
"iot_class": "cloud_polling",
"loggers": ["googleapiclient"],
"requirements": ["gcal-sync==6.0.4", "oauth2client==4.1.3", "ical==8.0.1"]
"requirements": ["gcal-sync==6.1.4", "oauth2client==4.1.3", "ical==8.1.1"]
}

View File

@@ -95,9 +95,12 @@ def _format_tool(
) -> dict[str, Any]:
"""Format tool specification."""
parameters = _format_schema(
convert(tool.parameters, custom_serializer=custom_serializer)
)
if tool.parameters.schema:
parameters = _format_schema(
convert(tool.parameters, custom_serializer=custom_serializer)
)
else:
parameters = None
return protos.Tool(
{

View File

@@ -6,5 +6,5 @@
"dependencies": ["network"],
"documentation": "https://www.home-assistant.io/integrations/govee_light_local",
"iot_class": "local_push",
"requirements": ["govee-local-api==1.5.0"]
"requirements": ["govee-local-api==1.5.1"]
}

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/gree",
"iot_class": "local_polling",
"loggers": ["greeclimate"],
"requirements": ["greeclimate==1.4.1"]
"requirements": ["greeclimate==1.4.6"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/here_travel_time",
"iot_class": "cloud_polling",
"loggers": ["here_routing", "here_transit", "homeassistant.helpers.location"],
"requirements": ["here-routing==0.2.0", "here-transit==1.2.0"]
"requirements": ["here-routing==1.0.1", "here-transit==1.2.1"]
}

View File

@@ -71,13 +71,12 @@ class HoneywellSwitch(SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on if heat mode is enabled."""
if self._device.system_mode == "heat":
try:
await self._device.set_system_mode("emheat")
except SomeComfortError as err:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="switch_failed_on"
) from err
try:
await self._device.set_system_mode("emheat")
except SomeComfortError as err:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="switch_failed_on"
) from err
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off if on."""

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
"iot_class": "cloud_push",
"loggers": ["aioautomower"],
"requirements": ["aioautomower==2024.6.1"]
"requirements": ["aioautomower==2024.6.4"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/incomfort",
"iot_class": "local_polling",
"loggers": ["incomfortclient"],
"requirements": ["incomfort-client==0.6.2"]
"requirements": ["incomfort-client==0.6.3"]
}

View File

@@ -28,5 +28,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/inkbird",
"iot_class": "local_push",
"requirements": ["inkbird-ble==0.5.6"]
"requirements": ["inkbird-ble==0.5.7"]
}

View File

@@ -27,8 +27,6 @@ from homeassistant.const import (
CONF_METHOD,
CONF_NAME,
CONF_UNIQUE_ID,
EVENT_STATE_CHANGED,
EVENT_STATE_REPORTED,
STATE_UNAVAILABLE,
UnitOfTime,
)
@@ -45,7 +43,11 @@ from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers.device import async_device_info_to_link_from_entity
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.event import (
async_call_later,
async_track_state_change_event,
async_track_state_report_event,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
@@ -440,21 +442,17 @@ class IntegrationSensor(RestoreSensor):
self._derive_and_set_attributes_from_state(state)
self.async_on_remove(
self.hass.bus.async_listen(
EVENT_STATE_CHANGED,
async_track_state_change_event(
self.hass,
self._sensor_source_id,
handle_state_change,
event_filter=callback(
lambda event_data: event_data["entity_id"] == self._sensor_source_id
),
)
)
self.async_on_remove(
self.hass.bus.async_listen(
EVENT_STATE_REPORTED,
async_track_state_report_event(
self.hass,
self._sensor_source_id,
handle_state_report,
event_filter=callback(
lambda event_data: event_data["entity_id"] == self._sensor_source_id
),
)
)

View File

@@ -28,7 +28,7 @@ class JewishCalendarEntity(Entity):
) -> None:
"""Initialize a Jewish Calendar entity."""
self.entity_description = description
self._attr_unique_id = f"{config_entry.entry_id}_{description.key}"
self._attr_unique_id = f"{config_entry.entry_id}-{description.key}"
self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, config_entry.entry_id)},

View File

@@ -48,6 +48,7 @@ class KnockiTrigger(EventEntity):
_attr_event_types = [EVENT_TRIGGERED]
_attr_has_entity_name = True
_attr_should_poll = False
_attr_translation_key = "knocki"
def __init__(self, trigger: Trigger, client: KnockiClient) -> None:

View File

@@ -112,7 +112,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: LaMarzoccoConfigEntry) -
entry.runtime_data = coordinator
gateway_version = coordinator.device.firmware[FirmwareType.GATEWAY].current_version
if version.parse(gateway_version) < version.parse("v3.5-rc5"):
if version.parse(gateway_version) < version.parse("v3.4-rc5"):
# incompatible gateway firmware, create an issue
ir.async_create_issue(
hass,

View File

@@ -9,6 +9,7 @@ from lmcloud.lm_machine import LaMarzoccoMachine
from lmcloud.models import LaMarzoccoMachineConfig
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -105,6 +106,7 @@ class LaMarzoccoAutoOnOffSwitchEntity(LaMarzoccoBaseEntity, SwitchEntity):
super().__init__(coordinator, f"auto_on_off_{identifier}")
self._identifier = identifier
self._attr_translation_placeholders = {"id": identifier}
self.entity_category = EntityCategory.CONFIG
async def _async_enable(self, state: bool) -> None:
"""Enable or disable the auto on/off schedule."""

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/local_calendar",
"iot_class": "local_polling",
"loggers": ["ical"],
"requirements": ["ical==8.0.1"]
"requirements": ["ical==8.1.1"]
}

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/local_todo",
"iot_class": "local_polling",
"requirements": ["ical==8.0.1"]
"requirements": ["ical==8.1.1"]
}

View File

@@ -60,6 +60,7 @@ SUPPORT_DRY_MODE_DEVICES: set[tuple[int, int]] = {
# In the list below specify tuples of (vendorid, productid) of devices that
# support dry mode.
(0x0001, 0x0108),
(0x0001, 0x010A),
(0x1209, 0x8007),
}
@@ -68,6 +69,7 @@ SUPPORT_FAN_MODE_DEVICES: set[tuple[int, int]] = {
# In the list below specify tuples of (vendorid, productid) of devices that
# support fan-only mode.
(0x0001, 0x0108),
(0x0001, 0x010A),
(0x1209, 0x8007),
}
@@ -225,6 +227,13 @@ class MatterClimate(MatterEntity, ClimateEntity):
self._attr_current_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.LocalTemperature
)
if self.get_matter_attribute_value(clusters.OnOff.Attributes.OnOff) is False:
# special case: the appliance has a dedicated Power switch on the OnOff cluster
# if the mains power is off - treat it as if the HVAC mode is off
self._attr_hvac_mode = HVACMode.OFF
self._attr_hvac_action = None
return
# update hvac_mode from SystemMode
system_mode_value = int(
self.get_matter_attribute_value(clusters.Thermostat.Attributes.SystemMode)
@@ -265,19 +274,13 @@ class MatterClimate(MatterEntity, ClimateEntity):
self._attr_hvac_action = HVACAction.FAN
case _:
self._attr_hvac_action = HVACAction.OFF
# update target_temperature
if self._attr_hvac_mode == HVACMode.HEAT_COOL:
self._attr_target_temperature = None
elif self._attr_hvac_mode == HVACMode.COOL:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
)
else:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
)
# update target temperature high/low
if self._attr_hvac_mode == HVACMode.HEAT_COOL:
supports_range = (
self._attr_supported_features
& ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
if supports_range and self._attr_hvac_mode == HVACMode.HEAT_COOL:
self._attr_target_temperature = None
self._attr_target_temperature_high = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
)
@@ -287,6 +290,16 @@ class MatterClimate(MatterEntity, ClimateEntity):
else:
self._attr_target_temperature_high = None
self._attr_target_temperature_low = None
# update target_temperature
if self._attr_hvac_mode == HVACMode.COOL:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
)
else:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
)
# update min_temp
if self._attr_hvac_mode == HVACMode.COOL:
attribute = clusters.Thermostat.Attributes.AbsMinCoolSetpointLimit

View File

@@ -170,6 +170,14 @@ class MatterFan(MatterEntity, FanEntity):
"""Update from device."""
if not hasattr(self, "_attr_preset_modes"):
self._calculate_features()
if self.get_matter_attribute_value(clusters.OnOff.Attributes.OnOff) is False:
# special case: the appliance has a dedicated Power switch on the OnOff cluster
# if the mains power is off - treat it as if the fan mode is off
self._attr_preset_mode = None
self._attr_percentage = 0
return
if self._attr_supported_features & FanEntityFeature.DIRECTION:
direction_value = self.get_matter_attribute_value(
clusters.FanControl.Attributes.AirflowDirection
@@ -200,7 +208,13 @@ class MatterFan(MatterEntity, FanEntity):
wind_setting = self.get_matter_attribute_value(
clusters.FanControl.Attributes.WindSetting
)
if (
fan_mode = self.get_matter_attribute_value(
clusters.FanControl.Attributes.FanMode
)
if fan_mode == clusters.FanControl.Enums.FanModeEnum.kOff:
self._attr_preset_mode = None
self._attr_percentage = 0
elif (
self._attr_preset_modes
and PRESET_NATURAL_WIND in self._attr_preset_modes
and wind_setting & WindBitmap.kNaturalWind

View File

@@ -6,6 +6,6 @@
"dependencies": ["websocket_api"],
"documentation": "https://www.home-assistant.io/integrations/matter",
"iot_class": "local_push",
"requirements": ["python-matter-server==6.2.0b1"],
"requirements": ["python-matter-server==6.2.2"],
"zeroconf": ["_matter._tcp.local.", "_matterc._udp.local."]
}

View File

@@ -6,7 +6,11 @@ from dataclasses import dataclass
from chip.clusters import Objects as clusters
from chip.clusters.Types import Nullable, NullValue
from matter_server.common.custom_clusters import EveCluster
from matter_server.common.custom_clusters import (
EveCluster,
NeoCluster,
ThirdRealityMeteringCluster,
)
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -171,9 +175,6 @@ DISCOVERY_SCHEMAS = [
),
entity_class=MatterSensor,
required_attributes=(EveCluster.Attributes.Watt,),
# Add OnOff Attribute as optional attribute to poll
# the primary value when the relay is toggled
optional_attributes=(clusters.OnOff.Attributes.OnOff,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
@@ -213,9 +214,6 @@ DISCOVERY_SCHEMAS = [
),
entity_class=MatterSensor,
required_attributes=(EveCluster.Attributes.Current,),
# Add OnOff Attribute as optional attribute to poll
# the primary value when the relay is toggled
optional_attributes=(clusters.OnOff.Attributes.OnOff,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
@@ -364,4 +362,90 @@ DISCOVERY_SCHEMAS = [
clusters.ActivatedCarbonFilterMonitoring.Attributes.Condition,
),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="ThirdRealityEnergySensorWatt",
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_display_precision=2,
state_class=SensorStateClass.MEASUREMENT,
measurement_to_ha=lambda x: x / 1000,
),
entity_class=MatterSensor,
required_attributes=(
ThirdRealityMeteringCluster.Attributes.InstantaneousDemand,
),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="ThirdRealityEnergySensorWattAccumulated",
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=3,
state_class=SensorStateClass.TOTAL_INCREASING,
measurement_to_ha=lambda x: x / 1000,
),
entity_class=MatterSensor,
required_attributes=(
ThirdRealityMeteringCluster.Attributes.CurrentSummationDelivered,
),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorWatt",
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_display_precision=2,
state_class=SensorStateClass.MEASUREMENT,
measurement_to_ha=lambda x: x / 10,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.Watt,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorWattAccumulated",
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_display_precision=1,
state_class=SensorStateClass.TOTAL_INCREASING,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.WattAccumulated,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorVoltage",
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
measurement_to_ha=lambda x: x / 10,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.Voltage,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorWattCurrent",
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.Current,),
),
]

View File

@@ -30,8 +30,8 @@ async def async_setup_entry(
def _get_event_from_mealplan(mealplan: Mealplan) -> CalendarEvent:
"""Create a CalendarEvent from a Mealplan."""
description: str | None = None
name = "No recipe"
description: str | None = mealplan.description
name = mealplan.title or "No recipe"
if mealplan.recipe:
name = mealplan.recipe.name
description = mealplan.recipe.description
@@ -50,12 +50,9 @@ class MealieMealplanCalendarEntity(MealieEntity, CalendarEntity):
self, coordinator: MealieCoordinator, entry_type: MealplanEntryType
) -> None:
"""Create the Calendar entity."""
super().__init__(coordinator)
super().__init__(coordinator, entry_type.name.lower())
self._entry_type = entry_type
self._attr_translation_key = entry_type.name.lower()
self._attr_unique_id = (
f"{self.coordinator.config_entry.entry_id}_{entry_type.name.lower()}"
)
@property
def event(self) -> CalendarEvent | None:

View File

@@ -28,14 +28,13 @@ class MealieConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a flow initialized by the user."""
errors: dict[str, str] = {}
if user_input:
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
client = MealieClient(
user_input[CONF_HOST],
token=user_input[CONF_API_TOKEN],
session=async_get_clientsession(self.hass),
)
try:
await client.get_mealplan_today()
info = await client.get_user_info()
except MealieConnectionError:
errors["base"] = "cannot_connect"
except MealieAuthenticationError:
@@ -44,6 +43,8 @@ class MealieConfigFlow(ConfigFlow, domain=DOMAIN):
LOGGER.exception("Unexpected error")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(info.user_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title="Mealie",
data=user_input,

View File

@@ -12,10 +12,13 @@ class MealieEntity(CoordinatorEntity[MealieCoordinator]):
_attr_has_entity_name = True
def __init__(self, coordinator: MealieCoordinator) -> None:
def __init__(self, coordinator: MealieCoordinator, key: str) -> None:
"""Initialize Mealie entity."""
super().__init__(coordinator)
unique_id = coordinator.config_entry.unique_id
assert unique_id is not None
self._attr_unique_id = f"{unique_id}_{key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
identifiers={(DOMAIN, unique_id)},
entry_type=DeviceEntryType.SERVICE,
)

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/mealie",
"integration_type": "service",
"iot_class": "local_polling",
"requirements": ["aiomealie==0.4.0"]
"requirements": ["aiomealie==0.5.0"]
}

View File

@@ -8,6 +8,6 @@
"iot_class": "calculated",
"loggers": ["yt_dlp"],
"quality_scale": "internal",
"requirements": ["yt-dlp==2024.05.27"],
"requirements": ["yt-dlp==2024.07.01"],
"single_config_entry": true
}

View File

@@ -421,11 +421,6 @@ class MpdDevice(MediaPlayerEntity):
"""Name of the current input source."""
return self._current_playlist
@property
def source_list(self):
"""Return the list of available input sources."""
return self._playlists
async def async_select_source(self, source: str) -> None:
"""Choose a different available playlist and play it."""
await self.async_play_media(MediaType.PLAYLIST, source)

View File

@@ -1141,8 +1141,8 @@ class MQTT:
# see https://github.com/eclipse/paho.mqtt.python/issues/687
# properties and reason codes are not used in Home Assistant
future = self._async_get_mid_future(mid)
if future.done() and future.exception():
# Timed out
if future.done() and (future.cancelled() or future.exception()):
# Timed out or cancelled
return
future.set_result(None)

View File

@@ -36,7 +36,7 @@ from .const import (
)
from .models import DATA_MQTT, DATA_MQTT_AVAILABLE, ReceiveMessage
AVAILABILITY_TIMEOUT = 30.0
AVAILABILITY_TIMEOUT = 50.0
TEMP_DIR_NAME = f"home-assistant-{DOMAIN}"

View File

@@ -18,6 +18,7 @@ from nextdns import (
NextDns,
Settings,
)
from tenacity import RetryError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, Platform
@@ -84,9 +85,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: NextDnsConfigEntry) -> b
websession = async_get_clientsession(hass)
try:
async with asyncio.timeout(10):
nextdns = await NextDns.create(websession, api_key)
except (ApiError, ClientConnectorError, TimeoutError) as err:
nextdns = await NextDns.create(websession, api_key)
except (ApiError, ClientConnectorError, RetryError, TimeoutError) as err:
raise ConfigEntryNotReady from err
tasks = []

View File

@@ -2,11 +2,11 @@
from __future__ import annotations
import asyncio
from typing import Any
from aiohttp.client_exceptions import ClientConnectorError
from nextdns import ApiError, InvalidApiKeyError, NextDns
from tenacity import RetryError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@@ -37,13 +37,12 @@ class NextDnsFlowHandler(ConfigFlow, domain=DOMAIN):
if user_input is not None:
self.api_key = user_input[CONF_API_KEY]
try:
async with asyncio.timeout(10):
self.nextdns = await NextDns.create(
websession, user_input[CONF_API_KEY]
)
self.nextdns = await NextDns.create(
websession, user_input[CONF_API_KEY]
)
except InvalidApiKeyError:
errors["base"] = "invalid_api_key"
except (ApiError, ClientConnectorError, TimeoutError):
except (ApiError, ClientConnectorError, RetryError, TimeoutError):
errors["base"] = "cannot_connect"
except Exception: # noqa: BLE001
errors["base"] = "unknown"

View File

@@ -1,6 +1,5 @@
"""NextDns coordinator."""
import asyncio
from datetime import timedelta
import logging
from typing import TypeVar
@@ -19,6 +18,7 @@ from nextdns import (
Settings,
)
from nextdns.model import NextDnsData
from tenacity import RetryError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
@@ -58,9 +58,13 @@ class NextDnsUpdateCoordinator(DataUpdateCoordinator[CoordinatorDataT]):
async def _async_update_data(self) -> CoordinatorDataT:
"""Update data via internal method."""
try:
async with asyncio.timeout(10):
return await self._async_update_data_internal()
except (ApiError, ClientConnectorError, InvalidApiKeyError) as err:
return await self._async_update_data_internal()
except (
ApiError,
ClientConnectorError,
InvalidApiKeyError,
RetryError,
) as err:
raise UpdateFailed(err) from err
async def _async_update_data_internal(self) -> CoordinatorDataT:

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["nextdns"],
"quality_scale": "platinum",
"requirements": ["nextdns==3.0.0"]
"requirements": ["nextdns==3.1.0"]
}

View File

@@ -261,7 +261,7 @@ class NMBSSensor(SensorEntity):
attrs["via_arrival_platform"] = via["arrival"]["platform"]
attrs["via_transfer_platform"] = via["departure"]["platform"]
attrs["via_transfer_time"] = get_delay_in_minutes(
via["timeBetween"]
via["timebetween"]
) + get_delay_in_minutes(via["departure"]["delay"])
if delay > 0:

View File

@@ -78,8 +78,8 @@ HOURLY = "hourly"
OBSERVATION_VALID_TIME = timedelta(minutes=60)
FORECAST_VALID_TIME = timedelta(minutes=45)
# A lot of stations update once hourly plus some wiggle room
UPDATE_TIME_PERIOD = timedelta(minutes=70)
# Ask for observations for last four hours
UPDATE_TIME_PERIOD = timedelta(minutes=240)
DEBOUNCE_TIME = 10 * 60 # in seconds
DEFAULT_SCAN_INTERVAL = timedelta(minutes=10)

View File

@@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/openai_conversation",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["openai==1.3.8"]
"requirements": ["openai==1.35.7"]
}

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/opensky",
"iot_class": "cloud_polling",
"requirements": ["python-opensky==1.0.0"]
"requirements": ["python-opensky==1.0.1"]
}

View File

@@ -109,17 +109,20 @@ BINARY_SENSOR_DESCRIPTIONS: list[OverkizBinarySensorDescription] = [
key=OverkizState.CORE_HEATING_STATUS,
name="Heating status",
device_class=BinarySensorDeviceClass.HEAT,
value_fn=lambda state: state == OverkizCommandParam.ON,
value_fn=lambda state: cast(str, state).lower()
in (OverkizCommandParam.ON, OverkizCommandParam.HEATING),
),
OverkizBinarySensorDescription(
key=OverkizState.MODBUSLINK_DHW_ABSENCE_MODE,
name="Absence mode",
value_fn=lambda state: state == OverkizCommandParam.ON,
value_fn=lambda state: state
in (OverkizCommandParam.ON, OverkizCommandParam.PROG),
),
OverkizBinarySensorDescription(
key=OverkizState.MODBUSLINK_DHW_BOOST_MODE,
name="Boost mode",
value_fn=lambda state: state == OverkizCommandParam.ON,
value_fn=lambda state: state
in (OverkizCommandParam.ON, OverkizCommandParam.PROG),
),
]

View File

@@ -182,6 +182,13 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
),
OverkizSensorDescription(
key=OverkizState.MODBUSLINK_POWER_HEAT_ELECTRICAL,
name="Electric power consumption",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
),
OverkizSensorDescription(
key=OverkizState.CORE_CONSUMPTION_TARIFF1,
name="Consumption tariff 1",
@@ -413,6 +420,13 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
OverkizSensorDescription(
key=OverkizState.CORE_REMAINING_HOT_WATER,
name="Warm water remaining",
device_class=SensorDeviceClass.VOLUME,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfVolume.LITERS,
),
# Cover
OverkizSensorDescription(
key=OverkizState.CORE_TARGET_CLOSURE,

View File

@@ -9,7 +9,11 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import HomeAssistantOverkizData
from .const import DOMAIN
from .water_heater_entities import WIDGET_TO_WATER_HEATER_ENTITY
from .entity import OverkizEntity
from .water_heater_entities import (
CONTROLLABLE_NAME_TO_WATER_HEATER_ENTITY,
WIDGET_TO_WATER_HEATER_ENTITY,
)
async def async_setup_entry(
@@ -19,11 +23,20 @@ async def async_setup_entry(
) -> None:
"""Set up the Overkiz DHW from a config entry."""
data: HomeAssistantOverkizData = hass.data[DOMAIN][entry.entry_id]
entities: list[OverkizEntity] = []
async_add_entities(
WIDGET_TO_WATER_HEATER_ENTITY[device.widget](
device.device_url, data.coordinator
)
for device in data.platforms[Platform.WATER_HEATER]
if device.widget in WIDGET_TO_WATER_HEATER_ENTITY
)
for device in data.platforms[Platform.WATER_HEATER]:
if device.controllable_name in CONTROLLABLE_NAME_TO_WATER_HEATER_ENTITY:
entities.append(
CONTROLLABLE_NAME_TO_WATER_HEATER_ENTITY[device.controllable_name](
device.device_url, data.coordinator
)
)
elif device.widget in WIDGET_TO_WATER_HEATER_ENTITY:
entities.append(
WIDGET_TO_WATER_HEATER_ENTITY[device.widget](
device.device_url, data.coordinator
)
)
async_add_entities(entities)

View File

@@ -2,6 +2,9 @@
from pyoverkiz.enums.ui import UIWidget
from .atlantic_domestic_hot_water_production_mlb_component import (
AtlanticDomesticHotWaterProductionMBLComponent,
)
from .atlantic_pass_apc_dhw import AtlanticPassAPCDHW
from .domestic_hot_water_production import DomesticHotWaterProduction
from .hitachi_dhw import HitachiDHW
@@ -11,3 +14,7 @@ WIDGET_TO_WATER_HEATER_ENTITY = {
UIWidget.DOMESTIC_HOT_WATER_PRODUCTION: DomesticHotWaterProduction,
UIWidget.HITACHI_DHW: HitachiDHW,
}
CONTROLLABLE_NAME_TO_WATER_HEATER_ENTITY = {
"modbuslink:AtlanticDomesticHotWaterProductionMBLComponent": AtlanticDomesticHotWaterProductionMBLComponent,
}

View File

@@ -0,0 +1,182 @@
"""Support for AtlanticDomesticHotWaterProductionMBLComponent."""
from typing import Any, cast
from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState
from homeassistant.components.water_heater import (
STATE_ECO,
STATE_OFF,
STATE_PERFORMANCE,
WaterHeaterEntity,
WaterHeaterEntityFeature,
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from .. import OverkizDataUpdateCoordinator
from ..entity import OverkizEntity
class AtlanticDomesticHotWaterProductionMBLComponent(OverkizEntity, WaterHeaterEntity):
"""Representation of AtlanticDomesticHotWaterProductionMBLComponent (modbuslink)."""
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_supported_features = (
WaterHeaterEntityFeature.TARGET_TEMPERATURE
| WaterHeaterEntityFeature.OPERATION_MODE
| WaterHeaterEntityFeature.AWAY_MODE
| WaterHeaterEntityFeature.ON_OFF
)
_attr_operation_list = [
OverkizCommandParam.PERFORMANCE,
OverkizCommandParam.ECO,
OverkizCommandParam.MANUAL,
]
def __init__(
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
) -> None:
"""Init method."""
super().__init__(device_url, coordinator)
self._attr_max_temp = cast(
float,
self.executor.select_state(
OverkizState.CORE_MAXIMAL_TEMPERATURE_MANUAL_MODE
),
)
self._attr_min_temp = cast(
float,
self.executor.select_state(
OverkizState.CORE_MINIMAL_TEMPERATURE_MANUAL_MODE
),
)
@property
def current_temperature(self) -> float:
"""Return the current temperature."""
return cast(
float,
self.executor.select_state(
OverkizState.MODBUSLINK_MIDDLE_WATER_TEMPERATURE
),
)
@property
def target_temperature(self) -> float:
"""Return the temperature corresponding to the PRESET."""
return cast(
float,
self.executor.select_state(OverkizState.CORE_WATER_TARGET_TEMPERATURE),
)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new temperature."""
temperature = kwargs[ATTR_TEMPERATURE]
await self.executor.async_execute_command(
OverkizCommand.SET_TARGET_DHW_TEMPERATURE, temperature
)
@property
def is_boost_mode_on(self) -> bool:
"""Return true if boost mode is on."""
return self.executor.select_state(OverkizState.MODBUSLINK_DHW_BOOST_MODE) in (
OverkizCommandParam.ON,
OverkizCommandParam.PROG,
)
@property
def is_eco_mode_on(self) -> bool:
"""Return true if eco mode is on."""
return self.executor.select_state(OverkizState.MODBUSLINK_DHW_MODE) in (
OverkizCommandParam.MANUAL_ECO_ACTIVE,
OverkizCommandParam.AUTO_MODE,
)
@property
def is_away_mode_on(self) -> bool:
"""Return true if away mode is on."""
return (
self.executor.select_state(OverkizState.MODBUSLINK_DHW_ABSENCE_MODE)
== OverkizCommandParam.ON
)
@property
def current_operation(self) -> str:
"""Return current operation."""
if self.is_away_mode_on:
return STATE_OFF
if self.is_boost_mode_on:
return STATE_PERFORMANCE
if self.is_eco_mode_on:
return STATE_ECO
if (
cast(str, self.executor.select_state(OverkizState.MODBUSLINK_DHW_MODE))
== OverkizCommandParam.MANUAL_ECO_INACTIVE
):
return OverkizCommandParam.MANUAL
return STATE_OFF
async def async_set_operation_mode(self, operation_mode: str) -> None:
"""Set new operation mode."""
if operation_mode in (STATE_PERFORMANCE, OverkizCommandParam.BOOST):
if self.is_away_mode_on:
await self.async_turn_away_mode_off()
await self.async_turn_boost_mode_on()
elif operation_mode in (
OverkizCommandParam.ECO,
OverkizCommandParam.MANUAL_ECO_ACTIVE,
):
if self.is_away_mode_on:
await self.async_turn_away_mode_off()
if self.is_boost_mode_on:
await self.async_turn_boost_mode_off()
await self.executor.async_execute_command(
OverkizCommand.SET_DHW_MODE, OverkizCommandParam.AUTO_MODE
)
elif operation_mode in (
OverkizCommandParam.MANUAL,
OverkizCommandParam.MANUAL_ECO_INACTIVE,
):
if self.is_away_mode_on:
await self.async_turn_away_mode_off()
if self.is_boost_mode_on:
await self.async_turn_boost_mode_off()
await self.executor.async_execute_command(
OverkizCommand.SET_DHW_MODE, OverkizCommandParam.MANUAL_ECO_INACTIVE
)
else:
if self.is_away_mode_on:
await self.async_turn_away_mode_off()
if self.is_boost_mode_on:
await self.async_turn_boost_mode_off()
await self.executor.async_execute_command(
OverkizCommand.SET_DHW_MODE, operation_mode
)
async def async_turn_away_mode_on(self) -> None:
"""Turn away mode on."""
await self.executor.async_execute_command(
OverkizCommand.SET_ABSENCE_MODE, OverkizCommandParam.ON
)
async def async_turn_away_mode_off(self) -> None:
"""Turn away mode off."""
await self.executor.async_execute_command(
OverkizCommand.SET_ABSENCE_MODE, OverkizCommandParam.OFF
)
async def async_turn_boost_mode_on(self) -> None:
"""Turn boost mode on."""
await self.executor.async_execute_command(
OverkizCommand.SET_BOOST_MODE, OverkizCommandParam.ON
)
async def async_turn_boost_mode_off(self) -> None:
"""Turn boost mode off."""
await self.executor.async_execute_command(
OverkizCommand.SET_BOOST_MODE, OverkizCommandParam.OFF
)

View File

@@ -7,5 +7,5 @@
"iot_class": "local_polling",
"loggers": ["p1monitor"],
"quality_scale": "platinum",
"requirements": ["p1monitor==3.0.0"]
"requirements": ["p1monitor==3.0.1"]
}

View File

@@ -196,10 +196,10 @@ class Remote:
self.muted = self._control.get_mute()
self.volume = self._control.get_volume() / 100
async def async_send_key(self, key):
async def async_send_key(self, key: Keys | str) -> None:
"""Send a key to the TV and handle exceptions."""
try:
key = getattr(Keys, key)
key = getattr(Keys, key.upper())
except (AttributeError, TypeError):
key = getattr(key, "value", key)
@@ -211,13 +211,13 @@ class Remote:
await self._on_action.async_run(context=context)
await self.async_update()
elif self.state != STATE_ON:
await self.async_send_key(Keys.power)
await self.async_send_key(Keys.POWER)
await self.async_update()
async def async_turn_off(self):
"""Turn off the TV."""
if self.state != STATE_OFF:
await self.async_send_key(Keys.power)
await self.async_send_key(Keys.POWER)
self.state = STATE_OFF
await self.async_update()

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/panasonic_viera",
"iot_class": "local_polling",
"loggers": ["panasonic_viera"],
"requirements": ["panasonic-viera==0.3.6"]
"requirements": ["panasonic-viera==0.4.2"]
}

View File

@@ -126,11 +126,11 @@ class PanasonicVieraTVEntity(MediaPlayerEntity):
async def async_volume_up(self) -> None:
"""Volume up the media player."""
await self._remote.async_send_key(Keys.volume_up)
await self._remote.async_send_key(Keys.VOLUME_UP)
async def async_volume_down(self) -> None:
"""Volume down media player."""
await self._remote.async_send_key(Keys.volume_down)
await self._remote.async_send_key(Keys.VOLUME_DOWN)
async def async_mute_volume(self, mute: bool) -> None:
"""Send mute command."""
@@ -143,33 +143,33 @@ class PanasonicVieraTVEntity(MediaPlayerEntity):
async def async_media_play_pause(self) -> None:
"""Simulate play pause media player."""
if self._remote.playing:
await self._remote.async_send_key(Keys.pause)
await self._remote.async_send_key(Keys.PAUSE)
self._remote.playing = False
else:
await self._remote.async_send_key(Keys.play)
await self._remote.async_send_key(Keys.PLAY)
self._remote.playing = True
async def async_media_play(self) -> None:
"""Send play command."""
await self._remote.async_send_key(Keys.play)
await self._remote.async_send_key(Keys.PLAY)
self._remote.playing = True
async def async_media_pause(self) -> None:
"""Send pause command."""
await self._remote.async_send_key(Keys.pause)
await self._remote.async_send_key(Keys.PAUSE)
self._remote.playing = False
async def async_media_stop(self) -> None:
"""Stop playback."""
await self._remote.async_send_key(Keys.stop)
await self._remote.async_send_key(Keys.STOP)
async def async_media_next_track(self) -> None:
"""Send the fast forward command."""
await self._remote.async_send_key(Keys.fast_forward)
await self._remote.async_send_key(Keys.FAST_FORWARD)
async def async_media_previous_track(self) -> None:
"""Send the rewind command."""
await self._remote.async_send_key(Keys.rewind)
await self._remote.async_send_key(Keys.REWIND)
async def async_play_media(
self, media_type: MediaType | str, media_id: str, **kwargs: Any

View File

@@ -26,7 +26,7 @@ from homeassistant.components.cover import (
ATTR_CURRENT_POSITION,
ATTR_CURRENT_TILT_POSITION,
)
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http import KEY_HASS, HomeAssistantView
from homeassistant.components.humidifier import ATTR_AVAILABLE_MODES, ATTR_HUMIDITY
from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.components.sensor import SensorDeviceClass
@@ -729,7 +729,11 @@ class PrometheusView(HomeAssistantView):
"""Handle request for Prometheus metrics."""
_LOGGER.debug("Received Prometheus metrics request")
hass = request.app[KEY_HASS]
body = await hass.async_add_executor_job(
prometheus_client.generate_latest, prometheus_client.REGISTRY
)
return web.Response(
body=prometheus_client.generate_latest(prometheus_client.REGISTRY),
body=body,
content_type=CONTENT_TYPE_TEXT_PLAIN,
)

View File

@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/pure_energie",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["gridnet==5.0.0"],
"requirements": ["gridnet==5.0.1"],
"zeroconf": [
{
"type": "_http._tcp.local.",

View File

@@ -142,6 +142,13 @@ _DEFAULT_TABLE_ARGS = {
"mariadb_engine": MYSQL_ENGINE,
}
_MATCH_ALL_KEEP = {
ATTR_DEVICE_CLASS,
ATTR_STATE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
ATTR_FRIENDLY_NAME,
}
class UnusedDateTime(DateTime):
"""An unused column type that behaves like a datetime."""
@@ -597,19 +604,8 @@ class StateAttributes(Base):
if MATCH_ALL in unrecorded_attributes:
# Don't exclude device class, state class, unit of measurement
# or friendly name when using the MATCH_ALL exclude constant
_exclude_attributes = {
k: v
for k, v in state.attributes.items()
if k
not in (
ATTR_DEVICE_CLASS,
ATTR_STATE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
ATTR_FRIENDLY_NAME,
)
}
exclude_attrs.update(_exclude_attributes)
exclude_attrs.update(state.attributes)
exclude_attrs -= _MATCH_ALL_KEEP
else:
exclude_attrs = ALL_DOMAIN_EXCLUDE_ATTRS
encoder = json_bytes_strip_null if dialect == PSQL_DIALECT else json_bytes

View File

@@ -81,7 +81,7 @@ BINARY_SENSOR_TYPES: tuple[RenaultBinarySensorEntityDescription, ...] = tuple(
key="hvac_status",
coordinator="hvac_status",
on_key="hvacStatus",
on_value=2,
on_value="on",
translation_key="hvac_status",
),
RenaultBinarySensorEntityDescription(

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["renault_api"],
"quality_scale": "platinum",
"requirements": ["renault-api==0.2.3"]
"requirements": ["renault-api==0.2.4"]
}

View File

@@ -73,9 +73,9 @@
"charge_mode": {
"name": "Charge mode",
"state": {
"always": "Instant",
"always_charging": "[%key:component::renault::entity::select::charge_mode::state::always%]",
"schedule_mode": "Planner",
"always": "Always",
"always_charging": "Always charging",
"schedule_mode": "Schedule mode",
"scheduled": "Scheduled"
}
}

View File

@@ -133,7 +133,11 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
)
# If camera WAN blocked, firmware check fails and takes long, do not prevent setup
config_entry.async_create_task(hass, firmware_coordinator.async_refresh())
config_entry.async_create_background_task(
hass,
firmware_coordinator.async_refresh(),
f"Reolink firmware check {config_entry.entry_id}",
)
# Fetch initial data so we have data when entities subscribe
try:
await device_coordinator.async_config_entry_first_refresh()
@@ -147,9 +151,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
firmware_coordinator=firmware_coordinator,
)
# first migrate and then cleanup, otherwise entities lost
migrate_entity_ids(hass, config_entry.entry_id, host)
cleanup_disconnected_cams(hass, config_entry.entry_id, host)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
@@ -179,6 +181,50 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return unload_ok
async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: ConfigEntry, device: dr.DeviceEntry
) -> bool:
"""Remove a device from a config entry."""
host: ReolinkHost = hass.data[DOMAIN][config_entry.entry_id].host
(device_uid, ch) = get_device_uid_and_ch(device, host)
if not host.api.is_nvr or ch is None:
_LOGGER.warning(
"Cannot remove Reolink device %s, because it is not a camera connected "
"to a NVR/Hub, please remove the integration entry instead",
device.name,
)
return False # Do not remove the host/NVR itself
if ch not in host.api.channels:
_LOGGER.debug(
"Removing Reolink device %s, "
"since no camera is connected to NVR channel %s anymore",
device.name,
ch,
)
return True
await host.api.get_state(cmd="GetChannelstatus") # update the camera_online status
if not host.api.camera_online(ch):
_LOGGER.debug(
"Removing Reolink device %s, "
"since the camera connected to channel %s is offline",
device.name,
ch,
)
return True
_LOGGER.warning(
"Cannot remove Reolink device %s on channel %s, because it is still connected "
"to the NVR/Hub, please first remove the camera from the NVR/Hub "
"in the reolink app",
device.name,
ch,
)
return False
def get_device_uid_and_ch(
device: dr.DeviceEntry, host: ReolinkHost
) -> tuple[list[str], int | None]:
@@ -197,47 +243,6 @@ def get_device_uid_and_ch(
return (device_uid, ch)
def cleanup_disconnected_cams(
hass: HomeAssistant, config_entry_id: str, host: ReolinkHost
) -> None:
"""Clean-up disconnected camera channels."""
if not host.api.is_nvr:
return
device_reg = dr.async_get(hass)
devices = dr.async_entries_for_config_entry(device_reg, config_entry_id)
for device in devices:
(device_uid, ch) = get_device_uid_and_ch(device, host)
if ch is None:
continue # Do not consider the NVR itself
ch_model = host.api.camera_model(ch)
remove = False
if ch not in host.api.channels:
remove = True
_LOGGER.debug(
"Removing Reolink device %s, "
"since no camera is connected to NVR channel %s anymore",
device.name,
ch,
)
if ch_model not in [device.model, "Unknown"]:
remove = True
_LOGGER.debug(
"Removing Reolink device %s, "
"since the camera model connected to channel %s changed from %s to %s",
device.name,
ch,
device.model,
ch_model,
)
if not remove:
continue
# clean device registry and associated entities
device_reg.async_remove_device(device.id)
def migrate_entity_ids(
hass: HomeAssistant, config_entry_id: str, host: ReolinkHost
) -> None:

View File

@@ -18,5 +18,5 @@
"documentation": "https://www.home-assistant.io/integrations/reolink",
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"requirements": ["reolink-aio==0.9.3"]
"requirements": ["reolink-aio==0.9.4"]
}

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