Compare commits

...

405 Commits

Author SHA1 Message Date
Paulus Schoutsen
740209a81d Merge pull request #38964 from home-assistant/rc 2020-08-17 12:43:35 +02:00
Paulus Schoutsen
3d66065fe6 Bumped version to 0.114.2 2020-08-17 09:29:00 +00:00
J. Nick Koston
68c83ea629 Accommodate systems with very large databases and slow disk/cpu (#38947)
On startup we run an sqlite3 quick_check to verify the database
integrity. In the majority of cases, the quick_check takes under
10 seconds.

On systems with very large databases and very slow disk/cpu,
this can take much longer so we freeze the timeout.
2020-08-17 09:28:20 +00:00
pbalogh77
7fb879f191 Fix HC3 compatibility further (#38931)
* Update __init__.py

Further fixes for HC3 compatibility.

* Update homeassistant/components/fibaro/__init__.py

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

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-08-17 09:28:19 +00:00
cgtobi
79924fcc7a Fix Netatmo climate boost/heat event handling (#38923)
* Fix boost event handling

* Replace strings with vars
2020-08-17 09:28:18 +00:00
Oncleben31
5fa14aae7d Fix error in meteo_france for overseas France cities (#38895) 2020-08-17 09:28:18 +00:00
automaton82
588fb283cc Fix the CONF_LOOP check to use the config (#38890) 2020-08-17 09:28:17 +00:00
Martin Hjelmare
48267c2705 Fix ozw pure rgb dimmer light (#38877)
* Fix ozw pure rgb light

* Add test
2020-08-17 09:28:16 +00:00
escoand
9ee6ae8f94 Better timeout handling in samsungtv integration (#38759)
* handle PlatformNotReady

* set timeout in bridge

* set timeout in test

* Revert "handle PlatformNotReady"

This reverts commit 118ee06ba0.
2020-08-17 09:28:15 +00:00
tizzen33
431fe5950c Fix 'Not Available' message for Onkyo integration (#38554) 2020-08-17 09:28:14 +00:00
Paulus Schoutsen
e8b7ddc966 Merge pull request #38894 from home-assistant/rc 2020-08-15 07:35:34 +02:00
Paulus Schoutsen
2485aa772c Bumped version to 0.114.1 2020-08-15 05:17:31 +00:00
J. Nick Koston
10525c7aa7 Adjust slow add entities timeouts to handle slowest known case (#38876)
With this change, we should still be able to startup
in under 10 minutes if something really goes wrong.

The testing done in #38661 was used to determine
these values.
2020-08-15 05:17:13 +00:00
Aidan Timson
0c83156ba4 Update ovoenergy package to v1.1.7 (#38875) 2020-08-15 05:17:12 +00:00
J. Nick Koston
0d4331829c Ensure service browser does not collapse on bad dns names (#38851)
If a device on the network presented a bad name, zeroconf
would throw zeroconf.BadTypeInNameException and the service
browser would die off.  We now trap the exception and continue.
2020-08-15 05:16:49 +00:00
Chris
276874c414 Fix ozw dimming transition (#38850)
* Handle float from light component

* Test with float

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-08-15 05:14:43 +00:00
Aidan Timson
ca5368f01f Fix OVO Energy Sensors (#38849) 2020-08-15 05:14:43 +00:00
Eric Severance
d4a7cefa8d Bump pywemo to 0.4.46 (#38845) 2020-08-15 05:14:42 +00:00
RogerSelwyn
473d0af85d Fix creation of unrequired sensors in OVO energy (#38835) 2020-08-15 05:14:41 +00:00
J. Nick Koston
eb16ca847a Make executor max_workers consistent between python versions (#38821)
The default on python 3.8 is for max_workers is significantly
lower than the default on python 3.7 which means we can get starved
for workers.

To determine a reasonable maximum, the maximum was increased to large
number on 5 production instances.

The number of worker threads created during startup that were
needed to avoid waiting for a thread:

  HOU 1 - 71
  HOU 2 - 48
  OGG 1 - 60
  OGG 2 - 68
  OGG 3 - 64

This lead to a selection of 64 as it was reliable in all cases
and did not have a significant memory impact
2020-08-15 05:14:40 +00:00
Aaron Bach
8feb382ae5 Handle unhandled exceptions related to unavailable SimpliSafe features (#38812) 2020-08-15 05:14:39 +00:00
Paulus Schoutsen
18eeda0e03 Catch upnp timeout error (#38794) 2020-08-15 05:14:39 +00:00
Quentame
caf5020bac Update meteo_france based on code review (#38789)
* Review: if not to pop

* Review: async_add_job --> async_add_executor_job

* Review: const

* Review: start logging messages with capital letter

* Review : UTC isoformated time --> fix "Invalid date""

* Fix hail forecast condition

* Review: _show_setup_form is a callback

* Fix update option

* Review: no icon for next_rain

* Review: inline cities form

* Review: store places as an instance attribute

* UNDO_UPDATE_LISTENER()
2020-08-15 05:14:38 +00:00
Franck Nijhof
8eb6f29c53 Merge pull request #38785 from home-assistant/rc 2020-08-12 12:52:23 +02:00
Franck Nijhof
e3f10f977b Bumped version to 0.114.0 2020-08-12 10:58:46 +02:00
Alexei Chetroi
f33d120ebf Bump up ZHA dependencies (#38775) 2020-08-12 10:58:14 +02:00
Franck Nijhof
c0730a519a Fix lastest version in updater for Supervisor enabled installs (#38773)
* Fix lastest version in update for Supervisor enabled installs

* Fix updater tests
2020-08-12 10:58:09 +02:00
David F. Mulcahey
e60e82c424 Bump ZHA quirks lib to 0.0.43 (#38762) 2020-08-12 10:58:04 +02:00
Joakim Sørensen
7722bb05bc Bump frontend to 20200811.0 (#38760) 2020-08-12 10:58:00 +02:00
etheralm
d1e6fce652 Bump dyson upstream library version (#38756) 2020-08-12 10:57:57 +02:00
J. Nick Koston
eada72e2e1 Install a threading.excepthook on python 3.8 and later (#38741)
Exceptions in threads were being silently discarded and never
logged as the new in python 3.8 threading.excepthook was not
being set.
2020-08-12 10:57:53 +02:00
David F. Mulcahey
48617534e9 Make default duration 1/10th of a second for ZHA light calls (#38739)
* default duration to 1/10th of a second
* update test
2020-08-12 10:57:50 +02:00
Andrew Sayre
6717e67352 Bump pysmartthings 0.7.3 (#38732) 2020-08-12 10:57:46 +02:00
Pascal Vizeli
57e99af9ec Add scikit-build to installed env (#38726) 2020-08-12 10:57:43 +02:00
Paulus Schoutsen
db646141c6 Add scan_tag webhook to mobile app (#38721) 2020-08-12 10:57:39 +02:00
Paulus Schoutsen
e3335eea00 Bumped version to 0.114.0b4 2020-08-10 12:49:12 +00:00
Pascal Vizeli
e8587408ae Update base image 8.2.1 (#38716) 2020-08-10 12:49:04 +00:00
Paulus Schoutsen
7f0c97fad8 Bump updater timeout (#38690) 2020-08-10 12:49:03 +00:00
Paulus Schoutsen
c0be9aca48 Bumped version to 0.114.0b3 2020-08-09 11:30:45 +00:00
On Freund
cb51a00c37 Bump pyvolumio to 0.1.1 (#38685) 2020-08-09 11:30:38 +00:00
Aaron Bach
6f5884805e Fix missing data for Guardian "AP enabled" binary sensor (#38681) 2020-08-09 11:30:37 +00:00
J. Nick Koston
65450d8518 Update aiohomekit to handle homekit devices that do not send format (#38679) 2020-08-09 11:30:36 +00:00
J. Nick Koston
f1e3023d44 Ensure shared zeroconf is passed to homekit controller devices (#38678) 2020-08-09 11:30:36 +00:00
Alejandro Rivera
aa4e879e1a Fix rest_command UnboundLocalError in exception handling (#38656)
```
2020-08-07 22:38:10 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.3903193064] local variable 'response' referenced before assignment
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/rest_command/__init__.py", line 115, in async_service_handler
    async with getattr(websession, method)(
  File "/usr/local/lib/python3.8/site-packages/aiohttp/client.py", line 1012, in __aenter__
    self._resp = await self._coro
  File "/usr/local/lib/python3.8/site-packages/aiohttp/client.py", line 582, in _request
    break
  File "/usr/local/lib/python3.8/site-packages/aiohttp/helpers.py", line 586, in __exit__
    raise asyncio.TimeoutError from None
asyncio.exceptions.TimeoutError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 125, in handle_call_service
    await hass.services.async_call(
  File "/usr/src/homeassistant/homeassistant/core.py", line 1281, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1316, in _execute_service
    await handler.func(service_call)
  File "/usr/src/homeassistant/homeassistant/components/rest_command/__init__.py", line 137, in async_service_handler
    _LOGGER.warning("Timeout call %s", response.url, exc_info=1)
UnboundLocalError: local variable 'response' referenced before assignment
```
2020-08-09 11:30:35 +00:00
Chris Talkington
43961dc36b Fix AccuWeather async timeout (#38654) 2020-08-09 11:30:34 +00:00
Aaron Bach
24c3cbfff9 Bump regenmaschine to 2.1.0 (#38649) 2020-08-09 11:30:33 +00:00
Ole-Martin Heggen
95ffe12264 Fix url in seventeentrack delivered notification (#38646) 2020-08-09 11:30:33 +00:00
Franck Nijhof
eac5619001 Remove tf-models-official from wheels builder (#38637) 2020-08-09 11:30:32 +00:00
Joakim Sørensen
ab9df350fd Update frontend to 20200807.1 (#38626) 2020-08-09 11:30:32 +00:00
starkillerOG
a58a67923b Fix xiaomi_aqara discovery (#38622) 2020-08-09 11:30:31 +00:00
Thomas Hollstegge
a92bc562d3 Make sure groups are initialized before template sensors (#37766)
* Make sure groups are initialized before template sensors

This way users may use the `expand` function in templates to expand
groups and have HA listen for changes to group members.

Fixes #35872

* Patch async_setup_platform instead of async_setup

* Cleanup

* Use an event to avoid sleep

* Update tests/components/template/test_sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-08-09 11:30:30 +00:00
Paulus Schoutsen
214bc81d02 Fix lint 2020-08-07 10:02:10 +00:00
Paulus Schoutsen
b626368b6a Bumped version to 0.114.0b2 2020-08-07 08:45:05 +00:00
J. Nick Koston
30e1ff83b9 Ensure doorbird does not block startup (#38619) 2020-08-07 08:44:53 +00:00
Austin Drummond
6e31a2e67d Expose video doorbell button state to HomeKit (#38617) 2020-08-07 08:44:52 +00:00
Pascal Vizeli
64749a0f85 Bump OpenCV 4.3.0 and Numpy 1.19.1 (#38616) 2020-08-07 08:44:31 +00:00
Paulus Schoutsen
6baded622b Handle unavailable input_select in Google Assistant (#38611) 2020-08-07 08:44:07 +00:00
J. Nick Koston
790a136c0a Ensure homekit pairing barcode is usable on dark themes (#38609) 2020-08-07 08:44:07 +00:00
Pascal Vizeli
4fc56cec1c V2 timeout for async_add_entities (#38601)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-08-07 08:44:06 +00:00
Aidan Timson
93cdd4dbf3 Improve the OVO Energy integration (#38598)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-08-07 08:43:41 +00:00
Jason Hunter
f0f112ff42 Upgrade to TensorFlow 2 (#38384)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-08-07 08:42:37 +00:00
Paulus Schoutsen
ec14bf215a Bumped version to 0.114.0b1 2020-08-06 10:58:06 +00:00
Paulus Schoutsen
9713b5806f Do not print warning when command line switch queries off (#38591) 2020-08-06 10:57:59 +00:00
Pascal Vizeli
e287c9cf08 Revert "Add a timeout for async_add_entities (#38474)" (#38584)
This reverts commit 7590af3930.
2020-08-06 10:57:58 +00:00
Martin Hjelmare
a25f1cc6c3 Fix missing rfxtrx strings (#38570)
* Fix missing rfxtrx strings

* Clean
2020-08-06 10:57:57 +00:00
Erik Montnemery
5bdeb46c12 Suppress MQTT discovery updates without changes (#38568) 2020-08-06 10:57:57 +00:00
Quentame
187f47233c Remove Linky integration (#38565) 2020-08-06 10:57:56 +00:00
starkillerOG
e43fb649a4 Improve Xioami Aqara zeroconf discovery handling (#37469)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-08-06 10:57:55 +00:00
Franck Nijhof
485752a033 Bumped version to 0.114.0b0 2020-08-05 20:32:53 +02:00
Markus Bong
6c5bcbfc3e Add devolo light devices (#37366) 2020-08-05 18:16:21 +02:00
J. Nick Koston
7590af3930 Add a timeout for async_add_entities (#38474) 2020-08-05 18:06:21 +02:00
Bram Kragten
d66ddeb69e Allow to set default dark theme and persist frontend default themes (#38548)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-08-05 17:42:23 +02:00
J. Nick Koston
d0d0403664 Add zeroconf/homekit/ssdp discovery support for custom components (#38466)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-08-05 15:50:56 +02:00
Joakim Sørensen
1ebc420c75 Bump frontend to 20200805.0 (#38557) 2020-08-05 14:59:33 +02:00
Pascal Vizeli
c291d4aa7d Intelligent timeout handler for setup/bootstrap (#38329)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2020-08-05 14:58:19 +02:00
Aidan Timson
caca762088 OVO Energy Integration (#36104)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2020-08-05 14:38:29 +02:00
Steffen Zimmermann
b50f3103fe Bump python-wiffi to 1.0.1 (#38556)
fix missing exception asyncio.IncompleteReadError
2020-08-05 13:41:56 +02:00
Maciej Bieniek
8258dcf41d Add device_info property and simplify generation of unique_id for Airly integration (#38479) 2020-08-05 12:55:14 +02:00
Maciej Bieniek
56f8ced267 Add device_info property for AccuWeather integration (#38480) 2020-08-05 12:50:34 +02:00
Maciej Bieniek
d47900473e Add device_info to GIOS integration (#38503) 2020-08-05 12:47:33 +02:00
J. Nick Koston
a91f5b7192 Prevent ping integration from blocking startup (#38504) 2020-08-05 12:43:35 +02:00
Kevin Fronczak
3fdec7946c Blink auth flow improvement and mini camera support (#38027) 2020-08-05 12:21:14 +02:00
chewbh
3fc5f9deb8 Add Xiaomi Aqara wireless and light switches (2020 model) (#37985) 2020-08-05 12:15:19 +02:00
Peter Nijssen
c0c30bb1cc Update pyrainbird to 0.4.2 (#38542) 2020-08-05 11:42:34 +02:00
dependabot[bot]
74ba209f06 Bump actions/upload-artifact from v2.1.2 to v2.1.3 (#38552)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v2.1.2 to v2.1.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.1.2...268d7547644ab8a9d0c1163299e59a1f5d93f39b)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-05 11:32:53 +02:00
Pedro Lamas
e422274085 Use IP Address (host) provided by mDNS on Elgato Key Light (#38539) 2020-08-05 11:26:17 +02:00
A C++ MaNong
5b234b80e8 Keep webostv source list when TV is off (#38250)
* keep source list when TV is off

* remove source_list reset as the method ends here
2020-08-05 11:01:12 +02:00
J. Nick Koston
7b728b17f7 Add missing timeout to command_line platforms: cover, notify, switch (#38497)
* Add missing timeout to command_line platforms: cover, notify, switch

* add timeout test for notify
2020-08-04 17:00:02 -10:00
J. Nick Koston
dddcb8e299 Add a 60s timeout to shell_command to prevent processes from building up (#38491)
If a process never ended, there was not timeout and they would
build up in the background until Home Assistant crashed.
2020-08-04 16:59:19 -10:00
Steven Looman
c33f309d5f Fix upnp error on unload_entry if device does not exist (#38230) 2020-08-05 02:24:42 +02:00
HomeAssistant Azure
e76db60367 [ci skip] Translation update 2020-08-05 00:02:19 +00:00
Chris Talkington
d89bfe79f9 Allow device class to control icons for tesla (#37526) 2020-08-05 02:00:05 +02:00
Janis Jansons
17c9e31e2c Fix Mikrotik encoding by setting utf8 (#38091) 2020-08-05 01:39:55 +02:00
tizzen33
2d7b9326ee Add new Water Meter Sensor for Toon (#37879)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-08-05 01:02:19 +02:00
Daniel Hjelseth Høyer
e53d770b3d Update pymetno lib, and start using met api v2 (#38547) 2020-08-05 00:31:12 +02:00
Franck Nijhof
1e32d0e2b9 Upgrade toonapi to v0.2.0 (#38543) 2020-08-04 23:42:53 +02:00
Peter Nijssen
ab512a1273 Add spider config flow (#36001) 2020-08-04 22:37:20 +02:00
Franck Nijhof
bbf31b1101 Merge branch 'master' into dev 2020-08-04 22:29:28 +02:00
lawtancool
96c6e4c2f4 Fix Control4 token refresh (#38302) 2020-08-04 21:35:28 +02:00
J. Nick Koston
e208d8b93e Move system log processing out of the event loop (#38445) 2020-08-04 21:21:45 +02:00
cgtobi
31dbdff3c4 Add Netatmo data handler (#35571)
* Fix webhook registration

* Only load camera platform with valid scope

* Add initial data handler and netatmo base class

* Update camera to use data handler

* Update init

* Parallelize API calls

* Remove cruft

* Minor tweaks

* Refactor data handler

* Update climate to use data handler

* Fix pylint error

* Fix climate update not getting fresh data

* Update climate data

* update to pyatmo 4.0.0

* Refactor for pyatmo 4.0.0

* Exclude from coverage until tests are written

* Fix typo

* Reduce parallel calls

* Add heating request attr

* Async get_entities

* Undo parallel updates

* Fix camera issue

* Introduce individual scan interval per device class

* Some cleanup

* Add basic webhook support for climate to improve responsiveness

* Replace ClimateDevice by ClimateEntity

* Add support for turning camera on/off

* Update camera state upon webhook events

* Guard data class registration with lock

* Capture errors

* Add light platform

* Add dis-/connect handling

* Fix set schedule service

* Remove extra calls

* Add service to set person(s) home/away

* Add service descriptions

* Improve service descriptions

* Use LightEntity instead of Light

* Add guard if no data is retrieved

* Make services entity based

* Only raise platform not ready if there is a NOC

* Register webhook even during runtime

* Fix turning off event

* Fix linter error

* Fix linter error

* Exclude light platform from coverage

* Change log level

* Refactor public weather sensor to use data handler

* Prevent too short coordinates

* Ignore modules without _id

* Code cleanup

* Fix test

* Exit early if no home data is retrieved

* Prevent discovery if already active

* Add services to (un-)register webhook

* Fix tests

* Not actually a coroutine

* Move methods to base class

* Address pylint comment

* Address pylint complaints

* Address comments

* Address more comments

* Add docstring

* Use single instance allowed

* Extract method

* Remove cruft

* Write state directly

* Fix test

* Add file to coverage

* Move nested function

* Move nested function

* Update docstring

* Clean up code

* Fix webhook bug

* Clean up listeners

* Use deque

* Clean up prints

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/camera.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/camera.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/camera.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/camera.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/camera.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/netatmo/camera.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Rename data_class variable

* Break when match

* Extract method

* Extract methods

* Rename variable

* Improve comment

* Some refinements

* Extra

* Extract method

* Simplify code

* Improve reability

* Code simplification

* Simplify code

* Simplify code

* Code cleanup

* Fix import

* Clean up

* Clean up magic strings

* Replace data_class_name with CAMERA_DATA_CLASS_NAME

* Replace data_class_name with CAMERA_DATA_CLASS_NAME

* Replace data_class_name with HOMEDATA_DATA_CLASS_NAME

* Replace data_class_name in public weather sensor

* Clean up

* Remove deprecated config options

* Schedule immediate update on camera reconnect

* Use UUID to clearly identify public weather areas

* Use subscription mode

* Move clean up of temporary data classes

* Delay data class removal

* Fix linter complaints

* Adjust test

* Only setup lights if webhook are registered

* Prevent crash with old config entries

* Don't cache home ids

* Remove stale code

* Fix coordinates if entered mixed up by the user

* Move nested function

* Add test case for swapped coordinates

* Only wait for discovery entries

* Only use what I need

* Bring stuff closer to where it's used

* Auto clean up setup data classes

* Code cleanup

* Remove unneccessary lock

* Update homeassistant/components/netatmo/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update tests/components/netatmo/test_config_flow.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Clean up dead code

* Fix formating

* Extend coverage

* Extend coverage

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-08-04 20:46:46 +02:00
Patrick
0780650015 Make ozw CCT use device attributes instead of hard coded values (#38054) 2020-08-04 20:15:21 +02:00
Daniel Correa Lobato
86fc977ff5 Update notify.py (#38526)
Clickatell can returns 202 when a message is accepted for delivery. So, success can be indicated by 200 or 202 code (https://archive.clickatell.com/developers/api-docs/http-status-codes-rest/)
2020-08-04 16:51:15 +02:00
Phil Bruckner
df8e179207 Fix sensor.time intermittent startup exception (#38525) 2020-08-04 16:39:36 +02:00
Stefan Agner
c2f5831181 Support dual stack IP support (IPv4 and IPv6) (#38046)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-08-04 15:34:23 +02:00
pbalogh77
fe07d79744 Fix Fibaro component failure to load with HC3 (#38528)
Fixed a rarely occuring problem (maybe a change with Fibaro HC3) where some scenes don't have a "visible" parameter, which was assumed to be mandatory in the past.
2020-08-04 14:55:03 +02:00
Daniel Perna
0c030cb8cf Update pyhomematic to 0.1.68 (#38530) 2020-08-04 14:42:25 +02:00
Davide Varricchio
45e451271e Bump pyaehw4a1 to 0.3.9 (#38347)
* Bump pyaehw4a1 to 0.3.9

* Add myself to xiaomi miio codeowners (#38350)

* add myself to xiaomi miio codeowners

* Update CODEOWNERS

* Update manifest.json

* Upgrade youtube_dl to version 2020.07.28 (#38328)

* Temporary lock pip to 20.1.1 to avoid build issue (#38358)

* Add wheels job for building core wheels (#38359)

* Bump androidtv to 0.0.47 and adb-shell to 0.2.1 (#38344)

* Add jobs names to Wheels builds (#38363)

* Update aioharmony to 0.2.6 (#38360)

* Update run-in-env.sh (#36577)

* Bump aioambient to 1.2.0 (#38364)

* Bump simplisafe-python to 9.2.2 (#38365)

* Ignore remote Plex clients during plex.tv lookup (#38327)

* Avoid error with ignored harmony config entries (#38367)

* Bump ElkM1 library version. (#38368)

To reduce required version of dependent library.
No code changed.

* Add basic websocket api for OZW (#38265)

* Prevent nut config flow error when checking ignored entries (#38372)

* Revert "Prevent nut config flow error when checking ignored entries (#38372)"

This reverts commit 9e0530df1d.

* Revert "Add basic websocket api for OZW (#38265)"

This reverts commit 3ca93baa55.

* Revert "Bump ElkM1 library version. (#38368)"

This reverts commit 143f55ad12.

* Revert "Avoid error with ignored harmony config entries (#38367)"

This reverts commit 90a10baf38.

* Revert "Ignore remote Plex clients during plex.tv lookup (#38327)"

This reverts commit 67cdeafe21.

* Revert "Bump simplisafe-python to 9.2.2 (#38365)"

This reverts commit 01d68e01c6.

* Revert "Bump aioambient to 1.2.0 (#38364)"

This reverts commit bec6904eb9.

* Revert "Update run-in-env.sh (#36577)"

This reverts commit 53acc1b41e.

* Revert "Update aioharmony to 0.2.6 (#38360)"

This reverts commit a991d6f131.

* Revert "Add jobs names to Wheels builds (#38363)"

This reverts commit 58dcc059c7.

* Revert "Bump androidtv to 0.0.47 and adb-shell to 0.2.1 (#38344)"

This reverts commit 14b4722b69.

* Revert "Add wheels job for building core wheels (#38359)"

This reverts commit cb9e76adb7.

* Revert "Temporary lock pip to 20.1.1 to avoid build issue (#38358)"

This reverts commit b2207ed776.

* Revert "Upgrade youtube_dl to version 2020.07.28 (#38328)"

This reverts commit 144e827ce9.

* Revert "Add myself to xiaomi miio codeowners (#38350)"

This reverts commit 88538254ec.

Co-authored-by: starkillerOG <starkiller.og@gmail.com>
Co-authored-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Jeff Irion <JeffLIrion@users.noreply.github.com>
Co-authored-by: ehendrix23 <hendrix_erik@hotmail.com>
Co-authored-by: Aaron Bach <bachya1208@gmail.com>
Co-authored-by: jjlawren <jjlawren@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Glenn Waters <gwwaters@gmail.com>
Co-authored-by: Charles Garwood <cgarwood@gmail.com>
2020-08-04 12:22:58 +02:00
Jean-Yves Avenard
988cbf12ce Cache emulated hue states attributes between get and put calls to avoid unexpected alexa errors (#38451)
* Wait for the state of the entity to actually change before resolving PUT request

Additionally, we cache the entity's properties for up to two seconds for the successive GET state request

When Alexa issues a command to a Hue hub; it immediately queries the hub for the entity's state to confirm if the command was successful.
It expects the state to be effective immediately after the PUT request has been completed.
There may be a delay for the new state to actually be active, this is particularly obvious when using group lights.
This leads Alexa to report that the light had an error.

So we wait for the state of the entity to actually change before responding to the PUT request.

Due to rounding issue when converting the HA range (0..255) to Hue range (1..254) we now cache the state sets by Alexa and return those cached values for up to two seconds so that Alexa gets the same value as it originally set.

Fixes #38446

* Add new tests verifying emulated_hue behaviour.

* Increase code test coverage.

The remaining uncovered lines can't be tested as they mostly check that the hass framework or the http server properly work.

This commit doesn't attempt to fix exposed issues as it would be out of scope ; it merely create the tests to exercise the whole code.

* Update homeassistant/components/emulated_hue/hue_api.py

* Add test for state change wait timeout

* Preserve the cache long enough for groups to change

* Update tests/components/emulated_hue/test_hue_api.py

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-08-03 15:30:16 -10:00
J. Nick Koston
62c664fbbd Reduce time to reload yaml and check configuration (#38469)
* Reduce time to reload yaml and check configuration

We spend a significant amount of time compiling templates
that we have already compiled.

Use an LRU cache to avoid re-compiling templates that
we frequently use.

* pylint

* switch to WeakValueDictionary

* preen
2020-08-04 03:00:44 +02:00
Ville Skyttä
63403f894d Fix run-in-env.sh sh options (#38520)
Shebang takes only one arg, regression in
f6540e3002
2020-08-03 21:20:12 +02:00
Aaron Bach
d498246fb6 Bump aioambient to 1.2.1 (#38519) 2020-08-03 12:30:46 -06:00
Aaron Bach
6fd39f57ee Remove YAML configuration support for IQVIA (#38141) 2020-08-03 19:35:36 +02:00
Aaron Bach
53e162c922 Remove deprecated Slack attachments framework (#38139) 2020-08-03 19:33:49 +02:00
Cooper Dale
0b0e323632 Fix missing .name at entity_id in service example (#38515)
for propper filename
2020-08-03 18:22:52 +02:00
Eugene Prystupa
809c2980df Log the version reported by Bond hub upon startup to facilitate troub… (#38508) 2020-08-03 17:55:04 +02:00
Erik Montnemery
86e38a8467 Add missing MQTT strings (#38513) 2020-08-03 17:54:09 +02:00
Bram Kragten
a187183bd1 Update frontend to 20200803.0 (#38514) 2020-08-03 17:52:58 +02:00
Shane Qi
85497c6b75 Fix Lutron Caseta devices loading when missing serials (#38255)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-08-03 16:50:47 +02:00
J. Nick Koston
b3fd8a8343 Fix flapping chained task logging test (#38492) 2020-08-03 15:01:15 +02:00
J. Nick Koston
2f1d989df2 Bump hass-nabucasa to avoid the performance penalty loading ecdsa (#38056) 2020-08-03 12:55:30 +02:00
J. Nick Koston
8f2abc2ee1 Fix harmony activity starting initial state (#38439) 2020-08-03 12:55:15 +02:00
jjlawren
e940811a8b Clean up Plex clip handling (#38500) 2020-08-03 12:41:24 +02:00
jjlawren
67312e2d42 Fix lookup by Plex media key when playing on Sonos (#38119) 2020-08-03 12:40:48 +02:00
dependabot[bot]
364aaceb1c Bump actions/upload-artifact from v2.1.1 to v2.1.2 (#38505)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v2.1.1 to v2.1.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.1.1...c8879bf5aef7bef66f9b82b197f34c4eeeb1731b)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-03 10:12:37 +02:00
James Hilliard
064cc52ad6 Add config flow to HLK-SW16 (#37190)
* Add config flow to HLK-SW16

* Use entry_id for unique_id

* Add options update capability

* Refactor entry_id under domain

* Remove name from config

* Set options

* Remove options flow

* remove unneccesary else block from validate_input and move domain cleanup to async_unload_entry

* Add tests and config import

* Add back config schema

* Remove config import

* Refactor unload

* Add back config import

* Update coveragerc

* Don't mock validate_input

* Test duplicate configs

* Add import test

* Use patch for timeout test

* Use mock for testing timeout

* Use MockSW16Client for tests

* Check mock_calls count

* Remove unused NameExists exception

* Remove title from strings.json

* Mock setup for import test

* Set PARALLEL_UPDATES for switch

* Move hass.data.setdefault(DOMAIN, {}) to async_setup_entry
2020-08-02 17:52:53 -10:00
Matthew Garrett
76b46b9175 Provide a unique entity ID for lgsoundbar (#38494)
The device gives us a UUID, so let's just use that to construct a unique
ID.
2020-08-02 16:51:01 -10:00
Eugene Prystupa
542c6cce25 Ensure bond unique ids are unique across hubs (#38496) 2020-08-02 16:50:03 -10:00
HomeAssistant Azure
ce71775722 [ci skip] Translation update 2020-08-03 00:02:55 +00:00
Chris Talkington
1e685a4a00 Optimize ipp tests (#38485)
* optimize ipp tests

* Update test_config_flow.py
2020-08-02 13:02:47 -10:00
clssn
428c376fe4 Update numato-gpio to 0.8.0 (#38415)
* Bump the numato-gpio dependency

This relaxes the pyserial dependency to >=3.1.1 as requested by the
project with respect to the upcoming, stricter pip resolver.

* Update numato-gpio due to deprecation of class BinarySensorDevice
2020-08-02 16:35:21 -05:00
Villhellm
c403c77cff Catch AssertionError when onkyo zone 3 detection fails (#38374)
This error would cause the entire integration to fail. This at least catches it gracefully.
2020-08-02 14:06:16 -05:00
J. Nick Koston
03a0114e10 Avoid shutdown delays when emulated_hue is enabled (#38472)
We would have to wait for the select to timeout for
emulated_hue upnp thread to shutdown

We now close the socket so the select unblocks
right away
2020-08-02 08:32:07 -10:00
Matthias Weiss
34b911203c Add homematic IPWKeyBlindMulti device (#38345) 2020-08-02 17:55:17 +02:00
Xiaonan Shen
e8b6ed5a27 Add platform tests to yeelight (#37745)
* Add platform tests to yeelight

* Update requirements

* Break long string
2020-08-02 16:37:31 +02:00
Robert Van Gorkom
c913d17913 Add bed sensor availability for withings (#37906) 2020-08-02 09:36:14 -05:00
Maciej Bieniek
a57dca1e11 Add sensor platform for AccuWeather integration (#38312)
* Add sensor platform

* Fix typo
2020-08-02 09:22:51 -05:00
RogerSelwyn
2c887dfe12 Update pyskyqhu to 0.1.1 (#38461)
* Fix module pinning in pyskyhub

* Bump pyskyqhub to 0.1.1
2020-08-02 09:13:17 -05:00
Jeff Irion
071b8ed8a5 Fix Android TV ADB authorization (#38471) 2020-08-02 09:08:12 -05:00
Chris Talkington
72b0f95719 Optimize directv config flow tests. (#38460) 2020-08-01 16:39:55 -10:00
Phil Bruckner
9e12e3f96c Allow automation to be turned off without stopping actions (#38436) 2020-08-01 21:31:47 -05:00
HomeAssistant Azure
1c7cc63f4c [ci skip] Translation update 2020-08-02 00:02:48 +00:00
Michaël Arnauts
f09a9abc1c Add optional unique_id attribute to the template platforms (#38011)
* Add unique_id to template platforms

* Update test_binary_sensor.py

* Update test_binary_sensor.py
2020-08-01 12:45:55 -10:00
Oncleben31
6b85e23408 Refactor Météo-France to use API instead of web scraping (#37737)
* Add new python library

* Update requirements

* Remove old libs

* config flow with client.search_places

* WIP: UI config + weather OK

* WIP: sensors

* WIP: add pressure to weather + available to sensor

* WIP: coordinator next_rain + alert

* Make import step working

* migrate to meteofrance-api v0.0.3

* Create coordinator for rain only if data available in API

* Fix avoid creation of rain sensor when not available.

* Add options flow for forecast mode

* Fix import config causing bug with UI

* Add alert sensor

* Add coastal alerts when available (#5)

* Use meteofrance-api feature branch on Github

* Update unit of next_rain sensor

* Test different type of attibutes

* Typo for attribute

* Next rain sensor device class as timestamp

* Better design for rain entity attributes

* use master branch for meteofrance-api

* time displayed in the HA server timezone.

* fix bug when next_rain_date_locale is None

* Add precipitation and cloud cover sensors

* Add variable to avoid repeating computing

* Apply suggestions from code review

Co-authored-by: Quentame <polletquentin74@me.com>

* Attributes names in const.

* Cleaning

* Cleaning: use current_forecast and today_forecast

* Write state to HA after fetch

* Refactor, Log messages and bug fix. (#6)

* Add messages in log

* Refactor using 'current_forecast'.

* Use % string format with _LOGGER

* Remove inconsistent path

* Secure timestamp value and get current day forecast

* new unique_id

* Change Log message to debug

* Log messages improvement

* Don't try to create weather alert sensor if not in covered zone.

* convert wind speed in km/h

* Better list of city in config_flow

* Manage initial CONF_MODE as None

* Review correction

* Review coorections

* unique id correction

* Migrate from previous config

* Make config name detailed

* Fix weather alert sensor unload (#7)

* Unload weather alert platform

* Revert "Unload weather alert platform"

This reverts commit 95259fdee84f30a5be915eb1fbb2e19fcddc97e4.

* second try in async_unload_entry

* Make it work

* isort modification

* remove weather alert logic in sensor.py

* Refactor to avoid too long code lines

Co-authored-by: Quentin POLLET <polletquentin74@me.com>

* Update config tests to Meteo-France (#18)

* Update meteo_france exception name

* Update MeteoFranceClient name used in tests

* Update 'test_user'

* Make test_user works

* Add test test_user_list

* Make test_import works

* Quick & Dirty fix on exception managment. WIP

* allow to catch MeteoFranceClient() exceptions

* remove test_abort_if_already_setup_district

* bump meteofrance-api version

* We do not need to test Exception in flow yet

* Remove unused data

* Change client1 fixture name

* Change client2 fixture name

* Finish cities step

* Test import with multiple choice

* refactor places

* Add option flow test

Co-authored-by: Quentin POLLET <polletquentin74@me.com>

* Fix errors due to missing data in the API (#22)

* fix case where probability_forecast it not in API
* Workaround for probabilty_forecast data null value
* Fix weather alert sensor added when shouldn't
* Add a partlycloudy and cloudy value options in condition map
* Enable snow chance entity

* fix from review

* remove summary

* Other fix from PR review

* WIP: error if no results in city search

* Add test for config_flow when no result in search

* Lint fix

* generate en.json

* Update homeassistant/components/meteo_france/__init__.py

* Update homeassistant/components/meteo_france/__init__.py

* Update homeassistant/components/meteo_france/__init__.py

* Update homeassistant/components/meteo_france/sensor.py

* Update homeassistant/components/meteo_france/__init__.py

* Update homeassistant/components/meteo_france/__init__.py

* string: city input --> city field

Co-authored-by: Quentin POLLET <polletquentin74@me.com>
2020-08-01 22:56:00 +02:00
Chris
607ba08e23 Add node neighbors to ozw websocket api (#38447)
* Add node neighbors to websocket api

* Update homeassistant/components/ozw/websocket_api.py

Co-authored-by: Charles Garwood <cgarwood@newdealmultimedia.com>

* Update tests/components/ozw/test_websocket_api.py

Co-authored-by: Charles Garwood <cgarwood@newdealmultimedia.com>

Co-authored-by: Charles Garwood <cgarwood@newdealmultimedia.com>
2020-08-01 15:50:04 -04:00
Rob Bierbooms
af97141f4f Increase test coverage for rfxtrx integration (#38435)
* Remove rfxtrx from coveragerc

* Tweak binary sensor test

* Tweak light test
2020-08-01 11:26:26 -05:00
Eugene Prystupa
11994d207a Add zeroconf discovery for bond integration (#38448)
* Add zeroconf discovery for bond integration

* Add zeroconf discovery for bond integration (fix typo)

* Add zeroconf discovery for bond integration (PR feedback)

* Add zeroconf discovery for bond integration (PR feedback)

* Add zeroconf discovery for bond integration (PR feedback)
2020-08-01 11:18:40 -05:00
Phil Bruckner
c3a820c4a3 Fix queued script not updating current attribute when queuing (#38432) 2020-08-01 15:51:48 +02:00
Paulus Schoutsen
c3aa9f9a6f Merge pull request #38443 from home-assistant/rc 2020-08-01 15:37:05 +02:00
Franck Nijhof
c4fcd8bd2e Temporary lock pip to 20.1.1 to avoid build issue (#38358) 2020-08-01 13:18:38 +00:00
J. Nick Koston
fe69a85386 Improve logging when a unique id conflict is detected (#38434)
* fix error when unique id is re-used

* add test for the logging

* Update homeassistant/helpers/entity_platform.py

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

* Update homeassistant/helpers/entity_platform.py

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

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-08-01 11:20:37 +02:00
Rob Bierbooms
ff1709979f Add unique ids for "buienradar" platforms weather and camera (#37761)
* Add unique ids for buienradar weather and camera

* Remove prefix from unique ids
2020-08-01 09:13:17 +02:00
Marcio Granzotto Rodrigues
416ee7f143 Add support to climate devices in Google Assistant Fan Trait (#38337)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-08-01 04:05:00 +02:00
Paulus Schoutsen
b209c1a7b5 Bumped version to 0.113.3 2020-08-01 02:02:48 +00:00
Franck Nijhof
82b3fe1ab6 Fix double encoding issue in google_translate TTS (#38429) 2020-08-01 02:02:40 +00:00
Franck Nijhof
7a8d9b6065 Pin yarl dependency to 1.4.2 as core dependency (#38428) 2020-08-01 02:02:22 +00:00
Stefan Lehmann
f4afa2dc68 Fix ads integration after 0.113 (#38402) 2020-08-01 01:59:47 +00:00
cgtobi
2a3947f7cc Fix rmvtransport breaking when destinations don't match (#38401) 2020-08-01 01:59:47 +00:00
Franck Nijhof
47a729495d Ensure Toon webhook ID isn't registered on re-registration (#38376) 2020-08-01 01:59:46 +00:00
J. Nick Koston
293655f988 Prevent nut config flow error when checking ignored entries (#38372) 2020-08-01 01:59:45 +00:00
J. Nick Koston
0ecaab1a7d Avoid error with ignored harmony config entries (#38367) 2020-08-01 01:59:45 +00:00
ehendrix23
9819d70941 Update aioharmony to 0.2.6 (#38360) 2020-08-01 01:59:44 +00:00
Erik Montnemery
abad6dfdd7 Bump pychromecast to 7.2.0 (#38351) 2020-08-01 01:59:43 +00:00
Jeff Irion
dd4e0511a3 Bump androidtv to 0.0.47 and adb-shell to 0.2.1 (#38344) 2020-08-01 01:59:43 +00:00
jjlawren
d65545964b Ignore remote Plex clients during plex.tv lookup (#38327) 2020-08-01 01:59:42 +00:00
J. Nick Koston
d1fbcba7b6 Prevent kodi from blocking startup (#38257)
* Prevent kodi from blocking startup

* Update homeassistant/components/kodi/media_player.py

* isort

* ignore args

* adjustments per review

* asyncio
2020-08-01 01:59:41 +00:00
Xiaonan Shen
1b73bcbff7 Fix songpal already configured check in config flow (#37813)
* Fix already configured check

* Mark endpoint duplicate check as callback
2020-08-01 01:59:41 +00:00
shred86
7a2f6a5006 Add Abode camera on and off support (#35164)
* Add Abode camera controls

* Add tests for camera turn on and off service

* Bump abodepy version

* Bump abodepy version and updates to reflect changes

* Update manifest
2020-08-01 01:59:40 +00:00
Eugene Prystupa
2e340d2c2f Update bond-api to 0.1.8 (#38442)
* Bump bond API dependency version

* Bump bond API dependency version (PR feedback)
2020-07-31 19:36:02 -05:00
HomeAssistant Azure
04e5fc7ccd [ci skip] Translation update 2020-08-01 00:03:18 +00:00
Aidan Timson
a9c34b1d2b Update aioazuredevops to v1.3.5 (#38441) 2020-07-31 18:43:14 -05:00
Franck Nijhof
1c9a36b731 Fix double encoding issue in google_translate TTS (#38429) 2020-07-31 22:06:17 +02:00
Franck Nijhof
f4c0dc99c2 Pin yarl dependency to 1.4.2 as core dependency (#38428) 2020-07-31 22:06:02 +02:00
J. Nick Koston
476235a259 Restore the ability to tell when a harmony activity is starting (#38335)
* Restore the ability to tell when a harmony activity is starting

* adjust for poweroff

* switch to activity name for activity starting

* adjust

* do not set starting on initial update
2020-07-31 09:55:38 -10:00
Austin Drummond
9d0f58009e Add support for HomeKit doorbell (#38419)
* Add support for HomeKit doorbell

* Update homeassistant/components/homekit/type_cameras.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/homekit/type_cameras.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* add speaker service for doorbells

* fixed test as doorbell char requires null value

* removed null value for doorbell presses. and removed broken override of default values

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-07-31 09:41:36 -10:00
Andrew Sayre
bb69aba051 Remove unused SmartThings capability subscriptions (#38128) 2020-07-31 17:40:23 +02:00
Charles Garwood
49cbc9735c Add identifiers to device registry api output (#38427) 2020-07-31 15:47:01 +02:00
Eugene Prystupa
c795c68bc0 Support 'stop' action for covers in device automation (#38219) 2020-07-31 14:45:03 +02:00
cgtobi
d02c432e4d Fix rmvtransport breaking when destinations don't match (#38401) 2020-07-31 14:38:49 +02:00
Eugene Prystupa
89cbcdb9dc Abort bond hub config flow if hub is already registered (#38416)
Co-authored-by: Chris Talkington <chris@talkingtontech.com>
2020-07-31 14:08:25 +02:00
Stefan Lehmann
0136c565eb Fix ads integration after 0.113 (#38402) 2020-07-31 13:59:32 +02:00
J. Nick Koston
79055487ed Simplify generate_entity_id (#38418)
* Simplify generate_entity_id

Use similar optimized logic for async_generate_entity_id
from entity_registry that was already optimized

* pylint

* make generate_entity_id a wrapper around async_generate_entity_id instead
2020-07-31 08:50:42 +02:00
J. Nick Koston
57883ec10a Fix variable error during stream close (#38417) 2020-07-30 16:58:17 -10:00
HomeAssistant Azure
4bd9509fa7 [ci skip] Translation update 2020-07-31 00:02:40 +00:00
Xiaonan Shen
695585b68c Add battery sensor to xiaomi_aqara (#38004) 2020-07-30 18:38:35 -05:00
Eric Severance
06ddb2c95e Bump pywemo to 0.4.45 (#38414) 2020-07-30 18:19:43 -05:00
Eugene Prystupa
0493f1c206 Generate bond config entry ID from the hub metadata (#38354)
* Generate bond config entry ID from the hub metadata

* Generate bond config entry ID from the hub metadata (PR feedback)
2020-07-30 18:00:58 -05:00
Franck Nijhof
ecf22198c5 Ensure Toon webhook ID isn't registered on re-registration (#38376) 2020-07-30 21:37:34 +02:00
Maciej Bieniek
ad0560ef37 Improve tests for Airly integration (#38357)
* Add tests

* More tests

* Add PARALLEL_UPDATES

* Change Quality scale to platinum

* Change PARALLEL_UPDATES value
2020-07-30 08:41:18 -10:00
Ville Skyttä
c2a21fa496 Update coordinator improvements (#38366)
* Make generic

* Add type info to bunch of uses

* Recognize requests exceptions

* Recognize urllib exceptions
2020-07-30 18:04:00 +03:00
On Freund
76e8870e98 Clean up Volumio code (#38400) 2020-07-30 16:51:46 +02:00
Eugene Prystupa
a00aa6740e Fix bond fans without defined max_speed (#38382) 2020-07-30 14:44:26 +02:00
Sergiy Maysak
00a4bcff3d Bump wirelesstagpy to 0.4.1 (#38387) 2020-07-30 11:45:04 +02:00
Marcio Granzotto Rodrigues
8ab1b41974 Add support for dimmable bond lights (#38203)
* Add support for dimmable lights

* Fix formatting

* Add supported features test on Bond Light

* Add more tests to bond light and fixes comments

* Fix rebase conflict resolution

* Apply suggestions from code review

Co-authored-by: Chris Talkington <chris@talkingtontech.com>
2020-07-29 20:01:59 -05:00
Franck Nijhof
fa9866db96 Add support for multiple time triggers in automations (#37975)
* Add support for multiple time triggers in automations

* Attach with single callback

* Patch time in tests

* Improve test coverage

* Adjusting my facepalm moment
2020-07-29 14:51:30 -10:00
HomeAssistant Azure
99b624a676 [ci skip] Translation update 2020-07-30 00:03:25 +00:00
Erik Montnemery
e3dc8a1ff2 Bump pychromecast to 7.2.0 (#38351) 2020-07-29 23:46:14 +02:00
J. Nick Koston
1b593e3169 Prevent nut config flow error when checking ignored entries (#38372) 2020-07-29 23:20:19 +02:00
Charles Garwood
13e8e28778 Add basic websocket api for OZW (#38265) 2020-07-29 22:35:26 +02:00
Glenn Waters
c5ca484eaa Bump ElkM1 library version. (#38368)
To reduce required version of dependent library.
No code changed.
2020-07-29 21:49:10 +02:00
J. Nick Koston
ed104d1927 Avoid error with ignored harmony config entries (#38367) 2020-07-29 21:20:06 +02:00
jjlawren
1d987b4846 Ignore remote Plex clients during plex.tv lookup (#38327) 2020-07-29 20:56:32 +02:00
Aaron Bach
497c1587fe Bump simplisafe-python to 9.2.2 (#38365) 2020-07-29 12:12:07 -06:00
Aaron Bach
e86fd9af8a Bump aioambient to 1.2.0 (#38364) 2020-07-29 11:56:44 -06:00
ehendrix23
b916eb6cf2 Update run-in-env.sh (#36577) 2020-07-29 19:50:09 +02:00
ehendrix23
1d01a5ed7b Update aioharmony to 0.2.6 (#38360) 2020-07-29 19:49:13 +02:00
Franck Nijhof
cb40ee342e Add jobs names to Wheels builds (#38363) 2020-07-29 18:47:48 +02:00
Jeff Irion
98ce4897ab Bump androidtv to 0.0.47 and adb-shell to 0.2.1 (#38344) 2020-07-29 17:16:24 +02:00
Franck Nijhof
167b10ccc1 Add wheels job for building core wheels (#38359) 2020-07-29 16:11:06 +02:00
Franck Nijhof
417e00ee9c Temporary lock pip to 20.1.1 to avoid build issue (#38358) 2020-07-29 15:31:29 +02:00
Josef Schlehofer
00e50d18b9 Upgrade youtube_dl to version 2020.07.28 (#38328) 2020-07-29 09:12:48 -04:00
starkillerOG
b7976d2856 Add myself to xiaomi miio codeowners (#38350)
* add myself to xiaomi miio codeowners

* Update CODEOWNERS

* Update manifest.json
2020-07-29 11:21:47 +02:00
HomeAssistant Azure
ff3d76b464 [ci skip] Translation update 2020-07-29 00:02:39 +00:00
Xiaonan Shen
d2022aa07b Fix songpal already configured check in config flow (#37813)
* Fix already configured check

* Mark endpoint duplicate check as callback
2020-07-29 01:49:43 +02:00
J. Nick Koston
03582402fa Add debug logging for when a chain of tasks blocks startup (#38311)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-07-28 18:24:29 +02:00
Franck Nijhof
2c6686c5e1 Remove AdGuard version check (#38326) 2020-07-28 17:51:35 +02:00
J. Nick Koston
e8c9734f3a Bump tesla-powerwall to 0.2.12 to handle powerwall firmware 1.48+ (#38180) 2020-07-28 17:26:06 +02:00
dependabot[bot]
a92a7ec848 Bump actions/upload-artifact from 2.1.0 to v2.1.1 (#38315)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-28 12:05:20 +02:00
Kyle Hendricks
0a7dc40712 Fix issue with certain Samsung TVs repeatedly showing auth dialog (#38308)
Through some testing with the samsungtvws library, it was determined
that the issue is related to the short read timeout (1s).  Increasing
the timeout to 10s should solve the issue.
2020-07-28 12:03:56 +02:00
Matthew Garrett
508fc3fa0e Fix lg_soundbar callback (#38259)
* Don't schedule an update if the hass instance isn't instantiated

If we get a status update packet before self.hass exists, we trip a
"assert self.hass is not None" that was added in 0.112 and setup fails.

* Fix callback hander properly

The right fix is to register the callback after hass is ready for it.

* Remove unnecessary check

This is now guaranteed by the core code.

* Don't request an immediate device update and do an async connect.

* Remove unnecessary return
2020-07-28 09:55:24 +02:00
Chris Mandich
ae8a38757a Update PyFlume version, support for multiple state attributes (#38138)
* Update PyFlume version, support for multiple state attributes

* Update PyFlume to resolve issue https://github.com/ChrisMandich/PyFlume/issues/7

* Update PyFlume package to 0.5.2, flatten values in sensor

* Delete setup

* Remove 'current_interval' from attributes and round values to 1 decimal place.

* Add missing brackets to remove 'current_interval' from attributes

* Set attribute keys explicitly, check attribute format.

* Breakout intervals into separate sensors.

* Update 'unit_of_measurement' for each sensor, update sensor 'available', remove unusued variables

* Update "Device unique ID."

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

* Update PyFlume, resolve API query update for request.

* Cleanup debug logging

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-07-27 21:30:38 -10:00
J. Nick Koston
77b6f8c9f2 Prevent speedtest from blocking startup or causing other intergations to fail setup (#38305)
When speedtest starts up, it would saturate the network interface and cause other
integrations to randomly fail to setup. We now wait to do the first speed test
until after the started event is fired.
2020-07-27 22:57:36 -07:00
J. Nick Koston
f06ae1fa95 Prevent kodi from blocking startup (#38257)
* Prevent kodi from blocking startup

* Update homeassistant/components/kodi/media_player.py

* isort

* ignore args

* adjustments per review

* asyncio
2020-07-28 07:43:42 +02:00
Chris
5fef9653a8 Fix ozw dimming duration (#38254)
* Dimming duration fix

Fixes #38068 - allows dimming duration to 7620 (default of 7621)

* Forgot to commit my test updates

* Added backwards compatibility with pre-150+ builds

Added tests for backwards compatibility

* Upped the build number cut off

* Add check for major.minor version as well

* Fix major.minor detection

* Adjust variable name

* Adjust version checking logic

* Math is hard

* Rename files, adjust test names

* Update doc string
2020-07-28 07:37:09 +02:00
Eugene Prystupa
c29f412a70 Add debug logging for bond (#38304) 2020-07-27 21:53:56 -05:00
Teemu R
213496095f Bump python-miio to 0.5.3 (#38300) 2020-07-27 21:26:29 -05:00
Eugene Prystupa
020dd39c08 Apply changes from bond code review (#38303) 2020-07-27 21:18:24 -05:00
Marcio Granzotto Rodrigues
02e2c40c48 Bond - Make assumed state conditional (#38209) 2020-07-27 20:39:23 -05:00
HomeAssistant Azure
e6e3517a94 [ci skip] Translation update 2020-07-28 00:04:53 +00:00
Joakim Plate
0bcee21333 Restore rfxtrx state to off when delay off is in effect (#38239) 2020-07-28 01:45:41 +02:00
Jeroen Van den Keybus
a1e2bce1b9 Fix detection of zones 2 and 3 in Onkyo/Pioneer amplifiers (#38234) 2020-07-28 01:40:21 +02:00
Joakim Plate
c93fc8af4a Clean up commands generation for rfxtrx (#38236) 2020-07-28 00:44:30 +02:00
Joakim Plate
c3966a5ef2 Setup rfxtrx event listener directly (#38298) 2020-07-28 00:29:35 +02:00
Phil Bruckner
1158925b53 Fix repeat action when variables present (#38237) 2020-07-27 16:51:34 -05:00
David Bonnes
bea1570354 Delint recent change to evohome (#38294) 2020-07-27 23:17:07 +02:00
Marcio Granzotto Rodrigues
561e4b537a Fix #38289 issue with xboxapi lib (#38293) 2020-07-27 14:56:39 -04:00
J. Nick Koston
ddf7ceecd4 Prevent harmony from resetting state with multiple turn ons (#38183)
Automations or HomeKit may turn the device on multiple times
when the current activity is already active which will cause
harmony to lose state.  This behavior is unexpected as turning
the device on when its already on isn't expected to reset state.
2020-07-27 06:31:30 -10:00
Greg Dowling
f38e1ae2c0 Don't set up callbacks until entity is created. (#38251) 2020-07-27 17:15:28 +02:00
David Bonnes
26bb604243 Remove evohome hvac_action as it is inaccurate (#38244) 2020-07-27 15:20:18 +02:00
James Callaghan
818949abd9 Corrected typo (#38278) 2020-07-27 15:19:10 +02:00
Phil Bruckner
1a760c63d0 Fix parallel script containing repeat or choose action with max_runs > 10 (#38243) 2020-07-27 10:43:58 +02:00
Markus Bong
569caf9e40 Change devolo Home Control entity naming (#38275)
* adding suffix after the entity name just in case the device class is not known

* remove if else
2020-07-27 10:17:45 +02:00
On Freund
b226a7183f Add config flow to Volumio (#38252) 2020-07-27 09:19:19 +02:00
J. Nick Koston
8b06d1d4bd Prevent onvif from blocking startup (#38256) 2020-07-27 08:51:53 +02:00
MikeTsenatek
da30ed06d8 Update holidays to 0.10.3 (#38246) 2020-07-27 08:17:40 +02:00
Mister Wil
8fec0da5be Fix Skybell useragent (#38245) 2020-07-27 08:08:01 +02:00
J. Nick Koston
56186a3d75 Improve setup retry logic to handle inconsistent powerview hub availability (#38249) 2020-07-26 17:01:29 -10:00
Maciej Bieniek
2b0914994d Add changes from comments after merging AccuWeather (#38227)
* Fix documentation url

* Return None instead STATE_UNKNOWN

* Invert forecast check

* Patch async_setup_entry in test_create entry

* Improve test name, docstring and add comment
2020-07-26 20:00:47 -05:00
HomeAssistant Azure
8abdc2c969 [ci skip] Translation update 2020-07-27 00:02:58 +00:00
Eugene Prystupa
4d73f107c4 Implement resilient startup for bond integration with ConfigEntryNotReady support (#38253) 2020-07-26 18:27:18 -05:00
David Bonnes
455ac1cadf fix issue #34559 (#38241) 2020-07-26 21:13:10 +01:00
shred86
36cb818cd0 Add Abode camera on and off support (#35164)
* Add Abode camera controls

* Add tests for camera turn on and off service

* Bump abodepy version

* Bump abodepy version and updates to reflect changes

* Update manifest
2020-07-26 08:59:11 -10:00
Alan Tse
093bd863ba Add update available binary sensor to Tesla (#37991)
* Add update available binary sensor to Tesla

* Bump teslajsonpy to 0.10.1

* Add check for DEVICE_CLASS

* Change to relative import
2020-07-26 08:00:07 -10:00
Eugene Prystupa
2d6eb5c05d Refactor bond unit tests to reduce boilerplate (#38177)
* Refactor bond unit tests to reduce boilerplate

* Refactor bond unit tests to reduce boilerplate (PR feedback)

* Refactor bond unit tests to reduce boilerplate (PR feedback, nullcontext)
2020-07-26 12:15:21 -05:00
Ville Skyttä
34ac4e78af Fix libav install in Travis CI (#38221) 2020-07-26 15:56:00 +02:00
J. Nick Koston
f6b0f8d6de Update logbook to use async_add_executor_job (#38217) 2020-07-26 10:42:28 +02:00
J. Nick Koston
a39aec862e Attempt to fix islamic prayer times tests (#38220)
* Attempt to fix islamic_prayer_times tests

* adj
2020-07-26 10:26:32 +02:00
J. Nick Koston
34d01d5e47 Mark event tests to run as callbacks (#38212)
* Mark event tests to run as callbacks

* revert change to same state check that is expected to run in a thread
2020-07-25 17:52:48 -10:00
HomeAssistant Azure
d1464211a6 [ci skip] Translation update 2020-07-26 00:04:14 +00:00
Aidan Timson
dcba45e67d Add Azure DevOps Integration (#33765)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-07-26 00:04:10 +02:00
Joakim Plate
fd11748a1a Make rfxtrx RfyDevices have sun automation switches (#38210)
* RfyDevices have sun automation

* We must accept sun automation commands for switch

* Add test for Rfy sun automation
2020-07-25 22:56:58 +02:00
Emil Stjerneman
85c856cfa3 Volvo on call updates (#38142)
* Add "doors_tailgate_open" and "average_speed" to resource list

* Bump volvooncall from 0.8.7 to 0.8.12

* Bump volvooncall in requirements_all.txt
2020-07-25 14:48:19 -05:00
Robert Van Gorkom
da380d89c2 Removing gogogate2 emulated cover transitional states. (#38199) 2020-07-25 21:43:45 +02:00
Joakim Plate
1776540757 Rfxtrx fixup config entry creation (#38185)
* Make sure import flow completely replace existing config

* Make sure added device contain correct config data

* Revert change to directly run init
2020-07-25 19:13:10 +02:00
J. Nick Koston
3206f4dc83 Support multiple camera streams in HomeKit (#37968)
* Support multiple camera stream in HomeKit

* Update homeassistant/components/homekit/type_cameras.py

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

* Revert "Update homeassistant/components/homekit/type_cameras.py"

This reverts commit d7624c5bff.

* Update homeassistant/components/homekit/type_cameras.py

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

* Update homeassistant/components/homekit/type_cameras.py

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

* black

* bump pyhap

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-07-25 07:12:14 -10:00
Phil Bruckner
bbc8748e3b Stop automation runs when turned off or reloaded (#38174)
* Add automation turn off / reload test

* Stop automation runs when turned off or reloaded
2020-07-25 12:19:55 +02:00
Daniel Hjelseth Høyer
662d79eb86 Prevent unnecessary updates of met component (#38168) 2020-07-25 11:18:30 +02:00
J. Nick Koston
a07f4e0986 Ignore harmony hubs ips that are already configured during ssdp discovery (#38181)
We would connect to the hub via discovery and via setup
around the same time.  This put additional load on the hub
which can increase the risk of timeouts.
2020-07-25 11:13:47 +02:00
J. Nick Koston
973688d87e Bump netdisco to 2.8.1 (#38173)
* Bump netdisco to 2.8.1

* bump ssdp
2020-07-24 21:00:08 -10:00
Nick Whyte
7599997a46 Enable Homekit remote support for devices without play/pause (#37180)
* Enable Homekit remote support for devices without play/pause

* linting

* Update tests

* code review

* code review
2020-07-24 18:20:29 -10:00
J. Nick Koston
b868f13591 Ensure all track time change tests mock a specific start time (#38178)
* Ensure all track time change tests mock a specific start time

* make sure tests calling async_track_utc_time_change fire time in utc
2020-07-24 19:04:36 -07:00
HomeAssistant Azure
a1ebb52813 [ci skip] Translation update 2020-07-25 00:04:22 +00:00
Markus Korbel
b55d1127de Added 2020 version Aqara double wall switch (#38164)
Added support for new 2020 version of the Aqara D1 double wall switch (lumi.remote.b286acn02)
Confirmed that all button press events use the same codes after updating deconz rest api to add support for this switch.
A contributor of the API currently has the working version @ git clone --branch Legrand-teleruptor https://github.com/Smanar/deconz-rest-plugin.git
2020-07-24 23:17:39 +02:00
Maciej Bieniek
581c4a4edd Add AccuWeather integration (#37166)
* Initial commit

* Fix strings

* Fix unit system

* Add config_flow tests

* Simplify tests

* More tests

* Update comment

* Fix pylint error

* Run gen_requirements_all

* Fix pyline error

* Round precipitation and precipitation probability

* Bump backend library

* Bump backend library

* Add undo update listener on unload

* Add translation key for invalid_api_key

* Remove entity_registry_enabled_default property

* Suggested change

* Bump library
2020-07-24 15:59:15 -05:00
Daniel Hjelseth Høyer
9fe142a114 Prevent unnecessary updates of sun component (#38169) 2020-07-24 10:46:05 -10:00
Daniel Hjelseth Høyer
8943954b18 Prevent unnecessary updates of zone component (#38167) 2020-07-24 10:45:34 -10:00
Eugene Prystupa
69203b5373 Gracefully handle bond API errors and timeouts through available state (#38137)
* Gracefully handle API errors and timeouts through available state

* Gracefully handle API errors and timeouts through available state
2020-07-24 15:14:47 -05:00
Heiko Rothe
21db4a4160 Fix Xbox Live integration (#38146)
* Fix Xbox Live component

The API moved to a different domain, so the integration was broken.
The library upgrade contains the required fixes.

* Fix API connectivity check

* Access dict values directly
2020-07-24 21:45:59 +02:00
Joakim Plate
632a36d819 Support rfxtrx smoke detectors, motion sensors as binary_sensors (#38000)
* Add binary sensor support to motion sensors and smoke detectors

* Add support for new sensor events as binary sensors

Adds a default device_class for motion sensors and smoke detector

* Use device type instead of event to set class

* Add some additional binary values
2020-07-24 17:16:41 +02:00
Franck Nijhof
84df0efb5e Upgrade coverage to 5.2.1 (#38158) 2020-07-24 17:03:10 +02:00
Kevin Fronczak
3149cf6849 Bump python-slugify to 4.0.1 (#38140)
* Bump python-slugify to 4.0.1

* Dummy commit to re-trigger tests
2020-07-24 16:55:54 +02:00
Philipp Schmitt
027ece52e6 Fix Nuki Locks and Openers not being available after some time (#38159) 2020-07-24 16:42:42 +02:00
Tomasz
19e06c613b convert_until isn't returning anything (#38157) 2020-07-24 15:24:19 +01:00
Aaron Bach
5f6bd22f18 Remove leftover print statement (#38163) 2020-07-24 16:11:02 +02:00
Thomas Delaet
3ff5c17009 Support unavailable state in template fan (#38114)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-07-24 15:48:07 +02:00
Joakim Plate
5d28e109e8 Asyncify rfxtrx startup and event handling (#38155)
* Asyncify startup and event handling

* Adjust linting error

* Must use the thread safe add_job function

* Switch to correct async function
2020-07-24 14:40:10 +02:00
Erik Montnemery
686e6b8fc3 Add test (#37890) 2020-07-24 12:29:19 +02:00
Rob Bierbooms
c76b11f9d7 Write device_id to ConfigEntry of rfxtrx integration (#38064)
* Write device_id to ConfigEntry

* Rework based on review comment

* Add hass add job

* Cleanup
2020-07-24 11:49:48 +02:00
Franck Nijhof
0b9663a23b Fix incorrect mesurement in Toon for meter low (#38149) 2020-07-24 11:00:17 +02:00
Phil Bruckner
2f87da8aa9 Fix script repeat variable lifetime (#38124) 2020-07-23 23:11:21 -07:00
J. Nick Koston
a7459b3126 Log which task is blocking startup when debug logging is on (#38134)
* Log which task is blocking startup when debug logging for homeassistant.core is on

* test needs to go one level deeper now
2020-07-23 20:03:42 -06:00
Aaron Bach
a5b7a2c228 Fix SimpliSafe to work with new MFA (#38097)
* Fix SimpliSafe to work with new MFA

* Code review (part 1)

* Input needed from Martin

* Code review

* Code review

* Restore YAML

* Tests

* Code review

* Remove JSON patching in tests

* Add reauth test

* One more reauth test

* Don't abuse the word "conf"

* Update homeassistant/components/simplisafe/config_flow.py

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

* Test coverage

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-07-23 20:02:29 -06:00
Eugene Prystupa
2dfd767b8c Upgrade bond-api to 0.1.7 (#38121) 2020-07-23 19:55:27 -05:00
HomeAssistant Azure
124ea04e57 [ci skip] Translation update 2020-07-24 00:02:30 +00:00
Rob Bierbooms
0e0f61764a Fix updates of Rssi for control devices in rfxtrx (#38131)
* Change entity to entity_info

* Fix bug in RSSI for Control devices
2020-07-24 01:49:46 +02:00
Jeff Irion
eb1970c015 Bump androidtv to 0.0.46 (#38090) 2020-07-23 23:18:46 +02:00
Eugenio Panadero
8cfffd00d6 Fix state automation trigger (#38014) (#38032)
for scenarios of enabling/disabling (~ creating/removing) entities,
so it does not trigger in removal if a `to: xxx` is defined, and also
does not trigger in creation if a `from: xxx` is present.
2020-07-23 14:17:11 -05:00
mvn23
6652af5cc0 Add set_central_heating_ovrd service to opentherm_gw (#34425)
* Add set_central_heating_ovrd service to opentherm_gw

* Use await instead of async_create_task

Use await instead of async_create_task as per review.

Co-authored-by: J. Nick Koston <nick@koston.org>

* Use boolean values for service call to opentherm_gw.set_central_heating_ovrd

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-07-23 08:24:17 -10:00
Daniel
98a8ce0555 Add homematic IPKeyBlindMulti device (#38059)
* Update const.py

Added device class "IPKeyBlindMulti" to const.py in order to support HomematicIP device HmIP-DRBLI4. The site package pyhomematic supports this class already in actors.py.

* Update const.py
2020-07-23 15:02:54 +02:00
melyux
3365677484 Add 'alarm_event_occurred' property from AlarmDecoder (#38055) 2020-07-23 14:43:57 +02:00
dependabot[bot]
4001eabafa Bump codecov/codecov-action from v1.0.11 to v1.0.12 (#38102)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from v1.0.11 to v1.0.12.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Commits](https://github.com/codecov/codecov-action/compare/v1.0.11...07127fde53bc3ccd346d47ab2f14c390161ad108)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-23 13:16:31 +02:00
J. Nick Koston
fdc5208d18 Prevent the zeroconf service browser from terminating when a device without any addresses is discovered. (#38094) 2020-07-22 20:21:57 -10:00
Paulus Schoutsen
5583f43030 Clean up fido tests (#38098) 2020-07-22 20:21:32 -10:00
Joakim Sørensen
f742875e0d Fix text error when getting getting external IP in route53 (#38100) 2020-07-22 20:20:49 -10:00
J. Nick Koston
6e2025a748 Use postgresql style uuid generation (uuid_generate_v1mc) for Context uuids (#38089)
This avoids the syscall to getrandom() every time we generate a uuid.
2020-07-22 21:53:01 -07:00
J. Nick Koston
d7811a4adf Avoid generating a Context() object every second (#38085)
Every second we were calling the getrandom() syscall to generate a uuid4
for a context that will never be looked:

 * In most setups there are no more time_changed listeners

 * The ones that do exist never care about context

 * time_changed events are never saved in the database
2020-07-22 21:52:10 -07:00
Paulus Schoutsen
2b3f22c871 Fix route53 depending on broken package (#38079) 2020-07-22 21:43:51 -07:00
Eugene Prystupa
a756d1e637 Centralize bond update state logic (#38093)
* Refactor bond integration to centralize update state logic in single superclass

* Refactor bond integration to centralize update state logic in single superclass
2020-07-23 04:15:27 +02:00
Eugene Prystupa
3480fb6996 Refactor bond integration to be completely async (#38066) 2020-07-22 20:22:25 -05:00
HomeAssistant Azure
15fe727f2c [ci skip] Translation update 2020-07-23 00:02:48 +00:00
Andrew Sayre
ae5c50c1b6 Bump pysmartthings to v0.7.2 (#38086) 2020-07-22 16:01:57 -07:00
Martin Hjelmare
2f4c1e683a Fix ozw light color values check (#38067)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-07-22 15:50:44 -07:00
Rob Bierbooms
83a27f4855 Fix issue with creation of PT2262 devices in rfxtrx integration (#38074) 2020-07-22 15:09:37 -07:00
Franck Nijhof
b15dad8c4b Upgrade aiohttp to 3.6.2 (#38082) 2020-07-22 14:56:28 -07:00
Dubh Ad
e0ceacdf85 Update discord.py to v1.3.4 for API change (#38060) 2020-07-22 21:48:48 +02:00
Franck Nijhof
d7fdbbc2a5 Bump version to 0.114.0dev0 (#38071) 2020-07-22 20:17:11 +02:00
RogerSelwyn
393dd4fe7f Change sky_hub to async and fix exception spamming (#37129)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-07-22 19:58:07 +02:00
Franck Nijhof
bb8e4db47b Merge branch 'master' into dev 2020-07-22 19:11:00 +02:00
Phil Bruckner
65d1dfba62 Update automation logger to include object_id like scripts (#37948) 2020-07-22 10:55:49 -05:00
Ville Skyttä
aa1c5fc43d Various type hint improvements (#37952) 2020-07-22 08:06:37 -07:00
Pascal Vizeli
3e2555e2c1 Update home assistant base image (#38063) 2020-07-22 16:39:50 +02:00
Joakim Plate
bbff9ff6a0 Fix rfxtrx stop after first non light (#38057) 2020-07-22 13:08:31 +02:00
Paulus Schoutsen
0ffeb4dea4 Add MQTT to constraints file (#38049) 2020-07-21 19:19:32 -07:00
Phil Bruckner
726d5fdd94 Allow float values in time periods (#38023) 2020-07-21 19:41:42 -05:00
J. Nick Koston
4a5a09a0e9 Speed up group setup (#38048) 2020-07-21 17:29:57 -07:00
Donnie
6e87c2ad3e Support default transition in light profiles (#36747)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-07-21 17:19:07 -07:00
J. Nick Koston
5cf7b1b1bc Ensure we do not start discovered flows until after the started event has fired (#38047)
* Ensure we do not start discovered flows until after the start event has fired

This change makes zeroconf and ssdp match discovery behavior of not
creating config flows until the start event has been fired.  This
prevents config flow creation/dependency installs for discovered
config flows from competing for cpu time during startup.

* Start discovery/service browser/ssdp when EVENT_HOMEASSISTANT_STARTED is fired instead of EVENT_HOMEASSISTANT_START
2020-07-21 14:18:43 -10:00
HomeAssistant Azure
e766a119d2 [ci skip] Translation update 2020-07-22 00:02:30 +00:00
Joakim Plate
7a3c6d6525 Avoid using implementation internal to trigger events (#38041)
This uses the moxking in fixture to trigger events.
2020-07-22 00:24:26 +02:00
Joakim Plate
945acb4e29 Make sure command entities restore from state (#38038) 2020-07-22 00:01:31 +02:00
Rob Bierbooms
ad5d7ee615 Implement unload entry for rfxtrx integration (#38037)
* Implement unload entry

* Change async_remove to remove

* Pop data from hass.data

* Change sequence order in unload

* Dont unload internal when unload platforms fail
2020-07-21 23:43:05 +02:00
Alexei Chetroi
ec17ed9364 ZHA dependencies bump bellows to 0.18.0 (#38043) 2020-07-21 11:42:23 -10:00
J. Nick Koston
4015991622 Update tests that track time to account for microsecond precision (#38044) 2020-07-21 14:22:55 -07:00
Erik Montnemery
fa0e12ffe8 Use keywords for MQTT birth and will (#38040) 2020-07-21 14:10:34 -07:00
Erik Montnemery
908b72370b Correct arguments to MQTT will_set (#38036) 2020-07-21 12:36:21 -07:00
Ville Skyttä
01d2d2f315 Fix wolflink datetime import (#38028) 2020-07-21 10:37:54 -07:00
dependabot[bot]
1fc37fec7b Bump actions/setup-python from v2 to v2.1.1 (#38034)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from v2 to v2.1.1.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...0c28554988f6ccf1a4e2818e703679796e41a214)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-21 12:54:35 +02:00
Rob Bierbooms
d9dba9142c Move data on import in rfxtrx integration into ConfigEntry (#38022)
* Move all data imported from yaml to ConfigEntry

* Revert changes that prevent updating yaml entry

* Cleanup code around time conversion
2020-07-21 09:44:00 +02:00
J. Nick Koston
60009ec2f9 Use event loop scheduling for tracking time patterns (#38021)
* Use event loop scheduling for tracking time patterns

* make patching of time targetable

* patch time tests since time can tick to match during the test

* fix more tests

* time can only move forward

* time can only move forward

* back to 100% coverage

* simplify since the event loop time cannot move backwards

* simplify some more

* revert simplify

* Revert "revert simplify"

This reverts commit bd42f232f6.

* Revert "simplify some more"

This reverts commit 2a6c57d514.

* Revert "simplify since the event loop time cannot move backwards"

This reverts commit 3b13714ef4.

* Attempt another simplify

* time does not move backwards in the last two

* remove next_time <= now check

* fix previous merge error
2020-07-20 20:18:31 -10:00
Daniel Shokouhi
7bc8caca96 Check if robot has boundaries to update (#38030) 2020-07-20 22:00:11 -07:00
HomeAssistant Azure
83d4e5bbb7 [ci skip] Translation update 2020-07-21 00:03:00 +00:00
Ryan
59063a7d61 Add scrape sensor name to logs (#38020) 2020-07-20 23:07:36 +02:00
Patrick
19870ea867 Fix ozw color temp (#38012)
* Fix color temp math

* Ran black --fast

* Update test_light.py

* tweaking mireds

* updating comments

* fixing test_light to match standards

* fixing comments, need coffee
2020-07-20 14:35:30 -04:00
Tom Harris
dd459a7855 Fix issue with Insteon events not firing correctly (#37974) 2020-07-20 16:23:59 +02:00
David F. Mulcahey
d5a03b4d6a Cleanup async_accept_signal in ZHA (#38009) 2020-07-20 07:04:57 -07:00
dependabot[bot]
2a975db9cf Bump codecov/codecov-action from v1.0.10 to v1.0.11 (#38006)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from v1.0.10 to v1.0.11.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Commits](https://github.com/codecov/codecov-action/compare/v1.0.10...6d208f5b527841fb050f92f778e86cb808dacdcb)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-20 11:58:22 +02:00
Adam Król
bedb0753f3 Add Wolflink integration (#34104)
* WOLF Smart-set integration

* Removed translations. Changed device class of timestamp. Added new test for unknown exception

* Remove unit_of_measurement from hours sensor

* Code cleanup. Pull Request comments fixes

* ConnectError import change. Removed DEVICE_CLASS_TIMESTAMP

* Add unique id guard with tests. Use common translations. Move device_id resolution to config_flow.

* Remove debug print
2020-07-20 11:52:52 +02:00
Rob Bierbooms
d0d4e08a2a Force updates for ozw sensors (#38003)
* Force updates and disable polling

* Move force_update to sensor
2020-07-20 11:49:05 +02:00
Rob Bierbooms
65eedcf434 Disable polling for ozw entities (#38005) 2020-07-20 10:56:22 +02:00
Jean-Yves Avenard
36ee9ff58f Don't advertise switch devices as dimmable lights (#37978)
This issue has been corrected and then reverted multiple times.
It seems that the core issue was a casing one (On/off vs On/Off) ; for better
matching with a real Hue hub, also add the productname.

Tested to work with echo 2 and echo 5.
2020-07-19 22:33:56 -10:00
J. Nick Koston
6ea5c8aed9 Index the device registry (#37990) 2020-07-19 23:32:05 -07:00
Jesse Newland
92d72f26c7 Fix notify.slack service calls using data_template (#37980) 2020-07-19 22:55:50 -07:00
J. Nick Koston
890562e3ae Index the entity registry (#37994) 2020-07-19 22:52:41 -07:00
Daniel Shokouhi
41421b56a4 Bumpy pyobihai to make last reboot update as needed (#37914) 2020-07-19 19:02:45 -10:00
Jeff Irion
2c3618e2c7 Close androidtv ADB socket connection when Home Assistant stops (#37973)
* Close the ADB connection on HA stop

* Get the test to pass

* Remove unnecessary test code

* Register the callback sooner

* '_async_stop' -> 'async_close'

* 'async_close' method -> '_async_close' function
2020-07-20 02:48:08 +02:00
David F. Mulcahey
967a168ab7 Update comment about parallel updates to match the documentation (#37964) 2020-07-19 14:40:08 -10:00
HomeAssistant Azure
9092b83869 [ci skip] Translation update 2020-07-20 00:03:01 +00:00
David F. Mulcahey
bfba44f6bb Force updates for ZHA light group entity members (Part 2) (#37995) 2020-07-19 15:05:53 -07:00
lawtancool
432cbd3148 Add Control4 integration (#37632)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-07-19 13:48:08 -07:00
Erik Montnemery
c994904e75 Bump pychromecast to 7.1.2 (#37976) 2020-07-19 12:36:59 +02:00
michaeldavie
619707e0e3 Make nested get() statements safe (#37965) 2020-07-19 11:52:46 +02:00
HomeAssistant Azure
650d61e4f3 [ci skip] Translation update 2020-07-19 00:03:02 +00:00
Thorjan Knudsvik
e6ff8d6839 Adds median to min_max component (#36686) 2020-07-18 16:18:31 -07:00
Joakim Plate
6fa04aa3e3 Add support for InputSelector trait (#35753) 2020-07-18 16:07:32 -07:00
David F. Mulcahey
2354d0117b Force updates for ZHA light group entity members (#37961)
* Force updates for ZHA light group entity members

* add a 3 second debouncer to the forced refresh

* lint
2020-07-18 14:47:32 -04:00
Joakim Plate
f173805c2f Make sensor and binary_sensor inherit from base class (#37946)
* Make sensor and binary_sensor inherit from base class

* Drop several pointless public properties

* Make sure base function has same parameters

* Drop pass

* Missed one

* Adjust inherit order
2020-07-18 13:43:38 +02:00
J. Nick Koston
b030ed1adf fix (#37889) 2020-07-18 07:25:07 -04:00
J. Nick Koston
ad22619efb Bump zeroconf to 0.28.0 (#37951) 2020-07-17 21:47:43 -10:00
Xiaonan Shen
394194d1e6 Add switch to pi_hole integration (#35605)
Co-authored-by: Ian <vividboarder@gmail.com>
2020-07-17 23:19:01 -07:00
J. Nick Koston
1acdb28cdd Automatically recover when the sqlite3 database is malformed or corrupted (#37949)
* Validate sqlite database on startup and move away if corruption is detected.

* do not switch context in test -- its all sync
2020-07-17 19:07:37 -10:00
J. Nick Koston
910b6c9c2c Index entity_registry_updated listeners (#37940) 2020-07-17 21:59:18 -07:00
Shulyaka
9ae08585dc Add humidifier device triggers (#36887)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-07-17 21:57:52 -07:00
jjlawren
a7dfa60208 Fix Sonos speaker lookup for Plex (#37942) 2020-07-17 18:18:53 -07:00
J. Nick Koston
1582e4347d Mock out I/O in the default_config test (#37897)
This test never passed locally because of the I/O.
2020-07-17 18:17:40 -07:00
HomeAssistant Azure
72309a0dfc [ci skip] Translation update 2020-07-18 00:02:49 +00:00
Joakim Plate
d0040e60cc Rftrx force update (#37944)
* Make sure sensor and binary_sensor force update

This make sure it's possible to react to events on all updates.

* Correct addition of binary sensors
2020-07-18 01:23:49 +02:00
Ivan Belokobylskiy
cecdce07cc Fix Yandex transport Integration, add signature to requests (#37365) 2020-07-17 12:55:30 -07:00
Shulyaka
cee136ec55 Add humidifier device conditions (#36962) 2020-07-17 11:33:52 -07:00
Shulyaka
7c9ef39ef6 Add humidifier intents (#37335)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-07-17 11:20:34 -07:00
Martin Weinelt
2e4b4dc188 prometheus: Reduce loglevel of failed float conversion to debug (#37936)
It creates alot of useless noise currently.

Fixes #30186
2020-07-17 20:18:35 +02:00
Daniel Shokouhi
6ad794e1f8 Switch back to create task for Neato (#37913) 2020-07-17 09:29:20 -07:00
Martin Weinelt
1dd5a36f5c Improve setup script portability (#37935) 2020-07-17 09:27:46 -07:00
rajlaud
1e8676bf2c Fix bugs updating state of hdmi_cec switch (#37786) 2020-07-17 09:21:42 -07:00
Abílio Costa
8beaccf2dd Change ZHA power unit from kW to W (#37896)
* Change ZHA power unit from kW to W

* Use POWER_WATT

* Move kW to W conversion; ignore unit for power
2020-07-17 10:04:04 -04:00
Marcel van der Veldt
24ed932b01 Add ozw support for single setpoint thermostat devices (#37713)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-07-17 14:25:16 +02:00
Tim Messerschmidt
0297c9e612 Fix: Passes secure parameter when setting up Nuki (#36844) (#37932)
* Passes secure parameter when setting up Nuki (#36844)

* Adds an additional configuration option for soft bridges instead of passing True when setting up the bridge

* Revert "Adds an additional configuration option for soft bridges instead of passing True when setting up the bridge"

This reverts commit af1d839ab1.
2020-07-17 13:04:12 +02:00
Joakim Plate
7c9be024bb Rfxtrx use previous received event to do complete restore (#37819)
* Add event attribute to display last received event

* Restore state using event attribute

* Allow empty dict for device config

* Must also validate normal case

* Do early return
2020-07-17 10:27:07 +02:00
J. Nick Koston
f5b628c04f Cleanup logbook tests to prevent failure on race condition (#37928) 2020-07-16 21:48:22 -07:00
J. Nick Koston
fa4e9c0485 Increase test line coverage of homeassistant/helpers/event.py to 100% (#37927)
* Increase test line coverage of homeassistant/helpers/event.py to 100%

* fix test
2020-07-16 21:47:53 -07:00
Eugene Prystupa
aaf084d713 Apply feedback on bond integration (#37921) 2020-07-16 21:25:04 -05:00
Chris
a6129467aa Add RGB light support to ozw (#37636) 2020-07-16 18:10:36 -07:00
Perry Naseck
93919dea88 Add Firmata Integration (attempt 2) (#35591) 2020-07-16 17:58:45 -07:00
J. Nick Koston
b6befa2e83 Ensure a state change tracker setup from inside a state change listener does not fire immediately (#37924)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-07-16 17:50:06 -07:00
Anders Melchiorsen
2d93f8eae8 Upgrade pysonos to 0.0.32 (#37923) 2020-07-16 17:26:29 -07:00
cgtobi
baa7bb69b3 Fix unavailable when value is zero (#37918) 2020-07-17 00:28:49 +02:00
Alexei Chetroi
33dc015083 Fix ZHA electrical measurement sensor initialization (#37915)
* Refactor cached ZHA channel reads.

If doing a cached ZCL attribute read, do "only_from_cache" read for
battery operated devices only. Mains operated devices will do a network
read in case of a cache miss.

* Use cached attributes for ZHA electrical measurement

* Bump up ZHA zigpy dependency.
2020-07-16 16:25:42 -04:00
Phil Bruckner
716cee6907 Fix automation & script restart mode bug (#37909) 2020-07-16 12:03:43 -07:00
Bram Kragten
a224b944e9 Updated frontend to 20200716.0 (#37910) 2020-07-16 20:18:31 +02:00
Eugene Prystupa
37a70c73a5 Improve bond startup performance (#37900) 2020-07-16 10:31:15 -05:00
Franck Nijhof
93c6a9cd96 Fix swapped variables deprecation in log message (#37901) 2020-07-16 10:08:05 +02:00
mdegat01
16e5d02794 Add ignore_attributes option to influxdb (#37747)
* Added ignore_attributes option and tests

* adjusted config for overlapping customization with ignore attrs
2020-07-16 09:42:02 +02:00
Sly Gryphon
4ef581622d Feature/izone temperature precision (#37669)
* Change to precision tenths for current temp
2020-07-16 08:05:31 +08:00
Eugene Prystupa
0bfcd8c2ab Refactor bond tests (#37868) 2020-07-15 15:49:58 -05:00
J. Nick Koston
9db6318122 Remove support for legacy logbook events created before 0.112 (#37822)
* Remove support for legacy logbook events created before 0.112

Reduce the complexity of the logbook code.  This
should also have a small performance boost.

* None is the default
2020-07-15 10:38:08 -10:00
Bram Kragten
261f0b971c Update frontend to 20200715.1 (#37888) 2020-07-15 21:35:33 +02:00
941 changed files with 27019 additions and 5989 deletions

View File

@@ -8,6 +8,10 @@ omit =
homeassistant/scripts/*.py
# omit pieces of code that rely on external devices being present
homeassistant/components/accuweather/__init__.py
homeassistant/components/accuweather/const.py
homeassistant/components/accuweather/sensor.py
homeassistant/components/accuweather/weather.py
homeassistant/components/acer_projector/switch.py
homeassistant/components/actiontec/device_tracker.py
homeassistant/components/acmeda/__init__.py
@@ -28,10 +32,6 @@ omit =
homeassistant/components/agent_dvr/camera.py
homeassistant/components/agent_dvr/const.py
homeassistant/components/agent_dvr/helpers.py
homeassistant/components/airly/__init__.py
homeassistant/components/airly/air_quality.py
homeassistant/components/airly/sensor.py
homeassistant/components/airly/const.py
homeassistant/components/airvisual/__init__.py
homeassistant/components/airvisual/air_quality.py
homeassistant/components/airvisual/sensor.py
@@ -69,6 +69,9 @@ omit =
homeassistant/components/avion/light.py
homeassistant/components/avri/const.py
homeassistant/components/avri/sensor.py
homeassistant/components/azure_devops/__init__.py
homeassistant/components/azure_devops/const.py
homeassistant/components/azure_devops/sensor.py
homeassistant/components/azure_service_bus/*
homeassistant/components/baidu/tts.py
homeassistant/components/beewi_smartclim/sensor.py
@@ -139,6 +142,10 @@ omit =
homeassistant/components/comfoconnect/*
homeassistant/components/concord232/alarm_control_panel.py
homeassistant/components/concord232/binary_sensor.py
homeassistant/components/control4/__init__.py
homeassistant/components/control4/light.py
homeassistant/components/control4/const.py
homeassistant/components/control4/director_utils.py
homeassistant/components/coolmaster/__init__.py
homeassistant/components/coolmaster/climate.py
homeassistant/components/coolmaster/const.py
@@ -164,6 +171,8 @@ omit =
homeassistant/components/devolo_home_control/binary_sensor.py
homeassistant/components/devolo_home_control/const.py
homeassistant/components/devolo_home_control/devolo_device.py
homeassistant/components/devolo_home_control/devolo_multi_level_switch.py
homeassistant/components/devolo_home_control/light.py
homeassistant/components/devolo_home_control/sensor.py
homeassistant/components/devolo_home_control/subscriber.py
homeassistant/components/devolo_home_control/switch.py
@@ -254,6 +263,13 @@ omit =
homeassistant/components/fibaro/*
homeassistant/components/filesize/sensor.py
homeassistant/components/fints/sensor.py
homeassistant/components/firmata/__init__.py
homeassistant/components/firmata/binary_sensor.py
homeassistant/components/firmata/board.py
homeassistant/components/firmata/const.py
homeassistant/components/firmata/entity.py
homeassistant/components/firmata/pin.py
homeassistant/components/firmata/switch.py
homeassistant/components/fitbit/sensor.py
homeassistant/components/fixer/sensor.py
homeassistant/components/fleetgo/device_tracker.py
@@ -337,7 +353,8 @@ omit =
homeassistant/components/hisense_aehw4a1/*
homeassistant/components/hitron_coda/device_tracker.py
homeassistant/components/hive/*
homeassistant/components/hlk_sw16/*
homeassistant/components/hlk_sw16/__init__.py
homeassistant/components/hlk_sw16/switch.py
homeassistant/components/home_connect/*
homeassistant/components/homematic/*
homeassistant/components/homematic/climate.py
@@ -443,8 +460,6 @@ omit =
homeassistant/components/lightwave/*
homeassistant/components/limitlessled/light.py
homeassistant/components/linksys_smart/device_tracker.py
homeassistant/components/linky/__init__.py
homeassistant/components/linky/sensor.py
homeassistant/components/linode/*
homeassistant/components/linux_battery/sensor.py
homeassistant/components/lirc/*
@@ -537,6 +552,10 @@ omit =
homeassistant/components/netatmo/camera.py
homeassistant/components/netatmo/climate.py
homeassistant/components/netatmo/const.py
homeassistant/components/netatmo/data_handler.py
homeassistant/components/netatmo/helper.py
homeassistant/components/netatmo/light.py
homeassistant/components/netatmo/netatmo_entity_base.py
homeassistant/components/netatmo/sensor.py
homeassistant/components/netatmo/webhook.py
homeassistant/components/netdata/sensor.py
@@ -605,6 +624,9 @@ omit =
homeassistant/components/orvibo/switch.py
homeassistant/components/osramlightify/light.py
homeassistant/components/otp/sensor.py
homeassistant/components/ovo_energy/__init__.py
homeassistant/components/ovo_energy/const.py
homeassistant/components/ovo_energy/sensor.py
homeassistant/components/panasonic_bluray/media_player.py
homeassistant/components/panasonic_viera/media_player.py
homeassistant/components/pandora/media_player.py
@@ -617,6 +639,7 @@ omit =
homeassistant/components/picotts/tts.py
homeassistant/components/piglow/light.py
homeassistant/components/pilight/*
homeassistant/components/ping/const.py
homeassistant/components/ping/binary_sensor.py
homeassistant/components/ping/device_tracker.py
homeassistant/components/pioneer/media_player.py
@@ -679,7 +702,6 @@ omit =
homeassistant/components/rest/binary_sensor.py
homeassistant/components/rest/notify.py
homeassistant/components/rest/switch.py
homeassistant/components/rfxtrx/*
homeassistant/components/ring/camera.py
homeassistant/components/ripple/sensor.py
homeassistant/components/rocketchat/notify.py
@@ -729,7 +751,7 @@ omit =
homeassistant/components/simplisafe/lock.py
homeassistant/components/simulated/sensor.py
homeassistant/components/sisyphus/*
homeassistant/components/sky_hub/device_tracker.py
homeassistant/components/sky_hub/*
homeassistant/components/skybeacon/sensor.py
homeassistant/components/skybell/*
homeassistant/components/slack/notify.py
@@ -909,6 +931,7 @@ omit =
homeassistant/components/vlc/media_player.py
homeassistant/components/vlc_telnet/media_player.py
homeassistant/components/volkszaehler/sensor.py
homeassistant/components/volumio/__init__.py
homeassistant/components/volumio/media_player.py
homeassistant/components/volvooncall/*
homeassistant/components/w800rf32/*
@@ -923,6 +946,9 @@ omit =
homeassistant/components/wiffi/*
homeassistant/components/wink/*
homeassistant/components/wirelesstag/*
homeassistant/components/wolflink/__init__.py
homeassistant/components/wolflink/sensor.py
homeassistant/components/wolflink/const.py
homeassistant/components/worldtidesinfo/sensor.py
homeassistant/components/worxlandroid/sensor.py
homeassistant/components/x10/light.py
@@ -955,7 +981,6 @@ omit =
homeassistant/components/yale_smart_alarm/alarm_control_panel.py
homeassistant/components/yamaha_musiccast/media_player.py
homeassistant/components/yandex_transport/*
homeassistant/components/yeelight/*
homeassistant/components/yeelightsunflower/light.py
homeassistant/components/yi/camera.py
homeassistant/components/zabbix/*

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment
@@ -46,7 +46,7 @@ jobs:
run: |
python -m venv venv
. venv/bin/activate
pip install -U pip setuptools
pip install -U pip==20.1.1 setuptools
pip install -r requirements.txt -r requirements_test.txt
# Uninstalling typing as a workaround. Eventually we should make sure
# all our dependencies drop typing.
@@ -75,7 +75,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -119,7 +119,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -163,7 +163,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -229,7 +229,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -276,7 +276,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -323,7 +323,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -367,7 +367,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -414,7 +414,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -469,7 +469,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -516,7 +516,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -548,7 +548,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2
uses: actions/setup-python@v2.1.1
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -603,7 +603,7 @@ jobs:
run: |
python -m venv venv
. venv/bin/activate
pip install -U pip setuptools wheel
pip install -U pip==20.1.1 setuptools wheel
pip install -r requirements_all.txt
pip install -r requirements_test.txt
# Uninstalling typing as a workaround. Eventually we should make sure
@@ -737,7 +737,7 @@ jobs:
-p no:sugar \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@2.1.0
uses: actions/upload-artifact@v2.1.3
with:
name: coverage-${{ matrix.python-version }}-group${{ matrix.group }}
path: .coverage
@@ -781,4 +781,4 @@ jobs:
coverage report --fail-under=94
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.10
uses: codecov/codecov-action@v1.0.12

View File

@@ -12,6 +12,7 @@ addons:
- libavfilter-dev
sources:
- sourceline: ppa:savoury1/ffmpeg4
- sourceline: ppa:savoury1/multimedia
python:
- "3.7.1"

View File

@@ -14,6 +14,7 @@ homeassistant/scripts/check_config.py @kellerza
# Integrations
homeassistant/components/abode/* @shred86
homeassistant/components/accuweather/* @bieniu
homeassistant/components/acmeda/* @atmurray
homeassistant/components/adguard/* @frenck
homeassistant/components/agent_dvr/* @ispysoftware
@@ -48,6 +49,7 @@ homeassistant/components/avri/* @timvancann
homeassistant/components/awair/* @ahayworth @danielsjf
homeassistant/components/aws/* @awarecan
homeassistant/components/axis/* @Kane610
homeassistant/components/azure_devops/* @timmo001
homeassistant/components/azure_event_hub/* @eavanvalkenburg
homeassistant/components/azure_service_bus/* @hfurubotten
homeassistant/components/beewi_smartclim/* @alemuro
@@ -77,6 +79,7 @@ homeassistant/components/cloudflare/* @ludeeus
homeassistant/components/comfoconnect/* @michaelarnauts
homeassistant/components/config/* @home-assistant/core
homeassistant/components/configurator/* @home-assistant/core
homeassistant/components/control4/* @lawtancool
homeassistant/components/conversation/* @home-assistant/core
homeassistant/components/coolmaster/* @OnFreund
homeassistant/components/coronavirus/* @home_assistant/core
@@ -110,7 +113,7 @@ homeassistant/components/edl21/* @mtdcr
homeassistant/components/egardia/* @jeroenterheerdt
homeassistant/components/eight_sleep/* @mezz64
homeassistant/components/elgato/* @frenck
homeassistant/components/elkm1/* @bdraco
homeassistant/components/elkm1/* @gwww @bdraco
homeassistant/components/elv/* @majuss
homeassistant/components/emby/* @mezz64
homeassistant/components/emoncms/* @borpin
@@ -128,6 +131,7 @@ homeassistant/components/ezviz/* @baqs
homeassistant/components/fastdotcom/* @rohankapoorcom
homeassistant/components/file/* @fabaff
homeassistant/components/filter/* @dgomes
homeassistant/components/firmata/* @DaAwesomeP
homeassistant/components/fixer/* @fabaff
homeassistant/components/flick_electric/* @ZephireNZ
homeassistant/components/flock/* @fabaff
@@ -168,6 +172,7 @@ homeassistant/components/hikvisioncam/* @fbradyirl
homeassistant/components/hisense_aehw4a1/* @bannhead
homeassistant/components/history/* @home-assistant/core
homeassistant/components/hive/* @Rendili @KJonline
homeassistant/components/hlk_sw16/* @jameshilliard
homeassistant/components/home_connect/* @DavidMStraub
homeassistant/components/homeassistant/* @home-assistant/core
homeassistant/components/homekit/* @bdraco
@@ -221,7 +226,6 @@ homeassistant/components/lametric/* @robbiet480
homeassistant/components/launch_library/* @ludeeus
homeassistant/components/lcn/* @alengwenus
homeassistant/components/life360/* @pnbruckner
homeassistant/components/linky/* @Quentame
homeassistant/components/linux_battery/* @fabaff
homeassistant/components/local_ip/* @issacg
homeassistant/components/logger/* @home-assistant/core
@@ -239,7 +243,7 @@ homeassistant/components/mediaroom/* @dgomes
homeassistant/components/melcloud/* @vilppuvuorinen
homeassistant/components/melissa/* @kennedyshead
homeassistant/components/met/* @danielhiversen
homeassistant/components/meteo_france/* @victorcerutti @oncleben31 @Quentame
homeassistant/components/meteo_france/* @hacf-fr @oncleben31 @Quentame
homeassistant/components/meteoalarm/* @rolfberkenbosch
homeassistant/components/metoffice/* @MrHarcombe
homeassistant/components/miflora/* @danielhiversen @ChristianKuehnel
@@ -297,6 +301,7 @@ homeassistant/components/openweathermap/* @fabaff
homeassistant/components/opnsense/* @mtreinish
homeassistant/components/orangepi_gpio/* @pascallj
homeassistant/components/oru/* @bvlaicu
homeassistant/components/ovo_energy/* @timmo001
homeassistant/components/ozw/* @cgarwood @marcelveldt @MartinHjelmare
homeassistant/components/panasonic_viera/* @joogps
homeassistant/components/panel_custom/* @home-assistant/frontend
@@ -362,6 +367,7 @@ homeassistant/components/signal_messenger/* @bbernhard
homeassistant/components/simplisafe/* @bachya
homeassistant/components/sinch/* @bendikrb
homeassistant/components/sisyphus/* @jkeljo
homeassistant/components/sky_hub/* @rogerselwyn
homeassistant/components/slide/* @ualex73
homeassistant/components/sma/* @kellerza
homeassistant/components/smappee/* @bsmappee
@@ -449,6 +455,7 @@ homeassistant/components/vilfo/* @ManneW
homeassistant/components/vivotek/* @HarlemSquirrel
homeassistant/components/vizio/* @raman325
homeassistant/components/vlc_telnet/* @rodripf
homeassistant/components/volumio/* @OnFreund
homeassistant/components/waqi/* @andrey-git
homeassistant/components/watson_tts/* @rutkai
homeassistant/components/weather/* @fabaff
@@ -457,16 +464,17 @@ homeassistant/components/websocket_api/* @home-assistant/core
homeassistant/components/wiffi/* @mampfes
homeassistant/components/withings/* @vangorra
homeassistant/components/wled/* @frenck
homeassistant/components/wolflink/* @adamkrol93
homeassistant/components/workday/* @fabaff
homeassistant/components/worldclock/* @fabaff
homeassistant/components/xbox_live/* @MartinHjelmare
homeassistant/components/xfinity/* @cisasteelersfan
homeassistant/components/xiaomi_aqara/* @danielhiversen @syssi
homeassistant/components/xiaomi_miio/* @rytilahti @syssi
homeassistant/components/xiaomi_miio/* @rytilahti @syssi @starkillerOG
homeassistant/components/xiaomi_tv/* @simse
homeassistant/components/xmpp/* @fabaff @flowolf
homeassistant/components/yamaha_musiccast/* @jalmeroth
homeassistant/components/yandex_transport/* @rishatik92
homeassistant/components/yandex_transport/* @rishatik92 @devbis
homeassistant/components/yeelight/* @rytilahti @zewelor
homeassistant/components/yeelightsunflower/* @lindsaymarkward
homeassistant/components/yessssms/* @flowolf

View File

@@ -29,12 +29,31 @@ jobs:
- template: templates/azp-job-wheels.yaml@azure
parameters:
builderVersion: '$(versionWheels)'
builderApk: 'build-base;cmake;git;linux-headers;bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;autoconf;automake;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev'
builderApk: 'build-base;cmake;git;linux-headers;bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev'
builderPip: 'Cython;numpy'
skipBinary: 'aiohttp'
wheelsRequirement: 'requirements.txt'
wheelsRequirementDiff: 'requirements_diff.txt'
wheelsConstraint: 'homeassistant/package_constraints.txt'
jobName: 'Wheels_Core'
preBuild:
- script: |
if [[ "$(Build.Reason)" =~ (Schedule|Manual) ]]; then
exit 0
else
curl -s -o requirements_diff.txt https://raw.githubusercontent.com/home-assistant/core/master/requirements.txt
fi
displayName: 'Prepare requirements files for Home Assistant Core wheels'
- template: templates/azp-job-wheels.yaml@azure
parameters:
builderVersion: '$(versionWheels)'
builderApk: 'build-base;cmake;git;linux-headers;bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;autoconf;automake;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev'
builderPip: 'Cython;numpy;scikit-build'
skipBinary: 'aiohttp'
wheelsRequirement: 'requirements_wheels.txt'
wheelsRequirementDiff: 'requirements_diff.txt'
wheelsConstraint: 'homeassistant/package_constraints.txt'
jobName: 'Wheels_Integrations'
preBuild:
- script: |
cp requirements_all.txt requirements_wheels.txt

View File

@@ -1,11 +1,11 @@
{
"image": "homeassistant/{arch}-homeassistant",
"build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:8.0.0",
"armhf": "homeassistant/armhf-homeassistant-base:8.0.0",
"armv7": "homeassistant/armv7-homeassistant-base:8.0.0",
"amd64": "homeassistant/amd64-homeassistant-base:8.0.0",
"i386": "homeassistant/i386-homeassistant-base:8.0.0"
"aarch64": "homeassistant/aarch64-homeassistant-base:8.2.1",
"armhf": "homeassistant/armhf-homeassistant-base:8.2.1",
"armv7": "homeassistant/armv7-homeassistant-base:8.2.1",
"amd64": "homeassistant/amd64-homeassistant-base:8.2.1",
"i386": "homeassistant/i386-homeassistant-base:8.2.1"
},
"labels": {
"io.hass.type": "core"

View File

@@ -7,7 +7,7 @@ from homeassistant.util.async_ import protect_loop
def enable() -> None:
"""Enable the detection of I/O in the event loop."""
# Prevent urllib3 and requests doing I/O in event loop
HTTPConnection.putrequest = protect_loop(HTTPConnection.putrequest)
HTTPConnection.putrequest = protect_loop(HTTPConnection.putrequest) # type: ignore
# Currently disabled. pytz doing I/O when getting timezone.
# Prevent files being opened inside the event loop

View File

@@ -6,10 +6,10 @@ import logging
import logging.handlers
import os
import sys
import threading
from time import monotonic
from typing import TYPE_CHECKING, Any, Dict, Optional, Set
from async_timeout import timeout
import voluptuous as vol
import yarl
@@ -44,6 +44,11 @@ DATA_LOGGING = "logging"
LOG_SLOW_STARTUP_INTERVAL = 60
STAGE_1_TIMEOUT = 120
STAGE_2_TIMEOUT = 300
WRAP_UP_TIMEOUT = 300
COOLDOWN_TIME = 60
DEBUGGER_INTEGRATIONS = {"debugpy", "ptvsd"}
CORE_INTEGRATIONS = ("homeassistant", "persistent_notification")
LOGGING_INTEGRATIONS = {
@@ -136,7 +141,7 @@ async def async_setup_hass(
hass.async_track_tasks()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP, {})
with contextlib.suppress(asyncio.TimeoutError):
async with timeout(10):
async with hass.timeout.async_timeout(10):
await hass.async_block_till_done()
safe_mode = True
@@ -304,6 +309,12 @@ def async_enable_logging(
"Uncaught exception", exc_info=args # type: ignore
)
if sys.version_info[:2] >= (3, 8):
threading.excepthook = lambda args: logging.getLogger(None).exception(
"Uncaught thread exception",
exc_info=(args.exc_type, args.exc_value, args.exc_traceback),
)
# Log errors to a file if we have write access to file or config dir
if log_file is None:
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
@@ -496,24 +507,42 @@ async def _async_set_up_integrations(
stage_2_domains = domains_to_setup - logging_domains - debuggers - stage_1_domains
# Kick off loading the registries. They don't need to be awaited.
asyncio.gather(
hass.helpers.device_registry.async_get_registry(),
hass.helpers.entity_registry.async_get_registry(),
hass.helpers.area_registry.async_get_registry(),
)
asyncio.create_task(hass.helpers.device_registry.async_get_registry())
asyncio.create_task(hass.helpers.entity_registry.async_get_registry())
asyncio.create_task(hass.helpers.area_registry.async_get_registry())
# Start setup
if stage_1_domains:
_LOGGER.info("Setting up stage 1: %s", stage_1_domains)
await async_setup_multi_components(hass, stage_1_domains, config, setup_started)
try:
async with hass.timeout.async_timeout(
STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME
):
await async_setup_multi_components(
hass, stage_1_domains, config, setup_started
)
except asyncio.TimeoutError:
_LOGGER.warning("Setup timed out for stage 1 - moving forward")
# Enables after dependencies
async_set_domains_to_be_loaded(hass, stage_1_domains | stage_2_domains)
if stage_2_domains:
_LOGGER.info("Setting up stage 2: %s", stage_2_domains)
await async_setup_multi_components(hass, stage_2_domains, config, setup_started)
try:
async with hass.timeout.async_timeout(
STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME
):
await async_setup_multi_components(
hass, stage_2_domains, config, setup_started
)
except asyncio.TimeoutError:
_LOGGER.warning("Setup timed out for stage 2 - moving forward")
# Wrap up startup
_LOGGER.debug("Waiting for startup to wrap up")
await hass.async_block_till_done()
try:
async with hass.timeout.async_timeout(WRAP_UP_TIMEOUT, cool_down=COOLDOWN_TIME):
await hass.async_block_till_done()
except asyncio.TimeoutError:
_LOGGER.warning("Setup timed out for bootstrap - moving forward")

View File

@@ -261,6 +261,7 @@ def setup_abode_events(hass):
TIMELINE.AUTOMATION_GROUP,
TIMELINE.DISARM_GROUP,
TIMELINE.ARM_GROUP,
TIMELINE.ARM_FAULT_GROUP,
TIMELINE.TEST_GROUP,
TIMELINE.CAPTURE_GROUP,
TIMELINE.DEVICE_GROUP,

View File

@@ -82,8 +82,21 @@ class AbodeCamera(AbodeDevice, Camera):
return None
def turn_on(self):
"""Turn on camera."""
self._device.privacy_mode(False)
def turn_off(self):
"""Turn off camera."""
self._device.privacy_mode(True)
def _capture_callback(self, capture):
"""Update the image with the device then refresh device."""
self._device.update_image_location(capture)
self.get_image()
self.schedule_update_ha_state()
@property
def is_on(self):
"""Return true if on."""
return self._device.is_on

View File

@@ -3,7 +3,7 @@
"name": "Abode",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/abode",
"requirements": ["abodepy==0.19.0"],
"requirements": ["abodepy==1.1.0"],
"codeowners": ["@shred86"],
"homekit": {
"models": ["Abode", "Iota"]

View File

@@ -0,0 +1,132 @@
"""The AccuWeather component."""
import asyncio
from datetime import timedelta
import logging
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp.client_exceptions import ClientConnectorError
from async_timeout import timeout
from homeassistant.const import CONF_API_KEY
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
ATTR_FORECAST,
CONF_FORECAST,
COORDINATOR,
DOMAIN,
UNDO_UPDATE_LISTENER,
)
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor", "weather"]
async def async_setup(hass: HomeAssistant, config: Config) -> bool:
"""Set up configured AccuWeather."""
hass.data.setdefault(DOMAIN, {})
return True
async def async_setup_entry(hass, config_entry) -> bool:
"""Set up AccuWeather as config entry."""
api_key = config_entry.data[CONF_API_KEY]
location_key = config_entry.unique_id
forecast = config_entry.options.get(CONF_FORECAST, False)
_LOGGER.debug("Using location_key: %s, get forecast: %s", location_key, forecast)
websession = async_get_clientsession(hass)
coordinator = AccuWeatherDataUpdateCoordinator(
hass, websession, api_key, location_key, forecast
)
await coordinator.async_refresh()
if not coordinator.last_update_success:
raise ConfigEntryNotReady
undo_listener = config_entry.add_update_listener(update_listener)
hass.data[DOMAIN][config_entry.entry_id] = {
COORDINATOR: coordinator,
UNDO_UPDATE_LISTENER: undo_listener,
}
for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component)
)
return True
async def async_unload_entry(hass, config_entry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in PLATFORMS
]
)
)
hass.data[DOMAIN][config_entry.entry_id][UNDO_UPDATE_LISTENER]()
if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id)
return unload_ok
async def update_listener(hass, config_entry):
"""Update listener."""
await hass.config_entries.async_reload(config_entry.entry_id)
class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching AccuWeather data API."""
def __init__(self, hass, session, api_key, location_key, forecast: bool):
"""Initialize."""
self.location_key = location_key
self.forecast = forecast
self.is_metric = hass.config.units.is_metric
self.accuweather = AccuWeather(api_key, session, location_key=self.location_key)
# Enabling the forecast download increases the number of requests per data
# update, we use 32 minutes for current condition only and 64 minutes for
# current condition and forecast as update interval to not exceed allowed number
# of requests. We have 50 requests allowed per day, so we use 45 and leave 5 as
# a reserve for restarting HA.
update_interval = (
timedelta(minutes=64) if self.forecast else timedelta(minutes=32)
)
_LOGGER.debug("Data will be update every %s", update_interval)
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
async def _async_update_data(self):
"""Update data via library."""
try:
async with timeout(10):
current = await self.accuweather.async_get_current_conditions()
forecast = (
await self.accuweather.async_get_forecast(metric=self.is_metric)
if self.forecast
else {}
)
except (
ApiError,
ClientConnectorError,
InvalidApiKeyError,
RequestsExceededError,
) as error:
raise UpdateFailed(error)
_LOGGER.debug("Requests remaining: %s", self.accuweather.requests_remaining)
return {**current, **{ATTR_FORECAST: forecast}}

View File

@@ -0,0 +1,112 @@
"""Adds config flow for AccuWeather."""
import asyncio
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientError
from aiohttp.client_exceptions import ClientConnectorError
from async_timeout import timeout
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from .const import CONF_FORECAST, DOMAIN # pylint:disable=unused-import
class AccuWeatherFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for AccuWeather."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
# Under the terms of use of the API, one user can use one free API key. Due to
# the small number of requests allowed, we only allow one integration instance.
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
errors = {}
if user_input is not None:
websession = async_get_clientsession(self.hass)
try:
with timeout(10):
accuweather = AccuWeather(
user_input[CONF_API_KEY],
websession,
latitude=user_input[CONF_LATITUDE],
longitude=user_input[CONF_LONGITUDE],
)
await accuweather.async_get_location()
except (ApiError, ClientConnectorError, asyncio.TimeoutError, ClientError):
errors["base"] = "cannot_connect"
except InvalidApiKeyError:
errors[CONF_API_KEY] = "invalid_api_key"
except RequestsExceededError:
errors[CONF_API_KEY] = "requests_exceeded"
else:
await self.async_set_unique_id(
accuweather.location_key, raise_on_progress=False
)
return self.async_create_entry(
title=user_input[CONF_NAME], data=user_input
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_API_KEY): str,
vol.Optional(
CONF_LATITUDE, default=self.hass.config.latitude
): cv.latitude,
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(
CONF_NAME, default=self.hass.config.location_name
): str,
}
),
errors=errors,
)
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Options callback for AccuWeather."""
return AccuWeatherOptionsFlowHandler(config_entry)
class AccuWeatherOptionsFlowHandler(config_entries.OptionsFlow):
"""Config flow options for AccuWeather."""
def __init__(self, config_entry):
"""Initialize AccuWeather options flow."""
self.config_entry = config_entry
async def async_step_init(self, user_input=None):
"""Manage the options."""
return await self.async_step_user()
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Optional(
CONF_FORECAST,
default=self.config_entry.options.get(CONF_FORECAST, False),
): bool
}
),
)

View File

@@ -0,0 +1,279 @@
"""Constants for AccuWeather integration."""
from homeassistant.const import (
ATTR_DEVICE_CLASS,
DEVICE_CLASS_TEMPERATURE,
LENGTH_FEET,
LENGTH_INCHES,
LENGTH_METERS,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
TIME_HOURS,
UNIT_PERCENTAGE,
UV_INDEX,
VOLUME_CUBIC_METERS,
)
ATTRIBUTION = "Data provided by AccuWeather"
ATTR_ICON = "icon"
ATTR_FORECAST = CONF_FORECAST = "forecast"
ATTR_LABEL = "label"
ATTR_UNIT_IMPERIAL = "Imperial"
ATTR_UNIT_METRIC = "Metric"
CONCENTRATION_PARTS_PER_CUBIC_METER = f"p/{VOLUME_CUBIC_METERS}"
COORDINATOR = "coordinator"
DOMAIN = "accuweather"
LENGTH_MILIMETERS = "mm"
MANUFACTURER = "AccuWeather, Inc."
NAME = "AccuWeather"
UNDO_UPDATE_LISTENER = "undo_update_listener"
CONDITION_CLASSES = {
"clear-night": [33, 34, 37],
"cloudy": [7, 8, 38],
"exceptional": [24, 30, 31],
"fog": [11],
"hail": [25],
"lightning": [15],
"lightning-rainy": [16, 17, 41, 42],
"partlycloudy": [4, 6, 35, 36],
"pouring": [18],
"rainy": [12, 13, 14, 26, 39, 40],
"snowy": [19, 20, 21, 22, 23, 43, 44],
"snowy-rainy": [29],
"sunny": [1, 2, 3, 5],
"windy": [32],
}
FORECAST_DAYS = [0, 1, 2, 3, 4]
FORECAST_SENSOR_TYPES = {
"CloudCoverDay": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-cloudy",
ATTR_LABEL: "Cloud Cover Day",
ATTR_UNIT_METRIC: UNIT_PERCENTAGE,
ATTR_UNIT_IMPERIAL: UNIT_PERCENTAGE,
},
"CloudCoverNight": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-cloudy",
ATTR_LABEL: "Cloud Cover Night",
ATTR_UNIT_METRIC: UNIT_PERCENTAGE,
ATTR_UNIT_IMPERIAL: UNIT_PERCENTAGE,
},
"Grass": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:grass",
ATTR_LABEL: "Grass Pollen",
ATTR_UNIT_METRIC: CONCENTRATION_PARTS_PER_CUBIC_METER,
ATTR_UNIT_IMPERIAL: CONCENTRATION_PARTS_PER_CUBIC_METER,
},
"HoursOfSun": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-partly-cloudy",
ATTR_LABEL: "Hours Of Sun",
ATTR_UNIT_METRIC: TIME_HOURS,
ATTR_UNIT_IMPERIAL: TIME_HOURS,
},
"Mold": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_LABEL: "Mold Pollen",
ATTR_UNIT_METRIC: CONCENTRATION_PARTS_PER_CUBIC_METER,
ATTR_UNIT_IMPERIAL: CONCENTRATION_PARTS_PER_CUBIC_METER,
},
"Ozone": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:vector-triangle",
ATTR_LABEL: "Ozone",
ATTR_UNIT_METRIC: None,
ATTR_UNIT_IMPERIAL: None,
},
"Ragweed": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:sprout",
ATTR_LABEL: "Ragweed Pollen",
ATTR_UNIT_METRIC: CONCENTRATION_PARTS_PER_CUBIC_METER,
ATTR_UNIT_IMPERIAL: CONCENTRATION_PARTS_PER_CUBIC_METER,
},
"RealFeelTemperatureMax": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "RealFeel Temperature Max",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"RealFeelTemperatureMin": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "RealFeel Temperature Min",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"RealFeelTemperatureShadeMax": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "RealFeel Temperature Shade Max",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"RealFeelTemperatureShadeMin": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "RealFeel Temperature Shade Min",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"ThunderstormProbabilityDay": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-lightning",
ATTR_LABEL: "Thunderstorm Probability Day",
ATTR_UNIT_METRIC: UNIT_PERCENTAGE,
ATTR_UNIT_IMPERIAL: UNIT_PERCENTAGE,
},
"ThunderstormProbabilityNight": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-lightning",
ATTR_LABEL: "Thunderstorm Probability Night",
ATTR_UNIT_METRIC: UNIT_PERCENTAGE,
ATTR_UNIT_IMPERIAL: UNIT_PERCENTAGE,
},
"Tree": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:tree-outline",
ATTR_LABEL: "Tree Pollen",
ATTR_UNIT_METRIC: CONCENTRATION_PARTS_PER_CUBIC_METER,
ATTR_UNIT_IMPERIAL: CONCENTRATION_PARTS_PER_CUBIC_METER,
},
"UVIndex": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-sunny",
ATTR_LABEL: "UV Index",
ATTR_UNIT_METRIC: UV_INDEX,
ATTR_UNIT_IMPERIAL: UV_INDEX,
},
"WindGustDay": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-windy",
ATTR_LABEL: "Wind Gust Day",
ATTR_UNIT_METRIC: SPEED_KILOMETERS_PER_HOUR,
ATTR_UNIT_IMPERIAL: SPEED_MILES_PER_HOUR,
},
"WindGustNight": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-windy",
ATTR_LABEL: "Wind Gust Night",
ATTR_UNIT_METRIC: SPEED_KILOMETERS_PER_HOUR,
ATTR_UNIT_IMPERIAL: SPEED_MILES_PER_HOUR,
},
}
OPTIONAL_SENSORS = (
"ApparentTemperature",
"CloudCover",
"CloudCoverDay",
"CloudCoverNight",
"DewPoint",
"Grass",
"Mold",
"Ozone",
"Ragweed",
"RealFeelTemperatureShade",
"RealFeelTemperatureShadeMax",
"RealFeelTemperatureShadeMin",
"Tree",
"WetBulbTemperature",
"WindChillTemperature",
"WindGust",
"WindGustDay",
"WindGustNight",
)
SENSOR_TYPES = {
"ApparentTemperature": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "Apparent Temperature",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"Ceiling": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-fog",
ATTR_LABEL: "Cloud Ceiling",
ATTR_UNIT_METRIC: LENGTH_METERS,
ATTR_UNIT_IMPERIAL: LENGTH_FEET,
},
"CloudCover": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-cloudy",
ATTR_LABEL: "Cloud Cover",
ATTR_UNIT_METRIC: UNIT_PERCENTAGE,
ATTR_UNIT_IMPERIAL: UNIT_PERCENTAGE,
},
"DewPoint": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "Dew Point",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"RealFeelTemperature": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "RealFeel Temperature",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"RealFeelTemperatureShade": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "RealFeel Temperature Shade",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"Precipitation": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-rainy",
ATTR_LABEL: "Precipitation",
ATTR_UNIT_METRIC: LENGTH_MILIMETERS,
ATTR_UNIT_IMPERIAL: LENGTH_INCHES,
},
"PressureTendency": {
ATTR_DEVICE_CLASS: "accuweather__pressure_tendency",
ATTR_ICON: "mdi:gauge",
ATTR_LABEL: "Pressure Tendency",
ATTR_UNIT_METRIC: None,
ATTR_UNIT_IMPERIAL: None,
},
"UVIndex": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-sunny",
ATTR_LABEL: "UV Index",
ATTR_UNIT_METRIC: UV_INDEX,
ATTR_UNIT_IMPERIAL: UV_INDEX,
},
"WetBulbTemperature": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "Wet Bulb Temperature",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"WindChillTemperature": {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_LABEL: "Wind Chill Temperature",
ATTR_UNIT_METRIC: TEMP_CELSIUS,
ATTR_UNIT_IMPERIAL: TEMP_FAHRENHEIT,
},
"WindGust": {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:weather-windy",
ATTR_LABEL: "Wind Gust",
ATTR_UNIT_METRIC: SPEED_KILOMETERS_PER_HOUR,
ATTR_UNIT_IMPERIAL: SPEED_MILES_PER_HOUR,
},
}

View File

@@ -0,0 +1,8 @@
{
"domain": "accuweather",
"name": "AccuWeather",
"documentation": "https://www.home-assistant.io/integrations/accuweather/",
"requirements": ["accuweather==0.0.9"],
"codeowners": ["@bieniu"],
"config_flow": true
}

View File

@@ -0,0 +1,189 @@
"""Support for the AccuWeather service."""
from homeassistant.const import (
ATTR_ATTRIBUTION,
ATTR_DEVICE_CLASS,
CONF_NAME,
DEVICE_CLASS_TEMPERATURE,
)
from homeassistant.helpers.entity import Entity
from .const import (
ATTR_FORECAST,
ATTR_ICON,
ATTR_LABEL,
ATTRIBUTION,
COORDINATOR,
DOMAIN,
FORECAST_DAYS,
FORECAST_SENSOR_TYPES,
MANUFACTURER,
NAME,
OPTIONAL_SENSORS,
SENSOR_TYPES,
)
PARALLEL_UPDATES = 1
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add AccuWeather entities from a config_entry."""
name = config_entry.data[CONF_NAME]
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
sensors = []
for sensor in SENSOR_TYPES:
sensors.append(AccuWeatherSensor(name, sensor, coordinator))
if coordinator.forecast:
for sensor in FORECAST_SENSOR_TYPES:
for day in FORECAST_DAYS:
# Some air quality/allergy sensors are only available for certain
# locations.
if sensor in coordinator.data[ATTR_FORECAST][0]:
sensors.append(
AccuWeatherSensor(name, sensor, coordinator, forecast_day=day)
)
async_add_entities(sensors, False)
class AccuWeatherSensor(Entity):
"""Define an AccuWeather entity."""
def __init__(self, name, kind, coordinator, forecast_day=None):
"""Initialize."""
self._name = name
self.kind = kind
self.coordinator = coordinator
self._device_class = None
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
self._unit_system = "Metric" if self.coordinator.is_metric else "Imperial"
self.forecast_day = forecast_day
@property
def name(self):
"""Return the name."""
if self.forecast_day is not None:
return f"{self._name} {FORECAST_SENSOR_TYPES[self.kind][ATTR_LABEL]} {self.forecast_day}d"
return f"{self._name} {SENSOR_TYPES[self.kind][ATTR_LABEL]}"
@property
def unique_id(self):
"""Return a unique_id for this entity."""
if self.forecast_day is not None:
return f"{self.coordinator.location_key}-{self.kind}-{self.forecast_day}".lower()
return f"{self.coordinator.location_key}-{self.kind}".lower()
@property
def device_info(self):
"""Return the device info."""
return {
"identifiers": {(DOMAIN, self.coordinator.location_key)},
"name": NAME,
"manufacturer": MANUFACTURER,
"entry_type": "service",
}
@property
def should_poll(self):
"""Return the polling requirement of the entity."""
return False
@property
def available(self):
"""Return True if entity is available."""
return self.coordinator.last_update_success
@property
def state(self):
"""Return the state."""
if self.forecast_day is not None:
if (
FORECAST_SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
== DEVICE_CLASS_TEMPERATURE
):
return self.coordinator.data[ATTR_FORECAST][self.forecast_day][
self.kind
]["Value"]
if self.kind in ["WindGustDay", "WindGustNight"]:
return self.coordinator.data[ATTR_FORECAST][self.forecast_day][
self.kind
]["Speed"]["Value"]
if self.kind in ["Grass", "Mold", "Ragweed", "Tree", "UVIndex", "Ozone"]:
return self.coordinator.data[ATTR_FORECAST][self.forecast_day][
self.kind
]["Value"]
return self.coordinator.data[ATTR_FORECAST][self.forecast_day][self.kind]
if self.kind == "Ceiling":
return round(self.coordinator.data[self.kind][self._unit_system]["Value"])
if self.kind == "PressureTendency":
return self.coordinator.data[self.kind]["LocalizedText"].lower()
if SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS] == DEVICE_CLASS_TEMPERATURE:
return self.coordinator.data[self.kind][self._unit_system]["Value"]
if self.kind == "Precipitation":
return self.coordinator.data["PrecipitationSummary"][self.kind][
self._unit_system
]["Value"]
if self.kind == "WindGust":
return self.coordinator.data[self.kind]["Speed"][self._unit_system]["Value"]
return self.coordinator.data[self.kind]
@property
def icon(self):
"""Return the icon."""
if self.forecast_day is not None:
return FORECAST_SENSOR_TYPES[self.kind][ATTR_ICON]
return SENSOR_TYPES[self.kind][ATTR_ICON]
@property
def device_class(self):
"""Return the device_class."""
if self.forecast_day is not None:
return FORECAST_SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
return SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
if self.forecast_day is not None:
return FORECAST_SENSOR_TYPES[self.kind][self._unit_system]
return SENSOR_TYPES[self.kind][self._unit_system]
@property
def device_state_attributes(self):
"""Return the state attributes."""
if self.forecast_day is not None:
if self.kind == "WindGustDay":
self._attrs["direction"] = self.coordinator.data[ATTR_FORECAST][
self.forecast_day
][self.kind]["Direction"]["English"]
elif self.kind == "WindGustNight":
self._attrs["direction"] = self.coordinator.data[ATTR_FORECAST][
self.forecast_day
][self.kind]["Direction"]["English"]
elif self.kind in ["Grass", "Mold", "Ragweed", "Tree", "UVIndex", "Ozone"]:
self._attrs["level"] = self.coordinator.data[ATTR_FORECAST][
self.forecast_day
][self.kind]["Category"]
return self._attrs
if self.kind == "UVIndex":
self._attrs["level"] = self.coordinator.data["UVIndexText"]
elif self.kind == "Precipitation":
self._attrs["type"] = self.coordinator.data["PrecipitationType"]
return self._attrs
@property
def entity_registry_enabled_default(self):
"""Return if the entity should be enabled when first added to the entity registry."""
return bool(self.kind not in OPTIONAL_SENSORS)
async def async_added_to_hass(self):
"""Connect to dispatcher listening for entity data notifications."""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)
async def async_update(self):
"""Update AccuWeather entity."""
await self.coordinator.async_request_refresh()

View File

@@ -0,0 +1,35 @@
{
"config": {
"step": {
"user": {
"title": "AccuWeather",
"description": "If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/accuweather/\n\nSome sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options.",
"data": {
"name": "Name of the integration",
"api_key": "[%key:common::config_flow::data::api_key%]",
"latitude": "Latitude",
"longitude": "Longitude"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
"requests_exceeded": "The allowed number of requests to Accuweather API has been exceeded. You have to wait or change API Key."
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
}
},
"options": {
"step": {
"user": {
"title": "AccuWeather Options",
"description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 64 minutes instead of every 32 minutes.",
"data": {
"forecast": "Weather forecast"
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"steady": "Steady",
"rising": "Rising",
"falling": "Falling"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3."
},
"error": {
"cannot_connect": "Ha fallat la connexi\u00f3",
"invalid_api_key": "Clau API inv\u00e0lida",
"requests_exceeded": "S'ha superat el nombre m\u00e0xim de sol\u00b7licituds permeses a l'API d'AccuWeather. Has d'esperar-te o canviar la clau API."
},
"step": {
"user": {
"data": {
"api_key": "Clau API",
"latitude": "Latitud",
"longitude": "Longitud",
"name": "Nom de la integraci\u00f3"
},
"description": "Si necessites ajuda amb la configuraci\u00f3, consulta: https://www.home-assistant.io/integrations/accuweather/ \n\n La previsi\u00f3 meteorol\u00f2gica no est\u00e0 habilitada de manera predeterminada. Pots activar-la en les opcions de la integraci\u00f3.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Previsi\u00f3 meteorol\u00f2gica"
},
"description": "Per culpa de les limitacions de la versi\u00f3 gratu\u00efta l'API d'AccuWeather, quan habilitis la previsi\u00f3 meteorol\u00f2gica, les actualitzacions es realitzaran cada 64 minuts en comptes de 32.",
"title": "Opcions d'AccuWeather"
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Already configured. Only a single configuration possible."
},
"error": {
"cannot_connect": "Failed to connect",
"invalid_api_key": "Invalid API key",
"requests_exceeded": "The allowed number of requests to Accuweather API has been exceeded. You have to wait or change API Key."
},
"step": {
"user": {
"data": {
"api_key": "API Key",
"latitude": "Latitude",
"longitude": "Longitude",
"name": "Name of the integration"
},
"description": "If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/accuweather/\n\nSome sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Weather forecast"
},
"description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 64 minutes instead of every 32 minutes.",
"title": "AccuWeather Options"
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Ya est\u00e1 configurado. S\u00f3lo es posible una \u00fanica configuraci\u00f3n."
},
"error": {
"cannot_connect": "No se pudo conectar",
"invalid_api_key": "Clave API no v\u00e1lida",
"requests_exceeded": "Se ha excedido el n\u00famero permitido de solicitudes a la API de Accuweather. Tienes que esperar o cambiar la Clave API."
},
"step": {
"user": {
"data": {
"api_key": "Clave API",
"latitude": "Latitud",
"longitude": "Longitud",
"name": "Nombre de la integraci\u00f3n"
},
"description": "Si necesitas ayuda con la configuraci\u00f3n, echa un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/accuweather/ \n\nEl pron\u00f3stico del tiempo no est\u00e1 habilitado por defecto. Puedes habilitarlo en las opciones de la integraci\u00f3n.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Pron\u00f3stico del tiempo"
},
"description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos.",
"title": "Opciones de AccuWeather"
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione."
},
"error": {
"cannot_connect": "Impossibile connettersi",
"invalid_api_key": "Chiave API non valida",
"requests_exceeded": "\u00c8 stato superato il numero consentito di richieste all'API di Accuweather. \u00c8 necessario attendere o modificare la chiave API."
},
"step": {
"user": {
"data": {
"api_key": "Chiave API",
"latitude": "Latitudine",
"longitude": "Logitudine",
"name": "Nome dell'integrazione"
},
"description": "Se hai bisogno di aiuto con la configurazione dai un'occhiata qui: https://www.home-assistant.io/integrations/accuweather/ \n\nAlcuni sensori non sono abilitati per impostazione predefinita. \u00c8 possibile abilitarli nel registro entit\u00e0 dopo la configurazione di integrazione. \nLe previsioni meteo non sono abilitate per impostazione predefinita. Puoi abilitarle nelle opzioni di integrazione.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Previsioni meteo"
},
"description": "A causa delle limitazioni della versione gratuita della chiave API AccuWeather, quando si abilitano le previsioni del tempo, gli aggiornamenti dei dati verranno eseguiti ogni 64 minuti invece che ogni 32 minuti.",
"title": "Opzioni AccuWeather"
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun ass m\u00e9iglech."
},
"error": {
"cannot_connect": "Feeler beim verbannen",
"invalid_api_key": "Ong\u00ebltegen API Schl\u00ebssel",
"requests_exceeded": "D\u00e9i zougelooss Zuel vun Ufroen un Accuweather API gouf iwwerschratt. Du muss ofwaarden oder den API Schl\u00ebssel \u00e4nneren."
},
"step": {
"user": {
"data": {
"api_key": "API Schl\u00ebssel",
"latitude": "Breedegrad",
"longitude": "L\u00e4ngegrad",
"name": "Numm vun der Integratioun"
},
"description": "Falls du H\u00ebllef mat der Konfiguratioun brauch kuck h\u00e9i:\nhttps://www.home-assistant.io/integrations/accuweather/\n\nWieder Pr\u00e9visounen si standardm\u00e9isseg net aktiv. Du kanns d\u00e9i an den Optioune vun der Integratioun aschalten.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Wieder Pr\u00e9visioun"
},
"description": "Duerch d'Limite vun der Gratis Versioun vun der AccuWeather API, wann d'Wieder Pr\u00e9visoune aktiv\u00e9iert sinn, ginn d'Aktualis\u00e9ierungen all 64 Minutten gemaach, am plaatz vun all 32 Minutten.",
"title": "AccuWeather Optiounen"
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
},
"error": {
"cannot_connect": "Tilkobling mislyktes.",
"invalid_api_key": "Ugyldig API-n\u00f8kkel",
"requests_exceeded": "Det tillatte antallet foresp\u00f8rsler til Accuweather API er overskredet. Du m\u00e5 vente eller endre API-n\u00f8kkel."
},
"step": {
"user": {
"data": {
"api_key": "API-n\u00f8kkel",
"latitude": "Breddegrad",
"longitude": "Lengdegrad",
"name": "Navn p\u00e5 integrasjon"
},
"description": "Hvis du trenger hjelp med konfigurasjonen, kan du se her: https://www.home-assistant.io/integrations/accuweather/ \n\n Noen sensorer er ikke aktivert som standard. Du kan aktivere dem i enhetsregisteret etter integrasjonskonfigurasjonen. \n V\u00e6rmelding er ikke aktivert som standard. Du kan aktivere det i integrasjonsalternativene.",
"title": ""
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "V\u00e6rmelding"
},
"description": "P\u00e5 grunn av begrensningene i gratisversjonen av AccuWeather API-n\u00f8kkelen, n\u00e5r du aktiverer v\u00e6rmelding, vil dataoppdateringer bli utf\u00f8rt hvert 64. minutt i stedet for hvert 32. minutt.",
"title": "AccuWeather-alternativer"
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
},
"error": {
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia.",
"invalid_api_key": "Nieprawid\u0142owy klucz API.",
"requests_exceeded": "Dozwolona liczba zapyta\u0144 do interfejsu API Accuweather zosta\u0142a przekroczona. Musisz poczeka\u0107 lub zmieni\u0107 klucz API."
},
"step": {
"user": {
"data": {
"api_key": "Klucz API",
"latitude": "Szeroko\u015b\u0107 geograficzna",
"longitude": "D\u0142ugo\u015b\u0107 geograficzna",
"name": "Nazwa integracji"
},
"description": "Je\u015bli potrzebujesz pomocy z konfiguracj\u0105, przejd\u017a na stron\u0119: https://www.home-assistant.io/integrations/accuweather/ \n\nCz\u0119\u015b\u0107 sensor\u00f3w nie jest w\u0142\u0105czona domy\u015blnie. Mo\u017cesz je w\u0142\u0105czy\u0107 w rejestrze encji po konfiguracji integracji.\nPrognoza pogody nie jest domy\u015blnie w\u0142\u0105czona. Mo\u017cesz j\u0105 w\u0142\u0105czy\u0107 w opcjach integracji.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Prognoza pogody"
},
"description": "Ze wzgl\u0119du na ograniczenia darmowej wersji klucza API AccuWeather po w\u0142\u0105czeniu prognozy pogody aktualizacje danych b\u0119d\u0105 wykonywane co 64 minut zamiast co 32 minut.",
"title": "Opcje AccuWeather"
}
}
}
}

View File

@@ -0,0 +1,21 @@
{
"config": {
"step": {
"user": {
"data": {
"latitude": "Latitude",
"longitude": "Longitude"
}
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Previs\u00e3o meteorol\u00f3gica"
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e."
},
"error": {
"cannot_connect": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.",
"invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.",
"requests_exceeded": "\u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a API Accuweather. \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API."
},
"step": {
"user": {
"data": {
"api_key": "\u041a\u043b\u044e\u0447 API",
"latitude": "\u0428\u0438\u0440\u043e\u0442\u0430",
"longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430",
"name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"
},
"description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438, \u0435\u0441\u043b\u0438 \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439:\nhttps://www.home-assistant.io/integrations/accuweather/ \n\n\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0441\u043a\u0440\u044b\u0442\u044b \u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b"
},
"description": "\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u043f\u043e\u0433\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0435 64 \u043c\u0438\u043d\u0443\u0442\u044b, \u0430 \u043d\u0435 \u043a\u0430\u0436\u0434\u044b\u0435 32 \u043c\u0438\u043d\u0443\u0442\u044b.",
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 AccuWeather"
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "Klesaj\u00edc\u00ed",
"rising": "Roustouc\u00ed",
"steady": "St\u00e1l\u00fd"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "Falling",
"rising": "Rising",
"steady": "Steady"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "Cayendo",
"rising": "Subiendo",
"steady": "Estable"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "Diminuzione",
"rising": "Aumento",
"steady": "Stabile"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "Fallende",
"rising": "Stiger",
"steady": "Jevn"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "spada",
"rising": "ro\u015bnie",
"steady": "bez zmian"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "\u041f\u043e\u043d\u0438\u0436\u0430\u044e\u0449\u0435\u0435\u0441\u044f",
"rising": "\u041f\u043e\u0432\u044b\u0448\u0430\u044e\u0449\u0435\u0435\u0441\u044f",
"steady": "\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0435"
}
}
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"invalid_api_key": "\u0425\u0438\u0431\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API"
},
"step": {
"user": {
"data": {
"latitude": "\u0428\u0438\u0440\u043e\u0442\u0430",
"longitude": "\u0414\u043e\u0432\u0433\u043e\u0442\u0430",
"name": "\u041d\u0430\u0437\u0432\u0430 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457"
},
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u0438"
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"abort": {
"single_instance_allowed": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u8a2d\u5099\u3002"
},
"error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557",
"invalid_api_key": "API \u5bc6\u9470\u7121\u6548",
"requests_exceeded": "\u5df2\u8d85\u904e Accuweather API \u5141\u8a31\u7684\u8acb\u6c42\u6b21\u6578\u3002\u5fc5\u9808\u7b49\u5019\u6216\u8b8a\u66f4 API \u5bc6\u9470\u3002"
},
"step": {
"user": {
"data": {
"api_key": "API \u5bc6\u9470",
"latitude": "\u7def\u5ea6",
"longitude": "\u7d93\u5ea6",
"name": "\u6574\u5408\u540d\u7a31"
},
"description": "\u5047\u5982\u4f60\u9700\u8981\u5354\u52a9\u9032\u884c\u8a2d\u5b9a\uff0c\u8acb\u53c3\u95b1\uff1ahttps://www.home-assistant.io/integrations/accuweather/\n\n\u5929\u6c23\u9810\u5831\u9810\u8a2d\u672a\u958b\u555f\u3002\u53ef\u4ee5\u65bc\u6574\u5408\u9078\u9805\u4e2d\u958b\u555f\u3002",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "\u5929\u6c23\u9810\u5831"
},
"description": "\u7531\u65bc AccuWeather API \u5bc6\u9470\u514d\u8cbb\u7248\u672c\u9650\u5236\uff0c\u7576\u958b\u555f\u5929\u6c23\u9810\u5831\u6642\u3001\u6578\u64da\u6703\u6bcf 64 \u5206\u9418\u66f4\u65b0\u4e00\u6b21\uff0c\u800c\u975e 32 \u5206\u9418\u3002",
"title": "AccuWeather \u9078\u9805"
}
}
}
}

View File

@@ -0,0 +1,195 @@
"""Support for the AccuWeather service."""
from statistics import mean
from homeassistant.components.weather import (
ATTR_FORECAST_CONDITION,
ATTR_FORECAST_PRECIPITATION,
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
ATTR_FORECAST_TEMP,
ATTR_FORECAST_TEMP_LOW,
ATTR_FORECAST_TIME,
ATTR_FORECAST_WIND_BEARING,
ATTR_FORECAST_WIND_SPEED,
WeatherEntity,
)
from homeassistant.const import CONF_NAME, TEMP_CELSIUS, TEMP_FAHRENHEIT
from homeassistant.util.dt import utc_from_timestamp
from .const import (
ATTR_FORECAST,
ATTRIBUTION,
CONDITION_CLASSES,
COORDINATOR,
DOMAIN,
MANUFACTURER,
NAME,
)
PARALLEL_UPDATES = 1
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add a AccuWeather weather entity from a config_entry."""
name = config_entry.data[CONF_NAME]
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
async_add_entities([AccuWeatherEntity(name, coordinator)], False)
class AccuWeatherEntity(WeatherEntity):
"""Define an AccuWeather entity."""
def __init__(self, name, coordinator):
"""Initialize."""
self._name = name
self.coordinator = coordinator
self._attrs = {}
self._unit_system = "Metric" if self.coordinator.is_metric else "Imperial"
@property
def name(self):
"""Return the name."""
return self._name
@property
def attribution(self):
"""Return the attribution."""
return ATTRIBUTION
@property
def unique_id(self):
"""Return a unique_id for this entity."""
return self.coordinator.location_key
@property
def device_info(self):
"""Return the device info."""
return {
"identifiers": {(DOMAIN, self.coordinator.location_key)},
"name": NAME,
"manufacturer": MANUFACTURER,
"entry_type": "service",
}
@property
def should_poll(self):
"""Return the polling requirement of the entity."""
return False
@property
def available(self):
"""Return True if entity is available."""
return self.coordinator.last_update_success
@property
def condition(self):
"""Return the current condition."""
try:
return [
k
for k, v in CONDITION_CLASSES.items()
if self.coordinator.data["WeatherIcon"] in v
][0]
except IndexError:
return None
@property
def temperature(self):
"""Return the temperature."""
return self.coordinator.data["Temperature"][self._unit_system]["Value"]
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS if self.coordinator.is_metric else TEMP_FAHRENHEIT
@property
def pressure(self):
"""Return the pressure."""
return self.coordinator.data["Pressure"][self._unit_system]["Value"]
@property
def humidity(self):
"""Return the humidity."""
return self.coordinator.data["RelativeHumidity"]
@property
def wind_speed(self):
"""Return the wind speed."""
return self.coordinator.data["Wind"]["Speed"][self._unit_system]["Value"]
@property
def wind_bearing(self):
"""Return the wind bearing."""
return self.coordinator.data["Wind"]["Direction"]["Degrees"]
@property
def visibility(self):
"""Return the visibility."""
return self.coordinator.data["Visibility"][self._unit_system]["Value"]
@property
def ozone(self):
"""Return the ozone level."""
# We only have ozone data for certain locations and only in the forecast data.
if self.coordinator.forecast and self.coordinator.data[ATTR_FORECAST][0].get(
"Ozone"
):
return self.coordinator.data[ATTR_FORECAST][0]["Ozone"]["Value"]
return None
@property
def forecast(self):
"""Return the forecast array."""
if not self.coordinator.forecast:
return None
# remap keys from library to keys understood by the weather component
forecast = [
{
ATTR_FORECAST_TIME: utc_from_timestamp(item["EpochDate"]).isoformat(),
ATTR_FORECAST_TEMP: item["TemperatureMax"]["Value"],
ATTR_FORECAST_TEMP_LOW: item["TemperatureMin"]["Value"],
ATTR_FORECAST_PRECIPITATION: self._calc_precipitation(item),
ATTR_FORECAST_PRECIPITATION_PROBABILITY: round(
mean(
[
item["PrecipitationProbabilityDay"],
item["PrecipitationProbabilityNight"],
]
)
),
ATTR_FORECAST_WIND_SPEED: item["WindDay"]["Speed"]["Value"],
ATTR_FORECAST_WIND_BEARING: item["WindDay"]["Direction"]["Degrees"],
ATTR_FORECAST_CONDITION: [
k for k, v in CONDITION_CLASSES.items() if item["IconDay"] in v
][0],
}
for item in self.coordinator.data[ATTR_FORECAST]
]
return forecast
async def async_added_to_hass(self):
"""Connect to dispatcher listening for entity data notifications."""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)
async def async_update(self):
"""Update AccuWeather entity."""
await self.coordinator.async_request_refresh()
@staticmethod
def _calc_precipitation(day: dict) -> float:
"""Return sum of the precipitation."""
precip_sum = 0
precip_types = ["Rain", "Snow", "Ice"]
for precip in precip_types:
precip_sum = sum(
[
precip_sum,
day[f"{precip}Day"]["Value"],
day[f"{precip}Night"]["Value"],
]
)
return round(precip_sum, 1)

View File

@@ -17,8 +17,10 @@
"user": {
"data": {
"host": "Vert",
"password": "Passord",
"port": "",
"ssl": "AdGuard Hjem bruker et SSL-sertifikat",
"username": "Brukernavn",
"verify_ssl": "AdGuard Home bruker et riktig sertifikat"
},
"description": "Sett opp din AdGuard Hjem instans for \u00e5 tillate overv\u00e5king og kontroll."

View File

@@ -230,7 +230,13 @@ class AdsHub:
hnotify = int(contents.hNotification)
_LOGGER.debug("Received notification %d", hnotify)
data = contents.data
# get dynamically sized data array
data_size = contents.cbSampleSize
data = (ctypes.c_ubyte * data_size).from_address(
ctypes.addressof(contents)
+ pyads.structs.SAdsNotificationHeader.data.offset
)
try:
with self._lock:
@@ -241,17 +247,17 @@ class AdsHub:
# Parse data to desired datatype
if notification_item.plc_datatype == self.PLCTYPE_BOOL:
value = bool(struct.unpack("<?", bytearray(data)[:1])[0])
value = bool(struct.unpack("<?", bytearray(data))[0])
elif notification_item.plc_datatype == self.PLCTYPE_INT:
value = struct.unpack("<h", bytearray(data)[:2])[0]
value = struct.unpack("<h", bytearray(data))[0]
elif notification_item.plc_datatype == self.PLCTYPE_BYTE:
value = struct.unpack("<B", bytearray(data)[:1])[0]
value = struct.unpack("<B", bytearray(data))[0]
elif notification_item.plc_datatype == self.PLCTYPE_UINT:
value = struct.unpack("<H", bytearray(data)[:2])[0]
value = struct.unpack("<H", bytearray(data))[0]
elif notification_item.plc_datatype == self.PLCTYPE_DINT:
value = struct.unpack("<i", bytearray(data)[:4])[0]
value = struct.unpack("<i", bytearray(data))[0]
elif notification_item.plc_datatype == self.PLCTYPE_UDINT:
value = struct.unpack("<I", bytearray(data)[:4])[0]
value = struct.unpack("<I", bytearray(data))[0]
else:
value = bytearray(data)
_LOGGER.warning("No callback available for this datatype")

View File

@@ -2,6 +2,6 @@
"domain": "ads",
"name": "ADS",
"documentation": "https://www.home-assistant.io/integrations/ads",
"requirements": ["pyads==3.1.3"],
"requirements": ["pyads==3.2.1"],
"codeowners": []
}

View File

@@ -3,7 +3,7 @@
"step": {
"user": {
"data": {
"username": "E-pasts"
"port": "Poort"
}
}
}

View File

@@ -11,7 +11,7 @@
"user": {
"data": {
"host": "Vert",
"port": "Port"
"port": ""
},
"title": "Konfigurere Agent DVR"
}

View File

@@ -18,7 +18,9 @@ from .const import (
ATTR_API_PM25,
ATTR_API_PM25_LIMIT,
ATTR_API_PM25_PERCENT,
DEFAULT_NAME,
DOMAIN,
MANUFACTURER,
)
ATTRIBUTION = "Data provided by Airly"
@@ -31,6 +33,8 @@ LABEL_PM_2_5_PERCENT = f"{ATTR_PM_2_5}_percent_of_limit"
LABEL_PM_10_LIMIT = f"{ATTR_PM_10}_limit"
LABEL_PM_10_PERCENT = f"{ATTR_PM_10}_percent_of_limit"
PARALLEL_UPDATES = 1
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Airly air_quality entity based on a config entry."""
@@ -38,9 +42,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
coordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities(
[AirlyAirQuality(coordinator, name, config_entry.unique_id)], False
)
async_add_entities([AirlyAirQuality(coordinator, name)], False)
def round_state(func):
@@ -58,11 +60,10 @@ def round_state(func):
class AirlyAirQuality(AirQualityEntity):
"""Define an Airly air quality."""
def __init__(self, coordinator, name, unique_id):
def __init__(self, coordinator, name):
"""Initialize."""
self.coordinator = coordinator
self._name = name
self._unique_id = unique_id
self._icon = "mdi:blur"
@property
@@ -106,7 +107,19 @@ class AirlyAirQuality(AirQualityEntity):
@property
def unique_id(self):
"""Return a unique_id for this entity."""
return self._unique_id
return f"{self.coordinator.latitude}-{self.coordinator.longitude}"
@property
def device_info(self):
"""Return the device info."""
return {
"identifiers": {
(DOMAIN, self.coordinator.latitude, self.coordinator.longitude)
},
"name": DEFAULT_NAME,
"manufacturer": MANUFACTURER,
"entry_type": "service",
}
@property
def available(self):

View File

@@ -15,5 +15,6 @@ ATTR_API_PRESSURE = "PRESSURE"
ATTR_API_TEMPERATURE = "TEMPERATURE"
DEFAULT_NAME = "Airly"
DOMAIN = "airly"
MANUFACTURER = "Airly sp. z o.o."
MAX_REQUESTS_PER_DAY = 100
NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet."

View File

@@ -4,5 +4,6 @@
"documentation": "https://www.home-assistant.io/integrations/airly",
"codeowners": ["@bieniu"],
"requirements": ["airly==0.0.2"],
"config_flow": true
"config_flow": true,
"quality_scale": "platinum"
}

View File

@@ -18,7 +18,9 @@ from .const import (
ATTR_API_PM1,
ATTR_API_PRESSURE,
ATTR_API_TEMPERATURE,
DEFAULT_NAME,
DOMAIN,
MANUFACTURER,
)
ATTRIBUTION = "Data provided by Airly"
@@ -27,6 +29,8 @@ ATTR_ICON = "icon"
ATTR_LABEL = "label"
ATTR_UNIT = "unit"
PARALLEL_UPDATES = 1
SENSOR_TYPES = {
ATTR_API_PM1: {
ATTR_DEVICE_CLASS: None,
@@ -63,8 +67,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
sensors = []
for sensor in SENSOR_TYPES:
unique_id = f"{config_entry.unique_id}-{sensor.lower()}"
sensors.append(AirlySensor(coordinator, name, sensor, unique_id))
sensors.append(AirlySensor(coordinator, name, sensor))
async_add_entities(sensors, False)
@@ -72,11 +75,10 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class AirlySensor(Entity):
"""Define an Airly sensor."""
def __init__(self, coordinator, name, kind, unique_id):
def __init__(self, coordinator, name, kind):
"""Initialize."""
self.coordinator = coordinator
self._name = name
self._unique_id = unique_id
self.kind = kind
self._device_class = None
self._state = None
@@ -123,7 +125,19 @@ class AirlySensor(Entity):
@property
def unique_id(self):
"""Return a unique_id for this entity."""
return self._unique_id
return f"{self.coordinator.latitude}-{self.coordinator.longitude}-{self.kind.lower()}"
@property
def device_info(self):
"""Return the device info."""
return {
"identifiers": {
(DOMAIN, self.coordinator.latitude, self.coordinator.longitude)
},
"name": DEFAULT_NAME,
"manufacturer": MANUFACTURER,
"entry_type": "service",
}
@property
def unit_of_measurement(self):

View File

@@ -10,7 +10,7 @@
"step": {
"user": {
"data": {
"api_key": "Clave API de Airly",
"api_key": "Clave API",
"latitude": "Latitud",
"longitude": "Longitud",
"name": "Nombre de la integraci\u00f3n"

View File

@@ -1,25 +1,39 @@
{
"device_automation": {
"action_type": {
"arm_away": "Aktivovat {entity_name} v re\u017eimu mimo domov",
"arm_home": "Aktivovat {entity_name} v re\u017eimu doma",
"arm_night": "Aktivovat {entity_name} v re\u017eimu noc",
"disarm": "Deaktivovat {entity_name}",
"arm_away": "Aktivovat {entity_name} v re\u017eimu nep\u0159\u00edtomnost",
"arm_home": "Aktivovat {entity_name} v re\u017eimu domov",
"arm_night": "Aktivovat {entity_name} v no\u010dn\u00edm re\u017eimu",
"disarm": "Odbezpe\u010dit {entity_name}",
"trigger": "Spustit {entity_name}"
},
"condition_type": {
"is_armed_away": "{entity_name} je v re\u017eimu nep\u0159\u00edtomnost",
"is_armed_home": "{entity_name} je v re\u017eimu domov",
"is_armed_night": "{entity_name} je v no\u010dn\u00edm re\u017eimu",
"is_disarmed": "{entity_name} nen\u00ed zabezpe\u010den",
"is_triggered": "{entity_name} je spu\u0161t\u011bn"
},
"trigger_type": {
"armed_away": "{entity_name} v re\u017eimu nep\u0159\u00edtomnost",
"armed_home": "{entity_name} v re\u017eimu domov",
"armed_night": "{entity_name} v no\u010dn\u00edm re\u017eimu",
"disarmed": "{entity_name} nezabezpe\u010den",
"triggered": "{entity_name} spu\u0161t\u011bn"
}
},
"state": {
"_": {
"armed": "Aktivn\u00ed",
"armed_away": "Aktivn\u00ed re\u017eim mimo domov",
"armed_custom_bypass": "Aktivn\u00ed u\u017eivatelsk\u00fdm obejit\u00edm",
"armed_home": "Aktivn\u00ed re\u017eim doma",
"armed_night": "Aktivn\u00ed no\u010dn\u00ed re\u017eim",
"arming": "Aktivov\u00e1n\u00ed",
"disarmed": "Neaktivn\u00ed",
"disarming": "Deaktivov\u00e1n\u00ed",
"pending": "Nadch\u00e1zej\u00edc\u00ed",
"triggered": "Spu\u0161t\u011bno"
"armed": "Zabezpe\u010deno",
"armed_away": "Re\u017eim nep\u0159\u00edtomnost",
"armed_custom_bypass": "Zabezpe\u010deno u\u017eivatelsk\u00fdm obejit\u00edm",
"armed_home": "Re\u017eim domov",
"armed_night": "No\u010dn\u00ed re\u017eim",
"arming": "Zabezpe\u010dov\u00e1n\u00ed",
"disarmed": "Nezabezpe\u010deno",
"disarming": "Odbezpe\u010dov\u00e1n\u00ed",
"pending": "\u010cekaj\u00edc\u00ed",
"triggered": "Spu\u0161t\u011bn"
}
},
"title": "Ovl\u00e1dac\u00ed panel alarmu"

View File

@@ -84,6 +84,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
self._name = "Alarm Panel"
self._state = None
self._ac_power = None
self._alarm_event_occurred = None
self._backlight_on = None
self._battery_low = None
self._check_zone = None
@@ -117,6 +118,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
self._state = STATE_ALARM_DISARMED
self._ac_power = message.ac_power
self._alarm_event_occurred = message.alarm_event_occurred
self._backlight_on = message.backlight_on
self._battery_low = message.battery_low
self._check_zone = message.check_zone
@@ -163,6 +165,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
"""Return the state attributes."""
return {
"ac_power": self._ac_power,
"alarm_event_occurred": self._alarm_event_occurred,
"backlight_on": self._backlight_on,
"battery_low": self._battery_low,
"check_zone": self._check_zone,

View File

@@ -293,7 +293,7 @@ async def async_setup_entry(hass, config_entry):
Client(
config_entry.data[CONF_API_KEY],
config_entry.data[CONF_APP_KEY],
session,
session=session,
),
)
hass.loop.create_task(ambient.ws_connect())

View File

@@ -43,7 +43,9 @@ class AmbientStationFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured()
session = aiohttp_client.async_get_clientsession(self.hass)
client = Client(user_input[CONF_API_KEY], user_input[CONF_APP_KEY], session)
client = Client(
user_input[CONF_API_KEY], user_input[CONF_APP_KEY], session=session
)
try:
devices = await client.api.get_devices()

View File

@@ -3,6 +3,6 @@
"name": "Ambient Weather Station",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/ambient_station",
"requirements": ["aioambient==1.1.1"],
"requirements": ["aioambient==1.2.1"],
"codeowners": ["@bachya"]
}

View File

@@ -3,8 +3,8 @@
"name": "Android TV",
"documentation": "https://www.home-assistant.io/integrations/androidtv",
"requirements": [
"adb-shell[async]==0.2.0",
"androidtv[async]==0.0.46",
"adb-shell[async]==0.2.1",
"androidtv[async]==0.0.47",
"pure-python-adb==0.2.2.dev0"
],
"codeowners": ["@JeffLIrion"]

View File

@@ -5,7 +5,6 @@ import logging
import os
from adb_shell.auth.keygen import keygen
from adb_shell.auth.sign_pythonrsa import PythonRSASigner
from adb_shell.exceptions import (
AdbTimeoutError,
InvalidChecksumError,
@@ -14,6 +13,7 @@ from adb_shell.exceptions import (
TcpTimeoutException,
)
from androidtv import ha_state_detection_rules_validator
from androidtv.adb_manager.adb_manager_sync import ADBPythonSync
from androidtv.constants import APPS, KEYS
from androidtv.exceptions import LockNotAcquiredException
from androidtv.setup_async import setup
@@ -40,6 +40,7 @@ from homeassistant.const import (
CONF_HOST,
CONF_NAME,
CONF_PORT,
EVENT_HOMEASSISTANT_STOP,
STATE_IDLE,
STATE_OFF,
STATE_PAUSED,
@@ -175,9 +176,7 @@ def setup_androidtv(hass, config):
keygen(adbkey)
# Load the ADB key
with open(adbkey) as priv_key:
priv = priv_key.read()
signer = PythonRSASigner("", priv)
signer = ADBPythonSync.load_adbkey(adbkey)
adb_log = f"using Python ADB implementation with adbkey='{adbkey}'"
else:
@@ -230,6 +229,13 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
)
raise PlatformNotReady
async def _async_close(event):
"""Close the ADB socket connection when HA stops."""
await aftv.adb_close()
# Close the ADB connection when HA stops
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_close)
device_args = [
aftv,
config[CONF_NAME],

View File

@@ -11,6 +11,10 @@
"description": "Vil du legge Arcam FMJ p\u00e5 ` {host} ` til Home Assistant? "
},
"user": {
"data": {
"host": "Vert",
"port": ""
},
"description": "Vennligst skriv inn vertsnavnet eller IP-adressen til enheten."
}
}

View File

@@ -1,5 +1,8 @@
{
"config": {
"abort": {
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane."
},
"step": {
"user": {
"data": {

View File

@@ -12,7 +12,7 @@
"data": {
"email": "E-post (valgfritt)",
"host": "Vert",
"port": "Port "
"port": ""
},
"title": "Koble til enheten"
}

View File

@@ -23,7 +23,13 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import Context, CoreState, HomeAssistant, callback
from homeassistant.core import (
Context,
CoreState,
HomeAssistant,
callback,
split_entity_id,
)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import condition, extract_domain_configs
import homeassistant.helpers.config_validation as cv
@@ -61,6 +67,7 @@ CONF_TRIGGER = "trigger"
CONF_CONDITION_TYPE = "condition_type"
CONF_INITIAL_STATE = "initial_state"
CONF_SKIP_CONDITION = "skip_condition"
CONF_STOP_ACTIONS = "stop_actions"
CONDITION_USE_TRIGGER_VALUES = "use_trigger_values"
CONDITION_TYPE_AND = "and"
@@ -69,6 +76,7 @@ CONDITION_TYPE_OR = "or"
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
DEFAULT_INITIAL_STATE = True
DEFAULT_STOP_ACTIONS = True
EVENT_AUTOMATION_RELOADED = "automation_reloaded"
EVENT_AUTOMATION_TRIGGERED = "automation_triggered"
@@ -219,7 +227,11 @@ async def async_setup(hass, config):
)
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
component.async_register_entity_service(
SERVICE_TURN_OFF,
{vol.Optional(CONF_STOP_ACTIONS, default=DEFAULT_STOP_ACTIONS): cv.boolean},
"async_turn_off",
)
async def reload_service_handler(service_call):
"""Remove all automations and load new ones from config."""
@@ -255,11 +267,13 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
self._async_detach_triggers = None
self._cond_func = cond_func
self.action_script = action_script
self.action_script.change_listener = self.async_write_ha_state
self._last_triggered = None
self._initial_state = initial_state
self._is_enabled = False
self._referenced_entities: Optional[Set[str]] = None
self._referenced_devices: Optional[Set[str]] = None
self._logger = _LOGGER
@property
def name(self):
@@ -282,11 +296,10 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
attrs = {
ATTR_LAST_TRIGGERED: self._last_triggered,
ATTR_MODE: self.action_script.script_mode,
ATTR_CUR: self.action_script.runs,
}
if self.action_script.supports_max:
attrs[ATTR_MAX] = self.action_script.max_runs
if self.is_on:
attrs[ATTR_CUR] = self.action_script.runs
return attrs
@property
@@ -337,13 +350,18 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
"""Startup with initial state or previous state."""
await super().async_added_to_hass()
self._logger = logging.getLogger(
f"{__name__}.{split_entity_id(self.entity_id)[1]}"
)
self.action_script.update_logger(self._logger)
state = await self.async_get_last_state()
if state:
enable_automation = state.state == STATE_ON
last_triggered = state.attributes.get("last_triggered")
if last_triggered is not None:
self._last_triggered = parse_datetime(last_triggered)
_LOGGER.debug(
self._logger.debug(
"Loaded automation %s with state %s from state "
" storage last state %s",
self.entity_id,
@@ -352,7 +370,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
)
else:
enable_automation = DEFAULT_INITIAL_STATE
_LOGGER.debug(
self._logger.debug(
"Automation %s not in state storage, state %s from default is used",
self.entity_id,
enable_automation,
@@ -360,7 +378,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
if self._initial_state is not None:
enable_automation = self._initial_state
_LOGGER.debug(
self._logger.debug(
"Automation %s initial state %s overridden from "
"config initial_state",
self.entity_id,
@@ -376,7 +394,10 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
await self.async_disable()
if CONF_STOP_ACTIONS in kwargs:
await self.async_disable(kwargs[CONF_STOP_ACTIONS])
else:
await self.async_disable()
async def async_trigger(self, variables, skip_condition=False, context=None):
"""Trigger automation.
@@ -403,12 +424,12 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
context=trigger_context,
)
_LOGGER.info("Executing %s", self._name)
self._logger.info("Executing %s", self._name)
try:
await self.action_script.async_run(variables, trigger_context)
except Exception: # pylint: disable=broad-except
_LOGGER.exception("While executing automation %s", self.entity_id)
self._logger.exception("While executing automation %s", self.entity_id)
async def async_will_remove_from_hass(self):
"""Remove listeners when removing automation from Home Assistant."""
@@ -444,9 +465,9 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
)
self.async_write_ha_state()
async def async_disable(self):
async def async_disable(self, stop_actions=DEFAULT_STOP_ACTIONS):
"""Disable the automation entity."""
if not self._is_enabled:
if not self._is_enabled and not self.action_script.runs:
return
self._is_enabled = False
@@ -455,7 +476,8 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
self._async_detach_triggers()
self._async_detach_triggers = None
await self.action_script.async_stop()
if stop_actions:
await self.action_script.async_stop()
self.async_write_ha_state()
@@ -478,13 +500,13 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
results = await asyncio.gather(*triggers)
if None in results:
_LOGGER.error("Error setting up trigger %s", self._name)
self._logger.error("Error setting up trigger %s", self._name)
removes = [remove for remove in results if remove is not None]
if not removes:
return None
_LOGGER.info("Initialized trigger %s", self._name)
self._logger.info("Initialized trigger %s", self._name)
@callback
def remove_triggers():

View File

@@ -12,6 +12,9 @@ turn_off:
entity_id:
description: Name of the automation to turn off.
example: "automation.notify_home"
stop_actions:
description: Stop currently running actions (defaults to true).
example: false
toggle:
description: Toggle an automation.
@@ -27,7 +30,7 @@ trigger:
description: Name of the automation to trigger.
example: "automation.notify_home"
skip_condition:
description: Whether or not the condition will be skipped (defaults to True).
description: Whether or not the condition will be skipped (defaults to true).
example: true
reload:

View File

@@ -73,16 +73,13 @@ async def async_attach_trigger(
from_s = event.data.get("old_state")
to_s = event.data.get("new_state")
old_state = getattr(from_s, "state", None)
new_state = getattr(to_s, "state", None)
if (
(from_s is not None and not match_from_state(from_s.state))
or (to_s is not None and not match_to_state(to_s.state))
or (
not match_all
and from_s is not None
and to_s is not None
and from_s.state == to_s.state
)
not match_from_state(old_state)
or not match_to_state(new_state)
or (not match_all and old_state == new_state)
):
return
@@ -104,15 +101,6 @@ async def async_attach_trigger(
)
)
# Ignore changes to state attributes if from/to is in use
if (
not match_all
and from_s is not None
and to_s is not None
and from_s.state == to_s.state
):
return
if not time_delta:
call_action()
return

View File

@@ -13,20 +13,37 @@ from homeassistant.helpers.event import async_track_time_change
_LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.Schema(
{vol.Required(CONF_PLATFORM): "time", vol.Required(CONF_AT): cv.time}
{
vol.Required(CONF_PLATFORM): "time",
vol.Required(CONF_AT): vol.All(cv.ensure_list, [cv.time]),
}
)
async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration."""
at_time = config.get(CONF_AT)
hours, minutes, seconds = at_time.hour, at_time.minute, at_time.second
at_times = config[CONF_AT]
@callback
def time_automation_listener(now):
"""Listen for time changes and calls action."""
hass.async_run_job(action, {"trigger": {"platform": "time", "now": now}})
return async_track_time_change(
hass, time_automation_listener, hour=hours, minute=minutes, second=seconds
)
removes = [
async_track_time_change(
hass,
time_automation_listener,
hour=at_time.hour,
minute=at_time.minute,
second=at_time.second,
)
for at_time in at_times
]
@callback
def remove_track_time_changes():
"""Remove tracked time changes."""
for remove in removes:
remove()
return remove_track_time_changes

View File

@@ -1,17 +1,25 @@
{
"config": {
"abort": {
"already_configured": "Kontoen er allerede konfigurert",
"no_devices": "Ingen enheter funnet p\u00e5 nettverket",
"reauth_successful": "Tilgangstoken oppdatert"
},
"error": {
"auth": "Ugyldig tilgangstoken",
"unknown": "Ukjent Awair API-feil."
},
"step": {
"reauth": {
"data": {
"access_token": "Tilgangstoken",
"email": "Epost"
},
"description": "Skriv inn tilgangstokenet for Awair-utviklere p\u00e5 nytt."
},
"user": {
"data": {
"access_token": "Tilgangstoken",
"email": "Epost "
},
"description": "Du m\u00e5 registrere deg for et Awair-utviklertilgangstoken p\u00e5: https://developer.getawair.com/onboard/login"

View File

@@ -0,0 +1,26 @@
{
"config": {
"abort": {
"already_configured": "Konto jest ju\u017c skonfigurowane.",
"no_devices": "Nie znaleziono urz\u0105dze\u0144 w sieci.",
"reauth_successful": "Token dost\u0119pu pomy\u015blnie zaktualizowano."
},
"error": {
"auth": "Token dost\u0119pu"
},
"step": {
"reauth": {
"data": {
"access_token": "Token dost\u0119pu",
"email": "Adres e-mail"
}
},
"user": {
"data": {
"access_token": "Token dost\u0119pu",
"email": "Adres e-mail"
}
}
}
}
}

View File

@@ -18,7 +18,7 @@
"data": {
"host": "Vert",
"password": "Passord",
"port": "Port",
"port": "",
"username": "Brukernavn"
},
"title": "Sett opp Axis enhet"

View File

@@ -0,0 +1,121 @@
"""Support for Azure DevOps."""
import logging
from typing import Any, Dict
from aioazuredevops.client import DevOpsClient
import aiohttp
from homeassistant.components.azure_devops.const import (
CONF_ORG,
CONF_PAT,
CONF_PROJECT,
DATA_AZURE_DEVOPS_CLIENT,
DOMAIN,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
"""Set up the Azure DevOps components."""
return True
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
"""Set up Azure DevOps from a config entry."""
client = DevOpsClient()
try:
if entry.data[CONF_PAT] is not None:
await client.authorize(entry.data[CONF_PAT], entry.data[CONF_ORG])
if not client.authorized:
_LOGGER.warning(
"Could not authorize with Azure DevOps. You may need to update your token"
)
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=entry.data,
)
)
return False
await client.get_project(entry.data[CONF_ORG], entry.data[CONF_PROJECT])
except aiohttp.ClientError as exception:
_LOGGER.warning(exception)
raise ConfigEntryNotReady from exception
instance_key = f"{DOMAIN}_{entry.data[CONF_ORG]}_{entry.data[CONF_PROJECT]}"
hass.data.setdefault(instance_key, {})[DATA_AZURE_DEVOPS_CLIENT] = client
# Setup components
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "sensor")
)
return True
async def async_unload_entry(hass: HomeAssistantType, entry: ConfigType) -> bool:
"""Unload Azure DevOps config entry."""
del hass.data[f"{DOMAIN}_{entry.data[CONF_ORG]}_{entry.data[CONF_PROJECT]}"]
return await hass.config_entries.async_forward_entry_unload(entry, "sensor")
class AzureDevOpsEntity(Entity):
"""Defines a base Azure DevOps entity."""
def __init__(self, organization: str, project: str, name: str, icon: str) -> None:
"""Initialize the Azure DevOps entity."""
self._name = name
self._icon = icon
self._available = True
self.organization = organization
self.project = project
@property
def name(self) -> str:
"""Return the name of the entity."""
return self._name
@property
def icon(self) -> str:
"""Return the mdi icon of the entity."""
return self._icon
@property
def available(self) -> bool:
"""Return True if entity is available."""
return self._available
async def async_update(self) -> None:
"""Update Azure DevOps entity."""
if await self._azure_devops_update():
self._available = True
else:
if self._available:
_LOGGER.debug(
"An error occurred while updating Azure DevOps sensor.",
exc_info=True,
)
self._available = False
async def _azure_devops_update(self) -> None:
"""Update Azure DevOps entity."""
raise NotImplementedError()
class AzureDevOpsDeviceEntity(AzureDevOpsEntity):
"""Defines a Azure DevOps device entity."""
@property
def device_info(self) -> Dict[str, Any]:
"""Return device information about this Azure DevOps instance."""
return {
"identifiers": {(DOMAIN, self.organization, self.project,)},
"manufacturer": self.organization,
"name": self.project,
}

View File

@@ -0,0 +1,134 @@
"""Config flow to configure the Azure DevOps integration."""
import logging
from aioazuredevops.client import DevOpsClient
import aiohttp
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.azure_devops.const import ( # pylint:disable=unused-import
CONF_ORG,
CONF_PAT,
CONF_PROJECT,
DOMAIN,
)
from homeassistant.config_entries import ConfigFlow
_LOGGER = logging.getLogger(__name__)
class AzureDevOpsFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a Azure DevOps config flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
def __init__(self):
"""Initialize config flow."""
self._organization = None
self._project = None
self._pat = None
async def _show_setup_form(self, errors=None):
"""Show the setup form to the user."""
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_ORG, default=self._organization): str,
vol.Required(CONF_PROJECT, default=self._project): str,
vol.Optional(CONF_PAT): str,
}
),
errors=errors or {},
)
async def _show_reauth_form(self, errors=None):
"""Show the reauth form to the user."""
return self.async_show_form(
step_id="reauth",
description_placeholders={
"project_url": f"{self._organization}/{self._project}"
},
data_schema=vol.Schema({vol.Required(CONF_PAT): str}),
errors=errors or {},
)
async def _check_setup(self):
"""Check the setup of the flow."""
errors = {}
client = DevOpsClient()
try:
if self._pat is not None:
await client.authorize(self._pat, self._organization)
if not client.authorized:
errors["base"] = "authorization_error"
return errors
project_info = await client.get_project(self._organization, self._project)
if project_info is None:
errors["base"] = "project_error"
return errors
except aiohttp.ClientError:
errors["base"] = "connection_error"
return errors
return None
async def async_step_user(self, user_input=None):
"""Handle a flow initiated by the user."""
if user_input is None:
return await self._show_setup_form(user_input)
self._organization = user_input[CONF_ORG]
self._project = user_input[CONF_PROJECT]
self._pat = user_input.get(CONF_PAT)
await self.async_set_unique_id(f"{self._organization}_{self._project}")
self._abort_if_unique_id_configured()
errors = await self._check_setup()
if errors is not None:
return await self._show_setup_form(errors)
return self._async_create_entry()
async def async_step_reauth(self, user_input):
"""Handle configuration by re-auth."""
if user_input.get(CONF_ORG) and user_input.get(CONF_PROJECT):
self._organization = user_input[CONF_ORG]
self._project = user_input[CONF_PROJECT]
self._pat = user_input[CONF_PAT]
# pylint: disable=no-member
self.context["title_placeholders"] = {
"project_url": f"{self._organization}/{self._project}",
}
await self.async_set_unique_id(f"{self._organization}_{self._project}")
errors = await self._check_setup()
if errors is not None:
return await self._show_reauth_form(errors)
for entry in self._async_current_entries():
if entry.unique_id == self.unique_id:
self.hass.config_entries.async_update_entry(
entry,
data={
CONF_ORG: self._organization,
CONF_PROJECT: self._project,
CONF_PAT: self._pat,
},
)
return self.async_abort(reason="reauth_successful")
def _async_create_entry(self):
"""Handle create entry."""
return self.async_create_entry(
title=f"{self._organization}/{self._project}",
data={
CONF_ORG: self._organization,
CONF_PROJECT: self._project,
CONF_PAT: self._pat,
},
)

View File

@@ -0,0 +1,11 @@
"""Constants for the Azure DevOps integration."""
DOMAIN = "azure_devops"
DATA_AZURE_DEVOPS_CLIENT = "azure_devops_client"
DATA_ORG = "organization"
DATA_PROJECT = "project"
DATA_PAT = "personal_access_token"
CONF_ORG = "organization"
CONF_PROJECT = "project"
CONF_PAT = "personal_access_token"

View File

@@ -0,0 +1,8 @@
{
"domain": "azure_devops",
"name": "Azure DevOps",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/azure_devops",
"requirements": ["aioazuredevops==1.3.5"],
"codeowners": ["@timmo001"]
}

View File

@@ -0,0 +1,148 @@
"""Support for Azure DevOps sensors."""
from datetime import timedelta
import logging
from typing import List
from aioazuredevops.builds import DevOpsBuild
from aioazuredevops.client import DevOpsClient
import aiohttp
from homeassistant.components.azure_devops import AzureDevOpsDeviceEntity
from homeassistant.components.azure_devops.const import (
CONF_ORG,
CONF_PROJECT,
DATA_AZURE_DEVOPS_CLIENT,
DATA_ORG,
DATA_PROJECT,
DOMAIN,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.typing import HomeAssistantType
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=300)
PARALLEL_UPDATES = 4
BUILDS_QUERY = "?queryOrder=queueTimeDescending&maxBuildsPerDefinition=1"
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
) -> None:
"""Set up Azure DevOps sensor based on a config entry."""
instance_key = f"{DOMAIN}_{entry.data[CONF_ORG]}_{entry.data[CONF_PROJECT]}"
client = hass.data[instance_key][DATA_AZURE_DEVOPS_CLIENT]
organization = entry.data[DATA_ORG]
project = entry.data[DATA_PROJECT]
sensors = []
try:
builds: List[DevOpsBuild] = await client.get_builds(
organization, project, BUILDS_QUERY
)
except aiohttp.ClientError as exception:
_LOGGER.warning(exception)
raise PlatformNotReady from exception
for build in builds:
sensors.append(
AzureDevOpsLatestBuildSensor(client, organization, project, build)
)
async_add_entities(sensors, True)
class AzureDevOpsSensor(AzureDevOpsDeviceEntity):
"""Defines a Azure DevOps sensor."""
def __init__(
self,
client: DevOpsClient,
organization: str,
project: str,
key: str,
name: str,
icon: str,
measurement: str = "",
unit_of_measurement: str = "",
) -> None:
"""Initialize Azure DevOps sensor."""
self._state = None
self._attributes = None
self._available = False
self._unit_of_measurement = unit_of_measurement
self.measurement = measurement
self.client = client
self.organization = organization
self.project = project
self.key = key
super().__init__(organization, project, name, icon)
@property
def unique_id(self) -> str:
"""Return the unique ID for this sensor."""
return "_".join([self.organization, self.key])
@property
def state(self) -> str:
"""Return the state of the sensor."""
return self._state
@property
def device_state_attributes(self) -> object:
"""Return the attributes of the sensor."""
return self._attributes
@property
def unit_of_measurement(self) -> str:
"""Return the unit this state is expressed in."""
return self._unit_of_measurement
class AzureDevOpsLatestBuildSensor(AzureDevOpsSensor):
"""Defines a Azure DevOps card count sensor."""
def __init__(
self, client: DevOpsClient, organization: str, project: str, build: DevOpsBuild
):
"""Initialize Azure DevOps sensor."""
self.build: DevOpsBuild = build
super().__init__(
client,
organization,
project,
f"{build.project.id}_{build.definition.id}_latest_build",
f"{build.project.name} {build.definition.name} Latest Build",
"mdi:pipe",
)
async def _azure_devops_update(self) -> bool:
"""Update Azure DevOps entity."""
try:
build: DevOpsBuild = await self.client.get_build(
self.organization, self.project, self.build.id
)
except aiohttp.ClientError as exception:
_LOGGER.warning(exception)
self._available = False
return False
self._state = build.build_number
self._attributes = {
"definition_id": build.definition.id,
"definition_name": build.definition.name,
"id": build.id,
"reason": build.reason,
"result": build.result,
"source_branch": build.source_branch,
"source_version": build.source_version,
"status": build.status,
"url": build.links.web,
"queue_time": build.queue_time,
"start_time": build.start_time,
"finish_time": build.finish_time,
}
self._available = True
return True

View File

@@ -0,0 +1,33 @@
{
"config": {
"flow_title": "Azure DevOps: {project_url}",
"error": {
"authorization_error": "Authorization error. Check you have access to the project and have the correct credentials.",
"connection_error": "Could not connect to Azure DevOps.",
"project_error": "Could not get project info."
},
"step": {
"user": {
"data": {
"organization": "Organization",
"project": "Project",
"personal_access_token": "Personal Access Token (PAT)"
},
"description": "Set up an Azure DevOps instance to access your project. A Personal Access Token is only required for a private project.",
"title": "Add Azure DevOps Project"
},
"reauth": {
"data": {
"personal_access_token": "Personal Access Token (PAT)"
},
"description": "Authentication failed for {project_url}. Please enter your current credentials.",
"title": "Reauthentication"
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"reauth_successful": "[%key:common::config_flow::data::access_token%] updated successfully"
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "El compte ja ha estat configurat",
"reauth_successful": "Token d'acc\u00e9s actualitzat correctament"
},
"error": {
"authorization_error": "Error d'autoritzaci\u00f3. Comprova que tens acc\u00e9s al projecte i tens les credencials correctes.",
"connection_error": "No s'ha pogut connectar a Azure DevOps.",
"project_error": "No s'ha pogut obtenir la informaci\u00f3 del projecte."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "Token d'Acc\u00e9s Personal (PAT)"
},
"description": "L'autenticaci\u00f3 de {project_url} ha fallat. Si us plau, introdueix les teves credencials actuals.",
"title": "Reautenticaci\u00f3"
},
"user": {
"data": {
"organization": "Organitzaci\u00f3",
"personal_access_token": "Token d'Acc\u00e9s Personal (PAT)",
"project": "Projecte"
},
"description": "Configura una inst\u00e0ncia d'Azure DevOps per accedir al teu projecte. El token d'acc\u00e9s personal nom\u00e9s \u00e9s necessari per a projectes privats.",
"title": "Afegeix un projecte Azure DevOps"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "Account is already configured",
"reauth_successful": "Access Token updated successfully"
},
"error": {
"authorization_error": "Authorization error. Check you have access to the project and have the correct credentials.",
"connection_error": "Could not connect to Azure DevOps.",
"project_error": "Could not get project info."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "Personal Access Token (PAT)"
},
"description": "Authentication failed for {project_url}. Please enter your current credentials.",
"title": "Reauthentication"
},
"user": {
"data": {
"organization": "Organization",
"personal_access_token": "Personal Access Token (PAT)",
"project": "Project"
},
"description": "Set up an Azure DevOps instance to access your project. A Personal Access Token is only required for a private project.",
"title": "Add Azure DevOps Project"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "La cuenta ya ha sido configurada",
"reauth_successful": "Token de acceso actualizado correctamente "
},
"error": {
"authorization_error": "Error de autorizaci\u00f3n. Comprueba que tienes acceso al proyecto y las credenciales son correctas.",
"connection_error": "No se pudo conectar con Azure DevOps",
"project_error": "No se pudo obtener informaci\u00f3n del proyecto."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "Token Personal de Acceso (PAT)"
},
"description": "Error de autenticaci\u00f3n para {project_url}. Por favor, introduce tus credenciales actuales.",
"title": "Reautenticaci\u00f3n"
},
"user": {
"data": {
"organization": "Organizaci\u00f3n",
"personal_access_token": "Token Personal de Acceso (PAT)",
"project": "Proyecto"
},
"description": "Configura una instancia de Azure DevOps para acceder a tu proyecto. Un Token Personal de Acceso s\u00f3lo es necesario para un proyecto privado.",
"title": "A\u00f1adir Proyecto Azure DevOps"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "L'account \u00e8 gi\u00e0 configurato",
"reauth_successful": "Token di accesso aggiornato correttamente"
},
"error": {
"authorization_error": "Errore di autorizzazione. Verificare di avere accesso al progetto e disporre delle credenziali corrette.",
"connection_error": "Impossibile connettersi ad Azure DevOps.",
"project_error": "Non \u00e8 stato possibile ottenere informazioni sul progetto."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "Token di Accesso Personale (PAT)"
},
"description": "Autenticazione non riuscita per {project_url}. Si prega di inserire le proprie credenziali attuali.",
"title": "Riautenticazione"
},
"user": {
"data": {
"organization": "Organizzazione",
"personal_access_token": "Token di Accesso Personale (PAT)",
"project": "Progetto"
},
"description": "Configurare un'istanza di DevOps di Azure per accedere al progetto. Un Token di Accesso Personale (PAT) \u00e8 richiesto solo per un progetto privato.",
"title": "Aggiungere un progetto Azure DevOps"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "Kont ass scho konfigur\u00e9iert",
"reauth_successful": "Acc\u00e8s Jeton erfollegr\u00e4ich aktualis\u00e9iert"
},
"error": {
"authorization_error": "Feeler bei der Authorisatioun. Iwwerpr\u00e9if ob d\u00e4in Kont den acc\u00e8s zum Projet souw\u00e9i d\u00e9i richteg Umeldungsinformatioune huet",
"connection_error": "Konnt sech net mat Azure DevOps verbannen",
"project_error": "Konnt keng Projet Informatiounen ausliesen."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "Pers\u00e9inlechen Acc\u00e8s Jeton (PAT)"
},
"description": "Feeler bei der Authentifikatioun fir {project_url}. G\u00ebff deng aktuell Umeldungsinformatiounen an.",
"title": "Reauthentifikatioun"
},
"user": {
"data": {
"organization": "Organisatioun",
"personal_access_token": "Pers\u00e9inlechen Acc\u00e8s Jeton (PAT)",
"project": "Projet"
},
"description": "Riicht eng Azure DevOps Instanz an fir d\u00e4in Projet z'acc\u00e9d\u00e9ieren. E Pers\u00e9inlechen Acc\u00e8s Jetons ass n\u00ebmme fir ee Private Projet n\u00e9ideg.",
"title": "Azure DevOps Project dob\u00e4isetzen"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "Kontoen er allerede konfigurert",
"reauth_successful": "Tilgangstoken oppdatert"
},
"error": {
"authorization_error": "Autoriseringsfeil. Sjekk at du har tilgang til prosjektet og har riktig legitimasjon.",
"connection_error": "Kunne ikke koble til Azure DevOps.",
"project_error": "Kunne ikke f\u00e5 prosjektinformasjon."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "Token for personlig tilgang (PAT)"
},
"description": "Autentiseringen mislyktes for {project_url} . Vennligst skriv inn gjeldende legitimasjon.",
"title": "reautentisering"
},
"user": {
"data": {
"organization": "Organisasjon",
"personal_access_token": "Token for personlig tilgang (PAT)",
"project": "Prosjekt"
},
"description": "Sett opp en Azure DevOps-forekomst for \u00e5 f\u00e5 tilgang til prosjektet ditt. En personlig tilgangstoken er bare n\u00f8dvendig for et privat prosjekt.",
"title": "Legg til Azure DevOps Project"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430.",
"reauth_successful": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d."
},
"error": {
"authorization_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0443 \u0412\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0443, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.",
"connection_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a Azure DevOps.",
"project_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0435."
},
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "\u041f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (PAT)"
},
"description": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 {project_url}. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.",
"title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f"
},
"user": {
"data": {
"organization": "\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f",
"personal_access_token": "\u041f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (PAT)",
"project": "\u041f\u0440\u043e\u0435\u043a\u0442"
},
"description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c Azure DevOps. \u041f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0430\u0441\u0442\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432.",
"title": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 Azure DevOps"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"flow_title": "Azure DevOps: {project_url}",
"step": {
"reauth": {
"title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f"
},
"user": {
"data": {
"organization": "\u041e\u0440\u0433\u0430\u043d\u0456\u0437\u0430\u0446\u0456\u044f",
"personal_access_token": "\u0422\u043e\u043a\u0435\u043d \u043e\u0441\u043e\u0431\u0438\u0441\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0443 (PAT)",
"project": "\u041f\u0440\u043e\u0454\u043a\u0442"
},
"title": "\u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 Azure DevOps"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
"reauth_successful": "\u5b58\u53d6\u5bc6\u9470\u5df2\u6210\u529f\u66f4\u65b0"
},
"error": {
"authorization_error": "\u8a8d\u8b49\u932f\u8aa4\u3002\u8acb\u78ba\u8a8d\u64c1\u6709\u5c08\u6848\u5b58\u53d6\u6b0a\u8207\u6b63\u78ba\u7684\u8b49\u66f8\u3002",
"connection_error": "\u7121\u6cd5\u9023\u7dda\u81f3 Azure DevOps\u3002",
"project_error": "\u7121\u6cd5\u53d6\u5f97\u5c08\u6848\u8cc7\u8a0a\u3002"
},
"flow_title": "Azure DevOps\uff1a{project_url}",
"step": {
"reauth": {
"data": {
"personal_access_token": "\u500b\u4eba\u5b58\u53d6\u5bc6\u9470\uff08PAT\uff09"
},
"description": "{project_url}\u8a8d\u8b49\u5931\u6557\u3002\u8acb\u8f38\u5165\u76ee\u524d\u8b49\u66f8\u3002",
"title": "\u91cd\u65b0\u8a8d\u8b49"
},
"user": {
"data": {
"organization": "\u7d44\u7e54",
"personal_access_token": "\u500b\u4eba\u5b58\u53d6\u5bc6\u9470\uff08PAT\uff09",
"project": "\u5c08\u6848"
},
"description": "\u8a2d\u5b9a Azure DevOps \u4ee5\u5b58\u53d6\u5c08\u6848\u3002\u79c1\u4eba\u5c08\u6848\u5247\u9700\u8981\u8f38\u5165\u300c\u500b\u4eba\u5b58\u53d6\u5bc6\u9470\uff09\u3002",
"title": "\u65b0\u589e Azure DevOps \u5c08\u6848"
}
}
},
"title": "Azure DevOps"
}

View File

@@ -131,7 +131,7 @@
"on": "H\u00famedo"
},
"motion": {
"off": "Sin movimiento",
"off": "No detectado",
"on": "Detectado"
},
"occupancy": {

View File

@@ -0,0 +1,12 @@
{
"config": {
"step": {
"user": {
"data": {
"host": "IP-adres",
"port": "Poort"
}
}
}
}
}

View File

@@ -14,7 +14,7 @@
"user": {
"data": {
"host": "IP adresse",
"port": "Port"
"port": ""
},
"description": "Konfigurer BleBox-en til \u00e5 integreres med Home Assistant.",
"title": "Konfigurere BleBox-enheten"

View File

@@ -1,32 +1,25 @@
"""Support for Blink Home Camera System."""
import asyncio
from copy import deepcopy
import logging
from blinkpy.auth import Auth
from blinkpy.blinkpy import Blink
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import (
CONF_FILENAME,
CONF_NAME,
CONF_PASSWORD,
CONF_PIN,
CONF_SCAN_INTERVAL,
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from .const import (
DEFAULT_OFFSET,
from homeassistant.components import persistent_notification
from homeassistant.components.blink.const import (
DEFAULT_SCAN_INTERVAL,
DEVICE_ID,
DOMAIN,
PLATFORMS,
SERVICE_REFRESH,
SERVICE_SAVE_VIDEO,
SERVICE_SEND_PIN,
)
from homeassistant.const import CONF_FILENAME, CONF_NAME, CONF_PIN, CONF_SCAN_INTERVAL
from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -35,58 +28,50 @@ SERVICE_SAVE_VIDEO_SCHEMA = vol.Schema(
)
SERVICE_SEND_PIN_SCHEMA = vol.Schema({vol.Optional(CONF_PIN): cv.string})
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): int,
}
)
},
extra=vol.ALLOW_EXTRA,
)
def _blink_startup_wrapper(entry):
def _blink_startup_wrapper(hass, entry):
"""Startup wrapper for blink."""
blink = Blink(
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
motion_interval=DEFAULT_OFFSET,
legacy_subdomain=False,
no_prompt=True,
device_id=DEVICE_ID,
)
blink = Blink()
auth_data = deepcopy(dict(entry.data))
blink.auth = Auth(auth_data, no_prompt=True)
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
try:
blink.login_response = entry.data["login_response"]
blink.setup_params(entry.data["login_response"])
except KeyError:
blink.get_auth_token()
if blink.start():
blink.setup_post_verify()
elif blink.auth.check_key_required():
_LOGGER.debug("Attempting a reauth flow")
_reauth_flow_wrapper(hass, auth_data)
blink.setup_params(entry.data["login_response"])
blink.setup_post_verify()
return blink
async def async_setup(hass, config):
"""Set up a config entry."""
hass.data[DOMAIN] = {}
if DOMAIN not in config:
return True
conf = config.get(DOMAIN, {})
if not hass.config_entries.async_entries(DOMAIN):
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=conf
)
def _reauth_flow_wrapper(hass, data):
"""Reauth flow wrapper."""
hass.add_job(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=data
)
)
persistent_notification.async_create(
hass,
"Blink configuration migrated to a new version. Please go to the integrations page to re-configure (such as sending a new 2FA key).",
"Blink Migration",
)
async def async_setup(hass, config):
"""Set up a Blink component."""
hass.data[DOMAIN] = {}
return True
async def async_migrate_entry(hass, entry):
"""Handle migration of a previous version config entry."""
data = {**entry.data}
if entry.version == 1:
data.pop("login_response", None)
await hass.async_add_executor_job(_reauth_flow_wrapper, hass, data)
return False
return True
@@ -95,12 +80,11 @@ async def async_setup_entry(hass, entry):
_async_import_options_from_data_if_missing(hass, entry)
hass.data[DOMAIN][entry.entry_id] = await hass.async_add_executor_job(
_blink_startup_wrapper, entry
_blink_startup_wrapper, hass, entry
)
if not hass.data[DOMAIN][entry.entry_id].available:
_LOGGER.error("Blink unavailable for setup")
return False
raise ConfigEntryNotReady
for component in PLATFORMS:
hass.async_create_task(
@@ -118,7 +102,7 @@ async def async_setup_entry(hass, entry):
def send_pin(call):
"""Call blink to send new pin."""
pin = call.data[CONF_PIN]
hass.data[DOMAIN][entry.entry_id].login_handler.send_auth_key(
hass.data[DOMAIN][entry.entry_id].auth.send_auth_key(
hass.data[DOMAIN][entry.entry_id], pin,
)

View File

@@ -1,10 +1,16 @@
"""Config flow to configure Blink."""
import logging
from blinkpy.blinkpy import Blink
from blinkpy.auth import Auth, LoginError, TokenRefreshFailed
from blinkpy.blinkpy import Blink, BlinkSetupError
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.components.blink.const import (
DEFAULT_SCAN_INTERVAL,
DEVICE_ID,
DOMAIN,
)
from homeassistant.const import (
CONF_PASSWORD,
CONF_PIN,
@@ -13,36 +19,36 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from .const import DEFAULT_OFFSET, DEFAULT_SCAN_INTERVAL, DEVICE_ID, DOMAIN
_LOGGER = logging.getLogger(__name__)
async def validate_input(hass: core.HomeAssistant, blink):
def validate_input(hass: core.HomeAssistant, auth):
"""Validate the user input allows us to connect."""
response = await hass.async_add_executor_job(blink.get_auth_token)
if not response:
try:
auth.startup()
except (LoginError, TokenRefreshFailed):
raise InvalidAuth
if blink.key_required:
if auth.check_key_required():
raise Require2FA
return blink.login_response
def _send_blink_2fa_pin(auth, pin):
"""Send 2FA pin to blink servers."""
blink = Blink()
blink.auth = auth
blink.setup_urls()
return auth.send_auth_key(blink, pin)
class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a Blink config flow."""
VERSION = 1
VERSION = 2
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
def __init__(self):
"""Initialize the blink flow."""
self.blink = None
self.data = {
CONF_USERNAME: "",
CONF_PASSWORD: "",
"login_response": None,
}
self.auth = None
@staticmethod
@callback
@@ -53,28 +59,19 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None):
"""Handle a flow initiated by the user."""
errors = {}
data = {CONF_USERNAME: "", CONF_PASSWORD: "", "device_id": DEVICE_ID}
if user_input is not None:
self.data[CONF_USERNAME] = user_input["username"]
self.data[CONF_PASSWORD] = user_input["password"]
data[CONF_USERNAME] = user_input["username"]
data[CONF_PASSWORD] = user_input["password"]
await self.async_set_unique_id(self.data[CONF_USERNAME])
if CONF_SCAN_INTERVAL in user_input:
self.data[CONF_SCAN_INTERVAL] = user_input[CONF_SCAN_INTERVAL]
self.blink = Blink(
username=self.data[CONF_USERNAME],
password=self.data[CONF_PASSWORD],
motion_interval=DEFAULT_OFFSET,
legacy_subdomain=False,
no_prompt=True,
device_id=DEVICE_ID,
)
self.auth = Auth(data, no_prompt=True)
await self.async_set_unique_id(data[CONF_USERNAME])
try:
response = await validate_input(self.hass, self.blink)
self.data["login_response"] = response
return self.async_create_entry(title=DOMAIN, data=self.data,)
await self.hass.async_add_executor_job(
validate_input, self.hass, self.auth
)
return self._async_finish_flow()
except Require2FA:
return await self.async_step_2fa()
except InvalidAuth:
@@ -94,23 +91,40 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_2fa(self, user_input=None):
"""Handle 2FA step."""
errors = {}
if user_input is not None:
pin = user_input.get(CONF_PIN)
if await self.hass.async_add_executor_job(
self.blink.login_handler.send_auth_key, self.blink, pin
):
return await self.async_step_user(user_input=self.data)
try:
valid_token = await self.hass.async_add_executor_job(
_send_blink_2fa_pin, self.auth, pin
)
except BlinkSetupError:
errors["base"] = "cannot_connect"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
if valid_token:
return self._async_finish_flow()
errors["base"] = "invalid_access_token"
return self.async_show_form(
step_id="2fa",
data_schema=vol.Schema(
{vol.Optional("pin"): vol.All(str, vol.Length(min=1))}
),
errors=errors,
)
async def async_step_import(self, import_data):
"""Import blink config from configuration.yaml."""
return await self.async_step_user(import_data)
async def async_step_reauth(self, entry_data):
"""Perform reauth upon migration of old entries."""
return await self.async_step_user(entry_data)
@callback
def _async_finish_flow(self):
"""Finish with setup."""
return self.async_create_entry(title=DOMAIN, data=self.auth.login_attributes)
class BlinkOptionsFlowHandler(config_entries.OptionsFlow):

View File

@@ -2,6 +2,7 @@
DOMAIN = "blink"
DEVICE_ID = "Home Assistant"
CONF_MIGRATE = "migrate"
CONF_CAMERA = "camera"
CONF_ALARM_CONTROL_PANEL = "alarm_control_panel"

View File

@@ -2,7 +2,7 @@
"domain": "blink",
"name": "Blink",
"documentation": "https://www.home-assistant.io/integrations/blink",
"requirements": ["blinkpy==0.15.1"],
"requirements": ["blinkpy==0.16.3"],
"codeowners": ["@fronzbot"],
"config_flow": true
}

View File

@@ -11,11 +11,13 @@
"2fa": {
"title": "Two-factor authentication",
"data": { "2fa": "Two-factor code" },
"description": "Enter the pin sent to your email. If the email does not contain a pin, leave blank"
"description": "Enter the pin sent to your email"
}
},
"error": {
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_access_token": "[%key:common::config_flow::error::invalid_access_token%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {

View File

@@ -1,17 +1,24 @@
"""The Bond integration."""
import asyncio
from asyncio import TimeoutError as AsyncIOTimeoutError
import logging
from bond import Bond
from aiohttp import ClientError, ClientTimeout
from bond_api import Bond
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import SLOW_UPDATE_WARNING
from .const import DOMAIN
from .utils import BondHub
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["cover", "fan", "light", "switch"]
_API_TIMEOUT = SLOW_UPDATE_WARNING - 1
async def async_setup(hass: HomeAssistant, config: dict):
@@ -25,11 +32,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
host = entry.data[CONF_HOST]
token = entry.data[CONF_ACCESS_TOKEN]
bond = Bond(bondIp=host, bondToken=token)
bond = Bond(host=host, token=token, timeout=ClientTimeout(total=_API_TIMEOUT))
hub = BondHub(bond)
await hass.async_add_executor_job(hub.setup)
try:
await hub.setup()
except (ClientError, AsyncIOTimeoutError, OSError) as error:
raise ConfigEntryNotReady from error
hass.data[DOMAIN][entry.entry_id] = hub
if not entry.unique_id:
hass.config_entries.async_update_entry(entry, unique_id=hub.bond_id)
device_registry = await dr.async_get_registry(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,

View File

@@ -1,42 +1,45 @@
"""Config flow for Bond integration."""
from json import JSONDecodeError
import logging
from typing import Any, Dict, Optional
from bond import Bond
from requests.exceptions import ConnectionError as RequestConnectionError
from aiohttp import ClientConnectionError, ClientResponseError
from bond_api import Bond
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
from homeassistant import config_entries, exceptions
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME
from .const import CONF_BOND_ID
from .const import DOMAIN # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema(
DATA_SCHEMA_USER = vol.Schema(
{vol.Required(CONF_HOST): str, vol.Required(CONF_ACCESS_TOKEN): str}
)
DATA_SCHEMA_DISCOVERY = vol.Schema({vol.Required(CONF_ACCESS_TOKEN): str})
async def validate_input(hass: core.HomeAssistant, data):
async def _validate_input(data: Dict[str, Any]) -> str:
"""Validate the user input allows us to connect."""
def authenticate(bond_hub: Bond) -> bool:
try:
bond_hub.getDeviceIds()
return True
except RequestConnectionError:
raise CannotConnect
except JSONDecodeError:
return False
try:
bond = Bond(data[CONF_HOST], data[CONF_ACCESS_TOKEN])
version = await bond.version()
# call to non-version API is needed to validate authentication
await bond.devices()
except ClientConnectionError:
raise InputValidationError("cannot_connect")
except ClientResponseError as error:
if error.status == 401:
raise InputValidationError("invalid_auth")
raise InputValidationError("unknown")
except Exception:
_LOGGER.exception("Unexpected exception")
raise InputValidationError("unknown")
bond = Bond(data[CONF_HOST], data[CONF_ACCESS_TOKEN])
if not await hass.async_add_executor_job(authenticate, bond):
raise InvalidAuth
# Return info that you want to store in the config entry.
return {"title": data[CONF_HOST]}
# Return unique ID from the hub to be stored in the config entry.
return version["bondid"]
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@@ -45,30 +48,73 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
_discovered: dict = None
async def async_step_zeroconf(
self, discovery_info: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Handle a flow initialized by zeroconf discovery."""
name: str = discovery_info[CONF_NAME]
host: str = discovery_info[CONF_HOST]
bond_id = name.partition(".")[0]
await self.async_set_unique_id(bond_id)
self._abort_if_unique_id_configured({CONF_HOST: host})
self._discovered = {
CONF_HOST: host,
CONF_BOND_ID: bond_id,
}
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context.update({"title_placeholders": self._discovered})
return await self.async_step_confirm()
async def async_step_confirm(
self, user_input: Dict[str, Any] = None
) -> Dict[str, Any]:
"""Handle confirmation flow for discovered bond hub."""
errors = {}
if user_input is not None:
data = user_input.copy()
data[CONF_HOST] = self._discovered[CONF_HOST]
try:
return await self._try_create_entry(data)
except InputValidationError as error:
errors["base"] = error.base
return self.async_show_form(
step_id="confirm",
data_schema=DATA_SCHEMA_DISCOVERY,
errors=errors,
description_placeholders=self._discovered,
)
async def async_step_user(
self, user_input: Dict[str, Any] = None
) -> Dict[str, Any]:
"""Handle a flow initialized by the user."""
errors = {}
if user_input is not None:
try:
info = await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
return self.async_create_entry(title=info["title"], data=user_input)
return await self._try_create_entry(user_input)
except InputValidationError as error:
errors["base"] = error.base
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
step_id="user", data_schema=DATA_SCHEMA_USER, errors=errors
)
class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""
async def _try_create_entry(self, data: Dict[str, Any]) -> Dict[str, Any]:
bond_id = await _validate_input(data)
await self.async_set_unique_id(bond_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=bond_id, data=data)
class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth."""
class InputValidationError(exceptions.HomeAssistantError):
"""Error to indicate we cannot proceed due to invalid input."""
def __init__(self, base: str):
"""Initialize with error base."""
super().__init__()
self.base = base

View File

@@ -1,3 +1,5 @@
"""Constants for the Bond integration."""
DOMAIN = "bond"
CONF_BOND_ID: str = "bond_id"

View File

@@ -1,7 +1,7 @@
"""Support for Bond covers."""
from typing import Any, Callable, List, Optional
from bond import DeviceTypes
from bond_api import Action, DeviceType
from homeassistant.components.cover import DEVICE_CLASS_SHADE, CoverEntity
from homeassistant.config_entries import ConfigEntry
@@ -21,12 +21,10 @@ async def async_setup_entry(
"""Set up Bond cover devices."""
hub: BondHub = hass.data[DOMAIN][entry.entry_id]
devices = await hass.async_add_executor_job(hub.get_bond_devices)
covers = [
BondCover(hub, device)
for device in devices
if device.type == DeviceTypes.MOTORIZED_SHADES
for device in hub.devices
if device.type == DeviceType.MOTORIZED_SHADES
]
async_add_entities(covers, True)
@@ -41,30 +39,28 @@ class BondCover(BondEntity, CoverEntity):
self._closed: Optional[bool] = None
def _apply_state(self, state: dict):
cover_open = state.get("open")
self._closed = True if cover_open == 0 else False if cover_open == 1 else None
@property
def device_class(self) -> Optional[str]:
"""Get device class."""
return DEVICE_CLASS_SHADE
def update(self):
"""Fetch assumed state of the cover from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
cover_open = state.get("open")
self._closed = True if cover_open == 0 else False if cover_open == 1 else None
@property
def is_closed(self):
"""Return if the cover is closed or not."""
return self._closed
def open_cover(self, **kwargs: Any) -> None:
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
self._hub.bond.open(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.open())
def close_cover(self, **kwargs: Any) -> None:
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close cover."""
self._hub.bond.close(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.close())
def stop_cover(self, **kwargs):
async def async_stop_cover(self, **kwargs):
"""Hold cover."""
self._hub.bond.hold(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.hold())

View File

@@ -1,24 +1,35 @@
"""An abstract class common to all Bond entities."""
from abc import abstractmethod
from asyncio import TimeoutError as AsyncIOTimeoutError
import logging
from typing import Any, Dict, Optional
from aiohttp import ClientError
from homeassistant.const import ATTR_NAME
from homeassistant.helpers.entity import Entity
from .const import DOMAIN
from .utils import BondDevice, BondHub
_LOGGER = logging.getLogger(__name__)
class BondEntity:
class BondEntity(Entity):
"""Generic Bond entity encapsulating common features of any Bond controlled device."""
def __init__(self, hub: BondHub, device: BondDevice):
"""Initialize entity with API and device info."""
self._hub = hub
self._device = device
self._available = True
@property
def unique_id(self) -> Optional[str]:
"""Get unique ID for the entity."""
return self._device.device_id
hub_id = self._hub.bond_id
device_id = self._device.device_id
return f"{hub_id}_{device_id}"
@property
def name(self) -> Optional[str]:
@@ -37,4 +48,30 @@ class BondEntity:
@property
def assumed_state(self) -> bool:
"""Let HA know this entity relies on an assumed state tracked by Bond."""
return True
return self._hub.is_bridge and not self._device.trust_state
@property
def available(self) -> bool:
"""Report availability of this entity based on last API call results."""
return self._available
async def async_update(self):
"""Fetch assumed state of the cover from the hub using API."""
try:
state: dict = await self._hub.bond.device_state(self._device.device_id)
except (ClientError, AsyncIOTimeoutError, OSError) as error:
if self._available:
_LOGGER.warning(
"Entity %s has become unavailable", self.entity_id, exc_info=error
)
self._available = False
else:
_LOGGER.debug("Device state for %s is:\n%s", self.entity_id, state)
if not self._available:
_LOGGER.info("Entity %s has come back", self.entity_id)
self._available = True
self._apply_state(state)
@abstractmethod
def _apply_state(self, state: dict):
raise NotImplementedError

View File

@@ -2,7 +2,7 @@
import math
from typing import Any, Callable, List, Optional
from bond import DeviceTypes, Directions
from bond_api import Action, DeviceType, Direction
from homeassistant.components.fan import (
DIRECTION_FORWARD,
@@ -32,12 +32,8 @@ async def async_setup_entry(
"""Set up Bond fan devices."""
hub: BondHub = hass.data[DOMAIN][entry.entry_id]
devices = await hass.async_add_executor_job(hub.get_bond_devices)
fans = [
BondFan(hub, device)
for device in devices
if device.type == DeviceTypes.CEILING_FAN
BondFan(hub, device) for device in hub.devices if DeviceType.is_fan(device.type)
]
async_add_entities(fans, True)
@@ -54,6 +50,11 @@ class BondFan(BondEntity, FanEntity):
self._speed: Optional[int] = None
self._direction: Optional[int] = None
def _apply_state(self, state: dict):
self._power = state.get("power")
self._speed = state.get("speed")
self._direction = state.get("direction")
@property
def supported_features(self) -> int:
"""Flag supported features."""
@@ -74,7 +75,7 @@ class BondFan(BondEntity, FanEntity):
return None
# map 1..max_speed Bond speed to 1..3 HA speed
max_speed = self._device.props.get("max_speed", 3)
max_speed = max(self._device.props.get("max_speed", 3), self._speed)
ha_speed = math.ceil(self._speed * (len(self.speed_list) - 1) / max_speed)
return self.speed_list[ha_speed]
@@ -87,21 +88,14 @@ class BondFan(BondEntity, FanEntity):
def current_direction(self) -> Optional[str]:
"""Return fan rotation direction."""
direction = None
if self._direction == Directions.FORWARD:
if self._direction == Direction.FORWARD:
direction = DIRECTION_FORWARD
elif self._direction == Directions.REVERSE:
elif self._direction == Direction.REVERSE:
direction = DIRECTION_REVERSE
return direction
def update(self):
"""Fetch assumed state of the fan from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
self._power = state.get("power")
self._speed = state.get("speed")
self._direction = state.get("direction")
def set_speed(self, speed: str) -> None:
async def async_set_speed(self, speed: str) -> None:
"""Set the desired speed for the fan."""
max_speed = self._device.props.get("max_speed", 3)
if speed == SPEED_LOW:
@@ -110,21 +104,27 @@ class BondFan(BondEntity, FanEntity):
bond_speed = max_speed
else:
bond_speed = math.ceil(max_speed / 2)
self._hub.bond.setSpeed(self._device.device_id, speed=bond_speed)
def turn_on(self, speed: Optional[str] = None, **kwargs) -> None:
await self._hub.bond.action(
self._device.device_id, Action.set_speed(bond_speed)
)
async def async_turn_on(self, speed: Optional[str] = None, **kwargs) -> None:
"""Turn on the fan."""
if speed is not None:
self.set_speed(speed)
self._hub.bond.turnOn(self._device.device_id)
await self.async_set_speed(speed)
else:
await self._hub.bond.action(self._device.device_id, Action.turn_on())
def turn_off(self, **kwargs: Any) -> None:
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the fan off."""
self._hub.bond.turnOff(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.turn_off())
def set_direction(self, direction: str) -> None:
async def async_set_direction(self, direction: str):
"""Set fan rotation direction."""
bond_direction = (
Directions.REVERSE if direction == DIRECTION_REVERSE else Directions.FORWARD
Direction.REVERSE if direction == DIRECTION_REVERSE else Direction.FORWARD
)
await self._hub.bond.action(
self._device.device_id, Action.set_direction(bond_direction)
)
self._hub.bond.setDirection(self._device.device_id, bond_direction)

View File

@@ -1,7 +1,7 @@
"""Support for Bond lights."""
from typing import Any, Callable, List, Optional
from bond import DeviceTypes
from bond_api import Action, DeviceType
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
@@ -26,21 +26,19 @@ async def async_setup_entry(
"""Set up Bond light devices."""
hub: BondHub = hass.data[DOMAIN][entry.entry_id]
devices = await hass.async_add_executor_job(hub.get_bond_devices)
lights = [
lights: List[Entity] = [
BondLight(hub, device)
for device in devices
if device.type == DeviceTypes.CEILING_FAN and device.supports_light()
for device in hub.devices
if DeviceType.is_fan(device.type) and device.supports_light()
]
async_add_entities(lights, True)
fireplaces = [
fireplaces: List[Entity] = [
BondFireplace(hub, device)
for device in devices
if device.type == DeviceTypes.FIREPLACE
for device in hub.devices
if DeviceType.is_fireplace(device.type)
]
async_add_entities(fireplaces, True)
async_add_entities(lights + fireplaces, True)
class BondLight(BondEntity, LightEntity):
@@ -49,26 +47,49 @@ class BondLight(BondEntity, LightEntity):
def __init__(self, hub: BondHub, device: BondDevice):
"""Create HA entity representing Bond fan."""
super().__init__(hub, device)
self._brightness: Optional[int] = None
self._light: Optional[int] = None
def _apply_state(self, state: dict):
self._light = state.get("light")
self._brightness = state.get("brightness")
@property
def supported_features(self) -> Optional[int]:
"""Flag supported features."""
features = 0
if self._device.supports_set_brightness():
features |= SUPPORT_BRIGHTNESS
return features
@property
def is_on(self) -> bool:
"""Return if light is currently on."""
return self._light == 1
def update(self):
"""Fetch assumed state of the light from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
self._light = state.get("light")
@property
def brightness(self) -> int:
"""Return the brightness of this light between 1..255."""
brightness_value = (
round(self._brightness * 255 / 100) if self._brightness else None
)
return brightness_value
def turn_on(self, **kwargs: Any) -> None:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
self._hub.bond.turnLightOn(self._device.device_id)
brightness = kwargs.get(ATTR_BRIGHTNESS)
if brightness:
await self._hub.bond.action(
self._device.device_id,
Action.set_brightness(round((brightness * 100) / 255)),
)
else:
await self._hub.bond.action(self._device.device_id, Action.turn_light_on())
def turn_off(self, **kwargs: Any) -> None:
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
self._hub.bond.turnLightOff(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.turn_light_off())
class BondFireplace(BondEntity, LightEntity):
@@ -82,6 +103,10 @@ class BondFireplace(BondEntity, LightEntity):
# Bond flame level, 0-100
self._flame: Optional[int] = None
def _apply_state(self, state: dict):
self._power = state.get("power")
self._flame = state.get("flame")
@property
def supported_features(self) -> Optional[int]:
"""Flag brightness as supported feature to represent flame level."""
@@ -92,18 +117,18 @@ class BondFireplace(BondEntity, LightEntity):
"""Return True if power is on."""
return self._power == 1
def turn_on(self, **kwargs: Any) -> None:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the fireplace on."""
self._hub.bond.turnOn(self._device.device_id)
brightness = kwargs.get(ATTR_BRIGHTNESS)
if brightness:
flame = round((brightness * 100) / 255)
self._hub.bond.setFlame(self._device.device_id, flame)
await self._hub.bond.action(self._device.device_id, Action.set_flame(flame))
else:
await self._hub.bond.action(self._device.device_id, Action.turn_on())
def turn_off(self, **kwargs: Any) -> None:
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the fireplace off."""
self._hub.bond.turnOff(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.turn_off())
@property
def brightness(self):
@@ -114,9 +139,3 @@ class BondFireplace(BondEntity, LightEntity):
def icon(self) -> Optional[str]:
"""Show fireplace icon for the entity."""
return "mdi:fireplace" if self._power == 1 else "mdi:fireplace-off"
def update(self):
"""Fetch assumed state of the device from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
self._power = state.get("power")
self._flame = state.get("flame")

View File

@@ -3,10 +3,8 @@
"name": "Bond",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bond",
"requirements": [
"bond-home==0.0.9"
],
"codeowners": [
"@prystupa"
]
"requirements": ["bond-api==0.1.8"],
"zeroconf": ["_bond._tcp.local."],
"codeowners": ["@prystupa"],
"quality_scale": "platinum"
}

View File

@@ -1,6 +1,13 @@
{
"config": {
"flow_title": "Bond: {bond_id} ({host})",
"step": {
"confirm": {
"description": "Do you want to set up {bond_id}?",
"data": {
"access_token": "[%key:common::config_flow::data::access_token%]"
}
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
@@ -12,6 +19,9 @@
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
}

View File

@@ -1,13 +1,13 @@
"""Support for Bond generic devices."""
from typing import Any, Callable, List, Optional
from bond import DeviceTypes
from bond_api import Action, DeviceType
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from ..switch import SwitchEntity
from .const import DOMAIN
from .entity import BondEntity
from .utils import BondDevice, BondHub
@@ -21,12 +21,10 @@ async def async_setup_entry(
"""Set up Bond generic devices."""
hub: BondHub = hass.data[DOMAIN][entry.entry_id]
devices = await hass.async_add_executor_job(hub.get_bond_devices)
switches = [
BondSwitch(hub, device)
for device in devices
if device.type == DeviceTypes.GENERIC_DEVICE
for device in hub.devices
if DeviceType.is_generic(device.type)
]
async_add_entities(switches, True)
@@ -41,20 +39,18 @@ class BondSwitch(BondEntity, SwitchEntity):
self._power: Optional[bool] = None
def _apply_state(self, state: dict):
self._power = state.get("power")
@property
def is_on(self) -> bool:
"""Return True if power is on."""
return self._power == 1
def turn_on(self, **kwargs: Any) -> None:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
self._hub.bond.turnOn(self._device.device_id)
await self._hub.bond.action(self._device.device_id, Action.turn_on())
def turn_off(self, **kwargs: Any) -> None:
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
self._hub.bond.turnOff(self._device.device_id)
def update(self):
"""Fetch assumed state of the device from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
self._power = state.get("power")
await self._hub.bond.action(self._device.device_id, Action.turn_off())

View File

@@ -1,5 +1,8 @@
{
"config": {
"abort": {
"already_configured": "El dispositiu ja est\u00e0 configurat"
},
"error": {
"cannot_connect": "Ha fallat la connexi\u00f3",
"invalid_auth": "Autenticaci\u00f3 inv\u00e0lida",

View File

@@ -0,0 +1,16 @@
{
"config": {
"abort": {
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nakofigurovan\u00e9"
},
"flow_title": "Bond: {bond_id} ({host})",
"step": {
"confirm": {
"data": {
"access_token": "P\u0159\u00edstupov\u00fd token"
},
"description": "Chcete nastavit {bond_id} ?"
}
}
}
}

View File

@@ -1,11 +1,21 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured"
},
"error": {
"cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication",
"unknown": "Unexpected error"
},
"flow_title": "Bond: {bond_id} ({host})",
"step": {
"confirm": {
"data": {
"access_token": "Access Token"
},
"description": "Do you want to set up {bond_id}?"
},
"user": {
"data": {
"access_token": "Access Token",

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