Compare commits

..

174 Commits

Author SHA1 Message Date
Paulus Schoutsen
ca5a88342b 2023.3.6 (#90150) 2023-03-22 23:58:20 -04:00
Paulus Schoutsen
117113cdfc Bumped version to 2023.3.6 2023-03-22 22:59:47 -04:00
Paulus Schoutsen
174342860b Always enforce URL param ordering for signed URLs (#90148)
Always enforce URL param ordering
2023-03-22 22:59:42 -04:00
J. Nick Koston
a7b5a0297e Bump PySwitchbot to 0.37.4 (#90146)
fixes #90090 fixes #89061

changelog: https://github.com/Danielhiversen/pySwitchbot/compare/0.37.3...0.37.4
2023-03-22 22:59:41 -04:00
Luke
406e92511b Bump to oralb-ble 0.17.6 (#90081) 2023-03-22 22:59:40 -04:00
Klaas Schoute
146347e31a Bump easyEnergy to v0.2.2 (#90080) 2023-03-22 22:59:39 -04:00
Klaas Schoute
3747fd5dcb Bump easyEnergy to v0.2.1 (#89630) 2023-03-22 22:59:38 -04:00
J. Nick Koston
53d400ca96 Bump yalexs-ble to 2.1.1 (#90015)
* Bump yalexs-ble to 2.1.1

There was another task that could be prematurely GCed

changelog: https://github.com/bdraco/yalexs-ble/compare/v2.1.0...v2.1.1

* fixes
2023-03-22 22:57:41 -04:00
J. Nick Koston
2a18261efb Bump yalexs_ble to 2.1.0 (#89772)
switches to using cryptography to reduce the number of deps

changelog: https://github.com/bdraco/yalexs-ble/compare/v2.0.4...v2.1.0
2023-03-22 22:57:40 -04:00
J. Nick Koston
1f71068740 Handle cancelation of wait_for_ble_connections_free in esphome bluetooth (#90014)
Handle cancelation in wait_for_ble_connections_free

If `wait_for_ble_connections_free` was canceled due to timeout or
the esp disconnecting from Home Assistant the future would get
canceled. When we reconnect and get the next callback we need
to handle it being done.

fixes
```
2023-03-21 02:34:36.876 ERROR (MainThread) [homeassistant] Error doing job: Fatal error: protocol.data_received() call failed.
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/asyncio/selector_events.py", line 868, in _read_ready__data_received
    self._protocol.data_received(data)
  File "/usr/local/lib/python3.10/site-packages/aioesphomeapi/_frame_helper.py", line 195, in data_received
    self._callback_packet(msg_type_int, bytes(packet_data))
  File "/usr/local/lib/python3.10/site-packages/aioesphomeapi/_frame_helper.py", line 110, in _callback_packet
    self._on_pkt(Packet(type_, data))
  File "/usr/local/lib/python3.10/site-packages/aioesphomeapi/connection.py", line 688, in _process_packet
    handler(msg)
  File "/usr/local/lib/python3.10/site-packages/aioesphomeapi/client.py", line 482, in on_msg
    on_bluetooth_connections_free_update(resp.free, resp.limit)
  File "/usr/src/homeassistant/homeassistant/components/esphome/entry_data.py", line 136, in async_update_ble_connection_limits
    fut.set_result(free)
asyncio.exceptions.InvalidStateError: invalid state
```
2023-03-22 22:54:42 -04:00
micha91
92fb978a03 Bump aiomusiccast to 0.14.8 (#89978) 2023-03-22 22:54:42 -04:00
J. Nick Koston
127f2289a1 Remove async_block_till_done in freebox (#89928)
async_block_till_done() is not meant to be called in integrations
2023-03-22 22:54:41 -04:00
Jan Bouwhuis
de6f55dcfb Fix blocking MQTT entry unload (#89922)
* Remove unneeded async_block_till_done

* use await asyncio.sleep(0) instead
2023-03-22 22:54:40 -04:00
Joakim Plate
713d3025f2 Correct missing wordswap for S series nibe (#89866)
Correct missing wordswap for nibe
2023-03-22 22:54:39 -04:00
J. Nick Koston
1e03ff68a2 Bump aioharmony to 0.2.10 (#89831)
fixes #89823
2023-03-22 22:54:38 -04:00
Jan Bouwhuis
a5aa5c0c01 Fix imap_email_content unknown status and replaying stale states (#89563) 2023-03-22 22:54:37 -04:00
Franck Nijhof
b6d001bfe6 2023.3.5 (#89814) 2023-03-16 20:45:14 +01:00
Franck Nijhof
7e18e15cac Bumped version to 2023.3.5 2023-03-16 18:48:17 +01:00
Bram Kragten
e651ca747b Update frontend to 20230309.1 (#89802) 2023-03-16 18:47:51 +01:00
J. Nick Koston
9fa73fe3a9 Bump aioesphomeapi to 13.5.1 (#89777) 2023-03-16 18:47:47 +01:00
Jan Bouwhuis
abda7b8a5b Fix imap server push holding HA startup (#89750) 2023-03-16 18:47:44 +01:00
jan iversen
90a4afb6fa Correct modbus serial method parameter (#89738) 2023-03-16 18:47:40 +01:00
Marcio Granzotto Rodrigues
52981699cf Bump bond-async to 0.1.23 (#89697) 2023-03-16 18:47:37 +01:00
Joakim Plate
c3d7696c2e Update to nibe 2.1.4 (#89686) 2023-03-16 18:47:33 +01:00
jan iversen
f120bac17f Secure modbus hub_collect remains valid (#89684)
Secure hub_collect remains valid.
2023-03-16 18:47:28 +01:00
Joakim Plate
02738fb9d4 Handle int or mapping for off case in nibe cooling (#89680)
Handle int or mapping for off case in nibe
2023-03-16 18:47:25 +01:00
J. Nick Koston
a9a6ff50cc Bump aioesphomeapi to 13.5.0 (#89262) 2023-03-16 18:47:21 +01:00
zhangshengdong29
fdd9c5383f ArestData does not have available (#88631) 2023-03-16 18:47:17 +01:00
Paulus Schoutsen
d084e70aff 2023.3.4 (#89647) 2023-03-14 00:10:23 -04:00
puddly
69582b7ecb Bump ZHA dependencies (#89667)
* Bump `zha-quirks` library and account for `setup_quirks` signature

* Bump other ZHA dependencies

* Revert zigpy bump
2023-03-13 22:06:05 -04:00
Paulus Schoutsen
160518350f Bump SQLAlchemy to 2.0.6 (#89650) 2023-03-13 14:54:27 -04:00
Paulus Schoutsen
daa5718a80 Bumped version to 2023.3.4 2023-03-13 13:26:50 -04:00
tomrennen
f5562e93ac Improved "ON" state check for Use room sensor for cooling (#89634) 2023-03-13 13:26:44 -04:00
Erik Montnemery
d2f90236d1 Rename modules named repairs.py which are not repairs platforms (#89618) 2023-03-13 13:26:43 -04:00
J. Nick Koston
65c614421a Increase maximum aiohttp connections to 4096 (#89611)
fixes #89408
2023-03-13 13:26:41 -04:00
Eugenio Panadero
22922da607 Bump aiopvpc to 4.1.0 (#89593) 2023-03-13 13:26:40 -04:00
J. Nick Koston
ca0304ffc4 Fix get_significant_states_with_session query looking at legacy columns (#89558) 2023-03-13 13:26:39 -04:00
Robert Svensson
950a1f6e9e Bump pydeconz to v110 (#89527)
* Bump pydeconz to v109

* Bump pydeconz to v110 for additional color modes
2023-03-13 13:26:38 -04:00
rappenze
1e7f58d859 Fix bug in fibaro cover (#89502) 2023-03-13 13:26:37 -04:00
J. Nick Koston
7cb4620671 Fix data migration never finishing when database has invalid datetimes (#89474)
* Fix data migration never finishing when database has invalid datetimes

If there were impossible datetime values in the database (likely
from a manual sqlite to MySQL conversion) the conversion would
never complete

* Update homeassistant/components/recorder/migration.py
2023-03-13 13:26:36 -04:00
Kevin Worrel
8c2569d2ce Reconnect on any ScreenLogic exception (#89269)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-03-13 13:26:34 -04:00
Arjan
6ebd493c4d Fix gtfs with 2023.3 (sqlachemy update) (#89175) 2023-03-13 13:26:33 -04:00
Jan Stienstra
990ecbba72 Recode Home Assistant instance name to ascii for Jellyfin (#87368)
Recode instance name to ascii
2023-03-13 13:26:32 -04:00
Paulus Schoutsen
ddde17606d 2023.3.3 (#89459) 2023-03-09 14:40:06 -05:00
Paulus Schoutsen
3fba181e7b Bumped version to 2023.3.3 2023-03-09 13:30:46 -05:00
Erik Montnemery
da79bf8534 Fix Dormakaba dKey deadbolt binary sensor (#89447)
* Fix Dormakaba dKey deadbolt binary sensor

* Spelling
2023-03-09 13:18:23 -05:00
Paul Bottein
83e2cc32b7 Update frontend to 20230309.0 (#89446) 2023-03-09 13:18:22 -05:00
Joakim Sørensen
c7fb404a17 Add paths for add-on changelog and documentation (#89411) 2023-03-09 13:18:21 -05:00
Jan Bouwhuis
f1e114380a Allow enum as MQTT sensor device_class (#89391) 2023-03-09 13:18:20 -05:00
Brandon Rothweiler
04e4a644cb Bump pymazda to 0.3.8 (#89387) 2023-03-09 13:18:19 -05:00
Dillon Fearns
e606c2e227 Bump roombapy to 1.6.6 (#89366)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-03-09 13:18:17 -05:00
Jan Bouwhuis
ebf95feff3 Fix MQTT rgb light brightness scaling (#89264)
* Normalize received RGB colors to 100% brightness

* Assert on rgb_color attribute

* Use max for RGB to get brightness

* Avoid division and add clamp

* remove clamp

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

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2023-03-09 13:18:15 -05:00
Franck Nijhof
3dca4c2f23 2023.3.2 (#89381) 2023-03-08 18:35:50 +01:00
Franck Nijhof
3f8f38f2df Bumped version to 2023.3.2 2023-03-08 16:24:08 +01:00
epenet
0844a0b269 Fix invalid state class in litterrobot (#89380) 2023-03-08 16:23:30 +01:00
Franck Nijhof
b65180d20a Improve Supervisor API handling (#89379) 2023-03-08 16:23:26 +01:00
starkillerOG
7f8a9697f0 Fix setting Reolink focus (#89374)
fix setting focus
2023-03-08 16:23:22 +01:00
J. Nick Koston
563bd4a0dd Fix bluetooth history and device expire running in the executor (#89342) 2023-03-08 16:23:18 +01:00
Florent Thoumie
29b5ef31c1 Recreate iaqualink httpx client upon service exception (#89341) 2023-03-08 16:23:13 +01:00
Renat Sibgatulin
863f8b727d Remove invalid device class in air-Q integration (#89329)
Remove device_class from sensors using inconsistent units
2023-03-08 16:23:09 +01:00
J. Nick Koston
83ed8cf689 Fix thread diagnostics loading blocking the event loop (#89307)
* Fix thread diagnostics loading blocking the event loop

* patch target
2023-03-08 16:23:06 +01:00
Tom Harris
52cd2f9429 Fix Insteon open issues with adding devices by address and missing events (#89305)
* Add missing events

* Bump dependancies

* Update for code review
2023-03-08 16:23:02 +01:00
puddly
74d3b2374b Clean ZHA radio path with trailing whitespace (#89299)
* Clean config flow entries with trailing whitespace

* Rewrite the config entry at runtime, without upgrading

* Skip intermediate `data = config_entry.data` variable

* Perform a deepcopy to ensure the config entry will actually be updated
2023-03-08 16:22:58 +01:00
epenet
f982af2412 Ignore DSL entities if SFR box is not adsl (#89291) 2023-03-08 16:22:53 +01:00
luar123
0b5ddd9cbf Bump python-snapcast to 2.3.2 (#89259) 2023-03-08 16:22:49 +01:00
J. Nick Koston
8d1aa0132e Make sql subqueries threadsafe (#89254)
* Make sql subqueries threadsafe

fixes #89224

* fix join outside of lambda

* move statement generation into a seperate function to make it easier to test

* add cache key tests

* no need to mock hass
2023-03-08 16:22:45 +01:00
J. Nick Koston
d737b97c91 Bump sqlalchemy to 2.0.5post1 (#89253)
changelog: https://docs.sqlalchemy.org/en/20/changelog/changelog_20.html#change-2.0.5

mostly bugfixes for 2.x regressions
2023-03-08 16:22:41 +01:00
Marc Mueller
0fac12866d Fix conditional check (#89231) 2023-03-08 16:22:38 +01:00
Bram Kragten
e3fe71f76e Update frontend to 20230306.0 (#89227) 2023-03-08 16:22:34 +01:00
J. Nick Koston
eba1bfad51 Bump aioesphomeapi to 13.4.2 (#89210) 2023-03-08 16:22:30 +01:00
Franck Nijhof
1a0a385e03 Fix Tuya Python 3.11 compatibility issue (#89189) 2023-03-08 16:22:26 +01:00
MarkGodwin
c9999cd08c Fix host IP and scheme entry issues in TP-Link Omada (#89130)
Fixing host IP and scheme entry issues
2023-03-08 16:22:22 +01:00
rappenze
8252aeead2 Bump pyfibaro version to 0.6.9 (#89120) 2023-03-08 16:22:18 +01:00
J. Nick Koston
c27a69ef85 Handle InnoDB deadlocks during migration (#89073)
* Handle slow InnoDB rollback when encountering duplicates during migration

fixes #89069

* adjust

* fix mock

* tests

* return on success
2023-03-08 16:22:15 +01:00
J. Nick Koston
d4c28a1f4a Cache transient templates compiles provided via api (#89065)
* Cache transient templates compiles provided via api

partially fixes #89047 (there is more going on here)

* add a bit more coverage just to be sure

* switch method

* Revert "switch method"

This reverts commit 0e9e1c8cbe8753159f4fd6775cdc9cf217d66f0e.

* tweak

* hold hass

* empty for github flakey
2023-03-08 16:22:10 +01:00
Andrew Westrope
322eb4bd83 Check type key of zone exists in geniushub (#86798)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2023-03-08 16:22:05 +01:00
Paulus Schoutsen
f0f12fd14a 2023.3.1 (#89059) 2023-03-02 15:53:50 -05:00
Mitch
1836e35717 Bump nuheat to 1.0.1 (#88958) 2023-03-02 15:15:15 -05:00
Paulus Schoutsen
4eb55146be Bumped version to 2023.3.1 2023-03-02 14:22:23 -05:00
Jan Bouwhuis
b1ee6e304e Fix check on non numeric custom sensor device classes (#89052)
* Custom device classes are not numeric

* Update homeassistant/components/sensor/__init__.py

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Add test

* Update homeassistant/components/sensor/__init__.py

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

---------

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2023-03-02 14:22:12 -05:00
Paul Bottein
d0b195516b Update frontend to 20230302.0 (#89042) 2023-03-02 14:22:11 -05:00
Franck Nijhof
a867f1d3c8 Update orjson to 3.8.7 (#89037) 2023-03-02 14:22:09 -05:00
Matthias Alphart
f7eaeb7a39 Fix KNX Keyfile upload (#89029)
* Fix KNX Keyfile upload

* use shutil.move instead
2023-03-02 14:22:08 -05:00
Erik Montnemery
3e961d3e17 Bump py-dormakaba-dkey to 1.0.4 (#88992) 2023-03-02 14:22:07 -05:00
Mitch
c28e16fa8b Bump requests to 2.28.2 (#88956)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-03-02 14:22:06 -05:00
Toni Juvani
e2e8d74aa6 Update pyTibber to 0.27.0 (#86940)
* Update pyTibber to 0.27.0

* Handle new exceptions
2023-03-02 14:22:05 -05:00
Franck Nijhof
8a9fbd650a 2023.3.0 (#88979) 2023-03-01 19:53:46 +01:00
Erik Montnemery
243725efe3 Tweak OTBR tests (#88839) 2023-03-01 17:53:38 +01:00
Franck Nijhof
8d59489da8 Bumped version to 2023.3.0 2023-03-01 17:25:44 +01:00
Stefan Agner
c146413a1a Add Home Assistant with space as brand (#88976) 2023-03-01 17:25:08 +01:00
Bram Kragten
a46d63a11b Update frontend to 20230301.0 (#88975) 2023-03-01 17:25:05 +01:00
mkmer
db4f6fb94d Bump Aiosomecomfort to 0.0.11 (#88970) 2023-03-01 17:25:01 +01:00
Erik Montnemery
c50c920589 Revert "Add state_class = MEASUREMENT to Derivative sensor (#88408)" (#88952) 2023-03-01 17:24:56 +01:00
starkillerOG
fe22aa0b4b Motion Blinds DHCP restrict (#88919)
Co-authored-by: J. Nick Koston <nick@koston.org>
2023-03-01 17:23:00 +01:00
Aaron Godfrey
a0162e4986 Fix todoist filtering custom projects by labels (#87904)
* Fix filtering custom projects by labels.

* Don't lowercase the label.

* Labels are case-sensitive, don't lowercase them.
2023-03-01 17:22:56 +01:00
RogerSelwyn
62c5cf51f5 Fix geniushub heating hvac action (#87531) 2023-03-01 17:22:53 +01:00
Frédéric Guardia
89aebba3ab Fix Google Assistant temperature attribute (#85921) 2023-03-01 17:22:48 +01:00
Paulus Schoutsen
6c73b9024b Bumped version to 2023.3.0b7 2023-02-28 22:18:39 -05:00
Michael Hansen
59a9ace171 Update intent sentences package (#88933)
* Actually use translated state names in response

* Change test result now that locks are excluded from HassTurnOn

* Bump home-assistant-intents and hassil versions
2023-02-28 22:18:32 -05:00
PatrickGlesner
e751948bc8 Update Tado services.yaml defaults (#88929)
Update services.yaml

Deletes default values in 'time_period' and 'requested_overlay' fields in 'set_climate_timer'.
2023-02-28 22:18:31 -05:00
djtimca
702646427d Bump auroranoaa to 0.0.3 (#88927)
* Bump aurora_api version to fix issues with NOAA conversion values. Fix #82587

* update requirements for aurora.

* Add state_class to aurora sensor.

* Fixed environment to run requirements_all script.

* Revert "Add state_class to aurora sensor."

This reverts commit 213e21e8424aafd50242e77bcedc39f0a4b50074.
2023-02-28 22:18:30 -05:00
Tom Harris
8a605b1377 Bump pyinsteon to 1.3.3 (#88925)
Bump pyinsteon
2023-02-28 22:18:29 -05:00
Erik Montnemery
8eb8415d3f Bump py-dormakaba-dkey to 1.0.3 (#88924)
* Bump py-dormakaba-dkey to 1.0.3

* Log unexpected errors in config flow
2023-02-28 22:18:28 -05:00
Volker Stolz
9f3f71d0c3 Introduce a UUID configuration option for API token (#88765)
* Introduce a UUID configuration option for API token. (#86547)

If the uuid is configured, it will be used in the HTTP headers. Otherwise,
we'll hash the salted instance URL which should be good enough(tm).

* Generate random 6-digit uuid on startup.
2023-02-28 22:18:28 -05:00
Paulus Schoutsen
b82da9418d Bumped version to 2023.3.0b6 2023-02-28 12:13:24 -05:00
Erik Montnemery
38cf725075 Fix Dormakaba dKey binary sensor (#88922) 2023-02-28 12:12:52 -05:00
Franck Nijhof
04cedab8d4 Small improvements to middleware filter (#88921)
Small improvements middleware filter
2023-02-28 12:12:51 -05:00
Erik Montnemery
2238a3f201 Reset state of template cover on error (#88915) 2023-02-28 12:12:50 -05:00
Marcel van der Veldt
f58ca17926 Bump aiohue library to version 4.6.2 (#88907)
* Bump aiohue library to 4.6.2

* Fix long press (fixed in aiohue lib)

* fix test
2023-02-28 12:12:48 -05:00
Marcel van der Veldt
d5e517b874 Do not create Area for Hue zones (#88904)
Do not create HA area for Hue zones
2023-02-28 12:12:47 -05:00
Bram Kragten
f9eeb4f4d8 Fix string for OTBR config flow abort (#88902) 2023-02-28 12:12:46 -05:00
Marcel van der Veldt
86d5e4aaa8 Fix removal of non device-bound resources in Hue (#88897)
Fix removal of non device-bound resources (like entertainment areas)
2023-02-28 12:12:45 -05:00
b-uwe
a56935ed7c Add virtual integration for HELTUN (#88892) 2023-02-28 12:12:44 -05:00
Erik Montnemery
fc56c958c3 Only allow channel 15 during configuration of OTBR (#88874)
* Only allow channel 15 during automatic configuration of OTBR

* Also force channel 15 when creating a new network
2023-02-28 12:12:43 -05:00
Erik Montnemery
a8e1dc8962 Create repairs issue if Thread network is insecure (#88888)
* Bump python-otbr-api to 1.0.5

* Create repairs issue if Thread network is insecure

* Address review comments
2023-02-28 12:12:11 -05:00
Erik Montnemery
32b138b6c6 Add WS API for creating a Thread network (#88830)
* Add WS API for creating a Thread network

* Add tests
2023-02-28 12:11:14 -05:00
Erik Montnemery
2112c66804 Add confirm step to thread zeroconf flow (#88869)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-02-28 12:08:18 -05:00
Paulus Schoutsen
72c0526d87 Bumped version to 2023.3.0b5 2023-02-27 20:58:22 -05:00
Matthias Alphart
9ed4e01e94 Update xknx to 2.6.0 (#88864) 2023-02-27 20:58:11 -05:00
Paul Bottein
dcf1ecfeb5 Update frontend to 20230227.0 (#88857) 2023-02-27 20:58:10 -05:00
Klaas Schoute
b72224ceff Bump odp-amsterdam to v5.1.0 (#88847) 2023-02-27 20:58:09 -05:00
Erik Montnemery
96ad5c9666 Add thread user flow (#88842) 2023-02-27 20:58:09 -05:00
Erik Montnemery
00b59c142a Fix sensor unit conversion bug (#88825)
* Fix sensor unit conversion bug

* Ensure the correct unit is stored in the entity registry
2023-02-27 20:58:08 -05:00
Michael Davie
b054c81e13 Bump env_canada to 0.5.29 (#88821) 2023-02-27 20:58:07 -05:00
puddly
b0cbcad440 Bump ZHA dependencies (#88799)
* Bump ZHA dependencies

* Use `importlib.metadata.version` to get package versions
2023-02-27 20:58:06 -05:00
stickpin
bafe552af6 Upgrade caldav to 1.2.0 (#88791) 2023-02-27 20:58:05 -05:00
stickpin
d399855e50 Upgrade caldav to 1.1.3 (#88681)
* Update caldav to 1.1.3

* update caldav to 1.1.3

* update caldav to 1.1.3

---------

Co-authored-by: Allen Porter <allen@thebends.org>
2023-02-27 20:58:03 -05:00
mkmer
d26f430766 Bump aiosomecomfort to 0.0.10 (#88766) 2023-02-27 20:56:46 -05:00
Erik Montnemery
f2e4943a53 Catch CancelledError when setting up components (#88635)
* Catch CancelledError when setting up components

* Catch CancelledError when setting up components

* Also catch SystemExit
2023-02-27 20:56:45 -05:00
Bouwe Westerdijk
6512cd901f Correct Plugwise gas_consumed_interval sensor (#87449)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2023-02-27 20:56:45 -05:00
Paulus Schoutsen
fbe1524f6c Bumped version to 2023.3.0b4 2023-02-26 22:37:34 -05:00
J. Nick Koston
95e337277c Avoid starting a bluetooth poll when Home Assistant is stopping (#88819)
* Avoid starting a bluetooth poll when Home Assistant is stopping

* tests
2023-02-26 22:37:26 -05:00
J. Nick Koston
1503674bd6 Prevent integrations from retrying setup once shutdown has started (#88818)
* Prevent integrations from retrying setup once shutdown has started

* coverage
2023-02-26 22:37:25 -05:00
J. Nick Koston
ab6bd75b70 Fix flux_led discovery running at shutdown (#88817) 2023-02-26 22:37:24 -05:00
J. Nick Koston
2fff836bd4 Fix lock services not removing entity fields (#88805) 2023-02-26 22:37:23 -05:00
J. Nick Koston
d8850758f1 Fix unifiprotect discovery running at shutdown (#88802)
* Fix unifiprotect discovery running at shutdown

Move the discovery start into `async_setup` so we only
start discovery once reguardless of how many config entries
for unifiprotect they have (or how many times they reload).

Always make discovery a background task so it does not get
to block shutdown

* missing decorator
2023-02-26 22:37:22 -05:00
J. Nick Koston
0449856064 Bump yalexs-ble to 2.0.4 (#88798)
changelog: https://github.com/bdraco/yalexs-ble/compare/v2.0.3...v2.0.4
2023-02-26 22:37:21 -05:00
starkillerOG
e48089e0c9 Do not block on reolink firmware check fail (#88797)
Do not block on firmware check fail
2023-02-26 22:37:20 -05:00
starkillerOG
a7e081f70d Simplify reolink update unique_id (#88794)
simplify unique_id
2023-02-26 22:37:19 -05:00
Paulus Schoutsen
fe181425d8 Check circular dependencies (#88778) 2023-02-26 22:37:18 -05:00
Joakim Plate
8c7b29db25 Update nibe library to 2.0.0 (#88769) 2023-02-26 22:37:17 -05:00
J. Nick Koston
aaa5bb9f86 Fix checking if a package is installed on py3.11 (#88768)
pkg_resources is abandoned and we need to move away
from using it https://github.com/pypa/pkg_resources

In the mean time we need to keep it working. This fixes
a new exception in py3.11 when a module is not installed
which allows proper fallback to pkg_resources.Requirement.parse
when needed

```
2023-02-25 15:46:21.101 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/aiohttp/web_protocol.py", line 433, in _handle_request
    resp = await request_handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/aiohttp/web_app.py", line 504, in _handle
    resp = await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/aiohttp/web_middlewares.py", line 117, in impl
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/security_filter.py", line 60, in security_filter_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/forwarded.py", line 100, in forwarded_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/request_context.py", line 28, in request_context_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/ban.py", line 80, in ban_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/auth.py", line 235, in auth_middleware
    return await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/view.py", line 146, in handle
    result = await result
             ^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/config/config_entries.py", line 148, in post
    return await super().post(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/components/http/data_validator.py", line 72, in wrapper
    result = await method(view, request, data, *args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/helpers/data_entry_flow.py", line 71, in post
    result = await self._flow_mgr.async_init(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/config_entries.py", line 826, in async_init
    flow, result = await task
                   ^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/config_entries.py", line 844, in _async_init
    flow = await self.async_create_flow(handler, context=context, data=data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/config_entries.py", line 950, in async_create_flow
    await async_process_deps_reqs(self.hass, self._hass_config, integration)
  File "/Users/bdraco/home-assistant/homeassistant/setup.py", line 384, in async_process_deps_reqs
    await requirements.async_get_integration_with_requirements(
  File "/Users/bdraco/home-assistant/homeassistant/requirements.py", line 52, in async_get_integration_with_requirements
    return await manager.async_get_integration_with_requirements(domain)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/requirements.py", line 171, in async_get_integration_with_requirements
    await self._async_process_integration(integration, done)
  File "/Users/bdraco/home-assistant/homeassistant/requirements.py", line 186, in _async_process_integration
    await self.async_process_requirements(
  File "/Users/bdraco/home-assistant/homeassistant/requirements.py", line 252, in async_process_requirements
    await self._async_process_requirements(name, missing)
  File "/Users/bdraco/home-assistant/homeassistant/requirements.py", line 284, in _async_process_requirements
    installed, failures = await self.hass.async_add_executor_job(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.11/3.11.1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/requirements.py", line 113, in _install_requirements_if_missing
    if pkg_util.is_installed(req) or _install_with_retry(req, kwargs):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bdraco/home-assistant/homeassistant/util/package.py", line 40, in is_installed
    pkg_resources.get_distribution(package)
  File "/opt/homebrew/lib/python3.11/site-packages/pkg_resources/__init__.py", line 478, in get_distribution
    dist = get_provider(dist)
           ^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pkg_resources/__init__.py", line 354, in get_provider
    return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
                                            ~~~~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range
``
2023-02-26 22:37:17 -05:00
J. Nick Koston
5b78e0c4ff Restore previous behavior of only waiting for new tasks at shutdown (#88740)
* Restore previous behavior of only waiting for new tasks at shutdown

* cleanup

* do a swap instead

* await canceled tasks

* await canceled tasks

* fix

* not needed since we no longer clear

* log it

* reword

* wait for airvisual

* tests
2023-02-26 22:37:16 -05:00
Franck Nijhof
2063dbf00d Bumped version to 2023.3.0b3 2023-02-25 12:07:47 +01:00
Joakim Sørensen
91a03ab83d Remove homeassistant_hardware after dependency from zha (#88751) 2023-02-25 12:07:25 +01:00
J. Nick Koston
ed8f538890 Prevent new discovery flows from being created when stopping (#88743) 2023-02-25 12:07:22 +01:00
J. Nick Koston
6196607c5d Make hass.async_stop an untracked task (#88738) 2023-02-25 12:07:19 +01:00
J. Nick Koston
833ccafb76 Log futures that are blocking shutdown stages (#88736) 2023-02-25 12:07:15 +01:00
mkmer
ca539d0a09 Add missing reauth strings to Honeywell (#88733)
Add missing reauth strings
2023-02-25 12:07:12 +01:00
Austin Mroczek
0e3e954000 Bump total_connect_client to v2023.2 (#88729)
* bump total_connect_client to v2023.2

* Trigger Build
2023-02-25 12:07:09 +01:00
avee87
4ef96c76e4 Fix log message in recorder on total_increasing reset (#88710) 2023-02-25 12:07:05 +01:00
Álvaro Fernández Rojas
d5b0c1faa0 Update aioqsw v0.3.2 (#88695)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2023-02-25 12:07:02 +01:00
Arturo
2405908cdd Fix matter light color capabilities bit map (#88693)
* Adds matter light color capabilities bit map

* Fixed matter light hue and saturation test
2023-02-25 12:06:58 +01:00
Paulus Schoutsen
b6e50135f5 Bumped version to 2023.3.0b2 2023-02-24 21:41:02 -05:00
Bram Kragten
64197aa5f5 Update frontend to 20230224.0 (#88721) 2023-02-24 21:40:56 -05:00
J. Nick Koston
5a2d7a5dd4 Reduce overhead to save json data to postgresql (#88717)
* Reduce overhead to strip nulls from json

* Reduce overhead to strip nulls from json

* small cleanup
2023-02-24 21:40:55 -05:00
J. Nick Koston
2d6f84b2a8 Fix timeout in purpleapi test (#88715)
https://github.com/home-assistant/core/actions/runs/4264644494/jobs/7423099757
2023-02-24 21:40:54 -05:00
J. Nick Koston
0c6a469218 Fix migration failing when existing data has duplicates (#88712) 2023-02-24 21:40:53 -05:00
J. Nick Koston
e69271cb46 Bump aioesphomeapi to 13.4.1 (#88703)
changelog: https://github.com/esphome/aioesphomeapi/releases/tag/v13.4.1
2023-02-24 21:40:52 -05:00
Michael Hansen
02bd3f897d Make a copy of matching states so translated state names can be used (#88683) 2023-02-24 21:40:51 -05:00
J. Nick Koston
64ad5326dd Bump mopeka_iot_ble to 0.4.1 (#88680)
* Bump mopeka_iot_ble to 0.4.1

closes #88232

* adjust tests
2023-02-24 21:40:50 -05:00
puddly
74696a3fac Name the Yellow-internal radio and multi-PAN addon as ZHA serial ports (#88208)
* Expose the Yellow-internal radio and multi-PAN addon as named serial ports

* Remove the serial number if it isn't available

* Use consistent names for the addon and Zigbee radio

* Add `homeassistant_hardware` and `_yellow` as `after_dependencies`

* Handle `hassio` not existing when listing serial ports

* Add unit tests
2023-02-24 21:40:49 -05:00
Paulus Schoutsen
70e1d14da0 Bumped version to 2023.3.0b1 2023-02-23 15:00:13 -05:00
Bram Kragten
25f066d476 Update frontend to 20230223.0 (#88677) 2023-02-23 15:00:07 -05:00
Marcel van der Veldt
5adf1dcc90 Fix support for Bridge(d) and composed devices in Matter (#88662)
* Refactor discovery of entities to support composed and bridged devices

* Bump library version to 3.1.0

* move discovery schemas to platforms

* optimize a tiny bit

* simplify even more

* fixed bug in light platform

* fix color control logic

* fix some issues

* Update homeassistant/components/matter/discovery.py

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* fix some tests

* fix light test

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-02-23 15:00:05 -05:00
epenet
0fb28dcf9e Add missing async_setup_entry mock in openuv (#88661) 2023-02-23 15:00:04 -05:00
Allen Porter
2fddbcedcf Fix local calendar issue with events created with fixed UTC offsets (#88650)
Fix issue with events created with UTC offsets
2023-02-23 15:00:03 -05:00
J. Nick Koston
951df3df57 Fix untrapped exceptions during Yale Access Bluetooth first setup (#88642) 2023-02-23 15:00:02 -05:00
starkillerOG
35142e456a Bump reolink-aio to 0.5.1 and check if update supported (#88641) 2023-02-23 15:00:01 -05:00
Paulus Schoutsen
cfaba87dd6 Error checking for OTBR (#88620)
* Error checking for OTBR

* Other errors in flow too

* Tests
2023-02-23 15:00:00 -05:00
Erik Montnemery
2db8d4b73a Bump python-otbr-api to 1.0.4 (#88613)
* Bump python-otbr-api to 1.0.4

* Adjust tests
2023-02-23 14:59:59 -05:00
Raman Gupta
0d2006bf33 Add support for firmware target in zwave_js FirmwareUploadView (#88523)
* Add support for firmware target in zwave_js FirmwareUploadView

fix

* Update tests/components/zwave_js/test_api.py

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

* Update tests/components/zwave_js/test_api.py

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

* Update tests/components/zwave_js/test_api.py

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

* Update tests/components/zwave_js/test_api.py

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

* fix types

* Switch back to using Any

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2023-02-23 14:59:58 -05:00
puddly
45547d226e Disable the ZHA bellows UART thread when connecting to a TCP coordinator (#88202)
Disable the bellows UART thread when connecting to a TCP coordinator
2023-02-23 14:59:56 -05:00
Franck Nijhof
cebc6dd096 Bumped version to 2023.3.0b0 2023-02-22 20:44:37 +01:00
492 changed files with 4570 additions and 8630 deletions

View File

@@ -639,10 +639,6 @@ omit =
homeassistant/components/linode/*
homeassistant/components/linux_battery/sensor.py
homeassistant/components/lirc/*
homeassistant/components/livisi/__init__.py
homeassistant/components/livisi/climate.py
homeassistant/components/livisi/coordinator.py
homeassistant/components/livisi/switch.py
homeassistant/components/llamalab_automate/notify.py
homeassistant/components/logi_circle/__init__.py
homeassistant/components/logi_circle/camera.py
@@ -807,9 +803,7 @@ omit =
homeassistant/components/nuki/sensor.py
homeassistant/components/nx584/alarm_control_panel.py
homeassistant/components/oasa_telematics/sensor.py
homeassistant/components/obihai/__init__.py
homeassistant/components/obihai/connectivity.py
homeassistant/components/obihai/sensor.py
homeassistant/components/obihai/*
homeassistant/components/octoprint/__init__.py
homeassistant/components/oem/climate.py
homeassistant/components/ohmconnect/sensor.py
@@ -1369,6 +1363,7 @@ omit =
homeassistant/components/verisure/sensor.py
homeassistant/components/verisure/switch.py
homeassistant/components/versasense/*
homeassistant/components/vesync/__init__.py
homeassistant/components/vesync/common.py
homeassistant/components/vesync/fan.py
homeassistant/components/vesync/light.py

View File

@@ -31,7 +31,7 @@ env:
CACHE_VERSION: 5
PIP_CACHE_VERSION: 4
MYPY_CACHE_VERSION: 4
HA_SHORT_VERSION: 2023.4
HA_SHORT_VERSION: 2023.3
DEFAULT_PYTHON: "3.10"
ALL_PYTHON_VERSIONS: "['3.10', '3.11']"
# 10.3 is the oldest supported version
@@ -1073,10 +1073,10 @@ jobs:
ffmpeg \
postgresql-server-dev-14
- name: Check out code from GitHub
uses: actions/checkout@v3.3.0
uses: actions/checkout@v3.1.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v4.5.0
uses: actions/setup-python@v4.3.0
with:
python-version: ${{ matrix.python-version }}
check-latest: true

View File

@@ -186,7 +186,6 @@ homeassistant.components.ld2410_ble.*
homeassistant.components.lidarr.*
homeassistant.components.lifx.*
homeassistant.components.light.*
homeassistant.components.litejet.*
homeassistant.components.litterrobot.*
homeassistant.components.local_ip.*
homeassistant.components.lock.*

View File

@@ -659,8 +659,8 @@ build.json @home-assistant/supervisor
/tests/components/litejet/ @joncar
/homeassistant/components/litterrobot/ @natekspencer @tkdrob
/tests/components/litterrobot/ @natekspencer @tkdrob
/homeassistant/components/livisi/ @StefanIacobLivisi @planbnet
/tests/components/livisi/ @StefanIacobLivisi @planbnet
/homeassistant/components/livisi/ @StefanIacobLivisi
/tests/components/livisi/ @StefanIacobLivisi
/homeassistant/components/local_calendar/ @allenporter
/tests/components/local_calendar/ @allenporter
/homeassistant/components/local_ip/ @issacg
@@ -825,8 +825,7 @@ build.json @home-assistant/supervisor
/tests/components/nws/ @MatthewFlamm @kamiyo
/homeassistant/components/nzbget/ @chriscla
/tests/components/nzbget/ @chriscla
/homeassistant/components/obihai/ @dshokouhi @ejpenney
/tests/components/obihai/ @dshokouhi @ejpenney
/homeassistant/components/obihai/ @dshokouhi
/homeassistant/components/octoprint/ @rfleming71
/tests/components/octoprint/ @rfleming71
/homeassistant/components/ohmconnect/ @robbiet480
@@ -1101,6 +1100,7 @@ build.json @home-assistant/supervisor
/homeassistant/components/smhi/ @gjohansson-ST
/tests/components/smhi/ @gjohansson-ST
/homeassistant/components/sms/ @ocalvo
/homeassistant/components/snapcast/ @luar123
/homeassistant/components/snooz/ @AustinBrunkhorst
/tests/components/snooz/ @AustinBrunkhorst
/homeassistant/components/solaredge/ @frenck
@@ -1139,8 +1139,8 @@ build.json @home-assistant/supervisor
/tests/components/starline/ @anonym-tsk
/homeassistant/components/starlink/ @boswelja
/tests/components/starlink/ @boswelja
/homeassistant/components/statistics/ @ThomDietrich
/tests/components/statistics/ @ThomDietrich
/homeassistant/components/statistics/ @fabaff @ThomDietrich
/tests/components/statistics/ @fabaff @ThomDietrich
/homeassistant/components/steam_online/ @tkdrob
/tests/components/steam_online/ @tkdrob
/homeassistant/components/steamist/ @bdraco
@@ -1213,6 +1213,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/thethingsnetwork/ @fabaff
/homeassistant/components/thread/ @home-assistant/core
/tests/components/thread/ @home-assistant/core
/homeassistant/components/threshold/ @fabaff
/tests/components/threshold/ @fabaff
/homeassistant/components/tibber/ @danielhiversen
/tests/components/tibber/ @danielhiversen
/homeassistant/components/tile/ @bachya

View File

@@ -6,12 +6,15 @@ from typing import TYPE_CHECKING
import attr
if TYPE_CHECKING:
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers import (
device_registry as dev_reg,
entity_registry as ent_reg,
)
@attr.s(slots=True)
class PermissionLookup:
"""Class to hold data for permission lookups."""
entity_registry: er.EntityRegistry = attr.ib()
device_registry: dr.DeviceRegistry = attr.ib()
entity_registry: ent_reg.EntityRegistry = attr.ib()
device_registry: dev_reg.DeviceRegistry = attr.ib()

View File

@@ -68,7 +68,6 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="co",
name="CO",
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("co"),
@@ -289,7 +288,6 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="tvoc",
name="VOC",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("tvoc"),
@@ -297,7 +295,6 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="tvoc_ionsc",
name="VOC (Industrial)",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("tvoc_ionsc"),

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Mapping
import logging
from typing import Any
from AIOAladdinConnect import AladdinConnectClient
@@ -19,6 +20,8 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CLIENT_ID, DOMAIN
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_USERNAME): str,
@@ -131,6 +134,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
async def async_step_import(
self, import_data: dict[str, Any] | None = None
) -> FlowResult:
"""Import Aladin Connect config from configuration.yaml."""
return await self.async_step_user(import_data)
class InvalidAuth(HomeAssistantError):
"""Error to indicate there is invalid auth."""

View File

@@ -2,24 +2,63 @@
from __future__ import annotations
from datetime import timedelta
from typing import Any
import logging
from typing import Any, Final
from AIOAladdinConnect import AladdinConnectClient
import voluptuous as vol
from homeassistant.components.cover import CoverDeviceClass, CoverEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPENING
from homeassistant.components.cover import (
PLATFORM_SCHEMA as BASE_PLATFORM_SCHEMA,
CoverDeviceClass,
CoverEntity,
)
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
STATE_CLOSED,
STATE_CLOSING,
STATE_OPENING,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DOMAIN, STATES_MAP, SUPPORTED_FEATURES
from .model import DoorDevice
_LOGGER: Final = logging.getLogger(__name__)
PLATFORM_SCHEMA: Final = BASE_PLATFORM_SCHEMA.extend(
{vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)
SCAN_INTERVAL = timedelta(seconds=300)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up Aladdin Connect devices yaml depreciated."""
_LOGGER.warning(
"Configuring Aladdin Connect through yaml is deprecated. Please remove it from"
" your configuration as it has already been imported to a config entry"
)
await hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=config,
)
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,

View File

@@ -21,7 +21,7 @@ from homeassistant.const import (
SERVICE_ALARM_TRIGGER,
)
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -57,11 +57,11 @@ async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for Alarm control panel devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
actions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -21,11 +21,7 @@ from homeassistant.const import (
STATE_ALARM_TRIGGERED,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
condition,
config_validation as cv,
entity_registry as er,
)
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -68,11 +64,11 @@ async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device conditions for Alarm control panel devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
conditions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -23,7 +23,7 @@ from homeassistant.const import (
STATE_ALARM_TRIGGERED,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
@@ -57,11 +57,11 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for Alarm control panel devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
triggers: list[dict[str, str]] = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -5,7 +5,6 @@ import asyncio
from http import HTTPStatus
import json
import logging
from typing import cast
import aiohttp
import async_timeout
@@ -16,7 +15,6 @@ 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 .entities import ENTITY_ADAPTERS, AlexaEntity, generate_alexa_id
@@ -164,10 +162,9 @@ async def async_send_changereport_message(
if response.status == HTTPStatus.ACCEPTED:
return
response_json = json_loads_object(response_text)
response_payload = cast(JsonObjectType, response_json["payload"])
response_json = json.loads(response_text)
if response_payload["code"] == "INVALID_ACCESS_TOKEN_EXCEPTION":
if response_json["payload"]["code"] == "INVALID_ACCESS_TOKEN_EXCEPTION":
if invalidate_access_token:
# Invalidate the access token and try again
config.async_invalidate_access_token()
@@ -183,8 +180,8 @@ async def async_send_changereport_message(
_LOGGER.error(
"Error when sending ChangeReport for %s to Alexa: %s: %s",
alexa_entity.entity_id,
response_payload["code"],
response_payload["description"],
response_json["payload"]["code"],
response_json["payload"]["description"],
)
@@ -302,12 +299,11 @@ async def async_send_doorbell_event_message(hass, config, alexa_entity):
if response.status == HTTPStatus.ACCEPTED:
return
response_json = json_loads_object(response_text)
response_payload = cast(JsonObjectType, response_json["payload"])
response_json = json.loads(response_text)
_LOGGER.error(
"Error when sending DoorbellPress event for %s to Alexa: %s: %s",
alexa_entity.entity_id,
response_payload["code"],
response_payload["description"],
response_json["payload"]["code"],
response_json["payload"]["description"],
)

View File

@@ -20,12 +20,13 @@ from homeassistant.components.camera import (
from homeassistant.components.ffmpeg import FFmpegManager, get_ffmpeg_manager
from homeassistant.const import ATTR_ENTITY_ID, CONF_NAME, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.aiohttp_client import (
async_aiohttp_proxy_stream,
async_aiohttp_proxy_web,
async_get_clientsession,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@@ -145,7 +146,7 @@ async def async_setup_platform(
# with this version, update the old entity with the new unique id.
serial_number = await device.api.async_serial_number
serial_number = serial_number.strip()
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
entity_id = registry.async_get_entity_id(CAMERA_DOMAIN, DOMAIN, serial_number)
if entity_id is not None:
_LOGGER.debug("Updating unique id for camera %s", entity_id)

View File

@@ -1,5 +1,6 @@
"""Rest API for Home Assistant."""
import asyncio
from functools import lru_cache
from http import HTTPStatus
import logging
@@ -350,6 +351,12 @@ class APIComponentsView(HomeAssistantView):
return self.json(request.app["hass"].config.components)
@lru_cache
def _cached_template(template_str: str, hass: ha.HomeAssistant) -> template.Template:
"""Return a cached template."""
return template.Template(template_str, hass)
class APITemplateView(HomeAssistantView):
"""View to handle Template requests."""
@@ -362,7 +369,7 @@ class APITemplateView(HomeAssistantView):
raise Unauthorized()
try:
data = await request.json()
tpl = template.Template(data["template"], request.app["hass"])
tpl = _cached_template(data["template"], request.app["hass"])
return tpl.async_render(variables=data.get("variables"), parse_result=False)
except (ValueError, TemplateError) as ex:
return self.json_message(

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/apprise",
"iot_class": "cloud_push",
"loggers": ["apprise"],
"requirements": ["apprise==1.3.0"]
"requirements": ["apprise==1.2.1"]
}

View File

@@ -13,7 +13,7 @@ from homeassistant.const import (
CONF_TYPE,
)
from homeassistant.core import CALLBACK_TYPE, Event, HassJob, HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
@@ -32,11 +32,11 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for Arcam FMJ Receiver control devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
triggers = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain == "media_player":
triggers.append(
{

View File

@@ -180,7 +180,7 @@ class ArestData:
self._resource = resource
self._pin = pin
self.data = {}
self._attr_available = True
self.available = True
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
@@ -201,7 +201,7 @@ class ArestData:
f"{self._resource}/digital/{self._pin}", timeout=10
)
self.data = {"value": response.json()["return_value"]}
self._attr_available = True
self.available = True
except requests.exceptions.ConnectionError:
_LOGGER.error("No route to device %s", self._resource)
self._attr_available = False
self.available = False

View File

@@ -1,6 +1,7 @@
"""Support for collecting data from the ARWN project."""
from __future__ import annotations
import json
import logging
from homeassistant.components import mqtt
@@ -10,7 +11,6 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import slugify
from homeassistant.util.json import json_loads_object
_LOGGER = logging.getLogger(__name__)
@@ -102,7 +102,7 @@ async def async_setup_platform(
"""Set up the ARWN platform."""
@callback
def async_sensor_event_received(msg: mqtt.ReceiveMessage) -> None:
def async_sensor_event_received(msg):
"""Process events as sensors.
When a new event on our topic (arwn/#) is received we map it
@@ -115,7 +115,7 @@ async def async_setup_platform(
This lets us dynamically incorporate sensors without any
configuration on our side.
"""
event = json_loads_object(msg.payload)
event = json.loads(msg.payload)
sensors = discover_sensors(msg.topic, event)
if not sensors:
return

View File

@@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==1.2.7", "yalexs_ble==2.0.4"]
"requirements": ["yalexs==1.2.7", "yalexs-ble==2.1.1"]
}

View File

@@ -1,5 +1,5 @@
"""Support for Aurora Forecast sensor."""
from homeassistant.components.sensor import SensorEntity, SensorStateClass
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
@@ -28,7 +28,6 @@ class AuroraSensor(AuroraEntity, SensorEntity):
"""Implementation of an aurora sensor."""
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
@property
def native_value(self):

View File

@@ -60,7 +60,7 @@ from .const import (
DEFAULT_PROBABILITY_THRESHOLD,
)
from .helpers import Observation
from .repairs import raise_mirrored_entries, raise_no_prob_given_false
from .issues import raise_mirrored_entries, raise_no_prob_given_false
_LOGGER = logging.getLogger(__name__)

View File

@@ -1,8 +1,8 @@
"""Helpers for generating repairs."""
"""Helpers for generating issues."""
from __future__ import annotations
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers import issue_registry
from . import DOMAIN
from .helpers import Observation
@@ -15,13 +15,13 @@ def raise_mirrored_entries(
if len(observations) != 2:
return
if observations[0].is_mirror(observations[1]):
ir.async_create_issue(
issue_registry.async_create_issue(
hass,
DOMAIN,
"mirrored_entry/" + text,
breaks_in_ha_version="2022.10.0",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
severity=issue_registry.IssueSeverity.WARNING,
translation_key="manual_migration",
translation_placeholders={"entity": text},
learn_more_url="https://github.com/home-assistant/core/pull/67631",
@@ -31,13 +31,13 @@ def raise_mirrored_entries(
# Should deprecate in some future version (2022.10 at time of writing) & make prob_given_false required in schemas.
def raise_no_prob_given_false(hass: HomeAssistant, text: str) -> None:
"""In previous 2022.9 and earlier, prob_given_false was optional and had a default version."""
ir.async_create_issue(
issue_registry.async_create_issue(
hass,
DOMAIN,
f"no_prob_given_false/{text}",
breaks_in_ha_version="2022.10.0",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
severity=issue_registry.IssueSeverity.ERROR,
translation_key="no_prob_given_false",
translation_placeholders={"entity": text},
learn_more_url="https://github.com/home-assistant/core/pull/67631",

View File

@@ -227,20 +227,21 @@ class BaseHaRemoteScanner(BaseHaScanner):
self.hass, self._async_expire_devices, timedelta(seconds=30)
)
cancel_stop = self.hass.bus.async_listen(
EVENT_HOMEASSISTANT_STOP, self._save_history
EVENT_HOMEASSISTANT_STOP, self._async_save_history
)
self._async_setup_scanner_watchdog()
@hass_callback
def _cancel() -> None:
self._save_history()
self._async_save_history()
self._async_stop_scanner_watchdog()
cancel_track()
cancel_stop()
return _cancel
def _save_history(self, event: Event | None = None) -> None:
@hass_callback
def _async_save_history(self, event: Event | None = None) -> None:
"""Save the history."""
self._storage.async_set_advertisement_history(
self.source,
@@ -252,6 +253,7 @@ class BaseHaRemoteScanner(BaseHaScanner):
),
)
@hass_callback
def _async_expire_devices(self, _datetime: datetime.datetime) -> None:
"""Expire old devices."""
now = MONOTONIC_TIME()

View File

@@ -7,6 +7,6 @@
"iot_class": "local_push",
"loggers": ["bond_async"],
"quality_scale": "platinum",
"requirements": ["bond-async==0.1.22"],
"requirements": ["bond-async==0.1.23"],
"zeroconf": ["_bond._tcp.local."]
}

View File

@@ -20,5 +20,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bthome",
"iot_class": "local_push",
"requirements": ["bthome-ble==2.7.0"]
"requirements": ["bthome-ble==2.5.2"]
}

View File

@@ -119,16 +119,6 @@ SENSOR_DESCRIPTIONS = {
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
),
# Gas (m3)
(
BTHomeSensorDeviceClass.GAS,
Units.VOLUME_CUBIC_METERS,
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.GAS}_{Units.VOLUME_CUBIC_METERS}",
device_class=SensorDeviceClass.GAS,
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
state_class=SensorStateClass.TOTAL_INCREASING,
),
# Humidity in (percent)
(BTHomeSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}",

View File

@@ -11,7 +11,7 @@ from homeassistant.const import (
CONF_TYPE,
)
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -31,7 +31,7 @@ async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for button devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
return [
{
CONF_DEVICE_ID: device_id,
@@ -39,7 +39,7 @@ async def async_get_actions(
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: "press",
}
for entry in er.async_entries_for_device(registry, device_id)
for entry in entity_registry.async_entries_for_device(registry, device_id)
if entry.domain == DOMAIN
]

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
CONF_TYPE,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
@@ -36,7 +36,7 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for button devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
return [
{
CONF_PLATFORM: "device",
@@ -45,7 +45,7 @@ async def async_get_triggers(
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: "pressed",
}
for entry in er.async_entries_for_device(registry, device_id)
for entry in entity_registry.async_entries_for_device(registry, device_id)
if entry.domain == DOMAIN
]

View File

@@ -12,7 +12,7 @@ from homeassistant.const import (
)
from homeassistant.core import Context, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import get_capability, get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -44,11 +44,11 @@ async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for Climate devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
actions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -13,11 +13,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
condition,
config_validation as cv,
entity_registry as er,
)
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.entity import get_capability, get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -49,11 +45,11 @@ async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device conditions for Climate devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
conditions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -20,7 +20,7 @@ from homeassistant.const import (
PERCENTAGE,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
@@ -62,11 +62,11 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for Climate devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
triggers = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -10,7 +10,8 @@ from coinbase.wallet.error import AuthenticationError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
from .const import (
@@ -70,8 +71,10 @@ async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> Non
await hass.config_entries.async_reload(config_entry.entry_id)
registry = er.async_get(hass)
entities = er.async_entries_for_config_entry(registry, config_entry.entry_id)
registry = entity_registry.async_get(hass)
entities = entity_registry.async_entries_for_config_entry(
registry, config_entry.entry_id
)
# Remove orphaned entities
for entity in entities:

View File

@@ -8,7 +8,7 @@ from homeassistant.components.automation.config import (
)
from homeassistant.config import AUTOMATION_CONFIG_PATH
from homeassistant.const import CONF_ID, SERVICE_RELOAD
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from . import ACTION_DELETE, EditIdBasedConfigView
@@ -23,7 +23,7 @@ async def async_setup(hass):
if action != ACTION_DELETE:
return
ent_reg = er.async_get(hass)
ent_reg = entity_registry.async_get(hass)
entity_id = ent_reg.async_get_entity_id(DOMAIN, DOMAIN, config_key)

View File

@@ -5,7 +5,7 @@ from homeassistant.components.scene import DOMAIN, PLATFORM_SCHEMA
from homeassistant.config import SCENE_CONFIG_PATH
from homeassistant.const import CONF_ID, SERVICE_RELOAD
from homeassistant.core import DOMAIN as HA_DOMAIN
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from . import ACTION_DELETE, EditIdBasedConfigView
@@ -19,7 +19,7 @@ async def async_setup(hass):
await hass.services.async_call(DOMAIN, SERVICE_RELOAD)
return
ent_reg = er.async_get(hass)
ent_reg = entity_registry.async_get(hass)
entity_id = ent_reg.async_get_entity_id(DOMAIN, HA_DOMAIN, config_key)

View File

@@ -3,7 +3,6 @@
DOMAIN = "conversation"
DEFAULT_EXPOSED_DOMAINS = {
"binary_sensor",
"climate",
"cover",
"fan",
@@ -17,5 +16,3 @@ DEFAULT_EXPOSED_DOMAINS = {
"vacuum",
"water_heater",
}
DEFAULT_EXPOSED_ATTRIBUTES = {"device_class"}

View File

@@ -18,9 +18,9 @@ import yaml
from homeassistant import core, setup
from homeassistant.helpers import (
area_registry as ar,
device_registry as dr,
entity_registry as er,
area_registry,
device_registry,
entity_registry,
intent,
template,
translation,
@@ -28,7 +28,7 @@ from homeassistant.helpers import (
from homeassistant.util.json import JsonObjectType, json_loads_object
from .agent import AbstractConversationAgent, ConversationInput, ConversationResult
from .const import DEFAULT_EXPOSED_ATTRIBUTES, DEFAULT_EXPOSED_DOMAINS, DOMAIN
from .const import DEFAULT_EXPOSED_DOMAINS, DOMAIN
_LOGGER = logging.getLogger(__name__)
_DEFAULT_ERROR_TEXT = "Sorry, I couldn't understand that"
@@ -95,12 +95,12 @@ class DefaultAgent(AbstractConversationAgent):
self._config_intents = config_intents
self.hass.bus.async_listen(
ar.EVENT_AREA_REGISTRY_UPDATED,
area_registry.EVENT_AREA_REGISTRY_UPDATED,
self._async_handle_area_registry_changed,
run_immediately=True,
)
self.hass.bus.async_listen(
er.EVENT_ENTITY_REGISTRY_UPDATED,
entity_registry.EVENT_ENTITY_REGISTRY_UPDATED,
self._async_handle_entity_registry_changed,
run_immediately=True,
)
@@ -471,20 +471,14 @@ class DefaultAgent(AbstractConversationAgent):
states = [
state for state in self.hass.states.async_all() if is_entity_exposed(state)
]
entities = er.async_get(self.hass)
devices = dr.async_get(self.hass)
entities = entity_registry.async_get(self.hass)
devices = device_registry.async_get(self.hass)
# Gather exposed entity names
entity_names = []
for state in states:
# Checked against "requires_context" and "excludes_context" in hassil
context = {"domain": state.domain}
if state.attributes:
# Include some attributes
for attr_key, attr_value in state.attributes.items():
if attr_key not in DEFAULT_EXPOSED_ATTRIBUTES:
continue
context[attr_key] = attr_value
entity = entities.async_get(state.entity_id)
if entity is not None:
@@ -512,7 +506,7 @@ class DefaultAgent(AbstractConversationAgent):
entity_names.append((state.name, state.name, context))
# Gather areas from exposed entities
areas = ar.async_get(self.hass)
areas = area_registry.async_get(self.hass)
area_names = []
for area_id in area_ids_with_entities:
area = areas.async_get_area(area_id)
@@ -524,9 +518,6 @@ class DefaultAgent(AbstractConversationAgent):
for alias in area.aliases:
area_names.append((alias, area.id))
_LOGGER.debug("Exposed areas: %s", area_names)
_LOGGER.debug("Exposed entities: %s", entity_names)
self._slot_lists = {
"area": TextSlotList.from_tuples(area_names, allow_template=False),
"name": TextSlotList.from_tuples(entity_names, allow_template=False),

View File

@@ -8,11 +8,7 @@ import coronavirus
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
aiohttp_client,
entity_registry as er,
update_coordinator,
)
from homeassistant.helpers import aiohttp_client, entity_registry, update_coordinator
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
@@ -35,14 +31,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
@callback
def _async_migrator(entity_entry: er.RegistryEntry):
def _async_migrator(entity_entry: entity_registry.RegistryEntry):
"""Migrate away from unstable ID."""
country, info_type = entity_entry.unique_id.rsplit("-", 1)
if not country.isnumeric():
return None
return {"new_unique_id": f"{entry.title}-{info_type}"}
await er.async_migrate_entries(hass, entry.entry_id, _async_migrator)
await entity_registry.async_migrate_entries(
hass, entry.entry_id, _async_migrator
)
if not entry.unique_id:
hass.config_entries.async_update_entry(entry, unique_id=entry.data["country"])

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
SERVICE_STOP_COVER,
)
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -63,11 +63,11 @@ async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for Cover devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
actions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -18,11 +18,7 @@ from homeassistant.const import (
STATE_OPENING,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
condition,
config_validation as cv,
entity_registry as er,
)
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -70,11 +66,11 @@ async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device conditions for Cover devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
conditions: list[dict[str, str]] = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -24,7 +24,7 @@ from homeassistant.const import (
STATE_OPENING,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
@@ -71,11 +71,11 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for Cover devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
triggers = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -8,7 +8,7 @@
"iot_class": "local_push",
"loggers": ["pydeconz"],
"quality_scale": "platinum",
"requirements": ["pydeconz==108"],
"requirements": ["pydeconz==110"],
"ssdp": [
{
"manufacturer": "Royal Philips Electronics",

View File

@@ -14,7 +14,7 @@ from homeassistant.components.sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_IDLE, Platform, UnitOfDataRate
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import entity_platform
from homeassistant.helpers.typing import StateType
from . import DelugeEntity
@@ -71,7 +71,9 @@ SENSOR_TYPES: tuple[DelugeSensorEntityDescription, ...] = (
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: entity_platform.AddEntitiesCallback,
) -> None:
"""Set up the Deluge sensor."""
async_add_entities(

View File

@@ -7,7 +7,7 @@ from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import entity_platform
from . import DelugeEntity
from .const import DOMAIN
@@ -15,7 +15,9 @@ from .coordinator import DelugeDataUpdateCoordinator
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: entity_platform.AddEntitiesCallback,
) -> None:
"""Set up the Deluge switch."""
async_add_entities([DelugeSwitch(hass.data[DOMAIN][entry.entry_id])])

View File

@@ -8,7 +8,6 @@ import voluptuous as vol
from homeassistant.const import CONF_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.condition import ConditionProtocol, trace_condition_function
from homeassistant.helpers.typing import ConfigType
from . import DeviceAutomationType, async_get_device_automation_platform
@@ -18,13 +17,24 @@ if TYPE_CHECKING:
from homeassistant.helpers import condition
class DeviceAutomationConditionProtocol(ConditionProtocol, Protocol):
class DeviceAutomationConditionProtocol(Protocol):
"""Define the format of device_condition modules.
Each module must define either CONDITION_SCHEMA or async_validate_condition_config
from ConditionProtocol.
Each module must define either CONDITION_SCHEMA or async_validate_condition_config.
"""
CONDITION_SCHEMA: vol.Schema
async def async_validate_condition_config(
self, hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
def async_condition_from_config(
self, hass: HomeAssistant, config: ConfigType
) -> condition.ConditionCheckerType:
"""Evaluate state based on configuration."""
async def async_get_condition_capabilities(
self, hass: HomeAssistant, config: ConfigType
) -> dict[str, vol.Schema]:
@@ -52,4 +62,4 @@ async def async_condition_from_config(
platform = await async_get_device_automation_platform(
hass, config[CONF_DOMAIN], DeviceAutomationType.CONDITION
)
return trace_condition_function(platform.async_condition_from_config(hass, config))
return platform.async_condition_from_config(hass, config)

View File

@@ -13,11 +13,7 @@ from homeassistant.const import (
STATE_HOME,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
condition,
config_validation as cv,
entity_registry as er,
)
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -37,11 +33,11 @@ async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device conditions for Device tracker devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
conditions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -17,7 +17,7 @@ from homeassistant.const import (
CONF_ZONE,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
@@ -38,11 +38,11 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for Device Tracker devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
triggers = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -12,7 +12,7 @@ from homeassistant.components.device_tracker import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_UNKNOWN, UnitOfFrequency
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
@@ -30,7 +30,7 @@ async def async_setup_entry(
coordinators: dict[
str, DataUpdateCoordinator[list[ConnectedStationInfo]]
] = hass.data[DOMAIN][entry.entry_id]["coordinators"]
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
tracked = set()
@callback
@@ -53,7 +53,9 @@ async def async_setup_entry(
def restore_entities() -> None:
"""Restore clients that are not a part of active clients list."""
missing = []
for entity in er.async_entries_for_config_entry(registry, entry.entry_id):
for entity in entity_registry.async_entries_for_config_entry(
registry, entry.entry_id
):
if (
entity.platform == DOMAIN
and entity.domain == DEVICE_TRACKER_DOMAIN

View File

@@ -21,7 +21,8 @@ from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_MAC, CONF_TYPE,
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import IntegrationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers import device_registry
import homeassistant.helpers.config_validation as cv
from .const import (
CONF_BROWSE_UNFILTERED,
@@ -500,4 +501,4 @@ async def _async_get_mac_address(hass: HomeAssistant, host: str) -> str | None:
if not mac_address:
return None
return dr.format_mac(mac_address)
return device_registry.format_mac(mac_address)

View File

@@ -29,7 +29,7 @@ from homeassistant.components.media_player import (
)
from homeassistant.const import CONF_DEVICE_ID, CONF_MAC, CONF_TYPE, CONF_URL
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers import device_registry, entity_registry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
@@ -363,21 +363,21 @@ class DlnaDmrEntity(MediaPlayerEntity):
# device's UDN. They may be the same, if the DMR is the root device.
connections.add(
(
dr.CONNECTION_UPNP,
device_registry.CONNECTION_UPNP,
self._device.profile_device.root_device.udn,
)
)
connections.add((dr.CONNECTION_UPNP, self._device.udn))
connections.add((device_registry.CONNECTION_UPNP, self._device.udn))
if self.mac_address:
# Connection based on MAC address, if known
connections.add(
# Device MAC is obtained from the config entry, which uses getmac
(dr.CONNECTION_NETWORK_MAC, self.mac_address)
(device_registry.CONNECTION_NETWORK_MAC, self.mac_address)
)
# Create linked HA DeviceEntry now the information is known.
dev_reg = dr.async_get(self.hass)
dev_reg = device_registry.async_get(self.hass)
device_entry = dev_reg.async_get_or_create(
config_entry_id=self.registry_entry.config_entry_id,
connections=connections,
@@ -388,7 +388,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
)
# Update entity registry to link to the device
ent_reg = er.async_get(self.hass)
ent_reg = entity_registry.async_get(self.hass)
ent_reg.async_get_or_create(
self.registry_entry.domain,
self.registry_entry.platform,

View File

@@ -45,9 +45,10 @@ BINARY_SENSOR_DESCRIPTIONS = (
),
DormakabaDkeyBinarySensorDescription(
key="security_locked",
name="Dead bolt",
name="Deadbolt",
device_class=BinarySensorDeviceClass.LOCK,
is_on=lambda state: state.unlock_status != UnlockStatus.SECURITY_LOCKED,
is_on=lambda state: state.unlock_status
not in (UnlockStatus.SECURITY_LOCKED, UnlockStatus.UNLOCKED_SECURITY_LOCKED),
),
)

View File

@@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/dormakaba_dkey",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["py-dormakaba-dkey==1.0.3"]
"requirements": ["py-dormakaba-dkey==1.0.4"]
}

View File

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

View File

@@ -16,7 +16,7 @@ from homeassistant.components.sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfEnergy, UnitOfPower
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import entity_platform
from homeassistant.helpers.typing import StateType
from . import EfergyEntity
@@ -104,7 +104,9 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: entity_platform.AddEntitiesCallback,
) -> None:
"""Set up Efergy sensors."""
api: Efergy = hass.data[DOMAIN][entry.entry_id]

View File

@@ -15,10 +15,7 @@ from homeassistant.components.sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_current_platform,
)
from homeassistant.helpers import entity_platform as ep
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import EightSleepBaseEntity, EightSleepConfigEntryData
@@ -71,7 +68,7 @@ SERVICE_EIGHT_SCHEMA = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: ep.AddEntitiesCallback
) -> None:
"""Set up the eight sleep sensors."""
config_entry_data: EightSleepConfigEntryData = hass.data[DOMAIN][entry.entry_id]
@@ -98,7 +95,7 @@ async def async_setup_entry(
async_add_entities(all_sensors)
platform = async_get_current_platform()
platform = ep.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_HEAT_SET,
SERVICE_EIGHT_SCHEMA,

View File

@@ -9,7 +9,8 @@ import voluptuous as vol
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity
from homeassistant.const import CONF_ID, CONF_NAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@@ -37,7 +38,7 @@ def _migrate_to_new_unique_id(hass: HomeAssistant, dev_id, channel) -> None:
"""Migrate old unique ids to new unique ids."""
old_unique_id = f"{combine_hex(dev_id)}"
ent_reg = er.async_get(hass)
ent_reg = entity_registry.async_get(hass)
entity_id = ent_reg.async_get_entity_id(Platform.SWITCH, DOMAIN, old_unique_id)
if entity_id is not None:

View File

@@ -130,10 +130,15 @@ class RuntimeEntryData:
)
self.ble_connections_free = free
self.ble_connections_limit = limit
if free:
for fut in self._ble_connection_free_futures:
if not free:
return
for fut in self._ble_connection_free_futures:
# If wait_for_ble_connections_free gets cancelled, it will
# leave a future in the list. We need to check if it's done
# before setting the result.
if not fut.done():
fut.set_result(free)
self._ble_connection_free_futures.clear()
self._ble_connection_free_futures.clear()
async def wait_for_ble_connections_free(self) -> int:
"""Wait until there are free BLE connections."""

View File

@@ -14,6 +14,6 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["aioesphomeapi", "noiseprotocol"],
"requirements": ["aioesphomeapi==13.4.1", "esphome-dashboard-api==1.2.3"],
"requirements": ["aioesphomeapi==13.5.1", "esphome-dashboard-api==1.2.3"],
"zeroconf": ["_esphomelib._tcp.local."]
}

View File

@@ -17,10 +17,10 @@ from homeassistant.config_entries import (
)
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, discovery_flow
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_current_platform,
from homeassistant.helpers import (
config_validation as cv,
discovery_flow,
entity_platform,
)
from .const import (
@@ -53,7 +53,9 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: entity_platform.AddEntitiesCallback,
) -> None:
"""Set up EZVIZ cameras based on a config entry."""
@@ -130,7 +132,7 @@ async def async_setup_entry(
async_add_entities(camera_entities)
platform = async_get_current_platform()
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_PTZ,

View File

@@ -14,11 +14,7 @@ from homeassistant.const import (
STATE_ON,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
condition,
config_validation as cv,
entity_registry as er,
)
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -38,11 +34,11 @@ async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device conditions for Fan devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
conditions = []
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -94,9 +94,9 @@ class FibaroCover(FibaroDevice, CoverEntity):
"""Return if the cover is closed."""
if self._is_open_close_only():
state = self.fibaro_device.state
if not state.has_value or state.str_value.lower() == "unknown":
if not state.has_value or state.str_value().lower() == "unknown":
return None
return state.str_value.lower() == "closed"
return state.str_value().lower() == "closed"
if self.current_cover_position is None:
return None

View File

@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["pyfibaro"],
"requirements": ["pyfibaro==0.6.8"]
"requirements": ["pyfibaro==0.6.9"]
}

View File

@@ -27,7 +27,7 @@ from homeassistant.helpers.icon import icon_for_battery_level
from homeassistant.helpers.json import save_json
from homeassistant.helpers.network import NoURLAvailableError, get_url
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.json import load_json_object
from homeassistant.util.json import load_json
from homeassistant.util.unit_system import METRIC_SYSTEM
from .const import (
@@ -85,7 +85,7 @@ def request_app_setup(
"""Handle configuration updates."""
config_path = hass.config.path(FITBIT_CONFIG_FILE)
if os.path.isfile(config_path):
config_file = load_json_object(config_path)
config_file = load_json(config_path)
if config_file == DEFAULT_CONFIG:
error_msg = (
f"You didn't correctly modify {FITBIT_CONFIG_FILE}, please try"
@@ -161,7 +161,7 @@ def setup_platform(
"""Set up the Fitbit sensor."""
config_path = hass.config.path(FITBIT_CONFIG_FILE)
if os.path.isfile(config_path):
config_file = load_json_object(config_path)
config_file: ConfigType = cast(ConfigType, load_json(config_path))
if config_file == DEFAULT_CONFIG:
request_app_setup(
hass, config, add_entities, config_path, discovery_info=None
@@ -175,10 +175,13 @@ def setup_platform(
if "fitbit" in _CONFIGURING:
configurator.request_done(hass, _CONFIGURING.pop("fitbit"))
access_token: str | None = config_file.get(ATTR_ACCESS_TOKEN)
refresh_token: str | None = config_file.get(ATTR_REFRESH_TOKEN)
expires_at: int | None = config_file.get(ATTR_LAST_SAVED_AT)
if (
(access_token := config_file.get(ATTR_ACCESS_TOKEN)) is not None
and (refresh_token := config_file.get(ATTR_REFRESH_TOKEN)) is not None
and (expires_at := config_file.get(ATTR_LAST_SAVED_AT)) is not None
access_token is not None
and refresh_token is not None
and expires_at is not None
):
authd_client = Fitbit(
config_file.get(CONF_CLIENT_ID),
@@ -189,7 +192,7 @@ def setup_platform(
refresh_cb=lambda x: None,
)
if int(time.time()) - cast(int, expires_at) > 3600:
if int(time.time()) - expires_at > 3600:
authd_client.client.refresh_token()
user_profile = authd_client.user_profile_get()["user"]

View File

@@ -77,7 +77,6 @@ class FreeboxFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# Check permissions
await fbx.system.get_config()
await fbx.lan.get_hosts_list()
await self.hass.async_block_till_done()
# Close connection
await fbx.close()

View File

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

View File

@@ -41,7 +41,7 @@ async def async_setup_platform(
[
GeniusClimateZone(broker, z)
for z in broker.client.zone_objs
if z.data["type"] in GH_ZONES
if z.data.get("type") in GH_ZONES
]
)

View File

@@ -42,7 +42,7 @@ async def async_setup_platform(
[
GeniusSwitch(broker, z)
for z in broker.client.zone_objs
if z.data["type"] == GH_ON_OFF_ZONE
if z.data.get("type") == GH_ON_OFF_ZONE
]
)

View File

@@ -48,7 +48,7 @@ async def async_setup_platform(
[
GeniusWaterHeater(broker, z)
for z in broker.client.zone_objs
if z.data["type"] in GH_HEATERS
if z.data.get("type") in GH_HEATERS
]
)

View File

@@ -3,7 +3,7 @@ from homeassistant.components.device_tracker import SourceType, TrackerEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -34,7 +34,7 @@ async def async_setup_entry(
] = async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data)
# Restore previously loaded devices
dev_reg = dr.async_get(hass)
dev_reg = device_registry.async_get(hass)
dev_ids = {
identifier[1]
for device in dev_reg.devices.values()

View File

@@ -21,7 +21,7 @@ from homeassistant.const import (
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -257,7 +257,7 @@ async def async_setup_entry(
hass: HomeAssistant, old_unique_id: str, new_key: str
) -> None:
"""Migrate unique IDs to the new format."""
ent_reg = er.async_get(hass)
ent_reg = entity_registry.async_get(hass)
if entity_id := ent_reg.async_get_entity_id(
Platform.SENSOR, DOMAIN, old_unique_id

View File

@@ -22,12 +22,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE,
)
from homeassistant.core import Context, HomeAssistant, State, callback
from homeassistant.helpers import (
area_registry as ar,
device_registry as dr,
entity_registry as er,
start,
)
from homeassistant.helpers import area_registry, device_registry, entity_registry, start
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.network import get_url
from homeassistant.helpers.storage import Store
@@ -57,11 +52,15 @@ LOCAL_SDK_MIN_VERSION = AwesomeVersion("2.1.5")
@callback
def _get_registry_entries(
hass: HomeAssistant, entity_id: str
) -> tuple[er.RegistryEntry | None, dr.DeviceEntry | None, ar.AreaEntry | None,]:
) -> tuple[
entity_registry.RegistryEntry | None,
device_registry.DeviceEntry | None,
area_registry.AreaEntry | None,
]:
"""Get registry entries."""
ent_reg = er.async_get(hass)
dev_reg = dr.async_get(hass)
area_reg = ar.async_get(hass)
ent_reg = entity_registry.async_get(hass)
dev_reg = device_registry.async_get(hass)
area_reg = area_registry.async_get(hass)
if (entity_entry := ent_reg.async_get(entity_id)) and entity_entry.device_id:
device_entry = dev_reg.devices.get(entity_entry.device_id)

View File

@@ -8,7 +8,7 @@ from homeassistant.const import (
ATTR_LONGITUDE,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -44,7 +44,7 @@ async def async_setup_entry(
] = async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data)
# Restore previously loaded devices
dev_reg = dr.async_get(hass)
dev_reg = device_registry.async_get(hass)
dev_ids = {
identifier[1]
for device in dev_reg.devices.values()

View File

@@ -342,12 +342,14 @@ def get_next_departure(
origin_stop_time.departure_time
LIMIT :limit
"""
result = schedule.engine.execute(
result = schedule.engine.connect().execute(
text(sql_query),
origin_station_id=start_station_id,
end_station_id=end_station_id,
today=now_date,
limit=limit,
{
"origin_station_id": start_station_id,
"end_station_id": end_station_id,
"today": now_date,
"limit": limit,
},
)
# Create lookup timetable for today and possibly tomorrow, taking into
@@ -357,7 +359,8 @@ def get_next_departure(
yesterday_start = today_start = tomorrow_start = None
yesterday_last = today_last = ""
for row in result:
for row_cursor in result:
row = row_cursor._asdict()
if row["yesterday"] == 1 and yesterday_date >= row["start_date"]:
extras = {"day": "yesterday", "first": None, "last": False}
if yesterday_start is None:
@@ -800,7 +803,10 @@ class GTFSDepartureSensor(SensorEntity):
@staticmethod
def dict_for_table(resource: Any) -> dict:
"""Return a dictionary for the SQLAlchemy resource given."""
return {col: getattr(resource, col) for col in resource.__table__.columns}
_dict = {}
for column in resource.__table__.columns:
_dict[column.name] = str(getattr(resource, column.name))
return _dict
def append_keys(self, resource: dict, prefix: str | None = None) -> None:
"""Properly format key val pairs to append to attributes."""

View File

@@ -12,7 +12,7 @@ from aioguardian.errors import GuardianError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -41,7 +41,7 @@ def async_finish_entity_domain_replacements(
entity_replacement_strategies: Iterable[EntityDomainReplacementStrategy],
) -> None:
"""Remove old entities and create a repairs issue with info on their replacement."""
ent_reg = er.async_get(hass)
ent_reg = entity_registry.async_get(hass)
for strategy in entity_replacement_strategies:
try:
[registry_entry] = [

View File

@@ -5,7 +5,7 @@ from homeassistant.components.remote import ATTR_ACTIVITY, ATTR_DELAY_SECS
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import (
@@ -60,7 +60,7 @@ async def _migrate_old_unique_ids(
names_to_ids = {activity["label"]: activity["id"] for activity in data.activities}
@callback
def _async_migrator(entity_entry: er.RegistryEntry):
def _async_migrator(entity_entry: entity_registry.RegistryEntry):
# Old format for switches was {remote_unique_id}-{activity_name}
# New format is activity_{activity_id}
parts = entity_entry.unique_id.split("-", 1)
@@ -78,7 +78,7 @@ async def _migrate_old_unique_ids(
return None
await er.async_migrate_entries(hass, entry_id, _async_migrator)
await entity_registry.async_migrate_entries(hass, entry_id, _async_migrator)
@callback

View File

@@ -13,7 +13,7 @@
"documentation": "https://www.home-assistant.io/integrations/harmony",
"iot_class": "local_push",
"loggers": ["aioharmony", "slixmpp"],
"requirements": ["aioharmony==0.2.9"],
"requirements": ["aioharmony==0.2.10"],
"ssdp": [
{
"manufacturer": "Logitech",

View File

@@ -96,7 +96,7 @@ from .handler import ( # noqa: F401
)
from .http import HassIOView
from .ingress import async_setup_ingress_view
from .repairs import SupervisorRepairs
from .issues import SupervisorIssues
from .websocket_api import async_load_websocket_api
_LOGGER = logging.getLogger(__name__)
@@ -123,7 +123,7 @@ DATA_SUPERVISOR_INFO = "hassio_supervisor_info"
DATA_ADDONS_CHANGELOGS = "hassio_addons_changelogs"
DATA_ADDONS_INFO = "hassio_addons_info"
DATA_ADDONS_STATS = "hassio_addons_stats"
DATA_SUPERVISOR_REPAIRS = "supervisor_repairs"
DATA_SUPERVISOR_ISSUES = "supervisor_issues"
HASSIO_UPDATE_INTERVAL = timedelta(minutes=5)
ADDONS_COORDINATOR = "hassio_addons_coordinator"
@@ -581,9 +581,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
hass.config_entries.flow.async_init(DOMAIN, context={"source": "system"})
)
# Start listening for problems with supervisor and making repairs
hass.data[DATA_SUPERVISOR_REPAIRS] = repairs = SupervisorRepairs(hass, hassio)
await repairs.setup()
# Start listening for problems with supervisor and making issues
hass.data[DATA_SUPERVISOR_ISSUES] = issues = SupervisorIssues(hass, hassio)
await issues.setup()
return True

View File

@@ -36,6 +36,7 @@ X_AUTH_TOKEN = "X-Supervisor-Token"
X_INGRESS_PATH = "X-Ingress-Path"
X_HASS_USER_ID = "X-Hass-User-ID"
X_HASS_IS_ADMIN = "X-Hass-Is-Admin"
X_HASS_SOURCE = "X-Hass-Source"
WS_TYPE = "type"
WS_ID = "id"

View File

@@ -17,7 +17,7 @@ from homeassistant.const import SERVER_PORT
from homeassistant.core import HomeAssistant
from homeassistant.loader import bind_hass
from .const import ATTR_DISCOVERY, DOMAIN
from .const import ATTR_DISCOVERY, DOMAIN, X_HASS_SOURCE
_LOGGER = logging.getLogger(__name__)
@@ -445,6 +445,8 @@ class HassIO:
payload=None,
timeout=10,
return_text=False,
*,
source="core.handler",
):
"""Send API command to Hass.io.
@@ -458,7 +460,8 @@ class HassIO:
headers={
aiohttp.hdrs.AUTHORIZATION: (
f"Bearer {os.environ.get('SUPERVISOR_TOKEN', '')}"
)
),
X_HASS_SOURCE: source,
},
timeout=aiohttp.ClientTimeout(total=timeout),
)

View File

@@ -6,6 +6,7 @@ from http import HTTPStatus
import logging
import os
import re
from urllib.parse import quote, unquote
import aiohttp
from aiohttp import web
@@ -19,13 +20,16 @@ from aiohttp.hdrs import (
TRANSFER_ENCODING,
)
from aiohttp.web_exceptions import HTTPBadGateway
from multidict import istr
from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView
from homeassistant.components.http import (
KEY_AUTHENTICATED,
KEY_HASS_USER,
HomeAssistantView,
)
from homeassistant.components.onboarding import async_is_onboarded
from homeassistant.core import HomeAssistant
from .const import X_HASS_IS_ADMIN, X_HASS_USER_ID
from .const import X_HASS_SOURCE
_LOGGER = logging.getLogger(__name__)
@@ -34,23 +38,53 @@ MAX_UPLOAD_SIZE = 1024 * 1024 * 1024
# pylint: disable=implicit-str-concat
NO_TIMEOUT = re.compile(
r"^(?:"
r"|homeassistant/update"
r"|hassos/update"
r"|hassos/update/cli"
r"|supervisor/update"
r"|addons/[^/]+/(?:update|install|rebuild)"
r"|backups/.+/full"
r"|backups/.+/partial"
r"|backups/[^/]+/(?:upload|download)"
r")$"
)
NO_AUTH_ONBOARDING = re.compile(r"^(?:" r"|supervisor/logs" r"|backups/[^/]+/.+" r")$")
# fmt: off
# Onboarding can upload backups and restore it
PATHS_NOT_ONBOARDED = re.compile(
r"^(?:"
r"|backups/[a-f0-9]{8}(/info|/new/upload|/download|/restore/full|/restore/partial)?"
r"|backups/new/upload"
r")$"
)
NO_AUTH = re.compile(r"^(?:" r"|app/.*" r"|[store\/]*addons/[^/]+/(logo|icon)" r")$")
# Authenticated users manage backups + download logs, changelog and documentation
PATHS_ADMIN = re.compile(
r"^(?:"
r"|backups/[a-f0-9]{8}(/info|/download|/restore/full|/restore/partial)?"
r"|backups/new/upload"
r"|audio/logs"
r"|cli/logs"
r"|core/logs"
r"|dns/logs"
r"|host/logs"
r"|multicast/logs"
r"|observer/logs"
r"|supervisor/logs"
r"|addons/[^/]+/(changelog|documentation|logs)"
r")$"
)
NO_STORE = re.compile(r"^(?:" r"|app/entrypoint.js" r")$")
# Unauthenticated requests come in for Supervisor panel + add-on images
PATHS_NO_AUTH = re.compile(
r"^(?:"
r"|app/.*"
r"|(store/)?addons/[^/]+/(logo|icon)"
r")$"
)
NO_STORE = re.compile(
r"^(?:"
r"|app/entrypoint.js"
r")$"
)
# pylint: enable=implicit-str-concat
# fmt: on
class HassIOView(HomeAssistantView):
@@ -65,38 +99,66 @@ class HassIOView(HomeAssistantView):
self._host = host
self._websession = websession
async def _handle(
self, request: web.Request, path: str
) -> web.Response | web.StreamResponse:
"""Route data to Hass.io."""
hass = request.app["hass"]
if _need_auth(hass, path) and not request[KEY_AUTHENTICATED]:
return web.Response(status=HTTPStatus.UNAUTHORIZED)
return await self._command_proxy(path, request)
delete = _handle
get = _handle
post = _handle
async def _command_proxy(
self, path: str, request: web.Request
) -> web.StreamResponse:
async def _handle(self, request: web.Request, path: str) -> web.StreamResponse:
"""Return a client request with proxy origin for Hass.io supervisor.
This method is a coroutine.
Use cases:
- Onboarding allows restoring backups
- Load Supervisor panel and add-on logo unauthenticated
- User upload/restore backups
"""
headers = _init_header(request)
if path == "backups/new/upload":
# We need to reuse the full content type that includes the boundary
headers[
CONTENT_TYPE
] = request._stored_content_type # pylint: disable=protected-access
# No bullshit
if path != unquote(path):
return web.Response(status=HTTPStatus.BAD_REQUEST)
hass: HomeAssistant = request.app["hass"]
is_admin = request[KEY_AUTHENTICATED] and request[KEY_HASS_USER].is_admin
authorized = is_admin
if is_admin:
allowed_paths = PATHS_ADMIN
elif not async_is_onboarded(hass):
allowed_paths = PATHS_NOT_ONBOARDED
# During onboarding we need the user to manage backups
authorized = True
else:
# Either unauthenticated or not an admin
allowed_paths = PATHS_NO_AUTH
no_auth_path = PATHS_NO_AUTH.match(path)
headers = {
X_HASS_SOURCE: "core.http",
}
if no_auth_path:
if request.method != "GET":
return web.Response(status=HTTPStatus.METHOD_NOT_ALLOWED)
else:
if not allowed_paths.match(path):
return web.Response(status=HTTPStatus.UNAUTHORIZED)
if authorized:
headers[
AUTHORIZATION
] = f"Bearer {os.environ.get('SUPERVISOR_TOKEN', '')}"
if request.method == "POST":
headers[CONTENT_TYPE] = request.content_type
# _stored_content_type is only computed once `content_type` is accessed
if path == "backups/new/upload":
# We need to reuse the full content type that includes the boundary
headers[
CONTENT_TYPE
] = request._stored_content_type # pylint: disable=protected-access
try:
client = await self._websession.request(
method=request.method,
url=f"http://{self._host}/{path}",
url=f"http://{self._host}/{quote(path)}",
params=request.query,
data=request.content,
headers=headers,
@@ -123,20 +185,8 @@ class HassIOView(HomeAssistantView):
raise HTTPBadGateway()
def _init_header(request: web.Request) -> dict[istr, str]:
"""Create initial header."""
headers = {
AUTHORIZATION: f"Bearer {os.environ.get('SUPERVISOR_TOKEN', '')}",
CONTENT_TYPE: request.content_type,
}
# Add user data
if request.get("hass_user") is not None:
headers[istr(X_HASS_USER_ID)] = request["hass_user"].id
headers[istr(X_HASS_IS_ADMIN)] = str(int(request["hass_user"].is_admin))
return headers
get = _handle
post = _handle
def _response_header(response: aiohttp.ClientResponse, path: str) -> dict[str, str]:
@@ -164,12 +214,3 @@ def _get_timeout(path: str) -> ClientTimeout:
if NO_TIMEOUT.match(path):
return ClientTimeout(connect=10, total=None)
return ClientTimeout(connect=10, total=300)
def _need_auth(hass: HomeAssistant, path: str) -> bool:
"""Return if a path need authentication."""
if not async_is_onboarded(hass) and NO_AUTH_ONBOARDING.match(path):
return False
if NO_AUTH.match(path):
return False
return True

View File

@@ -3,20 +3,22 @@ from __future__ import annotations
import asyncio
from collections.abc import Iterable
from functools import lru_cache
from ipaddress import ip_address
import logging
import os
from urllib.parse import quote
import aiohttp
from aiohttp import ClientTimeout, hdrs, web
from aiohttp.web_exceptions import HTTPBadGateway, HTTPBadRequest
from multidict import CIMultiDict
from yarl import URL
from homeassistant.components.http import HomeAssistantView
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import X_AUTH_TOKEN, X_INGRESS_PATH
from .const import X_HASS_SOURCE, X_INGRESS_PATH
_LOGGER = logging.getLogger(__name__)
@@ -42,9 +44,19 @@ class HassIOIngress(HomeAssistantView):
self._host = host
self._websession = websession
@lru_cache
def _create_url(self, token: str, path: str) -> str:
"""Create URL to service."""
return f"http://{self._host}/ingress/{token}/{path}"
base_path = f"/ingress/{token}/"
url = f"http://{self._host}{base_path}{quote(path)}"
try:
if not URL(url).path.startswith(base_path):
raise HTTPBadRequest()
except ValueError as err:
raise HTTPBadRequest() from err
return url
async def _handle(
self, request: web.Request, token: str, path: str
@@ -185,10 +197,8 @@ def _init_header(request: web.Request, token: str) -> CIMultiDict | dict[str, st
continue
headers[name] = value
# Inject token / cleanup later on Supervisor
headers[X_AUTH_TOKEN] = os.environ.get("SUPERVISOR_TOKEN", "")
# Ingress information
headers[X_HASS_SOURCE] = "core.ingress"
headers[X_INGRESS_PATH] = f"/api/hassio_ingress/{token}"
# Set X-Forwarded-For

View File

@@ -70,11 +70,11 @@ UNHEALTHY_REASONS = {
}
class SupervisorRepairs:
"""Create repairs from supervisor events."""
class SupervisorIssues:
"""Create issues from supervisor events."""
def __init__(self, hass: HomeAssistant, client: HassIO) -> None:
"""Initialize supervisor repairs."""
"""Initialize supervisor issues."""
self._hass = hass
self._client = client
self._unsupported_reasons: set[str] = set()
@@ -87,7 +87,7 @@ class SupervisorRepairs:
@unhealthy_reasons.setter
def unhealthy_reasons(self, reasons: set[str]) -> None:
"""Set unhealthy reasons. Create or delete repairs as necessary."""
"""Set unhealthy reasons. Create or delete issues as necessary."""
for unhealthy in reasons - self.unhealthy_reasons:
if unhealthy in UNHEALTHY_REASONS:
translation_key = f"unhealthy_{unhealthy}"
@@ -119,7 +119,7 @@ class SupervisorRepairs:
@unsupported_reasons.setter
def unsupported_reasons(self, reasons: set[str]) -> None:
"""Set unsupported reasons. Create or delete repairs as necessary."""
"""Set unsupported reasons. Create or delete issues as necessary."""
for unsupported in reasons - UNSUPPORTED_SKIP_REPAIR - self.unsupported_reasons:
if unsupported in UNSUPPORTED_REASONS:
translation_key = f"unsupported_{unsupported}"
@@ -149,18 +149,18 @@ class SupervisorRepairs:
await self.update()
async_dispatcher_connect(
self._hass, EVENT_SUPERVISOR_EVENT, self._supervisor_events_to_repairs
self._hass, EVENT_SUPERVISOR_EVENT, self._supervisor_events_to_issues
)
async def update(self) -> None:
"""Update repairs from Supervisor resolution center."""
"""Update issuess from Supervisor resolution center."""
data = await self._client.get_resolution_info()
self.unhealthy_reasons = set(data[ATTR_UNHEALTHY])
self.unsupported_reasons = set(data[ATTR_UNSUPPORTED])
@callback
def _supervisor_events_to_repairs(self, event: dict[str, Any]) -> None:
"""Create repairs from supervisor events."""
def _supervisor_events_to_issues(self, event: dict[str, Any]) -> None:
"""Create issues from supervisor events."""
if ATTR_WS_EVENT not in event:
return

View File

@@ -116,6 +116,7 @@ async def websocket_supervisor_api(
method=msg[ATTR_METHOD],
timeout=msg.get(ATTR_TIMEOUT, 10),
payload=msg.get(ATTR_DATA, {}),
source="core.websocket_api",
)
if result.get(ATTR_RESULT) == "error":

View File

@@ -1,7 +1,6 @@
"""Config flow for HLK-SW16."""
import asyncio
import async_timeout
from hlk_sw16 import create_hlk_sw16_connection
import voluptuous as vol
@@ -36,8 +35,7 @@ async def connect_client(hass, user_input):
reconnect_interval=DEFAULT_RECONNECT_INTERVAL,
keep_alive_interval=DEFAULT_KEEP_ALIVE_INTERVAL,
)
async with async_timeout.timeout(CONNECTION_TIMEOUT):
return await client_aw
return await asyncio.wait_for(client_aw, timeout=CONNECTION_TIMEOUT)
async def validate_input(hass: HomeAssistant, user_input):

View File

@@ -50,12 +50,8 @@ from homeassistant.const import (
)
from homeassistant.core import CoreState, HomeAssistant, ServiceCall, State, callback
from homeassistant.exceptions import HomeAssistantError, Unauthorized
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
instance_id,
)
from homeassistant.helpers import device_registry, entity_registry, instance_id
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entityfilter import (
BASE_FILTER_SCHEMA,
FILTER_SCHEMA,
@@ -435,19 +431,20 @@ def _async_register_events_and_services(hass: HomeAssistant) -> None:
async def async_handle_homekit_unpair(service: ServiceCall) -> None:
"""Handle unpair HomeKit service call."""
referenced = async_extract_referenced_entity_ids(hass, service)
dev_reg = dr.async_get(hass)
dev_reg = device_registry.async_get(hass)
for device_id in referenced.referenced_devices:
if not (dev_reg_ent := dev_reg.async_get(device_id)):
raise HomeAssistantError(f"No device found for device id: {device_id}")
macs = [
cval
for ctype, cval in dev_reg_ent.connections
if ctype == dr.CONNECTION_NETWORK_MAC
if ctype == device_registry.CONNECTION_NETWORK_MAC
]
matching_instances = [
homekit
for homekit in _async_all_homekit_instances(hass)
if homekit.driver and dr.format_mac(homekit.driver.state.mac) in macs
if homekit.driver
and device_registry.format_mac(homekit.driver.state.mac) in macs
]
if not matching_instances:
raise HomeAssistantError(
@@ -701,7 +698,7 @@ class HomeKit:
return False
def add_bridge_triggers_accessory(
self, device: dr.DeviceEntry, device_triggers: list[dict[str, Any]]
self, device: device_registry.DeviceEntry, device_triggers: list[dict[str, Any]]
) -> None:
"""Add device automation triggers to the bridge."""
if self._would_exceed_max_devices(device.name):
@@ -737,8 +734,8 @@ class HomeKit:
async def async_configure_accessories(self) -> list[State]:
"""Configure accessories for the included states."""
dev_reg = dr.async_get(self.hass)
ent_reg = er.async_get(self.hass)
dev_reg = device_registry.async_get(self.hass)
ent_reg = entity_registry.async_get(self.hass)
device_lookup = ent_reg.async_get_device_class_lookup(
{
(BINARY_SENSOR_DOMAIN, BinarySensorDeviceClass.BATTERY_CHARGING),
@@ -833,8 +830,8 @@ class HomeKit:
def _async_register_bridge(self) -> None:
"""Register the bridge as a device so homekit_controller and exclude it from discovery."""
assert self.driver is not None
dev_reg = dr.async_get(self.hass)
formatted_mac = dr.format_mac(self.driver.state.mac)
dev_reg = device_registry.async_get(self.hass)
formatted_mac = device_registry.format_mac(self.driver.state.mac)
# Connections and identifiers are both used here.
#
# connections exists so homekit_controller can know the
@@ -847,7 +844,7 @@ class HomeKit:
# because this was the way you had to fix homekit when pairing
# failed.
#
connection = (dr.CONNECTION_NETWORK_MAC, formatted_mac)
connection = (device_registry.CONNECTION_NETWORK_MAC, formatted_mac)
identifier = (DOMAIN, self._entry_id, BRIDGE_SERIAL_NUMBER)
self._async_purge_old_bridges(dev_reg, identifier, connection)
is_accessory_mode = self._homekit_mode == HOMEKIT_MODE_ACCESSORY
@@ -861,13 +858,13 @@ class HomeKit:
manufacturer=MANUFACTURER,
name=accessory_friendly_name(self._entry_title, self.driver.accessory),
model=f"HomeKit {hk_mode_name}",
entry_type=dr.DeviceEntryType.SERVICE,
entry_type=device_registry.DeviceEntryType.SERVICE,
)
@callback
def _async_purge_old_bridges(
self,
dev_reg: dr.DeviceRegistry,
dev_reg: device_registry.DeviceRegistry,
identifier: tuple[str, str, str],
connection: tuple[str, str],
) -> None:
@@ -923,7 +920,7 @@ class HomeKit:
async def _async_add_trigger_accessories(self) -> None:
"""Add devices with triggers to the bridge."""
dev_reg = dr.async_get(self.hass)
dev_reg = device_registry.async_get(self.hass)
valid_device_ids = []
for device_id in self._devices:
if not dev_reg.async_get(device_id):
@@ -992,7 +989,7 @@ class HomeKit:
@callback
def _async_configure_linked_sensors(
self,
ent_reg_ent: er.RegistryEntry,
ent_reg_ent: entity_registry.RegistryEntry,
device_lookup: dict[str, dict[tuple[str, str | None], str]],
state: State,
) -> None:
@@ -1054,8 +1051,8 @@ class HomeKit:
async def _async_set_device_info_attributes(
self,
ent_reg_ent: er.RegistryEntry,
dev_reg: dr.DeviceRegistry,
ent_reg_ent: entity_registry.RegistryEntry,
dev_reg: device_registry.DeviceRegistry,
entity_id: str,
) -> None:
"""Set attributes that will be used for homekit device info."""
@@ -1073,7 +1070,7 @@ class HomeKit:
ent_cfg[ATTR_INTEGRATION] = ent_reg_ent.platform
def _fill_config_from_device_registry_entry(
self, device_entry: dr.DeviceEntry, config: dict[str, Any]
self, device_entry: device_registry.DeviceEntry, config: dict[str, Any]
) -> None:
"""Populate a config dict from the registry."""
if device_entry.manufacturer:

View File

@@ -28,11 +28,8 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback, split_entity_id
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers import device_registry, entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entityfilter import (
CONF_EXCLUDE_DOMAINS,
CONF_EXCLUDE_ENTITIES,
@@ -633,7 +630,7 @@ async def _async_get_supported_devices(hass: HomeAssistant) -> dict[str, str]:
results = await device_automation.async_get_device_automations(
hass, device_automation.DeviceAutomationType.TRIGGER
)
dev_reg = dr.async_get(hass)
dev_reg = device_registry.async_get(hass)
unsorted: dict[str, str] = {}
for device_id in results:
entry = dev_reg.async_get(device_id)
@@ -642,7 +639,7 @@ async def _async_get_supported_devices(hass: HomeAssistant) -> dict[str, str]:
def _exclude_by_entity_registry(
ent_reg: er.EntityRegistry,
ent_reg: entity_registry.EntityRegistry,
entity_id: str,
include_entity_category: bool,
include_hidden: bool,
@@ -664,7 +661,7 @@ def _async_get_matching_entities(
include_hidden: bool = False,
) -> dict[str, str]:
"""Fetch all entities or entities in the given domains."""
ent_reg = er.async_get(hass)
ent_reg = entity_registry.async_get(hass)
return {
state.entity_id: (
f"{state.attributes.get(ATTR_FRIENDLY_NAME, state.entity_id)} ({state.entity_id})"

View File

@@ -7,7 +7,7 @@ from typing import Any
from pyhap.const import CATEGORY_SENSOR
from homeassistant.core import CALLBACK_TYPE, Context
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.trigger import async_initialize_triggers
from .accessories import TYPES, HomeAccessory
@@ -42,7 +42,7 @@ class DeviceTriggerAccessory(HomeAccessory):
self._remove_triggers: CALLBACK_TYPE | None = None
self.triggers = []
assert device_triggers is not None
ent_reg = er.async_get(self.hass)
ent_reg = entity_registry.async_get(self.hass)
for idx, trigger in enumerate(device_triggers):
type_: str = trigger["type"]
subtype: str | None = trigger.get("subtype")

View File

@@ -14,7 +14,6 @@ PLATFORMS = [
Platform.CLIMATE,
Platform.COVER,
Platform.LIGHT,
Platform.LOCK,
Platform.SENSOR,
Platform.SWITCH,
Platform.WEATHER,

View File

@@ -1,39 +0,0 @@
"""Helper functions for Homematicip Cloud Integration."""
from functools import wraps
import json
import logging
from homeassistant.exceptions import HomeAssistantError
from . import HomematicipGenericEntity
_LOGGER = logging.getLogger(__name__)
def is_error_response(response) -> bool:
"""Response from async call contains errors or not."""
if isinstance(response, dict):
return response.get("errorCode") not in ("", None)
return False
def handle_errors(func):
"""Handle async errors."""
@wraps(func)
async def inner(self: HomematicipGenericEntity) -> None:
"""Handle errors from async call."""
result = await func(self)
if is_error_response(result):
_LOGGER.error(
"Error while execute function %s: %s",
__name__,
json.dumps(result),
)
raise HomeAssistantError(
f"Error while execute function {func.__name__}: {result.get('errorCode')}. See log for more information."
)
return inner

View File

@@ -1,95 +0,0 @@
"""Support for HomematicIP Cloud lock devices."""
from __future__ import annotations
import logging
from typing import Any
from homematicip.aio.device import AsyncDoorLockDrive
from homematicip.base.enums import LockState, MotorState
from homeassistant.components.lock import LockEntity, LockEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericEntity
from .helpers import handle_errors
_LOGGER = logging.getLogger(__name__)
ATTR_AUTO_RELOCK_DELAY = "auto_relock_delay"
ATTR_DOOR_HANDLE_TYPE = "door_handle_type"
ATTR_DOOR_LOCK_DIRECTION = "door_lock_direction"
ATTR_DOOR_LOCK_NEUTRAL_POSITION = "door_lock_neutral_position"
ATTR_DOOR_LOCK_TURNS = "door_lock_turns"
DEVICE_DLD_ATTRIBUTES = {
"autoRelockDelay": ATTR_AUTO_RELOCK_DELAY,
"doorHandleType": ATTR_DOOR_HANDLE_TYPE,
"doorLockDirection": ATTR_DOOR_LOCK_DIRECTION,
"doorLockNeutralPosition": ATTR_DOOR_LOCK_NEUTRAL_POSITION,
"doorLockTurns": ATTR_DOOR_LOCK_TURNS,
}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the HomematicIP locks from a config entry."""
hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id]
async_add_entities(
HomematicipDoorLockDrive(hap, device)
for device in hap.home.devices
if isinstance(device, AsyncDoorLockDrive)
)
class HomematicipDoorLockDrive(HomematicipGenericEntity, LockEntity):
"""Representation of the HomematicIP DoorLockDrive."""
_attr_supported_features = LockEntityFeature.OPEN
@property
def is_locked(self) -> bool | None:
"""Return true if device is locked."""
return (
self._device.lockState == LockState.LOCKED
and self._device.motorState == MotorState.STOPPED
)
@property
def is_locking(self) -> bool:
"""Return true if device is locking."""
return self._device.motorState == MotorState.CLOSING
@property
def is_unlocking(self) -> bool:
"""Return true if device is unlocking."""
return self._device.motorState == MotorState.OPENING
@handle_errors
async def async_lock(self, **kwargs: Any) -> None:
"""Lock the device."""
return await self._device.set_lock_state(LockState.LOCKED)
@handle_errors
async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the device."""
return await self._device.set_lock_state(LockState.UNLOCKED)
@handle_errors
async def async_open(self, **kwargs: Any) -> None:
"""Open the door latch."""
return await self._device.set_lock_state(LockState.OPEN)
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the device."""
return super().extra_state_attributes | {
attr_key: attr_value
for attr, attr_key in DEVICE_DLD_ATTRIBUTES.items()
if (attr_value := getattr(self._device, attr, None)) is not None
}

View File

@@ -60,9 +60,7 @@ def async_sign_path(
url = URL(path)
now = dt_util.utcnow()
params = dict(sorted(url.query.items()))
for param in SAFE_QUERY_PARAMS:
params.pop(param, None)
params = [itm for itm in url.query.items() if itm[0] not in SAFE_QUERY_PARAMS]
encoded = jwt.encode(
{
"iss": refresh_token_id,
@@ -75,7 +73,7 @@ def async_sign_path(
algorithm="HS256",
)
params[SIGN_QUERY_PARAM] = encoded
params.append((SIGN_QUERY_PARAM, encoded))
url = url.with_query(params)
return f"{url.path}?{url.query_string}"
@@ -184,10 +182,11 @@ async def async_setup_auth(hass: HomeAssistant, app: Application) -> None:
if claims["path"] != request.path:
return False
params = dict(sorted(request.query.items()))
del params[SIGN_QUERY_PARAM]
for param in SAFE_QUERY_PARAMS:
params.pop(param, None)
params = [
list(itm) # claims stores tuples as lists
for itm in request.query.items()
if itm[0] not in SAFE_QUERY_PARAMS and itm[0] != SIGN_QUERY_PARAM
]
if claims["params"] != params:
return False

View File

@@ -44,7 +44,7 @@ from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
discovery,
entity_registry as er,
entity_registry,
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
from homeassistant.helpers.entity import DeviceInfo, Entity
@@ -359,8 +359,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Transitional from < 2021.8: update None config entry and entity unique ids
if router_info and (serial_number := router_info.get("SerialNumber")):
hass.config_entries.async_update_entry(entry, unique_id=serial_number)
ent_reg = er.async_get(hass)
for entity_entry in er.async_entries_for_config_entry(
ent_reg = entity_registry.async_get(hass)
for entity_entry in entity_registry.async_entries_for_config_entry(
ent_reg, entry.entry_id
):
if not entity_entry.unique_id.startswith("None-"):

View File

@@ -15,7 +15,7 @@ from homeassistant.components.device_tracker import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -66,7 +66,7 @@ async def async_setup_entry(
# Initialize already tracked entities
tracked: set[str] = set()
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
known_entities: list[Entity] = []
track_wired_clients = router.config_entry.options.get(
CONF_TRACK_WIRED_CLIENTS, DEFAULT_TRACK_WIRED_CLIENTS

View File

@@ -18,11 +18,8 @@ from homeassistant.components import zeroconf
from homeassistant.const import CONF_API_KEY, CONF_HOST
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import (
aiohttp_client,
config_validation as cv,
device_registry as dr,
)
from homeassistant.helpers import aiohttp_client, device_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.util.network import is_ipv6_address
from .const import (
@@ -309,8 +306,10 @@ class HueV2OptionsFlowHandler(config_entries.OptionsFlow):
# create a list of Hue device ID's that the user can select
# to ignore availability status
dev_reg = dr.async_get(self.hass)
entries = dr.async_entries_for_config_entry(dev_reg, self.config_entry.entry_id)
dev_reg = device_registry.async_get(self.hass)
entries = device_registry.async_entries_for_config_entry(
dev_reg, self.config_entry.entry_id
)
dev_ids = {
identifier[1]: entry.name
for entry in entries

View File

@@ -13,12 +13,9 @@ import voluptuous as vol
from homeassistant.components.scene import ATTR_TRANSITION, Scene as SceneEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_platform
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_current_platform,
)
from .bridge import HueBridge
from .const import DOMAIN
@@ -34,7 +31,7 @@ ATTR_BRIGHTNESS = "brightness"
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
async_add_entities: entity_platform.AddEntitiesCallback,
) -> None:
"""Set up scene platform from Hue group scenes."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id]
@@ -65,7 +62,7 @@ async def async_setup_entry(
)
# add platform service to turn_on/activate scene with advanced options
platform = async_get_current_platform()
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_ACTIVATE_SCENE,
{

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
ATTR_VIA_DEVICE,
)
from homeassistant.core import callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry
from ..const import DOMAIN
@@ -29,11 +29,11 @@ async def async_setup_devices(bridge: "HueBridge"):
entry = bridge.config_entry
hass = bridge.hass
api: HueBridgeV2 = bridge.api # to satisfy typing
dev_reg = dr.async_get(hass)
dev_reg = device_registry.async_get(hass)
dev_controller = api.devices
@callback
def add_device(hue_device: Device) -> dr.DeviceEntry:
def add_device(hue_device: Device) -> device_registry.DeviceEntry:
"""Register a Hue device in device registry."""
model = f"{hue_device.product_data.product_name} ({hue_device.product_data.model_id})"
params = {
@@ -51,7 +51,9 @@ async def async_setup_devices(bridge: "HueBridge"):
params[ATTR_VIA_DEVICE] = (DOMAIN, api.config.bridge_device.id)
zigbee = dev_controller.get_zigbee_connectivity(hue_device.id)
if zigbee and zigbee.mac_address:
params[ATTR_CONNECTIONS] = {(dr.CONNECTION_NETWORK_MAC, zigbee.mac_address)}
params[ATTR_CONNECTIONS] = {
(device_registry.CONNECTION_NETWORK_MAC, zigbee.mac_address)
}
return dev_reg.async_get_or_create(config_entry_id=entry.entry_id, **params)
@@ -75,7 +77,9 @@ async def async_setup_devices(bridge: "HueBridge"):
known_devices = [add_device(hue_device) for hue_device in dev_controller]
# Check for nodes that no longer exist and remove them
for device in dr.async_entries_for_config_entry(dev_reg, entry.entry_id):
for device in device_registry.async_entries_for_config_entry(
dev_reg, entry.entry_id
):
if device not in known_devices:
# handle case where a virtual device was created for a Hue group
hue_dev_id = next(x[1] for x in device.identifiers if x[0] == DOMAIN)

View File

@@ -9,7 +9,7 @@ from aiohue.v2.models.relative_rotary import RelativeRotary
from homeassistant.const import CONF_DEVICE_ID, CONF_ID, CONF_TYPE, CONF_UNIQUE_ID
from homeassistant.core import callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry
from homeassistant.util import slugify
from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN
@@ -29,7 +29,7 @@ async def async_setup_hue_events(bridge: "HueBridge"):
hass = bridge.hass
api: HueBridgeV2 = bridge.api # to satisfy typing
conf_entry = bridge.config_entry
dev_reg = dr.async_get(hass)
dev_reg = device_registry.async_get(hass)
btn_controller = api.sensors.button
rotary_controller = api.sensors.relative_rotary

View File

@@ -8,10 +8,14 @@ from energyflip.const import (
SOURCE_TYPE_GAS,
)
from homeassistant.const import UnitOfTime, UnitOfVolume
DATA_COORDINATOR = "coordinator"
DOMAIN = "huisbaasje"
FLOW_CUBIC_METERS_PER_HOUR = f"{UnitOfVolume.CUBIC_METERS}/{UnitOfTime.HOURS}"
"""Interval in seconds between polls to huisbaasje."""
POLLING_INTERVAL = 20

View File

@@ -21,13 +21,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_ID,
UnitOfEnergy,
UnitOfPower,
UnitOfVolume,
UnitOfVolumeFlowRate,
)
from homeassistant.const import CONF_ID, UnitOfEnergy, UnitOfPower, UnitOfVolume
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
@@ -38,6 +32,7 @@ from homeassistant.helpers.update_coordinator import (
from .const import (
DATA_COORDINATOR,
DOMAIN,
FLOW_CUBIC_METERS_PER_HOUR,
SENSOR_TYPE_RATE,
SENSOR_TYPE_THIS_DAY,
SENSOR_TYPE_THIS_MONTH,
@@ -184,7 +179,7 @@ SENSORS_INFO = [
),
HuisbaasjeSensorEntityDescription(
name="Huisbaasje Current Gas",
native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
native_unit_of_measurement=FLOW_CUBIC_METERS_PER_HOUR,
sensor_type=SENSOR_TYPE_RATE,
state_class=SensorStateClass.MEASUREMENT,
key=SOURCE_TYPE_GAS,

View File

@@ -14,7 +14,7 @@ from homeassistant.const import (
)
from homeassistant.core import Context, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import get_capability, get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -48,11 +48,11 @@ async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for Humidifier devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
actions = await toggle_entity.async_get_actions(hass, device_id, DOMAIN)
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

View File

@@ -15,11 +15,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
condition,
config_validation as cv,
entity_registry as er,
)
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.entity import get_capability, get_supported_features
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
@@ -45,11 +41,11 @@ async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device conditions for Humidifier devices."""
registry = er.async_get(hass)
registry = entity_registry.async_get(hass)
conditions = await toggle_entity.async_get_conditions(hass, device_id, DOMAIN)
# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

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