Compare commits

...

123 Commits

Author SHA1 Message Date
Paulus Schoutsen
07ce284acd Merge pull request #33269 from home-assistant/rc
0.107.7
2020-03-25 17:53:05 -07:00
Paulus Schoutsen
eac03ef4be Bumped version to 0.107.7 2020-03-25 16:04:19 -07:00
jjlawren
a38e075bda Schedule Unifi shutdown callback earlier (#33257) 2020-03-25 16:04:06 -07:00
J. Nick Koston
cced74740f Ensure recorder event loop recovers if the database server dis… (#33253)
If the database server disconnects there were exceptions
that were not trapped which would cause the recorder event
loop to collapse.  As we never want the loop to end
we trap exceptions broadly.

Fix a bug in the new commit interval setting which caused
it to always commit after 1s
2020-03-25 16:04:05 -07:00
Alexei Chetroi
1236c2b91e Refactor ZHA platform setup (#33226)
Setup ZHA platforms only after successful gateway startup.
2020-03-25 16:04:05 -07:00
Diogo Gomes
d54d7c6958 Increase timeout setting up IPMA (#33194) 2020-03-25 16:04:04 -07:00
brefra
9365ba3fcf Fix velbus dimming control (#33139)
* Fix light control

* Optimize conversion logic
2020-03-25 16:04:03 -07:00
Pascal Vizeli
baf7fb7264 Merge pull request #33206 from home-assistant/rc
0.107.6
2020-03-24 14:08:27 +01:00
Pascal Vizeli
edfe8e1583 Bump version to 0.107.6 2020-03-24 11:50:25 +00:00
Pascal Vizeli
b36b1dbc70 Bump OZW fork to 0.1.10 (#33205) 2020-03-24 11:49:35 +00:00
Franck Nijhof
253c848692 Fix minut point updating frozen config entry data (#33148)
* Fix minut point updating frozen config entry data

* Update webhooks handling for configuration entry
2020-03-24 11:49:33 +00:00
Paulus Schoutsen
7a6ac578b4 Fix script logging with name (#33120) 2020-03-24 11:49:32 +00:00
Pascal Vizeli
95de94e53f Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 16:48:44 +00:00
Pascal Vizeli
181b2803cd Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 15:56:50 +00:00
Pascal Vizeli
e0f2fa33df [skip ci] Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 13:19:14 +00:00
Pascal Vizeli
884c346bdf Fix dockerfile 2020-03-23 13:19:02 +00:00
Pascal Vizeli
c218ff5a75 Integrate dockerbuild (#33168)
* Integrate dockerbuild

* cleanup
2020-03-23 13:18:48 +00:00
Pascal Vizeli
fa43a218d2 Update azure-pipelines-wheels.yml for Azure Pipelines 2020-03-23 13:18:33 +00:00
Pascal Vizeli
f4cc64d289 [skip ci] add rc into build 2020-03-23 13:18:18 +00:00
Pascal Vizeli
4c31829832 [skip ci] update wheels builder version 2020-03-23 13:18:00 +00:00
Pascal Vizeli
ff32c1c3e9 Update azure-pipelines-wheels.yml 2020-03-23 13:17:49 +00:00
Paulus Schoutsen
677c276b41 Merge pull request #33118 from home-assistant/rc
0.107.5
2020-03-21 17:22:31 -07:00
Paulus Schoutsen
ca1c696f54 Bumped version to 0.107.5 2020-03-21 16:34:11 -07:00
Franck Nijhof
78e5878247 Fix Extend ONVIF unique ID with profile index (#33103) 2020-03-21 16:34:05 -07:00
guillempages
470537bc5f Fix tankerkoenig with more than 10 stations (#33098)
* Fix tankerkoenig with more than 10 stations

There seemed to be a problem, if more than 10 fuel stations were tracked.
Added a warning in this case, and split the calls to the API in chunks of
10, so that the data can be fetched anyway.

* Update homeassistant/components/tankerkoenig/__init__.py

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-03-21 16:33:55 -07:00
Austin Mroczek
64556f6f69 Fix totalconnect AttributeError introduced in 0.107 (#33079)
* Fit git

* Remove period from loggging message

* Remove tests for now

* Add const.py

* Fix lint error
2020-03-21 16:32:26 -07:00
Alexei Chetroi
2785b067e3 Split ZHA device loading and entities adding (#33075)
* Split ZHA device loading and entities adding.
Load zha devices from ZHA gateway, but add entities after integration
was setup.

* Use hass.loop.create_task()
* Split ZHA device initialization from loading.
Restore and initialize ZHA devices separately.

* Use hass.async_create_task()
Load devices prior group initialization.
2020-03-21 16:32:26 -07:00
escoand
a129bc05ae Try all Samsung TV websocket ports (#33001)
* Update bridge.py

* add test

* silence pylint

* correct pylint

* add some tests

* Update test_media_player.py
2020-03-21 16:32:25 -07:00
Ville Skyttä
197736f66b Upgrade huawei-lte-api to 1.4.11 (#32791)
https://github.com/Salamek/huawei-lte-api/releases/tag/1.4.11
2020-03-21 15:16:33 -07:00
Franck Nijhof
d82d7fa2e9 Merge pull request #33080 from home-assistant/rc
0.107.4
2020-03-21 07:51:18 +01:00
Paulus Schoutsen
663db747e9 Bumped version to 0.107.4 2020-03-20 23:02:50 -07:00
Paulus Schoutsen
57998f6f0f Fix package default extraction (#33071) 2020-03-20 23:02:29 -07:00
Knapoc
edbb995fff Bump aioasuswrt to 1.2.3 and fix asuswrt sensor (#33064)
* Bump aioasuswrt to 1.2.3

* Fix asuswrt connection setup parameters

* fix typo
2020-03-20 23:02:29 -07:00
Aaron Bach
312903025d Bump simplisafe-python to 9.0.4 (#33059) 2020-03-20 23:02:28 -07:00
Paulus Schoutsen
0ae5c325fe Add negative tests for identify schema for packages (#33050) 2020-03-20 23:02:27 -07:00
Pascal Vizeli
a309a00929 Merge pull request #33054 from home-assistant/rc
0.107.3
2020-03-20 18:33:47 +01:00
Pascal Vizeli
55be5bf880 Bump version to 0.107.3 2020-03-20 16:29:10 +00:00
Franck Nijhof
7b37dcd8ed Fix packages for schemas without a default (#33045) 2020-03-20 16:27:29 +00:00
cgtobi
e36bdd717a Fix discovery issue with netatmo climate devices (#33040) 2020-03-20 16:27:28 +00:00
cgtobi
fa650b648c Fix netatmo webhook registration issue (#32994)
* Wait for cloud connection

* Just wait

* Remove redundant entry

* Drop webhook before unloading other platforms

* Add missing scope

* Update homeassistant/components/netatmo/__init__.py

Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>

* Fix test

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-03-20 16:27:27 +00:00
Paulus Schoutsen
ac2310e7f9 Merge pull request #33030 from home-assistant/rc
0.107.2
2020-03-19 22:51:13 -07:00
Aaron Bach
aee5c16803 Fix RainMachine not properly storing data in the config entry (#33002)
* Fix bug related to RainMachine's default config flow

* A

* Fix tests

* Code review
2020-03-19 20:54:57 -07:00
Paulus Schoutsen
5f0816ea25 Fix zones in packages (#33027) 2020-03-19 20:52:08 -07:00
Paulus Schoutsen
6a6037790f Bumped version to 0.107.2 2020-03-19 20:50:46 -07:00
Alexei Chetroi
d2b0c35319 Handle zigpy clusters without ep_attribute attribute. (#33028) 2020-03-19 20:50:34 -07:00
Jc2k
d707a1b072 0.107.2 - Bump aiohomekit to fix Insignia NS-CH1XGO8 and Lenno… (#33016) 2020-03-19 20:46:45 -07:00
Aaron Bach
ca12db9271 Bump simplisafe-python to 9.0.3 (#33013) 2020-03-19 20:45:57 -07:00
Robin
346a4b399d Fix sighthound dependency issue (#33010) 2020-03-19 20:45:56 -07:00
Robert Svensson
2090252936 Axis - Fix char in stream url (#33004)
* An unwanted character had found its way into a stream string, reverting f-string work to remove duplication of code and improve readability

* Fix failing tests
2020-03-19 20:45:56 -07:00
tetienne
a28091e94a Fix somfy optimistic mode when missing in conf (#32995)
* Fix optimistic mode when missing in conf #32971

* Ease code using a default value

* Client id and secret are now inclusive
2020-03-19 20:45:55 -07:00
Alexei Chetroi
ae8cb0ccdf Refactor ZHA setup (#32959)
* Refactor ZHA setup.
Catch errors and raise if needed.

* Cleanup.
2020-03-19 20:45:54 -07:00
Maikel Punie
06a608e342 Fix velbus in the 107 release (#32936)
velbus 2.0.41 introduced some data files in the python module. They are not copied when installing the velbus module. 2.0.43 fixes this problem
2020-03-19 20:45:53 -07:00
ochlocracy
9af95e8577 Fix camera.options to camera.stream_options. (#32767) 2020-03-19 20:45:53 -07:00
Paulus Schoutsen
29a9781bf7 Fix unifi tests 2020-03-19 15:28:38 -07:00
Jc2k
877eddf43d 0.107.2 - Bump aiohomekit to fix Insignia NS-CH1XGO8 and Lenno… (#33016) 2020-03-19 15:26:01 -07:00
Paulus Schoutsen
88e3e73bb4 Merge pull request #32964 from home-assistant/rc
0.107.1
2020-03-18 22:14:25 -07:00
Paulus Schoutsen
3aa1bcbb77 Fix mobile app test 2020-03-18 22:13:49 -07:00
Paulus Schoutsen
f973b35cef Bumped version to 0.107.1 2020-03-18 18:19:33 -07:00
Paulus Schoutsen
4e08aa8b05 Fix zone config (#32963)
* Fix zone config

* Add zone as dependency again to device tracker

* Fix tests
2020-03-18 18:18:57 -07:00
Paulus Schoutsen
8e917ccf73 Add device automation as frontend dependency (#32962) 2020-03-18 18:17:11 -07:00
Bram Kragten
0b62011626 Updated frontend to 20200318.1 (#32957) 2020-03-18 18:17:11 -07:00
Franck Nijhof
d520a02b8c Merge pull request #32932 from home-assistant/rc
0.107.0
2020-03-18 15:35:44 +01:00
SukramJ
1e469b39ad Fix flaky tests for HMIPC (#32806) 2020-03-18 14:44:29 +01:00
Franck Nijhof
c2f615839d Bumped version to 0.107.0 2020-03-18 13:31:02 +01:00
Bram Kragten
657bf33e32 Updated frontend to 20200318.0 (#32931) 2020-03-18 13:27:21 +01:00
Paulus Schoutsen
0ca87007fd Bumped version to 0.107.0b8 2020-03-17 17:56:18 -07:00
Paulus Schoutsen
d0d9d853f2 Fix hassio panel load (#32922)
* Fix loading hassio panel

* Remove blacklist
2020-03-17 17:55:57 -07:00
Hans Oischinger
8348878e7e Introduce safe scan_interval for vicare (#32915) 2020-03-17 17:55:56 -07:00
Paulus Schoutsen
b70be5f2f2 Blacklist auto_backup (#32912)
* Blacklist auto_backup

* Mock with a set
2020-03-17 17:55:55 -07:00
Bram Kragten
fddb565e4c Fix input text reload (#32911)
* Fix input text reload

* FIx schema instead
2020-03-17 17:55:54 -07:00
Rami Mosleh
f3e6820042 Fix setting up options due to config data freeze (#32872) 2020-03-17 17:55:54 -07:00
Paulus Schoutsen
ae98f13181 Bumped version to 0.107.0b7 2020-03-17 10:36:59 -07:00
Paulus Schoutsen
ab38e7d98a Bump cast to 4.2.0 (#32906) 2020-03-17 10:35:39 -07:00
brubaked
9797b09d44 Changed Sensor icons to be more emotionally sensitive (#32904)
The existing sensor icons, while descriptive - dead = dead - are perhaps too matter of fact and don't accurately convey the tragedy. I changed emoticon-dead-outline to emoticon-cry-outline, as I think it better conveys the reality of the situation along with the emotions tied to the statistic.
2020-03-17 10:35:39 -07:00
Quentame
4908d4358c Bump iCloud to 0.9.5 (#32901) 2020-03-17 10:35:38 -07:00
Paulus Schoutsen
67d728fc50 Make zone dependency of device tracker an after dep (#32880)
* Make zone dependency of device tracker an after dep

* Fix test
2020-03-17 10:34:34 -07:00
Jason Lachowsky
912409ed0c Corrected minor misspellings (#32857) 2020-03-17 10:32:01 -07:00
Paolo Tuninetto
ac8c889b0f Add default port to samsung tv (#32820)
* Default port for websocket tv

* Update config entry

* move bridge creation

* fix indent

* remove loop
2020-03-17 10:32:00 -07:00
Quentame
67a721d39b Fix iCloud init while pending (#32750)
* Fix iCloud init while pending

Continue if device is pending while setup
Create devices and fetch 15s if pending, otherwise determine interval to fetch.

* Add retried_fetch guard
2020-03-17 10:32:00 -07:00
Paulus Schoutsen
d196fd136d Bumped version to 0.107.0b6 2020-03-16 14:53:13 -07:00
Bram Kragten
4f78674a4c Updated frontend to 20200316.1 (#32878) 2020-03-16 14:53:05 -07:00
Bram Kragten
a7aca10668 Lovelace: storage key based on id instead of url_path (#32873)
* Fix storage key based on url_path

* Fix test
2020-03-16 14:53:04 -07:00
Paulus Schoutsen
03b1c6ddee Remove group as a dependency from entity integrations (#32870)
* remove group dependency

* Update device sun light trigger

* Add zone dep back to device tracker
2020-03-16 14:53:03 -07:00
David F. Mulcahey
661f1b69f2 Bump ZHA quirks to 0.0.37 (#32867) 2020-03-16 14:53:03 -07:00
Bram Kragten
ccb34083fe Add lovelace reload service for yaml resources (#32865)
* Lovelace add reload service for yaml resources

* Clean up imports

* Comments
2020-03-16 14:53:02 -07:00
Bram Kragten
7f6b3c1130 Bumped version to 0.107.0b5 2020-03-16 13:59:27 +01:00
Bram Kragten
f2c3f76b8e Updated frontend to 20200316.0 (#32866) 2020-03-16 13:49:50 +01:00
Pascal Vizeli
b6a3bcf87f Update pyozw 0.1.9 (#32864) 2020-03-16 13:49:49 +01:00
Tom Harris
65423bb62b Bump insteonplm to 0.16.8 (#32847) 2020-03-16 13:49:48 +01:00
Kit Klein
104665d849 Ignore the ignored konnected config entries (#32845)
* ignore the ignored konnected config entries

* key off data instead of source
2020-03-16 13:49:47 +01:00
Alan Tse
fb1ba86b08 Bump teslajsonpy to 0.5.1 (#32827) 2020-03-16 13:49:47 +01:00
David Bonnes
cee72724b6 Ensure unique_ids for all evohome thermostats (#32604)
* initial commit

* small tweak
2020-03-16 13:49:46 +01:00
Paulus Schoutsen
a3d74651a8 Bumped version to 0.107.0b4 2020-03-15 11:56:56 -07:00
Paulus Schoutsen
d88275d6d2 Make sure panel_custom won't crash on invalid data (#32835)
* Make sure panel_custom won't crash on invalid data

* Add a test
2020-03-15 11:52:54 -07:00
SukramJ
42998f898b Add SF transition to HmIP-BSL and remove obsolete code in HMIPC (#32833) 2020-03-15 11:52:53 -07:00
Daniel Høyer Iversen
875671cc2b Add Netatmo Home Coach as model (#32829) 2020-03-15 11:52:53 -07:00
Bram Kragten
3b84b6e6d5 Require a hyphen in lovelace dashboard url (#32816)
* Require a hyphen in lovelace dashboard url

* Keep storage dashboards working

* register during startup again

* Update homeassistant/components/lovelace/dashboard.py

Co-Authored-By: Paulus Schoutsen <balloob@gmail.com>

* Comments

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-03-15 11:52:52 -07:00
Chris Talkington
3b1fb2f416 Remove extra logging from directv init. (#32809) 2020-03-15 11:52:51 -07:00
Chris Talkington
226a0bcaad Fix directv location of unknown error string (#32807)
* Update strings.json

* Update en.json
2020-03-15 11:52:50 -07:00
Greg
57dd45318d Bump eagle_reader API version to v0.2.4 (#32789) 2020-03-15 11:52:50 -07:00
Franck Nijhof
e666485ea9 Fix brightness_pct in light device turn_on action (#32787) 2020-03-15 11:52:49 -07:00
Aidan Timson
b5c8b5b91f Fix onvif error with non ptz cameras (#32783) 2020-03-15 11:52:48 -07:00
David F. Mulcahey
706607f1d2 Fix handling of attribute reports in ZHA sensors and binary sensors (#32776)
* Update sensor tests.

* Update light tests.

* Update binary_sensor tests.

* Update cover tests.

* Update device tracker tests.

* Update fan tests.

* Update lock tests.

* Update switch tests.

* add sensor attr to sensors

* add sensor attr to binary sensors

* cleanup extra var

Co-authored-by: Alexei Chetroi <alexei.chetroi@outlook.com>
2020-03-15 11:52:47 -07:00
Steven Looman
0788bbd629 Add log message on timeout and update less often for upnp devices (#32740)
* Catch asyncio.TimeoutError, show a proper message instead

* Throttle updates to max once per 30s

* Change code owner

* Fix CODEOWNERS + linting

* Warn on connection timeout
2020-03-15 11:52:46 -07:00
Chris Talkington
1b622925a1 Optimize directv client initialization (#32706)
* Optimize directv client initialization.

* Update config_flow.py

* Update media_player.py

* Update media_player.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update test_media_player.py

* Update __init__.py

* Update media_player.py

* Update test_media_player.py

* Update media_player.py

* Update test_media_player.py

* Update config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update __init__.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_media_player.py

* Update test_media_player.py

* Update __init__.py

* Update __init__.py

* Update __init__.py
2020-03-15 11:52:45 -07:00
Slava
86c4fa0fc5 Add brightness state to emulated hue when devices support only color temp and brightness (#31834) 2020-03-15 11:52:45 -07:00
Jc2k
e365dc398b Fix homekit_controller beta connectivity issues (#32810) 2020-03-14 15:43:09 -04:00
Bram Kragten
dfd29e6d73 Bumped version to 0.107.0b3 2020-03-13 22:23:34 +01:00
Bram Kragten
6780bded7e Updated frontend to 20200313.0 (#32777) 2020-03-13 22:19:22 +01:00
Paulus Schoutsen
56686dd14c Bumped version to 0.107.0b2 2020-03-13 12:16:55 -07:00
Bram Kragten
7268bcd9be Check if panel url used and delay dashboard reg till start (#32771)
* Check if panel url used and delay dashboard reg till start

* move storage_dashboard_changed

* fix tests
2020-03-13 12:16:49 -07:00
J. Nick Koston
4f78e04315 Bump py-august to 0.25.0 (#32769)
Fixes a bug in the conversion to async where code validation failed.
2020-03-13 12:16:49 -07:00
Austin Mroczek
7bdac8ef2e Bump total-connect-client to 0.54.1 #32758) 2020-03-13 12:16:48 -07:00
Paulus Schoutsen
a66f4ca4ec Bumped version to 0.107.0b1 2020-03-12 17:02:38 -07:00
Bram Kragten
3345d85dca Updated frontend to 20200312.0 (#32741) 2020-03-12 17:02:29 -07:00
Raman Gupta
9ad776e55d Set self._current_app to None when vizio device is off (#32725) 2020-03-12 17:02:29 -07:00
escoand
2f2a908573 Fix legacy Samsung TV (#32719)
* Update bridge.py

* Update test_init.py
2020-03-12 17:02:28 -07:00
J. Nick Koston
9f76a8c12d Resolve Home Assistant fails to start when Sense integration i… (#32716)
* Bump sense_energy 0.7.1 which also fixes throwing ConfigEntryNotReady
2020-03-12 17:02:27 -07:00
Raman Gupta
fe6ca522e8 Update Vizio source property to only return current app if i… (#32713)
* only return current app for source if current app is set

* check for None specifically

* make sure current app isn't called for speaker
2020-03-12 17:02:26 -07:00
Paulus Schoutsen
c46d0e4a49 Sonos idle (#32712)
* Sonos idle

* F-string

* Add to properties

* Fixes
2020-03-12 17:02:26 -07:00
Aaron Bach
8db426e5da Broaden exception handling for IQVIA (#32708) 2020-03-12 17:02:25 -07:00
Franck Nijhof
bfacd9a1c3 Remove deprecated hide_if_away from device trackers (#32705) 2020-03-12 17:02:24 -07:00
Barry Williams
943c7ee11a If device has volume disabled, the volume will be None. However in these (#32702)
instances whenever the volume was requested a division calculation was made
resulting in a TypeError. The volume adjustment from `0-100` to `0-1` is now
calculated during the `update()` method.
2020-03-12 17:02:23 -07:00
162 changed files with 1299 additions and 845 deletions

View File

@@ -2,9 +2,15 @@
.git
.github
config
docs
# Development
.devcontainer
.vscode
# Test related files
.tox
tests
# Other virtualization methods
venv

View File

@@ -386,7 +386,7 @@ homeassistant/components/unifiled/* @florisvdk
homeassistant/components/upc_connect/* @pvizeli
homeassistant/components/upcloud/* @scop
homeassistant/components/updater/* @home-assistant/core
homeassistant/components/upnp/* @robbiet480
homeassistant/components/upnp/* @StevenLooman
homeassistant/components/uptimerobot/* @ludeeus
homeassistant/components/usgs_earthquakes_feed/* @exxamalte
homeassistant/components/utility_meter/* @dgomes

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
ARG BUILD_FROM
FROM ${BUILD_FROM}
WORKDIR /usr/src
## Setup Home Assistant
COPY . homeassistant/
RUN pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
-r homeassistant/requirements_all.txt -c homeassistant/homeassistant/package_constraints.txt \
&& pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
-e ./homeassistant \
&& python3 -m compileall homeassistant/homeassistant
# Home Assistant S6-Overlay
COPY rootfs /
WORKDIR /config

View File

@@ -14,7 +14,7 @@ schedules:
always: true
variables:
- name: versionBuilder
value: '6.9'
value: '7.2.0'
- group: docker
- group: github
- group: twine
@@ -108,11 +108,9 @@ stages:
docker run --rm --privileged \
-v ~/.docker:/root/.docker:rw \
-v /run/docker.sock:/run/docker.sock:rw \
-v $(pwd):/homeassistant:ro \
-v $(pwd):/data:ro \
homeassistant/amd64-builder:$(versionBuilder) \
--homeassistant $(homeassistantRelease) "--$(buildArch)" \
-r https://github.com/home-assistant/hassio-homeassistant \
-t generic --docker-hub homeassistant
--generic $(homeassistantRelease) "--$(buildArch)" -t /data \
docker run --rm --privileged \
-v ~/.docker:/root/.docker \

View File

@@ -5,6 +5,7 @@ trigger:
branches:
include:
- dev
- rc
paths:
include:
- requirements_all.txt
@@ -18,7 +19,7 @@ schedules:
always: true
variables:
- name: versionWheels
value: '1.4-3.7-alpine3.10'
value: '1.10.1-3.7-alpine3.11'
resources:
repositories:
- repository: azure
@@ -32,8 +33,10 @@ jobs:
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'
skipBinary: 'aiohttp'
wheelsRequirement: 'requirements_wheels.txt'
wheelsRequirementDiff: 'requirements_diff.txt'
wheelsConstraint: 'homeassistant/package_constraints.txt'
preBuild:
- script: |
cp requirements_all.txt requirements_wheels.txt
@@ -69,9 +72,5 @@ jobs:
sed -i "s|# py_noaa|py_noaa|g" ${requirement_file}
sed -i "s|# bme680|bme680|g" ${requirement_file}
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
if [[ "$(buildArch)" =~ arm ]]; then
sed -i "s|# VL53L1X|VL53L1X|g" ${requirement_file}
fi
done
displayName: 'Prepare requirements files for Hass.io'

14
build.json Normal file
View File

@@ -0,0 +1,14 @@
{
"image": "homeassistant/{arch}-homeassistant",
"build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:7.0.1",
"armhf": "homeassistant/armhf-homeassistant-base:7.0.1",
"armv7": "homeassistant/armv7-homeassistant-base:7.0.1",
"amd64": "homeassistant/amd64-homeassistant-base:7.0.1",
"i386": "homeassistant/i386-homeassistant-base:7.0.1"
},
"labels": {
"io.hass.type": "core"
},
"version_tag": true
}

View File

@@ -73,8 +73,8 @@ async def async_setup(hass, config):
conf.get("ssh_key", conf.get("pub_key", "")),
conf[CONF_MODE],
conf[CONF_REQUIRE_IP],
conf[CONF_INTERFACE],
conf[CONF_DNSMASQ],
interface=conf[CONF_INTERFACE],
dnsmasq=conf[CONF_DNSMASQ],
)
await api.connection.async_connect()

View File

@@ -2,7 +2,7 @@
"domain": "asuswrt",
"name": "ASUSWRT",
"documentation": "https://www.home-assistant.io/integrations/asuswrt",
"requirements": ["aioasuswrt==1.2.2"],
"requirements": ["aioasuswrt==1.2.3"],
"dependencies": [],
"codeowners": ["@kennedyshead"]
}

View File

@@ -3,7 +3,7 @@
"name": "August",
"documentation": "https://www.home-assistant.io/integrations/august",
"requirements": [
"py-august==0.24.0"
"py-august==0.25.0"
],
"dependencies": [
"configurator"

View File

@@ -3,7 +3,8 @@
"name": "Automation",
"documentation": "https://www.home-assistant.io/integrations/automation",
"requirements": [],
"dependencies": ["device_automation", "group", "webhook"],
"dependencies": [],
"after_dependencies": ["device_automation", "webhook"],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal"
}

View File

@@ -21,6 +21,10 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .axis_base import AxisEntityBase
from .const import DOMAIN as AXIS_DOMAIN
AXIS_IMAGE = "http://{host}:{port}/axis-cgi/jpg/image.cgi"
AXIS_VIDEO = "http://{host}:{port}/axis-cgi/mjpg/video.cgi"
AXIS_STREAM = "rtsp://{user}:{password}@{host}/axis-media/media.amp?videocodec=h264"
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Axis camera video stream."""
@@ -32,13 +36,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
CONF_NAME: config_entry.data[CONF_NAME],
CONF_USERNAME: config_entry.data[CONF_USERNAME],
CONF_PASSWORD: config_entry.data[CONF_PASSWORD],
CONF_MJPEG_URL: (
f"http://{config_entry.data[CONF_HOST]}"
f":{config_entry.data[CONF_PORT]}/axis-cgi/mjpg/video.cgi"
CONF_MJPEG_URL: AXIS_VIDEO.format(
host=config_entry.data[CONF_HOST], port=config_entry.data[CONF_PORT],
),
CONF_STILL_IMAGE_URL: (
f"http://{config_entry.data[CONF_HOST]}"
f":{config_entry.data[CONF_PORT]}/axis-cgi/jpg/image.cgi"
CONF_STILL_IMAGE_URL: AXIS_IMAGE.format(
host=config_entry.data[CONF_HOST], port=config_entry.data[CONF_PORT],
),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
}
@@ -70,19 +72,17 @@ class AxisCamera(AxisEntityBase, MjpegCamera):
async def stream_source(self):
"""Return the stream source."""
return (
f"rtsp://{self.device.config_entry.data[CONF_USERNAME]}´"
f":{self.device.config_entry.data[CONF_PASSWORD]}"
f"@{self.device.host}/axis-media/media.amp?videocodec=h264"
return AXIS_STREAM.format(
user=self.device.config_entry.data[CONF_USERNAME],
password=self.device.config_entry.data[CONF_PASSWORD],
host=self.device.host,
)
def _new_address(self):
"""Set new device address for video stream."""
port = self.device.config_entry.data[CONF_PORT]
self._mjpeg_url = (f"http://{self.device.host}:{port}/axis-cgi/mjpg/video.cgi",)
self._still_image_url = (
f"http://{self.device.host}:{port}/axis-cgi/jpg/image.cgi"
)
self._mjpeg_url = AXIS_VIDEO.format(host=self.device.host, port=port)
self._still_image_url = AXIS_IMAGE.format(host=self.device.host, port=port)
@property
def unique_id(self):

View File

@@ -141,7 +141,7 @@ async def async_request_stream(hass, entity_id, fmt):
source,
fmt=fmt,
keepalive=camera_prefs.preload_stream,
options=camera.options,
options=camera.stream_options,
)

View File

@@ -3,7 +3,7 @@
"name": "Google Cast",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/cast",
"requirements": ["pychromecast==4.1.1"],
"requirements": ["pychromecast==4.2.0"],
"dependencies": [],
"after_dependencies": ["cloud"],
"zeroconf": ["_googlecast._tcp.local."],

View File

@@ -7,9 +7,9 @@ from .const import ATTRIBUTION, OPTION_WORLDWIDE
SENSORS = {
"confirmed": "mdi:emoticon-neutral-outline",
"current": "mdi:emoticon-frown-outline",
"current": "mdi:emoticon-sad-outline",
"recovered": "mdi:emoticon-happy-outline",
"deaths": "mdi:emoticon-dead-outline",
"deaths": "mdi:emoticon-cry-outline",
}

View File

@@ -3,7 +3,7 @@
"name": "Cover",
"documentation": "https://www.home-assistant.io/integrations/cover",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal"
}

View File

@@ -3,7 +3,8 @@
"name": "Presence-based Lights",
"documentation": "https://www.home-assistant.io/integrations/device_sun_light_trigger",
"requirements": [],
"dependencies": ["device_tracker", "group", "light", "person"],
"dependencies": [],
"after_dependencies": ["device_tracker", "group", "light", "person"],
"codeowners": [],
"quality_scale": "internal"
}

View File

@@ -25,12 +25,10 @@ from .const import (
ATTR_LOCATION_NAME,
ATTR_MAC,
ATTR_SOURCE_TYPE,
CONF_AWAY_HIDE,
CONF_CONSIDER_HOME,
CONF_NEW_DEVICE_DEFAULTS,
CONF_SCAN_INTERVAL,
CONF_TRACK_NEW,
DEFAULT_AWAY_HIDE,
DEFAULT_CONSIDER_HOME,
DEFAULT_TRACK_NEW,
DOMAIN,
@@ -53,15 +51,7 @@ SOURCE_TYPES = (
NEW_DEVICE_DEFAULTS_SCHEMA = vol.Any(
None,
vol.All(
cv.deprecated(CONF_AWAY_HIDE, invalidation_version="0.107.0"),
vol.Schema(
{
vol.Optional(CONF_TRACK_NEW, default=DEFAULT_TRACK_NEW): cv.boolean,
vol.Optional(CONF_AWAY_HIDE, default=DEFAULT_AWAY_HIDE): cv.boolean,
}
),
),
vol.Schema({vol.Optional(CONF_TRACK_NEW, default=DEFAULT_TRACK_NEW): cv.boolean}),
)
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(
{

View File

@@ -20,9 +20,6 @@ SCAN_INTERVAL = timedelta(seconds=12)
CONF_TRACK_NEW = "track_new_devices"
DEFAULT_TRACK_NEW = True
CONF_AWAY_HIDE = "hide_if_away"
DEFAULT_AWAY_HIDE = False
CONF_CONSIDER_HOME = "consider_home"
DEFAULT_CONSIDER_HOME = timedelta(seconds=180)

View File

@@ -37,11 +37,9 @@ from .const import (
ATTR_HOST_NAME,
ATTR_MAC,
ATTR_SOURCE_TYPE,
CONF_AWAY_HIDE,
CONF_CONSIDER_HOME,
CONF_NEW_DEVICE_DEFAULTS,
CONF_TRACK_NEW,
DEFAULT_AWAY_HIDE,
DEFAULT_CONSIDER_HOME,
DEFAULT_TRACK_NEW,
DOMAIN,
@@ -198,7 +196,6 @@ class DeviceTracker:
mac,
picture=picture,
icon=icon,
hide_if_away=self.defaults.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE),
)
self.devices[dev_id] = device
if mac is not None:
@@ -303,7 +300,6 @@ class Device(RestoreEntity):
picture: str = None,
gravatar: str = None,
icon: str = None,
hide_if_away: bool = False,
) -> None:
"""Initialize a device."""
self.hass = hass
@@ -331,8 +327,6 @@ class Device(RestoreEntity):
self.icon = icon
self.away_hide = hide_if_away
self.source_type = None
self._attributes = {}
@@ -372,11 +366,6 @@ class Device(RestoreEntity):
"""Return device state attributes."""
return self._attributes
@property
def hidden(self):
"""If device should be hidden."""
return self.away_hide and self.state != STATE_HOME
async def async_seen(
self,
host_name: str = None,
@@ -524,7 +513,6 @@ async def async_load_config(
vol.Optional(CONF_MAC, default=None): vol.Any(
None, vol.All(cv.string, vol.Upper)
),
vol.Optional(CONF_AWAY_HIDE, default=DEFAULT_AWAY_HIDE): cv.boolean,
vol.Optional("gravatar", default=None): vol.Any(None, cv.string),
vol.Optional("picture", default=None): vol.Any(None, cv.string),
vol.Optional(CONF_CONSIDER_HOME, default=consider_home): vol.All(
@@ -544,6 +532,7 @@ async def async_load_config(
for dev_id, device in devices.items():
# Deprecated option. We just ignore it to avoid breaking change
device.pop("vendor", None)
device.pop("hide_if_away", None)
try:
device = dev_schema(device)
device["dev_id"] = cv.slugify(dev_id)
@@ -564,7 +553,6 @@ def update_config(path: str, dev_id: str, device: Device):
ATTR_ICON: device.icon,
"picture": device.config_picture,
"track": device.track,
CONF_AWAY_HIDE: device.away_hide,
}
}
out.write("\n")

View File

@@ -3,7 +3,8 @@
"name": "Device Tracker",
"documentation": "https://www.home-assistant.io/integrations/device_tracker",
"requirements": [],
"dependencies": ["group", "zone"],
"dependencies": ["zone"],
"after_dependencies": [],
"codeowners": [],
"quality_scale": "internal"
}

View File

@@ -1,11 +1,11 @@
{
"config": {
"abort": {
"already_configured": "DirecTV receiver is already configured"
"already_configured": "DirecTV receiver is already configured",
"unknown": "Unexpected error"
},
"error": {
"cannot_connect": "Failed to connect, please try again",
"unknown": "Unexpected error"
"cannot_connect": "Failed to connect, please try again"
},
"flow_title": "DirecTV: {name}",
"step": {
@@ -23,4 +23,4 @@
},
"title": "DirecTV"
}
}
}

View File

@@ -32,7 +32,7 @@ def get_dtv_data(
hass: HomeAssistant, host: str, port: int = DEFAULT_PORT, client_addr: str = "0"
) -> dict:
"""Retrieve a DIRECTV instance, locations list, and version info for the receiver device."""
dtv = DIRECTV(host, port, client_addr)
dtv = DIRECTV(host, port, client_addr, determine_state=False)
locations = dtv.get_locations()
version_info = dtv.get_version()

View File

@@ -29,8 +29,7 @@ def validate_input(data: Dict) -> Dict:
Data has the keys from DATA_SCHEMA with values provided by the user.
"""
# directpy does IO in constructor.
dtv = DIRECTV(data["host"], DEFAULT_PORT)
dtv = DIRECTV(data["host"], DEFAULT_PORT, determine_state=False)
version_info = dtv.get_version()
return {
@@ -76,8 +75,7 @@ class DirecTVConfigFlow(ConfigFlow, domain=DOMAIN):
return self._show_form(errors)
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = ERROR_UNKNOWN
return self._show_form(errors)
return self.async_abort(reason=ERROR_UNKNOWN)
await self.async_set_unique_id(info["receiver_id"])
self._abort_if_unique_id_configured()

View File

@@ -83,22 +83,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
)
def get_dtv_instance(
host: str, port: int = DEFAULT_PORT, client_addr: str = "0"
) -> DIRECTV:
"""Retrieve a DIRECTV instance for the receiver or client device."""
try:
return DIRECTV(host, port, client_addr)
except RequestException as exception:
_LOGGER.debug(
"Request exception %s trying to retrieve DIRECTV instance for client address %s on device %s",
exception,
client_addr,
host,
)
return None
async def async_setup_entry(
hass: HomeAssistantType,
entry: ConfigEntry,
@@ -114,16 +98,15 @@ async def async_setup_entry(
continue
if loc["clientAddr"] != "0":
# directpy does IO in constructor.
dtv = await hass.async_add_executor_job(
get_dtv_instance, entry.data[CONF_HOST], DEFAULT_PORT, loc["clientAddr"]
dtv = DIRECTV(
entry.data[CONF_HOST],
DEFAULT_PORT,
loc["clientAddr"],
determine_state=False,
)
else:
dtv = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT]
if not dtv:
continue
entities.append(
DirecTvDevice(
str.title(loc["locationName"]), loc["clientAddr"], dtv, version_info,
@@ -175,15 +158,6 @@ class DirecTvDevice(MediaPlayerDevice):
self._model = MODEL_HOST
self._software_version = version_info["stbSoftwareVersion"]
if self._is_client:
_LOGGER.debug(
"Created DirecTV media player for client %s on device %s",
self._name,
device,
)
else:
_LOGGER.debug("Created DirecTV media player for device %s", self._name)
def update(self):
"""Retrieve latest state."""
_LOGGER.debug("%s: Updating status", self.entity_id)

View File

@@ -16,11 +16,11 @@
}
},
"error": {
"cannot_connect": "Failed to connect, please try again",
"unknown": "Unexpected error"
"cannot_connect": "Failed to connect, please try again"
},
"abort": {
"already_configured": "DirecTV receiver is already configured"
"already_configured": "DirecTV receiver is already configured",
"unknown": "Unexpected error"
}
}
}

View File

@@ -677,7 +677,11 @@ def entity_to_json(config, entity):
retval["type"] = "Color temperature light"
retval["modelid"] = "HASS312"
retval["state"].update(
{HUE_API_STATE_COLORMODE: "ct", HUE_API_STATE_CT: state[STATE_COLOR_TEMP]}
{
HUE_API_STATE_COLORMODE: "ct",
HUE_API_STATE_CT: state[STATE_COLOR_TEMP],
HUE_API_STATE_BRI: state[STATE_BRIGHTNESS],
}
)
elif entity_features & (
SUPPORT_BRIGHTNESS

View File

@@ -149,7 +149,12 @@ class EvoZone(EvoChild, EvoClimateDevice):
"""Initialize a Honeywell TCC Zone."""
super().__init__(evo_broker, evo_device)
self._unique_id = evo_device.zoneId
if evo_device.modelType.startswith("VisionProWifi"):
# this system does not have a distinct ID for the zone
self._unique_id = f"{evo_device.zoneId}z"
else:
self._unique_id = evo_device.zoneId
self._name = evo_device.name
self._icon = "mdi:radiator"

View File

@@ -3,7 +3,7 @@
"name": "Fan",
"documentation": "https://www.home-assistant.io/integrations/fan",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": [],
"quality_scale": "internal"
}

View File

@@ -276,8 +276,7 @@ async def async_setup(hass, config):
hass.http.app.router.register_resource(IndexView(repo_path, hass))
for panel in ("kiosk", "states", "profile"):
async_register_built_in_panel(hass, panel)
async_register_built_in_panel(hass, "profile")
# To smooth transition to new urls, add redirects to new urls of dev tools
# Added June 27, 2019. Can be removed in 2021.

View File

@@ -3,11 +3,12 @@
"name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": [
"home-assistant-frontend==20200311.1"
"home-assistant-frontend==20200318.1"
],
"dependencies": [
"api",
"auth",
"device_automation",
"http",
"lovelace",
"onboarding",
@@ -19,4 +20,4 @@
"@home-assistant/frontend"
],
"quality_scale": "internal"
}
}

View File

@@ -190,16 +190,15 @@ async def async_setup(hass, config):
hass.http.register_view(HassIOView(host, websession))
if "frontend" in hass.config.components:
await hass.components.panel_custom.async_register_panel(
frontend_url_path="hassio",
webcomponent_name="hassio-main",
sidebar_title="Supervisor",
sidebar_icon="hass:home-assistant",
js_url="/api/hassio/app/entrypoint.js",
embed_iframe=True,
require_admin=True,
)
await hass.components.panel_custom.async_register_panel(
frontend_url_path="hassio",
webcomponent_name="hassio-main",
sidebar_title="Supervisor",
sidebar_icon="hass:home-assistant",
js_url="/api/hassio/app/entrypoint.js",
embed_iframe=True,
require_admin=True,
)
await hassio.update_hass_api(config.get("http", {}), refresh_token)

View File

@@ -3,6 +3,7 @@
"name": "Hass.io",
"documentation": "https://www.home-assistant.io/hassio",
"requirements": [],
"dependencies": ["http", "panel_custom"],
"dependencies": ["http"],
"after_dependencies": ["panel_custom"],
"codeowners": ["@home-assistant/hass-io"]
}

View File

@@ -14,7 +14,7 @@
"busy_error": "Device refused to add pairing as it is already pairing with another controller.",
"max_peers_error": "Device refused to add pairing as it has no free pairing storage.",
"max_tries_error": "Device refused to add pairing as it has received more than 100 unsuccessful authentication attempts.",
"pairing_failed": "An unhandled error occured while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently.",
"pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently.",
"unable_to_pair": "Unable to pair, please try again.",
"unknown_error": "Device reported an unknown error. Pairing failed."
},

View File

@@ -3,7 +3,7 @@
"name": "HomeKit Controller",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"requirements": ["aiohomekit[IP]==0.2.29"],
"requirements": ["aiohomekit[IP]==0.2.29.2"],
"dependencies": [],
"zeroconf": ["_hap._tcp.local."],
"codeowners": ["@Jc2k"]

View File

@@ -154,7 +154,7 @@ class HomeKitTelevision(HomeKitEntity, MediaPlayerDevice):
homekit_state = self.service.value(CharacteristicsTypes.CURRENT_MEDIA_STATE)
if homekit_state is not None:
return HK_TO_HA_STATE[homekit_state]
return HK_TO_HA_STATE.get(homekit_state, STATE_OK)
return STATE_OK

View File

@@ -25,7 +25,7 @@
"max_peers_error": "Device refused to add pairing as it has no free pairing storage.",
"busy_error": "Device refused to add pairing as it is already pairing with another controller.",
"max_tries_error": "Device refused to add pairing as it has received more than 100 unsuccessful authentication attempts.",
"pairing_failed": "An unhandled error occured while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently."
"pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently."
},
"abort": {
"no_devices": "No unpaired devices could be found",

View File

@@ -137,11 +137,6 @@ class HomematicipHAP:
job = self.hass.async_create_task(self.get_state())
job.add_done_callback(self.get_state_finished)
self._accesspoint_connected = True
else:
# Update home with the given json from arg[0],
# without devices and groups.
self.home.update_home_only(args[0])
@callback
def async_create_entity(self, *args, **kwargs) -> None:

View File

@@ -20,6 +20,7 @@ from homeassistant.components.light import (
ATTR_TRANSITION,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_TRANSITION,
Light,
)
from homeassistant.config_entries import ConfigEntry
@@ -197,7 +198,7 @@ class HomematicipNotificationLight(HomematicipGenericDevice, Light):
@property
def supported_features(self) -> int:
"""Flag supported features."""
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_TRANSITION
@property
def unique_id(self) -> str:

View File

@@ -5,7 +5,7 @@
"documentation": "https://www.home-assistant.io/integrations/huawei_lte",
"requirements": [
"getmac==0.8.1",
"huawei-lte-api==1.4.10",
"huawei-lte-api==1.4.11",
"stringcase==1.2.0",
"url-normalize==1.4.1"
],

View File

@@ -97,6 +97,7 @@ class IcloudAccount:
self._owner_fullname = None
self._family_members_fullname = {}
self._devices = {}
self._retried_fetch = False
self.listeners = []
@@ -122,10 +123,6 @@ class IcloudAccount:
_LOGGER.error("No iCloud device found")
raise ConfigEntryNotReady
if DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending":
_LOGGER.warning("Pending devices, trying again ...")
raise ConfigEntryNotReady
self._owner_fullname = f"{user_info['firstName']} {user_info['lastName']}"
self._family_members_fullname = {}
@@ -157,28 +154,15 @@ class IcloudAccount:
)
return
if DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending":
_LOGGER.warning("Pending devices, trying again in 15s")
self._fetch_interval = 0.25
dispatcher_send(self.hass, self.signal_device_update)
track_point_in_utc_time(
self.hass,
self.keep_alive,
utcnow() + timedelta(minutes=self._fetch_interval),
)
return
# Gets devices infos
new_device = False
for device in api_devices:
status = device.status(DEVICE_STATUS_SET)
device_id = status[DEVICE_ID]
device_name = status[DEVICE_NAME]
device_status = DEVICE_STATUS_CODES.get(status[DEVICE_STATUS], "error")
if (
device_status == "pending"
or status[DEVICE_BATTERY_STATUS] == "Unknown"
status[DEVICE_BATTERY_STATUS] == "Unknown"
or status.get(DEVICE_BATTERY_LEVEL) is None
):
continue
@@ -198,7 +182,16 @@ class IcloudAccount:
self._devices[device_id].update(status)
new_device = True
self._fetch_interval = self._determine_interval()
if (
DEVICE_STATUS_CODES.get(list(api_devices)[0][DEVICE_STATUS]) == "pending"
and not self._retried_fetch
):
_LOGGER.warning("Pending devices, trying again in 15s")
self._fetch_interval = 0.25
self._retried_fetch = True
else:
self._fetch_interval = self._determine_interval()
self._retried_fetch = False
dispatcher_send(self.hass, self.signal_device_update)
if new_device:

View File

@@ -3,7 +3,7 @@
"name": "Apple iCloud",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/icloud",
"requirements": ["pyicloud==0.9.4"],
"requirements": ["pyicloud==0.9.5"],
"dependencies": [],
"codeowners": ["@Quentame"]
}

View File

@@ -88,24 +88,22 @@ def _cv_input_text(cfg):
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: cv.schema_with_slug_keys(
vol.Any(
vol.All(
{
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_MIN, default=CONF_MIN_VALUE): vol.Coerce(int),
vol.Optional(CONF_MAX, default=CONF_MAX_VALUE): vol.Coerce(int),
vol.Optional(CONF_INITIAL, ""): cv.string,
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(CONF_PATTERN): cv.string,
vol.Optional(CONF_MODE, default=MODE_TEXT): vol.In(
[MODE_TEXT, MODE_PASSWORD]
),
},
_cv_input_text,
),
None,
)
vol.All(
lambda value: value or {},
{
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_MIN, default=CONF_MIN_VALUE): vol.Coerce(int),
vol.Optional(CONF_MAX, default=CONF_MAX_VALUE): vol.Coerce(int),
vol.Optional(CONF_INITIAL, ""): cv.string,
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(CONF_PATTERN): cv.string,
vol.Optional(CONF_MODE, default=MODE_TEXT): vol.In(
[MODE_TEXT, MODE_PASSWORD]
),
},
_cv_input_text,
),
)
},
extra=vol.ALLOW_EXTRA,
@@ -203,13 +201,6 @@ class InputText(RestoreEntity):
@classmethod
def from_yaml(cls, config: typing.Dict) -> "InputText":
"""Return entity instance initialized from yaml storage."""
# set defaults for empty config
config = {
CONF_MAX: CONF_MAX_VALUE,
CONF_MIN: CONF_MIN_VALUE,
CONF_MODE: MODE_TEXT,
**config,
}
input_text = cls(config)
input_text.entity_id = f"{DOMAIN}.{config[CONF_ID]}"
input_text.editable = False

View File

@@ -2,7 +2,7 @@
"domain": "insteon",
"name": "Insteon",
"documentation": "https://www.home-assistant.io/integrations/insteon",
"requirements": ["insteonplm==0.16.7"],
"requirements": ["insteonplm==0.16.8"],
"dependencies": [],
"codeowners": []
}

View File

@@ -104,7 +104,7 @@ async def async_get_api(hass):
async def async_get_location(hass, api, latitude, longitude):
"""Retrieve pyipma location, location name to be used as the entity name."""
with async_timeout.timeout(10):
with async_timeout.timeout(30):
location = await Location.get(api, float(latitude), float(longitude))
_LOGGER.debug(

View File

@@ -4,7 +4,7 @@ from datetime import timedelta
import logging
from pyiqvia import Client
from pyiqvia.errors import InvalidZipError, IQVIAError
from pyiqvia.errors import InvalidZipError
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT
@@ -172,7 +172,7 @@ class IQVIAData:
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
for key, result in zip(tasks, results):
if isinstance(result, IQVIAError):
if isinstance(result, Exception):
_LOGGER.error("Unable to get %s data: %s", key, result)
self.data[key] = {}
continue

View File

@@ -306,6 +306,7 @@ class KonnectedView(HomeAssistantView):
[
entry.data[CONF_ACCESS_TOKEN]
for entry in hass.config_entries.async_entries(DOMAIN)
if entry.data.get(CONF_ACCESS_TOKEN)
]
)
if auth is None or not next(

View File

@@ -15,7 +15,7 @@ from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
from . import ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRIGHTNESS
from . import ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRIGHTNESS
TYPE_BRIGHTNESS_INCREASE = "brightness_increase"
TYPE_BRIGHTNESS_DECREASE = "brightness_decrease"
@@ -28,7 +28,7 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
toggle_entity.DEVICE_ACTION_TYPES
+ [TYPE_BRIGHTNESS_INCREASE, TYPE_BRIGHTNESS_DECREASE]
),
vol.Optional(ATTR_BRIGHTNESS): vol.All(
vol.Optional(ATTR_BRIGHTNESS_PCT): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
),
}
@@ -57,8 +57,8 @@ async def async_call_action_from_config(
data[ATTR_BRIGHTNESS_STEP_PCT] = 10
elif config[CONF_TYPE] == TYPE_BRIGHTNESS_DECREASE:
data[ATTR_BRIGHTNESS_STEP_PCT] = -10
elif ATTR_BRIGHTNESS in config:
data[ATTR_BRIGHTNESS] = config[ATTR_BRIGHTNESS]
elif ATTR_BRIGHTNESS_PCT in config:
data[ATTR_BRIGHTNESS_PCT] = config[ATTR_BRIGHTNESS_PCT]
await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=context
@@ -125,7 +125,7 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di
return {
"extra_fields": vol.Schema(
{
vol.Optional(ATTR_BRIGHTNESS): vol.All(
vol.Optional(ATTR_BRIGHTNESS_PCT): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
}

View File

@@ -3,7 +3,7 @@
"name": "Light",
"documentation": "https://www.home-assistant.io/integrations/light",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": [],
"quality_scale": "internal"
}

View File

@@ -3,7 +3,7 @@
"name": "Lock",
"documentation": "https://www.home-assistant.io/integrations/lock",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": [],
"quality_scale": "internal"
}

View File

@@ -4,10 +4,14 @@ import logging
import voluptuous as vol
from homeassistant.components import frontend
from homeassistant.config import async_hass_config_yaml, async_process_component_config
from homeassistant.const import CONF_FILENAME
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import collection, config_validation as cv
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.helpers.typing import ConfigType, HomeAssistantType, ServiceCallType
from homeassistant.loader import async_get_integration
from homeassistant.util import sanitize_filename
from . import dashboard, resources, websocket
@@ -25,8 +29,10 @@ from .const import (
MODE_STORAGE,
MODE_YAML,
RESOURCE_CREATE_FIELDS,
RESOURCE_RELOAD_SERVICE_SCHEMA,
RESOURCE_SCHEMA,
RESOURCE_UPDATE_FIELDS,
SERVICE_RELOAD_RESOURCES,
STORAGE_DASHBOARD_CREATE_FIELDS,
STORAGE_DASHBOARD_UPDATE_FIELDS,
url_slug,
@@ -62,29 +68,41 @@ CONFIG_SCHEMA = vol.Schema(
)
async def async_setup(hass, config):
async def async_setup(hass: HomeAssistantType, config: ConfigType):
"""Set up the Lovelace commands."""
mode = config[DOMAIN][CONF_MODE]
yaml_resources = config[DOMAIN].get(CONF_RESOURCES)
frontend.async_register_built_in_panel(hass, DOMAIN, config={"mode": mode})
async def reload_resources_service_handler(service_call: ServiceCallType) -> None:
"""Reload yaml resources."""
try:
conf = await async_hass_config_yaml(hass)
except HomeAssistantError as err:
_LOGGER.error(err)
return
integration = await async_get_integration(hass, DOMAIN)
config = await async_process_component_config(hass, conf, integration)
resource_collection = await create_yaml_resource_col(
hass, config[DOMAIN].get(CONF_RESOURCES)
)
hass.data[DOMAIN]["resources"] = resource_collection
if mode == MODE_YAML:
default_config = dashboard.LovelaceYAML(hass, None, None)
resource_collection = await create_yaml_resource_col(hass, yaml_resources)
if yaml_resources is None:
try:
ll_conf = await default_config.async_load(False)
except HomeAssistantError:
pass
else:
if CONF_RESOURCES in ll_conf:
_LOGGER.warning(
"Resources need to be specified in your configuration.yaml. Please see the docs."
)
yaml_resources = ll_conf[CONF_RESOURCES]
resource_collection = resources.ResourceYAMLCollection(yaml_resources or [])
async_register_admin_service(
hass,
DOMAIN,
SERVICE_RELOAD_RESOURCES,
reload_resources_service_handler,
schema=RESOURCE_RELOAD_SERVICE_SCHEMA,
)
else:
default_config = dashboard.LovelaceStorage(hass, None)
@@ -127,25 +145,12 @@ async def async_setup(hass, config):
# We store a dictionary mapping url_path: config. None is the default.
"dashboards": {None: default_config},
"resources": resource_collection,
"yaml_dashboards": config[DOMAIN].get(CONF_DASHBOARDS, {}),
}
if hass.config.safe_mode:
return True
# Process YAML dashboards
for url_path, dashboard_conf in config[DOMAIN].get(CONF_DASHBOARDS, {}).items():
# For now always mode=yaml
config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf)
hass.data[DOMAIN]["dashboards"][url_path] = config
try:
_register_panel(hass, url_path, MODE_YAML, dashboard_conf, False)
except ValueError:
_LOGGER.warning("Panel url path %s is not unique", url_path)
# Process storage dashboards
dashboards_collection = dashboard.DashboardsCollection(hass)
async def storage_dashboard_changed(change_type, item_id, item):
"""Handle a storage dashboard change."""
url_path = item[CONF_URL_PATH]
@@ -156,6 +161,7 @@ async def async_setup(hass, config):
return
if change_type == collection.CHANGE_ADDED:
existing = hass.data[DOMAIN]["dashboards"].get(url_path)
if existing:
@@ -180,6 +186,20 @@ async def async_setup(hass, config):
except ValueError:
_LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path)
# Process YAML dashboards
for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items():
# For now always mode=yaml
config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf)
hass.data[DOMAIN]["dashboards"][url_path] = config
try:
_register_panel(hass, url_path, MODE_YAML, dashboard_conf, False)
except ValueError:
_LOGGER.warning("Panel url path %s is not unique", url_path)
# Process storage dashboards
dashboards_collection = dashboard.DashboardsCollection(hass)
dashboards_collection.async_add_listener(storage_dashboard_changed)
await dashboards_collection.async_load()
@@ -194,6 +214,24 @@ async def async_setup(hass, config):
return True
async def create_yaml_resource_col(hass, yaml_resources):
"""Create yaml resources collection."""
if yaml_resources is None:
default_config = dashboard.LovelaceYAML(hass, None, None)
try:
ll_conf = await default_config.async_load(False)
except HomeAssistantError:
pass
else:
if CONF_RESOURCES in ll_conf:
_LOGGER.warning(
"Resources need to be specified in your configuration.yaml. Please see the docs."
)
yaml_resources = ll_conf[CONF_RESOURCES]
return resources.ResourceYAMLCollection(yaml_resources or [])
async def system_health_info(hass):
"""Get info for the info page."""
return await hass.data[DOMAIN]["dashboards"][None].async_get_info()

View File

@@ -41,6 +41,9 @@ RESOURCE_UPDATE_FIELDS = {
vol.Optional(CONF_URL): cv.string,
}
SERVICE_RELOAD_RESOURCES = "reload_resources"
RESOURCE_RELOAD_SERVICE_SCHEMA = vol.Schema({})
CONF_TITLE = "title"
CONF_REQUIRE_ADMIN = "require_admin"
CONF_SHOW_IN_SIDEBAR = "show_in_sidebar"
@@ -76,6 +79,8 @@ def url_slug(value: Any) -> str:
"""Validate value is a valid url slug."""
if value is None:
raise vol.Invalid("Slug should not be None")
if "-" not in value:
raise vol.Invalid("Url path needs to contain a hyphen (-)")
str_value = str(value)
slg = slugify(str_value, separator="-")
if str_value == slg:

View File

@@ -3,9 +3,11 @@ from abc import ABC, abstractmethod
import logging
import os
import time
from typing import Optional, cast
import voluptuous as vol
from homeassistant.components.frontend import DATA_PANELS
from homeassistant.const import CONF_FILENAME
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
@@ -86,7 +88,7 @@ class LovelaceStorage(LovelaceConfig):
storage_key = CONFIG_STORAGE_KEY_DEFAULT
else:
url_path = config[CONF_URL_PATH]
storage_key = CONFIG_STORAGE_KEY.format(url_path)
storage_key = CONFIG_STORAGE_KEY.format(config["id"])
super().__init__(hass, url_path, config)
@@ -229,10 +231,32 @@ class DashboardsCollection(collection.StorageCollection):
_LOGGER,
)
async def _async_load_data(self) -> Optional[dict]:
"""Load the data."""
data = await self.store.async_load()
if data is None:
return cast(Optional[dict], data)
updated = False
for item in data["items"] or []:
if "-" not in item[CONF_URL_PATH]:
updated = True
item[CONF_URL_PATH] = f"lovelace-{item[CONF_URL_PATH]}"
if updated:
await self.store.async_save(data)
return cast(Optional[dict], data)
async def _process_create_data(self, data: dict) -> dict:
"""Validate the config is valid."""
if data[CONF_URL_PATH] in self.hass.data[DOMAIN]["dashboards"]:
raise vol.Invalid("Dashboard url path needs to be unique")
if "-" not in data[CONF_URL_PATH]:
raise vol.Invalid("Url path needs to contain a hyphen (-)")
if data[CONF_URL_PATH] in self.hass.data[DATA_PANELS]:
raise vol.Invalid("Panel url path needs to be unique")
return self.CREATE_SCHEMA(data)

View File

@@ -0,0 +1,4 @@
# Describes the format for available lovelace services
reload_resources:
description: Reload Lovelace resources from yaml configuration.

View File

@@ -332,16 +332,17 @@ class MikrotikHub:
async def async_add_options(self):
"""Populate default options for Mikrotik."""
if not self.config_entry.options:
data = dict(self.config_entry.data)
options = {
CONF_ARP_PING: self.config_entry.data.pop(CONF_ARP_PING, False),
CONF_FORCE_DHCP: self.config_entry.data.pop(CONF_FORCE_DHCP, False),
CONF_DETECTION_TIME: self.config_entry.data.pop(
CONF_ARP_PING: data.pop(CONF_ARP_PING, False),
CONF_FORCE_DHCP: data.pop(CONF_FORCE_DHCP, False),
CONF_DETECTION_TIME: data.pop(
CONF_DETECTION_TIME, DEFAULT_DETECTION_TIME
),
}
self.hass.config_entries.async_update_entry(
self.config_entry, options=options
self.config_entry, data=data, options=options
)
async def request_update(self):

View File

@@ -28,6 +28,7 @@ from . import api, config_flow
from .const import (
AUTH,
CONF_CLOUDHOOK_URL,
DATA_DEVICE_IDS,
DATA_PERSONS,
DOMAIN,
OAUTH2_AUTHORIZE,
@@ -65,6 +66,7 @@ async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Netatmo component."""
hass.data[DOMAIN] = {}
hass.data[DOMAIN][DATA_PERSONS] = {}
hass.data[DOMAIN][DATA_DEVICE_IDS] = {}
if DOMAIN not in config:
return True
@@ -104,7 +106,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
webhook_unregister(hass, entry.data[CONF_WEBHOOK_ID])
async def register_webhook(event):
# Wait for the could integration to be ready
# Wait for the cloud integration to be ready
await asyncio.sleep(WAIT_FOR_CLOUD)
if CONF_WEBHOOK_ID not in entry.data:
@@ -112,6 +114,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.config_entries.async_update_entry(entry, data=data)
if hass.components.cloud.async_active_subscription():
# Wait for cloud connection to be established
await asyncio.sleep(WAIT_FOR_CLOUD)
if CONF_CLOUDHOOK_URL not in entry.data:
webhook_url = await hass.components.cloud.async_create_cloudhook(
entry.data[CONF_WEBHOOK_ID]
@@ -144,6 +149,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
if CONF_WEBHOOK_ID in entry.data:
await hass.async_add_executor_job(
hass.data[DOMAIN][entry.entry_id][AUTH].dropwebhook
)
unload_ok = all(
await asyncio.gather(
*[
@@ -152,14 +162,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
]
)
)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
if CONF_WEBHOOK_ID in entry.data:
await hass.async_add_executor_job(
hass.data[DOMAIN][entry.entry_id][AUTH].dropwebhook()
)
return unload_ok

View File

@@ -84,21 +84,11 @@ class NetatmoCamera(Camera):
self._unique_id = f"{self._camera_id}-{self._camera_type}"
self._verify_ssl = verify_ssl
self._quality = quality
# URLs
self._vpnurl = None
self._localurl = None
# Monitoring status
self._status = None
# SD Card status
self._sd_status = None
# Power status
self._alim_status = None
# Is local
self._is_local = None
def camera_image(self):
@@ -219,8 +209,6 @@ class NetatmoCamera(Camera):
def update(self):
"""Update entity status."""
# Refresh camera data
self._data.update()
camera = self._data.camera_data.get_camera(cid=self._camera_id)

View File

@@ -441,6 +441,11 @@ class ThermostatData:
except TypeError:
_LOGGER.error("ThermostatData::setup() got error")
return False
except pyatmo.exceptions.NoDevice:
_LOGGER.debug(
"No climate devices for %s (%s)", self.home_name, self.home_id
)
return False
return True
@Throttle(MIN_TIME_BETWEEN_UPDATES)

View File

@@ -33,6 +33,7 @@ class NetatmoFlowHandler(
"read_station",
"read_thermostat",
"write_camera",
"write_presence",
"write_thermostat",
]

View File

@@ -14,6 +14,7 @@ MODELS = {
"NOC": "Smart Outdoor Camera",
"NSD": "Smart Smoke Alarm",
"NACamDoorTag": "Smart Door and Window Sensors",
"NHC": "Smart Indoor Air Quality Monitor",
"NAMain": "Smart Home Weather station indoor module",
"NAModule1": "Smart Home Weather station outdoor module",
"NAModule4": "Smart Additional Indoor module",
@@ -31,6 +32,7 @@ CONF_CLOUDHOOK_URL = "cloudhook_url"
OAUTH2_AUTHORIZE = "https://api.netatmo.com/oauth2/authorize"
OAUTH2_TOKEN = "https://api.netatmo.com/oauth2/token"
DATA_DEVICE_IDS = "netatmo_device_ids"
DATA_PERSONS = "netatmo_persons"
NETATMO_WEBHOOK_URL = None

View File

@@ -375,7 +375,7 @@ class ONVIFHassCamera(Camera):
def setup_ptz(self):
"""Set up PTZ if available."""
_LOGGER.debug("Setting up the ONVIF PTZ service")
if self._camera.get_service("ptz") is None:
if self._camera.get_service("ptz", create=False) is None:
_LOGGER.debug("PTZ is not available")
else:
self._ptz_service = self._camera.create_ptz_service()
@@ -505,4 +505,6 @@ class ONVIFHassCamera(Camera):
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
if self._profile_index:
return f"{self._mac}_{self._profile_index}"
return self._mac

View File

@@ -85,7 +85,7 @@ class OpenhomeDevice(MediaPlayerDevice):
self._supported_features |= (
SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET
)
self._volume_level = self._device.VolumeLevel()
self._volume_level = self._device.VolumeLevel() / 100.0
self._volume_muted = self._device.IsMuted()
for source in self._device.Sources():
@@ -222,7 +222,7 @@ class OpenhomeDevice(MediaPlayerDevice):
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
return self._volume_level / 100.0
return self._volume_level
@property
def is_volume_muted(self):

View File

@@ -146,8 +146,6 @@ async def async_setup(hass, config):
if DOMAIN not in config:
return True
success = False
for panel in config[DOMAIN]:
name = panel[CONF_COMPONENT_NAME]
@@ -182,8 +180,13 @@ async def async_setup(hass, config):
hass.http.register_static_path(url, panel_path)
kwargs["html_url"] = url
await async_register_panel(hass, **kwargs)
try:
await async_register_panel(hass, **kwargs)
except ValueError as err:
_LOGGER.error(
"Unable to register panel %s: %s",
panel.get(CONF_SIDEBAR_TITLE, name),
err,
)
success = True
return success
return True

View File

@@ -77,7 +77,11 @@ PERSON_SCHEMA = vol.Schema(
)
CONFIG_SCHEMA = vol.Schema(
{vol.Optional(DOMAIN): vol.All(cv.ensure_list, cv.remove_falsy, [PERSON_SCHEMA])},
{
vol.Optional(DOMAIN, default=[]): vol.All(
cv.ensure_list, cv.remove_falsy, [PERSON_SCHEMA]
)
},
extra=vol.ALLOW_EXTRA,
)

View File

@@ -3,7 +3,7 @@
"name": "Plant Monitor",
"documentation": "https://www.home-assistant.io/integrations/plant",
"requirements": [],
"dependencies": ["group", "zone"],
"dependencies": [],
"after_dependencies": ["recorder"],
"codeowners": ["@ChristianKuehnel"],
"quality_scale": "internal"

View File

@@ -75,8 +75,9 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
def token_saver(token):
_LOGGER.debug("Saving updated token")
entry.data[CONF_TOKEN] = token
hass.config_entries.async_update_entry(entry, data={**entry.data})
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_TOKEN: token}
)
# Force token update.
entry.data[CONF_TOKEN]["expires_in"] = -1
@@ -105,12 +106,18 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
async def async_setup_webhook(hass: HomeAssistantType, entry: ConfigEntry, session):
"""Set up a webhook to handle binary sensor events."""
if CONF_WEBHOOK_ID not in entry.data:
entry.data[CONF_WEBHOOK_ID] = hass.components.webhook.async_generate_id()
entry.data[CONF_WEBHOOK_URL] = hass.components.webhook.async_generate_url(
entry.data[CONF_WEBHOOK_ID]
webhook_id = hass.components.webhook.async_generate_id()
webhook_url = hass.components.webhook.async_generate_url(webhook_id)
_LOGGER.info("Registering new webhook at: %s", webhook_url)
hass.config_entries.async_update_entry(
entry,
data={
**entry.data,
CONF_WEBHOOK_ID: webhook_id,
CONF_WEBHOOK_URL: webhook_url,
},
)
_LOGGER.info("Registering new webhook at: %s", entry.data[CONF_WEBHOOK_URL])
hass.config_entries.async_update_entry(entry, data={**entry.data})
await hass.async_add_executor_job(
session.update_webhook,
entry.data[CONF_WEBHOOK_URL],

View File

@@ -3,7 +3,7 @@
"name": "Rainforest Eagle-200",
"documentation": "https://www.home-assistant.io/integrations/rainforest_eagle",
"requirements": [
"eagle200_reader==0.2.1",
"eagle200_reader==0.2.4",
"uEagle==0.0.1"
],
"dependencies": [],

View File

@@ -25,6 +25,7 @@ from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.service import verify_domain_control
from .const import (
CONF_ZONE_RUN_TIME,
DATA_CLIENT,
DATA_PROGRAMS,
DATA_PROVISION_SETTINGS,
@@ -33,6 +34,8 @@ from .const import (
DATA_ZONES,
DATA_ZONES_DETAILS,
DEFAULT_PORT,
DEFAULT_SCAN_INTERVAL,
DEFAULT_ZONE_RUN,
DOMAIN,
PROGRAM_UPDATE_TOPIC,
SENSOR_UPDATE_TOPIC,
@@ -41,19 +44,14 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
DATA_LISTENER = "listener"
CONF_CONTROLLERS = "controllers"
CONF_PROGRAM_ID = "program_id"
CONF_SECONDS = "seconds"
CONF_ZONE_ID = "zone_id"
CONF_ZONE_RUN_TIME = "zone_run_time"
DEFAULT_ATTRIBUTION = "Data provided by Green Electronics LLC"
DEFAULT_ICON = "mdi:water"
DEFAULT_SCAN_INTERVAL = timedelta(seconds=60)
DEFAULT_SSL = True
DEFAULT_ZONE_RUN = 60 * 10
SERVICE_ALTER_PROGRAM = vol.Schema({vol.Required(CONF_PROGRAM_ID): cv.positive_int})
@@ -109,7 +107,6 @@ async def async_setup(hass, config):
"""Set up the RainMachine component."""
hass.data[DOMAIN] = {}
hass.data[DOMAIN][DATA_CLIENT] = {}
hass.data[DOMAIN][DATA_LISTENER] = {}
if DOMAIN not in config:
return True
@@ -143,7 +140,7 @@ async def async_setup_entry(hass, config_entry):
config_entry.data[CONF_IP_ADDRESS],
config_entry.data[CONF_PASSWORD],
port=config_entry.data[CONF_PORT],
ssl=config_entry.data[CONF_SSL],
ssl=config_entry.data.get(CONF_SSL, DEFAULT_SSL),
)
except RainMachineError as err:
_LOGGER.error("An error occurred: %s", err)
@@ -156,8 +153,10 @@ async def async_setup_entry(hass, config_entry):
rainmachine = RainMachine(
hass,
controller,
config_entry.data[CONF_ZONE_RUN_TIME],
config_entry.data[CONF_SCAN_INTERVAL],
config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN),
config_entry.data.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL.total_seconds()
),
)
# Update the data object, which at this point (prior to any sensors registering
@@ -260,9 +259,6 @@ async def async_unload_entry(hass, config_entry):
"""Unload an OpenUV config entry."""
hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
remove_listener = hass.data[DOMAIN][DATA_LISTENER].pop(config_entry.entry_id)
remove_listener()
tasks = [
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in ("binary_sensor", "sensor", "switch")

View File

@@ -4,10 +4,22 @@ from regenmaschine.errors import RainMachineError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT
from homeassistant.const import (
CONF_IP_ADDRESS,
CONF_PASSWORD,
CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_SSL,
)
from homeassistant.helpers import aiohttp_client
from .const import DEFAULT_PORT, DOMAIN # pylint: disable=unused-import
from .const import ( # pylint: disable=unused-import
CONF_ZONE_RUN_TIME,
DEFAULT_PORT,
DEFAULT_SCAN_INTERVAL,
DEFAULT_ZONE_RUN,
DOMAIN,
)
class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@@ -53,8 +65,8 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
user_input[CONF_IP_ADDRESS],
user_input[CONF_PASSWORD],
websession,
port=user_input.get(CONF_PORT, DEFAULT_PORT),
ssl=True,
port=user_input[CONF_PORT],
ssl=user_input.get(CONF_SSL, True),
)
except RainMachineError:
return await self._show_form({CONF_PASSWORD: "invalid_credentials"})
@@ -63,5 +75,17 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# access token without using the IP address and password, so we have to
# store it:
return self.async_create_entry(
title=user_input[CONF_IP_ADDRESS], data=user_input
title=user_input[CONF_IP_ADDRESS],
data={
CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS],
CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_PORT: user_input[CONF_PORT],
CONF_SSL: user_input.get(CONF_SSL, True),
CONF_SCAN_INTERVAL: user_input.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL.total_seconds()
),
CONF_ZONE_RUN_TIME: user_input.get(
CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN
),
},
)

View File

@@ -1,6 +1,10 @@
"""Define constants for the SimpliSafe component."""
from datetime import timedelta
DOMAIN = "rainmachine"
CONF_ZONE_RUN_TIME = "zone_run_time"
DATA_CLIENT = "client"
DATA_PROGRAMS = "programs"
DATA_PROVISION_SETTINGS = "provision.settings"
@@ -10,6 +14,8 @@ DATA_ZONES = "zones"
DATA_ZONES_DETAILS = "zones_details"
DEFAULT_PORT = 8080
DEFAULT_SCAN_INTERVAL = timedelta(seconds=60)
DEFAULT_ZONE_RUN = 60 * 10
PROGRAM_UPDATE_TOPIC = f"{DOMAIN}_program_update"
SENSOR_UPDATE_TOPIC = f"{DOMAIN}_data_update"

View File

@@ -342,7 +342,6 @@ class Recorder(threading.Thread):
# has changed. This reduces the disk io.
while True:
event = self.queue.get()
if event is None:
self._close_run()
self._close_connection()
@@ -356,7 +355,7 @@ class Recorder(threading.Thread):
self.queue.task_done()
if self.commit_interval:
self._timechanges_seen += 1
if self.commit_interval >= self._timechanges_seen:
if self._timechanges_seen >= self.commit_interval:
self._timechanges_seen = 0
self._commit_event_session_or_retry()
continue
@@ -376,6 +375,9 @@ class Recorder(threading.Thread):
self.event_session.flush()
except (TypeError, ValueError):
_LOGGER.warning("Event is not JSON serializable: %s", event)
except Exception as err: # pylint: disable=broad-except
# Must catch the exception to prevent the loop from collapsing
_LOGGER.exception("Error adding event: %s", err)
if dbevent and event.event_type == EVENT_STATE_CHANGED:
try:
@@ -387,6 +389,9 @@ class Recorder(threading.Thread):
"State is not JSON serializable: %s",
event.data.get("new_state"),
)
except Exception as err: # pylint: disable=broad-except
# Must catch the exception to prevent the loop from collapsing
_LOGGER.exception("Error adding state change: %s", err)
# If they do not have a commit interval
# than we commit right away
@@ -404,17 +409,26 @@ class Recorder(threading.Thread):
try:
self._commit_event_session()
return
except exc.OperationalError as err:
_LOGGER.error(
"Error in database connectivity: %s. " "(retrying in %s seconds)",
err,
self.db_retry_wait,
)
except (exc.InternalError, exc.OperationalError) as err:
if err.connection_invalidated:
_LOGGER.error(
"Database connection invalidated: %s. "
"(retrying in %s seconds)",
err,
self.db_retry_wait,
)
else:
_LOGGER.error(
"Error in database connectivity: %s. "
"(retrying in %s seconds)",
err,
self.db_retry_wait,
)
tries += 1
except exc.SQLAlchemyError:
_LOGGER.exception("Error saving events")
except Exception as err: # pylint: disable=broad-except
# Must catch the exception to prevent the loop from collapsing
_LOGGER.exception("Error saving events: %s", err)
return
_LOGGER.error(
@@ -423,10 +437,15 @@ class Recorder(threading.Thread):
)
try:
self.event_session.close()
except exc.SQLAlchemyError:
_LOGGER.exception("Failed to close event session.")
except Exception as err: # pylint: disable=broad-except
# Must catch the exception to prevent the loop from collapsing
_LOGGER.exception("Error while closing event session: %s", err)
self.event_session = self.get_session()
try:
self.event_session = self.get_session()
except Exception as err: # pylint: disable=broad-except
# Must catch the exception to prevent the loop from collapsing
_LOGGER.exception("Error while creating new event session: %s", err)
def _commit_event_session(self):
try:

View File

@@ -3,6 +3,6 @@
"name": "Remote",
"documentation": "https://www.home-assistant.io/integrations/remote",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": []
}

View File

@@ -46,6 +46,7 @@ class SamsungTVBridge(ABC):
self.method = method
self.host = host
self.token = None
self.default_port = None
self._remote = None
self._callback = None
@@ -130,10 +131,11 @@ class SamsungTVLegacyBridge(SamsungTVBridge):
super().__init__(method, host, None)
self.config = {
CONF_NAME: VALUE_CONF_NAME,
CONF_ID: VALUE_CONF_ID,
CONF_DESCRIPTION: VALUE_CONF_NAME,
CONF_METHOD: method,
CONF_ID: VALUE_CONF_ID,
CONF_HOST: host,
CONF_METHOD: method,
CONF_PORT: None,
CONF_TIMEOUT: 1,
}
@@ -190,6 +192,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
"""Initialize Bridge."""
super().__init__(method, host, port)
self.token = token
self.default_port = 8001
def try_connect(self):
"""Try to connect to the Websocket TV."""
@@ -203,6 +206,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
CONF_TIMEOUT: 31,
}
result = None
try:
LOGGER.debug("Try config: %s", config)
with SamsungTVWS(
@@ -220,9 +224,13 @@ class SamsungTVWSBridge(SamsungTVBridge):
return RESULT_SUCCESS
except WebSocketException:
LOGGER.debug("Working but unsupported config: %s", config)
return RESULT_NOT_SUPPORTED
result = RESULT_NOT_SUPPORTED
except (OSError, ConnectionFailure) as err:
LOGGER.debug("Failing config: %s, error: %s", config, err)
# pylint: disable=useless-else-on-loop
else:
if result:
return result
return RESULT_NOT_SUCCESSFUL

View File

@@ -71,13 +71,27 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
):
turn_on_action = hass.data[DOMAIN][ip_address][CONF_ON_ACTION]
on_script = Script(hass, turn_on_action)
async_add_entities([SamsungTVDevice(config_entry, on_script)])
# Initialize bridge
data = config_entry.data.copy()
bridge = SamsungTVBridge.get_bridge(
data[CONF_METHOD], data[CONF_HOST], data[CONF_PORT], data.get(CONF_TOKEN),
)
if bridge.port is None and bridge.default_port is not None:
# For backward compat, set default port for websocket tv
data[CONF_PORT] = bridge.default_port
hass.config_entries.async_update_entry(config_entry, data=data)
bridge = SamsungTVBridge.get_bridge(
data[CONF_METHOD], data[CONF_HOST], data[CONF_PORT], data.get(CONF_TOKEN),
)
async_add_entities([SamsungTVDevice(bridge, config_entry, on_script)])
class SamsungTVDevice(MediaPlayerDevice):
"""Representation of a Samsung TV."""
def __init__(self, config_entry, on_script):
def __init__(self, bridge, config_entry, on_script):
"""Initialize the Samsung device."""
self._config_entry = config_entry
self._manufacturer = config_entry.data.get(CONF_MANUFACTURER)
@@ -93,13 +107,7 @@ class SamsungTVDevice(MediaPlayerDevice):
# Mark the end of a shutdown command (need to wait 15 seconds before
# sending the next command to avoid turning the TV back ON).
self._end_of_power_off = None
# Initialize bridge
self._bridge = SamsungTVBridge.get_bridge(
config_entry.data[CONF_METHOD],
config_entry.data[CONF_HOST],
config_entry.data[CONF_PORT],
config_entry.data.get(CONF_TOKEN),
)
self._bridge = bridge
self._bridge.register_reauth_callback(self.access_denied)
def access_denied(self):

View File

@@ -3,7 +3,7 @@
"name": "Scripts",
"documentation": "https://www.home-assistant.io/integrations/script",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal"
}

View File

@@ -3,11 +3,11 @@
"name": "Sense",
"documentation": "https://www.home-assistant.io/integrations/sense",
"requirements": [
"sense_energy==0.7.0"
"sense_energy==0.7.1"
],
"dependencies": [],
"codeowners": [
"@kbickar"
],
"config_flow": true
}
}

View File

@@ -3,6 +3,7 @@
"name": "Sighthound",
"documentation": "https://www.home-assistant.io/integrations/sighthound",
"requirements": [
"pillow==7.0.0",
"simplehound==0.3"
],
"dependencies": [],

View File

@@ -3,7 +3,7 @@
"name": "SimpliSafe",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
"requirements": ["simplisafe-python==9.0.2"],
"requirements": ["simplisafe-python==9.0.4"],
"dependencies": [],
"codeowners": ["@bachya"]
}

View File

@@ -27,7 +27,7 @@ DOMAIN = "somfy"
CONF_CLIENT_ID = "client_id"
CONF_CLIENT_SECRET = "client_secret"
CONF_OPTIMISTIC = "optimisitic"
CONF_OPTIMISTIC = "optimistic"
SOMFY_AUTH_CALLBACK_PATH = "/auth/somfy/callback"
SOMFY_AUTH_START = "/auth/somfy"
@@ -36,8 +36,8 @@ CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Inclusive(CONF_CLIENT_ID, "oauth"): cv.string,
vol.Inclusive(CONF_CLIENT_SECRET, "oauth"): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
}
)
@@ -51,23 +51,21 @@ SOMFY_COMPONENTS = ["cover", "switch"]
async def async_setup(hass, config):
"""Set up the Somfy component."""
hass.data[DOMAIN] = {}
domain_config = config.get(DOMAIN, {})
hass.data[DOMAIN][CONF_OPTIMISTIC] = domain_config.get(CONF_OPTIMISTIC, False)
if DOMAIN not in config:
return True
hass.data[DOMAIN][CONF_OPTIMISTIC] = config[DOMAIN][CONF_OPTIMISTIC]
config_flow.SomfyFlowHandler.async_register_implementation(
hass,
config_entry_oauth2_flow.LocalOAuth2Implementation(
if CONF_CLIENT_ID in domain_config:
config_flow.SomfyFlowHandler.async_register_implementation(
hass,
DOMAIN,
config[DOMAIN][CONF_CLIENT_ID],
config[DOMAIN][CONF_CLIENT_SECRET],
"https://accounts.somfy.com/oauth/oauth/v2/auth",
"https://accounts.somfy.com/oauth/oauth/v2/token",
),
)
config_entry_oauth2_flow.LocalOAuth2Implementation(
hass,
DOMAIN,
config[DOMAIN][CONF_CLIENT_ID],
config[DOMAIN][CONF_CLIENT_SECRET],
"https://accounts.somfy.com/oauth/oauth/v2/auth",
"https://accounts.somfy.com/oauth/oauth/v2/token",
),
)
return True

View File

@@ -93,6 +93,8 @@ ATTR_NIGHT_SOUND = "night_sound"
ATTR_SPEECH_ENHANCE = "speech_enhance"
ATTR_QUEUE_POSITION = "queue_position"
UNAVAILABLE_VALUES = {"", "NOT_IMPLEMENTED", None}
class SonosData:
"""Storage class for platform global data."""
@@ -330,7 +332,7 @@ def soco_coordinator(funct):
def _timespan_secs(timespan):
"""Parse a time-span into number of seconds."""
if timespan in ("", "NOT_IMPLEMENTED", None):
if timespan in UNAVAILABLE_VALUES:
return None
return sum(60 ** x[0] * int(x[1]) for x in enumerate(reversed(timespan.split(":"))))
@@ -427,7 +429,11 @@ class SonosEntity(MediaPlayerDevice):
@soco_coordinator
def state(self):
"""Return the state of the entity."""
if self._status in ("PAUSED_PLAYBACK", "STOPPED"):
if self._status in ("PAUSED_PLAYBACK", "STOPPED",):
# Sonos can consider itself "paused" but without having media loaded
# (happens if playing Spotify and via Spotify app you pick another device to play on)
if self._media_title is None:
return STATE_IDLE
return STATE_PAUSED
if self._status in ("PLAYING", "TRANSITIONING"):
return STATE_PLAYING
@@ -511,16 +517,14 @@ class SonosEntity(MediaPlayerDevice):
def _radio_artwork(self, url):
"""Return the private URL with artwork for a radio stream."""
if url not in ("", "NOT_IMPLEMENTED", None):
if url.find("tts_proxy") > 0:
# If the content is a tts don't try to fetch an image from it.
return None
url = "http://{host}:{port}/getaa?s=1&u={uri}".format(
host=self.soco.ip_address,
port=1400,
uri=urllib.parse.quote(url, safe=""),
)
return url
if url in UNAVAILABLE_VALUES:
return None
if url.find("tts_proxy") > 0:
# If the content is a tts don't try to fetch an image from it.
return None
return f"http://{self.soco.ip_address}:1400/getaa?s=1&u={urllib.parse.quote(url, safe='')}"
def _attach_player(self):
"""Get basic information and add event subscriptions."""
@@ -606,9 +610,9 @@ class SonosEntity(MediaPlayerDevice):
self._media_image_url = None
self._media_artist = source
self._media_artist = None
self._media_album_name = None
self._media_title = None
self._media_title = source
self._source_name = source
@@ -640,7 +644,7 @@ class SonosEntity(MediaPlayerDevice):
# For radio streams we set the radio station name as the title.
current_uri_metadata = media_info["CurrentURIMetaData"]
if current_uri_metadata not in ("", "NOT_IMPLEMENTED", None):
if current_uri_metadata not in UNAVAILABLE_VALUES:
# currently soco does not have an API for this
current_uri_metadata = pysonos.xml.XML.fromstring(
pysonos.utils.really_utf8(current_uri_metadata)
@@ -650,7 +654,7 @@ class SonosEntity(MediaPlayerDevice):
".//{http://purl.org/dc/elements/1.1/}title"
)
if md_title not in ("", "NOT_IMPLEMENTED", None):
if md_title not in UNAVAILABLE_VALUES:
self._media_title = md_title
if self._media_artist and self._media_title:
@@ -867,25 +871,25 @@ class SonosEntity(MediaPlayerDevice):
@soco_coordinator
def media_artist(self):
"""Artist of current playing media, music track only."""
return self._media_artist
return self._media_artist or None
@property
@soco_coordinator
def media_album_name(self):
"""Album name of current playing media, music track only."""
return self._media_album_name
return self._media_album_name or None
@property
@soco_coordinator
def media_title(self):
"""Title of current playing media."""
return self._media_title
return self._media_title or None
@property
@soco_coordinator
def source(self):
"""Name of the current input source."""
return self._source_name
return self._source_name or None
@property
@soco_coordinator

View File

@@ -3,7 +3,7 @@
"name": "Switch",
"documentation": "https://www.home-assistant.io/integrations/switch",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": [],
"quality_scale": "internal"
}

View File

@@ -91,7 +91,7 @@ class LogEntry:
def __init__(self, record, stack, source):
"""Initialize a log entry."""
self.first_occured = self.timestamp = record.created
self.first_occurred = self.timestamp = record.created
self.name = record.name
self.level = record.levelname
self.message = deque([record.getMessage()], maxlen=5)
@@ -117,7 +117,7 @@ class LogEntry:
"timestamp": self.timestamp,
"exception": self.exception,
"count": self.count,
"first_occured": self.first_occured,
"first_occurred": self.first_occurred,
}

View File

@@ -1,6 +1,7 @@
"""Ask tankerkoenig.de for petrol price information."""
from datetime import timedelta
import logging
from math import ceil
import pytankerkoenig
import voluptuous as vol
@@ -164,27 +165,41 @@ class TankerkoenigData:
)
return False
self.add_station(additional_station_data["station"])
if len(self.stations) > 10:
_LOGGER.warning(
"Found more than 10 stations to check. "
"This might invalidate your api-key on the long run. "
"Try using a smaller radius"
)
return True
async def fetch_data(self):
"""Get the latest data from tankerkoenig.de."""
_LOGGER.debug("Fetching new data from tankerkoenig.de")
station_ids = list(self.stations)
data = await self._hass.async_add_executor_job(
pytankerkoenig.getPriceList, self._api_key, station_ids
)
if data["ok"]:
prices = {}
# The API seems to only return at most 10 results, so split the list in chunks of 10
# and merge it together.
for index in range(ceil(len(station_ids) / 10)):
data = await self._hass.async_add_executor_job(
pytankerkoenig.getPriceList,
self._api_key,
station_ids[index * 10 : (index + 1) * 10],
)
_LOGGER.debug("Received data: %s", data)
if not data["ok"]:
_LOGGER.error(
"Error fetching data from tankerkoenig.de: %s", data["message"]
)
raise TankerkoenigError(data["message"])
if "prices" not in data:
_LOGGER.error("Did not receive price information from tankerkoenig.de")
raise TankerkoenigError("No prices in data")
else:
_LOGGER.error(
"Error fetching data from tankerkoenig.de: %s", data["message"]
)
raise TankerkoenigError(data["message"])
return data["prices"]
prices.update(data["prices"])
return prices
def add_station(self, station: dict):
"""Add fuel station to the entity list."""

View File

@@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tesla",
"requirements": [
"teslajsonpy==0.4.0"
"teslajsonpy==0.5.1"
],
"dependencies": [],
"codeowners": [

View File

@@ -5,7 +5,7 @@
"client_secret": "The client secret from the configuration is invalid.",
"no_agreements": "This account has no Toon displays.",
"no_app": "You need to configure Toon before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/toon/).",
"unknown_auth_fail": "Unexpected error occured, while authenticating."
"unknown_auth_fail": "Unexpected error occurred, while authenticating."
},
"error": {
"credentials": "The provided credentials are invalid.",

View File

@@ -26,7 +26,7 @@
"abort": {
"client_id": "The client ID from the configuration is invalid.",
"client_secret": "The client secret from the configuration is invalid.",
"unknown_auth_fail": "Unexpected error occured, while authenticating.",
"unknown_auth_fail": "Unexpected error occurred, while authenticating.",
"no_agreements": "This account has no Toon displays.",
"no_app": "You need to configure Toon before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/toon/)."
}

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
STATE_ALARM_TRIGGERED,
)
from . import DOMAIN as TOTALCONNECT_DOMAIN
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -30,7 +30,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
alarms = []
client = hass.data[TOTALCONNECT_DOMAIN].client
client = hass.data[DOMAIN].client
for location_id, location in client.locations.items():
location_name = location.location_name
@@ -71,7 +71,7 @@ class TotalConnectAlarm(alarm.AlarmControlPanel):
def update(self):
"""Return the state of the device."""
status = self._client.get_armed_status(self._location_id)
self._client.get_armed_status(self._location_id)
attr = {
"location_name": self._name,
"location_id": self._location_id,
@@ -79,47 +79,36 @@ class TotalConnectAlarm(alarm.AlarmControlPanel):
"low_battery": self._client.locations[self._location_id].low_battery,
"cover_tampered": self._client.locations[
self._location_id
].is_cover_tampered,
].is_cover_tampered(),
"triggered_source": None,
"triggered_zone": None,
}
if status in (self._client.DISARMED, self._client.DISARMED_BYPASS):
if self._client.locations[self._location_id].is_disarmed():
state = STATE_ALARM_DISARMED
elif status in (
self._client.ARMED_STAY,
self._client.ARMED_STAY_INSTANT,
self._client.ARMED_STAY_INSTANT_BYPASS,
):
elif self._client.locations[self._location_id].is_armed_home():
state = STATE_ALARM_ARMED_HOME
elif status == self._client.ARMED_STAY_NIGHT:
elif self._client.locations[self._location_id].is_armed_night():
state = STATE_ALARM_ARMED_NIGHT
elif status in (
self._client.ARMED_AWAY,
self._client.ARMED_AWAY_BYPASS,
self._client.ARMED_AWAY_INSTANT,
self._client.ARMED_AWAY_INSTANT_BYPASS,
):
elif self._client.locations[self._location_id].is_armed_away():
state = STATE_ALARM_ARMED_AWAY
elif status == self._client.ARMED_CUSTOM_BYPASS:
elif self._client.locations[self._location_id].is_armed_custom_bypass():
state = STATE_ALARM_ARMED_CUSTOM_BYPASS
elif status == self._client.ARMING:
elif self._client.locations[self._location_id].is_arming():
state = STATE_ALARM_ARMING
elif status == self._client.DISARMING:
elif self._client.locations[self._location_id].is_disarming():
state = STATE_ALARM_DISARMING
elif status == self._client.ALARMING:
elif self._client.locations[self._location_id].is_triggered_police():
state = STATE_ALARM_TRIGGERED
attr["triggered_source"] = "Police/Medical"
elif status == self._client.ALARMING_FIRE_SMOKE:
elif self._client.locations[self._location_id].is_triggered_fire():
state = STATE_ALARM_TRIGGERED
attr["triggered_source"] = "Fire/Smoke"
elif status == self._client.ALARMING_CARBON_MONOXIDE:
elif self._client.locations[self._location_id].is_triggered_gas():
state = STATE_ALARM_TRIGGERED
attr["triggered_source"] = "Carbon Monoxide"
else:
logging.info(
"Total Connect Client returned unknown status code: %s", status
)
logging.info("Total Connect Client returned unknown status")
state = None
self._state = state

View File

@@ -0,0 +1,3 @@
"""TotalConnect constants."""
DOMAIN = "totalconnect"

View File

@@ -2,7 +2,7 @@
"domain": "totalconnect",
"name": "Honeywell Total Connect Alarm",
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
"requirements": ["total_connect_client==0.53"],
"requirements": ["total_connect_client==0.54.1"],
"dependencies": [],
"codeowners": ["@austinmroczek"]
}

View File

@@ -83,6 +83,8 @@ async def async_setup_entry(hass, config_entry):
controller_id = get_controller_id_from_config_entry(config_entry)
hass.data[DOMAIN][controller_id] = controller
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller.shutdown)
if controller.mac is None:
return True
@@ -96,8 +98,6 @@ async def async_setup_entry(hass, config_entry):
# sw_version=config.raw['swversion'],
)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller.shutdown)
return True

View File

@@ -142,16 +142,28 @@ class Device:
async def async_get_total_bytes_received(self):
"""Get total bytes received."""
return await self._igd_device.async_get_total_bytes_received()
try:
return await self._igd_device.async_get_total_bytes_received()
except asyncio.TimeoutError:
_LOGGER.warning("Timeout during get_total_bytes_received")
async def async_get_total_bytes_sent(self):
"""Get total bytes sent."""
return await self._igd_device.async_get_total_bytes_sent()
try:
return await self._igd_device.async_get_total_bytes_sent()
except asyncio.TimeoutError:
_LOGGER.warning("Timeout during get_total_bytes_sent")
async def async_get_total_packets_received(self):
"""Get total packets received."""
return await self._igd_device.async_get_total_packets_received()
try:
return await self._igd_device.async_get_total_packets_received()
except asyncio.TimeoutError:
_LOGGER.warning("Timeout during get_total_packets_received")
async def async_get_total_packets_sent(self):
"""Get total packets sent."""
return await self._igd_device.async_get_total_packets_sent()
try:
return await self._igd_device.async_get_total_packets_sent()
except asyncio.TimeoutError:
_LOGGER.warning("Timeout during get_total_packets_sent")

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/upnp",
"requirements": ["async-upnp-client==0.14.12"],
"dependencies": [],
"codeowners": ["@robbiet480"]
"codeowners": ["@StevenLooman"]
}

View File

@@ -1,4 +1,5 @@
"""Support for UPnP/IGD Sensors."""
from datetime import timedelta
import logging
from homeassistant.const import DATA_BYTES, DATA_KIBIBYTES, TIME_SECONDS
@@ -7,6 +8,7 @@ from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import Throttle
import homeassistant.util.dt as dt_util
from .const import DOMAIN as DOMAIN_UPNP, SIGNAL_REMOVE_SENSOR
@@ -29,6 +31,8 @@ IN = "received"
OUT = "sent"
KIBIBYTE = 1024
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
async def async_setup_platform(
hass: HomeAssistantType, config, async_add_entities, discovery_info=None
@@ -142,6 +146,7 @@ class RawUPnPIGDSensor(UpnpSensor):
"""Return the unit of measurement of this entity, if any."""
return self._type["unit"]
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Get the latest information from the IGD."""
if self._type_name == BYTES_RECEIVED:

View File

@@ -3,6 +3,6 @@
"name": "Vacuum",
"documentation": "https://www.home-assistant.io/integrations/vacuum",
"requirements": [],
"dependencies": ["group"],
"dependencies": [],
"codeowners": []
}

View File

@@ -64,7 +64,7 @@ class VelbusLight(VelbusEntity, Light):
@property
def brightness(self):
"""Return the brightness of the light."""
return self._module.get_dimmer_state(self._channel)
return int((self._module.get_dimmer_state(self._channel) * 255) / 100)
def turn_on(self, **kwargs):
"""Instruct the Velbus light to turn on."""
@@ -80,10 +80,15 @@ class VelbusLight(VelbusEntity, Light):
attr, *args = "set_led_state", self._channel, "on"
else:
if ATTR_BRIGHTNESS in kwargs:
# Make sure a low but non-zero value is not rounded down to zero
if kwargs[ATTR_BRIGHTNESS] == 0:
brightness = 0
else:
brightness = max(int((kwargs[ATTR_BRIGHTNESS] * 100) / 255), 1)
attr, *args = (
"set_dimmer_state",
self._channel,
kwargs[ATTR_BRIGHTNESS],
brightness,
kwargs.get(ATTR_TRANSITION, 0),
)
else:

View File

@@ -2,7 +2,7 @@
"domain": "velbus",
"name": "Velbus",
"documentation": "https://www.home-assistant.io/integrations/velbus",
"requirements": ["python-velbus==2.0.42"],
"requirements": ["python-velbus==2.0.43"],
"config_flow": true,
"dependencies": [],
"codeowners": ["@Cereal2nd", "@brefra"]

View File

@@ -1,4 +1,5 @@
"""Viessmann ViCare climate device."""
from datetime import timedelta
import logging
import requests
@@ -79,6 +80,9 @@ HA_TO_VICARE_PRESET_HEATING = {
PYVICARE_ERROR = "error"
# Scan interval of 15 minutes seems to be safe to not hit the ViCare server rate limit
SCAN_INTERVAL = timedelta(seconds=900)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Create the ViCare climate devices."""

View File

@@ -1,4 +1,5 @@
"""Viessmann ViCare water_heater device."""
from datetime import timedelta
import logging
import requests
@@ -42,6 +43,9 @@ HA_TO_VICARE_HVAC_DHW = {
PYVICARE_ERROR = "error"
# Scan interval of 15 minutes seems to be safe to not hit the ViCare server rate limit
SCAN_INTERVAL = timedelta(seconds=900)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Create the ViCare water_heater devices."""

View File

@@ -190,6 +190,7 @@ class VizioDevice(MediaPlayerDevice):
self._is_muted = None
self._current_input = None
self._available_inputs = None
self._current_app = None
self._available_apps = None
return
@@ -304,7 +305,7 @@ class VizioDevice(MediaPlayerDevice):
@property
def source(self) -> str:
"""Return current input of the device."""
if self._current_input in INPUT_APPS:
if self._current_app is not None and self._current_input in INPUT_APPS:
return self._current_app
return self._current_input

View File

@@ -8,6 +8,8 @@ import voluptuous as vol
from homeassistant import config_entries, const as ha_const
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.typing import HomeAssistantType
from . import api
from .core import ZHAGateway
@@ -27,6 +29,7 @@ from .core.const import (
DEFAULT_BAUDRATE,
DEFAULT_RADIO_TYPE,
DOMAIN,
SIGNAL_ADD_ENTITIES,
RadioType,
)
@@ -89,24 +92,11 @@ async def async_setup_entry(hass, config_entry):
Will automatically load components to support devices found on the network.
"""
hass.data[DATA_ZHA] = hass.data.get(DATA_ZHA, {})
hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS] = []
hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED] = asyncio.Event()
platforms = []
zha_data = hass.data.setdefault(DATA_ZHA, {})
config = zha_data.get(DATA_ZHA_CONFIG, {})
for component in COMPONENTS:
platforms.append(
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component)
)
)
async def _platforms_loaded():
await asyncio.gather(*platforms)
hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED].set()
hass.async_create_task(_platforms_loaded())
config = hass.data[DATA_ZHA].get(DATA_ZHA_CONFIG, {})
zha_data.setdefault(component, [])
if config.get(CONF_ENABLE_QUIRKS, True):
# needs to be done here so that the ZHA module is finished loading
@@ -116,6 +106,12 @@ async def async_setup_entry(hass, config_entry):
zha_gateway = ZHAGateway(hass, config, config_entry)
await zha_gateway.async_initialize()
zha_data[DATA_ZHA_DISPATCHERS] = []
zha_data[DATA_ZHA_PLATFORM_LOADED] = []
for component in COMPONENTS:
coro = hass.config_entries.async_forward_entry_setup(config_entry, component)
zha_data[DATA_ZHA_PLATFORM_LOADED].append(hass.async_create_task(coro))
device_registry = await hass.helpers.device_registry.async_get_registry()
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
@@ -130,11 +126,11 @@ async def async_setup_entry(hass, config_entry):
async def async_zha_shutdown(event):
"""Handle shutdown tasks."""
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].shutdown()
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].async_update_device_storage()
await zha_data[DATA_ZHA_GATEWAY].shutdown()
await zha_data[DATA_ZHA_GATEWAY].async_update_device_storage()
hass.bus.async_listen_once(ha_const.EVENT_HOMEASSISTANT_STOP, async_zha_shutdown)
hass.async_create_task(zha_gateway.async_load_devices())
asyncio.create_task(async_load_entities(hass, config_entry))
return True
@@ -152,3 +148,16 @@ async def async_unload_entry(hass, config_entry):
await hass.config_entries.async_forward_entry_unload(config_entry, component)
return True
async def async_load_entities(
hass: HomeAssistantType, config_entry: config_entries.ConfigEntry
) -> None:
"""Load entities after integration was setup."""
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].async_prepare_entities()
to_setup = hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED]
results = await asyncio.gather(*to_setup, return_exceptions=True)
for res in results:
if isinstance(res, Exception):
_LOGGER.warning("Couldn't setup zha platform: %s", res)
async_dispatcher_send(hass, SIGNAL_ADD_ENTITIES)

View File

@@ -49,7 +49,7 @@ STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Zigbee Home Automation binary sensor from config entry."""
entities_to_create = hass.data[DATA_ZHA][DOMAIN] = []
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
unsub = async_dispatcher_connect(
hass,
@@ -64,6 +64,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class BinarySensor(ZhaEntity, BinarySensorDevice):
"""ZHA BinarySensor."""
SENSOR_ATTR = None
DEVICE_CLASS = None
def __init__(self, unique_id, zha_device, channels, **kwargs):
@@ -105,6 +106,8 @@ class BinarySensor(ZhaEntity, BinarySensorDevice):
@callback
def async_set_state(self, attr_id, attr_name, value):
"""Set the state."""
if self.SENSOR_ATTR is None or self.SENSOR_ATTR != attr_name:
return
self._state = bool(value)
self.async_write_ha_state()
@@ -121,6 +124,7 @@ class BinarySensor(ZhaEntity, BinarySensorDevice):
class Accelerometer(BinarySensor):
"""ZHA BinarySensor."""
SENSOR_ATTR = "acceleration"
DEVICE_CLASS = DEVICE_CLASS_MOVING
@@ -128,6 +132,7 @@ class Accelerometer(BinarySensor):
class Occupancy(BinarySensor):
"""ZHA BinarySensor."""
SENSOR_ATTR = "occupancy"
DEVICE_CLASS = DEVICE_CLASS_OCCUPANCY
@@ -135,6 +140,7 @@ class Occupancy(BinarySensor):
class Opening(BinarySensor):
"""ZHA BinarySensor."""
SENSOR_ATTR = "on_off"
DEVICE_CLASS = DEVICE_CLASS_OPENING
@@ -142,6 +148,8 @@ class Opening(BinarySensor):
class IASZone(BinarySensor):
"""ZHA IAS BinarySensor."""
SENSOR_ATTR = "zone_status"
async def get_device_class(self) -> None:
"""Get the HA device class from the channel."""
zone_type = await self._channel.get_attribute_value("zone_type")

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