Compare commits

..

317 Commits

Author SHA1 Message Date
Paulus Schoutsen
6fb8378b45 Bumped version to 0.85.0b0 2019-01-04 13:47:02 -05:00
Maciej Bieniek
27a9f5a05c Round illumination and lux value to one (#19747) 2019-01-04 18:25:37 +01:00
Daniel Høyer Iversen
16ab799798 Upgrade tibber library (#19768) 2019-01-04 17:59:46 +01:00
Sean Dague
03488af3fb Add mychevy optional country parameter (#19727)
* Add optional country parameter

mychevy 1.2.0 provides the ability to work in canada as well as the us
(there are different service urls for each region). This creates a new
config option to enable it.

* Update mychevy.py
2019-01-04 10:01:47 -05:00
Dan Cinnamon
dbb3802b4e Move envisalink component to package and add services.yaml (#19731)
* Moved component to a package and added a services.yaml file.

* Fixing coverage issue and grammar issue on the services.yaml file.

* Fixed typo in the services.yaml file.
2019-01-04 09:57:32 -05:00
Abílio Costa
ead38f6005 Proactive Alexa ChangeReport messages (#18114)
* Alexa: implement auth and proactive ChangeReport messages

* refactor after rebase from dev to use the new AlexaDirective and Response classes

* move to aiohttp; cleanup

* better function name

* move endpoint to config

* allow passing token function

* remove uneeded state get

* use iterable directly

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

* missing delete from previous commit

* checks for when user has no auth config

* update cloud component

* PR suggestions

* string lint

* Revert "string lint"

This reverts commit a05a1f134c9ebc7a6e67c093009744f142256365.

* linters are now happier

* more happy linters

* use internal date parser; improve json response handling

* remove unused import

* use await instead of async_add_job

* protect access token update method

* add test_report_state

* line too long

* add docstring

* Update test_smart_home.py

* test accept grant api

* init prefs if None

* add tests for auth and token requests

* replace global with hass.data

* doc lint
2019-01-03 22:28:43 +01:00
Rohan Kapoor
c2525bede2 Filter urllib3.connectionpool warnings in camera.axis and camera.zoneminder (#19641)
* Filter urllib3.connectionpool warnings in camera.axis and camera.zoneminder

* Lint
2019-01-03 11:56:36 -07:00
carstenschroeder
b79057348d Add exception handling to ADS shutdown (#19682)
* Added exception handling to ADS shutdown

* corrected whitespaces

* deleted blank line
2019-01-03 11:47:16 -07:00
ctborg
6b18b92bdd Drop bme680 os_lookup for temp_offset (#19733)
Drops os_lookup, as it isn't needed to set the temperature offset.
2019-01-03 11:35:13 -07:00
Adam Belebczuk
ada0f7cf65 Fix WeMo incorrect mapping of device type during discovery (#19691) 2019-01-03 11:28:40 -07:00
Andrew Hayworth
87a0118082 Do not choke on no awair data (#19708)
* awair: do not choke on no data

The awair API returns an empty response for various air data queries
when a device is offline. The underlying library (python_awair) does
not directly inform us that a device is offline, since we really can
only infer it from an empty response - there is no online/offline
indicator in the graphql API.

So - we should just ensure that we do not attempt to update device state
from an empty response. This ensures that the platform does not crash
when starting up with offline devices, and also ensures that the
platform is marked unavailable once devices go offline.

* Fix typo

Further proof that coding after 10pm is rolling the dice.
2019-01-03 14:41:18 +01:00
ctborg
688bdc6532 Adds ability to calibrate temperature for BME680 (#19684)
* Adds temperature calibration

* Add deps.  Lint fix
2019-01-02 13:02:29 -05:00
kennedyshead
bba9ef7d7d Bumping aioasuswrt version to 1.1.17 (#19714) 2019-01-02 07:55:09 -05:00
mvn23
635252ec8e Bump pyotgw to 0.4b1 (#19715) 2019-01-02 07:54:23 -05:00
Dan Cinnamon
a10ca95c01 Envisalink pgm (#19499)
* Added a new service for calling custom PGM functions.

* Fixed lint issues

* Fixed lint issues reported by travis-CI

* Fixed style issue.

* Complete rename of attribute.
2019-01-02 05:46:33 -07:00
Daniel Perna
4244ea78d0 Update pyhomematic 0.1.54 + small fixes (#19667)
* Update pyhomematic + small fix

* Add casting for ILLUMINATION

* Revert suggested fix
2019-01-01 16:25:57 +01:00
Nick Whyte
5aa2bd81cf Add ness alarm control panel using nessclient (#18463)
* Add ness alarm control panel using nessclient

* indenting

* .

* Remove availability functionality, will improve and add back in another PR

* Use call_count

* lint

* lint

* Review changes

* Lint

* Bump nessclient to 0.9.8

* Bump nessclient to 0.9.9

* Remove from .coveragerc
2019-01-01 08:08:13 -07:00
Daniel Chesterton
61d5b3028d Add support for color_temp_command_template in MQTT light component (#19675)
* Add support for color_temp_command_template in MQTT light component
2019-01-01 15:42:41 +01:00
javicalle
b9f4a7220e Improve rflink coverage (#19596)
* some minor tests refactor
* async/await refactor
* toggle have not brightness
* test for race condition in unknown device
* test for 'no_command' and 'not_connected'
* test for race condition in unknown device
* sensor events are handled in sensor devices, RflinkDevice handle
command events
* test race conditions & bogus entity remove
* two more tests
* Test race condition for unknown components
* Test cleanup for `commands events` and `sensor events`
2019-01-01 15:35:31 +01:00
Fabian Affolter
2ea53e0787 Suppress traceback if network is not available (#19651) 2019-01-01 14:21:46 +01:00
Fabian Affolter
7c302bfd7e Luftdaten traceback (#19666)
* Suppress traceback if there is not connection available

* Remove line break
2019-01-01 14:21:02 +01:00
Robin
ff80fc347b Fix london_underground issue (#19642)
* Update london_underground.py

* Update test

* Update london_underground.py

* Update london_underground.py

* Update london_underground.py

* Fix lint

* Use london-tube-status==0.2
2018-12-31 06:24:52 -08:00
Michael Dubno
4b541f4058 Add IDTECK proximity card component (#18309)
* Added IDTECK proximity card sensor component.

* Moved from sensor to platform

* Made requested standards changes
2018-12-30 20:15:45 -08:00
Jc2k
855274e354 Fix homekit_controller pairing regression (#19654)
* Fix homekit_controller pairing regression

* Use constant for pairing file name
2018-12-30 14:44:26 -05:00
ehendrix23
43eaa960e8 Fix error in got_connected for remote.harmony (#19662)
* Fix config call in connected

* Change aioharmony version for fixes
2018-12-30 13:35:08 -05:00
Thom Troy
81a0ce621e Fix exception checking for next dublin bus (#19663) 2018-12-30 18:35:12 +01:00
Pär Svanström
18d36e011a Added regexp validation allowing Twilio notifications to use Sender ID instead of phone number (#19644)
* Added regexp validation allowing Twilio notifications to use Sender ID instead of phone number

* Fix line length
2018-12-30 18:34:29 +01:00
Joakim Sørensen
6d44245456 pytraccar version bump (#19659) 2018-12-30 14:59:43 +01:00
carstenschroeder
4b90ed6b22 Fix ADS light when parameter adsvar_brightness is not set (#19636)
* ADS light breaks if optional parameter adsvar_brightness is not set

Just a small change to prevent exception if optional parameter adsvar_brightness is not set

* corrected blank lines
2018-12-30 02:39:00 -08:00
John Mihalic
cc8b811572 Bump pyHik library to 0.1.9 to improve device support. (#19656) 2018-12-30 10:13:49 +01:00
ehendrix23
faeee4f7ad Use aioharmony for remote.harmony platform (#19595)
* Use aioharmony for async

Use aioharmony to interact with Harmony hub. Due to this following improvements:
-) Setting of available state for entity
-) Automatic config update if configuration changes (including updating file containing config)
-) Allow using of device name instead of number
-) When sending command with repeat, nothing else will be able to put a IR command in between

* Requirements updated

* Version update for fix

* Mainly cleanup

* Update requirements

Updated requirements

* Fixed lint issue

* Small bump for aioharmony

Small version bump increase for aioharmony

* Updated based on review
2018-12-29 17:22:27 -08:00
Markus Ressel
9aa6037219 Add RaspyRFM switch platform (#19130)
* added components and requirement

* change config to allow the definition of multiple switches without redefining the gateway

* dont assume false state
fix default value

* added exclude to coveragerc

* sorted imports

* review fixes

* review fixes

* bugfix
review fixes

* review fix
2018-12-29 16:40:03 -08:00
David F. Mulcahey
d0742cb332 Only bind clusters in ZHA remote entity (#19577)
* split bind and configure reporting helpers

* only bind remote clusters

* update comments - review comment
2018-12-29 16:17:17 -08:00
Alexei Chetroi
e096532cf1 Use async_configure for ZHA IAS binary sensor (#19629)
* Update Zha IAS binary sensor to use async_configure().

* Make less debug logging noise.
2018-12-29 16:13:52 -08:00
Adam Belebczuk
25e5864a22 Improve Wemo setup speed (#19563)
* Wemo - Improve setup speed

Move WeMo device discovery to an async context so it won't block initial component setup from completing quickly.

* WeMo - Fix too long lines

* WeMo - Update subscription shutdown log message

* WeMo - Fix flake8 issues

* WeMo - Code review fixes

* WeMo - Fix long lines

* WeMo - More code review fixes

* WeMo - Code review fixes
2018-12-29 16:05:21 -08:00
Marvin Wichmann
338077f557 Support knx operation types (#19546)
* Updated version to 0.9.3

Adjusted climate component due to changes in the underlying library.

* Climate.KNX: fix updating view when operation mode is changed due to refactoring

* Addressed review comments

* Added validation for config.
2018-12-29 15:18:55 -08:00
Ville Skyttä
f925d9ca6b Use xml.etree through defusedxml (#19640) 2018-12-30 00:07:48 +01:00
Fabian Affolter
b1c9f8d55d Suppress traceback if network is not available 2018-12-29 23:55:43 +01:00
Marius Retegan
32eb4e518b Fix cpu_temp issue on Vero 4K (#19638) 2018-12-29 21:01:47 +01:00
Robbert Müller
9928b977fd Added events STARTED, RESTARTED AND PAUSED (#19516)
Rewrote the tests a bit
the 'wait for the timer to finish' part of the test is now it's own test.
The rest is a sequence of fire/assert. Which i rewrote to a loop to
reduce the amount of duplicate code
2018-12-29 16:40:17 +01:00
mvn23
dc9da79a1c Revert "Bump pyotgw to 0.4b0 (#19618)" (#19635)
This reverts commit dae4543e54.
There's a bug in the new version of the library that may cause 100% CPU usage, rendering Home Assistant unresponsive.
2018-12-29 16:38:55 +01:00
Steven Looman
2ba86310f0 Upgrade to async_upnp_client==0.13.8 (#19634) 2018-12-29 14:09:29 +00:00
Marcelo Moreira de Mello
457708cbda Upgraded pyarlo to 0.2.3 (#19626) 2018-12-28 16:51:59 -05:00
Andrei
82d6fe5bd5 Fix cpu_temp issue on Odroid (#19620) 2018-12-28 21:36:00 +01:00
mvn23
dae4543e54 Bump pyotgw to 0.4b0 (#19618) 2018-12-28 10:12:10 -05:00
Daniel Shokouhi
33c5e09ac2 Add additional neato alerts and errors (#19608) 2018-12-28 09:53:54 -05:00
Andre Lengwenus
f09cea1499 LCN component and light platform (#18621)
* Initial commit of LCN component and light platform

* Corrected pre-review comments

* Fixed dimming behaviour in combination with transitions for lcn lights

* Removed unused logger

* Combined __init__.py and core.py into lcn.py component. Bumped to pypck==0.5.6

* Fixed .coveragerc

* Bumped to pypck==0.5.7

* Bump to pypck==0.5.8

* Fixed requirements_all.txt

* Moved unique generation of connection names to config schema's validator

* Minor changes due to review comments.
Bump to pypck==0.5.9.

* Address_connection is passed into LcnDevice

* Set should_poll property on LcnDevice to return False

* Moved platform config validation to component. Load platform using discovery helper

* Furtehr changes due to the review

* Light configuration is set required as there are no other platforms up to now
2018-12-28 03:39:06 -08:00
Rene Nulsch
14c39f7c24 Systemmonitor - add device_class property (#19614) 2018-12-28 10:28:40 +01:00
Ville Skyttä
b83a405b14 Upgrade huawei-lte-api to 1.1.1 (#19615) 2018-12-28 11:10:34 +02:00
SNoof85
699a38de52 Add Freebox component with sensors and device tracker (#18472)
* Add freebox component with sensor and device tracker

* script/gen_requirements_all passed and pylint fixes

* Fix docstring in wrong place

* Fix indentation

* Lint fixes

* More lint fixes

* Lint fixes again

* Pylint fixes

* Bump aiopyfreebox version

* Close freebox connection on HA Stop

* Fixed docstring

* Fixed ident

* Lint fixes

* Fix cloing session when HA stop

* Fix URL

* Fix URL

* Fix double look up in discovery datas

* Fix logging level

* Fix get_device_name

Thx for the hint Martin

* Fix async_update_info

* Update requirements_all.txt
2018-12-27 15:26:09 -08:00
Fabian Affolter
fe14be53e3 Upgrade aiohttp to 3.5.1 (#19584) 2018-12-27 21:56:08 +01:00
Max Rydahl Andersen
b32e6fe0d5 Add AfterShip sensor for packages (#18034)
* Add AfterShip sensor for packages

Why:

 * I receive a lot of packages from many different shipping companies.
 * I would like to see in haas how many packages are being delivered.

This change addreses the need by:

 * Adding a sensor for AfterShip (aftership.com)
 * AfterShip supports ~490 couriers world wide thus should cover
   almost any sensible tracking.

Notes:
  - For now this sensor assumes you somehow have added trackings to
    aftership manually.
  - Future idea is to expose service that allows adding a tracking
    based on incoming mails.
  - Other improvments would be to add map markers for package locations.

Related:
- https://community.home-assistant.io/t/package-tracking/858
- https://community.home-assistant.io/t/aftership-package-tracking/24068
- https://community.home-assistant.io/t/aftership-shipment-tracking-platform/14074
- https://community.home-assistant.io/t/aftership-state-card/57912

* Fix typo and update ordering
2018-12-27 10:01:57 -08:00
Daniel Shokouhi
d05450487c Improve how neato displays alerts and add alerts for persistent maps (#19593)
* Improve how neato displays alerts and add alerts for persistent maps

* Review comments
2018-12-27 09:57:38 -08:00
emontnemery
f9aa364b6d Don't truncate brightness and white_value of MQTT light (#19502)
* MQTT light: Don't truncate brightness

* Clamp after rounding
2018-12-27 18:18:12 +01:00
Ioan Loosley
5eab4f1dcc Version Bump for aioftp (#19510)
* Version Bump for aioftp

* Version Bump
2018-12-27 18:17:12 +01:00
Andrei
4c59a6522a Updated set of available voices for Yandex TTS (#19603) 2018-12-27 17:54:12 +01:00
Simon Nørager Sørensen
40bb4266c9 Update pymitv dependency (#19601)
* Security update, fixed fatal error when TV could become unresponsive

* Dependency update
2018-12-27 17:38:07 +01:00
FieldofClay
bf8b201bb3 Add verify_ssl option to Splunk component (#19112)
* added verify_ssl option to Splunk component

* update Splunk tests

* fix typo in Splunk tests

* Update test
2018-12-27 14:23:04 +01:00
apetrycki
cd0da4ed0e Fix mpd shuffle/random status (#19308)
* Fix shuffle/random status

MPD always shows true for shuffle.  For some reason casting the 0 or 1 as a boolean does not work.  Now returns 'true' or 'false' based on the value of 'random'.

* Update homeassistant/components/media_player/mpd.py

Change to correct way of returning shuffle boolean. 'random' needs to be cast as an integer before being cast as a boolean.

Co-Authored-By: apetrycki <34962392+apetrycki@users.noreply.github.com>

* Remove incorrect string code

Original fix method returns a string instead of a boolean.  Removed in favor of MartinHjelmare's method.
2018-12-27 00:04:40 -08:00
Fabian Affolter
22acc03fb8 Upgrade Sphinx to 1.8.3 (#19580) 2018-12-27 08:26:37 +01:00
Fabian Affolter
10831a0889 Upgrade rpi-rf to 0.9.7 (#19394) 2018-12-26 14:50:45 -05:00
Fabian Affolter
5de4f546f9 Upgrade keyring to 17.1.0 (#19583) 2018-12-26 14:47:38 -05:00
Fabian Affolter
2efa297df1 Upgrade pyowm to 2.10.0 (#19582) 2018-12-26 14:46:59 -05:00
Fabian Affolter
98229899dc Upgrade TwitterAPI to 2.5.8 (#19581) 2018-12-26 14:46:18 -05:00
Fabian Affolter
0a792620f8 Upgrade sphinx-autodoc-typehints to 1.6.0 (#19579) 2018-12-26 14:44:44 -05:00
Ted Sluis
54f6cfd569 Add a new click_type double_both to improve the support of the new Xiaomi aqara remote.b286acn01 dual switch. (#19578) 2018-12-26 17:48:58 +01:00
javicalle
70fff26383 Clean up remaining rflink tests (#19551)
* some minor tests refactor

* unused import

* async/await refactor

* Correct tests failures
2018-12-26 15:25:16 +01:00
Sebastian Muszynski
dc11e41cf0 Add a new click_type "long_both" to improve the support of the new Xiaomi Wireless Wall Switch (remote.b286acn01) (#19573) 2018-12-26 14:16:53 +01:00
Ville Skyttä
a6e091f60f Link to dicttoxml excessive INFO logging issue (#19575) 2018-12-26 14:16:08 +01:00
Daniel Høyer Iversen
1428919f98 Tibber, improve server reconnection (#19574) 2018-12-26 13:03:06 +01:00
Michael Dubno
6f9943787a Pencom (#19369)
* Added Pencom relay switch.

* Added Pencom relay switch.

* Cleaned up for submission.

* Fixed attribute keys. Switched to add_entities.
2018-12-26 08:49:34 +01:00
DoloresHA
796b195c73 Update pylaunches dependency to 0.2.0 (#19570)
* Update pylaunches dependency to 0.2.0

Update launch library to use pylaunches 0.2.0 as a dependency. launch_time sensor attribute will now be passed in ISO format, allowing for templating/easier automating with this attribute.

* Update pylaunches to 0.2.0
2018-12-25 23:47:19 +01:00
Heine Furubotten
47f8d248f7 Whitelisting of lines on entur sensor (#19539) 2018-12-25 23:43:46 +01:00
Igor Motov
b9a0f40827 Add device_id configuration option to Bluetooth tracker (#18539)
* Bluetooth tracker: add device_id configuration option

Makes the device id that bluetooth tracker is using configurable instead 
of using the first available device.

* Remove unncessary default in config.get

It is already handled in the configuration validation.
2018-12-25 18:29:44 +01:00
sander76
6b204941cf Add homematicip cloud full flush measuring switch (#19247) 2018-12-25 16:43:28 +01:00
mreiling
4d62e77049 Added support for triggered state on NX584 alarm. (#19524)
* Added support for triggered state on NX584 alarm.

* Minor update
2018-12-25 16:29:53 +01:00
Mattias Welponer
b80bed64f5 Add HomematicIP SMI55 device (#19400) 2018-12-25 10:40:21 +01:00
sander76
18b7f74ad7 Clean up homematicip cloud (#19481)
* Better logging, remove unused method, re-try handling fix. Other minor fixes.

* fix test

* typo fix
2018-12-25 09:40:36 +01:00
cdheiser
b2081c579b Improve Lutron RadioRA2 support, adding switches and scenes (#18330)
* Improve Lutron RadioRA2 support, adding switch and scene support.

- Update the version of pylutron to 0.2 which has various bug fixes.
- Switch to pylutron's per-device subscribe
- Add switch support, and configure any non-dimmable output as a switch.
- Add scene support, using any configured keypad button with a corresponding LED as a scene.

* Fixes for comments in pull request home-assistant/home-assistant#18330

* More fixes for comments in pull request #18330

* Remove unused imports

* Cleanup in docstrings for Lutron scene support.
2018-12-25 09:33:03 +01:00
David F. Mulcahey
bef85ecd2e Remove global from ZHA application controller (#19557)
* remove global from application controller per request

* remove unneeded line

* don't store controller application in hass.data - review comment
2018-12-25 09:20:09 +01:00
Jc2k
e0f50a9e54 Update homekit controller to homekit==0.12.0 (#19549) 2018-12-24 22:13:17 +01:00
emontnemery
a8797a08c6 Improve handling of MQTT light discovery (#19436) 2018-12-24 14:28:26 +01:00
emontnemery
edb7ec78f9 Fix support for base topic for empty values in MQTT discovery msg (#19501)
* Fix topic expansion for short strings

* Lint
2018-12-24 14:21:58 +01:00
Alexei Chetroi
4a1da0b041 Configure ZHA entity on new ZHA device join (#19470)
* Address PR#19177 comments.

* Make 'new_join' part of ZhaEntity.

Call async_configure() automatically when new device ZHA device joins.
2018-12-23 20:47:06 +01:00
Fredrik Erlandsson
0b84eefa2e Add hub- and device-info for tellduslive (#19180)
* add hub- and device-info

* attempt to make I/O outside event loop

* add_executer_job

* coroutines

* async_get_hubs

Co-Authored-By: fredrike <fredrik.e@gmail.com>

* asyncio fixes

* do device_info IO when device is discovered

* it's called async_add_executor_job

* nicer unique_id

* add comment

* it's called `async_add_executor_job`

* hub always contains 'name'

* await each new device

* add comment to why gather is bad
2018-12-23 13:13:49 -05:00
David F. Mulcahey
50888ae339 Fix issues in ZHA light (#19368)
* make light report on/off and level

* refactoring and review comments

* refactor

* use boolean for set_state - review comment

* async_schedule_update_ha_state() on level change - review comment

* fix docstring - review comment
2018-12-23 16:16:21 +01:00
kdvlr
a9f796a97c Updated to support per device find iphone sound. (#19535)
Added additional log statements to help debug
2018-12-23 13:35:39 +01:00
Steve9F
10ff169c76 Change ISY binary_sensor subnode to hex (#19471)
The subnode id for the motion enable node of Insteon 2844-222 motion sensor II on the ISY is 'D'. The binary_sensory/isy994.py assumed this value will be an integer rather than hex and fails. Changing line 55 to subnode_id = int(node.nid[-1], 16) fixes the issue.
2018-12-23 13:31:16 +01:00
Diogo Gomes
0b22880f22 increase robustness, when something upstream fails (#19493) 2018-12-23 13:29:02 +01:00
Alexei Chetroi
5a4e6bbb07 Support ZHA light turn_off transition (#19531) 2018-12-23 12:15:54 +01:00
Adam Belebczuk
0776456b59 Pywemo version bump (#19538)
* Bump pywemo version

* Bump pywemo version
2018-12-23 11:40:34 +01:00
Alexei Chetroi
01fc322488 Make ZHA entities non-polled by default (#19536) 2018-12-23 11:11:24 +01:00
David F. Mulcahey
2a2af80309 Add ZHA occupancy sensor (#19365)
* occupancy sensor

* lint

* map occupancy cluster to binary_sensor

* update to use reporting configuration and async_configure

* refactor

* fix typo - review comment

* handle restore entity functionality
2018-12-22 20:53:15 +01:00
William Comartin
2765440aa5 Implement path in the config to fix issues for some users (#19491)
* Update Tautulli sensor dependency pytautulli

Implement path in the config to fix issues for some users

* Add requirement
2018-12-22 14:36:06 -05:00
Andrew Loe
43e174899d Add additional Z-Wave Bulbs to ZW098 Workaround (#19480) 2018-12-22 14:33:28 -05:00
Panagiotis Panagiotopoulos
07b6aaec63 Add long click at new Aqara Wireless Remote Switch (#19518)
New Aqara Wireless Remote Switch Single supports long click.
2018-12-22 19:35:25 +01:00
Daniel Høyer Iversen
ef53a2d118 Fix Mill connection problem (#19519)
* Update mill library, improve connection

* Mill connection

* revert print

* Mill connection
2018-12-22 19:01:52 +01:00
Antoine GRÉA
1099018a5e Fix fail2ban by removal of internal timer logic (#19456)
* Remove timer logic from sensor class

Proposed fix for issue #10500

* Updating the tests to remove timer logic

* Removing unecessary dependancy

* Fixing requested changes

* Commit to try to fix the CLA ?
2018-12-22 18:25:02 +01:00
David F. Mulcahey
4bdb21a871 Update ZHA entity state on ZigBee zdo device announce (#19208)
* call async_update if defined on device_announce

* lint

* change update method

* remove unneeded listener
2018-12-22 18:18:48 +01:00
Fabian Affolter
6880be5aeb Add sunrise and sunset to Darksky weather sensor (#19492)
* Add sunrise and sunset to Darksky weather sensor

* Fix lint issue
2018-12-22 08:52:36 -05:00
Dom
f0e187e306 Update yale smart alarm client to v0.1.6 (#19495) 2018-12-22 08:50:03 -05:00
Joakim Sørensen
7c5ac88aae Add deprecation warning (#19515)
* Update ruter.py

* formating
2018-12-22 14:01:31 +01:00
Alexei Chetroi
54c57fe5db Restore state for zha binary_sensors on restart. (#19314)
Poll zha devices for current status, if not available restore state.
2018-12-22 09:34:47 +01:00
David F. Mulcahey
b444dfe8a6 Add ZHA battery sensor (#19363)
* add batery sensor

* add additional battery sizes

* remove blank line

* lint

* fix attribute report configuration

* return None - review comments
2018-12-22 09:11:33 +01:00
javicalle
fb226e3e3b Clean up RFLink tests and add two tests (#19511)
* some minor tests refactor

* unused import
2018-12-22 08:53:02 +01:00
uchagani
3a3d488de3 Allow scrape sensor to retry setting up platform if initial setup fails (#19498) 2018-12-22 08:40:30 +01:00
Michael Dubno
30841ef4da Add Lutron Homeworks component (#18311)
* Added Lutron Homeworks components.

* Made all requested changes other than config.

* Removed commented out code.

* Removed binary_sensor.
Implemented new signal/events for button presses.
Cleaned up some data passing.

* Fixed minor formatting.

* Got rid of unused config stuff.
Reordered imports.

* Missed removing vol, and forgot an extra line.

* More requested changes
Removed HomeworksController, it wasn't needed.

* Removed stale code.

* Imperative doc change.
2018-12-21 18:11:00 -05:00
Steven Looman
501b3f9927 Disable creating port mappings from UI, add discovery from component (#18565)
* Disable creating port mappings from UI, add discovery from component

* Remove unused constant

* Upgrade to async_upnp_client==0.13.6 and use manufacturer from device

* Upgrade to async_upnp_client==0.13.7
2018-12-21 17:25:23 +00:00
Paulus Schoutsen
5efc61feaf Merge branch 'master' into dev 2018-12-21 15:34:40 +01:00
Paulus Schoutsen
28abc30b4d Merge pull request #19505 from home-assistant/rc
0.84.6
2018-12-21 15:33:17 +01:00
Paulus Schoutsen
0471e15c28 Bumped version to 0.84.6 2018-12-21 14:04:54 +01:00
Paulus Schoutsen
ec28ee3c42 Remove check if base url is local (#19494) 2018-12-21 14:04:46 +01:00
Paulus Schoutsen
1281da024c Remove check if base url is local (#19494) 2018-12-21 11:23:05 +01:00
Diogo Gomes
c789f11ef8 Fixed the range filter unknown argument precision (#19428)
In HomeAssistant 0.84.3, the range filter would not work due to the unexpected precision filter parameter. 
The default range scheme has been edited to remove the unexpected precision parameter.

Verified and tested.
2018-12-20 22:30:06 +00:00
Otto Winter
dbd5396dc7 Add native ESPHome Home Assistant state feature (#19429)
* Add native ESPHome Home Assistant state feature

* Update aioesphomeapi
2018-12-20 23:29:57 +01:00
uchagani
71900ca719 Add new sensor platform to expose Islamic prayer times (#19444)
* added new sensor platform to expose Islamic prayer times

* added new sensor platform to expose Islamic prayer times

* updated tests according to feedback

* make prayer_times_info a public attribute

* remove stale comments
2018-12-20 22:52:43 +01:00
Aaron Bach
c15445159d Add timeout to RainMachine login (#19476)
* Add timeout to RainMachine login

* Moved timeout logic to regenmaschine

* Moving logic back into try/except

* Bumped requirements
2018-12-20 22:51:10 +01:00
Tom French
dd885a456e Reorder FLOW entries in config_entries.py (#19475) 2018-12-20 21:54:42 +01:00
Mathieu Velten
b5c9eca654 Update pynetgear to 0.5.2 (#19490) 2018-12-20 21:16:50 +01:00
jumpkick
dcf925a67f Rename ocr.png to ssocr-(entity_name).png to allow multiple instances (#18634)
* * Rename ocr.png to ssocr-(entity_name).png to allow multiple seven_segments instances to run without overwrting each other's data.

* Update seven_segments.py

* Update seven_segments.py

* Use string formatting
2018-12-20 11:31:58 -05:00
Bob Clough
d42d8543c8 Add Mythic Beasts DNSAPI Component (#18333)
* Add Mythic Beasts DNSAPI Component

* Added timeout, and tests for exceptions while updating

* Move API to external module

* Move mbddns import into function

* Updated tests to mock out mbddns library
2018-12-20 11:33:47 +01:00
Jens
fa0185a481 Adds battery_percent which had been introduced with pyatmo 1.4 and resolves unknown var warning. (#19309) 2018-12-19 23:42:16 +01:00
Daniel Shokouhi
28d2f9bd87 Bump Pybotvac To Support D7 On Latest Firmware (#19463)
* Bump pybotvac to support D7 on latest firmware

* Update requirements
2018-12-19 22:37:20 +01:00
emontnemery
27ea59f6c3 Add device registry to MQTT climate (#19332)
* Add device registry to MQTT climate

* Add testcase test_entity_id_update
2018-12-19 19:27:44 +01:00
emontnemery
ae776e2d28 Add device registry to MQTT alarm control panel (#19331)
* Add device registry to MQTT alarm control panel

* Add testcase test_entity_id_update
2018-12-19 19:27:23 +01:00
emontnemery
fed5d0f5be Add device registry to MQTT lock (#19333) 2018-12-19 19:26:07 +01:00
Paulus Schoutsen
4f134f339c Merge pull request #19461 from home-assistant/rc
0.84.5
2018-12-19 16:49:21 +01:00
Paulus Schoutsen
264d18bc83 Bumped version to 0.84.5 2018-12-19 15:42:02 +01:00
Paulus Schoutsen
4c1d978aa4 Bump pyharmony (#19460) 2018-12-19 15:41:25 +01:00
Paulus Schoutsen
196fe4b927 Bump pyharmony (#19460) 2018-12-19 15:41:14 +01:00
Erik
a9de9aa58d Add testcase test_entity_id_update 2018-12-19 15:28:25 +01:00
Erik
e874093818 Add device registry to MQTT climate 2018-12-19 15:28:25 +01:00
Paulus Schoutsen
b10149c2a0 Merge pull request #19459 from home-assistant/rc
0.84.4
2018-12-19 15:13:42 +01:00
Paulus Schoutsen
c71a6ee562 Updated frontend to 20181219.0 2018-12-19 15:01:02 +01:00
Paulus Schoutsen
57ee514d70 Update translations 2018-12-19 15:01:02 +01:00
Alexei Chetroi
4692605974 ZHA entity ZCL reporting configuration (#19177)
* Implement async_configure() method for ZHA entities.

Allow attribute reporting configuration to be stored as dict of zha
entity.

* Update ZHA platform to use new attribute reporting configuration.

* Use const declaration instead of magic numbers.

* Add support for manufacturer_id in ZCL attribute reporting configuration.

* Refactor async_configure() method.

Rename attribute reporting dict to zcl_reporting_config.
2018-12-19 14:52:20 +01:00
Paulus Schoutsen
2b82830eb1 Bumped version to 0.84.4 2018-12-19 14:23:07 +01:00
ehendrix23
ff1dba3529 Use web sockets for Harmony HUB (#19440)
* Updates to Harmony for web sockets

Updates to harmony to use web sockets with async

* Lint

* Small fixes

* Fix send_command

Continued improvements:
-) Fixed send_command
-) Get HUB configuration during update in case it was not retrieved earlier (i.e. HUB unavailable)

* Further improvements

Completely removed dependency on __main__ for pyharmony, instead everything is now done from the HarmonyClient class.
Writing out Harmony configuration file as a JSON file.
Using same functionality to determine if activity provided is an ID or name for device, allowing send_command to receive a device ID or device name.

* Point requirements to updated pyharmony repo

Updated REQUIREMENTS to point to repository containing the updates for pyharmony.

* lint

lint

* Small fix for device and activity ID

Small fix in checking if provided device or activity ID is valid.

* Pin package version

* No I/O in event loop

* Point at HA fork with correct version bump

* Fix req
2018-12-19 14:23:00 +01:00
Morten Lüneborg
257a91d929 Fix IHC config schema (#19415)
* Update __init__.py

Update "unit" -> "unit_of_measurement" and configuration (from plural to singular)

* Update __init__.py

* Removing vol.ALLOW_EXTRA arguments

* Update __init__.py
2018-12-19 14:22:59 +01:00
ehendrix23
23a579421d Use web sockets for Harmony HUB (#19440)
* Updates to Harmony for web sockets

Updates to harmony to use web sockets with async

* Lint

* Small fixes

* Fix send_command

Continued improvements:
-) Fixed send_command
-) Get HUB configuration during update in case it was not retrieved earlier (i.e. HUB unavailable)

* Further improvements

Completely removed dependency on __main__ for pyharmony, instead everything is now done from the HarmonyClient class.
Writing out Harmony configuration file as a JSON file.
Using same functionality to determine if activity provided is an ID or name for device, allowing send_command to receive a device ID or device name.

* Point requirements to updated pyharmony repo

Updated REQUIREMENTS to point to repository containing the updates for pyharmony.

* lint

lint

* Small fix for device and activity ID

Small fix in checking if provided device or activity ID is valid.

* Pin package version

* No I/O in event loop

* Point at HA fork with correct version bump

* Fix req
2018-12-19 14:21:40 +01:00
emontnemery
1568de62df Correct calls to subscription.async_unsubscribe_topics (#19414)
* Correct calls to subscription.async_unsubscribe_topics

* Review comments

* Add testcases
2018-12-19 14:05:24 +01:00
Paulus Schoutsen
a7e98f12f4 Updated frontend to 20181211.2 2018-12-19 14:04:08 +01:00
Fabian Affolter
8cec559103 Various updates (#19449)
* Various updates

* Fix lint issues
2018-12-19 12:39:16 +01:00
Gido
258fe1f09b Add sensor platform for SolarEdge Monitoring API (#18846)
* Adding sensor for SolarEdge Monitoring API support

* Adding support for Rova garbage calendar

* Update solaredge to pass lint and flake8

* Added solaredge.py to .coveragerc

* Added extend for Voluptuous schema

* Fixed styling issues

* Removed rova.py for later feature

* Replaced API requests with python pip package

* Fixed styling issues

* Updated to new async syntax
Added async_update to sensor class
Added Throttle to SolarEdge data update
Added CONF_NAME to platform settings
Added credentials check for api
Minor code style changes

* Remove unnecessary debug logging

* Updated dict keys

* Added SCAN_INTERVAL
Updated platform setup

* Remove DOMAIN variable
Correct import for PLATFORM_SCHEMA

* Change some debug to error messages
Correct return statements
Remove initial update call

* Fix pylint and flake8 errors
2018-12-19 09:56:45 +01:00
Fredrik Erlandsson
e5487722a8 Add device_info to Daikin (#19372)
* add device_info to Daikin

* Use the constant CONNECTION_NETWORK_MAC from the device registry helper.
2018-12-19 08:18:40 +01:00
Adam Belebczuk
7f0dd442fd Various enhancements for WeMo component/platforms (#19419)
* WeMo - Various fixes and improvements

Various fixes & improvements to the WeMo components, including:
-- Fixes to rediscovery
-- New reset filter service for the WeMo Humidifier
-- Switched the remainder of the WeMo components to async IO
-- Removed any remaining IO in entity properties and moved them to the polling/subscription update process

* WeMo - Fix pywemo version and remove test code from WeMo fan component

* WeMo Humidifier - Add services.yaml entry for reset filter life service

* WeMo - Update binary_sensor component to use asyncio

* WeMo - Add available property to binary_sensor component

* WeMo - Fixed line length issue

* WeMo - Fix issue with discovering the same device multiple times

* WeMo - Fix for the fix for discovering devices multiple times

* WeMo - Fix long lines

* WeMo - Fixes from code review

* WeMo - Breaking Change - entity_ids is now required on wemo_set_humidity

* WeMo - Code review fixes

* WeMo - Code review fixes

* WeMo - Code review fixes
2018-12-19 08:12:32 +01:00
Fabian Affolter
ef6c39f911 Fix typo (#19433) 2018-12-18 19:32:42 +01:00
Otto Winter
7317b1bb8b Miscellaneous ESPHome cleanups (#19425) 2018-12-18 19:04:50 +01:00
Fabian Affolter
c0ae7b1a49 Upgrade requests to 2.21.0 (#19385) 2018-12-18 17:29:38 +01:00
Fabian Affolter
686a856a17 Upgrade sqlalchemy to 1.2.15 (#19383) 2018-12-18 15:48:06 +01:00
Eliseo Martelli
51e6371991 Add Prezzibenzina (Italian Fuel Price) Sensor (#19297)
* complete(?)

* fixed linting

* update requirements

* added to coveragerc

* fixed linting

* added ability to set custom name

* fixed linting

* added filter

* spacing

* Added list of possible fuels

* Minor updates
2018-12-18 15:47:38 +01:00
Fabian Affolter
96c233d4b9 Use string foratting (#19427) 2018-12-18 15:23:53 +01:00
timkoers
17fbeb6245 Fixed the range filter unknown argument precision
In HomeAssistant 0.84.3, the range filter would not work due to the unexpected precision filter parameter. 
The default range scheme has been edited to remove the unexpected precision parameter.

Verified and tested.
2018-12-18 13:21:08 +01:00
Morten Lüneborg
c59e049050 Fix IHC config schema (#19415)
* Update __init__.py

Update "unit" -> "unit_of_measurement" and configuration (from plural to singular)

* Update __init__.py

* Removing vol.ALLOW_EXTRA arguments

* Update __init__.py
2018-12-18 12:40:03 +01:00
Rohan Kapoor
6c64b315db Optionally disable ssl verification for mjpeg (#19277) 2018-12-18 11:22:47 +01:00
Paulus Schoutsen
2f6ef08959 Remove reviewed by hound. That's not worth a badge. 2018-12-18 11:06:30 +01:00
Fabian Affolter
9c8e10936b Add openSenseMap air pollutants platform (#19357)
* Add openSenseMap air pollutants platform

* Fix issues (Docstring, log entries and check)

* Use SCAN_INTERVAL and name handling
2018-12-18 09:15:07 +01:00
John Mihalic
f2c7e3fed4 Bump pyEmby to 1.6, add channel media type mapping (#19318) 2018-12-17 19:16:32 -05:00
Nick Horvath
6adbf3ba84 Add camera selection config to skybell camera (#19310)
* add camera selection config to skybell camera

* code review changes.
2018-12-17 18:31:10 -05:00
Marius Retegan
da10598fa1 Fix cpu_temp issue on Raspberry Pi (#19404) 2018-12-17 18:30:17 -05:00
c-soft
6c8ed86f3e Satel integra monitor outputs (#19149)
* Adjusted api for new library version.

* Added support for output monitoring. Added initial status check for the alarm.

* Added default values to the configuration as per review notes.
2018-12-17 18:28:41 -05:00
jumpkick
f1005d37a7 Cast lametric cycles parameter to int (#19370) 2018-12-18 00:14:55 +01:00
Erik Eriksson
d270d52cb5 Upgrade volvooncall to 0.8.7 (#19398) 2018-12-18 00:08:57 +01:00
Eliseo Martelli
6e26713184 Add GTT Sensor (#18449)
* added gtt sensor

* removed trailing space

* updated requirements_all

* fixed two errors in the code style

* fixed imperative in docstring

* disabled pylint false positive

* fixed description on top of the file

* added files to .coveragerc

* fixes

* linting

* fixed linting 👍

* left a trailing space, now it's gone.

* fix
2018-12-17 18:03:34 -05:00
Sean Dague
e9c19462d6 Provide charging indicator for mychevy (#19348)
* Provide charging indicator for mychevy

This expands the mychevy sensor for the battery level to also know if
the system is currently charging to get the correct icon.

* address review feedback
2018-12-17 17:56:49 -05:00
Fabian Affolter
57ccd8283d Upgade colorlog to 4.0.2 (#19390) 2018-12-17 17:54:07 -05:00
Otto Winter
4ffacec4be Add native ESPHome API service call feature (#19401)
* Add native ESPHome API service call feature

* 🚑 Fix
2018-12-17 21:18:54 +01:00
Otto Winter
44bf5ba001 Add native ESPHome API device registry feature (#19381)
* Add native ESPHome API device registry feature

* 😅 Actually call method

* Run script/gen_requirements_all

* Don't prefix sw_version
2018-12-17 20:54:39 +01:00
Otto Winter
77e4f69af0 ESPHome Native API Restore Entities on startup (#19379)
* Update __init__.py

* Use attr.fields_dict
2018-12-17 20:53:51 +01:00
Otto Winter
8861909ea4 Add native ESPHome API text sensor (#19377)
* Update

* 🚑 Lint
2018-12-17 20:51:59 +01:00
Otto Winter
4b124e4c25 Add native ESPHome API switch (#19376)
* Add esphomelib native API switch

* Update

* 🚑 Lint
2018-12-17 20:50:56 +01:00
Otto Winter
c45beeef6d Add native ESPHome API light (#19375)
* Add esphomelib native API light

* Update

* 🚑 Lint
2018-12-17 20:49:03 +01:00
Otto Winter
a158397b6d Add native ESPHome API fan (#19374)
* Add esphomelib native API fan

* Update

* 🚑 Lint
2018-12-17 20:46:57 +01:00
Otto Winter
a1fb6ae38f Add native ESPHome API cover (#19373)
* Add esphomelib native API cover

* Update

* 🚑 Lint
2018-12-17 20:44:47 +01:00
Otto Winter
8c67ebc143 Add native ESPHome API binary sensor (#19371)
* Add esphomelib native API binary sensor

* Fixes

* 🚑 Lint
2018-12-17 20:40:57 +01:00
Fredrik Erlandsson
40d8bd43a1 fix unique_id for Tellduslive sensors (#19389) 2018-12-17 20:33:01 +01:00
Fabian Affolter
c7ea1d07be Add air pollutants PROP_TO_ATTR (#19336)
* Add PROP_TO_ATTR

* Change condition
2018-12-17 19:38:36 +01:00
Fabian Affolter
e60de53404 Upgrade RPi.GPIO to 0.6.5 (#19392) 2018-12-17 12:31:03 -05:00
Fabian Affolter
3a1dc16c0d Set pytz to >=2018.07 (#19387) 2018-12-17 11:35:13 -05:00
Fabian Affolter
a6568fba7a Upgrade keyrings.alt==3.1.1 (#19386) 2018-12-17 11:34:19 -05:00
Fredrik Erlandsson
0ab9e33110 Version bump pydaikin (#19388)
* version bump pydaikin

* remove requirements from platform
2018-12-17 11:32:10 -05:00
Fabian Affolter
8483850729 Upgrade ruamel.yaml to 0.15.81 (#19384) 2018-12-17 08:28:35 -05:00
Fabian Affolter
90608da5c2 Upgrade TwitterAPI to 2.5.7 (#19382) 2018-12-17 13:52:09 +01:00
Fabian Affolter
6b8835b196 Upgrade youtube_dl to 2018.12.17 (#19378) 2018-12-17 13:28:20 +01:00
Paulus Schoutsen
f55ab9d4ea Merge pull request #19391 from home-assistant/rc
0.84.3
2018-12-17 12:44:50 +01:00
Paulus Schoutsen
23cc4d1453 Bumped version to 0.84.3 2018-12-17 10:46:39 +01:00
Glen Takahashi
f613cd38fc Fix not being able to update entities (#19344)
When editing an entity in the frontend dialog, pressing save causes a "save failed: Entity is already registered" error. This is because the frontend always sets `name` and `new_entity_id` in the websocket command even if they haven't been changed. This adds a check that the `new_entity_id` is actually different from `entity_id` before erroring that the `new_entity_id` is already registered.
2018-12-17 10:46:31 +01:00
liaanvdm
45238295df Fix restore state for manual alarm control panel (#19284)
* Fixed manual alarm control panel restore state

* Revert "Fixed manual alarm control panel restore state"

This reverts commit 61c9faf434.

* Fixed manual alarm control panel's state restore
2018-12-17 10:46:31 +01:00
Andrew Hayworth
65bd308491 Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks

PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)

When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.

For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:

RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report

Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report

Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report

Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.

The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.

* Fix linter
2018-12-17 10:46:30 +01:00
Paulus Schoutsen
30345489e6 Updated frontend to 20181211.1 2018-12-17 10:21:24 +01:00
Pascal Vizeli
2bf36bb1db Use unicode slugify (#19192)
* Update __init__.py

* Update setup.py

* Update requirements_all.txt

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* remove `-`

* fix packages

* Update package_constraints.txt

* Update __init__.py

* Update package_constraints.txt

* Update requirements_all.txt

* Update setup.py

* Fix tests

* Fix line issue

* fix all test

* fix type

* Fix lint
2018-12-17 07:51:13 +01:00
Aaron Bach
cc90cba78a Add support for statewide data for Flu Near You (#19341)
* Initial changes

* Add support for state-wide data for Flu Near You

* Bumped requirements

* Member comments
2018-12-16 18:51:56 -07:00
Otto Winter
a08bab7b18 Add native ESPHome API component (#19334)
* Create esphomelib component

* Update requirements

* Remove python 2 string literals

* MVP

* Remove config flow

* Remove config flow translations

* Use dispatcher more

* Use data classes

* Type Hints

* Cleanup on remove

* Use helper and cleanup

* Fix HA stop listener cleanup

* Add config flow

* Fix SyntaxError for Py3.5

* Update

* Lint

* Re-do tests

*  Rename to ESPHome

* Better error message for resolve errors

* Fix tests when aioesphomeapi not installed

* Refactor mock

* Update requirements

* Add strings.json

* 🍵 100% config flow  test coverage
2018-12-17 01:29:32 +01:00
Anders Melchiorsen
f9c02889b2 Remove recorder purge protection (#19358) 2018-12-16 23:31:50 +01:00
PeteBa
9d4de2a722 Initialise plant attributes at startup (#19315)
* Initialise plant attributes at startup

* Pvizeli review comments

* Martin review change
2018-12-16 21:54:33 +01:00
Joakim Sørensen
92c5249746 Add traccar motion, speed and battery_level attributes (#19090)
* Added motion, speed and battery attributes.

* Use dict[key] when we know there is a value there.

Co-Authored-By: ludeeus <joasoe@gmail.com>

* Use dict[key] when we know there is a value there.

Co-Authored-By: ludeeus <joasoe@gmail.com>

* Use dict[key] when we know there is a value there.

Co-Authored-By: ludeeus <joasoe@gmail.com>

* Use dict[key] when we know there is a value there.

Co-Authored-By: ludeeus <joasoe@gmail.com>

* Use dict[key] when we know there is a value there.

Co-Authored-By: ludeeus <joasoe@gmail.com>

* Use dict[key] when we know there is a value there.

Co-Authored-By: ludeeus <joasoe@gmail.com>
2018-12-16 20:09:45 +01:00
Glen Takahashi
b031ded671 Fix not being able to update entities (#19344)
When editing an entity in the frontend dialog, pressing save causes a "save failed: Entity is already registered" error. This is because the frontend always sets `name` and `new_entity_id` in the websocket command even if they haven't been changed. This adds a check that the `new_entity_id` is actually different from `entity_id` before erroring that the `new_entity_id` is already registered.
2018-12-16 13:06:27 -05:00
Fredrik Erlandsson
5a295ad42b Add config flow for Daikin (#19182)
* config flow for daikin

* tox test

* return fixes

* tox test fixes

* tox formatting
2018-12-16 16:19:18 +01:00
Jens
266477a4f5 Adds io:OnOffIOComponent as switch to tahoma.py (#19338) 2018-12-16 16:05:21 +01:00
emontnemery
07e3843918 Fix broken sensor.mqtt json_attributes deprecation message (#19349) 2018-12-16 15:44:25 +01:00
Christian Biamont
c26c8affc7 Add Brottsplatskartan sensor (#19018)
* Added brottsplatskartan sensor

* Update brottsplatskartan sensor

* Remove redundant configuration checks.
* Set attributes during initialization.
* Setup brottsplatskartan module in setup_platform().
* Just do a simple return, no need for returning false.

* Remove file that was used during testing

* Better variable name and remove excessive newline.

* More updates to brottsplatskartan

* Import uuid at the top of the module.
* Use sensor as domain.
* Don't fire custom events.
* Remove period from logging message.

* Adding validation for area parameter

* Validate empty area configuration

* Fixing indentation.

* Fixing the config schema
2018-12-16 12:21:07 +01:00
Malte Franken
629dd24ff3 bump georss_client to 0.5 (#19337) 2018-12-16 11:14:36 +01:00
Ville Skyttä
c545d2e22e Upgrade pydocstyle to 3.0.0 (#19328) 2018-12-15 21:11:28 +02:00
Ville Skyttä
06383a4383 Upgrade pytest to 4.0.2 (#19327) 2018-12-15 21:10:55 +02:00
emontnemery
3a7083900c Merge pull request #19223 from emontnemery/mqtt_sensor_json_attributes_topic
Add JSON attribute topic to MQTT sensor
2018-12-15 16:28:34 +01:00
John Mihalic
e2d3beca22 Bump pyEight to fix Single Sleeper (#19316) 2018-12-14 23:25:12 -05:00
Charles Garwood
70dbbbd974 Add note to issue template regarding frontend issues (#19295) 2018-12-14 19:06:20 -05:00
tmd224
d60b7d46b7 Add Ambient Weather PWS Sensor component (#18551)
* Adding ambient_station.py sensor and updating requirements file

* Cleaning up code and fixing flake8 warnings

* Fixing flake8 and pylint warnings

* Adding ambient_station.py sensor and updating requirements file

* Cleaning up code and fixing flake8 warnings

* Fixing flake8 and pylint warnings

* Fixing bug, things are working well now

* removing nosetests file

* Adding changes as requested in pull request #18551

* Updating with more change requests from PR code review

* Removing SCAN_INTERVAL from PLATFORM_SCHEMA

* Removing unused import

* Adding platform schema validation for monitored_conditions and conf_units

* Updating link to documentation in doc-string.  File already named in doc repo

* Only setup platform if component can successfully communicate with API

* Inverting check for platform setup success
2018-12-14 23:53:49 +01:00
Paulus Schoutsen
d83f20f743 Fix tests 2018-12-14 17:58:45 +01:00
Ville Skyttä
1c147b5c5f huawei_lte: Fetch only required data (#17618)
When debug logging is enabled, still fetch everything in order to
provide indication of available/supported data to users, as instructed
in docs.
2018-12-14 14:51:13 +01:00
Colin Harrington
9c62149b00 Adding support for Plum Lightpad (#16576)
* Adding basic Plum Lightpad support - https://plumlife.com/

* Used Const values
is_on is a bool

* Added LightpadPowerMeter Sensor to the plum_lightpad platform

* Moved to async setup, Introduced a PlumManager, events, subscription, Light and Power meter working

* Added PlumMotionSensor

* Added Glow Ring support

* Updated plum library and re-normalized

* set the glow-ring's icon

* Naming the glow ring

* Formatting and linting

* Cleaned up a number of linting issues.  Left a number of documentation warnings

* setup_platform migrated to async_setup_platform Plum discovery run as a job

* bumped plumlightpad version

* On shutdown disconnect the telnet session from each plum lightpad

* Cleanup & formatting. Worked on parallell cloud update

* Moved setup from async to non-async

* Utilize async_call_later from the helpers

* Cleanedup and linted, down to documentation & one #TODO

* Remove commented out debug lines

* Fixed Linting issues

* Remove TODO

* Updated comments & fixed Linting issues

* Added plumlightpad to requirements_all.txt

* Fixing imports with isort

* Added components to .coveragerc

* Added PLUM_DATA constant for accessing hass.data[PLUM_DATA]

* used dictionary syntax vs get(...) for config

* Linting needed an additonal line

* Fully async_setup now. removed @callback utilize bus events for detecting new devices found.

* Upgraded to plumlightpad 0.0.10

* Removed extra unused PLATFORM_SCHEMA declarations

* Moved listener attachment to `async_added_to_hass` and removed unused properties & device_state_attributes

* Utilized Discovery when devices were located

* Linting and cleanup

* used `hass.async_create_task` instead of `hass.async_add_job` per Martin

* Removed redundant criteria in if block

* Without discovery info, there is no need to setup

* Better state management and async on/off for Glow Ring

* renamed async_set_config back to set_config, fixed cleanup callback and Plum Initialization

* Fixed flake8 linting issues

* plumlightpad package update

* Add 'motion' device_class to Motion Sensor

* Fixed last known Linting issue

* let homeassistant handle setting the brightness state

* String formatting vs concatenation

* use shared aiohttp session from homeassistant

* Updating to use new formatting style

* looks like @cleanup isn't neccesary

* ditch the serial awaits

* Ensure async_add_entities is only called once per async_setup_platform

* Creating tasks to wait for vs coroutines

* Remove unused white component in the GlowRing

* Used local variables for GlowRing colors & added a setter for the hs_color property to keep the values in sync

* Linted and added docstring

* Update the documentation path to point to the component page

* Removed the extra sensor and binary_sensor platforms as requested. (To be added in later PRs)

* Update plum_lightpad.py

* Update plum_lightpad.py
2018-12-14 14:42:04 +01:00
liaanvdm
027920ff52 Fix restore state for manual alarm control panel (#19284)
* Fixed manual alarm control panel restore state

* Revert "Fixed manual alarm control panel restore state"

This reverts commit 61c9faf434.

* Fixed manual alarm control panel's state restore
2018-12-14 14:04:04 +01:00
Brian Towles
a30921e67d Set InsteonEntity name to be combo of description and address. (#17262)
* Set InsteonEntity name to be combo of description and address.

ie "LampLinc Dimmer 26453a" or "Keypad Dimmer 291abb_2"

Using a centralized network name

* Updated the name to have a more contextual references for device
functions then just the group id.

* Cleanup for hound

* Removed the _generate_network_address function.  Not used anymore

* Cleanup for lint

* clean for hound

* Moved descriptor mapper to be a class variable of the InsteonEntity in order to fix lib loading issue for lint

* Well, moved DescriptorMapper instance to a function variable now...

* fix hound

* Lint Cleanup

* Clean up docstrings

* Simplify Label lookup based on state name
2018-12-14 14:02:06 +01:00
pbalogh77
7f9cc10447 Device config for Fibaro hub integration (#19171) 2018-12-14 13:35:12 +01:00
Fabian Affolter
b88cf64850 Add air pollutants component (#18707)
* Add air pollutants component

* Fix lint issue

* Update docstrings

* Revert change

* Remove entries

* Remove imports

* Fix variable and other fixes

* Change tests

* Set SCAN_INTERVAL
2018-12-14 13:32:58 +01:00
Erik Eriksson
004179775c Updated ELIQ Online sensor to async API (#19248)
* Updated ELIQ Online sensor to async API

* Remove use of STATE_UNKNOWN
2018-12-14 13:25:28 +01:00
MaxG88
f95bd9c78f Set unavailable when unreachable (#19012)
* Turn GPMDP Off When Unavailable

* Update requirements_all.txt

* Specified Exception Type

* Update gpmdp.py
2018-12-14 13:14:32 +01:00
emontnemery
b97f0c0261 Make variable entity_id available to value_template for MQTT binary sensor (#19195)
* MQTT binary_sensor: Make variable `entity_id` available to value_template

* Review comments

* Add testcase
2018-12-14 13:00:37 +01:00
Eric Nagley
e886576a64 home-assistant/home-assistant#17333: update to use DOMAIN constants and standards. (#19242) 2018-12-14 12:41:09 +01:00
Paulus Schoutsen
bb11b0f067 Merge branch 'master' into dev 2018-12-14 11:22:05 +01:00
emontnemery
7a7c2ad416 Fix race in entity_platform.async_add_entities (#19222) 2018-12-14 10:33:37 +01:00
Luca Angemi
fb680bc1e4 Add automation and script events to logbook filter events (#19253)
* Add automation and script events to logbook filter events

* Update logbook.py

* Update logbook.py

* Update logbook tests

* Update test_logbook.py

* Update test_logbook.py

* Update test_logbook.py

* Update test_logbook.py
2018-12-14 10:25:02 +01:00
Paulus Schoutsen
4f98818258 Rename is_owner decorator to is_admin (#19266)
* Rename is_owner decorator to is_admin

* Update test_auth.py
2018-12-14 10:19:27 +01:00
Paulus Schoutsen
a5a896b519 Check admin permission before able to manage config entries (#19265)
* Check admin permission before able to manage config entries

* Lint
2018-12-14 10:19:04 +01:00
Paulus Schoutsen
0eb0faff03 Add permission check to light service (#19259) 2018-12-14 10:18:36 +01:00
Thibault Maekelbergh
4a23d4c7d3 Add NMBS (Belgian railway) sensor platform (#18610)
* Add custom component to core

* Add pyrail to reqs

* Format & lint

* Sort nmbs.py into place on coveragerc

* Only set up station live if provided

* Only set up sensor if config is provided

* Fix line too long linting error

* PR Remarks

* Add docstrings

* Fix hound line to long error

* Fix quotes

* Rebase coveragerc

* PR Review

* Init empty attrs

* Dont include delay if there is none

* PR review

* Safer check

* Rebase reqs

* Generate req

* Update homeassistant/components/sensor/nmbs.py

Co-Authored-By: thibmaek <thibault.maekelbergh@iCloud.com>

* PR remarks
2018-12-14 09:52:34 +01:00
Eric Nagley
377c61203d Fix call to super() (#19279)
* home-assistant/home-assistant#19273: fix call to super()

* home-assistant/home-assistant#19273: adjust to python3 standards.

* home-assistant/home-assistant#19273: remove bad test.
2018-12-14 08:52:29 +01:00
bremor
74a93fe764 Synology chat add verify ssl (#19276)
* Update synology_chat.py

* Added verify_ssl option to notify.synology_chat

Python requests will verify ssl by default, this configuration options allows the user to specify if they want to verify ssl or not. Non breaking change, default is True - do verify ssl.
2018-12-14 08:42:01 +01:00
Heine Furubotten
ddbfdf14e9 Upgraded enturclient to 0.1.2 (#19267) 2018-12-14 08:33:46 +01:00
Rohan Kapoor
e8ec74b944 Expose ZoneMinder availability to Home Assistant (#18946)
* Expose ZoneMinder availability to Home Assistant

* Bump zm-py to 0.2.0 with the availability changes published
2018-12-14 08:10:54 +01:00
damarco
f60f9bae00 Always add friendly name attribute to ZHA entities (#19141)
* Always add friendly name attribute

* Only change device_info name
2018-12-13 23:08:35 +01:00
Paulus Schoutsen
eada1a184c Improve check for duplicated entity_id (#19194)
* Fail if new entity_id is in hass.states

* Move check to websocket

* Review comments
2018-12-13 20:57:33 +01:00
Paulus Schoutsen
34cfdb4e35 Fix OwnTracks deadlocking (#19260)
* Fix OwnTracks deadlocking

* Fix deadlock
2018-12-13 20:56:48 +01:00
Paulus Schoutsen
85e6f92c5a Lint 2018-12-13 20:08:31 +01:00
Tom Harris
9efb90d23c Resolve IOLinc sensor name (#19050) 2018-12-13 16:52:12 +01:00
Fabian Affolter
66aa7d0e68 Fix list (fixes #19235) (#19258) 2018-12-13 16:43:59 +01:00
Fredrik Erlandsson
9f790325bb Fix point sensor discovery (#19245) 2018-12-13 16:40:56 +01:00
Sander Geerts
0fa7186296 Support for the Harman Kardon AVR (#18471)
* Feature: support for the HK AVR

* Remove testcode

* Feature: support for the HK AVR

* Remove testcode

* Added checklist

* Review fixes whitespaces

* Lint fixes

* Review fixes, add current source

* Remove unused imports

* Review fixes; State constants, dict[key]

* More review fixes, Unknown state and Sources

* Review fix; rename devices to entities
2018-12-13 16:31:14 +01:00
Paulus Schoutsen
90df932fe1 Check admin permission before able to manage config entries 2018-12-13 16:13:43 +01:00
emontnemery
7436c0fe42 Add device registry to MQTT light (#19013) 2018-12-13 15:51:50 +01:00
Erik Eriksson
6766d25e62 Re-use connection-pool (#19249)
Re-use connection-pool of VOC
2018-12-13 12:25:39 +01:00
Paulus Schoutsen
9d9e11372b Make automations log errors (#18965) 2018-12-13 12:21:16 +01:00
Paulus Schoutsen
8ea0a8d40b RFC: Deprecate auto target all for services and introduce entity_id: * (#19006)
* Deprecate auto target all

* Match on word 'all'
2018-12-13 10:07:59 +01:00
Andrey Kupreychik
56c7e78cf2 Bumped NDMS2 client to 0.0.6 (#19244) 2018-12-13 10:01:41 +01:00
Teemu R
2fc0dfecb1 Convert songpal to use asynchronous websocket for state updates (#19129)
* Add websocket-based non-polling variant for songpal

* linting fixes

* changes based on Martin's feedback

* Fix linting

* add backoff timer for reconnects, fix variable naming (I thought that this wouldn't matter for internals..)

* Remove poll configuration variable

* bump the version just to be sure, the previous release lacked a version file (required for setup.py)
2018-12-12 23:05:55 +01:00
kennedyshead
8c6b9b57cd Bump aioasuswrt (#19229)
* bump aioasuswrt version

* run gen_requirements
2018-12-12 20:24:44 +01:00
Aaron Bach
f8438e96d1 Add package data attribute to 17track.net summary sensors (#19213)
* 17track.net: Add package data attribute to summary sensors

* Member comments
2018-12-12 19:07:06 +01:00
Paulus Schoutsen
2926989ec3 Merge remote-tracking branch 'origin/master' into dev 2018-12-12 18:47:41 +01:00
Paulus Schoutsen
7d9e257713 Fix owntracks topic in encrypted ios (#19220)
* Fix owntracks topic

* Warn if per-topic secret and using HTTP
2018-12-12 17:17:27 +01:00
David F. Mulcahey
031ee71adf Add ZHA device handler library (#19099)
* event foundation

* implement quirks

* lock zha-quirks version

* allow quirks handling to be toggled on and off

* revert event commit

* disable warning

* update requirements_all

* Remove fix in favor of #19141

#19141 should be what ultimately corrects this issue.

* review comment
2018-12-12 17:06:22 +01:00
Erik
d03dfd985b Review comments 2018-12-12 16:30:42 +01:00
Erik
0e868deedd Add JSON attribute topic to MQTT sensor 2018-12-12 16:26:44 +01:00
Lukas Barth
4984030871 Fix geizhals crash if no price found (#19197)
* Fix geizhals crash if no price found

* Return None on unknown price.

* Linting

* Linting the linting
2018-12-12 16:11:18 +01:00
Jason Hunter
7de509dc76 Add automation and script events to logbook event types (#19219) 2018-12-12 15:54:25 +01:00
Paulus Schoutsen
6f4657fe02 Revert PR #18602 (#19188) 2018-12-12 11:44:50 +01:00
Fredrik Erlandsson
c0cd2d48ec add unique_id to SMHI (#19183) 2018-12-12 09:29:45 +01:00
Daniel Høyer Iversen
d1eb5da5f4 Update switchbot library (#19202) 2018-12-12 09:16:20 +01:00
Erik
c7492b0feb Move check to websocket 2018-12-11 20:20:57 +01:00
Fredrik Erlandsson
c20322232a Move daikin to package (#19187) 2018-12-11 18:17:45 +01:00
javicalle
61ca9bb8e4 Restore states for RFLink devices (#18816)
* Merge branch 'master' of https://github.com/home-assistant/home-assistant into dev

# Conflicts:
#	homeassistant/components/binary_sensor/point.py
#	homeassistant/components/cloud/__init__.py
#	homeassistant/components/cloud/prefs.py
#	homeassistant/components/frontend/__init__.py
#	homeassistant/components/light/fibaro.py
#	homeassistant/components/logbook.py
#	homeassistant/components/point/__init__.py
#	homeassistant/config_entries.py
#	homeassistant/const.py
#	homeassistant/helpers/service.py
#	requirements_all.txt
#	requirements_test_all.txt

* one 'async_get_last_state' refactor left behind

* Remove RestoreEntity inheritance (already in parent class)

* # pylint: disable=too-many-ancestors

* code predictor can be a bitch

* lint corrections

* # pylint: disable=too-many-ancestors

* recover from dict[key]

* Remove all 'coroutine' decorator, replace for 'async def'
Replace all 'yield from' for 'await'
Replace 'hass.async_add_job' for 'hass.async_create_task'
2018-12-11 17:20:30 +01:00
Erik
1f8156e26c Fail if new entity_id is in hass.states 2018-12-11 17:17:42 +01:00
Fabian Affolter
3e7b908a61 Add SCAN_INTERVAL (#19186)
* Add SCAN_INTERVAL

* More clean-up
2018-12-11 14:12:27 +01:00
Jonathan Keljo
eb4a44535c Enable alarmdecoder to see open/close state of bypassed RF zones when armed (#18477)
* Enable alarmdecoder to see open/close state of bypassed zones when armed

The alarmdecoder component already reported RF state bits as attributes. If the user knows which loop is set up for the zone in the alarm panel, they can use that information to tell whether the zone is open or closed even when the system is armed by monitoring the appropriate attribute. That’s awkward, so this commit enables the user to simply configure which loop is used and the component will update the state itself.

* Simplify, also it's more correct to treat it as a state change rather than a
permanent state, since it's possible the decoder might miss some events.

* Remove relative import
2018-12-11 11:34:03 +01:00
Paulus Schoutsen
ab8cf4f1e4 Updated frontend to 20181211.0 2018-12-11 10:30:24 +01:00
Paulus Schoutsen
557720b094 Fix cloud defaults (#19172) 2018-12-11 06:50:54 +01:00
Fredrik Erlandsson
92e19f6001 TelldusLive config flow (#18758)
* update TelldusLive to use config flow

* fixes from Martin

* Update homeassistant/components/tellduslive/config_flow.py

Co-Authored-By: fredrike <fredrik.e@gmail.com>

* revert changes in entry.py

* tox tests

* tox fixes

* woof woof (fix for hound)

* lint ignore

* unload entry

* coverall toxtests

* fix some toxtests
2018-12-10 18:44:45 +01:00
David F. Mulcahey
f4f42176bd ZHA - Event foundation (#19095)
* event foundation

* add missing periods to comments

* reworked so that entities don't fire events

* lint

* review comments
2018-12-10 10:59:50 -05:00
Paulus Schoutsen
59581786d3 Add raw service data to event (#19163) 2018-12-10 12:58:51 +01:00
Paulus Schoutsen
3cf8610cb3 Updated frontend to 20181210.1 2018-12-10 12:50:30 +01:00
Paulus Schoutsen
802497b05c Google_assistant: fix climate modes (#19084)
* home-assistant/home-assistant#18645: Fix climate mode mapping.

* home-assistant/home-assistant#18645: Remove un-used constants.

* home-assistant/home-assistant#18645: revert heat-cool -> auto change
2018-12-10 12:32:24 +01:00
Eric Nagley
df2f476c67 Google assistant fix target temp for *F values. (#19083)
* home-assistant/home-assistant#18524 : Add rounding to *F temps

* home-assistant/home-assistant#18524 : Linting

* simplify round behavior

* fix trailing whitespace

(thanks github editor)
2018-12-10 12:31:52 +01:00
Paulus Schoutsen
da338f2c1a Fix lovelace save (#19162) 2018-12-10 12:25:08 +01:00
Paulus Schoutsen
fdea9cb426 Drop OwnTracks bad packets (#19161) 2018-12-10 12:24:56 +01:00
Paulus Schoutsen
fe2d24c240 Update translations 2018-12-10 09:54:12 +01:00
Paulus Schoutsen
faab0aa9df Updated frontend to 20181210.0 2018-12-10 09:53:53 +01:00
Paulus Schoutsen
a521b885bf Lovelace using storage (#19101)
* Add MVP

* Remove unused code

* Fix

* Add force back

* Fix tests

* Storage keyed

* Error out when storage doesnt find config

* Use old load_yaml

* Set config for panel correct

* Use instance cache var

* Make config option
2018-12-10 08:57:17 +01:00
Yaron de Leeuw
a744dc270b Update pygtfs to upstream's 0.1.5 (#19151)
This works by adding `?check_same_thread=False` to the sqlite
connection string, as suggested by robbiet480@.
It upgrades pygtfs from homeassitant's forked 0.1.3 version to 0.1.5.

Fixes #15725
2018-12-09 23:32:48 +01:00
clayton craft
866c2ca994 Update radiotherm to 2.0.0 and handle change in tstat error detection (#19107)
* radiotherm: bump version to 2.0.0

* radiotherm: change handling of transient errors from tstat

Radiotherm 2.0.0 now throws an exception when a transient error is
detected, instead of returning -1 for the field where the error was
detected. This change supports handling the exception.
2018-12-09 23:27:31 +01:00
Fabian Affolter
bc8414a6f8 Merge pull request #19148 from scop/upgrade-upcloud
Upgrade upcloud-api to 0.4.3
2018-12-09 23:23:50 +01:00
Fabian Affolter
527f9cdfb2 Upgrade slacker to 0.12.0 (#19142) 2018-12-09 23:23:07 +01:00
Fabian Affolter
4c04fe652c Upgrade sphinx-autodoc-typehints to 1.5.2 (#19140) 2018-12-09 23:22:56 +01:00
Lukas Barth
5e65e27bda Update geizhals dependency (#19152) 2018-12-09 23:22:33 +01:00
Ville Skyttä
7d3a962f73 Upgrade mypy to 0.650 (#19150)
* Upgrade to 0.650

* Remove no longer needed type: ignore
2018-12-09 21:22:08 +02:00
Ville Skyttä
ce998cdc87 Upgrade upcloud-api to 0.4.3 2018-12-09 19:19:13 +02:00
Fabian Affolter
dbbbfaa869 Upgrade youtube_dl to 2018.12.03 (#19139) 2018-12-09 12:38:42 +01:00
Fabian Affolter
863edfd660 Upgrade slacker to 0.12.0 2018-12-09 11:34:53 +01:00
Ludovico de Nittis
4d4967d0dd Add code support for iAlarm (#19124) 2018-12-09 11:08:39 +01:00
Bas Schipper
4b4f51fb6f Fixed doorbird config without events (empty list) (#19121) 2018-12-09 10:33:39 +01:00
arigilder
30064655c2 Remove marking device tracker stale if state is stale (#19133) 2018-12-08 21:27:01 -07:00
edif30
fd5b92b2fb Update Google Assistant services description and request sync timeout (#19113)
* Fix google assistant request sync service call

* More descriptive services.yaml

* Update services.yaml

* Update __init__.py

* Update request sync service call timeout

Change from 5s to 15s to allow Google to respond.  5s was too short.  The service would sync but the service call would time out and throw the error.
2018-12-08 20:39:51 -05:00
Abílio Costa
6e55c2a345 update edp_redy version (#19078)
* update edp_redy version

* update requirements_all.txt
2018-12-08 21:16:16 +01:00
Sebastian Muszynski
2134331e2b Add Philips Moonlight Bedside Lamp support (#18496)
* Add Philips Moonlight Bedside Lamp support

* Update comment

* Make hound happy

* Wrap the call that could raise the exception only

* Remote blank line

* Use updated python-miio API
2018-12-08 14:49:14 +01:00
Daniel Høyer Iversen
ffe83d9ab1 Upgrade Mill library (#19117) 2018-12-08 11:38:42 +01:00
Sebastian Muszynski
f2f649680f Don't avoid async_schedule_update_ha_state by returning false (#19102) 2018-12-08 08:45:03 +01:00
Sebastian Muszynski
ece7b498ed Fix the Xiaomi Aqara Cube rotate event of the LAN protocol 2.0 (Closes: #18199) (#19104) 2018-12-08 08:28:39 +01:00
Sebastian Muszynski
65c2a25736 Support next generation of the Xiaomi Mi Smart Plug (chuangmi.plug.hmi205) (#19071)
* Add next generation of the Xiaomi Mi Smart Plug (chuangmi.plug.hmi205)

* Fix linting
2018-12-07 23:40:48 +01:00
Andrew Hayworth
05586de51f Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks

PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)

When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.

For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:

RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report

Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report

Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report

Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.

The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.

* Fix linter
2018-12-07 21:17:34 +01:00
Daniel Høyer Iversen
a58b3aad59 Upgrade Tibber lib (#19098) 2018-12-07 19:33:06 +01:00
Nick Horvath
e567e3d4e7 Bump skybellpy version to fix api issue (#19100) 2018-12-07 19:20:05 +01:00
phnx
385f0298bd home-assistant/home-assistant#18645: revert heat-cool -> auto change 2018-12-07 10:00:56 -05:00
speedmann
7edd241059 Automatically detect if ipv4/ipv6 is used for cert_expiry (#18916)
* Automatically detect if ipv4/ipv6 is used for cert_expiry

Fixes #18818
Python sockets use ipv4 per default. If the domain which should be checked
only has an ipv6 record, socket creation errors out with
`[Errno -2] Name or service not known`
This fix tries to guess the protocol family and creates the socket
with the correct family type

* Fix line length violation
2018-12-07 11:08:41 +01:00
Pierre Ståhl
5bf6951311 Upgrade pyatv to 0.3.12 (#19085) 2018-12-07 11:06:38 +01:00
phnx
24d0aa3f55 home-assistant/home-assistant#18645: Remove un-used constants. 2018-12-07 01:50:48 -05:00
emontnemery
5ff7563070 Merge pull request #18923 from emontnemery/mqtt_binary_sensor_json_attributes_topic
Add JSON attribute topic to MQTT binary sensor
2018-12-07 07:48:55 +01:00
Matthew Garrett
def4e89372 Bump lakeside requirement to support more Eufy devices (#19080)
The T1203 works fine with the existing protocol.
2018-12-07 07:32:21 +01:00
ehendrix23
8a62bc9237 Set directv unavailable state when errors returned for longer then a minute (#19014)
* Fix unavailable

Change setting to unavailable when getting request exceptions only after 1 minute has past since 1st occurrence.

* Put common code in _check_state_available

Put common code to determine if available should be set to False in method _check_state_available
2018-12-07 07:26:49 +01:00
phnx
471d94b6cd home-assistant/home-assistant#18645: Fix climate mode mapping. 2018-12-07 01:15:04 -05:00
Paulus Schoutsen
ce736e7ba1 Updated frontend to 20181207.0 2018-12-07 07:12:59 +01:00
Bram Kragten
455508deac Force refresh Lovelace (#19073)
* Force refresh Lovelace

* Check config on load

* Update __init__.py

* Update __init__.py
2018-12-07 07:09:05 +01:00
Anders Melchiorsen
0a3af545fe Upgrade aiolifx to 0.6.7 (#19077) 2018-12-07 07:06:35 +01:00
Mike Miller
dd92318762 Fix missing colorTemperatureInKelvin from Alexa responses (#19069)
* Fix missing colorTemperatureInKelvin from Alexa responses

* Update smart_home.py

* Add test
2018-12-06 17:05:14 +01:00
Sean Wilson
c931619269 Add CM17A support (#19041)
* Add CM17A support.

* Update log entry
2018-12-06 16:25:58 +01:00
Ville Skyttä
1be440a72b Upgrade pylint to 2.2.2 (#18750)
* Upgrade to 2.2.0

* simplifiable-if-expression fixes

* duplicate-string-formatting-argument fixes

* unused-import fixes

* Upgrade to 2.2.1

* Remove no longer needed disable

* Upgrade to 2.2.2
2018-12-06 11:54:44 +01:00
Paulus Schoutsen
04c7d5c128 Revert #17745 (#19064) 2018-12-06 10:38:26 +01:00
Paulus Schoutsen
30c77b9e64 Bumped version to 0.85.0.dev0 2018-12-06 09:36:44 +01:00
Erik
21197fb968 Review comments 2018-12-04 19:56:34 +01:00
Erik
b9ad19acbf Add JSON attribute topic to MQTT binary sensor
Add MqttAttributes mixin
2018-12-02 17:00:31 +01:00
446 changed files with 14667 additions and 4026 deletions

View File

@@ -73,7 +73,8 @@ omit =
homeassistant/components/comfoconnect.py
homeassistant/components/*/comfoconnect.py
homeassistant/components/daikin.py
homeassistant/components/daikin/__init__.py
homeassistant/components/daikin/const.py
homeassistant/components/*/daikin.py
homeassistant/components/digital_ocean.py
@@ -105,18 +106,24 @@ omit =
homeassistant/components/enocean.py
homeassistant/components/*/enocean.py
homeassistant/components/envisalink.py
homeassistant/components/envisalink/__init__.py
homeassistant/components/*/envisalink.py
homeassistant/components/evohome.py
homeassistant/components/*/evohome.py
homeassistant/components/freebox.py
homeassistant/components/*/freebox.py
homeassistant/components/fritzbox.py
homeassistant/components/*/fritzbox.py
homeassistant/components/ecovacs.py
homeassistant/components/*/ecovacs.py
homeassistant/components/esphome/__init__.py
homeassistant/components/*/esphome.py
homeassistant/components/eufy.py
homeassistant/components/*/eufy.py
@@ -160,6 +167,9 @@ omit =
homeassistant/components/homematicip_cloud.py
homeassistant/components/*/homematicip_cloud.py
homeassistant/components/homeworks.py
homeassistant/components/*/homeworks.py
homeassistant/components/huawei_lte.py
homeassistant/components/*/huawei_lte.py
@@ -203,6 +213,9 @@ omit =
homeassistant/components/lametric.py
homeassistant/components/*/lametric.py
homeassistant/components/lcn.py
homeassistant/components/*/lcn.py
homeassistant/components/linode.py
homeassistant/components/*/linode.py
@@ -265,6 +278,9 @@ omit =
homeassistant/components/openuv/__init__.py
homeassistant/components/*/openuv.py
homeassistant/components/plum_lightpad.py
homeassistant/components/*/plum_lightpad.py
homeassistant/components/pilight.py
homeassistant/components/*/pilight.py
@@ -287,6 +303,8 @@ omit =
homeassistant/components/raspihats.py
homeassistant/components/*/raspihats.py
homeassistant/components/*/raspyrfm.py
homeassistant/components/rfxtrx.py
homeassistant/components/*/rfxtrx.py
@@ -407,6 +425,7 @@ omit =
homeassistant/components/zha/__init__.py
homeassistant/components/zha/const.py
homeassistant/components/zha/event.py
homeassistant/components/zha/entities/*
homeassistant/components/zha/helpers.py
homeassistant/components/*/zha.py
@@ -423,6 +442,7 @@ omit =
homeassistant/components/spider.py
homeassistant/components/*/spider.py
homeassistant/components/air_pollutants/opensensemap.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/canary.py
homeassistant/components/alarm_control_panel/concord232.py
@@ -496,7 +516,6 @@ omit =
homeassistant/components/device_tracker/bt_smarthub.py
homeassistant/components/device_tracker/cisco_ios.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/freebox.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/google_maps.py
homeassistant/components/device_tracker/googlehome.py
@@ -533,6 +552,7 @@ omit =
homeassistant/components/folder_watcher.py
homeassistant/components/foursquare.py
homeassistant/components/goalfeed.py
homeassistant/components/idteck_prox.py
homeassistant/components/ifttt.py
homeassistant/components/image_processing/dlib_face_detect.py
homeassistant/components/image_processing/dlib_face_identify.py
@@ -596,6 +616,7 @@ omit =
homeassistant/components/media_player/frontier_silicon.py
homeassistant/components/media_player/gpmdp.py
homeassistant/components/media_player/gstreamer.py
homeassistant/components/media_player/harman_kardon_avr.py
homeassistant/components/media_player/horizon.py
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
@@ -680,8 +701,10 @@ omit =
homeassistant/components/route53.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/scene/lifx_cloud.py
homeassistant/components/sensor/aftership.py
homeassistant/components/sensor/airvisual.py
homeassistant/components/sensor/alpha_vantage.py
homeassistant/components/sensor/ambient_station.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/arwn.py
homeassistant/components/sensor/bbox.py
@@ -692,6 +715,7 @@ omit =
homeassistant/components/sensor/bme680.py
homeassistant/components/sensor/bom.py
homeassistant/components/sensor/broadlink.py
homeassistant/components/sensor/brottsplatskartan.py
homeassistant/components/sensor/buienradar.py
homeassistant/components/sensor/cert_expiry.py
homeassistant/components/sensor/citybikes.py
@@ -737,6 +761,7 @@ omit =
homeassistant/components/sensor/google_travel_time.py
homeassistant/components/sensor/gpsd.py
homeassistant/components/sensor/gtfs.py
homeassistant/components/sensor/gtt.py
homeassistant/components/sensor/haveibeenpwned.py
homeassistant/components/sensor/hp_ilo.py
homeassistant/components/sensor/htu21d.py
@@ -752,6 +777,7 @@ omit =
homeassistant/components/sensor/launch_library.py
homeassistant/components/sensor/linky.py
homeassistant/components/sensor/linux_battery.py
homeassistant/components/sensor/london_underground.py
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/luftdaten.py
homeassistant/components/sensor/lyft.py
@@ -769,6 +795,7 @@ omit =
homeassistant/components/sensor/netdata.py
homeassistant/components/sensor/netdata_public.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nmbs.py
homeassistant/components/sensor/noaa_tides.py
homeassistant/components/sensor/nsw_fuel_station.py
homeassistant/components/sensor/nut.py
@@ -785,6 +812,7 @@ omit =
homeassistant/components/sensor/pocketcasts.py
homeassistant/components/sensor/pollen.py
homeassistant/components/sensor/postnl.py
homeassistant/components/sensor/prezzibenzina.py
homeassistant/components/sensor/pushbullet.py
homeassistant/components/sensor/pvoutput.py
homeassistant/components/sensor/pyload.py
@@ -809,6 +837,7 @@ omit =
homeassistant/components/sensor/snmp.py
homeassistant/components/sensor/sochain.py
homeassistant/components/sensor/socialblade.py
homeassistant/components/sensor/solaredge.py
homeassistant/components/sensor/sonarr.py
homeassistant/components/sensor/speedtest.py
homeassistant/components/sensor/spotcrime.py
@@ -864,6 +893,7 @@ omit =
homeassistant/components/switch/mystrom.py
homeassistant/components/switch/netio.py
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/pencom.py
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rainbird.py
homeassistant/components/switch/rest.py

View File

@@ -1,6 +1,7 @@
<!-- READ THIS FIRST:
- If you need additional help with this template please refer to https://www.home-assistant.io/help/reporting_issues/
- Make sure you are running the latest version of Home Assistant before reporting an issue: https://github.com/home-assistant/home-assistant/releases
- Frontend issues should be submitted to the home-assistant-polymer repository: https://github.com/home-assistant/home-assistant-polymer/issues
- Do not report issues for components if you are using custom components: files in <config-dir>/custom_components
- This is for bugs only. Feature and enhancement requests should go in our community forum: https://community.home-assistant.io/c/feature-requests
- Provide as many details as possible. Paste logs, configuration sample and code into the backticks. Do not delete any text from this template!

View File

@@ -7,6 +7,7 @@ about: Create a report to help us improve
<!-- READ THIS FIRST:
- If you need additional help with this template please refer to https://www.home-assistant.io/help/reporting_issues/
- Make sure you are running the latest version of Home Assistant before reporting an issue: https://github.com/home-assistant/home-assistant/releases
- Frontend issues should be submitted to the home-assistant-polymer repository: https://github.com/home-assistant/home-assistant-polymer/issues
- Do not report issues for components if you are using custom components: files in <config-dir>/custom_components
- This is for bugs only. Feature and enhancement requests should go in our community forum: https://community.home-assistant.io/c/feature-requests
- Provide as many details as possible. Paste logs, configuration sample and code into the backticks. Do not delete any text from this template!

View File

@@ -184,6 +184,8 @@ homeassistant/components/*/edp_redy.py @abmantis
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
@@ -211,6 +213,10 @@ homeassistant/components/melissa.py @kennedyshead
homeassistant/components/*/melissa.py @kennedyshead
homeassistant/components/*/mystrom.py @fabaff
# N
homeassistant/components/ness_alarm.py @nickw444
homeassistant/components/*/ness_alarm.py @nickw444
# O
homeassistant/components/openuv/* @bachya
homeassistant/components/*/openuv.py @bachya

View File

@@ -1,4 +1,4 @@
Home Assistant |Build Status| |Coverage Status| |Chat Status| |Reviewed by Hound|
Home Assistant |Build Status| |Coverage Status| |Chat Status|
=================================================================================
Home Assistant is a home automation platform running on Python 3. It is able to track and control all devices at home and offer a platform for automating control.
@@ -33,8 +33,6 @@ of a component, check the `Home Assistant help section <https://home-assistant.i
:target: https://coveralls.io/r/home-assistant/home-assistant?branch=master
.. |Chat Status| image:: https://img.shields.io/discord/330944238910963714.svg
:target: https://discord.gg/c5DvZ4e
.. |Reviewed by Hound| image:: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg
:target: https://houndci.com
.. |screenshot-states| image:: https://raw.github.com/home-assistant/home-assistant/master/docs/screenshots.png
:target: https://home-assistant.io/demo/
.. |screenshot-components| image:: https://raw.github.com/home-assistant/home-assistant/dev/docs/screenshot-components.png

View File

@@ -1,5 +1,6 @@
"""Permission constants."""
CAT_ENTITIES = 'entities'
CAT_CONFIG_ENTRIES = 'config_entries'
SUBCAT_ALL = 'all'
POLICY_READ = 'read'

View File

@@ -125,16 +125,23 @@ class AdsHub:
def shutdown(self, *args, **kwargs):
"""Shutdown ADS connection."""
import pyads
_LOGGER.debug("Shutting down ADS")
for notification_item in self._notification_items.values():
self._client.del_device_notification(
notification_item.hnotify,
notification_item.huser
)
_LOGGER.debug(
"Deleting device notification %d, %d",
notification_item.hnotify, notification_item.huser)
self._client.close()
try:
self._client.del_device_notification(
notification_item.hnotify,
notification_item.huser
)
except pyads.ADSError as err:
_LOGGER.error(err)
try:
self._client.close()
except pyads.ADSError as err:
_LOGGER.error(err)
def register_device(self, device):
"""Register a new device."""

View File

@@ -0,0 +1,147 @@
"""
Component for handling Air Pollutants data for your location.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/air_pollutants/
"""
from datetime import timedelta
import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
ATTR_AIR_POLLUTANTS_AQI = 'air_quality_index'
ATTR_AIR_POLLUTANTS_ATTRIBUTION = 'attribution'
ATTR_AIR_POLLUTANTS_C02 = 'carbon_dioxide'
ATTR_AIR_POLLUTANTS_CO = 'carbon_monoxide'
ATTR_AIR_POLLUTANTS_N2O = 'nitrogen_oxide'
ATTR_AIR_POLLUTANTS_NO = 'nitrogen_monoxide'
ATTR_AIR_POLLUTANTS_NO2 = 'nitrogen_dioxide'
ATTR_AIR_POLLUTANTS_OZONE = 'ozone'
ATTR_AIR_POLLUTANTS_PM_0_1 = 'particulate_matter_0_1'
ATTR_AIR_POLLUTANTS_PM_10 = 'particulate_matter_10'
ATTR_AIR_POLLUTANTS_PM_2_5 = 'particulate_matter_2_5'
ATTR_AIR_POLLUTANTS_SO2 = 'sulphur_dioxide'
DOMAIN = 'air_pollutants'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SCAN_INTERVAL = timedelta(seconds=30)
PROP_TO_ATTR = {
'air_quality_index': ATTR_AIR_POLLUTANTS_AQI,
'attribution': ATTR_AIR_POLLUTANTS_ATTRIBUTION,
'carbon_dioxide': ATTR_AIR_POLLUTANTS_C02,
'carbon_monoxide': ATTR_AIR_POLLUTANTS_CO,
'nitrogen_oxide': ATTR_AIR_POLLUTANTS_N2O,
'nitrogen_monoxide': ATTR_AIR_POLLUTANTS_NO,
'nitrogen_dioxide': ATTR_AIR_POLLUTANTS_NO2,
'ozone': ATTR_AIR_POLLUTANTS_OZONE,
'particulate_matter_0_1': ATTR_AIR_POLLUTANTS_PM_0_1,
'particulate_matter_10': ATTR_AIR_POLLUTANTS_PM_10,
'particulate_matter_2_5': ATTR_AIR_POLLUTANTS_PM_2_5,
'sulphur_dioxide': ATTR_AIR_POLLUTANTS_SO2,
}
async def async_setup(hass, config):
"""Set up the air pollutants component."""
component = hass.data[DOMAIN] = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
await component.async_setup(config)
return True
async def async_setup_entry(hass, entry):
"""Set up a config entry."""
return await hass.data[DOMAIN].async_setup_entry(entry)
async def async_unload_entry(hass, entry):
"""Unload a config entry."""
return await hass.data[DOMAIN].async_unload_entry(entry)
class AirPollutantsEntity(Entity):
"""ABC for air pollutants data."""
@property
def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level."""
raise NotImplementedError()
@property
def particulate_matter_10(self):
"""Return the particulate matter 10 level."""
return None
@property
def particulate_matter_0_1(self):
"""Return the particulate matter 0.1 level."""
return None
@property
def air_quality_index(self):
"""Return the Air Quality Index (AQI)."""
return None
@property
def ozone(self):
"""Return the O3 (ozone) level."""
return None
@property
def carbon_monoxide(self):
"""Return the CO (carbon monoxide) level."""
return None
@property
def carbon_dioxide(self):
"""Return the CO2 (carbon dioxide) level."""
return None
@property
def attribution(self):
"""Return the attribution."""
return None
@property
def sulphur_dioxide(self):
"""Return the SO2 (sulphur dioxide) level."""
return None
@property
def nitrogen_oxide(self):
"""Return the N2O (nitrogen oxide) level."""
return None
@property
def nitrogen_monoxide(self):
"""Return the NO (nitrogen monoxide) level."""
return None
@property
def nitrogen_dioxide(self):
"""Return the NO2 (nitrogen dioxide) level."""
return None
@property
def state_attributes(self):
"""Return the state attributes."""
data = {}
for prop, attr in PROP_TO_ATTR.items():
value = getattr(self, prop)
if value is not None:
data[attr] = value
return data
@property
def state(self):
"""Return the current state."""
return self.particulate_matter_2_5

View File

@@ -0,0 +1,56 @@
"""
Demo platform that offers fake air pollutants data.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
from homeassistant.components.air_pollutants import AirPollutantsEntity
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Air Pollutants."""
add_entities([
DemoAirPollutants('Home', 14, 23, 100),
DemoAirPollutants('Office', 4, 16, None)
])
class DemoAirPollutants(AirPollutantsEntity):
"""Representation of Air Pollutants data."""
def __init__(self, name, pm_2_5, pm_10, n2o):
"""Initialize the Demo Air Pollutants."""
self._name = name
self._pm_2_5 = pm_2_5
self._pm_10 = pm_10
self._n2o = n2o
@property
def name(self):
"""Return the name of the sensor."""
return '{} {}'.format('Demo Air Pollutants', self._name)
@property
def should_poll(self):
"""No polling needed for Demo Air Pollutants."""
return False
@property
def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level."""
return self._pm_2_5
@property
def particulate_matter_10(self):
"""Return the particulate matter 10 level."""
return self._pm_10
@property
def nitrogen_oxide(self):
"""Return the nitrogen oxide (N2O) level."""
return self._n2o
@property
def attribution(self):
"""Return the attribution."""
return 'Powered by Home Assistant'

View File

@@ -0,0 +1,105 @@
"""
Support for openSenseMap Air Pollutants data.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/air_pollutants_opensensemap/
"""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.components.air_pollutants import (
PLATFORM_SCHEMA, AirPollutantsEntity)
from homeassistant.const import CONF_NAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
REQUIREMENTS = ['opensensemap-api==0.1.3']
_LOGGER = logging.getLogger(__name__)
ATTRIBUTION = 'Data provided by openSenseMap'
CONF_STATION_ID = 'station_id'
SCAN_INTERVAL = timedelta(minutes=10)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_STATION_ID): cv.string,
vol.Optional(CONF_NAME): cv.string,
})
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Set up the openSenseMap air pollutants platform."""
from opensensemap_api import OpenSenseMap
name = config.get(CONF_NAME)
station_id = config[CONF_STATION_ID]
session = async_get_clientsession(hass)
osm_api = OpenSenseMapData(OpenSenseMap(station_id, hass.loop, session))
await osm_api.async_update()
if 'name' not in osm_api.api.data:
_LOGGER.error("Station %s is not available", station_id)
return
station_name = osm_api.api.data['name'] if name is None else name
async_add_entities([OpenSenseMapPollutants(station_name, osm_api)], True)
class OpenSenseMapPollutants(AirPollutantsEntity):
"""Implementation of an openSenseMap air pollutants entity."""
def __init__(self, name, osm):
"""Initialize the air pollutants entity."""
self._name = name
self._osm = osm
@property
def name(self):
"""Return the name of the air pollutants entity."""
return self._name
@property
def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level."""
return self._osm.api.pm2_5
@property
def particulate_matter_10(self):
"""Return the particulate matter 10 level."""
return self._osm.api.pm10
@property
def attribution(self):
"""Return the attribution."""
return ATTRIBUTION
async def async_update(self):
"""Get the latest data from the openSenseMap API."""
await self._osm.async_update()
class OpenSenseMapData:
"""Get the latest data and update the states."""
def __init__(self, api):
"""Initialize the data object."""
self.api = api
@Throttle(SCAN_INTERVAL)
async def async_update(self):
"""Get the latest data from the Pi-hole."""
from opensensemap_api.exceptions import OpenSenseMapError
try:
await self.api.get_data()
except OpenSenseMapError as err:
_LOGGER.error("Unable to fetch data: %s", err)

View File

@@ -25,7 +25,7 @@ ATTR_CHANGED_BY = 'changed_by'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
ALARM_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Optional(ATTR_CODE): cv.string,
})

View File

@@ -5,14 +5,16 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.ialarm/
"""
import logging
import re
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED)
CONF_CODE, CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyialarm==0.3']
@@ -36,6 +38,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): vol.All(cv.string, no_application_protocol),
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_CODE): cv.positive_int,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
@@ -43,23 +46,25 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up an iAlarm control panel."""
name = config.get(CONF_NAME)
code = config.get(CONF_CODE)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
host = config.get(CONF_HOST)
url = 'http://{}'.format(host)
ialarm = IAlarmPanel(name, username, password, url)
ialarm = IAlarmPanel(name, code, username, password, url)
add_entities([ialarm], True)
class IAlarmPanel(alarm.AlarmControlPanel):
"""Representation of an iAlarm status."""
def __init__(self, name, username, password, url):
def __init__(self, name, code, username, password, url):
"""Initialize the iAlarm status."""
from pyialarm import IAlarm
self._name = name
self._code = str(code) if code else None
self._username = username
self._password = password
self._url = url
@@ -71,6 +76,15 @@ class IAlarmPanel(alarm.AlarmControlPanel):
"""Return the name of the device."""
return self._name
@property
def code_format(self):
"""Return one or more digits/characters."""
if self._code is None:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
@property
def state(self):
"""Return the state of the device."""
@@ -98,12 +112,22 @@ class IAlarmPanel(alarm.AlarmControlPanel):
def alarm_disarm(self, code=None):
"""Send disarm command."""
self._client.disarm()
if self._validate_code(code):
self._client.disarm()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._client.arm_away()
if self._validate_code(code):
self._client.arm_away()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._client.arm_stay()
if self._validate_code(code):
self._client.arm_stay()
def _validate_code(self, code):
"""Validate given code."""
check = self._code is None or code == self._code
if not check:
_LOGGER.warning("Wrong code entered")
return check

View File

@@ -310,7 +310,15 @@ class ManualAlarm(alarm.AlarmControlPanel, RestoreEntity):
async def async_added_to_hass(self):
"""Run when entity about to be added to hass."""
await super().async_added_to_hass()
state = await self.async_get_last_state()
if state:
self._state = state.state
self._state_ts = state.last_updated
if state.state == STATE_ALARM_PENDING and \
hasattr(state, 'attributes') and \
state.attributes['pre_pending_state']:
# If in pending state, we return to the pre_pending_state
self._state = state.attributes['pre_pending_state']
self._state_ts = dt_util.utcnow()
else:
self._state = state.state
self._state_ts = state.last_updated

View File

@@ -13,13 +13,14 @@ from homeassistant.core import callback
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components import mqtt
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN,
CONF_NAME, CONF_CODE)
CONF_CODE, CONF_DEVICE, CONF_NAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
from homeassistant.components.mqtt import (
ATTR_DISCOVERY_HASH, CONF_AVAILABILITY_TOPIC, CONF_STATE_TOPIC,
CONF_COMMAND_TOPIC, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE,
CONF_QOS, CONF_RETAIN, MqttAvailability, MqttDiscoveryUpdate, subscription)
ATTR_DISCOVERY_HASH, CONF_AVAILABILITY_TOPIC, CONF_COMMAND_TOPIC,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_QOS, CONF_RETAIN,
CONF_STATE_TOPIC, MqttAvailability, MqttDiscoveryUpdate,
MqttEntityDeviceInfo, subscription)
from homeassistant.components.mqtt.discovery import MQTT_DISCOVERY_NEW
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -30,6 +31,7 @@ _LOGGER = logging.getLogger(__name__)
CONF_PAYLOAD_DISARM = 'payload_disarm'
CONF_PAYLOAD_ARM_HOME = 'payload_arm_home'
CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away'
CONF_UNIQUE_ID = 'unique_id'
DEFAULT_ARM_AWAY = 'ARM_AWAY'
DEFAULT_ARM_HOME = 'ARM_HOME'
@@ -45,6 +47,8 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
@@ -73,7 +77,7 @@ async def _async_setup_entity(config, async_add_entities,
async_add_entities([MqttAlarm(config, discovery_hash)])
class MqttAlarm(MqttAvailability, MqttDiscoveryUpdate,
class MqttAlarm(MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo,
alarm.AlarmControlPanel):
"""Representation of a MQTT alarm status."""
@@ -81,17 +85,20 @@ class MqttAlarm(MqttAvailability, MqttDiscoveryUpdate,
"""Init the MQTT Alarm Control Panel."""
self._state = STATE_UNKNOWN
self._config = config
self._unique_id = config.get(CONF_UNIQUE_ID)
self._sub_state = None
availability_topic = config.get(CONF_AVAILABILITY_TOPIC)
payload_available = config.get(CONF_PAYLOAD_AVAILABLE)
payload_not_available = config.get(CONF_PAYLOAD_NOT_AVAILABLE)
qos = config.get(CONF_QOS)
device_config = config.get(CONF_DEVICE)
MqttAvailability.__init__(self, availability_topic, qos,
payload_available, payload_not_available)
MqttDiscoveryUpdate.__init__(self, discovery_hash,
self.discovery_update)
MqttEntityDeviceInfo.__init__(self, device_config)
async def async_added_to_hass(self):
"""Subscribe mqtt events."""
@@ -127,7 +134,8 @@ class MqttAlarm(MqttAvailability, MqttDiscoveryUpdate,
async def async_will_remove_from_hass(self):
"""Unsubscribe when removed."""
await subscription.async_unsubscribe_topics(self.hass, self._sub_state)
self._sub_state = await subscription.async_unsubscribe_topics(
self.hass, self._sub_state)
await MqttAvailability.async_will_remove_from_hass(self)
@property
@@ -140,6 +148,11 @@ class MqttAlarm(MqttAvailability, MqttDiscoveryUpdate,
"""Return the name of the device."""
return self._config.get(CONF_NAME)
@property
def unique_id(self):
"""Return a unique ID."""
return self._unique_id
@property
def state(self):
"""Return the state of the device."""

View File

@@ -0,0 +1,107 @@
"""
Support for Ness D8X/D16X alarm panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.ness_alarm/
"""
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.ness_alarm import (
DATA_NESS, SIGNAL_ARMING_STATE_CHANGED)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING,
STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_DISARMED)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['ness_alarm']
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up the Ness Alarm alarm control panel devices."""
if discovery_info is None:
return
device = NessAlarmPanel(hass.data[DATA_NESS], 'Alarm Panel')
async_add_entities([device])
class NessAlarmPanel(alarm.AlarmControlPanel):
"""Representation of a Ness alarm panel."""
def __init__(self, client, name):
"""Initialize the alarm panel."""
self._client = client
self._name = name
self._state = None
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_ARMING_STATE_CHANGED,
self._handle_arming_state_change)
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def should_poll(self):
"""Return the polling state."""
return False
@property
def code_format(self):
"""Return the regex for code format or None if no code is required."""
return 'Number'
@property
def state(self):
"""Return the state of the device."""
return self._state
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
await self._client.disarm(code)
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
await self._client.arm_away(code)
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
await self._client.arm_home(code)
async def async_alarm_trigger(self, code=None):
"""Send trigger/panic command."""
await self._client.panic(code)
@callback
def _handle_arming_state_change(self, arming_state):
"""Handle arming state update."""
from nessclient import ArmingState
if arming_state == ArmingState.UNKNOWN:
self._state = None
elif arming_state == ArmingState.DISARMED:
self._state = STATE_ALARM_DISARMED
elif arming_state == ArmingState.ARMING:
self._state = STATE_ALARM_ARMING
elif arming_state == ArmingState.EXIT_DELAY:
self._state = STATE_ALARM_ARMING
elif arming_state == ArmingState.ARMED:
self._state = STATE_ALARM_ARMED_AWAY
elif arming_state == ArmingState.ENTRY_DELAY:
self._state = STATE_ALARM_PENDING
elif arming_state == ArmingState.TRIGGERED:
self._state = STATE_ALARM_TRIGGERED
else:
_LOGGER.warning("Unhandled arming state: %s", arming_state)
self.async_schedule_update_ha_state()

View File

@@ -13,7 +13,7 @@ import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PORT, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pynx584==0.4']
@@ -43,7 +43,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
add_entities([NX584Alarm(hass, url, name)])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error("Unable to connect to NX584: %s", str(ex))
return False
return
class NX584Alarm(alarm.AlarmControlPanel):
@@ -60,7 +60,7 @@ class NX584Alarm(alarm.AlarmControlPanel):
# talk to the API and trigger a requests exception for setup_platform()
# to catch
self._alarm.list_zones()
self._state = STATE_UNKNOWN
self._state = None
@property
def name(self):
@@ -85,11 +85,11 @@ class NX584Alarm(alarm.AlarmControlPanel):
except requests.exceptions.ConnectionError as ex:
_LOGGER.error("Unable to connect to %(host)s: %(reason)s",
dict(host=self._url, reason=ex))
self._state = STATE_UNKNOWN
self._state = None
zones = []
except IndexError:
_LOGGER.error("NX584 reports no partitions")
self._state = STATE_UNKNOWN
self._state = None
zones = []
bypassed = False
@@ -107,6 +107,10 @@ class NX584Alarm(alarm.AlarmControlPanel):
else:
self._state = STATE_ALARM_ARMED_AWAY
for flag in part['condition_flags']:
if flag == "Siren on":
self._state = STATE_ALARM_TRIGGERED
def alarm_disarm(self, code=None):
"""Send disarm command."""
self._alarm.disarm(code)

View File

@@ -15,7 +15,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['yalesmartalarmclient==0.1.5']
REQUIREMENTS = ['yalesmartalarmclient==0.1.6']
CONF_AREA_ID = 'area_id'

View File

@@ -32,6 +32,7 @@ CONF_DEVICE_TYPE = 'type'
CONF_PANEL_DISPLAY = 'panel_display'
CONF_ZONE_NAME = 'name'
CONF_ZONE_TYPE = 'type'
CONF_ZONE_LOOP = 'loop'
CONF_ZONE_RFID = 'rfid'
CONF_ZONES = 'zones'
CONF_RELAY_ADDR = 'relayaddr'
@@ -75,6 +76,8 @@ ZONE_SCHEMA = vol.Schema({
vol.Optional(CONF_ZONE_TYPE,
default=DEFAULT_ZONE_TYPE): vol.Any(DEVICE_CLASSES_SCHEMA),
vol.Optional(CONF_ZONE_RFID): cv.string,
vol.Optional(CONF_ZONE_LOOP):
vol.All(vol.Coerce(int), vol.Range(min=1, max=4)),
vol.Inclusive(CONF_RELAY_ADDR, 'relaylocation',
'Relay address and channel must exist together'): cv.byte,
vol.Inclusive(CONF_RELAY_CHAN, 'relaylocation',

View File

@@ -13,8 +13,9 @@ from homeassistant.helpers import entityfilter
from . import flash_briefings, intent, smart_home
from .const import (
CONF_AUDIO, CONF_DISPLAY_URL, CONF_TEXT, CONF_TITLE, CONF_UID, DOMAIN,
CONF_FILTER, CONF_ENTITY_CONFIG)
CONF_AUDIO, CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DISPLAY_URL,
CONF_ENDPOINT, CONF_TEXT, CONF_TITLE, CONF_UID, DOMAIN, CONF_FILTER,
CONF_ENTITY_CONFIG)
_LOGGER = logging.getLogger(__name__)
@@ -30,6 +31,9 @@ ALEXA_ENTITY_SCHEMA = vol.Schema({
})
SMART_HOME_SCHEMA = vol.Schema({
vol.Optional(CONF_ENDPOINT): cv.string,
vol.Optional(CONF_CLIENT_ID): cv.string,
vol.Optional(CONF_CLIENT_SECRET): cv.string,
vol.Optional(CONF_FILTER, default={}): entityfilter.FILTER_SCHEMA,
vol.Optional(CONF_ENTITY_CONFIG): {cv.entity_id: ALEXA_ENTITY_SCHEMA}
})

View File

@@ -0,0 +1,154 @@
"""Support for Alexa skill auth."""
import asyncio
import json
import logging
from datetime import timedelta
import aiohttp
import async_timeout
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client
from homeassistant.util import dt
from .const import DEFAULT_TIMEOUT
_LOGGER = logging.getLogger(__name__)
LWA_TOKEN_URI = "https://api.amazon.com/auth/o2/token"
LWA_HEADERS = {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
}
PREEMPTIVE_REFRESH_TTL_IN_SECONDS = 300
STORAGE_KEY = 'alexa_auth'
STORAGE_VERSION = 1
STORAGE_EXPIRE_TIME = "expire_time"
STORAGE_ACCESS_TOKEN = "access_token"
STORAGE_REFRESH_TOKEN = "refresh_token"
class Auth:
"""Handle authentication to send events to Alexa."""
def __init__(self, hass, client_id, client_secret):
"""Initialize the Auth class."""
self.hass = hass
self.client_id = client_id
self.client_secret = client_secret
self._prefs = None
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
self._get_token_lock = asyncio.Lock(loop=hass.loop)
async def async_do_auth(self, accept_grant_code):
"""Do authentication with an AcceptGrant code."""
# access token not retrieved yet for the first time, so this should
# be an access token request
lwa_params = {
"grant_type": "authorization_code",
"code": accept_grant_code,
"client_id": self.client_id,
"client_secret": self.client_secret
}
_LOGGER.debug("Calling LWA to get the access token (first time), "
"with: %s", json.dumps(lwa_params))
return await self._async_request_new_token(lwa_params)
async def async_get_access_token(self):
"""Perform access token or token refresh request."""
async with self._get_token_lock:
if self._prefs is None:
await self.async_load_preferences()
if self.is_token_valid():
_LOGGER.debug("Token still valid, using it.")
return self._prefs[STORAGE_ACCESS_TOKEN]
if self._prefs[STORAGE_REFRESH_TOKEN] is None:
_LOGGER.debug("Token invalid and no refresh token available.")
return None
lwa_params = {
"grant_type": "refresh_token",
"refresh_token": self._prefs[STORAGE_REFRESH_TOKEN],
"client_id": self.client_id,
"client_secret": self.client_secret
}
_LOGGER.debug("Calling LWA to refresh the access token.")
return await self._async_request_new_token(lwa_params)
@callback
def is_token_valid(self):
"""Check if a token is already loaded and if it is still valid."""
if not self._prefs[STORAGE_ACCESS_TOKEN]:
return False
expire_time = dt.parse_datetime(self._prefs[STORAGE_EXPIRE_TIME])
preemptive_expire_time = expire_time - timedelta(
seconds=PREEMPTIVE_REFRESH_TTL_IN_SECONDS)
return dt.utcnow() < preemptive_expire_time
async def _async_request_new_token(self, lwa_params):
try:
session = aiohttp_client.async_get_clientsession(self.hass)
with async_timeout.timeout(DEFAULT_TIMEOUT, loop=self.hass.loop):
response = await session.post(LWA_TOKEN_URI,
headers=LWA_HEADERS,
data=lwa_params,
allow_redirects=True)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Timeout calling LWA to get auth token.")
return None
_LOGGER.debug("LWA response header: %s", response.headers)
_LOGGER.debug("LWA response status: %s", response.status)
if response.status != 200:
_LOGGER.error("Error calling LWA to get auth token.")
return None
response_json = await response.json()
_LOGGER.debug("LWA response body : %s", response_json)
access_token = response_json["access_token"]
refresh_token = response_json["refresh_token"]
expires_in = response_json["expires_in"]
expire_time = dt.utcnow() + timedelta(seconds=expires_in)
await self._async_update_preferences(access_token, refresh_token,
expire_time.isoformat())
return access_token
async def async_load_preferences(self):
"""Load preferences with stored tokens."""
self._prefs = await self._store.async_load()
if self._prefs is None:
self._prefs = {
STORAGE_ACCESS_TOKEN: None,
STORAGE_REFRESH_TOKEN: None,
STORAGE_EXPIRE_TIME: None
}
async def _async_update_preferences(self, access_token, refresh_token,
expire_time):
"""Update user preferences."""
if self._prefs is None:
await self.async_load_preferences()
if access_token is not None:
self._prefs[STORAGE_ACCESS_TOKEN] = access_token
if refresh_token is not None:
self._prefs[STORAGE_REFRESH_TOKEN] = refresh_token
if expire_time is not None:
self._prefs[STORAGE_EXPIRE_TIME] = expire_time
await self._store.async_save(self._prefs)

View File

@@ -10,6 +10,9 @@ CONF_DISPLAY_URL = 'display_url'
CONF_FILTER = 'filter'
CONF_ENTITY_CONFIG = 'entity_config'
CONF_ENDPOINT = 'endpoint'
CONF_CLIENT_ID = 'client_id'
CONF_CLIENT_SECRET = 'client_secret'
ATTR_UID = 'uid'
ATTR_UPDATE_DATE = 'updateDate'
@@ -21,3 +24,5 @@ ATTR_REDIRECTION_URL = 'redirectionURL'
SYN_RESOLUTION_MATCH = 'ER_SUCCESS_MATCH'
DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.0Z'
DEFAULT_TIMEOUT = 30

View File

@@ -5,15 +5,22 @@ https://developer.amazon.com/docs/smarthome/understand-the-smart-home-skill-api.
https://developer.amazon.com/docs/device-apis/message-guide.html
"""
import asyncio
from collections import OrderedDict
from datetime import datetime
import json
import logging
import math
from uuid import uuid4
import aiohttp
import async_timeout
from homeassistant.components import (
alert, automation, binary_sensor, climate, cover, fan, group, http,
input_boolean, light, lock, media_player, scene, script, sensor, switch)
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.event import async_track_state_change
from homeassistant.const import (
ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES,
ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, CLOUD_NEVER_EXPOSED_ENTITIES,
@@ -21,13 +28,15 @@ from homeassistant.const import (
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)
TEMP_CELSIUS, TEMP_FAHRENHEIT, MATCH_ALL)
import homeassistant.core as ha
import homeassistant.util.color as color_util
from homeassistant.util.decorator import Registry
from homeassistant.util.temperature import convert as convert_temperature
from .const import CONF_ENTITY_CONFIG, CONF_FILTER
from .const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_ENDPOINT, \
CONF_ENTITY_CONFIG, CONF_FILTER, DATE_FORMAT, DEFAULT_TIMEOUT
from .auth import Auth
_LOGGER = logging.getLogger(__name__)
@@ -37,6 +46,8 @@ API_EVENT = 'event'
API_CONTEXT = 'context'
API_HEADER = 'header'
API_PAYLOAD = 'payload'
API_SCOPE = 'scope'
API_CHANGE = 'change'
API_TEMP_UNITS = {
TEMP_FAHRENHEIT: 'FAHRENHEIT',
@@ -66,6 +77,8 @@ HANDLERS = Registry()
ENTITY_ADAPTERS = Registry()
EVENT_ALEXA_SMART_HOME = 'alexa_smart_home'
AUTH_KEY = "alexa.smart_home.auth"
class _DisplayCategory:
"""Possible display categories for Discovery response.
@@ -375,6 +388,8 @@ class _AlexaInterface:
'name': prop_name,
'namespace': self.name(),
'value': prop_value,
'timeOfSample': datetime.now().strftime(DATE_FORMAT),
'uncertaintyInMilliseconds': 0
}
@@ -390,6 +405,9 @@ class _AlexaPowerController(_AlexaInterface):
def properties_supported(self):
return [{'name': 'powerState'}]
def properties_proactively_reported(self):
return True
def properties_retrievable(self):
return True
@@ -417,6 +435,9 @@ class _AlexaLockController(_AlexaInterface):
def properties_retrievable(self):
return True
def properties_proactively_reported(self):
return True
def get_property(self, name):
if name != 'lockState':
raise _UnsupportedProperty(name)
@@ -454,6 +475,9 @@ class _AlexaBrightnessController(_AlexaInterface):
def properties_supported(self):
return [{'name': 'brightness'}]
def properties_proactively_reported(self):
return True
def properties_retrievable(self):
return True
@@ -585,6 +609,9 @@ class _AlexaTemperatureSensor(_AlexaInterface):
def properties_supported(self):
return [{'name': 'temperature'}]
def properties_proactively_reported(self):
return True
def properties_retrievable(self):
return True
@@ -625,6 +652,9 @@ class _AlexaContactSensor(_AlexaInterface):
def properties_supported(self):
return [{'name': 'detectionState'}]
def properties_proactively_reported(self):
return True
def properties_retrievable(self):
return True
@@ -648,6 +678,9 @@ class _AlexaMotionSensor(_AlexaInterface):
def properties_supported(self):
return [{'name': 'detectionState'}]
def properties_proactively_reported(self):
return True
def properties_retrievable(self):
return True
@@ -686,6 +719,9 @@ class _AlexaThermostatController(_AlexaInterface):
properties.append({'name': 'thermostatMode'})
return properties
def properties_proactively_reported(self):
return True
def properties_retrievable(self):
return True
@@ -948,8 +984,11 @@ class _Cause:
class Config:
"""Hold the configuration for Alexa."""
def __init__(self, should_expose, entity_config=None):
def __init__(self, endpoint, async_get_access_token, should_expose,
entity_config=None):
"""Initialize the configuration."""
self.endpoint = endpoint
self.async_get_access_token = async_get_access_token
self.should_expose = should_expose
self.entity_config = entity_config or {}
@@ -964,12 +1003,62 @@ def async_setup(hass, config):
Even if that's disabled, the functionality in this module may still be used
by the cloud component which will call async_handle_message directly.
"""
if config.get(CONF_CLIENT_ID) and config.get(CONF_CLIENT_SECRET):
hass.data[AUTH_KEY] = Auth(hass, config[CONF_CLIENT_ID],
config[CONF_CLIENT_SECRET])
async_get_access_token = \
hass.data[AUTH_KEY].async_get_access_token if AUTH_KEY in hass.data \
else None
smart_home_config = Config(
endpoint=config.get(CONF_ENDPOINT),
async_get_access_token=async_get_access_token,
should_expose=config[CONF_FILTER],
entity_config=config.get(CONF_ENTITY_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))
async def async_enable_proactive_mode(hass, smart_home_config):
"""Enable the proactive mode.
Proactive mode makes this component report state changes to Alexa.
"""
if smart_home_config.async_get_access_token is None:
# no function to call to get token
return
if await smart_home_config.async_get_access_token() is None:
# not ready yet
return
async def async_entity_state_listener(changed_entity, old_state,
new_state):
if not smart_home_config.should_expose(changed_entity):
_LOGGER.debug("Not exposing %s because filtered by config",
changed_entity)
return
if new_state.domain not in ENTITY_ADAPTERS:
return
alexa_changed_entity = \
ENTITY_ADAPTERS[new_state.domain](hass, smart_home_config,
new_state)
for interface in alexa_changed_entity.interfaces():
if interface.properties_proactively_reported():
await async_send_changereport_message(hass, smart_home_config,
alexa_changed_entity)
return
async_track_state_change(hass, MATCH_ALL, async_entity_state_listener)
class SmartHomeView(http.HomeAssistantView):
"""Expose Smart Home v3 payload interface via HTTP POST."""
@@ -1112,6 +1201,24 @@ class _AlexaResponse:
"""
self._response[API_EVENT][API_HEADER]['correlationToken'] = token
def set_endpoint_full(self, bearer_token, endpoint_id, cookie=None):
"""Set the endpoint dictionary.
This is used to send proactive messages to Alexa.
"""
self._response[API_EVENT][API_ENDPOINT] = {
API_SCOPE: {
'type': 'BearerToken',
'token': bearer_token
}
}
if endpoint_id is not None:
self._response[API_EVENT][API_ENDPOINT]['endpointId'] = endpoint_id
if cookie is not None:
self._response[API_EVENT][API_ENDPOINT]['cookie'] = cookie
def set_endpoint(self, endpoint):
"""Set the endpoint.
@@ -1222,6 +1329,62 @@ async def async_handle_message(
return response.serialize()
async def async_send_changereport_message(hass, config, alexa_entity):
"""Send a ChangeReport message for an Alexa entity."""
token = await config.async_get_access_token()
if not token:
_LOGGER.error("Invalid access token.")
return
headers = {
"Authorization": "Bearer {}".format(token),
"Content-Type": "application/json;charset=UTF-8"
}
endpoint = alexa_entity.entity_id()
# this sends all the properties of the Alexa Entity, whether they have
# changed or not. this should be improved, and properties that have not
# changed should be moved to the 'context' object
properties = list(alexa_entity.serialize_properties())
payload = {
API_CHANGE: {
'cause': {'type': _Cause.APP_INTERACTION},
'properties': properties
}
}
message = _AlexaResponse(name='ChangeReport', namespace='Alexa',
payload=payload)
message.set_endpoint_full(token, endpoint)
message_str = json.dumps(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,
allow_redirects=True)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Timeout calling LWA to get auth token.")
return None
response_text = await response.text()
_LOGGER.debug("Sent: %s", message_str)
_LOGGER.debug("Received (%s): %s", response.status, response_text)
if response.status != 202:
response_json = json.loads(response_text)
_LOGGER.error("Error when sending ChangeReport to Alexa: %s: %s",
response_json["payload"]["code"],
response_json["payload"]["description"])
@HANDLERS.register(('Alexa.Discovery', 'Discover'))
async def async_api_discovery(hass, config, directive, context):
"""Create a API formatted discovery response.
@@ -1258,8 +1421,9 @@ async def async_api_discovery(hass, config, directive, context):
i.serialize_discovery() for i in alexa_entity.interfaces()]
if not endpoint['capabilities']:
_LOGGER.debug("Not exposing %s because it has no capabilities",
entity.entity_id)
_LOGGER.debug(
"Not exposing %s because it has no capabilities",
entity.entity_id)
continue
discovery_endpoints.append(endpoint)
@@ -1270,6 +1434,25 @@ async def async_api_discovery(hass, config, directive, context):
)
@HANDLERS.register(('Alexa.Authorization', 'AcceptGrant'))
async def async_api_accept_grant(hass, config, directive, context):
"""Create a API formatted AcceptGrant response.
Async friendly.
"""
auth_code = directive.payload['grant']['code']
_LOGGER.debug("AcceptGrant code: %s", auth_code)
if AUTH_KEY in hass.data:
await hass.data[AUTH_KEY].async_do_auth(auth_code)
await async_enable_proactive_mode(hass, config)
return directive.response(
name='AcceptGrant.Response',
namespace='Alexa.Authorization',
payload={})
@HANDLERS.register(('Alexa.PowerController', 'TurnOn'))
async def async_api_turn_on(hass, config, directive, context):
"""Process a turn on request."""

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
from homeassistant.helpers.event import track_time_interval
from homeassistant.helpers.dispatcher import dispatcher_send
REQUIREMENTS = ['pyarlo==0.2.2']
REQUIREMENTS = ['pyarlo==0.2.3']
_LOGGER = logging.getLogger(__name__)

View File

@@ -14,7 +14,7 @@ from homeassistant.const import (
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.discovery import async_load_platform
REQUIREMENTS = ['aioasuswrt==1.1.15']
REQUIREMENTS = ['aioasuswrt==1.1.17']
_LOGGER = logging.getLogger(__name__)

View File

@@ -5,28 +5,28 @@
"no_available_service": "No hi ha serveis de notificaci\u00f3 disponibles."
},
"error": {
"invalid_code": "Codi inv\u00e0lid, si us plau torni a provar-ho."
"invalid_code": "Codi inv\u00e0lid, si us plau torna a provar-ho."
},
"step": {
"init": {
"description": "Seleccioneu un dels serveis de notificaci\u00f3:",
"title": "Configureu una contrasenya d'un sol \u00fas a trav\u00e9s del component de notificacions"
"description": "Selecciona un dels serveis de notificaci\u00f3:",
"title": "Configuraci\u00f3 d'una contrasenya d'un sol \u00fas a trav\u00e9s del component de notificacions"
},
"setup": {
"description": "S'ha enviat una contrasenya d'un sol \u00fas mitjan\u00e7ant **notify.{notify_service}**. Introdu\u00efu-la a continuaci\u00f3:",
"title": "Verifiqueu la configuraci\u00f3"
"description": "S'ha enviat una contrasenya d'un sol \u00fas mitjan\u00e7ant **notify.{notify_service}**. Introdueix-la a continuaci\u00f3:",
"title": "Verificaci\u00f3 de la configuraci\u00f3"
}
},
"title": "Contrasenya d'un sol \u00fas del servei de notificacions"
},
"totp": {
"error": {
"invalid_code": "Codi inv\u00e0lid, si us plau torni a provar-ho. Si obteniu aquest error repetidament, assegureu-vos que la data i hora de Home Assistant sigui correcta i precisa."
"invalid_code": "Codi inv\u00e0lid, si us plau torna a provar-ho. Si obtens aquest error repetidament, assegura't que la data i hora de Home Assistant siguin correctes i acurades."
},
"step": {
"init": {
"description": "Per activar la verificaci\u00f3 en dos passos mitjan\u00e7ant contrasenyes d'un sol \u00fas basades en temps, escanegeu el codi QR amb la vostre aplicaci\u00f3 de verificaci\u00f3. Si no en teniu cap, us recomanem [Google Authenticator](https://support.google.com/accounts/answer/1066447) o b\u00e9 [Authy](https://authy.com/). \n\n {qr_code} \n \nDespr\u00e9s d'escanejar el codi QR, introdu\u00efu el codi de sis d\u00edgits proporcionat per l'aplicaci\u00f3. Si teniu problemes per escanejar el codi QR, feu una configuraci\u00f3 manual amb el codi **`{code}`**.",
"title": "Configureu la verificaci\u00f3 en dos passos utilitzant TOTP"
"description": "Per activar la verificaci\u00f3 en dos passos mitjan\u00e7ant contrasenyes d'un sol \u00fas basades en temps, escaneja el codi QR amb la teva aplicaci\u00f3 de verificaci\u00f3. Si no en tens cap, et recomanem [Google Authenticator](https://support.google.com/accounts/answer/1066447) o b\u00e9 [Authy](https://authy.com/). \n\n {qr_code} \n \nDespr\u00e9s d'escanejar el codi QR, introdueix el codi de sis d\u00edgits proporcionat per l'aplicaci\u00f3. Si tens problemes per escanejar el codi QR, fes una configuraci\u00f3 manual amb el codi **`{code}`**.",
"title": "Configura la verificaci\u00f3 en dos passos utilitzant TOTP"
}
},
"title": "TOTP"

View File

@@ -94,11 +94,11 @@ PLATFORM_SCHEMA = vol.Schema({
})
SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
})
TRIGGER_SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Optional(ATTR_VARIABLES, default={}): dict,
})
@@ -375,7 +375,15 @@ def _async_get_action(hass, config, name):
async def action(entity_id, variables, context):
"""Execute an action."""
_LOGGER.info('Executing %s', name)
await script_obj.async_run(variables, context)
hass.components.logbook.async_log_entry(
name, 'has been triggered', DOMAIN, entity_id)
try:
await script_obj.async_run(variables, context)
except Exception as err: # pylint: disable=broad-except
script_obj.async_log_exception(
_LOGGER,
'Error while executing automation {}'.format(entity_id), err)
return action

View File

@@ -9,7 +9,7 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.alarmdecoder import (
ZONE_SCHEMA, CONF_ZONES, CONF_ZONE_NAME, CONF_ZONE_TYPE,
CONF_ZONE_RFID, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE,
CONF_ZONE_RFID, CONF_ZONE_LOOP, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE,
SIGNAL_RFX_MESSAGE, SIGNAL_REL_MESSAGE, CONF_RELAY_ADDR,
CONF_RELAY_CHAN)
@@ -37,10 +37,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
zone_rfid = device_config_data.get(CONF_ZONE_RFID)
zone_loop = device_config_data.get(CONF_ZONE_LOOP)
relay_addr = device_config_data.get(CONF_RELAY_ADDR)
relay_chan = device_config_data.get(CONF_RELAY_CHAN)
device = AlarmDecoderBinarySensor(
zone_num, zone_name, zone_type, zone_rfid, relay_addr, relay_chan)
zone_num, zone_name, zone_type, zone_rfid, zone_loop, relay_addr,
relay_chan)
devices.append(device)
add_entities(devices)
@@ -51,7 +53,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Representation of an AlarmDecoder binary sensor."""
def __init__(self, zone_number, zone_name, zone_type, zone_rfid,
def __init__(self, zone_number, zone_name, zone_type, zone_rfid, zone_loop,
relay_addr, relay_chan):
"""Initialize the binary_sensor."""
self._zone_number = zone_number
@@ -59,6 +61,7 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
self._state = None
self._name = zone_name
self._rfid = zone_rfid
self._loop = zone_loop
self._rfstate = None
self._relay_addr = relay_addr
self._relay_chan = relay_chan
@@ -92,14 +95,14 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Return the state attributes."""
attr = {}
if self._rfid and self._rfstate is not None:
attr[ATTR_RF_BIT0] = True if self._rfstate & 0x01 else False
attr[ATTR_RF_LOW_BAT] = True if self._rfstate & 0x02 else False
attr[ATTR_RF_SUPERVISED] = True if self._rfstate & 0x04 else False
attr[ATTR_RF_BIT3] = True if self._rfstate & 0x08 else False
attr[ATTR_RF_LOOP3] = True if self._rfstate & 0x10 else False
attr[ATTR_RF_LOOP2] = True if self._rfstate & 0x20 else False
attr[ATTR_RF_LOOP4] = True if self._rfstate & 0x40 else False
attr[ATTR_RF_LOOP1] = True if self._rfstate & 0x80 else False
attr[ATTR_RF_BIT0] = bool(self._rfstate & 0x01)
attr[ATTR_RF_LOW_BAT] = bool(self._rfstate & 0x02)
attr[ATTR_RF_SUPERVISED] = bool(self._rfstate & 0x04)
attr[ATTR_RF_BIT3] = bool(self._rfstate & 0x08)
attr[ATTR_RF_LOOP3] = bool(self._rfstate & 0x10)
attr[ATTR_RF_LOOP2] = bool(self._rfstate & 0x20)
attr[ATTR_RF_LOOP4] = bool(self._rfstate & 0x40)
attr[ATTR_RF_LOOP1] = bool(self._rfstate & 0x80)
return attr
@property
@@ -128,6 +131,8 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Update RF state."""
if self._rfid and message and message.serial_number == self._rfid:
self._rfstate = message.value
if self._loop:
self._state = 1 if message.loop[self._loop - 1] else 0
self.schedule_update_ha_state()
def _rel_message_callback(self, message):

View File

@@ -0,0 +1,63 @@
"""Support for ESPHome binary sensors."""
import logging
from typing import TYPE_CHECKING, Optional
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.esphome import EsphomeEntity, \
platform_async_setup_entry
if TYPE_CHECKING:
# pylint: disable=unused-import
from aioesphomeapi import BinarySensorInfo, BinarySensorState # noqa
DEPENDENCIES = ['esphome']
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up ESPHome binary sensors based on a config entry."""
# pylint: disable=redefined-outer-name
from aioesphomeapi import BinarySensorInfo, BinarySensorState # noqa
await platform_async_setup_entry(
hass, entry, async_add_entities,
component_key='binary_sensor',
info_type=BinarySensorInfo, entity_type=EsphomeBinarySensor,
state_type=BinarySensorState
)
class EsphomeBinarySensor(EsphomeEntity, BinarySensorDevice):
"""A binary sensor implementation for ESPHome."""
@property
def _static_info(self) -> 'BinarySensorInfo':
return super()._static_info
@property
def _state(self) -> Optional['BinarySensorState']:
return super()._state
@property
def is_on(self):
"""Return true if the binary sensor is on."""
if self._static_info.is_status_binary_sensor:
# Status binary sensors indicated connected state.
# So in their case what's usually _availability_ is now state
return self._entry_data.available
if self._state is None:
return None
return self._state.state
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
return self._static_info.device_class
@property
def available(self):
"""Return True if entity is available."""
if self._static_info.is_status_binary_sensor:
return True
return super().available

View File

@@ -10,6 +10,7 @@ from homeassistant.components.binary_sensor import (
BinarySensorDevice, ENTITY_ID_FORMAT)
from homeassistant.components.fibaro import (
FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice)
from homeassistant.const import (CONF_DEVICE_CLASS, CONF_ICON)
DEPENDENCIES = ['fibaro']
@@ -45,6 +46,7 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorDevice):
super().__init__(fibaro_device, controller)
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
stype = None
devconf = fibaro_device.device_config
if fibaro_device.type in SENSOR_TYPES:
stype = fibaro_device.type
elif fibaro_device.baseType in SENSOR_TYPES:
@@ -55,6 +57,10 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorDevice):
else:
self._device_class = None
self._icon = None
# device_config overrides:
self._device_class = devconf.get(CONF_DEVICE_CLASS,
self._device_class)
self._icon = devconf.get(CONF_ICON, self._icon)
@property
def icon(self):

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
CONF_SSL, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START,
ATTR_LAST_TRIP_TIME, CONF_CUSTOMIZE)
REQUIREMENTS = ['pyhik==0.1.8']
REQUIREMENTS = ['pyhik==0.1.9']
_LOGGER = logging.getLogger(__name__)
CONF_IGNORED = 'ignored'

View File

@@ -28,14 +28,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the HomematicIP Cloud binary sensor from a config entry."""
from homematicip.aio.device import (
AsyncShutterContact, AsyncMotionDetectorIndoor, AsyncSmokeDetector,
AsyncWaterSensor, AsyncRotaryHandleSensor)
AsyncWaterSensor, AsyncRotaryHandleSensor,
AsyncMotionDetectorPushButton)
home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home
devices = []
for device in home.devices:
if isinstance(device, (AsyncShutterContact, AsyncRotaryHandleSensor)):
devices.append(HomematicipShutterContact(home, device))
elif isinstance(device, AsyncMotionDetectorIndoor):
elif isinstance(device, (AsyncMotionDetectorIndoor,
AsyncMotionDetectorPushButton)):
devices.append(HomematicipMotionDetector(home, device))
elif isinstance(device, AsyncSmokeDetector):
devices.append(HomematicipSmokeDetector(home, device))

View File

@@ -14,6 +14,7 @@ DEPENDENCIES = ['insteon']
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {'openClosedSensor': 'opening',
'ioLincSensor': 'opening',
'motionSensor': 'motion',
'doorSensor': 'door',
'wetLeakSensor': 'moisture',
@@ -58,7 +59,7 @@ class InsteonBinarySensor(InsteonEntity, BinarySensorDevice):
on_val = bool(self._insteon_device_state.value)
if self._insteon_device_state.name in ['lightSensor',
'openClosedSensor']:
'ioLincSensor']:
return not on_val
return on_val

View File

@@ -52,7 +52,7 @@ def setup_platform(hass, config: ConfigType,
node.nid, node.parent_nid)
else:
device_type = _detect_device_type(node)
subnode_id = int(node.nid[-1])
subnode_id = int(node.nid[-1], 16)
if device_type in ('opening', 'moisture'):
# These sensors use an optional "negative" subnode 2 to snag
# all state changes

View File

@@ -16,10 +16,10 @@ from homeassistant.const import (
CONF_FORCE_UPDATE, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_PAYLOAD_ON,
CONF_PAYLOAD_OFF, CONF_DEVICE_CLASS, CONF_DEVICE)
from homeassistant.components.mqtt import (
ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC, CONF_AVAILABILITY_TOPIC,
ATTR_DISCOVERY_HASH, CONF_AVAILABILITY_TOPIC, CONF_STATE_TOPIC,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_QOS,
MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo,
subscription)
MqttAttributes, MqttAvailability, MqttDiscoveryUpdate,
MqttEntityDeviceInfo, subscription)
from homeassistant.components.mqtt.discovery import MQTT_DISCOVERY_NEW
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -49,7 +49,8 @@ PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
# This is an exception because MQTT is a message transport, not a protocol
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend(
mqtt.MQTT_JSON_ATTRS_SCHEMA.schema)
async def async_setup_platform(hass: HomeAssistantType, config: ConfigType,
@@ -76,7 +77,7 @@ async def _async_setup_entity(config, async_add_entities, discovery_hash=None):
async_add_entities([MqttBinarySensor(config, discovery_hash)])
class MqttBinarySensor(MqttAvailability, MqttDiscoveryUpdate,
class MqttBinarySensor(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate,
MqttEntityDeviceInfo, BinarySensorDevice):
"""Representation a binary sensor that is updated by MQTT."""
@@ -94,6 +95,7 @@ class MqttBinarySensor(MqttAvailability, MqttDiscoveryUpdate,
qos = config.get(CONF_QOS)
device_config = config.get(CONF_DEVICE)
MqttAttributes.__init__(self, config)
MqttAvailability.__init__(self, availability_topic, qos,
payload_available, payload_not_available)
MqttDiscoveryUpdate.__init__(self, discovery_hash,
@@ -109,6 +111,7 @@ class MqttBinarySensor(MqttAvailability, MqttDiscoveryUpdate,
"""Handle updated discovery message."""
config = PLATFORM_SCHEMA(discovery_payload)
self._config = config
await self.attributes_discovery_update(config)
await self.availability_discovery_update(config)
await self._subscribe_topics()
self.async_schedule_update_ha_state()
@@ -132,7 +135,7 @@ class MqttBinarySensor(MqttAvailability, MqttDiscoveryUpdate,
value_template = self._config.get(CONF_VALUE_TEMPLATE)
if value_template is not None:
payload = value_template.async_render_with_possible_json_value(
payload)
payload, variables={'entity_id': self.entity_id})
if payload == self._config.get(CONF_PAYLOAD_ON):
self._state = True
elif payload == self._config.get(CONF_PAYLOAD_OFF):
@@ -163,7 +166,9 @@ class MqttBinarySensor(MqttAvailability, MqttDiscoveryUpdate,
async def async_will_remove_from_hass(self):
"""Unsubscribe when removed."""
await subscription.async_unsubscribe_topics(self.hass, self._sub_state)
self._sub_state = await subscription.async_unsubscribe_topics(
self.hass, self._sub_state)
await MqttAttributes.async_will_remove_from_hass(self)
await MqttAvailability.async_will_remove_from_hass(self)
@property

View File

@@ -61,8 +61,7 @@ class MyStromView(HomeAssistantView):
'{}_{}'.format(button_id, button_action))
self.add_entities([self.buttons[entity_id]])
else:
new_state = True if self.buttons[entity_id].state == 'off' \
else False
new_state = self.buttons[entity_id].state == 'off'
self.buttons[entity_id].async_on_update(new_state)

View File

@@ -0,0 +1,81 @@
"""
Support for Ness D8X/D16X zone states - represented as binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ness_alarm/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.ness_alarm import (
CONF_ZONES, CONF_ZONE_TYPE, CONF_ZONE_NAME, CONF_ZONE_ID,
SIGNAL_ZONE_CHANGED, ZoneChangedData)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
DEPENDENCIES = ['ness_alarm']
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up the Ness Alarm binary sensor devices."""
if not discovery_info:
return
configured_zones = discovery_info[CONF_ZONES]
devices = []
for zone_config in configured_zones:
zone_type = zone_config[CONF_ZONE_TYPE]
zone_name = zone_config[CONF_ZONE_NAME]
zone_id = zone_config[CONF_ZONE_ID]
device = NessZoneBinarySensor(zone_id=zone_id, name=zone_name,
zone_type=zone_type)
devices.append(device)
async_add_entities(devices)
class NessZoneBinarySensor(BinarySensorDevice):
"""Representation of an Ness alarm zone as a binary sensor."""
def __init__(self, zone_id, name, zone_type):
"""Initialize the binary_sensor."""
self._zone_id = zone_id
self._name = name
self._type = zone_type
self._state = 0
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_CHANGED, self._handle_zone_change)
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def is_on(self):
"""Return true if sensor is on."""
return self._state == 1
@property
def device_class(self):
"""Return the class of this sensor, from DEVICE_CLASSES."""
return self._type
@callback
def _handle_zone_change(self, data: ZoneChangedData):
"""Handle zone state update."""
if self._zone_id == data.zone_id:
self._state = data.state
self.async_schedule_update_ha_state()

View File

@@ -7,8 +7,7 @@ https://home-assistant.io/components/binary_sensor.point/
import logging
from homeassistant.components.binary_sensor import (
DOMAIN as PARENT_DOMAIN, BinarySensorDevice)
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components.point import MinutPointEntity
from homeassistant.components.point.const import (
DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW, SIGNAL_WEBHOOK)
@@ -49,7 +48,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
for device_class in EVENTS), True)
async_dispatcher_connect(
hass, POINT_DISCOVERY_NEW.format(PARENT_DOMAIN, POINT_DOMAIN),
hass, POINT_DISCOVERY_NEW.format(DOMAIN, POINT_DOMAIN),
async_discover_sensor)

View File

@@ -8,9 +8,11 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.satel_integra import (CONF_ZONES,
CONF_OUTPUTS,
CONF_ZONE_NAME,
CONF_ZONE_TYPE,
SIGNAL_ZONES_UPDATED)
SIGNAL_ZONES_UPDATED,
SIGNAL_OUTPUTS_UPDATED)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -32,7 +34,17 @@ async def async_setup_platform(hass, config, async_add_entities,
for zone_num, device_config_data in configured_zones.items():
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
device = SatelIntegraBinarySensor(zone_num, zone_name, zone_type)
device = SatelIntegraBinarySensor(zone_num, zone_name, zone_type,
SIGNAL_ZONES_UPDATED)
devices.append(device)
configured_outputs = discovery_info[CONF_OUTPUTS]
for zone_num, device_config_data in configured_outputs.items():
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
device = SatelIntegraBinarySensor(zone_num, zone_name, zone_type,
SIGNAL_OUTPUTS_UPDATED)
devices.append(device)
async_add_entities(devices)
@@ -41,17 +53,18 @@ async def async_setup_platform(hass, config, async_add_entities,
class SatelIntegraBinarySensor(BinarySensorDevice):
"""Representation of an Satel Integra binary sensor."""
def __init__(self, zone_number, zone_name, zone_type):
def __init__(self, device_number, device_name, zone_type, react_to_signal):
"""Initialize the binary_sensor."""
self._zone_number = zone_number
self._name = zone_name
self._device_number = device_number
self._name = device_name
self._zone_type = zone_type
self._state = 0
self._react_to_signal = react_to_signal
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_ZONES_UPDATED, self._zones_updated)
self.hass, self._react_to_signal, self._devices_updated)
@property
def name(self):
@@ -80,9 +93,9 @@ class SatelIntegraBinarySensor(BinarySensorDevice):
return self._zone_type
@callback
def _zones_updated(self, zones):
def _devices_updated(self, zones):
"""Update the zone's state, if needed."""
if self._zone_number in zones \
and self._state != zones[self._zone_number]:
self._state = zones[self._zone_number]
if self._device_number in zones \
and self._state != zones[self._device_number]:
self._state = zones[self._device_number]
self.async_schedule_update_ha_state()

View File

@@ -9,22 +9,35 @@ https://home-assistant.io/components/binary_sensor.tellduslive/
"""
import logging
from homeassistant.components import tellduslive
from homeassistant.components import binary_sensor, tellduslive
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.tellduslive.entry import TelldusLiveEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Tellstick sensors."""
if discovery_info is None:
return
client = hass.data[tellduslive.DOMAIN]
add_entities(
TelldusLiveSensor(client, binary_sensor)
for binary_sensor in discovery_info
)
"""Old way of setting up TelldusLive.
Can only be called when a user accidentally mentions the platform in their
config. But even in that case it would have been ignored.
"""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up tellduslive sensors dynamically."""
async def async_discover_binary_sensor(device_id):
"""Discover and add a discovered sensor."""
client = hass.data[tellduslive.DOMAIN]
async_add_entities([TelldusLiveSensor(client, device_id)])
async_dispatcher_connect(
hass,
tellduslive.TELLDUS_DISCOVERY_NEW.format(binary_sensor.DOMAIN,
tellduslive.DOMAIN),
async_discover_binary_sensor)
class TelldusLiveSensor(TelldusLiveEntity, BinarySensorDevice):

View File

@@ -4,7 +4,10 @@ Support for WeMo sensors.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.wemo/
"""
import asyncio
import logging
import async_timeout
import requests
from homeassistant.components.binary_sensor import BinarySensorDevice
@@ -15,7 +18,7 @@ DEPENDENCIES = ['wemo']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities_callback, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Register discovered WeMo binary sensors."""
from pywemo import discovery
@@ -31,7 +34,7 @@ def setup_platform(hass, config, add_entities_callback, discovery_info=None):
raise PlatformNotReady
if device:
add_entities_callback([WemoBinarySensor(hass, device)])
add_entities([WemoBinarySensor(hass, device)])
class WemoBinarySensor(BinarySensorDevice):
@@ -41,48 +44,90 @@ class WemoBinarySensor(BinarySensorDevice):
"""Initialize the WeMo sensor."""
self.wemo = device
self._state = None
self._available = True
self._update_lock = None
self._model_name = self.wemo.model_name
self._name = self.wemo.name
self._serialnumber = self.wemo.serialnumber
wemo = hass.components.wemo
wemo.SUBSCRIPTION_REGISTRY.register(self.wemo)
wemo.SUBSCRIPTION_REGISTRY.on(self.wemo, None, self._update_callback)
def _update_callback(self, _device, _type, _params):
"""Handle state changes."""
_LOGGER.info("Subscription update for %s", _device)
def _subscription_callback(self, _device, _type, _params):
"""Update the state by the Wemo sensor."""
_LOGGER.debug("Subscription update for %s", self.name)
updated = self.wemo.subscription_update(_type, _params)
self._update(force_update=(not updated))
self.hass.add_job(
self._async_locked_subscription_callback(not updated))
if not hasattr(self, 'hass'):
async def _async_locked_subscription_callback(self, force_update):
"""Handle an update from a subscription."""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return
self.schedule_update_ha_state()
@property
def should_poll(self):
"""No polling needed with subscriptions."""
return False
await self._async_locked_update(force_update)
self.async_schedule_update_ha_state()
async def async_added_to_hass(self):
"""Wemo sensor added to HASS."""
# Define inside async context so we know our event loop
self._update_lock = asyncio.Lock()
registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY
await self.hass.async_add_executor_job(registry.register, self.wemo)
registry.on(self.wemo, None, self._subscription_callback)
async def async_update(self):
"""Update WeMo state.
Wemo has an aggressive retry logic that sometimes can take over a
minute to return. If we don't get a state after 5 seconds, assume the
Wemo sensor is unreachable. If update goes through, it will be made
available again.
"""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return
try:
with async_timeout.timeout(5):
await asyncio.shield(self._async_locked_update(True))
except asyncio.TimeoutError:
_LOGGER.warning('Lost connection to %s', self.name)
self._available = False
async def _async_locked_update(self, force_update):
"""Try updating within an async lock."""
async with self._update_lock:
await self.hass.async_add_executor_job(self._update, force_update)
def _update(self, force_update=True):
"""Update the sensor state."""
try:
self._state = self.wemo.get_state(force_update)
if not self._available:
_LOGGER.info('Reconnected to %s', self.name)
self._available = True
except AttributeError as err:
_LOGGER.warning("Could not update status for %s (%s)",
self.name, err)
self._available = False
@property
def unique_id(self):
"""Return the id of this WeMo device."""
return self.wemo.serialnumber
"""Return the id of this WeMo sensor."""
return self._serialnumber
@property
def name(self):
"""Return the name of the service if any."""
return self.wemo.name
return self._name
@property
def is_on(self):
"""Return true if sensor is on."""
return self._state
def update(self):
"""Update WeMo state."""
self._update(force_update=True)
def _update(self, force_update=True):
try:
self._state = self.wemo.get_state(force_update)
except AttributeError as err:
_LOGGER.warning(
"Could not update status for %s (%s)", self.name, err)
@property
def available(self):
"""Return true if sensor is available."""
return self._available

View File

@@ -409,10 +409,14 @@ class XiaomiButton(XiaomiBinarySensor):
click_type = 'double'
elif value == 'both_click':
click_type = 'both'
elif value == 'double_both_click':
click_type = 'double_both'
elif value == 'shake':
click_type = 'shake'
elif value in ['long_click', 'long_both_click']:
return False
elif value == 'long_click':
click_type = 'long'
elif value == 'long_both_click':
click_type = 'long_both'
else:
_LOGGER.warning("Unsupported click_type detected: %s", value)
return False
@@ -465,4 +469,12 @@ class XiaomiCube(XiaomiBinarySensor):
})
self._last_action = 'rotate'
if 'rotate_degree' in data:
self._hass.bus.fire('xiaomi_aqara.cube_action', {
'entity_id': self.entity_id,
'action_type': 'rotate',
'action_value': float(data['rotate_degree'].replace(",", "."))
})
self._last_action = 'rotate'
return True

View File

@@ -9,9 +9,14 @@ import logging
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components.zha import helpers
from homeassistant.components.zha.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW)
DATA_ZHA, DATA_ZHA_DISPATCHERS, REPORT_CONFIG_IMMEDIATE, ZHA_DISCOVERY_NEW)
from homeassistant.components.zha.entities import ZhaEntity
from homeassistant.const import STATE_ON
from homeassistant.components.zha.entities.listeners import (
OnOffListener, LevelListener
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.restore_state import RestoreEntity
_LOGGER = logging.getLogger(__name__)
@@ -26,6 +31,7 @@ CLASS_MAPPING = {
0x002b: 'gas',
0x002d: 'vibration',
}
DEVICE_CLASS_OCCUPANCY = 'occupancy'
async def async_setup_platform(hass, config, async_add_entities,
@@ -54,14 +60,19 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async def _async_setup_entities(hass, config_entry, async_add_entities,
discovery_infos):
"""Set up the ZHA binary sensors."""
from zigpy.zcl.clusters.general import OnOff
from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.clusters.security import IasZone
entities = []
for discovery_info in discovery_infos:
from zigpy.zcl.clusters.general import OnOff
from zigpy.zcl.clusters.security import IasZone
if IasZone.cluster_id in discovery_info['in_clusters']:
entities.append(await _async_setup_iaszone(discovery_info))
elif OccupancySensing.cluster_id in discovery_info['in_clusters']:
entities.append(
BinarySensor(DEVICE_CLASS_OCCUPANCY, **discovery_info))
elif OnOff.cluster_id in discovery_info['out_clusters']:
entities.append(await _async_setup_remote(discovery_info))
entities.append(Remote(**discovery_info))
async_add_entities(entities, update_before_add=True)
@@ -70,10 +81,6 @@ async def _async_setup_iaszone(discovery_info):
device_class = None
from zigpy.zcl.clusters.security import IasZone
cluster = discovery_info['in_clusters'][IasZone.cluster_id]
if discovery_info['new_join']:
await cluster.bind()
ieee = cluster.endpoint.device.application.ieee
await cluster.write_attributes({'cie_addr': ieee})
try:
zone_type = await cluster['zone_type']
@@ -82,33 +89,11 @@ async def _async_setup_iaszone(discovery_info):
# If we fail to read from the device, use a non-specific class
pass
return BinarySensor(device_class, **discovery_info)
return IasZoneSensor(device_class, **discovery_info)
async def _async_setup_remote(discovery_info):
remote = Remote(**discovery_info)
if discovery_info['new_join']:
from zigpy.zcl.clusters.general import OnOff, LevelControl
out_clusters = discovery_info['out_clusters']
if OnOff.cluster_id in out_clusters:
cluster = out_clusters[OnOff.cluster_id]
await helpers.configure_reporting(
remote.entity_id, cluster, 0, min_report=0, max_report=600,
reportable_change=1
)
if LevelControl.cluster_id in out_clusters:
cluster = out_clusters[LevelControl.cluster_id]
await helpers.configure_reporting(
remote.entity_id, cluster, 0, min_report=1, max_report=600,
reportable_change=1
)
return remote
class BinarySensor(ZhaEntity, BinarySensorDevice):
"""The ZHA Binary Sensor."""
class IasZoneSensor(RestoreEntity, ZhaEntity, BinarySensorDevice):
"""The IasZoneSensor Binary Sensor."""
_domain = DOMAIN
@@ -119,11 +104,6 @@ class BinarySensor(ZhaEntity, BinarySensorDevice):
from zigpy.zcl.clusters.security import IasZone
self._ias_zone_cluster = self._in_clusters[IasZone.cluster_id]
@property
def should_poll(self) -> bool:
"""Let zha handle polling."""
return False
@property
def is_on(self) -> bool:
"""Return True if entity is on."""
@@ -147,6 +127,26 @@ class BinarySensor(ZhaEntity, BinarySensorDevice):
res = self._ias_zone_cluster.enroll_response(0, 0)
self.hass.async_add_job(res)
async def async_added_to_hass(self):
"""Run when about to be added to hass."""
await super().async_added_to_hass()
old_state = await self.async_get_last_state()
if self._state is not None or old_state is None:
return
_LOGGER.debug("%s restoring old state: %s", self.entity_id, old_state)
if old_state.state == STATE_ON:
self._state = 3
else:
self._state = 0
async def async_configure(self):
"""Configure IAS device."""
await self._ias_zone_cluster.bind()
ieee = self._ias_zone_cluster.endpoint.device.application.ieee
await self._ias_zone_cluster.write_attributes({'cie_addr': ieee})
_LOGGER.debug("%s: finished configuration", self.entity_id)
async def async_update(self):
"""Retrieve latest state."""
from zigpy.types.basic import uint16_t
@@ -160,81 +160,33 @@ class BinarySensor(ZhaEntity, BinarySensorDevice):
self._state = result.get('zone_status', self._state) & 3
class Remote(ZhaEntity, BinarySensorDevice):
class Remote(RestoreEntity, ZhaEntity, BinarySensorDevice):
"""ZHA switch/remote controller/button."""
_domain = DOMAIN
class OnOffListener:
"""Listener for the OnOff Zigbee cluster."""
def __init__(self, entity):
"""Initialize OnOffListener."""
self._entity = entity
def cluster_command(self, tsn, command_id, args):
"""Handle commands received to this cluster."""
if command_id in (0x0000, 0x0040):
self._entity.set_state(False)
elif command_id in (0x0001, 0x0041, 0x0042):
self._entity.set_state(True)
elif command_id == 0x0002:
self._entity.set_state(not self._entity.is_on)
def attribute_updated(self, attrid, value):
"""Handle attribute updates on this cluster."""
if attrid == 0:
self._entity.set_state(value)
def zdo_command(self, *args, **kwargs):
"""Handle ZDO commands on this cluster."""
pass
class LevelListener:
"""Listener for the LevelControl Zigbee cluster."""
def __init__(self, entity):
"""Initialize LevelListener."""
self._entity = entity
def cluster_command(self, tsn, command_id, args):
"""Handle commands received to this cluster."""
if command_id in (0x0000, 0x0004): # move_to_level, -with_on_off
self._entity.set_level(args[0])
elif command_id in (0x0001, 0x0005): # move, -with_on_off
# We should dim slowly -- for now, just step once
rate = args[1]
if args[0] == 0xff:
rate = 10 # Should read default move rate
self._entity.move_level(-rate if args[0] else rate)
elif command_id in (0x0002, 0x0006): # step, -with_on_off
# Step (technically may change on/off)
self._entity.move_level(-args[1] if args[0] else args[1])
def attribute_update(self, attrid, value):
"""Handle attribute updates on this cluster."""
if attrid == 0:
self._entity.set_level(value)
def zdo_command(self, *args, **kwargs):
"""Handle ZDO commands on this cluster."""
pass
def __init__(self, **kwargs):
"""Initialize Switch."""
super().__init__(**kwargs)
self._state = False
self._level = 0
from zigpy.zcl.clusters import general
self._out_listeners = {
general.OnOff.cluster_id: self.OnOffListener(self),
general.LevelControl.cluster_id: self.LevelListener(self),
general.OnOff.cluster_id: OnOffListener(
self,
self._out_clusters[general.OnOff.cluster_id]
)
}
@property
def should_poll(self) -> bool:
"""Let zha handle polling."""
return False
out_clusters = kwargs.get('out_clusters')
self._zcl_reporting = {}
if general.LevelControl.cluster_id in out_clusters:
self._out_listeners.update({
general.LevelControl.cluster_id: LevelListener(
self,
out_clusters[general.LevelControl.cluster_id]
)
})
@property
def is_on(self) -> bool:
@@ -249,6 +201,11 @@ class Remote(ZhaEntity, BinarySensorDevice):
})
return self._device_state_attributes
@property
def zcl_reporting_config(self):
"""Return ZCL attribute reporting configuration."""
return self._zcl_reporting
def move_level(self, change):
"""Increment the level, setting state if appropriate."""
if not self._state and change > 0:
@@ -270,6 +227,31 @@ class Remote(ZhaEntity, BinarySensorDevice):
self._level = 255
self.async_schedule_update_ha_state()
async def async_configure(self):
"""Bind clusters."""
from zigpy.zcl.clusters import general
await helpers.bind_cluster(
self.entity_id,
self._out_clusters[general.OnOff.cluster_id]
)
if general.LevelControl.cluster_id in self._out_clusters:
await helpers.bind_cluster(
self.entity_id,
self._out_clusters[general.LevelControl.cluster_id]
)
async def async_added_to_hass(self):
"""Run when about to be added to hass."""
await super().async_added_to_hass()
old_state = await self.async_get_last_state()
if self._state is not None or old_state is None:
return
_LOGGER.debug("%s restoring old state: %s", self.entity_id, old_state)
if 'level' in old_state.attributes:
self._level = old_state.attributes['level']
self._state = old_state.state == STATE_ON
async def async_update(self):
"""Retrieve latest state."""
from zigpy.zcl.clusters.general import OnOff
@@ -280,3 +262,56 @@ class Remote(ZhaEntity, BinarySensorDevice):
only_cache=(not self._initialized)
)
self._state = result.get('on_off', self._state)
class BinarySensor(RestoreEntity, ZhaEntity, BinarySensorDevice):
"""ZHA switch."""
_domain = DOMAIN
_device_class = None
value_attribute = 0
def __init__(self, device_class, **kwargs):
"""Initialize the ZHA binary sensor."""
super().__init__(**kwargs)
self._device_class = device_class
self._cluster = list(kwargs['in_clusters'].values())[0]
def attribute_updated(self, attribute, value):
"""Handle attribute update from device."""
_LOGGER.debug("Attribute updated: %s %s %s", self, attribute, value)
if attribute == self.value_attribute:
self._state = bool(value)
self.async_schedule_update_ha_state()
async def async_added_to_hass(self):
"""Run when about to be added to hass."""
await super().async_added_to_hass()
old_state = await self.async_get_last_state()
if self._state is not None or old_state is None:
return
_LOGGER.debug("%s restoring old state: %s", self.entity_id, old_state)
self._state = old_state.state == STATE_ON
@property
def cluster(self):
"""Zigbee cluster for this entity."""
return self._cluster
@property
def zcl_reporting_config(self):
"""ZHA reporting configuration."""
return {self.cluster: {self.value_attribute: REPORT_CONFIG_IMMEDIATE}}
@property
def is_on(self) -> bool:
"""Return if the switch is on based on the statemachine."""
if self._state is None:
return False
return self._state
@property
def device_class(self) -> str:
"""Return device class from component DEVICE_CLASSES."""
return self._device_class

View File

@@ -61,7 +61,7 @@ FALLBACK_STREAM_INTERVAL = 1 # seconds
MIN_STREAM_INTERVAL = 0.5 # seconds
CAMERA_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
})
CAMERA_SERVICE_SNAPSHOT = CAMERA_SERVICE_SCHEMA.extend({

View File

@@ -7,7 +7,7 @@ https://home-assistant.io/components/camera.axis/
import logging
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera, filter_urllib3_logging)
from homeassistant.const import (
CONF_AUTHENTICATION, CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT,
CONF_USERNAME, HTTP_DIGEST_AUTHENTICATION)
@@ -29,6 +29,8 @@ def _get_image_url(host, port, mode):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Axis camera."""
filter_urllib3_logging()
camera_config = {
CONF_NAME: discovery_info[CONF_NAME],
CONF_USERNAME: discovery_info[CONF_USERNAME],

View File

@@ -16,7 +16,7 @@ import voluptuous as vol
from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_AUTHENTICATION,
HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION, CONF_VERIFY_SSL)
from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera)
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_web)
@@ -29,6 +29,7 @@ CONF_STILL_IMAGE_URL = 'still_image_url'
CONTENT_TYPE_HEADER = 'Content-Type'
DEFAULT_NAME = 'Mjpeg Camera'
DEFAULT_VERIFY_SSL = True
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MJPEG_URL): cv.url,
@@ -38,13 +39,22 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
})
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up a MJPEG IP Camera."""
# Filter header errors from urllib3 due to a urllib3 bug
filter_urllib3_logging()
if discovery_info:
config = PLATFORM_SCHEMA(discovery_info)
async_add_entities([MjpegCamera(config)])
def filter_urllib3_logging():
"""Filter header errors from urllib3 due to a urllib3 bug."""
urllib3_logger = logging.getLogger("urllib3.connectionpool")
if not any(isinstance(x, NoHeaderErrorFilter)
for x in urllib3_logger.filters):
@@ -52,10 +62,6 @@ async def async_setup_platform(hass, config, async_add_entities,
NoHeaderErrorFilter()
)
if discovery_info:
config = PLATFORM_SCHEMA(discovery_info)
async_add_entities([MjpegCamera(config)])
def extract_image_from_mjpeg(stream):
"""Take in a MJPEG stream object, return the jpg from it."""
@@ -95,6 +101,7 @@ class MjpegCamera(Camera):
self._auth = aiohttp.BasicAuth(
self._username, password=self._password
)
self._verify_ssl = device_info.get(CONF_VERIFY_SSL)
async def async_camera_image(self):
"""Return a still image response from the camera."""
@@ -105,7 +112,10 @@ class MjpegCamera(Camera):
self.camera_image)
return image
websession = async_get_clientsession(self.hass)
websession = async_get_clientsession(
self.hass,
verify_ssl=self._verify_ssl
)
try:
with async_timeout.timeout(10, loop=self.hass.loop):
response = await websession.get(
@@ -128,7 +138,12 @@ class MjpegCamera(Camera):
else:
auth = HTTPBasicAuth(self._username, self._password)
req = requests.get(
self._mjpeg_url, auth=auth, stream=True, timeout=10)
self._mjpeg_url,
auth=auth,
stream=True,
timeout=10,
verify=self._verify_ssl
)
else:
req = requests.get(self._mjpeg_url, stream=True, timeout=10)
@@ -144,7 +159,10 @@ class MjpegCamera(Camera):
return await super().handle_async_mjpeg_stream(request)
# connect to stream
websession = async_get_clientsession(self.hass)
websession = async_get_clientsession(
self.hass,
verify_ssl=self._verify_ssl
)
stream_coro = websession.get(self._mjpeg_url, auth=self._auth)
return await async_aiohttp_proxy_web(self.hass, request, stream_coro)

View File

@@ -8,6 +8,11 @@ from datetime import timedelta
import logging
import requests
import voluptuous as vol
from homeassistant.components.camera import PLATFORM_SCHEMA
from homeassistant.const import CONF_MONITORED_CONDITIONS
import homeassistant.helpers.config_validation as cv
from homeassistant.components.camera import Camera
from homeassistant.components.skybell import (
@@ -19,14 +24,33 @@ _LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=90)
IMAGE_AVATAR = 'avatar'
IMAGE_ACTIVITY = 'activity'
CONF_ACTIVITY_NAME = 'activity_name'
CONF_AVATAR_NAME = 'avatar_name'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_MONITORED_CONDITIONS, default=[IMAGE_AVATAR]):
vol.All(cv.ensure_list, [vol.In([IMAGE_AVATAR, IMAGE_ACTIVITY])]),
vol.Optional(CONF_ACTIVITY_NAME): cv.string,
vol.Optional(CONF_AVATAR_NAME): cv.string,
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the platform for a Skybell device."""
cond = config[CONF_MONITORED_CONDITIONS]
names = {}
names[IMAGE_ACTIVITY] = config.get(CONF_ACTIVITY_NAME)
names[IMAGE_AVATAR] = config.get(CONF_AVATAR_NAME)
skybell = hass.data.get(SKYBELL_DOMAIN)
sensors = []
for device in skybell.get_devices():
sensors.append(SkybellCamera(device))
for camera_type in cond:
sensors.append(SkybellCamera(device, camera_type,
names.get(camera_type)))
add_entities(sensors, True)
@@ -34,11 +58,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class SkybellCamera(SkybellDevice, Camera):
"""A camera implementation for Skybell devices."""
def __init__(self, device):
def __init__(self, device, camera_type, name=None):
"""Initialize a camera for a Skybell device."""
self._type = camera_type
SkybellDevice.__init__(self, device)
Camera.__init__(self)
self._name = self._device.name
if name is not None:
self._name = "{} {}".format(self._device.name, name)
else:
self._name = self._device.name
self._url = None
self._response = None
@@ -47,12 +75,19 @@ class SkybellCamera(SkybellDevice, Camera):
"""Return the name of the sensor."""
return self._name
@property
def image_url(self):
"""Get the camera image url based on type."""
if self._type == IMAGE_ACTIVITY:
return self._device.activity_image
return self._device.image
def camera_image(self):
"""Get the latest camera image."""
super().update()
if self._url != self._device.image:
self._url = self._device.image
if self._url != self.image_url:
self._url = self.image_url
try:
self._response = requests.get(

View File

@@ -17,7 +17,7 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
from homeassistant.exceptions import PlatformNotReady
REQUIREMENTS = ['aioftp==0.10.1']
REQUIREMENTS = ['aioftp==0.12.0']
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)

View File

@@ -6,9 +6,9 @@ https://home-assistant.io/components/camera.zoneminder/
"""
import logging
from homeassistant.const import CONF_NAME
from homeassistant.const import CONF_NAME, CONF_VERIFY_SSL
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera, filter_urllib3_logging)
from homeassistant.components.zoneminder import DOMAIN as ZONEMINDER_DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -18,6 +18,7 @@ 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()
@@ -28,22 +29,24 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
cameras = []
for monitor in monitors:
_LOGGER.info("Initializing camera %s", monitor.id)
cameras.append(ZoneMinderCamera(monitor))
cameras.append(ZoneMinderCamera(monitor, zm_client.verify_ssl))
add_entities(cameras)
class ZoneMinderCamera(MjpegCamera):
"""Representation of a ZoneMinder Monitor Stream."""
def __init__(self, monitor):
def __init__(self, monitor, verify_ssl):
"""Initialize as a subclass of MjpegCamera."""
device_info = {
CONF_NAME: monitor.name,
CONF_MJPEG_URL: monitor.mjpeg_image_url,
CONF_STILL_IMAGE_URL: monitor.still_image_url
CONF_STILL_IMAGE_URL: monitor.still_image_url,
CONF_VERIFY_SSL: verify_ssl
}
super().__init__(device_info)
self._is_recording = None
self._is_available = None
self._monitor = monitor
@property
@@ -55,8 +58,14 @@ class ZoneMinderCamera(MjpegCamera):
"""Update our recording state from the ZM API."""
_LOGGER.debug("Updating camera state for monitor %i", self._monitor.id)
self._is_recording = self._monitor.is_recording
self._is_available = self._monitor.is_available
@property
def is_recording(self):
"""Return whether the monitor is in alarm mode."""
return self._is_recording
@property
def available(self):
"""Return True if entity is available."""
return self._is_available

View File

@@ -6,7 +6,7 @@
},
"step": {
"confirm": {
"description": "Voleu configurar Google Cast?",
"description": "Vols configurar Google Cast?",
"title": "Google Cast"
}
},

View File

@@ -1,7 +1,8 @@
{
"config": {
"abort": {
"no_devices_found": "Nem tal\u00e1lhat\u00f3k Google Cast eszk\u00f6z\u00f6k a h\u00e1l\u00f3zaton."
"no_devices_found": "Nem tal\u00e1lhat\u00f3k Google Cast eszk\u00f6z\u00f6k a h\u00e1l\u00f3zaton.",
"single_instance_allowed": "Csak egyetlen Google Cast konfigur\u00e1ci\u00f3 sz\u00fcks\u00e9ges."
},
"step": {
"confirm": {

View File

@@ -92,15 +92,15 @@ CONVERTIBLE_ATTRIBUTE = [
_LOGGER = logging.getLogger(__name__)
ON_OFF_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
})
SET_AWAY_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_AWAY_MODE): cv.boolean,
})
SET_AUX_HEAT_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_AUX_HEAT): cv.boolean,
})
SET_TEMPERATURE_SCHEMA = vol.Schema(vol.All(
@@ -110,28 +110,28 @@ SET_TEMPERATURE_SCHEMA = vol.Schema(vol.All(
vol.Exclusive(ATTR_TEMPERATURE, 'temperature'): vol.Coerce(float),
vol.Inclusive(ATTR_TARGET_TEMP_HIGH, 'temperature'): vol.Coerce(float),
vol.Inclusive(ATTR_TARGET_TEMP_LOW, 'temperature'): vol.Coerce(float),
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Optional(ATTR_OPERATION_MODE): cv.string,
}
))
SET_FAN_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_FAN_MODE): cv.string,
})
SET_HOLD_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_HOLD_MODE): cv.string,
})
SET_OPERATION_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_OPERATION_MODE): cv.string,
})
SET_HUMIDITY_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_HUMIDITY): vol.Coerce(float),
})
SET_SWING_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
vol.Required(ATTR_SWING_MODE): cv.string,
})

View File

@@ -15,14 +15,13 @@ from homeassistant.components.climate import (
STATE_FAN_ONLY, STATE_HEAT, STATE_OFF, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE,
ClimateDevice)
from homeassistant.components.daikin import (
ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_TARGET_TEMPERATURE,
daikin_api_setup)
from homeassistant.components.daikin import DOMAIN as DAIKIN_DOMAIN
from homeassistant.components.daikin.const import (
ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_TARGET_TEMPERATURE)
from homeassistant.const import (
ATTR_TEMPERATURE, CONF_HOST, CONF_NAME, TEMP_CELSIUS)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pydaikin==0.8']
_LOGGER = logging.getLogger(__name__)
@@ -60,18 +59,18 @@ HA_ATTR_TO_DAIKIN = {
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Daikin HVAC platform."""
if discovery_info is not None:
host = discovery_info.get('ip')
name = None
_LOGGER.debug("Discovered a Daikin AC on %s", host)
else:
host = config.get(CONF_HOST)
name = config.get(CONF_NAME)
_LOGGER.debug("Added Daikin AC on %s", host)
"""Old way of setting up the Daikin HVAC platform.
api = daikin_api_setup(hass, host, name)
add_entities([DaikinClimate(api)], True)
Can only be called when a user accidentally mentions the platform in their
config. But even in that case it would have been ignored.
"""
pass
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up Daikin climate based on config_entry."""
daikin_api = hass.data[DAIKIN_DOMAIN].get(entry.entry_id)
async_add_entities([DaikinClimate(daikin_api)])
class DaikinClimate(ClimateDevice):
@@ -266,3 +265,8 @@ class DaikinClimate(ClimateDevice):
def update(self):
"""Retrieve latest state."""
self._api.update()
@property
def device_info(self):
"""Return a device description for device registry."""
return self._api.device_info

View File

@@ -9,7 +9,8 @@ import logging
import voluptuous as vol
from homeassistant.components.climate import (
STATE_ON, STATE_OFF, STATE_AUTO, PLATFORM_SCHEMA, ClimateDevice,
STATE_ON, STATE_OFF, STATE_HEAT, STATE_MANUAL, STATE_ECO, PLATFORM_SCHEMA,
ClimateDevice,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, SUPPORT_AWAY_MODE,
SUPPORT_ON_OFF)
from homeassistant.const import (
@@ -21,8 +22,6 @@ REQUIREMENTS = ['python-eq3bt==0.1.9', 'construct==2.9.45']
_LOGGER = logging.getLogger(__name__)
STATE_BOOST = 'boost'
STATE_AWAY = 'away'
STATE_MANUAL = 'manual'
ATTR_STATE_WINDOW_OPEN = 'window_open'
ATTR_STATE_VALVE = 'valve'
@@ -65,10 +64,10 @@ class EQ3BTSmartThermostat(ClimateDevice):
self.modes = {
eq3.Mode.Open: STATE_ON,
eq3.Mode.Closed: STATE_OFF,
eq3.Mode.Auto: STATE_AUTO,
eq3.Mode.Auto: STATE_HEAT,
eq3.Mode.Manual: STATE_MANUAL,
eq3.Mode.Boost: STATE_BOOST,
eq3.Mode.Away: STATE_AWAY,
eq3.Mode.Away: STATE_ECO,
}
self.reverse_modes = {v: k for k, v in self.modes.items()}
@@ -140,20 +139,20 @@ class EQ3BTSmartThermostat(ClimateDevice):
def turn_away_mode_off(self):
"""Away mode off turns to AUTO mode."""
self.set_operation_mode(STATE_AUTO)
self.set_operation_mode(STATE_HEAT)
def turn_away_mode_on(self):
"""Set away mode on."""
self.set_operation_mode(STATE_AWAY)
self.set_operation_mode(STATE_ECO)
@property
def is_away_mode_on(self):
"""Return if we are away."""
return self.current_operation == STATE_AWAY
return self.current_operation == STATE_ECO
def turn_on(self):
"""Turn device on."""
self.set_operation_mode(STATE_AUTO)
self.set_operation_mode(STATE_HEAT)
def turn_off(self):
"""Turn device off."""

View File

@@ -50,23 +50,23 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
def update_characteristics(self, characteristics):
"""Synchronise device state with Home Assistant."""
# pylint: disable=import-error
from homekit import CharacteristicsTypes as ctypes
from homekit.models.characteristics import CharacteristicsTypes
for characteristic in characteristics:
ctype = characteristic['type']
if ctype == ctypes.HEATING_COOLING_CURRENT:
if ctype == CharacteristicsTypes.HEATING_COOLING_CURRENT:
self._state = MODE_HOMEKIT_TO_HASS.get(
characteristic['value'])
if ctype == ctypes.HEATING_COOLING_TARGET:
if ctype == CharacteristicsTypes.HEATING_COOLING_TARGET:
self._chars['target_mode'] = characteristic['iid']
self._features |= SUPPORT_OPERATION_MODE
self._current_mode = MODE_HOMEKIT_TO_HASS.get(
characteristic['value'])
self._valid_modes = [MODE_HOMEKIT_TO_HASS.get(
mode) for mode in characteristic['valid-values']]
elif ctype == ctypes.TEMPERATURE_CURRENT:
elif ctype == CharacteristicsTypes.TEMPERATURE_CURRENT:
self._current_temp = characteristic['value']
elif ctype == ctypes.TEMPERATURE_TARGET:
elif ctype == CharacteristicsTypes.TEMPERATURE_TARGET:
self._chars['target_temp'] = characteristic['iid']
self._features |= SUPPORT_TARGET_TEMPERATURE
self._target_temp = characteristic['value']

View File

@@ -6,14 +6,17 @@ https://home-assistant.io/components/climate.knx/
"""
import voluptuous as vol
from homeassistant.components.climate import (
PLATFORM_SCHEMA, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
ClimateDevice)
from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX
from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.components.climate import (
PLATFORM_SCHEMA, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE,
SUPPORT_TARGET_TEMPERATURE, STATE_HEAT,
STATE_IDLE, STATE_MANUAL, STATE_DRY,
STATE_FAN_ONLY, STATE_ECO, ClimateDevice)
from homeassistant.const import (
ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS)
from homeassistant.core import callback
from homeassistant.components.knx import DATA_KNX, ATTR_DISCOVER_DEVICES
CONF_SETPOINT_SHIFT_ADDRESS = 'setpoint_shift_address'
CONF_SETPOINT_SHIFT_STATE_ADDRESS = 'setpoint_shift_state_address'
@@ -26,10 +29,17 @@ CONF_OPERATION_MODE_ADDRESS = 'operation_mode_address'
CONF_OPERATION_MODE_STATE_ADDRESS = 'operation_mode_state_address'
CONF_CONTROLLER_STATUS_ADDRESS = 'controller_status_address'
CONF_CONTROLLER_STATUS_STATE_ADDRESS = 'controller_status_state_address'
CONF_CONTROLLER_MODE_ADDRESS = 'controller_mode_address'
CONF_CONTROLLER_MODE_STATE_ADDRESS = 'controller_mode_state_address'
CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS = \
'operation_mode_frost_protection_address'
CONF_OPERATION_MODE_NIGHT_ADDRESS = 'operation_mode_night_address'
CONF_OPERATION_MODE_COMFORT_ADDRESS = 'operation_mode_comfort_address'
CONF_OPERATION_MODES = 'operation_modes'
CONF_ON_OFF_ADDRESS = 'on_off_address'
CONF_ON_OFF_STATE_ADDRESS = 'on_off_state_address'
CONF_MIN_TEMP = 'min_temp'
CONF_MAX_TEMP = 'max_temp'
DEFAULT_NAME = 'KNX Climate'
DEFAULT_SETPOINT_SHIFT_STEP = 0.5
@@ -37,6 +47,21 @@ DEFAULT_SETPOINT_SHIFT_MAX = 6
DEFAULT_SETPOINT_SHIFT_MIN = -6
DEPENDENCIES = ['knx']
# Map KNX operation modes to HA modes. This list might not be full.
OPERATION_MODES = {
# Map DPT 201.100 HVAC operating modes
"Frost Protection": STATE_MANUAL,
"Night": STATE_IDLE,
"Standby": STATE_ECO,
"Comfort": STATE_HEAT,
# Map DPT 201.104 HVAC control modes
"Fan only": STATE_FAN_ONLY,
"Dehumidification": STATE_DRY
}
OPERATION_MODES_INV = dict((
reversed(item) for item in OPERATION_MODES.items()))
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_TEMPERATURE_ADDRESS): cv.string,
@@ -54,9 +79,17 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_OPERATION_MODE_STATE_ADDRESS): cv.string,
vol.Optional(CONF_CONTROLLER_STATUS_ADDRESS): cv.string,
vol.Optional(CONF_CONTROLLER_STATUS_STATE_ADDRESS): cv.string,
vol.Optional(CONF_CONTROLLER_MODE_ADDRESS): cv.string,
vol.Optional(CONF_CONTROLLER_MODE_STATE_ADDRESS): cv.string,
vol.Optional(CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS): cv.string,
vol.Optional(CONF_OPERATION_MODE_NIGHT_ADDRESS): cv.string,
vol.Optional(CONF_OPERATION_MODE_COMFORT_ADDRESS): cv.string,
vol.Optional(CONF_ON_OFF_ADDRESS): cv.string,
vol.Optional(CONF_ON_OFF_STATE_ADDRESS): cv.string,
vol.Optional(CONF_OPERATION_MODES): vol.All(cv.ensure_list,
[vol.In(OPERATION_MODES)]),
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
vol.Optional(CONF_MAX_TEMP): vol.Coerce(float),
})
@@ -84,6 +117,30 @@ def async_add_entities_config(hass, config, async_add_entities):
"""Set up climate for KNX platform configured within platform."""
import xknx
climate_mode = xknx.devices.ClimateMode(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME) + " Mode",
group_address_operation_mode=config.get(CONF_OPERATION_MODE_ADDRESS),
group_address_operation_mode_state=config.get(
CONF_OPERATION_MODE_STATE_ADDRESS),
group_address_controller_status=config.get(
CONF_CONTROLLER_STATUS_ADDRESS),
group_address_controller_status_state=config.get(
CONF_CONTROLLER_STATUS_STATE_ADDRESS),
group_address_controller_mode=config.get(
CONF_CONTROLLER_MODE_ADDRESS),
group_address_controller_mode_state=config.get(
CONF_CONTROLLER_MODE_STATE_ADDRESS),
group_address_operation_mode_protection=config.get(
CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS),
group_address_operation_mode_night=config.get(
CONF_OPERATION_MODE_NIGHT_ADDRESS),
group_address_operation_mode_comfort=config.get(
CONF_OPERATION_MODE_COMFORT_ADDRESS),
operation_modes=config.get(
CONF_OPERATION_MODES))
hass.data[DATA_KNX].xknx.devices.add(climate_mode)
climate = xknx.devices.Climate(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
@@ -96,20 +153,15 @@ def async_add_entities_config(hass, config, async_add_entities):
setpoint_shift_step=config.get(CONF_SETPOINT_SHIFT_STEP),
setpoint_shift_max=config.get(CONF_SETPOINT_SHIFT_MAX),
setpoint_shift_min=config.get(CONF_SETPOINT_SHIFT_MIN),
group_address_operation_mode=config.get(CONF_OPERATION_MODE_ADDRESS),
group_address_operation_mode_state=config.get(
CONF_OPERATION_MODE_STATE_ADDRESS),
group_address_controller_status=config.get(
CONF_CONTROLLER_STATUS_ADDRESS),
group_address_controller_status_state=config.get(
CONF_CONTROLLER_STATUS_STATE_ADDRESS),
group_address_operation_mode_protection=config.get(
CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS),
group_address_operation_mode_night=config.get(
CONF_OPERATION_MODE_NIGHT_ADDRESS),
group_address_operation_mode_comfort=config.get(
CONF_OPERATION_MODE_COMFORT_ADDRESS))
group_address_on_off=config.get(
CONF_ON_OFF_ADDRESS),
group_address_on_off_state=config.get(
CONF_ON_OFF_STATE_ADDRESS),
min_temp=config.get(CONF_MIN_TEMP),
max_temp=config.get(CONF_MAX_TEMP),
mode=climate_mode)
hass.data[DATA_KNX].xknx.devices.add(climate)
async_add_entities([KNXClimate(climate)])
@@ -119,26 +171,25 @@ class KNXClimate(ClimateDevice):
def __init__(self, device):
"""Initialize of a KNX climate device."""
self.device = device
self._unit_of_measurement = TEMP_CELSIUS
@property
def supported_features(self):
"""Return the list of supported features."""
support = SUPPORT_TARGET_TEMPERATURE
if self.device.supports_operation_mode:
if self.device.mode.supports_operation_mode:
support |= SUPPORT_OPERATION_MODE
if self.device.supports_on_off:
support |= SUPPORT_ON_OFF
return support
def async_register_callbacks(self):
async def async_added_to_hass(self):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
await self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)
async def async_added_to_hass(self):
"""Store register state change callback."""
self.async_register_callbacks()
@property
def name(self):
"""Return the name of the KNX device."""
@@ -157,7 +208,7 @@ class KNXClimate(ClimateDevice):
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS
return self._unit_of_measurement
@property
def current_temperature(self):
@@ -195,20 +246,37 @@ class KNXClimate(ClimateDevice):
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
if self.device.supports_operation_mode:
return self.device.operation_mode.value
if self.device.mode.supports_operation_mode:
return OPERATION_MODES.get(self.device.mode.operation_mode.value)
return None
@property
def operation_list(self):
"""Return the list of available operation modes."""
return [operation_mode.value for
return [OPERATION_MODES.get(operation_mode.value) for
operation_mode in
self.device.get_supported_operation_modes()]
self.device.mode.operation_modes]
async def async_set_operation_mode(self, operation_mode):
"""Set operation mode."""
if self.device.supports_operation_mode:
if self.device.mode.supports_operation_mode:
from xknx.knx import HVACOperationMode
knx_operation_mode = HVACOperationMode(operation_mode)
await self.device.set_operation_mode(knx_operation_mode)
knx_operation_mode = HVACOperationMode(
OPERATION_MODES_INV.get(operation_mode))
await self.device.mode.set_operation_mode(knx_operation_mode)
await self.async_update_ha_state()
@property
def is_on(self):
"""Return true if the device is on."""
if self.device.supports_on_off:
return self.device.is_on
return None
async def async_turn_on(self):
"""Turn on."""
await self.device.turn_on()
async def async_turn_off(self):
"""Turn off."""
await self.device.turn_off()

View File

@@ -19,7 +19,7 @@ from homeassistant.const import (
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
REQUIREMENTS = ['millheater==0.2.9']
REQUIREMENTS = ['millheater==0.3.3']
_LOGGER = logging.getLogger(__name__)

View File

@@ -18,12 +18,13 @@ from homeassistant.components.climate import (
SUPPORT_SWING_MODE, SUPPORT_FAN_MODE, SUPPORT_AWAY_MODE, SUPPORT_HOLD_MODE,
SUPPORT_AUX_HEAT, DEFAULT_MIN_TEMP, DEFAULT_MAX_TEMP)
from homeassistant.const import (
STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME, CONF_VALUE_TEMPLATE)
ATTR_TEMPERATURE, CONF_DEVICE, CONF_NAME, CONF_VALUE_TEMPLATE, STATE_ON,
STATE_OFF)
from homeassistant.components.mqtt import (
ATTR_DISCOVERY_HASH, CONF_AVAILABILITY_TOPIC, CONF_QOS, CONF_RETAIN,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE,
MQTT_BASE_PLATFORM_SCHEMA, MqttAvailability, MqttDiscoveryUpdate,
subscription)
MqttEntityDeviceInfo, subscription)
from homeassistant.components.mqtt.discovery import MQTT_DISCOVERY_NEW
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -78,6 +79,8 @@ CONF_MIN_TEMP = 'min_temp'
CONF_MAX_TEMP = 'max_temp'
CONF_TEMP_STEP = 'temp_step'
CONF_UNIQUE_ID = 'unique_id'
TEMPLATE_KEYS = (
CONF_POWER_STATE_TEMPLATE,
CONF_MODE_STATE_TEMPLATE,
@@ -139,8 +142,9 @@ PLATFORM_SCHEMA = SCHEMA_BASE.extend({
vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float),
vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float),
vol.Optional(CONF_TEMP_STEP, default=1.0): vol.Coerce(float)
vol.Optional(CONF_TEMP_STEP, default=1.0): vol.Coerce(float),
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA,
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
@@ -174,12 +178,14 @@ async def _async_setup_entity(hass, config, async_add_entities,
)])
class MqttClimate(MqttAvailability, MqttDiscoveryUpdate, ClimateDevice):
class MqttClimate(MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo,
ClimateDevice):
"""Representation of an MQTT climate device."""
def __init__(self, hass, config, discovery_hash):
"""Initialize the climate device."""
self._config = config
self._unique_id = config.get(CONF_UNIQUE_ID)
self._sub_state = None
self.hass = hass
@@ -201,11 +207,13 @@ class MqttClimate(MqttAvailability, MqttDiscoveryUpdate, ClimateDevice):
payload_available = config.get(CONF_PAYLOAD_AVAILABLE)
payload_not_available = config.get(CONF_PAYLOAD_NOT_AVAILABLE)
qos = config.get(CONF_QOS)
device_config = config.get(CONF_DEVICE)
MqttAvailability.__init__(self, availability_topic, qos,
payload_available, payload_not_available)
MqttDiscoveryUpdate.__init__(self, discovery_hash,
self.discovery_update)
MqttEntityDeviceInfo.__init__(self, device_config)
async def async_added_to_hass(self):
"""Handle being added to home assistant."""
@@ -453,7 +461,8 @@ class MqttClimate(MqttAvailability, MqttDiscoveryUpdate, ClimateDevice):
async def async_will_remove_from_hass(self):
"""Unsubscribe when removed."""
await subscription.async_unsubscribe_topics(self.hass, self._sub_state)
self._sub_state = await subscription.async_unsubscribe_topics(
self.hass, self._sub_state)
await MqttAvailability.async_will_remove_from_hass(self)
@property
@@ -466,6 +475,11 @@ class MqttClimate(MqttAvailability, MqttDiscoveryUpdate, ClimateDevice):
"""Return the name of the climate device."""
return self._config.get(CONF_NAME)
@property
def unique_id(self):
"""Return a unique ID."""
return self._unique_id
@property
def temperature_unit(self):
"""Return the unit of measurement."""

View File

@@ -17,7 +17,7 @@ from homeassistant.const import (
CONF_HOST, TEMP_FAHRENHEIT, ATTR_TEMPERATURE, PRECISION_HALVES)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['radiotherm==1.4.1']
REQUIREMENTS = ['radiotherm==2.0.0']
_LOGGER = logging.getLogger(__name__)
@@ -235,13 +235,15 @@ class RadioThermostat(ClimateDevice):
self._name = self.device.name['raw']
# Request the current state from the thermostat.
data = self.device.tstat['raw']
import radiotherm
try:
data = self.device.tstat['raw']
except radiotherm.validate.RadiothermTstatError:
_LOGGER.error('%s (%s) was busy (invalid value returned)',
self._name, self.device.host)
return
current_temp = data['temp']
if current_temp == -1:
_LOGGER.error('%s (%s) was busy (temp == -1)', self._name,
self.device.host)
return
# Map thermostat values into various STATE_ flags.
self._current_temperature = current_temp

View File

@@ -99,6 +99,8 @@ async def async_setup(hass, config):
kwargs[CONF_GOOGLE_ACTIONS] = GACTIONS_SCHEMA({})
kwargs[CONF_ALEXA] = alexa_sh.Config(
endpoint=None,
async_get_access_token=None,
should_expose=alexa_conf[CONF_FILTER],
entity_config=alexa_conf.get(CONF_ENTITY_CONFIG),
)

View File

@@ -39,7 +39,7 @@ async def async_setup(hass):
return True
@websocket_api.require_owner
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_list(hass, connection, msg):
"""Return a list of users."""
@@ -49,7 +49,7 @@ async def websocket_list(hass, connection, msg):
websocket_api.result_message(msg['id'], result))
@websocket_api.require_owner
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_delete(hass, connection, msg):
"""Delete a user."""
@@ -72,7 +72,7 @@ async def websocket_delete(hass, connection, msg):
websocket_api.result_message(msg['id']))
@websocket_api.require_owner
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_create(hass, connection, msg):
"""Create a user."""

View File

@@ -3,7 +3,6 @@ import voluptuous as vol
from homeassistant.auth.providers import homeassistant as auth_ha
from homeassistant.components import websocket_api
from homeassistant.components.websocket_api.decorators import require_owner
WS_TYPE_CREATE = 'config/auth_provider/homeassistant/create'
@@ -54,7 +53,7 @@ def _get_provider(hass):
raise RuntimeError('Provider not found')
@require_owner
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_create(hass, connection, msg):
"""Create credentials and attach to a user."""
@@ -91,7 +90,7 @@ async def websocket_create(hass, connection, msg):
connection.send_message(websocket_api.result_message(msg['id']))
@require_owner
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_delete(hass, connection, msg):
"""Delete username and related credential."""
@@ -123,6 +122,7 @@ async def websocket_delete(hass, connection, msg):
websocket_api.result_message(msg['id']))
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_change_password(hass, connection, msg):
"""Change user password."""

View File

@@ -1,7 +1,9 @@
"""Http views to control the config manager."""
from homeassistant import config_entries, data_entry_flow
from homeassistant.auth.permissions.const import CAT_CONFIG_ENTRIES
from homeassistant.components.http import HomeAssistantView
from homeassistant.exceptions import Unauthorized
from homeassistant.helpers.data_entry_flow import (
FlowManagerIndexView, FlowManagerResourceView)
@@ -63,6 +65,9 @@ class ConfigManagerEntryResourceView(HomeAssistantView):
async def delete(self, request, entry_id):
"""Delete a config entry."""
if not request['hass_user'].is_admin:
raise Unauthorized(config_entry_id=entry_id, permission='remove')
hass = request.app['hass']
try:
@@ -85,12 +90,26 @@ class ConfigManagerFlowIndexView(FlowManagerIndexView):
Example of a non-user initiated flow is a discovered Hue hub that
requires user interaction to finish setup.
"""
if not request['hass_user'].is_admin:
raise Unauthorized(
perm_category=CAT_CONFIG_ENTRIES, permission='add')
hass = request.app['hass']
return self.json([
flw for flw in hass.config_entries.flow.async_progress()
if flw['context']['source'] != config_entries.SOURCE_USER])
# pylint: disable=arguments-differ
async def post(self, request):
"""Handle a POST request."""
if not request['hass_user'].is_admin:
raise Unauthorized(
perm_category=CAT_CONFIG_ENTRIES, permission='add')
# pylint: disable=no-value-for-parameter
return await super().post(request)
class ConfigManagerFlowResourceView(FlowManagerResourceView):
"""View to interact with the flow manager."""
@@ -98,6 +117,24 @@ class ConfigManagerFlowResourceView(FlowManagerResourceView):
url = '/api/config/config_entries/flow/{flow_id}'
name = 'api:config:config_entries:flow:resource'
async def get(self, request, flow_id):
"""Get the current state of a data_entry_flow."""
if not request['hass_user'].is_admin:
raise Unauthorized(
perm_category=CAT_CONFIG_ENTRIES, permission='add')
return await super().get(request, flow_id)
# pylint: disable=arguments-differ
async def post(self, request, flow_id):
"""Handle a POST request."""
if not request['hass_user'].is_admin:
raise Unauthorized(
perm_category=CAT_CONFIG_ENTRIES, permission='add')
# pylint: disable=no-value-for-parameter
return await super().post(request, flow_id)
class ConfigManagerAvailableFlowView(HomeAssistantView):
"""View to query available flows."""

View File

@@ -104,7 +104,7 @@ async def websocket_update_entity(hass, connection, msg):
if 'name' in msg:
changes['name'] = msg['name']
if 'new_entity_id' in msg:
if 'new_entity_id' in msg and msg['new_entity_id'] != msg['entity_id']:
changes['new_entity_id'] = msg['new_entity_id']
if hass.states.get(msg['new_entity_id']) is not None:
connection.send_message(websocket_api.error_message(

View File

@@ -33,7 +33,7 @@ SERVICE_INCREMENT = 'increment'
SERVICE_RESET = 'reset'
SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
})
CONFIG_SCHEMA = vol.Schema({

View File

@@ -60,7 +60,7 @@ INTENT_OPEN_COVER = 'HassOpenCover'
INTENT_CLOSE_COVER = 'HassCloseCover'
COVER_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_ENTITY_ID): cv.comp_entity_ids,
})
COVER_SET_COVER_POSITION_SCHEMA = COVER_SERVICE_SCHEMA.extend({

View File

@@ -0,0 +1,89 @@
"""Support for ESPHome covers."""
import logging
from typing import TYPE_CHECKING, Optional
from homeassistant.components.cover import CoverDevice, SUPPORT_CLOSE, \
SUPPORT_OPEN, SUPPORT_STOP
from homeassistant.components.esphome import EsphomeEntity, \
platform_async_setup_entry
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_CLOSED, STATE_OPEN
from homeassistant.helpers.typing import HomeAssistantType
if TYPE_CHECKING:
# pylint: disable=unused-import
from aioesphomeapi import CoverInfo, CoverState # noqa
DEPENDENCIES = ['esphome']
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistantType,
entry: ConfigEntry, async_add_entities) -> None:
"""Set up ESPHome covers based on a config entry."""
# pylint: disable=redefined-outer-name
from aioesphomeapi import CoverInfo, CoverState # noqa
await platform_async_setup_entry(
hass, entry, async_add_entities,
component_key='cover',
info_type=CoverInfo, entity_type=EsphomeCover,
state_type=CoverState
)
COVER_STATE_INT_TO_STR = {
0: STATE_OPEN,
1: STATE_CLOSED
}
class EsphomeCover(EsphomeEntity, CoverDevice):
"""A cover implementation for ESPHome."""
@property
def _static_info(self) -> 'CoverInfo':
return super()._static_info
@property
def _state(self) -> Optional['CoverState']:
return super()._state
@property
def supported_features(self) -> int:
"""Flag supported features."""
return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP
@property
def assumed_state(self) -> bool:
"""Return true if we do optimistic updates."""
return self._static_info.is_optimistic
@property
def is_closed(self) -> Optional[bool]:
"""Return if the cover is closed or not."""
if self._state is None:
return None
return COVER_STATE_INT_TO_STR[self._state.state]
async def async_open_cover(self, **kwargs) -> None:
"""Open the cover."""
from aioesphomeapi.client import COVER_COMMAND_OPEN
await self._client.cover_command(key=self._static_info.key,
command=COVER_COMMAND_OPEN)
async def async_close_cover(self, **kwargs) -> None:
"""Close cover."""
from aioesphomeapi.client import COVER_COMMAND_CLOSE
await self._client.cover_command(key=self._static_info.key,
command=COVER_COMMAND_CLOSE)
async def async_stop_cover(self, **kwargs):
"""Stop the cover."""
from aioesphomeapi.client import COVER_COMMAND_STOP
await self._client.cover_command(key=self._static_info.key,
command=COVER_COMMAND_STOP)

View File

@@ -287,7 +287,8 @@ class MqttCover(MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo,
async def async_will_remove_from_hass(self):
"""Unsubscribe when removed."""
await subscription.async_unsubscribe_topics(self.hass, self._sub_state)
self._sub_state = await subscription.async_unsubscribe_topics(
self.hass, self._sub_state)
await MqttAvailability.async_will_remove_from_hass(self)
@property

View File

@@ -15,8 +15,8 @@ from homeassistant.components.rflink import (
from homeassistant.components.cover import (
CoverDevice, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.const import CONF_NAME, STATE_OPEN
DEPENDENCIES = ['rflink']
@@ -60,9 +60,17 @@ async def async_setup_platform(hass, config, async_add_entities,
async_add_entities(devices_from_config(config))
class RflinkCover(RflinkCommand, CoverDevice):
class RflinkCover(RflinkCommand, CoverDevice, RestoreEntity):
"""Rflink entity which can switch on/stop/off (eg: cover)."""
async def async_added_to_hass(self):
"""Restore RFLink cover state (OPEN/CLOSE)."""
await super().async_added_to_hass()
old_state = await self.async_get_last_state()
if old_state is not None:
self._state = old_state.state == STATE_OPEN
def _handle_event(self, event):
"""Adjust state if Rflink picks up a remote command for this device."""
self.cancel_queued_send_commands()

View File

@@ -8,20 +8,36 @@ https://home-assistant.io/components/cover.tellduslive/
"""
import logging
from homeassistant.components import tellduslive
from homeassistant.components import cover, tellduslive
from homeassistant.components.cover import CoverDevice
from homeassistant.components.tellduslive.entry import TelldusLiveEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Telldus Live covers."""
if discovery_info is None:
return
"""Old way of setting up TelldusLive.
client = hass.data[tellduslive.DOMAIN]
add_entities(TelldusLiveCover(client, cover) for cover in discovery_info)
Can only be called when a user accidentally mentions the platform in their
config. But even in that case it would have been ignored.
"""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up tellduslive sensors dynamically."""
async def async_discover_cover(device_id):
"""Discover and add a discovered sensor."""
client = hass.data[tellduslive.DOMAIN]
async_add_entities([TelldusLiveCover(client, device_id)])
async_dispatcher_connect(
hass,
tellduslive.TELLDUS_DISCOVERY_NEW.format(cover.DOMAIN,
tellduslive.DOMAIN),
async_discover_cover,
)
class TelldusLiveCover(TelldusLiveEntity, CoverDevice):

View File

@@ -1,139 +0,0 @@
"""
Platform for the Daikin AC.
For more details about this component, please refer to the documentation
https://home-assistant.io/components/daikin/
"""
import logging
from datetime import timedelta
from socket import timeout
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.discovery import SERVICE_DAIKIN
from homeassistant.const import (
CONF_HOSTS, CONF_ICON, CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_TYPE
)
from homeassistant.helpers import discovery
from homeassistant.helpers.discovery import load_platform
from homeassistant.util import Throttle
REQUIREMENTS = ['pydaikin==0.8']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'daikin'
ATTR_TARGET_TEMPERATURE = 'target_temperature'
ATTR_INSIDE_TEMPERATURE = 'inside_temperature'
ATTR_OUTSIDE_TEMPERATURE = 'outside_temperature'
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
COMPONENT_TYPES = ['climate', 'sensor']
SENSOR_TYPE_TEMPERATURE = 'temperature'
SENSOR_TYPES = {
ATTR_INSIDE_TEMPERATURE: {
CONF_NAME: 'Inside Temperature',
CONF_ICON: 'mdi:thermometer',
CONF_TYPE: SENSOR_TYPE_TEMPERATURE
},
ATTR_OUTSIDE_TEMPERATURE: {
CONF_NAME: 'Outside Temperature',
CONF_ICON: 'mdi:thermometer',
CONF_TYPE: SENSOR_TYPE_TEMPERATURE
}
}
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Optional(
CONF_HOSTS, default=[]
): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(
CONF_MONITORED_CONDITIONS,
default=list(SENSOR_TYPES.keys())
): vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)])
})
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Establish connection with Daikin."""
def discovery_dispatch(service, discovery_info):
"""Dispatcher for Daikin discovery events."""
host = discovery_info.get('ip')
if daikin_api_setup(hass, host) is None:
return
for component in COMPONENT_TYPES:
load_platform(hass, component, DOMAIN, discovery_info,
config)
discovery.listen(hass, SERVICE_DAIKIN, discovery_dispatch)
for host in config.get(DOMAIN, {}).get(CONF_HOSTS, []):
if daikin_api_setup(hass, host) is None:
continue
discovery_info = {
'ip': host,
CONF_MONITORED_CONDITIONS:
config[DOMAIN][CONF_MONITORED_CONDITIONS]
}
load_platform(hass, 'sensor', DOMAIN, discovery_info, config)
return True
def daikin_api_setup(hass, host, name=None):
"""Create a Daikin instance only once."""
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
api = hass.data[DOMAIN].get(host)
if api is None:
from pydaikin import appliance
try:
device = appliance.Appliance(host)
except timeout:
_LOGGER.error("Connection to Daikin could not be established")
return False
if name is None:
name = device.values['name']
api = DaikinApi(device, name)
return api
class DaikinApi:
"""Keep the Daikin instance in one place and centralize the update."""
def __init__(self, device, name):
"""Initialize the Daikin Handle."""
self.device = device
self.name = name
self.ip_address = device.ip
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs):
"""Pull the latest data from Daikin."""
try:
self.device.update_status()
except timeout:
_LOGGER.warning(
"Connection failed for %s", self.ip_address
)
@property
def mac(self):
"""Return mac-address of device."""
return self.device.values.get('mac')

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "El dispositiu ja est\u00e0 configurat",
"device_fail": "S'ha produ\u00eft un error inesperat al crear el dispositiu.",
"device_timeout": "S'ha acabat el temps d'espera en la connexi\u00f3 amb el dispositiu."
},
"step": {
"user": {
"data": {
"host": "Amfitri\u00f3"
},
"description": "Introdueix l'adre\u00e7a IP del teu Daikin AC.",
"title": "Configuraci\u00f3 de Daikin AC"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured",
"device_fail": "Unexpected error creating device.",
"device_timeout": "Timeout connecting to the device."
},
"step": {
"user": {
"data": {
"host": "Host"
},
"description": "Enter IP address of your Daikin AC.",
"title": "Configure Daikin AC"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4",
"device_fail": "\uc7a5\uce58\ub97c \uad6c\uc131\ud558\ub294\uc911 \uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.",
"device_timeout": "\uc7a5\uce58 \uc5f0\uacb0 \uc2dc\uac04\uc774 \ucd08\uacfc\ud588\uc2b5\ub2c8\ub2e4."
},
"step": {
"user": {
"data": {
"host": "\ud638\uc2a4\ud2b8"
},
"description": "Daikin AC \uc758 IP \uc8fc\uc18c\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.",
"title": "Daikin AC \uad6c\uc131"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Apparat ass scho konfigur\u00e9iert",
"device_fail": "Onerwaarte Feeler beim erstelle vum Apparat.",
"device_timeout": "Z\u00e4it Iwwerschreidung beim verbannen mam Apparat."
},
"step": {
"user": {
"data": {
"host": "Apparat"
},
"description": "Gitt d'IP Adresse vum Daikin AC an:",
"title": "Daikin AC konfigur\u00e9ieren"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.",
"device_fail": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.",
"device_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443."
},
"step": {
"user": {
"data": {
"host": "\u0425\u043e\u0441\u0442"
},
"description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0432\u0430\u0448\u0435\u0433\u043e Daikin AC.",
"title": "Daikin AC"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Naprava je \u017ee konfigurirana",
"device_fail": "Nepri\u010dakovana napaka pri ustvarjanju naprave.",
"device_timeout": "\u010casovna omejitev za priklop na napravo je potekla."
},
"step": {
"user": {
"data": {
"host": "Gostitelj"
},
"description": "Vnesite naslov IP va\u0161e Daikin klime.",
"title": "Nastavite Daikin klimo"
}
},
"title": "Daikin AC"
}
}

View File

@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
"device_fail": "\u5275\u5efa\u88dd\u7f6e\u6642\u767c\u751f\u672a\u77e5\u932f\u8aa4\u3002",
"device_timeout": "\u9023\u7dda\u81f3\u88dd\u7f6e\u903e\u6642\u3002"
},
"step": {
"user": {
"data": {
"host": "\u4e3b\u6a5f\u7aef"
},
"description": "\u8f38\u5165\u60a8\u7684\u5927\u91d1\u7a7a\u8abf IP \u4f4d\u5740\u3002",
"title": "\u8a2d\u5b9a\u5927\u91d1\u7a7a\u8abf"
}
},
"title": "\u5927\u91d1\u7a7a\u8abf\uff08Daikin AC\uff09"
}
}

View File

@@ -0,0 +1,146 @@
"""
Platform for the Daikin AC.
For more details about this component, please refer to the documentation
https://home-assistant.io/components/daikin/
"""
import asyncio
from datetime import timedelta
import logging
from socket import timeout
import async_timeout
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOSTS
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import Throttle
from . import config_flow # noqa pylint_disable=unused-import
from .const import KEY_HOST
REQUIREMENTS = ['pydaikin==0.9']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'daikin'
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
COMPONENT_TYPES = ['climate', 'sensor']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Optional(
CONF_HOSTS, default=[]
): vol.All(cv.ensure_list, [cv.string]),
})
}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass, config):
"""Establish connection with Daikin."""
if DOMAIN not in config:
return True
hosts = config[DOMAIN].get(CONF_HOSTS)
if not hosts:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={'source': config.SOURCE_IMPORT}))
for host in hosts:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={'source': config.SOURCE_IMPORT},
data={
KEY_HOST: host,
}))
return True
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Establish connection with Daikin."""
conf = entry.data
daikin_api = await daikin_api_setup(hass, conf[KEY_HOST])
if not daikin_api:
return False
hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api})
await asyncio.wait([
hass.config_entries.async_forward_entry_setup(entry, component)
for component in COMPONENT_TYPES
])
return True
async def async_unload_entry(hass, config_entry):
"""Unload a config entry."""
await asyncio.wait([
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in COMPONENT_TYPES
])
hass.data[DOMAIN].pop(config_entry.entry_id)
if not hass.data[DOMAIN]:
hass.data.pop(DOMAIN)
return True
async def daikin_api_setup(hass, host):
"""Create a Daikin instance only once."""
from pydaikin.appliance import Appliance
try:
with async_timeout.timeout(10):
device = await hass.async_add_executor_job(Appliance, host)
except asyncio.TimeoutError:
_LOGGER.error("Connection to Daikin could not be established")
return None
except Exception: # pylint: disable=broad-except
_LOGGER.error("Unexpected error creating device")
return None
name = device.values['name']
api = DaikinApi(device, name)
return api
class DaikinApi:
"""Keep the Daikin instance in one place and centralize the update."""
def __init__(self, device, name):
"""Initialize the Daikin Handle."""
self.device = device
self.name = name
self.ip_address = device.ip
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs):
"""Pull the latest data from Daikin."""
try:
self.device.update_status()
except timeout:
_LOGGER.warning(
"Connection failed for %s", self.ip_address
)
@property
def mac(self):
"""Return mac-address of device."""
return self.device.values.get(CONNECTION_NETWORK_MAC)
@property
def device_info(self):
"""Return a device description for device registry."""
info = self.device.values
return {
'connections': {(CONNECTION_NETWORK_MAC, self.mac)},
'identifieres': self.mac,
'manufacturer': 'Daikin',
'model': info.get('model'),
'name': info.get('name'),
'sw_version': info.get('ver').replace('_', '.'),
}

View File

@@ -0,0 +1,74 @@
"""Config flow for the Daikin platform."""
import asyncio
import logging
import async_timeout
import voluptuous as vol
from homeassistant import config_entries
from .const import KEY_HOST, KEY_IP, KEY_MAC
_LOGGER = logging.getLogger(__name__)
@config_entries.HANDLERS.register('daikin')
class FlowHandler(config_entries.ConfigFlow):
"""Handle a config flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
async def _create_entry(self, host, mac):
"""Register new entry."""
# Check if mac already is registered
for entry in self._async_current_entries():
if entry.data[KEY_MAC] == mac:
return self.async_abort(reason='already_configured')
return self.async_create_entry(
title=host,
data={
KEY_HOST: host,
KEY_MAC: mac
})
async def _create_device(self, host):
"""Create device."""
from pydaikin.appliance import Appliance
try:
with async_timeout.timeout(10):
device = await self.hass.async_add_executor_job(
Appliance, host)
except asyncio.TimeoutError:
return self.async_abort(reason='device_timeout')
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected error creating device")
return self.async_abort(reason='device_fail')
mac = device.values.get('mac')
return await self._create_entry(host, mac)
async def async_step_user(self, user_input=None):
"""User initiated config flow."""
if user_input is None:
return self.async_show_form(
step_id='user',
data_schema=vol.Schema({
vol.Required(KEY_HOST): str
})
)
return await self._create_device(user_input[KEY_HOST])
async def async_step_import(self, user_input):
"""Import a config entry."""
host = user_input.get(KEY_HOST)
if not host:
return await self.async_step_user()
return await self._create_device(host)
async def async_step_discovery(self, user_input):
"""Initialize step from discovery."""
_LOGGER.info("Discovered device: %s", user_input)
return await self._create_entry(user_input[KEY_IP],
user_input[KEY_MAC])

View File

@@ -0,0 +1,25 @@
"""Constants for Daikin."""
from homeassistant.const import CONF_ICON, CONF_NAME, CONF_TYPE
ATTR_TARGET_TEMPERATURE = 'target_temperature'
ATTR_INSIDE_TEMPERATURE = 'inside_temperature'
ATTR_OUTSIDE_TEMPERATURE = 'outside_temperature'
SENSOR_TYPE_TEMPERATURE = 'temperature'
SENSOR_TYPES = {
ATTR_INSIDE_TEMPERATURE: {
CONF_NAME: 'Inside Temperature',
CONF_ICON: 'mdi:thermometer',
CONF_TYPE: SENSOR_TYPE_TEMPERATURE
},
ATTR_OUTSIDE_TEMPERATURE: {
CONF_NAME: 'Outside Temperature',
CONF_ICON: 'mdi:thermometer',
CONF_TYPE: SENSOR_TYPE_TEMPERATURE
}
}
KEY_HOST = 'host'
KEY_MAC = 'mac'
KEY_IP = 'ip'

View File

@@ -0,0 +1,19 @@
{
"config": {
"title": "Daikin AC",
"step": {
"user": {
"title": "Configure Daikin AC",
"description": "Enter IP address of your Daikin AC.",
"data": {
"host": "Host"
}
}
},
"abort": {
"device_timeout": "Timeout connecting to the device.",
"device_fail": "Unexpected error creating device.",
"already_configured": "Device is already configured"
}
}
}

View File

@@ -14,7 +14,7 @@
"host": "Amfitri\u00f3",
"port": "Port"
},
"title": "Definiu la passarel\u00b7la deCONZ"
"title": "Definici\u00f3 de la passarel\u00b7la deCONZ"
},
"link": {
"description": "Desbloqueja la teva passarel\u00b7la d'enlla\u00e7 deCONZ per a registrar-te amb Home Assistant.\n\n1. V\u00e9s a la configuraci\u00f3 del sistema deCONZ\n2. Prem el bot\u00f3 \"Desbloquejar passarel\u00b7la\"",
@@ -23,7 +23,7 @@
"options": {
"data": {
"allow_clip_sensor": "Permet la importaci\u00f3 de sensors virtuals",
"allow_deconz_groups": "Permet la importaci\u00f3 de grups deCONZ"
"allow_deconz_groups": "Permetre la importaci\u00f3 de grups deCONZ"
},
"title": "Opcions de configuraci\u00f3 addicionals per deCONZ"
}

View File

@@ -22,8 +22,10 @@
},
"options": {
"data": {
"allow_clip_sensor": "Virtu\u00e1lis szenzorok import\u00e1l\u00e1s\u00e1nak enged\u00e9lyez\u00e9se"
}
"allow_clip_sensor": "Virtu\u00e1lis szenzorok import\u00e1l\u00e1s\u00e1nak enged\u00e9lyez\u00e9se",
"allow_deconz_groups": "deCONZ csoportok import\u00e1l\u00e1s\u00e1nak enged\u00e9lyez\u00e9se"
},
"title": "Extra be\u00e1ll\u00edt\u00e1si lehet\u0151s\u00e9gek a deCONZhoz"
}
},
"title": "deCONZ Zigbee gateway"

View File

@@ -28,6 +28,6 @@
"title": "Extra Konfiguratiouns Optiounen fir deCONZ"
}
},
"title": "deCONZ"
"title": "deCONZ Zigbee gateway"
}
}

View File

@@ -15,6 +15,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone']
DOMAIN = 'demo'
COMPONENTS_WITH_DEMO_PLATFORM = [
'air_pollutants',
'alarm_control_panel',
'binary_sensor',
'calendar',

View File

@@ -24,9 +24,15 @@ BT_PREFIX = 'BT_'
CONF_REQUEST_RSSI = 'request_rssi'
CONF_DEVICE_ID = "device_id"
DEFAULT_DEVICE_ID = -1
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_TRACK_NEW): cv.boolean,
vol.Optional(CONF_REQUEST_RSSI): cv.boolean
vol.Optional(CONF_REQUEST_RSSI): cv.boolean,
vol.Optional(CONF_DEVICE_ID, default=DEFAULT_DEVICE_ID):
vol.All(vol.Coerce(int), vol.Range(min=-1))
})
@@ -44,11 +50,13 @@ def setup_scanner(hass, config, see, discovery_info=None):
see(mac="{}{}".format(BT_PREFIX, mac), host_name=name,
attributes=attributes, source_type=SOURCE_TYPE_BLUETOOTH)
device_id = config.get(CONF_DEVICE_ID)
def discover_devices():
"""Discover Bluetooth devices."""
result = bluetooth.discover_devices(
duration=8, lookup_names=True, flush_cache=True,
lookup_class=False)
lookup_class=False, device_id=device_id)
_LOGGER.debug("Bluetooth devices discovered = %d", len(result))
return result

View File

@@ -1,56 +1,25 @@
"""
Support for device tracking through Freebox routers.
Support for Freebox devices (Freebox v6 and Freebox mini 4K).
This tracker keeps track of the devices connected to the configured Freebox.
For more details about this platform, please refer to the documentation at
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/device_tracker.freebox/
"""
import asyncio
import copy
import logging
import socket
from collections import namedtuple
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.components.device_tracker import DeviceScanner
from homeassistant.components.freebox import DATA_FREEBOX
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
from homeassistant.const import (
CONF_HOST, CONF_PORT)
REQUIREMENTS = ['aiofreepybox==0.0.5']
DEPENDENCIES = ['freebox']
_LOGGER = logging.getLogger(__name__)
FREEBOX_CONFIG_FILE = 'freebox.conf'
PLATFORM_SCHEMA = vol.All(
PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.port
}))
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
async def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up the Freebox device tracker and start the polling."""
freebox_config = copy.deepcopy(config)
if discovery_info is not None:
freebox_config[CONF_HOST] = discovery_info['properties']['api_domain']
freebox_config[CONF_PORT] = discovery_info['properties']['https_port']
_LOGGER.info("Discovered Freebox server: %s:%s",
freebox_config[CONF_HOST], freebox_config[CONF_PORT])
scanner = FreeboxDeviceScanner(hass, freebox_config, async_see)
interval = freebox_config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
await scanner.async_start(hass, interval)
return True
async def async_get_scanner(hass, config):
"""Validate the configuration and return a Freebox scanner."""
scanner = FreeboxDeviceScanner(hass.data[DATA_FREEBOX])
await scanner.async_connect()
return scanner if scanner.success_init else None
Device = namedtuple('Device', ['id', 'name', 'ip'])
@@ -62,59 +31,41 @@ def _build_device(device_dict):
device_dict['l3connectivities'][0]['addr'])
class FreeboxDeviceScanner:
"""This class scans for devices connected to the Freebox."""
class FreeboxDeviceScanner(DeviceScanner):
"""Queries the Freebox device."""
def __init__(self, hass, config, async_see):
def __init__(self, fbx):
"""Initialize the scanner."""
from aiofreepybox import Freepybox
self.last_results = {}
self.success_init = False
self.connection = fbx
self.host = config[CONF_HOST]
self.port = config[CONF_PORT]
self.token_file = hass.config.path(FREEBOX_CONFIG_FILE)
self.async_see = async_see
async def async_connect(self):
"""Initialize connection to the router."""
# Test the router is accessible.
data = await self.connection.lan.get_hosts_list()
self.success_init = data is not None
# Hardcode the app description to avoid invalidating the authentication
# file at each new version.
# The version can be changed if we want the user to re-authorize HASS
# on her Freebox.
app_desc = {
'app_id': 'hass',
'app_name': 'Home Assistant',
'app_version': '0.65',
'device_name': socket.gethostname()
}
api_version = 'v1' # Use the lowest working version.
self.fbx = Freepybox(
app_desc=app_desc,
token_file=self.token_file,
api_version=api_version)
async def async_start(self, hass, interval):
"""Perform a first update and start polling at the given interval."""
async def async_scan_devices(self):
"""Scan for new devices and return a list with found device IDs."""
await self.async_update_info()
interval = max(interval, MIN_TIME_BETWEEN_SCANS)
async_track_time_interval(hass, self.async_update_info, interval)
return [device.id for device in self.last_results]
async def async_update_info(self, now=None):
"""Check the Freebox for devices."""
from aiofreepybox.exceptions import HttpRequestError
async def get_device_name(self, device):
"""Return the name of the given device or None if we don't know."""
name = next((
result.name for result in self.last_results
if result.id == device), None)
return name
_LOGGER.info('Scanning devices')
async def async_update_info(self):
"""Ensure the information from the Freebox router is up to date."""
_LOGGER.debug('Checking Devices')
await self.fbx.open(self.host, self.port)
try:
hosts = await self.fbx.lan.get_hosts_list()
except HttpRequestError:
_LOGGER.exception('Failed to scan devices')
else:
active_devices = [_build_device(device)
for device in hosts
if device['active']]
hosts = await self.connection.lan.get_hosts_list()
if active_devices:
await asyncio.wait([self.async_see(mac=d.id, host_name=d.name)
for d in active_devices])
last_results = [_build_device(device)
for device in hosts
if device['active']]
await self.fbx.close()
self.last_results = last_results

View File

@@ -23,10 +23,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_URL): cv.url,
})
HOSTS_PATH = "wlan_host_list.Hosts"
def get_scanner(hass, config):
"""Get a Huawei LTE router scanner."""
data = hass.data[DATA_KEY].get_data(config)
data.subscribe(HOSTS_PATH)
return HuaweiLteScanner(data)
@@ -43,7 +46,7 @@ class HuaweiLteScanner(DeviceScanner):
self.data.update()
self._hosts = {
x["MacAddress"]: x
for x in self.data["wlan_host_list.Hosts.Host"]
for x in self.data[HOSTS_PATH + ".Host"]
if x.get("MacAddress")
}
return list(self._hosts)

View File

@@ -200,7 +200,9 @@ class Icloud(DeviceScanner):
self._intervals = {}
for device in self.api.devices:
status = device.status(DEVICESTATUSSET)
_LOGGER.debug('Device Status is %s', status)
devicename = slugify(status['name'].replace(' ', '', 99))
_LOGGER.info('Adding icloud device: %s', devicename)
if devicename in self.devices:
_LOGGER.error('Multiple devices with name: %s', devicename)
continue
@@ -404,6 +406,7 @@ class Icloud(DeviceScanner):
continue
status = device.status(DEVICESTATUSSET)
_LOGGER.debug('Device Status is %s', status)
dev_id = status['name'].replace(' ', '', 99)
dev_id = slugify(dev_id)
attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get(
@@ -441,9 +444,9 @@ class Icloud(DeviceScanner):
return
self.api.authenticate()
for device in self.api.devices:
if devicename is None or device == self.devices[devicename]:
if str(device) == str(self.devices[devicename]):
_LOGGER.info("Playing Lost iPhone sound for %s", devicename)
device.play_sound()
def update_icloud(self, devicename=None):

View File

@@ -15,7 +15,7 @@ from homeassistant.const import (
CONF_HOST, CONF_PORT, CONF_PASSWORD, CONF_USERNAME
)
REQUIREMENTS = ['ndms2_client==0.0.5']
REQUIREMENTS = ['ndms2_client==0.0.6']
_LOGGER = logging.getLogger(__name__)

View File

@@ -15,7 +15,7 @@ from homeassistant.const import (
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_SSL,
CONF_DEVICES, CONF_EXCLUDE)
REQUIREMENTS = ['pynetgear==0.5.1']
REQUIREMENTS = ['pynetgear==0.5.2']
_LOGGER = logging.getLogger(__name__)

View File

@@ -12,20 +12,22 @@ import voluptuous as vol
from homeassistant.components.device_tracker import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_HOST, CONF_PORT, CONF_SSL, CONF_VERIFY_SSL,
CONF_PASSWORD, CONF_USERNAME)
CONF_PASSWORD, CONF_USERNAME, ATTR_BATTERY_LEVEL)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.util import slugify
REQUIREMENTS = ['pytraccar==0.1.2']
REQUIREMENTS = ['pytraccar==0.2.1']
_LOGGER = logging.getLogger(__name__)
ATTR_ADDRESS = 'address'
ATTR_CATEGORY = 'category'
ATTR_GEOFENCE = 'geofence'
ATTR_MOTION = 'motion'
ATTR_SPEED = 'speed'
ATTR_TRACKER = 'tracker'
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
@@ -78,13 +80,21 @@ class TraccarScanner:
await self._api.get_device_info()
for devicename in self._api.device_info:
device = self._api.device_info[devicename]
device_attributes = {
ATTR_ADDRESS: device['address'],
ATTR_GEOFENCE: device['geofence'],
ATTR_CATEGORY: device['category'],
ATTR_TRACKER: 'traccar'
}
attr = {}
attr[ATTR_TRACKER] = 'traccar'
if device.get('address') is not None:
attr[ATTR_ADDRESS] = device['address']
if device.get('geofence') is not None:
attr[ATTR_GEOFENCE] = device['geofence']
if device.get('category') is not None:
attr[ATTR_CATEGORY] = device['category']
if device.get('speed') is not None:
attr[ATTR_SPEED] = device['speed']
if device.get('battery') is not None:
attr[ATTR_BATTERY_LEVEL] = device['battery']
if device.get('motion') is not None:
attr[ATTR_MOTION] = device['motion']
await self._async_see(
dev_id=slugify(device['device_id']),
gps=(device['latitude'], device['longitude']),
attributes=device_attributes)
gps=(device.get('latitude'), device.get('longitude')),
attributes=attr)

View File

@@ -1,16 +1,16 @@
{
"config": {
"abort": {
"not_internet_accessible": "La vostra inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per rebre missatges de Dialogflow.",
"not_internet_accessible": "La inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per rebre missatges de Dialogflow.",
"one_instance_allowed": "Nom\u00e9s cal una sola inst\u00e0ncia."
},
"create_entry": {
"default": "Per enviar esdeveniments a Home Assistant, haureu de configurar [integraci\u00f3 webhook de Dialogflow]({dialogflow_url}). \n\n Ompliu la informaci\u00f3 seg\u00fcent: \n\n - URL: `{webhook_url}` \n - M\u00e8tode: POST \n - Tipus de contingut: application/json\n\nVegeu [la documentaci\u00f3]({docs_url}) per a m\u00e9s detalls."
"default": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar la [integraci\u00f3 webhook de Dialogflow]({dialogflow_url}). \n\n Completa la seg\u00fcent informaci\u00f3: \n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n- Tipus de contingut: application/json\n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls."
},
"step": {
"user": {
"description": "Esteu segur que voleu configurar Dialogflow?",
"title": "Configureu el Webhook de Dialogflow"
"description": "Est\u00e0s segur que vols configurar Dialogflow?",
"title": "Configuraci\u00f3 del Webhook de Dialogflow"
}
},
"title": "Dialogflow"

View File

@@ -1,7 +1,12 @@
{
"config": {
"abort": {
"not_internet_accessible": "A Home Assistant rendszerednek el\u00e9rhet\u0151nek kell lennie az internetr\u0151l a Dialogflow \u00fczenetek fogad\u00e1s\u00e1hoz.",
"one_instance_allowed": "Csak egyetlen konfigur\u00e1ci\u00f3 sz\u00fcks\u00e9ges."
},
"step": {
"user": {
"description": "Biztosan be szeretn\u00e9d \u00e1ll\u00edtani az Dialogflowt?",
"title": "Dialogflow Webhook be\u00e1ll\u00edt\u00e1sa"
}
},

View File

@@ -44,13 +44,19 @@ SERVICE_SABNZBD = 'sabnzbd'
SERVICE_SAMSUNG_PRINTER = 'samsung_printer'
SERVICE_HOMEKIT = 'homekit'
SERVICE_OCTOPRINT = 'octoprint'
SERVICE_FREEBOX = 'freebox'
SERVICE_IGD = 'igd'
SERVICE_DLNA_DMR = 'dlna_dmr'
CONFIG_ENTRY_HANDLERS = {
SERVICE_DAIKIN: 'daikin',
SERVICE_DECONZ: 'deconz',
'google_cast': 'cast',
SERVICE_HUE: 'hue',
SERVICE_TELLDUSLIVE: 'tellduslive',
SERVICE_IKEA_TRADFRI: 'tradfri',
'sonos': 'sonos',
SERVICE_IGD: 'upnp',
}
SERVICE_HANDLERS = {
@@ -62,12 +68,11 @@ SERVICE_HANDLERS = {
SERVICE_APPLE_TV: ('apple_tv', None),
SERVICE_WINK: ('wink', None),
SERVICE_XIAOMI_GW: ('xiaomi_aqara', None),
SERVICE_TELLDUSLIVE: ('tellduslive', None),
SERVICE_DAIKIN: ('daikin', None),
SERVICE_SABNZBD: ('sabnzbd', None),
SERVICE_SAMSUNG_PRINTER: ('sensor', 'syncthru'),
SERVICE_KONNECTED: ('konnected', None),
SERVICE_OCTOPRINT: ('octoprint', None),
SERVICE_FREEBOX: ('freebox', None),
'panasonic_viera': ('media_player', 'panasonic_viera'),
'plex_mediaserver': ('media_player', 'plex'),
'roku': ('media_player', 'roku'),
@@ -87,12 +92,11 @@ SERVICE_HANDLERS = {
'volumio': ('media_player', 'volumio'),
'lg_smart_device': ('media_player', 'lg_soundbar'),
'nanoleaf_aurora': ('light', 'nanoleaf_aurora'),
'freebox': ('device_tracker', 'freebox'),
}
OPTIONAL_SERVICE_HANDLERS = {
SERVICE_HOMEKIT: ('homekit_controller', None),
'dlna_dmr': ('media_player', 'dlna_dmr'),
SERVICE_DLNA_DMR: ('media_player', 'dlna_dmr'),
}
CONF_IGNORE = 'ignore'
@@ -134,7 +138,7 @@ async def async_setup(hass, config):
discovery_hash = json.dumps([service, info], sort_keys=True)
if discovery_hash in already_discovered:
logger.debug("Already discoverd service %s %s.", service, info)
logger.debug("Already discovered service %s %s.", service, info)
return
already_discovered.add(discovery_hash)
@@ -169,20 +173,23 @@ async def async_setup(hass, config):
async def scan_devices(now):
"""Scan for devices."""
results = await hass.async_add_job(_discover, netdisco)
try:
results = await hass.async_add_job(_discover, netdisco)
for result in results:
hass.async_create_task(new_service_found(*result))
for result in results:
hass.async_create_task(new_service_found(*result))
except OSError:
logger.error("Network is unreachable")
async_track_point_in_utc_time(hass, scan_devices,
dt_util.utcnow() + SCAN_INTERVAL)
async_track_point_in_utc_time(
hass, scan_devices, dt_util.utcnow() + SCAN_INTERVAL)
@callback
def schedule_first(event):
"""Schedule the first discovery when Home Assistant starts up."""
async_track_point_in_utc_time(hass, scan_devices, dt_util.utcnow())
# discovery local services
# Discovery for local services
if 'HASSIO' in os.environ:
hass.async_create_task(new_service_found(SERVICE_HASSIO, {}))

View File

@@ -104,7 +104,7 @@ def setup(hass, config):
return False
# Subscribe to doorbell or motion events
if events is not None:
if events:
doorstation.update_schedule(hass)
hass.data[DOMAIN] = doorstations

View File

@@ -26,7 +26,7 @@ EDP_REDY = 'edp_redy'
DATA_UPDATE_TOPIC = '{0}_data_update'.format(DOMAIN)
UPDATE_INTERVAL = 60
REQUIREMENTS = ['edp_redy==0.0.2']
REQUIREMENTS = ['edp_redy==0.0.3']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({

View File

@@ -21,7 +21,7 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.util.dt import utcnow
REQUIREMENTS = ['pyeight==0.0.9']
REQUIREMENTS = ['pyeight==0.1.0']
_LOGGER = logging.getLogger(__name__)

View File

@@ -11,12 +11,12 @@ import voluptuous as vol
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_TIMEOUT
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send
REQUIREMENTS = ['pyenvisalink==3.7']
REQUIREMENTS = ['pyenvisalink==3.8']
_LOGGER = logging.getLogger(__name__)
@@ -46,6 +46,7 @@ DEFAULT_KEEPALIVE = 60
DEFAULT_ZONEDUMP_INTERVAL = 30
DEFAULT_ZONETYPE = 'opening'
DEFAULT_PANIC = 'Police'
DEFAULT_TIMEOUT = 10
SIGNAL_ZONE_UPDATE = 'envisalink.zones_updated'
SIGNAL_PARTITION_UPDATE = 'envisalink.partition_updated'
@@ -65,7 +66,7 @@ CONFIG_SCHEMA = vol.Schema({
vol.All(cv.string, vol.In(['HONEYWELL', 'DSC'])),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASS): cv.string,
vol.Required(CONF_CODE): cv.string,
vol.Optional(CONF_CODE): cv.string,
vol.Optional(CONF_PANIC, default=DEFAULT_PANIC): cv.string,
vol.Optional(CONF_ZONES): {vol.Coerce(int): ZONE_SCHEMA},
vol.Optional(CONF_PARTITIONS): {vol.Coerce(int): PARTITION_SCHEMA},
@@ -77,9 +78,21 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(
CONF_ZONEDUMP_INTERVAL,
default=DEFAULT_ZONEDUMP_INTERVAL): vol.Coerce(int),
vol.Optional(
CONF_TIMEOUT,
default=DEFAULT_TIMEOUT): vol.Coerce(int),
}),
}, extra=vol.ALLOW_EXTRA)
SERVICE_CUSTOM_FUNCTION = 'invoke_custom_function'
ATTR_CUSTOM_FUNCTION = 'pgm'
ATTR_PARTITION = 'partition'
SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_CUSTOM_FUNCTION): cv.string,
vol.Required(ATTR_PARTITION): cv.string,
})
async def async_setup(hass, config):
"""Set up for Envisalink devices."""
@@ -99,11 +112,12 @@ async def async_setup(hass, config):
zone_dump = conf.get(CONF_ZONEDUMP_INTERVAL)
zones = conf.get(CONF_ZONES)
partitions = conf.get(CONF_PARTITIONS)
connection_timeout = conf.get(CONF_TIMEOUT)
sync_connect = asyncio.Future(loop=hass.loop)
controller = EnvisalinkAlarmPanel(
host, port, panel_type, version, user, password, zone_dump,
keep_alive, hass.loop)
keep_alive, hass.loop, connection_timeout)
hass.data[DATA_EVL] = controller
@callback
@@ -153,6 +167,12 @@ async def async_setup(hass, config):
_LOGGER.info("Shutting down Envisalink")
controller.stop()
async def handle_custom_function(call):
"""Handle custom/PGM service."""
custom_function = call.data.get(ATTR_CUSTOM_FUNCTION)
partition = call.data.get(ATTR_PARTITION)
controller.command_output(code, partition, custom_function)
controller.callback_zone_timer_dump = zones_updated_callback
controller.callback_zone_state_change = zones_updated_callback
controller.callback_partition_state_change = partition_updated_callback
@@ -190,6 +210,11 @@ async def async_setup(hass, config):
}, config
))
hass.services.async_register(DOMAIN,
SERVICE_CUSTOM_FUNCTION,
handle_custom_function,
schema=SERVICE_SCHEMA)
return True

View File

@@ -0,0 +1,15 @@
# Describes the format for available Envisalink services.
invoke_custom_function:
description: >
Allows users with DSC panels to trigger a PGM output (1-4).
Note that you need to specify the alarm panel's "code" parameter for this to work.
fields:
partition:
description: >
The alarm panel partition to trigger the PGM output on.
Typically this is just "1".
example: "1"
pgm:
description: The PGM number to trigger on the alarm panel. This will be 1-4.
example: "2"

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