Compare commits

..

126 Commits

Author SHA1 Message Date
Paulus Schoutsen a7e42798da 2023.2.5 (#88190) 2023-02-15 15:30:55 -05:00
Marcel van der Veldt 684845fd8c Bump python-matter-server to 2.1.0 (#88192)
* Bump python-matter-server to 2.1.0

* Fix tests

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-02-15 13:24:01 -05:00
Paulus Schoutsen 3e3936e783 backport try_parse_enum 2023-02-15 12:17:03 -05:00
Paulus Schoutsen 0d140426cc Bumped version to 2023.2.5 2023-02-15 11:14:27 -05:00
starkillerOG bbbc864a8c Bump reolink-aio to 0.4.2 (#88175) 2023-02-15 11:13:40 -05:00
Michał Huryn 1ca6c26312 Blebox fix thermobox reporting wrong state (#88169)
fix: fixed climate hvac_mode for when device is off
2023-02-15 11:12:45 -05:00
Mark Broadbent 2d293c19bf Update orjson to resolve segmentation fault during JSON serialisation (#88119)
Home Assistant uses orjson 3.8.5 that contains an issue[1] on musl libc
platforms that causes a segmentation fault.  This particularly affect
Home Assistant container installations reported in #87283 and #87522.

This updates the version to 3.8.6 that resolves the segmentation
fault during json serialisation.

[1] https://github.com/ijl/orjson/issues/335
2023-02-15 11:12:18 -05:00
Thomas Dietrich 634aff0006 Statistics component fix device_class for incremental source sensors (#88096)
* Return None device_class for incremental source sensors

* Ignore linting error

* Fix ignore linting error

* Fix ignore linting error

* Fix ignore linting error

* Catch potential parsing error with enum
2023-02-15 11:11:25 -05:00
Gertjan 55fed18e3e Fixed float number validation in sensor component (#88074) 2023-02-15 11:11:24 -05:00
Ryan Fleming be5777ba59 Bump pyoctoprintapi to 0.1.11 (#88052)
Bump pyoctoprint to get camera url fixes
2023-02-15 11:11:12 -05:00
Paulus Schoutsen 8024a17025 Handle device reg fields not being valid data in openai conversion (#88047)
Handle device reg fields not being valid data
2023-02-15 11:10:15 -05:00
mkmer 97dab32a4c Bump AIOAladdinConnect 0.1.56 (#88041) 2023-02-15 11:09:15 -05:00
mkmer e3273a75da Honeywell disable detergent level by default (#88040)
* Disable fill by default

* Fix tests

* use TANK_FILL.get

* Remove None from attribute get
add reload to sensor test

* Typing
fix iteration error
2023-02-15 11:08:37 -05:00
Aaron Bach 7337cc8e89 Bump pyopenuv to 2023.02.0 (#88039) 2023-02-15 11:08:25 -05:00
Daniel Hjelseth Høyer 7a3fdc08a4 Update tibber lib 0.26.13 (#88018) 2023-02-15 11:07:15 -05:00
Paulus Schoutsen 2fa35e174a 2023.2.4 (#88007) 2023-02-13 10:06:16 -05:00
Paulus Schoutsen 3b4efba606 Bumped version to 2023.2.4 2023-02-13 09:24:50 -05:00
J. Nick Koston fb456a66c6 Bump aioesphomeapi to 13.3.1 (#87969)
* Bump aioesphomeapi to 13.3.0

We probably need to include https://github.com/esphome/aioesphomeapi/pull/382
as well in another bump

* bump
2023-02-13 09:23:32 -05:00
Florent Thoumie cd5042e2c2 Fix iaqualink exception handling after switch to httpx (#87898)
Fix exception handling after switch to httpx
2023-02-13 09:22:57 -05:00
J. Nick Koston 65c8895bf7 Retrigger Bluetooth discovery when calling async_rediscover_address (#87884)
* Retrigger Bluetooth discovery when calling async_rediscover_address

* Retrigger Bluetooth discovery when calling async_rediscover_address

* tweak
2023-02-13 09:22:56 -05:00
G Johansson e8081c6159 Fix unbound variable in Group sensor (#87878)
* group bound last

* Add test
2023-02-13 09:22:55 -05:00
Diogo Gomes da2c1c1142 Bump pyipma to 3.0.6 (#87867)
bump pyipma
2023-02-13 09:22:44 -05:00
J. Nick Koston 250f032a0d Ensure recorder still shuts down if the final commit fails (#87799) 2023-02-13 09:22:14 -05:00
Avi Miller ec755b34f0 Bump aiolifx to 0.8.9 (#87790)
Signed-off-by: Avi Miller <me@dje.li>
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-02-13 09:21:54 -05:00
starkillerOG f8075f2dfd Bump reolink-aio to 0.4.0 (#87733)
bump reolink-aio to 0.4.0
2023-02-13 09:20:42 -05:00
John Pettitt 8780db82e5 Fix bad battery sense in ambient_station (#87668) 2023-02-13 09:18:09 -05:00
J. Nick Koston 09970a083b Bump yalexs_ble to 1.12.12 to fix reconnect when services fail to resolve (#87664)
* Bump yalexs_ble to 1.12.11 to fix reconnect when services fail to resolve

changelog: https://github.com/bdraco/yalexs-ble/compare/v1.12.8...v1.12.11

* bump to make it work with esphome proxy as well

* empty
2023-02-13 09:17:54 -05:00
J. Nick Koston a779e43e2a Bump cryptography to 39.0.1 for CVE-2023-23931 (#87658)
Bump cryptography to 39.0.1

CVE-2023-23931
2023-02-13 09:16:57 -05:00
Luke 1603a0b037 Bump oralb-ble to 0.17.5 (#87657)
fixes undefined
2023-02-13 09:16:56 -05:00
Ernst Klamer 151b33a4ab Keep sleepy xiaomi-ble devices that don't broadcast regularly available (#87654)
Co-authored-by: J. Nick Koston <nick@koston.org>
fixes undefined
2023-02-13 09:16:18 -05:00
H. Balázs 03ba7672ea Upgrading volvooncall to 0.10.2 (#87572)
* Upgrading volvooncall to 0.10.2

This release fixes the incorrect remaining time of charging the battery

* updating volvooncall test requirements

* Updating volvooncall requirements

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2023-02-13 09:14:01 -05:00
jan iversen f2697b3e96 Correct sensor restore entity in modbus (#87563)
* Correct sensor restore entity.

* Review comments.
2023-02-13 09:12:09 -05:00
starkillerOG 0a69f825d2 Reolink unregistered webhook on unexpected error (#87538) 2023-02-13 09:12:08 -05:00
starkillerOG 640f5f41cc Netgear ssdp discovery abort if no serial (#87532)
fixes undefined
2023-02-13 09:11:31 -05:00
Paulus Schoutsen 9962e9b67e 2023.2.3 (#87652) 2023-02-07 20:21:44 -05:00
shbatm 8e8a170121 Bump PyISY to 3.1.13, check portal for network buttons (#87650) 2023-02-07 15:41:54 -05:00
Allen Porter aacd8e044d Bump pyrainbird to 2.0.0 (#86851) 2023-02-07 15:41:53 -05:00
Paulus Schoutsen 08fb16a0c2 Bumped version to 2023.2.3 2023-02-07 13:40:53 -05:00
Thomas Hollstegge 354d77d26b Do not return cached values for entity states in emulated_hue (#87642)
* Do not return cached values for state and brightness

* Move building the uncached state dict behind a lru_cache (_build_entity_state_dict)

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2023-02-07 13:40:28 -05:00
Allen Porter 2e541e7ef6 Improve rainbird device reliability by sending requests serially (#87603)
Send rainbird requests serially
2023-02-07 13:40:27 -05:00
J. Nick Koston a2c9f92420 Bump sensorpush-ble to 1.5.5 (#87595)
* Bump sensorpush-ble to 1.5.4

changelog: https://github.com/Bluetooth-Devices/sensorpush-ble/compare/v1.5.2..v1.5.4

same fix as #85432 but for sensorpush

* bump again to fix parser with passive scans
2023-02-07 13:40:26 -05:00
J. Nick Koston e67d9988fd Bump bluemaestro-ble to 0.2.3 (#87594)
changelog: https://github.com/Bluetooth-Devices/bluemaestro-ble/compare/v0.2.1..v0.2.3

same fix as #85432 but for bluemaestro
2023-02-07 13:40:24 -05:00
J. Nick Koston c4470fc36d Bump thermopro-ble to 0.4.5 (#87592)
changelog: https://github.com/Bluetooth-Devices/thermopro-ble/compare/v0.4.3..v0.4.5

same fix as #85432 but for thermopro
2023-02-07 13:40:23 -05:00
J. Nick Koston ab14671dbc Bump sensorpro-ble to 0.5.3 (#87591)
changelog: https://github.com/Bluetooth-Devices/sensorpro-ble/compare/v0.5.1...v0.5.3

same fix as #85432 but for sensorpro
2023-02-07 13:40:22 -05:00
J. Nick Koston be83753514 Bump inkbird-ble to 0.5.6 (#87590)
changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.5.5...v0.5.6

fixes #85432
2023-02-07 13:40:21 -05:00
J. Nick Koston e0a6a6cfa6 Fix LD2410 BLE detection with passive scans (#87584) 2023-02-07 13:40:19 -05:00
J. Nick Koston 0d9393ca79 Fix indent on slow_range_in_select for MySQL/MariaDB (#87581)
We would calculate this value and throw it away
because it was only used on first_connection
2023-02-07 13:40:18 -05:00
Martin Hjelmare d06e640889 Fix matter remove config entry device (#87571) 2023-02-07 13:40:17 -05:00
Luke 9657567280 Bump oralb-ble to 0.17.4 (#87570)
closes undefined
2023-02-07 13:40:16 -05:00
Paulus Schoutsen 5bc49b1757 OpenAI: Ignore devices without a name (#87558)
Ignore devices without a name
2023-02-07 13:40:14 -05:00
J. Nick Koston 590bdc1f49 Optimize history.get_last_state_changes query (#87554)
fixes undefined
2023-02-07 13:40:13 -05:00
Michael Davie bc01df6b52 Bump env_canada to 0.5.28 (#87509)
Co-authored-by: J. Nick Koston <nick@koston.org>
fixes undefined
2023-02-07 13:40:12 -05:00
Ben Corrado 7fd3f404de Add LD2410BLE support for new firmware version (#87507)
* Updated local_name to reflect the naming scheme in HLK firmware 2.01.23020209

* Adding generated bluetooth file.
2023-02-07 13:40:10 -05:00
rikroe f59eb6c277 Bump bimmer_connected to 0.12.1 (#87506)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
fixes undefined
2023-02-07 13:40:09 -05:00
snapcase 08c23dd688 Bump jaraco.abode to 3.3.0 (#87498)
Fixes https://github.com/home-assistant/core/issues/86765
fixes undefined
2023-02-07 13:40:08 -05:00
Ernst Klamer 1a72b64024 Bump xiaomi-ble to 0.16.1 (#87496)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-02-07 13:40:07 -05:00
J. Nick Koston 8a257df59f Fix recorder run history during schema migration and startup (#87492)
Fix recorder run history during schema migration

RunHistory.get and RunHistory.current can be called before
RunHistory.start. We need to return a RecorderRuns object
with the recording_start time that will be used when start
it called to ensure history queries still work as expected.

fixes #87112
2023-02-07 13:40:05 -05:00
Maikel Punie e30ea3e344 Add the correct loggers to velbus manifest.json (#87488) 2023-02-07 13:40:04 -05:00
Robert Hillis 423d8f0bca Disable uptime sensor by default in Unifi (#87484)
Disable Uptime sensor by default in Unifi
2023-02-07 13:40:03 -05:00
Michael b2ccf2e87e Bump py-synologydsm-api to 2.1.4 (#87471)
fixes undefined
2023-02-07 13:40:02 -05:00
majuss 4a7aee4bde Bump lupupy to 0.2.7 (#87469) 2023-02-07 13:40:00 -05:00
Gregory Haynes 5b0c7321b5 Add missing name field to emulated_hue config (#87456)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-02-07 13:39:59 -05:00
J. Nick Koston 1df82f39c1 Speed up purge time with newer MariaDB versions (#87409)
* Speed up purge time with newer MariaDB versions

* fix

* document

* document

* document

* rename

* self review

* Update homeassistant/components/recorder/util.py

* fixes
2023-02-07 13:39:58 -05:00
Paulus Schoutsen 1c88195d32 2023.2.2 (#87433) 2023-02-04 22:01:23 -05:00
Franck Nijhof d82e409d8e Update black to 23.1.0 (#87188) 2023-02-04 21:26:10 -05:00
Paulus Schoutsen 3253eeca7a Bumped version to 2023.2.2 2023-02-04 21:08:23 -05:00
J. Nick Koston f4f1b0dfc5 Bump aiohomekit to 2.4.6 (#87427)
fixes #86083

changelog: https://github.com/Jc2k/aiohomekit/compare/2.4.5...2.4.6
2023-02-04 21:08:15 -05:00
J. Nick Koston 1f08fc72bb Disable mopeka accelerometer sensors by default (#87420)
* Disable mopeka accelerometer sensors by default

These generate a significant amount of noise and are
only useful when placing the sensor. Disable them by
default. This not a breaking change because existing
preferences are preserved.

* adjust tests
2023-02-04 21:08:14 -05:00
J. Nick Koston 323ab97ff5 Ignore invalid zeroconf names from devices with broken firmwares (#87414) 2023-02-04 21:08:13 -05:00
Vincent Knoop Pathuis 93229196d8 Fix state class in Enphase Envoy (#87397)
* Change total_increasing to total

* As suggested in PR: only Last Seven Days TOTAL
2023-02-04 21:08:12 -05:00
Aarni Koskela 23e04ba891 Fix Ruuvi Gateway data being ignored when system is not using UTC time (#87384) 2023-02-04 21:08:11 -05:00
Jc2k 2d5ab5a0dc Fix exception when trying to poll a HomeKit device over Thread with no active encryption context (#87379)
Bump aiohomekit==2.4.5
2023-02-04 21:08:10 -05:00
Matthew Donoughe 30f63ae0ea Update pylutron-caseta to 0.18.1 (#87361)
update pylutron-caseta to 0.18.1
2023-02-04 21:08:09 -05:00
Luke 2d85890789 Bump oralb-ble to 0.17.2 (#87355) 2023-02-04 21:08:08 -05:00
Michael c791a7c7fb Bump py-synologydsm-api to 2.1.2 (#87324)
fixes undefined
2023-02-04 21:08:07 -05:00
G Johansson fe7c7001ad Fix code format issue in Yale Smart Alarm (#87323)
fixes undefined
2023-02-04 21:08:06 -05:00
Koen van Zuijlen 6d2a2b1c91 Fixed parser for zeversolar hardware version M10 (#87319)
fixes undefined
2023-02-04 21:08:05 -05:00
Artem Draft 1df7fcea09 Fix Bravia TV refreshing zero volume level (#87318)
fixes undefined
2023-02-04 21:08:04 -05:00
Franck Nijhof 681de9a516 Don't override icon in sensor group when device class is set (#87304) 2023-02-04 21:08:03 -05:00
Franck Nijhof a5378ec9a8 Fix incorrect description in sensor group config flow (#87298) 2023-02-04 21:08:02 -05:00
Franck Nijhof 54687d6b56 Extend state class sensor warnings with expected values (#87294) 2023-02-04 21:08:01 -05:00
epenet 4c0ea397c8 Bump sfrbox-api to 0.0.6 (#87281) 2023-02-04 21:08:00 -05:00
epenet 9769a0f0ec Fix volume state class in renault (#87280) 2023-02-04 21:07:59 -05:00
starkillerOG e78dd34a45 Bump reolink-aio to 0.3.4 (#87272) 2023-02-04 21:07:58 -05:00
G Johansson 6543a1169b Filesize timestamp remove state class (#87247)
fixes undefined
2023-02-04 21:07:57 -05:00
jjlawren 012ba55154 Handle failed Sonos subscriptions better (#87240)
Catch unsubscribe failure separately from ZGS poll
2023-02-04 21:07:56 -05:00
Brett Adams e12425c229 Fix call values in Aussie Broadband (#87229)
Fixed calls values

Added lamda for International, Voicemail, and Other calls.
2023-02-04 21:07:55 -05:00
J. Nick Koston 5aaddf72e6 Add missing mopeka translations (#87421)
fixes #87163
2023-02-04 20:08:18 -05:00
Paulus Schoutsen dbd8ffc282 2023.2.1 (#87221) 2023-02-02 20:52:38 -05:00
Paulus Schoutsen 372afc5c28 Bumped version to 2023.2.1 2023-02-02 16:48:09 -05:00
Karlie Meads ed8a0ef0ea Fix disabled condition within an automation action (#87213)
fixes undefined
2023-02-02 16:48:04 -05:00
Michael 1d8f5b2e16 Bump py-synologydsm-api to 2.1.1 (#87211)
bump py-synologydsm-api to 2.1.1
2023-02-02 16:48:03 -05:00
Bram Kragten 75796e1f0f Update frontend to 20230202.0 (#87208) 2023-02-02 16:48:02 -05:00
mkmer 063bbe91d1 Bump AIOSomecomfort to 0.0.6 (#87203)
* Bump to 0.0.5

* Bump aiosomecomfort to 0.0.6

* lower case aiosomecomfort

* Fix other bad imports....

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-02-02 16:48:01 -05:00
mkmer bfcae4e07b Add Reauth config flow to honeywell (#86170) 2023-02-02 16:48:00 -05:00
J. Nick Koston be77d7daa5 Fix statistics graphs not loading with data_rate, electric_current, voltage, information, and unitless units (#87202)
* Add missing converts to recorder/statistics_during_period API

This was resulting in the stats graphs not loading on the frontend

* its in two places
2023-02-02 16:46:16 -05:00
starkillerOG a58e4e0f88 Reolink unsubscribe webhook when first refresh fails (#87147)
* catch ValueError on webhook async_register

* add ONVIF to webhook_id

* Unsubscribe webhook when ConfigEntryNotReady for async_config_entry_first_refresh

* Revert catching ValueError
2023-02-02 16:46:15 -05:00
Dmitry Vlasov 56a583e6ac Add missing supported features to Z-Wave.Me siren (#87141) 2023-02-02 16:46:14 -05:00
epenet 517e89ab3c Add missing converters to recorder statistics (#87137) 2023-02-02 16:46:13 -05:00
epenet ef8029ebbf Fix invalid state class in renault (#87135) 2023-02-02 16:46:12 -05:00
starkillerOG 264b6d4f77 Bump reolink-aio to 0.3.2 (#87121) 2023-02-02 16:46:11 -05:00
starkillerOG b24d0a86ee Bump reolink_aio to 0.3.1 (#87118) 2023-02-02 16:46:10 -05:00
shbatm 8a7e2922c2 Support ISY994 Z-Wave motorized blinds as cover (#87102) 2023-02-02 16:46:09 -05:00
Franck Nijhof 5fe3adff57 2023.2.0 (#87101) 2023-02-01 19:33:00 +01:00
Joakim Sørensen 4641497806 Bump isort from 5.11.4 to 5.12.0 (#86890) 2023-02-01 18:47:22 +01:00
Franck Nijhof eed15bb9fa Bumped version to 2023.2.0 2023-02-01 18:41:13 +01:00
Bram Kragten 7028aa7dac Update frontend to 20230201.0 (#87099) 2023-02-01 18:40:59 +01:00
Daniel Hjelseth Høyer 6c93b28374 Update pyTibber to 0.26.12 (#87098) 2023-02-01 18:40:55 +01:00
Paulus Schoutsen 65286d0544 Fix Assist skipping entities that are hidden or have entity category (#87096)
Skipping entities that are hidden or have entity category
2023-02-01 18:40:51 +01:00
J. Nick Koston a678eee31b Reduce chance of queue overflow during schema migration (#87090)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-02-01 18:40:47 +01:00
mkmer fe541583a8 Bump AIOAladdinConnect to 0.1.55 (#87086) 2023-02-01 18:40:44 +01:00
mkmer eabcfa419e Bump AIOAladdinConnect to 0.1.54 (#86749) 2023-02-01 18:40:39 +01:00
Paulus Schoutsen d57ce25287 Bumped version to 2023.2.0b9 2023-01-31 23:16:09 -05:00
Paulus Schoutsen c786fe27d7 Guard what version we can install ESPHome updates with (#87059)
* Guard what version we can install ESPHome updates with

* Update homeassistant/components/esphome/dashboard.py
2023-01-31 23:16:04 -05:00
Paulus Schoutsen fd3d76988e Trigger update of ESPHome update entity when static info updates (#87058)
Trigger update of update entity when static info updates
2023-01-31 23:16:03 -05:00
J. Nick Koston c43174ee4b Ensure humidity is still exported to HomeKit when it is read-only (#87051)
* Ensure humidity is still exported to HomeKit when is cannot be set

We would only send humidity to HomeKit if the device supported
changing the humidity

* remove unrelated changes
2023-01-31 23:16:02 -05:00
Paulus Schoutsen 0bae47c992 Bumped version to 2023.2.0b8 2023-01-31 15:16:21 -05:00
Paulus Schoutsen 0d3a368a1f Improve JSON errors from HTTP view (#87042) 2023-01-31 15:16:16 -05:00
Franck Nijhof c7871d13cf Fix Yamaha MusicCast zone sleep select entity (#87041) 2023-01-31 15:16:15 -05:00
J. Nick Koston 3d6ced2a16 Add a repair issue when using MariaDB is affected by MDEV-25020 (#87040)
closes https://github.com/home-assistant/core/issues/83787
2023-01-31 15:16:14 -05:00
Michael Hansen 2f403b712c Bump home-assistant-intents to 2023.1.31 (#87034) 2023-01-31 15:16:12 -05:00
mkmer 1caca91174 Honeywell Correct key name (#87018)
* Correct key name

* Logic error around setpoint and auto mode

* Set tempurature and setpoints correctly

* Only high/low in auto.
2023-01-31 15:16:11 -05:00
Franck Nijhof 1859dcf99b Only report invalid numeric value for sensors once (#87010) 2023-01-31 15:16:10 -05:00
Bouwe Westerdijk c34eb1ad9d Bump plugwise to v0.27.5 (#87001)
fixes undefined
2023-01-31 15:16:08 -05:00
Paulus Schoutsen be69e9579c Bump ESPHome Dashboard API 1.2.3 (#86997) 2023-01-31 15:16:07 -05:00
Michael Davie ac6fa3275b Bump env_canada to 0.5.27 (#86996)
fixes undefined
2023-01-31 15:16:06 -05:00
473 changed files with 2832 additions and 1208 deletions
+2 -2
View File
@@ -20,7 +20,7 @@ repos:
- --remove-all-unused-imports
stages: [manual]
- repo: https://github.com/psf/black
rev: 22.12.0
rev: 23.1.0
hooks:
- id: black
args:
@@ -60,7 +60,7 @@ repos:
- --configfile=tests/bandit.yaml
files: ^(homeassistant|script|tests)/.+\.py$
- repo: https://github.com/PyCQA/isort
rev: 5.11.4
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks
-1
View File
@@ -406,7 +406,6 @@ 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
+1 -1
View File
@@ -3,7 +3,7 @@
"name": "Abode",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/abode",
"requirements": ["jaraco.abode==3.2.1"],
"requirements": ["jaraco.abode==3.3.0"],
"codeowners": ["@shred86"],
"homekit": {
"models": ["Abode", "Iota"]
@@ -24,7 +24,6 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
# convert title and unique_id to string
if config_entry.version == 1:
if isinstance(config_entry.unique_id, int):
hass.config_entries.async_update_entry(
config_entry,
unique_id=str(config_entry.unique_id),
@@ -88,7 +88,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors["base"] = "cannot_connect"
else:
self.hass.config_entries.async_update_entry(
self.entry,
data={
@@ -2,7 +2,7 @@
"domain": "aladdin_connect",
"name": "Aladdin Connect",
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"requirements": ["AIOAladdinConnect==0.1.53"],
"requirements": ["AIOAladdinConnect==0.1.56"],
"codeowners": ["@mkmer"],
"iot_class": "cloud_polling",
"loggers": ["aladdin_connect"],
@@ -270,7 +270,6 @@ class Alert(Entity):
await self._send_notification_message(message)
async def _send_notification_message(self, message: Any) -> None:
if not self._notifiers:
return
-1
View File
@@ -103,7 +103,6 @@ class Auth:
return dt.utcnow() < preemptive_expire_time
async def _async_request_new_token(self, lwa_params):
try:
session = aiohttp_client.async_get_clientsession(self.hass)
async with async_timeout.timeout(10):
-1
View File
@@ -193,7 +193,6 @@ def resolve_slot_synonyms(key, request):
and "resolutionsPerAuthority" in request["resolutions"]
and len(request["resolutions"]["resolutionsPerAuthority"]) >= 1
):
# Extract all of the possible values from each authority with a
# successful match
possible_values = []
@@ -168,28 +168,28 @@ BINARY_SENSOR_DESCRIPTIONS = (
name="Leak detector battery 1",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK2,
name="Leak detector battery 2",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK3,
name="Leak detector battery 3",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK4,
name="Leak detector battery 4",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM1,
@@ -273,7 +273,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
name="Lightning detector battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_LEAK1,
@@ -274,7 +274,6 @@ class AsusWrtRouter:
entity_reg, self._entry.entry_id
)
for entry in track_entries:
if entry.domain != TRACKER_DOMAIN:
continue
device_mac = format_mac(entry.unique_id)
@@ -2,7 +2,7 @@
"domain": "august",
"name": "August",
"documentation": "https://www.home-assistant.io/integrations/august",
"requirements": ["yalexs==1.2.6", "yalexs_ble==1.12.8"],
"requirements": ["yalexs==1.2.6", "yalexs_ble==1.12.12"],
"codeowners": ["@bdraco"],
"dhcp": [
{
@@ -78,6 +78,7 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone-plus",
value=lambda x: x.get("calls"),
),
SensorValueEntityDescription(
key="sms",
@@ -101,6 +102,7 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone",
value=lambda x: x.get("calls"),
),
SensorValueEntityDescription(
key="other",
@@ -108,6 +110,7 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = (
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:phone",
value=lambda x: x.get("calls"),
),
# Generic sensors
SensorValueEntityDescription(
@@ -112,7 +112,6 @@ class AxisBinarySensor(AxisEventEntity, BinarySensorEntity):
self._attr_name = self.device.api.vapix.ports[event.id].name
elif event.group == EventGroup.MOTION:
for event_topic, event_data in (
(EventTopic.FENCE_GUARD, self.device.api.vapix.fence_guard),
(EventTopic.LOITERING_GUARD, self.device.api.vapix.loitering_guard),
@@ -120,7 +119,6 @@ class AxisBinarySensor(AxisEventEntity, BinarySensorEntity):
(EventTopic.OBJECT_ANALYTICS, self.device.api.vapix.object_analytics),
(EventTopic.MOTION_DETECTION_4, self.device.api.vapix.vmd4),
):
if (
event.topic_base == event_topic
and event_data
@@ -399,7 +399,6 @@ class BayesianBinarySensor(BinarySensorEntity):
observations_by_entity: dict[str, list[Observation]] = {}
for observation in self._observations:
if (key := observation.entity_id) is None:
continue
observations_by_entity.setdefault(key, []).append(observation)
+4 -1
View File
@@ -22,6 +22,7 @@ from .const import DOMAIN, PRODUCT
SCAN_INTERVAL = timedelta(seconds=5)
BLEBOX_TO_HVACMODE = {
None: None,
0: HVACMode.OFF,
1: HVACMode.HEAT,
2: HVACMode.COOL,
@@ -58,13 +59,15 @@ class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEn
@property
def hvac_modes(self):
"""Return list of supported HVAC modes."""
return [HVACMode.OFF, self.hvac_mode]
return [HVACMode.OFF, BLEBOX_TO_HVACMODE[self._feature.mode]]
@property
def hvac_mode(self):
"""Return the desired HVAC mode."""
if self._feature.is_on is None:
return None
if not self._feature.is_on:
return HVACMode.OFF
if self._feature.mode is not None:
return BLEBOX_TO_HVACMODE[self._feature.mode]
return HVACMode.HEAT if self._feature.is_on else HVACMode.OFF
@@ -9,7 +9,7 @@
"connectable": false
}
],
"requirements": ["bluemaestro-ble==0.2.1"],
"requirements": ["bluemaestro-ble==0.2.3"],
"dependencies": ["bluetooth_adapters"],
"codeowners": ["@bdraco"],
"iot_class": "local_push"
@@ -396,7 +396,6 @@ class BluesoundPlayer(MediaPlayerEntity):
_LOGGER.debug("Calling URL: %s", url)
try:
async with async_timeout.timeout(125):
response = await self._polling_session.get(
url, headers={CONNECTION: KEEP_ALIVE}
+18 -7
View File
@@ -216,13 +216,7 @@ class BluetoothManager:
if address in seen:
continue
seen.add(address)
for domain in self._integration_matcher.match_domains(service_info):
discovery_flow.async_create_flow(
self.hass,
domain,
{"source": config_entries.SOURCE_BLUETOOTH},
service_info,
)
self._async_trigger_matching_discovery(service_info)
@hass_callback
def async_stop(self, event: Event) -> None:
@@ -649,10 +643,27 @@ class BluetoothManager:
"""Return the last service info for an address."""
return self._get_history_by_type(connectable).get(address)
def _async_trigger_matching_discovery(
self, service_info: BluetoothServiceInfoBleak
) -> None:
"""Trigger discovery for matching domains."""
for domain in self._integration_matcher.match_domains(service_info):
discovery_flow.async_create_flow(
self.hass,
domain,
{"source": config_entries.SOURCE_BLUETOOTH},
service_info,
)
@hass_callback
def async_rediscover_address(self, address: str) -> None:
"""Trigger discovery of devices which have already been seen."""
self._integration_matcher.async_clear_address(address)
if service_info := self._connectable_history.get(address):
self._async_trigger_matching_discovery(service_info)
return
if service_info := self._all_history.get(address):
self._async_trigger_matching_discovery(service_info)
def _get_scanners_by_type(self, connectable: bool) -> list[BaseHaScanner]:
"""Return the scanners by type."""
@@ -12,7 +12,6 @@ from home_assistant_bluetooth import BluetoothServiceInfoBleak
from homeassistant.util.dt import monotonic_time_coarse
if TYPE_CHECKING:
from .manager import BluetoothManager
@@ -2,7 +2,7 @@
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"requirements": ["bimmer_connected==0.12.0"],
"requirements": ["bimmer_connected==0.12.1"],
"codeowners": ["@gerard33", "@rikroe"],
"config_flow": true,
"iot_class": "cloud_polling",
@@ -95,7 +95,6 @@ async def async_setup_entry(
session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION]
for switch in session.device_helper.smart_plugs:
entities.append(
SHCSwitch(
device=switch,
@@ -113,7 +112,6 @@ async def async_setup_entry(
)
for switch in session.device_helper.light_switches:
entities.append(
SHCSwitch(
device=switch,
@@ -124,7 +122,6 @@ async def async_setup_entry(
)
for switch in session.device_helper.smart_plugs_compact:
entities.append(
SHCSwitch(
device=switch,
@@ -135,7 +132,6 @@ async def async_setup_entry(
)
for switch in session.device_helper.camera_eyes:
entities.append(
SHCSwitch(
device=switch,
@@ -146,7 +142,6 @@ async def async_setup_entry(
)
for switch in session.device_helper.camera_360:
entities.append(
SHCSwitch(
device=switch,
@@ -171,7 +171,7 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]):
async def async_update_volume(self) -> None:
"""Update volume information."""
volume_info = await self.client.get_volume_info()
if volume_level := volume_info.get("volume"):
if (volume_level := volume_info.get("volume")) is not None:
self.volume_level = volume_level / 100
self.volume_muted = volume_info.get("mute", False)
self.volume_target = volume_info.get("target")
+8 -2
View File
@@ -231,7 +231,10 @@ SENSOR_DESCRIPTIONS = {
state_class=SensorStateClass.MEASUREMENT,
),
# UV index (-)
(BTHomeSensorDeviceClass.UV_INDEX, None,): SensorEntityDescription(
(
BTHomeSensorDeviceClass.UV_INDEX,
None,
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.UV_INDEX}",
state_class=SensorStateClass.MEASUREMENT,
),
@@ -256,7 +259,10 @@ SENSOR_DESCRIPTIONS = {
state_class=SensorStateClass.MEASUREMENT,
),
# Volume (L)
(BTHomeSensorDeviceClass.VOLUME, Units.VOLUME_LITERS,): SensorEntityDescription(
(
BTHomeSensorDeviceClass.VOLUME,
Units.VOLUME_LITERS,
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.VOLUME}_{Units.VOLUME_LITERS}",
device_class=SensorDeviceClass.VOLUME,
native_unit_of_measurement=UnitOfVolume.LITERS,
@@ -735,7 +735,6 @@ class BrSensor(SensorEntity):
or sensor_type.endswith("_4d")
or sensor_type.endswith("_5d")
):
# update forecasting sensors:
fcday = 0
if sensor_type.endswith("_2d"):
@@ -96,7 +96,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors = {}
if user_input is not None:
hub = Control4Validator(
user_input[CONF_HOST],
user_input[CONF_USERNAME],
@@ -403,16 +403,20 @@ class DefaultAgent(AbstractConversationAgent):
entity = entities.async_get(state.entity_id)
if entity is not None:
if entity.entity_category:
# Skip configuration/diagnostic entities
if entity.entity_category or entity.hidden:
# Skip configuration/diagnostic/hidden entities
continue
if entity.aliases:
for alias in entity.aliases:
names.append((alias, state.entity_id, context))
# Default name
names.append((state.name, state.entity_id, context))
# Default name
names.append((state.name, state.entity_id, context))
else:
# Default name
names.append((state.name, state.entity_id, context))
self._names_list = TextSlotList.from_tuples(names, allow_template=False)
return self._names_list
@@ -2,7 +2,7 @@
"domain": "conversation",
"name": "Conversation",
"documentation": "https://www.home-assistant.io/integrations/conversation",
"requirements": ["hassil==0.2.6", "home-assistant-intents==2023.1.25"],
"requirements": ["hassil==0.2.6", "home-assistant-intents==2023.1.31"],
"dependencies": ["http"],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal",
@@ -188,6 +188,7 @@ class DarkSkyWeather(WeatherEntity):
@property
def forecast(self):
"""Return the forecast array."""
# Per conversation with Joshua Reyes of Dark Sky, to get the total
# forecasted precipitation, you have to multiple the intensity by
# the hours for the forecast interval
@@ -271,7 +271,6 @@ class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
attr[ATTR_TEMPERATURE] = self._device.internal_temperature
if isinstance(self._device, Presence):
if self._device.dark is not None:
attr[ATTR_DARK] = self._device.dark
@@ -87,7 +87,6 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
If no bridge is found allow user to manually input configuration.
"""
if user_input is not None:
if CONF_MANUAL_INPUT == user_input[CONF_HOST]:
return await self.async_step_manual_input()
@@ -372,7 +372,6 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
attr[ATTR_DAYLIGHT] = self._device.daylight
elif isinstance(self._device, LightLevel):
if self._device.dark is not None:
attr[ATTR_DARK] = self._device.dark
@@ -186,10 +186,8 @@ async def async_remove_orphaned_entries_service(gateway: DeconzGateway) -> None:
devices_to_be_removed.remove(event.device_id)
for entry in entity_entries:
# Don't remove available entities
if entry.unique_id in gateway.entities[entry.domain]:
# Don't remove devices with available entities
if entry.device_id in devices_to_be_removed:
devices_to_be_removed.remove(entry.device_id)
+1 -1
View File
@@ -121,6 +121,6 @@ class DeLijnPublicTransportSensor(SensorEntity):
self._attr_extra_state_attributes["next_passages"] = self.line.passages
self._attr_available = True
except (KeyError) as error:
except KeyError as error:
_LOGGER.error("Invalid data received from De Lijn: %s", error)
self._attr_available = False
@@ -245,7 +245,7 @@ class DerivativeSensor(RestoreEntity, SensorEntity):
derivative = new_derivative
else:
derivative = Decimal(0)
for (start, end, value) in self._state_list:
for start, end, value in self._state_list:
weight = calculate_weight(start, end, new_state.last_updated)
derivative = derivative + (value * Decimal(weight))
@@ -214,7 +214,6 @@ async def activate_automation( # noqa: C901
elif start_point and start_point < now < get_astral_event_next(
hass, SUN_EVENT_SUNSET
):
# Check for every light if it would be on if someone was home
# when the fading in started and turn it on if so
for index, light_id in enumerate(light_ids):
@@ -93,7 +93,6 @@ class DnsIPConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors = {}
if user_input:
hostname = user_input[CONF_HOSTNAME]
name = DEFAULT_NAME if hostname == DEFAULT_HOSTNAME else hostname
resolver = user_input.get(CONF_RESOLVER, DEFAULT_RESOLVER)
@@ -255,7 +255,6 @@ class Doods(ImageProcessingEntity):
)
for label, values in matches.items():
# Draw custom label regions/areas
if label in self._label_areas and self._label_areas[label] != [0, 0, 1, 1]:
box_label = f"{label.capitalize()} Detection Area"
@@ -66,7 +66,6 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
server_address = (conf.get(CONF_HOST), conf.get(CONF_PORT))
try:
ebusdpy.init(server_address)
hass.data[DOMAIN] = EbusdData(server_address, circuit)
@@ -160,7 +160,6 @@ class EcovacsVacuum(VacuumEntity):
def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
"""Set fan speed."""
if self.is_on:
self.device.run(sucks.Clean(mode=self.device.clean_status, speed=fan_speed))
def send_command(
+1 -1
View File
@@ -87,7 +87,7 @@ class SmartPlugSwitch(SwitchEntity):
self._state = self._pca.get_state(self._device_id)
self._available = True
except (OSError) as ex:
except OSError as ex:
if self._available:
_LOGGER.warning("Could not read state for %s: %s", self.name, ex)
self._available = False
@@ -106,7 +106,6 @@ def setup_platform(
sensors = []
for elem in data.data:
if exclude_feeds is not None and int(elem["id"]) in exclude_feeds:
continue
@@ -379,7 +379,7 @@ class HueOneLightChangeView(HomeAssistantView):
else:
parsed[STATE_ON] = entity.state != STATE_OFF
for (key, attr) in (
for key, attr in (
(HUE_API_STATE_BRI, STATE_BRIGHTNESS),
(HUE_API_STATE_HUE, STATE_HUE),
(HUE_API_STATE_SAT, STATE_SATURATION),
@@ -587,7 +587,7 @@ class HueOneLightChangeView(HomeAssistantView):
)
]
for (key, val) in (
for key, val in (
(STATE_BRIGHTNESS, HUE_API_STATE_BRI),
(STATE_HUE, HUE_API_STATE_HUE),
(STATE_SATURATION, HUE_API_STATE_SAT),
@@ -634,78 +634,88 @@ def get_entity_state_dict(config: Config, entity: State) -> dict[str, Any]:
# Remove the now stale cached entry.
config.cached_states.pop(entity.entity_id)
if cached_state is None:
return _build_entity_state_dict(entity)
data: dict[str, Any] = cached_state
# Make sure brightness is valid
if data[STATE_BRIGHTNESS] is None:
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0
# Make sure hue/saturation are valid
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
# If the light is off, set the color to off
if data[STATE_BRIGHTNESS] == 0:
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
_clamp_values(data)
return data
@lru_cache(maxsize=512)
def _build_entity_state_dict(entity: State) -> dict[str, Any]:
"""Build a state dict for an entity."""
data: dict[str, Any] = {
STATE_ON: False,
STATE_ON: entity.state != STATE_OFF,
STATE_BRIGHTNESS: None,
STATE_HUE: None,
STATE_SATURATION: None,
STATE_COLOR_TEMP: None,
}
if cached_state is None:
data[STATE_ON] = entity.state != STATE_OFF
if data[STATE_ON]:
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
entity.attributes.get(ATTR_BRIGHTNESS, 0)
)
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
if hue_sat is not None:
hue = hue_sat[0]
sat = hue_sat[1]
# Convert hass hs values back to hue hs values
data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX)
data[STATE_SATURATION] = int((sat / 100.0) * HUE_API_STATE_SAT_MAX)
else:
data[STATE_HUE] = HUE_API_STATE_HUE_MIN
data[STATE_SATURATION] = HUE_API_STATE_SAT_MIN
data[STATE_COLOR_TEMP] = entity.attributes.get(ATTR_COLOR_TEMP, 0)
if data[STATE_ON]:
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
entity.attributes.get(ATTR_BRIGHTNESS, 0)
)
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
if hue_sat is not None:
hue = hue_sat[0]
sat = hue_sat[1]
# Convert hass hs values back to hue hs values
data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX)
data[STATE_SATURATION] = int((sat / 100.0) * HUE_API_STATE_SAT_MAX)
else:
data[STATE_BRIGHTNESS] = 0
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
data[STATE_COLOR_TEMP] = 0
data[STATE_HUE] = HUE_API_STATE_HUE_MIN
data[STATE_SATURATION] = HUE_API_STATE_SAT_MIN
data[STATE_COLOR_TEMP] = entity.attributes.get(ATTR_COLOR_TEMP, 0)
if entity.domain == climate.DOMAIN:
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == humidifier.DOMAIN:
humidity = entity.attributes.get(ATTR_HUMIDITY, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(humidity * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == media_player.DOMAIN:
level = entity.attributes.get(
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
)
# Convert 0.0-1.0 to 0-254
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
elif entity.domain == fan.DOMAIN:
percentage = entity.attributes.get(ATTR_PERCENTAGE) or 0
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(percentage * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == cover.DOMAIN:
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
else:
data = cached_state
# Make sure brightness is valid
if data[STATE_BRIGHTNESS] is None:
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0
data[STATE_BRIGHTNESS] = 0
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
data[STATE_COLOR_TEMP] = 0
# Make sure hue/saturation are valid
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
if entity.domain == climate.DOMAIN:
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == humidifier.DOMAIN:
humidity = entity.attributes.get(ATTR_HUMIDITY, 0)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(humidity * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == media_player.DOMAIN:
level = entity.attributes.get(
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
)
# Convert 0.0-1.0 to 0-254
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
elif entity.domain == fan.DOMAIN:
percentage = entity.attributes.get(ATTR_PERCENTAGE) or 0
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(percentage * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == cover.DOMAIN:
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
_clamp_values(data)
return data
# If the light is off, set the color to off
if data[STATE_BRIGHTNESS] == 0:
data[STATE_HUE] = 0
data[STATE_SATURATION] = 0
# Clamp brightness, hue, saturation, and color temp to valid values
for (key, v_min, v_max) in (
def _clamp_values(data: dict[str, Any]) -> None:
"""Clamp brightness, hue, saturation, and color temp to valid values."""
for key, v_min, v_max in (
(STATE_BRIGHTNESS, HUE_API_STATE_BRI_MIN, HUE_API_STATE_BRI_MAX),
(STATE_HUE, HUE_API_STATE_HUE_MIN, HUE_API_STATE_HUE_MAX),
(STATE_SATURATION, HUE_API_STATE_SAT_MIN, HUE_API_STATE_SAT_MAX),
@@ -714,8 +724,6 @@ def get_entity_state_dict(config: Config, entity: State) -> dict[str, Any]:
if data[key] is not None:
data[key] = max(v_min, min(data[key], v_max))
return data
@lru_cache(maxsize=1024)
def _entity_unique_id(entity_id: str) -> str:
@@ -831,6 +839,7 @@ def create_hue_success_response(
def create_config_model(config: Config, request: web.Request) -> dict[str, Any]:
"""Create a config resource."""
return {
"name": "HASS BRIDGE",
"mac": "00:00:00:00:00:00",
"swversion": "01003542",
"apiversion": "1.17.0",
@@ -842,10 +851,18 @@ def create_config_model(config: Config, request: web.Request) -> dict[str, Any]:
def create_list_of_entities(config: Config, request: web.Request) -> dict[str, Any]:
"""Create a list of all entities."""
json_response: dict[str, Any] = {
config.entity_id_to_number(state.entity_id): state_to_json(config, state)
for state in config.get_exposed_states()
}
hass: core.HomeAssistant = request.app["hass"]
json_response: dict[str, Any] = {}
for cached_state in config.get_exposed_states():
entity_id = cached_state.entity_id
state = hass.states.get(entity_id)
assert state is not None
json_response[config.entity_id_to_number(entity_id)] = state_to_json(
config, state
)
return json_response
@@ -33,7 +33,7 @@ SENSORS = (
key="seven_days_production",
name="Last Seven Days Energy Production",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
@@ -61,7 +61,7 @@ SENSORS = (
key="seven_days_consumption",
name="Last Seven Days Energy Consumption",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
@@ -2,7 +2,7 @@
"domain": "environment_canada",
"name": "Environment Canada",
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
"requirements": ["env_canada==0.5.22"],
"requirements": ["env_canada==0.5.28"],
"codeowners": ["@gwww", "@michaeldavie"],
"config_flow": true,
"iot_class": "cloud_polling",
@@ -21,7 +21,6 @@ _LOGGER = logging.getLogger(__name__)
async def _async_has_devices(hass: HomeAssistant) -> bool:
controller_ready = asyncio.Event()
@callback
+3 -2
View File
@@ -646,9 +646,10 @@ async def platform_async_setup_entry(
# Add entities to Home Assistant
async_add_entities(add_entities)
signal = f"esphome_{entry.entry_id}_on_list"
entry_data.cleanup_callbacks.append(
async_dispatcher_connect(hass, signal, async_list_entities)
async_dispatcher_connect(
hass, entry_data.signal_static_info_updated, async_list_entities
)
)
@@ -6,6 +6,7 @@ from datetime import timedelta
import logging
import aiohttp
from awesomeversion import AwesomeVersion
from esphome_dashboard_api import ConfiguredDevice, ESPHomeDashboardAPI
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
@@ -83,6 +84,20 @@ class ESPHomeDashboard(DataUpdateCoordinator[dict[str, ConfiguredDevice]]):
self.url = url
self.api = ESPHomeDashboardAPI(url, session)
@property
def supports_update(self) -> bool:
"""Return whether the dashboard supports updates."""
if self.data is None:
raise RuntimeError("Data needs to be loaded first")
if len(self.data) == 0:
return False
esphome_version: str = next(iter(self.data.values()))["current_version"]
# There is no January release
return AwesomeVersion(esphome_version) > AwesomeVersion("2023.1.0")
async def _async_update_data(self) -> dict:
"""Fetch device data."""
devices = await self.api.get_devices()
@@ -107,6 +107,11 @@ class RuntimeEntryData:
return self.device_info.friendly_name
return self.name
@property
def signal_static_info_updated(self) -> str:
"""Return the signal to listen to for updates on static info."""
return f"esphome_{self.entry_id}_on_list"
@callback
def async_update_ble_connection_limits(self, free: int, limit: int) -> None:
"""Update the BLE connection limits."""
@@ -168,8 +173,7 @@ class RuntimeEntryData:
await self._ensure_platforms_loaded(hass, entry, needed_platforms)
# Then send dispatcher event
signal = f"esphome_{self.entry_id}_on_list"
async_dispatcher_send(hass, signal, infos)
async_dispatcher_send(hass, self.signal_static_info_updated, infos)
@callback
def async_subscribe_state_update(
@@ -3,7 +3,7 @@
"name": "ESPHome",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/esphome",
"requirements": ["aioesphomeapi==13.1.0", "esphome-dashboard-api==1.2.1"],
"requirements": ["aioesphomeapi==13.3.1", "esphome-dashboard-api==1.2.3"],
"zeroconf": ["_esphomelib._tcp.local."],
"dhcp": [{ "registered_devices": true }],
"codeowners": ["@OttoWinter", "@jesserockz"],
+21 -3
View File
@@ -5,7 +5,7 @@ import asyncio
import logging
from typing import Any, cast
from aioesphomeapi import DeviceInfo as ESPHomeDeviceInfo
from aioesphomeapi import DeviceInfo as ESPHomeDeviceInfo, EntityInfo
from homeassistant.components.update import (
UpdateDeviceClass,
@@ -13,7 +13,7 @@ from homeassistant.components.update import (
UpdateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo
@@ -68,7 +68,6 @@ class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity):
_attr_has_entity_name = True
_attr_device_class = UpdateDeviceClass.FIRMWARE
_attr_supported_features = UpdateEntityFeature.INSTALL
_attr_title = "ESPHome"
_attr_name = "Firmware"
@@ -85,6 +84,8 @@ class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity):
(dr.CONNECTION_NETWORK_MAC, entry_data.device_info.mac_address)
}
)
if coordinator.supports_update:
self._attr_supported_features = UpdateEntityFeature.INSTALL
@property
def _device_info(self) -> ESPHomeDeviceInfo:
@@ -115,6 +116,23 @@ class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity):
"""URL to the full release notes of the latest version available."""
return "https://esphome.io/changelog/"
async def async_added_to_hass(self) -> None:
"""Handle entity added to Home Assistant."""
await super().async_added_to_hass()
@callback
def _static_info_updated(infos: list[EntityInfo]) -> None:
"""Handle static info update."""
self.async_write_ha_state()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
self._entry_data.signal_static_info_updated,
_static_info_updated,
)
)
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
-3
View File
@@ -66,7 +66,6 @@ async def async_setup_entry(
camera_entities = []
for camera, value in coordinator.data.items():
camera_rtsp_entry = [
item
for item in hass.config_entries.async_entries(DOMAIN)
@@ -81,7 +80,6 @@ async def async_setup_entry(
)
if camera_rtsp_entry:
ffmpeg_arguments = camera_rtsp_entry[0].options[CONF_FFMPEG_ARGUMENTS]
camera_username = camera_rtsp_entry[0].data[CONF_USERNAME]
camera_password = camera_rtsp_entry[0].data[CONF_PASSWORD]
@@ -96,7 +94,6 @@ async def async_setup_entry(
)
else:
discovery_flow.async_create_flow(
hass,
DOMAIN,
@@ -182,7 +182,6 @@ class EzvizConfigFlow(ConfigFlow, domain=DOMAIN):
errors = {}
if user_input is not None:
if user_input[CONF_URL] == CONF_CUSTOMIZE:
self.context["data"] = {
CONF_USERNAME: user_input[CONF_USERNAME],
@@ -25,7 +25,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors = {}
if user_input is not None:
await self.async_set_unique_id(user_input[CONF_ID])
self._abort_if_unique_id_configured()
@@ -56,7 +56,6 @@ SENSOR_TYPES = (
icon=ICON,
name="Last Updated",
device_class=SensorDeviceClass.TIMESTAMP,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
@@ -507,7 +507,6 @@ class RangeFilter(Filter, SensorEntity):
new_state_value = cast(float, new_state.state)
if self._upper_bound is not None and new_state_value > self._upper_bound:
self._stats_internal["erasures_up"] += 1
_LOGGER.debug(
@@ -519,7 +518,6 @@ class RangeFilter(Filter, SensorEntity):
new_state.state = self._upper_bound
elif self._lower_bound is not None and new_state_value < self._lower_bound:
self._stats_internal["erasures_low"] += 1
_LOGGER.debug(
@@ -564,7 +562,6 @@ class OutlierFilter(Filter, SensorEntity):
len(self.states) == self.states.maxlen
and abs(new_state_value - median) > self._radius
):
self._stats_internal["erasures"] += 1
_LOGGER.debug(
-1
View File
@@ -208,7 +208,6 @@ class FinTsClient:
holdings_accounts = []
for account in self.client.get_sepa_accounts():
if self.is_balance_account(account):
balance_accounts.append(account)
+1 -1
View File
@@ -213,7 +213,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
_LOGGER.debug("Closing Firmata board %s", config_entry.data[CONF_NAME])
unload_entries = []
for (conf, platform) in CONF_PLATFORM_MAP.items():
for conf, platform in CONF_PLATFORM_MAP.items():
if conf in config_entry.data:
unload_entries.append(
hass.config_entries.async_forward_entry_unload(config_entry, platform)
@@ -86,7 +86,6 @@ class FleetGoDeviceScanner:
for device in devices:
if not self._include or device.license_plate in self._include:
if device.active or device.current_address is None:
device.get_map_details()
+1 -1
View File
@@ -74,7 +74,7 @@ class FliprDataUpdateCoordinator(DataUpdateCoordinator):
data = await self.hass.async_add_executor_job(
self.client.get_pool_measure_latest, self.flipr_id
)
except (FliprError) as error:
except FliprError as error:
raise UpdateFailed(error) from error
return data
+1 -1
View File
@@ -43,7 +43,7 @@ class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator):
await self.send_presence_ping()
await self._update_device()
await self._update_consumption_data()
except (RequestError) as error:
except RequestError as error:
raise UpdateFailed(error) from error
@property
-1
View File
@@ -97,7 +97,6 @@ async def async_setup_entry(
]
flume_entity_list = []
for device in flume_devices:
device_id = device[KEY_DEVICE_ID]
device_timezone = device[KEY_DEVICE_LOCATION][KEY_DEVICE_LOCATION_TIMEZONE]
device_location_name = device[KEY_DEVICE_LOCATION][KEY_DEVICE_LOCATION_NAME]
-2
View File
@@ -102,7 +102,6 @@ async def _async_port_entities_list(
_LOGGER.debug("IP source for %s is %s", avm_wrapper.host, local_ip)
for i in range(port_forwards_count):
portmap = await avm_wrapper.async_get_port_mapping(
avm_wrapper.device_conn_type, i
)
@@ -406,7 +405,6 @@ class FritzBoxPortSwitch(FritzBoxBaseSwitch, SwitchEntity):
self._attributes[attr] = self.port_mapping[key]
async def _async_switch_on_off_executor(self, turn_on: bool) -> bool:
if self.port_mapping is None:
return False
@@ -2,7 +2,7 @@
"domain": "frontend",
"name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": ["home-assistant-frontend==20230130.0"],
"requirements": ["home-assistant-frontend==20230202.0"],
"dependencies": [
"api",
"auth",
@@ -262,7 +262,6 @@ async def async_setup_entry(
if entity_id := ent_reg.async_get_entity_id(
Platform.SENSOR, DOMAIN, old_unique_id
):
ent_reg.async_update_entity(
entity_id, new_unique_id=f"{config_entry.entry_id}-{new_key}"
)
@@ -289,7 +289,6 @@ class AbstractConfig(ABC):
return
for user_agent_id, _ in self._store.agent_user_ids.items():
if (webhook_id := self.get_local_webhook_id(user_agent_id)) is None:
setup_successful = False
break
+19 -7
View File
@@ -137,7 +137,7 @@ async def async_setup_entry(
def calc_min(
sensor_values: list[tuple[str, float, State]]
) -> tuple[dict[str, str | None], float]:
) -> tuple[dict[str, str | None], float | None]:
"""Calculate min value."""
val: float | None = None
entity_id: str | None = None
@@ -153,7 +153,7 @@ def calc_min(
def calc_max(
sensor_values: list[tuple[str, float, State]]
) -> tuple[dict[str, str | None], float]:
) -> tuple[dict[str, str | None], float | None]:
"""Calculate max value."""
val: float | None = None
entity_id: str | None = None
@@ -169,7 +169,7 @@ def calc_max(
def calc_mean(
sensor_values: list[tuple[str, float, State]]
) -> tuple[dict[str, str | None], float]:
) -> tuple[dict[str, str | None], float | None]:
"""Calculate mean value."""
result = (sensor_value for _, sensor_value, _ in sensor_values)
@@ -179,7 +179,7 @@ def calc_mean(
def calc_median(
sensor_values: list[tuple[str, float, State]]
) -> tuple[dict[str, str | None], float]:
) -> tuple[dict[str, str | None], float | None]:
"""Calculate median value."""
result = (sensor_value for _, sensor_value, _ in sensor_values)
@@ -189,10 +189,11 @@ def calc_median(
def calc_last(
sensor_values: list[tuple[str, float, State]]
) -> tuple[dict[str, str | None], float]:
) -> tuple[dict[str, str | None], float | None]:
"""Calculate last value."""
last_updated: datetime | None = None
last_entity_id: str | None = None
last: float | None = None
for entity_id, state_f, state in sensor_values:
if last_updated is None or state.last_updated > last_updated:
last_updated = state.last_updated
@@ -227,7 +228,9 @@ def calc_sum(
CALC_TYPES: dict[
str,
Callable[[list[tuple[str, float, State]]], tuple[dict[str, str | None], float]],
Callable[
[list[tuple[str, float, State]]], tuple[dict[str, str | None], float | None]
],
] = {
"min": calc_min,
"max": calc_max,
@@ -244,7 +247,6 @@ class SensorGroup(GroupEntity, SensorEntity):
_attr_available = False
_attr_should_poll = False
_attr_icon = "mdi:calculator"
def __init__(
self,
@@ -352,6 +354,16 @@ class SensorGroup(GroupEntity, SensorEntity):
return self._attr_device_class
return self.calc_device_class
@property
def icon(self) -> str | None:
"""Return the icon.
Only override the icon if the device class is not set.
"""
if not self.device_class:
return "mdi:calculator"
return None
@property
def state_class(self) -> SensorStateClass | str | None:
"""Return state class."""
+1 -2
View File
@@ -68,7 +68,6 @@
},
"sensor": {
"title": "[%key:component::group::config::step::user::title%]",
"description": "If \"ignore non-numeric\" is enabled, the group's state is calculated if at least one member has a numerical value. If \"ignore non-numeric\" is disabled, the group's state is calculated only if all group members have numerical values.",
"data": {
"ignore_non_numeric": "Ignore non-numeric",
"entities": "Members",
@@ -134,7 +133,7 @@
}
},
"sensor": {
"description": "[%key:component::group::config::step::sensor::description%]",
"description": "If \"ignore non-numeric\" is enabled, the group's state is calculated if at least one member has a numerical value. If \"ignore non-numeric\" is disabled, the group's state is calculated only if all group members have numerical values.",
"data": {
"ignore_non_numeric": "[%key:component::group::config::step::sensor::data::ignore_non_numeric%]",
"entities": "[%key:component::group::config::step::sensor::data::entities%]",
@@ -63,7 +63,6 @@
"type": "Type",
"unit_of_measurement": "Unit of Measurement"
},
"description": "If \"ignore non-numeric\" is enabled, the group's state is calculated if at least one member has a numerical value. If \"ignore non-numeric\" is disabled, the group's state is calculated only if all group members have numerical values.",
"title": "Add Group"
},
"switch": {
@@ -64,7 +64,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors = {}
if user_input is not None:
try:
validated = await validate_input(user_input)
except CannotConnect:
@@ -188,8 +188,14 @@ class Thermostat(HomeAccessory):
(CHAR_COOLING_THRESHOLD_TEMPERATURE, CHAR_HEATING_THRESHOLD_TEMPERATURE)
)
if (
ATTR_CURRENT_HUMIDITY in attributes
or features & ClimateEntityFeature.TARGET_HUMIDITY
):
self.chars.append(CHAR_CURRENT_HUMIDITY)
if features & ClimateEntityFeature.TARGET_HUMIDITY:
self.chars.extend((CHAR_TARGET_HUMIDITY, CHAR_CURRENT_HUMIDITY))
self.chars.append(CHAR_TARGET_HUMIDITY)
serv_thermostat = self.add_preload_service(SERV_THERMOSTAT, self.chars)
self.set_primary_service(serv_thermostat)
@@ -253,7 +259,6 @@ class Thermostat(HomeAccessory):
properties={PROP_MIN_VALUE: hc_min_temp, PROP_MAX_VALUE: hc_max_temp},
)
self.char_target_humidity = None
self.char_current_humidity = None
if CHAR_TARGET_HUMIDITY in self.chars:
self.char_target_humidity = serv_thermostat.configure_char(
CHAR_TARGET_HUMIDITY,
@@ -265,6 +270,8 @@ class Thermostat(HomeAccessory):
# of 0-80%
properties={PROP_MIN_VALUE: min_humidity},
)
self.char_current_humidity = None
if CHAR_CURRENT_HUMIDITY in self.chars:
self.char_current_humidity = serv_thermostat.configure_char(
CHAR_CURRENT_HUMIDITY, value=50
)
@@ -3,7 +3,7 @@
"name": "HomeKit Controller",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"requirements": ["aiohomekit==2.4.4"],
"requirements": ["aiohomekit==2.4.6"],
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."],
"bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }],
"dependencies": ["bluetooth_adapters", "zeroconf"],
@@ -316,7 +316,12 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity):
@property
def _first_radiator_thermostat(
self,
) -> AsyncHeatingThermostat | AsyncHeatingThermostatCompact | AsyncHeatingThermostatEvo | None:
) -> (
AsyncHeatingThermostat
| AsyncHeatingThermostatCompact
| AsyncHeatingThermostatEvo
| None
):
"""Return the first radiator thermostat from the hmip heating group."""
for device in self._device.devices:
if isinstance(
+9 -12
View File
@@ -2,12 +2,12 @@
import asyncio
from dataclasses import dataclass
import AIOSomecomfort
import aiosomecomfort
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import (
@@ -50,22 +50,19 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
username = config_entry.data[CONF_USERNAME]
password = config_entry.data[CONF_PASSWORD]
client = AIOSomecomfort.AIOSomeComfort(
client = aiosomecomfort.AIOSomeComfort(
username, password, session=async_get_clientsession(hass)
)
try:
await client.login()
await client.discover()
except AIOSomecomfort.AuthError as ex:
raise ConfigEntryNotReady(
"Failed to initialize the Honeywell client: "
"Check your configuration (username, password), "
) from ex
except aiosomecomfort.device.AuthError as ex:
raise ConfigEntryAuthFailed("Incorrect Password") from ex
except (
AIOSomecomfort.ConnectionError,
AIOSomecomfort.ConnectionTimeout,
aiosomecomfort.device.ConnectionError,
aiosomecomfort.device.ConnectionTimeout,
asyncio.TimeoutError,
) as ex:
raise ConfigEntryNotReady(
@@ -117,5 +114,5 @@ class HoneywellData:
"""Shared data for Honeywell."""
entry_id: str
client: AIOSomecomfort.AIOSomeComfort
devices: dict[str, AIOSomecomfort.device.Device]
client: aiosomecomfort.AIOSomeComfort
devices: dict[str, aiosomecomfort.device.Device]
+22 -21
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
import datetime
from typing import Any
import AIOSomecomfort
import aiosomecomfort
from homeassistant.components.climate import (
ATTR_TARGET_TEMP_HIGH,
@@ -100,7 +100,7 @@ class HoneywellUSThermostat(ClimateEntity):
def __init__(
self,
data: HoneywellData,
device: AIOSomecomfort.device.Device,
device: aiosomecomfort.device.Device,
cool_away_temp: int | None,
heat_away_temp: int | None,
) -> None:
@@ -274,10 +274,10 @@ class HoneywellUSThermostat(ClimateEntity):
if self._device.hold_heat is False and self._device.hold_cool is False:
# Get next period time
hour_heat, minute_heat = divmod(
self._device.raw_ui_data["HEATNextPeriod"] * 15, 60
self._device.raw_ui_data["HeatNextPeriod"] * 15, 60
)
hour_cool, minute_cool = divmod(
self._device.raw_ui_data["COOLNextPeriod"] * 15, 60
self._device.raw_ui_data["CoolNextPeriod"] * 15, 60
)
# Set hold time
if mode in COOLING_MODES:
@@ -290,23 +290,26 @@ class HoneywellUSThermostat(ClimateEntity):
)
# Set temperature if not in auto
elif mode == "cool":
if mode == "cool":
await self._device.set_setpoint_cool(temperature)
elif mode == "heat":
if mode == "heat":
await self._device.set_setpoint_heat(temperature)
elif mode == "auto":
if temperature := kwargs.get(ATTR_TARGET_TEMP_HIGH):
await self._device.set_setpoint_cool(temperature)
if temperature := kwargs.get(ATTR_TARGET_TEMP_LOW):
await self._device.set_setpoint_heat(temperature)
except AIOSomecomfort.SomeComfortError as err:
except aiosomecomfort.SomeComfortError as err:
_LOGGER.error("Invalid temperature %.1f: %s", temperature, err)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if {HVACMode.COOL, HVACMode.HEAT} & set(self._hvac_mode_map):
await self._set_temperature(**kwargs)
try:
if temperature := kwargs.get(ATTR_TARGET_TEMP_HIGH):
await self._device.set_setpoint_cool(temperature)
if temperature := kwargs.get(ATTR_TARGET_TEMP_LOW):
await self._device.set_setpoint_heat(temperature)
except aiosomecomfort.SomeComfortError as err:
_LOGGER.error("Invalid temperature %.1f: %s", temperature, err)
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new target fan mode."""
@@ -327,11 +330,10 @@ class HoneywellUSThermostat(ClimateEntity):
try:
# Get current mode
mode = self._device.system_mode
except AIOSomecomfort.SomeComfortError:
except aiosomecomfort.SomeComfortError:
_LOGGER.error("Can not get system mode")
return
try:
# Set permanent hold
# and Set temperature
if mode in COOLING_MODES:
@@ -341,8 +343,7 @@ class HoneywellUSThermostat(ClimateEntity):
await self._device.set_hold_heat(True)
await self._device.set_setpoint_heat(self._heat_away_temp)
except AIOSomecomfort.SomeComfortError:
except aiosomecomfort.SomeComfortError:
_LOGGER.error(
"Temperature out of range. Mode: %s, Heat Temperature: %.1f, Cool Temperature: %.1f",
mode,
@@ -355,7 +356,7 @@ class HoneywellUSThermostat(ClimateEntity):
try:
# Get current mode
mode = self._device.system_mode
except AIOSomecomfort.SomeComfortError:
except aiosomecomfort.SomeComfortError:
_LOGGER.error("Can not get system mode")
return
# Check that we got a valid mode back
@@ -367,7 +368,7 @@ class HoneywellUSThermostat(ClimateEntity):
if mode in HEATING_MODES:
await self._device.set_hold_heat(True)
except AIOSomecomfort.SomeComfortError:
except aiosomecomfort.SomeComfortError:
_LOGGER.error("Couldn't set permanent hold")
else:
_LOGGER.error("Invalid system mode returned: %s", mode)
@@ -379,7 +380,7 @@ class HoneywellUSThermostat(ClimateEntity):
# Disabling all hold modes
await self._device.set_hold_cool(False)
await self._device.set_hold_heat(False)
except AIOSomecomfort.SomeComfortError:
except aiosomecomfort.SomeComfortError:
_LOGGER.error("Can not stop hold mode")
async def async_set_preset_mode(self, preset_mode: str) -> None:
@@ -408,13 +409,13 @@ class HoneywellUSThermostat(ClimateEntity):
try:
await self._device.refresh()
except (
AIOSomecomfort.SomeComfortError,
aiosomecomfort.SomeComfortError,
OSError,
):
try:
await self._data.client.login()
except AIOSomecomfort.SomeComfortError:
except aiosomecomfort.SomeComfortError:
self._attr_available = False
await self.hass.async_create_task(
self.hass.config_entries.async_reload(self._data.entry_id)
@@ -2,8 +2,10 @@
from __future__ import annotations
import asyncio
from collections.abc import Mapping
from typing import Any
import AIOSomecomfort
import aiosomecomfort
import voluptuous as vol
from homeassistant import config_entries
@@ -20,11 +22,66 @@ from .const import (
DOMAIN,
)
REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a honeywell config flow."""
VERSION = 1
entry: config_entries.ConfigEntry | None
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
"""Handle re-authentication with Honeywell."""
self.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: dict[str, Any] | None = None
) -> FlowResult:
"""Confirm re-authentication with Honeywell."""
errors: dict[str, str] = {}
if user_input:
assert self.entry is not None
password = user_input[CONF_PASSWORD]
data = {
CONF_USERNAME: self.entry.data[CONF_USERNAME],
CONF_PASSWORD: password,
}
try:
await self.is_valid(
username=data[CONF_USERNAME], password=data[CONF_PASSWORD]
)
except aiosomecomfort.AuthError:
errors["base"] = "invalid_auth"
except (
aiosomecomfort.ConnectionError,
aiosomecomfort.ConnectionTimeout,
asyncio.TimeoutError,
):
errors["base"] = "cannot_connect"
else:
self.hass.config_entries.async_update_entry(
self.entry,
data={
**self.entry.data,
CONF_PASSWORD: password,
},
)
await self.hass.config_entries.async_reload(self.entry.entry_id)
return self.async_abort(reason="reauth_successful")
return self.async_show_form(
step_id="reauth_confirm",
data_schema=REAUTH_SCHEMA,
errors=errors,
)
async def async_step_user(self, user_input=None) -> FlowResult:
"""Create config entry. Show the setup form to the user."""
@@ -32,11 +89,11 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is not None:
try:
await self.is_valid(**user_input)
except AIOSomecomfort.AuthError:
except aiosomecomfort.AuthError:
errors["base"] = "invalid_auth"
except (
AIOSomecomfort.ConnectionError,
AIOSomecomfort.ConnectionTimeout,
aiosomecomfort.ConnectionError,
aiosomecomfort.ConnectionTimeout,
asyncio.TimeoutError,
):
errors["base"] = "cannot_connect"
@@ -57,7 +114,7 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def is_valid(self, **kwargs) -> bool:
"""Check if login credentials are valid."""
client = AIOSomecomfort.AIOSomeComfort(
client = aiosomecomfort.AIOSomeComfort(
kwargs[CONF_USERNAME],
kwargs[CONF_PASSWORD],
session=async_get_clientsession(self.hass),
@@ -3,7 +3,7 @@
"name": "Honeywell Total Connect Comfort (US)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/honeywell",
"requirements": ["aiosomecomfort==0.0.3"],
"requirements": ["aiosomecomfort==0.0.6"],
"codeowners": ["@rdfurman", "@mkmer"],
"iot_class": "cloud_polling",
"loggers": ["somecomfort"]
+1 -1
View File
@@ -5,7 +5,7 @@ from collections.abc import Callable
from dataclasses import dataclass
from typing import Any
from AIOSomecomfort.device import Device
from aiosomecomfort.device import Device
from homeassistant.components.sensor import (
SensorDeviceClass,
+11 -2
View File
@@ -20,7 +20,11 @@ import voluptuous as vol
from homeassistant import exceptions
from homeassistant.const import CONTENT_TYPE_JSON
from homeassistant.core import Context, is_callback
from homeassistant.helpers.json import JSON_ENCODE_EXCEPTIONS, json_bytes
from homeassistant.helpers.json import JSON_ENCODE_EXCEPTIONS, json_bytes, json_dumps
from homeassistant.util.json import (
find_paths_unserializable_data,
format_unserializable_data,
)
from .const import KEY_AUTHENTICATED, KEY_HASS
@@ -54,7 +58,12 @@ class HomeAssistantView:
try:
msg = json_bytes(result)
except JSON_ENCODE_EXCEPTIONS as err:
_LOGGER.error("Unable to serialize to JSON: %s\n%s", err, result)
_LOGGER.error(
"Unable to serialize to JSON. Bad data found at %s",
format_unserializable_data(
find_paths_unserializable_data(result, dump=json_dumps)
),
)
raise HTTPInternalServerError from err
response = web.Response(
body=msg,
@@ -92,7 +92,6 @@ async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> N
# initialize bridge connection just for the migration
async with HueBridgeV2(host, api_key) as api:
sensor_class_mapping = {
SensorDeviceClass.BATTERY.value: ResourceTypes.DEVICE_POWER,
BinarySensorDeviceClass.MOTION.value: ResourceTypes.MOTION,
@@ -130,7 +129,6 @@ async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> N
# loop through all entities for device and find match
for ent in async_entries_for_device(ent_reg, hass_dev_id, True):
if ent.entity_id.startswith("light"):
# migrate light
# should always return one lightid here
@@ -48,7 +48,6 @@ async def async_setup_entry(
for partial_station in station_information.get("partialStations", []):
for elevator in partial_station.get("elevators", []):
state = elevator.get("state") != "READY"
available = elevator.get("state") != "UNKNOWN"
label = elevator.get("label")
@@ -81,7 +81,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_station(self, user_input=None):
"""Handle the step where the user inputs his/her station."""
if user_input is not None:
errors = {}
check_name = await self.hub.gti.checkName(
@@ -145,7 +144,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
"""Manage the options."""
errors = {}
if not self.departure_filters:
departure_list = {}
hub: GTIHub = self.hass.data[DOMAIN][self.config_entry.entry_id]
@@ -172,7 +170,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
}
if user_input is not None and not errors:
options = {
CONF_FILTER: [
self.departure_filters[x] for x in user_input[CONF_FILTER]
@@ -259,7 +259,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
for device_entry in dr.async_entries_for_config_entry(
device_registry, entry.entry_id
):
for (kind, key) in device_entry.identifiers:
for kind, key in device_entry.identifiers:
if kind == DOMAIN and key in known_devices:
break
else:
@@ -7,7 +7,7 @@ from functools import wraps
import logging
from typing import Any, Concatenate, ParamSpec, TypeVar
import aiohttp.client_exceptions
import httpx
from iaqualink.client import AqualinkClient
from iaqualink.device import (
AqualinkBinarySensor,
@@ -77,10 +77,7 @@ async def async_setup_entry( # noqa: C901
_LOGGER.error("Failed to login: %s", login_exception)
await aqualink.close()
return False
except (
asyncio.TimeoutError,
aiohttp.client_exceptions.ClientConnectorError,
) as aio_exception:
except (asyncio.TimeoutError, httpx.HTTPError) as aio_exception:
await aqualink.close()
raise ConfigEntryNotReady(
f"Error while attempting login: {aio_exception}"
@@ -149,7 +146,7 @@ async def async_setup_entry( # noqa: C901
try:
await system.update()
except AqualinkServiceException as svc_exception:
except (AqualinkServiceException, httpx.HTTPError) as svc_exception:
if prev is not None:
_LOGGER.warning(
"Failed to refresh system %s state: %s",
@@ -3,6 +3,7 @@ from __future__ import annotations
from typing import Any
import httpx
from iaqualink.client import AqualinkClient
from iaqualink.exception import (
AqualinkServiceException,
@@ -42,7 +43,7 @@ class AqualinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
pass
except AqualinkServiceUnauthorizedException:
errors["base"] = "invalid_auth"
except AqualinkServiceException:
except (AqualinkServiceException, httpx.HTTPError):
errors["base"] = "cannot_connect"
else:
return self.async_create_entry(title=username, data=user_input)
@@ -77,7 +77,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
target_keys[target] = api_keys[target]
try:
for target, key in target_keys.items():
res = pyfttt.send_event(key, event, value1, value2, value3)
if res.status_code != HTTPStatus.OK:
@@ -10,7 +10,7 @@
{ "local_name": "xBBQ*", "connectable": false },
{ "local_name": "tps", "connectable": false }
],
"requirements": ["inkbird-ble==0.5.5"],
"requirements": ["inkbird-ble==0.5.6"],
"dependencies": ["bluetooth_adapters"],
"codeowners": ["@bdraco"],
"iot_class": "local_push"
@@ -119,7 +119,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
)
if user_input is not None:
control_schema = vol.Schema(
{
vol.Required(
@@ -33,7 +33,6 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
self._api = api
async def _async_update_data(self) -> IntellifirePollData:
if not self._api.is_polling_in_background:
LOGGER.info("Starting Intellifire Background Polling Loop")
await self._api.start_background_polling()
+1 -1
View File
@@ -3,7 +3,7 @@
"name": "Instituto Portugu\u00eas do Mar e Atmosfera (IPMA)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/ipma",
"requirements": ["pyipma==3.0.5"],
"requirements": ["pyipma==3.0.6"],
"codeowners": ["@dgomes", "@abmantis"],
"iot_class": "cloud_polling",
"loggers": ["geopy", "pyipma"]
+5 -3
View File
@@ -7,6 +7,7 @@ from urllib.parse import urlparse
from aiohttp import CookieJar
import async_timeout
from pyisy import ISY, ISYConnectionError, ISYInvalidAuthError, ISYResponseParseError
from pyisy.constants import CONFIG_NETWORKING, CONFIG_PORTAL
import voluptuous as vol
from homeassistant import config_entries
@@ -43,7 +44,6 @@ from .const import (
ISY_CONF_FIRMWARE,
ISY_CONF_MODEL,
ISY_CONF_NAME,
ISY_CONF_NETWORKING,
MANUFACTURER,
PLATFORMS,
SCHEME_HTTP,
@@ -220,9 +220,11 @@ async def async_setup_entry(
numbers = isy_data.variables[Platform.NUMBER]
for vtype, _, vid in isy.variables.children:
numbers.append(isy.variables[vtype][vid])
if isy.conf[ISY_CONF_NETWORKING]:
if (
isy.conf[CONFIG_NETWORKING] or isy.conf[CONFIG_PORTAL]
) and isy.networking.nobjs:
isy_data.devices[CONF_NETWORK] = _create_service_device_info(
isy, name=ISY_CONF_NETWORKING, unique_id=CONF_NETWORK
isy, name=CONFIG_NETWORKING, unique_id=CONF_NETWORK
)
for resource in isy.networking.nobjs:
isy_data.net_resources.append(resource)
@@ -95,7 +95,7 @@ async def async_setup_entry(
entities_by_address[node.address] = entity
# Handle some special child node cases for Insteon Devices
for (node, device_class, device_type, device_info) in child_nodes:
for node, device_class, device_type, device_info in child_nodes:
subnode_id = int(node.address.split(" ")[-1], 16)
# Handle Insteon Thermostats
if device_type is not None and device_type.startswith(TYPE_CATEGORY_CLIMATE):
+1 -2
View File
@@ -118,7 +118,6 @@ SUPPORTED_BIN_SENS_CLASSES = ["moisture", "opening", "motion", "climate"]
# (they can turn off, and report their state)
ISY_GROUP_PLATFORM = Platform.SWITCH
ISY_CONF_NETWORKING = "Networking Module"
ISY_CONF_UUID = "uuid"
ISY_CONF_NAME = "name"
ISY_CONF_MODEL = "model"
@@ -255,7 +254,7 @@ NODE_FILTERS: dict[Platform, dict[str, list[str]]] = {
FILTER_STATES: ["open", "closed", "closing", "opening", "stopped"],
FILTER_NODE_DEF_ID: ["DimmerMotorSwitch_ADV"],
FILTER_INSTEON_TYPE: [TYPE_CATEGORY_COVER],
FILTER_ZWAVE_CAT: [],
FILTER_ZWAVE_CAT: ["106", "107"],
},
Platform.LIGHT: {
FILTER_UOM: ["51"],
@@ -109,7 +109,6 @@ def _check_for_insteon_type(
device_type.startswith(t)
for t in set(NODE_FILTERS[platform][FILTER_INSTEON_TYPE])
):
# Hacky special-cases for certain devices with different platforms
# included as subnodes. Note that special-cases are not necessary
# on ISY 5.x firmware as it uses the superior NodeDefs method
@@ -3,7 +3,7 @@
"name": "Universal Devices ISY/IoX",
"integration_type": "hub",
"documentation": "https://www.home-assistant.io/integrations/isy994",
"requirements": ["pyisy==3.1.11"],
"requirements": ["pyisy==3.1.13"],
"codeowners": ["@bdraco", "@shbatm"],
"config_flow": true,
"ssdp": [
+2 -2
View File
@@ -23,7 +23,7 @@ import homeassistant.helpers.entity_registry as er
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.service import entity_service_call
from .const import _LOGGER, CONF_NETWORK, DOMAIN, ISY_CONF_NAME, ISY_CONF_NETWORKING
from .const import _LOGGER, CONF_NETWORK, DOMAIN, ISY_CONF_NAME
from .util import _async_cleanup_registry_entries
# Common Services for All Platforms:
@@ -233,7 +233,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
isy = isy_data.root
if isy_name and isy_name != isy.conf[ISY_CONF_NAME]:
continue
if isy.networking is None or not isy.conf[ISY_CONF_NETWORKING]:
if isy.networking is None:
continue
command = None
if address:
+3 -2
View File
@@ -173,6 +173,7 @@ class ControllerDevice(ClimateEntity):
async def async_added_to_hass(self) -> None:
"""Call on adding to hass."""
# Register for connect/disconnect/update events
@callback
def controller_disconnected(ctrl: Controller, ex: Exception) -> None:
@@ -290,7 +291,7 @@ class ControllerDevice(ClimateEntity):
return HVACMode.OFF
if (mode := self._controller.mode) == Controller.Mode.FREE_AIR:
return HVACMode.FAN_ONLY
for (key, value) in self._state_to_pizone.items():
for key, value in self._state_to_pizone.items():
if value == mode:
return key
assert False, "Should be unreachable"
@@ -527,7 +528,7 @@ class ZoneDevice(ClimateEntity):
def hvac_mode(self) -> HVACMode | None:
"""Return current operation ie. heat, cool, idle."""
mode = self._zone.mode
for (key, value) in self._state_to_pizone.items():
for key, value in self._state_to_pizone.items():
if value == mode:
return key
return None
@@ -17,7 +17,6 @@ _LOGGER = logging.getLogger(__name__)
async def _async_has_devices(hass: HomeAssistant) -> bool:
controller_ready = asyncio.Event()
@callback
@@ -46,7 +46,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the initial step."""
errors = {}
if user_input is not None:
await self.async_set_unique_id(user_input[CONF_ACCESS_TOKEN])
self._abort_if_unique_id_configured()
@@ -17,7 +17,7 @@ class DemoFixFlow(RepairsFlow):
) -> data_entry_flow.FlowResult:
"""Handle the first step of a fix flow."""
return await (self.async_step_confirm())
return await self.async_step_confirm()
async def async_step_confirm(
self, user_input: dict[str, str] | None = None

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