Compare commits

...

249 Commits

Author SHA1 Message Date
Paulus Schoutsen
e049b35413 Merge pull request #20354 from home-assistant/rc
0.86.0
2019-01-23 12:49:23 -08:00
Paulus Schoutsen
cdcc535ae1 Version bump to 0.86.0 2019-01-23 10:48:55 -08:00
Paulus Schoutsen
4662ab215c Fix invalid entity ID in entity registry (#20328) 2019-01-23 10:48:15 -08:00
rolfberkenbosch
80aa2075c6 Update locationsharinglib to version 3.0.11 (#20322)
* Update google_maps.py

There are known bug in locationsharinglib version 3.0.9, the developer has fixed this in 3.0.11. (https://github.com/costastf/locationsharinglib/issues/42)

* Update requirements_all.txt
2019-01-23 10:48:14 -08:00
Sebastian Muszynski
4b7d944a74 Fix xiaomi speed attribute name clash (#20312) 2019-01-23 10:48:14 -08:00
Richard Mitchell
b7218e6a1d Should require the 'GATTOOL' setup extras which includes pexpect. (#20263)
* Should require the 'GATTOOL' setup extras which includes pexpect.

* Also fix skybeacon's requirement and requirements_all.
2019-01-23 10:48:13 -08:00
Paulus Schoutsen
6d0ac30687 Bumped version to 0.86.0b3 2019-01-21 21:23:00 -08:00
Fabien Piuzzi
0ceace96e7 Bugfix: prevent error notification when octoprint server auto detected but no configuration present. (#20303) 2019-01-21 21:22:20 -08:00
Johann Kellerman
ec7f2657cd Config Validator: schema_with_slug_keys (#20298)
* schema_with_slug_keys

* Update config_validation.py

* Update config_validation.py
2019-01-21 21:21:00 -08:00
Paulus Schoutsen
5945929e7e Updated frontend to 20190121.1 2019-01-21 21:20:23 -08:00
Paulus Schoutsen
9667c8057f Bumped version to 0.86.0b2 2019-01-21 09:45:42 -08:00
Johann Kellerman
028cc8d24f Align valid_entity_id with new slugify (#20231)
* slug

* ensure a dot

* fix

* schema_with_slug_keys

* lint

* test
2019-01-21 09:45:37 -08:00
Paulus Schoutsen
df47a8c58c Updated frontend to 20190121.0 2019-01-21 09:38:16 -08:00
Paulus Schoutsen
657544a381 Clean up build artifacts correctly 2019-01-20 17:31:22 -08:00
Paulus Schoutsen
143eb4e8f4 Bumped version to 0.86.0b1 2019-01-20 17:02:44 -08:00
Anders Melchiorsen
41d2321756 Fix 'all' entity_id in service call extraction (#20281) 2019-01-20 17:02:36 -08:00
Anders Melchiorsen
f02e887fcc Allow 'all' entity_id in service schema (#20278) 2019-01-20 17:02:36 -08:00
Anders Melchiorsen
97e8e20bcc Remove double logging of automation action (#20264) 2019-01-20 17:02:36 -08:00
Otto Winter
33ed113211 Bump aioesphomeapi to 1.4.2 (#20247)
* Bump aioesphomeapi to 1.4.2

* Update requirements_all.txt
2019-01-20 17:02:35 -08:00
Phil Bruckner
ba2b28cd4d Handle non-string values in JSON renderer (#20233)
Handle the case of async_render_with_possible_json_value's value argument
being something other than a string. This can happen, e.g., when using the
SQL sensor to extract a datetime column such as last_changed and also using
its value_template to convert that datetime to another format. This was
causing a TypeError from json.loads, but async_render_with_possible_json_value
was only catching ValueError's.
2019-01-20 17:02:35 -08:00
Anders Melchiorsen
d843bf9c58 Improve Sonos discovery (#20196) 2019-01-20 17:02:34 -08:00
Paulus Schoutsen
84d6453a97 Add command to refresh auth (#20183) 2019-01-20 17:02:34 -08:00
Paulus Schoutsen
1f54edfbc4 Distribute reconnect (#20181) 2019-01-20 17:02:33 -08:00
Paulus Schoutsen
27be95e597 Sensibo to use HA operation modes (#20180) 2019-01-20 17:02:33 -08:00
Paulus Schoutsen
d2dec44b18 Updated frontend to 20190120.0 2019-01-20 16:22:57 -08:00
Paulus Schoutsen
722d285904 Bumped version to 0.86.0b0 2019-01-16 16:27:15 -08:00
Paulus Schoutsen
bc8aa73448 Updated frontend to 20190116.0 2019-01-16 16:14:55 -08:00
damarco
b557157ea1 Add support for deconz radios to zha component (#20167)
* Add support for deconz radios

* Update check_zigpy_connection()
2019-01-16 16:09:09 -08:00
Sebastian Muszynski
85404783d6 Add Xiaomi Airpurifier Pro V7 support (#20093)
* Add Xiaomi Air Purifier Pro V7 support

* Reorder the model list

* Improve the list of supported attributes/properties

* Fix lint
2019-01-16 16:08:41 -08:00
damarco
06440bf076 Bump pynuki to 1.3.2 (#20173) 2019-01-16 16:07:40 -08:00
Paulus Schoutsen
84a2e5d8fb Strip login username in backend (#20150)
* Add modern mode to HA auth provider that strips usernames

* Add tests for async_get_or_create_credentials

* Fix test
2019-01-16 15:03:05 -08:00
Fabian Affolter
9bb7e40ee3 Upgrade aiohttp to 3.5.3 (#19957)
* Upgrade aiohttp to 3.5.3

* Upgrade aiohttp to 3.5.4

* Remove test for webhook component from camera.push

* Lint
2019-01-16 14:23:46 -08:00
emontnemery
368682647d Log exceptions thrown by MQTT message callbacks (#19977)
* Log exceptions thrown by MQTT message callbacks

* Fix tests

* Correct method for skipping wrapper in traceback

* Lint

* Simplify traceback print

* Add test

* Move wrapper to common helper function

* Typing

* Lint
2019-01-16 13:50:21 -08:00
Paulus Schoutsen
1d86905d5b Switch locative to use the webhook component (#20043)
* Switch locative to use the webhook component

* Lint

* Remove dead test code

* Use voluptuous to validate the webhook schema

* Validate test mode schema as well

* Lint

* Remove allow_extra

* Return web.Response correctly

* #20043: Remove superfluous dict in WEBHOOK_SCHEMA validation
2019-01-16 11:08:42 -08:00
David F. Mulcahey
8748ace244 Make imports relative in ZHA component (#20020)
* make imports relative

* remove cyclic import
2019-01-16 11:06:22 -08:00
cvwillegen
5e73846bcc imap_email_content: allow configuring folder to read. (#20160)
* Update imap_email_content.py

Add configuration for selection of IMAP folder to track

* Update imap_email_content.py
2019-01-16 10:59:54 -08:00
Rohan Kapoor
b5bfc759ec Migrate gpslogger to the automatically generated webhook (#20079)
* Migrate gpslogger to the automatically generated webhook

* Lint

* Lint and return error code
2019-01-16 10:56:25 -08:00
Nikolay Vasilchuk
075b575bde Support device_class for rest sensor (#20132)
* Ready

* Tests fixed
2019-01-16 10:03:53 -08:00
Paulus Schoutsen
29c6584fe2 Enable setting alarm mode night for arlo platform (#20143)
* Enable setting alarm mode night for arlo platform

* Missed one small comment
2019-01-16 10:02:30 -08:00
Rohan Kapoor
48127cade0 Embed mailgun platform into component (#20147)
* Embed mailgun platform into component

* #20147: Update .coveragerc

* #20147 update requirements.txt
2019-01-16 10:01:08 -08:00
cvwillegen
c8efbb2cdc Fix link to documentation link, select read-only (#20155)
- Fixed link to documentation page at the top of the sensor
- Select the IMAP4 Inbox read-only so that reading e-mail does not mark messages as Read
2019-01-16 09:56:53 -08:00
Pascal Vizeli
9d112dc3f0 Remove .isort because we use the config from setup.cfg (#20158) 2019-01-16 09:54:11 -08:00
Rohan Kapoor
11c78d5de8 Embed geofency platform into component (#20083)
* Embded geofency platform into component

* #20083: Change docstring for geofency device tracker platform
2019-01-16 10:33:25 +01:00
Rohan Kapoor
bc30491dc0 Add support for connecting to multiple zoneminder instances (#19955)
* Add support for connecting to multiple zoneminder instances

* Lint

* Lint

* Clean up and better error handling

* Fix config validation

* Lint
2019-01-16 10:15:13 +01:00
Tim Gerla
fe93ea9bdf Use the correct Unicode degree symbol (#20058)
The previous symbol used for degrees was U+00BA, the "Masculine Ordinal Indicator". This patch changes the symbol to U+00B0, "Degree Sign", to match the rest of the Home Assistant system.
2019-01-16 09:08:23 +01:00
Paulus Schoutsen
75fa9b2fba Fix TTS say config validation (#20145) 2019-01-16 09:07:32 +01:00
ehendrix23
78da6828f0 Reconnect and device name fix for harmony platform (#20108)
* Update requirements

Updated requirements

* Add attributes

Add firmware and config version attributes

* Small bump for aioharmony

Small version bump increase for aioharmony

* Fix requirements file

For some reason aioharmony ended up in there as a duplicate. Fixed it.

* Fix for send command with named device

* Update requirements to get reconnect fix

* Set aioharmony version to 0.1.4

Version 0.1.4 has additional small fixes.

* Keep trying to connect on startup

Keep trying to connect to the HUB on startup

* Revert rebase changes

Revert some changes that should have been reverted back as part of rebase.

* PlatformNotReady if unable to connect on startup

Will call PlatformNotReady if unable to connect to Hub on startup

* Increase aioharmony requirement to 0.1.5

Increase aioharmony requirement to 0.1.5

* Register callbacks when entity added to HASS

Register the callbacks only once the entity has been added to HASS instead of during setup of platform.

* Removed debug log in __init__

Removed debug log in __init__
2019-01-16 08:35:29 +01:00
Robert Svensson
19e19009cc Make all deCONZ platforms use a common base (#20137)
* Make all deconz platforms use a common base

* Fix one cover to device replacement too much...
2019-01-16 08:33:04 +01:00
Rohan Kapoor
1e784b4d7a #20043: Remove superfluous dict in WEBHOOK_SCHEMA validation 2019-01-15 22:26:42 -08:00
Anders Melchiorsen
c218757336 Accept both domains and entities in influxdb include (#19927)
* Accept both domains and entities in influxdb include

* Explicit test

* Remove lint
2019-01-15 16:20:51 -08:00
Fabian Affolter
25f6302813 Switch to ipapi.co (fixes #19846) (#19886)
* Switch to ipapi.co (fixes #19846)

* Fix name

* Update name
2019-01-15 16:18:57 -08:00
Alexei Chetroi
4b3d4b275e Zha light.turn_on service fixes. (#20085)
Set color only if light supports color mode.
Set color temp only light supports color temp.
Update entity's brightness only if Zigbee command to set the brightness
was sent successfuly.
2019-01-15 16:12:22 -08:00
Rohan Kapoor
f36755e447 Switch geofency tests to using an unauthenticated HTTP client (#20080) 2019-01-15 16:11:30 -08:00
Paulus Schoutsen
9fd21d20ae Fix loading translations for embedded platforms (#20122)
* Fix loading translations for embedded platforms

* Update doc string

* Lint
2019-01-15 16:06:04 -08:00
Paulus Schoutsen
cd6679eb5b Updated frontend to 20190115.0 2019-01-15 15:35:18 -08:00
Tommy Jonsson
1b79872dd6 Add notify.html5_dismiss service (#19912)
* Add notify.html5_dismiss service

* fix test

* add can_dismiss

* fix service data payload

* fix hasattr -> getattr

* fixes

* move dismiss service to html5

* fix services.yaml

* fix line to long
2019-01-15 15:31:57 -08:00
Morgan Kesler
732743aeb5 Missed one small comment 2019-01-15 17:27:56 -05:00
emontnemery
0ec1401be7 Minor refactoring of MQTT availability (#20136)
* Small refactor of MQTT availability

* Use dict[key] for required config keys and keys with default values.
2019-01-15 14:26:37 -08:00
Morgan Kesler
cc166bf6a7 Enable setting alarm mode night for arlo platform 2019-01-15 17:19:04 -05:00
starkillerOG
11602c1da0 Improve Philips Hue color conversion 2 (#20118)
* Add gamut capability to color util

* Include gamut in hue_test

* Improve Philips Hue color conversion

* correct import for new location hue.light

* include file changes between PR's

* update aiohue version

* update aiohue version

* update aiohue version

* fix hue_test

Now Idea why it failed compared to the previous time

* Include gamut in hue_test

* fix hue_test

* Try to test hue gamut conversion

supply a color that is well outside the color gamut of the light, and see if the response is correctly converted to within the reach of the light.

* switch from gamut A to gamut B for the tests.

* remove white space in blanck line

* Fix gamut hue test

* Add Gamut tests for the util.color

* fix hue gamut test

* fix hue gamut test

* Improve Philips Hue color conversion
2019-01-15 11:30:50 -08:00
Robert Svensson
a3f0d55737 Change deCONZ to embedded platforms (#20113)
Move all platforms into components/deconz
2019-01-15 19:29:56 +01:00
Adam Mills
336b6adc88 Split time_pattern triggers from time trigger (#19825)
* Split interval triggers from time trigger

* Default smaller interval units to zero

* Rename interval to schedule

* Rename schedule to time_pattern
2019-01-15 09:33:34 -08:00
emontnemery
5b53bd6aa0 Move MQTT platforms under the component (#20050)
* Move MQTT platforms under the component
2019-01-15 17:31:06 +01:00
Eliseo Martelli
5fd1053a38 fixed gtt to report isotime (#20128) 2019-01-15 13:39:43 +01:00
Rohan Kapoor
80bc42af4f Use voluptuous to perform validation for the geofency webhook (#20067)
* Use voluptuous to perform validation for the geofency webhook

* Add missing attribute to schema

* Lint
2019-01-15 12:50:09 +01:00
Fredrik Erlandsson
c8d885fb78 Fix tellduslive discovery and auth issues (#20023)
* fix for #19954, discovered tellsticks shows up to be configured

* fix for #19954, authentication issues

* updated tests

* move I/O to executer thread pool

* Apply suggestions from code review

Co-Authored-By: fredrike <fredrik.e@gmail.com>
2019-01-15 06:36:17 +01:00
Andrew Sayre
e73569c203 Added partial detection to async_add_job (#20119) 2019-01-14 15:08:44 -08:00
emontnemery
0f3b6f1739 Reconfigure MQTT lock component if discovery info is changed (#19468)
* Reconfigure MQTT lock component if discovery info is changed

* Use dict[key] for required config keys and keys with default config schema values.
2019-01-14 21:01:42 +01:00
Aaron Bach
af2949f85f Embed OpenUV platforms into the component (#20072)
* Embed OpenUV platforms into the component

* Updated CODEOWNERS

* Updated .coveragerc
2019-01-14 11:44:00 -07:00
Aaron Bach
b3886820b4 Embed SimpliSafe platforms into the component (#20069)
* Embed SimpliSafe platforms into the component

* Updated CODEOWNERS

* Updated .coveragerc
2019-01-14 11:42:48 -07:00
Aaron Bach
d717d9f6be Embed RainMachine platforms into the component (#20066)
* Embed RainMachine platforms into the component

* Updated CODEOWNERS

* Updated .coveragerc
2019-01-14 11:42:21 -07:00
Otto Winter
f225570980 Move ESPHome Source Files (#20092)
* Move ESPHome source files

* Update .coveragerc

* Update CODEOWNERS
2019-01-14 09:00:48 -07:00
Morten Lüneborg
e505a9b7b4 Fix ihc issues caused by update to defusedxml (#20091)
* Update __init__.py

* Update __init__.py
2019-01-14 13:12:57 +01:00
Aaron Bach
ef79566864 Adjust OpenUV integration for upcoming API limit changes (#19949)
* Adjust OpenUV integration for upcoming API limit changes

* Added fix for "Invalid API Key"

* Bugfix

* Add initial nighttime check

* Move from polling to a service-based model

* Fixed test

* Removed unnecessary scan interval

* Fixed test

* Moving test imports

* Member comments

* Hound

* Removed unused import
2019-01-14 13:12:06 +01:00
Alok Saboo
fff3cb0b46 Change return text code for alarm control panels (#20055)
* Change return text code for alarmdotcom

* Change return text code for ialarm

* Change return text code for IFTTT

* Change return text code for manual alarm panel

* Change return text code for manual MQTT alarm

* Change return text code for MQTT

* Change return text code for Simplisafe
2019-01-14 13:02:30 +01:00
shred86
5652a4a58b Bump abode to 0.15.0 (#20064)
Fix for motion sensor states
2019-01-14 13:01:59 +01:00
Thom Troy
cb9e0c03d5 fix logic error in dubln bus (#20075) 2019-01-14 10:51:37 +01:00
Paulus Schoutsen
d6d28dd3e9 Lowercase code format (#20077) 2019-01-14 10:49:38 +01:00
Ville Skyttä
eb610e6093 Upgrade pytest to 4.1.1 (#20088) 2019-01-14 10:05:24 +02:00
Spencer Oberstadt
7db28d3d91 Add Roku hub and remote (#17548)
* add roku remote component

* remove name config (for now)

* update coveragerc and requirements_all

* fix linting errors

* remove extra requirements entry

* fix flake8 errors

* remove some references to apple tv

* remove redundant REQUIREMENTS

* Update requirements_all.txt

* Pass hass_config to load_platform

* don't expose registry constant

* remove unnecessary registry list

* use await instead of add_job

* use ensure_list

* fix code style

* some review fixes

* code style fixes

* stop using async

* use add with update

* fix whitespace

* remove I/O from init loop

* move import
2019-01-14 08:44:30 +01:00
Rohan Kapoor
452d7cfd61 Return web.Response correctly 2019-01-13 17:07:45 -08:00
Rohan Kapoor
7f3871028d Split out gpslogger into a separate component and platform (#20044)
* Split out gpslogger into a separate component and platform

* Lint

* Lint

* Increase test coverage
2019-01-14 01:09:47 +01:00
Rohan Kapoor
e476949c3e Remove allow_extra 2019-01-13 13:35:13 -08:00
Rohan Kapoor
9036aafc81 Lint 2019-01-13 13:26:51 -08:00
Teemu R
2a2318b7f6 warning -> debug, this should not have been visible to users (#20061) 2019-01-13 21:31:08 +01:00
Rohan Kapoor
b75356d532 Validate test mode schema as well 2019-01-13 12:12:04 -08:00
Rohan Kapoor
0f92d061c4 Use voluptuous to validate the webhook schema 2019-01-13 11:49:20 -08:00
Austin Drummond
3b83a64f7c Add support for HomeKit Controller covers (#19866)
* Added support for HomeKit Controller covers

* removed copied code

* more linting fixes

* added device type to service info

* added checks for value in characteristics

* added state stopped parsing

* removed logger

* removed unused args

* fixed inits

* removed unused imports

* fixed lint issues

* fixed lint issues

* remove state_unknown

* remove validation of kwargs in homekit controller covers

* guarantee tilt position is not none before setting
2019-01-13 19:09:47 +01:00
Matt Snyder
db87842335 Show persistent notification on Doorbird schedule failure (#20033)
* Remove unnecessary return.
Add persistent notification on failure to configure doorbird schedule.

* Update doorbirdpy to 2.0.5

* Fix bare except

* Bump version again

* Lint

* Return false
2019-01-13 18:47:50 +01:00
Paulus Schoutsen
798f630029 Updated frontend to 20190113.0 2019-01-13 09:38:35 -08:00
Paulus Schoutsen
96b8c517f0 Update translations 2019-01-13 09:38:22 -08:00
Anders Melchiorsen
4af4b2d10e Fix remote.harmony_change_channel services.yaml indentation (#20051) 2019-01-13 16:39:50 +01:00
Otto Winter
2339cb05ad Fix errors in ESPHome integration (#20048)
* Fix Home Assistant State Import

* Fix cover state

* Fix fan supported features

* Fix typo
2019-01-13 15:52:23 +01:00
carstenschroeder
aae6ff830a ADS service: Enable use of templates for value (#20024) 2019-01-13 14:23:22 +01:00
Alok Saboo
1c11394f5f Change alarm panel code format (#20037)
* Change code format

* Update elkm1 code format

* Update alarmdecodes code_format

* Update alarmdotcom code_format

* Update concord232 code_format

* Update envisalink code_format

* Update ialarm code_format

* Update ifttt code_format

* Update manual alarm code_format

* Update manual mqtt code_format

* Update mqtt code_format

* Update ness code_format

* Update nx584 code_format

* Update satel_integra code_format

* Update simplisafe code_format

* Update verisure code_format

* Change text to be consistent with the Polymer PR
2019-01-13 11:59:12 +01:00
Ville Skyttä
162e2b8385 Upgrade pytest to 4.1.0 (#20013) 2019-01-13 08:56:26 +01:00
Caleb Dunn
e295ca7b8e update to pyunifi 2.16 (#20042)
* update to pyunifi 2.16

* update requirements to version 2.16
2019-01-13 08:56:05 +01:00
Rohan Kapoor
3e325a4ef9 Remove dead test code 2019-01-12 20:24:55 -08:00
Rohan Kapoor
0007f35f96 Lint 2019-01-12 19:23:19 -08:00
Rohan Kapoor
4e020b90e1 Switch locative to use the webhook component 2019-01-12 19:18:33 -08:00
ehendrix23
04636e9ba7 Add harmony service to remote services.yaml (#20031)
Added the harmony_change_channel service to services.yaml for remote component.
2019-01-12 20:15:27 +01:00
Sean Dague
218c82eaf3 mychevy: Fix wrong attribute on battery level selector (#20016)
The battery level sensor is broken because the logic for determining
if the battery is charged accessed the wrong variable. This one
character fix makes it work again.
2019-01-12 13:51:01 -05:00
ehendrix23
22c0733d8e Add service change_channel to Harmony component (#19649)
* Update requirements

Updated requirements

* Add attributes

Add firmware and config version attributes

* Small bump for aioharmony

Small version bump increase for aioharmony

* Order ATTR constants

* Add the service

Add service change_channel

* Fix requirements file

For some reason aioharmony ended up in there as a duplicate. Fixed it.

* Updates based on review
2019-01-12 19:02:00 +01:00
Robert Svensson
d3f2854c89 UniFi - Fix issue with POE switch reset switch config (#20021)
* Fix issue when controlling POE would reset configuration for all other ports on same device
2019-01-12 17:59:44 +01:00
carstenschroeder
eabc7b22cd Enable bool type for ADS service (#20011) 2019-01-12 16:36:50 +01:00
David F. Mulcahey
012e91f9b1 version bump for zha-quirks (#20019) 2019-01-12 15:21:30 +01:00
Jonathan Keljo
6395087a40 Upgrade greeneye_monitor to 1.0 (#19631)
* Upgrade greeneye_monitor to 1.0

This is a breaking change; it causes the `serial_number` field in
configuration to be treated as the full 8-digit serial number rather
than the last 5 digits as was previously done, which results in the unique
identifiers for the sensors being different. (Fixing them up in
`config/.storage/core.entity_registry` before rebooting into the updated
version seems to prevent any weirdness.)

The last-5-digits behavior was a result of me misunderstanding the packet
format docs and not realizing that the true serial number was split across
two fields. In addition to being confusing (see
https://community.home-assistant.io/t/brultech-greeneye-issues/86852), it
was technically incorrect. The `greeneye_monitor` platform was just introduced
in 0.82, so it seems like the kind of thing that's best to fix now while
adoption is relatively low rather than later when somebody runs into it
as more than just a point of confusion.

* Switch to 8-character string

* Coerce to int

* Remove now-unnecessary cast
2019-01-11 20:54:22 -08:00
Matt Snyder
b3580f46b9 Update doorbird events to include URLs on event_data (#19262)
* Update doorbird events to include URLs on event_data as shown in documentation.

(cherry picked from commit 2405bc96fe)

* Format timestamp

* Update timestamp

* Lint
2019-01-11 20:45:03 -08:00
Tommy Jonsson
3bdee57066 Support for html5 notifications to suggest their names (#19965)
* support for devices to suggest their names

* houndci fixes

* Lint
2019-01-11 20:44:29 -08:00
Thomas Delaet
2208563de4 catch TypeError's in addition to ValueError's for unifi direct device tracker (#19994)
* catch TypeError's in addition to ValueError's in response from unifi access point

sometimes unifi's access point returns incomplete json which results in a TypeError because ssid_table is None

* fix syntax error
2019-01-11 20:44:16 -08:00
Sriram Vaidyanathan
418fa226e6 'latest_dir' referenced before assignment (#19952)
* 'latest_dir' referenced before assignment

local variable 'latest_dir' referenced before assignment

* Better fix
2019-01-11 20:44:01 -08:00
David F. Mulcahey
ba21608042 Repackage ZHA component (#19989)
* move files

* rename files

* rename files

* move files

* relative import

* update coveragerc
2019-01-11 20:34:48 -08:00
Austin Drummond
7676b3fbe8 Add support for HomeKit Controller Locks (#19867)
* Added HomeKit Controller Lock

* cleaned up code according to standards

* fixed lint issues

* added private constant for jammed state

* removed state_unknown
2019-01-12 03:48:28 +01:00
Alexei Chetroi
5ab3c7b765 Don't set friendly_name in Zha entity. (#19991)
Use @property name instead of setting friendly_name device state attr.
2019-01-11 21:41:27 -05:00
Paulus Schoutsen
6cba51fd0e Lint 2019-01-11 16:31:16 -08:00
Louis-Etienne
fe148606b8 Wink: Update pubnubsub-handler version to make it compatible with python 3.7 (#19625) 2019-01-11 16:30:31 -08:00
Ville Skyttä
574669bd20 Upgrade pytest-cov to 2.6.1 (#19988) 2019-01-11 16:24:59 -08:00
Ville Skyttä
83c5dc67f7 Upgrade huawei-lte-api to 1.1.3 (#19987) 2019-01-11 16:24:31 -08:00
Paulus Schoutsen
2ffadde0a3 Add Hass.io user headers to supervisor proxy (#19395)
* Add Hass.io user headers to supervisor proxy

* Update test_http.py

* Fix tests

* Update test_auth.py
2019-01-11 15:30:40 -08:00
pbalogh77
7dac7b9e5e Support for multiple Fibaro gateways (#19705)
* Preparing for transition to config flow

Added multiple gateway support
Reworked parameter flow to platforms to enable multiple controllers
Breaking change to config, now a list of gateways is expected instead of a single config

* Updated coveragerc

Added new location of fibaro component

* Fixes based on code review and extended logging

Addressed issues raised by code review
Added extended debug logging to get better reports from users if the device type mapping is not perfect

* Changhes based on code review

Changes to how configuration is read and schemas
Fix to device type mapping logic

* simplified reading config

* oops

oops

* grr

grr

* change based on code review

* changes based on code review

changes based on code review
2019-01-11 15:29:54 -08:00
Rohan Kapoor
d820efc4e3 Split locative to a separate component (#19964)
* Split locative to a separate component

* Switch tests to use constants for http codes

* Fix tests
2019-01-11 15:14:11 -08:00
Paulus Schoutsen
8755389c49 Bumped version to 0.86.0.dev0 2019-01-11 15:10:01 -08:00
Paulus Schoutsen
11647f9fab Merge remote-tracking branch 'origin/master' into dev 2019-01-11 15:09:48 -08:00
Paulus Schoutsen
578bfe9798 Fix fail2ban tests 2019-01-11 15:01:24 -08:00
Bas
8c27bf8c7c Expose more information about shipments by PostNL (#18334)
* Expose more information about shipments by PostNL

* Update postnl.py
2019-01-11 14:59:31 -08:00
Andrew Chatham
ab4e1fddd5 Fix the anthemav component by removing a debugging line. (#19979)
This lane ended up calling vars(transport) on an asyncio Transport
object. In a standard setup, that's a python object provided by syncio,
and it works. Home Assistant injects uvloop into asyncio, which makes this
a Python C object, and those don't support vars().
2019-01-11 23:34:32 +01:00
Fredrik Erlandsson
b9a488912a Add support for 'via_hub' for device_info (#19454)
* Add support for 'via_hub'

* Update config schema

* add domain to via_hub

* add tests for via_hub
2019-01-11 13:11:13 -08:00
Matt Snyder
199db7219e Add ability to monitor relay events (#18730)
* Add ability to monitor relay events

* Account for empty events array instead of none.
2019-01-11 13:07:12 -08:00
Martin Hjelmare
937688f7a6 Add mysensors state update delay (#18891)
* Add mysensors state update delay

* Schedule state update after delay to avoid updating state multiple
  times during the same short timespan.

* Code review
2019-01-11 13:06:06 -08:00
Oliver
25408bd483 Include Scripts/ directory to .gitignore - this is created by virtualenv on Windows (#18918) 2019-01-11 13:04:56 -08:00
Mike Miller
a65d14c0cd Always use datetime and timedelta in camera.proxy instead of int/float (#19571) 2019-01-11 12:58:14 -08:00
Paulus Schoutsen
12d16d9bdc Update test_auth.py 2019-01-11 12:55:23 -08:00
Antoine GRÉA
14dd8791ec Adding IPv6 to fail2ban sensor (#19457)
* Fixing fail2ban regex for ipv6

* Adding IPv6 tests for fail2ban

* Formating code for hound

* Formating again

* Formating again 2
2019-01-11 12:54:47 -08:00
Paulus Schoutsen
8ef2f1f67b Fix warning (#19946)
* Fix warning

* Update service.py
2019-01-11 11:58:14 -08:00
Paulus Schoutsen
99c2e4ac44 Fix warning (#19946)
* Fix warning

* Update service.py
2019-01-11 11:52:07 -08:00
Paulus Schoutsen
7dbbea2238 Updated frontend to 20190109.1 2019-01-11 11:48:32 -08:00
David F. Mulcahey
7be015fcc6 Add services and helper functions to support a config panel for ZHA (#19664)
* reconfigure zha device service

add log line to reconfigure service for consistency

* add entity functions to support new services

* added new services and web socket api and split them into their own module

* support manufacturer code

logging to debug

get safe value for manufacturer

* update services.yaml

* add comma back

* update coveragerc

* remove blank line

* fix type

* api cleanup - review comments

* move static method to helpers - review comment

* convert reconfigure service to websocket command - review comment

* change path

* fix attribute
2019-01-11 11:34:29 -08:00
Paulus Schoutsen
a8f22287ca Allow embedded platforms (#19948)
* Allow embedded platforms

* Fix test
2019-01-11 11:30:22 -08:00
Jarle B. Hjortand
b3a08d5876 When tradfri experience communication errors make the lights/devices unavailable. (#19288) 2019-01-11 10:55:55 -08:00
Sebastian Muszynski
e2f55a959f Support next generation of the Xiaomi Mi Smart Plug (chuangmi.plug.m3) (#19972)
* Support next generation of the Xiaomi Mi Smart Plug (chuangmi.plug.m3)

* Fix indent
2019-01-11 19:44:55 +01:00
Paulus Schoutsen
b81260e912 Fix tests 2019-01-11 10:10:36 -08:00
Paulus Schoutsen
7be197b845 Update test_http.py 2019-01-11 10:00:38 -08:00
Paulus Schoutsen
fd21d6cc9d Add Hass.io user headers to supervisor proxy 2019-01-11 10:00:38 -08:00
Paulus Schoutsen
3f65a03024 Merge pull request #19959 from home-assistant/awarecan-trusted-network-auth-must-have-user
Trusted Network Auth: only authenticate request when owner can be found
2019-01-11 09:53:35 -08:00
Paulus Schoutsen
82c6d3d8c2 Add support for spot cleaning that was introduced in pybotvac 0.12 (#19857)
* Added support for spot cleaning that was introduced in pybotvac 0.12.

* Corrected formating.

* Added missing operator.
2019-01-11 09:52:06 -08:00
Eliseo Martelli
71eaef8da4 add service type in name (#19980) 2019-01-11 09:51:41 -08:00
Paulus Schoutsen
d812f23f6b min_max sensor support for STATE_UNAVAILABLE (#19914) 2019-01-11 09:27:48 -08:00
Sören Oldag
17dce6697f Add support for restoring state to rpi_gpio_pwm (#19944) 2019-01-11 09:25:14 -08:00
Fabian Affolter
49cfebd903 Upgrade keyring to 17.1.1 (#19962) 2019-01-11 10:48:07 -05:00
Fabian Affolter
2228f2ef66 Upgrade pysnmp to 4.4.8 (#19961) 2019-01-11 10:47:53 -05:00
Fabian Affolter
734d8c52e9 Upgrade ruamel.yaml to 0.15.85 (#19960) 2019-01-11 07:44:11 -08:00
Fabian Affolter
caf0751be8 Upgrade pillow to 5.4.1 (#19958) 2019-01-11 12:37:15 +01:00
Fabian Affolter
7b81727c69 Upgrade mutagen to 1.42.0 (#19956) 2019-01-11 10:38:39 +01:00
Jason Hu
97394df0b9 Only authenticate request when owner can be found 2019-01-11 00:26:25 -08:00
Tyler Page
c3e9bd1444 Change state() to try/except to catch KeyError (#19935)
* Change state() to try/except to catch KeyError

When Tautulli is up but Plex is down, the API doesn't return a 'stream_count' key. This causes calls to state() to raise KeyError exceptions. The new code includes a try/except to catch the KeyError and return -1 signifying that the Tautulli API cannot talk to Plex

* Update tautulli.py
2019-01-11 08:48:36 +01:00
mindigmarton
31d92683f7 Add emulated_roku component (#17596)
* Add emulated_roku component

* Add emulated_roku config tests

* Fix emulated_roku test dependencies

* Remove emulated_roku yaml support, add tests

* Add yaml support, simplify config flow

* Improve emulated_roku code quality

* Fix emulated_roku translation, improve code quality

* Fix emulated_roku translation

* Bump emulated_roku to 0.1.6 to fix SSDP discovery

* Bump emulated roku to 0.1.7, refactor component start/stop methods
2019-01-11 03:20:35 +01:00
Thomas Hervé
cee51ecb2b Remove spurious libzwave error (#19928)
If a network_key is not configuired, the following error is logged:
TypeError: expected bytes, NoneType found
Exception ignored in: 'libopenzwave.str_to_cppstr'
TypeError: expected bytes, NoneType found

We don't need to set the key if it's None, let's skip in that case.
2019-01-10 17:45:50 -08:00
Paulus Schoutsen
646aaab936 Fix botvac connected alert retrieval (#19937) 2019-01-10 17:43:50 -08:00
Fabian Affolter
4c1eeb9e96 Upgrade pylast to 3.0.0 (#19938) 2019-01-10 17:43:31 -08:00
Abílio Costa
ca460ace5d Small refactoring for the alexa component (#19782)
* small refactoring

* fix tests
2019-01-10 17:39:49 -08:00
Kevin Fronczak
2be0d1b096 Upgrade blinkpy and use calibrated temperature for sensor (#19723) 2019-01-10 17:24:35 -08:00
Sergey Rymsha
47f64b472d Add nad telnet (#19704)
* fix cla-bot

* fix bug introduced after linter complaint

* merge two components into one
support telnet port configuration

* remove obsolete nadtcp component. nad component must be used instead.

* back to correct nad_receiver version
2019-01-10 17:21:57 -08:00
SNoof85
96d20a64d5 add_entities -> async_add_entities (#19943)
Better state handling at HA startup
2019-01-10 17:13:29 -08:00
arigilder
4d187e08d4 Add sensors to jewish_calendar for upcoming Shabbat times (#19278)
* Initial pass of cleanup for shabbat_times

* Switch to async defs

* First pass of unit tests + fixture data

* Completion of first round of unit tests, 100% passing

* Unit tests for state restoring

* Style fixes

* More style fixes

* Lint fix

* Add upcoming candelighting and havdalah sensors

* Add unit tests, remove havdalah offset

* More unit tests + small bugfix for weekly_portion

* Add issur melacha sensor

* Remove old shabbat_times work-in-progress files

* Bump required version of hdate

* Add havdalah offset config parameter

* Bump hdate version required

* Pin hdate requirement

* Lint fixes

* Changes based on review + API changes for hdate 0.8.7

* Add three-day holiday unit tests

* Remove debugging line

* Add missing docstring

* Fix doc lint comment
2019-01-10 16:27:34 -08:00
so3n
4d52adb008 Remove Discovery dependency from konnected.py (#19910) 2019-01-10 15:53:12 -08:00
Abílio Costa
6c29315088 Add Alexa's EndpointHealth reporting (#19784)
* add Health reports

* add health report for all devices

* update tests

* Update homeassistant/components/alexa/smart_home.py

Co-Authored-By: abmantis <abmantis@users.noreply.github.com>

* lint

* add tests
2019-01-10 15:52:21 -08:00
Daniel Shokouhi
6403a13ea3 Fix botvac connected alert retrieval 2019-01-10 13:56:10 -08:00
Fabian Affolter
de76b59d0b Upgrade youtube_dl to 2019.01.10 2019-01-10 22:50:39 +01:00
Clifford W. Hansen
616f23ae1d Add btle_name attribute to devices (#19915)
* Update googlehome.py

Added name from bluetooth device to attributes

* Update homeassistant/components/device_tracker/googlehome.py

Check if key exists before assigning name

Co-Authored-By: cliffordwhansen <clifford@nighthawk.co.za>
2019-01-10 21:55:17 +01:00
Alexei Chetroi
d859c3fa86 Don't map LevelControl to light for single cluster devices. (#19929) 2019-01-10 14:43:24 -05:00
Richard Mitchell
e753ffca94 Correctly map Nest hvac_state to Home Assistant states. (#19895) 2019-01-10 09:28:01 -08:00
Roy Duineveld
c44f5d31ef Plant monitor defaults (#19891)
* Plant monitor defaults

* houndci-bot fixes
2019-01-10 15:49:13 +01:00
Jérôme Wiedemann
e6a2c18430 min_max sensor support for STATE_UNAVAILABLE 2019-01-10 13:48:24 +01:00
Artem Tokarev
8b49ecbe7d Removed mkdir, If the WORKDIR doesn’t exist, it will be created. (#19892) 2019-01-10 07:18:59 +01:00
Aaron Bach
01eee52990 Bump pyflunearyou to 1.0.1 (#19899) 2019-01-09 19:38:19 -07:00
Paulus Schoutsen
9aed40a88d Update translations 2019-01-09 15:29:31 -08:00
Paulus Schoutsen
5cab319798 Updated frontend to 20190109.0 2019-01-09 15:12:29 -08:00
Rendili
4394e37df9 Bug fix with getting a device battery level when API unavailable for Hive (#19841)
* hive updates - bug fix and add entity registration

* remove hive entity registration code
2019-01-09 11:21:03 +01:00
Paulus Schoutsen
64b4c8f43a Fix deprecation warning (#19882) 2019-01-08 21:09:47 -08:00
Paulus Schoutsen
a3d05328ec Refactor motion sensor of the xiaomi_aqara platform (#19805)
* Refactor motion sensor

* Improve debug output
2019-01-08 20:48:09 -08:00
Rohan Kapoor
2bdbf6955d Migrate geofency over to the Webhook component (#18951)
* Migrate geofency over to the Webhook component

* Return web.Response correctly

* Fix test

* Lint

* Fix error that tests caught
2019-01-08 20:47:05 -08:00
Florian Ludwig
23382ab199 assign user to websocket connection when using legacy_api_password (#19797) 2019-01-08 20:45:24 -08:00
Rendili
6d9fda04ac add entity support to hive (#19879) 2019-01-08 20:15:12 -08:00
Pascal Vizeli
b4c657a39c Update OZW to 0.1.2 (#19878)
* Update ozw 0.1.2

* Update requirements_all.txt
2019-01-08 20:14:27 -08:00
Alistair Galbraith
35cb0458fa Resolves #17196, Resolves #18739 - Hue Beyond light fixture errors (#19874)
* Resolves #17196, Resolves #18739 - Hue Beyond light fixtures being incorrectly recognized

* Removed long code lines that were failing code review

* Removed trailing whitespace
2019-01-08 20:13:47 -08:00
Malte Franken
6d3343e4d1 Geo Location -> Geolocation (class names and unit test comments) (#19877)
* fixed geolocation naming in class names

* fixed geolocation naming in comments in unit test
2019-01-08 20:11:51 -08:00
Thomas Lovén
f73bda1218 Allow other icon prefixes than mdi: (#19872) 2019-01-08 20:08:20 -08:00
Pierre
c29bffc8d8 Replace influxdb query by another query that is more lightweight (#19880)
same as #6289
2019-01-08 23:31:39 +00:00
Steven Looman
cc6e70a270 Fix error when trying to log used UPnP device, if multiple found (#19875) 2019-01-08 21:05:36 +00:00
Lars Lydersen
42821b5f64 Added missing operator. 2019-01-08 21:12:35 +01:00
Lars Lydersen
c164533404 Corrected formating. 2019-01-08 20:36:57 +01:00
David F. Mulcahey
acdf9c7ce2 Relay events for onoff and levelcontrol output clusters in ZHA (#19863)
* auto relay events for onoff and levelcontrol output clusters

* fix docstring

* correct copy/paste failure - review comment

* add space - review comment
2019-01-08 17:20:50 +01:00
emontnemery
0cea54cea1 Cleanup if discovered mqtt climate can't be added (#19739)
* Cleanup if discovered mqtt climate can't be added
2019-01-08 16:53:02 +01:00
emontnemery
203701bc7c Cleanup if discovered mqtt fan can't be added (#19741)
* Cleanup if discovered mqtt fan can't be added
2019-01-08 16:51:03 +01:00
emontnemery
44f6151548 Cleanup if discovered mqtt alarm can't be added (#19742)
* Cleanup if discovered mqtt alarm can't be added
2019-01-08 16:49:47 +01:00
emontnemery
1a5fe3d880 Cleanup if discovered mqtt cover can't be added (#19743)
* Cleanup if discovered mqtt cover can't be added
2019-01-08 16:48:42 +01:00
emontnemery
a62e514d8f Merge pull request #19744 from emontnemery/mqtt_discovery_cleanup_binary_sensor
Cleanup if discovered mqtt binary_sensor can't be added
2019-01-08 16:47:36 +01:00
emontnemery
f0f386e314 Cleanup if discovered mqtt sensor can't be added (#19745)
* Cleanup if discovered mqtt sensor can't be added

* No bare except

* Clear ALREADY_DISCOVERED list with helper
2019-01-08 16:46:26 +01:00
emontnemery
bb37cf906c Cleanup if discovered mqtt lock can't be added (#19746)
* Cleanup if discovered mqtt lock can't be added
2019-01-08 16:45:38 +01:00
Fabian Affolter
406b45c6e7 Upgrade bcrypt to 3.1.5 (#19854) 2019-01-08 09:22:45 -05:00
kennedyshead
377b129c9c Make asuswrt sensor optional (#19736) @kennedyshead
* Dont load if not in config

* Adding config options for sensors

* Fixed mistake with iterating over wrong things

* lint

* lint

* Setting None state

* Using .get when fetching optional config
2019-01-08 09:14:16 -05:00
Rene Nulsch
410f19c777 Replace MyChevy persistant_notification with error log entry (#19804)
The mychevy service is notoriously unreliable, often only having 50% uptime. 
Previously a persistent notification was emitted when the platform errored out. 
Users have found that is happening too often, so instead log an error when
this happens instead.
2019-01-08 08:06:08 -05:00
Malte Franken
4bbfc04f5e Geo Location -> Geolocation (comments and default group name) (#19865)
* fixed geolocation naming in comments

* fixed geolocation naming in default group name

* fixed link to documentation (after https://github.com/home-assistant/home-assistant.io/pull/8086)
2019-01-08 11:24:57 +01:00
kennedyshead
c7a32e59b7 Fix state and attribute fetching in vasttrafik (#19856)
* Fixing state and attribute fetching

* Fixing state and attribute fetching

* Setting None state

* Need to brreak loop
2019-01-08 09:54:22 +01:00
emontnemery
fb9aad8791 Small cleanup of MQTT light (#19816)
* Small refactor of MQTT light removing unused variable
2019-01-08 07:21:26 +01:00
Fabian Affolter
493d2743ba Merge pull request #19853 from home-assistant/upgrade-beautifulsoup4
Upgrade beautifulsoup4 to 4.7.1
2019-01-08 00:13:50 +01:00
Fabian Affolter
f259c5724b Upgrade holidays to 0.9.9 (#19851) 2019-01-08 00:13:33 +01:00
Vincent KHERBACHE
ea8bb28d21 Fix french Amazon Polly voice 'Léa'. (#19852)
The accent must be removed (Léa -> Lea) just like the other voices (eg. Celine, Peneloppe) to match with Amazon voices ID. 
Fun fact: there is no alternative name for "Léa" on Amazon Polly documentation: https://docs.aws.amazon.com/en_us/polly/latest/dg/voicelist.html, probably just omitted.
Mitigation: alternative voices (with and without accents) can be put into `SUPPORTED_VOICES`, both `voice.get('Id')` and `voice.get('Name')` must be then checked for a match.
This fixes #19802.
2019-01-08 00:13:09 +01:00
Lars Lydersen
8aa136f7ed Added support for spot cleaning that was introduced in pybotvac 0.12. 2019-01-07 20:03:22 +01:00
Fabian Affolter
4905f4dd97 Upgrade beautifulsoup4 to 4.7.1 2019-01-07 19:16:04 +01:00
koomik
45fae5a50e Upgrade tahoma-api to 0.0.14 (#19840)
* Update requirements_all.txt

Change to tahoma-api 0.0.14 to solve #19542 
https://github.com/home-assistant/home-assistant/issues/19542

* Update tahoma.py
2019-01-07 19:02:42 +01:00
Fabian Affolter
2eec2cc656 Upgrade holidays to 0.9.9 2019-01-07 19:00:03 +01:00
Mickaël Schoentgen
a57aae9891 Fix 2 ResourceWarning: unclosed file in test_ruamel_yaml.py (#19780)
Signed-off-by: Mickaël Schoentgen <contact@tiger-222.fr>
2019-01-07 11:53:31 -05:00
Sean Dague
9cdfa77a21 bump watefurnace version to 1.1.0 (#19847)
There is better retry logic in the new library to handle login faults.
2019-01-07 11:36:02 -05:00
sander76
0af635e8d7 adding more dimmer components (#19843)
* adding more dimmer components

* updated library version

* updated requirements_test_all
2019-01-07 11:32:28 -05:00
emontnemery
08ac6da8a6 Clear ALREADY_DISCOVERED list with helper 2019-01-07 17:03:10 +01:00
emontnemery
8701be095b No bare except 2019-01-07 17:03:10 +01:00
emontnemery
0b57cfb004 Cleanup if discovered mqtt sensor can't be added 2019-01-07 17:03:10 +01:00
emontnemery
ddeb7f3bea Clear ALREADY_DISCOVERED list with helper 2019-01-07 17:00:03 +01:00
emontnemery
44c619a853 No bare except 2019-01-07 17:00:03 +01:00
emontnemery
d8370f44cb Cleanup if discovered mqtt binary_sensor can't be added 2019-01-07 17:00:03 +01:00
emontnemery
dd75c49796 Cleanup if discovered mqtt switch can't be added (#19721)
* Cleanup if discovered mqtt switch can't be added
2019-01-07 16:57:51 +01:00
Sebastian Muszynski
8b232e7ce6 Simplify data_key for a stable unique_id because the order of the dict will not be preserved (Closes: #13522) (#19766) 2019-01-07 13:36:16 +01:00
Fredrik Erlandsson
3c465434cd fixes #19814, Daikin config setting (#19823) 2019-01-07 13:04:53 +01:00
Otto Winter
e30c324b32 Bump aioesphomeapi (#19838) 2019-01-07 11:58:10 +01:00
Daniel Shokouhi
903c86a116 Bump pybotvac (#19831)
* Bump pybotvac to support No Go lines

* Update requirements
2019-01-06 23:58:36 +01:00
kennedyshead
c96778c82a This makes the vasttrafik platform stop spamming the logs with warnings (#19792)
* This makes the vasttrafik platform stop spamming the logs with warrnings

* Forcing build
2019-01-06 21:06:20 +01:00
Rene Nulsch
1e18a2c679 Remove temperature from the list of available forecast sensors (#19818) 2019-01-06 20:52:55 +01:00
Tommy Jonsson
5b35317e1e [3/3] mqtt-vacuum device-registry (#19479)
* add device registry to mqtt-vacuum
2019-01-06 19:23:33 +01:00
cdheiser
bf4830bc07 Fix a bug in Lutron RadioRA2 Scene support (#19819) 2019-01-06 19:25:09 +02:00
Johann Kellerman
3ffa0176cc SMA sensor - updated library (#19753) 2019-01-06 19:20:19 +02:00
Tommy Jonsson
ccbc231d3a [2/3] vacuum mqtt-discovery (#19478)
* add discoverability to mqtt-vacuum
2019-01-06 17:05:04 +01:00
Tommy Jonsson
dee229152f [1/3] Refactor mqtt-vacuum in preparation for discovery and device registry (#19462)
* Refactor mqtt-vacuum in preparation for discovery and device registry
2019-01-06 14:16:46 +01:00
emontnemery
76c30aca38 Remove duplicated MQTT switch test case (#19799) 2019-01-06 06:27:57 +01:00
Mattias Welponer
3d0c3ab746 HomematicIP update version to 0.10.1 (#19788)
* Update version to 0.10.1

* Update of requirements files
2019-01-05 16:25:36 -07:00
Sebastian Muszynski
32faf5b709 Improve debug output 2019-01-05 22:39:02 +01:00
Sebastian Muszynski
09ff272290 Refactor motion sensor 2019-01-05 22:31:41 +01:00
Eliseo Martelli
3a5ba77e04 Rename air pollutants to air quality (#19448)
* mv component folder

* moved in airquality

* changed names in files

* renamed test init

* renamed test air quality

* renamed in tests

* renamed coverage

* fixed naming

* corrected attr names

* changed attr names
2019-01-05 11:42:36 -05:00
Otto Winter
68723730a7 Add ESPHome native API discovery (#19399)
* ESPHome discovery

* Add note about netdisco

* 🔡

* Address comments

* Bump netdisco to 2.3.0

* Update requirements_all.txt
2019-01-05 16:00:07 +01:00
Sören Oldag
0125b3fd80 Upgrade pwmled to 1.4.0 (#19783) 2019-01-05 08:05:37 -05:00
Austin Drummond
fb5b5223fb Added zwave lock state from alarm type workaround (#18996)
Thank you 👍 

* added zwave lock state from alarm type workaround

* fixed test indents

* more linting fixes

* one more linting fix

* simplified logic

* fixed lint new lines

* fixed merge conflict issue

* fixed definition of _alarm_type_workaround in zwave lock
2019-01-05 09:59:43 +01:00
keesak
aacf7ba9aa Add support for Kwikset 914 Convert - lock.zwave id0446 (#19710) (#19722)
Thank you 👍
2019-01-05 09:48:40 +01:00
cdce8p
bf29824dac Update HAP-python to 2.4.2 (#19776)
* Bugfixes for connection issues
2019-01-04 22:37:42 +01:00
sander76
a1cb4018a1 update powerview scene component to latest api. (#19717) 2019-01-04 22:19:06 +01:00
Otto Winter
c7700ad11c Fix some ESPHome race conditions (#19772)
* Fix some ESPHome race conditions

* Remove debug

* Update requirements_all.txt

* 🚑 Fix IDE line length settings
2019-01-04 22:10:52 +01:00
Alexei Chetroi
ed8f89df74 Use manufacturer id only for configure_reporting only when specified. (#19729) 2019-01-04 16:05:37 -05:00
Sebastian Muszynski
65c7bdc1ad Don't slugify unique id (#19770) 2019-01-04 16:02:42 -05:00
Austin Drummond
bf40bea965 Support for Homekit controller/alarm control panel (#19612)
* added support for homekit security systems

* fixed linting issues

* fixed indentation issues

* simplifired logic on homekit_controller alarm controller panel

* cleaned up battery level const on homekit controller alarm control panel
2019-01-04 12:54:37 -07:00
David F. Mulcahey
ef180c489a check config instead of config_entry for quirks flag (#19730) 2019-01-04 14:00:26 -05:00
476 changed files with 9862 additions and 3739 deletions

View File

@@ -122,12 +122,17 @@ omit =
homeassistant/components/*/ecovacs.py
homeassistant/components/esphome/__init__.py
homeassistant/components/*/esphome.py
homeassistant/components/esphome/binary_sensor.py
homeassistant/components/esphome/cover.py
homeassistant/components/esphome/fan.py
homeassistant/components/esphome/light.py
homeassistant/components/esphome/sensor.py
homeassistant/components/esphome/switch.py
homeassistant/components/eufy.py
homeassistant/components/*/eufy.py
homeassistant/components/fibaro.py
homeassistant/components/fibaro/__init__.py
homeassistant/components/*/fibaro.py
homeassistant/components/gc100.py
@@ -234,7 +239,7 @@ omit =
homeassistant/components/lutron_caseta.py
homeassistant/components/*/lutron_caseta.py
homeassistant/components/*/mailgun.py
homeassistant/components/mailgun/notify.py
homeassistant/components/matrix.py
homeassistant/components/*/matrix.py
@@ -276,7 +281,8 @@ omit =
homeassistant/components/*/opentherm_gw.py
homeassistant/components/openuv/__init__.py
homeassistant/components/*/openuv.py
homeassistant/components/openuv/binary_sensor.py
homeassistant/components/openuv/sensor.py
homeassistant/components/plum_lightpad.py
homeassistant/components/*/plum_lightpad.py
@@ -298,7 +304,9 @@ omit =
homeassistant/components/*/raincloud.py
homeassistant/components/rainmachine/__init__.py
homeassistant/components/*/rainmachine.py
homeassistant/components/rainmachine/binary_sensor.py
homeassistant/components/rainmachine/sensor.py
homeassistant/components/rainmachine/switch.py
homeassistant/components/raspihats.py
homeassistant/components/*/raspihats.py
@@ -308,6 +316,9 @@ omit =
homeassistant/components/rfxtrx.py
homeassistant/components/*/rfxtrx.py
homeassistant/components/roku.py
homeassistant/components/*/roku.py
homeassistant/components/rpi_gpio.py
homeassistant/components/*/rpi_gpio.py
@@ -327,7 +338,7 @@ omit =
homeassistant/components/*/sense.py
homeassistant/components/simplisafe/__init__.py
homeassistant/components/*/simplisafe.py
homeassistant/components/simplisafe/alarm_control_panel.py
homeassistant/components/sisyphus.py
homeassistant/components/*/sisyphus.py
@@ -424,8 +435,14 @@ omit =
homeassistant/components/*/zabbix.py
homeassistant/components/zha/__init__.py
homeassistant/components/zha/binary_sensor.py
homeassistant/components/zha/const.py
homeassistant/components/zha/event.py
homeassistant/components/zha/fan.py
homeassistant/components/zha/light.py
homeassistant/components/zha/sensor.py
homeassistant/components/zha/switch.py
homeassistant/components/zha/api.py
homeassistant/components/zha/entities/*
homeassistant/components/zha/helpers.py
homeassistant/components/*/zha.py
@@ -519,7 +536,6 @@ omit =
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/google_maps.py
homeassistant/components/device_tracker/googlehome.py
homeassistant/components/device_tracker/gpslogger.py
homeassistant/components/device_tracker/hitron_coda.py
homeassistant/components/device_tracker/huawei_router.py
homeassistant/components/device_tracker/icloud.py
@@ -637,7 +653,6 @@ omit =
homeassistant/components/media_player/pioneer.py
homeassistant/components/media_player/pjlink.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/roku.py
homeassistant/components/media_player/russound_rio.py
homeassistant/components/media_player/russound_rnet.py
homeassistant/components/media_player/snapcast.py

1
.gitignore vendored
View File

@@ -78,6 +78,7 @@ venv
.venv
Pipfile*
share/*
Scripts/
# vimmy stuff
*.swp

View File

@@ -1,2 +0,0 @@
[settings]
multi_line_output=4

View File

@@ -185,7 +185,6 @@ homeassistant/components/edp_redy.py @abmantis
homeassistant/components/eight_sleep.py @mezz64
homeassistant/components/*/eight_sleep.py @mezz64
homeassistant/components/esphome/*.py @OttoWinter
homeassistant/components/*/esphome.py @OttoWinter
# H
homeassistant/components/hive.py @Rendili @KJonline
@@ -219,7 +218,6 @@ homeassistant/components/*/ness_alarm.py @nickw444
# O
homeassistant/components/openuv/* @bachya
homeassistant/components/*/openuv.py @bachya
# P
homeassistant/components/point/* @fredrike
@@ -231,13 +229,11 @@ homeassistant/components/*/qwikswitch.py @kellerza
# R
homeassistant/components/rainmachine/* @bachya
homeassistant/components/*/rainmachine.py @bachya
homeassistant/components/*/random.py @fabaff
homeassistant/components/*/rfxtrx.py @danielhiversen
# S
homeassistant/components/simplisafe/* @bachya
homeassistant/components/*/simplisafe.py @bachya
# T
homeassistant/components/tahoma.py @philklei

View File

@@ -16,7 +16,6 @@ LABEL maintainer="Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>"
VOLUME /config
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Copy build scripts

View File

@@ -1,6 +1,7 @@
"""Home Assistant auth provider."""
import base64
from collections import OrderedDict
import logging
from typing import Any, Dict, List, Optional, cast
import bcrypt
@@ -51,6 +52,15 @@ class Data:
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY,
private=True)
self._data = None # type: Optional[Dict[str, Any]]
self.is_legacy = False
@callback
def normalize_username(self, username: str) -> str:
"""Normalize a username based on the mode."""
if self.is_legacy:
return username
return username.strip()
async def async_load(self) -> None:
"""Load stored data."""
@@ -61,6 +71,20 @@ class Data:
'users': []
}
for user in data['users']:
username = user['username']
# check if we have unstripped usernames
if username != username.strip():
self.is_legacy = True
logging.getLogger(__name__).warning(
"Home Assistant auth provider is running in legacy mode "
"because we detected usernames that start or end in a "
"space. Please change the username.")
break
self._data = data
@property
@@ -73,6 +97,7 @@ class Data:
Raises InvalidAuth if auth invalid.
"""
username = self.normalize_username(username)
dummy = b'$2b$12$CiuFGszHx9eNHxPuQcwBWez4CwDTOcLTX5CbOpV6gef2nYuXkY7BO'
found = None
@@ -105,7 +130,10 @@ class Data:
def add_auth(self, username: str, password: str) -> None:
"""Add a new authenticated user/pass."""
if any(user['username'] == username for user in self.users):
username = self.normalize_username(username)
if any(self.normalize_username(user['username']) == username
for user in self.users):
raise InvalidUser
self.users.append({
@@ -116,9 +144,11 @@ class Data:
@callback
def async_remove_auth(self, username: str) -> None:
"""Remove authentication."""
username = self.normalize_username(username)
index = None
for i, user in enumerate(self.users):
if user['username'] == username:
if self.normalize_username(user['username']) == username:
index = i
break
@@ -132,8 +162,10 @@ class Data:
Raises InvalidUser if user cannot be found.
"""
username = self.normalize_username(username)
for user in self.users:
if user['username'] == username:
if self.normalize_username(user['username']) == username:
user['password'] = self.hash_password(
new_password, True).decode()
break
@@ -178,10 +210,15 @@ class HassAuthProvider(AuthProvider):
async def async_get_or_create_credentials(
self, flow_result: Dict[str, str]) -> Credentials:
"""Get credentials based on the flow result."""
username = flow_result['username']
if self.data is None:
await self.async_initialize()
assert self.data is not None
norm_username = self.data.normalize_username
username = norm_username(flow_result['username'])
for credential in await self.async_credentials():
if credential.data['username'] == username:
if norm_username(credential.data['username']) == username:
return credential
# Create new credentials.

View File

@@ -18,7 +18,7 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['abodepy==0.14.0']
REQUIREMENTS = ['abodepy==0.15.0']
_LOGGER = logging.getLogger(__name__)

View File

@@ -46,8 +46,8 @@ CONFIG_SCHEMA = vol.Schema({
SCHEMA_SERVICE_WRITE_DATA_BY_NAME = vol.Schema({
vol.Required(CONF_ADS_TYPE):
vol.In([ADSTYPE_INT, ADSTYPE_UINT, ADSTYPE_BYTE]),
vol.Required(CONF_ADS_VALUE): cv.match_all,
vol.In([ADSTYPE_INT, ADSTYPE_UINT, ADSTYPE_BYTE, ADSTYPE_BOOL]),
vol.Required(CONF_ADS_VALUE): vol.Coerce(int),
vol.Required(CONF_ADS_VAR): cv.string,
})

View File

@@ -21,6 +21,8 @@ from homeassistant.helpers.entity_component import EntityComponent
DOMAIN = 'alarm_control_panel'
SCAN_INTERVAL = timedelta(seconds=30)
ATTR_CHANGED_BY = 'changed_by'
FORMAT_TEXT = 'text'
FORMAT_NUMBER = 'number'
ENTITY_ID_FORMAT = DOMAIN + '.{}'

View File

@@ -99,7 +99,7 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return one or more digits/characters."""
return 'Number'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -81,8 +81,8 @@ class AlarmDotCom(alarm.AlarmControlPanel):
if self._code is None:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT
@property
def state(self):

View File

@@ -17,7 +17,7 @@ from homeassistant.components.arlo import (
DATA_ARLO, CONF_ATTRIBUTION, SIGNAL_UPDATE_ARLO)
from homeassistant.const import (
ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED)
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_NIGHT)
_LOGGER = logging.getLogger(__name__)
@@ -25,6 +25,7 @@ ARMED = 'armed'
CONF_HOME_MODE_NAME = 'home_mode_name'
CONF_AWAY_MODE_NAME = 'away_mode_name'
CONF_NIGHT_MODE_NAME = 'night_mode_name'
DEPENDENCIES = ['arlo']
@@ -35,6 +36,7 @@ ICON = 'mdi:security'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOME_MODE_NAME, default=ARMED): cv.string,
vol.Optional(CONF_AWAY_MODE_NAME, default=ARMED): cv.string,
vol.Optional(CONF_NIGHT_MODE_NAME, default=ARMED): cv.string,
})
@@ -47,21 +49,23 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
home_mode_name = config.get(CONF_HOME_MODE_NAME)
away_mode_name = config.get(CONF_AWAY_MODE_NAME)
night_mode_name = config.get(CONF_NIGHT_MODE_NAME)
base_stations = []
for base_station in arlo.base_stations:
base_stations.append(ArloBaseStation(base_station, home_mode_name,
away_mode_name))
away_mode_name, night_mode_name))
add_entities(base_stations, True)
class ArloBaseStation(AlarmControlPanel):
"""Representation of an Arlo Alarm Control Panel."""
def __init__(self, data, home_mode_name, away_mode_name):
def __init__(self, data, home_mode_name, away_mode_name, night_mode_name):
"""Initialize the alarm control panel."""
self._base_station = data
self._home_mode_name = home_mode_name
self._away_mode_name = away_mode_name
self._night_mode_name = night_mode_name
self._state = None
@property
@@ -105,6 +109,10 @@ class ArloBaseStation(AlarmControlPanel):
"""Send arm home command. Uses custom mode."""
self._base_station.mode = self._home_mode_name
async def async_alarm_arm_night(self, code=None):
"""Send arm night command. Uses custom mode."""
self._base_station.mode = self._night_mode_name
@property
def name(self):
"""Return the name of the base station."""
@@ -128,4 +136,6 @@ class ArloBaseStation(AlarmControlPanel):
return STATE_ALARM_ARMED_HOME
if mode == self._away_mode_name:
return STATE_ALARM_ARMED_AWAY
if mode == self._night_mode_name:
return STATE_ALARM_ARMED_NIGHT
return mode

View File

@@ -80,7 +80,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return the characters if code is defined."""
return 'Number'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -116,7 +116,7 @@ class ElkArea(ElkEntity, alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return the alarm code format."""
return '^[0-9]{4}([0-9]{2})?$'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -104,7 +104,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
"""Regex for code format or None if no code is required."""
if self._code:
return None
return 'Number'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -0,0 +1,117 @@
"""
Support for Homekit Alarm Control Panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.homekit_controller/
"""
import logging
from homeassistant.components.homekit_controller import (HomeKitEntity,
KNOWN_ACCESSORIES)
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_TRIGGERED)
from homeassistant.const import ATTR_BATTERY_LEVEL
DEPENDENCIES = ['homekit_controller']
ICON = 'mdi:security'
_LOGGER = logging.getLogger(__name__)
CURRENT_STATE_MAP = {
0: STATE_ALARM_ARMED_HOME,
1: STATE_ALARM_ARMED_AWAY,
2: STATE_ALARM_ARMED_NIGHT,
3: STATE_ALARM_DISARMED,
4: STATE_ALARM_TRIGGERED
}
TARGET_STATE_MAP = {
STATE_ALARM_ARMED_HOME: 0,
STATE_ALARM_ARMED_AWAY: 1,
STATE_ALARM_ARMED_NIGHT: 2,
STATE_ALARM_DISARMED: 3,
}
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Homekit Alarm Control Panel support."""
if discovery_info is None:
return
accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']]
add_entities([HomeKitAlarmControlPanel(accessory, discovery_info)],
True)
class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel):
"""Representation of a Homekit Alarm Control Panel."""
def __init__(self, *args):
"""Initialise the Alarm Control Panel."""
super().__init__(*args)
self._state = None
self._battery_level = None
def update_characteristics(self, characteristics):
"""Synchronise the Alarm Control Panel state with Home Assistant."""
# pylint: disable=import-error
from homekit.model.characteristics import CharacteristicsTypes
for characteristic in characteristics:
ctype = characteristic['type']
ctype = CharacteristicsTypes.get_short(ctype)
if ctype == "security-system-state.current":
self._chars['security-system-state.current'] = \
characteristic['iid']
self._state = CURRENT_STATE_MAP[characteristic['value']]
elif ctype == "security-system-state.target":
self._chars['security-system-state.target'] = \
characteristic['iid']
elif ctype == "battery-level":
self._chars['battery-level'] = characteristic['iid']
self._battery_level = characteristic['value']
@property
def icon(self):
"""Return icon."""
return ICON
@property
def state(self):
"""Return the state of the device."""
return self._state
def alarm_disarm(self, code=None):
"""Send disarm command."""
self.set_alarm_state(STATE_ALARM_DISARMED, code)
def alarm_arm_away(self, code=None):
"""Send arm command."""
self.set_alarm_state(STATE_ALARM_ARMED_AWAY, code)
def alarm_arm_home(self, code=None):
"""Send stay command."""
self.set_alarm_state(STATE_ALARM_ARMED_HOME, code)
def alarm_arm_night(self, code=None):
"""Send night command."""
self.set_alarm_state(STATE_ALARM_ARMED_NIGHT, code)
def set_alarm_state(self, state, code=None):
"""Send state command."""
characteristics = [{'aid': self._aid,
'iid': self._chars['security-system-state.target'],
'value': TARGET_STATE_MAP[state]}]
self.put_characteristics(characteristics)
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
if self._battery_level is None:
return None
return {
ATTR_BATTERY_LEVEL: self._battery_level,
}

View File

@@ -82,8 +82,8 @@ class IAlarmPanel(alarm.AlarmControlPanel):
if self._code is None:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT
@property
def state(self):

View File

@@ -129,8 +129,8 @@ class IFTTTAlarmPanel(alarm.AlarmControlPanel):
if self._code is None:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT
def alarm_disarm(self, code=None):
"""Send disarm command."""

View File

@@ -207,8 +207,8 @@ class ManualAlarm(alarm.AlarmControlPanel, RestoreEntity):
if self._code is None:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT
def alarm_disarm(self, code=None):
"""Send disarm command."""

View File

@@ -241,8 +241,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
if self._code is None:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT
def alarm_disarm(self, code=None):
"""Send disarm command."""

View File

@@ -59,7 +59,7 @@ class NessAlarmPanel(alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return the regex for code format or None if no code is required."""
return 'Number'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -70,7 +70,7 @@ class NX584Alarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return one or more digits/characters."""
return 'Number'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -64,7 +64,7 @@ class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return the regex for code format or None if no code is required."""
return 'Number'
return alarm.FORMAT_NUMBER
@property
def state(self):

View File

@@ -61,7 +61,7 @@ class VerisureAlarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""Return one or more digits/characters."""
return 'Number'
return alarm.FORMAT_NUMBER
@property
def changed_by(self):

View File

@@ -46,9 +46,7 @@ ALERT_SCHEMA = vol.Schema({
vol.Required(CONF_NOTIFIERS): cv.ensure_list})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: ALERT_SCHEMA,
}),
DOMAIN: cv.schema_with_slug_keys(ALERT_SCHEMA),
}, extra=vol.ALLOW_EXTRA)

View File

@@ -72,6 +72,6 @@ async def async_setup(hass, config):
pass
else:
smart_home_config = smart_home_config or SMART_HOME_SCHEMA({})
smart_home.async_setup(hass, smart_home_config)
await smart_home.async_setup(hass, smart_home_config)
return True

View File

@@ -27,8 +27,9 @@ from homeassistant.const import (
CONF_NAME, SERVICE_LOCK, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON,
SERVICE_UNLOCK, SERVICE_VOLUME_SET, STATE_LOCKED, STATE_ON, STATE_UNLOCKED,
TEMP_CELSIUS, TEMP_FAHRENHEIT, MATCH_ALL)
SERVICE_UNLOCK, SERVICE_VOLUME_SET, STATE_LOCKED, STATE_ON,
STATE_UNAVAILABLE, STATE_UNLOCKED, TEMP_CELSIUS, TEMP_FAHRENHEIT,
MATCH_ALL)
import homeassistant.core as ha
import homeassistant.util.color as color_util
from homeassistant.util.decorator import Registry
@@ -393,6 +394,37 @@ class _AlexaInterface:
}
class _AlexaEndpointHealth(_AlexaInterface):
"""Implements Alexa.EndpointHealth.
https://developer.amazon.com/docs/smarthome/state-reporting-for-a-smart-home-skill.html#report-state-when-alexa-requests-it
"""
def __init__(self, hass, entity):
super().__init__(entity)
self.hass = hass
def name(self):
return 'Alexa.EndpointHealth'
def properties_supported(self):
return [{'name': 'connectivity'}]
def properties_proactively_reported(self):
return False
def properties_retrievable(self):
return True
def get_property(self, name):
if name != 'connectivity':
raise _UnsupportedProperty(name)
if self.entity.state == STATE_UNAVAILABLE:
return {'value': 'UNREACHABLE'}
return {'value': 'OK'}
class _AlexaPowerController(_AlexaInterface):
"""Implements Alexa.PowerController.
@@ -769,7 +801,8 @@ class _GenericCapabilities(_AlexaEntity):
return [_DisplayCategory.OTHER]
def interfaces(self):
return [_AlexaPowerController(self.entity)]
return [_AlexaPowerController(self.entity),
_AlexaEndpointHealth(self.hass, self.entity)]
@ENTITY_ADAPTERS.register(switch.DOMAIN)
@@ -778,7 +811,8 @@ class _SwitchCapabilities(_AlexaEntity):
return [_DisplayCategory.SWITCH]
def interfaces(self):
return [_AlexaPowerController(self.entity)]
return [_AlexaPowerController(self.entity),
_AlexaEndpointHealth(self.hass, self.entity)]
@ENTITY_ADAPTERS.register(climate.DOMAIN)
@@ -792,6 +826,7 @@ class _ClimateCapabilities(_AlexaEntity):
yield _AlexaPowerController(self.entity)
yield _AlexaThermostatController(self.hass, self.entity)
yield _AlexaTemperatureSensor(self.hass, self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
@ENTITY_ADAPTERS.register(cover.DOMAIN)
@@ -804,6 +839,7 @@ class _CoverCapabilities(_AlexaEntity):
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & cover.SUPPORT_SET_POSITION:
yield _AlexaPercentageController(self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
@ENTITY_ADAPTERS.register(light.DOMAIN)
@@ -821,6 +857,7 @@ class _LightCapabilities(_AlexaEntity):
yield _AlexaColorController(self.entity)
if supported & light.SUPPORT_COLOR_TEMP:
yield _AlexaColorTemperatureController(self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
@ENTITY_ADAPTERS.register(fan.DOMAIN)
@@ -833,6 +870,7 @@ class _FanCapabilities(_AlexaEntity):
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & fan.SUPPORT_SET_SPEED:
yield _AlexaPercentageController(self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
@ENTITY_ADAPTERS.register(lock.DOMAIN)
@@ -841,7 +879,8 @@ class _LockCapabilities(_AlexaEntity):
return [_DisplayCategory.SMARTLOCK]
def interfaces(self):
return [_AlexaLockController(self.entity)]
return [_AlexaLockController(self.entity),
_AlexaEndpointHealth(self.hass, self.entity)]
@ENTITY_ADAPTERS.register(media_player.DOMAIN)
@@ -851,6 +890,7 @@ class _MediaPlayerCapabilities(_AlexaEntity):
def interfaces(self):
yield _AlexaPowerController(self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & media_player.SUPPORT_VOLUME_SET:
@@ -913,6 +953,7 @@ class _SensorCapabilities(_AlexaEntity):
TEMP_CELSIUS,
):
yield _AlexaTemperatureSensor(self.hass, self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
@ENTITY_ADAPTERS.register(binary_sensor.DOMAIN)
@@ -934,6 +975,8 @@ class _BinarySensorCapabilities(_AlexaEntity):
elif sensor_type is self.TYPE_MOTION:
yield _AlexaMotionSensor(self.hass, self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity)
def get_type(self):
"""Return the type of binary sensor."""
attrs = self.entity.attributes
@@ -993,8 +1036,7 @@ class Config:
self.entity_config = entity_config or {}
@ha.callback
def async_setup(hass, config):
async def async_setup(hass, config):
"""Activate Smart Home functionality of Alexa component.
This is optional, triggered by having a `smart_home:` sub-section in the
@@ -1020,8 +1062,7 @@ def async_setup(hass, config):
hass.http.register_view(SmartHomeView(smart_home_config))
if AUTH_KEY in hass.data:
hass.loop.create_task(
async_enable_proactive_mode(hass, smart_home_config))
await async_enable_proactive_mode(hass, smart_home_config)
async def async_enable_proactive_mode(hass, smart_home_config):
@@ -1337,8 +1378,7 @@ async def async_send_changereport_message(hass, config, alexa_entity):
return
headers = {
"Authorization": "Bearer {}".format(token),
"Content-Type": "application/json;charset=UTF-8"
"Authorization": "Bearer {}".format(token)
}
endpoint = alexa_entity.entity_id()
@@ -1359,14 +1399,14 @@ async def async_send_changereport_message(hass, config, alexa_entity):
payload=payload)
message.set_endpoint_full(token, endpoint)
message_str = json.dumps(message.serialize())
message_serialized = message.serialize()
try:
session = aiohttp_client.async_get_clientsession(hass)
with async_timeout.timeout(DEFAULT_TIMEOUT, loop=hass.loop):
response = await session.post(config.endpoint,
headers=headers,
data=message_str,
json=message_serialized,
allow_redirects=True)
except (asyncio.TimeoutError, aiohttp.ClientError):
@@ -1375,7 +1415,7 @@ async def async_send_changereport_message(hass, config, alexa_entity):
response_text = await response.text()
_LOGGER.debug("Sent: %s", message_str)
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
_LOGGER.debug("Received (%s): %s", response.status, response_text)
if response.status != 202:

View File

@@ -26,6 +26,8 @@ CONF_SSH_KEY = 'ssh_key'
CONF_REQUIRE_IP = 'require_ip'
DEFAULT_SSH_PORT = 22
SECRET_GROUP = 'Password or SSH Key'
CONF_SENSORS = 'sensors'
SENSOR_TYPES = ['upload_speed', 'download_speed', 'download', 'upload']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
@@ -37,7 +39,9 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_REQUIRE_IP, default=True): cv.boolean,
vol.Exclusive(CONF_PASSWORD, SECRET_GROUP): cv.string,
vol.Exclusive(CONF_SSH_KEY, SECRET_GROUP): cv.isfile,
vol.Exclusive(CONF_PUB_KEY, SECRET_GROUP): cv.isfile
vol.Exclusive(CONF_PUB_KEY, SECRET_GROUP): cv.isfile,
vol.Optional(CONF_SENSORS): vol.All(
cv.ensure_list, [vol.In(SENSOR_TYPES)]),
}),
}, extra=vol.ALLOW_EXTRA)
@@ -62,7 +66,8 @@ async def async_setup(hass, config):
hass.data[DATA_ASUSWRT] = api
hass.async_create_task(async_load_platform(
hass, 'sensor', DOMAIN, {}, config))
hass, 'sensor', DOMAIN, config[DOMAIN].get(CONF_SENSORS), config))
hass.async_create_task(async_load_platform(
hass, 'device_tracker', DOMAIN, {}, config))
return True

View File

@@ -9,11 +9,11 @@
},
"step": {
"init": {
"description": "Bitte w\u00e4hle einen der Benachrichtigungsdienste:",
"description": "Bitte w\u00e4hlen Sie einen der Benachrichtigungsdienste:",
"title": "Einmal Passwort f\u00fcr Notify einrichten"
},
"setup": {
"description": "Ein Einmal-Passwort wurde per ** notify gesendet. {notify_service} **. Bitte gebe es unten ein:",
"description": "Ein Einmal-Passwort wurde per **notify.{notify_service}** gesendet. Bitte geben Sie es unten ein:",
"title": "\u00dcberpr\u00fcfe das Setup"
}
},

View File

@@ -0,0 +1,7 @@
{
"mfa_setup": {
"totp": {
"title": ""
}
}
}

View File

@@ -375,8 +375,6 @@ def _async_get_action(hass, config, name):
async def action(entity_id, variables, context):
"""Execute an action."""
_LOGGER.info('Executing %s', name)
hass.components.logbook.async_log_entry(
name, 'has been triggered', DOMAIN, entity_id)
try:
await script_obj.async_run(variables, context)

View File

@@ -1,9 +1,9 @@
"""
Offer geo location automation rules.
Offer geolocation automation rules.
For more details about this automation trigger, please refer to the
documentation at
https://home-assistant.io/docs/automation/trigger/#geo-location-trigger
https://home-assistant.io/docs/automation/trigger/#geolocation-trigger
"""
import voluptuous as vol

View File

@@ -13,30 +13,18 @@ from homeassistant.const import CONF_AT, CONF_PLATFORM
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_track_time_change
CONF_HOURS = 'hours'
CONF_MINUTES = 'minutes'
CONF_SECONDS = 'seconds'
_LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.All(vol.Schema({
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'time',
CONF_AT: cv.time,
CONF_HOURS: vol.Any(vol.Coerce(int), vol.Coerce(str)),
CONF_MINUTES: vol.Any(vol.Coerce(int), vol.Coerce(str)),
CONF_SECONDS: vol.Any(vol.Coerce(int), vol.Coerce(str)),
}), cv.has_at_least_one_key(CONF_HOURS, CONF_MINUTES, CONF_SECONDS, CONF_AT))
vol.Required(CONF_AT): cv.time,
})
async def async_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration."""
if CONF_AT in config:
at_time = config.get(CONF_AT)
hours, minutes, seconds = at_time.hour, at_time.minute, at_time.second
else:
hours = config.get(CONF_HOURS)
minutes = config.get(CONF_MINUTES)
seconds = config.get(CONF_SECONDS)
at_time = config.get(CONF_AT)
hours, minutes, seconds = at_time.hour, at_time.minute, at_time.second
@callback
def time_automation_listener(now):

View File

@@ -0,0 +1,53 @@
"""
Offer time listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/docs/automation/trigger/#time-trigger
"""
import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import CONF_PLATFORM
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_track_time_change
CONF_HOURS = 'hours'
CONF_MINUTES = 'minutes'
CONF_SECONDS = 'seconds'
_LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.All(vol.Schema({
vol.Required(CONF_PLATFORM): 'time_pattern',
CONF_HOURS: vol.Any(vol.Coerce(int), vol.Coerce(str)),
CONF_MINUTES: vol.Any(vol.Coerce(int), vol.Coerce(str)),
CONF_SECONDS: vol.Any(vol.Coerce(int), vol.Coerce(str)),
}), cv.has_at_least_one_key(CONF_HOURS, CONF_MINUTES, CONF_SECONDS))
async def async_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration."""
hours = config.get(CONF_HOURS)
minutes = config.get(CONF_MINUTES)
seconds = config.get(CONF_SECONDS)
# If larger units are specified, default the smaller units to zero
if minutes is None and hours is not None:
minutes = 0
if seconds is None and minutes is not None:
seconds = 0
@callback
def time_automation_listener(now):
"""Listen for time changes and calls action."""
hass.async_run_job(action, {
'trigger': {
'platform': 'time_pattern',
'now': now,
},
})
return async_track_time_change(hass, time_automation_listener,
hour=hours, minute=minutes, second=seconds)

View File

@@ -50,9 +50,7 @@ DEVICE_SCHEMA = vol.Schema({
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: DEVICE_SCHEMA,
}),
DOMAIN: cv.schema_with_slug_keys(DEVICE_SCHEMA),
}, extra=vol.ALLOW_EXTRA)
SERVICE_VAPIX_CALL = 'vapix_call'

View File

@@ -1,147 +0,0 @@
"""
Support for deCONZ binary sensor.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.deconz/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.deconz.const import (
ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DECONZ_REACHABLE,
DOMAIN as DECONZ_DOMAIN)
from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
from homeassistant.helpers.dispatcher import async_dispatcher_connect
DEPENDENCIES = ['deconz']
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old way of setting up deCONZ binary sensors."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ binary sensor."""
gateway = hass.data[DECONZ_DOMAIN]
@callback
def async_add_sensor(sensors):
"""Add binary sensor from deCONZ."""
from pydeconz.sensor import DECONZ_BINARY_SENSOR
entities = []
allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True)
for sensor in sensors:
if sensor.type in DECONZ_BINARY_SENSOR and \
not (not allow_clip_sensor and sensor.type.startswith('CLIP')):
entities.append(DeconzBinarySensor(sensor, gateway))
async_add_entities(entities, True)
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor))
async_add_sensor(gateway.api.sensors.values())
class DeconzBinarySensor(BinarySensorDevice):
"""Representation of a binary sensor."""
def __init__(self, sensor, gateway):
"""Set up sensor and add update callback to get data from websocket."""
self._sensor = sensor
self.gateway = gateway
self.unsub_dispatcher = None
async def async_added_to_hass(self):
"""Subscribe sensors events."""
self._sensor.register_async_callback(self.async_update_callback)
self.gateway.deconz_ids[self.entity_id] = self._sensor.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect sensor object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._sensor.remove_callback(self.async_update_callback)
self._sensor = None
@callback
def async_update_callback(self, reason):
"""Update the sensor's state.
If reason is that state is updated,
or reachable has changed or battery has changed.
"""
if reason['state'] or \
'reachable' in reason['attr'] or \
'battery' in reason['attr'] or \
'on' in reason['attr']:
self.async_schedule_update_ha_state()
@property
def is_on(self):
"""Return true if sensor is on."""
return self._sensor.is_tripped
@property
def name(self):
"""Return the name of the sensor."""
return self._sensor.name
@property
def unique_id(self):
"""Return a unique identifier for this sensor."""
return self._sensor.uniqueid
@property
def device_class(self):
"""Return the class of the sensor."""
return self._sensor.sensor_class
@property
def icon(self):
"""Return the icon to use in the frontend."""
return self._sensor.sensor_icon
@property
def available(self):
"""Return True if sensor is available."""
return self.gateway.available and self._sensor.reachable
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
from pydeconz.sensor import PRESENCE
attr = {}
if self._sensor.battery:
attr[ATTR_BATTERY_LEVEL] = self._sensor.battery
if self._sensor.on is not None:
attr[ATTR_ON] = self._sensor.on
if self._sensor.type in PRESENCE and self._sensor.dark is not None:
attr[ATTR_DARK] = self._sensor.dark
return attr
@property
def device_info(self):
"""Return a device description for device registry."""
if (self._sensor.uniqueid is None or
self._sensor.uniqueid.count(':') != 7):
return None
serial = self._sensor.uniqueid.split('-', 1)[0]
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
'manufacturer': self._sensor.manufacturer,
'model': self._sensor.modelid,
'name': self._sensor.name,
'sw_version': self._sensor.swversion,
'via_hub': (DECONZ_DOMAIN, bridgeid),
}

View File

@@ -9,7 +9,7 @@ import logging
from homeassistant.components.binary_sensor import (
BinarySensorDevice, ENTITY_ID_FORMAT)
from homeassistant.components.fibaro import (
FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice)
FIBARO_DEVICES, FibaroDevice)
from homeassistant.const import (CONF_DEVICE_CLASS, CONF_ICON)
DEPENDENCIES = ['fibaro']
@@ -33,17 +33,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
return
add_entities(
[FibaroBinarySensor(device, hass.data[FIBARO_CONTROLLER])
[FibaroBinarySensor(device)
for device in hass.data[FIBARO_DEVICES]['binary_sensor']], True)
class FibaroBinarySensor(FibaroDevice, BinarySensorDevice):
"""Representation of a Fibaro Binary Sensor."""
def __init__(self, fibaro_device, controller):
def __init__(self, fibaro_device):
"""Initialize the binary_sensor."""
self._state = None
super().__init__(fibaro_device, controller)
super().__init__(fibaro_device)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
stype = None
devconf = fibaro_device.device_config

View File

@@ -5,7 +5,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.hive/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.hive import DATA_HIVE
from homeassistant.components.hive import DATA_HIVE, DOMAIN
DEPENDENCIES = ['hive']
@@ -35,9 +35,24 @@ class HiveBinarySensorEntity(BinarySensorDevice):
self.attributes = {}
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
self._unique_id = '{}-{}'.format(self.node_id, self.device_type)
self.session.entities.append(self)
@property
def unique_id(self):
"""Return unique ID of entity."""
return self._unique_id
@property
def device_info(self):
"""Return device information."""
return {
'identifiers': {
(DOMAIN, self.unique_id)
},
'name': self.name
}
def handle_update(self, updatesource):
"""Handle the new update request."""
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource:

View File

@@ -41,7 +41,7 @@ SENSOR_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_SENSORS): vol.Schema({cv.slug: SENSOR_SCHEMA}),
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys(SENSOR_SCHEMA),
})

View File

@@ -51,7 +51,7 @@ SENSOR_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_SENSORS): vol.Schema({cv.slug: SENSOR_SCHEMA}),
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys(SENSOR_SCHEMA),
})

View File

@@ -14,7 +14,7 @@ from homeassistant.const import CONF_NAME, WEEKDAYS
from homeassistant.components.binary_sensor import BinarySensorDevice
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['holidays==0.9.8']
REQUIREMENTS = ['holidays==0.9.9']
_LOGGER = logging.getLogger(__name__)
@@ -26,6 +26,7 @@ ALL_COUNTRIES = [
'Canada', 'CA', 'Colombia', 'CO', 'Croatia', 'HR', 'Czech', 'CZ',
'Denmark', 'DK', 'England', 'EuropeanCentralBank', 'ECB', 'TAR',
'Finland', 'FI', 'France', 'FRA', 'Germany', 'DE', 'Hungary', 'HU',
'Honduras', 'HUD',
'India', 'IND', 'Ireland', 'Isle of Man', 'Italy', 'IT', 'Japan', 'JP',
'Mexico', 'MX', 'Netherlands', 'NL', 'NewZealand', 'NZ',
'Northern Ireland', 'Norway', 'NO', 'Polish', 'PL', 'Portugal', 'PT',

View File

@@ -107,7 +107,7 @@ class XiaomiBinarySensor(XiaomiDevice, BinarySensorDevice):
def update(self):
"""Update the sensor state."""
_LOGGER.debug('Updating xiaomi sensor by polling')
_LOGGER.debug('Updating xiaomi sensor (%s) by polling', self._sid)
self._get_from_hub(self._sid)
@@ -178,7 +178,28 @@ class XiaomiMotionSensor(XiaomiBinarySensor):
self.async_schedule_update_ha_state()
def parse_data(self, data, raw_data):
"""Parse data sent by gateway."""
"""Parse data sent by gateway.
Polling (proto v1, firmware version 1.4.1_159.0143)
>> { "cmd":"read","sid":"158..."}
<< {'model': 'motion', 'sid': '158...', 'short_id': 26331,
'cmd': 'read_ack', 'data': '{"voltage":3005}'}
Multicast messages (proto v1, firmware version 1.4.1_159.0143)
<< {'model': 'motion', 'sid': '158...', 'short_id': 26331,
'cmd': 'report', 'data': '{"status":"motion"}'}
<< {'model': 'motion', 'sid': '158...', 'short_id': 26331,
'cmd': 'report', 'data': '{"no_motion":"120"}'}
<< {'model': 'motion', 'sid': '158...', 'short_id': 26331,
'cmd': 'report', 'data': '{"no_motion":"180"}'}
<< {'model': 'motion', 'sid': '158...', 'short_id': 26331,
'cmd': 'report', 'data': '{"no_motion":"300"}'}
<< {'model': 'motion', 'sid': '158...', 'short_id': 26331,
'cmd': 'heartbeat', 'data': '{"voltage":3005}'}
"""
if raw_data['cmd'] == 'heartbeat':
_LOGGER.debug(
'Skipping heartbeat of the motion sensor. '
@@ -187,8 +208,7 @@ class XiaomiMotionSensor(XiaomiBinarySensor):
'11631#issuecomment-357507744).')
return
self._should_poll = False
if NO_MOTION in data: # handle push from the hub
if NO_MOTION in data:
self._no_motion_since = data[NO_MOTION]
self._state = False
return True
@@ -203,26 +223,20 @@ class XiaomiMotionSensor(XiaomiBinarySensor):
self._unsub_set_no_motion()
self._unsub_set_no_motion = async_call_later(
self._hass,
180,
120,
self._async_set_no_motion
)
else:
self._should_poll = True
if self.entity_id is not None:
self._hass.bus.fire('xiaomi_aqara.motion', {
'entity_id': self.entity_id
})
if self.entity_id is not None:
self._hass.bus.fire('xiaomi_aqara.motion', {
'entity_id': self.entity_id
})
self._no_motion_since = 0
if self._state:
return False
self._state = True
return True
if value == NO_MOTION:
if not self._state:
return False
self._state = False
return True
class XiaomiDoorSensor(XiaomiBinarySensor):

View File

@@ -15,7 +15,7 @@ from homeassistant.const import (
CONF_BINARY_SENSORS, CONF_SENSORS, CONF_FILENAME,
CONF_MONITORED_CONDITIONS, TEMP_FAHRENHEIT)
REQUIREMENTS = ['blinkpy==0.11.0']
REQUIREMENTS = ['blinkpy==0.11.1']
_LOGGER = logging.getLogger(__name__)

View File

@@ -7,6 +7,7 @@ https://www.home-assistant.io/components/camera.proxy/
import asyncio
import logging
from datetime import timedelta
import voluptuous as vol
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
@@ -18,7 +19,7 @@ from homeassistant.util.async_ import run_coroutine_threadsafe
import homeassistant.util.dt as dt_util
from homeassistant.components.camera import async_get_still_stream
REQUIREMENTS = ['pillow==5.3.0']
REQUIREMENTS = ['pillow==5.4.1']
_LOGGER = logging.getLogger(__name__)
@@ -206,7 +207,7 @@ class ProxyCamera(Camera):
self._cache_images = bool(
config.get(CONF_IMAGE_REFRESH_RATE)
or config.get(CONF_CACHE_IMAGES))
self._last_image_time = 0
self._last_image_time = dt_util.utc_from_timestamp(0)
self._last_image = None
self._headers = (
{HTTP_HEADER_HA_AUTH: self.hass.config.api.api_password}
@@ -223,7 +224,8 @@ class ProxyCamera(Camera):
now = dt_util.utcnow()
if (self._image_refresh_rate and
now < self._last_image_time + self._image_refresh_rate):
now < self._last_image_time +
timedelta(seconds=self._image_refresh_rate)):
return self._last_image
self._last_image_time = now

View File

@@ -107,7 +107,7 @@ class XiaomiCamera(Camera):
_LOGGER.warning("There don't appear to be any folders")
return False
first_dir = dirs[-1]
first_dir = latest_dir = dirs[-1]
try:
ftp.cwd(first_dir)
except error_perm as exc:

View File

@@ -19,17 +19,18 @@ DEPENDENCIES = ['zoneminder']
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the ZoneMinder cameras."""
filter_urllib3_logging()
zm_client = hass.data[ZONEMINDER_DOMAIN]
monitors = zm_client.get_monitors()
if not monitors:
_LOGGER.warning("Could not fetch monitors from ZoneMinder")
return
cameras = []
for monitor in monitors:
_LOGGER.info("Initializing camera %s", monitor.id)
cameras.append(ZoneMinderCamera(monitor, zm_client.verify_ssl))
for zm_client in hass.data[ZONEMINDER_DOMAIN].values():
monitors = zm_client.get_monitors()
if not monitors:
_LOGGER.warning(
"Could not fetch monitors from ZoneMinder host: %s"
)
return
for monitor in monitors:
_LOGGER.info("Initializing camera %s", monitor.id)
cameras.append(ZoneMinderCamera(monitor, zm_client.verify_ssl))
add_entities(cameras)

View File

@@ -0,0 +1,10 @@
{
"config": {
"step": {
"confirm": {
"title": ""
}
},
"title": ""
}
}

View File

@@ -8,7 +8,7 @@ from homeassistant.components.climate import (
ClimateDevice, STATE_AUTO, STATE_HEAT, STATE_OFF, STATE_ON,
SUPPORT_AUX_HEAT, SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.components.hive import DATA_HIVE
from homeassistant.components.hive import DATA_HIVE, DOMAIN
DEPENDENCIES = ['hive']
HIVE_TO_HASS_STATE = {'SCHEDULE': STATE_AUTO, 'MANUAL': STATE_HEAT,
@@ -44,6 +44,7 @@ class HiveClimateEntity(ClimateDevice):
self.attributes = {}
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
self._unique_id = '{}-{}'.format(self.node_id, self.device_type)
if self.device_type == "Heating":
self.modes = [STATE_AUTO, STATE_HEAT, STATE_OFF]
@@ -52,6 +53,21 @@ class HiveClimateEntity(ClimateDevice):
self.session.entities.append(self)
@property
def unique_id(self):
"""Return unique ID of entity."""
return self._unique_id
@property
def device_info(self):
"""Return device information."""
return {
'identifiers': {
(DOMAIN, self.unique_id)
},
'name': self.name
}
@property
def supported_features(self):
"""Return the list of supported features."""

View File

@@ -19,7 +19,8 @@ from homeassistant.components.climate import (
ATTR_CURRENT_HUMIDITY, ClimateDevice, DOMAIN, PLATFORM_SCHEMA,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE,
SUPPORT_FAN_MODE, SUPPORT_SWING_MODE,
SUPPORT_ON_OFF)
SUPPORT_ON_OFF, STATE_HEAT, STATE_COOL, STATE_FAN_ONLY, STATE_DRY,
STATE_AUTO)
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -57,6 +58,16 @@ FIELD_TO_FLAG = {
'on': SUPPORT_ON_OFF,
}
SENSIBO_TO_HA = {
"cool": STATE_COOL,
"heat": STATE_HEAT,
"fan": STATE_FAN_ONLY,
"auto": STATE_AUTO,
"dry": STATE_DRY
}
HA_TO_SENSIBO = {value: key for key, value in SENSIBO_TO_HA.items()}
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
@@ -129,9 +140,10 @@ class SensiboClimate(ClimateDevice):
self._ac_states = data['acState']
self._status = data['connectionStatus']['isAlive']
capabilities = data['remoteCapabilities']
self._operations = sorted(capabilities['modes'].keys())
self._current_capabilities = capabilities[
'modes'][self.current_operation]
self._operations = [SENSIBO_TO_HA[mode] for mode
in capabilities['modes']]
self._current_capabilities = \
capabilities['modes'][self._ac_states['mode']]
temperature_unit_key = data.get('temperatureUnit') or \
self._ac_states.get('temperatureUnit')
if temperature_unit_key:
@@ -186,7 +198,7 @@ class SensiboClimate(ClimateDevice):
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
return self._ac_states['mode']
return SENSIBO_TO_HA.get(self._ac_states['mode'])
@property
def current_humidity(self):
@@ -293,7 +305,8 @@ class SensiboClimate(ClimateDevice):
"""Set new target operation mode."""
with async_timeout.timeout(TIMEOUT):
await self._client.async_set_ac_state_property(
self._id, 'mode', operation_mode, self._ac_states)
self._id, 'mode', HA_TO_SENSIBO[operation_mode],
self._ac_states)
async def async_set_swing_mode(self, swing_mode):
"""Set new target swing operation."""

View File

@@ -2,6 +2,7 @@
import asyncio
import logging
import pprint
import random
import uuid
from aiohttp import hdrs, client_exceptions, WSMsgType
@@ -107,9 +108,11 @@ class CloudIoT:
self.tries += 1
try:
# Sleep 2^tries seconds between retries
self.retry_task = hass.async_create_task(asyncio.sleep(
2**min(9, self.tries), loop=hass.loop))
# Sleep 2^tries + 0…tries*3 seconds between retries
self.retry_task = hass.async_create_task(
asyncio.sleep(2**min(9, self.tries) +
random.randint(0, self.tries * 3),
loop=hass.loop))
yield from self.retry_task
self.retry_task = None
except asyncio.CancelledError:
@@ -313,15 +316,20 @@ def async_handle_google_actions(hass, cloud, payload):
@HANDLERS.register('cloud')
@asyncio.coroutine
def async_handle_cloud(hass, cloud, payload):
async def async_handle_cloud(hass, cloud, payload):
"""Handle an incoming IoT message for cloud component."""
action = payload['action']
if action == 'logout':
yield from cloud.logout()
# Log out of Home Assistant Cloud
await cloud.logout()
_LOGGER.error("You have been logged out from Home Assistant cloud: %s",
payload['reason'])
elif action == 'refresh_auth':
# Refresh the auth token between now and payload['seconds']
hass.helpers.event.async_call_later(
random.randint(0, payload['seconds']),
lambda now: auth_api.check_token(cloud))
else:
_LOGGER.warning("Received unknown cloud action: %s", action)

View File

@@ -37,8 +37,8 @@ SERVICE_SCHEMA = vol.Schema({
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: vol.Any({
DOMAIN: cv.schema_with_slug_keys(
vol.Any({
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_INITIAL, default=DEFAULT_INITIAL):
cv.positive_int,
@@ -46,7 +46,7 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_RESTORE, default=True): cv.boolean,
vol.Optional(CONF_STEP, default=DEFAULT_STEP): cv.positive_int,
}, None)
})
)
}, extra=vol.ALLOW_EXTRA)

View File

@@ -27,7 +27,7 @@ COVER_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_SCHEMA),
})

View File

@@ -9,7 +9,7 @@ import logging
from homeassistant.components.cover import (
CoverDevice, ENTITY_ID_FORMAT, ATTR_POSITION, ATTR_TILT_POSITION)
from homeassistant.components.fibaro import (
FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice)
FIBARO_DEVICES, FibaroDevice)
DEPENDENCIES = ['fibaro']
@@ -22,16 +22,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
return
add_entities(
[FibaroCover(device, hass.data[FIBARO_CONTROLLER]) for
[FibaroCover(device) for
device in hass.data[FIBARO_DEVICES]['cover']], True)
class FibaroCover(FibaroDevice, CoverDevice):
"""Representation a Fibaro Cover."""
def __init__(self, fibaro_device, controller):
def __init__(self, fibaro_device):
"""Initialize the Vera device."""
super().__init__(fibaro_device, controller)
super().__init__(fibaro_device)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
@staticmethod

View File

@@ -47,7 +47,7 @@ COVER_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_SCHEMA),
})

View File

@@ -0,0 +1,305 @@
"""
Support for Homekit Cover.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.homekit_controller/
"""
import logging
from homeassistant.components.homekit_controller import (HomeKitEntity,
KNOWN_ACCESSORIES)
from homeassistant.components.cover import (
CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION,
SUPPORT_OPEN_TILT, SUPPORT_CLOSE_TILT, SUPPORT_SET_TILT_POSITION,
ATTR_POSITION, ATTR_TILT_POSITION)
from homeassistant.const import (
STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING)
STATE_STOPPED = 'stopped'
DEPENDENCIES = ['homekit_controller']
_LOGGER = logging.getLogger(__name__)
CURRENT_GARAGE_STATE_MAP = {
0: STATE_OPEN,
1: STATE_CLOSED,
2: STATE_OPENING,
3: STATE_CLOSING,
4: STATE_STOPPED
}
TARGET_GARAGE_STATE_MAP = {
STATE_OPEN: 0,
STATE_CLOSED: 1,
STATE_STOPPED: 2
}
CURRENT_WINDOW_STATE_MAP = {
0: STATE_OPENING,
1: STATE_CLOSING,
2: STATE_STOPPED
}
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up HomeKit Cover support."""
if discovery_info is None:
return
accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']]
if discovery_info['device-type'] == 'garage-door-opener':
add_entities([HomeKitGarageDoorCover(accessory, discovery_info)],
True)
else:
add_entities([HomeKitWindowCover(accessory, discovery_info)],
True)
class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice):
"""Representation of a HomeKit Garage Door."""
def __init__(self, accessory, discovery_info):
"""Initialise the Cover."""
super().__init__(accessory, discovery_info)
self._name = None
self._state = None
self._obstruction_detected = None
self.lock_state = None
@property
def device_class(self):
"""Define this cover as a garage door."""
return 'garage'
def update_characteristics(self, characteristics):
"""Synchronise the Cover state with Home Assistant."""
# pylint: disable=import-error
from homekit.model.characteristics import CharacteristicsTypes
for characteristic in characteristics:
ctype = characteristic['type']
ctype = CharacteristicsTypes.get_short(ctype)
if ctype == "door-state.current":
self._chars['door-state.current'] = \
characteristic['iid']
self._state = CURRENT_GARAGE_STATE_MAP[characteristic['value']]
elif ctype == "door-state.target":
self._chars['door-state.target'] = \
characteristic['iid']
elif ctype == "obstruction-detected":
self._chars['obstruction-detected'] = characteristic['iid']
self._obstruction_detected = characteristic['value']
elif ctype == "name":
self._chars['name'] = characteristic['iid']
self._name = characteristic['value']
@property
def name(self):
"""Return the name of the cover."""
return self._name
@property
def available(self):
"""Return True if entity is available."""
return self._state is not None
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_OPEN | SUPPORT_CLOSE
@property
def is_closed(self):
"""Return true if cover is closed, else False."""
return self._state == STATE_CLOSED
@property
def is_closing(self):
"""Return if the cover is closing or not."""
return self._state == STATE_CLOSING
@property
def is_opening(self):
"""Return if the cover is opening or not."""
return self._state == STATE_OPENING
def open_cover(self, **kwargs):
"""Send open command."""
self.set_door_state(STATE_OPEN)
def close_cover(self, **kwargs):
"""Send close command."""
self.set_door_state(STATE_CLOSED)
def set_door_state(self, state):
"""Send state command."""
characteristics = [{'aid': self._aid,
'iid': self._chars['door-state.target'],
'value': TARGET_GARAGE_STATE_MAP[state]}]
self.put_characteristics(characteristics)
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
if self._obstruction_detected is None:
return None
return {
'obstruction-detected': self._obstruction_detected,
}
class HomeKitWindowCover(HomeKitEntity, CoverDevice):
"""Representation of a HomeKit Window or Window Covering."""
def __init__(self, accessory, discovery_info):
"""Initialise the Cover."""
super().__init__(accessory, discovery_info)
self._name = None
self._state = None
self._position = None
self._tilt_position = None
self._hold = None
self._obstruction_detected = None
self.lock_state = None
@property
def available(self):
"""Return True if entity is available."""
return self._state is not None
def update_characteristics(self, characteristics):
"""Synchronise the Cover state with Home Assistant."""
# pylint: disable=import-error
from homekit.model.characteristics import CharacteristicsTypes
for characteristic in characteristics:
ctype = characteristic['type']
ctype = CharacteristicsTypes.get_short(ctype)
if ctype == "position.state":
self._chars['position.state'] = \
characteristic['iid']
if 'value' in characteristic:
self._state = \
CURRENT_WINDOW_STATE_MAP[characteristic['value']]
elif ctype == "position.current":
self._chars['position.current'] = \
characteristic['iid']
self._position = characteristic['value']
elif ctype == "position.target":
self._chars['position.target'] = \
characteristic['iid']
elif ctype == "position.hold":
self._chars['position.hold'] = characteristic['iid']
if 'value' in characteristic:
self._hold = characteristic['value']
elif ctype == "vertical-tilt.current":
self._chars['vertical-tilt.current'] = characteristic['iid']
if characteristic['value'] is not None:
self._tilt_position = characteristic['value']
elif ctype == "horizontal-tilt.current":
self._chars['horizontal-tilt.current'] = characteristic['iid']
if characteristic['value'] is not None:
self._tilt_position = characteristic['value']
elif ctype == "vertical-tilt.target":
self._chars['vertical-tilt.target'] = \
characteristic['iid']
elif ctype == "horizontal-tilt.target":
self._chars['vertical-tilt.target'] = \
characteristic['iid']
elif ctype == "obstruction-detected":
self._chars['obstruction-detected'] = characteristic['iid']
self._obstruction_detected = characteristic['value']
elif ctype == "name":
self._chars['name'] = characteristic['iid']
if 'value' in characteristic:
self._name = characteristic['value']
@property
def name(self):
"""Return the name of the cover."""
return self._name
@property
def supported_features(self):
"""Flag supported features."""
supported_features = (
SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION)
if self._tilt_position is not None:
supported_features |= (
SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT |
SUPPORT_SET_TILT_POSITION)
return supported_features
@property
def current_cover_position(self):
"""Return the current position of cover."""
return self._position
@property
def is_closed(self):
"""Return true if cover is closed, else False."""
return self._position == 0
@property
def is_closing(self):
"""Return if the cover is closing or not."""
return self._state == STATE_CLOSING
@property
def is_opening(self):
"""Return if the cover is opening or not."""
return self._state == STATE_OPENING
def open_cover(self, **kwargs):
"""Send open command."""
self.set_cover_position(position=100)
def close_cover(self, **kwargs):
"""Send close command."""
self.set_cover_position(position=0)
def set_cover_position(self, **kwargs):
"""Send position command."""
position = kwargs[ATTR_POSITION]
characteristics = [{'aid': self._aid,
'iid': self._chars['position.target'],
'value': position}]
self.put_characteristics(characteristics)
@property
def current_cover_tilt_position(self):
"""Return current position of cover tilt."""
return self._tilt_position
def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
tilt_position = kwargs[ATTR_TILT_POSITION]
if 'vertical-tilt.target' in self._chars:
characteristics = [{'aid': self._aid,
'iid': self._chars['vertical-tilt.target'],
'value': tilt_position}]
self.put_characteristics(characteristics)
elif 'horizontal-tilt.target' in self._chars:
characteristics = [{'aid': self._aid,
'iid':
self._chars['horizontal-tilt.target'],
'value': tilt_position}]
self.put_characteristics(characteristics)
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
state_attributes = {}
if self._obstruction_detected is not None:
state_attributes['obstruction-detected'] = \
self._obstruction_detected
if self._hold is not None:
state_attributes['hold-position'] = \
self._hold
return state_attributes

View File

@@ -46,7 +46,7 @@ COVER_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_SCHEMA),
})

View File

@@ -18,7 +18,8 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['scsgate']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_DEVICES): vol.Schema({cv.slug: scsgate.SCSGATE_SCHEMA}),
vol.Required(CONF_DEVICES):
cv.schema_with_slug_keys(scsgate.SCSGATE_SCHEMA),
})

View File

@@ -67,7 +67,7 @@ COVER_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_SCHEMA),
})

View File

@@ -26,7 +26,7 @@ COVER_SCHEMA = vol.Schema({
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_SCHEMA),
})
DEPENDENCIES = ['velbus']

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Ger\u00e4t ist bereits konfiguriert",
"device_fail": "Unerwarteter Fehler beim Erstellen des Ger\u00e4ts.",
"device_timeout": "Zeit\u00fcberschreitung beim Verbinden mit dem Ger\u00e4t."
},
"step": {
"user": {
"data": {
"host": "Host"
},
"description": "Geben Sie die IP-Adresse Ihrer Daikin AC ein.",
"title": "Daikin AC konfigurieren"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,11 @@
{
"config": {
"step": {
"user": {
"data": {
"host": "Kiszolg\u00e1l\u00f3"
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Apparaat is al geconfigureerd",
"device_fail": "Onverwachte fout bij het aanmaken van een apparaat.",
"device_timeout": "Time-out voor verbinding met het apparaat."
},
"step": {
"user": {
"data": {
"host": "Host"
},
"description": "Voer het IP-adres van uw Daikin AC in.",
"title": "Daikin AC instellen"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Enheten er allerede konfigurert",
"device_fail": "Uventet feil under oppretting av enheten.",
"device_timeout": "Tidsavbrudd for tilkobling til enheten."
},
"step": {
"user": {
"data": {
"host": "Vert"
},
"description": "Angi IP-adressen til din Daikin AC.",
"title": "Konfigurer Daikin AC"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane",
"device_fail": "Nieoczekiwany b\u0142\u0105d tworzenia urz\u0105dzenia.",
"device_timeout": "Limit czasu pod\u0142\u0105czenia do urz\u0105dzenia."
},
"step": {
"user": {
"data": {
"host": "Host"
},
"description": "Wprowad\u017a adres IP Daikin AC.",
"title": "Konfiguracja Daikin AC"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,16 @@
{
"config": {
"abort": {
"already_configured": "O dispositivo j\u00e1 est\u00e1 configurado",
"device_fail": "Erro inesperado ao criar dispositivo.",
"device_timeout": "Excedido tempo limite conectando ao dispositivo"
},
"step": {
"user": {
"description": "Digite o endere\u00e7o IP do seu AC Daikin.",
"title": "Configurar o AC Daikin"
}
},
"title": "AC Daikin"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "O dispositivo j\u00e1 est\u00e1 configurado",
"device_fail": "Erro inesperado ao criar dispositivo.",
"device_timeout": "Tempo excedido a tentar ligar ao dispositivo."
},
"step": {
"user": {
"data": {
"host": "Servidor"
},
"description": "Introduza o endere\u00e7o IP do seu Daikin AC.",
"title": "Configurar o Daikin AC"
}
},
"title": "Daikin AC"
}
}

View File

@@ -12,7 +12,7 @@
"init": {
"data": {
"host": "Host",
"port": "Port (Standartwert : '80')"
"port": "Port"
},
"title": "Definiere das deCONZ-Gateway"
},

View File

@@ -0,0 +1,13 @@
{
"config": {
"step": {
"init": {
"data": {
"host": "",
"port": ""
}
}
},
"title": "deCONZ Zigbee l\u00fc\u00fcs"
}
}

View File

@@ -12,7 +12,7 @@
"init": {
"data": {
"host": "Gostitelj",
"port": "Vrata (privzeta vrednost: '80')"
"port": "Vrata"
},
"title": "Dolo\u010dite deCONZ prehod"
},
@@ -28,6 +28,6 @@
"title": "Dodatne mo\u017enosti konfiguracije za deCONZ"
}
},
"title": "deCONZ"
"title": "deCONZ Zigbee prehod"
}
}

View File

@@ -0,0 +1,89 @@
"""
Support for deCONZ binary sensor.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.deconz/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .const import (
ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN)
from .deconz_device import DeconzDevice
DEPENDENCIES = ['deconz']
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old way of setting up deCONZ binary sensors."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ binary sensor."""
gateway = hass.data[DECONZ_DOMAIN]
@callback
def async_add_sensor(sensors):
"""Add binary sensor from deCONZ."""
from pydeconz.sensor import DECONZ_BINARY_SENSOR
entities = []
allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True)
for sensor in sensors:
if sensor.type in DECONZ_BINARY_SENSOR and \
not (not allow_clip_sensor and sensor.type.startswith('CLIP')):
entities.append(DeconzBinarySensor(sensor, gateway))
async_add_entities(entities, True)
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor))
async_add_sensor(gateway.api.sensors.values())
class DeconzBinarySensor(DeconzDevice, BinarySensorDevice):
"""Representation of a deCONZ binary sensor."""
@callback
def async_update_callback(self, reason):
"""Update the sensor's state.
If reason is that state is updated,
or reachable has changed or battery has changed.
"""
if reason['state'] or \
'reachable' in reason['attr'] or \
'battery' in reason['attr'] or \
'on' in reason['attr']:
self.async_schedule_update_ha_state()
@property
def is_on(self):
"""Return true if sensor is on."""
return self._device.is_tripped
@property
def device_class(self):
"""Return the class of the sensor."""
return self._device.sensor_class
@property
def icon(self):
"""Return the icon to use in the frontend."""
return self._device.sensor_icon
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
from pydeconz.sensor import PRESENCE
attr = {}
if self._device.battery:
attr[ATTR_BATTERY_LEVEL] = self._device.battery
if self._device.on is not None:
attr[ATTR_ON] = self._device.on
if self._device.type in PRESENCE and self._device.dark is not None:
attr[ATTR_DARK] = self._device.dark
return attr

View File

@@ -4,16 +4,15 @@ Support for deCONZ covers.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.deconz/
"""
from homeassistant.components.deconz.const import (
COVER_TYPES, DAMPERS, DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN,
WINDOW_COVERS)
from homeassistant.components.cover import (
ATTR_POSITION, CoverDevice, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP,
SUPPORT_SET_POSITION)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .const import COVER_TYPES, DAMPERS, DOMAIN as DECONZ_DOMAIN, WINDOW_COVERS
from .deconz_device import DeconzDevice
DEPENDENCIES = ['deconz']
ZIGBEE_SPEC = ['lumi.curtain']
@@ -50,67 +49,36 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_cover(gateway.api.lights.values())
class DeconzCover(CoverDevice):
class DeconzCover(DeconzDevice, CoverDevice):
"""Representation of a deCONZ cover."""
def __init__(self, cover, gateway):
def __init__(self, device, gateway):
"""Set up cover and add update callback to get data from websocket."""
self._cover = cover
self.gateway = gateway
self.unsub_dispatcher = None
super().__init__(device, gateway)
self._features = SUPPORT_OPEN
self._features |= SUPPORT_CLOSE
self._features |= SUPPORT_STOP
self._features |= SUPPORT_SET_POSITION
async def async_added_to_hass(self):
"""Subscribe to covers events."""
self._cover.register_async_callback(self.async_update_callback)
self.gateway.deconz_ids[self.entity_id] = self._cover.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect cover object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._cover.remove_callback(self.async_update_callback)
self._cover = None
@callback
def async_update_callback(self, reason):
"""Update the cover's state."""
self.async_schedule_update_ha_state()
@property
def current_cover_position(self):
"""Return the current position of the cover."""
if self.is_closed:
return 0
return int(self._cover.brightness / 255 * 100)
return int(self._device.brightness / 255 * 100)
@property
def is_closed(self):
"""Return if the cover is closed."""
return not self._cover.state
@property
def name(self):
"""Return the name of the cover."""
return self._cover.name
@property
def unique_id(self):
"""Return a unique identifier for this cover."""
return self._cover.uniqueid
return not self._device.state
@property
def device_class(self):
"""Return the class of the cover."""
if self._cover.type in DAMPERS:
if self._device.type in DAMPERS:
return 'damper'
if self._cover.type in WINDOW_COVERS:
if self._device.type in WINDOW_COVERS:
return 'window'
@property
@@ -118,16 +86,6 @@ class DeconzCover(CoverDevice):
"""Flag supported features."""
return self._features
@property
def available(self):
"""Return True if light is available."""
return self.gateway.available and self._cover.reachable
@property
def should_poll(self):
"""No polling needed."""
return False
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
position = kwargs[ATTR_POSITION]
@@ -135,7 +93,7 @@ class DeconzCover(CoverDevice):
if position > 0:
data['on'] = True
data['bri'] = int(position / 100 * 255)
await self._cover.async_set_state(data)
await self._device.async_set_state(data)
async def async_open_cover(self, **kwargs):
"""Open cover."""
@@ -150,25 +108,7 @@ class DeconzCover(CoverDevice):
async def async_stop_cover(self, **kwargs):
"""Stop cover."""
data = {'bri_inc': 0}
await self._cover.async_set_state(data)
@property
def device_info(self):
"""Return a device description for device registry."""
if (self._cover.uniqueid is None or
self._cover.uniqueid.count(':') != 7):
return None
serial = self._cover.uniqueid.split('-', 1)[0]
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
'manufacturer': self._cover.manufacturer,
'model': self._cover.modelid,
'name': self._cover.name,
'sw_version': self._cover.swversion,
'via_hub': (DECONZ_DOMAIN, bridgeid),
}
await self._device.async_set_state(data)
class DeconzCoverZigbeeSpec(DeconzCover):
@@ -177,12 +117,12 @@ class DeconzCoverZigbeeSpec(DeconzCover):
@property
def current_cover_position(self):
"""Return the current position of the cover."""
return 100 - int(self._cover.brightness / 255 * 100)
return 100 - int(self._device.brightness / 255 * 100)
@property
def is_closed(self):
"""Return if the cover is closed."""
return self._cover.state
return self._device.state
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
@@ -191,4 +131,4 @@ class DeconzCoverZigbeeSpec(DeconzCover):
if position < 100:
data['on'] = True
data['bri'] = 255 - int(position / 100 * 255)
await self._cover.async_set_state(data)
await self._device.async_set_state(data)

View File

@@ -0,0 +1,74 @@
"""Base class for deCONZ devices."""
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from .const import DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN
class DeconzDevice(Entity):
"""Representation of a deCONZ device."""
def __init__(self, device, gateway):
"""Set up device and add update callback to get data from websocket."""
self._device = device
self.gateway = gateway
self.unsub_dispatcher = None
async def async_added_to_hass(self):
"""Subscribe to device events."""
self._device.register_async_callback(self.async_update_callback)
self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect device object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._device.remove_callback(self.async_update_callback)
self._device = None
@callback
def async_update_callback(self, reason):
"""Update the device's state."""
self.async_schedule_update_ha_state()
@property
def name(self):
"""Return the name of the device."""
return self._device.name
@property
def unique_id(self):
"""Return a unique identifier for this device."""
return self._device.uniqueid
@property
def available(self):
"""Return True if device is available."""
return self.gateway.available and self._device.reachable
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_info(self):
"""Return a device description for device registry."""
if (self._device.uniqueid is None or
self._device.uniqueid.count(':') != 7):
return None
serial = self._device.uniqueid.split('-', 1)[0]
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
'manufacturer': self._device.manufacturer,
'model': self._device.modelid,
'name': self._device.name,
'sw_version': self._device.swversion,
'via_hub': (DECONZ_DOMAIN, bridgeid),
}

View File

@@ -4,19 +4,20 @@ Support for deCONZ light.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/light.deconz/
"""
from homeassistant.components.deconz.const import (
CONF_ALLOW_DECONZ_GROUPS, DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN,
COVER_TYPES, SWITCH_TYPES)
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_HS_COLOR,
ATTR_TRANSITION, EFFECT_COLORLOOP, FLASH_LONG, FLASH_SHORT,
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT,
SUPPORT_FLASH, SUPPORT_TRANSITION, Light)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.util.color as color_util
from .const import (
CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DECONZ_DOMAIN, COVER_TYPES,
SWITCH_TYPES)
from .deconz_device import DeconzDevice
DEPENDENCIES = ['deconz']
@@ -59,51 +60,30 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_group(gateway.api.groups.values())
class DeconzLight(Light):
class DeconzLight(DeconzDevice, Light):
"""Representation of a deCONZ light."""
def __init__(self, light, gateway):
def __init__(self, device, gateway):
"""Set up light and add update callback to get data from websocket."""
self._light = light
self.gateway = gateway
self.unsub_dispatcher = None
super().__init__(device, gateway)
self._features = SUPPORT_BRIGHTNESS
self._features |= SUPPORT_FLASH
self._features |= SUPPORT_TRANSITION
if self._light.ct is not None:
if self._device.ct is not None:
self._features |= SUPPORT_COLOR_TEMP
if self._light.xy is not None:
if self._device.xy is not None:
self._features |= SUPPORT_COLOR
if self._light.effect is not None:
if self._device.effect is not None:
self._features |= SUPPORT_EFFECT
async def async_added_to_hass(self):
"""Subscribe to lights events."""
self._light.register_async_callback(self.async_update_callback)
self.gateway.deconz_ids[self.entity_id] = self._light.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect light object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._light.remove_callback(self.async_update_callback)
self._light = None
@callback
def async_update_callback(self, reason):
"""Update the light's state."""
self.async_schedule_update_ha_state()
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
return self._light.brightness
return self._device.brightness
@property
def effect_list(self):
@@ -113,48 +93,28 @@ class DeconzLight(Light):
@property
def color_temp(self):
"""Return the CT color value."""
if self._light.colormode != 'ct':
if self._device.colormode != 'ct':
return None
return self._light.ct
return self._device.ct
@property
def hs_color(self):
"""Return the hs color value."""
if self._light.colormode in ('xy', 'hs') and self._light.xy:
return color_util.color_xy_to_hs(*self._light.xy)
if self._device.colormode in ('xy', 'hs') and self._device.xy:
return color_util.color_xy_to_hs(*self._device.xy)
return None
@property
def is_on(self):
"""Return true if light is on."""
return self._light.state
@property
def name(self):
"""Return the name of the light."""
return self._light.name
@property
def unique_id(self):
"""Return a unique identifier for this light."""
return self._light.uniqueid
return self._device.state
@property
def supported_features(self):
"""Flag supported features."""
return self._features
@property
def available(self):
"""Return True if light is available."""
return self.gateway.available and self._light.reachable
@property
def should_poll(self):
"""No polling needed."""
return False
async def async_turn_on(self, **kwargs):
"""Turn on light."""
data = {'on': True}
@@ -185,7 +145,7 @@ class DeconzLight(Light):
else:
data['effect'] = 'none'
await self._light.async_set_state(data)
await self._device.async_set_state(data)
async def async_turn_off(self, **kwargs):
"""Turn off light."""
@@ -203,31 +163,13 @@ class DeconzLight(Light):
data['alert'] = 'lselect'
del data['on']
await self._light.async_set_state(data)
await self._device.async_set_state(data)
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attributes = {}
attributes['is_deconz_group'] = self._light.type == 'LightGroup'
if self._light.type == 'LightGroup':
attributes['all_on'] = self._light.all_on
attributes['is_deconz_group'] = self._device.type == 'LightGroup'
if self._device.type == 'LightGroup':
attributes['all_on'] = self._device.all_on
return attributes
@property
def device_info(self):
"""Return a device description for device registry."""
if (self._light.uniqueid is None or
self._light.uniqueid.count(':') != 7):
return None
serial = self._light.uniqueid.split('-', 1)[0]
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
'manufacturer': self._light.manufacturer,
'model': self._light.modelid,
'name': self._light.name,
'sw_version': self._light.swversion,
'via_hub': (DECONZ_DOMAIN, bridgeid),
}

View File

@@ -0,0 +1,153 @@
"""
Support for deCONZ sensor.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/sensor.deconz/
"""
from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.util import slugify
from .const import (
ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN)
from .deconz_device import DeconzDevice
DEPENDENCIES = ['deconz']
ATTR_CURRENT = 'current'
ATTR_DAYLIGHT = 'daylight'
ATTR_EVENT_ID = 'event_id'
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old way of setting up deCONZ sensors."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ sensors."""
gateway = hass.data[DECONZ_DOMAIN]
@callback
def async_add_sensor(sensors):
"""Add sensors from deCONZ."""
from pydeconz.sensor import DECONZ_SENSOR, SWITCH as DECONZ_REMOTE
entities = []
allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True)
for sensor in sensors:
if sensor.type in DECONZ_SENSOR and \
not (not allow_clip_sensor and sensor.type.startswith('CLIP')):
if sensor.type in DECONZ_REMOTE:
if sensor.battery:
entities.append(DeconzBattery(sensor, gateway))
else:
entities.append(DeconzSensor(sensor, gateway))
async_add_entities(entities, True)
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor))
async_add_sensor(gateway.api.sensors.values())
class DeconzSensor(DeconzDevice):
"""Representation of a deCONZ sensor."""
@callback
def async_update_callback(self, reason):
"""Update the sensor's state.
If reason is that state is updated,
or reachable has changed or battery has changed.
"""
if reason['state'] or \
'reachable' in reason['attr'] or \
'battery' in reason['attr'] or \
'on' in reason['attr']:
self.async_schedule_update_ha_state()
@property
def state(self):
"""Return the state of the sensor."""
return self._device.state
@property
def device_class(self):
"""Return the class of the sensor."""
return self._device.sensor_class
@property
def icon(self):
"""Return the icon to use in the frontend."""
return self._device.sensor_icon
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this sensor."""
return self._device.sensor_unit
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
from pydeconz.sensor import LIGHTLEVEL
attr = {}
if self._device.battery:
attr[ATTR_BATTERY_LEVEL] = self._device.battery
if self._device.on is not None:
attr[ATTR_ON] = self._device.on
if self._device.type in LIGHTLEVEL and self._device.dark is not None:
attr[ATTR_DARK] = self._device.dark
if self.unit_of_measurement == 'Watts':
attr[ATTR_CURRENT] = self._device.current
attr[ATTR_VOLTAGE] = self._device.voltage
if self._device.sensor_class == 'daylight':
attr[ATTR_DAYLIGHT] = self._device.daylight
return attr
class DeconzBattery(DeconzDevice):
"""Battery class for when a device is only represented as an event."""
def __init__(self, device, gateway):
"""Register dispatcher callback for update of battery state."""
super().__init__(device, gateway)
self._name = '{} {}'.format(self._device.name, 'Battery Level')
self._unit_of_measurement = "%"
@callback
def async_update_callback(self, reason):
"""Update the battery's state, if needed."""
if 'reachable' in reason['attr'] or 'battery' in reason['attr']:
self.async_schedule_update_ha_state()
@property
def state(self):
"""Return the state of the battery."""
return self._device.battery
@property
def name(self):
"""Return the name of the battery."""
return self._name
@property
def device_class(self):
"""Return the class of the sensor."""
return DEVICE_CLASS_BATTERY
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity."""
return self._unit_of_measurement
@property
def device_state_attributes(self):
"""Return the state attributes of the battery."""
attr = {
ATTR_EVENT_ID: slugify(self._device.name),
}
return attr

View File

@@ -0,0 +1,83 @@
"""
Support for deCONZ switches.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.deconz/
"""
from homeassistant.components.switch import SwitchDevice
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .const import DOMAIN as DECONZ_DOMAIN, POWER_PLUGS, SIRENS
from .deconz_device import DeconzDevice
DEPENDENCIES = ['deconz']
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old way of setting up deCONZ switches."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up switches for deCONZ component.
Switches are based same device class as lights in deCONZ.
"""
gateway = hass.data[DECONZ_DOMAIN]
@callback
def async_add_switch(lights):
"""Add switch from deCONZ."""
entities = []
for light in lights:
if light.type in POWER_PLUGS:
entities.append(DeconzPowerPlug(light, gateway))
elif light.type in SIRENS:
entities.append(DeconzSiren(light, gateway))
async_add_entities(entities, True)
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_light', async_add_switch))
async_add_switch(gateway.api.lights.values())
class DeconzPowerPlug(DeconzDevice, SwitchDevice):
"""Representation of a deCONZ power plug."""
@property
def is_on(self):
"""Return true if switch is on."""
return self._device.state
async def async_turn_on(self, **kwargs):
"""Turn on switch."""
data = {'on': True}
await self._device.async_set_state(data)
async def async_turn_off(self, **kwargs):
"""Turn off switch."""
data = {'on': False}
await self._device.async_set_state(data)
class DeconzSiren(DeconzDevice, SwitchDevice):
"""Representation of a deCONZ siren."""
@property
def is_on(self):
"""Return true if switch is on."""
return self._device.alert == 'lselect'
async def async_turn_on(self, **kwargs):
"""Turn on switch."""
data = {'alert': 'lselect'}
await self._device.async_set_state(data)
async def async_turn_off(self, **kwargs):
"""Turn off switch."""
data = {'alert': 'none'}
await self._device.async_set_state(data)

View File

@@ -15,7 +15,7 @@ import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pygatt==3.2.0']
REQUIREMENTS = ['pygatt[GATTTOOL]==3.2.0']
BLE_PREFIX = 'BLE_'
MIN_SEEN_NEW = 5

View File

@@ -19,7 +19,7 @@ from homeassistant.helpers.event import track_time_interval
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import slugify, dt as dt_util
REQUIREMENTS = ['locationsharinglib==3.0.9']
REQUIREMENTS = ['locationsharinglib==3.0.11']
_LOGGER = logging.getLogger(__name__)

View File

@@ -89,5 +89,7 @@ class GoogleHomeDeviceScanner(DeviceScanner):
devices[uuid]['btle_mac_address'] = device['mac_address']
devices[uuid]['ghname'] = ghname
devices[uuid]['source_type'] = 'bluetooth'
if device['name']:
devices[uuid]['btle_name'] = device['name']
await self.scanner.clear_scan_result()
self.last_results = devices

View File

@@ -5,104 +5,28 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.gpslogger/
"""
import logging
from hmac import compare_digest
from aiohttp.web import Request, HTTPUnauthorized
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_PASSWORD, HTTP_UNPROCESSABLE_ENTITY
)
from homeassistant.components.http import (
CONF_API_PASSWORD, HomeAssistantView
)
# pylint: disable=unused-import
from homeassistant.components.device_tracker import ( # NOQA
DOMAIN, PLATFORM_SCHEMA
)
from homeassistant.components.gpslogger import TRACKER_UPDATE
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.typing import HomeAssistantType, ConfigType
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PASSWORD): cv.string,
})
DEPENDENCIES = ['gpslogger']
async def async_setup_scanner(hass: HomeAssistantType, config: ConfigType,
async_see, discovery_info=None):
"""Set up an endpoint for the GPSLogger application."""
hass.http.register_view(GPSLoggerView(async_see, config))
return True
class GPSLoggerView(HomeAssistantView):
"""View to handle GPSLogger requests."""
url = '/api/gpslogger'
name = 'api:gpslogger'
def __init__(self, async_see, config):
"""Initialize GPSLogger url endpoints."""
self.async_see = async_see
self._password = config.get(CONF_PASSWORD)
# this component does not require external authentication if
# password is set
self.requires_auth = self._password is None
async def get(self, request: Request):
"""Handle for GPSLogger message received as GET."""
hass = request.app['hass']
data = request.query
if self._password is not None:
authenticated = CONF_API_PASSWORD in data and compare_digest(
self._password,
data[CONF_API_PASSWORD]
)
if not authenticated:
raise HTTPUnauthorized()
if 'latitude' not in data or 'longitude' not in data:
return ('Latitude and longitude not specified.',
HTTP_UNPROCESSABLE_ENTITY)
if 'device' not in data:
_LOGGER.error("Device id not specified")
return ('Device id not specified.',
HTTP_UNPROCESSABLE_ENTITY)
device = data['device'].replace('-', '')
gps_location = (data['latitude'], data['longitude'])
accuracy = 200
battery = -1
if 'accuracy' in data:
accuracy = int(float(data['accuracy']))
if 'battery' in data:
battery = float(data['battery'])
attrs = {}
if 'speed' in data:
attrs['speed'] = float(data['speed'])
if 'direction' in data:
attrs['direction'] = float(data['direction'])
if 'altitude' in data:
attrs['altitude'] = float(data['altitude'])
if 'provider' in data:
attrs['provider'] = data['provider']
if 'activity' in data:
attrs['activity'] = data['activity']
hass.async_create_task(self.async_see(
"""Set up an endpoint for the GPSLogger device tracker."""
async def _set_location(device, gps_location, battery, accuracy, attrs):
"""Fire HA event to set location."""
await async_see(
dev_id=device,
gps=gps_location, battery=battery,
gps=gps_location,
battery=battery,
gps_accuracy=accuracy,
attributes=attrs
))
)
return 'Setting location for {}'.format(device)
async_dispatcher_connect(hass, TRACKER_UPDATE, _set_location)
return True

View File

@@ -19,7 +19,7 @@ from homeassistant.const import (
INTERFACES = 2
DEFAULT_TIMEOUT = 10
REQUIREMENTS = ['beautifulsoup4==4.6.3']
REQUIREMENTS = ['beautifulsoup4==4.7.1']
_LOGGER = logging.getLogger(__name__)
@@ -81,13 +81,14 @@ class LinksysAPDeviceScanner(DeviceScanner):
request = self._make_request(interface)
self.last_results.extend(
[x.find_all('td')[1].text
for x in BS(request.content, "html.parser")
for x in BS(request.content, 'html.parser')
.find_all(class_='section-row')]
)
return True
def _make_request(self, unit=0):
"""Create a request to get the data."""
# No, the '&&' is not a typo - this is expected by the web interface.
login = base64.b64encode(bytes(self.username, 'utf8')).decode('ascii')
pwd = base64.b64encode(bytes(self.password, 'utf8')).decode('ascii')

View File

@@ -4,106 +4,25 @@ Support for the Locative platform.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.locative/
"""
from functools import partial
import logging
from homeassistant.const import (
ATTR_LATITUDE, ATTR_LONGITUDE, STATE_NOT_HOME, HTTP_UNPROCESSABLE_ENTITY)
from homeassistant.components.http import HomeAssistantView
# pylint: disable=unused-import
from homeassistant.components.device_tracker import ( # NOQA
DOMAIN, PLATFORM_SCHEMA)
from homeassistant.components.locative import TRACKER_UPDATE
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
URL = '/api/locative'
DEPENDENCIES = ['locative']
def setup_scanner(hass, config, see, discovery_info=None):
"""Set up an endpoint for the Locative application."""
hass.http.register_view(LocativeView(see))
async def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up an endpoint for the Locative device tracker."""
async def _set_location(device, gps_location, location_name):
"""Fire HA event to set location."""
await async_see(
dev_id=device,
gps=gps_location,
location_name=location_name
)
async_dispatcher_connect(hass, TRACKER_UPDATE, _set_location)
return True
class LocativeView(HomeAssistantView):
"""View to handle Locative requests."""
url = URL
name = 'api:locative'
def __init__(self, see):
"""Initialize Locative URL endpoints."""
self.see = see
async def get(self, request):
"""Locative message received as GET."""
res = await self._handle(request.app['hass'], request.query)
return res
async def post(self, request):
"""Locative message received."""
data = await request.post()
res = await self._handle(request.app['hass'], data)
return res
async def _handle(self, hass, data):
"""Handle locative request."""
if 'latitude' not in data or 'longitude' not in data:
return ('Latitude and longitude not specified.',
HTTP_UNPROCESSABLE_ENTITY)
if 'device' not in data:
_LOGGER.error('Device id not specified.')
return ('Device id not specified.',
HTTP_UNPROCESSABLE_ENTITY)
if 'trigger' not in data:
_LOGGER.error('Trigger is not specified.')
return ('Trigger is not specified.',
HTTP_UNPROCESSABLE_ENTITY)
if 'id' not in data and data['trigger'] != 'test':
_LOGGER.error('Location id not specified.')
return ('Location id not specified.',
HTTP_UNPROCESSABLE_ENTITY)
device = data['device'].replace('-', '')
location_name = data.get('id', data['trigger']).lower()
direction = data['trigger']
gps_location = (data[ATTR_LATITUDE], data[ATTR_LONGITUDE])
if direction == 'enter':
await hass.async_add_job(
partial(self.see, dev_id=device, location_name=location_name,
gps=gps_location))
return 'Setting location to {}'.format(location_name)
if direction == 'exit':
current_state = hass.states.get(
'{}.{}'.format(DOMAIN, device))
if current_state is None or current_state.state == location_name:
location_name = STATE_NOT_HOME
await hass.async_add_job(
partial(self.see, dev_id=device,
location_name=location_name, gps=gps_location))
return 'Setting location to not home'
# Ignore the message if it is telling us to exit a zone that we
# aren't currently in. This occurs when a zone is entered
# before the previous zone was exited. The enter message will
# be sent first, then the exit message will be sent second.
return 'Ignoring exit from {} (already in {})'.format(
location_name, current_state)
if direction == 'test':
# In the app, a test message can be sent. Just return something to
# the user to let them know that it works.
return 'Received test message.'
_LOGGER.error('Received unidentified message from Locative: %s',
direction)
return ('Received unidentified message: {}'.format(direction),
HTTP_UNPROCESSABLE_ENTITY)

View File

@@ -14,7 +14,7 @@ async def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up the MySensors device scanner."""
new_devices = mysensors.setup_mysensors_platform(
hass, DOMAIN, discovery_info, MySensorsDeviceScanner,
device_args=(async_see, ))
device_args=(hass, async_see))
if not new_devices:
return False
@@ -37,12 +37,13 @@ async def async_setup_scanner(hass, config, async_see, discovery_info=None):
class MySensorsDeviceScanner(mysensors.device.MySensorsDevice):
"""Represent a MySensors scanner."""
def __init__(self, async_see, *args):
def __init__(self, hass, async_see, *args):
"""Set up instance."""
super().__init__(*args)
self.async_see = async_see
self.hass = hass
async def async_update_callback(self):
async def _async_update_callback(self):
"""Update the device."""
await self.async_update()
node = self.gateway.sensors[self.node_id]

View File

@@ -14,7 +14,7 @@ from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST
REQUIREMENTS = ['pysnmp==4.4.6']
REQUIREMENTS = ['pysnmp==4.4.8']
_LOGGER = logging.getLogger(__name__)

View File

@@ -15,7 +15,7 @@ from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import CONF_VERIFY_SSL, CONF_MONITORED_CONDITIONS
import homeassistant.util.dt as dt_util
REQUIREMENTS = ['pyunifi==2.13']
REQUIREMENTS = ['pyunifi==2.16']
_LOGGER = logging.getLogger(__name__)
CONF_PORT = 'port'

View File

@@ -131,6 +131,6 @@ def _response_to_json(response):
active_clients[client.get("mac")] = client
return active_clients
except ValueError:
except (ValueError, TypeError):
_LOGGER.error("Failed to decode response from AP.")
return {}

View File

@@ -10,7 +10,7 @@
"step": {
"user": {
"description": "Est\u00e0s segur que vols configurar Dialogflow?",
"title": "Configuraci\u00f3 del Webhook de Dialogflow"
"title": "Configuraci\u00f3 del Webhook Dialogflow"
}
},
"title": "Dialogflow"

View File

@@ -1,7 +1,15 @@
{
"config": {
"abort": {
"not_internet_accessible": "Ihre Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Dialogflow-Nachrichten empfangen zu k\u00f6nnen.",
"one_instance_allowed": "Nur eine einzige Instanz ist notwendig."
},
"create_entry": {
"default": "Um Ereignisse an den Home Assistant zu senden, m\u00fcssen Sie [Webhook-Integration von Dialogflow]({dialogflow_url}) einrichten. \n\nF\u00fcllen Sie die folgenden Informationen aus: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n - Inhaltstyp: application / json \n\nWeitere Informationen finden Sie in der [Dokumentation]({docs_url})."
},
"step": {
"user": {
"description": "M\u00f6chten Sie Dialogflow wirklich einrichten?",
"title": "Dialogflow Webhook einrichten"
}
},

View File

@@ -4,6 +4,9 @@
"not_internet_accessible": "Su instancia de Home Assistant debe ser accesible desde Internet para recibir mensajes de Dialogflow.",
"one_instance_allowed": "Solo una instancia es necesaria."
},
"create_entry": {
"default": "Para enviar eventos a Home Assistant, necesitas configurar [Integracion de flujos de dialogo de webhook]({dialogflow_url}).\n\nRellena la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nVer [Documentaci\u00f3n]({docs_url}) para mas detalles."
},
"step": {
"user": {
"description": "\u00bfEst\u00e1 seguro de que desea configurar Dialogflow?"

View File

@@ -4,6 +4,9 @@
"not_internet_accessible": "Uw Home Assistant instantie moet toegankelijk zijn vanaf het internet om Dialogflow-berichten te ontvangen.",
"one_instance_allowed": "Slechts \u00e9\u00e9n instantie is nodig."
},
"create_entry": {
"default": "Om evenementen naar de Home Assistant te verzenden, moet u [webhookintegratie van Dialogflow]({dialogflow_url}) instellen. \n\n Vul de volgende info in: \n\n - URL: `{webhook_url}` \n - Method: POST \n - Content Type: application/json \n\nZie [de documentatie]({docs_url}) voor verdere informatie."
},
"step": {
"user": {
"description": "Weet u zeker dat u Dialogflow wilt instellen?",

View File

@@ -47,6 +47,7 @@ SERVICE_OCTOPRINT = 'octoprint'
SERVICE_FREEBOX = 'freebox'
SERVICE_IGD = 'igd'
SERVICE_DLNA_DMR = 'dlna_dmr'
SERVICE_ROKU = 'roku'
CONFIG_ENTRY_HANDLERS = {
SERVICE_DAIKIN: 'daikin',
@@ -67,6 +68,7 @@ SERVICE_HANDLERS = {
SERVICE_HASSIO: ('hassio', None),
SERVICE_AXIS: ('axis', None),
SERVICE_APPLE_TV: ('apple_tv', None),
SERVICE_ROKU: ('roku', None),
SERVICE_WINK: ('wink', None),
SERVICE_XIAOMI_GW: ('xiaomi_aqara', None),
SERVICE_SABNZBD: ('sabnzbd', None),
@@ -76,7 +78,6 @@ SERVICE_HANDLERS = {
SERVICE_FREEBOX: ('freebox', None),
'panasonic_viera': ('media_player', 'panasonic_viera'),
'plex_mediaserver': ('media_player', 'plex'),
'roku': ('media_player', 'roku'),
'yamaha': ('media_player', 'yamaha'),
'logitech_mediaserver': ('media_player', 'squeezebox'),
'directv': ('media_player', 'directv'),

View File

@@ -6,15 +6,16 @@ https://home-assistant.io/components/doorbird/
"""
import logging
from urllib.error import HTTPError
import voluptuous as vol
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import CONF_HOST, CONF_USERNAME, \
CONF_PASSWORD, CONF_NAME, CONF_DEVICES, CONF_MONITORED_CONDITIONS
import homeassistant.helpers.config_validation as cv
from homeassistant.util import slugify
from homeassistant.util import slugify, dt as dt_util
REQUIREMENTS = ['doorbirdpy==2.0.4']
REQUIREMENTS = ['doorbirdpy==2.0.6']
_LOGGER = logging.getLogger(__name__)
@@ -25,6 +26,7 @@ API_URL = '/api/{}'.format(DOMAIN)
CONF_CUSTOM_URL = 'hass_url_override'
CONF_DOORBELL_EVENTS = 'doorbell_events'
CONF_DOORBELL_NUMS = 'doorbell_numbers'
CONF_RELAY_NUMS = 'relay_numbers'
CONF_MOTION_EVENTS = 'motion_events'
CONF_TOKEN = 'token'
@@ -37,6 +39,10 @@ SENSOR_TYPES = {
'name': 'Motion',
'device_class': 'motion',
},
'relay': {
'name': 'Relay',
'device_class': 'relay',
}
}
RESET_DEVICE_FAVORITES = 'doorbird_reset_favorites'
@@ -47,6 +53,8 @@ DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_DOORBELL_NUMS, default=[1]): vol.All(
cv.ensure_list, [cv.positive_int]),
vol.Optional(CONF_RELAY_NUMS, default=[1]): vol.All(
cv.ensure_list, [cv.positive_int]),
vol.Optional(CONF_CUSTOM_URL): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_MONITORED_CONDITIONS, default=[]):
@@ -80,6 +88,7 @@ def setup(hass, config):
username = doorstation_config.get(CONF_USERNAME)
password = doorstation_config.get(CONF_PASSWORD)
doorbell_nums = doorstation_config.get(CONF_DOORBELL_NUMS)
relay_nums = doorstation_config.get(CONF_RELAY_NUMS)
custom_url = doorstation_config.get(CONF_CUSTOM_URL)
events = doorstation_config.get(CONF_MONITORED_CONDITIONS)
name = (doorstation_config.get(CONF_NAME)
@@ -90,7 +99,7 @@ def setup(hass, config):
if status[0]:
doorstation = ConfiguredDoorBird(device, name, events, custom_url,
doorbell_nums, token)
doorbell_nums, relay_nums, token)
doorstations.append(doorstation)
_LOGGER.info('Connected to DoorBird "%s" as %s@%s',
doorstation.name, username, device_ip)
@@ -105,7 +114,18 @@ def setup(hass, config):
# Subscribe to doorbell or motion events
if events:
doorstation.update_schedule(hass)
try:
doorstation.update_schedule(hass)
except HTTPError:
hass.components.persistent_notification.create(
'Doorbird configuration failed. Please verify that API '
'Operator permission is enabled for the Doorbird user. '
'A restart will be required once permissions have been '
'verified.',
title='Doorbird Configuration Failure',
notification_id='doorbird_schedule_error')
return False
hass.data[DOMAIN] = doorstations
@@ -148,13 +168,15 @@ def handle_event(event):
class ConfiguredDoorBird():
"""Attach additional information to pass along with configured device."""
def __init__(self, device, name, events, custom_url, doorbell_nums, token):
def __init__(self, device, name, events, custom_url, doorbell_nums,
relay_nums, token):
"""Initialize configured device."""
self._name = name
self._device = device
self._custom_url = custom_url
self._monitored_events = events
self._doorbell_nums = doorbell_nums
self._relay_nums = relay_nums
self._token = token
@property
@@ -218,9 +240,9 @@ class ConfiguredDoorBird():
# Register HA URL as webhook if not already, then get the ID
if not self.webhook_is_registered(hass_url):
self.device.change_favorite('http',
'Home Assistant on {} ({} events)'
.format(hass_url, event), hass_url)
self.device.change_favorite('http', 'Home Assistant ({} events)'
.format(event), hass_url)
fav_id = self.get_webhook_id(hass_url)
if not fav_id:
@@ -239,6 +261,11 @@ class ConfiguredDoorBird():
entry = self.device.get_schedule_entry(event, str(doorbell))
entry.output.append(output)
self.device.change_schedule(entry)
elif event == 'relay':
# Repeat edit for each monitored doorbell number
for relay in self._relay_nums:
entry = self.device.get_schedule_entry(event, str(relay))
entry.output.append(output)
else:
entry = self.device.get_schedule_entry(event)
entry.output.append(output)
@@ -303,6 +330,16 @@ class ConfiguredDoorBird():
return None
def get_event_data(self):
"""Get data to pass along with HA event."""
return {
'timestamp': dt_util.utcnow().isoformat(),
'live_video_url': self._device.live_video_url,
'live_image_url': self._device.live_image_url,
'rtsp_live_video_url': self._device.rtsp_live_video_url,
'html5_viewer_url': self._device.html5_viewer_url
}
class DoorBirdRequestView(HomeAssistantView):
"""Provide a page for the device to call."""
@@ -330,7 +367,14 @@ class DoorBirdRequestView(HomeAssistantView):
if request_token == '' or not authenticated:
return web.Response(status=401, text='Unauthorized')
hass.bus.async_fire('{}_{}'.format(DOMAIN, sensor))
doorstation = get_doorstation_by_slug(hass, sensor)
if doorstation:
event_data = doorstation.get_event_data()
else:
event_data = {}
hass.bus.async_fire('{}_{}'.format(DOMAIN, sensor), event_data)
return web.Response(status=200, text='OK')

View File

@@ -0,0 +1,21 @@
{
"config": {
"abort": {
"name_exists": "El nom ja existeix"
},
"step": {
"user": {
"data": {
"advertise_ip": "IP d'advert\u00e8ncies",
"advertise_port": "Port d'advert\u00e8ncies",
"host_ip": "IP de l'amfitri\u00f3",
"listen_port": "Port d'escolta",
"name": "Nom",
"upnp_bind_multicast": "Enlla\u00e7ar multicast (true/false)"
},
"title": "Configuraci\u00f3 del servidor"
}
},
"title": "EmulatedRoku"
}
}

View File

@@ -0,0 +1,21 @@
{
"config": {
"abort": {
"name_exists": "Name already exists"
},
"step": {
"user": {
"data": {
"advertise_ip": "Advertise IP",
"advertise_port": "Advertise port",
"host_ip": "Host IP",
"listen_port": "Listen port",
"name": "Name",
"upnp_bind_multicast": "Bind multicast (True/False)"
},
"title": "Define server configuration"
}
},
"title": "EmulatedRoku"
}
}

View File

@@ -0,0 +1,13 @@
{
"config": {
"step": {
"user": {
"data": {
"host_ip": "",
"name": "Nimi"
}
}
},
"title": ""
}
}

View File

@@ -0,0 +1,21 @@
{
"config": {
"abort": {
"name_exists": "\uc774\ub984\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4"
},
"step": {
"user": {
"data": {
"advertise_ip": "\uad11\uace0 IP",
"advertise_port": "\uad11\uace0 \ud3ec\ud2b8",
"host_ip": "\ud638\uc2a4\ud2b8 IP",
"listen_port": "\uc218\uc2e0 \ud3ec\ud2b8",
"name": "\uc774\ub984",
"upnp_bind_multicast": "\uba40\ud2f0 \uce90\uc2a4\ud2b8 \ubc14\uc778\ub4dc (\ucc38/\uac70\uc9d3)"
},
"title": "\uc11c\ubc84 \uad6c\uc131 \uc815\uc758"
}
},
"title": "EmulatedRoku"
}
}

View File

@@ -0,0 +1,21 @@
{
"config": {
"abort": {
"name_exists": "Navnet eksisterer allerede"
},
"step": {
"user": {
"data": {
"advertise_ip": "Annonser IP",
"advertise_port": "Annonser port",
"host_ip": "Vert IP",
"listen_port": "Lytte port",
"name": "Navn",
"upnp_bind_multicast": "Bind multicast (True/False)"
},
"title": "Definer serverkonfigurasjon"
}
},
"title": "EmulatedRoku"
}
}

View File

@@ -0,0 +1,18 @@
{
"config": {
"abort": {
"name_exists": "\u0418\u043c\u044f \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442"
},
"step": {
"user": {
"data": {
"host_ip": "\u0425\u043e\u0441\u0442",
"listen_port": "\u041f\u043e\u0440\u0442",
"name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"
},
"title": "EmulatedRoku"
}
},
"title": "EmulatedRoku"
}
}

View File

@@ -0,0 +1,84 @@
"""
Support for Roku API emulation.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/emulated_roku/
"""
import voluptuous as vol
from homeassistant import config_entries, util
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
from .binding import EmulatedRoku
from .config_flow import configured_servers
from .const import (
CONF_ADVERTISE_IP, CONF_ADVERTISE_PORT, CONF_HOST_IP, CONF_LISTEN_PORT,
CONF_SERVERS, CONF_UPNP_BIND_MULTICAST, DOMAIN)
REQUIREMENTS = ['emulated_roku==0.1.7']
SERVER_CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_LISTEN_PORT): cv.port,
vol.Optional(CONF_HOST_IP): cv.string,
vol.Optional(CONF_ADVERTISE_IP): cv.string,
vol.Optional(CONF_ADVERTISE_PORT): cv.port,
vol.Optional(CONF_UPNP_BIND_MULTICAST): cv.boolean
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_SERVERS):
vol.All(cv.ensure_list, [SERVER_CONFIG_SCHEMA]),
}),
}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass, config):
"""Set up the emulated roku component."""
conf = config.get(DOMAIN)
if conf is None:
return True
existing_servers = configured_servers(hass)
for entry in conf[CONF_SERVERS]:
if entry[CONF_NAME] not in existing_servers:
hass.async_create_task(hass.config_entries.flow.async_init(
DOMAIN,
context={'source': config_entries.SOURCE_IMPORT},
data=entry
))
return True
async def async_setup_entry(hass, config_entry):
"""Set up an emulated roku server from a config entry."""
config = config_entry.data
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
name = config[CONF_NAME]
listen_port = config[CONF_LISTEN_PORT]
host_ip = config.get(CONF_HOST_IP) or util.get_local_ip()
advertise_ip = config.get(CONF_ADVERTISE_IP)
advertise_port = config.get(CONF_ADVERTISE_PORT)
upnp_bind_multicast = config.get(CONF_UPNP_BIND_MULTICAST)
server = EmulatedRoku(hass, name, host_ip, listen_port,
advertise_ip, advertise_port, upnp_bind_multicast)
hass.data[DOMAIN][name] = server
return await server.setup()
async def async_unload_entry(hass, entry):
"""Unload a config entry."""
name = entry.data[CONF_NAME]
server = hass.data[DOMAIN].pop(name)
return await server.unload()

View File

@@ -0,0 +1,147 @@
"""Bridge between emulated_roku and Home Assistant."""
import logging
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.core import CoreState, EventOrigin
LOGGER = logging.getLogger('homeassistant.components.emulated_roku')
EVENT_ROKU_COMMAND = 'roku_command'
ATTR_COMMAND_TYPE = 'type'
ATTR_SOURCE_NAME = 'source_name'
ATTR_KEY = 'key'
ATTR_APP_ID = 'app_id'
ROKU_COMMAND_KEYDOWN = 'keydown'
ROKU_COMMAND_KEYUP = 'keyup'
ROKU_COMMAND_KEYPRESS = 'keypress'
ROKU_COMMAND_LAUNCH = 'launch'
class EmulatedRoku:
"""Manages an emulated_roku server."""
def __init__(self, hass, name, host_ip, listen_port,
advertise_ip, advertise_port, upnp_bind_multicast):
"""Initialize the properties."""
self.hass = hass
self.roku_usn = name
self.host_ip = host_ip
self.listen_port = listen_port
self.advertise_port = advertise_port
self.advertise_ip = advertise_ip
self.bind_multicast = upnp_bind_multicast
self._api_server = None
self._unsub_start_listener = None
self._unsub_stop_listener = None
async def setup(self):
"""Start the emulated_roku server."""
from emulated_roku import EmulatedRokuServer, \
EmulatedRokuCommandHandler
class EventCommandHandler(EmulatedRokuCommandHandler):
"""emulated_roku command handler to turn commands into events."""
def __init__(self, hass):
self.hass = hass
def on_keydown(self, roku_usn, key):
"""Handle keydown event."""
self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
ATTR_SOURCE_NAME: roku_usn,
ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYDOWN,
ATTR_KEY: key
}, EventOrigin.local)
def on_keyup(self, roku_usn, key):
"""Handle keyup event."""
self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
ATTR_SOURCE_NAME: roku_usn,
ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYUP,
ATTR_KEY: key
}, EventOrigin.local)
def on_keypress(self, roku_usn, key):
"""Handle keypress event."""
self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
ATTR_SOURCE_NAME: roku_usn,
ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYPRESS,
ATTR_KEY: key
}, EventOrigin.local)
def launch(self, roku_usn, app_id):
"""Handle launch event."""
self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
ATTR_SOURCE_NAME: roku_usn,
ATTR_COMMAND_TYPE: ROKU_COMMAND_LAUNCH,
ATTR_APP_ID: app_id
}, EventOrigin.local)
LOGGER.debug("Intializing emulated_roku %s on %s:%s",
self.roku_usn, self.host_ip, self.listen_port)
handler = EventCommandHandler(self.hass)
self._api_server = EmulatedRokuServer(
self.hass.loop, handler,
self.roku_usn, self.host_ip, self.listen_port,
advertise_ip=self.advertise_ip,
advertise_port=self.advertise_port,
bind_multicast=self.bind_multicast
)
async def emulated_roku_stop(event):
"""Wrap the call to emulated_roku.close."""
LOGGER.debug("Stopping emulated_roku %s", self.roku_usn)
self._unsub_stop_listener = None
await self._api_server.close()
async def emulated_roku_start(event):
"""Wrap the call to emulated_roku.start."""
try:
LOGGER.debug("Starting emulated_roku %s", self.roku_usn)
self._unsub_start_listener = None
await self._api_server.start()
except OSError:
LOGGER.exception("Failed to start Emulated Roku %s on %s:%s",
self.roku_usn, self.host_ip, self.listen_port)
# clean up inconsistent state on errors
await emulated_roku_stop(None)
else:
self._unsub_stop_listener = self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP,
emulated_roku_stop)
# start immediately if already running
if self.hass.state == CoreState.running:
await emulated_roku_start(None)
else:
self._unsub_start_listener = self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START,
emulated_roku_start)
return True
async def unload(self):
"""Unload the emulated_roku server."""
LOGGER.debug("Unloading emulated_roku %s", self.roku_usn)
if self._unsub_start_listener:
self._unsub_start_listener()
self._unsub_start_listener = None
if self._unsub_stop_listener:
self._unsub_stop_listener()
self._unsub_stop_listener = None
await self._api_server.close()
return True

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