Compare commits

...

1105 Commits

Author SHA1 Message Date
Bram Kragten
1f0b3c4e33 Bump version to 2023.9.0b2 2023-09-01 18:01:07 +02:00
Bram Kragten
528e8c0fe7 Update frontend to 20230901.0 (#99464) 2023-09-01 17:58:15 +02:00
Erik Montnemery
eb8d375e35 Fix template helper strings (#99456) 2023-09-01 17:58:15 +02:00
Keilin Bickar
987a959b19 Update asynsleepiq library to 1.3.7 (#99431) 2023-09-01 17:58:14 +02:00
Paul Bottein
469a72a5f9 Use common key for away mode state translations (#99425) 2023-09-01 17:58:13 +02:00
Álvaro Fernández Rojas
a95691f306 Update AEMET-OpenData to v0.4.4 (#99418)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-09-01 17:58:12 +02:00
J. Nick Koston
ebf42ad342 Significantly reduce overhead to filter event triggers (#99376)
* fast

* cleanups

* cleanups

* cleanups

* comment

* comment

* add more cover

* comment

* pull more examples from forums to validate cover
2023-09-01 17:58:11 +02:00
Michael
057daa5fdb Address late review for Nextcloud (#99226) 2023-09-01 17:58:10 +02:00
puddly
2ec9abfd24 Create a ZHA repair when directly accessing a radio with multi-PAN firmware (#98275)
* Add the SiLabs flasher as a dependency

* Create a repair if the wrong firmware is detected on an EZSP device

* Update homeassistant/components/zha/strings.json

Co-authored-by: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com>

* Provide the ZHA config entry as a reusable fixture

* Create a separate repair when using non-Nabu Casa hardware

* Add unit tests

* Drop extraneous `config_entry.add_to_hass` added in 021def44

* Fully unit test all edge cases

* Move `socket://`-ignoring logic into repair function

* Open a repair from ZHA flows when the wrong firmware is running

* Fix existing unit tests

* Link to the flashing section in the documentation

* Reduce repair severity to `ERROR`

* Make issue persistent

* Add unit tests for new radio probing states

* Add unit tests for new config flow steps

* Handle probing failure raising an exception

* Implement review suggestions

* Address review comments

---------

Co-authored-by: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com>
2023-09-01 17:58:09 +02:00
puddly
0bae0824b4 Initialize ZHA device database before connecting to the radio (#98082)
* Create ZHA entities before attempting to connect to the coordinator

* Delete the ZHA gateway object when unloading the config entry

* Only load ZHA groups if the coordinator device info is known offline

* Do not create a coordinator ZHA device until it is ready

* [WIP] begin fixing unit tests

* [WIP] Fix existing unit tests (one failure left)

* Fix remaining unit test
2023-09-01 17:58:08 +02:00
Bram Kragten
8284c288bf Bump version to 2023.9.0b1 2023-08-31 17:19:10 +02:00
Joakim Sørensen
a603a99bd6 Add remote alias to connection info response (#99410) 2023-08-31 17:17:51 +02:00
Bram Kragten
9836d17c92 Update frontend to 20230831.0 (#99405) 2023-08-31 17:17:50 +02:00
Paul Bottein
db8980246b Add entity component translation for water heater away mode attribute (#99394) 2023-08-31 17:17:49 +02:00
G Johansson
d1c154fc0d Revert "Sonos add yaml config issue" (#99379)
Revert "Sonos add yaml config issue (#97365)"

This reverts commit 2299430dbe.
2023-08-31 17:17:48 +02:00
Erik Montnemery
97b0815122 Add documentation URL for homeassistant_sky_connect (#99377) 2023-08-31 17:17:47 +02:00
J. Nick Koston
a0d03d6bb1 Revert orjson to 3.9.2 (#99374)
* Revert "Update orjson to 3.9.4 (#98108)"

This reverts commit 3dd377cb2a.

* Revert "Update orjson to 3.9.3 (#97930)"

This reverts commit d993aa59ea.
2023-08-31 17:17:46 +02:00
Erik Montnemery
eb423c39b6 Improve template sensor config flow validation (#99373) 2023-08-31 17:17:45 +02:00
Austin Brunkhorst
316f89bead Update pysnooz to 0.8.6 (#99368) 2023-08-31 17:17:44 +02:00
puddly
3066d70809 Bump ZHA dependencies (#99341)
* Bump ZHA dependencies

* Include bellows as well
2023-08-31 17:17:43 +02:00
Erik Montnemery
52f8dbf25b Add documentation URL for homeassistant_yellow (#99336)
* Add documentation URL for homeassistant_yellow

* Fix test

* Tweak
2023-08-31 17:17:42 +02:00
tronikos
794071449a Opower MFA fixes (#99317)
opower mfa fixes
2023-08-31 17:17:41 +02:00
Brett Adams
cb33d82c24 Patch service validation in Aussie Broadband (#99077)
* Bump pyAussieBB

* rolling back to previous version

* patching the pydantic 2.x issue in aussie_broadband integration

* adding test for validate_service_type

* adding test for validate_service_type

* fixing tests, again

* adding additional test

* doing fixes for live tests

* Implement Feedback

* Add test to detect pydantic2

* Update test_init.py

* Update docstring

---------

Co-authored-by: James Hodgkinson <james@terminaloutcomes.com>
2023-08-31 17:17:40 +02:00
Bram Kragten
92007fce56 2023.9.0b0 (#99347) 2023-08-30 18:21:27 +02:00
Bram Kragten
1018e82725 Bump version to 2023.9.0b0 2023-08-30 18:17:32 +02:00
Raman Gupta
867e9b73bb Add zwave_js device config file change fix/repair (#99314)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-30 17:29:22 +02:00
Erik Montnemery
501d5db375 Add config flow for template binary sensor (#99339) 2023-08-30 17:28:56 +02:00
Joost Lekkerkerker
a5dcc25aab Add snapshot assertion to Airzone (#98760)
Co-authored-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-30 17:11:55 +02:00
Bram Kragten
2ee55f5086 Update frontend to 20230830.0 (#99340) 2023-08-30 16:42:04 +02:00
Joost Lekkerkerker
549399cca6 Remove unneeded variable in Flo (#99322) 2023-08-30 16:37:59 +02:00
Erik Montnemery
f9b2e10f72 Add new board type (#99334) 2023-08-30 16:37:13 +02:00
Erik Montnemery
63c538b024 Add config flow for template sensor (#98970)
* Add config flow for template sensor

* Tweak error reporting

* Improve validation

* Fix test

* Rename translation strings

* Improve validation

* Fix sensor async_setup_entry

* Avoid duplicating sensor device class translations

* Avoid duplicating sensor device class translations

* Add config flow tests

* Include all units from DEVICE_CLASS_UNITS in unit_of_measurement select

* Address review comments
2023-08-30 16:22:52 +02:00
Mike Woudenberg
bc5f934f35 Correct loqed token URL to production server (#99316)
* Corrects token URL to production server

* Update homeassistant/components/loqed/strings.json

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-30 16:21:52 +02:00
Joost Lekkerkerker
a1b2b9a78c Add entity translations to Tellduslive (#98963) 2023-08-30 16:03:13 +02:00
Joost Lekkerkerker
fb6d19d08f Add pressure device class to Telldus live (#99337) 2023-08-30 15:53:30 +02:00
Joost Lekkerkerker
5f05d0d7e9 Map abode units to HA units (#99323) 2023-08-30 15:33:38 +02:00
Joost Lekkerkerker
e56db78b27 Use shorthand attributes for Freebox (#99327) 2023-08-30 15:33:05 +02:00
Joost Lekkerkerker
587928223a Use shorthand attributes in Gree (#99332) 2023-08-30 15:16:45 +02:00
Joost Lekkerkerker
66ad605d3e Use shorthand attribute in Google Travel Time (#99331) 2023-08-30 15:09:02 +02:00
Maikel Punie
7170f5d36c Bump pyduotecno to 2023.8.4 (#99328) 2023-08-30 14:26:58 +02:00
Álvaro Fernández Rojas
71a6db0c9d Update AEMET-OpenData to v0.4.3 (#99312) 2023-08-30 07:23:17 -05:00
Jan-Philipp Benecke
5c8e5a7af2 Split bsblan coordinator and randomize update interval (#99269)
* Split out bsblan coordinator and randomize update interval

* Use logger const

* Add randomising update interval for following updates

* Implement review comments

* Re-add config_entry

* Remove line
2023-08-30 13:14:30 +02:00
Raman Gupta
6e5f4566d5 Add zwave_js controller status sensor (#99252)
* Add zwave_js controller status sensor

* Also update network status command

* fix tests

* Remove WS command since we have a sensor entity

* Update sensor.py

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

* move driver assertion out of closures

* store state in tests

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-30 06:50:23 -04:00
Jan-Philipp Benecke
027ce55fa6 Use snapshot assertion for google assistant diagnostics test (#99167) 2023-08-30 12:25:06 +02:00
Jan-Philipp Benecke
fb42042402 Use snapshot assertion for nextdns diagnostics test (#99157) 2023-08-30 12:22:10 +02:00
Erik Montnemery
15de221c3e Trigger full CI run if assist_pipeline is modified (#99319) 2023-08-30 12:17:26 +02:00
Joost Lekkerkerker
775f815afc Use shorthand attributes for Ecobee (#99239)
* Use shorthand attributes for Ecobee

* Use shorthand attributes for Ecobee
2023-08-30 12:11:13 +02:00
Joost Lekkerkerker
a4818c5f54 Use shorthand attributes for Elmax (#99277) 2023-08-30 12:07:55 +02:00
Joost Lekkerkerker
7d70b42e4a Use shorthand attributes for EnOcean (#99278) 2023-08-30 11:57:56 +02:00
Joost Lekkerkerker
38267699e5 Use device info object in ezviz (#99280) 2023-08-30 11:52:06 +02:00
Joost Lekkerkerker
fae50169d9 Add typing to Blink config flow (#98873) 2023-08-30 11:50:47 +02:00
G Johansson
2ce5b08fc3 Deprecate timer start optional duration parameter (#93471)
* Deprecate timer start option duration parameter

* Add test

* Fix strings

* breaks_in_ha_version

* strings

* Mod string
2023-08-30 11:48:13 +02:00
Richard Kroegel
021b14fc17 Refactor & enhance BMW tests (#97895)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-08-30 11:45:09 +02:00
jan iversen
9ef3ec3dd3 Add modbus test for configuration errors (#98697) 2023-08-30 11:34:51 +02:00
Erik Montnemery
9e178ae2ce Fix assist_pipeline schema (#99318) 2023-08-30 11:33:41 +02:00
Raman Gupta
40748a6c34 Add zwave_js controller identify event (#99254) 2023-08-30 11:32:41 +02:00
Joost Lekkerkerker
56b99d2bc6 Add entity translations to QNAP QSW (#98915) 2023-08-30 11:20:15 +02:00
ollo69
a89a5f486d Migrate Melcloud to has entity name (#99025) 2023-08-30 10:53:52 +02:00
Michael
e7462e916a Conditional category for temperature sensor entities in AVM Fritz!Smarthome (#98981) 2023-08-30 10:29:35 +02:00
Joost Lekkerkerker
bd04cafb91 Use shorthand attributes for Daikin (#99225)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-08-30 10:25:53 +02:00
Michał Huryn
e911b73b61 Add extra sensors to Blebox (#90516) 2023-08-30 10:20:45 +02:00
Joost Lekkerkerker
56e5c34283 Add entity translations to Garages Amsterdam (#98584) 2023-08-30 09:46:28 +02:00
Erik Montnemery
7e7cb15d5b Revert "Allows defining list of attributes excluded from history in manifest.json" (#99300)
Revert "Allows defining list of attributes excluded from history in manifest.json (#99283)"

This reverts commit 0366e14630.
2023-08-30 08:26:26 +02:00
Daniel Hjelseth Høyer
fb4e93071e Update Mill lib, improve error handling (#99296) 2023-08-30 00:03:56 -04:00
Paulus Schoutsen
9e4bcd298e Move more Oral-B entities to be diagnostic (#99297) 2023-08-30 00:03:37 -04:00
Michal Jál
d7d989b9fb Switchbot nightlatch feature (#98620)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-30 00:03:08 -04:00
Sebastian Mayr
6e8b3837b0 Add support for MFA auth in opower (#97878)
* Add support for MFA auth in opower

* Make MFA an extra step

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-08-29 23:37:37 -04:00
rct
cc8f5ca827 Opower add new virtual integrations ConEd and ORU (#99230) 2023-08-29 23:35:19 -04:00
Willem-Jan van Rootselaar
1e37e1e355 Bump python-bsblan to 0.5.16 (#99238) 2023-08-29 23:23:20 -04:00
Michael Hansen
054a63c3a2 Add option to save Assist pipeline audio (#98928)
* Add pipeline option to save wake/stt audio to media

* Add debug_recording_dir to assist_pipeline YAML config

* Clean up and additional tests

* Remove I/O in event loop

* Organize saved audio by pipeline name and device id

* Record wake/stt debug audio in separate thread

* Fix after rebase

* Use timestamp instead of pipeline id for directory name

* Add WAV write error test

* Join thread in executor
2023-08-29 23:07:27 -04:00
Øyvind Matheson Wergeland
de30712d76 Verisure: propagate lock code digits configuration immediately (#99241) 2023-08-29 22:02:09 +02:00
Richard Kroegel
b403cb41c0 Allow one retry before raising ConfigEntryAuthFailed for bmw_connected_drive (#99168)
* Allow one retry before raising ConfigEntryAuthFailed

* Move time with freezer

* Update homeassistant/components/bmw_connected_drive/coordinator.py

---------

Co-authored-by: rikroe <rikroe@users.noreply.github.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-29 21:41:50 +02:00
Álvaro Fernández Rojas
4508e341c9 Add wind gust to AEMET hourly forecasts (#99289)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-29 21:14:37 +02:00
Stefan Agner
53c5b187c0 Update Home Assistant base image to 2023.08.0 (#99281) 2023-08-29 20:43:32 +02:00
Erik Montnemery
0366e14630 Allows defining list of attributes excluded from history in manifest.json (#99283)
* Move list of attributes excluded from history to manifest.json

* Address comments
2023-08-29 20:14:33 +02:00
kel30a
50150f5577 Bump pydaikin version to 2.11.1 (#99288) 2023-08-29 13:08:39 -05:00
Álvaro Fernández Rojas
c3ef518551 Update AEMET-OpenData to v0.4.2 (#99286) 2023-08-29 13:06:46 -05:00
Mike Degatano
e0eb63c588 Validate slug in addon services (#99232)
* Validate slug in addon services

* Move validator into hassio component

* Fixes from mypy

* Fix test for changes

* Adjust fixtures to current supervisor

* Fix call counts after fixture adjustment

* Increase coverage
2023-08-29 13:57:41 -04:00
Joost Lekkerkerker
e2dd7f2069 Add entity translations to NZBGet (#98805) 2023-08-29 18:15:44 +02:00
Maciej Bieniek
fe713cec8f Don't assume that the activity/sleep labels are always present in Tractive event (#99197)
* Don't assume that the activity_label and sleep_labes are always present in an event

* Catch KeyError
2023-08-29 17:52:29 +02:00
Joost Lekkerkerker
b9fd2ee3b6 Use functions to get value and unit in Abode (#99084) 2023-08-29 17:37:26 +02:00
Phil Bruckner
10c53dd284 Fix Life360 reauthorization config flow (#99227) 2023-08-29 16:38:11 +02:00
Álvaro Fernández Rojas
2e0a22fdaf Use freezegun in AEMET tests (#99253) 2023-08-29 09:33:12 -05:00
J. Nick Koston
5006244f4c Bump aioesphomeapi to 16.0.3 (#99282) 2023-08-29 09:31:41 -05:00
Joost Lekkerkerker
f28634ea11 Migrate PVPC to has entity name (#98894)
* Migrate PVPC to has entity name

* Set device name

* Fix feedback
2023-08-29 16:26:23 +02:00
Robert Resch
6223b1f599 Add ws endpoint "auth/delete_all_refresh_tokens" (#98976)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-29 15:57:54 +02:00
Erik Montnemery
691bbedfc8 Fix typo in TrackTemplateResultInfo (#99276) 2023-08-29 08:54:38 -05:00
David Knowles
9476ade34a Bump pydrawise to 2023.8.0 (#99270) 2023-08-29 15:43:27 +02:00
Álvaro Fernández Rojas
fae82731e1 Simplify and improve AEMET coordinator updates (#99273) 2023-08-29 08:43:14 -05:00
Joost Lekkerkerker
98cb5b4b5d Use shorthand attributes for Elkm1 (#99275) 2023-08-29 14:46:24 +02:00
Álvaro Fernández Rojas
dac77040a2 Update AEMET-OpenData to v0.4.1 (#99261)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-29 13:41:34 +02:00
Joost Lekkerkerker
00ecc108d1 Use shorthand attributes for DuneHD (#99237) 2023-08-29 13:08:24 +02:00
Quentame
750cfeb76a Refactor Freebox Home categories (#99224) 2023-08-29 12:43:22 +02:00
Maciej Bieniek
62b1211dee Remove myself from Dune HD codeowners (#99268) 2023-08-29 12:41:19 +02:00
Quentame
f1ec99b9c9 Add Freebox Home battery sensor (#99222)
* Add Freebox Home battery sensor

* Review

* Review 2

* Freebox battery is a SensorEntity, not a FreeboxSensor
2023-08-29 12:13:01 +02:00
G Johansson
657ed0bcdb Clean out compatibility for deprecated methods in Weather (#99263)
Clean out compatability in Weather
2023-08-29 11:33:56 +02:00
Erik Montnemery
e6eadc79e9 Small typing fix in light group (#99259) 2023-08-29 11:12:34 +02:00
escoand
4632a07f3f Add possibility to have multiple values for every modbus hvac mode (#98829)
Co-authored-by: jan iversen <jancasacondor@gmail.com>
2023-08-29 10:45:37 +02:00
G Johansson
7a690d7359 Add deprecation to legacy forecast for Weather (#97294)
* Add deprecation to legacy forecast

* Mod _reported

* issue

* remove not need variable

* kitchen_sink

* 2024.3

* remove demo and mqtt

* add checks

* fix deprecation

* remove variable

* fix kitchen_sink

* Fix deprecation warning

* Expand issue

* clean

* Fix tests

* fix kitchen_sink

* not report on core integrations
2023-08-29 10:38:59 +02:00
Erik Montnemery
b22b51fe3b Fix stale docstring in trafikverket_camera tests (#99260) 2023-08-29 10:28:32 +02:00
Joost Lekkerkerker
ddbf85fc38 Abort YouTube configuration if user doesn't have subscriptions (#99140) 2023-08-29 09:36:27 +02:00
liangjia2019
63c2a2994f Add new zigbee button SONOFF_SNZB_01P to deconz (#99205)
add new zigbee button
2023-08-29 09:17:27 +02:00
epenet
c81d39f651 Fix Renault AssertionError (#99189) 2023-08-29 08:58:20 +02:00
Joost Lekkerkerker
b5ff0b4ec2 Add entity translations to Vilfo (#99019) 2023-08-29 08:47:35 +02:00
Raman Gupta
be126da72d Bump zwave-js-server-python to 0.51.0 (#99250)
* Bump zwave-js-server-python to 0.51.0

* Fix how we patch the command
2023-08-29 02:45:43 -04:00
Joost Lekkerkerker
cdf39ec365 Migrate Vilfo to has entity name (#99018) 2023-08-29 08:42:37 +02:00
Joost Lekkerkerker
202b0b5300 Migrate Venstar to has entity name (#99013) 2023-08-29 08:40:35 +02:00
J. Nick Koston
5ec645161d Bump zeroconf to 0.88.0 (#99248)
changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.86.0...0.88.0
2023-08-29 00:01:39 -04:00
puddly
c8ef3f9393 Automatic migration from multi-PAN back to Zigbee firmware (#93831)
* Initial implementation of migration back to Zigbee firmware

* Fix typo in `BACKUP_RETRIES` constant name

* Name potentially long-running tasks

* Add an explicit timeout to `_async_wait_until_addon_state`

* Guard against the addon not being installed when uninstalling

* Do not launch the progress flow unless the addon is being installed

* Use a separate translation key for confirmation before disabling multi-PAN

* Disable the bellows UART thread within the ZHA config flow radio manager

* Enhance config flow progress keys for flasher addon installation

* Allow `zha.async_unload_entry` to succeed when ZHA is not loaded

* Do not endlessly spawn task when uninstalling addon synchronously

* Include `uninstall_addon.data.*` in SkyConnect and Yellow translations

* Make `homeassistant_hardware` unit tests pass

* Fix SkyConnect unit test USB mock

* Fix unit tests in related integrations

* Use a separate constant for connection retrying

* Unit test ZHA migration from multi-PAN

* Test ZHA multi-PAN migration helper changes

* Fix flaky SkyConnect unit test being affected by system USB devices

* Unit test the synchronous addon uninstall helper

* Test failure when flasher addon is already running

* Test failure where flasher addon fails to install

* Test ZHA migration failures

* Rename `get_addon_manager` to `get_multiprotocol_addon_manager`

* Remove stray "addon uninstall" comment

* Use better variable names for the two addon managers

* Remove extraneous `self.install_task = None`

* Use the addon manager's `addon_name` instead of constants

* Migrate synchronous addon operations into a new class

* Remove wrapper functions with `finally` clause

* Use a more descriptive error message when the flasher addon is stalled

* Fix existing unit tests

* Remove `wait_until_done`

* Fully replace all addon name constants with those from managers

* Fix OTBR breakage

* Simplify `is_hassio` mocking

* Add missing tests for `check_multi_pan_addon`

* Add missing tests for `multi_pan_addon_using_device`

* Use `waiting` instead of `sync` in class name and methods
2023-08-28 17:26:34 -04:00
puddly
23839a7f10 Wrap most ZHA exceptions in HomeAssistantError (#98421)
* Wrap attribute writes in a helper throwing `HomeAssistantError`

* Do not check for `Exception` instances, they are now propagated

* Write `cie_addr` synchronously

* Fix unnecessary `if` in `async_set_native_value`

* Fix unit tests

* Use `HomeAssistantError` in cover commands

* Revert writing `cie_addr` synchronously

* Disallow proxying of some cluster methods to fix unit test warnings

* Unit test cover failures to increase coverage

* Unit test missing climate device

* Unit test remaining cover commands
2023-08-28 17:24:12 -04:00
Jan-Philipp Benecke
97fd73f9f7 Bump syrupy to 4.2.1 (#99156) 2023-08-28 23:14:07 +02:00
J. Nick Koston
0e6b3d6583 Switch async_track_same_state to use async_call_later (#99219)
* Switch async_track_same_state to use async_call_later

There was no need to use async_track_point_in_utc_time here since
we only need a delay

* update trigger tests

* remove some more utcnow patching

* remove some more utcnow patching

* remove some more utcnow patching
2023-08-28 22:30:20 +02:00
J.P. Krauss
95c03b4192 Add Options Flow to change radius after initial configuration (#97285)
* Add Options Flow to change radius after initial configuration

* Add tests for Options Flow

* Apply suggestions from code review

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

* Incorporate review suggestions

* Fix diagnostics test case

* Apply suggestions from code review

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

* Incorporate review suggestions

* Revert "Incorporate review suggestions"

This reverts commit 421e140a4fc78da22ea74c95cd1a17f9305ebbf6.

* Fix broken review comments

* Incorporate rest of review comments

* Incorporate rest of review comments

* Use Config Entry Migration

* Remove old migration code

* Update diagnostics snapshot for config entry migration

* Incorporate review feedback

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-28 21:21:52 +02:00
jimmyd-be
9e8d89c4f5 Renson binary sensors (#94490)
* Add binary sensors

* Add Renson services

* Add fan to Renson

* Revert "Add fan to Renson"

This reverts commit 8e7c09671ebf0a53ce0bb633d5209a3add2856b6.

* Revert "Add Renson services"

This reverts commit d862976c81404623eccd64275f412dc9cdb2c1c4.

* Add binary sensor to coveragerc file

* Update homeassistant/components/renson/binary_sensor.py

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

* Update homeassistant/components/renson/binary_sensor.py

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

* Changed hard coded names to use translation

* Code cleaning

* Use super()._handle_coordinator_update()

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-28 21:15:18 +02:00
J. Nick Koston
80d2309896 Switch async_track_time_interval to use async_call_later internally (#99220) 2023-08-28 13:56:22 -05:00
Joost Lekkerkerker
821d74e904 Add entity translations to Switcher kis (#99223)
* Add entity translations to switcher_kis

* Add entity translations

* Fix tests
2023-08-28 20:53:42 +03:00
Joost Lekkerkerker
fc6f48e076 Enhance Androidtv remote config flow typing (#99144) 2023-08-28 18:03:30 +02:00
Joost Lekkerkerker
3db61a99a4 Remove polling interval property from Aurora (#99198) 2023-08-28 18:01:23 +02:00
Joost Lekkerkerker
1c0d5f8637 Clean up Balboa entity (#99203) 2023-08-28 17:59:53 +02:00
Joost Lekkerkerker
00cc57c4ed Use shorthand attribute for Coolmaster (#99211) 2023-08-28 17:57:51 +02:00
Joost Lekkerkerker
377f7cba60 Improve aurora data schema (#99200) 2023-08-28 17:56:27 +02:00
Aidan Timson
42597f80a3 Add power service to System Bridge integration (#95719)
* Add power service to System Bridge

Add missing return types

Use in list validator and fix command

* Use attr map instead of concatination

* Update strings

* Update homeassistant/components/system_bridge/strings.json

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-28 17:44:23 +02:00
Ian Foster
ef7a246f09 Fix ruckus_unleashed for python 3.11 (#94835)
Co-authored-by: Tony <29752086+ms264556@users.noreply.github.com>
2023-08-28 17:26:40 +02:00
J. Nick Koston
739eeeccb0 Switch hassio to use async_call_later (#99216) 2023-08-28 10:21:05 -05:00
J. Nick Koston
1bf7b4b2c7 Switch lifx to use async_call_later (#99217) 2023-08-28 10:20:42 -05:00
J. Nick Koston
ccb91e3676 Switch axis to use async_call_later (#99215) 2023-08-28 10:19:44 -05:00
Erik Montnemery
9dac6a2948 Use loop.time in DataUpdateCoordinator (#98937) 2023-08-28 10:16:34 -05:00
J. Nick Koston
d4e72c49fa Bump aiohomekit to 3.0.1 (#99210) 2023-08-28 10:02:51 -05:00
J. Nick Koston
6c16d89c1d Switch w800rf32 to use async_call_later (#99214) 2023-08-28 17:00:52 +02:00
J. Nick Koston
4eb71a534f Switch async_track_point_in_time to async_call_later in alarmdecoder (#99213) 2023-08-28 16:57:16 +02:00
Franck Nijhof
f7a45e31c1 Merge branch 'master' into dev 2023-08-28 16:26:29 +02:00
Jake Colman
f1378bba8e Add indoor sensors to Honeywell integration (#98609)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-28 15:45:01 +02:00
Joost Lekkerkerker
a6788208fe Add entity translations to System bridge (#98959) 2023-08-28 15:36:18 +02:00
Joost Lekkerkerker
61ff53fcf7 Use shorthand attributes in August (#99196) 2023-08-28 08:14:38 -05:00
Simone Chemelli
1692d83063 Vodafone Station device tracker (#94032)
* New integration for Vodafone Station

* coveragerc

* Add ConfigFlow,ScannerEntity,DataUpdateCoordinator

* Introduce aiovodafone lib

* heavy cleanup

* bump aiovodafone to v0.0.5

* add config_flow tests (100% coverage)

* run pre-comimit scripts again

* Remove redundant parameter SSL

* rename and cleanup

* cleanup and bug fix

* cleanup exceptions

* constructor comment review

* improve test patching

* move VodafoneStationDeviceInfo to dataclass

* intriduce home field

* dispacher cleanup

* remove extra attributes (reduces state writes)

* attempt to complete test flow

* complete flow for test_exception_connection

* add comment about unique id
2023-08-28 15:10:23 +02:00
Guido Schmitz
660167cb1b Add image platform to devolo_home_network (#98036) 2023-08-28 14:55:49 +02:00
Joost Lekkerkerker
3f0a8b7a56 Initialize static shorthand attributes outside of constructor for BAF (#99202)
Initialize static shorthand attributes outside of init
2023-08-28 14:43:51 +02:00
Joost Lekkerkerker
60844954d2 Add typing to media extractor (#99207)
* Add typing to media extractor

* Add typing to media extractor

* Add typing to media extractor

* Add typing to media extractor
2023-08-28 14:43:22 +02:00
Rami Mosleh
1d403a961f Reorganize Transmission entry setup (#99195)
* simplify integration setup

* Update transmission entry setup to avoid None attributes

* keep api property in TransmissionData

* Apply suggestion

* Add __init__.py tp .coveragerc
2023-08-28 13:13:35 +02:00
Joost Lekkerkerker
efcf3ddb57 Remove BleBox switch constructor (#99204) 2023-08-28 12:49:20 +02:00
Rami Mosleh
8edae37082 Add more type hints to Transmission (#99190)
* More type hints of transmssion

* More type hints
2023-08-28 11:20:18 +02:00
Shay Levy
b0f3b7bb76 Revert "Change naming of Shelly entities to correspond with HA guidelines" (#99059) 2023-08-28 10:42:24 +02:00
Robert Resch
bb545b1c4d Fix typos in home_plus_controls (#99188) 2023-08-28 10:15:14 +02:00
Álvaro Fernández Rojas
1683ffb830 Update aioqsw to v0.3.4 (#99183)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-28 09:35:29 +02:00
Eric Severance
30b815bfb8 Bump pywemo to 1.3.0 (#99172) 2023-08-28 09:34:30 +02:00
J. Nick Koston
01d29512ff Bump zeroconf to 0.86.0 (#99177)
changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.85.0...0.86.0
2023-08-28 09:33:08 +02:00
Erik Montnemery
e97e9ae55a Use freezegun in trafikverket_camera tests (#99067) 2023-08-28 09:23:32 +02:00
jan iversen
a3b526eef6 Address late modbus review (#99123)
Post review comments.
2023-08-28 09:07:31 +02:00
Erik Montnemery
4b50c95d1d Fix trafikverket_camera recorder platform setup (#99080) 2023-08-28 09:05:09 +02:00
Maciej Bieniek
579c760f53 Add missing low state for ENUM Tractive sensors (#99057)
* Add missing "low" option

* Use existing translations
2023-08-28 08:23:26 +02:00
Richard Kroegel
c686f962b5 Bump bimmer_connected to 0.14.0 (#99161)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-08-27 18:37:08 -07:00
Joost Lekkerkerker
dbb00b1725 Add code owner for Media Extractor (#99153) 2023-08-28 00:05:41 +02:00
Joost Lekkerkerker
c9905fda6d Add entity translations to Watttime (#99151) 2023-08-27 23:54:49 +02:00
J. Nick Koston
16041e5127 Bump zeroconf to 0.85.0 (#99165) 2023-08-27 16:00:19 -05:00
Michael
1bd37612af Introduce more sensors to Nextcloud (#99155) 2023-08-27 20:50:27 +02:00
Michael
0ce9d21bea Rework to use list of entity descriptions in Nextcloud integration (#99150) 2023-08-27 20:18:55 +02:00
Joost Lekkerkerker
c88672c352 Make Anova device unique id public (#99147) 2023-08-27 20:10:08 +02:00
Joost Lekkerkerker
fbe2228c3f Extract Ambient Station base entity to separate file (#99142)
* Extract Ambient Station entity to separate file

* Add to coveragerc
2023-08-27 20:09:10 +02:00
Joost Lekkerkerker
65103d4515 Improve Anova typing (#99146) 2023-08-27 20:06:19 +02:00
Joost Lekkerkerker
a48c7f67b4 Remove codeowner from airtouch4 (#99145) 2023-08-27 20:04:49 +02:00
J. Nick Koston
17fd538198 Bump zeroconf to 0.84.0 (#99138) 2023-08-27 13:00:58 -05:00
Joost Lekkerkerker
e1dc133fa1 Add device info to Watttime (#99022) 2023-08-27 19:50:36 +02:00
Joost Lekkerkerker
4394fc2897 Fix typo in AnthemAV const (#99149) 2023-08-27 19:38:12 +02:00
Michael
cc103ddbaa Split Owncloud CPU load in separate sensors (#99141)
* split cpu load values into own sensors

* apply suggestion
2023-08-27 19:34:58 +02:00
J. Nick Koston
d17ffff3e3 Retry tplink setup later if device has an unexpected mac address (#98784)
Retry tplink setup later if device has an unexpected serial

If the DHCP reservation changed and there is now a different tplink device at
the saved IP address, retry setup later to avoid cross linking devices
2023-08-27 19:00:39 +02:00
escoand
d21ee30ddf Extend Nextcloud integration (#94066) 2023-08-27 18:51:31 +02:00
J. Nick Koston
f42b8e217b Bump ulid-transform to 0.8.1 (#99139) 2023-08-27 11:32:41 -05:00
J. Nick Koston
6cd28b64e8 Bump bluetooth-data-tools 1.9.1 (#99131) 2023-08-27 10:10:00 -05:00
J. Nick Koston
6992ea9af0 Bump fnv-hash-fast to 0.4.1 (#99135) 2023-08-27 10:08:21 -05:00
Robert Svensson
5e5193eeb5 Rework UniFi Network Controller device and add software version (#99136)
Rework Network Controller device and add software version
2023-08-27 17:07:38 +02:00
Robert Svensson
71bf782b22 Improve UniFi PoE control by queueing commands together (#99114)
* Working draft without timer

* Clean up
Improve tests

* Use async_call_later
2023-08-27 16:58:48 +02:00
J. Nick Koston
20b8c5dd26 Bump home-assistant-bluetooth to 1.10.3 (#99133) 2023-08-27 09:29:17 -05:00
J. Nick Koston
842a56f5c7 Bump zeroconf to 0.83.1 (#99134) 2023-08-27 09:28:23 -05:00
J. Nick Koston
5cc49f6dd6 Bump dbus-fast to 1.94.1 (#99132) 2023-08-27 09:27:30 -05:00
Joost Lekkerkerker
b266096ae1 Use snapshot assertion for Watttime diagnostics test (#99023) 2023-08-27 09:19:15 -05:00
Joost Lekkerkerker
11cecc3f0a Use shorthand attributes for airtouch4 (#99086) 2023-08-27 09:11:45 -05:00
Joost Lekkerkerker
faed58c01b Migrate Somfy mylink to has entity name (#98947) 2023-08-27 16:09:15 +02:00
Steven Looman
045c514c18 Bump async-upnp-client to 0.35.0 (#99129) 2023-08-27 09:08:58 -05:00
Joost Lekkerkerker
6e157fef18 Add device info to Withings (#99052) 2023-08-27 16:06:08 +02:00
Joost Lekkerkerker
a73f214ead Add typing to Venstar Config flow (#99016) 2023-08-27 16:03:36 +02:00
J. Nick Koston
c087e6eab6 Revert "Bump python bsblan 0.5.14" (#99130) 2023-08-27 08:59:28 -05:00
Allen Porter
7070302001 Use climate entity built in attrs for nest climate (#99093)
* Use climate entity built in attrs for nest climate

* Update homeassistant/components/nest/climate.py

Co-authored-by: Shay Levy <levyshay1@gmail.com>

---------

Co-authored-by: Shay Levy <levyshay1@gmail.com>
2023-08-27 14:05:17 +03:00
Rami Mosleh
2dd6b26fbc Add type hints to transmission (#99117)
* Add type hints

* Apply suggestions
2023-08-27 12:43:30 +02:00
J. Nick Koston
54cd0e8183 Add some missing typing to isy994 (#99110) 2023-08-27 12:33:00 +02:00
J. Nick Koston
0362ce92b5 Drop switchbot codeowner (#99108)
I picked up working on this integration because I wanted to make sure the new Bluetooth stack had a good test case to work out issues and did not generate unexpected breaking changes.

Since I do not use switchbot in production, I usually cannot help solve problems beyond the Bluetooth stack that is visible to HA.

While I am still happy to do code reviews here, the Bluetooth stack has matured to the point where watching for issues here is no longer helpful to maintaining the stack as the signal to noise ratio is too high
2023-08-27 09:37:27 +02:00
Robert Svensson
45efe29262 Bump aiounifi to v58 (#99103) 2023-08-26 18:27:45 -05:00
Willem-Jan van Rootselaar
a81e6d5811 Bump python bsblan 0.5.14 (#99089)
* update python-bsblan to 0.5.14

* fix test diagnostics
2023-08-26 22:13:25 +03:00
Joost Lekkerkerker
2fc728db42 Remove unused variable from Airthings BLE (#99085)
* Remove unused variable from Airthings BLE

* Remove unused variable from Airthings BLE
2023-08-26 19:42:49 +02:00
uvjustin
407aa31adc Generate Stream snapshots using next keyframe (#96991)
* Add wait_for_next_keyframe option to stream images
Add STREAM_SNAPSHOT to CameraEntityFeature
Use wait_for_next_keyframe option for snapshots using stream

* Update stream test comments

* Add generic camera snapshot test

* Get stream still images directly in camera
Remove getting stream images from generic, nest, and ONVIF
Refactor camera preferences
Add use_stream_for_stills setting to camera
Update tests

* Only attempt to get stream image if integration supports stream

* Use property instead of entity registry setting

* Split out getting stream prerequisites from stream_source in nest

* Use cached_property for rtsp live stream trait

* Make rtsp live stream trait NestCamera attribute

* Update homeassistant/components/nest/camera.py

Co-authored-by: Allen Porter <allen.porter@gmail.com>

* Change usage of async_timeout

* Change import formatting in generic/test_camera

* Simplify Nest camera property initialization

---------

Co-authored-by: Allen Porter <allen.porter@gmail.com>
2023-08-26 10:39:40 -07:00
J. Nick Koston
e003903bc5 Bump zeroconf to 0.83.0 (#99091) 2023-08-26 12:26:12 -05:00
Ville Skyttä
c287bd1a3b Remove pylint configs flagged by useless-suppression (#99081) 2023-08-26 17:46:03 +03:00
tronikos
a25a7ebbeb Bump opower to 0.0.32 (#99079) 2023-08-26 15:39:48 +02:00
Erik Montnemery
d74a0fd6dd Use freezegun in additional fronius tests (#99066) 2023-08-26 09:11:42 +02:00
Marc Mueller
6f43dd1c14 Adjust netatmo test (#99071) 2023-08-26 07:35:10 +02:00
Allen Porter
8d9c5a61ec Update calendar handle state updates at start/end of active/upcoming event (#98037)
* Update calendar handle state updates at start/end of active/upcoming event

* Use async_write_ha_state intercept state updates

Remove unrelated changes and whitespace.

* Revert unnecessary changes

* Move demo calendar to config entries to cleanup event timers

* Fix docs on calendars

* Move method inside from PR feedback
2023-08-25 18:32:20 -07:00
Marc Mueller
544d6b05a5 Replace mock_coro with AsyncMock (#99014)
* Replace mock_coro with AsyncMock

* Remove mock_coro test helper function

* Remove redundant AsyncMock
2023-08-25 22:54:55 +02:00
Joost Lekkerkerker
57144a6064 Use entity descriptions in Switcher (#98958) 2023-08-25 21:12:21 +03:00
Erik Montnemery
3a71e21d6a Add and improve comments about staggering of event listeners (#99058) 2023-08-25 19:47:13 +02:00
Michael Hansen
8768c39021 Wake word cleanup (#98652)
* Make arguments for async_pipeline_from_audio_stream keyword-only after hass

* Use a bytearray ring buffer

* Move generator outside

* Move stt stream generator outside

* Clean up execute

* Refactor VAD to use bytearray

* More tests

* Refactor chunk_samples to be more correct and robust

* Change AudioBuffer to use append instead of setitem

* Cleanup

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-08-25 13:28:48 -04:00
Jan Bouwhuis
49897341ba Add lawn_mower platform to MQTT (#98831)
* Add MQTT lawn_mower platform

* Use separate command topics and templates

* Remove unrelated change
2023-08-25 17:56:22 +02:00
Erik Montnemery
27f7399071 Modernize accuweather weather (#99001) 2023-08-25 16:46:23 +02:00
Jan-Philipp Benecke
f96c1516f8 Use snapshot assertion for gios diagnostics test (#98984) 2023-08-25 16:46:10 +02:00
Erik Montnemery
b0952bc54a Use freezegun in shelly tests (#99042) 2023-08-25 09:06:43 -05:00
Erik Montnemery
452caee41a Use freezegun in pvpc_hourly_pricing tests (#99040) 2023-08-25 09:06:14 -05:00
Erik Montnemery
c827af5826 Use freezegun in uptimerobot tests (#99046) 2023-08-25 09:05:44 -05:00
Erik Montnemery
0d3663c52a Use freezegun in fronius tests (#99030) 2023-08-25 09:05:16 -05:00
Erik Montnemery
5617a738c0 Use freezegun in airly tests (#99028) 2023-08-25 09:04:51 -05:00
Erik Montnemery
dd39e8fe64 Use freezegun in hue tests (#99033) 2023-08-25 09:04:28 -05:00
Erik Montnemery
3b07181d87 Use freezegun in fully_kiosk tests (#99031) 2023-08-25 09:03:51 -05:00
Erik Montnemery
1f9c180233 Use freezegun in iotawatt tests (#99034) 2023-08-25 09:03:29 -05:00
Erik Montnemery
e6728f2f19 Use freezegun in kraken tests (#99035) 2023-08-25 09:03:12 -05:00
Erik Montnemery
cb8842b1fb Use freezegun in landisgyr_heat_meter tests (#99037) 2023-08-25 09:02:47 -05:00
Erik Montnemery
75743ed947 Use freezegun in here_travel_time tests (#99032) 2023-08-25 09:02:25 -05:00
Erik Montnemery
676f59fded Use freezegun in trafikverket_ferry tests (#99045) 2023-08-25 09:02:07 -05:00
Erik Montnemery
81aa35a9ce Use freezegun in version tests (#99047) 2023-08-25 09:01:48 -05:00
Erik Montnemery
f99743bedb Use freezegun in tomorrowio tests (#99044) 2023-08-25 09:01:28 -05:00
Erik Montnemery
f18a277cac Use freezegun in ws66i tests (#99049) 2023-08-25 09:00:50 -05:00
Erik Montnemery
ee073e9e3e Use freezegun in lacrosse_view tests (#99036) 2023-08-25 09:00:30 -05:00
Erik Montnemery
e0af9de877 Use freezegun in motioneye tests (#99038) 2023-08-25 09:00:11 -05:00
Erik Montnemery
943db9e0d5 Use freezegun in devolo_home_network tests (#99029) 2023-08-25 08:59:52 -05:00
Erik Montnemery
346674a1a8 Use freezegun in wled tests (#99048) 2023-08-25 08:59:30 -05:00
Erik Montnemery
65d555b138 Use freezegun in qnap_qsw tests (#99041) 2023-08-25 08:59:08 -05:00
Erik Montnemery
64306ec053 Use freezegun in solaredge tests (#99043) 2023-08-25 08:58:52 -05:00
Erik Montnemery
8161810159 Use freezegun in opensky tests (#99039) 2023-08-25 14:03:51 +02:00
Jan-Philipp Benecke
4fb00e448c Use snapshot assertion for rdw diagnostics test (#99027) 2023-08-25 13:40:08 +02:00
J. Nick Koston
d79e8b7a02 Avoid fetching state and charging state multiple time for hkc icon (#98995) 2023-08-25 06:35:31 -05:00
Álvaro Fernández Rojas
3f2d2a85b7 Update AEMET-OpenData to v0.4.0 (#99015)
* Update AEMET-OpenData to v0.4.0

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

* Trigger Github CI

---------

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-25 12:53:26 +02:00
Marc Mueller
3ebd7d2fd1 Fix asyncio DeprecationWarning [3.12] (#98989)
* Fix asyncio DeprecationWarning [3.12]

* Use AsyncMock

* Rewrite ffmpeg tests

* Remove test classes

* Rename test file
2023-08-25 12:27:23 +02:00
starkillerOG
bab7d289a6 Reolink fix unknown value in select enums (#99012) 2023-08-25 11:42:55 +02:00
Joost Lekkerkerker
da9fc495ca Improve SRP Energy coordinator (#99010)
* Improve SRP Energy coordinator

* Use time instead of asyncio
2023-08-25 11:19:40 +02:00
Joost Lekkerkerker
11c5e3534a Add unique id to srp energy entity (#99008) 2023-08-25 10:52:07 +02:00
Joost Lekkerkerker
475fd77019 Extract SRP Energy coordinator to separate file (#98956) 2023-08-25 10:33:02 +02:00
J. Nick Koston
3ebf96143a Improve performance of bluetooth coordinators (#98997) 2023-08-25 10:31:43 +02:00
Niels Perfors
c2713f0aed Upgrade Verisure to 2.6.6 (#98258) 2023-08-25 10:27:35 +02:00
Erik Montnemery
48b6b1c11a Modernize openweathermap weather (#99002) 2023-08-25 10:25:03 +02:00
dependabot[bot]
07494f129c Bump actions/checkout from 3.5.3 to 3.6.0 (#99003)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.5.3...v3.6.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-25 10:18:43 +02:00
Ville Skyttä
e7b6037419 Remove unnnecessary pylint configs from components [e-l]* (#99004) 2023-08-25 09:46:34 +02:00
Allen Porter
960d66e168 Bump ical to 5.0.1 (#98998) 2023-08-25 09:43:11 +02:00
Erik Montnemery
3e02fb1f07 Add preview support to all groups (#98951) 2023-08-25 08:59:33 +02:00
Aaron Bach
a741298461 Bump simplisafe-python to 2023.08.0 (#98991) 2023-08-24 21:11:58 -05:00
Marc Mueller
72e6f79086 Replace remaining utcnow calls + add ruff check (#97964) 2023-08-25 03:23:43 +02:00
Marty Sun
588db501fb Add new integration Yardian (#97326)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-24 17:48:49 -05:00
Jan-Philipp Benecke
3bcd1d5a1a Use snapshot assertion for iqvia diagnostics test (#98983) 2023-08-24 23:26:21 +02:00
Jan-Philipp Benecke
4d8941d4b7 Use snapshot assertion for onvif diagnostics test (#98982) 2023-08-24 22:40:45 +02:00
Joost Lekkerkerker
54ed8fc914 Use device class translations for 1-wire (#98813) 2023-08-24 22:19:29 +02:00
Joost Lekkerkerker
d7adc2621d Migrate Life360 to has entity name (#98796) 2023-08-24 22:03:26 +02:00
Michael Arthur
b03ffe6a6a Electric Kiwi: Fix time for installations in UTC (#97881) 2023-08-24 21:57:52 +02:00
mkmer
f2c475cf1b Bump aiosomecomfort to 0.0.17 (#98978)
* Clean up imports
Add refresh after login in update

* Bump somecomfort 0.0.17
Separate Somecomfort error to unauthorized

* Add tests

* Run Black format
2023-08-24 21:13:42 +02:00
Joost Lekkerkerker
417fd0838a Migrate Snooz to has entity name (#98940) 2023-08-24 21:05:00 +02:00
Jan Bouwhuis
948b34b045 Do not force update mqtt device_tracker (#98838) 2023-08-24 20:09:14 +02:00
Joost Lekkerkerker
a5cced1da9 Add device to Tile (#98964) 2023-08-24 20:07:02 +02:00
Joost Lekkerkerker
7575ffa24e Add entity translations to Tankerkoenig (#98961) 2023-08-24 19:59:34 +02:00
Joost Lekkerkerker
480db1f1e6 Migrate Squeezebox to has entity name (#98948) 2023-08-24 19:58:54 +02:00
Joost Lekkerkerker
be78169065 Add entity translations to Risco (#98921) 2023-08-24 19:56:02 +02:00
Joost Lekkerkerker
969063ccf8 Use shorthand attributes for SRP Energy (#98953) 2023-08-24 19:54:49 +02:00
Erik Montnemery
2066cf6b31 Remove group_type from group preview events (#98952) 2023-08-24 19:54:04 +02:00
Joost Lekkerkerker
998a390da5 Use device class in TPLink Omada Update entity (#98971) 2023-08-24 19:53:09 +02:00
Marc Mueller
089f76099d Fix stream test aiohttp DeprecationWarning (#98962) 2023-08-24 19:50:25 +02:00
Joost Lekkerkerker
7d35dcfa65 Make Sabnzbd entity translation clearer (#98938) 2023-08-24 19:49:53 +02:00
J. Nick Koston
53eb4d0ead Bump dbus-fast to 1.94.0 (#98973) 2023-08-24 11:10:38 -05:00
Joost Lekkerkerker
4e049f9bed Use snapshot assertion in Tile diagnostic test (#98965) 2023-08-24 17:11:24 +02:00
Joost Lekkerkerker
d300f2d0cc Remove default model from upcloud (#98972) 2023-08-24 16:39:14 +02:00
Ville Skyttä
9da192c752 Avoid use of datetime.utc* methods deprecated in Python 3.12 (#93684)
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
2023-08-24 16:38:22 +02:00
G Johansson
61c17291fb Move TemplateEntity to template (#98957)
* Move TemplateEntity to template

* Rename template_entity in helpers
2023-08-24 15:37:04 +02:00
J. Nick Koston
99e97782b6 Improve performance of abort_entries_match (#98932)
* Improve performance of abort_entries_match

In #90406 a ChainMap was added which called __iter__
and __contains__ which ends up creating temp dicts
for matching

174e9da083/Lib/collections/__init__.py (L1022)

We can avoid this by removing the ChainMap since there
are only two mappings to match on.

This also means options no longer obscures data

* adjust comment
2023-08-24 15:34:45 +02:00
Franck Nijhof
7548c4aced 2023.8.4 (#98955) 2023-08-24 13:47:32 +02:00
Erik Montnemery
b145352bbb Modernize meteo_france weather (#98022)
* Modernize meteofrance weather

* Remove options flow

* Remove unused constant

* Format code

---------

Co-authored-by: Quentin POLLET <polletquentin74@me.com>
2023-08-24 13:44:43 +02:00
Franck Nijhof
0d013767ee Add support for event groups (#98463)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-24 12:49:38 +02:00
Joost Lekkerkerker
87dd18cc2e Remove obsolete yaml check in SQL (#98950)
* Remove unique id check from SQL

* Remove unique id check from SQL
2023-08-24 12:35:11 +02:00
G Johansson
b69e8fda77 Remove TemplateSensor from the template_entity helper (#98945)
Clean off TemplateSensor
2023-08-24 12:14:39 +02:00
Franck Nijhof
9bc4866bd2 Bumped version to 2023.8.4 2023-08-24 12:12:41 +02:00
starkillerOG
e1b005128d Bump reolink-aio to 0.7.8 (#98942) 2023-08-24 12:12:28 +02:00
Nathan Spencer
c8b5191984 Bump pylitterbot to 2023.4.5 (#98854) 2023-08-24 12:12:25 +02:00
Nathan Spencer
63a687c37f Bump pylitterbot to 2023.4.4 (#98414) 2023-08-24 12:12:19 +02:00
tronikos
119bc7d847 Bump androidtvremote2 to 0.0.14 (#98801) 2023-08-24 12:10:34 +02:00
Franck Nijhof
e208e7da91 Remove repair issue for MQTT discovered items (#98768) 2023-08-24 12:09:58 +02:00
Florent Thiery
ce8eebebc5 Reduce Freebox router Raid warning to one occurence (#98740)
* consider Freebox router does not support Raid if the first enumeration raised an http error, fixes #98274

* add router name to warning message

* reduce log level to info, remove details
2023-08-24 12:08:05 +02:00
tronikos
dc6e752adf Bump opower to 0.0.31 (#98716) 2023-08-24 12:07:34 +02:00
jan iversen
ebdf48e41f Correct modbus swap/datatype error message (#98698) 2023-08-24 12:06:58 +02:00
tronikos
cdb088327c Bump opowerto 0.0.30 (#98660) 2023-08-24 12:06:23 +02:00
jan iversen
e1eb1cf2fb modbus: slave is allowed with custom (#98644) 2023-08-24 12:05:08 +02:00
Markus Ressel
bd0fe63dc8 Fix octoprint down every two minutes (#90001) 2023-08-24 12:05:02 +02:00
J. Nick Koston
849cfa3af8 Retry yeelight setup later if the wrong device is found (#98884) 2023-08-24 12:04:00 +02:00
starkillerOG
9a0507af3c Bump reolink-aio to 0.7.8 (#98942) 2023-08-24 12:01:22 +02:00
Erik Montnemery
d282ba6bac Use a single WS command for group preview (#98903)
* Use a single WS command for group preview

* Fix tests
2023-08-24 11:59:24 +02:00
G Johansson
31a8a62165 SNMP sensor refactor to ManualTriggerSensorEntity (#98630)
* SNMP to ManualTriggerSensorEntity

* Mods
2023-08-24 11:45:14 +02:00
G Johansson
3b31c58eba Add coordinator test for Yale Smart Living (#98638) 2023-08-24 11:44:04 +02:00
Joost Lekkerkerker
577f545113 Add entity translations to Rachio (#98917) 2023-08-24 11:43:10 +02:00
Erik Montnemery
c47983621c Teach CoordinatorWeatherEntity about multiple coordinators (#98830) 2023-08-24 11:28:20 +02:00
Joost Lekkerkerker
f395147f7c Move platform specifics out of Solaredge const (#98941) 2023-08-24 11:27:24 +02:00
Joost Lekkerkerker
f44215d286 Use snapshot assertion for Brother diagnostics test (#98904) 2023-08-24 11:19:16 +02:00
Joost Lekkerkerker
0a1ad8a119 Add entity translations to Ridwell (#98918) 2023-08-24 10:42:05 +02:00
G Johansson
147351be6e Add Trafikverket Camera integration (#79873) 2023-08-24 10:39:22 +02:00
Robert Resch
7926c5cea9 Add repair issue about the deprecation of home plus control (#98828)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-08-24 10:33:06 +02:00
Jan Bouwhuis
8b232047c4 Add origin info support for MQTT discovered items (#98782)
* Add integration info support for MQTT discovery.

* Moving logs to discovery

* Revert adding class property

* Rename to origin

* Follow up comments
2023-08-24 09:50:39 +02:00
Joost Lekkerkerker
fe164d06a7 Add entity translations to Sabnzbd (#98923) 2023-08-24 09:23:48 +02:00
Joost Lekkerkerker
602a80c35c Use snapshot assertion for EasyEnergy diagnostics test (#98909) 2023-08-24 09:19:36 +02:00
Joakim Sørensen
8d576c900d Bump hass-nabucasa from 0.69.0 to 0.70.0 (#98935) 2023-08-24 09:18:25 +02:00
Joost Lekkerkerker
14f80560c0 Use snapshot assertion for Ridwell diagnostics test (#98919) 2023-08-24 08:14:46 +02:00
J. Nick Koston
46a0f84101 Bump bluetooth-data-tools to 1.9.0 (#98927) 2023-08-23 20:18:21 -05:00
Ville Skyttä
b51c0f6ddc Remove unnnecessary pylint configs from components [s-z]* (#98925) 2023-08-24 01:25:32 +02:00
Joost Lekkerkerker
c39f6b3bea Use snapshot assertion for Coinbase diagnostics test (#98906) 2023-08-24 01:23:31 +02:00
Joost Lekkerkerker
faa4489f4c Use snapshot assertion for Co2signal diagnostics test (#98905) 2023-08-24 01:18:49 +02:00
Jan Bouwhuis
a1307e117d Add additional debug logging for imap (#98877) 2023-08-24 01:02:52 +02:00
Joost Lekkerkerker
f1fb28aad5 Use snapshot assertion for ESPHome diagnostics test (#98913) 2023-08-24 01:01:58 +02:00
Joost Lekkerkerker
a539d851cc Use snapshot assertion for Enphase Envoy diagnostics test (#98910) 2023-08-23 17:57:55 -05:00
Joost Lekkerkerker
360d2de1e1 Use snapshot assertion for Cpuspeed diagnostics test (#98907) 2023-08-24 00:57:27 +02:00
Ville Skyttä
34b47a2597 Remove unnnecessary pylint configs from components [m-r]* (#98924) 2023-08-24 00:56:50 +02:00
Ville Skyttä
3b4774d9ed Remove unnnecessary pylint configs from components/[a-d]* (#98911) 2023-08-24 00:54:02 +02:00
Joost Lekkerkerker
e471110288 Use snapshot assertion for August diagnostics test (#98901) 2023-08-23 17:52:04 -05:00
Klaas Schoute
d8f0c090cf Energyzero - Add sensor entity to pick best hours (#98916)
* Add entity to pick best hours

* Add entity also to diagnostics

* Remove string translation that doesn't exists

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-23 23:02:19 +02:00
Joakim Plate
816f834807 Add moisture sensors entities for gardena (#98282)
Add support for soil moisture sensors for gardena
2023-08-23 22:46:34 +02:00
Klaas Schoute
364d872a47 Bump energyzero to v0.5.0 (#98914) 2023-08-23 22:43:08 +02:00
Joost Lekkerkerker
f83c335409 Use snapshot assertion for Environment Canada diagnostics test (#98912) 2023-08-23 22:21:24 +02:00
Joost Lekkerkerker
1f0e8f93c5 Use snapshot assertion for Deconz diagnostics test (#98908) 2023-08-23 21:37:03 +02:00
Joost Lekkerkerker
82e92cdf82 Use snapshot assertion for Axis diagnostics test (#98902) 2023-08-23 21:36:18 +02:00
J. Nick Koston
e1db3ecf52 Retry rainmachine setup later if the wrong device is found (#98888) 2023-08-23 14:21:18 -05:00
Jan-Philipp Benecke
4aa7fb0e35 Use snapshot assertion for Discovergy diagnostics test (#98871)
Add snapshot assertion to Discovergy
2023-08-23 21:02:11 +02:00
Jean-François Roy
e96ce3f520 baf: Raise ConfigEntryNotReady when the device has a mismatched UUID (#98898) 2023-08-23 13:34:38 -05:00
Jan-Philipp Benecke
39992c2ccc Migrate BSB-Lan diagnostics test to snapshot assertion (#98899)
Migrate bsblan diagnostics test to snapshot assertion
2023-08-23 20:20:08 +02:00
Chris Talkington
22c1ddef71 Enable strict typing for ipp (#98792)
enable strict typing for ipp
2023-08-23 12:45:49 -05:00
Erik Montnemery
3c10d0e1f7 Deduplicate entities derived from GroupEntity (#98893) 2023-08-23 19:20:58 +02:00
Erik Montnemery
ee1b6a60a0 Deduplicate group preview tests (#98883) 2023-08-23 19:13:24 +02:00
Michael Hansen
4a417c7dcc Wake word entity state/category fix (#98886)
* Only change wake word entity state on detection

* Wake word entity is diagnostic
2023-08-23 12:11:14 -04:00
J. Nick Koston
ba9c969d91 Retry lookin setup later if the wrong device is found (#98881) 2023-08-23 10:21:38 -05:00
J. Nick Koston
b884dafa81 Retry enphase_envoy setup later if the wrong device is found (#98882) 2023-08-23 10:08:47 -05:00
Chris Talkington
b854551c77 Use entity descriptions for IPP (#93888) 2023-08-23 09:34:21 -05:00
Franck Nijhof
39c0689fe6 Revert "Add state classes to adguard sensors" (#98880) 2023-08-23 16:26:20 +02:00
Dennis
57990c7597 Add state classes to adguard sensors (#98577) 2023-08-23 16:09:07 +02:00
Erik Montnemery
6be20b5408 Add preview support to binary sensor group (#98872) 2023-08-23 14:24:48 +02:00
Erik Montnemery
e3b945a8d0 Don't allow numerical sensor state to be NaN or inf (#98110) 2023-08-23 14:16:40 +02:00
Erik Montnemery
3b16a3e1e0 Small typing fix in binary_sensor group (#98874) 2023-08-23 14:04:06 +02:00
Nathan Spencer
0aeeac5c42 Bump pylitterbot to 2023.4.5 (#98854) 2023-08-23 12:19:25 +02:00
Marc Mueller
480c34180e Fix forked_daapd test RuntimeWarning (#98864) 2023-08-23 12:17:32 +02:00
Joost Lekkerkerker
4f9c6351b0 Use constructor in Freebox config flow (#98870)
Create data object in init
2023-08-23 12:08:01 +02:00
Joost Lekkerkerker
5b3c60527a Clean up Freebox config flow (#97347)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-08-23 11:52:28 +02:00
Joost Lekkerkerker
a2b0149677 Remove config name from IPMA config flow (#98576) 2023-08-23 11:35:04 +02:00
Barry Williams
918d822ec7 Refactor openhome media player getters and attrs (#98690) 2023-08-23 11:31:46 +02:00
Erik Montnemery
b143fe285f Enable code coverage for metoffice sensor + weather (#98863) 2023-08-23 12:29:03 +03:00
Marc Mueller
5ae366957f Fix imap test RuntimeWarning (#98865) 2023-08-23 10:52:19 +02:00
Álvaro Fernández Rojas
6be47b1fbd Fix Airzone Cloud diagnostics (#98857)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-23 09:20:53 +02:00
Joost Lekkerkerker
26d7e9958f Remove YAML solution from Open Exchange Rates (#98815) 2023-08-23 08:11:28 +02:00
Charles Garwood
65691fffd6 Change Enphase dry contact relay binary_sensor to switch (#98467)
* Switch relay status from binary_sensor to switch

* docstring

* Bump pyenphase to 1.7.1

* review comments pt1

* review comments pt2

* Mutate data in lib instead of HA

* Bump pyenphase to 1.8.1
2023-08-22 21:28:39 -04:00
Joost Lekkerkerker
c4ae9ae430 Remove data rate converting code from NZBGet (#98806) 2023-08-22 23:23:13 +02:00
Álvaro Fernández Rojas
30628766ae Update AEMET-OpenData to v0.3.0 (#98810) 2023-08-22 23:21:42 +02:00
Ville Skyttä
6399d74c15 Remove unnnecessary pylint configs from core (#98704) 2023-08-22 23:12:12 +02:00
Joost Lekkerkerker
e9af99e469 Add entity translations to PECO (#98847) 2023-08-22 23:10:02 +02:00
Joost Lekkerkerker
2ff5d6290f Migrate Prosegur to has entity name (#98845) 2023-08-22 23:09:18 +02:00
starkillerOG
ade1d33367 Add entity name translations for Reolink (#98589)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-22 23:07:31 +02:00
J. Nick Koston
49d73441bf Abort ESPHome connection when both name and mac address do not match (#98787) 2023-08-22 23:02:23 +02:00
Álvaro Fernández Rojas
99b5c4932f Add hot water sensor support to Airzone (#98500)
* airzone: sensors: add hot water support

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

* airzone: sensor: dhw: enable _attr_has_entity_name

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

* Add requested changes

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

---------

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-22 22:48:05 +02:00
Álvaro Fernández Rojas
af915f1425 Add Airzone Cloud System binary sensors (#95121)
* airzone_cloud: add System binary sensors

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

* airzone-cloud: add error example

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

---------

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-22 22:41:03 +02:00
Jan Bouwhuis
0f58007e97 Deprecate aux heat for mqtt climate (#98666) 2023-08-22 22:39:55 +02:00
Maximilian
d179f8b47d Add filter for affected areas in NINA warnings (#97053)
* Add affected areas to warnings

* Update config flow

* Remove option from config_flow

* Add regex check

* Remove regex check
2023-08-22 22:23:34 +02:00
Joost Lekkerkerker
b65e3ddc99 Add entity translations to OVO Energy (#98835) 2023-08-22 22:21:20 +02:00
Joost Lekkerkerker
afc3899a1b Add device info to peco (#98836) 2023-08-22 22:16:30 +02:00
Joost Lekkerkerker
f10a5b7ee8 Add entity translations to Dexcom (#98795) 2023-08-22 22:09:18 +02:00
Jesse Hills
57bc8ae68e Set assist pipeline binary sensor to true only when stt-start is received (#98844) 2023-08-23 08:00:38 +12:00
J. Nick Koston
35a8385d9d Bump zeroconf to 0.82.1 (#98839) 2023-08-22 22:00:25 +02:00
Joost Lekkerkerker
0ab0901f0f Add entity translations to Powerwall (#98843) 2023-08-22 21:58:57 +02:00
Shay Levy
0d55718117 Downgrade Debouncer call ignored log message (#98840) 2023-08-22 14:42:33 -05:00
Joost Lekkerkerker
342e55409a Add entity translations to OpenGarage (#98834) 2023-08-22 19:55:47 +02:00
Marc Mueller
3f2c03fe77 Add input option to skip coverage [ci] (#98821) 2023-08-22 19:53:55 +02:00
mkmer
daade26466 Bump aiosomecomfort to 0.0.16 in Honeywell (#98824)
bump aiosomecomfort to 0.0.16
2023-08-22 19:35:57 +03:00
Joost Lekkerkerker
19576e6c95 Add options flow to OpenSky (#98177) 2023-08-22 18:06:19 +02:00
starkillerOG
59900a49e2 Add Reolink AI detection delay time (#98398) 2023-08-22 18:02:44 +02:00
J. Nick Koston
10b3cc4dd6 Bump zeroconf to 0.81.0 (#98826) 2023-08-22 10:58:29 -05:00
Michael Hansen
07884026c6 Detect wake word services in hassio discovery (#98827) 2023-08-22 11:31:09 -04:00
J. Nick Koston
d0fc0aea40 Retry lifx setup later if device has an unexpected serial (#98783) 2023-08-22 10:17:15 -05:00
Robert Resch
426fd62ee3 Adjust hassfest to require translations for core services (#98814)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-08-22 17:05:53 +02:00
Joost Lekkerkerker
890efd58e0 Add entity translations to Roku (#96083)
* Add entity translations to Roku

* Add entity translations to Roku
2023-08-22 16:59:56 +02:00
Shay Levy
406f06f0fc Abort Shelly setup if MAC address mismatch (#98807) 2023-08-22 07:41:50 -05:00
Joost Lekkerkerker
097c7fbfef Add entity translations to Nexia (#98803) 2023-08-22 07:41:15 -05:00
Joost Lekkerkerker
09efd1c972 Migrate Oncue to has entity name (#98812) 2023-08-22 07:37:33 -05:00
Ernst Klamer
1369874348 Add text sensor to BTHome (#98355) 2023-08-22 07:34:26 -05:00
tronikos
32d8d65add Bump androidtvremote2 to 0.0.14 (#98801) 2023-08-22 12:28:19 +02:00
Joost Lekkerkerker
08707b4abd Add entity translations to Logi circle (#98797) 2023-08-22 12:24:23 +02:00
Joost Lekkerkerker
5ad97827cf Use snapshot assertion for Airly diagnostics (#98726) 2023-08-22 12:17:43 +02:00
Maciej Bieniek
17050a3286 Add support for Shelly Gas Valve addon (#98705)
* Support Gas Valve

* Treat opening and closing as open

* Use set_state()

* Change entity icon and name

* Add valve state sensor

* Closing == closed

* Add translations for valve state entity

* Valve state -> Valve status

* Add tests; use control_result

* Fix mypy error

* Add missing "valve" to the Mock

* Improve docstrings

* Fix climate platform tests

* Increase test coverage

* Add mising return
2023-08-22 11:53:52 +03:00
Joost Lekkerkerker
c025244ac1 Add entity translations to Modem callerID (#98798) 2023-08-22 10:34:39 +02:00
Erik Montnemery
b885dfa5a8 Add preview to sensor group config and option flows (#83638) 2023-08-22 10:29:16 +02:00
Erik Montnemery
52b1e34af0 Migrate openweathermap weather to CoordinatorEntity (#98799) 2023-08-22 10:27:34 +02:00
Florian Bachmann
00b75ce58d Allows the supervisor to send a session's user to addon with header X-Remote-User (#88472)
* Working draft for x-remote-user

* Adds comment

* Submits user id instead of its name

* Move lines out of try-catch block

* Updates payload attribute

* Removes unnecessary user data from user info API

* revert changes
2023-08-22 10:14:21 +02:00
Erik Montnemery
68e2809c36 Modernize nws weather (#98748) 2023-08-22 10:01:17 +02:00
Erik Montnemery
79811984f0 Modernize open_meteo weather (#98504) 2023-08-22 09:43:33 +02:00
Joost Lekkerkerker
3e56d27bf7 Add device info to FOSCAM (#98167) 2023-08-22 09:37:37 +02:00
starkillerOG
2a78d7fa2d Add Reolink zoom in/out buttons (#97638) 2023-08-22 09:33:32 +02:00
Joost Lekkerkerker
6f7c3c949c Add snapshot assertion to Airvisual Pro (#98759) 2023-08-22 09:31:31 +02:00
Joost Lekkerkerker
d4b49726f4 Add snapshot assertion to Airzone cloud (#98761) 2023-08-22 09:30:43 +02:00
Joost Lekkerkerker
a89c0c944a Add device info to Life360 (#98772) 2023-08-22 09:28:47 +02:00
Joost Lekkerkerker
a0a06f16a7 Add entity translations to Bosch SHC (#98750) 2023-08-22 09:26:49 +02:00
Chris Talkington
2e0038b981 Require device id for Roku entities (#98734) 2023-08-22 09:22:46 +02:00
Joost Lekkerkerker
0f2b8570d2 Add device to Dexcom (#98574) 2023-08-22 09:20:30 +02:00
Chris Talkington
c93fcc37c4 Update pyipp to 0.14.4 (#98791)
* update pyipp to 0.14.4

* hassfest
2023-08-22 07:48:48 +02:00
Erik Montnemery
3e7ec88703 Add CoordinatorWeatherEntity (#98777) 2023-08-21 23:10:16 +02:00
jan iversen
92258b8e6f Correct modbus swap/datatype error message (#98698) 2023-08-21 22:55:50 +02:00
Erik Montnemery
4a03f6482a Set thread dataset's preferred router on add if not set (#98639) 2023-08-21 22:46:15 +02:00
Klaas Schoute
07fb47b849 Use VehicleType enum for Garages Amsterdam integration (#98780) 2023-08-21 22:45:29 +02:00
Joost Lekkerkerker
52cabed98f Migrate LastFM to has entity name (#98766) 2023-08-21 22:31:04 +02:00
Joost Lekkerkerker
53c118f652 Migrate LG Soundbar to has entity name (#98773) 2023-08-21 22:29:09 +02:00
Joost Lekkerkerker
5a835e703f Add entity translations to Honeywell Lyric (#98775) 2023-08-21 22:28:20 +02:00
Maciej Bieniek
f97f33fff7 Only create an issue if push updates fail 5 times in a row for Shelly gen1 devices (#98747) 2023-08-21 22:27:36 +02:00
J. Nick Koston
9123e13774 Remove unused code in doorbird (#98779) 2023-08-21 22:23:16 +02:00
J. Nick Koston
d582e60a6e Enable strict typing for doorbird (#98778) 2023-08-21 22:22:25 +02:00
Robert Svensson
d0d160f11c Unifi add port forward control to switch platform (#98309) 2023-08-21 22:01:44 +02:00
Dennis
78f0d8bc9c Add/Modify tomorrow.io sensor entity icons (#98648) 2023-08-21 21:59:56 +02:00
Joakim Plate
41db088f5d Update to 1.3.0 of gardena bluetooth (#98776) 2023-08-21 21:58:21 +02:00
Joost Lekkerkerker
30d3df2d96 Add morning and evening damping to Forecast solar (#98721)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2023-08-21 21:43:09 +02:00
Joost Lekkerkerker
a1d554d1cb Add entity translations to Hyperion (#98635) 2023-08-21 21:40:01 +02:00
Joost Lekkerkerker
baf32658e5 Set battery device class in Logi Circle (#98774) 2023-08-21 21:30:23 +02:00
Guido Schmitz
365dc47740 Add update platform to devolo Home Network (#86003)
* Add update platform

* Take care of progress

* Adapt to recent development

* Only add platform if supported

* Avoid unneeded line change

* Fix ruff in tests

* Handle update failures like in button platform

* Apply suggestions

* Fix tests

* Remove unused logger
2023-08-21 20:59:58 +02:00
Franck Nijhof
c39fc0766e Remove repair issue for MQTT discovered items (#98768) 2023-08-21 20:03:19 +02:00
Joost Lekkerkerker
3eb2b7010d Add device info to LG Soundbar (#98771) 2023-08-21 19:54:17 +02:00
Joost Lekkerkerker
07ffbe82c1 Add snapshot assertion to Ambient Station (#98764) 2023-08-21 19:46:36 +02:00
Marc Mueller
2369964f27 Update aws boto dependencies (#98619) 2023-08-21 19:40:21 +02:00
Ville Skyttä
2399cd283a Python 3.10 support cleanups (#98640) 2023-08-21 19:14:07 +02:00
G Johansson
faf0f5f19b Fix default values in Scrape (#98755) 2023-08-21 19:10:35 +02:00
Joost Lekkerkerker
2d46b589b9 Add entity translations to Kraken (#98765) 2023-08-21 19:09:51 +02:00
J. Nick Koston
6023ee0cc4 Bump dbus-fast to 1.93.0 (#98758) 2023-08-21 19:08:26 +02:00
Martin Hjelmare
91df9434d0 Use storage helper in feedreader (#98754) 2023-08-21 18:21:34 +02:00
Jesse Hills
a42d975c49 ESPHome Wake Word support (#98544)
* ESPHome Wake Word support

* Remove all vad code from esphome integration

* Catch exception when no wake word provider found

* Remove import

* Remove esphome vad tests

* Add tests

* More tests
2023-08-21 12:13:02 -04:00
Florent Thiery
c86565b9bc Reduce Freebox router Raid warning to one occurence (#98740)
* consider Freebox router does not support Raid if the first enumeration raised an http error, fixes #98274

* add router name to warning message

* reduce log level to info, remove details
2023-08-21 17:45:15 +02:00
ZigStar
af0e48081f Add ZigStar UZG-01 ZHA zeroconf autodiscovery (#98657)
ZigStar UZG-01 ZHA Zeconf Autodiscovery
2023-08-21 11:28:42 -04:00
Marco Garzola
b8086f3c21 Map heatercooler rotation speed as 3 level fan speed in homekit controller (#98291)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-21 10:10:24 -05:00
G Johansson
207e3f90a6 Modernize template weather (#98064)
* Modernize template weather

* mods

* adds templates

* Fixes

* review comments

* more comments

* Fix validator

* Tests

* Mods

* Fix ruff
2023-08-21 15:48:14 +02:00
G Johansson
d9906b63b7 Add payload to Scrape config flow (#98412)
Payload to config flow
2023-08-21 15:47:11 +02:00
J. Nick Koston
00904a107d Bump yalexs to 1.8.0 (#98751) 2023-08-21 08:44:45 -05:00
Joost Lekkerkerker
4518dad83b Use snapshot assertion for Airnow diagnostics (#98727) 2023-08-21 14:19:21 +02:00
Álvaro Fernández Rojas
4a7088a996 Update aioairzone to v0.6.7 (#98744) 2023-08-21 14:10:41 +02:00
Joost Lekkerkerker
604964d5f0 Use shorthand attributes in GDACS (#98173) 2023-08-21 14:02:29 +02:00
G Johansson
fb56dd0615 Fix LiteJet import config issue (#97679) 2023-08-21 12:20:48 +02:00
Michael Arthur
82b3ced4f1 Add lawnmower entity (#93623)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-21 12:19:55 +02:00
Joost Lekkerkerker
538de6d1f3 Introduce base class for Neato (#98071)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-08-21 12:04:12 +02:00
G Johansson
1a4fb90897 Clean off unnecessary logger in Workday (#98741) 2023-08-21 12:03:01 +02:00
Jakob Schlyter
180dd3d11a Bump pyspcwebgw to 0.7.0 (#98593) 2023-08-21 12:01:49 +02:00
Joost Lekkerkerker
9c54c4abf5 Use snapshot assertion for KNX diagnostics (#98724)
Co-authored-by: farmio <farmio@alphart.net>
2023-08-21 11:48:55 +02:00
Chris Talkington
1fca665b77 Use snapshot assertion for Roku diagnostics (#98731)
* use snapshot assertion for Roku diagnostics

* Delete roku3-diagnostics-data.json

* fix state time variation. add snapshot.
2023-08-21 11:48:27 +02:00
Joost Lekkerkerker
8f9529d376 Use snapshot assertion for Forecast solar diagnostics (#98723) 2023-08-21 11:18:18 +02:00
Joost Lekkerkerker
973928ffe9 Use snapshot assertion for Airvisual diagnostics (#98728) 2023-08-21 11:17:43 +02:00
Chris Talkington
c0bb3dd6e0 Use snapshot assertion for Jellyfin diagnostics (#98732) 2023-08-21 11:15:58 +02:00
Álvaro Fernández Rojas
f373b27a3b Update aioqsw to v0.3.3 (#98737) 2023-08-21 11:14:54 +02:00
Markus Ressel
30a0cb1674 Fix octoprint down every two minutes (#90001) 2023-08-21 11:09:39 +02:00
J. Nick Koston
605c55109d Bump yalexs to 1.7.0 (#98720) 2023-08-21 10:56:39 +02:00
J. Nick Koston
976f6582e1 Reduce overhead to update august activities (#98730) 2023-08-21 10:48:54 +02:00
Shay Levy
a713d7585f Bump aioshelly to 6.0.0 (#98719) 2023-08-21 10:49:11 +03:00
Joost Lekkerkerker
af689d7c3e Use snapshot assertion for Accuweather diagnostics (#98725) 2023-08-21 09:12:43 +02:00
Joakim Plate
687bf5e808 Ignore ble name for gardena (#98126) 2023-08-21 08:43:52 +02:00
G Johansson
a29e4a5f02 Move Workday failures to __init__ (#98651)
Workday failures in init
2023-08-20 22:30:43 +02:00
tronikos
53b596101b Bump opower to 0.0.31 (#98716) 2023-08-20 22:29:16 +02:00
Ville Skyttä
c6c9d23530 Remove no longer used nest binary sensor (#98714) 2023-08-20 13:09:36 -07:00
Marc Mueller
4c3640878f Filter some pytest warnings (#98689) 2023-08-20 18:30:28 +02:00
J. Nick Koston
38af44225e Refactor doorbird to avoid using events internally (#98585) 2023-08-20 07:49:33 -05:00
Erik Montnemery
f07724ff52 Add additional tasmota tests (#98695) 2023-08-20 14:02:53 +02:00
Niels Perfors
614904512c Verisure Improve Unpack (#98696) 2023-08-20 13:45:58 +02:00
Erik Montnemery
e484066f2b Remove dead code from __main__.py (#98694) 2023-08-20 10:17:28 +02:00
Erik Montnemery
3fc043d99e Deduplicate Tasmota sensor tests (#98628) 2023-08-20 09:04:48 +02:00
andresb5555
384cee481a Add vicare sensor gas_summary_consumption_heating_lastsevendays (#95280)
* Add missing sensor entity gas_summary_consumption_heating_lastsevendays

* Changed location of the sensor
gas_summary_consumption_heating_lastsevendays code as per suggestion by
joostlek
2023-08-19 21:38:03 +02:00
Maikel Punie
5f5c012b0a Duotecno code-cleanup (#98675)
* small code cleanup, use a wrapper for the commands

* Better decorator naming

* update the error information
2023-08-19 21:34:07 +02:00
hahn-th
f020d17dd8 Support Eco Mode Preset in Climates (#98359)
* Fix #86145

* Add missing test coverage

* Add tests for state
2023-08-19 21:32:20 +02:00
Charles Garwood
91965a74d8 Enphase remove operating (#98682) 2023-08-19 12:30:12 -05:00
Benjamin Paul [MSFT]
5965918c86 Add Fan Speed number entity to Nexia (#98642)
* Add Fan Speed support to Nexia integration

Adds a new "set_fan_speed" service to the Nexia integration, to allow
setting speed of the air-handler/blower fans.

* Add Fan Speed to Nexia Tests

* Remove mistakenly-added changes to Climate tests

A previous version of this change made modifications to the base
Climate entity, but that approach was later abandonded. These changes
to Climate tests were left over from that, and should never have been
included.

* Add Fan Speed Number Entity

* Remove Set Fan Speed Service

* Remove fan_speed attribute

The fan_speed attribute is not necessary with the new Number entity.

* Address reviewer feedback

Rename test function to reflect fact that fan speed entities are
entities, and not sensors.
Added missing typing to variables.
Sorted list of platforms

* Add test_set_fan_speed

Also adds new test fixture for mock response to API call

* Update homeassistant/components/nexia/number.py

* Name change

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-19 16:42:13 +02:00
Ville Skyttä
3094991236 Upgrade ruff to 0.0.285 (#98647) 2023-08-19 08:17:17 -04:00
J. Nick Koston
f318063a77 Bump dbus-fast to 1.92.0 (#98656) 2023-08-19 06:13:22 -05:00
starkillerOG
39fc4b3d66 Reolink add pan position sensor (#98592)
* Add PTZ pan position sensor

* fix typing

* fix typing
2023-08-19 12:31:40 +02:00
Maciej Bieniek
66c10facfa Add Tractive sleep and activity sensors (#98575)
* Add sleep and activity sensors

* Use device class ENUM

* Default value for value_fn
2023-08-19 11:48:23 +02:00
Jack Boswell
8a6bde1191 Add Starlink device tracker (#91445)
* Fetch location data and redact in diagnostics

* Implement device tracker

* Fix failing tests

* Update starlink-grpc-core

* Update coveragerc

* Hardcode GPS source type

* Use translations

* Move DEVICE_TRACKERS a little higher in the file

* Separate status and location check try/catches

* Revert "Separate status and location check try/catches"

This reverts commit 7628ec62f639845e9a7f5b460b8c66aad1d1dca3.
2023-08-19 11:36:23 +02:00
Maciej Bieniek
c526d23686 Change naming of Shelly entities to correspond with HA guidelines (#97533)
* Do not use the device name to create the entity name

* Remove unnecessary return

* Fix mypy complains

* Gen1

* Uncapitalize description.name if channel name is used

* Fix for climate and button

* switch_3 -> switch 3

* Add _attr_has_entity_name to ShellyRestAttributeEntity

* Capitalize channel name
2023-08-19 11:13:22 +02:00
tronikos
7059252164 Bump opowerto 0.0.30 (#98660) 2023-08-19 08:57:25 +02:00
Marc Mueller
c1fb97f26b Fix aiohttp DeprecationWarning (#98626) 2023-08-19 02:28:27 +02:00
jan iversen
1a032cebdd modbus: slave is allowed with custom (#98644) 2023-08-18 23:18:55 +02:00
Jan Bouwhuis
a39af8aff9 Fix rest debug logging (#98649)
Correct rest debug logging
2023-08-18 23:03:56 +02:00
Robert Svensson
9e42451934 UniFi refactor using site data (#98549)
* Clean up

* Simplify admin verification

* Streamline using sites in config_flow

* Bump aiounifi
2023-08-18 22:44:59 +02:00
Arkadii Yakovets
7827f9ccae Update country province validation (#84463)
* Update country `province` validation.

* Run pre-commit.

* Add tests

* Mod config flow

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-18 21:20:04 +02:00
Diogo Gomes
f96446cb24 Clean up integration sensor (#98552)
always update
2023-08-18 20:45:12 +02:00
G Johansson
268e5244f0 Cleanup ManualTriggerSensorEntity (#98629)
* Cleanup ManualTriggerSensorEntity

* ConfigType
2023-08-18 20:19:17 +02:00
Erik Montnemery
7fcc2dd44e Make the check_config script open issue_registry read only (#98545)
* Don't blow up if validators can't access the issue registry

* Make the check_config script open issue_registry read only

* Update tests/helpers/test_issue_registry.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-18 20:15:00 +02:00
Erik Montnemery
90b9764578 Bump hatasmota to 0.7.0 (#98636)
* Bump hatasmota to 0.7.0

* Update tests according to new entity naming
2023-08-18 19:24:33 +02:00
Ville Skyttä
93683cef27 Use zoneinfo instead of pytz, mark pytz as banned in ruff (#98613)
Refs #43439, #49643.
2023-08-18 19:10:29 +02:00
G Johansson
4073f56c5e Remove default code in Yale Smart Living (#94675)
* Remove default code in Yale Smart Living

* Test and remove check

* Finalize

* migration

* add back

* add back 2

* Fix tests

* Fix migration if code not exist
2023-08-18 16:40:24 +02:00
G Johansson
4096de2dad Add Yale Smart Living diagnostics test (#98590)
* Yale test diagnostics

* clean

* From review
2023-08-18 16:31:07 +02:00
Robert Resch
790523126e Name unnamed update entities by their device class (#98579) 2023-08-18 13:40:35 +02:00
jan iversen
c268adb07e modbus: Repair swap for slaves (#97960) 2023-08-18 13:23:04 +02:00
Joakim Plate
fc444e4cd6 Allow control of pump mode for nibe (#98499)
* Allow control of pump mode

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-18 13:15:59 +02:00
jan iversen
1c56c39897 modbus config: count and slave_count can normally not be mixed. (#97902) 2023-08-18 13:10:13 +02:00
Bram Kragten
66685b796d Update frontend to 20230802.1 (#98616) 2023-08-18 13:09:35 +02:00
Jan Bouwhuis
5ef6c03610 Log entity_id payload and template on MQTT value template error (#98353)
* Log entity_id payload and template on error

* Also handle cases with default values.

* Do not log payload twice

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

* Tweak test to assert without payload

* black

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-18 13:05:53 +02:00
Joost Lekkerkerker
80a5e341b5 Add device to Garage Amsterdam entity (#98573) 2023-08-18 11:48:00 +02:00
Robert Resch
7ac2c61f24 Fix copy-paste error in comments of number tests (#98615) 2023-08-18 11:02:30 +02:00
jan iversen
59d37f65d5 Correct modbus config validator: slave/swap (#97798) 2023-08-18 10:55:39 +02:00
jan iversen
e42b9e6c4c Modbus: set state_class etc in slaves. (#98332) 2023-08-18 10:52:57 +02:00
Luca Leonardo Scorcia
9be532cea9 Fix inconsistent lyric temperature unit (#98457) 2023-08-18 10:52:22 +02:00
Maciej Bieniek
d5338e88f2 Fix the availability condition for Shelly N current sensor (#98518) 2023-08-18 10:49:43 +02:00
jan iversen
5a7084e78c Correct number of registers to read for sensors for modbus (#98534) 2023-08-18 10:48:57 +02:00
Franck Nijhof
2f204d5747 Remove unneeded startswith in content check of image upload (#98599) 2023-08-18 10:38:21 +02:00
Niels Perfors
89705a22cf Verisure unpack (#98605) 2023-08-18 10:26:01 +02:00
J. Nick Koston
d3ee2366b0 Bump dbus-fast to 1.91.4 (#98600) 2023-08-18 03:09:15 -05:00
tronikos
6b82bf2bc7 Fix Flume leak detected sensor (#98560) 2023-08-18 03:07:44 -05:00
Simone Chemelli
ab9d6ce61a New integration for Comelit SimpleHome (#96552)
* New integration for Comelit SimpleHome

* Address first review comments

* cleanup

* aiocomelit bump and coordinator cleanup

* address review comments

* Fix some review comments

* Use config_entry.unique_id as last resort

* review comments

* Add config_flow tests

* fix pre-commit missing checks

* test_conflig_flow coverage to 100%

* fix tests

* address latest review comments

* new ruff rule

* address review comments

* simplify unique_id
2023-08-18 08:40:23 +02:00
Faidon Liambotis
9fdad592c2 Add option to disable MQTT Alarm Control Panel supported features (#98363)
* Make MQTT Alarm Control Panel features conditional

The MQTT Alarm Control Panel currently enables all features (arm home,
arm away, arm night, arm vacation, arm custom bypass) unconditionally.
This clutters the interface and can even be potentially dangerous, by
enabling modes that the remote alarm may not support.

Make all the features conditional, by adding a new "supported_features"
configuration option, comprising a list of the supported features as
options. Feature enablement seems inconsistent across the MQTT
component; this implementation is most alike to the Humidifier modes
option, but using a generic "supported_features" name that other
implementations may reuse in the future.

The default value of this new setting remains to be all features, which
while it may be overly expansive, is necessary to maintain backwards
compatibility.

* Apply suggestions from code review

* Use vol.Optional() instead of vol.Required() for "supported_features".
* Move the initialization of _attr_supported_features to _setup_from_config.

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

* Apply suggestions from emontnemery's code review

* Use vol.In() instead of cv.multi_seelct()
* Remove superfluous _attr_supported_features initializers, already
  present in the base class.

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

* Add invalid config tests for the MQTT Alarm Control Panel

* Set expected_features to None in the invalid MQTT Alarm Control Panel tests

* Add another expected_features=None in the invalid tests

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

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-18 08:23:48 +02:00
lymanepp
f6a9be937b Add humidity and dew point to tomorrow.io integration (#98496)
* Add humidity and dew point to tomorrow.io integration

* Fix ruff complaints

* Make mypy happy

* Merge emontnemery's changes

* Fix formatting error

* Add fake humidity and dew point to test data (first interval only)

* Fix inconsistency

* Fix inconsistency
2023-08-18 07:41:25 +02:00
Michael Hansen
49d2c60992 Add pipeline VAD events (#98603)
* Add stt-vad-start and stt-vad-end pipeline events

* Update tests
2023-08-17 19:58:58 -04:00
Erik Montnemery
c17f08a3f5 Create a single entity for new met.no config entries (#98098)
* Create a single entity for new met.no config entries

* Fix lying docstring

* Fix test
2023-08-17 19:41:11 +02:00
Erwin Douna
d761b5ddbf Add tests and typing to Tado config flow (#98281)
* Upgrading tests

* Code improvements and removing unused function

* Update homeassistant/components/tado/config_flow.py

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

* Update homeassistant/components/tado/config_flow.py

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

* Update homeassistant/components/tado/config_flow.py

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

* Update tests/components/tado/test_config_flow.py

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

* Update tests/components/tado/test_config_flow.py

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

* Update tests/components/tado/test_config_flow.py

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

* Update tests/components/tado/test_config_flow.py

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

* Update tests/components/tado/test_config_flow.py

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

* Importing Any

* Rerunning Blackformatter

* Adding fallback scenario to options flow

* Adding constants

* Adding a retry on the exceptions

* Refactoring to standard

* Update homeassistant/components/tado/config_flow.py

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

* Adding type to validate_input

* Updating test

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-17 19:37:34 +02:00
Joost Lekkerkerker
dd69ba3136 Migrate Cert Expiry to has entity name (#98160)
* Migrate Cert Expiry to has entity name

* Migrate Cert Expiry to has entity name

* Fix entity name
2023-08-17 18:29:20 +02:00
epenet
a9b1f23b7f Bump renault-api to 0.2.0 (#98587) 2023-08-17 18:16:32 +02:00
Russell Cloran
49995a4667 Add tests for device tracker in Prometheus (#98054) 2023-08-17 17:58:52 +02:00
J. Nick Koston
3e14e5acba Bump aioesphomeapi to 16.0.1 (#98536) 2023-08-17 17:46:21 +02:00
Marc Mueller
529bc507a0 Fix aiohttp test RuntimeWarning (#98568) 2023-08-17 17:42:20 +02:00
J. Nick Koston
e95979e9af Bump ESPHome recommended BLE version to 2023.8.0 (#98586) 2023-08-17 17:39:35 +02:00
Franck Nijhof
740cabc21e Pin setuptools to 68.0.0 (#98582) 2023-08-17 17:36:22 +02:00
Maciej Bieniek
d44847bb23 Log Tractive events on debug level (#98539) 2023-08-17 17:09:16 +02:00
J. Nick Koston
30a88e9e61 Additional doorbird cleanups to prepare for event entities (#98542) 2023-08-17 09:37:54 -05:00
Erwin Douna
2d4decc9b1 Revert "Integration tado bump" (#98505)
Revert "Integration tado bump (#97791)"

This reverts commit 65365d1db5.
2023-08-17 16:16:47 +02:00
Joost Lekkerkerker
6f4294dc62 Migrate IPMA to has entity name (#98572)
* Migrate IPMA to has entity name

* Migrate IPMA to has entity name
2023-08-17 16:02:22 +02:00
Erwin Douna
ea5272ba62 Revert "Fix fanSpeed issue in Tado" (#98506)
Revert "Fix fanSpeed issue (#98293)"

This reverts commit d6498aa39e.
2023-08-17 15:44:23 +02:00
Øyvind Matheson Wergeland
cb4917f880 Fix GoGoGate2 configuration URL when remote access is disabled (#98387) 2023-08-17 15:12:35 +02:00
Maciej Bieniek
d6a7127b84 Improve availability of Tractive entities (#97091)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-08-17 12:15:36 +02:00
Dennis
1954539e65 Add state_class to tomorrowio UV Index (#98541)
* Added state_class to UV Index

Forgot to add a state_class as other sensors got their state_class from their device class. As there is no UV Index device class I left it out.

* Forgotten a comma, whoops

* Changed measurement to string.

* Changed from "measurement" to SensorStateClass
2023-08-17 11:13:11 +02:00
Klaas Schoute
8b4937f627 Bump odp-amsterdam to v5.3.0 (#98555)
* Bump package to v5.3.0

* Load only the garages for cars
2023-08-17 10:24:58 +02:00
G Johansson
6faa9abc75 Fix Verisure config entry migration (#98546) 2023-08-17 08:51:59 +02:00
Sebastian Lövdahl
fde498586e Expose dew point in Met.no (#98543) 2023-08-17 07:45:23 +02:00
puddly
52a8f01096 Make IKEA fan sensors diagnostic in ZHA (#97747) 2023-08-17 02:15:35 +01:00
Marc Mueller
a776ecddb7 Update mypy to 1.5.1 (#98554) 2023-08-16 18:44:02 -05:00
Erik Montnemery
992cc56c7e Modernize buienradar weather (#98473) 2023-08-16 22:19:22 +02:00
Erik Montnemery
1897be1467 Map demo and kitchen_sink weather condition codes once (#98510)
Map demo and kitchen_sink condition codes once
2023-08-16 22:12:22 +02:00
Erik Montnemery
614d6e929d Map meteoclimatic weather condition codes once (#98514) 2023-08-16 22:11:27 +02:00
Erik Montnemery
f85c2e5a92 Modernize environment_canada weather (#98502) 2023-08-16 22:10:48 +02:00
Erik Montnemery
227d4a590d Map metoffice weather condition codes once (#98515) 2023-08-16 22:09:06 +02:00
Erik Montnemery
f135c42524 Map openweathermap weather condition codes once (#98516) 2023-08-16 22:08:17 +02:00
Erik Montnemery
f643d2de46 Map SMHI weather condition codes once (#98517) 2023-08-16 22:07:12 +02:00
Erik Montnemery
5c1c8dc682 Modernize tomorrowio weather (#98466)
* Modernize tomorrowio weather

* Add test snapshot

* Update snapshots

* Address review comments

* Improve test coverage
2023-08-16 20:22:38 +02:00
Erik Montnemery
827e06a5c8 Improve typing of nws (#98485)
* Improve typing of nws

* Address review comments
2023-08-16 20:21:07 +02:00
Erik Montnemery
b1053e8077 Map accuweather weather condition codes once (#98509)
Map accuweather condition codes once
2023-08-16 20:20:47 +02:00
Erik Montnemery
8ed7d2dd3e Don't create certain start.ca sensors for unlimited plans (#98525)
Don't create certain startca sensors for unlimited setups
2023-08-16 20:20:14 +02:00
Erik Montnemery
4eb0f1cf37 Make eufylife_ble sensors inherit RestoreSensor (#98528) 2023-08-16 20:15:47 +02:00
Erik Montnemery
31f5932fe4 Log events with no listeners (#98540)
* Log events with no listeners

* Unconditionally create the Event object

* Reformat code
2023-08-16 20:14:49 +02:00
Joost Lekkerkerker
b9203cbeaf Add base entity for Dexcom (#98158) 2023-08-16 19:18:46 +02:00
mkmer
3e1d2a1000 Handle missing keys in Honeywell (#98392) 2023-08-16 18:59:34 +02:00
J. Nick Koston
5bf80a0f6d Make ESPHome deep sleep tests more robust (#98535) 2023-08-16 11:05:22 -05:00
Erik Montnemery
4180e2e477 Make EnOceanSensor a RestoreSensor (#98527)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-16 15:22:48 +02:00
Erik Montnemery
a2e619155a Map ipma weather condition codes once (#98512) 2023-08-16 15:01:54 +02:00
Nick Whyte
2c48f0e416 Fix ness alarm armed_home state appearing as disarmed/armed_away (#94351)
* Fix nessclient arm home appearing as arm away

* patch arming mode enum and use dynamic access

* Revert "patch arming mode enum and use dynamic access"

This reverts commit b9cca8e92bcb382abe364381a8cb1674c32d1d2a.

* Remove mock enums
2023-08-16 13:56:52 +02:00
Mike Heath
cf8c9ad184 Add PoE switch tests (#95087)
* Add PoE switch tests

* Update tests/components/tplink_omada/test_switch.py

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

* Remove files covered by tests from exclusion

* Rename entity_name to entity_id

* Fix test, use snapshot, other improvements

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-16 13:38:53 +02:00
Joost Lekkerkerker
732dac6f05 Create abstraction for Generic YeeLight (#97939)
* Create abstraction for Generic YeeLight

* Update light.py
2023-08-16 13:24:41 +02:00
Erik Montnemery
91faa53843 Don't allow hass.config.config_dir to be None (#98442) 2023-08-16 13:00:14 +02:00
VidFerris
5ed3e90607 Use Local Timezone for Withings Integration (#98137) 2023-08-16 12:57:16 +02:00
Diogo Gomes
abf065ed76 Fix checks for duplicated config entries in IPMA (#98319)
* fix unique_id

* old unique id detection

* update tests

* match entry not unique_id

* address review
2023-08-16 12:56:47 +02:00
J. Nick Koston
ed2f067c52 Bump zeroconf to 0.80.0 (#98416) 2023-08-16 12:03:40 +02:00
Erik Montnemery
636cb6279d Push updated ecobee weather forecast to listeners (#98511) 2023-08-16 11:59:59 +02:00
Erik Montnemery
e69090b943 Map meteo_france weather condition codes once (#98513) 2023-08-16 11:41:11 +02:00
Marc Mueller
0bcc02e908 Skip writing pyc files [ci] (#98423) 2023-08-16 11:36:52 +02:00
J. Nick Koston
b083f5bf89 Add some typing to doorbird (#98483) 2023-08-16 11:33:25 +02:00
J. Nick Koston
b680bca5e9 Bump aiohomekit to 2.6.16 (#98490) 2023-08-16 11:30:47 +02:00
tronikos
a0ea6e6a0c Bump opower to 0.0.29 (#98503) 2023-08-16 11:10:02 +02:00
Andy Barratt
6c573953e3 Update Light flash description (#98252)
* Update Light flash description

`light.turn_on` service description for the `flash` option gave the impression of a boolean value being required when in fact a string of either `short` or `long` was required.  Updated this to match the documentation found at https://www.home-assistant.io/integrations/light

`light.turn_off` also described the existence of a `flash` option when none exists.  I've removed this, which matches the aforementioned documentation too.

* Revert removal of flash from turn-off

As discussed in feedback, turn-off does indeed seem to support flash.
2023-08-16 10:06:37 +02:00
Emma Vanbrabant
8efb9dad7e Add device_class to Renault charging remaining time (#98393)
* Add device_class on charging remaining time

Set `device_class to `duration` on the `charging_remaining_time` entity in the Renault integration.

I had some issues showing this property on my dashboard, and setting this fixed it. The recommendation to open an issue against the original integration in these kinds of cases came from [here](https://community.home-assistant.io/t/how-to-format-a-card-to-show-hours-instead-of-seconds/425473/7).

* Update test const to add duration

* fix other cars

* Update test_sensor.ambr

* add duration in ambr
2023-08-16 09:42:38 +02:00
Jan Bouwhuis
c010b97abf Improve test recovery MQTT certificate files (#98223)
* Improve test recovery MQTT certificate files

* Spelling
2023-08-16 09:07:14 +02:00
dalinicus
45966069b4 Bump aiolyric to 1.1.0 (#98113)
version bump aiolyric to include new features for additional room sensors
2023-08-16 08:15:28 +02:00
J. Nick Koston
c671b1069e Bump protobuf to 4.24.0 (#98468) 2023-08-15 18:52:18 -05:00
Erik Montnemery
3cf86d5d1f Create a single entity for new met_eireann config entries (#98100) 2023-08-15 20:56:19 +02:00
Erik Montnemery
caeb20f9c9 Modernize aemet weather (#97969)
* Modernize aemet weather

* Improve test coverage

* Only create a single entity for new config entries
2023-08-15 20:55:16 +02:00
J. Nick Koston
857369625a Remove some bound attributes from enphase_envoy sensor (#98479) 2023-08-15 13:29:22 -05:00
J. Nick Koston
80d608bb5b Remove some bound attributes from enphase_envoy binary_sensor (#98477)
Some of these were never used
2023-08-15 14:16:05 -04:00
J. Nick Koston
73f882bf36 Small cleanups to enphase_envoy select platform (#98476) 2023-08-15 13:08:55 -05:00
Charles Garwood
92535277be Add number platform & battery setpoint entities to Enphase integration (#98427)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-15 13:08:11 -05:00
J. Nick Koston
5a69f9ed04 Remove unused code in enphase_envoy (#98474) 2023-08-15 12:37:16 -05:00
Erik Montnemery
90413daa8a Update buienweather data before adding entities (#98455)
* Update buienweather data before adding entities

* Fix tests
2023-08-15 18:15:23 +02:00
Sam Reed
ffe3d7c255 Replace "percents" -> "percentage" in flux_led option flow (#98059) 2023-08-15 10:44:24 -05:00
Marc Mueller
063ce9159d Use asyncio.timeout [o-s] (#98451) 2023-08-15 17:21:49 +02:00
Erik Montnemery
496a975c58 Set _attr_condition in WeatherEntity (#98459) 2023-08-15 17:17:35 +02:00
starkillerOG
92cf6ed2a0 Reolink 100% test coverage (#94763) 2023-08-15 09:50:17 -05:00
J. Nick Koston
e209f3723e Restore sensorpush state when device becomes available (#98420) 2023-08-15 09:29:25 -05:00
Erik Montnemery
3de402bd15 Fix AiohttpClientMockResponse.release (#98458) 2023-08-15 09:22:42 -05:00
Marc Mueller
5dd3f05db8 Use asyncio.timeout [f-h] (#98449) 2023-08-15 08:37:06 -05:00
Marc Mueller
a9ade1f84d Use asyncio.timeout [core] (#98447) 2023-08-15 08:36:05 -05:00
Marc Mueller
e2d2ec8817 Use asyncio.timeout [b-e] (#98448) 2023-08-15 08:30:20 -05:00
Charles Garwood
346a7292d7 Update Enphase dry contact relay DeviceInfo and name (#98429)
Switch relay binary_sensor to relay device
2023-08-15 08:49:19 -04:00
Marc Mueller
8b0fdd6fd2 Use asyncio.timeout [s-z] (#98452) 2023-08-15 14:34:18 +02:00
Marc Mueller
71d985e4d6 Use asyncio.timeout [i-n] (#98450) 2023-08-15 14:32:15 +02:00
Erik Montnemery
35b914af97 Disable polling in buienradar weather entity (#98443) 2023-08-15 13:28:43 +02:00
Franck Nijhof
a87878f723 Make image upload mimetype to match frontend (#98411) 2023-08-15 12:26:37 +02:00
G Johansson
ed18c6a013 Refactor Rest Switch with ManualTriggerEntity (#97403)
* Refactor Rest Switch with ManualTriggerEntity

* Fix test

* Fix 2

* review comments

* remove async_added_to_hass

* update on startup
2023-08-15 11:43:47 +02:00
Nathan Spencer
87b7fc6c61 Bump pylitterbot to 2023.4.4 (#98414) 2023-08-15 11:04:45 +02:00
Erik Montnemery
3b9d6f2dde Add setup function to the component loader (#98148)
* Add setup function to the component loader

* Update test

* Setup the loader in safe mode and in check_config script
2023-08-15 10:59:42 +02:00
starkillerOG
b1e5b3be34 Bump Reolink_aio to 0.7.7 (#98425) 2023-08-15 10:43:19 +02:00
J. Nick Koston
262483f3f6 Replace async_timeout with asyncio.timeout A-B (#98415) 2023-08-15 10:29:28 +02:00
Chris Talkington
eb4745012a Update rokuecp to 0.18.1 (#98432) 2023-08-15 10:02:38 +02:00
Chris Talkington
2da3b7177d Update pyipp to 0.14.3 (#98434) 2023-08-15 09:57:10 +02:00
Erik Montnemery
94ad4786c3 Include extended address in response to WS otbr/info (#98440) 2023-08-15 09:48:29 +02:00
Erik Montnemery
e6ea70fd00 Adjust thread router discovery typing (#98439)
* Adjust thread router discovery typing

* Adjust debug logs
2023-08-15 09:40:05 +02:00
Erik Montnemery
71b92265af Include border agent id in response to WS otbr/info (#98394)
* Include border agent id in response to WS otbr/info

* Assert border agent ID is not None
2023-08-15 09:17:47 +02:00
Erik Montnemery
6c7f50b5b2 Simplify error handling in otbr async_setup_entry (#98395)
* Simplify error handling in otbr async_setup_entry

* Create issue if the OTBR does not support border agent ID

* Update test

* Improve test coverage

* Catch the right exception
2023-08-15 08:27:50 +02:00
Paulus Schoutsen
ced4af1e22 Ignore smartthings storage on fresh install (#98418)
* Ignore smartthings storage on fresh install

* Also unload existing things when going for clean install

* Rename param

* Fix tests
2023-08-14 21:39:05 -05:00
Charles Garwood
e3438baf49 Add select platform to Enphase integration (#98368)
* Add select platform to Enphase integration

* Review comments pt1

* Review comments pt2

* Review comments

* Additional tweaks from code review

* .coveragerc

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-14 20:23:16 -04:00
Joost Lekkerkerker
49a9d0e439 Add entity translations to hunterdouglas powerview (#98232) 2023-08-14 15:26:20 -05:00
Jan-Philipp Benecke
9713466817 Add sensor when meter last sent its data to Discovergy (#97223)
* Add sensor for last reading by Discovergy

* Rename sensor to make it clear what it actually is

* Revert back to single sensor classe and extend entity_description with a value function
2023-08-14 21:42:47 +02:00
J. Nick Koston
69b3ae4588 Bump zeroconf to 0.78.0 (#98405) 2023-08-14 21:07:17 +02:00
Erik Montnemery
6294014fcd Bump python-otbr-api to 2.5.0 (#98403) 2023-08-14 20:09:50 +02:00
Abílio Costa
80fa034048 ipma: remove abmantis from codeowners (#98304)
* ipma: remove abmantis from codeowners

I am not currently maintaining this integration.

* Run hassfest

* Try again

---------

Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-08-14 19:36:58 +02:00
Joost Lekkerkerker
318aa9b95a Add entity translations to Goodwe (#98224)
* Add entity translations to Goodwe

* Add entity translations to Goodwe
2023-08-14 19:35:31 +02:00
Álvaro Fernández Rojas
c3c00e6984 Update aioairzone to v0.6.6 (#98399) 2023-08-14 11:21:12 -05:00
Joakim Plate
d6fcdeac06 Avoid leaking backtrace on connection lost in arcam (#98277)
* Avoid leaking backtrace on connection lost

* Correct ruff error after rebase
2023-08-14 18:03:17 +02:00
Franck Nijhof
85c2216cd7 Ensure headers middleware handles errors too (#98397) 2023-08-14 17:48:11 +02:00
Marco Ranieri
54223fe06c Enable Alexa Unlock directive (#97127)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2023-08-14 17:47:50 +02:00
Joost Lekkerkerker
77b421887b Add entity translations for August (#98077) 2023-08-14 16:58:57 +02:00
Erik Montnemery
d4753ebd3b Include border agent ID in thread router discovery (#98378) 2023-08-14 16:46:55 +02:00
Martin Hjelmare
2272a9db00 Improve picotts (#98391) 2023-08-14 15:54:43 +02:00
Marc Mueller
d059c9924a Update attrs to 23.1.0 (#98385) 2023-08-14 15:50:43 +02:00
Erik Montnemery
1869177f08 Rename some incorrectly named assist_pipeline tests (#98389) 2023-08-14 15:47:55 +02:00
Erik Montnemery
e0fd83daab Store preferred border agent ID for each thread dataset (#98384) 2023-08-14 15:47:18 +02:00
Robert Resch
b0f68f1ef3 Use @require_admin decorator (#98061)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
2023-08-14 15:07:20 +02:00
Marc Mueller
525f39fe28 Update todoist-api-python to 2.1.2 (#98383) 2023-08-14 14:10:45 +02:00
G Johansson
a093c383c3 Remove Verisure default lock code (#94676) 2023-08-14 13:43:08 +02:00
Erik Montnemery
318b8adbed Set preferred router when importing OTBR dataset (#98379) 2023-08-14 13:40:32 +02:00
Allen Porter
9ddf11f6cd Improve rainbird error handling (#98239) 2023-08-14 13:32:08 +02:00
Martin Hjelmare
6f97270cd2 Fix tts notify config validation (#98381)
* Add test

* Require either entity_id or tts_service
2023-08-14 13:30:25 +02:00
Joost Lekkerkerker
57cacbc2a7 Add entity translations to Aurora (#98079) 2023-08-14 13:16:02 +02:00
Joost Lekkerkerker
398a789ba2 Add entity translations to justnimbus (#98235) 2023-08-14 13:14:49 +02:00
Joost Lekkerkerker
11b1a42a1c Add entity translations to Aseko (#98117) 2023-08-14 12:52:27 +02:00
Joost Lekkerkerker
9ce033daeb Use default translations by removing names from tplink descriptions (#98338) 2023-08-14 11:51:08 +02:00
Marc Mueller
180ff24492 Add types-beautifulsoup4 dependency (#98377) 2023-08-14 11:50:14 +02:00
Erik Montnemery
4dd102f818 Bump python-otbr-api to 2.4.0 (#98376) 2023-08-14 11:33:07 +02:00
Erik Montnemery
e0ee713bb2 Store preferred border agent ID in thread dataset store (#98375) 2023-08-14 11:32:55 +02:00
Michael
533a8beac2 Raise ConfigEntryNotReady when unable to connect during setup of AVM Fritz!Smarthome (#97985) 2023-08-14 10:42:20 +02:00
Marc Mueller
e0d6210bd0 Create pytest output artifact [ci] (#98106) 2023-08-14 10:38:53 +02:00
Marc Mueller
7cf1ff887d Update caldav to 1.3.6 (#98371) 2023-08-14 10:31:24 +02:00
Marc Mueller
f7d95e00f6 Update tqdm to 4.66.1 (#98328) 2023-08-14 10:29:26 +02:00
Marc Mueller
e36a8f6e8b Update async-timeout to 4.0.3 (#98370) 2023-08-14 10:23:23 +02:00
Marc Mueller
21acb5527f Update beautifulsoup to 4.12.2 (#98372) 2023-08-14 10:22:53 +02:00
Kevin Stillhammer
066db11620 Exchange WazeRouteCalculator with pywaze in waze_travel_time (#98169)
* exchange WazeRouteCalculator with pywaze

* directly use async is_valid_config_entry

* store pywaze client as property

* fix tests

* Remove obsolete error logs

* Reuse existing httpx client

* Remove redundant typing

* Do not clcose common httpx client
2023-08-14 10:02:30 +02:00
J. Nick Koston
96f9b852a2 Bump zeroconf to 0.76.0 (#98366) 2023-08-13 22:47:29 -05:00
J. Nick Koston
790c1bc251 Decrease event loop latency by binding time.monotonic to loop.time directly (#98288)
* Decrease event loop latency by binding time.monotonic to loop.time directly

This is a small improvment to decrease event loop latency. While the goal is
is to reduce Bluetooth connection time latency, everything using asyncio
is a bit more responsive as a result.

* relo per comments

* fix too fast by adding resolution, ensure monotonic time is patchable by freezegun

* fix test that freezes time too late and has a race loop
2023-08-13 20:37:45 -04:00
Marc Mueller
07e20e1eab Downgrade todoist-api-python to 2.0.2 and attrs to 22.2.0 (#98354) 2023-08-13 17:23:38 -05:00
J. Nick Koston
429f939fee Bump zeroconf to 0.75.0 (#98360) 2023-08-13 17:23:03 -05:00
Mr-Ker
66b01bee49 Add support for Bosch 2nd Gen Shutter Contact (#98331)
Add support for Bosch 2nd Gen SHCShutterContact2

We only need to check for the shutter contact 2 types as both devices
provide the same properties that are used by the bosch_shc component.

Resolves: #86295
2023-08-13 21:05:57 +02:00
Chris
5b6a7edd8d Add Unifi outlet switches for PDU devices (#98320)
Updates the Unifi outlet switching feature to support PDU devices
2023-08-13 20:06:12 +02:00
Marc Mueller
ef6e75657a Update attrs to 23.1.0 (#97095) 2023-08-13 19:05:15 +02:00
Marc Mueller
e25fdebda1 Add types-caldav dependency (#98265) 2023-08-13 11:58:55 -05:00
Marc Mueller
ee3af29701 Update coverage to 7.3.0 (#98327) 2023-08-13 11:58:34 -05:00
Marc Mueller
54cbc85c13 Add types-Pillow dependency (#98266) 2023-08-13 11:57:46 -05:00
Jan Bouwhuis
e5f7d83912 Update entity feature constants google_assistant (#98335)
* Update entity feature constants google_assistant

* Update tests

* Direct import

* Some missed constants

* Add fan and cover feature imports
2023-08-13 17:17:47 +02:00
Jan Bouwhuis
fa6ffd994a Update vacuum entity constants for Alexa tests (#98336)
* Update vacuum entity constants for Alexa tests

* Import VacuumEntityFeature
2023-08-13 15:35:00 +02:00
Jan Bouwhuis
b36681b318 Update homekit entity feature constants (#98337) 2023-08-13 15:33:36 +02:00
Renier Moorcroft
a74d83de66 Cleanup EZVIZ number entity (#98333)
* EZVIZ - Cleanup number entity

* NL

* Fix naming
2023-08-13 13:41:37 +02:00
Renier Moorcroft
00c60151d4 Add Ezviz siren entity (#93612)
* Initial commit

* Add siren entity

* Update coveragerc

* Cleanup unused entity description.

* Add restore and fix entity property to standards.

* Schedule turn off to match camera firmware

* Only add siren for devices that support capability

* Removed unused attribute and import.

* Add translation

* Update camera.py

* Update strings.json

* Update camera.py

* Cleanup

* Update homeassistant/components/ezviz/siren.py

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

* use description

* Apply suggestions from code review

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

* Update strings.json

* Dont inherit coordinator class.

* Add assumed state

* Update homeassistant/components/ezviz/siren.py

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

* Reset delay listener if trigered

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-13 13:10:53 +02:00
Bouwe Westerdijk
b41d3b465c Add domestic_hot_water_setpoint number to Plugwise (#98092)
* Add max_dhw_temperature number

* Update strings.json

* Add related tests

* Correct test

* Black-fix
2023-08-13 12:57:34 +02:00
Bouwe Westerdijk
38cea8f31c Plugwise climate: add HVAC_Mode handling to set_temperature() (#98273)
* Add HVAC_Mode handling to set_temperature()

* Move added code down, as suggested

* Implement walrus as suggested

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

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-13 12:39:46 +02:00
tronikos
fea4af69d7 Add missing logging for opower library (#98325) 2023-08-13 08:08:33 +02:00
J. Nick Koston
483567529f Bump flux-led to 1.0.2 (#98312)
changelog: https://github.com/Danielhiversen/flux_led/compare/1.0.1...1.0.2

fixes #98310
2023-08-12 21:10:36 -04:00
tronikos
bdaa2285fc Google Assistant SDK: Allow responses for send_text_command (#95966)
google_assistant_sdk.send_text_command response
2023-08-12 16:20:01 -04:00
Allen Porter
79991c32dc Bump pyrainbird to 4.0.0 (#98271) 2023-08-12 13:37:43 -05:00
Marc Mueller
85b097af50 Update todoist-api-python to 2.1.1 (#98263) 2023-08-12 09:39:59 -07:00
elmurato
836b2de86f Add dataclass for Minecraft Server data (#98297)
* Add dataclass for Minecraft server data

* Sort dataclass variables
2023-08-12 09:36:03 -07:00
Luke
4780ea6a5b Bump Python-Roborock to 0.32.3 (#98303)
bump to 0.32.3
2023-08-12 09:31:14 -07:00
Chris
87753bdb82 Add UniFi power stats for PDU overall AC outlet metrics (#98217) 2023-08-12 18:12:59 +02:00
Erik Montnemery
ae8f9dcb77 Modernize ipma weather (#98062)
* Modernize ipma weather

* Add test snapshots

* Don't include forecast mode in weather entity unique_id for new config entries

* Remove old migration code

* Remove outdated test
2023-08-12 15:15:09 +02:00
Joost Lekkerkerker
be9afd7eae Add entity translations to DWD (#98254)
* Add device to DWD

* Add entity translations to DWD
2023-08-12 11:03:37 +02:00
Erwin Douna
d6498aa39e Fix fanSpeed issue (#98293) 2023-08-12 10:27:45 +02:00
Bouwe Westerdijk
5042c25bbc Plugwise climate: remove extra_state_attributes property (#98153)
* Remove extra_state_attributes property, replaced by a number

* Support HVAC_Mode in set_temperature()

* Remove set_temperature() update, as requested
2023-08-12 09:56:23 +02:00
Erik Montnemery
8b99d4678f Correct checks for non-finite numbers in ESPHome (#98102) 2023-08-12 08:10:25 +01:00
Charles Garwood
8912b19cf4 Add Enphase Encharge aggregate sensors (#98276)
* Add Encharge aggregate sensors

* Update dependency
2023-08-11 20:15:42 -04:00
Marc Mueller
58194a5eb4 Improve wake_word generic typing (#98279) 2023-08-11 23:08:52 +02:00
Marc Mueller
78ac36e3fe Improve met_eireann generic typing (#98278) 2023-08-11 23:08:21 +02:00
Marc Mueller
ff0566b11f Fix deque import (#98269) 2023-08-11 23:07:06 +02:00
Charles Garwood
4342a95be0 Add Enphase switch platform and grid enable switch (#98261)
* Add Enphase switch platform and grid enable switch

* Update dependency

* Fix docstrings

* Update .coveragerc
2023-08-11 13:31:47 -04:00
Marc Mueller
8fbcffcf9f Add types-psutil dependency (#98267) 2023-08-11 18:09:58 +02:00
Marc Mueller
2ed11d2900 Add types-xmltodict dependency (#98268) 2023-08-11 18:09:38 +02:00
Hessel
8bef39a2fb Wallbox Integration Change Switch Availability (#98111)
* change switch availability

* remove new test

* Update homeassistant/components/wallbox/switch.py

Also check super availability.

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

* black formatting

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-11 16:39:29 +02:00
Robert Svensson
7f616b0d44 Improve UniFi control PoE mode (#98119) 2023-08-11 14:11:06 +02:00
Joost Lekkerkerker
e6ba6c295c Add base entity to Garages Amsterdam (#98172) 2023-08-11 13:55:38 +02:00
Joost Lekkerkerker
3499ba3a9e Add device classes to Buienradar (#98151) 2023-08-11 13:54:57 +02:00
Joost Lekkerkerker
27876b929b Migrate iZone to has entity name (#98234) 2023-08-11 13:53:11 +02:00
tronikos
fe794e2be3 Fix Opower utilities that have different ReadResolution than previously assumed (#97823) 2023-08-11 13:47:49 +02:00
Erik Montnemery
97f3199d6d Do not add entities with invalid device info (#98150) 2023-08-11 13:14:47 +02:00
Joost Lekkerkerker
a2cf08a1ea Add entity translations to Keymitt ble (#98236) 2023-08-11 13:14:13 +02:00
Marc Mueller
fb66ceb302 Update mypy to 1.5.0 (#98179) 2023-08-11 13:13:04 +02:00
Joakim Plate
990ec1d445 Make gardena closing sensor unavailable when closed (#98133) 2023-08-11 13:07:45 +02:00
Allen Porter
a0ac8ba5a6 Enforce a minimum temperature range for nest thermostats (#98238) 2023-08-11 12:21:19 +02:00
Joost Lekkerkerker
b67e290eaa Use explicit device name in Broadlink (#98229) 2023-08-11 12:15:04 +02:00
Joost Lekkerkerker
41572480fd Migrate DenonAVR to has entity name (#98155) 2023-08-11 11:58:02 +02:00
epenet
c62081430b Refactor JSON attribute parsing in rest (#97526)
* Refactor JSON attribute parsing in rest

* Early return
2023-08-11 11:00:55 +02:00
G Johansson
832a8247de Fix CI mypy issues (#98241)
Fix CI
2023-08-11 10:11:13 +02:00
Joost Lekkerkerker
25231637a5 Add device to DWD (#98120) 2023-08-11 08:27:40 +02:00
MatthewFlamm
9a1bfe1e1c Bump pynws 1.5.1; fix regression for precipitation probability (#98237)
bump pynws 1.5.1
2023-08-11 08:19:06 +02:00
Joost Lekkerkerker
24add3f766 Migrate Doorbird to has entity name (#98161) 2023-08-10 17:09:26 -10:00
Joost Lekkerkerker
914baaa2ba Migrate DirecTV to has entity name (#98159)
* Migrate DirecTV to has entity name

* Migrate DirecTV to has entity name
2023-08-10 22:10:46 -04:00
Erik Montnemery
2e1a5ddf2b Don't allow creating device if config entry does not exist (#98157)
* Don't allow creating device if config entry does not exist

* Fix test

* Update test
2023-08-10 22:09:13 -04:00
Erik Montnemery
045c327928 Move DeviceInfo from entity to device registry (#98149)
* Move DeviceInfo from entity to device registry

* Update integrations
2023-08-10 22:04:26 -04:00
J. Nick Koston
108bcabf75 Add missing transmit power to ESPHome Bluetooth scanners (#98175)
We did not previously have a way to get the transmit power value when using
ESPHome scanners. bluetooth-data-tools 1.8.0 includes it in the
advertisment tuple to fully align with the bleak implementation.

txpower is not yet used in the HA codebase but may be expected by
upstream libaries that calculate estimated distance
2023-08-10 21:59:37 -04:00
Franck Nijhof
296c27859e Fix issue registry sending unneeded update events (#98230) 2023-08-10 21:57:42 -04:00
Charles Garwood
b653d7f683 Fix Enphase dry contact binary sensor state updates (#98225)
Fix dry contact binary sensor state updates
2023-08-11 00:00:54 +02:00
Joost Lekkerkerker
86f94662eb Add entity translations to EZVIZ (#98123) 2023-08-10 23:49:14 +02:00
G Johansson
aacb8aecfc Refactor Rest Sensor with ManualTriggerEntity (#97396)
* ManualTriggerEntity for rest sensor

* add availability test

* review comments

* last fixes
2023-08-10 21:46:56 +02:00
Joost Lekkerkerker
82ade574d8 Migrate WAQI to aiowaqi library (#98000)
* Migrate WAQI to aiowaqi library

* Migrate WAQI to aiowaqi library

* Migrate WAQI to aiowaqi library
2023-08-10 20:18:57 +02:00
Erik Montnemery
4e8b81370e Adjust device_registry tests which create devices (#98215) 2023-08-10 19:28:33 +02:00
Erik Montnemery
c7b4d4f361 Adjust helpers tests which create devices (#98214) 2023-08-10 19:28:16 +02:00
Erik Montnemery
f1d4a4bd26 Adjust zwave_js tests which create devices (#98213) 2023-08-10 18:27:22 +02:00
Erik Montnemery
49011f0158 Adjust hue tests which create devices (#98195) 2023-08-10 18:27:05 +02:00
Erik Montnemery
3fdc98063e Adjust bond tests which create devices (#98183) 2023-08-10 18:26:44 +02:00
Erik Montnemery
fcdfeb74c8 Adjust smartthings tests which create devices (#98207) 2023-08-10 18:26:20 +02:00
Erik Montnemery
6803a62368 Adjust ruckus_unleashed tests which create devices (#98206) 2023-08-10 18:26:13 +02:00
Erik Montnemery
b11dc50f9e Adjust homekit_controller tests which create devices (#98194) 2023-08-10 18:25:53 +02:00
Erik Montnemery
e1e4b0dcf0 Adjust device_automation tests which create devices (#98187) 2023-08-10 18:25:42 +02:00
Erik Montnemery
57d0fd7bb1 Adjust derivative tests which create devices (#98186) 2023-08-10 18:25:28 +02:00
Chris
fe1f617a35 Add unifi power stats for PDU outlets (#98081)
Adds support for power stats for PDU outlets.
2023-08-10 18:25:03 +02:00
Erik Montnemery
c1b47b88f2 Adjust utility_meter tests which create devices (#98212) 2023-08-10 18:24:47 +02:00
Erik Montnemery
05ac67eba2 Adjust unifi tests which create devices (#98211) 2023-08-10 18:24:42 +02:00
Erik Montnemery
fb1bb0d374 Adjust switch_as_x tests which create devices (#98210) 2023-08-10 18:24:39 +02:00
Erik Montnemery
8813140ed5 Adjust threshold tests which create devices (#98208) 2023-08-10 18:24:36 +02:00
Erik Montnemery
6ea7011a07 Adjust ps4 tests which create devices (#98205) 2023-08-10 18:24:25 +02:00
Erik Montnemery
7b157baed6 Adjust plex tests which create devices (#98204) 2023-08-10 18:24:22 +02:00
Erik Montnemery
9d3be60b05 Adjust openai_conversation tests which create devices (#98203) 2023-08-10 18:24:19 +02:00
Erik Montnemery
13a9b83ed3 Adjust mqtt tests which create devices (#98202) 2023-08-10 18:23:55 +02:00
Erik Montnemery
0b69f37d57 Adjust motioneye tests which create devices (#98201) 2023-08-10 18:23:51 +02:00
Erik Montnemery
bd4d1abc28 Adjust mikrotik tests which create devices (#98200) 2023-08-10 18:23:48 +02:00
Erik Montnemery
9831498259 Adjust mazda tests which create devices (#98199) 2023-08-10 18:23:44 +02:00
Erik Montnemery
e9a0436605 Adjust matter tests which create devices (#98198) 2023-08-10 18:23:41 +02:00
Erik Montnemery
3495d762a4 Adjust kraken tests which create devices (#98197) 2023-08-10 18:23:37 +02:00
Erik Montnemery
3e6c844048 Adjust integration tests which create devices (#98196) 2023-08-10 18:23:33 +02:00
Erik Montnemery
07b19b3dd9 Adjust homekit tests which create devices (#98193) 2023-08-10 18:23:17 +02:00
Erik Montnemery
4329a47ef8 Adjust google_generative_ai_conversation tests which create devices (#98192) 2023-08-10 18:23:13 +02:00
Erik Montnemery
f11f7ac45c Adjust google_assistant tests which create devices (#98191) 2023-08-10 18:23:10 +02:00
Erik Montnemery
983ebeff80 Adjust freebox tests which create devices (#98190) 2023-08-10 18:22:44 +02:00
Erik Montnemery
52183d64ae Adjust fibaro tests which create devices (#98189) 2023-08-10 18:22:39 +02:00
Erik Montnemery
ec143b26d7 Adjust device_tracker tests which create devices (#98188) 2023-08-10 18:22:33 +02:00
Erik Montnemery
da53944a37 Adjust conversation tests which create devices (#98185) 2023-08-10 18:22:17 +02:00
Erik Montnemery
5909a1187d Adjust config tests which create devices (#98184) 2023-08-10 18:22:12 +02:00
Erik Montnemery
f77387bd0f Adjust asuswrt tests which create devices (#98182) 2023-08-10 18:21:46 +02:00
RoboMagus
f0e9dccb58 Only handle shell commands output when return_response requested (#97777) 2023-08-10 18:11:15 +02:00
Joost Lekkerkerker
07a701551b Add missing translation key in Tuya (#98122) 2023-08-10 18:04:24 +02:00
Maximilian
6545c46ba0 Bump pynina to 0.3.2 (#98070) 2023-08-10 16:54:19 +02:00
tronikos
9a2cb3ad1f Opower: Add gas sensors for utilities that report CCF (#98142)
Add gas sensors for utilities that report CCF
2023-08-10 16:49:23 +02:00
Álvaro Fernández Rojas
4981eadd31 Update aioairzone to v0.6.5 (#98163)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-08-10 16:47:16 +02:00
Joost Lekkerkerker
59768635f2 Fix ruff checks for opensky (#98176) 2023-08-10 16:04:00 +02:00
Joost Lekkerkerker
a7f7f56342 Implement opensky data update coordinator (#97925)
Co-authored-by: Robert Resch <robert@resch.dev>
2023-08-10 15:42:46 +02:00
Stefan Agner
bba57f39d5 Add Home Assistant Green (#98171) 2023-08-10 15:00:43 +02:00
Franck Nijhof
868a5f377f Ruff: isort don't split imports based on trailing comma (#98162) 2023-08-10 14:27:03 +02:00
Thijs W
726b0c5179 Address late comments in #97955 (#98165) 2023-08-10 13:58:48 +02:00
Joost Lekkerkerker
e9f9c7799a Add device to cert expiry (#98152)
* Add device to cert expiry

* Apply suggestions from code review

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-10 13:05:58 +02:00
G Johansson
4531dbbe62 Refactor Rest Binary sensor with ManualTriggerEntity (#97400)
* Refactor Rest Binary sensor w/ ManualTriggerEntity

* test availability

* review comments

* Use super

* Fix config
2023-08-10 12:59:23 +02:00
Joakim Plate
9b74321487 Correct unit of rain pause (#98131) 2023-08-10 12:37:28 +02:00
Jan Bouwhuis
b872d74b1f Fix lingering test alexa (#98128) 2023-08-10 12:16:52 +02:00
tronikos
5812090eff Get Opower accounts from the customer endpoint (#98144)
Get accounts from the customer endpoint
2023-08-10 12:11:01 +02:00
tronikos
84d779fab7 Bump opower to 0.0.26 (#98141) 2023-08-10 11:13:55 +02:00
Joost Lekkerkerker
5dcffca88d Move Rova constants to separate file (#97566)
* Move Rova constants to separate file

* Update homeassistant/components/rova/const.py

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

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-10 10:41:06 +02:00
Thijs W
355ef4eac8 Add unique_id to frontier_silicon media_player entity (#97955) 2023-08-10 10:19:27 +02:00
Marc Mueller
3dd377cb2a Update orjson to 3.9.4 (#98108) 2023-08-09 20:37:59 -10:00
J. Nick Koston
e05b74668c Bump dbus-fast to 1.91.2 (#98105) 2023-08-09 20:31:57 -10:00
Raman Gupta
056d00fb11 Update zwave_js entity naming logic (#98140)
* Update zwave_js entity naming logic

* Update homeassistant/components/zwave_js/entity.py

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

* store primary value locally

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-08-10 02:08:14 -04:00
Raman Gupta
5d3d66e47d Bump zwave-js-server-python to 0.50.1 (#94760)
* Bump zwave-js-server-python to 0.50.0

* handle additional upstream changes

* Additional changes

* fix tests

* Convert two similar functions to be one function

* Fix docstring

* Remove conditional pydantic import

* Revert scope change

* Bump zwave-js-server-python

* Set default return value for command

* Remove line breaks

* Add coverage
2023-08-10 01:28:08 -04:00
Luke
2841cbbed2 Add Off-peak power control to Roborock (#97307)
* add off-peak switch and time

* Make off_peak disabled by default
2023-08-09 22:04:01 +02:00
Robert Svensson
02c27d8ad2 UniFi WLAN availability affected by WLAN enabled (#98020) 2023-08-09 18:52:35 +02:00
Joost Lekkerkerker
138854a9cc Migrate EAFM to has entity name (#98121) 2023-08-09 17:44:08 +02:00
G Johansson
4c03077dfe Add product filtering feature to Trafikverket Train (#86343) 2023-08-09 17:20:30 +02:00
Erik Montnemery
0317afeb17 Fix mock_integration and mock_platform test helpers (#98109) 2023-08-09 15:38:57 +02:00
David Knowles
023f2f8bb7 Add switch platform to Schlage (#98004)
* Add switch platform to Schlage

* Add a generic SchlageSwitch

* Use an is_on property instead of _attr_is_on

* Make value_fn always return a bool
2023-08-09 15:32:50 +02:00
Erik Montnemery
e1f0b44ba4 Use math.isfinite instead of explicitly checking for both nan and inf (#98103) 2023-08-09 14:13:57 +02:00
J. Nick Koston
7e9d0cca44 Refactor enphase_envoy to have a shared base class (#98088)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-09 01:28:27 -10:00
Sam Reed
2cae79415d zha: Fix double spaces in strings.json (#98097) 2023-08-09 11:21:05 +02:00
tronikos
35718b2917 Bump opower to 0.0.24 (#98091) 2023-08-09 10:32:00 +02:00
J. Nick Koston
1b54b22a91 Bump pyenphase to 1.3.0 (#98090) 2023-08-08 21:11:57 -10:00
Brandon Rothweiler
d569d01cfb Bump pymazda to 0.3.11 (#98084) 2023-08-09 07:17:17 +01:00
Charles Garwood
ce6b759b70 Add Envoy enpower sensors (#98086) 2023-08-08 17:05:52 -10:00
Joost Lekkerkerker
d975e93abc Add entity translations for Ambient station (#98075)
* Add entity translations for Ambient station

* Fix missed key
2023-08-09 01:16:55 +02:00
J. Nick Koston
331bdcc596 Bump pyenphase to 1.1.3 (#98074)
changelog: https://github.com/pyenphase/pyenphase/compare/v1.1.1...v1.1.3
2023-08-08 18:51:17 -04:00
Jan Bouwhuis
5c9bce9eac Allow float for inital MQTT climate temperature (#97995)
* Allow float for inital MQTT climate temperature

* Update tests/components/mqtt/test_climate.py

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

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-08 23:44:49 +02:00
Charles Garwood
25467b573e Bump pyenphase to 1.1.1 (#98065) 2023-08-08 11:27:59 -10:00
Joost Lekkerkerker
524a26d9e1 Add entity translations to Neato (#98067)
* Add entity translations to Neato

* Use robot name
2023-08-08 22:05:12 +02:00
Erik Montnemery
66e3d69606 Remove confusing comment from accuweather (#98063) 2023-08-08 22:02:45 +02:00
Sam Reed
3624e30380 Update silabs_multiprotocol_hardware change cannel options flow description (#98047)
strings.json: Update silabs_multiprotocol_hardware::options message

* Removes trailing space
* Fixes double space
* Adds word before noun
2023-08-08 20:13:19 +02:00
Charles Garwood
c154077177 Add Encharge binary sensors to Enphase integration (#98039)
* Add Encharge binary sensors to Enphase integration

* Code review minor cleanup

* Add to coveragerc
2023-08-08 20:03:02 +02:00
Sam Reed
45d4c307de Hyphenate "human-readable" in light service description (#98057) 2023-08-08 19:43:09 +02:00
G Johansson
8f2e30040c Add DeviceInfo to Scrape (#97399)
* Add DeviceInfo to Scrape

* simplify

* review comment
2023-08-08 19:39:41 +02:00
mkmer
314d91692f Bump AIOAladdinConnect to 0.1.57 (#98056) 2023-08-08 19:39:26 +02:00
Erik Montnemery
a77009c3ca Patch dt_util.utcnow earlier (#98050) 2023-08-08 19:16:52 +02:00
Sam Reed
ba3f0372f3 Fix duplicated word in imap_email_content deprecation issue description (#98051) 2023-08-08 19:15:39 +02:00
Michael
d7a1b1e941 Fallback to get_hosts_info on older Fritz!OS in AVM Fritz!Tools (#97844) 2023-08-08 19:15:06 +02:00
G Johansson
d557f3b742 Add state attributes translation and available modes for Sensibo (#85234)
* Sensibo translation climate

* Add available states

* Fix keys

* Delete en.json

* invalid fan_mode and swing_mode

* Translations

* Add back sorting

* Fix fan_mode and swing_mode

* Fix raise error

* review
2023-08-08 19:13:56 +02:00
Sam Reed
bfc578a757 Fix address typo in Reolink SSL issue description (#98060) 2023-08-08 19:13:35 +02:00
Erik Montnemery
f36e75ecf1 Add WeatherEntity.__post_init__ (#98034) 2023-08-08 19:11:55 +02:00
Sam Reed
75fbc7a97c Hyphenate "human-readable" in LIFX service description (#98058) 2023-08-08 19:07:40 +02:00
Sam Reed
14a993d33b Remove trailing . from melcloud service descriptions (#98053) 2023-08-08 19:07:17 +02:00
Erik Montnemery
eb64e89ecf Make changes in modbus trigger a full CI run (#98055) 2023-08-08 18:49:56 +02:00
Joost Lekkerkerker
466c5ce591 Add some constants back that were used to Flexit and Stiebel (#98042)
* Add some constants back that were used

* Update __init__.py

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-08 18:41:47 +02:00
Joost Lekkerkerker
c78c2b7c3b Add translation keys to Tuya cover (#98040) 2023-08-08 18:02:47 +02:00
Joost Lekkerkerker
ce1077934a Move all used modbus constants to Stiebel (#98044) 2023-08-08 17:38:38 +02:00
Jan Bouwhuis
500d9a4da0 Alexa strict type hints (#97485)
* Enable strict typing

* Adjustments for stict typing
2023-08-08 17:15:25 +02:00
Maciej Bieniek
9910da2f3d Add neutral current sensor for Shelly 3EM (#97981) 2023-08-08 16:47:18 +02:00
jan iversen
fc463e5831 modbus: remove unused constants and get 100% coverage. (#97779) 2023-08-08 16:40:16 +02:00
Tom
4a4523c249 Bump plugwise to v0.31.9 (#97203)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Bouwe <bouwe.s.westerdijk@gmail.com>
2023-08-08 16:38:37 +02:00
jan iversen
a224b668d7 modbus: Adjust read count by slave_count (#97908) 2023-08-08 16:09:53 +02:00
Jan Bouwhuis
2a48159b69 Alexa typing part 6 (state_report) (#97920)
state_report
2023-08-08 15:46:54 +02:00
Massimiliano Cannarozzo
8b56e28838 Add neato dismiss alert button (#97572)
* Bump pybotvac

* Add neato dismiss alert button

* fixup! Add neato dismiss alert button

* fixup! Add neato dismiss alert button

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

* fixup! Add neato dismiss alert button

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

* fixup! Add neato dismiss alert button

* fixup! Add neato dismiss alert button

* fixup! Add neato dismiss alert button

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

* fixup! Add neato dismiss alert button

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

* fixup! Add neato dismiss alert button

* fixup! Add neato dismiss alert button

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-08 14:35:41 +02:00
Erik Montnemery
0d55a7600e Modernize met_eireann weather (#98030) 2023-08-08 14:21:52 +02:00
Franck Nijhof
50ef674902 Use require_admin decorator for check_config permissions (#98028) 2023-08-08 12:19:20 +02:00
Jadson Santos
f6273bfca5 Add prometheus requires_auth parameter (#92964)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-08-08 12:15:08 +02:00
Franck Nijhof
40e256847c Add is_admin checks to scene/script/automation APIs (#98025) 2023-08-08 12:11:52 +02:00
Erik Montnemery
55619e7d6d Modernize ecobee weather (#98023) 2023-08-08 12:06:24 +02:00
Jan Bouwhuis
0614702f98 Alexa typing part 5 (smart_home) (#97918)
* smart_home

* Fix test_disabled

* Remove unused type ignore
2023-08-08 11:48:50 +02:00
Erik Montnemery
1ee0c907b0 Improve OTBR factory reset (#98017)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2023-08-08 11:38:01 +02:00
Jan Bouwhuis
3f542c47fd Alexa typing part 4 (capabilities) (#97915)
* capabilities

* hvacmode

* tweaks

* name

* Corrections

* Missed hints

* remove unreachabe code
2023-08-08 11:07:52 +02:00
Franck Nijhof
5e020ea354 Add is_admin checks to cloud APIs (#97804) 2023-08-08 11:02:42 +02:00
Stephan Uhle
3859d2e2a6 Add edl21 sensor for positive active instantaneous power (#94736) 2023-08-08 10:35:31 +02:00
Franck Nijhof
dacfa4b4dc Set up shopping list during onboarding (#97974)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2023-08-08 10:32:35 +02:00
Russell Cloran
dfdf592837 Update prometheus-client to 0.17.1 (#97998) 2023-08-08 10:16:37 +02:00
J. Nick Koston
2a80a63ac2 Cleanup enphase_envoy sensor inheritance (#98012) 2023-08-08 10:10:36 +02:00
Charles Garwood
128dadafae Add initial sensors for Enphase Encharge batteries (#97946) 2023-08-07 16:46:00 -10:00
Michael Hansen
7ea2998b55 Add wake word integration (#96380)
* Add wake component

* Add wake support to Wyoming

* Add helper function to assist_pipeline (not complete)

* Rename wake to wake_word

* Fix platform

* Use send_event and clean up

* Merge wake word into pipeline

* Add wake option to async_pipeline_from_audio_stream

* Add start/end stages to async_pipeline_from_audio_stream

* Add wake timeout

* Remove layer in wake_output

* Use VAD for wake word timeout

* Include audio metadata in wake-start

* Remove unnecessary websocket command

* wake -> wake_word

* Incorporate feedback

* Clean up wake_word tests

* Add wyoming wake word tests

* Add pipeline wake word test

* Add last processed state

* Fix tests

* Add tests for wake word

* More tests for the codebot
2023-08-07 22:22:16 -04:00
J. Nick Koston
798fb3e31a Bump aiohomekit to 2.6.15 (#98005)
changelog: https://github.com/Jc2k/aiohomekit/compare/2.6.14...2.6.15
2023-08-08 02:30:47 +02:00
Joost Lekkerkerker
0bdae8a382 Use global constant for enphase token (#98002) 2023-08-08 00:52:54 +02:00
Aaron Bach
aff369d64c Bump pyairvisual to 2023.08.1 (#97999) 2023-08-07 16:23:27 -06:00
puddly
323657e6d7 Bump ZHA dependency bellows to 0.35.9 (#97976)
Bump bellows to 0.35.8
2023-08-07 18:14:47 -04:00
Jan Bouwhuis
d403625e60 Alexa typing part 3 (handlers) (#97912)
handlers
2023-08-07 23:59:56 +02:00
Matthias Alphart
0f5d423d1e Move KNX keyring validation and storage to helper module (#97931)
* Move KNX keyfile validation and store to helper module

* Rename module and fix tests
2023-08-07 23:30:14 +02:00
J. Nick Koston
987897b0fa Add support for Yale Doorman to august (#97997) 2023-08-07 11:28:01 -10:00
Jan Bouwhuis
5657cfa052 Alexa typing part 2 (#97911)
* Alexa typing part 2

* Update homeassistant/components/alexa/intent.py

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

* Missed type hints

* precision

* Follow up comment

* value

* revert abstract class changes

* raise NotImplementedError()

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-07 23:26:44 +02:00
J. Nick Koston
c8256d1d3d Optimize august timings to prepare for Yale Doorman support (#97940) 2023-08-07 11:09:32 -10:00
J. Nick Koston
7080e0c280 Bump yalexs to 1.5.2 (#97991) 2023-08-07 11:00:30 -10:00
J. Nick Koston
d304d42051 Restore qingping state when device becomes available (#97980) 2023-08-07 09:27:18 -10:00
J. Nick Koston
56c2276630 Restore sleepy oralb devices state at startup (#97983) 2023-08-07 09:19:46 -10:00
J. Nick Koston
b7f936fcdd Restore govee_ble state when gateway device becomes available (#97984) 2023-08-07 09:13:50 -10:00
Jan Bouwhuis
40a221c923 Alexa typing part 1 (#97909)
* Typing part 1

* mypy

* Correct typing for logbook
2023-08-07 20:36:30 +02:00
J. Nick Koston
fb12c237ab Restore xiaomi_ble state at start when device is in range or sleepy (#97979) 2023-08-07 07:58:27 -10:00
Ståle Storø Hauknes
b34ce3c792 Improve airthings ble (#97905)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-07 07:15:51 -10:00
J. Nick Koston
a234ab51fe Restore bthome state at start when device is in range or sleepy (#97949) 2023-08-07 18:41:53 +02:00
Joost Lekkerkerker
d56484e2d6 Fix docstrings in mobile app device tracker (#97963) 2023-08-07 18:41:08 +02:00
David Knowles
a4721e9b36 Schlage: Set the changed by attribute on locks based on log messages (#97469) 2023-08-07 06:07:48 -10:00
lymanepp
4089bd43da Fix tomorrowio integration for new users (#97973)
The tomorrow.io integration isn't working for new users due to changes made by tomorrow.io. This fixes that with the following changes:
* Add 60 minute timestep option
* Change default timestep to 60 minutes
2023-08-07 17:54:06 +02:00
G Johansson
c4da5374ae Refactor Trafikverket Train to improve config flow (#97929)
* Refactor tvt

* review fixes

* review comments
2023-08-07 17:25:02 +02:00
G Johansson
15eed166ec Modernize SMHI weather (#97275)
* SMHI forecast service

* Mod weather

* reset weather

* Fix tests

* coverage

* add test
2023-08-07 17:24:43 +02:00
Erwin Douna
65365d1db5 Integration tado bump (#97791)
* Bumping to PyTado 0.16 and adding test coverage

* Removing comment

* Upgrading the deprecated functions

* Updating tests to support paramterization

* Delete test_config_flow.py

Reverting the tests, which will be placed in a different PR.

* Revert "Delete test_config_flow.py"

This reverts commit 1719ebc990a32d3309f241f8adc8262008ca4ff3.

* Reverting back changes
2023-08-07 15:59:46 +02:00
Erik Montnemery
3a0822e03b Modernize met.no weather (#97952) 2023-08-07 14:06:16 +02:00
Erik Montnemery
683c2f8d22 Add service for getting a weather forecast (#97078)
* Add service for getting a weather forecast

* Fix translations

* Improve service description

* Improve error handling

* Adjust typing

* Adjust typing

* Adjust service response format
2023-08-07 14:05:37 +02:00
tronikos
0a2ff3a676 Android TV Remote: Fix missing key and cert when adding a device via IP address (#97953)
Fix missing key and cert
2023-08-07 14:01:35 +02:00
Charles Garwood
eff7b8f81a Update enphase_envoy codeowners (#97947) 2023-08-07 13:27:51 +02:00
G Johansson
301eaa30b1 Neato add yaml config removal issue (#97447) 2023-08-07 13:14:33 +02:00
Marc Mueller
cf4287cd0c Fix alexa test RuntimeWarning (#97956) 2023-08-07 12:57:37 +02:00
Mick Vleeshouwer
b317d36d0f Bump pyoverkiz to 1.10.1 (#97916)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-07 12:32:59 +02:00
Guido Schmitz
5232b6ee6a Bump devolo_plc_api to 1.4.0 (#97951) 2023-08-07 12:08:19 +02:00
starkillerOG
d72057f41b Add repair issue for Reolink when using it with an incompatible global ssl certificate (#91597) 2023-08-07 11:52:14 +02:00
G Johansson
001dda6345 Fix weather entities with update_before_add (#97950)
Fix weather entities update_before_add
2023-08-07 09:42:20 +02:00
J. Nick Koston
56257b7a38 Restore passive bluetooth entity data at startup (#97462) 2023-08-06 20:25:18 -10:00
J. Nick Koston
1adfa6bbeb Reduce overhead to start a config entry flow by optimizing fetching the handler (#97883) 2023-08-06 20:25:03 -10:00
Franck Nijhof
369a484a78 Add default headers to webserver responses (#97784)
* Add default headers to webserver responses

* Set default server header

* Fix other tests
2023-08-06 23:25:13 -04:00
Franck Nijhof
3df71eca45 Ensure webhooks take HA cloud into account (#97801)
* Ensure webhooks take HA cloud into account

* Avoid circular import
2023-08-06 23:23:52 -04:00
J. Nick Koston
05e131452d Add model/part number data enphase_envoy (#97942) 2023-08-06 16:55:34 -10:00
Jan Bouwhuis
3969de6c76 Freeze time for whirlpool test to avoid fail (#97935) 2023-08-06 12:37:06 -10:00
Joost Lekkerkerker
b2d2de990d Remove DWD code owner (#97938) 2023-08-06 12:36:19 -10:00
Alex Yao
df8f1328e9 Bump yeelight to v0.7.13 (#97933)
Co-authored-by: alexyao2015 <alexyao2015@users.noreply.github.com>
2023-08-06 12:20:48 -10:00
J. Nick Koston
157f7a3079 Bump pyatv to 0.13.4 (#97932) 2023-08-06 12:20:28 -10:00
J. Nick Koston
4230465fcd Avoid polling event characteristic in homekit_controller (#97877) 2023-08-06 11:11:03 -10:00
Marc Mueller
d993aa59ea Update orjson to 3.9.3 (#97930) 2023-08-06 22:59:44 +02:00
Luke
c9edc973f0 Bump python-roborock to 0.32.2 (#97907)
* bump to 0.32.2

* fix test
2023-08-06 22:34:14 +02:00
David Knowles
50ccd68de1 Bump pyschlage to 2023.8.1 (#97927) 2023-08-06 10:20:16 -10:00
J. Nick Koston
99c3ca030d Bump pyenphase to 0.11.0 (#97926) 2023-08-06 09:31:37 -10:00
G Johansson
4d24a3ffaa Bump pytrafikverket to 0.3.5 (#97923) 2023-08-06 20:58:16 +02:00
J. Nick Koston
ceac5f8d5a Proactively refresh the enphase envoy token to handle cloud service downtime (#97880) 2023-08-06 08:31:45 -10:00
G Johansson
a59793df4c Bump pytrafikverket to 0.3.4 (#97921) 2023-08-06 20:01:01 +02:00
Joost Lekkerkerker
42bca0f94a Complete test coverage for OpenSky (#97863)
* Use mockobject for OpenSky testing

* Complete test coverage for OpenSky

* Complete test coverage for OpenSky

* Use method patching
2023-08-06 19:39:24 +02:00
Maikel Punie
0535578440 Velbus code cleanup (#97584)
* Some cleanup and code improvements for the velbus integration

* Comments

* More comments

* Update homeassistant/components/velbus/entity.py

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

* More comments

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-06 19:12:19 +02:00
G Johansson
6bc5b8989d Fix Trafivkerket Train coordinator exceptions (#97919)
Fix tvt coordinator exceptions
2023-08-06 19:07:18 +02:00
G Johansson
163bbe2c5d Fix Trafikverket Train departure state (#97917)
Fix tvt departure state
2023-08-06 18:50:00 +02:00
G Johansson
74d1c30f7e Trafikverket Train sensor and attributes to new sensors (#71432)
* Initial commit

* Finalize tvt sensors

* Translations

* Fix sensor translation

* migration

* migration fix 2

* Fix name

* Fix translation

* remove translation

* Fixes

* Fix isort and mypy

* translations

* logging

* Remove logging

* departure time

* Review comments

* Mod update entity unique id

* Fix uom

* not async

* UnitOfTime

* cleanup from rebase

* Remove call self

* cleanup extra attributes

* Fix rebase again

* string state
2023-08-06 18:08:07 +02:00
String-656
56dd88db17 Replace Float 'nan' with None for modbus floats (#93832)
Co-authored-by: jan iversen <jancasacondor@gmail.com>
2023-08-06 14:26:56 +02:00
Johannes Wagner
c4a5373976 Handle explicit Modbus NaN values (#90800)
Co-authored-by: jan iversen <jancasacondor@gmail.com>
2023-08-06 13:47:54 +02:00
David Knowles
0511071757 Schlage: Set the battery sensor state_class to measurement (#97879)
Set state_class to measurement
2023-08-06 10:26:16 +02:00
J. Nick Koston
91b308b4ad Fix handling HomeKit events when the char is in error state (#97884) 2023-08-05 19:14:18 -10:00
J. Nick Koston
00e78fbf19 Cache envoy auth tokens to ensure integration works if cloud is offline (#97872) 2023-08-05 14:51:19 -10:00
David Knowles
6a65a97715 Bump pyschlage to 2023.8.0 (#97875) 2023-08-05 14:33:01 -10:00
Joost Lekkerkerker
3bed32f16e Add entity translations for Enphase Envoy (#97876)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-05 14:32:35 -10:00
J. Nick Koston
02e546e3ef Refactor enphase_envoy to use pyenphase library (#97862) 2023-08-05 13:33:16 -10:00
Ståle Storø Hauknes
34013ac3e9 Use PRECISION_TENTHS for Mill local integration (#97874) 2023-08-06 01:25:17 +02:00
J. Nick Koston
a09090bf99 Do not fire homekit_controller events from IP polling (#97869)
* Fix homekit_controller triggers when value is None

* fixes

* cover
2023-08-06 00:31:50 +02:00
G Johansson
966784877f Remove long overdue deprecated service boost_heating from Hive (#97444)
* Hive heating_boost deprecation

* Remove strings

* Remove service

* services

* Remove strings
2023-08-05 23:37:01 +02:00
elmurato
8195c9d1a7 Use constants for translation keys and rename latency time to latency (#97866)
Use constants for translation keys, rename latency time to latency and some small cleanups
2023-08-05 23:35:54 +02:00
Ernst Klamer
0caeac1a82 Add support for toothbrushes to xiaomi-ble (#97276)
* Add support for toothbrushes to xiaomi-ble

* use str for string
2023-08-05 23:13:21 +02:00
G Johansson
e4d9daf746 Migrate to SensorEntityDescriptions for Trafikverket Train (#97318)
* tvt migrate to sensor entity description

* spelling

* revert spelling
2023-08-05 22:53:14 +02:00
Jan-Philipp Benecke
71a81e1f5d Change discovergy integration type (#97391) 2023-08-05 22:47:04 +02:00
Daniel Hjelseth Høyer
41c684f36e Use new Mill api (#97497)
* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api. Wip

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Mill, new api

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Update homeassistant/components/mill/sensor.py

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

* Update homeassistant/components/mill/sensor.py

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

* Mill, new api

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Update homeassistant/components/mill/climate.py

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

* Update homeassistant/components/mill/climate.py

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

---------

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-05 22:46:03 +02:00
Joost Lekkerkerker
c7b7ca8769 Add yeelight class to fix superclass issue (#97649)
* Add device naming to Yeelight

* Add extra light entity to fix superclass

* Add extra light entity to fix superclass
2023-08-05 22:36:45 +02:00
Richard Kroegel
74d02a1574 BMW: Remove deprecated refresh from cloud button (#97864)
* Remove deprecated refresh from cloud button

* Clean up strings.json
2023-08-05 22:28:24 +02:00
J. Nick Koston
c478a81deb Bump bluetooth-data-tools to 1.7.0 (#97821) 2023-08-05 22:21:46 +02:00
Alberto Geniola
e5261fe2a3 Implement Elmax cover platform (#79409)
* Implement Elmax cover platform.

* Reduce the number of code lines by leveraging the := operator

* Move _COMMAND_BY_MOTION_STATUS declaration at the top

* Remove redundant null-check

* Move conditional platform setup logic into the platform itself

* Remove redundant log

* Change log severity for stop request on IDLE cover state

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-05 22:03:51 +02:00
Jan Bouwhuis
2e8e5aabae Refactor alexa modules to avoid circular deps (#97618)
* Refactor alexa modules to avoid circula deps

* Add test http api auth and AlexaConfig

* Update test

* Improve test
2023-08-05 21:32:53 +02:00
Andreas Lindhé
6c8971f18a Improve code quality of CalDav (#97570)
* Use keyword arguments when constructing `WebDavCalendarData`

* Use keyword arguments when constructing `WebDavCalendarEntity`

* Remove random newlines
2023-08-05 20:44:26 +02:00
Richard Kroegel
4b6048b5e0 Bump bimmer_connected to 0.13.9, fix notify (#97860)
Bump bimmer_connected to 0.13.9

Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-08-05 20:08:14 +02:00
Robert Svensson
e43ad1c6a0 Add restart device to UniFi button platform (#97642)
* Add restart device to UniFi Button platform

* Add tests for button platform

* Small corrections
2023-08-05 20:07:20 +02:00
MarkGodwin
76c443777d Bump Omada API version to fix #96193 (#97848) 2023-08-05 19:59:03 +02:00
Jack Boswell
5fcac42a0f Add untested Starlink components to .coveragerc (#97825) 2023-08-05 19:55:35 +02:00
Jack Boswell
7436054352 Update starlink-grpc-tools to 1.1.2 (#97824) 2023-08-05 19:54:54 +02:00
Joost Lekkerkerker
24e4f0169c Fix Samsung syncthru device info (#97843)
Fix Samsung device info
2023-08-05 19:43:22 +02:00
G Johansson
249fa513c9 Bump pysensibo to 1.0.33 (#97853)
* Bump pysensibo to 1.0.33

* for loop
2023-08-05 19:02:44 +02:00
Joost Lekkerkerker
03335ae1dc Add missing translation key to Gardena Bluetooth (#97855) 2023-08-05 18:48:13 +02:00
Maikel Punie
6570517330 Add lightplatform to Duotecno (#97582)
* add light platform

* Add light to coveragerc

* Update homeassistant/components/duotecno/light.py

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

* Update homeassistant/components/duotecno/light.py

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

* Update homeassistant/components/duotecno/light.py

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

* Update homeassistant/components/duotecno/light.py

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

* Update homeassistant/components/duotecno/light.py

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

* Update homeassistant/components/duotecno/light.py

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

* comments

* revert

* re-implement comments

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-05 18:29:46 +02:00
G Johansson
a22aa285d3 Fix Melcloud import issue (#97673)
* Fix Melcloud import issue

* remove old issue

* Simplify

* Update homeassistant/components/melcloud/strings.json

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

* Update homeassistant/components/melcloud/strings.json

* Update homeassistant/components/melcloud/strings.json

* Apply suggestions from code review

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-05 17:00:33 +02:00
G Johansson
2e263560ec Fix Command Line template error when data is None (#97845)
Command Line template error
2023-08-05 16:21:39 +02:00
G-Two
c5e5567912 Add device tracker to Subaru integration (#79492)
* Add device tracker to subaru integration

* Fix timestamp in device tracker

* Add test for device tracker

* Incorporate PR review comments

* Apply suggestions from code review

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

* Incorporate code review comments

* Add tests for bad device tracker data

* Check device tracker data is available in entity

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-08-05 15:52:20 +02:00
Maciej Bieniek
d611b169ac Don't assume that battery_level value is always present in Tractive hw_info (#97766)
Don't assume that battery_level value is always present in hw_info
2023-08-05 13:05:15 +02:00
J. Nick Koston
c2023936c1 Bump aiohomekit to 2.6.13 (#97820) 2023-08-04 22:01:08 -10:00
jan iversen
f0abea48a6 Fix Flexit mypy error in pymodbus (#97799) 2023-08-04 21:38:32 +02:00
Jan Bouwhuis
50da5c3fae Fix typo in telegram_bot translations (#97793) 2023-08-04 21:35:35 +02:00
Jason Cook
32c1ffcde1 Update strings.json to correct grammer. (#97790)
Change of to off.
2023-08-04 21:23:33 +02:00
jan iversen
bbc34bae87 modbus: use pb not pymodbus consistently as name. (#97780)
Use pb not pymodbus consistently as name.
2023-08-04 20:14:32 +02:00
Franck Nijhof
b286da211a Add is_admin check to check configuration API (#97788) 2023-08-04 19:25:01 +02:00
Robert Svensson
66cb407e4f Improve counting of UniFi WLAN Clients sensor (#97785) 2023-08-04 19:22:46 +02:00
Ian Harcombe
b5e23ee650 Fix metoffice visibility range sensor device class (#97763)
* Fix for issue #97694

Issue #97694 points out that the visibility range is a string with a band of distances, not a single value, which is causing issues for front end components. Removed the device class to leave as an informational string value.

* Removed *all* empty device_class statements

* Missed an empty device_class :(
2023-08-04 17:04:18 +02:00
karwosts
41eca41638 Handle Alert exception on notification failure (#93632) 2023-08-04 14:08:49 +02:00
uvjustin
9282cb21ab Raise PlatformNotReady on initial OwnTone connection failure (#97257) 2023-08-04 12:54:54 +02:00
amitfin
d78e39d568 Fix allow_name_translation logic (#97701) 2023-08-04 12:51:04 +02:00
Joost Lekkerkerker
80d0f32237 Add has entity name to Solarlog (#97764) 2023-08-04 12:46:53 +02:00
Joost Lekkerkerker
ecce601d3f Fix WAQI being zero (#97767) 2023-08-04 12:46:23 +02:00
Robert Svensson
2820514c33 Break long strings in Axis integration (#97254) 2023-08-04 12:43:06 +02:00
Joost Lekkerkerker
0971449b94 Remove unused translation key from OpenSky (#97699) 2023-08-04 12:42:31 +02:00
Marc Mueller
3f5a21dce4 Fix mailbox PytestCollectionWarning (#97740) 2023-08-04 12:31:32 +02:00
Marc Mueller
447479d0a0 Add packaging as default requirement (#97712) 2023-08-04 12:29:18 +02:00
Matthieu Barthelemy
6adb06956b Add overkiz battery sensor level medium (#97472) 2023-08-04 11:50:03 +02:00
J. Nick Koston
8719aa76ca Avoid calling the http access logging when logging is disabled in emulated_hue (#97750) 2023-08-04 11:46:19 +02:00
Maikel Punie
95ca609124 Bump pyduotecno to 2023.8.3 (#97759) 2023-08-04 11:33:03 +02:00
Marc Mueller
cd8d6ecd81 Fix recorder DeprecationWarnings (#97738) 2023-08-04 11:32:51 +02:00
Marc Mueller
b23b2ac2e8 Fix http test DeprecationWarnings (#97737) 2023-08-04 11:32:23 +02:00
Nerdix
f68f9d4e01 Fix Kostal_Plenticore SELECT entities using device_info correctly (#97690) 2023-08-04 11:31:54 +02:00
Marc Mueller
f39a35c4ef Fix jinja2 DeprecationWarnings (#97728) 2023-08-04 11:25:08 +02:00
Marc Mueller
c33e3ce212 Fix core test RuntimeWarnings (#97730) 2023-08-04 11:21:57 +02:00
Marc Mueller
e905ac173f Fix command_line tests RuntimeWarnings (#97731) 2023-08-04 09:34:55 +02:00
Marc Mueller
99145d46d2 Fix keymitt_ble RuntimeWarning (#97729) 2023-08-04 09:34:04 +02:00
tronikos
b16254a0de Bump opower to 0.0.20 (#97752) 2023-08-04 09:32:59 +02:00
Luke
df45e52dc5 Add battery sensor to Roborock (#97715)
* add battery sensor

* Remove translation for battery

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-04 09:28:48 +02:00
Cyr-ius
3ac2106eb8 Fix freebox enumerate raid disks (#97696) 2023-08-04 09:25:51 +02:00
Marc Mueller
37885400c9 Fix mqtt test DeprecationWarnings (#97734) 2023-08-04 09:21:36 +02:00
Joost Lekkerkerker
1587ac2137 Waqi State unknown if value is string (#97617) 2023-08-04 08:45:36 +02:00
Nathan Spencer
615d7f0da7 Add ability to remove Litter-Robot if no longer provided by integration (#97702) 2023-08-03 20:30:03 -10:00
J. Nick Koston
0cc80a9d29 Add OUI to tplink diagnostics (#97646)
* Add OUI to tplink diagnostics

The main reason discovery does not work for new devices is we
are missing the OUI. Since we redact the whole mac address in
the diagnostics, this makes it difficult to fix. We now include
the OUI in the diagnostics

* fix: use cached mac

* fix: tests
2023-08-03 22:49:55 -04:00
J. Nick Koston
d1f8309423 Bump zeroconf to 0.74.0 (#97745)
* Bump zeroconf to 0.74.0

changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.72.3...0.74.0

- more cython build fixes
- performance improvements (mostly for pyatv)

* handle typing

* handle typing

* remove if TYPE_CHECKING, this doesnt get called that often

* remove if TYPE_CHECKING, this doesnt get called that often
2023-08-03 22:48:53 -04:00
Marc Mueller
ddb384c2ed Fix airvisual RuntimeWarning (#97739) 2023-08-04 01:23:12 +02:00
Marc Mueller
d1ad1c47e6 Fix zha test RuntimeWarnings (#97733) 2023-08-04 01:07:11 +02:00
J. Nick Koston
282ae80cc2 Fix hassfest check for schema (#97713)
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
2023-08-03 12:55:33 -10:00
Marc Mueller
52fc3c26d1 Fix yalex_ble test RuntimeWarning (#97732) 2023-08-03 12:55:18 -10:00
TheJulianJES
f7aec46b69 Bump zigpy to 0.56.4 (#97722) 2023-08-03 18:11:36 -04:00
Matthias Alphart
fd26739bbf Fix unloading KNX integration without sensors (#97720) 2023-08-03 23:23:12 +02:00
Michael Arthur
bfa394d399 address code comments / tidy ups (#97716) 2023-08-03 23:09:14 +02:00
Paul Bottein
83af2f5b8b Allow to sort options in select selector (#97680)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-08-03 21:49:22 +02:00
Blastoise186
d33955c467 Bump Cryptography to 41.0.3 for a second security fix (#97611)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-08-03 21:36:12 +02:00
TheJulianJES
bae5a3dbd6 Fix ZHA turn_on issues with transition=0, improve tests (#97539)
* Fix turn_on ignoring transition=0 and brightness=None, add test

This fixes light.turn_on for ZHA lights ignoring a transition of 0 when no brightness is given at the same time.
It also adds a test for that case.

Fixes https://github.com/home-assistant/core/issues/93265

* Add test for "force on" lights

This test checks that "force on" lights also get an "on" command (in addition to the "move to level" command) when turn_on is called with only transition=0.

* Fix "on" command sent for transition=0 calls, fix FORCE_ON missing for transition=0

This fixes an issue where the "on" command is sent in addition to a "move_to_level_with_on_off" command, even though the latter one is enough (for non-FORCE_ON lights).
It also fixes the test to not expect the unnecessary "on" command (in addition to the expected "move_to_level_with_on_off" command).

The `brightness != 0` change is needed to fix an issue where FORCE_ON lights did not get the required "on" command (in addition to "move_to_level_with_on_off") if turn_on was called with only transition=0.
(It could have been `brightness not None`, but that would also send an "on" command if turn_on is called with brightness=0 which HA somewhat "supports". The brightness != 0 check avoids that issue.)

* Improve comments in ZHA light class
2023-08-03 15:20:40 -04:00
Joost Lekkerkerker
415e4b2243 Bump python-opensky to 0.2.0 (#97687) 2023-08-03 21:17:00 +02:00
karwosts
9e7872e78b Fix NWS twice_daily forecast day/night detection (#97703) 2023-08-03 21:15:08 +02:00
Meow
6fd60024cc Refactored deprecated UNITS (#97368) 2023-08-03 21:12:01 +02:00
Robert Svensson
e7e68907fa Fix UniFi image platform not loading when passphrase is missing from WLAN (#97684) 2023-08-03 21:11:15 +02:00
Marcel van der Veldt
6ed31840bc Fix color mode attribute for both official and non official Hue lights (#97683) 2023-08-03 21:06:45 +02:00
Franck Nijhof
9b9b746138 Merge branch 'master' into dev 2023-08-03 20:59:51 +02:00
Michael
c43e6d9bd1 Fix detection of client wan-access rule in AVM Fritz!Box Tools (#97708) 2023-08-03 20:36:09 +02:00
tronikos
5e2d3b13b0 Bump opower to 0.0.19 (#97706) 2023-08-03 20:07:44 +02:00
J. Nick Koston
620525b2b4 Bump zeroconf to 0.72.3 (#97668) 2023-08-03 18:57:34 +02:00
Nick Iacullo
6f8d666b57 Enable the PRESET_MODE FanEntityFeature for VeSync air purifiers (#97657) 2023-08-03 18:30:56 +02:00
G Johansson
4c395c0124 Fix date and timestamp device class in Command Line Sensor (#97663)
* Fix date in Command Line sensor

* prettier
2023-08-03 13:59:37 +02:00
jan iversen
d50b993f64 Bump pymodbus v3.4.1. (#97612)
* Bump pymodbus v3.4.1.

* Solve mypy problem.
2023-08-03 14:34:20 +03:00
J. Nick Koston
9980051c3a Bump dbus-fast to 1.90.1 (#97619)
* Bump dbus-fast to 1.88.0

- cython 3 fixes
- performance improvements

changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.87.5...v1.88.0

* one more

* Bump dbus-fast to 1.90.0

* bump again for yet another round of cython3 fixes
2023-08-03 14:33:05 +03:00
J. Nick Koston
6e5baeec70 Bump pyatv to 0.13.3 (#97670)
changelog: https://github.com/postlund/pyatv/compare/v0.13.2...v0.13.3

maybe fixes #80215
2023-08-03 14:31:49 +03:00
Marc Mueller
1fa66953b4 Use mirror to run black with pre-commit (#95605) 2023-08-03 13:21:07 +02:00
J. Nick Koston
db5d1b10ea Fix tplink child plug state reporting (#97658)
regressed in https://github.com/home-assistant/core/pull/96246
2023-08-03 12:35:22 +02:00
J. Nick Koston
53703448ec Fix typo in tplink OUI (#97644)
The last two were reversed for https://ouilookup.com/search/788cb5
2023-08-03 12:20:35 +02:00
Luke
7b7b8689ef Bump python-roborock to 0.31.1 (#97632)
bump to 0.31.1
2023-08-03 12:15:20 +02:00
Michael Arthur
05e2acb091 Add hour of free power select to Electric Kiwi (#97515)
* add select sensor to Electric Kiwi

* Update homeassistant/components/electric_kiwi/select.py

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

* simplify the HOP select since there is only one

* remove handle coordinator state

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2023-08-03 12:06:42 +02:00
G Johansson
564e0110a4 Revert "OctoPrint add yaml config removal issue" (#97674)
Revert "OctoPrint add yaml config removal issue (#97431)"

This reverts commit 594d98822b.
2023-08-03 11:34:20 +02:00
Marc Mueller
1daac46635 Replace deprecated pkg_resources with importlib.metadata (#97628) 2023-08-03 09:14:00 +02:00
Joost Lekkerkerker
357bfc46bf Revert "Add device naming to Yeelight" (#97647)
Revert "Add device naming to Yeelight (#97639)"

This reverts commit 82f27115f5.
2023-08-03 09:13:23 +02:00
Marc Mueller
4196c43416 Fix deluge DeprecationWarning (#97624) 2023-08-03 09:12:08 +02:00
Marc Mueller
ad0873549d Fix ssl DeprecationWarnings (#97623) 2023-08-03 09:11:41 +02:00
Marc Mueller
3c2cebea72 Fix abode DeprecationWarnings (#97620) 2023-08-03 09:10:31 +02:00
Michael Hansen
7cf2199e8b Bump intents to 2023.8.2 (#97636) 2023-08-02 20:01:30 -04:00
Joost Lekkerkerker
82f27115f5 Add device naming to Yeelight (#97639) 2023-08-02 23:48:37 +02:00
Marc Mueller
9d9af0c884 Fix pylint DeprecationWarnings (#97627) 2023-08-02 20:30:41 +02:00
Marc Mueller
887e48c440 Replace deprecated aiohttp_unused_port fixture (#97626) 2023-08-02 20:30:13 +02:00
Marc Mueller
91a83e1ad2 Fix httpx DeprecationWarning (#97625) 2023-08-02 20:29:36 +02:00
Marc Mueller
0afa964724 Fix async_timeout DeprecationWarnings (#97622) 2023-08-02 20:29:03 +02:00
Marc Mueller
1a77121c02 Fix aiohttp code DeprecationWarnings (#97621) 2023-08-02 20:28:18 +02:00
Bram Kragten
02f8000f6c Update frontend to 20230802.0 (#97614) 2023-08-02 14:43:42 +02:00
Bruno Enten
3ce05314e0 use write_registers also for target temp (#97475) 2023-08-02 13:36:05 +02:00
Robert Resch
e4303e4534 Add rounding back when unique_id is not set (#97603) 2023-08-02 11:26:25 +02:00
Robert Resch
f9ac102c27 Fix duotecno's name to be sync with the docs (#97602) 2023-08-02 09:09:13 +02:00
Michael
db4c9c67a2 Do not set hass data before first coordinator refresh (#97343) 2023-08-02 08:29:00 +02:00
Jack Boswell
49b9dd2a4f Add Starlink to .strict-typing (#97598) 2023-08-02 08:26:50 +02:00
Maikel Punie
ac9ec6e402 Bump pyDuotecno to 2023.8.1 (#97583) 2023-08-02 08:23:37 +02:00
J. Nick Koston
93d7165fe9 Bump zeroconf to 0.72.0 (#97594) 2023-08-02 08:19:22 +02:00
Jack Boswell
7a92bda514 Fix Starlink Roaming name being blank (#97597) 2023-08-02 08:12:49 +02:00
Franck Nijhof
af5fc7e759 Ensure load the device registry if it contains invalid configuration URLs (#97589) 2023-08-01 23:15:31 +02:00
Franck Nijhof
33e5e3c5c2 Ensure we have an valid configuration URL in NetGear (#97590) 2023-08-01 22:29:16 +02:00
TheJulianJES
cad845f5c9 Bump zha-quirks to 0.0.102 (#97588) 2023-08-01 22:26:36 +02:00
Joost Lekkerkerker
7096daa0c1 Unignore today's collection for Rova (#97567) 2023-08-01 21:31:45 +02:00
Robert Svensson
6c95e07b7d Fix UniFi image platform failing to setup on read-only account (#97580) 2023-08-01 21:18:33 +02:00
J. Nick Koston
708b00d7ab Use legacy rules for ESPHome entity_id construction if friendly_name is unset (#97578) 2023-08-01 21:08:12 +02:00
Maikel Punie
b20a286b5b Bump pyduotecno to 2023.8.0 (beta fix) (#97564)
* Bump pyduotecno to 2023.7.4

* Bump to 2023.8.0
2023-08-01 14:39:31 +02:00
Bram Kragten
1773f1ead2 Update frontend to 20230801.0 (#97561) 2023-08-01 11:46:37 +02:00
Pedro Lamas
3f5c0a1285 Fixes London Air parsing error (#97557) 2023-08-01 11:04:30 +02:00
J. Nick Koston
7e134a3d44 Cleanups to the Bluetooth processor coordinators (#97546) 2023-08-01 10:27:25 +02:00
Eric Severance
8b8a51ffc8 Bump pywemo to 1.2.1 (#97550) 2023-08-01 10:08:08 +02:00
Jack Boswell
122794ada8 Fix Starlink ping drop rate reporting (#97555) 2023-08-01 10:06:19 +02:00
Michael Hansen
8ad37d7640 Send language to Wyoming STT (#97344) 2023-08-01 10:05:01 +02:00
Phil Bruckner
5aa3e36754 Bump life360 package to 6.0.0 (#97549) 2023-08-01 10:04:04 +02:00
Jan Bouwhuis
9c6c391eea Offer work- a-round for MQTT entity names that start with the device name (#97495)
Co-authored-by: SukramJ <sukramj@icloud.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-08-01 10:03:08 +02:00
J. Nick Koston
5ce8e0e33e Bump HAP-python to 4.7.1 (#97545) 2023-08-01 09:49:20 +02:00
tronikos
3a2549829e Bump opower to 0.0.18 (#97548) 2023-08-01 09:45:17 +02:00
janmolemans
f0640fc057 Add frequency sensors to Nibe (#89072)
* added frequency (for compressors etc)

---------

Co-authored-by: Joakim Plate <elupus@ecce.se>
2023-07-31 22:23:32 +02:00
Jan Bouwhuis
a8a3b6778e Fix unit tests for wake_on_lan (#97542) 2023-07-31 21:16:58 +02:00
starkillerOG
121fc7778d Bump reolink_aio to 0.7.6 + Timeout (#97464) 2023-07-31 21:01:25 +02:00
J. Nick Koston
094f2cbad7 Fix saving subclassed datetime objects in storage (#97502) 2023-07-31 18:49:02 +02:00
Joost Lekkerkerker
c2e9fd85c2 Fix RootFolder not iterable in Radarr (#97537)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2023-07-31 18:44:03 +02:00
David Knowles
085e274f44 Bump pyschlage to 2023.7.0 (#97366) 2023-07-31 18:38:50 +02:00
Marc Mueller
4d9b73033d Update python-typing-update to 0.6.0 (#97531) 2023-07-31 17:59:40 +02:00
Marc Mueller
dba6330fc8 Update pydiscovergy to 2.0.3 (#97509)
* Update pydiscovergy to 2.0.3

* Fix mypy

* Fix tests
2023-07-31 17:25:57 +02:00
epenet
9df1805b3b Remove myself from const and util codeowners (#97527) 2023-07-31 07:06:06 -07:00
J. Nick Koston
e7069d48be Load homekit_controller test data using its json loader (#97534) 2023-07-31 07:04:41 -07:00
Joakim Plate
651d4134cf Avoid leaking exception trace for philips_js (#97491)
Avoid leaking exception trace
2023-07-31 14:21:34 +02:00
epenet
787486b68d Remove myself from rest codeowners (#97528) 2023-07-31 12:50:44 +02:00
Eric Severance
7b25702605 Bump pywemo to 1.2.0 (#97520) 2023-07-31 12:38:31 +02:00
epenet
83db3c48c2 Fix unused variable in Renault tests (#97529) 2023-07-31 12:35:30 +02:00
epenet
927905ac84 Handle http error in Renault initialisation (#97530) 2023-07-31 12:17:51 +02:00
epenet
ed3ebdfea5 Remove myself from scrape codeowners (#97524) 2023-07-31 11:15:59 +02:00
J. Nick Koston
37cdd51183 Combine Bluetooth update coordinator subscriptions to reduce code duplication (#97503) 2023-07-31 01:03:19 -07:00
J. Nick Koston
5c4e47c127 Use internal imports in Bluetooth update coordinator to avoid future circular imports (#97506) 2023-07-31 01:02:57 -07:00
J. Nick Koston
28bebf338f Fix xiaomi_ble not remembering a device is a sleepy device (#97518) 2023-07-31 01:02:36 -07:00
J. Nick Koston
7bda873c2a Fix bthome not remembering a device is a sleepy device (#97517) 2023-07-31 01:02:15 -07:00
starkillerOG
b266514068 Delay creation of Reolink repair issues (#97476)
* delay creation of repair issues

* fix tests
2023-07-31 09:07:13 +02:00
J. Nick Koston
f218fb8cee Fix typo in PassiveBluetoothDataProcessor (#97508) 2023-07-31 08:56:30 +02:00
tronikos
eb56c7e1b7 Bump androidtvremote2==0.0.13 (#97494) 2023-07-30 21:27:57 +02:00
Meow
a4b2ded503 Refactor deprecated RESULT_TYPE_* (#97367) 2023-07-30 19:27:30 +02:00
Joost Lekkerkerker
04e72fec57 Add entity translation to Moon (#97404) 2023-07-30 19:10:45 +02:00
Joost Lekkerkerker
15b7035ad0 Change IoT class for Moon to calculated (#97405) 2023-07-30 19:03:25 +02:00
Jan-Philipp Benecke
cb033f7a7b Change IoT class for ToD to calculated (#97422) 2023-07-30 19:02:43 +02:00
Marc Mueller
1553ff1001 Update pydantic to 1.10.12 (#97479) 2023-07-30 18:55:13 +02:00
Marc Mueller
71cc227f09 Update aiopvpc to 4.2.2 (#97482) 2023-07-30 18:54:45 +02:00
Niels Perfors
a70c10d3c9 Upgrade Verisure to 2.6.4 (#97278) 2023-07-30 18:53:26 +02:00
starkillerOG
f4e79bbab8 Regard long poll without events as valid (#97383) 2023-07-30 18:49:27 +02:00
starkillerOG
c32b15c754 Reolink long poll recover (#97465) 2023-07-30 18:49:00 +02:00
G Johansson
1f11ce63fc Manual trigger entity fix name influence entity_id (#97398) 2023-07-30 18:47:34 +02:00
Joost Lekkerkerker
2b4387f7c2 Return the actual media url from media extractor (#97408) 2023-07-30 18:43:42 +02:00
Chris Talkington
9d2f52dbc5 Allow deleting config entry devices in jellyfin (#97377) 2023-07-30 18:42:28 +02:00
tronikos
28dc819212 Bump opower to 0.0.16 (#97437) 2023-07-30 18:41:14 +02:00
Marc Mueller
68cb7a7dde Update ha-av to 10.1.1 (#97481) 2023-07-30 18:40:38 +02:00
J. Nick Koston
84576672de Bump dbus-fast to 1.87.5 (#97364) 2023-07-30 18:39:40 +02:00
J. Nick Koston
afdbbefc31 Revert using has_entity_name in ESPHome when friendly_name is not set (#97488) 2023-07-30 18:28:45 +02:00
Marc Mueller
5e3bcc1224 Update zigpy to 0.56.3 (#97480) 2023-07-30 09:30:13 -04:00
Maciej Bieniek
12426dfad4 Add entity translations for AccuWeather (#95940)
* Add entity name translations

* Some improvements

* Update tests

* Suggested changes

* day 0 -> today

* night 0 -> tonight

* Fix precipitation
2023-07-29 21:22:53 +02:00
mbo18
598e26e481 Add device and state class to humidity sensor (#97331)
Add device and state class to humidity
2023-07-29 17:40:14 +02:00
Renier Moorcroft
f52876c7f6 Add entity description to EZVIZ SwitchEntity (#95672)
* Initial commit

* Update switch entity

* Add entity description

* Redundant get. Key will always be there.

* fixed dumb condition mistake.

* Removed names from entity description

* Implement suggestions

* async_add_entities has iterator so cleanup

* Update strings.json
2023-07-29 17:37:40 +02:00
Michael
750260b266 Add more sensors to PEGELONLINE (#97295)
* add further sensors

* adjust and improve tests

* add device classes were applicable

* fix doc string

* name for ph comes from device class

* use icon from device class for ph sensor
2023-07-29 17:03:29 +02:00
Richard Kroegel
b7ed163caf bmw_connected_drive: Add WASHING_FLUID to correct binary sensor attribute (#97448)
BMW: Add WASHING_FLUID to correct binary sensor attribute

Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2023-07-29 12:32:55 +02:00
David Knowles
0e8bbbd3d9 Add a battery sensor to Schlage (#97369) 2023-07-28 21:09:25 -07:00
G Johansson
78003886a5 GDACS add yaml config issue (#97424)
gdacs remove yaml issue
2023-07-29 02:22:54 +02:00
J. Nick Koston
0a56361ca4 Disable always_update in nexia coordinator (#97436) 2023-07-28 17:46:26 -05:00
J. Nick Koston
c11222c1d0 Bump nexia to 2.0.7 (#97432) 2023-07-28 16:37:08 -05:00
J. Nick Koston
a2555e71e2 Small cleanups to ambient station (#97421) 2023-07-28 16:30:29 -05:00
G Johansson
fc38451faf Disable always_update in yale_smart_alarm coordinator (#97426)
always update
2023-07-28 23:29:01 +02:00
J. Nick Koston
1b6f15e3da Disable always_update in enphase_envoy coordinator (#97425)
see https://github.com/home-assistant/developers.home-assistant/pull/1863
2023-07-28 23:28:41 +02:00
J. Nick Koston
a3110ef1c1 Disable always_update in oncue coordinator (#97434) 2023-07-28 16:23:37 -05:00
G Johansson
7966d8da76 LiteJet add yaml config removal issue (#97429)
litejet yaml config removal issue
2023-07-28 23:21:49 +02:00
G Johansson
05e9b63b16 MELCloud add yaml config removal issue (#97430)
melcloud yaml config removal issue
2023-07-28 23:21:34 +02:00
G Johansson
594d98822b OctoPrint add yaml config removal issue (#97431)
OctoPrint yaml config removal issue
2023-07-28 23:21:15 +02:00
G Johansson
cd311f4868 meteo_france add yaml config removal issue (#97428)
meteo_france yaml removal issue
2023-07-28 23:01:21 +02:00
J. Nick Koston
1b10c44a16 Disable always_update in esphome dashboard coordinator (#97419) 2023-07-28 12:42:04 -05:00
J. Nick Koston
c1ce14983c Disable always_update in filesize coordinator (#97418) 2023-07-28 12:41:45 -05:00
Joost Lekkerkerker
8101376ad5 Small cleanup in event entity (#97409) 2023-07-28 19:41:41 +02:00
J. Nick Koston
e1f14ed990 Disable always_update in cert_expiry coordinator (#97417) 2023-07-28 12:41:25 -05:00
J. Nick Koston
44a3885986 Disable always_update in powerwall coordinator (#97416) 2023-07-28 12:41:05 -05:00
J. Nick Koston
5829efb5d7 Disable always_update in lookin coordinator (#97415) 2023-07-28 12:40:50 -05:00
J. Nick Koston
8a9153d004 Disable always_update in emonitor coordinator (#97414) 2023-07-28 12:39:15 -05:00
J. Nick Koston
2ec2c25f5a Disable always_update in nut coordinator (#97413) 2023-07-28 12:38:52 -05:00
J. Nick Koston
e4ac8bdd6b Disable always_update in flux_led coordinator (#97412) 2023-07-28 12:38:33 -05:00
J. Nick Koston
48eebe43c9 Disable always_update in steamist coordinator (#97411) 2023-07-28 12:38:07 -05:00
J. Nick Koston
54e7185617 Disable always_update in rain machine coordinator (#97410) 2023-07-28 12:33:33 -05:00
J. Nick Koston
13349e76ed Avoid firing update coordinator callbacks when nothing has changed (#97268) 2023-07-28 12:19:20 -05:00
G Johansson
3a54448836 Bump pysensibo to 1.0.32 (#97382) 2023-07-28 11:52:23 +02:00
G Johansson
81c0dcff63 Home Connect deprecation issue yaml configuration (#97361)
Home Connect deprecation issue
2023-07-27 23:34:17 +02:00
G Johansson
2299430dbe Sonos add yaml config issue (#97365) 2023-07-27 23:33:49 +02:00
starkillerOG
053f4b08b6 Bump reolink_aio to 0.7.5 (#97357)
* bump reolink-aio to 0.7.4

* Bump reolink_aio to 0.7.5
2023-07-27 23:33:08 +02:00
Joost Lekkerkerker
973370b75e Deprecate Freebox YAML (#97345)
* Deprecate Freebox YAML

* Update homeassistant/components/freebox/__init__.py

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-07-27 23:01:17 +02:00
G Johansson
ae3cc0b619 Sure Petcare deprecation issue yaml configuration (#97363)
Sure Petcare yaml config issue
2023-07-27 22:53:51 +02:00
G Johansson
4ac139592a Plum Lightpad deprecation issue for yaml configuration (#97362)
Plum issue yaml config
2023-07-27 22:53:24 +02:00
G Johansson
2d4040c70a Netatmo add issue for yaml deprecation (#97360)
Netatmo add issue
2023-07-27 22:50:50 +02:00
G Johansson
3fdcb67322 Add breaks_in_ha_version for Dynalite YAML import (#97359)
Dynalite breaks in version
2023-07-27 22:26:35 +02:00
J.P. Krauss
70cb8afb94 Add AirNow Reporting Station as sensor (#97273)
* Add AirNow Reporting Station as sensor attribute

* Make Reporting Station a sensor instead of attribute as requested

* Update homeassistant/components/airnow/strings.json

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

* Fix reporting station attribute names to avoid showing on map

* Add attribute name translations

* Update homeassistant/components/airnow/strings.json

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

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2023-07-27 21:19:16 +02:00
Michael Hansen
7e3fdd85fc Add wildcards to sentence triggers (#97236)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-07-27 20:30:42 +02:00
Franck Nijhof
af286a8feb Add urllib3<2 package constraint (#97339) 2023-07-27 18:57:13 +02:00
Franck Nijhof
737ac8c600 Fix DeviceInfo configuration_url validation (#97319) 2023-07-27 18:57:01 +02:00
J. Nick Koston
b92e7c5ddf Bump aiohomekit to 2.6.12 (#97342) 2023-07-27 18:56:45 +02:00
Marcel van der Veldt
7ada88eab3 Hue event entity follow up (#97336) 2023-07-27 16:58:09 +02:00
Jc2k
cc47ff30b3 Split availability and data subscriptions in homekit_controller (#97337) 2023-07-27 09:32:53 -05:00
J. Nick Koston
cbc8ebb427 Bump aioesphomeapi to 15.1.15 (#97335)
changelog: https://github.com/esphome/aioesphomeapi/compare/v15.1.14...v15.1.15
2023-07-27 09:30:31 -05:00
Maikel Punie
374255ce87 Duotecno beta fix (#97325)
* Fix duotecno

* Implement comments

* small cover fix
2023-07-27 16:00:27 +02:00
David Knowles
2542c5f259 Fix Hydrawise zone addressing (#97333) 2023-07-27 15:57:36 +02:00
David Knowles
cd1a99a15f Bump pydrawise to 2023.7.1 (#97334) 2023-07-27 08:54:44 -05:00
dougiteixeira
e99ba1b0da Move async_client_device_info_fn to entity.py (#97270)
Move client device info
2023-07-27 10:21:22 +02:00
G Johansson
f610a9b1c9 Fix sql entities not loading (#97316) 2023-07-27 09:24:32 +02:00
Jan Bouwhuis
3fcfe7d0c6 Set mqtt entity name to null when it is a duplicate of the device name (#97304) 2023-07-27 09:23:23 +02:00
Franck Nijhof
c2bbb0b5db Fix implicit use of device name in TPLink switch (#97293) 2023-07-27 09:22:22 +02:00
Marcel van der Veldt
265fe51169 Bump aioslimproto to 2.3.3 (#97283) 2023-07-27 09:21:30 +02:00
J. Nick Koston
e9c3f0821f Fix dumping lru stats in the profiler (#97303) 2023-07-27 09:17:27 +02:00
Luke
01ba7a8698 bump python-roborock to 0.30.2 (#97306) 2023-07-27 09:13:49 +02:00
Markus Becker
3fd33c4ecc Fix typo Lomng -> Long (#97315) 2023-07-27 08:58:52 +02:00
Jan-Philipp Benecke
55beb26190 Fix authlib version constraint required by point (#97228) 2023-07-27 08:54:20 +02:00
David Knowles
b31cfe0b24 Add Schlage integration (#93777)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
2023-07-26 23:15:01 -05:00
G Johansson
7d8462b11c Weather remove forecast deprecation (#97292) 2023-07-26 23:12:01 +02:00
Michael
dce9a1b998 Rename key of water level sensor in PEGELONLINE (#97289) 2023-07-26 22:03:38 +02:00
J.P. Krauss
7113db8da4 Improve AirNow Configuration Error Handling (#97267)
* Fix config flow error handling when no data is returned by AirNow API

* Add test for PyAirNow EmptyResponseError

* Typo Fix
2023-07-26 21:30:25 +02:00
Franck Nijhof
fd3c2c2811 Fix zodiac import flow/issue (#97282) 2023-07-26 21:22:22 +02:00
Franck Nijhof
9b7dcce7ed Bump version to 2023.9.0dev0 (#97265) 2023-07-26 17:40:17 +02:00
2694 changed files with 84333 additions and 24351 deletions

View File

@@ -30,6 +30,7 @@ base_platforms: &base_platforms
- homeassistant/components/humidifier/**
- homeassistant/components/image/**
- homeassistant/components/image_processing/**
- homeassistant/components/lawn_mower/**
- homeassistant/components/light/**
- homeassistant/components/lock/**
- homeassistant/components/media_player/**
@@ -54,6 +55,7 @@ base_platforms: &base_platforms
components: &components
- homeassistant/components/alexa/**
- homeassistant/components/application_credentials/**
- homeassistant/components/assist_pipeline/**
- homeassistant/components/auth/**
- homeassistant/components/automation/**
- homeassistant/components/backup/**
@@ -86,6 +88,7 @@ components: &components
- homeassistant/components/lovelace/**
- homeassistant/components/media_source/**
- homeassistant/components/mjpeg/**
- homeassistant/components/modbus/**
- homeassistant/components/mqtt/**
- homeassistant/components/network/**
- homeassistant/components/onboarding/**

View File

@@ -57,6 +57,7 @@ omit =
homeassistant/components/ambiclimate/climate.py
homeassistant/components/ambient_station/__init__.py
homeassistant/components/ambient_station/binary_sensor.py
homeassistant/components/ambient_station/entity.py
homeassistant/components/ambient_station/sensor.py
homeassistant/components/amcrest/*
homeassistant/components/ampio/*
@@ -168,6 +169,10 @@ omit =
homeassistant/components/cmus/media_player.py
homeassistant/components/coinbase/sensor.py
homeassistant/components/comed_hourly_pricing/sensor.py
homeassistant/components/comelit/__init__.py
homeassistant/components/comelit/const.py
homeassistant/components/comelit/coordinator.py
homeassistant/components/comelit/light.py
homeassistant/components/comfoconnect/fan.py
homeassistant/components/concord232/alarm_control_panel.py
homeassistant/components/concord232/binary_sensor.py
@@ -212,10 +217,12 @@ omit =
homeassistant/components/dominos/*
homeassistant/components/doods/*
homeassistant/components/doorbird/__init__.py
homeassistant/components/doorbird/button.py
homeassistant/components/doorbird/camera.py
homeassistant/components/doorbird/button.py
homeassistant/components/doorbird/device.py
homeassistant/components/doorbird/entity.py
homeassistant/components/doorbird/util.py
homeassistant/components/doorbird/view.py
homeassistant/components/dormakaba_dkey/__init__.py
homeassistant/components/dormakaba_dkey/binary_sensor.py
homeassistant/components/dormakaba_dkey/entity.py
@@ -234,6 +241,7 @@ omit =
homeassistant/components/duotecno/entity.py
homeassistant/components/duotecno/switch.py
homeassistant/components/duotecno/cover.py
homeassistant/components/duotecno/light.py
homeassistant/components/dwd_weather_warnings/const.py
homeassistant/components/dwd_weather_warnings/coordinator.py
homeassistant/components/dwd_weather_warnings/sensor.py
@@ -270,6 +278,7 @@ omit =
homeassistant/components/electric_kiwi/oauth2.py
homeassistant/components/electric_kiwi/sensor.py
homeassistant/components/electric_kiwi/coordinator.py
homeassistant/components/electric_kiwi/select.py
homeassistant/components/eliqonline/sensor.py
homeassistant/components/elkm1/__init__.py
homeassistant/components/elkm1/alarm_control_panel.py
@@ -282,7 +291,8 @@ omit =
homeassistant/components/elmax/alarm_control_panel.py
homeassistant/components/elmax/binary_sensor.py
homeassistant/components/elmax/common.py
homeassistant/components/elmax/binary_sensor.py
homeassistant/components/elmax/const.py
homeassistant/components/elmax/cover.py
homeassistant/components/elmax/switch.py
homeassistant/components/elv/*
homeassistant/components/emby/media_player.py
@@ -299,7 +309,13 @@ omit =
homeassistant/components/enocean/sensor.py
homeassistant/components/enocean/switch.py
homeassistant/components/enphase_envoy/__init__.py
homeassistant/components/enphase_envoy/binary_sensor.py
homeassistant/components/enphase_envoy/coordinator.py
homeassistant/components/enphase_envoy/entity.py
homeassistant/components/enphase_envoy/number.py
homeassistant/components/enphase_envoy/select.py
homeassistant/components/enphase_envoy/sensor.py
homeassistant/components/enphase_envoy/switch.py
homeassistant/components/entur_public_transport/*
homeassistant/components/environment_canada/__init__.py
homeassistant/components/environment_canada/camera.py
@@ -334,6 +350,7 @@ omit =
homeassistant/components/ezviz/entity.py
homeassistant/components/ezviz/select.py
homeassistant/components/ezviz/sensor.py
homeassistant/components/ezviz/siren.py
homeassistant/components/ezviz/switch.py
homeassistant/components/ezviz/update.py
homeassistant/components/faa_delays/__init__.py
@@ -415,6 +432,7 @@ omit =
homeassistant/components/garadget/cover.py
homeassistant/components/garages_amsterdam/__init__.py
homeassistant/components/garages_amsterdam/binary_sensor.py
homeassistant/components/garages_amsterdam/entity.py
homeassistant/components/garages_amsterdam/sensor.py
homeassistant/components/gc100/*
homeassistant/components/geniushub/*
@@ -708,8 +726,6 @@ omit =
homeassistant/components/meteoclimatic/__init__.py
homeassistant/components/meteoclimatic/sensor.py
homeassistant/components/meteoclimatic/weather.py
homeassistant/components/metoffice/sensor.py
homeassistant/components/metoffice/weather.py
homeassistant/components/microsoft/tts.py
homeassistant/components/mikrotik/hub.py
homeassistant/components/mill/climate.py
@@ -764,10 +780,12 @@ omit =
homeassistant/components/neato/__init__.py
homeassistant/components/neato/api.py
homeassistant/components/neato/camera.py
homeassistant/components/neato/entity.py
homeassistant/components/neato/hub.py
homeassistant/components/neato/sensor.py
homeassistant/components/neato/switch.py
homeassistant/components/neato/vacuum.py
homeassistant/components/neato/button.py
homeassistant/components/nederlandse_spoorwegen/sensor.py
homeassistant/components/netdata/sensor.py
homeassistant/components/netgear/__init__.py
@@ -859,7 +877,6 @@ omit =
homeassistant/components/openhome/const.py
homeassistant/components/openhome/media_player.py
homeassistant/components/opensensemap/air_quality.py
homeassistant/components/opensky/sensor.py
homeassistant/components/opentherm_gw/__init__.py
homeassistant/components/opentherm_gw/binary_sensor.py
homeassistant/components/opentherm_gw/climate.py
@@ -989,6 +1006,7 @@ omit =
homeassistant/components/renson/const.py
homeassistant/components/renson/entity.py
homeassistant/components/renson/sensor.py
homeassistant/components/renson/binary_sensor.py
homeassistant/components/raspyrfm/*
homeassistant/components/recollect_waste/sensor.py
homeassistant/components/recorder/repack.py
@@ -1167,7 +1185,13 @@ omit =
homeassistant/components/squeezebox/__init__.py
homeassistant/components/squeezebox/browse_media.py
homeassistant/components/squeezebox/media_player.py
homeassistant/components/starlink/__init__.py
homeassistant/components/starlink/binary_sensor.py
homeassistant/components/starlink/button.py
homeassistant/components/starlink/coordinator.py
homeassistant/components/starlink/device_tracker.py
homeassistant/components/starlink/sensor.py
homeassistant/components/starlink/switch.py
homeassistant/components/starline/__init__.py
homeassistant/components/starline/account.py
homeassistant/components/starline/binary_sensor.py
@@ -1311,9 +1335,6 @@ omit =
homeassistant/components/tplink_omada/__init__.py
homeassistant/components/tplink_omada/binary_sensor.py
homeassistant/components/tplink_omada/controller.py
homeassistant/components/tplink_omada/coordinator.py
homeassistant/components/tplink_omada/entity.py
homeassistant/components/tplink_omada/switch.py
homeassistant/components/tplink_omada/update.py
homeassistant/components/traccar/device_tracker.py
homeassistant/components/tractive/__init__.py
@@ -1333,9 +1354,11 @@ omit =
homeassistant/components/trafikverket_train/__init__.py
homeassistant/components/trafikverket_train/coordinator.py
homeassistant/components/trafikverket_train/sensor.py
homeassistant/components/trafikverket_train/util.py
homeassistant/components/trafikverket_weatherstation/__init__.py
homeassistant/components/trafikverket_weatherstation/coordinator.py
homeassistant/components/trafikverket_weatherstation/sensor.py
homeassistant/components/transmission/__init__.py
homeassistant/components/transmission/sensor.py
homeassistant/components/transmission/switch.py
homeassistant/components/travisci/sensor.py
@@ -1418,6 +1441,10 @@ omit =
homeassistant/components/vlc/media_player.py
homeassistant/components/vlc_telnet/__init__.py
homeassistant/components/vlc_telnet/media_player.py
homeassistant/components/vodafone_station/__init__.py
homeassistant/components/vodafone_station/const.py
homeassistant/components/vodafone_station/coordinator.py
homeassistant/components/vodafone_station/device_tracker.py
homeassistant/components/volkszaehler/sensor.py
homeassistant/components/volumio/__init__.py
homeassistant/components/volumio/browse_media.py
@@ -1487,7 +1514,6 @@ omit =
homeassistant/components/yale_smart_alarm/alarm_control_panel.py
homeassistant/components/yale_smart_alarm/binary_sensor.py
homeassistant/components/yale_smart_alarm/button.py
homeassistant/components/yale_smart_alarm/coordinator.py
homeassistant/components/yale_smart_alarm/entity.py
homeassistant/components/yale_smart_alarm/lock.py
homeassistant/components/yalexs_ble/__init__.py
@@ -1502,6 +1528,9 @@ omit =
homeassistant/components/yamaha_musiccast/select.py
homeassistant/components/yamaha_musiccast/switch.py
homeassistant/components/yandex_transport/sensor.py
homeassistant/components/yardian/__init__.py
homeassistant/components/yardian/coordinator.py
homeassistant/components/yardian/switch.py
homeassistant/components/yeelightsunflower/light.py
homeassistant/components/yi/camera.py
homeassistant/components/yolink/__init__.py

View File

@@ -24,7 +24,7 @@ jobs:
publish: ${{ steps.version.outputs.publish }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
with:
fetch-depth: 0
@@ -56,7 +56,7 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.7.0
@@ -98,7 +98,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
@@ -197,7 +197,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2023.06.1
uses: home-assistant/builder@2023.08.0
with:
args: |
$BUILD_ARGS \
@@ -251,9 +251,10 @@ jobs:
- raspberrypi4-64
- tinker
- yellow
- green
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set build additional args
run: |
@@ -274,7 +275,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2023.06.1
uses: home-assistant/builder@2023.08.0
with:
args: |
$BUILD_ARGS \
@@ -292,7 +293,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
@@ -330,7 +331,7 @@ jobs:
id-token: write
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Install Cosign
uses: sigstore/cosign-installer@v3.1.1

View File

@@ -19,6 +19,10 @@ on:
description: "Skip pytest"
default: false
type: boolean
skip-coverage:
description: "Skip coverage"
default: false
type: boolean
pylint-only:
description: "Only run pylint"
default: false
@@ -32,7 +36,7 @@ env:
CACHE_VERSION: 5
PIP_CACHE_VERSION: 4
MYPY_CACHE_VERSION: 4
HA_SHORT_VERSION: 2023.8
HA_SHORT_VERSION: 2023.9
DEFAULT_PYTHON: "3.11"
ALL_PYTHON_VERSIONS: "['3.11']"
# 10.3 is the oldest supported version
@@ -79,10 +83,11 @@ jobs:
test_groups: ${{ steps.info.outputs.test_groups }}
tests_glob: ${{ steps.info.outputs.tests_glob }}
tests: ${{ steps.info.outputs.tests }}
skip_coverage: ${{ steps.info.outputs.skip_coverage }}
runs-on: ubuntu-22.04
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Generate partial Python venv restore key
id: generate_python_cache_key
run: >-
@@ -127,6 +132,7 @@ jobs:
test_group_count=10
tests="[]"
tests_glob=""
skip_coverage=""
if [[ "${{ steps.integrations.outputs.changes }}" != "[]" ]];
then
@@ -176,6 +182,12 @@ jobs:
test_full_suite="true"
fi
if [[ "${{ github.event.inputs.skip-coverage }}" == "true" ]] \
|| [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci-skip-coverage') }}" == "true" ]];
then
skip_coverage="true"
fi
# Output & sent to GitHub Actions
echo "mariadb_groups: ${mariadb_groups}"
echo "mariadb_groups=${mariadb_groups}" >> $GITHUB_OUTPUT
@@ -195,6 +207,8 @@ jobs:
echo "tests=${tests}" >> $GITHUB_OUTPUT
echo "tests_glob: ${tests_glob}"
echo "tests_glob=${tests_glob}" >> $GITHUB_OUTPUT
echo "skip_coverage: ${skip_coverage}"
echo "skip_coverage=${skip_coverage}" >> $GITHUB_OUTPUT
pre-commit:
name: Prepare pre-commit base
@@ -206,7 +220,7 @@ jobs:
- info
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v4.7.0
@@ -251,7 +265,7 @@ jobs:
- pre-commit
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.7.0
id: python
@@ -297,7 +311,7 @@ jobs:
- pre-commit
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.7.0
id: python
@@ -346,7 +360,7 @@ jobs:
- pre-commit
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.7.0
id: python
@@ -440,7 +454,7 @@ jobs:
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v4.7.0
@@ -508,7 +522,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v4.7.0
@@ -540,7 +554,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v4.7.0
@@ -573,7 +587,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v4.7.0
@@ -617,7 +631,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v4.7.0
@@ -699,7 +713,7 @@ jobs:
bluez \
ffmpeg
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v4.7.0
@@ -734,9 +748,19 @@ jobs:
- name: Run pytest (fully)
if: needs.info.outputs.test_full_suite == 'true'
timeout-minutes: 60
id: pytest-full
env:
PYTHONDONTWRITEBYTECODE: 1
run: |
. venv/bin/activate
python --version
set -o pipefail
cov_params=()
if [[ "${{ needs.info.outputs.skip_coverage }}" != "true" ]]; then
cov_params+=(--cov="homeassistant")
cov_params+=(--cov-report=xml)
fi
python3 -X dev -m pytest \
-qq \
--timeout=9 \
@@ -745,37 +769,54 @@ jobs:
--dist=loadfile \
--test-group-count ${{ needs.info.outputs.test_group_count }} \
--test-group=${{ matrix.group }} \
--cov="homeassistant" \
--cov-report=xml \
${cov_params[@]} \
-o console_output_style=count \
-p no:sugar \
tests
tests \
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Run pytest (partially)
if: needs.info.outputs.test_full_suite == 'false'
timeout-minutes: 10
id: pytest-partial
shell: bash
env:
PYTHONDONTWRITEBYTECODE: 1
run: |
. venv/bin/activate
python --version
set -o pipefail
if [[ ! -f "tests/components/${{ matrix.group }}/__init__.py" ]]; then
echo "::error:: missing file tests/components/${{ matrix.group }}/__init__.py"
exit 1
fi
cov_params=()
if [[ "${{ needs.info.outputs.skip_coverage }}" != "true" ]]; then
cov_params+=(--cov="homeassistant.components.${{ matrix.group }}")
cov_params+=(--cov-report=xml)
cov_params+=(--cov-report=term-missing)
fi
python3 -X dev -m pytest \
-qq \
--timeout=9 \
-n auto \
--cov="homeassistant.components.${{ matrix.group }}" \
--cov-report=xml \
--cov-report=term-missing \
${cov_params[@]} \
-o console_output_style=count \
--durations=0 \
--durations-min=1 \
-p no:sugar \
tests/components/${{ matrix.group }}
tests/components/${{ matrix.group }} \
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && (steps.pytest-full.conclusion == 'failure' || steps.pytest-partial.conclusion == 'failure')
uses: actions/upload-artifact@v3.1.2
with:
name: pytest-${{ github.run_number }}
path: pytest-*.txt
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v3.1.2
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
@@ -824,7 +865,7 @@ jobs:
ffmpeg \
libmariadb-dev-compat
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v4.7.0
@@ -862,18 +903,27 @@ jobs:
python3 -m script.translations develop --all
- name: Run pytest (partially)
timeout-minutes: 20
id: pytest-partial
shell: bash
env:
PYTHONDONTWRITEBYTECODE: 1
run: |
. venv/bin/activate
python --version
set -o pipefail
mariadb=$(echo "${{ matrix.mariadb-group }}" | sed "s/:/-/g")
cov_params=()
if [[ "${{ needs.info.outputs.skip_coverage }}" != "true" ]]; then
cov_params+=(--cov="homeassistant.components.recorder")
cov_params+=(--cov-report=xml)
cov_params+=(--cov-report=term-missing)
fi
python3 -X dev -m pytest \
-qq \
--timeout=20 \
-n 1 \
--cov="homeassistant.components.recorder" \
--cov-report=xml \
--cov-report=term-missing \
${cov_params[@]} \
-o console_output_style=count \
--durations=10 \
-p no:sugar \
@@ -881,8 +931,16 @@ jobs:
tests/components/history \
tests/components/logbook \
tests/components/recorder \
tests/components/sensor
tests/components/sensor \
2>&1 | tee pytest-${{ matrix.python-version }}-${mariadb}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v3.1.2
with:
name: pytest-${{ github.run_number }}
path: pytest-*.txt
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v3.1.2
with:
name: coverage-${{ matrix.python-version }}-mariadb
@@ -931,7 +989,7 @@ jobs:
ffmpeg \
postgresql-server-dev-14
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v4.7.0
@@ -969,18 +1027,27 @@ jobs:
python3 -m script.translations develop --all
- name: Run pytest (partially)
timeout-minutes: 20
id: pytest-partial
shell: bash
env:
PYTHONDONTWRITEBYTECODE: 1
run: |
. venv/bin/activate
python --version
set -o pipefail
postgresql=$(echo "${{ matrix.postgresql-group }}" | sed "s/:/-/g")
cov_params=()
if [[ "${{ needs.info.outputs.skip_coverage }}" != "true" ]]; then
cov_params+=(--cov="homeassistant.components.recorder")
cov_params+=(--cov-report=xml)
cov_params+=(--cov-report=term-missing)
fi
python3 -X dev -m pytest \
-qq \
--timeout=9 \
-n 1 \
--cov="homeassistant.components.recorder" \
--cov-report=xml \
--cov-report=term-missing \
${cov_params[@]} \
-o console_output_style=count \
--durations=0 \
--durations-min=10 \
@@ -989,8 +1056,16 @@ jobs:
tests/components/history \
tests/components/logbook \
tests/components/recorder \
tests/components/sensor
tests/components/sensor \
2>&1 | tee pytest-${{ matrix.python-version }}-${postgresql}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@v3.1.2
with:
name: pytest-${{ github.run_number }}
path: pytest-*.txt
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@v3.1.0
with:
name: coverage-${{ matrix.python-version }}-postgresql
@@ -1001,6 +1076,7 @@ jobs:
coverage:
name: Upload test coverage to Codecov
if: needs.info.outputs.skip_coverage != 'true'
runs-on: ubuntu-22.04
needs:
- info
@@ -1008,7 +1084,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Download all coverage artifacts
uses: actions/download-artifact@v3
- name: Upload coverage to Codecov (full coverage)

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.7.0

View File

@@ -26,7 +26,7 @@ jobs:
architectures: ${{ steps.info.outputs.architectures }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Get information
id: info
@@ -84,7 +84,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Download env_file
uses: actions/download-artifact@v3
@@ -122,7 +122,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Download env_file
uses: actions/download-artifact@v3

1
.gitignore vendored
View File

@@ -67,6 +67,7 @@ htmlcov/
test-reports/
test-results.xml
test-output.xml
pytest-*.txt
# Translations
*.mo

View File

@@ -1,11 +1,11 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.280
rev: v0.0.285
hooks:
- id: ruff
args:
- --fix
- repo: https://github.com/psf/black
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.7.0
hooks:
- id: black
@@ -43,7 +43,7 @@ repos:
hooks:
- id: prettier
- repo: https://github.com/cdce8p/python-typing-update
rev: v0.5.0
rev: v0.6.0
hooks:
# Run `python-typing-update` hook manually from time to time
# to update python typing syntax.
@@ -52,7 +52,7 @@ repos:
- id: python-typing-update
stages: [manual]
args:
- --py310-plus
- --py311-plus
- --force
- --keep-updates
files: ^(homeassistant|tests|script)/.+\.py$

View File

@@ -53,6 +53,7 @@ homeassistant.components.airzone_cloud.*
homeassistant.components.aladdin_connect.*
homeassistant.components.alarm_control_panel.*
homeassistant.components.alert.*
homeassistant.components.alexa.*
homeassistant.components.amazon_polly.*
homeassistant.components.ambient_station.*
homeassistant.components.amcrest.*
@@ -103,6 +104,7 @@ homeassistant.components.dhcp.*
homeassistant.components.diagnostics.*
homeassistant.components.dlna_dmr.*
homeassistant.components.dnsip.*
homeassistant.components.doorbird.*
homeassistant.components.dormakaba_dkey.*
homeassistant.components.dsmr.*
homeassistant.components.dunehd.*
@@ -147,6 +149,7 @@ homeassistant.components.history.*
homeassistant.components.homeassistant.exposed_entities
homeassistant.components.homeassistant.triggers.event
homeassistant.components.homeassistant_alerts.*
homeassistant.components.homeassistant_green.*
homeassistant.components.homeassistant_hardware.*
homeassistant.components.homeassistant_sky_connect.*
homeassistant.components.homeassistant_yellow.*
@@ -181,6 +184,7 @@ homeassistant.components.imap.*
homeassistant.components.input_button.*
homeassistant.components.input_select.*
homeassistant.components.integration.*
homeassistant.components.ipp.*
homeassistant.components.iqvia.*
homeassistant.components.isy994.*
homeassistant.components.jellyfin.*
@@ -193,6 +197,7 @@ homeassistant.components.lacrosse.*
homeassistant.components.lacrosse_view.*
homeassistant.components.lametric.*
homeassistant.components.laundrify.*
homeassistant.components.lawn_mower.*
homeassistant.components.lcn.*
homeassistant.components.ld2410_ble.*
homeassistant.components.lidarr.*
@@ -209,6 +214,7 @@ homeassistant.components.luftdaten.*
homeassistant.components.mailbox.*
homeassistant.components.mastodon.*
homeassistant.components.matter.*
homeassistant.components.media_extractor.*
homeassistant.components.media_player.*
homeassistant.components.media_source.*
homeassistant.components.metoffice.*
@@ -296,6 +302,7 @@ homeassistant.components.sonarr.*
homeassistant.components.speedtestdotnet.*
homeassistant.components.sql.*
homeassistant.components.ssdp.*
homeassistant.components.starlink.*
homeassistant.components.statistics.*
homeassistant.components.steamist.*
homeassistant.components.stookalert.*
@@ -321,6 +328,7 @@ homeassistant.components.tplink.*
homeassistant.components.tplink_omada.*
homeassistant.components.tractive.*
homeassistant.components.tradfri.*
homeassistant.components.trafikverket_camera.*
homeassistant.components.trafikverket_ferry.*
homeassistant.components.trafikverket_train.*
homeassistant.components.trafikverket_weatherstation.*

View File

@@ -19,8 +19,6 @@ build.json @home-assistant/supervisor
# Other code
/homeassistant/scripts/check_config.py @kellerza
/homeassistant/const.py @epenet
/homeassistant/util/ @epenet
# Integrations
/homeassistant/components/abode/ @shred86
@@ -51,8 +49,6 @@ build.json @home-assistant/supervisor
/tests/components/airthings/ @danielhiversen
/homeassistant/components/airthings_ble/ @vincegio
/tests/components/airthings_ble/ @vincegio
/homeassistant/components/airtouch4/ @LonePurpleWolf
/tests/components/airtouch4/ @LonePurpleWolf
/homeassistant/components/airvisual/ @bachya
/tests/components/airvisual/ @bachya
/homeassistant/components/airvisual_pro/ @bachya
@@ -211,6 +207,8 @@ build.json @home-assistant/supervisor
/tests/components/coinbase/ @tombrien
/homeassistant/components/color_extractor/ @GenericStudent
/tests/components/color_extractor/ @GenericStudent
/homeassistant/components/comelit/ @chemelli74
/tests/components/comelit/ @chemelli74
/homeassistant/components/comfoconnect/ @michaelarnauts
/tests/components/comfoconnect/ @michaelarnauts
/homeassistant/components/command_line/ @gjohansson-ST
@@ -295,12 +293,10 @@ build.json @home-assistant/supervisor
/tests/components/dsmr/ @Robbie1221 @frenck
/homeassistant/components/dsmr_reader/ @depl0y @glodenox
/tests/components/dsmr_reader/ @depl0y @glodenox
/homeassistant/components/dunehd/ @bieniu
/tests/components/dunehd/ @bieniu
/homeassistant/components/duotecno/ @cereal2nd
/tests/components/duotecno/ @cereal2nd
/homeassistant/components/dwd_weather_warnings/ @runningman84 @stephan192 @Hummel95 @andarotajo
/tests/components/dwd_weather_warnings/ @runningman84 @stephan192 @Hummel95 @andarotajo
/homeassistant/components/dwd_weather_warnings/ @runningman84 @stephan192 @andarotajo
/tests/components/dwd_weather_warnings/ @runningman84 @stephan192 @andarotajo
/homeassistant/components/dynalite/ @ziv1234
/tests/components/dynalite/ @ziv1234
/homeassistant/components/eafm/ @Jc2k
@@ -345,8 +341,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/enigma2/ @fbradyirl
/homeassistant/components/enocean/ @bdurrer
/tests/components/enocean/ @bdurrer
/homeassistant/components/enphase_envoy/ @gtdiehl
/tests/components/enphase_envoy/ @gtdiehl
/homeassistant/components/enphase_envoy/ @bdraco @cgarwood @dgomes @joostlek
/tests/components/enphase_envoy/ @bdraco @cgarwood @dgomes @joostlek
/homeassistant/components/entur_public_transport/ @hfurubotten
/homeassistant/components/environment_canada/ @gwww @michaeldavie
/tests/components/environment_canada/ @gwww @michaeldavie
@@ -525,6 +521,8 @@ build.json @home-assistant/supervisor
/tests/components/homeassistant/ @home-assistant/core
/homeassistant/components/homeassistant_alerts/ @home-assistant/core
/tests/components/homeassistant_alerts/ @home-assistant/core
/homeassistant/components/homeassistant_green/ @home-assistant/core
/tests/components/homeassistant_green/ @home-assistant/core
/homeassistant/components/homeassistant_hardware/ @home-assistant/core
/tests/components/homeassistant_hardware/ @home-assistant/core
/homeassistant/components/homeassistant_sky_connect/ @home-assistant/core
@@ -608,8 +606,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/iotawatt/ @gtdiehl @jyavenard
/tests/components/iotawatt/ @gtdiehl @jyavenard
/homeassistant/components/iperf3/ @rohankapoorcom
/homeassistant/components/ipma/ @dgomes @abmantis
/tests/components/ipma/ @dgomes @abmantis
/homeassistant/components/ipma/ @dgomes
/tests/components/ipma/ @dgomes
/homeassistant/components/ipp/ @ctalkington
/tests/components/ipp/ @ctalkington
/homeassistant/components/iqvia/ @bachya
@@ -673,6 +671,8 @@ build.json @home-assistant/supervisor
/tests/components/launch_library/ @ludeeus @DurgNomis-drol
/homeassistant/components/laundrify/ @xLarry
/tests/components/laundrify/ @xLarry
/homeassistant/components/lawn_mower/ @home-assistant/core
/tests/components/lawn_mower/ @home-assistant/core
/homeassistant/components/lcn/ @alengwenus
/tests/components/lcn/ @alengwenus
/homeassistant/components/ld2410_ble/ @930913
@@ -729,6 +729,7 @@ build.json @home-assistant/supervisor
/tests/components/mazda/ @bdr99
/homeassistant/components/meater/ @Sotolotl @emontnemery
/tests/components/meater/ @Sotolotl @emontnemery
/homeassistant/components/media_extractor/ @joostlek
/homeassistant/components/media_player/ @home-assistant/core
/tests/components/media_player/ @home-assistant/core
/homeassistant/components/media_source/ @hunterjm
@@ -1028,8 +1029,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/repairs/ @home-assistant/core
/tests/components/repairs/ @home-assistant/core
/homeassistant/components/repetier/ @MTrab @ShadowBr0ther
/homeassistant/components/rest/ @epenet
/tests/components/rest/ @epenet
/homeassistant/components/rflink/ @javicalle
/tests/components/rflink/ @javicalle
/homeassistant/components/rfxtrx/ @danielhiversen @elupus @RobBie1221
@@ -1058,8 +1057,8 @@ build.json @home-assistant/supervisor
/tests/components/rss_feed_template/ @home-assistant/core
/homeassistant/components/rtsp_to_webrtc/ @allenporter
/tests/components/rtsp_to_webrtc/ @allenporter
/homeassistant/components/ruckus_unleashed/ @gabe565
/tests/components/ruckus_unleashed/ @gabe565
/homeassistant/components/ruckus_unleashed/ @gabe565 @lanrat
/tests/components/ruckus_unleashed/ @gabe565 @lanrat
/homeassistant/components/ruuvi_gateway/ @akx
/tests/components/ruuvi_gateway/ @akx
/homeassistant/components/ruuvitag_ble/ @akx
@@ -1077,9 +1076,11 @@ build.json @home-assistant/supervisor
/tests/components/scene/ @home-assistant/core
/homeassistant/components/schedule/ @home-assistant/core
/tests/components/schedule/ @home-assistant/core
/homeassistant/components/schlage/ @dknowles2
/tests/components/schlage/ @dknowles2
/homeassistant/components/schluter/ @prairieapps
/homeassistant/components/scrape/ @fabaff @gjohansson-ST @epenet
/tests/components/scrape/ @fabaff @gjohansson-ST @epenet
/homeassistant/components/scrape/ @fabaff @gjohansson-ST
/tests/components/scrape/ @fabaff @gjohansson-ST
/homeassistant/components/screenlogic/ @dieselrabbit @bdraco
/tests/components/screenlogic/ @dieselrabbit @bdraco
/homeassistant/components/script/ @home-assistant/core
@@ -1226,8 +1227,8 @@ build.json @home-assistant/supervisor
/tests/components/switch_as_x/ @home-assistant/core
/homeassistant/components/switchbee/ @jafar-atili
/tests/components/switchbee/ @jafar-atili
/homeassistant/components/switchbot/ @bdraco @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski
/tests/components/switchbot/ @bdraco @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski
/homeassistant/components/switchbot/ @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski
/tests/components/switchbot/ @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski
/homeassistant/components/switcher_kis/ @thecode
/tests/components/switcher_kis/ @thecode
/homeassistant/components/switchmate/ @danielhiversen @qiz-li
@@ -1298,6 +1299,8 @@ build.json @home-assistant/supervisor
/tests/components/trace/ @home-assistant/core
/homeassistant/components/tractive/ @Danielhiversen @zhulik @bieniu
/tests/components/tractive/ @Danielhiversen @zhulik @bieniu
/homeassistant/components/trafikverket_camera/ @gjohansson-ST
/tests/components/trafikverket_camera/ @gjohansson-ST
/homeassistant/components/trafikverket_ferry/ @gjohansson-ST
/tests/components/trafikverket_ferry/ @gjohansson-ST
/homeassistant/components/trafikverket_train/ @endor-force @gjohansson-ST
@@ -1365,6 +1368,8 @@ build.json @home-assistant/supervisor
/tests/components/vizio/ @raman325
/homeassistant/components/vlc_telnet/ @rodripf @MartinHjelmare
/tests/components/vlc_telnet/ @rodripf @MartinHjelmare
/homeassistant/components/vodafone_station/ @paoloantinori @chemelli74
/tests/components/vodafone_station/ @paoloantinori @chemelli74
/homeassistant/components/voip/ @balloob @synesthesiam
/tests/components/voip/ @balloob @synesthesiam
/homeassistant/components/volumio/ @OnFreund
@@ -1375,6 +1380,8 @@ build.json @home-assistant/supervisor
/tests/components/vulcan/ @Antoni-Czaplicki
/homeassistant/components/wake_on_lan/ @ntilley905
/tests/components/wake_on_lan/ @ntilley905
/homeassistant/components/wake_word/ @home-assistant/core @synesthesiam
/tests/components/wake_word/ @home-assistant/core @synesthesiam
/homeassistant/components/wallbox/ @hesselonline
/tests/components/wallbox/ @hesselonline
/homeassistant/components/waqi/ @andrey-git
@@ -1438,6 +1445,7 @@ build.json @home-assistant/supervisor
/tests/components/yamaha_musiccast/ @vigonotion @micha91
/homeassistant/components/yandex_transport/ @rishatik92 @devbis
/tests/components/yandex_transport/ @rishatik92 @devbis
/homeassistant/components/yardian/ @h3l1o5
/homeassistant/components/yeelight/ @zewelor @shenxn @starkillerOG @alexyao2015
/tests/components/yeelight/ @zewelor @shenxn @starkillerOG @alexyao2015
/homeassistant/components/yeelightsunflower/ @lindsaymarkward

View File

@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2023.07.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2023.07.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2023.07.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2023.07.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2023.07.0
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2023.08.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2023.08.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2023.08.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2023.08.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2023.08.0
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io

View File

@@ -148,16 +148,6 @@ def get_arguments() -> argparse.Namespace:
return arguments
def cmdline() -> list[str]:
"""Collect path and arguments to re-execute the current hass instance."""
if os.path.basename(sys.argv[0]) == "__main__.py":
modulepath = os.path.dirname(sys.argv[0])
os.environ["PYTHONPATH"] = os.path.dirname(modulepath)
return [sys.executable, "-m", "homeassistant"] + list(sys.argv[1:])
return sys.argv
def check_threads() -> None:
"""Check if there are any lingering threads."""
try:

View File

@@ -8,7 +8,7 @@ from typing import Any, Generic, Self, TypeVar, overload
_T = TypeVar("_T")
class cached_property(Generic[_T]): # pylint: disable=invalid-name
class cached_property(Generic[_T]):
"""Backport of Python 3.12's cached_property.
Includes https://github.com/python/cpython/pull/101890/files

View File

@@ -110,8 +110,7 @@ async def async_setup_hass(
runtime_config: RuntimeConfig,
) -> core.HomeAssistant | None:
"""Set up Home Assistant."""
hass = core.HomeAssistant()
hass.config.config_dir = runtime_config.config_dir
hass = core.HomeAssistant(runtime_config.config_dir)
async_enable_logging(
hass,
@@ -134,6 +133,7 @@ async def async_setup_hass(
_LOGGER.info("Config directory: %s", runtime_config.config_dir)
loader.async_setup(hass)
config_dict = None
basic_setup_success = False
@@ -177,14 +177,15 @@ async def async_setup_hass(
old_config = hass.config
old_logging = hass.data.get(DATA_LOGGING)
hass = core.HomeAssistant()
hass = core.HomeAssistant(old_config.config_dir)
if old_logging:
hass.data[DATA_LOGGING] = old_logging
hass.config.skip_pip = old_config.skip_pip
hass.config.skip_pip_packages = old_config.skip_pip_packages
hass.config.internal_url = old_config.internal_url
hass.config.external_url = old_config.external_url
hass.config.config_dir = old_config.config_dir
# Setup loader cache after the config dir has been set
loader.async_setup(hass)
if safe_mode:
_LOGGER.info("Starting in safe mode")

View File

@@ -2,6 +2,7 @@
"domain": "trafikverket",
"name": "Trafikverket",
"integrations": [
"trafikverket_camera",
"trafikverket_ferry",
"trafikverket_train",
"trafikverket_weatherstation"

View File

@@ -28,6 +28,7 @@ from homeassistant.const import (
from homeassistant.core import Event, HomeAssistant, ServiceCall
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, entity
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import dispatcher_send
from .const import ATTRIBUTION, CONF_POLLING, DOMAIN, LOGGER
@@ -287,14 +288,14 @@ class AbodeDevice(AbodeEntity):
"""Initialize Abode device."""
super().__init__(data)
self._device = device
self._attr_unique_id = device.device_uuid
self._attr_unique_id = device.uuid
async def async_added_to_hass(self) -> None:
"""Subscribe to device events."""
await super().async_added_to_hass()
await self.hass.async_add_executor_job(
self._data.abode.events.add_device_callback,
self._device.device_id,
self._device.id,
self._update_callback,
)
@@ -302,7 +303,7 @@ class AbodeDevice(AbodeEntity):
"""Unsubscribe from device events."""
await super().async_will_remove_from_hass()
await self.hass.async_add_executor_job(
self._data.abode.events.remove_all_device_callbacks, self._device.device_id
self._data.abode.events.remove_all_device_callbacks, self._device.id
)
def update(self) -> None:
@@ -313,17 +314,17 @@ class AbodeDevice(AbodeEntity):
def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes."""
return {
"device_id": self._device.device_id,
"device_id": self._device.id,
"battery_low": self._device.battery_low,
"no_response": self._device.no_response,
"device_type": self._device.type,
}
@property
def device_info(self) -> entity.DeviceInfo:
def device_info(self) -> DeviceInfo:
"""Return device registry information for this entity."""
return entity.DeviceInfo(
identifiers={(DOMAIN, self._device.device_id)},
return DeviceInfo(
identifiers={(DOMAIN, self._device.id)},
manufacturer="Abode",
model=self._device.type,
name=self._device.name,

View File

@@ -69,7 +69,7 @@ class AbodeAlarm(AbodeDevice, alarm.AlarmControlPanelEntity):
def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes."""
return {
"device_id": self._device.device_id,
"device_id": self._device.id,
"battery_backup": self._device.battery,
"cellular_backup": self._device.is_cellular,
}

View File

@@ -30,7 +30,7 @@ async def async_setup_entry(
data: AbodeSystem = hass.data[DOMAIN]
async_add_entities(
AbodeCamera(data, device, TIMELINE.CAPTURE_IMAGE) # pylint: disable=no-member
AbodeCamera(data, device, TIMELINE.CAPTURE_IMAGE)
for device in data.abode.get_devices(generic_type=CONST.TYPE_CAMERA)
)

View File

@@ -1,6 +1,8 @@
"""Support for Abode Security System sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import cast
from jaraco.abode.devices.sensor import Sensor as AbodeSense
@@ -12,25 +14,52 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import LIGHT_LUX
from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AbodeDevice, AbodeSystem
from .const import DOMAIN
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
ABODE_TEMPERATURE_UNIT_HA_UNIT = {
CONST.UNIT_FAHRENHEIT: UnitOfTemperature.FAHRENHEIT,
CONST.UNIT_CELSIUS: UnitOfTemperature.CELSIUS,
}
@dataclass
class AbodeSensorDescriptionMixin:
"""Mixin for Abode sensor."""
value_fn: Callable[[AbodeSense], float]
native_unit_of_measurement_fn: Callable[[AbodeSense], str]
@dataclass
class AbodeSensorDescription(SensorEntityDescription, AbodeSensorDescriptionMixin):
"""Class describing Abode sensor entities."""
SENSOR_TYPES: tuple[AbodeSensorDescription, ...] = (
AbodeSensorDescription(
key=CONST.TEMP_STATUS_KEY,
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement_fn=lambda device: ABODE_TEMPERATURE_UNIT_HA_UNIT[
device.temp_unit
],
value_fn=lambda device: cast(float, device.temp),
),
SensorEntityDescription(
AbodeSensorDescription(
key=CONST.HUMI_STATUS_KEY,
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement_fn=lambda _: PERCENTAGE,
value_fn=lambda device: cast(float, device.humidity),
),
SensorEntityDescription(
AbodeSensorDescription(
key=CONST.LUX_STATUS_KEY,
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement_fn=lambda _: LIGHT_LUX,
value_fn=lambda device: cast(float, device.lux),
),
)
@@ -52,32 +81,26 @@ async def async_setup_entry(
class AbodeSensor(AbodeDevice, SensorEntity):
"""A sensor implementation for Abode devices."""
entity_description: AbodeSensorDescription
_device: AbodeSense
def __init__(
self,
data: AbodeSystem,
device: AbodeSense,
description: SensorEntityDescription,
description: AbodeSensorDescription,
) -> None:
"""Initialize a sensor for an Abode device."""
super().__init__(data, device)
self.entity_description = description
self._attr_unique_id = f"{device.device_uuid}-{description.key}"
if description.key == CONST.TEMP_STATUS_KEY:
self._attr_native_unit_of_measurement = device.temp_unit
elif description.key == CONST.HUMI_STATUS_KEY:
self._attr_native_unit_of_measurement = device.humidity_unit
elif description.key == CONST.LUX_STATUS_KEY:
self._attr_native_unit_of_measurement = LIGHT_LUX
self._attr_unique_id = f"{device.uuid}-{description.key}"
@property
def native_value(self) -> float | None:
def native_value(self) -> float:
"""Return the state of the sensor."""
if self.entity_description.key == CONST.TEMP_STATUS_KEY:
return cast(float, self._device.temp)
if self.entity_description.key == CONST.HUMI_STATUS_KEY:
return cast(float, self._device.humidity)
if self.entity_description.key == CONST.LUX_STATUS_KEY:
return cast(float, self._device.lux)
return None
return self.entity_description.value_fn(self._device)
@property
def native_unit_of_measurement(self) -> str:
"""Return the native unit of measurement."""
return self.entity_description.native_unit_of_measurement_fn(self._device)

View File

@@ -1,6 +1,7 @@
"""The AccuWeather component."""
from __future__ import annotations
from asyncio import timeout
from datetime import timedelta
import logging
from typing import Any
@@ -8,7 +9,6 @@ from typing import Any
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientSession
from aiohttp.client_exceptions import ClientConnectorError
from async_timeout import timeout
from homeassistant.components.sensor import DOMAIN as SENSOR_PLATFORM
from homeassistant.config_entries import ConfigEntry
@@ -16,8 +16,7 @@ from homeassistant.const import CONF_API_KEY, CONF_NAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import ATTR_FORECAST, CONF_FORECAST, DOMAIN, MANUFACTURER

View File

@@ -2,12 +2,12 @@
from __future__ import annotations
import asyncio
from asyncio import timeout
from typing import Any
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientError
from aiohttp.client_exceptions import ClientConnectorError
from async_timeout import timeout
import voluptuous as vol
from homeassistant import config_entries

View File

@@ -50,3 +50,8 @@ CONDITION_CLASSES: Final[dict[str, list[int]]] = {
ATTR_CONDITION_SUNNY: [1, 2, 5],
ATTR_CONDITION_WINDY: [32],
}
CONDITION_MAP = {
cond_code: cond_ha
for cond_ha, cond_codes in CONDITION_CLASSES.items()
for cond_code in cond_codes
}

View File

@@ -59,193 +59,280 @@ class AccuWeatherSensorDescription(
"""Class describing AccuWeather sensor entities."""
attr_fn: Callable[[dict[str, Any]], dict[str, Any]] = lambda _: {}
day: int | None = None
FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
AccuWeatherSensorDescription(
key="AirQuality",
icon="mdi:air-filter",
name="Air quality",
value_fn=lambda data: cast(str, data[ATTR_CATEGORY]),
device_class=SensorDeviceClass.ENUM,
options=["good", "hazardous", "high", "low", "moderate", "unhealthy"],
translation_key="air_quality",
*(
AccuWeatherSensorDescription(
key="AirQuality",
icon="mdi:air-filter",
value_fn=lambda data: cast(str, data[ATTR_CATEGORY]),
device_class=SensorDeviceClass.ENUM,
options=["good", "hazardous", "high", "low", "moderate", "unhealthy"],
translation_key=f"air_quality_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="CloudCoverDay",
icon="mdi:weather-cloudy",
name="Cloud cover day",
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
*(
AccuWeatherSensorDescription(
key="CloudCoverDay",
icon="mdi:weather-cloudy",
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
translation_key=f"cloud_cover_day_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="CloudCoverNight",
icon="mdi:weather-cloudy",
name="Cloud cover night",
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
*(
AccuWeatherSensorDescription(
key="CloudCoverNight",
icon="mdi:weather-cloudy",
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
translation_key=f"cloud_cover_night_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="Grass",
icon="mdi:grass",
name="Grass pollen",
entity_registry_enabled_default=False,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key="grass_pollen",
*(
AccuWeatherSensorDescription(
key="Grass",
icon="mdi:grass",
entity_registry_enabled_default=False,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key=f"grass_pollen_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="HoursOfSun",
icon="mdi:weather-partly-cloudy",
name="Hours of sun",
native_unit_of_measurement=UnitOfTime.HOURS,
value_fn=lambda data: cast(float, data),
*(
AccuWeatherSensorDescription(
key="HoursOfSun",
icon="mdi:weather-partly-cloudy",
native_unit_of_measurement=UnitOfTime.HOURS,
value_fn=lambda data: cast(float, data),
translation_key=f"hours_of_sun_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="LongPhraseDay",
name="Condition day",
value_fn=lambda data: cast(str, data),
*(
AccuWeatherSensorDescription(
key="LongPhraseDay",
value_fn=lambda data: cast(str, data),
translation_key=f"condition_day_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="LongPhraseNight",
name="Condition night",
value_fn=lambda data: cast(str, data),
*(
AccuWeatherSensorDescription(
key="LongPhraseNight",
value_fn=lambda data: cast(str, data),
translation_key=f"condition_night_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="Mold",
icon="mdi:blur",
name="Mold pollen",
entity_registry_enabled_default=False,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key="mold_pollen",
*(
AccuWeatherSensorDescription(
key="Mold",
icon="mdi:blur",
entity_registry_enabled_default=False,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key=f"mold_pollen_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="Ragweed",
icon="mdi:sprout",
name="Ragweed pollen",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key="ragweed_pollen",
*(
AccuWeatherSensorDescription(
key="Ragweed",
icon="mdi:sprout",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key=f"ragweed_pollen_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="RealFeelTemperatureMax",
device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature max",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
*(
AccuWeatherSensorDescription(
key="RealFeelTemperatureMax",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
translation_key=f"realfeel_temperature_max_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="RealFeelTemperatureMin",
device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature min",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
*(
AccuWeatherSensorDescription(
key="RealFeelTemperatureMin",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
translation_key=f"realfeel_temperature_min_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="RealFeelTemperatureShadeMax",
device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature shade max",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
*(
AccuWeatherSensorDescription(
key="RealFeelTemperatureShadeMax",
device_class=SensorDeviceClass.TEMPERATURE,
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
translation_key=f"realfeel_temperature_shade_max_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="RealFeelTemperatureShadeMin",
device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature shade min",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
*(
AccuWeatherSensorDescription(
key="RealFeelTemperatureShadeMin",
device_class=SensorDeviceClass.TEMPERATURE,
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
translation_key=f"realfeel_temperature_shade_min_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="SolarIrradianceDay",
icon="mdi:weather-sunny",
name="Solar irradiance day",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
*(
AccuWeatherSensorDescription(
key="SolarIrradianceDay",
icon="mdi:weather-sunny",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
translation_key=f"solar_irradiance_day_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="SolarIrradianceNight",
icon="mdi:weather-sunny",
name="Solar irradiance night",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
*(
AccuWeatherSensorDescription(
key="SolarIrradianceNight",
icon="mdi:weather-sunny",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
value_fn=lambda data: cast(float, data[ATTR_VALUE]),
translation_key=f"solar_irradiance_night_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="ThunderstormProbabilityDay",
icon="mdi:weather-lightning",
name="Thunderstorm probability day",
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
*(
AccuWeatherSensorDescription(
key="ThunderstormProbabilityDay",
icon="mdi:weather-lightning",
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
translation_key=f"thunderstorm_probability_day_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="ThunderstormProbabilityNight",
icon="mdi:weather-lightning",
name="Thunderstorm probability night",
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
*(
AccuWeatherSensorDescription(
key="ThunderstormProbabilityNight",
icon="mdi:weather-lightning",
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
translation_key=f"thunderstorm_probability_night_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="Tree",
icon="mdi:tree-outline",
name="Tree pollen",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key="tree_pollen",
*(
AccuWeatherSensorDescription(
key="Tree",
icon="mdi:tree-outline",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key=f"tree_pollen_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="UVIndex",
icon="mdi:weather-sunny",
name="UV index",
native_unit_of_measurement=UV_INDEX,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key="uv_index",
*(
AccuWeatherSensorDescription(
key="UVIndex",
icon="mdi:weather-sunny",
native_unit_of_measurement=UV_INDEX,
value_fn=lambda data: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
translation_key=f"uv_index_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="WindGustDay",
device_class=SensorDeviceClass.WIND_SPEED,
name="Wind gust day",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
*(
AccuWeatherSensorDescription(
key="WindGustDay",
device_class=SensorDeviceClass.WIND_SPEED,
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
translation_key=f"wind_gust_speed_day_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="WindGustNight",
device_class=SensorDeviceClass.WIND_SPEED,
name="Wind gust night",
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
*(
AccuWeatherSensorDescription(
key="WindGustNight",
device_class=SensorDeviceClass.WIND_SPEED,
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
translation_key=f"wind_gust_speed_night_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="WindDay",
device_class=SensorDeviceClass.WIND_SPEED,
name="Wind day",
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
*(
AccuWeatherSensorDescription(
key="WindDay",
device_class=SensorDeviceClass.WIND_SPEED,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
translation_key=f"wind_speed_day_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
AccuWeatherSensorDescription(
key="WindNight",
device_class=SensorDeviceClass.WIND_SPEED,
name="Wind night",
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
*(
AccuWeatherSensorDescription(
key="WindNight",
device_class=SensorDeviceClass.WIND_SPEED,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
translation_key=f"wind_speed_night_{day}d",
day=day,
)
for day in range(MAX_FORECAST_DAYS + 1)
),
)
@@ -253,118 +340,117 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
AccuWeatherSensorDescription(
key="ApparentTemperature",
device_class=SensorDeviceClass.TEMPERATURE,
name="Apparent temperature",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
translation_key="apparent_temperature",
),
AccuWeatherSensorDescription(
key="Ceiling",
device_class=SensorDeviceClass.DISTANCE,
icon="mdi:weather-fog",
name="Cloud ceiling",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.METERS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
suggested_display_precision=0,
translation_key="cloud_ceiling",
),
AccuWeatherSensorDescription(
key="CloudCover",
icon="mdi:weather-cloudy",
name="Cloud cover",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: cast(int, data),
translation_key="cloud_cover",
),
AccuWeatherSensorDescription(
key="DewPoint",
device_class=SensorDeviceClass.TEMPERATURE,
name="Dew point",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
translation_key="dew_point",
),
AccuWeatherSensorDescription(
key="RealFeelTemperature",
device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
translation_key="realfeel_temperature",
),
AccuWeatherSensorDescription(
key="RealFeelTemperatureShade",
device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature shade",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
translation_key="realfeel_temperature_shade",
),
AccuWeatherSensorDescription(
key="Precipitation",
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
name="Precipitation",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
attr_fn=lambda data: {"type": data["PrecipitationType"]},
translation_key="precipitation",
),
AccuWeatherSensorDescription(
key="PressureTendency",
device_class=SensorDeviceClass.ENUM,
icon="mdi:gauge",
name="Pressure tendency",
options=["falling", "rising", "steady"],
translation_key="pressure_tendency",
value_fn=lambda data: cast(str, data["LocalizedText"]).lower(),
translation_key="pressure_tendency",
),
AccuWeatherSensorDescription(
key="UVIndex",
icon="mdi:weather-sunny",
name="UV index",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UV_INDEX,
value_fn=lambda data: cast(int, data),
attr_fn=lambda data: {ATTR_LEVEL: data["UVIndexText"]},
translation_key="uv_index",
),
AccuWeatherSensorDescription(
key="WetBulbTemperature",
device_class=SensorDeviceClass.TEMPERATURE,
name="Wet bulb temperature",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
translation_key="wet_bulb_temperature",
),
AccuWeatherSensorDescription(
key="WindChillTemperature",
device_class=SensorDeviceClass.TEMPERATURE,
name="Wind chill temperature",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
translation_key="wind_chill_temperature",
),
AccuWeatherSensorDescription(
key="Wind",
device_class=SensorDeviceClass.WIND_SPEED,
name="Wind",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][API_METRIC][ATTR_VALUE]),
translation_key="wind_speed",
),
AccuWeatherSensorDescription(
key="WindGust",
device_class=SensorDeviceClass.WIND_SPEED,
name="Wind gust",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: cast(float, data[ATTR_SPEED][API_METRIC][ATTR_VALUE]),
translation_key="wind_gust_speed",
),
)
@@ -381,14 +467,12 @@ async def async_setup_entry(
]
if coordinator.forecast:
# Some air quality/allergy sensors are only available for certain
# locations.
sensors.extend(
AccuWeatherSensor(coordinator, description, forecast_day=day)
for day in range(MAX_FORECAST_DAYS + 1)
for description in FORECAST_SENSOR_TYPES
if description.key in coordinator.data[ATTR_FORECAST][0]
)
for description in FORECAST_SENSOR_TYPES:
# Some air quality/allergy sensors are only available for certain
# locations.
if description.key not in coordinator.data[ATTR_FORECAST][description.day]:
continue
sensors.append(AccuWeatherSensor(coordinator, description))
async_add_entities(sensors)
@@ -406,25 +490,21 @@ class AccuWeatherSensor(
self,
coordinator: AccuWeatherDataUpdateCoordinator,
description: AccuWeatherSensorDescription,
forecast_day: int | None = None,
) -> None:
"""Initialize."""
super().__init__(coordinator)
self.forecast_day = description.day
self.entity_description = description
self._sensor_data = _get_sensor_data(
coordinator.data, description.key, forecast_day
coordinator.data, description.key, self.forecast_day
)
if forecast_day is not None:
self._attr_name = f"{description.name} {forecast_day}d"
self._attr_unique_id = (
f"{coordinator.location_key}-{description.key}-{forecast_day}".lower()
)
if self.forecast_day is not None:
self._attr_unique_id = f"{coordinator.location_key}-{description.key}-{self.forecast_day}".lower()
else:
self._attr_unique_id = (
f"{coordinator.location_key}-{description.key}".lower()
)
self._attr_device_info = coordinator.device_info
self.forecast_day = forecast_day
@property
def native_value(self) -> str | int | float | None:

View File

@@ -24,14 +24,8 @@
},
"entity": {
"sensor": {
"pressure_tendency": {
"state": {
"steady": "Steady",
"rising": "Rising",
"falling": "Falling"
}
},
"air_quality": {
"air_quality_0d": {
"name": "Air quality today",
"state": {
"good": "Good",
"hazardous": "Hazardous",
@@ -41,80 +35,761 @@
"unhealthy": "Unhealthy"
}
},
"grass_pollen": {
"air_quality_1d": {
"name": "Air quality day 1",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
},
"air_quality_2d": {
"name": "Air quality day 2",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
},
"air_quality_3d": {
"name": "Air quality day 3",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
},
"air_quality_4d": {
"name": "Air quality day 4",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
},
"apparent_temperature": {
"name": "Apparent temperature"
},
"cloud_ceiling": {
"name": "Cloud ceiling"
},
"cloud_cover": {
"name": "Cloud cover"
},
"cloud_cover_day_0d": {
"name": "Cloud cover today"
},
"cloud_cover_day_1d": {
"name": "Cloud cover day 1"
},
"cloud_cover_day_2d": {
"name": "Cloud cover day 2"
},
"cloud_cover_day_3d": {
"name": "Cloud cover day 3"
},
"cloud_cover_day_4d": {
"name": "Cloud cover day 4"
},
"cloud_cover_night_0d": {
"name": "Cloud cover tonight"
},
"cloud_cover_night_1d": {
"name": "Cloud cover night 1"
},
"cloud_cover_night_2d": {
"name": "Cloud cover night 2"
},
"cloud_cover_night_3d": {
"name": "Cloud cover night 3"
},
"cloud_cover_night_4d": {
"name": "Cloud cover night 4"
},
"condition_day_0d": {
"name": "Condition today"
},
"condition_day_1d": {
"name": "Condition day 1"
},
"condition_day_2d": {
"name": "Condition day 2"
},
"condition_day_3d": {
"name": "Condition day 3"
},
"condition_day_4d": {
"name": "Condition day 4"
},
"condition_night_0d": {
"name": "Condition tonight"
},
"condition_night_1d": {
"name": "Condition night 1"
},
"condition_night_2d": {
"name": "Condition night 2"
},
"condition_night_3d": {
"name": "Condition night 3"
},
"condition_night_4d": {
"name": "Condition night 4"
},
"dew_point": {
"name": "Dew point"
},
"grass_pollen_0d": {
"name": "Grass pollen today",
"state_attributes": {
"level": {
"name": "Level",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality::state::unhealthy%]"
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"mold_pollen": {
"grass_pollen_1d": {
"name": "Grass pollen day 1",
"state_attributes": {
"level": {
"name": "Level",
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality::state::unhealthy%]"
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"ragweed_pollen": {
"grass_pollen_2d": {
"name": "Grass pollen day 2",
"state_attributes": {
"level": {
"name": "Level",
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality::state::unhealthy%]"
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"tree_pollen": {
"grass_pollen_3d": {
"name": "Grass pollen day 3",
"state_attributes": {
"level": {
"name": "Level",
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality::state::unhealthy%]"
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"grass_pollen_4d": {
"name": "Grass pollen day 4",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"hours_of_sun_0d": {
"name": "Hours of sun today"
},
"hours_of_sun_1d": {
"name": "Hours of sun day 1"
},
"hours_of_sun_2d": {
"name": "Hours of sun day 2"
},
"hours_of_sun_3d": {
"name": "Hours of sun day 3"
},
"hours_of_sun_4d": {
"name": "Hours of sun day 4"
},
"mold_pollen_0d": {
"name": "Mold pollen today",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"mold_pollen_1d": {
"name": "Mold pollen day 1",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"mold_pollen_2d": {
"name": "Mold pollen day 2",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"mold_pollen_3d": {
"name": "Mold pollen day 3",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"mold_pollen_4d": {
"name": "Mold pollen day 4",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"precipitation": {
"name": "[%key:component::sensor::entity_component::precipitation::name%]"
},
"pressure_tendency": {
"name": "Pressure tendency",
"state": {
"steady": "Steady",
"rising": "Rising",
"falling": "Falling"
}
},
"ragweed_pollen_0d": {
"name": "Ragweed pollen today",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"ragweed_pollen_1d": {
"name": "Ragweed pollen day 1",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"ragweed_pollen_2d": {
"name": "Ragweed pollen day 2",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"ragweed_pollen_3d": {
"name": "Ragweed pollen day 3",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"ragweed_pollen_4d": {
"name": "Ragweed pollen day 4",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"realfeel_temperature": {
"name": "RealFeel temperature"
},
"realfeel_temperature_max_0d": {
"name": "RealFeel temperature max today"
},
"realfeel_temperature_max_1d": {
"name": "RealFeel temperature max day 1"
},
"realfeel_temperature_max_2d": {
"name": "RealFeel temperature max day 2"
},
"realfeel_temperature_max_3d": {
"name": "RealFeel temperature max day 3"
},
"realfeel_temperature_max_4d": {
"name": "RealFeel temperature max day 4"
},
"realfeel_temperature_min_0d": {
"name": "RealFeel temperature min today"
},
"realfeel_temperature_min_1d": {
"name": "RealFeel temperature min day 1"
},
"realfeel_temperature_min_2d": {
"name": "RealFeel temperature min day 2"
},
"realfeel_temperature_min_3d": {
"name": "RealFeel temperature min day 3"
},
"realfeel_temperature_min_4d": {
"name": "RealFeel temperature min day 4"
},
"realfeel_temperature_shade": {
"name": "RealFeel temperature shade"
},
"realfeel_temperature_shade_max_0d": {
"name": "RealFeel temperature shade max today"
},
"realfeel_temperature_shade_max_1d": {
"name": "RealFeel temperature shade max day 1"
},
"realfeel_temperature_shade_max_2d": {
"name": "RealFeel temperature shade max day 2"
},
"realfeel_temperature_shade_max_3d": {
"name": "RealFeel temperature shade max day 3"
},
"realfeel_temperature_shade_max_4d": {
"name": "RealFeel temperature shade max day 4"
},
"realfeel_temperature_shade_min_0d": {
"name": "RealFeel temperature shade min today"
},
"realfeel_temperature_shade_min_1d": {
"name": "RealFeel temperature shade min day 1"
},
"realfeel_temperature_shade_min_2d": {
"name": "RealFeel temperature shade min day 2"
},
"realfeel_temperature_shade_min_3d": {
"name": "RealFeel temperature shade min day 3"
},
"realfeel_temperature_shade_min_4d": {
"name": "RealFeel temperature shade min day 4"
},
"solar_irradiance_day_0d": {
"name": "Solar irradiance today"
},
"solar_irradiance_day_1d": {
"name": "Solar irradiance day 1"
},
"solar_irradiance_day_2d": {
"name": "Solar irradiance day 2"
},
"solar_irradiance_day_3d": {
"name": "Solar irradiance day 3"
},
"solar_irradiance_day_4d": {
"name": "Solar irradiance day 4"
},
"solar_irradiance_night_0d": {
"name": "Solar irradiance tonight"
},
"solar_irradiance_night_1d": {
"name": "Solar irradiance night 1"
},
"solar_irradiance_night_2d": {
"name": "Solar irradiance night 2"
},
"solar_irradiance_night_3d": {
"name": "Solar irradiance night 3"
},
"solar_irradiance_night_4d": {
"name": "Solar irradiance night 4"
},
"thunderstorm_probability_day_0d": {
"name": "Thunderstorm probability today"
},
"thunderstorm_probability_day_1d": {
"name": "Thunderstorm probability day 1"
},
"thunderstorm_probability_day_2d": {
"name": "Thunderstorm probability day 2"
},
"thunderstorm_probability_day_3d": {
"name": "Thunderstorm probability day 3"
},
"thunderstorm_probability_day_4d": {
"name": "Thunderstorm probability day 4"
},
"thunderstorm_probability_night_0d": {
"name": "Thunderstorm probability tonight"
},
"thunderstorm_probability_night_1d": {
"name": "Thunderstorm probability night 1"
},
"thunderstorm_probability_night_2d": {
"name": "Thunderstorm probability night 2"
},
"thunderstorm_probability_night_3d": {
"name": "Thunderstorm probability night 3"
},
"thunderstorm_probability_night_4d": {
"name": "Thunderstorm probability night 4"
},
"tree_pollen_0d": {
"name": "Tree pollen today",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"tree_pollen_1d": {
"name": "Tree pollen day 1",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"tree_pollen_2d": {
"name": "Tree pollen day 2",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"tree_pollen_3d": {
"name": "Tree pollen day 3",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"tree_pollen_4d": {
"name": "Tree pollen day 4",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"uv_index": {
"name": "UV index",
"state_attributes": {
"level": {
"name": "Level",
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality::state::unhealthy%]"
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"uv_index_0d": {
"name": "UV index today",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"uv_index_1d": {
"name": "UV index day 1",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"uv_index_2d": {
"name": "UV index day 2",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"uv_index_3d": {
"name": "UV index day 3",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"uv_index_4d": {
"name": "UV index day 4",
"state_attributes": {
"level": {
"name": "[%key:component::accuweather::entity::sensor::grass_pollen_0d::state_attributes::level::name%]",
"state": {
"good": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::good%]",
"hazardous": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::hazardous%]",
"high": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::high%]",
"low": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::low%]",
"moderate": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::moderate%]",
"unhealthy": "[%key:component::accuweather::entity::sensor::air_quality_0d::state::unhealthy%]"
}
}
}
},
"wet_bulb_temperature": {
"name": "Wet bulb temperature"
},
"wind_speed": {
"name": "[%key:component::weather::entity_component::_::state_attributes::wind_speed::name%]"
},
"wind_chill_temperature": {
"name": "Wind chill temperature"
},
"wind_gust_speed": {
"name": "[%key:component::weather::entity_component::_::state_attributes::wind_gust_speed::name%]"
},
"wind_gust_speed_day_0d": {
"name": "Wind gust speed today"
},
"wind_gust_speed_day_1d": {
"name": "Wind gust speed day 1"
},
"wind_gust_speed_day_2d": {
"name": "Wind gust speed day 2"
},
"wind_gust_speed_day_3d": {
"name": "Wind gust speed day 3"
},
"wind_gust_speed_day_4d": {
"name": "Wind gust speed day 4"
},
"wind_gust_speed_night_0d": {
"name": "Wind gust speed tonight"
},
"wind_gust_speed_night_1d": {
"name": "Wind gust speed night 1"
},
"wind_gust_speed_night_2d": {
"name": "Wind gust speed night 2"
},
"wind_gust_speed_night_3d": {
"name": "Wind gust speed night 3"
},
"wind_gust_speed_night_4d": {
"name": "Wind gust speed night 4"
},
"wind_speed_day_0d": {
"name": "Wind speed today"
},
"wind_speed_day_1d": {
"name": "Wind speed day 1"
},
"wind_speed_day_2d": {
"name": "Wind speed day 2"
},
"wind_speed_day_3d": {
"name": "Wind speed day 3"
},
"wind_speed_day_4d": {
"name": "Wind speed day 4"
},
"wind_speed_night_0d": {
"name": "Wind speed tonight"
},
"wind_speed_night_1d": {
"name": "Wind speed night 1"
},
"wind_speed_night_2d": {
"name": "Wind speed night 2"
},
"wind_speed_night_3d": {
"name": "Wind speed night 3"
},
"wind_speed_night_4d": {
"name": "Wind speed night 4"
}
}
},

View File

@@ -17,7 +17,8 @@ from homeassistant.components.weather import (
ATTR_FORECAST_UV_INDEX,
ATTR_FORECAST_WIND_BEARING,
Forecast,
WeatherEntity,
SingleCoordinatorWeatherEntity,
WeatherEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -27,9 +28,8 @@ from homeassistant.const import (
UnitOfSpeed,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.dt import utc_from_timestamp
from . import AccuWeatherDataUpdateCoordinator
@@ -40,7 +40,7 @@ from .const import (
ATTR_SPEED,
ATTR_VALUE,
ATTRIBUTION,
CONDITION_CLASSES,
CONDITION_MAP,
DOMAIN,
)
@@ -58,7 +58,7 @@ async def async_setup_entry(
class AccuWeatherEntity(
CoordinatorEntity[AccuWeatherDataUpdateCoordinator], WeatherEntity
SingleCoordinatorWeatherEntity[AccuWeatherDataUpdateCoordinator]
):
"""Define an AccuWeather entity."""
@@ -68,9 +68,6 @@ class AccuWeatherEntity(
def __init__(self, coordinator: AccuWeatherDataUpdateCoordinator) -> None:
"""Initialize."""
super().__init__(coordinator)
# Coordinator data is used also for sensors which don't have units automatically
# converted, hence the weather entity's native units follow the configured unit
# system
self._attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
self._attr_native_pressure_unit = UnitOfPressure.HPA
self._attr_native_temperature_unit = UnitOfTemperature.CELSIUS
@@ -79,18 +76,13 @@ class AccuWeatherEntity(
self._attr_unique_id = coordinator.location_key
self._attr_attribution = ATTRIBUTION
self._attr_device_info = coordinator.device_info
if self.coordinator.forecast:
self._attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
@property
def condition(self) -> str | None:
"""Return the current condition."""
try:
return [
k
for k, v in CONDITION_CLASSES.items()
if self.coordinator.data["WeatherIcon"] in v
][0]
except IndexError:
return None
return CONDITION_MAP.get(self.coordinator.data["WeatherIcon"])
@property
def cloud_coverage(self) -> float:
@@ -180,9 +172,12 @@ class AccuWeatherEntity(
],
ATTR_FORECAST_UV_INDEX: item["UVIndex"][ATTR_VALUE],
ATTR_FORECAST_WIND_BEARING: item["WindDay"][ATTR_DIRECTION]["Degrees"],
ATTR_FORECAST_CONDITION: [
k for k, v in CONDITION_CLASSES.items() if item["IconDay"] in v
][0],
ATTR_FORECAST_CONDITION: CONDITION_MAP.get(item["IconDay"]),
}
for item in self.coordinator.data[ATTR_FORECAST]
]
@callback
def _async_forecast_daily(self) -> list[Forecast] | None:
"""Return the daily forecast in native units."""
return self.forecast

View File

@@ -74,9 +74,9 @@ class AcmedaBase(entity.Entity):
return self.roller.id
@property
def device_info(self) -> entity.DeviceInfo:
def device_info(self) -> dr.DeviceInfo:
"""Return the device info."""
return entity.DeviceInfo(
return dr.DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
manufacturer="Rollease Acmeda",
name=self.roller.name,

View File

@@ -2,11 +2,11 @@
from __future__ import annotations
import asyncio
from asyncio import timeout
from contextlib import suppress
from typing import Any
import aiopulse
import async_timeout
import voluptuous as vol
from homeassistant import config_entries
@@ -43,7 +43,7 @@ class AcmedaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
hubs: list[aiopulse.Hub] = []
with suppress(asyncio.TimeoutError):
async with async_timeout.timeout(5):
async with timeout(5):
async for hub in aiopulse.Hub.discover():
if hub.id not in already_configured:
hubs.append(hub)

View File

@@ -23,7 +23,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ACCOUNT_ID, CONNECTION_TYPE, DOMAIN, LOCAL

View File

@@ -4,8 +4,8 @@ from __future__ import annotations
from adguardhome import AdGuardHome, AdGuardHomeError
from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity import Entity
from .const import DATA_ADGUARD_VERSION, DOMAIN, LOGGER

View File

@@ -1,12 +1,12 @@
"""Support for Automation Device Specification (ADS)."""
import asyncio
from asyncio import timeout
from collections import namedtuple
import ctypes
import logging
import struct
import threading
import async_timeout
import pyads
import voluptuous as vol
@@ -301,7 +301,7 @@ class AdsEntity(Entity):
self._ads_hub.add_device_notification, ads_var, plctype, update
)
try:
async with async_timeout.timeout(10):
async with timeout(10):
await self._event.wait()
except asyncio.TimeoutError:
_LOGGER.debug("Variable %s: Timeout during first update", ads_var)

View File

@@ -4,7 +4,7 @@ from typing import Any
from advantage_air import ApiError
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN

View File

@@ -4,7 +4,7 @@ from typing import Any
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ADVANTAGE_AIR_STATE_ON, DOMAIN as ADVANTAGE_AIR_DOMAIN

View File

@@ -3,7 +3,7 @@
from homeassistant.components.update import UpdateEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN as ADVANTAGE_AIR_DOMAIN

View File

@@ -1,11 +1,14 @@
"""The AEMET OpenData component."""
import logging
from aemet_opendata.interface import AEMET
from aemet_opendata.exceptions import TownNotFound
from aemet_opendata.interface import AEMET, ConnectionOptions
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
from .const import (
CONF_STATION_UPDATES,
@@ -27,11 +30,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
longitude = entry.data[CONF_LONGITUDE]
station_updates = entry.options.get(CONF_STATION_UPDATES, True)
aemet = AEMET(api_key)
weather_coordinator = WeatherUpdateCoordinator(
hass, aemet, latitude, longitude, station_updates
)
options = ConnectionOptions(api_key, station_updates)
aemet = AEMET(aiohttp_client.async_get_clientsession(hass), options)
try:
await aemet.select_coordinates(latitude, longitude)
except TownNotFound as err:
_LOGGER.error(err)
return False
weather_coordinator = WeatherUpdateCoordinator(hass, aemet)
await weather_coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})

View File

@@ -1,13 +1,14 @@
"""Config flow for AEMET OpenData."""
from __future__ import annotations
from aemet_opendata import AEMET
from aemet_opendata.exceptions import AuthError
from aemet_opendata.interface import AEMET, ConnectionOptions
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.schema_config_entry_flow import (
SchemaFlowFormStep,
SchemaOptionsFlowHandler,
@@ -39,8 +40,11 @@ class AemetConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(f"{latitude}-{longitude}")
self._abort_if_unique_id_configured()
api_online = await _is_aemet_api_online(self.hass, user_input[CONF_API_KEY])
if not api_online:
options = ConnectionOptions(user_input[CONF_API_KEY], False)
aemet = AEMET(aiohttp_client.async_get_clientsession(self.hass), options)
try:
await aemet.select_coordinates(latitude, longitude)
except AuthError:
errors["base"] = "invalid_api_key"
if not errors:
@@ -70,10 +74,3 @@ class AemetConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) -> SchemaOptionsFlowHandler:
"""Get the options flow for this handler."""
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
async def _is_aemet_api_online(hass, api_key):
aemet = AEMET(api_key)
return await hass.async_add_executor_job(
aemet.get_conventional_observation_stations, False
)

View File

@@ -33,6 +33,7 @@ ATTR_API_FORECAST_TEMP = "temperature"
ATTR_API_FORECAST_TEMP_LOW = "templow"
ATTR_API_FORECAST_TIME = "datetime"
ATTR_API_FORECAST_WIND_BEARING = "wind_bearing"
ATTR_API_FORECAST_WIND_MAX_SPEED = "wind_max_speed"
ATTR_API_FORECAST_WIND_SPEED = "wind_speed"
ATTR_API_HUMIDITY = "humidity"
ATTR_API_PRESSURE = "pressure"

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/aemet",
"iot_class": "cloud_polling",
"loggers": ["aemet_opendata"],
"requirements": ["AEMET-OpenData==0.2.2"]
"requirements": ["AEMET-OpenData==0.4.4"]
}

View File

@@ -1,14 +1,20 @@
"""Support for the AEMET OpenData service."""
from typing import cast
from homeassistant.components.weather import (
ATTR_FORECAST_CONDITION,
ATTR_FORECAST_NATIVE_PRECIPITATION,
ATTR_FORECAST_NATIVE_TEMP,
ATTR_FORECAST_NATIVE_TEMP_LOW,
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED,
ATTR_FORECAST_NATIVE_WIND_SPEED,
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
ATTR_FORECAST_TIME,
ATTR_FORECAST_WIND_BEARING,
WeatherEntity,
DOMAIN as WEATHER_DOMAIN,
Forecast,
SingleCoordinatorWeatherEntity,
WeatherEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -17,9 +23,9 @@ from homeassistant.const import (
UnitOfSpeed,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ATTR_API_CONDITION,
@@ -30,6 +36,7 @@ from .const import (
ATTR_API_FORECAST_TEMP_LOW,
ATTR_API_FORECAST_TIME,
ATTR_API_FORECAST_WIND_BEARING,
ATTR_API_FORECAST_WIND_MAX_SPEED,
ATTR_API_FORECAST_WIND_SPEED,
ATTR_API_HUMIDITY,
ATTR_API_PRESSURE,
@@ -64,6 +71,7 @@ FORECAST_MAP = {
ATTR_API_FORECAST_TEMP: ATTR_FORECAST_NATIVE_TEMP,
ATTR_API_FORECAST_TIME: ATTR_FORECAST_TIME,
ATTR_API_FORECAST_WIND_BEARING: ATTR_FORECAST_WIND_BEARING,
ATTR_API_FORECAST_WIND_MAX_SPEED: ATTR_FORECAST_NATIVE_WIND_GUST_SPEED,
ATTR_API_FORECAST_WIND_SPEED: ATTR_FORECAST_NATIVE_WIND_SPEED,
},
}
@@ -79,15 +87,33 @@ async def async_setup_entry(
weather_coordinator = domain_data[ENTRY_WEATHER_COORDINATOR]
entities = []
for mode in FORECAST_MODES:
name = f"{domain_data[ENTRY_NAME]} {mode}"
unique_id = f"{config_entry.unique_id} {mode}"
entities.append(AemetWeather(name, unique_id, weather_coordinator, mode))
entity_registry = er.async_get(hass)
# Add daily + hourly entity for legacy config entries, only add daily for new
# config entries. This can be removed in HA Core 2024.3
if entity_registry.async_get_entity_id(
WEATHER_DOMAIN,
DOMAIN,
f"{config_entry.unique_id} {FORECAST_MODE_HOURLY}",
):
for mode in FORECAST_MODES:
name = f"{domain_data[ENTRY_NAME]} {mode}"
unique_id = f"{config_entry.unique_id} {mode}"
entities.append(AemetWeather(name, unique_id, weather_coordinator, mode))
else:
entities.append(
AemetWeather(
domain_data[ENTRY_NAME],
config_entry.unique_id,
weather_coordinator,
FORECAST_MODE_DAILY,
)
)
async_add_entities(entities, False)
class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):
class AemetWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordinator]):
"""Implementation of an AEMET OpenData sensor."""
_attr_attribution = ATTRIBUTION
@@ -95,6 +121,9 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):
_attr_native_pressure_unit = UnitOfPressure.HPA
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
_attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
_attr_supported_features = (
WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY
)
def __init__(
self,
@@ -117,15 +146,32 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):
"""Return the current condition."""
return self.coordinator.data[ATTR_API_CONDITION]
@property
def forecast(self):
def _forecast(self, forecast_mode: str) -> list[Forecast]:
"""Return the forecast array."""
forecasts = self.coordinator.data[FORECAST_MODE_ATTR_API[self._forecast_mode]]
forecast_map = FORECAST_MAP[self._forecast_mode]
return [
{ha_key: forecast[api_key] for api_key, ha_key in forecast_map.items()}
for forecast in forecasts
]
forecasts = self.coordinator.data[FORECAST_MODE_ATTR_API[forecast_mode]]
forecast_map = FORECAST_MAP[forecast_mode]
return cast(
list[Forecast],
[
{ha_key: forecast[api_key] for api_key, ha_key in forecast_map.items()}
for forecast in forecasts
],
)
@property
def forecast(self) -> list[Forecast]:
"""Return the forecast array."""
return self._forecast(self._forecast_mode)
@callback
def _async_forecast_daily(self) -> list[Forecast]:
"""Return the daily forecast in native units."""
return self._forecast(FORECAST_MODE_DAILY)
@callback
def _async_forecast_hourly(self) -> list[Forecast]:
"""Return the hourly forecast in native units."""
return self._forecast(FORECAST_MODE_HOURLY)
@property
def humidity(self):

View File

@@ -1,22 +1,21 @@
"""Weather data coordinator for the AEMET OpenData service."""
from __future__ import annotations
from dataclasses import dataclass, field
from asyncio import timeout
from datetime import timedelta
import logging
from typing import Any, Final
from aemet_opendata.const import (
AEMET_ATTR_DATE,
AEMET_ATTR_DAY,
AEMET_ATTR_DIRECTION,
AEMET_ATTR_ELABORATED,
AEMET_ATTR_FEEL_TEMPERATURE,
AEMET_ATTR_FORECAST,
AEMET_ATTR_HUMIDITY,
AEMET_ATTR_ID,
AEMET_ATTR_IDEMA,
AEMET_ATTR_MAX,
AEMET_ATTR_MIN,
AEMET_ATTR_NAME,
AEMET_ATTR_PRECIPITATION,
AEMET_ATTR_PRECIPITATION_PROBABILITY,
AEMET_ATTR_SKY_STATE,
@@ -25,24 +24,24 @@ from aemet_opendata.const import (
AEMET_ATTR_SPEED,
AEMET_ATTR_STATION_DATE,
AEMET_ATTR_STATION_HUMIDITY,
AEMET_ATTR_STATION_LOCATION,
AEMET_ATTR_STATION_PRESSURE,
AEMET_ATTR_STATION_PRESSURE_SEA,
AEMET_ATTR_STATION_TEMPERATURE,
AEMET_ATTR_STORM_PROBABILITY,
AEMET_ATTR_TEMPERATURE,
AEMET_ATTR_TEMPERATURE_FEELING,
AEMET_ATTR_WIND,
AEMET_ATTR_WIND_GUST,
ATTR_DATA,
)
from aemet_opendata.exceptions import AemetError
from aemet_opendata.helpers import (
get_forecast_day_value,
get_forecast_hour_value,
get_forecast_interval_value,
)
import async_timeout
from aemet_opendata.interface import AEMET
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
@@ -57,6 +56,7 @@ from .const import (
ATTR_API_FORECAST_TEMP_LOW,
ATTR_API_FORECAST_TIME,
ATTR_API_FORECAST_WIND_BEARING,
ATTR_API_FORECAST_WIND_MAX_SPEED,
ATTR_API_FORECAST_WIND_SPEED,
ATTR_API_HUMIDITY,
ATTR_API_PRESSURE,
@@ -83,6 +83,7 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
API_TIMEOUT: Final[int] = 120
STATION_MAX_DELTA = timedelta(hours=2)
WEATHER_UPDATE_INTERVAL = timedelta(minutes=10)
@@ -112,128 +113,33 @@ def format_int(value) -> int | None:
return None
class TownNotFound(UpdateFailed):
"""Raised when town is not found."""
class WeatherUpdateCoordinator(DataUpdateCoordinator):
"""Weather data update coordinator."""
def __init__(self, hass, aemet, latitude, longitude, station_updates):
def __init__(
self,
hass: HomeAssistant,
aemet: AEMET,
) -> None:
"""Initialize coordinator."""
self.aemet = aemet
super().__init__(
hass, _LOGGER, name=DOMAIN, update_interval=WEATHER_UPDATE_INTERVAL
hass,
_LOGGER,
name=DOMAIN,
update_interval=WEATHER_UPDATE_INTERVAL,
)
self._aemet = aemet
self._station = None
self._town = None
self._latitude = latitude
self._longitude = longitude
self._station_updates = station_updates
self._data = {
"daily": None,
"hourly": None,
"station": None,
}
async def _async_update_data(self):
data = {}
async with async_timeout.timeout(120):
weather_response = await self._get_aemet_weather()
data = self._convert_weather_response(weather_response)
return data
async def _get_aemet_weather(self):
"""Poll weather data from AEMET OpenData."""
weather = await self.hass.async_add_executor_job(self._get_weather_and_forecast)
return weather
def _get_weather_station(self):
if not self._station:
self._station = (
self._aemet.get_conventional_observation_station_by_coordinates(
self._latitude, self._longitude
)
)
if self._station:
_LOGGER.debug(
"station found for coordinates [%s, %s]: %s",
self._latitude,
self._longitude,
self._station,
)
if not self._station:
_LOGGER.debug(
"station not found for coordinates [%s, %s]",
self._latitude,
self._longitude,
)
return self._station
def _get_weather_town(self):
if not self._town:
self._town = self._aemet.get_town_by_coordinates(
self._latitude, self._longitude
)
if self._town:
_LOGGER.debug(
"Town found for coordinates [%s, %s]: %s",
self._latitude,
self._longitude,
self._town,
)
if not self._town:
_LOGGER.error(
"Town not found for coordinates [%s, %s]",
self._latitude,
self._longitude,
)
raise TownNotFound
return self._town
def _get_weather_and_forecast(self):
"""Get weather and forecast data from AEMET OpenData."""
self._get_weather_town()
daily = self._aemet.get_specific_forecast_town_daily(self._town[AEMET_ATTR_ID])
if not daily:
_LOGGER.error(
'Error fetching daily data for town "%s"', self._town[AEMET_ATTR_ID]
)
hourly = self._aemet.get_specific_forecast_town_hourly(
self._town[AEMET_ATTR_ID]
)
if not hourly:
_LOGGER.error(
'Error fetching hourly data for town "%s"', self._town[AEMET_ATTR_ID]
)
station = None
if self._station_updates and self._get_weather_station():
station = self._aemet.get_conventional_observation_station_data(
self._station[AEMET_ATTR_IDEMA]
)
if not station:
_LOGGER.error(
'Error fetching data for station "%s"',
self._station[AEMET_ATTR_IDEMA],
)
if daily:
self._data["daily"] = daily
if hourly:
self._data["hourly"] = hourly
if station:
self._data["station"] = station
return AemetWeather(
self._data["daily"],
self._data["hourly"],
self._data["station"],
)
async def _async_update_data(self) -> dict[str, Any]:
"""Update coordinator data."""
async with timeout(API_TIMEOUT):
try:
await self.aemet.update()
except AemetError as error:
raise UpdateFailed(error) from error
weather_response = self.aemet.legacy_weather()
return self._convert_weather_response(weather_response)
def _convert_weather_response(self, weather_response):
"""Format the weather response correctly."""
@@ -428,6 +334,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
),
ATTR_API_FORECAST_TEMP: self._get_temperature(day, hour),
ATTR_API_FORECAST_TIME: dt_util.as_utc(forecast_dt).isoformat(),
ATTR_API_FORECAST_WIND_MAX_SPEED: self._get_wind_max_speed(day, hour),
ATTR_API_FORECAST_WIND_SPEED: self._get_wind_speed(day, hour),
ATTR_API_FORECAST_WIND_BEARING: self._get_wind_bearing(day, hour),
}
@@ -518,14 +425,14 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
def _get_station_id(self):
"""Get station ID from weather data."""
if self._station:
return self._station[AEMET_ATTR_IDEMA]
if self.aemet.station:
return self.aemet.station.get_id()
return None
def _get_station_name(self):
"""Get station name from weather data."""
if self._station:
return self._station[AEMET_ATTR_STATION_LOCATION]
if self.aemet.station:
return self.aemet.station.get_name()
return None
@staticmethod
@@ -561,19 +468,19 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
@staticmethod
def _get_temperature_feeling(day_data, hour):
"""Get temperature from weather data."""
val = get_forecast_hour_value(day_data[AEMET_ATTR_TEMPERATURE_FEELING], hour)
val = get_forecast_hour_value(day_data[AEMET_ATTR_FEEL_TEMPERATURE], hour)
return format_int(val)
def _get_town_id(self):
"""Get town ID from weather data."""
if self._town:
return self._town[AEMET_ATTR_ID]
if self.aemet.town:
return self.aemet.town.get_id()
return None
def _get_town_name(self):
"""Get town name from weather data."""
if self._town:
return self._town[AEMET_ATTR_NAME]
if self.aemet.town:
return self.aemet.town.get_name()
return None
@staticmethod
@@ -623,12 +530,3 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
if val:
return format_int(val)
return None
@dataclass
class AemetWeather:
"""Class to harmonize weather data model."""
daily: dict = field(default_factory=dict)
hourly: dict = field(default_factory=dict)
station: dict = field(default_factory=dict)

View File

@@ -13,7 +13,7 @@ from homeassistant.const import (
STATE_ALARM_DISARMED,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import CONNECTION, DOMAIN as AGENT_DOMAIN

View File

@@ -8,7 +8,7 @@ from homeassistant.components.camera import CameraEntityFeature
from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_current_platform,

View File

@@ -1,6 +1,7 @@
"""The Airly integration."""
from __future__ import annotations
from asyncio import timeout
from datetime import timedelta
import logging
from math import ceil
@@ -9,7 +10,6 @@ from aiohttp import ClientSession
from aiohttp.client_exceptions import ClientConnectorError
from airly import Airly
from airly.exceptions import AirlyError
import async_timeout
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM
from homeassistant.config_entries import ConfigEntry
@@ -167,7 +167,7 @@ class AirlyDataUpdateCoordinator(DataUpdateCoordinator):
measurements = self.airly.create_measurements_session_point(
self.latitude, self.longitude
)
async with async_timeout.timeout(20):
async with timeout(20):
try:
await measurements.update()
except (AirlyError, ClientConnectorError) as error:

View File

@@ -1,13 +1,13 @@
"""Adds config flow for Airly."""
from __future__ import annotations
from asyncio import timeout
from http import HTTPStatus
from typing import Any
from aiohttp import ClientSession
from airly import Airly
from airly.exceptions import AirlyError
import async_timeout
import voluptuous as vol
from homeassistant import config_entries
@@ -105,7 +105,7 @@ async def test_location(
measurements = airly.create_measurements_session_point(
latitude=latitude, longitude=longitude
)
async with async_timeout.timeout(10):
async with timeout(10):
await measurements.update()
current = measurements.current

View File

@@ -20,8 +20,7 @@ from homeassistant.const import (
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

View File

@@ -47,7 +47,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api_key = entry.data[CONF_API_KEY]
latitude = entry.data[CONF_LATITUDE]
longitude = entry.data[CONF_LONGITUDE]
distance = entry.data[CONF_RADIUS]
# Station Radius is a user-configurable option
distance = entry.options[CONF_RADIUS]
# Reports are published hourly but update twice per hour
update_interval = datetime.timedelta(minutes=30)
@@ -65,11 +67,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = coordinator
# Listen for option changes
entry.async_on_unload(entry.add_update_listener(update_listener))
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
_LOGGER.debug("Migrating from version %s", entry.version)
if entry.version == 1:
new_options = {CONF_RADIUS: entry.data[CONF_RADIUS]}
new_data = entry.data.copy()
del new_data[CONF_RADIUS]
entry.version = 2
hass.config_entries.async_update_entry(
entry, data=new_data, options=new_options
)
_LOGGER.info("Migration to version %s successful", entry.version)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@@ -80,6 +104,11 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)
class AirNowDataUpdateCoordinator(DataUpdateCoordinator):
"""Define an object to hold Airly data."""

View File

@@ -1,11 +1,12 @@
"""Config flow for AirNow integration."""
import logging
from typing import Any
from pyairnow import WebServiceAPI
from pyairnow.errors import AirNowError, EmptyResponseError, InvalidKeyError
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant import config_entries, core, data_entry_flow, exceptions
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
@@ -48,7 +49,7 @@ async def validate_input(hass: core.HomeAssistant, data):
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for AirNow."""
VERSION = 1
VERSION = 2
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
@@ -75,12 +76,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors["base"] = "unknown"
else:
# Create Entry
radius = user_input.pop(CONF_RADIUS)
return self.async_create_entry(
title=(
f"AirNow Sensor at {user_input[CONF_LATITUDE]},"
f" {user_input[CONF_LONGITUDE]}"
),
data=user_input,
options={CONF_RADIUS: radius},
)
return self.async_show_form(
@@ -94,12 +97,49 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(CONF_RADIUS, default=150): int,
vol.Optional(CONF_RADIUS, default=150): vol.All(
int, vol.Range(min=5)
),
}
),
errors=errors,
)
@staticmethod
@core.callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:
"""Return the options flow."""
return OptionsFlowHandler(config_entry)
class OptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry):
"""Handle an options flow for AirNow."""
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> data_entry_flow.FlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(data=user_input)
options_schema = vol.Schema(
{
vol.Optional(CONF_RADIUS): vol.All(
int,
vol.Range(min=5),
),
}
)
return self.async_show_form(
step_id="init",
data_schema=self.add_suggested_values_to_schema(
options_schema, self.config_entry.options
),
)
class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""

View File

@@ -17,8 +17,7 @@ from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -30,6 +29,9 @@ from .const import (
ATTR_API_AQI_LEVEL,
ATTR_API_O3,
ATTR_API_PM25,
ATTR_API_STATION,
ATTR_API_STATION_LATITUDE,
ATTR_API_STATION_LONGITUDE,
DEFAULT_NAME,
DOMAIN,
)
@@ -40,6 +42,7 @@ PARALLEL_UPDATES = 1
ATTR_DESCR = "description"
ATTR_LEVEL = "level"
ATTR_STATION = "reporting_station"
@dataclass
@@ -85,6 +88,16 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
value_fn=lambda data: data.get(ATTR_API_O3),
extra_state_attributes_fn=None,
),
AirNowEntityDescription(
key=ATTR_API_STATION,
translation_key="station",
icon="mdi:blur",
value_fn=lambda data: data.get(ATTR_API_STATION),
extra_state_attributes_fn=lambda data: {
"lat": data[ATTR_API_STATION_LATITUDE],
"long": data[ATTR_API_STATION_LONGITUDE],
},
),
)

View File

@@ -21,10 +21,26 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
},
"options": {
"step": {
"init": {
"data": {
"radius": "Station Radius (miles)"
}
}
}
},
"entity": {
"sensor": {
"o3": {
"name": "[%key:component::sensor::entity_component::ozone::name%]"
},
"station": {
"name": "PM2.5 reporting station",
"state_attributes": {
"lat": { "name": "[%key:common::config_flow::data::latitude%]" },
"long": { "name": "[%key:common::config_flow::data::longitude%]" }
}
}
}
}

View File

@@ -10,7 +10,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, MANUFACTURER, TARGET_ROUTE, UPDATE_INTERVAL

View File

@@ -21,7 +21,7 @@ from homeassistant.const import (
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import (

View File

@@ -34,8 +34,12 @@ class Discovery:
def get_name(device: AirthingsDevice) -> str:
"""Generate name with identifier for device."""
return f"{device.name} ({device.identifier})"
"""Generate name with model and identifier for device."""
name = device.friendly_name()
if identifier := device.identifier:
name += f" ({identifier})"
return name
class AirthingsDeviceUpdateError(Exception):
@@ -156,7 +160,7 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="no_devices_found")
titles = {
address: get_name(discovery.device)
address: discovery.device.name
for (address, discovery) in self._discovered_devices.items()
}
return self.async_show_form(

View File

@@ -24,5 +24,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"iot_class": "local_polling",
"requirements": ["airthings-ble==0.5.3"]
"requirements": ["airthings-ble==0.5.6-2"]
}

View File

@@ -22,8 +22,7 @@ from homeassistant.const import (
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import (
@@ -162,11 +161,11 @@ class AirthingsSensor(
super().__init__(coordinator)
self.entity_description = entity_description
name = f"{airthings_device.name} {airthings_device.identifier}"
name = airthings_device.name
if identifier := airthings_device.identifier:
name += f" ({identifier})"
self._attr_unique_id = f"{name}_{entity_description.key}"
self._id = airthings_device.address
self._attr_device_info = DeviceInfo(
connections={
(
@@ -175,9 +174,10 @@ class AirthingsSensor(
)
},
name=name,
manufacturer="Airthings",
manufacturer=airthings_device.manufacturer,
hw_version=airthings_device.hw_version,
sw_version=airthings_device.sw_version,
model=airthings_device.model,
)
@property

View File

@@ -18,7 +18,7 @@ from homeassistant.components.climate import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -98,28 +98,20 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
self._ac_number = ac_number
self._airtouch = coordinator.airtouch
self._info = info
self._unit = self._airtouch.GetAcs()[self._ac_number]
self._unit = self._airtouch.GetAcs()[ac_number]
self._attr_unique_id = f"ac_{ac_number}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"ac_{ac_number}")},
name=f"AC {ac_number}",
manufacturer="Airtouch",
model="Airtouch 4",
)
@callback
def _handle_coordinator_update(self):
self._unit = self._airtouch.GetAcs()[self._ac_number]
return super()._handle_coordinator_update()
@property
def device_info(self) -> DeviceInfo:
"""Return device info for this device."""
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
name=f"AC {self._ac_number}",
manufacturer="Airtouch",
model="Airtouch 4",
)
@property
def unique_id(self):
"""Return unique ID for this device."""
return f"ac_{self._ac_number}"
@property
def current_temperature(self):
"""Return the current temperature."""
@@ -208,29 +200,21 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
"""Initialize the climate device."""
super().__init__(coordinator)
self._group_number = group_number
self._attr_unique_id = group_number
self._airtouch = coordinator.airtouch
self._info = info
self._unit = self._airtouch.GetGroupByGroupNumber(self._group_number)
@callback
def _handle_coordinator_update(self):
self._unit = self._airtouch.GetGroupByGroupNumber(self._group_number)
return super()._handle_coordinator_update()
@property
def device_info(self) -> DeviceInfo:
"""Return device info for this device."""
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
self._unit = self._airtouch.GetGroupByGroupNumber(group_number)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, group_number)},
manufacturer="Airtouch",
model="Airtouch 4",
name=self._unit.GroupName,
)
@property
def unique_id(self):
"""Return unique ID for this device."""
return self._group_number
@callback
def _handle_coordinator_update(self):
self._unit = self._airtouch.GetGroupByGroupNumber(self._group_number)
return super()._handle_coordinator_update()
@property
def min_temp(self):

View File

@@ -1,7 +1,7 @@
{
"domain": "airtouch4",
"name": "AirTouch 4",
"codeowners": ["@LonePurpleWolf"],
"codeowners": [],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airtouch4",
"iot_class": "local_polling",

View File

@@ -109,19 +109,21 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"airvisual_checked_api_keys_lock", asyncio.Lock()
)
if integration_type == INTEGRATION_TYPE_GEOGRAPHY_COORDS:
coro = cloud_api.air_quality.nearest_city()
error_schema = self.geography_coords_schema
error_step = "geography_by_coords"
else:
coro = cloud_api.air_quality.city(
user_input[CONF_CITY], user_input[CONF_STATE], user_input[CONF_COUNTRY]
)
error_schema = GEOGRAPHY_NAME_SCHEMA
error_step = "geography_by_name"
async with valid_keys_lock:
if user_input[CONF_API_KEY] not in valid_keys:
if integration_type == INTEGRATION_TYPE_GEOGRAPHY_COORDS:
coro = cloud_api.air_quality.nearest_city()
error_schema = self.geography_coords_schema
error_step = "geography_by_coords"
else:
coro = cloud_api.air_quality.city(
user_input[CONF_CITY],
user_input[CONF_STATE],
user_input[CONF_COUNTRY],
)
error_schema = GEOGRAPHY_NAME_SCHEMA
error_step = "geography_by_name"
try:
await coro
except (InvalidKeyError, KeyExpiredError, UnauthorizedError):

View File

@@ -23,7 +23,8 @@ from homeassistant.const import (
)
from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,

View File

@@ -1,13 +1,13 @@
"""The Airzone integration."""
from __future__ import annotations
from asyncio import timeout
from datetime import timedelta
import logging
from typing import Any
from aioairzone.exceptions import AirzoneError
from aioairzone.localapi import AirzoneLocalApi
import async_timeout
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -35,7 +35,7 @@ class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""
async with async_timeout.timeout(AIOAIRZONE_DEVICE_TIMEOUT_SEC):
async with timeout(AIOAIRZONE_DEVICE_TIMEOUT_SEC):
try:
await self.airzone.update()
except AirzoneError as error:

View File

@@ -10,6 +10,7 @@ from aioairzone.const import (
AZD_AVAILABLE,
AZD_FIRMWARE,
AZD_FULL_NAME,
AZD_HOT_WATER,
AZD_ID,
AZD_MAC,
AZD_MODEL,
@@ -26,7 +27,7 @@ from aioairzone.exceptions import AirzoneError
from homeassistant.config_entries import ConfigEntry
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
@@ -81,6 +82,31 @@ class AirzoneSystemEntity(AirzoneEntity):
return value
class AirzoneHotWaterEntity(AirzoneEntity):
"""Define an Airzone Hot Water entity."""
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
entry: ConfigEntry,
) -> None:
"""Initialize."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{entry.entry_id}_dhw")},
manufacturer=MANUFACTURER,
model="DHW",
name=self.get_airzone_value(AZD_NAME),
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
)
self._attr_unique_id = entry.unique_id or entry.entry_id
def get_airzone_value(self, key: str) -> Any:
"""Return DHW value by key."""
return self.coordinator.data[AZD_HOT_WATER].get(key)
class AirzoneWebServerEntity(AirzoneEntity):
"""Define an Airzone WebServer entity."""

View File

@@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone",
"iot_class": "local_polling",
"loggers": ["aioairzone"],
"requirements": ["aioairzone==0.6.5"]
"requirements": ["aioairzone==0.6.7"]
}

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
from typing import Any, Final
from aioairzone.const import (
AZD_HOT_WATER,
AZD_HUMIDITY,
AZD_NAME,
AZD_TEMP,
@@ -31,7 +32,21 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, TEMP_UNIT_LIB_TO_HASS
from .coordinator import AirzoneUpdateCoordinator
from .entity import AirzoneEntity, AirzoneWebServerEntity, AirzoneZoneEntity
from .entity import (
AirzoneEntity,
AirzoneHotWaterEntity,
AirzoneWebServerEntity,
AirzoneZoneEntity,
)
HOT_WATER_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
SensorEntityDescription(
device_class=SensorDeviceClass.TEMPERATURE,
key=AZD_TEMP,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
),
)
WEBSERVER_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
SensorEntityDescription(
@@ -71,6 +86,18 @@ async def async_setup_entry(
sensors: list[AirzoneSensor] = []
if AZD_HOT_WATER in coordinator.data:
dhw_data = coordinator.data[AZD_HOT_WATER]
for description in HOT_WATER_SENSOR_TYPES:
if description.key in dhw_data:
sensors.append(
AirzoneHotWaterSensor(
coordinator,
description,
entry,
)
)
if AZD_WEBSERVER in coordinator.data:
ws_data = coordinator.data[AZD_WEBSERVER]
for description in WEBSERVER_SENSOR_TYPES:
@@ -114,6 +141,30 @@ class AirzoneSensor(AirzoneEntity, SensorEntity):
self._attr_native_value = self.get_airzone_value(self.entity_description.key)
class AirzoneHotWaterSensor(AirzoneHotWaterEntity, AirzoneSensor):
"""Define an Airzone Hot Water sensor."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
description: SensorEntityDescription,
entry: ConfigEntry,
) -> None:
"""Initialize."""
super().__init__(coordinator, entry)
self._attr_unique_id = f"{self._attr_unique_id}_dhw_{description.key}"
self.entity_description = description
self._attr_native_unit_of_measurement = TEMP_UNIT_LIB_TO_HASS.get(
self.get_airzone_value(AZD_TEMP_UNIT)
)
self._async_update_attrs()
class AirzoneWebServerSensor(AirzoneWebServerEntity, AirzoneSensor):
"""Define an Airzone WebServer sensor."""

View File

@@ -9,6 +9,7 @@ from aioairzone_cloud.const import (
AZD_AIDOOS,
AZD_ERRORS,
AZD_PROBLEMS,
AZD_SYSTEMS,
AZD_WARNINGS,
AZD_ZONES,
)
@@ -25,7 +26,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .coordinator import AirzoneUpdateCoordinator
from .entity import AirzoneAidooEntity, AirzoneEntity, AirzoneZoneEntity
from .entity import (
AirzoneAidooEntity,
AirzoneEntity,
AirzoneSystemEntity,
AirzoneZoneEntity,
)
@dataclass
@@ -51,6 +57,20 @@ AIDOO_BINARY_SENSOR_TYPES: Final[tuple[AirzoneBinarySensorEntityDescription, ...
),
)
SYSTEM_BINARY_SENSOR_TYPES: Final[tuple[AirzoneBinarySensorEntityDescription, ...]] = (
AirzoneBinarySensorEntityDescription(
attributes={
"errors": AZD_ERRORS,
"warnings": AZD_WARNINGS,
},
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
key=AZD_PROBLEMS,
),
)
ZONE_BINARY_SENSOR_TYPES: Final[tuple[AirzoneBinarySensorEntityDescription, ...]] = (
AirzoneBinarySensorEntityDescription(
device_class=BinarySensorDeviceClass.RUNNING,
@@ -87,6 +107,18 @@ async def async_setup_entry(
)
)
for system_id, system_data in coordinator.data.get(AZD_SYSTEMS, {}).items():
for description in SYSTEM_BINARY_SENSOR_TYPES:
if description.key in system_data:
binary_sensors.append(
AirzoneSystemBinarySensor(
coordinator,
description,
system_id,
system_data,
)
)
for zone_id, zone_data in coordinator.data.get(AZD_ZONES, {}).items():
for description in ZONE_BINARY_SENSOR_TYPES:
if description.key in zone_data:
@@ -145,6 +177,27 @@ class AirzoneAidooBinarySensor(AirzoneAidooEntity, AirzoneBinarySensor):
self._async_update_attrs()
class AirzoneSystemBinarySensor(AirzoneSystemEntity, AirzoneBinarySensor):
"""Define an Airzone Cloud System binary sensor."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
description: AirzoneBinarySensorEntityDescription,
system_id: str,
system_data: dict[str, Any],
) -> None:
"""Initialize."""
super().__init__(coordinator, system_id, system_data)
self._attr_unique_id = f"{system_id}_{description.key}"
self.entity_description = description
self._async_update_attrs()
class AirzoneZoneBinarySensor(AirzoneZoneEntity, AirzoneBinarySensor):
"""Define an Airzone Cloud Zone binary sensor."""

View File

@@ -1,13 +1,13 @@
"""The Airzone Cloud integration coordinator."""
from __future__ import annotations
from asyncio import timeout
from datetime import timedelta
import logging
from typing import Any
from aioairzone_cloud.cloudapi import AirzoneCloudApi
from aioairzone_cloud.exceptions import AirzoneCloudError
import async_timeout
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -35,7 +35,7 @@ class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""
async with async_timeout.timeout(AIOAIRZONE_CLOUD_TIMEOUT_SEC):
async with timeout(AIOAIRZONE_CLOUD_TIMEOUT_SEC):
try:
await self.airzone.update()
except AirzoneCloudError as error:

View File

@@ -10,13 +10,14 @@ from aioairzone_cloud.const import (
AZD_FIRMWARE,
AZD_NAME,
AZD_SYSTEM_ID,
AZD_SYSTEMS,
AZD_WEBSERVER,
AZD_WEBSERVERS,
AZD_ZONES,
)
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
@@ -65,6 +66,35 @@ class AirzoneAidooEntity(AirzoneEntity):
return value
class AirzoneSystemEntity(AirzoneEntity):
"""Define an Airzone Cloud System entity."""
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
system_id: str,
system_data: dict[str, Any],
) -> None:
"""Initialize."""
super().__init__(coordinator)
self.system_id = system_id
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, system_id)},
manufacturer=MANUFACTURER,
name=system_data[AZD_NAME],
via_device=(DOMAIN, system_data[AZD_WEBSERVER]),
)
def get_airzone_value(self, key: str) -> Any:
"""Return system value by key."""
value = None
if system := self.coordinator.data[AZD_SYSTEMS].get(self.system_id):
value = system.get(key)
return value
class AirzoneWebServerEntity(AirzoneEntity):
"""Define an Airzone Cloud WebServer entity."""

View File

@@ -11,7 +11,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPENING
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, STATES_MAP, SUPPORTED_FEATURES

View File

@@ -16,7 +16,7 @@ from homeassistant.components.sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, SIGNAL_STRENGTH_DECIBELS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN

View File

@@ -16,8 +16,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import dispatcher_send
from homeassistant.helpers.event import async_track_point_in_time
from homeassistant.util import dt as dt_util
from homeassistant.helpers.event import async_call_later
from .const import (
CONF_DEVICE_BAUD,
@@ -66,9 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.async_add_executor_job(controller.open, baud)
except NoDeviceError:
_LOGGER.debug("Failed to connect. Retrying in 5 seconds")
async_track_point_in_time(
hass, open_connection, dt_util.utcnow() + timedelta(seconds=5)
)
async_call_later(hass, timedelta(seconds=5), open_connection)
return
_LOGGER.debug("Established a connection with the alarmdecoder")
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = True

View File

@@ -16,7 +16,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entityfilter
from homeassistant.helpers.typing import ConfigType
from . import flash_briefings, intent, smart_home_http
from . import flash_briefings, intent, smart_home
from .const import (
CONF_AUDIO,
CONF_DISPLAY_CATEGORIES,
@@ -100,6 +100,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if CONF_SMART_HOME in config:
smart_home_config: dict[str, Any] | None = config[CONF_SMART_HOME]
smart_home_config = smart_home_config or SMART_HOME_SCHEMA({})
await smart_home_http.async_setup(hass, smart_home_config)
await smart_home.async_setup(hass, smart_home_config)
return True

View File

@@ -1,15 +1,16 @@
"""Support for Alexa skill auth."""
import asyncio
from datetime import timedelta
from asyncio import timeout
from datetime import datetime, timedelta
from http import HTTPStatus
import json
import logging
from typing import Any
import aiohttp
import async_timeout
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.storage import Store
from homeassistant.util import dt as dt_util
@@ -30,24 +31,24 @@ STORAGE_REFRESH_TOKEN = "refresh_token"
class Auth:
"""Handle authentication to send events to Alexa."""
def __init__(self, hass, client_id, client_secret):
def __init__(self, hass: HomeAssistant, client_id: str, client_secret: str) -> None:
"""Initialize the Auth class."""
self.hass = hass
self.client_id = client_id
self.client_secret = client_secret
self._prefs = None
self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
self._prefs: dict[str, Any] | None = None
self._store: Store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
self._get_token_lock = asyncio.Lock()
async def async_do_auth(self, accept_grant_code):
async def async_do_auth(self, accept_grant_code: str) -> str | None:
"""Do authentication with an AcceptGrant code."""
# access token not retrieved yet for the first time, so this should
# be an access token request
lwa_params = {
lwa_params: dict[str, str] = {
"grant_type": "authorization_code",
"code": accept_grant_code,
CONF_CLIENT_ID: self.client_id,
@@ -61,25 +62,28 @@ class Auth:
return await self._async_request_new_token(lwa_params)
@callback
def async_invalidate_access_token(self):
def async_invalidate_access_token(self) -> None:
"""Invalidate access token."""
assert self._prefs is not None
self._prefs[STORAGE_ACCESS_TOKEN] = None
async def async_get_access_token(self):
async def async_get_access_token(self) -> str | None:
"""Perform access token or token refresh request."""
async with self._get_token_lock:
if self._prefs is None:
await self.async_load_preferences()
assert self._prefs is not None
if self.is_token_valid():
_LOGGER.debug("Token still valid, using it")
return self._prefs[STORAGE_ACCESS_TOKEN]
token: str = self._prefs[STORAGE_ACCESS_TOKEN]
return token
if self._prefs[STORAGE_REFRESH_TOKEN] is None:
_LOGGER.debug("Token invalid and no refresh token available")
return None
lwa_params = {
lwa_params: dict[str, str] = {
"grant_type": "refresh_token",
"refresh_token": self._prefs[STORAGE_REFRESH_TOKEN],
CONF_CLIENT_ID: self.client_id,
@@ -90,22 +94,26 @@ class Auth:
return await self._async_request_new_token(lwa_params)
@callback
def is_token_valid(self):
def is_token_valid(self) -> bool:
"""Check if a token is already loaded and if it is still valid."""
assert self._prefs is not None
if not self._prefs[STORAGE_ACCESS_TOKEN]:
return False
expire_time = dt_util.parse_datetime(self._prefs[STORAGE_EXPIRE_TIME])
expire_time: datetime | None = dt_util.parse_datetime(
self._prefs[STORAGE_EXPIRE_TIME]
)
assert expire_time is not None
preemptive_expire_time = expire_time - timedelta(
seconds=PREEMPTIVE_REFRESH_TTL_IN_SECONDS
)
return dt_util.utcnow() < preemptive_expire_time
async def _async_request_new_token(self, lwa_params):
async def _async_request_new_token(self, lwa_params: dict[str, str]) -> str | None:
try:
session = aiohttp_client.async_get_clientsession(self.hass)
async with async_timeout.timeout(10):
async with timeout(10):
response = await session.post(
LWA_TOKEN_URI,
headers=LWA_HEADERS,
@@ -127,9 +135,9 @@ class Auth:
response_json = await response.json()
_LOGGER.debug("LWA response body : %s", response_json)
access_token = response_json["access_token"]
refresh_token = response_json["refresh_token"]
expires_in = response_json["expires_in"]
access_token: str = response_json["access_token"]
refresh_token: str = response_json["refresh_token"]
expires_in: int = response_json["expires_in"]
expire_time = dt_util.utcnow() + timedelta(seconds=expires_in)
await self._async_update_preferences(
@@ -138,7 +146,7 @@ class Auth:
return access_token
async def async_load_preferences(self):
async def async_load_preferences(self) -> None:
"""Load preferences with stored tokens."""
self._prefs = await self._store.async_load()
@@ -149,10 +157,13 @@ class Auth:
STORAGE_EXPIRE_TIME: None,
}
async def _async_update_preferences(self, access_token, refresh_token, expire_time):
async def _async_update_preferences(
self, access_token: str, refresh_token: str, expire_time: str
) -> None:
"""Update user preferences."""
if self._prefs is None:
await self.async_load_preferences()
assert self._prefs is not None
if access_token is not None:
self._prefs[STORAGE_ACCESS_TOKEN] = access_token

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,9 @@ from __future__ import annotations
from abc import ABC, abstractmethod
import asyncio
import logging
from typing import Any
from yarl import URL
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.storage import Store
@@ -33,38 +36,38 @@ class AbstractConfig(ABC):
await self._store.async_load()
@property
def supports_auth(self):
def supports_auth(self) -> bool:
"""Return if config supports auth."""
return False
@property
def should_report_state(self):
def should_report_state(self) -> bool:
"""Return if states should be proactively reported."""
return False
@property
def endpoint(self):
@abstractmethod
def endpoint(self) -> str | URL | None:
"""Endpoint for report state."""
return None
@property
@abstractmethod
def locale(self):
def locale(self) -> str | None:
"""Return config locale."""
@property
def entity_config(self):
def entity_config(self) -> dict[str, Any]:
"""Return entity config."""
return {}
@property
def is_reporting_states(self):
def is_reporting_states(self) -> bool:
"""Return if proactive mode is enabled."""
return self._unsub_proactive_report is not None
@callback
@abstractmethod
def user_identifier(self):
def user_identifier(self) -> str:
"""Return an identifier for the user that represents this config."""
async def async_enable_proactive_mode(self) -> None:
@@ -85,29 +88,29 @@ class AbstractConfig(ABC):
self._unsub_proactive_report = None
@callback
def should_expose(self, entity_id):
def should_expose(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
return False
@callback
def async_invalidate_access_token(self):
def async_invalidate_access_token(self) -> None:
"""Invalidate access token."""
raise NotImplementedError
async def async_get_access_token(self):
async def async_get_access_token(self) -> str | None:
"""Get an access token."""
raise NotImplementedError
async def async_accept_grant(self, code):
async def async_accept_grant(self, code: str) -> str | None:
"""Accept a grant."""
raise NotImplementedError
@property
def authorized(self):
def authorized(self) -> bool:
"""Return authorization status."""
return self._store.authorized
async def set_authorized(self, authorized) -> None:
async def set_authorized(self, authorized: bool) -> None:
"""Set authorization status.
- Set when an incoming message is received from Alexa.
@@ -132,25 +135,26 @@ class AlexaConfigStore:
_STORAGE_VERSION = 1
_STORAGE_KEY = DOMAIN
def __init__(self, hass):
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize a configuration store."""
self._data = None
self._data: dict[str, Any] | None = None
self._hass = hass
self._store = Store(hass, self._STORAGE_VERSION, self._STORAGE_KEY)
self._store: Store = Store(hass, self._STORAGE_VERSION, self._STORAGE_KEY)
@property
def authorized(self):
def authorized(self) -> bool:
"""Return authorization status."""
return self._data[STORE_AUTHORIZED]
assert self._data is not None
return bool(self._data[STORE_AUTHORIZED])
@callback
def set_authorized(self, authorized):
def set_authorized(self, authorized: bool) -> None:
"""Set authorization status."""
if authorized != self._data[STORE_AUTHORIZED]:
if self._data is not None and authorized != self._data[STORE_AUTHORIZED]:
self._data[STORE_AUTHORIZED] = authorized
self._store.async_delay_save(lambda: self._data, 1.0)
async def async_load(self):
async def async_load(self) -> None:
"""Load saved configuration from disk."""
if data := await self._store.async_load():
self._data = data

View File

@@ -69,7 +69,7 @@ API_TEMP_UNITS = {
# Needs to be ordered dict for `async_api_set_thermostat_mode` which does a
# reverse mapping of this dict and we want to map the first occurrence of OFF
# back to HA state.
API_THERMOSTAT_MODES = OrderedDict(
API_THERMOSTAT_MODES: OrderedDict[str, str] = OrderedDict(
[
(climate.HVACMode.HEAT, "HEAT"),
(climate.HVACMode.COOL, "COOL"),

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from collections.abc import Generator, Iterable
import logging
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from homeassistant.components import (
alarm_control_panel,
@@ -274,22 +274,23 @@ class AlexaEntity:
self.entity_conf = config.entity_config.get(entity.entity_id, {})
@property
def entity_id(self):
def entity_id(self) -> str:
"""Return the Entity ID."""
return self.entity.entity_id
def friendly_name(self):
def friendly_name(self) -> str:
"""Return the Alexa API friendly name."""
return self.entity_conf.get(CONF_NAME, self.entity.name).translate(
TRANSLATION_TABLE
)
friendly_name: str = self.entity_conf.get(
CONF_NAME, self.entity.name
).translate(TRANSLATION_TABLE)
return friendly_name
def description(self):
def description(self) -> str:
"""Return the Alexa API description."""
description = self.entity_conf.get(CONF_DESCRIPTION) or self.entity_id
return f"{description} via Home Assistant".translate(TRANSLATION_TABLE)
def alexa_id(self):
def alexa_id(self) -> str:
"""Return the Alexa API entity id."""
return generate_alexa_id(self.entity.entity_id)
@@ -317,7 +318,7 @@ class AlexaEntity:
"""
raise NotImplementedError
def serialize_properties(self):
def serialize_properties(self) -> Generator[dict[str, Any], None, None]:
"""Yield each supported property in API format."""
for interface in self.interfaces():
if not interface.properties_proactively_reported():
@@ -325,9 +326,9 @@ class AlexaEntity:
yield from interface.serialize_properties()
def serialize_discovery(self):
def serialize_discovery(self) -> dict[str, Any]:
"""Serialize the entity for discovery."""
result = {
result: dict[str, Any] = {
"displayCategories": self.display_categories(),
"cookie": {},
"endpointId": self.alexa_id(),
@@ -366,7 +367,7 @@ def async_get_entities(
hass: HomeAssistant, config: AbstractConfig
) -> list[AlexaEntity]:
"""Return all entities that are supported by Alexa."""
entities = []
entities: list[AlexaEntity] = []
for state in hass.states.async_all():
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
continue
@@ -725,7 +726,7 @@ class MediaPlayerCapabilities(AlexaEntity):
class SceneCapabilities(AlexaEntity):
"""Class to represent Scene capabilities."""
def description(self):
def description(self) -> str:
"""Return the Alexa API description."""
description = AlexaEntity.description(self)
if "scene" not in description.casefold():

View File

@@ -1,8 +1,9 @@
"""Alexa related errors."""
from __future__ import annotations
from typing import Literal
from typing import Any, Literal
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from .const import API_TEMP_UNITS
@@ -29,7 +30,9 @@ class AlexaError(Exception):
namespace: str | None = None
error_type: str | None = None
def __init__(self, error_message, payload=None):
def __init__(
self, error_message: str, payload: dict[str, Any] | None = None
) -> None:
"""Initialize an alexa error."""
Exception.__init__(self)
self.error_message = error_message
@@ -42,7 +45,7 @@ class AlexaInvalidEndpointError(AlexaError):
namespace = "Alexa"
error_type = "NO_SUCH_ENDPOINT"
def __init__(self, endpoint_id):
def __init__(self, endpoint_id: str) -> None:
"""Initialize invalid endpoint error."""
msg = f"The endpoint {endpoint_id} does not exist"
AlexaError.__init__(self, msg)
@@ -93,7 +96,9 @@ class AlexaTempRangeError(AlexaError):
namespace = "Alexa"
error_type = "TEMPERATURE_VALUE_OUT_OF_RANGE"
def __init__(self, hass, temp, min_temp, max_temp):
def __init__(
self, hass: HomeAssistant, temp: float, min_temp: float, max_temp: float
) -> None:
"""Initialize TempRange error."""
unit = hass.config.units.temperature_unit
temp_range = {

View File

@@ -4,10 +4,13 @@ from http import HTTPStatus
import logging
import uuid
from aiohttp.web_response import StreamResponse
from homeassistant.components import http
from homeassistant.const import CONF_PASSWORD
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import template
from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util
from .const import (
@@ -32,7 +35,7 @@ FLASH_BRIEFINGS_API_ENDPOINT = "/api/alexa/flash_briefings/{briefing_id}"
@callback
def async_setup(hass, flash_briefing_config):
def async_setup(hass: HomeAssistant, flash_briefing_config: ConfigType) -> None:
"""Activate Alexa component."""
hass.http.register_view(AlexaFlashBriefingView(hass, flash_briefing_config))
@@ -44,14 +47,16 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
requires_auth = False
name = "api:alexa:flash_briefings"
def __init__(self, hass, flash_briefings):
def __init__(self, hass: HomeAssistant, flash_briefings: ConfigType) -> None:
"""Initialize Alexa view."""
super().__init__()
self.flash_briefings = flash_briefings
template.attach(hass, self.flash_briefings)
@callback
def get(self, request, briefing_id):
def get(
self, request: http.HomeAssistantRequest, briefing_id: str
) -> StreamResponse | tuple[bytes, HTTPStatus]:
"""Handle Alexa Flash Briefing request."""
_LOGGER.debug("Received Alexa flash briefing request for: %s", briefing_id)

View File

@@ -75,8 +75,7 @@ from .errors import (
AlexaUnsupportedThermostatModeError,
AlexaVideoActionNotPermittedForContentError,
)
from .messages import AlexaDirective, AlexaResponse
from .state_report import async_enable_proactive_mode
from .state_report import AlexaDirective, AlexaResponse, async_enable_proactive_mode
_LOGGER = logging.getLogger(__name__)
DIRECTIVE_NOT_SUPPORTED = "Entity does not support directive"
@@ -124,7 +123,7 @@ async def async_api_accept_grant(
Async friendly.
"""
auth_code = directive.payload["grant"]["code"]
auth_code: str = directive.payload["grant"]["code"]
_LOGGER.debug("AcceptGrant code: %s", auth_code)
if config.supports_auth:
@@ -340,8 +339,8 @@ async def async_api_decrease_color_temp(
) -> AlexaResponse:
"""Process a decrease color temperature request."""
entity = directive.entity
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
max_mireds = int(entity.attributes.get(light.ATTR_MAX_MIREDS))
current = int(entity.attributes[light.ATTR_COLOR_TEMP])
max_mireds = int(entity.attributes[light.ATTR_MAX_MIREDS])
value = min(max_mireds, current + 50)
await hass.services.async_call(
@@ -364,8 +363,8 @@ async def async_api_increase_color_temp(
) -> AlexaResponse:
"""Process an increase color temperature request."""
entity = directive.entity
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS))
current = int(entity.attributes[light.ATTR_COLOR_TEMP])
min_mireds = int(entity.attributes[light.ATTR_MIN_MIREDS])
value = max(min_mireds, current - 50)
await hass.services.async_call(
@@ -404,7 +403,7 @@ async def async_api_activate(
context=context,
)
payload = {
payload: dict[str, Any] = {
"cause": {"type": Cause.VOICE_INTERACTION},
"timestamp": dt_util.utcnow().strftime(DATE_FORMAT),
}
@@ -433,7 +432,7 @@ async def async_api_deactivate(
context=context,
)
payload = {
payload: dict[str, Any] = {
"cause": {"type": Cause.VOICE_INTERACTION},
"timestamp": dt_util.utcnow().strftime(DATE_FORMAT),
}
@@ -475,7 +474,24 @@ async def async_api_unlock(
context: ha.Context,
) -> AlexaResponse:
"""Process an unlock request."""
if config.locale not in {"de-DE", "en-US", "ja-JP"}:
if config.locale not in {
"ar-SA",
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}:
msg = (
"The unlock directive is not supported for the following locales:"
f" {config.locale}"
@@ -510,7 +526,7 @@ async def async_api_set_volume(
volume = round(float(directive.payload["volume"] / 100), 2)
entity = directive.entity
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
@@ -555,7 +571,7 @@ async def async_api_select_input(
)
raise AlexaInvalidValueError(msg)
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_INPUT_SOURCE: media_input,
}
@@ -582,7 +598,7 @@ async def async_api_adjust_volume(
volume_delta = int(directive.payload["volume"])
entity = directive.entity
current_level = entity.attributes.get(media_player.const.ATTR_MEDIA_VOLUME_LEVEL)
current_level = entity.attributes[media_player.const.ATTR_MEDIA_VOLUME_LEVEL]
# read current state
try:
@@ -592,7 +608,7 @@ async def async_api_adjust_volume(
volume = float(max(0, volume_delta + current) / 100)
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
@@ -632,7 +648,7 @@ async def async_api_adjust_volume_step(
if is_default:
volume_int = default_steps
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
for _ in range(abs(volume_int)):
await hass.services.async_call(
@@ -653,7 +669,7 @@ async def async_api_set_mute(
"""Process a set mute request."""
mute = bool(directive.payload["mute"])
entity = directive.entity
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_VOLUME_MUTED: mute,
}
@@ -674,7 +690,7 @@ async def async_api_play(
) -> AlexaResponse:
"""Process a play request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_PLAY, data, blocking=False, context=context
@@ -692,7 +708,7 @@ async def async_api_pause(
) -> AlexaResponse:
"""Process a pause request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_PAUSE, data, blocking=False, context=context
@@ -710,7 +726,7 @@ async def async_api_stop(
) -> AlexaResponse:
"""Process a stop request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_STOP, data, blocking=False, context=context
@@ -728,7 +744,7 @@ async def async_api_next(
) -> AlexaResponse:
"""Process a next request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_NEXT_TRACK, data, blocking=False, context=context
@@ -746,7 +762,7 @@ async def async_api_previous(
) -> AlexaResponse:
"""Process a previous request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
await hass.services.async_call(
entity.domain,
@@ -759,7 +775,9 @@ async def async_api_previous(
return directive.response()
def temperature_from_object(hass, temp_obj, interval=False):
def temperature_from_object(
hass: ha.HomeAssistant, temp_obj: dict[str, Any], interval: bool = False
) -> float:
"""Get temperature from Temperature object in requested unit."""
to_unit = hass.config.units.temperature_unit
from_unit = UnitOfTemperature.CELSIUS
@@ -785,11 +803,11 @@ async def async_api_set_target_temp(
) -> AlexaResponse:
"""Process a set target temperature request."""
entity = directive.entity
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
max_temp = entity.attributes.get(climate.ATTR_MAX_TEMP)
min_temp = entity.attributes[climate.ATTR_MIN_TEMP]
max_temp = entity.attributes[climate.ATTR_MAX_TEMP]
unit = hass.config.units.temperature_unit
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
payload = directive.payload
response = directive.response()
@@ -849,9 +867,10 @@ async def async_api_adjust_target_temp(
context: ha.Context,
) -> AlexaResponse:
"""Process an adjust target temperature request."""
data: dict[str, Any]
entity = directive.entity
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
max_temp = entity.attributes.get(climate.ATTR_MAX_TEMP)
min_temp = entity.attributes[climate.ATTR_MIN_TEMP]
max_temp = entity.attributes[climate.ATTR_MAX_TEMP]
unit = hass.config.units.temperature_unit
temp_delta = temperature_from_object(
@@ -862,7 +881,7 @@ async def async_api_adjust_target_temp(
current_target_temp_high = entity.attributes.get(climate.ATTR_TARGET_TEMP_HIGH)
current_target_temp_low = entity.attributes.get(climate.ATTR_TARGET_TEMP_LOW)
if current_target_temp_high and current_target_temp_low:
if current_target_temp_high is not None and current_target_temp_low is not None:
target_temp_high = float(current_target_temp_high) + temp_delta
if target_temp_high < min_temp or target_temp_high > max_temp:
raise AlexaTempRangeError(hass, target_temp_high, min_temp, max_temp)
@@ -892,7 +911,7 @@ async def async_api_adjust_target_temp(
}
)
else:
target_temp = float(entity.attributes.get(ATTR_TEMPERATURE)) + temp_delta
target_temp = float(entity.attributes[ATTR_TEMPERATURE]) + temp_delta
if target_temp < min_temp or target_temp > max_temp:
raise AlexaTempRangeError(hass, target_temp, min_temp, max_temp)
@@ -925,11 +944,13 @@ async def async_api_set_thermostat_mode(
context: ha.Context,
) -> AlexaResponse:
"""Process a set thermostat mode request."""
operation_list: list[str]
entity = directive.entity
mode = directive.payload["thermostatMode"]
mode = mode if isinstance(mode, str) else mode["value"]
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
ha_preset = next((k for k, v in API_THERMOSTAT_PRESETS.items() if v == mode), None)
@@ -944,7 +965,7 @@ async def async_api_set_thermostat_mode(
data[climate.ATTR_PRESET_MODE] = ha_preset
elif mode == "CUSTOM":
operation_list = entity.attributes.get(climate.ATTR_HVAC_MODES)
operation_list = entity.attributes.get(climate.ATTR_HVAC_MODES, [])
custom_mode = directive.payload["thermostatMode"]["customName"]
custom_mode = next(
(k for k, v in API_THERMOSTAT_MODES_CUSTOM.items() if v == custom_mode),
@@ -960,9 +981,13 @@ async def async_api_set_thermostat_mode(
data[climate.ATTR_HVAC_MODE] = custom_mode
else:
operation_list = entity.attributes.get(climate.ATTR_HVAC_MODES)
ha_modes = {k: v for k, v in API_THERMOSTAT_MODES.items() if v == mode}
ha_mode = next(iter(set(ha_modes).intersection(operation_list)), None)
operation_list = entity.attributes.get(climate.ATTR_HVAC_MODES, [])
ha_modes: dict[str, str] = {
k: v for k, v in API_THERMOSTAT_MODES.items() if v == mode
}
ha_mode: str | None = next(
iter(set(ha_modes).intersection(operation_list)), None
)
if ha_mode not in operation_list:
msg = f"The requested thermostat mode {mode} is not supported"
raise AlexaUnsupportedThermostatModeError(msg)
@@ -1007,7 +1032,7 @@ async def async_api_arm(
entity = directive.entity
service = None
arm_state = directive.payload["armState"]
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
if entity.state != STATE_ALARM_DISARMED:
msg = "You must disarm the system before you can set the requested arm state."
@@ -1027,7 +1052,7 @@ async def async_api_arm(
)
# return 0 until alarm integration supports an exit delay
payload = {"exitDelayInSeconds": 0}
payload: dict[str, Any] = {"exitDelayInSeconds": 0}
response = directive.response(
name="Arm.Response", namespace="Alexa.SecurityPanelController", payload=payload
@@ -1053,7 +1078,7 @@ async def async_api_disarm(
) -> AlexaResponse:
"""Process a Security Panel Disarm request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
response = directive.response()
# Per Alexa Documentation: If you receive a Disarm directive, and the
@@ -1095,7 +1120,7 @@ async def async_api_set_mode(
instance = directive.instance
domain = entity.domain
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
mode = directive.payload["mode"]
# Fan Direction
@@ -1108,8 +1133,11 @@ async def async_api_set_mode(
# Fan preset_mode
elif instance == f"{fan.DOMAIN}.{fan.ATTR_PRESET_MODE}":
preset_mode = mode.split(".")[1]
if preset_mode != PRESET_MODE_NA and preset_mode in entity.attributes.get(
fan.ATTR_PRESET_MODES
preset_modes: list[str] | None = entity.attributes.get(fan.ATTR_PRESET_MODES)
if (
preset_mode != PRESET_MODE_NA
and preset_modes
and preset_mode in preset_modes
):
service = fan.SERVICE_SET_PRESET_MODE
data[fan.ATTR_PRESET_MODE] = preset_mode
@@ -1120,9 +1148,8 @@ async def async_api_set_mode(
# Humidifier mode
elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}":
mode = mode.split(".")[1]
if mode != PRESET_MODE_NA and mode in entity.attributes.get(
humidifier.ATTR_AVAILABLE_MODES
):
modes: list[str] | None = entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES)
if mode != PRESET_MODE_NA and modes and mode in modes:
service = humidifier.SERVICE_SET_MODE
data[humidifier.ATTR_MODE] = mode
else:
@@ -1195,7 +1222,7 @@ async def async_api_toggle_on(
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
service = fan.SERVICE_OSCILLATE
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
fan.ATTR_OSCILLATING: True,
}
@@ -1234,7 +1261,7 @@ async def async_api_toggle_off(
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
service = fan.SERVICE_OSCILLATE
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
fan.ATTR_OSCILLATING: False,
}
@@ -1268,7 +1295,7 @@ async def async_api_set_range(
instance = directive.instance
domain = entity.domain
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
range_value = directive.payload["rangeValue"]
# Cover Position
@@ -1537,7 +1564,7 @@ async def async_api_changechannel(
channel = metadata_payload["name"]
payload_name = "callSign"
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.const.ATTR_MEDIA_CONTENT_ID: channel,
media_player.const.ATTR_MEDIA_CONTENT_TYPE: (
@@ -1577,7 +1604,7 @@ async def async_api_skipchannel(
channel = int(directive.payload["channelCount"])
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
if channel < 0:
service_media = SERVICE_MEDIA_PREVIOUS_TRACK
@@ -1624,7 +1651,7 @@ async def async_api_seek(
if media_duration and 0 < int(media_duration) < seek_position:
seek_position = media_duration
data = {
data: dict[str, Any] = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.ATTR_MEDIA_SEEK_POSITION: seek_position,
}
@@ -1640,7 +1667,9 @@ async def async_api_seek(
# convert seconds to milliseconds for StateReport.
seek_position = int(seek_position * 1000)
payload = {"properties": [{"name": "positionMilliseconds", "value": seek_position}]}
payload: dict[str, Any] = {
"properties": [{"name": "positionMilliseconds", "value": seek_position}]
}
return directive.response(
name="StateReport", namespace="Alexa.SeekController", payload=payload
)
@@ -1656,7 +1685,7 @@ async def async_api_set_eq_mode(
"""Process a SetMode request for EqualizerController."""
mode = directive.payload["mode"]
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
sound_mode_list = entity.attributes.get(media_player.const.ATTR_SOUND_MODE_LIST)
if sound_mode_list and mode.lower() in sound_mode_list:
@@ -1702,7 +1731,7 @@ async def async_api_hold(
) -> AlexaResponse:
"""Process a TimeHoldController Hold request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
if entity.domain == timer.DOMAIN:
service = timer.SERVICE_PAUSE
@@ -1729,7 +1758,7 @@ async def async_api_resume(
) -> AlexaResponse:
"""Process a TimeHoldController Resume request."""
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
if entity.domain == timer.DOMAIN:
service = timer.SERVICE_START
@@ -1774,7 +1803,7 @@ async def async_api_initialize_camera_stream(
"Failed to find suitable URL to serve to Alexa"
) from err
payload = {
payload: dict[str, Any] = {
"cameraStreams": [
{
"uri": f"{external_url}{stream_source}",

View File

@@ -3,8 +3,10 @@ import enum
import logging
from typing import Any
from aiohttp.web import Response
from homeassistant.components import http
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import intent
from homeassistant.util.decorator import Registry
@@ -18,7 +20,7 @@ HANDLERS = Registry() # type: ignore[var-annotated]
INTENTS_API_ENDPOINT = "/api/alexa"
class SpeechType(enum.Enum):
class SpeechType(enum.StrEnum):
"""The Alexa speech types."""
plaintext = "PlainText"
@@ -28,7 +30,7 @@ class SpeechType(enum.Enum):
SPEECH_MAPPINGS = {"plain": SpeechType.plaintext, "ssml": SpeechType.ssml}
class CardType(enum.Enum):
class CardType(enum.StrEnum):
"""The Alexa card types."""
simple = "Simple"
@@ -36,12 +38,12 @@ class CardType(enum.Enum):
@callback
def async_setup(hass):
def async_setup(hass: HomeAssistant) -> None:
"""Activate Alexa component."""
hass.http.register_view(AlexaIntentsView)
async def async_setup_intents(hass):
async def async_setup_intents(hass: HomeAssistant) -> None:
"""Do intents setup.
Right now this module does not expose any, but the intent component breaks
@@ -60,15 +62,15 @@ class AlexaIntentsView(http.HomeAssistantView):
url = INTENTS_API_ENDPOINT
name = "api:alexa"
async def post(self, request):
async def post(self, request: http.HomeAssistantRequest) -> Response | bytes:
"""Handle Alexa."""
hass = request.app["hass"]
message = await request.json()
hass: HomeAssistant = request.app["hass"]
message: dict[str, Any] = await request.json()
_LOGGER.debug("Received Alexa request: %s", message)
try:
response = await async_handle_message(hass, message)
response: dict[str, Any] = await async_handle_message(hass, message)
return b"" if response is None else self.json(response)
except UnknownRequest as err:
_LOGGER.warning(str(err))
@@ -99,15 +101,19 @@ class AlexaIntentsView(http.HomeAssistantView):
)
def intent_error_response(hass, message, error):
def intent_error_response(
hass: HomeAssistant, message: dict[str, Any], error: str
) -> dict[str, Any]:
"""Return an Alexa response that will speak the error message."""
alexa_intent_info = message.get("request").get("intent")
alexa_response = AlexaResponse(hass, alexa_intent_info)
alexa_intent_info = message["request"].get("intent")
alexa_response = AlexaIntentResponse(hass, alexa_intent_info)
alexa_response.add_speech(SpeechType.plaintext, error)
return alexa_response.as_dict()
async def async_handle_message(hass, message):
async def async_handle_message(
hass: HomeAssistant, message: dict[str, Any]
) -> dict[str, Any]:
"""Handle an Alexa intent.
Raises:
@@ -117,19 +123,22 @@ async def async_handle_message(hass, message):
- intent.IntentError
"""
req = message.get("request")
req = message["request"]
req_type = req["type"]
if not (handler := HANDLERS.get(req_type)):
raise UnknownRequest(f"Received unknown request {req_type}")
return await handler(hass, message)
response: dict[str, Any] = await handler(hass, message)
return response
@HANDLERS.register("SessionEndedRequest")
@HANDLERS.register("IntentRequest")
@HANDLERS.register("LaunchRequest")
async def async_handle_intent(hass, message):
async def async_handle_intent(
hass: HomeAssistant, message: dict[str, Any]
) -> dict[str, Any]:
"""Handle an intent request.
Raises:
@@ -138,9 +147,9 @@ async def async_handle_intent(hass, message):
- intent.IntentError
"""
req = message.get("request")
req = message["request"]
alexa_intent_info = req.get("intent")
alexa_response = AlexaResponse(hass, alexa_intent_info)
alexa_response = AlexaIntentResponse(hass, alexa_intent_info)
if req["type"] == "LaunchRequest":
intent_name = (
@@ -187,7 +196,7 @@ def resolve_slot_data(key: str, request: dict[str, Any]) -> dict[str, str]:
# passes the id and name of the nearest possible slot resolution. For
# reference to the request object structure, see the Alexa docs:
# https://tinyurl.com/ybvm7jhs
resolved_data = {}
resolved_data: dict[str, Any] = {}
resolved_data["value"] = request["value"]
resolved_data["id"] = ""
@@ -226,18 +235,18 @@ def resolve_slot_data(key: str, request: dict[str, Any]) -> dict[str, str]:
return resolved_data
class AlexaResponse:
class AlexaIntentResponse:
"""Help generating the response for Alexa."""
def __init__(self, hass, intent_info):
def __init__(self, hass: HomeAssistant, intent_info: dict[str, Any] | None) -> None:
"""Initialize the response."""
self.hass = hass
self.speech = None
self.card = None
self.reprompt = None
self.session_attributes = {}
self.speech: dict[str, Any] | None = None
self.card: dict[str, Any] | None = None
self.reprompt: dict[str, Any] | None = None
self.session_attributes: dict[str, Any] = {}
self.should_end_session = True
self.variables = {}
self.variables: dict[str, Any] = {}
# Intent is None if request was a LaunchRequest or SessionEndedRequest
if intent_info is not None:
@@ -252,7 +261,7 @@ class AlexaResponse:
self.variables[_key] = _slot_data["value"]
self.variables[_key + "_Id"] = _slot_data["id"]
def add_card(self, card_type, title, content):
def add_card(self, card_type: CardType, title: str, content: str) -> None:
"""Add a card to the response."""
assert self.card is None
@@ -266,7 +275,7 @@ class AlexaResponse:
card["content"] = content
self.card = card
def add_speech(self, speech_type, text):
def add_speech(self, speech_type: SpeechType, text: str) -> None:
"""Add speech to the response."""
assert self.speech is None
@@ -274,7 +283,7 @@ class AlexaResponse:
self.speech = {"type": speech_type.value, key: text}
def add_reprompt(self, speech_type, text):
def add_reprompt(self, speech_type: SpeechType, text: str) -> None:
"""Add reprompt if user does not answer."""
assert self.reprompt is None
@@ -284,9 +293,9 @@ class AlexaResponse:
self.reprompt = {"type": speech_type.value, key: text}
def as_dict(self):
def as_dict(self) -> dict[str, Any]:
"""Return response in an Alexa valid dict."""
response = {"shouldEndSession": self.should_end_session}
response: dict[str, Any] = {"shouldEndSession": self.should_end_session}
if self.card is not None:
response["card"] = self.card

View File

@@ -1,20 +1,26 @@
"""Describe logbook events."""
from collections.abc import Callable
from typing import Any
from homeassistant.components.logbook import (
LOGBOOK_ENTRY_ENTITY_ID,
LOGBOOK_ENTRY_MESSAGE,
LOGBOOK_ENTRY_NAME,
)
from homeassistant.core import callback
from homeassistant.core import Event, HomeAssistant, callback
from .const import DOMAIN, EVENT_ALEXA_SMART_HOME
@callback
def async_describe_events(hass, async_describe_event):
def async_describe_events(
hass: HomeAssistant,
async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None],
) -> None:
"""Describe logbook events."""
@callback
def async_describe_logbook_event(event):
def async_describe_logbook_event(event: Event) -> dict[str, Any]:
"""Describe a logbook event."""
data = event.data

View File

@@ -1,195 +0,0 @@
"""Alexa models."""
import logging
from uuid import uuid4
from .const import (
API_CONTEXT,
API_DIRECTIVE,
API_ENDPOINT,
API_EVENT,
API_HEADER,
API_PAYLOAD,
API_SCOPE,
)
from .entities import ENTITY_ADAPTERS
from .errors import AlexaInvalidEndpointError
_LOGGER = logging.getLogger(__name__)
class AlexaDirective:
"""An incoming Alexa directive."""
def __init__(self, request):
"""Initialize a directive."""
self._directive = request[API_DIRECTIVE]
self.namespace = self._directive[API_HEADER]["namespace"]
self.name = self._directive[API_HEADER]["name"]
self.payload = self._directive[API_PAYLOAD]
self.has_endpoint = API_ENDPOINT in self._directive
self.entity = self.entity_id = self.endpoint = self.instance = None
def load_entity(self, hass, config):
"""Set attributes related to the entity for this request.
Sets these attributes when self.has_endpoint is True:
- entity
- entity_id
- endpoint
- instance (when header includes instance property)
Behavior when self.has_endpoint is False is undefined.
Will raise AlexaInvalidEndpointError if the endpoint in the request is
malformed or nonexistent.
"""
_endpoint_id = self._directive[API_ENDPOINT]["endpointId"]
self.entity_id = _endpoint_id.replace("#", ".")
self.entity = hass.states.get(self.entity_id)
if not self.entity or not config.should_expose(self.entity_id):
raise AlexaInvalidEndpointError(_endpoint_id)
self.endpoint = ENTITY_ADAPTERS[self.entity.domain](hass, config, self.entity)
if "instance" in self._directive[API_HEADER]:
self.instance = self._directive[API_HEADER]["instance"]
def response(self, name="Response", namespace="Alexa", payload=None):
"""Create an API formatted response.
Async friendly.
"""
response = AlexaResponse(name, namespace, payload)
token = self._directive[API_HEADER].get("correlationToken")
if token:
response.set_correlation_token(token)
if self.has_endpoint:
response.set_endpoint(self._directive[API_ENDPOINT].copy())
return response
def error(
self,
namespace="Alexa",
error_type="INTERNAL_ERROR",
error_message="",
payload=None,
):
"""Create a API formatted error response.
Async friendly.
"""
payload = payload or {}
payload["type"] = error_type
payload["message"] = error_message
_LOGGER.info(
"Request %s/%s error %s: %s",
self._directive[API_HEADER]["namespace"],
self._directive[API_HEADER]["name"],
error_type,
error_message,
)
return self.response(name="ErrorResponse", namespace=namespace, payload=payload)
class AlexaResponse:
"""Class to hold a response."""
def __init__(self, name, namespace, payload=None):
"""Initialize the response."""
payload = payload or {}
self._response = {
API_EVENT: {
API_HEADER: {
"namespace": namespace,
"name": name,
"messageId": str(uuid4()),
"payloadVersion": "3",
},
API_PAYLOAD: payload,
}
}
@property
def name(self):
"""Return the name of this response."""
return self._response[API_EVENT][API_HEADER]["name"]
@property
def namespace(self):
"""Return the namespace of this response."""
return self._response[API_EVENT][API_HEADER]["namespace"]
def set_correlation_token(self, token):
"""Set the correlationToken.
This should normally mirror the value from a request, and is set by
AlexaDirective.response() usually.
"""
self._response[API_EVENT][API_HEADER]["correlationToken"] = token
def set_endpoint_full(self, bearer_token, endpoint_id, cookie=None):
"""Set the endpoint dictionary.
This is used to send proactive messages to Alexa.
"""
self._response[API_EVENT][API_ENDPOINT] = {
API_SCOPE: {"type": "BearerToken", "token": bearer_token}
}
if endpoint_id is not None:
self._response[API_EVENT][API_ENDPOINT]["endpointId"] = endpoint_id
if cookie is not None:
self._response[API_EVENT][API_ENDPOINT]["cookie"] = cookie
def set_endpoint(self, endpoint):
"""Set the endpoint.
This should normally mirror the value from a request, and is set by
AlexaDirective.response() usually.
"""
self._response[API_EVENT][API_ENDPOINT] = endpoint
def _properties(self):
context = self._response.setdefault(API_CONTEXT, {})
return context.setdefault("properties", [])
def add_context_property(self, prop):
"""Add a property to the response context.
The Alexa response includes a list of properties which provides
feedback on how states have changed. For example if a user asks,
"Alexa, set thermostat to 20 degrees", the API expects a response with
the new value of the property, and Alexa will respond to the user
"Thermostat set to 20 degrees".
async_handle_message() will call .merge_context_properties() for every
request automatically, however often handlers will call services to
change state but the effects of those changes are applied
asynchronously. Thus, handlers should call this method to confirm
changes before returning.
"""
self._properties().append(prop)
def merge_context_properties(self, endpoint):
"""Add all properties from given endpoint if not already set.
Handlers should be using .add_context_property().
"""
properties = self._properties()
already_set = {(p["namespace"], p["name"]) for p in properties}
for prop in endpoint.serialize_properties():
if (prop["namespace"], prop["name"]) not in already_set:
self.add_context_property(prop)
def serialize(self):
"""Return response as a JSON-able data structure."""
return self._response

View File

@@ -1,6 +1,9 @@
"""Alexa Resources and Assets."""
from typing import Any
class AlexaGlobalCatalog:
"""The Global Alexa catalog.
@@ -207,36 +210,40 @@ class AlexaCapabilityResource:
https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
"""
def __init__(self, labels):
def __init__(self, labels: list[str]) -> None:
"""Initialize an Alexa resource."""
self._resource_labels = []
for label in labels:
self._resource_labels.append(label)
def serialize_capability_resources(self):
def serialize_capability_resources(self) -> dict[str, list[dict[str, Any]]]:
"""Return capabilityResources object serialized for an API response."""
return self.serialize_labels(self._resource_labels)
def serialize_configuration(self):
def serialize_configuration(self) -> dict[str, Any]:
"""Return serialized configuration for an API response.
Return ModeResources, PresetResources friendlyNames serialized.
"""
return []
raise NotImplementedError()
def serialize_labels(self, resources):
def serialize_labels(self, resources: list[str]) -> dict[str, list[dict[str, Any]]]:
"""Return serialized labels for an API response.
Returns resource label objects for friendlyNames serialized.
"""
labels = []
labels: list[dict[str, Any]] = []
label_dict: dict[str, Any]
for label in resources:
if label in AlexaGlobalCatalog.__dict__.values():
label = {"@type": "asset", "value": {"assetId": label}}
label_dict = {"@type": "asset", "value": {"assetId": label}}
else:
label = {"@type": "text", "value": {"text": label, "locale": "en-US"}}
label_dict = {
"@type": "text",
"value": {"text": label, "locale": "en-US"},
}
labels.append(label)
labels.append(label_dict)
return {"friendlyNames": labels}
@@ -247,22 +254,22 @@ class AlexaModeResource(AlexaCapabilityResource):
https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
"""
def __init__(self, labels, ordered=False):
def __init__(self, labels: list[str], ordered: bool = False) -> None:
"""Initialize an Alexa modeResource."""
super().__init__(labels)
self._supported_modes = []
self._mode_ordered = ordered
self._supported_modes: list[dict[str, Any]] = []
self._mode_ordered: bool = ordered
def add_mode(self, value, labels):
def add_mode(self, value: str, labels: list[str]) -> None:
"""Add mode to the supportedModes object."""
self._supported_modes.append({"value": value, "labels": labels})
def serialize_configuration(self):
def serialize_configuration(self) -> dict[str, Any]:
"""Return serialized configuration for an API response.
Returns configuration for ModeResources friendlyNames serialized.
"""
mode_resources = []
mode_resources: list[dict[str, Any]] = []
for mode in self._supported_modes:
result = {
"value": mode["value"],
@@ -282,10 +289,17 @@ class AlexaPresetResource(AlexaCapabilityResource):
https://developer.amazon.com/docs/device-apis/resources-and-assets.html#presetresources
"""
def __init__(self, labels, min_value, max_value, precision, unit=None):
def __init__(
self,
labels: list[str],
min_value: int | float,
max_value: int | float,
precision: int | float,
unit: str | None = None,
) -> None:
"""Initialize an Alexa presetResource."""
super().__init__(labels)
self._presets = []
self._presets: list[dict[str, Any]] = []
self._minimum_value = min_value
self._maximum_value = max_value
self._precision = precision
@@ -293,16 +307,16 @@ class AlexaPresetResource(AlexaCapabilityResource):
if unit in AlexaGlobalCatalog.__dict__.values():
self._unit_of_measure = unit
def add_preset(self, value, labels):
def add_preset(self, value: int | float, labels: list[str]) -> None:
"""Add preset to configuration presets array."""
self._presets.append({"value": value, "labels": labels})
def serialize_configuration(self):
def serialize_configuration(self) -> dict[str, Any]:
"""Return serialized configuration for an API response.
Returns configuration for PresetResources friendlyNames serialized.
"""
configuration = {
configuration: dict[str, Any] = {
"supportedRange": {
"minimumValue": self._minimum_value,
"maximumValue": self._maximum_value,
@@ -372,26 +386,28 @@ class AlexaSemantics:
DIRECTIVE_MODE_SET_MODE = "SetMode"
DIRECTIVE_MODE_ADJUST_MODE = "AdjustMode"
def __init__(self):
def __init__(self) -> None:
"""Initialize an Alexa modeResource."""
self._action_mappings = []
self._state_mappings = []
self._action_mappings: list[dict[str, Any]] = []
self._state_mappings: list[dict[str, Any]] = []
def _add_action_mapping(self, semantics):
def _add_action_mapping(self, semantics: dict[str, Any]) -> None:
"""Add action mapping between actions and interface directives."""
self._action_mappings.append(semantics)
def _add_state_mapping(self, semantics):
def _add_state_mapping(self, semantics: dict[str, Any]) -> None:
"""Add state mapping between states and interface directives."""
self._state_mappings.append(semantics)
def add_states_to_value(self, states, value):
def add_states_to_value(self, states: list[str], value: Any) -> None:
"""Add StatesToValue stateMappings."""
self._add_state_mapping(
{"@type": self.STATES_TO_VALUE, "states": states, "value": value}
)
def add_states_to_range(self, states, min_value, max_value):
def add_states_to_range(
self, states: list[str], min_value: int | float, max_value: int | float
) -> None:
"""Add StatesToRange stateMappings."""
self._add_state_mapping(
{
@@ -401,7 +417,9 @@ class AlexaSemantics:
}
)
def add_action_to_directive(self, actions, directive, payload):
def add_action_to_directive(
self, actions: list[str], directive: str, payload: dict[str, Any]
) -> None:
"""Add ActionsToDirective actionMappings."""
self._add_action_mapping(
{
@@ -411,9 +429,9 @@ class AlexaSemantics:
}
)
def serialize_semantics(self):
def serialize_semantics(self) -> dict[str, Any]:
"""Return semantics object serialized for an API response."""
semantics = {}
semantics: dict[str, Any] = {}
if self._action_mappings:
semantics[self.MAPPINGS_ACTION] = self._action_mappings
if self._state_mappings:

View File

@@ -1,17 +1,170 @@
"""Support for alexa Smart Home Skill API."""
import logging
from typing import Any
import homeassistant.core as ha
from aiohttp import web
from yarl import URL
from .const import API_DIRECTIVE, API_HEADER, EVENT_ALEXA_SMART_HOME
from homeassistant import core
from homeassistant.auth.models import User
from homeassistant.components.http import HomeAssistantRequest
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.typing import ConfigType
from .auth import Auth
from .config import AbstractConfig
from .const import (
API_DIRECTIVE,
API_HEADER,
CONF_ENDPOINT,
CONF_ENTITY_CONFIG,
CONF_FILTER,
CONF_LOCALE,
EVENT_ALEXA_SMART_HOME,
)
from .errors import AlexaBridgeUnreachableError, AlexaError
from .handlers import HANDLERS
from .messages import AlexaDirective
from .state_report import AlexaDirective
_LOGGER = logging.getLogger(__name__)
SMART_HOME_HTTP_ENDPOINT = "/api/alexa/smart_home"
async def async_handle_message(hass, config, request, context=None, enabled=True):
class AlexaConfig(AbstractConfig):
"""Alexa config."""
_auth: Auth | None
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
"""Initialize Alexa config."""
super().__init__(hass)
self._config = config
if config.get(CONF_CLIENT_ID) and config.get(CONF_CLIENT_SECRET):
self._auth = Auth(hass, config[CONF_CLIENT_ID], config[CONF_CLIENT_SECRET])
else:
self._auth = None
@property
def supports_auth(self) -> bool:
"""Return if config supports auth."""
return self._auth is not None
@property
def should_report_state(self) -> bool:
"""Return if we should proactively report states."""
return self._auth is not None and self.authorized
@property
def endpoint(self) -> str | URL | None:
"""Endpoint for report state."""
return self._config.get(CONF_ENDPOINT)
@property
def entity_config(self) -> dict[str, Any]:
"""Return entity config."""
return self._config.get(CONF_ENTITY_CONFIG) or {}
@property
def locale(self) -> str | None:
"""Return config locale."""
return self._config.get(CONF_LOCALE)
@core.callback
def user_identifier(self) -> str:
"""Return an identifier for the user that represents this config."""
return ""
@core.callback
def should_expose(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
if not self._config[CONF_FILTER].empty_filter:
return bool(self._config[CONF_FILTER](entity_id))
entity_registry = er.async_get(self.hass)
if registry_entry := entity_registry.async_get(entity_id):
auxiliary_entity = (
registry_entry.entity_category is not None
or registry_entry.hidden_by is not None
)
else:
auxiliary_entity = False
return not auxiliary_entity
@core.callback
def async_invalidate_access_token(self) -> None:
"""Invalidate access token."""
assert self._auth is not None
self._auth.async_invalidate_access_token()
async def async_get_access_token(self) -> str | None:
"""Get an access token."""
assert self._auth is not None
return await self._auth.async_get_access_token()
async def async_accept_grant(self, code: str) -> str | None:
"""Accept a grant."""
assert self._auth is not None
return await self._auth.async_do_auth(code)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> None:
"""Activate Smart Home functionality of Alexa component.
This is optional, triggered by having a `smart_home:` sub-section in the
alexa configuration.
Even if that's disabled, the functionality in this module may still be used
by the cloud component which will call async_handle_message directly.
"""
smart_home_config = AlexaConfig(hass, config)
await smart_home_config.async_initialize()
hass.http.register_view(SmartHomeView(smart_home_config))
if smart_home_config.should_report_state:
await smart_home_config.async_enable_proactive_mode()
class SmartHomeView(HomeAssistantView):
"""Expose Smart Home v3 payload interface via HTTP POST."""
url = SMART_HOME_HTTP_ENDPOINT
name = "api:alexa:smart_home"
def __init__(self, smart_home_config: AlexaConfig) -> None:
"""Initialize."""
self.smart_home_config = smart_home_config
async def post(self, request: HomeAssistantRequest) -> web.Response | bytes:
"""Handle Alexa Smart Home requests.
The Smart Home API requires the endpoint to be implemented in AWS
Lambda, which will need to forward the requests to here and pass back
the response.
"""
hass: HomeAssistant = request.app["hass"]
user: User = request["hass_user"]
message: dict[str, Any] = await request.json()
_LOGGER.debug("Received Alexa Smart Home request: %s", message)
response = await async_handle_message(
hass, self.smart_home_config, message, context=core.Context(user_id=user.id)
)
_LOGGER.debug("Sending Alexa Smart Home response: %s", response)
return b"" if response is None else self.json(response)
async def async_handle_message(
hass: HomeAssistant,
config: AbstractConfig,
request: dict[str, Any],
context: Context | None = None,
enabled: bool = True,
) -> dict[str, Any]:
"""Handle incoming API messages.
If enabled is False, the response to all messages will be a
@@ -21,7 +174,7 @@ async def async_handle_message(hass, config, request, context=None, enabled=True
assert request[API_DIRECTIVE][API_HEADER]["payloadVersion"] == "3"
if context is None:
context = ha.Context()
context = Context()
directive = AlexaDirective(request)
@@ -48,7 +201,7 @@ async def async_handle_message(hass, config, request, context=None, enabled=True
response = directive.error()
except AlexaError as err:
response = directive.error(
error_type=err.error_type,
error_type=str(err.error_type),
error_message=err.error_message,
payload=err.payload,
)
@@ -61,9 +214,13 @@ async def async_handle_message(hass, config, request, context=None, enabled=True
)
response = directive.error(error_message="Unknown error")
request_info = {"namespace": directive.namespace, "name": directive.name}
request_info: dict[str, Any] = {
"namespace": directive.namespace,
"name": directive.name,
}
if directive.has_endpoint:
assert directive.entity_id is not None
request_info["entity_id"] = directive.entity_id
hass.bus.async_fire(

View File

@@ -1,137 +0,0 @@
"""Alexa HTTP interface."""
import logging
from homeassistant import core
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.typing import ConfigType
from .auth import Auth
from .config import AbstractConfig
from .const import CONF_ENDPOINT, CONF_ENTITY_CONFIG, CONF_FILTER, CONF_LOCALE
from .smart_home import async_handle_message
_LOGGER = logging.getLogger(__name__)
SMART_HOME_HTTP_ENDPOINT = "/api/alexa/smart_home"
class AlexaConfig(AbstractConfig):
"""Alexa config."""
def __init__(self, hass, config):
"""Initialize Alexa config."""
super().__init__(hass)
self._config = config
if config.get(CONF_CLIENT_ID) and config.get(CONF_CLIENT_SECRET):
self._auth = Auth(hass, config[CONF_CLIENT_ID], config[CONF_CLIENT_SECRET])
else:
self._auth = None
@property
def supports_auth(self):
"""Return if config supports auth."""
return self._auth is not None
@property
def should_report_state(self):
"""Return if we should proactively report states."""
return self._auth is not None and self.authorized
@property
def endpoint(self):
"""Endpoint for report state."""
return self._config.get(CONF_ENDPOINT)
@property
def entity_config(self):
"""Return entity config."""
return self._config.get(CONF_ENTITY_CONFIG) or {}
@property
def locale(self):
"""Return config locale."""
return self._config.get(CONF_LOCALE)
@core.callback
def user_identifier(self):
"""Return an identifier for the user that represents this config."""
return ""
@core.callback
def should_expose(self, entity_id):
"""If an entity should be exposed."""
if not self._config[CONF_FILTER].empty_filter:
return self._config[CONF_FILTER](entity_id)
entity_registry = er.async_get(self.hass)
if registry_entry := entity_registry.async_get(entity_id):
auxiliary_entity = (
registry_entry.entity_category is not None
or registry_entry.hidden_by is not None
)
else:
auxiliary_entity = False
return not auxiliary_entity
@core.callback
def async_invalidate_access_token(self):
"""Invalidate access token."""
self._auth.async_invalidate_access_token()
async def async_get_access_token(self):
"""Get an access token."""
return await self._auth.async_get_access_token()
async def async_accept_grant(self, code):
"""Accept a grant."""
return await self._auth.async_do_auth(code)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> None:
"""Activate Smart Home functionality of Alexa component.
This is optional, triggered by having a `smart_home:` sub-section in the
alexa configuration.
Even if that's disabled, the functionality in this module may still be used
by the cloud component which will call async_handle_message directly.
"""
smart_home_config = AlexaConfig(hass, config)
await smart_home_config.async_initialize()
hass.http.register_view(SmartHomeView(smart_home_config))
if smart_home_config.should_report_state:
await smart_home_config.async_enable_proactive_mode()
class SmartHomeView(HomeAssistantView):
"""Expose Smart Home v3 payload interface via HTTP POST."""
url = SMART_HOME_HTTP_ENDPOINT
name = "api:alexa:smart_home"
def __init__(self, smart_home_config):
"""Initialize."""
self.smart_home_config = smart_home_config
async def post(self, request):
"""Handle Alexa Smart Home requests.
The Smart Home API requires the endpoint to be implemented in AWS
Lambda, which will need to forward the requests to here and pass back
the response.
"""
hass = request.app["hass"]
user = request["hass_user"]
message = await request.json()
_LOGGER.debug("Received Alexa Smart Home request: %s", message)
response = await async_handle_message(
hass, self.smart_home_config, message, context=core.Context(user_id=user.id)
)
_LOGGER.debug("Sending Alexa Smart Home response: %s", response)
return b"" if response is None else self.json(response)

View File

@@ -2,27 +2,40 @@
from __future__ import annotations
import asyncio
from asyncio import timeout
from http import HTTPStatus
import json
import logging
from typing import TYPE_CHECKING, cast
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, cast
from uuid import uuid4
import aiohttp
import async_timeout
from homeassistant.components import event
from homeassistant.const import MATCH_ALL, STATE_ON
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, State, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.significant_change import create_checker
import homeassistant.util.dt as dt_util
from homeassistant.util.json import JsonObjectType, json_loads_object
from .const import API_CHANGE, DATE_FORMAT, DOMAIN, Cause
from .const import (
API_CHANGE,
API_CONTEXT,
API_DIRECTIVE,
API_ENDPOINT,
API_EVENT,
API_HEADER,
API_PAYLOAD,
API_SCOPE,
DATE_FORMAT,
DOMAIN,
Cause,
)
from .entities import ENTITY_ADAPTERS, AlexaEntity, generate_alexa_id
from .errors import NoTokenAvailable, RequireRelink
from .messages import AlexaResponse
from .errors import AlexaInvalidEndpointError, NoTokenAvailable, RequireRelink
if TYPE_CHECKING:
from .config import AbstractConfig
@@ -31,7 +44,202 @@ _LOGGER = logging.getLogger(__name__)
DEFAULT_TIMEOUT = 10
async def async_enable_proactive_mode(hass, smart_home_config):
class AlexaDirective:
"""An incoming Alexa directive."""
entity: State
entity_id: str | None
endpoint: AlexaEntity
instance: str | None
def __init__(self, request: dict[str, Any]) -> None:
"""Initialize a directive."""
self._directive: dict[str, Any] = request[API_DIRECTIVE]
self.namespace: str = self._directive[API_HEADER]["namespace"]
self.name: str = self._directive[API_HEADER]["name"]
self.payload: dict[str, Any] = self._directive[API_PAYLOAD]
self.has_endpoint: bool = API_ENDPOINT in self._directive
self.instance = None
self.entity_id = None
def load_entity(self, hass: HomeAssistant, config: AbstractConfig) -> None:
"""Set attributes related to the entity for this request.
Sets these attributes when self.has_endpoint is True:
- entity
- entity_id
- endpoint
- instance (when header includes instance property)
Behavior when self.has_endpoint is False is undefined.
Will raise AlexaInvalidEndpointError if the endpoint in the request is
malformed or nonexistent.
"""
_endpoint_id: str = self._directive[API_ENDPOINT]["endpointId"]
self.entity_id = _endpoint_id.replace("#", ".")
entity: State | None = hass.states.get(self.entity_id)
if not entity or not config.should_expose(self.entity_id):
raise AlexaInvalidEndpointError(_endpoint_id)
self.entity = entity
self.endpoint = ENTITY_ADAPTERS[self.entity.domain](hass, config, self.entity)
if "instance" in self._directive[API_HEADER]:
self.instance = self._directive[API_HEADER]["instance"]
def response(
self,
name: str = "Response",
namespace: str = "Alexa",
payload: dict[str, Any] | None = None,
) -> AlexaResponse:
"""Create an API formatted response.
Async friendly.
"""
response = AlexaResponse(name, namespace, payload)
token = self._directive[API_HEADER].get("correlationToken")
if token:
response.set_correlation_token(token)
if self.has_endpoint:
response.set_endpoint(self._directive[API_ENDPOINT].copy())
return response
def error(
self,
namespace: str = "Alexa",
error_type: str = "INTERNAL_ERROR",
error_message: str = "",
payload: dict[str, Any] | None = None,
) -> AlexaResponse:
"""Create a API formatted error response.
Async friendly.
"""
payload = payload or {}
payload["type"] = error_type
payload["message"] = error_message
_LOGGER.info(
"Request %s/%s error %s: %s",
self._directive[API_HEADER]["namespace"],
self._directive[API_HEADER]["name"],
error_type,
error_message,
)
return self.response(name="ErrorResponse", namespace=namespace, payload=payload)
class AlexaResponse:
"""Class to hold a response."""
def __init__(
self, name: str, namespace: str, payload: dict[str, Any] | None = None
) -> None:
"""Initialize the response."""
payload = payload or {}
self._response: dict[str, Any] = {
API_EVENT: {
API_HEADER: {
"namespace": namespace,
"name": name,
"messageId": str(uuid4()),
"payloadVersion": "3",
},
API_PAYLOAD: payload,
}
}
@property
def name(self) -> str:
"""Return the name of this response."""
name: str = self._response[API_EVENT][API_HEADER]["name"]
return name
@property
def namespace(self) -> str:
"""Return the namespace of this response."""
namespace: str = self._response[API_EVENT][API_HEADER]["namespace"]
return namespace
def set_correlation_token(self, token: str) -> None:
"""Set the correlationToken.
This should normally mirror the value from a request, and is set by
AlexaDirective.response() usually.
"""
self._response[API_EVENT][API_HEADER]["correlationToken"] = token
def set_endpoint_full(
self, bearer_token: str | None, endpoint_id: str | None
) -> None:
"""Set the endpoint dictionary.
This is used to send proactive messages to Alexa.
"""
self._response[API_EVENT][API_ENDPOINT] = {
API_SCOPE: {"type": "BearerToken", "token": bearer_token}
}
if endpoint_id is not None:
self._response[API_EVENT][API_ENDPOINT]["endpointId"] = endpoint_id
def set_endpoint(self, endpoint: dict[str, Any]) -> None:
"""Set the endpoint.
This should normally mirror the value from a request, and is set by
AlexaDirective.response() usually.
"""
self._response[API_EVENT][API_ENDPOINT] = endpoint
def _properties(self) -> list[dict[str, Any]]:
context: dict[str, Any] = self._response.setdefault(API_CONTEXT, {})
properties: list[dict[str, Any]] = context.setdefault("properties", [])
return properties
def add_context_property(self, prop: dict[str, Any]) -> None:
"""Add a property to the response context.
The Alexa response includes a list of properties which provides
feedback on how states have changed. For example if a user asks,
"Alexa, set thermostat to 20 degrees", the API expects a response with
the new value of the property, and Alexa will respond to the user
"Thermostat set to 20 degrees".
async_handle_message() will call .merge_context_properties() for every
request automatically, however often handlers will call services to
change state but the effects of those changes are applied
asynchronously. Thus, handlers should call this method to confirm
changes before returning.
"""
self._properties().append(prop)
def merge_context_properties(self, endpoint: AlexaEntity) -> None:
"""Add all properties from given endpoint if not already set.
Handlers should be using .add_context_property().
"""
properties = self._properties()
already_set = {(p["namespace"], p["name"]) for p in properties}
for prop in endpoint.serialize_properties():
if (prop["namespace"], prop["name"]) not in already_set:
self.add_context_property(prop)
def serialize(self) -> dict[str, Any]:
"""Return response as a JSON-able data structure."""
return self._response
async def async_enable_proactive_mode(
hass: HomeAssistant, smart_home_config: AbstractConfig
) -> CALLBACK_TYPE | None:
"""Enable the proactive mode.
Proactive mode makes this component report state changes to Alexa.
@@ -43,12 +251,12 @@ async def async_enable_proactive_mode(hass, smart_home_config):
def extra_significant_check(
hass: HomeAssistant,
old_state: str,
old_attrs: dict,
old_extra_arg: dict,
old_attrs: dict[Any, Any] | MappingProxyType[Any, Any],
old_extra_arg: Any,
new_state: str,
new_attrs: dict,
new_extra_arg: dict,
):
new_attrs: dict[str, Any] | MappingProxyType[Any, Any],
new_extra_arg: Any,
) -> bool:
"""Check if the serialized data has changed."""
return old_extra_arg is not None and old_extra_arg != new_extra_arg
@@ -58,7 +266,7 @@ async def async_enable_proactive_mode(hass, smart_home_config):
changed_entity: str,
old_state: State | None,
new_state: State | None,
):
) -> None:
if not hass.is_running:
return
@@ -117,8 +325,13 @@ async def async_enable_proactive_mode(hass, smart_home_config):
async def async_send_changereport_message(
hass, config, alexa_entity, alexa_properties, *, invalidate_access_token=True
):
hass: HomeAssistant,
config: AbstractConfig,
alexa_entity: AlexaEntity,
alexa_properties: list[dict[str, Any]],
*,
invalidate_access_token: bool = True,
) -> None:
"""Send a ChangeReport message for an Alexa entity.
https://developer.amazon.com/docs/smarthome/state-reporting-for-a-smart-home-skill.html#report-state-with-changereport-events
@@ -132,11 +345,11 @@ async def async_send_changereport_message(
)
return
headers = {"Authorization": f"Bearer {token}"}
headers: dict[str, Any] = {"Authorization": f"Bearer {token}"}
endpoint = alexa_entity.alexa_id()
payload = {
payload: dict[str, Any] = {
API_CHANGE: {
"cause": {"type": Cause.APP_INTERACTION},
"properties": alexa_properties,
@@ -149,8 +362,9 @@ async def async_send_changereport_message(
message_serialized = message.serialize()
session = async_get_clientsession(hass)
assert config.endpoint is not None
try:
async with async_timeout.timeout(DEFAULT_TIMEOUT):
async with timeout(DEFAULT_TIMEOUT):
response = await session.post(
config.endpoint,
headers=headers,
@@ -203,9 +417,9 @@ async def async_send_add_or_update_message(
"""
token = await config.async_get_access_token()
headers = {"Authorization": f"Bearer {token}"}
headers: dict[str, Any] = {"Authorization": f"Bearer {token}"}
endpoints = []
endpoints: list[dict[str, Any]] = []
for entity_id in entity_ids:
if (domain := entity_id.split(".", 1)[0]) not in ENTITY_ADAPTERS:
@@ -217,7 +431,10 @@ async def async_send_add_or_update_message(
alexa_entity = ENTITY_ADAPTERS[domain](hass, config, state)
endpoints.append(alexa_entity.serialize_discovery())
payload = {"endpoints": endpoints, "scope": {"type": "BearerToken", "token": token}}
payload: dict[str, Any] = {
"endpoints": endpoints,
"scope": {"type": "BearerToken", "token": token},
}
message = AlexaResponse(
name="AddOrUpdateReport", namespace="Alexa.Discovery", payload=payload
@@ -226,6 +443,7 @@ async def async_send_add_or_update_message(
message_serialized = message.serialize()
session = async_get_clientsession(hass)
assert config.endpoint is not None
return await session.post(
config.endpoint, headers=headers, json=message_serialized, allow_redirects=True
)
@@ -240,9 +458,9 @@ async def async_send_delete_message(
"""
token = await config.async_get_access_token()
headers = {"Authorization": f"Bearer {token}"}
headers: dict[str, Any] = {"Authorization": f"Bearer {token}"}
endpoints = []
endpoints: list[dict[str, Any]] = []
for entity_id in entity_ids:
domain = entity_id.split(".", 1)[0]
@@ -252,7 +470,10 @@ async def async_send_delete_message(
endpoints.append({"endpointId": generate_alexa_id(entity_id)})
payload = {"endpoints": endpoints, "scope": {"type": "BearerToken", "token": token}}
payload: dict[str, Any] = {
"endpoints": endpoints,
"scope": {"type": "BearerToken", "token": token},
}
message = AlexaResponse(
name="DeleteReport", namespace="Alexa.Discovery", payload=payload
@@ -261,19 +482,22 @@ async def async_send_delete_message(
message_serialized = message.serialize()
session = async_get_clientsession(hass)
assert config.endpoint is not None
return await session.post(
config.endpoint, headers=headers, json=message_serialized, allow_redirects=True
)
async def async_send_doorbell_event_message(hass, config, alexa_entity):
async def async_send_doorbell_event_message(
hass: HomeAssistant, config: AbstractConfig, alexa_entity: AlexaEntity
) -> None:
"""Send a DoorbellPress event message for an Alexa entity.
https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-doorbelleventsource.html
"""
token = await config.async_get_access_token()
headers = {"Authorization": f"Bearer {token}"}
headers: dict[str, Any] = {"Authorization": f"Bearer {token}"}
endpoint = alexa_entity.alexa_id()
@@ -291,8 +515,9 @@ async def async_send_doorbell_event_message(hass, config, alexa_entity):
message_serialized = message.serialize()
session = async_get_clientsession(hass)
assert config.endpoint is not None
try:
async with async_timeout.timeout(DEFAULT_TIMEOUT):
async with timeout(DEFAULT_TIMEOUT):
response = await session.post(
config.endpoint,
headers=headers,

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/amazon_polly",
"iot_class": "cloud_push",
"loggers": ["boto3", "botocore", "s3transfer"],
"requirements": ["boto3==1.20.24"]
"requirements": ["boto3==1.28.17"]
}

View File

@@ -24,7 +24,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

View File

@@ -100,7 +100,7 @@ class AmbiclimateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
try:
token_info = await oauth.get_access_token(code)
except ambiclimate.AmbiclimateOauthError:
_LOGGER.error("Failed to get access token", exc_info=True)
_LOGGER.exception("Failed to get access token")
return None
store = Store(self.hass, STORAGE_VERSION, STORAGE_KEY)

View File

@@ -5,7 +5,6 @@ from typing import Any
from aioambient import Websocket
from aioambient.errors import WebsocketError
from aioambient.util import get_public_device_id
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -19,11 +18,7 @@ from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
from homeassistant.helpers.dispatcher import async_dispatcher_send
import homeassistant.helpers.entity_registry as er
from .const import (
@@ -148,6 +143,7 @@ class AmbientStation:
"""Define a handler to fire when the data is received."""
mac = data["macAddress"]
# If data has not changed, don't update:
if data == self.stations[mac][ATTR_LAST_DATA]:
return
@@ -196,71 +192,3 @@ class AmbientStation:
async def ws_disconnect(self) -> None:
"""Disconnect from the websocket."""
await self.websocket.disconnect()
class AmbientWeatherEntity(Entity):
"""Define a base Ambient PWS entity."""
_attr_has_entity_name = True
_attr_should_poll = False
def __init__(
self,
ambient: AmbientStation,
mac_address: str,
station_name: str,
description: EntityDescription,
) -> None:
"""Initialize the entity."""
self._ambient = ambient
public_device_id = get_public_device_id(mac_address)
self._attr_device_info = DeviceInfo(
configuration_url=(
f"https://ambientweather.net/dashboard/{public_device_id}"
),
identifiers={(DOMAIN, mac_address)},
manufacturer="Ambient Weather",
name=station_name.capitalize(),
)
self._attr_unique_id = f"{mac_address}_{description.key}"
self._mac_address = mac_address
self.entity_description = description
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
@callback
def update() -> None:
"""Update the state."""
if self.entity_description.key == TYPE_SOLARRADIATION_LX:
self._attr_available = (
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
TYPE_SOLARRADIATION
]
is not None
)
else:
self._attr_available = (
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
self.entity_description.key
]
is not None
)
self.update_from_latest_data()
self.async_write_ha_state()
self.async_on_remove(
async_dispatcher_connect(
self.hass, f"ambient_station_data_update_{self._mac_address}", update
)
)
self.update_from_latest_data()
@callback
def update_from_latest_data(self) -> None:
"""Update the entity from the latest data."""
raise NotImplementedError

View File

@@ -14,8 +14,8 @@ from homeassistant.const import ATTR_NAME, EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AmbientWeatherEntity
from .const import ATTR_LAST_DATA, DOMAIN
from .entity import AmbientWeatherEntity
TYPE_BATT1 = "batt1"
TYPE_BATT10 = "batt10"
@@ -80,304 +80,303 @@ class AmbientBinarySensorDescription(
BINARY_SENSOR_DESCRIPTIONS = (
AmbientBinarySensorDescription(
key=TYPE_BATTOUT,
name="Battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT1,
name="Battery 1",
translation_key="battery_1",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT2,
name="Battery 2",
translation_key="battery_2",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT3,
name="Battery 3",
translation_key="battery_3",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT4,
name="Battery 4",
translation_key="battery_4",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT5,
name="Battery 5",
translation_key="battery_5",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT6,
name="Battery 6",
translation_key="battery_6",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT7,
name="Battery 7",
translation_key="battery_7",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT8,
name="Battery 8",
translation_key="battery_8",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT9,
name="Battery 9",
translation_key="battery_9",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATTIN,
name="Interior battery",
translation_key="interior_battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT10,
name="Battery 10",
translation_key="battery_10",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK1,
name="Leak detector battery 1",
translation_key="leak_detector_battery_1",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK2,
name="Leak detector battery 2",
translation_key="leak_detector_battery_2",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK3,
name="Leak detector battery 3",
translation_key="leak_detector_battery_3",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LEAK4,
name="Leak detector battery 4",
translation_key="leak_detector_battery_4",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM1,
name="Soil monitor battery 1",
translation_key="soil_monitor_battery_1",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM2,
name="Soil monitor battery 2",
translation_key="soil_monitor_battery_2",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM3,
name="Soil monitor battery 3",
translation_key="soil_monitor_battery_3",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM4,
name="Soil monitor battery 4",
translation_key="soil_monitor_battery_4",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM5,
name="Soil monitor battery 5",
translation_key="soil_monitor_battery_5",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM6,
name="Soil monitor battery 6",
translation_key="soil_monitor_battery_6",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM7,
name="Soil monitor battery 7",
translation_key="soil_monitor_battery_7",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM8,
name="Soil monitor battery 8",
translation_key="soil_monitor_battery_8",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM9,
name="Soil monitor battery 9",
translation_key="soil_monitor_battery_9",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM10,
name="Soil monitor battery 10",
translation_key="soil_monitor_battery_10",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_CO2,
name="CO2 battery",
translation_key="co2_battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LIGHTNING,
name="Lightning detector battery",
translation_key="lightning_detector_battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_LEAK1,
name="Leak detector 1",
translation_key="leak_detector_1",
device_class=BinarySensorDeviceClass.MOISTURE,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_LEAK2,
name="Leak detector 2",
translation_key="leak_detector_2",
device_class=BinarySensorDeviceClass.MOISTURE,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_LEAK3,
name="Leak detector 3",
translation_key="leak_detector_3",
device_class=BinarySensorDeviceClass.MOISTURE,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_LEAK4,
name="Leak detector 4",
translation_key="leak_detector_4",
device_class=BinarySensorDeviceClass.MOISTURE,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_PM25IN_BATT,
name="PM25 indoor battery",
translation_key="pm25_indoor_battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_PM25_BATT,
name="PM25 battery",
translation_key="pm25_battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY1,
name="Relay 1",
translation_key="relay_1",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY2,
name="Relay 2",
translation_key="relay_2",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY3,
name="Relay 3",
translation_key="relay_3",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY4,
name="Relay 4",
translation_key="relay_4",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY5,
name="Relay 5",
translation_key="relay_5",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY6,
name="Relay 6",
translation_key="relay_6",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY7,
name="Relay 7",
translation_key="relay_7",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY8,
name="Relay 8",
translation_key="relay_8",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY9,
name="Relay 9",
translation_key="relay_9",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
),
AmbientBinarySensorDescription(
key=TYPE_RELAY10,
name="Relay 10",
translation_key="relay_10",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=1,
@@ -409,9 +408,6 @@ class AmbientWeatherBinarySensor(AmbientWeatherEntity, BinarySensorEntity):
@callback
def update_from_latest_data(self) -> None:
"""Fetch new state data for the entity."""
self._attr_is_on = (
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
self.entity_description.key
]
== self.entity_description.on_state
)
description = self.entity_description
last_data = self._ambient.stations[self._mac_address][ATTR_LAST_DATA]
self._attr_is_on = last_data[description.key] == description.on_state

View File

@@ -0,0 +1,70 @@
"""Base entity Ambient Weather Station Service."""
from __future__ import annotations
from aioambient.util import get_public_device_id
from homeassistant.core import callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity, EntityDescription
from . import AmbientStation
from .const import ATTR_LAST_DATA, DOMAIN, TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX
class AmbientWeatherEntity(Entity):
"""Define a base Ambient PWS entity."""
_attr_has_entity_name = True
_attr_should_poll = False
def __init__(
self,
ambient: AmbientStation,
mac_address: str,
station_name: str,
description: EntityDescription,
) -> None:
"""Initialize the entity."""
self._ambient = ambient
public_device_id = get_public_device_id(mac_address)
self._attr_device_info = DeviceInfo(
configuration_url=(
f"https://ambientweather.net/dashboard/{public_device_id}"
),
identifiers={(DOMAIN, mac_address)},
manufacturer="Ambient Weather",
name=station_name.capitalize(),
)
self._attr_unique_id = f"{mac_address}_{description.key}"
self._mac_address = mac_address
self.entity_description = description
@callback
def _async_update(self) -> None:
"""Update the state."""
last_data = self._ambient.stations[self._mac_address][ATTR_LAST_DATA]
key = self.entity_description.key
available_key = TYPE_SOLARRADIATION if key == TYPE_SOLARRADIATION_LX else key
self._attr_available = last_data[available_key] is not None
self.update_from_latest_data()
self.async_write_ha_state()
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"ambient_station_data_update_{self._mac_address}",
self._async_update,
)
)
self.update_from_latest_data()
@callback
def update_from_latest_data(self) -> None:
"""Update the entity from the latest data."""
raise NotImplementedError

View File

@@ -28,8 +28,9 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AmbientStation, AmbientWeatherEntity
from . import AmbientStation
from .const import ATTR_LAST_DATA, DOMAIN, TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX
from .entity import AmbientWeatherEntity
TYPE_24HOURRAININ = "24hourrainin"
TYPE_AQI_PM25 = "aqi_pm25"
@@ -113,544 +114,536 @@ TYPE_YEARLYRAININ = "yearlyrainin"
SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_24HOURRAININ,
name="24 hr rain",
translation_key="24_hour_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_AQI_PM25,
name="AQI PM2.5",
translation_key="pm25_aqi",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_AQI_PM25_24H,
name="AQI PM2.5 24h avg",
translation_key="pm25_aqi_24h_average",
device_class=SensorDeviceClass.AQI,
),
SensorEntityDescription(
key=TYPE_AQI_PM25_IN,
name="AQI PM2.5 indoor",
translation_key="pm25_indoor_aqi",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_AQI_PM25_IN_24H,
name="AQI PM2.5 indoor 24h avg",
translation_key="pm25_indoor_aqi_24h_average",
device_class=SensorDeviceClass.AQI,
),
SensorEntityDescription(
key=TYPE_BAROMABSIN,
name="Abs pressure",
translation_key="absolute_pressure",
native_unit_of_measurement=UnitOfPressure.INHG,
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_BAROMRELIN,
name="Rel pressure",
translation_key="relative_pressure",
native_unit_of_measurement=UnitOfPressure.INHG,
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_CO2,
name="CO2",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_DAILYRAININ,
name="Daily rain",
translation_key="daily_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_DEWPOINT,
name="Dew point",
translation_key="dew_point",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_EVENTRAININ,
name="Event rain",
translation_key="event_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL,
),
SensorEntityDescription(
key=TYPE_FEELSLIKE,
name="Feels like",
translation_key="feels_like",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HOURLYRAININ,
name="Hourly rain rate",
native_unit_of_measurement=UnitOfVolumetricFlux.INCHES_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
),
SensorEntityDescription(
key=TYPE_HUMIDITY10,
name="Humidity 10",
translation_key="humidity_10",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY1,
name="Humidity 1",
translation_key="humidity_1",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY2,
name="Humidity 2",
translation_key="humidity_2",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY3,
name="Humidity 3",
translation_key="humidity_3",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY4,
name="Humidity 4",
translation_key="humidity_4",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY5,
name="Humidity 5",
translation_key="humidity_5",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY6,
name="Humidity 6",
translation_key="humidity_6",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY7,
name="Humidity 7",
translation_key="humidity_7",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY8,
name="Humidity 8",
translation_key="humidity_8",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY9,
name="Humidity 9",
translation_key="humidity_9",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY,
name="Humidity",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITYIN,
name="Humidity in",
translation_key="humidity_indoor",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_LASTRAIN,
name="Last rain",
translation_key="last_rain",
icon="mdi:water",
device_class=SensorDeviceClass.TIMESTAMP,
),
SensorEntityDescription(
key=TYPE_LIGHTNING_PER_DAY,
name="Lightning strikes per day",
translation_key="lightning_strikes_per_day",
icon="mdi:lightning-bolt",
native_unit_of_measurement="strikes",
state_class=SensorStateClass.TOTAL,
),
SensorEntityDescription(
key=TYPE_LIGHTNING_PER_HOUR,
name="Lightning strikes per hour",
translation_key="lightning_strikes_per_hour",
icon="mdi:lightning-bolt",
native_unit_of_measurement="strikes",
state_class=SensorStateClass.TOTAL,
),
SensorEntityDescription(
key=TYPE_MAXDAILYGUST,
name="Max gust",
translation_key="max_gust",
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.WIND_SPEED,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_MONTHLYRAININ,
name="Monthly rain",
translation_key="monthly_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL,
),
SensorEntityDescription(
key=TYPE_PM25_24H,
name="PM25 24h avg",
translation_key="pm25_24h_average",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
),
SensorEntityDescription(
key=TYPE_PM25_IN,
name="PM25 indoor",
translation_key="pm25_indoor",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM25_IN_24H,
name="PM25 indoor 24h avg",
translation_key="pm25_indoor_24h_average",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
),
SensorEntityDescription(
key=TYPE_PM25,
name="PM25",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM10,
name="Soil humidity 10",
translation_key="soil_humidity_10",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM1,
name="Soil humidity 1",
translation_key="soil_humidity_1",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM2,
name="Soil humidity 2",
translation_key="soil_humidity_2",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM3,
name="Soil humidity 3",
translation_key="soil_humidity_3",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM4,
name="Soil humidity 4",
translation_key="soil_humidity_4",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM5,
name="Soil humidity 5",
translation_key="soil_humidity_5",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM6,
name="Soil humidity 6",
translation_key="soil_humidity_6",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM7,
name="Soil humidity 7",
translation_key="soil_humidity_7",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM8,
name="Soil humidity 8",
translation_key="soil_humidity_8",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM9,
name="Soil humidity 9",
translation_key="soil_humidity_9",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP10F,
name="Soil temp 10",
translation_key="soil_temperature_10",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP1F,
name="Soil temp 1",
translation_key="soil_temperature_1",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP2F,
name="Soil temp 2",
translation_key="soil_temperature_2",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP3F,
name="Soil temp 3",
translation_key="soil_temperature_3",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP4F,
name="Soil temp 4",
translation_key="soil_temperature_4",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP5F,
name="Soil temp 5",
translation_key="soil_temperature_5",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP6F,
name="Soil temp 6",
translation_key="soil_temperature_6",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP7F,
name="Soil temp 7",
translation_key="soil_temperature_7",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP8F,
name="Soil temp 8",
translation_key="soil_temperature_8",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILTEMP9F,
name="Soil temp 9",
translation_key="soil_temperature_9",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOLARRADIATION,
name="Solar rad",
native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
device_class=SensorDeviceClass.IRRADIANCE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOLARRADIATION_LX,
name="Solar rad",
native_unit_of_measurement=LIGHT_LUX,
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP10F,
name="Temp 10",
translation_key="temperature_10",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP1F,
name="Temp 1",
translation_key="temperature_1",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP2F,
name="Temp 2",
translation_key="temperature_2",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP3F,
name="Temp 3",
translation_key="temperature_3",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP4F,
name="Temp 4",
translation_key="temperature_4",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP5F,
name="Temp 5",
translation_key="temperature_5",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP6F,
name="Temp 6",
translation_key="temperature_6",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP7F,
name="Temp 7",
translation_key="temperature_7",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP8F,
name="Temp 8",
translation_key="temperature_8",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMP9F,
name="Temp 9",
translation_key="temperature_9",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMPF,
name="Temp",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TEMPINF,
name="Inside temp",
translation_key="inside_temperature",
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_TOTALRAININ,
name="Lifetime rain",
translation_key="lifetime_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_UV,
name="UV index",
translation_key="uv_index",
native_unit_of_measurement="Index",
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_WEEKLYRAININ,
name="Weekly rain",
translation_key="weekly_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL,
),
SensorEntityDescription(
key=TYPE_WINDDIR,
name="Wind dir",
translation_key="wind_direction",
icon="mdi:weather-windy",
native_unit_of_measurement=DEGREE,
),
SensorEntityDescription(
key=TYPE_WINDDIR_AVG10M,
name="Wind dir avg 10m",
translation_key="wind_direction_average_10m",
icon="mdi:weather-windy",
native_unit_of_measurement=DEGREE,
),
SensorEntityDescription(
key=TYPE_WINDDIR_AVG2M,
name="Wind dir avg 2m",
translation_key="wind_direction_average_2m",
icon="mdi:weather-windy",
native_unit_of_measurement=DEGREE,
),
SensorEntityDescription(
key=TYPE_WINDGUSTDIR,
name="Gust dir",
translation_key="wind_gust_direction",
icon="mdi:weather-windy",
native_unit_of_measurement=DEGREE,
),
SensorEntityDescription(
key=TYPE_WINDGUSTMPH,
name="Wind gust",
translation_key="wind_gust",
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.WIND_SPEED,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_WINDSPDMPH_AVG10M,
name="Wind avg 10m",
translation_key="wind_average_10m",
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.WIND_SPEED,
),
SensorEntityDescription(
key=TYPE_WINDSPDMPH_AVG2M,
name="Wind avg 2m",
translation_key="wind_average_2m",
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.WIND_SPEED,
),
SensorEntityDescription(
key=TYPE_WINDSPEEDMPH,
name="Wind speed",
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.WIND_SPEED,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_YEARLYRAININ,
name="Yearly rain",
translation_key="yearly_rain",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL_INCREASING,
@@ -694,11 +687,9 @@ class AmbientWeatherSensor(AmbientWeatherEntity, SensorEntity):
@callback
def update_from_latest_data(self) -> None:
"""Fetch new state data for the sensor."""
raw = self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
self.entity_description.key
]
if self.entity_description.key == TYPE_LASTRAIN:
key = self.entity_description.key
raw = self._ambient.stations[self._mac_address][ATTR_LAST_DATA][key]
if key == TYPE_LASTRAIN:
self._attr_native_value = datetime.strptime(raw, "%Y-%m-%dT%H:%M:%S.%f%z")
else:
self._attr_native_value = raw

View File

@@ -16,5 +16,356 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
},
"entity": {
"binary_sensor": {
"battery_1": {
"name": "Battery 1"
},
"battery_2": {
"name": "Battery 2"
},
"battery_3": {
"name": "Battery 3"
},
"battery_4": {
"name": "Battery 4"
},
"battery_5": {
"name": "Battery 5"
},
"battery_6": {
"name": "Battery 6"
},
"battery_7": {
"name": "Battery 7"
},
"battery_8": {
"name": "Battery 8"
},
"battery_9": {
"name": "Battery 9"
},
"battery_10": {
"name": "Battery 10"
},
"interior_battery": {
"name": "Interior battery"
},
"leak_detector_battery_1": {
"name": "Leak detector battery 1"
},
"leak_detector_battery_2": {
"name": "Leak detector battery 2"
},
"leak_detector_battery_3": {
"name": "Leak detector battery 3"
},
"leak_detector_battery_4": {
"name": "Leak detector battery 4"
},
"soil_monitor_battery_1": {
"name": "Soil monitor battery 1"
},
"soil_monitor_battery_2": {
"name": "Soil monitor battery 2"
},
"soil_monitor_battery_3": {
"name": "Soil monitor battery 3"
},
"soil_monitor_battery_4": {
"name": "Soil monitor battery 4"
},
"soil_monitor_battery_5": {
"name": "Soil monitor battery 5"
},
"soil_monitor_battery_6": {
"name": "Soil monitor battery 6"
},
"soil_monitor_battery_7": {
"name": "Soil monitor battery 7"
},
"soil_monitor_battery_8": {
"name": "Soil monitor battery 8"
},
"soil_monitor_battery_9": {
"name": "Soil monitor battery 9"
},
"soil_monitor_battery_10": {
"name": "Soil monitor battery 10"
},
"co2_battery": {
"name": "Carbon dioxide battery"
},
"lightning_detector_battery": {
"name": "Lightning detector battery"
},
"leak_detector_1": {
"name": "Leak detector 1"
},
"leak_detector_2": {
"name": "Leak detector 2"
},
"leak_detector_3": {
"name": "Leak detector 3"
},
"leak_detector_4": {
"name": "Leak detector 4"
},
"pm25_indoor_battery": {
"name": "PM25 indoor battery"
},
"pm25_battery": {
"name": "PM25 battery"
},
"relay_1": {
"name": "Relay 1"
},
"relay_2": {
"name": "Relay 2"
},
"relay_3": {
"name": "Relay 3"
},
"relay_4": {
"name": "Relay 4"
},
"relay_5": {
"name": "Relay 5"
},
"relay_6": {
"name": "Relay 6"
},
"relay_7": {
"name": "Relay 7"
},
"relay_8": {
"name": "Relay 8"
},
"relay_9": {
"name": "Relay 9"
},
"relay_10": {
"name": "Relay 10"
}
},
"sensor": {
"24_hour_rain": {
"name": "Rain 24 hours"
},
"pm25_aqi": {
"name": "PM2.5 AQI"
},
"pm25_aqi_24h_average": {
"name": "PM2.5 AQI 24 hour average"
},
"pm25_indoor_aqi": {
"name": "PM2.5 indoor AQI"
},
"pm25_indoor_aqi_24h_average": {
"name": "PM2.5 indoor AQI"
},
"absolute_pressure": {
"name": "Absolute pressure"
},
"relative_pressure": {
"name": "Relative pressure"
},
"daily_rain": {
"name": "Daily rain"
},
"dew_point": {
"name": "Dew point"
},
"event_rain": {
"name": "Event rain"
},
"feels_like": {
"name": "Feels like"
},
"humidity_1": {
"name": "Humidity 1"
},
"humidity_2": {
"name": "Humidity 2"
},
"humidity_3": {
"name": "Humidity 3"
},
"humidity_4": {
"name": "Humidity 4"
},
"humidity_5": {
"name": "Humidity 5"
},
"humidity_6": {
"name": "Humidity 6"
},
"humidity_7": {
"name": "Humidity 7"
},
"humidity_8": {
"name": "Humidity 8"
},
"humidity_9": {
"name": "Humidity 9"
},
"humidity_10": {
"name": "Humidity 10"
},
"humidity_indoor": {
"name": "Humidity indoor"
},
"last_rain": {
"name": "Last rain"
},
"lightning_strikes_per_day": {
"name": "Lightning strikes per day"
},
"lightning_strikes_per_hour": {
"name": "Lightning strikes per hour"
},
"max_gust": {
"name": "Max gust"
},
"monthly_rain": {
"name": "Monthly rain"
},
"pm25_24h_average": {
"name": "PM2.5 24 hour average"
},
"pm25_indoor": {
"name": "PM2.5 indoor"
},
"pm25_indoor_24h_average": {
"name": "PM2.5 indoor 24 hour average"
},
"soil_humidity_1": {
"name": "Soil humidity 1"
},
"soil_humidity_2": {
"name": "Soil humidity 2"
},
"soil_humidity_3": {
"name": "Soil humidity 3"
},
"soil_humidity_4": {
"name": "Soil humidity 4"
},
"soil_humidity_5": {
"name": "Soil humidity 5"
},
"soil_humidity_6": {
"name": "Soil humidity 6"
},
"soil_humidity_7": {
"name": "Soil humidity 7"
},
"soil_humidity_8": {
"name": "Soil humidity 8"
},
"soil_humidity_9": {
"name": "Soil humidity 9"
},
"soil_humidity_10": {
"name": "Soil humidity 10"
},
"soil_temperature_1": {
"name": "Soil temperature 1"
},
"soil_temperature_2": {
"name": "Soil temperature 2"
},
"soil_temperature_3": {
"name": "Soil temperature 3"
},
"soil_temperature_4": {
"name": "Soil temperature 4"
},
"soil_temperature_5": {
"name": "Soil temperature 5"
},
"soil_temperature_6": {
"name": "Soil temperature 6"
},
"soil_temperature_7": {
"name": "Soil temperature 7"
},
"soil_temperature_8": {
"name": "Soil temperature 8"
},
"soil_temperature_9": {
"name": "Soil temperature 9"
},
"soil_temperature_10": {
"name": "Soil temperature 10"
},
"temperature_1": {
"name": "Temperature 1"
},
"temperature_2": {
"name": "Temperature 2"
},
"temperature_3": {
"name": "Temperature 3"
},
"temperature_4": {
"name": "Temperature 4"
},
"temperature_5": {
"name": "Temperature 5"
},
"temperature_6": {
"name": "Temperature 6"
},
"temperature_7": {
"name": "Temperature 7"
},
"temperature_8": {
"name": "Temperature 8"
},
"temperature_9": {
"name": "Temperature 9"
},
"temperature_10": {
"name": "Temperature 10"
},
"inside_temperature": {
"name": "Inside temperature"
},
"lifetime_rain": {
"name": "Lifetime rain"
},
"uv_index": {
"name": "UV index"
},
"weekly_rain": {
"name": "Weekly rain"
},
"wind_direction": {
"name": "Wind direction"
},
"wind_direction_average_10m": {
"name": "Wind direction average 10 minutes"
},
"wind_direction_average_2m": {
"name": "Wind direction average 2 minutes"
},
"wind_gust_direction": {
"name": "Wind gust direction"
},
"wind_gust": {
"name": "Wind gust"
},
"wind_average_10m": {
"name": "Wind average 10 minutes"
},
"wind_average_2m": {
"name": "Wind average 2 minutes"
},
"yearly_rain": {
"name": "Yearly rain"
}
}
}
}

View File

@@ -2,13 +2,13 @@
from __future__ import annotations
import asyncio
from asyncio import timeout
from dataclasses import asdict as dataclass_asdict, dataclass
from datetime import datetime
from typing import Any
import uuid
import aiohttp
import async_timeout
from homeassistant.components import hassio
from homeassistant.components.api import ATTR_INSTALLATION_TYPE
@@ -22,9 +22,7 @@ from homeassistant.components.recorder import (
get_instance as get_recorder_instance,
)
import homeassistant.config as conf_util
from homeassistant.config_entries import (
SOURCE_IGNORE,
)
from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
@@ -315,7 +313,7 @@ class Analytics:
)
try:
async with async_timeout.timeout(30):
async with timeout(30):
response = await self.session.post(self.endpoint, json=payload)
if response.status == 200:
LOGGER.info(

View File

@@ -11,7 +11,7 @@ from homeassistant.const import (
HTTP_BASIC_AUTHENTICATION,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN

View File

@@ -1,7 +1,7 @@
"""Base class for Android IP Webcam entities."""
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN

View File

@@ -32,9 +32,8 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import Throttle

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
from asyncio import timeout
import logging
from androidtvremote2 import (
@@ -10,7 +11,6 @@ from androidtvremote2 import (
ConnectionClosed,
InvalidAuth,
)
import async_timeout
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP, Platform
@@ -45,7 +45,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api.add_is_available_updated_callback(is_available_updated)
try:
async with async_timeout.timeout(5.0):
async with timeout(5.0):
await api.async_connect()
except InvalidAuth as exc:
# The Android TV is hard reset or the certificate and key files were deleted.

View File

@@ -12,8 +12,12 @@ from androidtvremote2 import (
)
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
@@ -35,7 +39,7 @@ STEP_PAIR_DATA_SCHEMA = vol.Schema(
)
class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Android TV Remote."""
VERSION = 1
@@ -43,7 +47,7 @@ class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self) -> None:
"""Initialize a new AndroidTVRemoteConfigFlow."""
self.api: AndroidTVRemote | None = None
self.reauth_entry: config_entries.ConfigEntry | None = None
self.reauth_entry: ConfigEntry | None = None
self.host: str | None = None
self.name: str | None = None
self.mac: str | None = None
@@ -192,19 +196,15 @@ class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:
config_entry: ConfigEntry,
) -> AndroidTVRemoteOptionsFlowHandler:
"""Create the options flow."""
return OptionsFlowHandler(config_entry)
return AndroidTVRemoteOptionsFlowHandler(config_entry)
class OptionsFlowHandler(config_entries.OptionsFlow):
class AndroidTVRemoteOptionsFlowHandler(OptionsFlowWithConfigEntry):
"""Android TV Remote options flow."""
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:

View File

@@ -7,8 +7,8 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity import Entity
from .const import DOMAIN

View File

@@ -8,6 +8,6 @@
"iot_class": "local_push",
"loggers": ["androidtvremote2"],
"quality_scale": "platinum",
"requirements": ["androidtvremote2==0.0.13"],
"requirements": ["androidtvremote2==0.0.14"],
"zeroconf": ["_androidtvremote2._tcp.local."]
}

View File

@@ -6,7 +6,7 @@ import logging
from anova_wifi import AnovaApi, AnovaPrecisionCooker, InvalidLogin, NoDevicesFound
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.const import CONF_DEVICES, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
@@ -42,7 +42,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
device[1],
api.jwt,
)
for device in entry.data["devices"]
for device in entry.data[CONF_DEVICES]
]
try:
new_devices = await api.get_devices()
@@ -55,7 +55,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry,
data={
**entry.data,
**{"devices": serialize_device_list(devices)},
**{CONF_DEVICES: serialize_device_list(devices)},
},
)
coordinators = [AnovaCoordinator(hass, device) for device in devices]

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