Compare commits

..

432 Commits

Author SHA1 Message Date
Paulus Schoutsen
6902e522b9 Merge pull request #7335 from home-assistant/release-0-43-2
0.43.2
2017-04-27 12:42:25 -07:00
Paulus Schoutsen
6750e33525 Right fix for Python Open Z-Wave in Docker (#7337) 2017-04-27 09:28:40 -07:00
Paulus Schoutsen
e6a690558b Fix breaking SSL in test HTML5 (#7310) 2017-04-27 09:27:31 -07:00
Adam Mills
a3bc559fa3 Version bump for automatic (#7329) 2017-04-27 09:05:58 -07:00
Adam Mills
3a1072a158 Version bump of aioautomatic (#7300)
* Version bump of aioautomatic

* Update requirements_all.txt
2017-04-27 09:05:58 -07:00
Paulus Schoutsen
0841bf8529 Update frontend (#7324)
* Initial version of hassio panel

* Update frontend
2017-04-27 09:03:54 -07:00
Fabian Affolter
2b82c222b0 Upgrade python-telegram-bot to 5.3.1 (#7311) 2017-04-27 09:03:46 -07:00
Paulus Schoutsen
d966129fd8 Upgrade pytradfri to 1.1 (#7290) 2017-04-27 09:03:25 -07:00
Paulus Schoutsen
84752b3b13 Hassio api v3 (#7323)
* HassIO rest API v3

* fix content type

* fix lint

* Update comment

* fix content type

* change proxy handling

* fix handling

* fix register

* fix addons

* fix routing

* Update hassio to just proxy

* Fix tests

* Lint
2017-04-27 09:02:43 -07:00
Pascal Vizeli
3735c2e761 Fix HassIO bug with supervisor update & log (#7282) 2017-04-27 09:02:43 -07:00
Pascal Vizeli
9fc89ba744 WIP: HassIO allow to access to container logs. (#7271)
* HassIO allow to access to container logs.

* Add unittest & make a fixture for env

* Add unittest to check if no env exists

* Fix lint
2017-04-27 09:02:43 -07:00
Paulus Schoutsen
f7603a421f Version bump to 0.43.2 2017-04-27 09:02:08 -07:00
Paulus Schoutsen
758aed07e8 Merge pull request #7288 from home-assistant/release-0-43-1
0.43.1
2017-04-24 21:02:39 -07:00
Paulus Schoutsen
166bcc0687 Recorder: Check for ENTITY_ID key that contains None value (#7287) 2017-04-24 20:51:14 -07:00
Klaas Hoekema
41a803a7be Work around bad content-type in Hook api response (#7267) 2017-04-24 20:36:16 -07:00
Martin Hjelmare
1b55dbeb44 Fix telegram webhooks (#7236)
* Always register the view if a webhook exists.
* Return True if platform is set up succesfully, False otherwise.
* Remove the webhook when home assistant stops. Webhooks and long
  polling are mutually excklusive. If a webhook is left after home
  assistant is stopped, a polling telegram bot is unable to be set up,
  on next start of home assistant.
2017-04-24 20:36:08 -07:00
Greg Dowling
6c594e20f6 Workround for wemo subscription bug. (#7245) 2017-04-24 20:35:59 -07:00
Fabian Affolter
c8404cb299 Upgrade paho-mqtt to 1.2.3 (#7214) 2017-04-24 20:35:51 -07:00
John Arild Berentsen
79c6467797 Zwave cover workaround for graber shades. (#7204)
* wierd pylint complaint

* Workaround for Graber csz1 shades

* logging

* Try direct

* Try direct

* Use workaround

* Review changes and tests

* test

* reset test

* Use Bright and Dim also as open and close is
2017-04-24 20:35:27 -07:00
Paulus Schoutsen
e01c36c906 Version bump to 0.43.1 2017-04-24 20:34:56 -07:00
Paulus Schoutsen
8be2ac70ec Merge pull request #7197 from home-assistant/release-0-43
0.43
2017-04-22 00:33:01 -07:00
Paulus Schoutsen
477ebd99b4 Version bump to 0.43 2017-04-22 00:32:27 -07:00
Martin Hjelmare
093c7f0e44 Fix tradfri lights (#7212)
* Remove leftover use of slugify

* The IKEA manufacturer key is now exactly as found in device info.

* Fix bitwise addition of supported features
2017-04-22 00:31:44 -07:00
Paulus Schoutsen
80e9e9bfda tradfri: Improve color temp support detection (#7211) 2017-04-21 23:33:25 -07:00
Henrik Nicolaisen
dafbdbd2d0 Issue 6749 updated pylgtv to 0.1.6 to fix thread leak in asyncio loop (#7199)
* updated pylgtv module to fix problems with timeouts

* - update pylgtv to 0.1.6
- handle new TimeoutError exception from pylgtv

* used full name for exception handling of concurrent.futures._base.TimeoutError

* the exception handling should now follow the rules

* float typecasting should not be necessary

* use asyncio for TimeoutError it’s an alias for concurrent.futures.TimeoutError
2017-04-21 20:24:33 -07:00
Sean Dague
b641f6863c Fix arwn platform to update hass state when events are received (#7202)
The arwn platform was refactored to be asyncio friendly, however in
doing so one thing was missed which was explicitly telling hass when
something interesting has happened. This led to the very interesting
to debug issue that the state cards were all out of date, even though
the graphs were not.
2017-04-21 20:22:49 -07:00
Nikolas Beutler
07fcf22aeb Update ios.py (#7160)
* Update ios.py

as discussed. the part: 
       if battery_state == ios.ATTR_BATTERY_STATE_FULL:
            returning_icon_level = DEFAULT_ICON_LEVEL
kinda screws up the charging icon.

i might just miss a logical solution for that though.
let me know what you think. it might not be beautiful but i think its an overall improve over the current "double battery" solution

* Update ios.py

chound fix and full_battery_charge fix

* Update ios.py

removed new line

* Update ios.py

* Update ios.py

* Update ios.py

* Update ios.py

* Update ios.py

* Update ios.py

* merged request from robbie

* Update ios.py

* Update ios.py

* Update ios.py
2017-04-21 20:18:31 -07:00
Anders Melchiorsen
2d57c6a1c7 Support xy_color with LIFX lights (#7208) 2017-04-21 20:18:31 -07:00
Pascal Vizeli
5c737cfa6e HassIO API v2 (#7201)
Add an optional extended description…
2017-04-21 12:21:55 +02:00
Anders Melchiorsen
e3f682c7d3 LIFX light effects (#7145)
* Refactor into find_hsbk

This will be useful for new methods that also have to find passed in colors.

* Add AwaitAioLIFX

This encapsulates the callback and Event that aiolifx needs and thus avoids an
explosion of those when new calls are added.

The refresh_state is now generally useful, so move it into its own method.

* Initial effects support for LIFX

These effects are useful as notifications. They mimic the breathe and pulse
effects from the LIFX HTTP API:

    https://api.developer.lifx.com/docs/breathe-effect
    https://api.developer.lifx.com/docs/pulse-effect

However, this implementation runs locally with the LIFX LAN protocol.

* Saturate LIFX no color value

Now the color is "full saturation, no brightness". This avoids a lot of
temporary white when fading from the "no color" value and into a real color.

* Organize LIFX effects in classes

This is to move the setup/restore away from the actual effect, making it quite
simple to add additional effects.

* Stop running LIFX effects on conflicting service calls

Turning the light on/off or starting a new effect will now stop the running
effect.

* Present default LIFX effects as light.turn_on effects

This makes the effects (with default parameters) easily accessible from
the UI.

* Add LIFX colorloop effect

This cycles the HSV colors, so that is added as an internal way to set a
color.

* Move lifx to its own package and split effects into a separate file

* Always show LIFX light name in logs

The name is actually the easiest way to identify a bulb so just using it
as a fallback was a bit odd.

* Compact effect getter

* Always use full brightness for random flash color

This is a stopgap. When a bit more infrastructure is in place, the intention
is to turn the current hue some degrees. This will guarantee a flash color
that is both unlike the current color and unlike white.

* Clear effects concurrently

We have to wait for the bulbs, so let us wait for all of them at once.

* Add lifx_effect_stop

The colorloop effect is most impressive if run on many lights. Testing
this has revealed the need for an easy way to stop effects on all lights
and return to the initial state of each bulb. This new call does just that.

Calling turn_on/turn_off could also stop the effect but that would not
restore the initial state.

* Always calculate the initial effect color

To fade nicely from power off, the breathe effect needs to keep an
unchanging hue. So give up on using a static start color and just find the
correct hue from the target color.

The colorloop effect can start from anything but we use a random color
just to keep things a little interesting during power on.

* Fix lint

* Update .coveragerc
2017-04-20 22:46:29 -07:00
Paulus Schoutsen
dbb0525311 Merge branch 'master' into dev 2017-04-20 22:39:09 -07:00
Pascal Vizeli
f641287aa2 Add HassIO to discovery component (#7195)
* Add HassIO to autodiscovery

* Fix tests

* fix tests

* fix test v2

* fix mock test

* call
2017-04-20 18:45:15 -07:00
LvivEchoes
bbeb64eb24 Add support of input registers while querying modbus sensor. (#7082)
* Add support of input registers while querying modbus sensor.

* Changed config option. Refactoring.
2017-04-20 21:28:49 -04:00
Fabian Affolter
eb2e5e5b9d Upgrade py-cpuinfo to 3.2.0 (#7190) 2017-04-20 12:07:56 +02:00
mountainsandcode
920d298c7e mvglive bug fixes and improvements (#6953)
* Refactored mvglive.py

This pull requests builds on the first work with the mvglive sensor:
- Refactoring the code so that multiple sensors for departures can be added
- Rewrites the transport mode restrictions ("products") to be more modular
- Fixes bugs, such as missing implementation of line restriction
- Other improvements, such as including data attribution

* Further improvements to mvglive sensor

- The API returns the property 'direction', which can be used to filter U-Bahn trains by direction without having to enter all final destinations
- The sensor icon now corresponds to the mode of transport of the next departure

* UBahnDirection refactored

U-Bahn SEV (bus replacement services) have unexpected direction values, fixed resulting bug and hound issues
2017-04-20 00:11:55 -07:00
Pierre Ståhl
93820d5124 Do not request artwork if not available (#7189)
This should fix the "dancing media player" issue where the media player
requests artwork when it's not really available, making the UI "dance".
2017-04-20 00:10:06 -07:00
Pierre Ståhl
2e11d49af3 Fix auto discovery for Apple TV (#7188) 2017-04-20 00:09:27 -07:00
Mitko Masarliev
2d5ab520ef Fix for errors on missing preview on LG webos TV (#6755)
* fix for missing image preview on LG webos TV

* fix to use largeIcon if it start with http
2017-04-20 00:08:53 -07:00
Sören Oldag
0c14c66fbc Added light.pwm component. (#7009)
* Added light.pwm component.

* Renamed pwm platform to rpi_gpio_pwm.

* Update requirements_all.txt
2017-04-19 23:32:20 -07:00
Fabian Affolter
b1621d4175 Add ping binary sensor (#7052)
* Add ping binary sensor

* Fix typo and lint issues

* Use SCAN_INTERVAL
2017-04-19 23:15:26 -07:00
happyleavesaoc
1860b6c521 opensky sensor (#7061)
* opensky sensor

* address opensky review comments

* update opensky distance calc
2017-04-19 22:56:20 -07:00
Gianluca Barbaro
f59b3da5fe JSON MQTT Device tracker (#7055)
* ready for PR

* minor fix

* another minor fix

* new platform mqtt_json

* using ATTR constants

* voluptuous check on JSON payload

* voluptuous check on JSON payload
2017-04-19 22:53:07 -07:00
happyleavesaoc
e020d5114a spotify media player (#6980)
* spotify media player

* fix refresh token

* spotify improvements

* add checks for idle

* import STATE_IDLE

* support more media_ids, limit updates

* move spotify device update

* Remove schedule_update_ha_state because should_poll is true
2017-04-19 22:45:12 -07:00
Paulus Schoutsen
ce51866bd2 Update frontend 2017-04-19 22:03:48 -07:00
Charles Blonde
931fce8239 Add Bose soundtouch discovery support and upgrade libsoundtouch library (#7005)
* Add Bose soundtouch discovery support and upgrade libsoundtouch library

* Remove DEVICE global variable

* Update netdisco to lastest version
2017-04-19 21:52:37 -07:00
Paulus Schoutsen
76d2154820 Fix wemo discovery (#7183)
* Fix wemo discovery

* Fix key
2017-04-19 21:25:45 -07:00
Henrik Nicolaisen
b985e4ef0b updated pylgtv module to fix problems with timeouts (#7184) 2017-04-19 19:36:11 -04:00
Gianluca Barbaro
632256fae2 Mqtt camera test (#7175)
* mock mqtt

* minor fix

* minor fix

* minor fix
2017-04-19 09:26:44 -07:00
Paulus Schoutsen
9b43b39370 Update frontend 2017-04-19 09:24:02 -07:00
Paulus Schoutsen
1a635fede3 Tweak Tradfri (#7172) 2017-04-19 09:15:39 -07:00
Paulus Schoutsen
90baa2ce4d Add history to component priority list (#7173) 2017-04-19 09:15:18 -07:00
Sytone
2f4b2ddc0a Add condition for API failure (#7181)
* Add condition for API failure

If you are not running the latest ve3rsion of ZM this will cause exceptions to fire. This fix handles a response from ZM but a non successful attempt. 

This resolves the issue https://github.com/home-assistant/home-assistant/issues/7178

* Fixed houndci-bot issues
2017-04-19 09:08:48 -07:00
Fabian Affolter
921760f8c1 Upgrade mypy to 0.501 (was renamed from mypy-lang) (#7117) 2017-04-19 14:34:23 +02:00
Fabian Affolter
8ba41563c9 Disable invalid-sequence-index (#7177) 2017-04-19 14:09:00 +02:00
Fabian Affolter
a41d0aced7 Supress trackback and upgrade PyMata to 2.14 (#7176) 2017-04-19 12:48:15 +02:00
Alessandro Mogavero
5179832f6f Added new services to platform kodi (#6426)
* added new service

* fixed basic test in kodi platform

* Added new method async_get_albums

* Added new methods in module kodi

* added method find_song in kodi module

* method add_song_to_playlist made
async

* Added media type to method async_play_media

* added methods async_clear_playlist
and play_song

* methods play_song and find_song
made async

* added new service play_song

* Improved kodi._find
now it find for whole words only

* added possibility to specify artist in
kodi.async_find_artist

* added kodi.async_find_album

* added new optional input to play_song service

* In async_play_song added handling of no song found

* default artist value changed to ''

* async_add_song_to_playlist now can also
search for musinc

* added service add_song_to_playlist

* Added new service add_album_to_playlist

* added services to switch shuffle mode

* added service add_all_albums_to_playlist

* handled error in async_unset_shuffle
and async_set_shuffle

* Added abstract methods to media_player

* _server substituted with server property

* style made consistent with requirements

* Fixed issue with pylint

* Services moved to kodi platform

* service play_song removed

* removed service unset_shuffle

* all add services merged into one

* removed service get_artists

* added kodi_ to service names

* Fixed some style issues

* Removed changes in media_player __init__

* Implemented requested changes

* Fixed pylint problem
2017-04-18 23:19:27 -07:00
Paulus Schoutsen
ce9bb0e84c Upgrade netdisco (#7171) 2017-04-18 20:58:25 -07:00
Johan Bloemberg
e5feeec7a4 Value of 0 should not be considered unknown. (#7139)
* Value of 0 should not be considered `unknown`.

* Reflect disconnect state in entity states.

* Due to adding unknown state on disconnect, the amount of reconnects can sometimes be more. Test for at least 2 reconnect attempts.
2017-04-18 20:24:44 -07:00
Paulus Schoutsen
4becfb66e3 Upgrade pytradfri to 1.0 (#7163) 2017-04-18 20:11:18 -07:00
Mitesh Patel
e4bbbe20dd Fix id zone mismatch (#7165)
* Fixes issue with id mismatch when multiple devices are connected to the lutron bridge

* Updates labels

* removes no longer needed config values.

* removes no longer needed imports
2017-04-19 00:24:58 +02:00
Erik Eriksson
1e758ed030 Keep track of already added players (#7149) 2017-04-19 00:20:52 +02:00
Michaël Arnauts
d007269ecc Update neato.py (#7166)
Fix leftover copy/paste error in comment of neato.py
2017-04-18 22:03:06 +02:00
Greg Dowling
bbad15f853 Add subscription update for Wemo switches, fix bug in Insight switches, fix wemo motion bug, fix wemo discovery (#7135)
* Fix wemo discovery.

* Bump wemo version, add subscription_update for basic switch, fix bug with turning insight switches off.

* Fix missed callback change for wemo motion.

* Regress netdisco changes.
2017-04-18 09:11:08 -07:00
Thibault Cohen
de71fee0a0 Fix #7026 adding a new wol parameter (#7144) 2017-04-18 09:09:06 -07:00
Fabian Affolter
c7a11277ac myStrom WiFi bulbs (#7161)
* Add initial support for myStrom WiFi bulbs

* Upgrade python-mystrom to 0.3.8

* Add myStrom light

* Fix lint issue
2017-04-18 09:03:56 -07:00
Paulus Schoutsen
5574686d74 Disable MQTT camera test (#7164) 2017-04-18 08:55:51 -07:00
Martin Hjelmare
15d8f8b827 Add support for tradfri color temp (#7153)
* Add support for tradfri color temp

* Only return rgb_color if supported features indicate support.
* Return color_temp if supported features indicate support and dict
  with allowed color temperatures exist for manufacturer.
* Add manufacturer specific supported features, with default.
* Color temperature is set from a dict of allowed pairs per
  manufacturer, where kelvin is mapped to hex_color. When a user sets a
  color temperature, the closest matching temperature is selected and
  set.

* Clean up
2017-04-18 08:26:59 -07:00
Greg Dowling
1925748f61 Add vera power meter. (#7134)
* Add vera power meter.

* Use W for power.
2017-04-18 12:01:23 +02:00
Mike Megally
226066eafd exposed content_type in rest_command (#7101)
* exposed content_type in rest_command, which allows for manually specifying the content_type for more-strict api endpoints

* fixed up column length

Length was 86 chars, and it needed to be 79

* double import of HTTP_HEADER_CONTENT_TYPE

Removed the accidental double-import of HTTP_HEADER_CONTENT_TYPE

* moved rest_command-specific config value into component

* if no content_type, default to None

* unit test

* newline

* unused CONTENT_TYPE_TEXT_PLAIN

* removed the http-agnostic abstraction hass provided in favor of aiohttps hdrs constant
2017-04-18 11:50:43 +02:00
Kevin
43799b8fee small fix for random effect in order to use the whole rgb range. So 255 is not excluded anymore. (#7156) 2017-04-18 11:46:18 +02:00
Robbie Trencheny
9c0171ec5e Track device last identify time 2017-04-17 23:31:50 -07:00
Robbie Trencheny
b7141901f6 Change iOS sensor unique ID to use the device ID 2017-04-17 22:58:04 -07:00
Robbie Trencheny
919bb08d02 Fix iOS icon calculation to return mdi:battery for levels above 95 2017-04-17 22:51:06 -07:00
Pascal Vizeli
cec39077ba Fix HassIO timeout bug (#7155)
* Fix HassIO timeout bug

* fix lint

* Add long polling timeout to stop event
2017-04-18 00:25:50 +02:00
thecynic
9ed4ed2e47 lutron: fix typo that prevented callback registration (#7148) 2017-04-17 23:04:44 +02:00
Anders Melchiorsen
d4b05a6a85 Fix LIFX lights with disappearing names (#7119)
* Cache the name of LIFX lights

After #7031 the LIFX device will change during an unregister/register
transition. This has the user-visible effect of the new device missing
a friendly name until the next poll.

We now cache the name internally and it will then transfer to the new
device when it registers.

* Allow LIFX logging even without an available device

This will allow us to set the device to None when it unregisters.

* Calculate LIFX availability from the existence of a device

This has become possible because the device is no longer needed
to provide the name of the light when it is unavailable.

We just have to forget the device when it unregisters.
2017-04-16 17:40:22 -07:00
Anders Melchiorsen
103377bdb0 Add LIFX Cloud scene support (#7124)
This uses the LIFX HTTP API to list and activate the scenes that are
stored in the LIFX cloud by the native LIFX smartphone apps.
2017-04-16 16:40:12 -07:00
Anders Melchiorsen
5fa8037231 Always return True/False from is_state and is_state_attr (#7138)
* Always return True/False from is_state and is_state_attr

These functions are documented to always return True/False but the
short-circuit evaluation would return None if the entity_id did not exist.

* Reword into a single statement
2017-04-16 16:36:15 -07:00
Paulus Schoutsen
a2d268a061 Merge pull request #7142 from home-assistant/release-0-42-4
0.42.4
2017-04-16 16:02:10 -07:00
Adam Mills
37f959eb02 Add debug logging to automation initial state (#7068) 2017-04-16 15:45:40 -07:00
Robbie Trencheny
1ce2b6357a Replace rollershutter with cover in demo (#7140) 2017-04-16 15:27:03 -07:00
Adam Mills
527223b992 Fix for zwave RGB setting (#7137) 2017-04-16 15:07:11 -07:00
Adam Mills
409fd62a7c Fix for zwave RGB setting (#7137) 2017-04-16 15:06:59 -07:00
Robbie Trencheny
fadd33bcb2 Make version number optional and a string to fix identify issue introduced in iOS 1.0.1 (#7141) 2017-04-16 15:00:08 -07:00
Fabian Affolter
904b017552 Upgrade aiohttp to 2.0.7 (#7106) 2017-04-16 15:00:08 -07:00
Martin Hjelmare
1efa6eaf0f Fix mysensors callback (#7057)
* Fix mysensors callback

* All messages was not triggering proper updates. Fix by checking all
  child value types each update.

* Upgrade mysensors dep

* Fix pickle persistence when upgrading.
2017-04-16 15:00:08 -07:00
Paulus Schoutsen
9744ec584a Version bump to 0.42.4 2017-04-16 14:59:15 -07:00
Robbie Trencheny
58dfc1d1b1 Make version number optional and a string to fix identify issue introduced in iOS 1.0.1 (#7141) 2017-04-16 14:53:03 -07:00
Paulus Schoutsen
951af6c76d Make Tradfri discoverable (#7128)
* Make Tradfri discoverable

* Fix lint errors

* Fix bugs and clean up calls to light_control

* Add more color util tests

* Add coap client to dockerfile
2017-04-16 14:37:39 -07:00
Patrik
75242e67a7 IKEA Tradfri Gateway: added support for RGB (#7115)
* After rebase and all fixes

* Added color_rgb_to_hex to util.color

* Added test_color_rgb_to_hex

* Changed reference to color_rgb_to_hex

* Bumped to pytradfri 0.5, having support for retry

* Bumped to pytradfri 0.5, having support for retry

* Bumped to pytradfri 0.5, having support for retry

* Bumped to pytradfri 0.5, having support for retry

* Rolled back to 0.4

* Rolled back to 0.4
2017-04-16 11:35:52 -07:00
Paulus Schoutsen
a1208261a8 Load zwave panel (#7127)
* Load Z-Wave panel when component loads

* Update frontend

* Fix tests
2017-04-16 11:10:55 -07:00
Martin Hjelmare
3528705afd Bump version to 0.43.0.dev0 (#7132) 2017-04-16 13:27:25 +02:00
Gianluca Barbaro
7d76186798 Mqtt camera (#7092)
* first commit

* minor fixes

* minor fix

* async_camera_image + test

* minor fix

* async calls
2017-04-16 11:06:57 +02:00
Marcelo Moreira de Mello
9249b6bc33 Upgraded Amcrest module to 1.1.9 to support new firmware versions: (#7130)
- V2.400.AC01.15.R.20170328
    - V2.420.AC00.17.R.20170322
2017-04-16 09:05:15 +02:00
Anders Melchiorsen
6cbe28a9cd Send stderr of ping tracker to devnull (#7096)
When pinging an inaccessible device, OS errors like

    ping: sendto: No route to host

can occur. For a ping tracker this is not an error but rather a normal
situation. Thus, it makes sense to hide the error.
2017-04-15 19:00:01 -07:00
Fabian Affolter
f7b6f8e8fb Upgrade chardet to 3.0.2 (#7112) 2017-04-15 18:32:06 -07:00
Adam Mills
35de3a1dc4 Use third-party lib aioautomatic for automatic (#7126) 2017-04-15 18:11:36 -07:00
Robbie Trencheny
815422a886 Merge pull request #7109 from home-assistant/allow-extra-in-zone-config
Allow extra keys in zone config
2017-04-15 16:39:29 -07:00
Fabian Affolter
c43a3efabd Remove globally disabled pylint issue and update docstrings (#7111) 2017-04-15 00:32:04 +02:00
Fabian Affolter
b0ffc55cfa Upgrade speedtest-cli to 1.0.4 (#7105) 2017-04-15 00:28:04 +02:00
Fabian Affolter
cce372ff66 Update file header, add const for defaults, and update log messages (#7110) 2017-04-15 00:26:04 +02:00
Fabian Affolter
5ffda53805 Upgrade aiohttp to 2.0.7 (#7106) 2017-04-15 00:18:39 +02:00
Robbie Trencheny
60f7a1947f Allow extra keys in zone config 2017-04-14 14:31:10 -07:00
Adam Mills
0ca80cc27e No product ids configured should fetch all ids (#7091) 2017-04-14 19:09:21 +02:00
Adam Mills
46352f6de9 Uber version bump (#7100) 2017-04-14 19:08:28 +02:00
Robbie Trencheny
7e3e742938 Merge pull request #7094 from robbiet480/zwave-manufacturer-product-names
Break Z-Wave product name up into manufacturer name and product name
2017-04-13 23:10:40 -07:00
Robbie Trencheny
e5756ba41d Break product name up into manufacturer name and product name 2017-04-13 22:48:59 -07:00
Andrey
b6ee2332f4 Better thread safety in zwave node_entity (#7087)
* Better thread safety

* Update node_entity.py
2017-04-13 23:45:27 +03:00
Patrik
c267326891 Added initial support for IKEA Tradfri Gateway (#7074)
* Added initial support for IKEA Tradfri Gateway

* Pinned requirement

* Fixed lint-errors

* Added file to .coveragerc

* Trying to fix commit

* Fixed requirements_all again

* Minor reorder of code

* Minor reorder of code

* Made the changes suggested by @balloob

* Made the changes suggested by @balloob and removed debug

* Update tradfri.py
2017-04-13 10:04:42 -07:00
John Arild Berentsen
38ad5714cd Add support fo map data from Neato (#6939)
* Responsiveness

* Delay was not needed as commands does not return until done.

* Add support for cleaning maps and cleaning data

* Hound

* Docstring

* Update requirements

* Review changes

* External lib now returns the raw data.

* debug

* Sensor did not refresh

* Error handling

* Issue warning on connection error

* Update requirements

* Review changes
2017-04-13 07:41:25 -07:00
Colin O'Dell
01c7616147 Bump qnapstats library version to 0.2.4 (#7085) 2017-04-13 07:39:36 -07:00
Gianluca Barbaro
fa65783f39 MQTT: Managing binary payloads (#6976)
* Managing binary payloads

Hello,
background: I wrote a HA camera component that gets the image from a binary payload. I'm testing it with Zanzito (https://play.google.com/store/apps/details?id=it.barbaro.zanzito) and it works apparently well: it gets the image and correctly displays it in the front-end.
But I had to make the changes I'm proposing here: the message was being blocked because the utf-8 decoding failed.
As far as I know, the utf-8 encoding is required for the topic, not for the payload. What I did here was try the utf-8 decoding, but even if unsuccessful, it dispatches the message anyway.
Is there anything else I'm missing?
thanks
Gianluca

* Update __init__.py

* Update __init__.py

* Update __init__.py

* git test - ignore

* Should work

* minor fixes

* updated mqtt/services.yaml

* added two tests, modified threaded subscribe

* removing polymer

* requested changes

* requested changes - minor fix

* security wrap around payload_file_path

* services.yaml updated

* removed file publishing

* minor fix
2017-04-13 07:38:09 -07:00
Thibault Cohen
9a9342ec3f Fix account balance in fido sensor (#7077) 2017-04-13 07:42:48 +02:00
Robbie Trencheny
34cb02177d Bump braviarc version to 0.3.7 (#7078) 2017-04-12 18:22:23 -07:00
Robbie Trencheny
5ba4033651 Merge pull request #6869 from JesseWebDotCom/dev
Exposed more attributes, enabled play_media tv show or season episodes
2017-04-12 15:17:53 -07:00
Andrey
5e18c997f7 Add product_name attribute to zwave nodes. (#7071) 2017-04-12 21:12:37 +03:00
John Arild Berentsen
d63028e44a Add communication data attributes to Zwave node_entity (#6813)
* Add quality attribute to node

* Move quality to node_entity

* adjustments

* Line lenght

* flake8

* Cleanup and add each entry from getNodeStatistics as attribute

* resolve conflict

* Move NETWORK to hass.data

* Test Done

* Cleanup from review

* Resolve conflicts

* Review changes

* Long lines....

* blank line

* import-error-disable

* Update tests Part1... Again

* Hound

* Argh!

* Argh!

* YABBADABBADOOOOOOO!

* Enhance tests

* hound

* Resolve

* resolve tests...
2017-04-12 19:09:29 +02:00
hawk259
f68542ba0d Adding AlarmDecoder platform (#6900)
* Added AlarmDecoder platform

* remove try/catch for generic execption

* Changes for @pvizeli, thanks for the review!

Removed _ prefix from normal function variables
Removed _hass as it will be set via .hass for us
Broke out the three config (socket, serial, usb) and use vol.Any
Added support for USB I think, don't have device, but should work
Removed components dictionary, was form old group all code that didn't work

* Fix hass string handling
2017-04-12 11:35:35 +02:00
Pascal Vizeli
9d20a17642 Lutron. Bugfix callback registration. (#7042)
* Lutron. Bugfix callback registration.

* Change handling to event
2017-04-12 09:52:01 +02:00
Pascal Vizeli
e026717239 Fix handling with register callbacks on added_to_hass (#7067) 2017-04-12 09:51:19 +02:00
Fabian Affolter
f06cff35ff Upgrade paho-mqtt to 1.2.2 (#7066) 2017-04-12 09:48:21 +02:00
sander76
7cb8f49d62 Telegram bot component (incl. webhook and polling platform) (#6913)
* first commit.

* removed pointless string statement

* manually removed  # homeassistant.components.telegram_webhooks from requirements_all.txt

* deleted obsolete file.

* coveragerc abc
2017-04-11 21:10:56 -07:00
Paulus Schoutsen
edf500e66b Upgrade netdisco to 1.0.0rc2 (#7008)
* Upgrade netdisco to 1.0.0rc2

* fix tests
2017-04-11 20:10:02 -07:00
Paulus Schoutsen
72a01b8a90 Speed up aiohttp (#7064) 2017-04-11 19:58:54 -07:00
micw
3c35d5ea58 Fix/slugify with german umlaut ss (#7029)
* more tests for slugify

* Fix german umlauts in slugify

* Update __init__.py
2017-04-11 19:51:07 -07:00
Paulus Schoutsen
4d9e681fc1 Constrain chardet to 2.3 (#7063) 2017-04-11 19:50:43 -07:00
Martin Hjelmare
4e388666b2 Fix mysensors callback (#7057)
* Fix mysensors callback

* All messages was not triggering proper updates. Fix by checking all
  child value types each update.

* Upgrade mysensors dep

* Fix pickle persistence when upgrading.
2017-04-11 19:17:09 -07:00
johanpalmqvist
ed012014bc Add MaryTTS platform (#6988)
* Add MaryTTS platform

* Fix lint error

* Doc link, config and formatting fixes

* Remove stuff not needed with aiohttp2

* Get rid of unnecessary else statement
2017-04-11 22:52:44 +02:00
Paulus Schoutsen
bf6c4604f4 Merge pull request #7050 from home-assistant/release-0-42-3
0.42.3
2017-04-11 09:30:04 -07:00
Pascal Vizeli
c91cf66dec Bugfix slider (#7047)
* Bugfix slider

* Update input_slider.py

* Update input_slider.py
2017-04-11 09:24:25 -07:00
Pascal Vizeli
11125864c6 Bugfix slider (#7047)
* Bugfix slider

* Update input_slider.py

* Update input_slider.py
2017-04-11 09:23:41 -07:00
Pascal Vizeli
7377ce2640 Bugfix wait on start event (#7013)
* Bugfix wait on start event

* Paulus sugestion

* Change handling with stop_track_task

* Add new unittests

* Update test_core.py
2017-04-11 09:15:31 -07:00
Anders Melchiorsen
0013139591 Plug file leak on LIFX unregister (#7031)
* Plug file leak on LIFX unregister

The aiolifx 0.4.4 release closes its socket when the unregister callback is
called. This plugs a file descriptor leak but also means that we must be
careful to not use the device after it goes unavailable.

Also, when a light reappears, it has a new device that must be used.

* Do not test self.available in service calls

The core will learn to handle that.
2017-04-11 09:15:30 -07:00
Fabian Affolter
e3c2d27f4a Fix US states check (fixes #7015) (#7017) 2017-04-11 09:15:30 -07:00
Xorso
f00d721293 Bump pyalarmdotcom to support new version of aiohttp (#7021)
Add an optional extended description…
2017-04-11 09:15:30 -07:00
Paulus Schoutsen
b295451d46 Fix two more instances of JSON parsing synology (#7014)
Add an optional extended description…
2017-04-11 09:15:30 -07:00
Paulus Schoutsen
7a3df037ba Fix Synology camera content type (#7010) 2017-04-11 09:15:30 -07:00
Paulus Schoutsen
a60e8b16c0 Version bump to 0.42.3 2017-04-11 09:14:37 -07:00
Pascal Vizeli
b52cabf2c0 Bugfix wait on start event (#7013)
* Bugfix wait on start event

* Paulus sugestion

* Change handling with stop_track_task

* Add new unittests

* Update test_core.py
2017-04-11 09:09:31 -07:00
Fabian Affolter
cc459e25cc Remove configuration sample (#7048) 2017-04-11 09:05:27 -07:00
Pascal Vizeli
d7ca9e7a66 Make core to look avilable state of device on servicecall (#7045) 2017-04-11 08:59:46 -07:00
Anders Melchiorsen
f099aee69a Plug file leak on LIFX unregister (#7031)
* Plug file leak on LIFX unregister

The aiolifx 0.4.4 release closes its socket when the unregister callback is
called. This plugs a file descriptor leak but also means that we must be
careful to not use the device after it goes unavailable.

Also, when a light reappears, it has a new device that must be used.

* Do not test self.available in service calls

The core will learn to handle that.
2017-04-11 08:58:51 -07:00
David Straub
07bb64815d Missing line name restriction added (fixes #7039) (#7040) 2017-04-11 13:55:42 +02:00
Fabian Affolter
2cfdb44df6 Upgrade sendgrid to 4.0.0 (#7038) 2017-04-11 13:52:47 +02:00
Fabian Affolter
2748bc4165 Upgrade psutil to 5.2.2 (#7037) 2017-04-11 13:52:34 +02:00
micw
f76a4b2806 Feature/min max improvements (#6786)
* Fix #6783, remove a test that makes no sense anymore

* Fix #6784

* Fix typo in docstring

* Fix handling of known->unknown state, extended test, fix lint errors

* Better handling of mismatch in unit of measurement.

Set state to "unkown" and unit of measurement to "ERR" if unit of measurement differs between aggregatet states.
Add entity_id to logged warning.

* Make icon configurable

* Fix typo

* Fix lint

* Fix lint

* Fix lint

* Add option to set entity_id on min_max sensor

* Fix lint logging-not-lazy

* Revert "Add option to set entity_id on min_max sensor"

This reverts commit 4685f26647.

* Revert "Make icon configurable"

This reverts commit fe45aec82d.

* Fixes
2017-04-11 13:52:12 +02:00
pezinek
197db6bded Google TTS can't read percent sign (#6971) (#7030) 2017-04-11 10:27:45 +02:00
happyleavesaoc
aa3ccf16ca update fedex (#7034) 2017-04-11 08:10:31 +02:00
happyleavesaoc
aa91351ff0 update usps version (#7035) 2017-04-11 08:10:07 +02:00
happyleavesaoc
32da163421 bump ups version (#7033) 2017-04-11 08:09:39 +02:00
Adam Mills
ee988dc884 Additional ZWave coverage (#7024)
* Additional ZWave coverage

* setup_platform tests
2017-04-10 16:11:39 -04:00
micw
05eb73a0e3 more tests for slugify (#7027) 2017-04-10 22:51:46 +03:00
Fabian Affolter
89e8e1a4c7 Replace 'vendor_id' with 'arch' (fixes #7003) (#7023) 2017-04-10 18:43:40 +02:00
Paulus Schoutsen
d081e5ab3a Remove deprecated remote classes (#7011)
* Remove deprecated remote classes

* Lint

* Fix tests

* Lint
2017-04-10 09:04:19 -07:00
Fabian Affolter
ab247b0f4d Fix US states check (fixes #7015) (#7017) 2017-04-10 16:13:43 +02:00
Xorso
6cd3758b58 Bump pyalarmdotcom to support new version of aiohttp (#7021)
Add an optional extended description…
2017-04-10 16:13:07 +02:00
Paulus Schoutsen
90e73fda3c Fix two more instances of JSON parsing synology (#7014)
Add an optional extended description…
2017-04-10 10:18:37 +02:00
Paulus Schoutsen
d5e3cd51a5 Fix Synology camera content type (#7010) 2017-04-10 08:19:22 +02:00
Greg Dowling
ecfe0770ed Bump pywemo version. (#7004) 2017-04-09 22:48:18 +01:00
Paulus Schoutsen
6cc5bb0713 Merge pull request #6995 from home-assistant/release-0-42-2
0.42.2
2017-04-09 01:37:18 -07:00
Paulus Schoutsen
f6e819e799 Downgrade aiohttp to 205 (#6994) 2017-04-09 01:36:36 -07:00
Paulus Schoutsen
c42293eb10 Downgrade aiohttp to 205 (#6994) 2017-04-09 01:36:01 -07:00
Paulus Schoutsen
a6dc86fa75 version bump to 0.42.2 2017-04-09 01:31:46 -07:00
Paulus Schoutsen
9c386c68dd Merge pull request #6993 from home-assistant/release-0-42-1
0.42.1
2017-04-09 01:06:10 -07:00
Paulus Schoutsen
f51d705ac7 Make discovery not block start (#6991)
* Make discovery not block start

* Fix tests
2017-04-09 01:06:16 -07:00
Paulus Schoutsen
ba8488d8f1 Make discovery not block start (#6991)
* Make discovery not block start

* Fix tests
2017-04-09 01:05:34 -07:00
Paulus Schoutsen
62d0df4f73 Upgrade to aiohttp 2.0.6 (#6992) 2017-04-08 18:30:02 -07:00
Paulus Schoutsen
d675804119 Version bump to 0.42.1 2017-04-08 18:29:39 -07:00
Paulus Schoutsen
eb0a9869d8 Upgrade to aiohttp 2.0.6 (#6992) 2017-04-08 18:29:28 -07:00
Paulus Schoutsen
cd8723f742 Merge pull request #6956 from home-assistant/release-0-42
0.42
2017-04-08 15:40:30 -07:00
Marcelo Moreira de Mello
50cc2ed97c Bump Amcrest module to 1.1.8 (#6990)
Fixed traceback when calculating SD card percent storage

   self._state = self._camera.percent(sd_used[0], sd_total[0])
AttributeError: 'Http' object has no attribute 'percent'
2017-04-08 14:55:12 -07:00
Paulus Schoutsen
dea9aec268 Warn if start takes a long time. (#6975)
* Warn if start takes a long time.

* ps - cleanup

* Tweak message

* Add tests

* Tweak messagE
2017-04-08 14:55:12 -07:00
Paulus Schoutsen
5d3fe83e62 Warn if start takes a long time. (#6975)
* Warn if start takes a long time.

* ps - cleanup

* Tweak message

* Add tests

* Tweak messagE
2017-04-08 14:53:32 -07:00
happyleavesaoc
2277778d8d update gstreamer (#6987) 2017-04-08 14:53:16 -07:00
Marcelo Moreira de Mello
c5d89499fa Bump Amcrest module to 1.1.8 (#6990)
Fixed traceback when calculating SD card percent storage

   self._state = self._camera.percent(sd_used[0], sd_total[0])
AttributeError: 'Http' object has no attribute 'percent'
2017-04-08 14:52:10 -07:00
Adam Mills
31da54d530 Add tests for ZWaveDeviceEntityValues helper (#6978)
* Add tests for ZWaveDeviceEntityValues helper

* Add remaining coverage
2017-04-08 06:34:59 -07:00
Teemu R
5a2ab3167b switch.tplink: bump pyhs100 version requirement (#6986) 2017-04-08 06:33:59 -07:00
Teemu R
475ac52180 switch.tplink: bump pyhs100 version requirement (#6986) 2017-04-08 06:33:25 -07:00
John Mihalic
660b1b616b Update Emby for aiohttp v2 (#6981) 2017-04-08 04:51:47 -07:00
Paulus Schoutsen
d8558ad173 Fix control+c quitting HASS (#6974) 2017-04-08 04:50:52 -07:00
Pascal Vizeli
a93c01788d Bugfix time and task coro (#6968)
* Bugfix time and task coro

* fix also other create_task

* fix tests

* fix lint in test
2017-04-08 04:50:52 -07:00
Adam Mills
d3c1a48475 Update kodi for aiohttp2 (#6967) 2017-04-08 04:50:52 -07:00
happyleavesaoc
01672e63ea Crime Reports sensor (#6966)
* add crimereports

* add crimereports metadata

* implicit interval

* remove zone support
2017-04-08 04:50:52 -07:00
viswa-swami
382519e082 Foscam Camera: Adding exception handling when fetching the camera image to avoid python exception errors when host is not reachable or rather any url error to camera (#6964)
* Adding exception handling when fetching the camera image to avoid python errors when host is not reachable or any url errors to camera

* Added exception as ConnectionError instead of plain except

* Added exception as ConnectionError instead of plain except. Removed the unused error handle
2017-04-08 04:50:52 -07:00
Andrey
5d1dbd61b2 Preserve customize glob order. (#6963)
* Preserve customize glob order.

* add tests
2017-04-08 04:50:52 -07:00
aufano
69dee168a1 Fix current_temperature is rounded (#6960)
* Fix current_temperature is rounded

* fix  Unnecessary parens after 'if'
2017-04-08 04:50:52 -07:00
Teemu R
6d8af58891 light.yeelight: catch i/o related exceptions from the backend lib (#6952)
Fixes/mitigates problems with #5949 and #6624
2017-04-08 04:50:52 -07:00
Pascal Vizeli
0bb224d8c7 Initial import for HassIO (#6935)
* Initial import for HassIO

* Cleanup api code for views

* First unittest for view

* Add test for edit view

* Finish unittest

* fix addons test

* cleanup service.yaml

* Address first round with ping command

* handle timeout dynamic

* fix lint
2017-04-08 04:50:52 -07:00
Teemu R
55077b9965 switch.tplink: upgrade to the newest upstream release which adds support for plugs using the newer communication protocol (#6979) 2017-04-07 19:19:11 -07:00
John Mihalic
ad8ee1383c Update Emby for aiohttp v2 (#6981) 2017-04-07 19:17:10 -07:00
Paulus Schoutsen
64174f5763 Fix control+c quitting HASS (#6974) 2017-04-07 21:02:49 +02:00
Adam Mills
df77529bfe Tests for zwave services (#6937)
* Initial tests for zwave services

* Fix linter issues

* Complete zwave service tests
2017-04-07 09:17:23 -04:00
Nate
8cff98d07b From Dusk till Dawn (#6857)
* Added dawn, dusk, noon and midnight to the Sun component

* Created a helper method for the solar events
2017-04-06 22:59:41 -07:00
happyleavesaoc
216c2682f0 Crime Reports sensor (#6966)
* add crimereports

* add crimereports metadata

* implicit interval

* remove zone support
2017-04-06 22:47:03 -07:00
Teemu R
d952a07658 light.yeelight: catch i/o related exceptions from the backend lib (#6952)
Fixes/mitigates problems with #5949 and #6624
2017-04-06 22:41:47 -07:00
viswa-swami
9254e7e862 Foscam Camera: Adding exception handling when fetching the camera image to avoid python exception errors when host is not reachable or rather any url error to camera (#6964)
* Adding exception handling when fetching the camera image to avoid python errors when host is not reachable or any url errors to camera

* Added exception as ConnectionError instead of plain except

* Added exception as ConnectionError instead of plain except. Removed the unused error handle
2017-04-06 22:40:33 -07:00
Andrey
f96e06a2c2 Preserve customize glob order. (#6963)
* Preserve customize glob order.

* add tests
2017-04-06 22:39:35 -07:00
Pascal Vizeli
3e66df50c8 Initial import for HassIO (#6935)
* Initial import for HassIO

* Cleanup api code for views

* First unittest for view

* Add test for edit view

* Finish unittest

* fix addons test

* cleanup service.yaml

* Address first round with ping command

* handle timeout dynamic

* fix lint
2017-04-06 22:19:08 -07:00
Pascal Vizeli
74ac160355 Bugfix time and task coro (#6968)
* Bugfix time and task coro

* fix also other create_task

* fix tests

* fix lint in test
2017-04-06 21:00:58 -07:00
JesseWebDotCom
c20d48c8e0 import order fix 2017-04-06 20:52:56 -04:00
aufano
2ce8c2f80e Fix current_temperature is rounded (#6960)
* Fix current_temperature is rounded

* fix  Unnecessary parens after 'if'
2017-04-06 17:40:59 -07:00
Adam Mills
51dc8b78cc Update kodi for aiohttp2 (#6967) 2017-04-07 00:12:24 +02:00
Fabian Affolter
eb55fc8e77 Update for 0.42 2017-04-06 09:36:57 +02:00
Fabian Affolter
37246449f1 Upgrade sqlalchemy to 1.1.9 (#6955) 2017-04-06 00:27:49 -07:00
Diogo Soares
2551bf8645 Added average temperature for the day before and the current period (#6883)
* Added average temperature for the day before and the current period

* Fixed "line too long" warnings

* Fixed "indentation contains tabs" and "indentation contains mixed spaces and tabs" warnings

* Fixed "trailing whitespace" warnings

* upgrade pyhydroquebec requirements to version 1.1.0
2017-04-06 00:26:26 -07:00
Martin Hjelmare
749f79e813 Upgrade mysensors dep and callbacks (#6950) 2017-04-06 00:21:21 -07:00
Pascal Vizeli
86568b443c Fix startup of sonos / snapshot handling / error handling (#6945)
* Fix startup of sonos / snapshot handling / error handling

* Use decorator for coordinator relay

* fix lint

* Fix unittest

* Move subscribe into executor
2017-04-05 23:24:30 -07:00
Paulus Schoutsen
29f385ea76 Fix automations listening to HOMEASSISTANT_START (#6936)
* Fire EVENT_HOMEASSISTANT_START automations off right away while starting

* Actually have core state be set to 'starting' during boot

* Fix correct start implementation

* Test and deprecate event automation platform on start

* Fix doc strings

* Remove shutting down exception

* More strict when to mark an instance as finished

* Add automation platform to listen for start/shutdown

* When we stop we should wait till it's all done

* Fix testing

* Fix async bugs in tests

* Only set UVLOOP when hass starts from CLI

* This hangs normal asyncio event loop

* Clean up Z-Wave node entity test
2017-04-05 23:23:02 -07:00
Fabian Affolter
289d6b6605 Upgrade py-cpuinfo to 3.0.0 (#6948) 2017-04-05 21:29:59 +02:00
Fabian Affolter
73f69085d9 Upgrade Sphinx to 1.5.5 (#6947) 2017-04-05 21:05:50 +02:00
Thibault Cohen
118bd34d74 Add multi phone numbers support (#6605)
* Add multi phone numbers support

* Update fido.py
2017-04-05 17:18:02 +02:00
Jeff Wilson
f1f033e5d2 Report proper features in mqtt_json light (#6941)
* Add tests for supported features in mqtt_json (it fails)

* Fix supported features in mqtt_json
2017-04-05 14:39:19 +02:00
Adam Mills
75a3747f61 Rename zwave nodes by node ID instead of entity ID (#6938) 2017-04-05 08:11:37 -04:00
Marcelo Moreira de Mello
a5f77d5f46 Clean artifacts after running Ring tests. (#6944)
* Clean artifacts after running Ring tests.

* Clean artifacts from top level test_ring.py
2017-04-05 11:26:56 +02:00
Greg Dowling
534187f4cd Bump pywemo version. Fixes Osram/Sylvania Lightify tunable white bulbs. (#6946)
Add an optional extended description…
2017-04-05 11:26:03 +02:00
Pascal Vizeli
8f4fd951e5 Add android ip webcam support for aiohttp2 (#6940) 2017-04-04 21:40:19 -07:00
JesseWebDotCom
90a834cbda String formatting, import order, type check fixes 2017-04-04 22:33:52 -04:00
citruz
e4e7141ae7 Eddystone Beacon Temperature Sensor (#6789)
* Added eddystone_temperature platform.

* Fixed style issues.

* Fixed style issues #2.

* Fixed style issues #3.

* Added new platform to .coveragerc

* Refactored platform to use the beacontools package.

* Fixed style issues and added beacontools to excluded requirements.

* Removed obsolete constants and added pylint exception.

* Added blank line

* Updated beacontools to version 1.0.0

* Updated beacontools to version 1.0.1

* Forgot to regenerate requirements_all

* Minor changes
2017-04-04 23:57:19 +02:00
Paulus Schoutsen
c4e1255a84 Initial state over restore state (#6924)
* Input Boolean: initial state > restore state

* Input select: initial state overrules restored state

* Input slider: initial state overrule restore state

* Lint

* Lint
2017-04-04 09:29:49 -07:00
Craig J. Ward
c5574c2684 total connect alarm support (#6887)
* total connect alarm support

* linting fixes

* linting fixes

* docstring

* docstring

* use sync and update coveragerc

* remove unused import

* changes as per notes

* Update HA code style
2017-04-04 12:21:53 +02:00
Greg Dowling
dcbc0b490c WIP - Fix bug in state handling in Vera Switch and Light (#6931)
* Fix bug in state handling.

* Tidy.
2017-04-04 12:02:09 +02:00
Klaas Hoekema
57a00c1fbf Allow token authentication for 'hook' switch component (#6922)
The Hook switch component currently uses username/password to get a token
from the Hook web app to use for subsequent requests. This adds an option
to specify the token directly in config.

Makes the 'password' and 'token' options mutually exclusive since
otherwise one would have to take precedence, and it seems worth
preventing people from filling their config with passwords that don't
even need to be there.
2017-04-04 10:55:43 +02:00
Fabian Affolter
aff8c0f695 Upgrade Sphinx to 1.5.4 (#6927) 2017-04-04 10:50:04 +02:00
Fabian Affolter
542e430c1c Upgrade distro to 1.0.4 (#6926) 2017-04-04 10:44:08 +02:00
Fabian Affolter
26e9e59a5b Upgrade paho-mqtt to 1.2.1 (#6928) 2017-04-04 10:43:41 +02:00
Daniel Høyer Iversen
86d265d407 Upgrade flux_led to 0.17 (#6929)
* Update flux_led lib
2017-04-04 10:42:51 +02:00
Paulus Schoutsen
23645da74c Automation: initial state > restore state (#6911)
* Automation: initial state > restore state

* Clean up code

* Ensure MQTT defaults are used.

* Ensure failed platforms always return None

* Automation: write state to state machine after start
2017-04-03 23:11:39 -07:00
Paulus Schoutsen
3895979e39 Update frontend 2017-04-03 23:02:15 -07:00
Greg Dowling
5b9d9954c5 Update vera cover refresh logic (#6897)
* Update cover update logic to fix compatibility bug. Bump vera library.

* Tidy.

* Add missed file.
2017-04-03 22:44:52 -07:00
John Mihalic
4c7ec4932c Bump pyHik library version to support more cameras (#6921) 2017-04-04 06:54:33 +02:00
Adam Mills
06e1c21b1f Support for zwave light transitions (#6868)
* Support for zwave light transitions

* Dimming duration is optional

* Updated supported_features to show transition
2017-04-03 14:56:48 -04:00
Mitesh Patel
01e581aced Adds support for the PlugInDimmer hardware (#6915) 2017-04-03 08:52:02 -07:00
John Arild Berentsen
a107a592de Fix for #6691 Neato Connection error handling (#6731)
* Responsiveness

* Delay was not needed as commands does not return until done.

* Offline error catch

* Remove unneeded code
2017-04-03 14:42:21 +02:00
Dan
134b21dfea Onkyo update (#6906)
* Update onkyo req, change volume to int

* Update Onkyo

Updates onkyo component. Pulls added sources (Bluetooth, built-in
streaming, etc.)

* Regenerated requirements_all.txt via script

* Update onkyo.py

* Update requirements_all.txt
2017-04-03 00:35:36 -07:00
David McNett
c27a526f5b Eliminate needless async_add_job invocation of async_add_devices (#6864) 2017-04-03 00:01:53 -07:00
Marcelo Moreira de Mello
f4d2ece2fe Make sensor.ring to handle scan_interval option as expected. (#6886)
* Make sensor.ring to handle scan_interval option as expected.

* Fix lint

* Fixed lint timedelta
2017-04-02 23:46:54 -07:00
Marcelo Moreira de Mello
5b8f1850fa Makes amcrest.sensor to handle properly the scan_interval option. (#6885)
* Makes amcrest.sensor to handle scan_interval option as expected.

* Added _LOGGER.debug statement for troubleshooting.

* Fixed lint
2017-04-02 23:46:18 -07:00
Paulus Schoutsen
ce42648a51 Update README.rst 2017-04-02 17:35:03 -07:00
Paulus Schoutsen
36e5878b2e Move examples out (#6908)
* Remove examples from main repo

* Simplify README

* Point screenshot for components at dev branch for now
2017-04-02 17:01:51 -07:00
Dan
f0027e3cc1 Fox UMP volume set (#6904)
async needs consistant paramater namming accross platforms. This fixes
UMP's volume set method by renaming the paramater.
2017-04-02 15:31:28 -07:00
Wolfgang Malgadey
864b57d42c Fix Tado climate set off mode (#6848) 2017-04-02 12:47:15 -04:00
JesseWebDotCom
b99dd19ad6 Fixed log errors if session username or content rating is blank 2017-04-02 10:51:03 -04:00
Daniel Høyer Iversen
8806265e99 Fluxled (#6892)
* Update flux_led lib
2017-04-02 14:12:38 +02:00
ChristianKuehnel
2413d97415 added support for Fibaro FGR-222 (similar to FGRM-222) (#6890) 2017-04-02 12:41:53 +03:00
Fabian Affolter
395f9b6548 Upgrade sqlalchemy to 1.1.8 (#6873) 2017-04-01 12:36:46 +02:00
Fabian Affolter
7afe694cc7 Upgrade aiohttp_cors to 0.5.2 (#6874) 2017-04-01 12:36:35 +02:00
Fabian Affolter
ec2df2ca0f Upgrade pytz to 2017.02 (#6875) 2017-04-01 12:36:24 +02:00
Marcelo Moreira de Mello
65b9383e04 Bumped amcrest module to 1.1.5 (#6872) 2017-04-01 12:36:04 +02:00
JesseWebDotCom
ae21fa9ce1 Fixed setting username and content rating data 2017-03-31 23:20:36 -04:00
JesseWebDotCom
564a01f344 mend
Exposed more attributes, enabled play_media tv show or season episodes
2017-03-31 22:36:37 -04:00
JesseWebDotCom
05bab8c808 Exposed more attributes, enabled play_media tv show or season episodes 2017-03-31 22:19:04 -04:00
Fabian Affolter
a0bb554f8a Upgrade speedtest-cli to 1.0.3 (#6867) 2017-03-31 22:57:29 +02:00
Jacob Tomlinson
2d6b09586d Added Met Office weather and sensor components (#6742)
* Added Met Office weather and sensor components

* Removed unnecessary dependancy

* Generated requirements

* Fix time interval

* Updated coverage

* Some review changes

* Allow user to specify lat and lon in component

* Added missing import

* Fixed unit

* Fixed import indent

* Updated condition to use CONDITION_CLASSES
2017-03-31 22:03:27 +02:00
Fabian Affolter
573b2a11c0 Upgrade sphinx-autodoc-typehints to 1.2.0 (#6865) 2017-03-31 21:39:22 +02:00
Fabian Affolter
ac25eff2d0 Upgrade sendgrid to 3.6.5 (#6866) 2017-03-31 12:37:34 -07:00
Marcelo Moreira de Mello
05398a9dff Introduced Ring binary sensors and refactored Ring component (#6520)
* - Introduced Ring binary_sensor.

- Added unittest for Ring binary_sensor.

- Bumped ring_doorbell 3rd party module.

* Updated requirements

* Added correct file for unittest

* - Introduced Ring binary_sensor.

- Added unittest for Ring binary_sensor.

- Bumped ring_doorbell 3rd party module.

* Updated requirements

* Added correct file for unittest

* Added extra sensors last_ding and last_motion

* Modified Ring binary_sensor and sensor to inherit DOMAIN configuration

* Moved static to top ring.py

* Fixed requirements

* Bump version ring_doorbell to 0.1.2

* testing unittests

* Use hass.data dict instead GLOBALS

* Fixed unittests

* Bump ring_doorbell to 0.1.3

* Updated unittest and coverted to use decorator @requests_mock.Mocker()

* Updated ring_session with lower case
2017-03-31 08:53:56 -07:00
Paulus Schoutsen
8c97bccaaa Handle aiohttp task cancellation better (#6862) 2017-03-31 11:55:22 +02:00
Craig J. Ward
5bb201c7fc use change light level to avoid variable ramp speeds (#6860) 2017-03-30 23:53:10 -07:00
Pascal Vizeli
72db4a80dd Update aioHTTP to 2.0.5 (#6856) 2017-03-30 08:27:53 -07:00
Johan Bloemberg
816b1891b5 Add option to disable automatic add for lights and sensors. (#6852) 2017-03-30 08:02:03 -07:00
Beat
ee8701b560 Fix configuration setup (#6853)
When the user exceeds the request limit for google APIs, the response status stays at 200 but the response body is different:
```
{
   "error_message" : "You have exceeded your daily request quota for this API. We recommend registering for a key at the Google Developers Console: https://console.developers.google.com/apis/credentials?project=_",
   "results" : [],
   "status" : "OVER_QUERY_LIMIT"
}
```
This prevents HA from creating the initial configuration
2017-03-30 15:26:11 +02:00
Paulus Schoutsen
714b516176 aiohttp 2 (#6835)
* Upgrade aiohttp2

* Fix resource caching

* Fix helpers.aiohttp_client

* Lint

* Use static path for api error_log

* Fix ClientErrors import

* Remove not needed DisconnectError

* Remove releasing responses code

* Add timeout if stream starts non responding

* More async_aiohttp_proxy_stream cleanup

* Fix references to ClientError

* Fix fingerprinting

* Fix aiohttp stream tests

* Rename aiohttp_proxy_stream

* Remove impossible darksky test

* Fix sleepiq requests escaping mocker

* Lint

* Remove deprecated parameter

* Break up aiohttp_proxy_stream in 2 methods

* Lint

* Upgrade to aiohttp 2.0.4

* Convert connector close to a callback

* Fix static fingerprinted links
2017-03-30 00:50:53 -07:00
Anders Melchiorsen
7b83a836f3 Lifx legacy (#6847)
* Add legacy LIFX platform for Windows support

The async platform introduced in 9ef084d903 has
turned out to use Python functionality that is not available in Windows.

This commit restores the previous implementation, now named lifx_legacy.

* Add a comment about the platform being a legacy implementation

* Warn when using unsupported lifx platform on Windows

* Update .coveragerc
2017-03-29 23:00:22 -07:00
Johan Bloemberg
ead00e956f Handle initial event after entity is instantiated. (#6760) 2017-03-29 22:50:32 -07:00
Paulus Schoutsen
556dba4020 Convert Alexa tests to use aiohttp test utils (#6839) 2017-03-29 22:21:39 -07:00
Paulus Schoutsen
bfe0aee468 Locative tests to use aiohttp test utils (#6838) 2017-03-29 22:19:58 -07:00
Lewis Juggins
9de4c2b056 [switch.wemo] Fix today_energy_kwh calculation. (#6846)
* [switch.wemo] Fix today_energy_kwh calculation.

* Blank line fail

* Round to two decimal places.
2017-03-29 21:49:28 +02:00
Anubhaw Arya
c935bfce2a Integration with lockitron (#6805)
* Integration with lockitron

* Let super class deal with polling and updating
2017-03-29 17:25:23 +02:00
Martin Hjelmare
7c614a6738 Add voluptuous config validation to scenes (#6830)
* Add platform schema to scene component and homeassistant platform.
* Clean up code and add constants.
* Add unit test and clean up tests.
2017-03-28 23:39:53 -07:00
Xorso
d1b519a418 Updating Alarm.com Component for async and no Selenium (#6752)
* Updating Alarm.com Component for async and no Selenium

* Fixed gen_all_requirements
2017-03-28 23:21:40 -07:00
Johan Bloemberg
e1ed076015 Rflink group commands (#5969)
* Add support for group commands (allon/alloff).
Add 'group_aliasses' config attribute that only respond to group commands.
Add nogroup_aliases that only respond to 'on' 'off' commands.
Allow settings device id group behaviour.

* Fix linting.

* Fix lint.
2017-03-29 01:04:25 -04:00
Oleksii Serdiuk
63c15e997a history_stats: Fix schema, as state can be arbitrary string (#6753) 2017-03-29 00:58:59 -04:00
Andrey
fb8323f48d Remove zwave cover invert workaround. Use config instead. (#6832) 2017-03-28 23:01:29 +03:00
William Scanlon
b5336ed04e Updated pubnubsub-handler version (#6829) 2017-03-28 14:13:45 -04:00
Teemu R
429367409c yeelight: adjust supported features on update() (#6799)
* yeelight: adjust supported features on update()

Earlier we checked for the type only during the initialization,
but this won't work when the bulb is disconnected during the init,
causing failures to adjust rgb&color temperature even if those should be supported.

fixes #6692

* Use reassign instead of OR for updating the supported features
2017-03-28 17:26:43 +02:00
Lewis Juggins
6dba05c79f [switch.wemo] Fix mW to kW conversion. (#6826)
* Fix mW to kW conversion.

* Line length.
2017-03-28 17:24:33 +02:00
Janne Grunau
5c80da6a8f lights/hue: use device class for on/off devices like the osram lightify plug (#6817)
* hue: remove duplicate SUPPORT_FLASH flag

* hue: use correct device class for the Osram Lightify Plug
2017-03-28 00:04:28 +02:00
Fabian Affolter
d027df5a89 Allow to monitor Windows hosts (#6803) 2017-03-27 22:11:15 +02:00
goto100
b8c1bc9542 fix WOL in docker/jail (#6810)
* fix WOL in docker/jail

add ip_address parameter to send_magic_packet if host specific.

in docker/jail, WOL doesn't works due to subnet broadcast issues.

* Update wake_on_lan.py

lint
2017-03-27 14:02:43 +02:00
Greg Dowling
c53de19246 Update Insight parameters using the subscription data. (#6782)
* Update Insight parameters using the subscription dta.

* Minor refactor.

* Tidy.
2017-03-27 12:43:43 +02:00
John Mihalic
f242ad26ca Add NVR support to Hikvision Binary Sensors (#6807)
* Add NVR support to hikvision

* Only append channel for nvr devices

* Descriptor cleanup

* Update requirements
2017-03-27 12:41:57 +02:00
tantecky
a70af62e60 Make get_snmp_data more robust (#6798) 2017-03-27 12:22:59 +02:00
Fabian Affolter
be04ef7be1 Upgrade matrix-client to 0.0.6 (#6808) 2017-03-27 10:35:40 +02:00
Fabian Affolter
f4f72e420a Fix typo and update name (#6809) 2017-03-27 10:35:27 +02:00
Fabian Affolter
84287872bb Use string formatting and remove already global disabled pylint issue (#6801) 2017-03-26 21:13:38 +02:00
David Straub
78b5eb7aac Platform for Munich public transport departure times (#6704)
* Add MVGLive (Munich public transport real-time departure) sensor platform

* Move update on startup to add_devices; rewrite dictionary filtering

* Fix lint error

* Updated requirement to PyMVGLive 1.1.3 (PyPI version)

* Refactor and clean up MVGLiveData.update method

* Shorten line
2017-03-26 19:06:40 +02:00
Fabian Affolter
6e44ccf683 Upgrade pysnmp to 4.3.5 (#6793) 2017-03-26 15:53:53 +02:00
Fabian Affolter
ad649009cd Upgrade zeroconf to 0.19.0 (#6792) 2017-03-26 15:53:42 +02:00
Fabian Affolter
7782e7e948 Add optional unit of measurement (#6796) 2017-03-26 15:52:59 +02:00
Fabian Affolter
5d5547cdb6 Update docstrings (#6795)
* Add link to docs and remove comments which are obvious

* Update docstrings

* Repleace conf details with link to docs

* Add link to docs

* Update docstrings

* Update import

* Update ordering

* Update ordering

* Update docstring

* Update ordering

* Update ordering
2017-03-26 15:50:40 +02:00
Fabian Affolter
22b28d85db Add switch to MQTT discovery (#6733) 2017-03-26 15:48:28 +02:00
Teemu R
f5d4f853ba switch.tplink: upgrade to the newest upstream release which adds support for plugs using the newer communication protocol (#6790) 2017-03-26 10:55:17 +02:00
Fabian Affolter
0f098df232 Merge branch 'master' into dev 2017-03-26 00:07:25 +01:00
Fabian Affolter
1f046972d9 Merge pull request #6756 from home-assistant/release-0-41
0.41
2017-03-26 00:01:49 +01:00
John Arild Berentsen
5a7155fc4a Wrong info in discovery schema (#6779) 2017-03-25 19:39:03 +01:00
Nick Sabinske
c817ab08b7 Fix bridge-led support in limitlessled.py (#6776)
Addressing #6382 . Feedback from github & forums is the bridge_led feature never worked and defining the bridge LED as another group+bulb type is the right way to do this in the limitlessled component.
2017-03-25 15:32:57 +01:00
William Scanlon
f8005153c9 Fix wink siren (#6775)
* Fix siren/switch attributes and update python-wink

* Updated requirements_all.txt
2017-03-25 15:28:16 +01:00
Daniel Perna
447048701c Homematic Fixes (#6769)
* Added missing operational modes for thermostats

* Added attributes

* Updated requirements

* Bumped dependency
2017-03-25 09:48:05 +01:00
Pascal Vizeli
f4e9466394 Bugfix automation fire on bootstrap (#6770)
* Bugfix automation fire on bootstrap

* Add test & fix bug

* fix lint
2017-03-24 15:52:14 -07:00
Adam Mills
cffc6c7bea Tests for zwave workaround detection (#6761)
* Tests for zwave workaround detection

* Remove unused code

* Revert "Remove unused code"

This reverts commit e06cce0abe.

* Tests for empty manufacturer ID
2017-03-24 15:31:46 -07:00
Fabian Affolter
8d606f8d16 Upgrade sleekxmpp to 1.3.2 (#6773) 2017-03-24 21:44:04 +01:00
Fabian Affolter
7ae814357a Upgrade psutil to 5.2.1 (#6771) 2017-03-24 21:43:48 +01:00
Fabian Affolter
1be2706de3 Upgrade pydroid-ipcam to 0.7 (#6772) 2017-03-24 21:42:00 +01:00
geekofweek
06d3889e1b Wink Aros Fixes (#6726)
Wink Aros only supports 3 Fan Modes:

Low
Medium
High

Fan Mode had a Typo and wasn't represented in the UI
2017-03-24 16:13:14 -04:00
John Arild Berentsen
ee6c9ab6a9 Typing error and update test (#6757) 2017-03-23 23:53:59 -07:00
John Arild Berentsen
82c599a749 current temp could be none (#6759) 2017-03-23 23:50:36 -07:00
Paulus Schoutsen
0b7f873120 Merge branch 'release-0-41' into dev 2017-03-23 23:34:08 -07:00
Paulus Schoutsen
9f2f0c5566 Version bump to 0.42.0.dev0 2017-03-23 23:33:49 -07:00
Paulus Schoutsen
53f8828181 Merge branch 'master' into release-0-41 2017-03-23 23:32:50 -07:00
Adam Mills
22613d8e2e Repair zwave sensor coverage (#6764) 2017-03-23 20:57:15 -07:00
Adam Mills
efbd66bca1 Fix flaky template test (#6768) 2017-03-23 20:56:39 -07:00
Tim Soderstrom
5dfdb9e481 New indexes for states and recording_runs tables (#6688)
* New indexes for states table

* Added recorder_runs indexes

* Created a new function for compound indexes.

A new function was created because it makes it a little cleaner when creating
a single-field index since one doesn't have to create a list. This is mostly
when creating the name of the index so with a bit more logic it's possible
to combine it into one function. Given how often migration changes are run,
I thought that code bloat was probably a worthy trade-off for now.

* Adjusted indexes, POC for ref indexes by name.

* Corrected lint errors

* Fixed pydocstyle error

* Moved create_index function outside apply_update

* Moved to single line (just barely)
2017-03-23 20:48:31 -07:00
micw
6c5989895a Adding expire_after to mqtt sensor to expire outdated values (#6708)
* Adding expire_after to mqtt sensor to expire outdated values

* Extending test case

* mqtt: refactoring expire_after to use timed events instead of polling; lint

* refactor to reset unused trigger

* Fix: handler must be set to None after execution or removal to avoid warning

* Commenting out non-working test

* Fix lint

* Commit to trigger new build

* Commit to trigger new build

* Make testcase work

* Undo unnecessary change

* Remove default value, add extra check
2017-03-23 17:55:07 -04:00
Daniel Høyer Iversen
3acd926d29 Flux led update lib (#6763)
* Update flux_led lib
2017-03-23 21:58:22 +01:00
Paulus Schoutsen
b6b40286ef Version bump to 0.41 2017-03-23 08:42:01 -07:00
Andrey
8a86ec5b74 Add zwave per-node entity. (#6690)
* Add zwave per-node entity.

* Disable lint import error

* Add tests for base class

* More tests

* More tests

* Sort .coveragerc

* more tests

* Move location, battery and wakeup to node entity

* More tests

* Cleanup

* Make zwave node entity visible by default

* Remove battery sensor

* Fix tests
2017-03-23 08:37:20 -07:00
Dan Ports
20c5f9de4b Add sensor for Lyft time and price (based on Uber sensor) (#6711)
* Add sensor for Lyft time and price (based on Uber sensor)

* Minor fixes to lyft sensor
 - use add_devices(...,True) instead of explicitly calling update
 - move sensor name check into constructor

* lyft sensor: disable sandbox mode
2017-03-23 08:15:52 -07:00
Mitesh Patel
61730012d8 Adds Support for Lutron Caseta devices. (#6631)
* Adds support for the Lutron Caseta family of devices

* Added external requirement

* Removes unuse import

* Adds requirement to requirements_all.txt

* Removes unuse import

* fixes requirement_all.txt. by regenerating

* Cleans up syantax

* Cleans up syantax

* Cleans up syantax

* Cleans up syantax

* Shortens long line

* adds lutron_caseta component to .coveragerc

* Merges into light, removes component

* Fixes long lines

* Fixes requirement.txt

* Removes 'ERROR' log statements. Adds missing dependency

* savig work to pick up on other machine

* Enables Lutron Caseta component with Light and Switch platforms

* Add missing file to .coveragerc

* Changes based on PR review

* fixes requirements file

* Fixes install of paramiko dependency.

* Moves callback registration to

* comment changes

* Platform have no return value

* Change style for guard

* fix logic

* fix lint
2017-03-23 01:18:14 +01:00
Paulus Schoutsen
25d2df5689 Merge pull request #6740 from home-assistant/release-0-40-2
0.40.2
2017-03-22 09:31:21 -07:00
Paulus Schoutsen
672b83db8a Update constraints 2017-03-22 09:30:36 -07:00
Paulus Schoutsen
b37438ebb7 Constrain core dependencies to core versions (#6738)
* Require at least pip 7.1

* Write and use constraint files for packages

* Update gen_requirements_all.py
2017-03-22 08:51:18 -07:00
Paulus Schoutsen
902b72ba1a Constrain core dependencies to core versions (#6738)
* Require at least pip 7.1

* Write and use constraint files for packages

* Update gen_requirements_all.py
2017-03-22 08:50:54 -07:00
Paulus Schoutsen
f10fede17f Bump PyChromecast to 0.8.1 (#6702) 2017-03-22 08:50:42 -07:00
Paulus Schoutsen
c9548b11b1 Version bump to 0.40.2 2017-03-22 08:50:15 -07:00
Wolfgang Malgadey
f4aec3ac88 Tado climate device (#6572)
* Added tado climate component

named the component v1 because of the unsupported state of the api I
used (mytado.com)

* sensor component
* climate component which uses sensors
* main component initiating sensor and climate devices
* order of imports
* consts for username and password
* removed redundant code
* changed wrong calls and properties

* remove pylint overrides

* merged update() and push_update()

* changed wrong calls
* removed pylint overrides
* moved try..except

* renamed MyTado hass-data object

* added TadoDataStore

* moved update methods from sensor to TadoDataStore

* reorganised climate component

* use new TadoDataStore

* small change to overlay handling

* code refactoring

* removed unnessesary comments
* changed throttle to attribute
* changed suggestions from PR

* Added constant variable for string literal

* remove wrong fget() call

* changed dependencies

* Changed operation mode list

* added human readable list of operations
* removed unnecessary const
* activated update on add_devices

* droped unit

* removed unnused property

* changed temperature conversion

* removed defaults from config
changed naming of tado data const

* switched operation_list key/values

* changed the value returned as state

* added one extra line

* dropped state to use base impl.

* renamed component

* had to inplement temperature_unit

* because it is not implemented in base class

* create a copy of the sensors list

* because it can be changed by other components

* had to check for empty data object

* hass is too fast now
2017-03-22 08:18:13 -04:00
Adam Mills
e7425e9808 ZWave Lock Tests (#6730)
* ZWave Lock Tests

* Linting fixes

* Missed coveragerc
2017-03-21 08:55:21 -07:00
Matt N
978b539111 camera.zoneminder: Show recording state (#6686) 2017-03-21 07:25:10 +01:00
Paulus Schoutsen
9f4cd5fafe Do not log warning on rest_command if no error (#6713) 2017-03-21 07:18:33 +01:00
Anders Melchiorsen
ba3c9f9765 Fix LIFX unregister races (#6723)
* Fix LIFX unregister races

If the initial state request never got a response, we tried to unregister
a device that was not yet registered.

Also, aiolifx 0.4.2 has an "unregister" race fix.

* Update requirements
2017-03-21 07:17:58 +01:00
Paulus Schoutsen
4ee8be52fe Update frontend 2017-03-20 21:31:58 -07:00
Adam Mills
866bf887d3 ZWave switch tests (#6722) 2017-03-20 13:17:42 -04:00
Adam Mills
dddbce82f5 ZWave Sensor tests (#6721)
* ZWave Sensor tests

* Add missed coverage
2017-03-20 13:17:17 -04:00
Jeff Wilson
be15ca3f23 Don't warn if octoprint completion is null (#6719) 2017-03-20 09:00:45 -07:00
Finbarr Brady
9a305c9742 Fix for issue: luci returns 403 invalid token when rebooted #6715 (#6717) 2017-03-20 08:55:59 -07:00
Paulus Schoutsen
de231cf9ab restore_state: do not crash if domain not defined (#6714) 2017-03-20 08:54:51 -07:00
Adam Mills
8325f9db8a Add zwave light tests (#6710)
* Add zwave light tests

* Test turn_off
2017-03-19 22:22:13 -07:00
Paulus Schoutsen
3f38b9e52f Update frontend 2017-03-19 21:59:13 -07:00
printzlau
1cb2a6add0 automatically use bundled certificate it set to auto (#6707)
* automatically use bundled certificate if certificate-parameter is set to 'auto' and seperate this from which port is specified

* Fix travis error and long lines

* Update __init__.py
2017-03-19 14:51:53 -07:00
Greg Dowling
acf75b5253 Revise power and energy units and property names. (#6212)
* Revise power and energy units and property names.

* Add change for new wemo parameter.
2017-03-19 22:02:11 +01:00
Adam Mills
970bde9e99 Fix Kodi when websocket is disabled (#6706) 2017-03-19 21:38:12 +01:00
Adam Mills
796143a6c6 Kodi use websocket loop task created by library (#6703) 2017-03-19 12:46:14 -07:00
Paulus Schoutsen
5569ae38f1 Bump PyChromecast to 0.8.1 (#6702) 2017-03-19 11:53:58 -07:00
John Mihalic
7eaad4fb3a Fix longitude (#6697) 2017-03-19 11:00:13 -07:00
Fabian Affolter
35c679a956 Upgrade distro to 1.0.3 (#6693) 2017-03-19 15:59:07 +01:00
martinfrancois
678f273002 Rflink: added support for lights with toggle type (#6521)
* added support for lights with toggle type

* fixed style errors

* introduced tests for the toggle type

it's not passing yet because of an assertionerror at line 407

* updated to reflect tristate of "state"

* Format code according to pep8

Added line break for import that was too long.

* fixed lint, replaced if statement with 'var = bool(test)'

* changed implementation of state check according to bug on 6bceb04ca1 (r106758784)
2017-03-18 21:34:17 +01:00
Tyler Crumpton
4e91c65d6e Update Torque component to match recent API. (#6671) 2017-03-18 11:25:38 +01:00
Robbie Trencheny
f6106706e5 Merge pull request #6672 from robbiet480/new-plex-fix
Fixed Show All Controls feature
2017-03-17 17:34:26 -07:00
JesseWebDotCom
ecf337b123 Fixed Show All Controls feature 2017-03-17 17:22:38 -07:00
Martin Nöhrer
f5d8327d9a Fix hass script execution on Windows (#4977). (#6601)
* Fix hass script execution on Windows (#4977).

hass.exe returned ERRNO2 on a windows machine and must be started using
package loading. This fix adapts the command line options for
`setup_and_run_hass()` to start
either a script with `python homeassistant/__main__.py` or with
`Scripts/hass.exe`

* Fix code style
2017-03-17 17:07:36 -07:00
John Mihalic
30d4c54187 Update Emby component to async (#6664)
* Update Emby component to async

* Address comments

* Make SSL default

* Bump library

* Port based on SSL, use available property
2017-03-17 15:55:07 +01:00
David Straub
edf20f542a Phone book lookup support for Fritz!Box call monitor (#6474)
* Update fritzconnection dependency in fritz and fritzbox_netmonitor components

* Add phone book name lookup support to FritzBox call monitor

* Updated requirements_all.txt

* Requested changes to FritzBox call monitor

* Listen to HOME_ASSISTANT_STOP and close thread

* Safe CPU time
2017-03-17 14:40:12 +01:00
Jay Love
9778000e9a Add new media_player platform: Volumio Media Player (#6556)
* Add new media_player platform: Volumio Media Player

Volumio media player is a rpi music player, this platfor adds http based control of the player.

* Modify mute command to accept boolean

* Adjust mute call to reset volume after unmute
Remove references to volimi"a"

* Use yield from calls in mute and volume calls

Trying to speed up the indication of mute and volume level changes in UI, but doesn't seem to do much.

* Adjust async_add_devices call
2017-03-16 23:32:52 -07:00
miniconfig
b5149dfba6 Added support for multiple efergy sensors in the same household. (#6630)
* Added support for multiple efergy sensors in the same household.
Also added inital tests for the efergy platform.

* Fixed current_values units.
Changed name to include efergy_ prefix.
2017-03-16 23:22:10 -07:00
John Mihalic
ced3cd2616 Refactor Neurio to add Daily Power Sensor (#6662)
* Refactor Neurio to add Daily Power Sensor
2017-03-16 23:20:14 -07:00
RickyTaterSalad
7050236a61 add latitude and longitude configuration to darksky sensor (#6191)
* Optional latitude and longitude to darksky sensor

allow configuration of latitude and longitude in the darksky sensor.
falls back to home assistant coordinates if latitude/longitude not
specified.

* lat/long as inclusive on schema. removed None check for lat/long in setup_platorm

altered schema to require latitude and longitude configured as a pair.
removed None check on latitude and longitude since values will fall back
to hass config if not present

* adhere to line limit of 79 characters

adhere to line limit of 79 characters
2017-03-16 23:04:01 -07:00
Pascal Vizeli
c8e1ffad89 Pump Android ip webcam to 0.6 (#6665)
* Pump Android ip webcam to 0.5

* update to 0.6
2017-03-16 22:19:48 +01:00
hawk259
e3edff8a72 Add configurable timeout option to camera.synology (#6655) 2017-03-16 21:26:35 +01:00
Sebastian
d6fd0f405e Corrected help text for refresh_node (#6659)
Changed entity_id to node_id.
2017-03-16 21:50:01 +02:00
Fabian Affolter
1ab47b5d2b Check if droplet exists (#6663)
* Check if droplet exists

* Add droplet to message and remove else
2017-03-16 19:59:34 +01:00
Fabian Affolter
a2365eccf6 Upgrade aiohttp to 1.3.5 (#6660) 2017-03-16 18:04:00 +01:00
Fabian Affolter
c46ba3446d Upgrade astral to 1.4 (#6332)
* Upgrade astral to 1.4

* Update test for norway
2017-03-16 17:38:46 +01:00
JesseWebDotCom
5714f156c3 Support for non-clients, NVidia shield, dynamic grouping, extra metad… (#6054)
* Support for non-clients, NVidia shield, dynamic grouping, extra metadata, and more

* Fixed import orderdering, line lengths, and comment violations

* Local player check and season fixes

* Honor entity_namespace when using custom entity ids

* Semi compatibility with channels, force controls option added

* media_position_updated_at fix - only update when media is playing

* Fix: controls now work as expected on 1) casted sessions and 2) client sessions when client and PMS reside on the same sever

* Made PEP8 compliant

* Made PEP8 compliant

* Made PEP8 compliant, hopefully

* Better Tivo compatibility

* Added frozen detection and remediation

* PlayMedia EPISODE now supports season number and episode number (instead of episode index)

* Fix: Dynamic groups now exclude hidden devices

* Fix: Dynamic groups now exclude hidden devices

* Implemented arsaboo suggested formatting

* Implemented pylint command line suggested fixes

* Implemented Travis CI build suggested fixes

* Sorted Imports using Importanize

* Grouped request imports

* Removed dynamic groups, network calls from properties, and other cleanup

* Balloob recommendations and Plex Protocol Capabilities checks

* Remove deprecated disable-msg in favor of disable

* Removed position related items (seek, frozen detection, etc)

* Removed unused datetime
2017-03-16 09:09:46 -07:00
joe248
959dd29c90 round output values (#6657) 2017-03-16 15:36:44 +01:00
John Arild Berentsen
e75a66ed20 Add Zwave sensors test (#6640)
* Test for zwave sensor

* Add test for ZWave sensors

* Hound...

* Hound...

* Review changes
2017-03-16 08:25:37 -04:00
Fabian Affolter
3e72aa8643 Upgrade python-digitalocean to 1.11 (#6653) 2017-03-16 11:14:36 +01:00
John Arild Berentsen
5aaa1f8404 Add test for Z-wave switch (#6619)
* Add test for Z-wave switch

* Changes for new tests
2017-03-16 11:05:51 +01:00
Wolf-Bastian Pöttner
509cfb6433 Added workday sensor (#6599)
* Added workday sensor

* Added unit tests
2017-03-15 23:46:13 -07:00
Pascal Vizeli
774fd19638 Bugfix RFLINK remove group (#6580)
* Bugfix RFLINK remove group

* Remove group hack from lutron too

* fix tests

* fix lint

* fix lint
2017-03-15 23:08:47 -07:00
Paulus Schoutsen
e265401cd0 self.loop.create_task -> self.add_job (#6632)
* self.loop.create_task -> self.add_job

* Core to use create task
2017-03-16 06:58:54 +01:00
deisi
5b3dc7f2a5 Fix #6534 (#6598)
* Fix #6534

Makes sure 0 is not passes to `color_temperature_kelvin_to_mired`.

* Update osramlightify.py

* Update osramlightify.py
2017-03-15 22:58:06 -07:00
Anders Melchiorsen
9ef084d903 Move LIFX to aiolifx for driving the bulbs (#6584)
* Move LIFX to aiolifx for driving the bulbs

* Fix whitespace

* Fix more whitespace

* Fix lint

* Define _available in init

* Add @callback decorators

* Use hass.async_add_job

* Rename class
2017-03-15 22:50:33 -07:00
Robbie Trencheny
95b1e257bb Merge pull request #6633 from home-assistant/deprecate-event-forwarding
Deprecate event forwarding
2017-03-15 22:27:49 -07:00
Adam Mills
b06cf87c74 Kodi: Fix episode media type classification (#6645) 2017-03-15 22:07:30 -07:00
Adam Mills
326337777a Add ZWave cover tests (#6648) 2017-03-15 22:06:37 -07:00
mvillarejo
2c8a06bfbe media_player.kodi extra attributes for tvshow and music media (#6622)
* media_player.kodi extra attributes for tvshow and music media

* removed extra whitespaces/CR

* Kodi - add extra attributes #6250 (removed music attributes)

* Restored music attributes, this is ready for merge

* linting amended

* Fix Kodi artist support

* Copy-paste error

* Fix for non-music artist lookup

Kodi returns an emtpy list on videos, so we need to be able to
handle that as well.
2017-03-15 19:51:31 -04:00
Pascal Vizeli
198a234468 aioHttp 1.3.4 (#6643) 2017-03-15 22:30:46 +01:00
Paulus Schoutsen
e94aa3afe9 Deprecate event forwarding 2017-03-15 08:38:26 -07:00
Paulus Schoutsen
96e22c7b41 Remove event decorators (#6634) 2017-03-15 14:46:57 +01:00
Nathan Henrie
33450c726d Use sqlite's WAL mode to avoid database is locked errors (#6519)
* Use sqlite's WAL mode to avoid `database is locked` errors

- Relevant issue: https://github.com/home-assistant/home-assistant/issues/4780

Code:

- http://stackoverflow.com/a/23661501/1588795
- http://docs.sqlalchemy.org/en/rel_0_9/dialects/sqlite.html#foreign-key-support
- https://github.com/g2p/bedup/pull/86/files

* Only set WAL if using sqlite

* Reorder imports

* Fix pylint warnings
2017-03-15 00:14:12 -07:00
Yum
97b9d3bd21 since knx_2_float can't handle 0, bypass converting 0 value from knx to float (#6626) 2017-03-14 20:25:52 -07:00
Adam Mills
fff589eeab Correctly flag Kodi media types (#6628) 2017-03-14 20:25:15 -07:00
Adam Mills
5e5d2e8ab8 Tests for ZWave climate (#6629)
But wait, there's more! Also a few fixes discovered writing the tests.
2017-03-14 20:24:35 -07:00
Paulus Schoutsen
1a7ffdca52 Add "Refactor zwave discovery to entity schema" (#6565)
* Revert "Revert "Refactor zwave discovery to entity schema (#6445)" (#6564)"

This reverts commit 58826b264a.

* Update zwave tests for enitity schema

* Fix merge error

* Switch dict_id to id(self)
2017-03-14 19:55:33 -04:00
Erik Eriksson
cada74df22 Merge pull request #6583 from molobrakos/voc-fix
Bump VOC version (fixes heater bug)
2017-03-14 23:37:32 +01:00
Johan Bloemberg
bd3fbe8363 Upgrade to dsmr_parser 0.8, supporting protocol 3 and 5. (#6600)
* Upgrade to dsmr_parser 0.8, supporting protocol 3 and 5.

* Update tests for new import.
2017-03-14 20:16:43 +01:00
Daniyar Yeralin
4d9c7d9684 Update mpd.py (#6553)
* Update mpd.py

Introducing new parameter "Name"

* Update mpd.py

Change `CONF_LOCATION` to `CONF_NAME`
2017-03-14 20:10:35 +01:00
ArrayLabs
0bf66384ed Cover myq fix update pymyq (#6595)
* update pymyq to 0.0.8

update to correct wrong version in setup.py

* update pymyq to 0.0.8

update to correct wrong version in setup.py
2017-03-14 20:06:30 +01:00
tflack
c7798ef43c Define db for SHOW DIAGNOSTICS query since some users will not have a… (#6566)
* Define db for SHOW DIAGNOSTICS query since some users will not have admin perms

* fix white space error from CI
2017-03-14 11:46:46 -07:00
hawk259
f4d8095e54 Add configurable timeout option to notify/smtp (#6609)
* Add configurable timeout option to notify/smtp

* Updated smtp test to include timeout param

* fixed 80 column style issue
2017-03-14 19:00:16 +01:00
Pascal Vizeli
5529d77c62 Prevent entities running multiple updates simultaneously (#6511)
* Protect entity for multible updates on same time.

* Address all comments / make update more robust

* fix unittest

* fix lint

* address comments
2017-03-14 09:26:55 -07:00
Erik Eriksson
c4e151f621 Error handling when connection refused (#6614)
Add an optional extended description…
2017-03-14 10:08:40 +01:00
Fabian Affolter
f58941a0d4 Upgrade googlemaps to 2.4.6 (#6611) 2017-03-14 07:54:19 +01:00
Fabian Affolter
bca673f039 Upgrade py-cpuinfo to 0.2.7 (#6610) 2017-03-14 07:54:10 +01:00
Fabian Affolter
2687f2f623 Fix link (#6612) 2017-03-14 07:53:48 +01:00
Johann Kellerman
134b3d2f3b Update SMA solar sensor to work with the new add_devices callback (#6602) 2017-03-14 06:39:30 +02:00
Dale Higgs
f450c1351c Update pyecobee version to 0.0.7 (#6593) 2017-03-13 21:19:51 +01:00
Fabian Affolter
c6b10f3703 Upgrade Sphinx to 1.5.3 (#6587) 2017-03-13 21:05:27 +01:00
Fabian Affolter
4a08067b9c Upgrade psutil to 5.2.0 (#6585) 2017-03-13 21:05:07 +01:00
Fabian Affolter
9c37437a59 Upgrade sqlalchemy to 1.1.6 (#6591) 2017-03-13 21:02:28 +01:00
Fabian Affolter
9330142987 Upgrade pyasn1 to 0.2.3 (#6588) 2017-03-13 21:01:45 +01:00
Fabian Affolter
2bbaac44d4 Upgrade async_timeout to 1.2.0 (#6590) 2017-03-13 21:01:25 +01:00
Thibault Cohen
253dee8e4d Fix hydroquebec (#6574) 2017-03-13 18:54:23 +01:00
Andrey
5722cf53bf Fix for the case of zwave value used in several devices. (#6577) 2017-03-13 12:19:45 -05:00
Pascal Vizeli
5d301590c3 Remove dispatcher camera (#6579) 2017-03-13 17:54:28 +01:00
Erik
353f5d6b49 bump voc version (fixes heater bug) 2017-03-13 17:28:27 +01:00
Jesse Newland
11da7bed12 Don't start the push updater if the Apple TV is 'off' (#6552)
Add an optional extended description…
2017-03-13 14:23:15 +01:00
Kevin Fronczak
a358c8e10d Upgraded blinkpy version, increased Throttle time for camera (#6561) 2017-03-13 00:12:48 -07:00
Paulus Schoutsen
58826b264a Revert "Refactor zwave discovery to entity schema (#6445)" (#6564)
This reverts commit 56abc7f9b4.
2017-03-12 23:35:10 -07:00
Adam Mills
56abc7f9b4 Refactor zwave discovery to entity schema (#6445)
* Refactor zwave discovery to entity schema

* Address PR concerns

* Split DISCOVERY_SCHEMAS into separate file

* Only check cover reverse workaround once
2017-03-12 23:13:34 -07:00
Adam Mills
55d60a6a13 ZWave binary sensor tests (#6555)
* ZWave binary sensor tests

* Test fixes

* Improve coverage of features
2017-03-12 22:08:53 -07:00
Dennis de Greef
5183cb5903 Be able to select mqtt:tls_version for Python < 3.6 (#6442)
* Be able to select tls_version

* This test should always assert this value, not only in 3.6

* Disable linting on future property (py36)

* Only allow TLS 1.0, 1.1 and 1.2

* Fix line length issue

* Fix check config tests

* Allow auto as a TLS version
2017-03-12 22:02:59 -07:00
Tyler Page
0aa8933df6 Fix wake_on_lan ping with None as host (#6532)
* Update configuration validation

With the new update, wake_on_lan requires a host key in the configuration

* cast self._host to str as requested

* Changed host key back to optional
2017-03-12 20:46:58 +01:00
siebert
fc46a24996 Fix gen_requirements_all.py script for Windows. (#6547) 2017-03-12 21:08:49 +02:00
Paulus Schoutsen
5be58bd056 Simplify Android IP webcam discovery (#6528) 2017-03-12 18:56:48 +01:00
Lewis Juggins
4a423e63f3 Version bump to 0.41.0.dev0 2017-03-12 08:34:35 +00:00
Adam Mills
157ab77232 Update Kodi notifier to async (#6497)
* Update Kodi notifier to async

* Change Kodi CONF_SSL to CONF_PROXY_SSL
2017-03-11 10:41:05 -08:00
Anders Melchiorsen
9a86ccaaea Fix colortemp conversion for osramlightify (#6516)
* Fix colortemp conversion for osramlightify

Copied from the LIFX fix in 75df4be733.

* Fix style

* Updates from review

@armills:

While we're doing cleanup here, can you just change self._brightness,
self._rgb, self._name, self._temperature, and self._state assignments in
__init__ to None? These will get overwritten when self.update() is called, so
it's safer/cleaner to initialize them to None since it shouldn't matter if
everything is working.
2017-03-11 10:40:16 -08:00
Greg Dowling
32dd815852 Discovery is a dict rather than an array. (#6525) 2017-03-11 10:39:26 -08:00
Boris K
9ac3928600 Add type configuration in history_stats (#6430) 2017-03-11 10:38:18 -08:00
William Scanlon
62e57456e1 Wink scene(shortcut) support (#6147)
* Wink scene(shortcut) support

* Scenes to Scene

* Moved wink scenes from switches to scenes

* Updated python-wink version
2017-03-11 10:18:29 -08:00
Róbert Nagy
11f11481b2 Force update support for MQTT sensor (#6492) 2017-03-11 10:07:52 -08:00
Greg Dowling
10f5e9744b Append vera device id to entity id - but not name. (#6523)
* Append vera device id to entity id - but not name.

* Tidy.

* Tidy.

* Tidy after review.

* Re-order.
2017-03-11 10:06:46 -08:00
Paulus Schoutsen
b2a2193ba3 Remove mint finance sensor (#6522) 2017-03-11 01:02:32 -08:00
Paulus Schoutsen
493c0bbb4c Update frontend 2017-03-10 22:51:44 -08:00
Martin Hjelmare
13dd17b2ab Fix mysensors gateway windows setup (#6500) 2017-03-11 03:30:23 +01:00
Pascal Vizeli
44da43065f Android webcam better error handling / pump library 0.4 (#6518) 2017-03-10 23:10:35 +01:00
Pascal Bach
ffb1613d55 [packaging] Include LICENSE.md in tarball (#6514) 2017-03-10 19:59:38 +00:00
Pascal Vizeli
b5fb558c62 Bugfix rpi_rf cleanup (#6513)
Add an optional extended description…
2017-03-10 14:56:13 +01:00
Robbie Trencheny
846a0513c7 Don't allow sending to invalid iOS targets (#6115)
* Don't allow sending to invalid targets

* Fix valid target check
2017-03-10 12:32:43 +01:00
Caleb
b705b3ddb9 Update to Pyunifi2.0 (#6490)
* Updated pyUnifi

* Missing comma

* Security opt-out, not opt-in

* Adjust minimal values

* Update to pyUnifi 2.0
2017-03-10 11:15:21 +01:00
Craig J. Ward
330d352d3a Insteon lib (#6505)
* use lib with caching to reduce collisions

* use 0.43

* change requirements

* update the  lib to 0.44

* update req

* fix typo

* just keep checking

* just keep checking - switch

* use 0.45 with file cache

* requirements

* Update requirements_all.txt

* Update insteon_local.py

* Update requirements_all.txt

* Update insteon_local.py

* update library

* fix lint
2017-03-10 11:14:31 +01:00
Pascal Vizeli
49308bec13 Bugfix android camera autodiscovery settings (#6510)
Add an optional extended description…
2017-03-10 10:10:35 +01:00
420 changed files with 17676 additions and 6670 deletions

View File

@@ -8,6 +8,9 @@ omit =
homeassistant/helpers/signal.py
# omit pieces of code that rely on external devices being present
homeassistant/components/alarmdecoder.py
homeassistant/components/*/alarmdecoder.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
@@ -59,6 +62,9 @@ omit =
homeassistant/components/lutron.py
homeassistant/components/*/lutron.py
homeassistant/components/lutron_caseta.py
homeassistant/components/*/lutron_caseta.py
homeassistant/components/modbus.py
homeassistant/components/*/modbus.py
@@ -91,6 +97,9 @@ omit =
homeassistant/components/*/thinkingcleaner.py
homeassistant/components/tradfri.py
homeassistant/components/*/tradfri.py
homeassistant/components/twilio.py
homeassistant/components/notify/twilio_sms.py
homeassistant/components/notify/twilio_call.py
@@ -115,9 +124,6 @@ omit =
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave/*
homeassistant/components/*/zwave.py
homeassistant/components/enocean.py
homeassistant/components/*/enocean.py
@@ -145,16 +151,21 @@ omit =
homeassistant/components/maxcube.py
homeassistant/components/*/maxcube.py
homeassistant/components/tado.py
homeassistant/components/*/tado.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/concord232.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/alarm_control_panel/simplisafe.py
homeassistant/components/alarm_control_panel/totalconnect.py
homeassistant/components/apiai.py
homeassistant/components/binary_sensor/arest.py
homeassistant/components/binary_sensor/concord232.py
homeassistant/components/binary_sensor/flic.py
homeassistant/components/binary_sensor/hikvision.py
homeassistant/components/binary_sensor/iss.py
homeassistant/components/binary_sensor/ping.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/browser.py
homeassistant/components/camera/amcrest.py
@@ -220,17 +231,22 @@ omit =
homeassistant/components/light/flux_led.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
homeassistant/components/light/lifx.py
homeassistant/components/light/lifx/*.py
homeassistant/components/light/lifx_legacy.py
homeassistant/components/light/limitlessled.py
homeassistant/components/light/mystrom.py
homeassistant/components/light/osramlightify.py
homeassistant/components/light/rpi_gpio_pwm.py
homeassistant/components/light/piglow.py
homeassistant/components/light/tikteck.py
homeassistant/components/light/tradfri.py
homeassistant/components/light/x10.py
homeassistant/components/light/yeelight.py
homeassistant/components/light/yeelightsunflower.py
homeassistant/components/light/piglow.py
homeassistant/components/light/zengge.py
homeassistant/components/lirc.py
homeassistant/components/lock/nuki.py
homeassistant/components/lock/lockitron.py
homeassistant/components/media_player/anthemav.py
homeassistant/components/media_player/apple_tv.py
homeassistant/components/media_player/aquostv.py
@@ -267,8 +283,10 @@ omit =
homeassistant/components/media_player/samsungtv.py
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/spotify.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/vlc.py
homeassistant/components/media_player/volumio.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/notify/aws_lambda.py
homeassistant/components/notify/aws_sns.py
@@ -307,6 +325,7 @@ omit =
homeassistant/components/remote/harmony.py
homeassistant/components/remote/itach.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/scene/lifx_cloud.py
homeassistant/components/sensor/amcrest.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/arwn.py
@@ -318,6 +337,7 @@ omit =
homeassistant/components/sensor/coinmarketcap.py
homeassistant/components/sensor/comed_hourly_pricing.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/crimereports.py
homeassistant/components/sensor/cups.py
homeassistant/components/sensor/currencylayer.py
homeassistant/components/sensor/darksky.py
@@ -327,7 +347,7 @@ omit =
homeassistant/components/sensor/dovado.py
homeassistant/components/sensor/dte_energy_bridge.py
homeassistant/components/sensor/ebox.py
homeassistant/components/sensor/efergy.py
homeassistant/components/sensor/eddystone_temperature.py
homeassistant/components/sensor/eliqonline.py
homeassistant/components/sensor/emoncms.py
homeassistant/components/sensor/fastdotcom.py
@@ -352,9 +372,12 @@ omit =
homeassistant/components/sensor/lastfm.py
homeassistant/components/sensor/linux_battery.py
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/lyft.py
homeassistant/components/sensor/metoffice.py
homeassistant/components/sensor/miflora.py
homeassistant/components/sensor/modem_callerid.py
homeassistant/components/sensor/mqtt_room.py
homeassistant/components/sensor/mvglive.py
homeassistant/components/sensor/netdata.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nut.py
@@ -363,6 +386,7 @@ omit =
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openevse.py
homeassistant/components/sensor/openexchangerates.py
homeassistant/components/sensor/opensky.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/pi_hole.py
homeassistant/components/sensor/plex.py
@@ -420,15 +444,17 @@ omit =
homeassistant/components/switch/tplink.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/telegram_webhooks.py
homeassistant/components/telegram_bot/*
homeassistant/components/thingspeak.py
homeassistant/components/tts/amazon_polly.py
homeassistant/components/tts/picotts.py
homeassistant/components/upnp.py
homeassistant/components/weather/bom.py
homeassistant/components/weather/metoffice.py
homeassistant/components/weather/openweathermap.py
homeassistant/components/weather/zamg.py
homeassistant/components/zeroconf.py
homeassistant/components/zwave/util.py
[report]

11
.gitignore vendored
View File

@@ -1,15 +1,4 @@
config/*
!config/home-assistant.conf.default
# There is not a better solution afaik..
!config/custom_components
config/custom_components/*
!config/custom_components/example.py
!config/custom_components/hello_world.py
!config/custom_components/mqtt_example.py
!config/panels
config/panels/*
!config/panels/react.html
tests/testing_config/deps
tests/testing_config/home-assistant.log

View File

@@ -8,6 +8,7 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
#ENV INSTALL_OPENZWAVE no
#ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no
#ENV INSTALL_COAP_CLIENT no
VOLUME /config
@@ -21,7 +22,7 @@ RUN virtualization/Docker/setup_docker_prereqs
# Install hass component dependencies
COPY requirements_all.txt requirements_all.txt
RUN pip3 install --no-cache-dir -r requirements_all.txt && \
pip3 install --no-cache-dir mysqlclient psycopg2 uvloop
pip3 install --no-cache-dir mysqlclient psycopg2 uvloop cchardet
# Copy source
COPY . .

View File

@@ -1,5 +1,5 @@
include README.rst
include LICENSE
include LICENSE.md
graft homeassistant
prune homeassistant/components/frontend/www_static/home-assistant-polymer
recursive-exclude * *.py[co]

View File

@@ -1,9 +1,7 @@
Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/home-assistant/home-assistant| |Join the dev chat at https://gitter.im/home-assistant/home-assistant/devs|
==============================================================================================================================================================================================
Home Assistant is a home automation platform running on Python 3. The
goal of Home Assistant is to be able to track and control all devices at
home and offer a platform for automating control.
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.
To get started:
@@ -12,83 +10,22 @@ To get started:
python3 -m pip install homeassistant
hass --open-ui
Check out `the website <https://home-assistant.io>`__ for `a
demo <https://home-assistant.io/demo/>`__, installation instructions,
tutorials and documentation.
Check out `home-assistant.io <https://home-assistant.io>`__ for `a
demo <https://home-assistant.io/demo/>`__, `installation instructions <https://home-assistant.io/getting-started/>`__,
`tutorials <https://home-assistant.io/getting-started/automation-2/>`__ and `documentation <https://home-assistant.io/docs/>`__.
|screenshot-states|
Examples of devices Home Assistant can interface with:
Featured integrations
---------------------
- Monitoring connected devices to a wireless router:
`OpenWrt <https://openwrt.org/>`__,
`Tomato <http://www.polarcloud.com/tomato>`__,
`Netgear <http://netgear.com>`__,
`DD-WRT <http://www.dd-wrt.com/site/index>`__,
`TPLink <http://www.tp-link.us/>`__,
`ASUSWRT <http://event.asus.com/2013/nw/ASUSWRT/>`__,
`Xiaomi <http://miwifi.com/>`__ and any SNMP
capable Linksys WAP/WRT
- `Philips Hue <http://meethue.com>`__ lights,
`WeMo <http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/>`__
switches, `Edimax <http://www.edimax.com/>`__ switches,
`Efergy <https://efergy.com>`__ energy monitoring, and
`Tellstick <http://www.telldus.se/products/tellstick>`__ devices and
sensors
- `Google
Chromecasts <http://www.google.com/intl/en/chrome/devices/chromecast>`__,
`Music Player Daemon <http://www.musicpd.org/>`__, `Logitech
Squeezebox <https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29>`__,
`Plex <https://plex.tv/>`__, `Kodi (XBMC) <http://kodi.tv/>`__,
iTunes (by way of
`itunes-api <https://github.com/maddox/itunes-api>`__), and Amazon
Fire TV (by way of
`python-firetv <https://github.com/happyleavesaoc/python-firetv>`__)
- Support for
`ISY994 <https://www.universal-devices.com/residential/isy994i-series/>`__
(Insteon and X10 devices), `Z-Wave <http://www.z-wave.com/>`__, `Nest
Thermostats <https://nest.com/>`__,
`RFXtrx <http://www.rfxcom.com/>`__,
`Arduino <https://www.arduino.cc/>`__, `Raspberry
Pi <https://www.raspberrypi.org/>`__, and
`Modbus <http://www.modbus.org/>`__
- Interaction with `IFTTT <https://ifttt.com/>`__
- Integrate data from the `Bitcoin <https://bitcoin.org>`__ network,
meteorological data from
`OpenWeatherMap <http://openweathermap.org/>`__ and
`Forecast.io <https://forecast.io/>`__,
`Transmission <http://www.transmissionbt.com/>`__, or
`SABnzbd <http://sabnzbd.org>`__.
- `See full list of supported
devices <https://home-assistant.io/components/>`__
|screenshot-components|
Build home automation on top of your devices:
- Keep a precise history of every change to the state of your house
- Turn on the lights when people get home after sunset
- Turn on lights slowly during sunset to compensate for less light
- Turn off all lights and devices when everybody leaves the house
- Offers a `REST API <https://home-assistant.io/developers/rest_api/>`__
and can interface with MQTT for easy integration with other projects
like `OwnTracks <http://owntracks.org/>`__
- Allow sending notifications using
`Instapush <https://instapush.im>`__, `Notify My Android
(NMA) <http://www.notifymyandroid.com/>`__,
`PushBullet <https://www.pushbullet.com/>`__,
`PushOver <https://pushover.net/>`__,
`Slack <https://slack.com/>`__,
`Telegram <https://telegram.org/>`__, `Join <http://joaoapps.com/join/>`__, and `Jabber
(XMPP) <http://xmpp.org>`__
The system is built using a modular approach so support for other devices or actions can
be implemented easily. See also the `section on
architecture <https://home-assistant.io/developers/architecture/>`__
and the `section on creating your own
The system is built using a modular approach so support for other devices or actions can be implemented easily. See also the `section on architecture <https://home-assistant.io/developers/architecture/>`__ and the `section on creating your own
components <https://home-assistant.io/developers/creating_components/>`__.
If you run into issues while using Home Assistant or during development
of a component, check the `Home Assistant help
section <https://home-assistant.io/help/>`__ of our website for further help and information.
of a component, check the `Home Assistant help section <https://home-assistant.io/help/>`__ of our website for further help and information.
.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master
:target: https://travis-ci.org/home-assistant/home-assistant
@@ -100,3 +37,5 @@ section <https://home-assistant.io/help/>`__ of our website for further help and
:target: https://gitter.im/home-assistant/home-assistant/devs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |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
:target: https://home-assistant.io/components/

View File

@@ -1,158 +0,0 @@
homeassistant:
# Omitted values in this section will be auto detected using freegeoip.io
# Location required to calculate the time the sun rises and sets.
# Coordinates are also used for location for weather related components.
# Google Maps can be used to determine more precise GPS coordinates.
latitude: 32.87336
longitude: 117.22743
# Impacts weather/sunrise data
elevation: 665
# 'metric' for Metric System, 'imperial' for imperial system
unit_system: metric
# Pick yours from here:
# http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
time_zone: America/Los_Angeles
# Name of the location where Home Assistant is running
name: Home
http:
api_password: mypass
# Set to 1 to enable development mode
# development: 1
# Enable the frontend
frontend:
light:
# platform: hue
wink:
# Get your token at https://winkbearertoken.appspot.com
access_token: 'YOUR_TOKEN'
device_tracker:
# The following tracker are available:
# https://home-assistant.io/components/#presence-detection
platform: netgear
host: 192.168.1.1
username: admin
password: PASSWORD
switch:
platform: wemo
climate:
platform: nest
# Required: username and password that are used to login to the Nest thermostat.
username: myemail@mydomain.com
password: mypassword
downloader:
download_dir: downloads
notify:
platform: pushbullet
api_key: ABCDEFGHJKLMNOPQRSTUVXYZ
device_sun_light_trigger:
# Optional: specify a specific light/group of lights that has to be turned on
light_group: group.living_room
# Optional: specify which light profile to use when turning lights on
light_profile: relax
# Optional: disable lights being turned off when everybody leaves the house
# disable_turn_off: 1
# A comma separated list of states that have to be tracked as a single group
# Grouped states should share the same type of states (ON/OFF or HOME/NOT_HOME)
# You can also have groups within groups.
# https://home-assistant.io/components/group/
group:
default_view:
view: yes
entities:
- group.awesome_people
- group.climate
kitchen:
name: Kitchen
entities:
- switch.kitchen_pin_3
upstairs:
name: Kids
icon: mdi:account-multiple
view: yes
entities:
- input_boolean.notify_home
- camera.demo_camera
browser:
keyboard:
# https://home-assistant.io/getting-started/automation/
automation:
- alias: Turn on light when sun sets
trigger:
platform: sun
event: sunset
offset: "-01:00:00"
condition:
condition: state
entity_id: group.all_devices
state: 'home'
action:
service: light.turn_on
# Another way to do is to collect all entries under one "sensor:"
# sensor:
# - platform: mqtt
# name: "MQTT Sensor 1"
# - platform: mqtt
# name: "MQTT Sensor 2"
#
# Details: https://home-assistant.io/getting-started/devices/
sensor:
platform: systemmonitor
resources:
- type: 'disk_use_percent'
arg: '/'
- type: 'disk_use_percent'
arg: '/home'
sensor 2:
platform: cpuspeed
script:
wakeup:
alias: Wake Up
sequence:
- event: LOGBOOK_ENTRY
event_data:
name: Paulus
message: is waking up
entity_id: device_tracker.paulus
domain: light
- alias: Bedroom lights on
service: light.turn_on
data:
entity_id: group.bedroom
brightness: 100
- delay:
minutes: 1
- alias: Living room lights on
service: light.turn_on
data:
entity_id: group.living_room
scene:
- name: Romantic
entities:
light.tv_back_light: on
light.ceiling:
state: on
xy_color: [0.33, 0.66]
brightness: 200

View File

@@ -1,149 +0,0 @@
"""
Example of a custom component.
Example component to target an entity_id to:
- turn it on at 7AM in the morning
- turn it on if anyone comes home and it is off
- turn it off if all lights are turned off
- turn it off if all people leave the house
- offer a service to turn it on for 10 seconds
Configuration:
To use the Example custom component you will need to add the following to
your configuration.yaml file.
example:
target: TARGET_ENTITY
Variable:
target
*Required
TARGET_ENTITY should be one of your devices that can be turned on and off,
ie a light or a switch. Example value could be light.Ceiling or switch.AC
(if you have these devices with those names).
"""
import time
import logging
from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_ON, STATE_OFF
from homeassistant.helpers import validate_config
from homeassistant.helpers.event_decorators import \
track_state_change, track_time_change
from homeassistant.helpers.service import service
import homeassistant.components as core
from homeassistant.components import device_tracker
from homeassistant.components import light
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "example"
# List of component names (string) your component depends upon.
# We depend on group because group will be loaded after all the components that
# initialize devices have been setup.
DEPENDENCIES = ['group', 'device_tracker', 'light']
# Configuration key for the entity id we are targeting.
CONF_TARGET = 'target'
# Variable for storing configuration parameters.
TARGET_ID = None
# Name of the service that we expose.
SERVICE_FLASH = 'flash'
# Shortcut for the logger
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
"""Setup example component."""
global TARGET_ID
# Validate that all required config options are given.
if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER):
return False
TARGET_ID = config[DOMAIN][CONF_TARGET]
# Validate that the target entity id exists.
if hass.states.get(TARGET_ID) is None:
_LOGGER.error("Target entity id %s does not exist",
TARGET_ID)
# Tell the bootstrapper that we failed to initialize and clear the
# stored target id so our functions don't run.
TARGET_ID = None
return False
# Tell the bootstrapper that we initialized successfully.
return True
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
def track_devices(hass, entity_id, old_state, new_state):
"""Called when the group.all devices change state."""
# If the target id is not set, return
if not TARGET_ID:
return
# If anyone comes home and the entity is not on, turn it on.
if new_state.state == STATE_HOME and not core.is_on(hass, TARGET_ID):
core.turn_on(hass, TARGET_ID)
# If all people leave the house and the entity is on, turn it off.
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
@track_time_change(hour=7, minute=0, second=0)
def wake_up(hass, now):
"""Turn light on in the morning.
Turn the light on at 7 AM if there are people home and it is not already
on.
"""
if not TARGET_ID:
return
if device_tracker.is_on(hass) and not core.is_on(hass, TARGET_ID):
_LOGGER.info('People home at 7AM, turning it on')
core.turn_on(hass, TARGET_ID)
@track_state_change(light.ENTITY_ID_ALL_LIGHTS, STATE_ON, STATE_OFF)
def all_lights_off(hass, entity_id, old_state, new_state):
"""If all lights turn off, turn off."""
if not TARGET_ID:
return
if core.is_on(hass, TARGET_ID):
_LOGGER.info('All lights have been turned off, turning it off')
core.turn_off(hass, TARGET_ID)
@service(DOMAIN, SERVICE_FLASH)
def flash_service(hass, call):
"""Service that will toggle the target.
Set the light to off for 10 seconds if on and vice versa.
"""
if not TARGET_ID:
return
if core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
time.sleep(10)
core.turn_on(hass, TARGET_ID)
else:
core.turn_on(hass, TARGET_ID)
time.sleep(10)
core.turn_off(hass, TARGET_ID)

View File

@@ -1,27 +0,0 @@
"""
The "hello world" custom component.
This component implements the bare minimum that a component should implement.
Configuration:
To use the hello_word component you will need to add the following to your
configuration.yaml file.
hello_world:
"""
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "hello_world"
# List of component names (string) your component depends upon.
DEPENDENCIES = []
def setup(hass, config):
"""Setup our skeleton component."""
# States are in the format DOMAIN.OBJECT_ID.
hass.states.set('hello_world.Hello_World', 'Works!')
# Return boolean to indicate that initialization was successfully.
return True

View File

@@ -1,55 +0,0 @@
"""
Example of a custom MQTT component.
Shows how to communicate with MQTT. Follows a topic on MQTT and updates the
state of an entity to the last message received on that topic.
Also offers a service 'set_state' that will publish a message on the topic that
will be passed via MQTT to our message received listener. Call the service with
example payload {"new_state": "some new state"}.
Configuration:
To use the mqtt_example component you will need to add the following to your
configuration.yaml file.
mqtt_example:
topic: "home-assistant/mqtt_example"
"""
import homeassistant.loader as loader
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "mqtt_example"
# List of component names (string) your component depends upon.
DEPENDENCIES = ['mqtt']
CONF_TOPIC = 'topic'
DEFAULT_TOPIC = 'home-assistant/mqtt_example'
def setup(hass, config):
"""Setup the MQTT example component."""
mqtt = loader.get_component('mqtt')
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
entity_id = 'mqtt_example.last_message'
# Listen to a message on MQTT.
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
hass.states.set(entity_id, payload)
mqtt.subscribe(hass, topic, message_received)
hass.states.set(entity_id, 'No messages')
# Service to publish a message on MQTT.
def set_state_service(call):
"""Service to send a message."""
mqtt.publish(hass, topic, call.data.get('new_state'))
# Register our service with Home Assistant.
hass.services.register(DOMAIN, 'set_state', set_state_service)
# Return boolean to indicate that initialization was successfully.
return True

View File

@@ -1,432 +0,0 @@
<!--
Custom Home Assistant panel example.
Currently only works in Firefox and Chrome because it uses ES6.
Make sure this file is in <config>/panels/react.html
Add to your configuration.yaml:
panel_custom:
- name: react
sidebar_title: TodoMVC
sidebar_icon: mdi:checkbox-marked-outline
config:
title: Wow hello!
-->
<script src="https://fb.me/react-15.2.1.min.js"></script>
<script src="https://fb.me/react-dom-15.2.1.min.js"></script>
<!-- for development, replace with:
<script src="https://fb.me/react-15.2.1.js"></script>
<script src="https://fb.me/react-dom-15.2.1.js"></script>
-->
<!--
CSS taken from ReactJS TodoMVC example by Pete Hunt
http://todomvc.com/examples/react/
-->
<style>
.todoapp input[type="checkbox"] {
outline: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.todoapp .main {
position: relative;
border-top: 1px solid #e6e6e6;
}
.todoapp .todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todoapp .todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todoapp .todo-list li:last-child {
border-bottom: none;
}
.todoapp .todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
cursor: pointer;
}
.todoapp .todo-list li .toggle:focus {
border-left: 3px solid rgba(175, 47, 47, 0.35);
}
.todoapp .todo-list li .toggle:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
}
.todoapp .todo-list li .toggle:checked:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
}
.todoapp .todo-list li label {
white-space: pre-line;
word-break: break-all;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
.todoapp .todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todoapp .footer {
color: #777;
padding: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.todoapp .footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todoapp .todo-count {
float: left;
text-align: left;
font-weight: 300;
}
.todoapp .toggle-menu {
position: absolute;
right: 15px;
font-weight: 300;
color: rgba(175, 47, 47, 0.75);
}
.todoapp .filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.todoapp .filters li {
display: inline;
}
.todoapp .filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.todoapp .filters li a.selected,
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.todoapp .filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.todoapp .toggle-all,
.todoapp .todo-list li .toggle {
background: none;
}
.todoapp .todo-list li .toggle {
height: 40px;
}
.todoapp .toggle-all {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
@media (max-width: 430px) {
.todoapp .footer {
height: 50px;
}
.todoapp .filters {
bottom: 10px;
}
}
</style>
<dom-module id='ha-panel-react'>
<template>
<style>
:host {
background: #f5f5f5;
display: block;
height: 100%;
overflow: auto;
}
.mount {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
font-weight: 300;
}
</style>
<div id='mount' class='mount'></div>
</template>
</dom-module>
<script>
// Example uses ES6. Will only work in modern browsers
class TodoMVC extends React.Component {
constructor(props) {
super(props);
this.state = {
filter: 'all',
// load initial value of entities
entities: this.props.hass.reactor.evaluate(
this.props.hass.entityGetters.visibleEntityMap),
};
}
componentDidMount() {
// register to entity updates
this._unwatchHass = this.props.hass.reactor.observe(
this.props.hass.entityGetters.visibleEntityMap,
entities => this.setState({entities}))
}
componentWillUnmount() {
// unregister to entity updates
this._unwatchHass();
}
handlePickFilter(filter, ev) {
ev.preventDefault();
this.setState({filter});
}
handleEntityToggle(entity, ev) {
this.props.hass.serviceActions.callService(
entity.domain, 'toggle', { entity_id: entity.entityId });
}
handleToggleMenu(ev) {
ev.preventDefault();
Polymer.Base.fire('open-menu', null, {node: ev.target});
}
entityRow(entity) {
const completed = entity.state === 'on';
return React.createElement(
'li', {
className: completed && 'completed',
key: entity.entityId,
},
React.createElement(
"div", { className: "view" },
React.createElement(
"input", {
checked: completed,
className: "toggle",
type: "checkbox",
onChange: ev => this.handleEntityToggle(entity, ev),
}),
React.createElement("label", null, entity.entityDisplay)));
}
filterRow(filter) {
return React.createElement(
"li", { key: filter },
React.createElement(
"a", {
href: "#",
className: this.state.filter === filter && "selected",
onClick: ev => this.handlePickFilter(filter, ev),
},
filter.substring(0, 1).toUpperCase() + filter.substring(1)
)
);
}
render() {
const { entities, filter } = this.state;
if (!entities) return null;
const filters = ['all', 'light', 'switch'];
const showEntities = filter === 'all' ?
entities.filter(ent => filters.includes(ent.domain)) :
entities.filter(ent => ent.domain == filter);
return React.createElement(
'div', { className: 'todoapp-wrapper' },
React.createElement(
"section", { className: "todoapp" },
React.createElement(
"div", null,
React.createElement(
"header", { className: "header" },
React.createElement("h1", null, this.props.title || "todos")
),
React.createElement(
"section", { className: "main" },
React.createElement(
"ul", { className: "todo-list" },
showEntities.valueSeq().map(ent => this.entityRow(ent)))
)
),
React.createElement(
"footer", { className: "footer" },
React.createElement(
"span", { className: "todo-count" },
showEntities.filter(ent => ent.state === 'off').size + " items left"
),
React.createElement(
"ul", { className: "filters" },
filters.map(filter => this.filterRow(filter))
),
!this.props.showMenu && React.createElement(
"a", {
className: "toggle-menu",
href: '#',
onClick: ev => this.handleToggleMenu(ev),
},
"Show menu"
)
)
));
}
}
Polymer({
is: 'ha-panel-react',
properties: {
// Home Assistant object
hass: {
type: Object,
},
// If should render in narrow mode
narrow: {
type: Boolean,
value: false,
},
// If sidebar is currently shown
showMenu: {
type: Boolean,
value: false,
},
// Home Assistant panel info
// panel.config contains config passed to register_panel serverside
panel: {
type: Object,
}
},
// This will make sure we forward changed properties to React
observers: [
'propsChanged(hass, narrow, showMenu, panel)',
],
// Mount React when element attached
attached: function () {
this.mount(this.hass, this.narrow, this.showMenu, this.panel);
},
// Called when properties change
propsChanged: function (hass, narrow, showMenu, panel) {
this.mount(hass, narrow, showMenu, panel);
},
// Render React. Debounce in case multiple properties change.
mount: function (hass, narrow, showMenu, panel) {
this.debounce('mount', function () {
ReactDOM.render(React.createElement(TodoMVC, {
hass: hass,
narrow: narrow,
showMenu: showMenu,
title: panel.config ? panel.config.title : null
}), this.$.mount);
}.bind(this));
},
// Unmount React node when panel no longer in use.
detached: function () {
ReactDOM.unmountComponentAtNode(this.$.mount);
},
});
</script>

BIN
docs/screenshot-components.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -20,6 +20,17 @@ from homeassistant.const import (
from homeassistant.util.async import run_callback_threadsafe
def attempt_use_uvloop():
"""Attempt to use uvloop."""
import asyncio
try:
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
except ImportError:
pass
def monkey_patch_asyncio():
"""Replace weakref.WeakSet to address Python 3 bug.
@@ -255,10 +266,13 @@ def closefds_osx(min_fd: int, max_fd: int) -> None:
def cmdline() -> List[str]:
"""Collect path and arguments to re-execute the current hass instance."""
if sys.argv[0].endswith('/__main__.py'):
if sys.argv[0].endswith(os.path.sep + '__main__.py'):
modulepath = os.path.dirname(sys.argv[0])
os.environ['PYTHONPATH'] = os.path.dirname(modulepath)
return [sys.executable] + [arg for arg in sys.argv if arg != '--daemon']
return [sys.executable] + [arg for arg in sys.argv if
arg != '--daemon']
else:
return [arg for arg in sys.argv if arg != '--daemon']
def setup_and_run_hass(config_dir: str,
@@ -308,8 +322,7 @@ def setup_and_run_hass(config_dir: str,
EVENT_HOMEASSISTANT_START, open_browser
)
hass.start()
return hass.exit_code
return hass.start()
def try_to_restart() -> None:
@@ -356,11 +369,13 @@ def try_to_restart() -> None:
def main() -> int:
"""Start Home Assistant."""
validate_python()
attempt_use_uvloop()
if sys.version_info[:3] < (3, 5, 3):
monkey_patch_asyncio()
validate_python()
args = get_arguments()
if args.script is not None:

View File

@@ -21,14 +21,14 @@ import homeassistant.loader as loader
from homeassistant.util.logging import AsyncHandler
from homeassistant.util.yaml import clear_secret_cache
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import event_decorators, service
from homeassistant.helpers.signal import async_register_signal_handling
_LOGGER = logging.getLogger(__name__)
ERROR_LOG_FILENAME = 'home-assistant.log'
FIRST_INIT_COMPONENT = set((
'recorder', 'mqtt', 'mqtt_eventstream', 'logger', 'introduction'))
'recorder', 'mqtt', 'mqtt_eventstream', 'logger', 'introduction',
'frontend', 'history'))
def from_config_dict(config: Dict[str, Any],
@@ -75,8 +75,6 @@ def async_from_config_dict(config: Dict[str, Any],
This method is a coroutine.
"""
start = time()
hass.async_track_tasks()
core_config = config.get(core.DOMAIN, {})
try:
@@ -127,10 +125,6 @@ def async_from_config_dict(config: Dict[str, Any],
_LOGGER.info('Home Assistant core initialized')
# Give event decorators access to HASS
event_decorators.HASS = hass
service.HASS = hass
# stage 1
for component in components:
if component not in FIRST_INIT_COMPONENT:
@@ -145,10 +139,10 @@ def async_from_config_dict(config: Dict[str, Any],
continue
hass.async_add_job(async_setup_component(hass, component, config))
yield from hass.async_stop_track_tasks()
yield from hass.async_block_till_done()
stop = time()
_LOGGER.info('Home Assistant initialized in %ss', round(stop-start, 2))
_LOGGER.info('Home Assistant initialized in %.2fs', stop-start)
async_register_signal_handling(hass)
return hass

View File

@@ -113,7 +113,7 @@ def async_setup(hass, config):
if not alarm.should_poll:
continue
update_coro = hass.loop.create_task(
update_coro = hass.async_add_job(
alarm.async_update_ha_state(True))
if hasattr(alarm, 'async_update'):
update_tasks.append(update_coro)

View File

@@ -0,0 +1,119 @@
"""
Support for AlarmDecoder-based alarm control panels (Honeywell/DSC).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.alarmdecoder/
"""
import asyncio
import logging
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarmdecoder import (DATA_AD,
SIGNAL_PANEL_MESSAGE)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN, STATE_ALARM_TRIGGERED)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['alarmdecoder']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Perform the setup for AlarmDecoder alarm panels."""
_LOGGER.debug("AlarmDecoderAlarmPanel: setup")
device = AlarmDecoderAlarmPanel("Alarm Panel", hass)
async_add_devices([device])
return True
class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
"""Representation of an AlarmDecoder-based alarm panel."""
def __init__(self, name, hass):
"""Initialize the alarm panel."""
self._display = ""
self._name = name
self._state = STATE_UNKNOWN
_LOGGER.debug("AlarmDecoderAlarm: Setting up panel")
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_PANEL_MESSAGE, self._message_callback)
@callback
def _message_callback(self, message):
if message.alarm_sounding or message.fire_alarm:
if self._state != STATE_ALARM_TRIGGERED:
self._state = STATE_ALARM_TRIGGERED
self.hass.async_add_job(self.async_update_ha_state())
elif message.armed_away:
if self._state != STATE_ALARM_ARMED_AWAY:
self._state = STATE_ALARM_ARMED_AWAY
self.hass.async_add_job(self.async_update_ha_state())
elif message.armed_home:
if self._state != STATE_ALARM_ARMED_HOME:
self._state = STATE_ALARM_ARMED_HOME
self.hass.async_add_job(self.async_update_ha_state())
else:
if self._state != STATE_ALARM_DISARMED:
self._state = STATE_ALARM_DISARMED
self.hass.async_add_job(self.async_update_ha_state())
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def code_format(self):
"""Regex for code format or None if no code is required."""
return '^\\d{4,6}$'
@property
def state(self):
"""Return the state of the device."""
return self._state
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
"""Send disarm command."""
_LOGGER.debug("AlarmDecoderAlarm::alarm_disarm: %s", code)
if code:
_LOGGER.debug("AlarmDecoderAlarm::alarm_disarm: sending %s1",
str(code))
self.hass.data[DATA_AD].send("{!s}1".format(code))
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_away: %s", code)
if code:
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_away: sending %s2",
str(code))
self.hass.data[DATA_AD].send("{!s}2".format(code))
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_home: %s", code)
if code:
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_home: sending %s3",
str(code))
self.hass.data[DATA_AD].send("{!s}3".format(code))

View File

@@ -1,13 +1,13 @@
"""
Interfaces with Alarm.com alarm control panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
"""
import logging
import asyncio
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 (
@@ -15,10 +15,9 @@ from homeassistant.const import (
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN, CONF_CODE,
CONF_NAME)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
REQUIREMENTS = ['https://github.com/Xorso/pyalarmdotcom'
'/archive/0.1.1.zip'
'#pyalarmdotcom==0.1.1']
REQUIREMENTS = ['pyalarmdotcom==0.3.0']
_LOGGER = logging.getLogger(__name__)
@@ -32,14 +31,17 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup an Alarm.com control panel."""
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a Alarm.com control panel."""
name = config.get(CONF_NAME)
code = config.get(CONF_CODE)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
add_devices([AlarmDotCom(hass, name, code, username, password)], True)
alarmdotcom = AlarmDotCom(hass, name, code, username, password)
yield from alarmdotcom.async_login()
async_add_devices([alarmdotcom])
class AlarmDotCom(alarm.AlarmControlPanel):
@@ -47,18 +49,30 @@ class AlarmDotCom(alarm.AlarmControlPanel):
def __init__(self, hass, name, code, username, password):
"""Initialize the Alarm.com status."""
from pyalarmdotcom.pyalarmdotcom import Alarmdotcom
self._alarm = Alarmdotcom(username, password, timeout=10)
from pyalarmdotcom import Alarmdotcom
_LOGGER.debug('Setting up Alarm.com...')
self._hass = hass
self._name = name
self._code = str(code) if code else None
self._username = username
self._password = password
self._websession = async_get_clientsession(self._hass)
self._state = STATE_UNKNOWN
self._alarm = Alarmdotcom(username,
password,
self._websession,
hass.loop)
def update(self):
@asyncio.coroutine
def async_login(self):
"""Login to Alarm.com."""
yield from self._alarm.async_login()
@asyncio.coroutine
def async_update(self):
"""Fetch the latest state."""
self._state = self._alarm.state
yield from self._alarm.async_update()
return self._alarm.state
@property
def name(self):
@@ -73,45 +87,36 @@ class AlarmDotCom(alarm.AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
if self._state == 'Disarmed':
if self._alarm.state.lower() == 'disarmed':
return STATE_ALARM_DISARMED
elif self._state == 'Armed Stay':
elif self._alarm.state.lower() == 'armed stay':
return STATE_ALARM_ARMED_HOME
elif self._state == 'Armed Away':
elif self._alarm.state.lower() == 'armed away':
return STATE_ALARM_ARMED_AWAY
else:
return STATE_UNKNOWN
def alarm_disarm(self, code=None):
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if not self._validate_code(code, 'disarming home'):
return
from pyalarmdotcom.pyalarmdotcom import Alarmdotcom
# Open another session to alarm.com to fire off the command
_alarm = Alarmdotcom(self._username, self._password, timeout=10)
_alarm.disarm()
if self._validate_code(code):
yield from self._alarm.async_alarm_disarm()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._validate_code(code, 'arming home'):
return
from pyalarmdotcom.pyalarmdotcom import Alarmdotcom
# Open another session to alarm.com to fire off the command
_alarm = Alarmdotcom(self._username, self._password, timeout=10)
_alarm.arm_stay()
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
"""Send arm hom command."""
if self._validate_code(code):
yield from self._alarm.async_alarm_arm_home()
def alarm_arm_away(self, code=None):
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, 'arming home'):
return
from pyalarmdotcom.pyalarmdotcom import Alarmdotcom
# Open another session to alarm.com to fire off the command
_alarm = Alarmdotcom(self._username, self._password, timeout=10)
_alarm.arm_away()
if self._validate_code(code):
yield from self._alarm.async_alarm_arm_away()
def _validate_code(self, code, state):
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 for %s', state)
_LOGGER.warning('Wrong code entered.')
return check

View File

@@ -0,0 +1,87 @@
"""Interfaces with TotalConnect alarm control panels."""
import logging
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_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN,
CONF_NAME)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['total_connect_client==0.7']
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Total Connect'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup a TotalConnect control panel."""
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
total_connect = TotalConnect(name, username, password)
add_devices([total_connect], True)
class TotalConnect(alarm.AlarmControlPanel):
"""Represent an TotalConnect status."""
def __init__(self, name, username, password):
"""Initialize the TotalConnect status."""
from total_connect_client import TotalConnectClient
_LOGGER.debug('Setting up TotalConnect...')
self._name = name
self._username = username
self._password = password
self._state = STATE_UNKNOWN
self._client = TotalConnectClient.TotalConnectClient(username,
password)
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._state
def update(self):
"""Return the state of the device."""
status = self._client.get_armed_status()
if status == self._client.DISARMED:
state = STATE_ALARM_DISARMED
elif status == self._client.ARMED_STAY:
state = STATE_ALARM_ARMED_HOME
elif status == self._client.ARMED_AWAY:
state = STATE_ALARM_ARMED_AWAY
else:
state = STATE_UNKNOWN
self._state = state
def alarm_disarm(self, code=None):
"""Send disarm command."""
self._client.disarm()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._client.arm_stay()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._client.arm_away()

View File

@@ -0,0 +1,171 @@
"""
Support for AlarmDecoder devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alarmdecoder/
"""
import asyncio
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send
REQUIREMENTS = ['alarmdecoder==0.12.1.0']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'alarmdecoder'
DATA_AD = 'alarmdecoder'
CONF_DEVICE = 'device'
CONF_DEVICE_TYPE = 'type'
CONF_DEVICE_HOST = 'host'
CONF_DEVICE_PORT = 'port'
CONF_DEVICE_PATH = 'path'
CONF_DEVICE_BAUD = 'baudrate'
CONF_ZONES = 'zones'
CONF_ZONE_NAME = 'name'
CONF_ZONE_TYPE = 'type'
CONF_PANEL_DISPLAY = 'panel_display'
DEFAULT_DEVICE_TYPE = 'socket'
DEFAULT_DEVICE_HOST = 'localhost'
DEFAULT_DEVICE_PORT = 10000
DEFAULT_DEVICE_PATH = '/dev/ttyUSB0'
DEFAULT_DEVICE_BAUD = 115200
DEFAULT_PANEL_DISPLAY = False
DEFAULT_ZONE_TYPE = 'opening'
SIGNAL_PANEL_MESSAGE = 'alarmdecoder.panel_message'
SIGNAL_PANEL_ARM_AWAY = 'alarmdecoder.panel_arm_away'
SIGNAL_PANEL_ARM_HOME = 'alarmdecoder.panel_arm_home'
SIGNAL_PANEL_DISARM = 'alarmdecoder.panel_disarm'
SIGNAL_ZONE_FAULT = 'alarmdecoder.zone_fault'
SIGNAL_ZONE_RESTORE = 'alarmdecoder.zone_restore'
DEVICE_SOCKET_SCHEMA = vol.Schema({
vol.Required(CONF_DEVICE_TYPE): 'socket',
vol.Optional(CONF_DEVICE_HOST, default=DEFAULT_DEVICE_HOST): cv.string,
vol.Optional(CONF_DEVICE_PORT, default=DEFAULT_DEVICE_PORT): cv.port})
DEVICE_SERIAL_SCHEMA = vol.Schema({
vol.Required(CONF_DEVICE_TYPE): 'serial',
vol.Optional(CONF_DEVICE_PATH, default=DEFAULT_DEVICE_PATH): cv.string,
vol.Optional(CONF_DEVICE_BAUD, default=DEFAULT_DEVICE_BAUD): cv.string})
DEVICE_USB_SCHEMA = vol.Schema({
vol.Required(CONF_DEVICE_TYPE): 'usb'})
ZONE_SCHEMA = vol.Schema({
vol.Required(CONF_ZONE_NAME): cv.string,
vol.Optional(CONF_ZONE_TYPE, default=DEFAULT_ZONE_TYPE): cv.string})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_DEVICE): vol.Any(DEVICE_SOCKET_SCHEMA,
DEVICE_SERIAL_SCHEMA,
DEVICE_USB_SCHEMA),
vol.Optional(CONF_PANEL_DISPLAY,
default=DEFAULT_PANEL_DISPLAY): cv.boolean,
vol.Optional(CONF_ZONES): {vol.Coerce(int): ZONE_SCHEMA},
}),
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
"""Common setup for AlarmDecoder devices."""
from alarmdecoder import AlarmDecoder
from alarmdecoder.devices import (SocketDevice, SerialDevice, USBDevice)
conf = config.get(DOMAIN)
device = conf.get(CONF_DEVICE)
display = conf.get(CONF_PANEL_DISPLAY)
zones = conf.get(CONF_ZONES)
device_type = device.get(CONF_DEVICE_TYPE)
host = DEFAULT_DEVICE_HOST
port = DEFAULT_DEVICE_PORT
path = DEFAULT_DEVICE_PATH
baud = DEFAULT_DEVICE_BAUD
sync_connect = asyncio.Future(loop=hass.loop)
def handle_open(device):
"""Callback for a successful connection."""
_LOGGER.info("Established a connection with the alarmdecoder.")
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
sync_connect.set_result(True)
@callback
def stop_alarmdecoder(event):
"""Callback to handle shutdown alarmdecoder."""
_LOGGER.debug("Shutting down alarmdecoder.")
controller.close()
@callback
def handle_message(sender, message):
"""Callback to handle message from alarmdecoder."""
async_dispatcher_send(hass, SIGNAL_PANEL_MESSAGE, message)
def zone_fault_callback(sender, zone):
"""Callback to handle zone fault from alarmdecoder."""
async_dispatcher_send(hass, SIGNAL_ZONE_FAULT, zone)
def zone_restore_callback(sender, zone):
"""Callback to handle zone restore from alarmdecoder."""
async_dispatcher_send(hass, SIGNAL_ZONE_RESTORE, zone)
controller = False
if device_type == 'socket':
host = device.get(CONF_DEVICE_HOST)
port = device.get(CONF_DEVICE_PORT)
controller = AlarmDecoder(SocketDevice(interface=(host, port)))
elif device_type == 'serial':
path = device.get(CONF_DEVICE_PATH)
baud = device.get(CONF_DEVICE_BAUD)
controller = AlarmDecoder(SerialDevice(interface=path))
elif device_type == 'usb':
AlarmDecoder(USBDevice.find())
return False
controller.on_open += handle_open
controller.on_message += handle_message
controller.on_zone_fault += zone_fault_callback
controller.on_zone_restore += zone_restore_callback
hass.data[DATA_AD] = controller
controller.open(baud)
result = yield from sync_connect
if not result:
return False
hass.async_add_job(async_load_platform(hass, 'alarm_control_panel', DOMAIN,
conf, config))
if zones:
hass.async_add_job(async_load_platform(
hass, 'binary_sensor', DOMAIN, {CONF_ZONES: zones}, config))
if display:
hass.async_add_job(async_load_platform(hass, 'sensor', DOMAIN,
conf, config))
return True

View File

@@ -26,17 +26,24 @@ from homeassistant.util.dt import utcnow
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL)
DOMAIN = 'android_ip_webcam'
REQUIREMENTS = ["pydroid-ipcam==0.4"]
REQUIREMENTS = ['pydroid-ipcam==0.8']
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=10)
DATA_IP_WEBCAM = 'android_ip_webcam'
ATTR_AUD_CONNS = 'Audio Connections'
ATTR_HOST = 'host'
ATTR_VID_CONNS = 'Video Connections'
ATTR_AUD_CONNS = 'Audio Connections'
CONF_MOTION_SENSOR = 'motion_sensor'
DATA_IP_WEBCAM = 'android_ip_webcam'
DEFAULT_NAME = 'IP Webcam'
DEFAULT_PORT = 8080
DEFAULT_TIMEOUT = 10
DOMAIN = 'android_ip_webcam'
SCAN_INTERVAL = timedelta(seconds=10)
SIGNAL_UPDATE_DATA = 'android_ip_webcam_update'
KEY_MAP = {
'audio_connections': 'Audio Connections',
@@ -123,18 +130,6 @@ SENSORS = ['audio_connections', 'battery_level', 'battery_temp',
'battery_voltage', 'light', 'motion', 'pressure', 'proximity',
'sound', 'video_connections']
SIGNAL_UPDATE_DATA = 'android_ip_webcam_update'
CONF_AUTO_DISCOVERY = 'auto_discovery'
CONF_MOTION_SENSOR = 'motion_sensor'
DEFAULT_AUTO_DISCOVERY = True
DEFAULT_MOTION_SENSOR = False
DEFAULT_NAME = 'IP Webcam'
DEFAULT_PORT = 8080
DEFAULT_TIMEOUT = 10
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.All(cv.ensure_list, [vol.Schema({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -145,21 +140,18 @@ CONFIG_SCHEMA = vol.Schema({
cv.time_period,
vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string,
vol.Optional(CONF_AUTO_DISCOVERY, default=DEFAULT_AUTO_DISCOVERY):
cv.boolean,
vol.Optional(CONF_SWITCHES, default=[]):
vol.Optional(CONF_SWITCHES, default=None):
vol.All(cv.ensure_list, [vol.In(SWITCHES)]),
vol.Optional(CONF_SENSORS, default=[]):
vol.Optional(CONF_SENSORS, default=None):
vol.All(cv.ensure_list, [vol.In(SENSORS)]),
vol.Optional(CONF_MOTION_SENSOR, default=DEFAULT_MOTION_SENSOR):
cv.boolean,
vol.Optional(CONF_MOTION_SENSOR, default=None): cv.boolean,
})])
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
"""Setup the IP Webcam component."""
"""Set up the IP Webcam component."""
from pydroid_ipcam import PyDroidIPCam
webcams = hass.data[DATA_IP_WEBCAM] = {}
@@ -167,7 +159,7 @@ def async_setup(hass, config):
@asyncio.coroutine
def async_setup_ipcamera(cam_config):
"""Setup a ip camera."""
"""Set up an IP camera."""
host = cam_config[CONF_HOST]
username = cam_config.get(CONF_USERNAME)
password = cam_config.get(CONF_PASSWORD)
@@ -177,16 +169,28 @@ def async_setup(hass, config):
sensors = cam_config[CONF_SENSORS]
motion = cam_config[CONF_MOTION_SENSOR]
# init ip webcam
# Init ip webcam
cam = PyDroidIPCam(
hass.loop, websession, host, cam_config[CONF_PORT],
username=username, password=password,
timeout=cam_config[CONF_TIMEOUT]
)
if switches is None:
switches = [setting for setting in cam.enabled_settings
if setting in SWITCHES]
if sensors is None:
sensors = [sensor for sensor in cam.enabled_sensors
if sensor in SENSORS]
sensors.extend(['audio_connections', 'video_connections'])
if motion is None:
motion = 'motion_active' in cam.enabled_sensors
@asyncio.coroutine
def async_update_data(now):
"""Update data from ipcam in SCAN_INTERVAL."""
"""Update data from IP camera in SCAN_INTERVAL."""
yield from cam.update()
async_dispatcher_send(hass, SIGNAL_UPDATE_DATA, host)
@@ -195,21 +199,7 @@ def async_setup(hass, config):
yield from async_update_data(None)
# use autodiscovery to detect sensors/configs
if cam_config[CONF_AUTO_DISCOVERY]:
if not cam.available:
_LOGGER.error(
"Android webcam %s not found for discovery!", cam.base_url)
return
sensors = [sensor for sensor in cam.enabled_sensors
if sensor in SENSORS]
switches = [setting for setting in cam.enabled_settings
if setting in SWITCHES]
motion = True if 'motion_active' in cam.enabled_sensors else False
sensors.extend(['audio_connections', 'video_connections'])
# load platforms
# Load platforms
webcams[host] = cam
mjpeg_camera = {

View File

@@ -17,9 +17,9 @@ from homeassistant.bootstrap import ERROR_LOG_FILENAME
from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED,
HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND,
HTTP_UNPROCESSABLE_ENTITY, MATCH_ALL, URL_API, URL_API_COMPONENTS,
MATCH_ALL, URL_API, URL_API_COMPONENTS,
URL_API_CONFIG, URL_API_DISCOVERY_INFO, URL_API_ERROR_LOG,
URL_API_EVENT_FORWARD, URL_API_EVENTS, URL_API_SERVICES,
URL_API_EVENTS, URL_API_SERVICES,
URL_API_STATES, URL_API_STATES_ENTITY, URL_API_STREAM, URL_API_TEMPLATE,
__version__)
from homeassistant.exceptions import TemplateError
@@ -48,11 +48,12 @@ def setup(hass, config):
hass.http.register_view(APIEventView)
hass.http.register_view(APIServicesView)
hass.http.register_view(APIDomainServicesView)
hass.http.register_view(APIEventForwardingView)
hass.http.register_view(APIComponentsView)
hass.http.register_view(APIErrorLogView)
hass.http.register_view(APITemplateView)
hass.http.register_static_path(
URL_API_ERROR_LOG, hass.config.path(ERROR_LOG_FILENAME), False)
return True
@@ -317,77 +318,6 @@ class APIDomainServicesView(HomeAssistantView):
return self.json(changed_states)
class APIEventForwardingView(HomeAssistantView):
"""View to handle EventForwarding requests."""
url = URL_API_EVENT_FORWARD
name = "api:event-forward"
event_forwarder = None
@asyncio.coroutine
def post(self, request):
"""Setup an event forwarder."""
hass = request.app['hass']
try:
data = yield from request.json()
except ValueError:
return self.json_message("No data received.", HTTP_BAD_REQUEST)
try:
host = data['host']
api_password = data['api_password']
except KeyError:
return self.json_message("No host or api_password received.",
HTTP_BAD_REQUEST)
try:
port = int(data['port']) if 'port' in data else None
except ValueError:
return self.json_message("Invalid value received for port.",
HTTP_UNPROCESSABLE_ENTITY)
api = rem.API(host, api_password, port)
valid = yield from hass.loop.run_in_executor(
None, api.validate_api)
if not valid:
return self.json_message("Unable to validate API.",
HTTP_UNPROCESSABLE_ENTITY)
if self.event_forwarder is None:
self.event_forwarder = rem.EventForwarder(hass)
self.event_forwarder.async_connect(api)
return self.json_message("Event forwarding setup.")
@asyncio.coroutine
def delete(self, request):
"""Remove event forwarder."""
try:
data = yield from request.json()
except ValueError:
return self.json_message("No data received.", HTTP_BAD_REQUEST)
try:
host = data['host']
except KeyError:
return self.json_message("No host received.", HTTP_BAD_REQUEST)
try:
port = int(data['port']) if 'port' in data else None
except ValueError:
return self.json_message("Invalid value received for port.",
HTTP_UNPROCESSABLE_ENTITY)
if self.event_forwarder is not None:
api = rem.API(host, None, port)
self.event_forwarder.async_disconnect(api)
return self.json_message("Event forwarding cancelled.")
class APIComponentsView(HomeAssistantView):
"""View to handle Components requests."""
@@ -400,20 +330,6 @@ class APIComponentsView(HomeAssistantView):
return self.json(request.app['hass'].config.components)
class APIErrorLogView(HomeAssistantView):
"""View to handle ErrorLog requests."""
url = URL_API_ERROR_LOG
name = "api:error-log"
@asyncio.coroutine
def get(self, request):
"""Serve error log."""
resp = yield from self.file(
request, request.app['hass'].config.path(ERROR_LOG_FILENAME))
return resp
class APITemplateView(HomeAssistantView):
"""View to handle requests."""

View File

@@ -13,7 +13,7 @@ from homeassistant.const import (
from homeassistant.const import CONF_PORT
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['PyMata==2.13']
REQUIREMENTS = ['PyMata==2.14']
_LOGGER = logging.getLogger(__name__)
@@ -29,18 +29,25 @@ CONFIG_SCHEMA = vol.Schema({
def setup(hass, config):
"""Setup the Arduino component."""
"""Set up the Arduino component."""
import serial
port = config[DOMAIN][CONF_PORT]
global BOARD
try:
BOARD = ArduinoBoard(config[DOMAIN][CONF_PORT])
BOARD = ArduinoBoard(port)
except (serial.serialutil.SerialException, FileNotFoundError):
_LOGGER.exception("Your port is not accessible.")
_LOGGER.error("Your port %s is not accessible", port)
return False
if BOARD.get_firmata()[1] <= 2:
_LOGGER.error("The StandardFirmata sketch should be 2.2 or newer.")
return False
try:
if BOARD.get_firmata()[1] <= 2:
_LOGGER.error("The StandardFirmata sketch should be 2.2 or newer")
return False
except IndexError:
_LOGGER.warning("The version of the StandardFirmata sketch was not"
"detected. This may lead to side effects")
def stop_arduino(event):
"""Stop the Arduino service."""
@@ -67,25 +74,20 @@ class ArduinoBoard(object):
def set_mode(self, pin, direction, mode):
"""Set the mode and the direction of a given pin."""
if mode == 'analog' and direction == 'in':
self._board.set_pin_mode(pin,
self._board.INPUT,
self._board.ANALOG)
self._board.set_pin_mode(
pin, self._board.INPUT, self._board.ANALOG)
elif mode == 'analog' and direction == 'out':
self._board.set_pin_mode(pin,
self._board.OUTPUT,
self._board.ANALOG)
self._board.set_pin_mode(
pin, self._board.OUTPUT, self._board.ANALOG)
elif mode == 'digital' and direction == 'in':
self._board.set_pin_mode(pin,
self._board.INPUT,
self._board.DIGITAL)
self._board.set_pin_mode(
pin, self._board.INPUT, self._board.DIGITAL)
elif mode == 'digital' and direction == 'out':
self._board.set_pin_mode(pin,
self._board.OUTPUT,
self._board.DIGITAL)
self._board.set_pin_mode(
pin, self._board.OUTPUT, self._board.DIGITAL)
elif mode == 'pwm':
self._board.set_pin_mode(pin,
self._board.OUTPUT,
self._board.PWM)
self._board.set_pin_mode(
pin, self._board.OUTPUT, self._board.PWM)
def get_analog_inputs(self):
"""Get the values from the pins."""

View File

@@ -12,10 +12,11 @@ import os
import voluptuous as vol
from homeassistant.setup import async_prepare_setup_platform
from homeassistant.core import CoreState
from homeassistant import config as conf_util
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE, SERVICE_RELOAD)
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START)
from homeassistant.components import logbook
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import extract_domain_configs, script, condition
@@ -81,8 +82,7 @@ _CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
PLATFORM_SCHEMA = vol.Schema({
CONF_ALIAS: cv.string,
vol.Optional(CONF_INITIAL_STATE,
default=DEFAULT_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_HIDE_ENTITY, default=DEFAULT_HIDE_ENTITY): cv.boolean,
vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA,
vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
@@ -101,15 +101,13 @@ TRIGGER_SERVICE_SCHEMA = vol.Schema({
RELOAD_SERVICE_SCHEMA = vol.Schema({})
def is_on(hass, entity_id=None):
def is_on(hass, entity_id):
"""
Return true if specified automation entity_id is on.
Check all automation if no entity_id specified.
Async friendly.
"""
entity_ids = [entity_id] if entity_id else hass.states.entity_ids(DOMAIN)
return any(hass.states.is_state(entity_id, STATE_ON)
for entity_id in entity_ids)
return hass.states.is_state(entity_id, STATE_ON)
def turn_on(hass, entity_id=None):
@@ -231,7 +229,6 @@ class AutomationEntity(ToggleEntity):
self._async_detach_triggers = None
self._cond_func = cond_func
self._async_action = async_action
self._enabled = False
self._last_triggered = None
self._hidden = hidden
self._initial_state = initial_state
@@ -261,38 +258,62 @@ class AutomationEntity(ToggleEntity):
@property
def is_on(self) -> bool:
"""Return True if entity is on."""
return self._enabled
return self._async_detach_triggers is not None
@asyncio.coroutine
def async_added_to_hass(self) -> None:
"""Startup with initial state or previous state."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if state is None:
if self._initial_state:
yield from self.async_enable()
if self._initial_state is not None:
enable_automation = self._initial_state
_LOGGER.debug("Automation %s initial state %s from config "
"initial_state", self.entity_id, enable_automation)
else:
self._last_triggered = state.attributes.get('last_triggered')
if state.state == STATE_ON:
state = yield from async_get_last_state(self.hass, self.entity_id)
if state:
enable_automation = state.state == STATE_ON
self._last_triggered = state.attributes.get('last_triggered')
_LOGGER.debug("Automation %s initial state %s from recorder "
"last state %s", self.entity_id,
enable_automation, state)
else:
enable_automation = DEFAULT_INITIAL_STATE
_LOGGER.debug("Automation %s initial state %s from default "
"initial state", self.entity_id,
enable_automation)
if not enable_automation:
return
# HomeAssistant is starting up
elif self.hass.state == CoreState.not_running:
@asyncio.coroutine
def async_enable_automation(event):
"""Start automation on startup."""
yield from self.async_enable()
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, async_enable_automation)
# HomeAssistant is running
else:
yield from self.async_enable()
@asyncio.coroutine
def async_turn_on(self, **kwargs) -> None:
"""Turn the entity on and update the state."""
if self._enabled:
if self.is_on:
return
yield from self.async_enable()
yield from self.async_update_ha_state()
@asyncio.coroutine
def async_turn_off(self, **kwargs) -> None:
"""Turn the entity off."""
if not self._enabled:
if not self.is_on:
return
self._async_detach_triggers()
self._async_detach_triggers = None
self._enabled = False
yield from self.async_update_ha_state()
@asyncio.coroutine
@@ -318,12 +339,12 @@ class AutomationEntity(ToggleEntity):
This method is a coroutine.
"""
if self._enabled:
if self.is_on:
return
self._async_detach_triggers = yield from self._async_attach_triggers(
self.async_trigger)
self._enabled = True
yield from self.async_update_ha_state()
@asyncio.coroutine
@@ -342,7 +363,7 @@ def _async_process_config(hass, config, component):
list_no)
hidden = config_block[CONF_HIDE_ENTITY]
initial_state = config_block[CONF_INITIAL_STATE]
initial_state = config_block.get(CONF_INITIAL_STATE)
action = _async_get_action(hass, config_block.get(CONF_ACTION, {}),
name)

View File

@@ -2,15 +2,15 @@
Offer event listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#event-trigger
at https://home-assistant.io/docs/automation/trigger/#event-trigger
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import CONF_PLATFORM
from homeassistant.core import callback, CoreState
from homeassistant.const import CONF_PLATFORM, EVENT_HOMEASSISTANT_START
from homeassistant.helpers import config_validation as cv
CONF_EVENT_TYPE = "event_type"
@@ -31,6 +31,19 @@ def async_trigger(hass, config, action):
event_type = config.get(CONF_EVENT_TYPE)
event_data = config.get(CONF_EVENT_DATA)
if (event_type == EVENT_HOMEASSISTANT_START and
hass.state == CoreState.starting):
_LOGGER.warning('Deprecation: Automations should not listen to event '
"'homeassistant_start'. Use platform 'homeassistant' "
'instead. Feature will be removed in 0.45')
hass.async_run_job(action, {
'trigger': {
'platform': 'event',
'event': None,
},
})
return lambda: None
@callback
def handle_event(event):
"""Listen for events and calls the action when data matches."""

View File

@@ -0,0 +1,55 @@
"""
Offer Home Assistant core automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#homeassistant-trigger
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback, CoreState
from homeassistant.const import (
CONF_PLATFORM, CONF_EVENT, EVENT_HOMEASSISTANT_STOP)
EVENT_START = 'start'
EVENT_SHUTDOWN = 'shutdown'
_LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'homeassistant',
vol.Required(CONF_EVENT): vol.Any(EVENT_START, EVENT_SHUTDOWN),
})
@asyncio.coroutine
def async_trigger(hass, config, action):
"""Listen for events based on configuration."""
event = config.get(CONF_EVENT)
if event == EVENT_SHUTDOWN:
@callback
def hass_shutdown(event):
"""Called when Home Assistant is shutting down."""
hass.async_run_job(action, {
'trigger': {
'platform': 'homeassistant',
'event': event,
},
})
return hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
hass_shutdown)
# Automation are enabled while hass is starting up, fire right away
# Check state because a config reload shouldn't trigger it.
elif hass.state == CoreState.starting:
hass.async_run_job(action, {
'trigger': {
'platform': 'homeassistant',
'event': event,
},
})
return lambda: None

View File

@@ -70,7 +70,7 @@ def async_trigger(hass, config, action):
nonlocal held_less_than, held_more_than
pressed_time = dt_util.utcnow()
if held_more_than is None and held_less_than is None:
call_action()
hass.add_job(call_action)
if held_more_than is not None and held_less_than is None:
cancel_pressed_more_than = track_point_in_utc_time(
hass,
@@ -88,7 +88,7 @@ def async_trigger(hass, config, action):
held_time = dt_util.utcnow() - pressed_time
if held_less_than is not None and held_time < held_less_than:
if held_more_than is None or held_time > held_more_than:
call_action()
hass.add_job(call_action)
hass.data['litejet_system'].on_switch_pressed(number, pressed)
hass.data['litejet_system'].on_switch_released(number, released)

View File

@@ -2,7 +2,7 @@
Offer MQTT listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#mqtt-trigger
at https://home-assistant.io/docs/automation/trigger/#mqtt-trigger
"""
import asyncio
import json

View File

@@ -2,7 +2,7 @@
Offer numeric state listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#numeric-state-trigger
at https://home-assistant.io/docs/automation/trigger/#numeric-state-trigger
"""
import asyncio
import logging

View File

@@ -2,7 +2,7 @@
Offer state listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#state-trigger
at https://home-assistant.io/docs/automation/trigger/#state-trigger
"""
import asyncio
import voluptuous as vol

View File

@@ -2,7 +2,7 @@
Offer sun based automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#sun-trigger
at https://home-assistant.io/docs/automation/trigger/#sun-trigger
"""
import asyncio
from datetime import timedelta

View File

@@ -2,7 +2,7 @@
Offer template automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#template-trigger
at https://home-assistant.io/docs/automation/trigger/#template-trigger
"""
import asyncio
import logging

View File

@@ -2,7 +2,7 @@
Offer time listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#time-trigger
at https://home-assistant.io/docs/automation/trigger/#time-trigger
"""
import asyncio
import logging

View File

@@ -2,7 +2,7 @@
Offer zone automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#zone-trigger
at https://home-assistant.io/docs/automation/trigger/#zone-trigger
"""
import asyncio
import voluptuous as vol

View File

@@ -0,0 +1,123 @@
"""
Support for AlarmDecoder 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.alarmdecoder/
"""
import asyncio
import logging
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import (STATE_ON, STATE_OFF, STATE_OPEN, STATE_CLOSED)
from homeassistant.components.alarmdecoder import (ZONE_SCHEMA,
CONF_ZONES,
CONF_ZONE_NAME,
CONF_ZONE_TYPE,
SIGNAL_ZONE_FAULT,
SIGNAL_ZONE_RESTORE)
DEPENDENCIES = ['alarmdecoder']
_LOGGER = logging.getLogger(__name__)
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup AlarmDecoder binary sensor devices."""
configured_zones = discovery_info[CONF_ZONES]
devices = []
for zone_num in configured_zones:
device_config_data = ZONE_SCHEMA(configured_zones[zone_num])
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
device = AlarmDecoderBinarySensor(hass,
zone_num,
zone_name,
zone_type)
devices.append(device)
async_add_devices(devices)
return True
class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Representation of an AlarmDecoder binary sensor."""
def __init__(self, hass, zone_number, zone_name, zone_type):
"""Initialize the binary_sensor."""
self._zone_number = zone_number
self._zone_type = zone_type
self._state = 0
self._name = zone_name
self._type = zone_type
_LOGGER.debug('AlarmDecoderBinarySensor: Setup up zone: ' + zone_name)
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_FAULT, self._fault_callback)
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_RESTORE, self._restore_callback)
@property
def state(self):
"""Return the state of the binary sensor."""
if self._type == 'opening':
return STATE_OPEN if self.is_on else STATE_CLOSED
return STATE_ON if self.is_on else STATE_OFF
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def icon(self):
"""Icon for device by its type."""
if "window" in self._name.lower():
return "mdi:window-open" if self.is_on else "mdi:window-closed"
if self._type == 'smoke':
return "mdi:fire"
return None
@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._zone_type
@callback
def _fault_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self._state = 1
self.hass.async_add_job(self.async_update_ha_state())
@callback
def _restore_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self._state = 0
self.hass.async_add_job(self.async_update_ha_state())

View File

@@ -11,7 +11,7 @@ DEPENDENCIES = ['blink']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the blink binary sensors."""
"""Set up the blink binary sensors."""
if discovery_info is None:
return

View File

@@ -36,6 +36,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
dev = []
for droplet in droplets:
droplet_id = digital_ocean.DIGITAL_OCEAN.get_droplet_id(droplet)
if droplet_id is None:
_LOGGER.error("Droplet %s is not available", droplet)
return False
dev.append(DigitalOceanBinarySensor(
digital_ocean.DIGITAL_OCEAN, droplet_id))

View File

@@ -1,4 +1,9 @@
"""Contains functionality to use flic buttons as a binary sensor."""
"""
Support to use flic buttons as a binary sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.flic/
"""
import logging
import threading
@@ -11,39 +16,40 @@ from homeassistant.const import (
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
REQUIREMENTS = ['https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4']
_LOGGER = logging.getLogger(__name__)
DEFAULT_TIMEOUT = 3
CLICK_TYPE_SINGLE = "single"
CLICK_TYPE_DOUBLE = "double"
CLICK_TYPE_HOLD = "hold"
CLICK_TYPE_SINGLE = 'single'
CLICK_TYPE_DOUBLE = 'double'
CLICK_TYPE_HOLD = 'hold'
CLICK_TYPES = [CLICK_TYPE_SINGLE, CLICK_TYPE_DOUBLE, CLICK_TYPE_HOLD]
CONF_IGNORED_CLICK_TYPES = "ignored_click_types"
CONF_IGNORED_CLICK_TYPES = 'ignored_click_types'
EVENT_NAME = "flic_click"
EVENT_DATA_NAME = "button_name"
EVENT_DATA_ADDRESS = "button_address"
EVENT_DATA_TYPE = "click_type"
EVENT_DATA_QUEUED_TIME = "queued_time"
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 5551
EVENT_NAME = 'flic_click'
EVENT_DATA_NAME = 'button_name'
EVENT_DATA_ADDRESS = 'button_address'
EVENT_DATA_TYPE = 'click_type'
EVENT_DATA_QUEUED_TIME = 'queued_time'
# Validation of the user's configuration
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST, default='localhost'): cv.string,
vol.Optional(CONF_PORT, default=5551): cv.port,
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_DISCOVERY, default=True): cv.boolean,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_IGNORED_CLICK_TYPES): vol.All(cv.ensure_list,
[vol.In(CLICK_TYPES)])
vol.Optional(CONF_IGNORED_CLICK_TYPES):
vol.All(cv.ensure_list, [vol.In(CLICK_TYPES)])
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Setup the flic platform."""
"""Set up the flic platform."""
import pyflic
# Initialize flic client responsible for
@@ -55,11 +61,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
try:
client = pyflic.FlicClient(host, port)
except ConnectionRefusedError:
_LOGGER.error("Failed to connect to flic server.")
_LOGGER.error("Failed to connect to flic server")
return
def new_button_callback(address):
"""Setup newly verified button as device in home assistant."""
"""Set up newly verified button as device in Home Assistant."""
setup_button(hass, config, add_entities, client, address)
client.on_new_verified_button = new_button_callback
@@ -74,7 +80,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
def get_info_callback(items):
"""Add entities for already verified buttons."""
addresses = items["bd_addr_of_verified_buttons"] or []
addresses = items['bd_addr_of_verified_buttons'] or []
for address in addresses:
setup_button(hass, config, add_entities, client, address)
@@ -83,7 +89,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
def start_scanning(config, add_entities, client):
"""Start a new flic client for scanning & connceting to new buttons."""
"""Start a new flic client for scanning and connecting to new buttons."""
import pyflic
scan_wizard = pyflic.ScanWizard()
@@ -91,10 +97,10 @@ def start_scanning(config, add_entities, client):
def scan_completed_callback(scan_wizard, result, address, name):
"""Restart scan wizard to constantly check for new buttons."""
if result == pyflic.ScanWizardResult.WizardSuccess:
_LOGGER.info("Found new button (%s)", address)
_LOGGER.info("Found new button %s", address)
elif result != pyflic.ScanWizardResult.WizardFailedTimeout:
_LOGGER.warning("Failed to connect to button (%s). Reason: %s",
address, result)
_LOGGER.warning(
"Failed to connect to button %s. Reason: %s", address, result)
# Restart scan wizard
start_scanning(config, add_entities, client)
@@ -108,7 +114,7 @@ def setup_button(hass, config, add_entities, client, address):
timeout = config.get(CONF_TIMEOUT)
ignored_click_types = config.get(CONF_IGNORED_CLICK_TYPES)
button = FlicButton(hass, client, address, timeout, ignored_click_types)
_LOGGER.info("Connected to button (%s)", address)
_LOGGER.info("Connected to button %s", address)
add_entities([button])
@@ -161,7 +167,7 @@ class FlicButton(BinarySensorDevice):
@property
def name(self):
"""Return the name of the device."""
return "flic_%s" % self.address.replace(":", "")
return 'flic_{}'.format(self.address.replace(':', ''))
@property
def address(self):
@@ -181,21 +187,21 @@ class FlicButton(BinarySensorDevice):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
return {"address": self.address}
return {'address': self.address}
def _queued_event_check(self, click_type, time_diff):
"""Generate a log message and returns true if timeout exceeded."""
time_string = "{:d} {}".format(
time_diff, "second" if time_diff == 1 else "seconds")
time_diff, 'second' if time_diff == 1 else 'seconds')
if time_diff > self._timeout:
_LOGGER.warning(
"Queued %s dropped for %s. Time in queue was %s.",
"Queued %s dropped for %s. Time in queue was %s",
click_type, self.address, time_string)
return True
else:
_LOGGER.info(
"Queued %s allowed for %s. Time in queue was %s.",
"Queued %s allowed for %s. Time in queue was %s",
click_type, self.address, time_string)
return False
@@ -227,8 +233,8 @@ class FlicButton(BinarySensorDevice):
EVENT_DATA_TYPE: hass_click_type
})
def _connection_status_changed(self, channel,
connection_status, disconnect_reason):
def _connection_status_changed(
self, channel, connection_status, disconnect_reason):
"""Remove device, if button disconnects."""
import pyflic

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.0']
REQUIREMENTS = ['pyhik==0.1.2']
_LOGGER = logging.getLogger(__name__)
CONF_IGNORED = 'ignored'
@@ -33,7 +33,6 @@ ATTR_DELAY = 'delay'
DEVICE_CLASS_MAP = {
'Motion': 'motion',
'Line Crossing': 'motion',
'IO Trigger': None,
'Field Detection': 'motion',
'Video Loss': None,
'Tamper Detection': 'motion',
@@ -47,6 +46,7 @@ DEVICE_CLASS_MAP = {
'Bad Video': None,
'PIR Alarm': 'motion',
'Face Detection': 'motion',
'Scene Change Detection': 'motion',
}
CUSTOMIZE_SCHEMA = vol.Schema({
@@ -91,24 +91,30 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
entities = []
for sensor in data.sensors:
# Build sensor name, then parse customize config.
sensor_name = sensor.replace(' ', '_')
for sensor, channel_list in data.sensors.items():
for channel in channel_list:
# Build sensor name, then parse customize config.
if data.type == 'NVR':
sensor_name = '{}_{}'.format(
sensor.replace(' ', '_'), channel[1])
else:
sensor_name = sensor.replace(' ', '_')
custom = customize.get(sensor_name.lower(), {})
ignore = custom.get(CONF_IGNORED)
delay = custom.get(CONF_DELAY)
custom = customize.get(sensor_name.lower(), {})
ignore = custom.get(CONF_IGNORED)
delay = custom.get(CONF_DELAY)
_LOGGER.debug('Entity: %s - %s, Options - Ignore: %s, Delay: %s',
data.name, sensor_name, ignore, delay)
if not ignore:
entities.append(HikvisionBinarySensor(hass, sensor, data, delay))
_LOGGER.debug('Entity: %s - %s, Options - Ignore: %s, Delay: %s',
data.name, sensor_name, ignore, delay)
if not ignore:
entities.append(HikvisionBinarySensor(
hass, sensor, channel[1], data, delay))
add_entities(entities)
class HikvisionData(object):
"""Hikvision camera event stream object."""
"""Hikvision device event stream object."""
def __init__(self, hass, url, port, name, username, password):
"""Initialize the data oject."""
@@ -144,25 +150,40 @@ class HikvisionData(object):
@property
def cam_id(self):
"""Return camera id."""
"""Return device id."""
return self.camdata.get_id
@property
def name(self):
"""Return camera name."""
"""Return device name."""
return self._name
@property
def type(self):
"""Return device type."""
return self.camdata.get_type
def get_attributes(self, sensor, channel):
"""Return attribute list for sensor/channel."""
return self.camdata.fetch_attributes(sensor, channel)
class HikvisionBinarySensor(BinarySensorDevice):
"""Representation of a Hikvision binary sensor."""
def __init__(self, hass, sensor, cam, delay):
def __init__(self, hass, sensor, channel, cam, delay):
"""Initialize the binary_sensor."""
self._hass = hass
self._cam = cam
self._name = self._cam.name + ' ' + sensor
self._id = self._cam.cam_id + '.' + sensor
self._sensor = sensor
self._channel = channel
if self._cam.type == 'NVR':
self._name = '{} {} {}'.format(self._cam.name, sensor, channel)
else:
self._name = '{} {}'.format(self._cam.name, sensor)
self._id = '{}.{}.{}'.format(self._cam.cam_id, sensor, channel)
if delay is None:
self._delay = 0
@@ -176,11 +197,11 @@ class HikvisionBinarySensor(BinarySensorDevice):
def _sensor_state(self):
"""Extract sensor state."""
return self._cam.sensors[self._sensor][0]
return self._cam.get_attributes(self._sensor, self._channel)[0]
def _sensor_last_update(self):
"""Extract sensor last update time."""
return self._cam.sensors[self._sensor][3]
return self._cam.get_attributes(self._sensor, self._channel)[3]
@property
def name(self):

View File

@@ -32,7 +32,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
InsteonPLMBinarySensorDevice(hass, plm, address, name)
)
hass.async_add_job(async_add_devices(device_list))
async_add_devices(device_list)
class InsteonPLMBinarySensorDevice(BinarySensorDevice):

View File

@@ -0,0 +1,130 @@
"""
Tracks the latency of a host by sending ICMP echo requests (ping).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ping/
"""
import logging
import subprocess
import re
import sys
from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import CONF_NAME, CONF_HOST
_LOGGER = logging.getLogger(__name__)
ATTR_ROUND_TRIP_TIME_AVG = 'round_trip_time_avg'
ATTR_ROUND_TRIP_TIME_MAX = 'round_trip_time_max'
ATTR_ROUND_TRIP_TIME_MDEV = 'round_trip_time_mdev'
ATTR_ROUND_TRIP_TIME_MIN = 'round_trip_time_min'
CONF_PING_COUNT = 'count'
DEFAULT_NAME = 'Ping Binary sensor'
DEFAULT_PING_COUNT = 5
DEFAULT_SENSOR_CLASS = 'connectivity'
SCAN_INTERVAL = timedelta(minutes=5)
PING_MATCHER = re.compile(
r'(?P<min>\d+.\d+)\/(?P<avg>\d+.\d+)\/(?P<max>\d+.\d+)\/(?P<mdev>\d+.\d+)')
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PING_COUNT, default=DEFAULT_PING_COUNT): cv.positive_int,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Ping Binary sensor."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
count = config.get(CONF_PING_COUNT)
add_devices([PingBinarySensor(name, PingData(host, count))], True)
class PingBinarySensor(BinarySensorDevice):
"""Representation of a Ping Binary sensor."""
def __init__(self, name, ping):
"""Initialize the Ping Binary sensor."""
self._name = name
self.ping = ping
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def device_class(self):
"""Return the class of this sensor."""
return DEFAULT_SENSOR_CLASS
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.ping.available
@property
def device_state_attributes(self):
"""Return the state attributes of the ICMP checo request."""
if self.ping.data is not False:
return {
ATTR_ROUND_TRIP_TIME_AVG: self.ping.data['avg'],
ATTR_ROUND_TRIP_TIME_MAX: self.ping.data['max'],
ATTR_ROUND_TRIP_TIME_MDEV: self.ping.data['mdev'],
ATTR_ROUND_TRIP_TIME_MIN: self.ping.data['min'],
}
def update(self):
"""Get the latest data."""
self.ping.update()
class PingData(object):
"""The Class for handling the data retrieval."""
def __init__(self, host, count):
"""Initialize the data object."""
self._ip_address = host
self._count = count
self.data = {}
self.available = False
if sys.platform == 'win32':
self._ping_cmd = [
'ping', '-n', str(self._count), '-w 1000', self._ip_address]
else:
self._ping_cmd = [
'ping', '-n', '-q', '-c', str(self._count), '-W1',
self._ip_address]
def ping(self):
"""Send ICMP echo request and return details if success."""
pinger = subprocess.Popen(
self._ping_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
out = pinger.communicate()
match = PING_MATCHER.search(str(out).split('\n')[-1])
rtt_min, rtt_avg, rtt_max, rtt_mdev = match.groups()
return {
'min': rtt_min,
'avg': rtt_avg,
'max': rtt_max,
'mdev': rtt_mdev}
except (subprocess.CalledProcessError, AttributeError):
return False
def update(self):
"""Retrieve the latest details from the host."""
self.data = self.ping()
self.available = bool(self.data)

View File

@@ -0,0 +1,109 @@
"""
This component provides HA sensor support for Ring Door Bell/Chimes.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ring/
"""
import logging
from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.ring import (
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE)
from homeassistant.const import (
ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
DEPENDENCIES = ['ring']
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=5)
# Sensor types: Name, category, device_class
SENSOR_TYPES = {
'ding': ['Ding', ['doorbell'], 'occupancy'],
'motion': ['Motion', ['doorbell'], 'motion'],
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_ENTITY_NAMESPACE, default=DEFAULT_ENTITY_NAMESPACE):
cv.string,
vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for a Ring device."""
ring = hass.data.get('ring')
sensors = []
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
for device in ring.doorbells:
if 'doorbell' in SENSOR_TYPES[sensor_type][1]:
sensors.append(RingBinarySensor(hass,
device,
sensor_type))
add_devices(sensors, True)
return True
class RingBinarySensor(BinarySensorDevice):
"""A binary sensor implementation for Ring device."""
def __init__(self, hass, data, sensor_type):
"""Initialize a sensor for Ring device."""
super(RingBinarySensor, self).__init__()
self._sensor_type = sensor_type
self._data = data
self._name = "{0} {1}".format(self._data.name,
SENSOR_TYPES.get(self._sensor_type)[0])
self._device_class = SENSOR_TYPES.get(self._sensor_type)[2]
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return True if the binary sensor is on."""
return self._state
@property
def device_class(self):
"""Return the class of the binary sensor."""
return self._device_class
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {}
attrs[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION
attrs['device_id'] = self._data.id
attrs['firmware'] = self._data.firmware
attrs['timezone'] = self._data.timezone
if self._data.alert and self._data.alert_expires_at:
attrs['expires_at'] = self._data.alert_expires_at
attrs['state'] = self._data.alert.get('state')
return attrs
def update(self):
"""Get the latest data and updates the state."""
self._data.check_alerts()
if self._data.alert:
self._state = (self._sensor_type ==
self._data.alert.get('kind'))
else:
self._state = False

View File

@@ -20,8 +20,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
import pywemo.discovery as discovery
if discovery_info is not None:
location = discovery_info[2]
mac = discovery_info[3]
location = discovery_info['ssdp_description']
mac = discovery_info['mac_address']
device = discovery.device_from_description(location, mac)
if device:
@@ -40,12 +40,14 @@ class WemoBinarySensor(BinarySensorDevice):
wemo.SUBSCRIPTION_REGISTRY.register(self.wemo)
wemo.SUBSCRIPTION_REGISTRY.on(self.wemo, None, self._update_callback)
def _update_callback(self, _device, _params):
"""Called by the wemo device callback to update state."""
def _update_callback(self, _device, _type, _params):
"""Called by the Wemo device callback to update state."""
_LOGGER.info(
'Subscription update for %s',
_device)
self.update()
updated = self.wemo.subscription_update(_type, _params)
self._update(force_update=(not updated))
if not hasattr(self, 'hass'):
return
self.schedule_update_ha_state()
@@ -72,7 +74,11 @@ class WemoBinarySensor(BinarySensorDevice):
def update(self):
"""Update WeMo state."""
self._update(force_update=True)
def _update(self, force_update=True):
try:
self._state = self.wemo.get_state(True)
except AttributeError:
_LOGGER.warning('Could not update status for %s', self.name)
self._state = self.wemo.get_state(force_update)
except AttributeError as err:
_LOGGER.warning('Could not update status for %s (%s)',
self.name, err)

View File

@@ -0,0 +1,148 @@
"""
Sensor to indicate whether the current day is a workday.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.workday/
"""
import asyncio
import logging
import datetime
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
STATE_ON, STATE_OFF, STATE_UNKNOWN, CONF_NAME, WEEKDAYS)
import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['holidays==0.8.1']
# List of all countries currently supported by holidays
# There seems to be no way to get the list out at runtime
ALL_COUNTRIES = ['Australia', 'AU', 'Austria', 'AT', 'Canada', 'CA',
'Colombia', 'CO', 'Czech', 'CZ', 'Denmark', 'DK', 'England',
'EuropeanCentralBank', 'ECB', 'TAR', 'Germany', 'DE',
'Ireland', 'Isle of Man', 'Mexico', 'MX', 'Netherlands', 'NL',
'NewZealand', 'NZ', 'Northern Ireland', 'Norway', 'NO',
'Portugal', 'PT', 'PortugalExt', 'PTE', 'Scotland', 'Spain',
'ES', 'UnitedKingdom', 'UK', 'UnitedStates', 'US', 'Wales']
CONF_COUNTRY = 'country'
CONF_PROVINCE = 'province'
CONF_WORKDAYS = 'workdays'
# By default, Monday - Friday are workdays
DEFAULT_WORKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri']
CONF_EXCLUDES = 'excludes'
# By default, public holidays, Saturdays and Sundays are excluded from workdays
DEFAULT_EXCLUDES = ['sat', 'sun', 'holiday']
DEFAULT_NAME = 'Workday Sensor'
ALLOWED_DAYS = WEEKDAYS + ['holiday']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COUNTRY): vol.In(ALL_COUNTRIES),
vol.Optional(CONF_PROVINCE, default=None): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_WORKDAYS, default=DEFAULT_WORKDAYS):
vol.All(cv.ensure_list, [vol.In(ALLOWED_DAYS)]),
vol.Optional(CONF_EXCLUDES, default=DEFAULT_EXCLUDES):
vol.All(cv.ensure_list, [vol.In(ALLOWED_DAYS)]),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Workday sensor."""
import holidays
sensor_name = config.get(CONF_NAME)
country = config.get(CONF_COUNTRY)
province = config.get(CONF_PROVINCE)
workdays = config.get(CONF_WORKDAYS)
excludes = config.get(CONF_EXCLUDES)
year = datetime.datetime.now().year
obj_holidays = getattr(holidays, country)(years=year)
if province:
if province not in obj_holidays.PROVINCES and \
province not in obj_holidays.STATES:
_LOGGER.error("There is no province/state %s in country %s",
province, country)
return False
else:
year = datetime.datetime.now().year
obj_holidays = getattr(holidays, country)(prov=province,
years=year)
_LOGGER.debug("Found the following holidays for your configuration:")
for date, name in sorted(obj_holidays.items()):
_LOGGER.debug("%s %s", date, name)
add_devices([IsWorkdaySensor(
obj_holidays, workdays, excludes, sensor_name)], True)
def day_to_string(day):
"""Convert day index 0 - 7 to string."""
try:
return ALLOWED_DAYS[day]
except IndexError:
return None
class IsWorkdaySensor(Entity):
"""Implementation of a Workday sensor."""
def __init__(self, obj_holidays, workdays, excludes, name):
"""Initialize the Workday sensor."""
self._name = name
self._obj_holidays = obj_holidays
self._workdays = workdays
self._excludes = excludes
self._state = STATE_UNKNOWN
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._state
def is_include(self, day, now):
"""Check if given day is in the includes list."""
if day in self._workdays:
return True
elif 'holiday' in self._workdays and now in self._obj_holidays:
return True
return False
def is_exclude(self, day, now):
"""Check if given day is in the excludes list."""
if day in self._excludes:
return True
elif 'holiday' in self._excludes and now in self._obj_holidays:
return True
return False
@asyncio.coroutine
def async_update(self):
"""Get date and look whether it is a holiday."""
# Default is no workday
self._state = STATE_OFF
# Get iso day of the week (1 = Monday, 7 = Sunday)
day = datetime.datetime.today().isoweekday() - 1
day_of_week = day_to_string(day)
if self.is_include(day_of_week, dt_util.now()):
self._state = STATE_ON
if self.is_exclude(day_of_week, dt_util.now()):
self._state = STATE_OFF

View File

@@ -19,34 +19,34 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
def get_device(value, **kwargs):
def get_device(values, **kwargs):
"""Create zwave entity device."""
device_mapping = workaround.get_device_mapping(value)
device_mapping = workaround.get_device_mapping(values.primary)
if device_mapping == workaround.WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
re_arm_multiplier = (zwave.get_config_value(value.node, 9) or 4)
return ZWaveTriggerSensor(value, "motion", re_arm_multiplier * 8)
re_arm_multiplier = zwave.get_config_value(values.primary.node, 9) or 4
return ZWaveTriggerSensor(values, "motion", re_arm_multiplier * 8)
if workaround.get_device_component_mapping(value) == DOMAIN:
return ZWaveBinarySensor(value, None)
if workaround.get_device_component_mapping(values.primary) == DOMAIN:
return ZWaveBinarySensor(values, None)
if value.command_class == zwave.const.COMMAND_CLASS_SENSOR_BINARY:
return ZWaveBinarySensor(value, None)
if values.primary.command_class == zwave.const.COMMAND_CLASS_SENSOR_BINARY:
return ZWaveBinarySensor(values, None)
return None
class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
"""Representation of a binary sensor within Z-Wave."""
def __init__(self, value, device_class):
def __init__(self, values, device_class):
"""Initialize the sensor."""
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
self._sensor_type = device_class
self._state = self._value.data
self._state = self.values.primary.data
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self._value.data
self._state = self.values.primary.data
@property
def is_on(self):
@@ -58,24 +58,19 @@ class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
"""Return the class of this sensor, from DEVICE_CLASSES."""
return self._sensor_type
@property
def should_poll(self):
"""No polling needed."""
return False
class ZWaveTriggerSensor(ZWaveBinarySensor):
"""Representation of a stateless sensor within Z-Wave."""
def __init__(self, value, device_class, re_arm_sec=60):
def __init__(self, values, device_class, re_arm_sec=60):
"""Initialize the sensor."""
super(ZWaveTriggerSensor, self).__init__(value, device_class)
super(ZWaveTriggerSensor, self).__init__(values, device_class)
self.re_arm_sec = re_arm_sec
self.invalidate_after = None
def update_properties(self):
"""Called when a value for this entity's node has changed."""
self._state = self._value.data
self._state = self.values.primary.data
# only allow this value to be true for re_arm secs
if not self.hass:
return

View File

@@ -5,17 +5,19 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/blink/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (CONF_USERNAME,
CONF_PASSWORD,
ATTR_FRIENDLY_NAME,
ATTR_ARMED)
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, ATTR_FRIENDLY_NAME, ATTR_ARMED)
from homeassistant.helpers import discovery
REQUIREMENTS = ['blinkpy==0.5.2']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'blink'
REQUIREMENTS = ['blinkpy==0.4.4']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
@@ -50,7 +52,7 @@ class BlinkSystem(object):
def setup(hass, config):
"""Setup Blink System."""
"""Set up Blink System."""
hass.data[DOMAIN] = BlinkSystem(config)
discovery.load_platform(hass, 'camera', DOMAIN, {}, config)
discovery.load_platform(hass, 'sensor', DOMAIN, {}, config)
@@ -77,11 +79,11 @@ def setup(hass, config):
hass.data[DOMAIN].blink.arm = value
hass.data[DOMAIN].blink.refresh()
hass.services.register(DOMAIN, 'snap_picture', snap_picture,
schema=SNAP_PICTURE_SCHEMA)
hass.services.register(DOMAIN, 'arm_camera', arm_camera,
schema=ARM_CAMERA_SCHEMA)
hass.services.register(DOMAIN, 'arm_system', arm_system,
schema=ARM_SYSTEM_SCHEMA)
hass.services.register(
DOMAIN, 'snap_picture', snap_picture, schema=SNAP_PICTURE_SCHEMA)
hass.services.register(
DOMAIN, 'arm_camera', arm_camera, schema=ARM_CAMERA_SCHEMA)
hass.services.register(
DOMAIN, 'arm_system', arm_system, schema=ARM_SYSTEM_SCHEMA)
return True

View File

@@ -7,6 +7,7 @@ https://home-assistant.io/components/camera/
"""
import asyncio
import collections
from contextlib import suppress
from datetime import timedelta
import logging
import hashlib
@@ -58,7 +59,6 @@ def async_get_image(hass, entity_id, timeout=10):
state.attributes.get(ATTR_ENTITY_PICTURE)
)
response = None
try:
with async_timeout.timeout(timeout, loop=hass.loop):
response = yield from websession.get(url)
@@ -70,13 +70,9 @@ def async_get_image(hass, entity_id, timeout=10):
image = yield from response.read()
return image
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
raise HomeAssistantError("Can't connect to {0}".format(url))
finally:
if response is not None:
yield from response.release()
@asyncio.coroutine
def async_setup(hass, config):
@@ -172,7 +168,7 @@ class Camera(Entity):
if not img_bytes:
break
if img_bytes is not None and img_bytes != last_image:
if img_bytes and img_bytes != last_image:
write(img_bytes)
# Chrome seems to always ignore first picture,
@@ -185,8 +181,8 @@ class Camera(Entity):
yield from asyncio.sleep(.5)
except (asyncio.CancelledError, ConnectionResetError):
_LOGGER.debug("Close stream by frontend.")
except asyncio.CancelledError:
_LOGGER.debug("Stream closed by frontend.")
response = None
finally:
@@ -268,16 +264,14 @@ class CameraImageView(CameraView):
@asyncio.coroutine
def handle(self, request, camera):
"""Serve camera image."""
try:
image = yield from camera.async_camera_image()
with suppress(asyncio.CancelledError, asyncio.TimeoutError):
with async_timeout.timeout(10, loop=request.app['hass'].loop):
image = yield from camera.async_camera_image()
if image is None:
return web.Response(status=500)
if image:
return web.Response(body=image)
return web.Response(body=image)
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
return web.Response(status=500)
class CameraMjpegStream(CameraView):

View File

@@ -16,9 +16,9 @@ from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_stream)
async_get_clientsession, async_aiohttp_proxy_web)
REQUIREMENTS = ['amcrest==1.1.4']
REQUIREMENTS = ['amcrest==1.1.9']
_LOGGER = logging.getLogger(__name__)
@@ -125,7 +125,7 @@ class AmcrestCam(Camera):
stream_coro = websession.get(
streaming_url, auth=self._token, timeout=TIMEOUT)
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
@property
def name(self):

View File

@@ -15,7 +15,7 @@ from homeassistant.util import Throttle
DEPENDENCIES = ['blink']
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
_LOGGER = logging.getLogger(__name__)

View File

@@ -1,67 +0,0 @@
"""
Support for internal dispatcher image push to Camera.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.dispatcher/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import CONF_NAME
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
CONF_SIGNAL = 'signal'
DEFAULT_NAME = 'Dispatcher Camera'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_SIGNAL): cv.slugify,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a dispatcher camera."""
if discovery_info:
config = PLATFORM_SCHEMA(discovery_info)
async_add_devices(
[DispatcherCamera(config[CONF_NAME], config[CONF_SIGNAL])])
class DispatcherCamera(Camera):
"""A dispatcher implementation of an camera."""
def __init__(self, name, signal):
"""Initialize a dispatcher camera."""
super().__init__()
self._name = name
self._signal = signal
self._image = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Register dispatcher and callbacks."""
@callback
def async_update_image(image):
"""Update image from dispatcher call."""
self._image = image
async_dispatcher_connect(self.hass, self._signal, async_update_image)
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
return self._image
@property
def name(self):
"""Return the name of this device."""
return self._name

View File

@@ -8,14 +8,14 @@ import asyncio
import logging
import voluptuous as vol
from aiohttp import web
from homeassistant.const import CONF_NAME
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import (
DATA_FFMPEG, CONF_INPUT, CONF_EXTRA_ARGUMENTS)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
from homeassistant.helpers.aiohttp_client import (
async_aiohttp_proxy_stream)
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
@@ -69,26 +69,10 @@ class FFmpegCamera(Camera):
yield from stream.open_camera(
self._input, extra_cmd=self._extra_arguments)
response = web.StreamResponse()
response.content_type = 'multipart/x-mixed-replace;boundary=ffserver'
yield from response.prepare(request)
try:
while True:
data = yield from stream.read(102400)
if not data:
break
response.write(data)
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
yield from stream.close()
if response is not None:
yield from response.write_eof()
yield from async_aiohttp_proxy_stream(
self.hass, request, stream,
'multipart/x-mixed-replace;boundary=ffserver')
yield from stream.close()
@property
def name(self):

View File

@@ -66,9 +66,13 @@ class FoscamCamera(Camera):
def camera_image(self):
"""Return a still image reponse from the camera."""
# Send the request to snap a picture and return raw jpg data
response = requests.get(self._snap_picture_url, timeout=10)
return response.content
# Handle exception if host is not reachable or url failed
try:
response = requests.get(self._snap_picture_url, timeout=10)
except requests.exceptions.ConnectionError:
return None
else:
return response.content
@property
def name(self):

View File

@@ -107,7 +107,6 @@ class GenericCamera(Camera):
None, fetch)
# async
else:
response = None
try:
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(10, loop=self.hass.loop):
@@ -117,14 +116,9 @@ class GenericCamera(Camera):
except asyncio.TimeoutError:
_LOGGER.error('Timeout getting camera image')
return self._last_image
except (aiohttp.errors.ClientError,
aiohttp.errors.DisconnectedError,
aiohttp.errors.HttpProcessingError) as err:
except aiohttp.ClientError as err:
_LOGGER.error('Error getting new camera image: %s', err)
return self._last_image
finally:
if response is not None:
yield from response.release()
self._last_url = url
return self._last_image

View File

@@ -19,7 +19,7 @@ from homeassistant.const import (
HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera)
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_stream)
async_get_clientsession, async_aiohttp_proxy_web)
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -93,7 +93,6 @@ class MjpegCamera(Camera):
return image
websession = async_get_clientsession(self.hass)
response = None
try:
with async_timeout.timeout(10, loop=self.hass.loop):
response = yield from websession.get(
@@ -105,14 +104,9 @@ class MjpegCamera(Camera):
except asyncio.TimeoutError:
_LOGGER.error('Timeout getting camera image')
except (aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError) as err:
except aiohttp.ClientError as err:
_LOGGER.error('Error getting new camera image: %s', err)
finally:
if response is not None:
yield from response.release()
def camera_image(self):
"""Return a still image response from the camera."""
if self._username and self._password:
@@ -140,7 +134,7 @@ class MjpegCamera(Camera):
websession = async_get_clientsession(self.hass)
stream_coro = websession.get(self._mjpeg_url, auth=self._auth)
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
@property
def name(self):

View File

@@ -0,0 +1,74 @@
"""
Camera that loads a picture from an MQTT topic.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.mqtt/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.components.mqtt as mqtt
from homeassistant.const import CONF_NAME
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
CONF_TOPIC = 'topic'
DEFAULT_NAME = 'MQTT Camera'
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup the Camera."""
topic = config[CONF_TOPIC]
async_add_devices([MqttCamera(config[CONF_NAME], topic)])
class MqttCamera(Camera):
"""MQTT camera."""
def __init__(self, name, topic):
"""Initialize Local File Camera component."""
super().__init__()
self._name = name
self._topic = topic
self._qos = 0
self._last_image = None
@asyncio.coroutine
def async_camera_image(self):
"""Return image response."""
return self._last_image
@property
def name(self):
"""Return the name of this camera."""
return self._name
def async_added_to_hass(self):
"""Subscribe mqtt events.
This method must be run in the event loop and returns a coroutine.
"""
@callback
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
self._last_image = payload
return mqtt.async_subscribe(
self.hass, self._topic, message_received, self._qos, None)

View File

@@ -0,0 +1,65 @@
"""
Camera that loads a picture from a local file.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.neato/
"""
import logging
from datetime import timedelta
from homeassistant.components.camera import Camera
from homeassistant.components.neato import (
NEATO_MAP_DATA, NEATO_ROBOTS, NEATO_LOGIN)
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['neato']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Camera."""
dev = []
for robot in hass.data[NEATO_ROBOTS]:
if 'maps' in robot.traits:
dev.append(NeatoCleaningMap(hass, robot))
_LOGGER.debug('Adding robots for cleaning maps %s', dev)
add_devices(dev, True)
class NeatoCleaningMap(Camera):
"""Neato cleaning map for last clean."""
def __init__(self, hass, robot):
"""Initialize Neato cleaning map."""
super().__init__()
self.robot = robot
self._robot_name = self.robot.name + ' Cleaning Map'
self._robot_serial = self.robot.serial
self.neato = hass.data[NEATO_LOGIN]
self._image_url = None
self._image = None
def camera_image(self):
"""Return image response."""
self.update()
return self._image
@Throttle(timedelta(seconds=10))
def update(self):
"""Check the contents of the map list."""
self.neato.update_robots()
image_url = None
map_data = self.hass.data[NEATO_MAP_DATA]
image_url = map_data[self._robot_serial]['maps'][0]['url']
if image_url == self._image_url:
_LOGGER.debug('The map image_url is the same as old')
return
image = self.neato.download_map(image_url)
self._image = image.read()
self._image_url = image_url
@property
def name(self):
"""Return the name of this camera."""
return self._robot_name

View File

@@ -14,12 +14,12 @@ import async_timeout
from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL)
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, CONF_TIMEOUT)
from homeassistant.components.camera import (
Camera, PLATFORM_SCHEMA)
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_create_clientsession,
async_aiohttp_proxy_stream)
async_aiohttp_proxy_web)
import homeassistant.helpers.config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
@@ -27,7 +27,7 @@ _LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Synology Camera'
DEFAULT_STREAM_ID = '0'
TIMEOUT = 5
DEFAULT_TIMEOUT = 5
CONF_CAMERA_NAME = 'camera_name'
CONF_STREAM_ID = 'stream_id'
@@ -51,6 +51,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_URL): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_WHITELIST, default=[]): cv.ensure_list,
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
})
@@ -60,6 +61,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a Synology IP Camera."""
verify_ssl = config.get(CONF_VERIFY_SSL)
timeout = config.get(CONF_TIMEOUT)
websession_init = async_get_clientsession(hass, verify_ssl)
# Determine API to use for authentication
@@ -72,28 +74,25 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
'version': '1',
'query': 'SYNO.'
}
query_req = None
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
with async_timeout.timeout(timeout, loop=hass.loop):
query_req = yield from websession_init.get(
syno_api_url,
params=query_payload
)
query_resp = yield from query_req.json()
# Skip content type check because Synology doesn't return JSON with
# right content type
query_resp = yield from query_req.json(content_type=None)
auth_path = query_resp['data'][AUTH_API]['path']
camera_api = query_resp['data'][CAMERA_API]['path']
camera_path = query_resp['data'][CAMERA_API]['path']
streaming_path = query_resp['data'][STREAMING_API]['path']
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", syno_api_url)
return False
finally:
if query_req is not None:
yield from query_req.release()
# Authticate to NAS to get a session id
syno_auth_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, auth_path)
@@ -103,7 +102,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
websession_init,
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
syno_auth_url
syno_auth_url,
timeout
)
# init websession
@@ -120,18 +120,17 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
'version': '1'
}
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
with async_timeout.timeout(timeout, loop=hass.loop):
camera_req = yield from websession.get(
syno_camera_url,
params=camera_payload
)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", syno_camera_url)
return False
camera_resp = yield from camera_req.json()
camera_resp = yield from camera_req.json(content_type=None)
cameras = camera_resp['data']['cameras']
yield from camera_req.release()
# add cameras
devices = []
@@ -149,7 +148,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
snapshot_path,
streaming_path,
camera_path,
auth_path
auth_path,
timeout
)
devices.append(device)
@@ -157,7 +157,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
@asyncio.coroutine
def get_session_id(hass, websession, username, password, login_url):
def get_session_id(hass, websession, username, password, login_url, timeout):
"""Get a session id."""
auth_payload = {
'api': AUTH_API,
@@ -168,31 +168,26 @@ def get_session_id(hass, websession, username, password, login_url):
'session': 'SurveillanceStation',
'format': 'sid'
}
auth_req = None
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
with async_timeout.timeout(timeout, loop=hass.loop):
auth_req = yield from websession.get(
login_url,
params=auth_payload
)
auth_resp = yield from auth_req.json()
auth_resp = yield from auth_req.json(content_type=None)
return auth_resp['data']['sid']
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", login_url)
return False
finally:
if auth_req is not None:
yield from auth_req.release()
class SynologyCamera(Camera):
"""An implementation of a Synology NAS based IP camera."""
def __init__(self, hass, websession, config, camera_id,
camera_name, snapshot_path, streaming_path, camera_path,
auth_path):
auth_path, timeout):
"""Initialize a Synology Surveillance Station camera."""
super().__init__()
self.hass = hass
@@ -206,6 +201,7 @@ class SynologyCamera(Camera):
self._streaming_path = streaming_path
self._camera_path = camera_path
self._auth_path = auth_path
self._timeout = timeout
def camera_image(self):
"""Return bytes of camera image."""
@@ -225,17 +221,16 @@ class SynologyCamera(Camera):
'cameraId': self._camera_id
}
try:
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
response = yield from self._websession.get(
image_url,
params=image_payload
)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", image_url)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error fetching %s", image_url)
return None
image = yield from response.read()
yield from response.release()
return image
@@ -255,7 +250,7 @@ class SynologyCamera(Camera):
stream_coro = self._websession.get(
streaming_url, params=streaming_payload)
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
@property
def name(self):

View File

@@ -19,6 +19,9 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['zoneminder']
DOMAIN = 'zoneminder'
# From ZoneMinder's web/includes/config.php.in
ZM_STATE_ALARM = "2"
def _get_image_url(hass, monitor, mode):
zm_data = hass.data[DOMAIN]
@@ -69,10 +72,49 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
CONF_MJPEG_URL: _get_image_url(hass, monitor, 'jpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(hass, monitor, 'single')
}
cameras.append(MjpegCamera(hass, device_info))
cameras.append(ZoneMinderCamera(hass, device_info, monitor))
if not cameras:
_LOGGER.warning('No active cameras found')
return
async_add_devices(cameras)
class ZoneMinderCamera(MjpegCamera):
"""Representation of a ZoneMinder Monitor Stream."""
def __init__(self, hass, device_info, monitor):
"""Initialize as a subclass of MjpegCamera."""
super().__init__(hass, device_info)
self._monitor_id = int(monitor['Id'])
self._is_recording = None
@property
def should_poll(self):
"""Update the recording state periodically."""
return True
def update(self):
"""Update our recording state from the ZM API."""
_LOGGER.debug('Updating camera state for monitor %i', self._monitor_id)
status_response = zoneminder.get_state(
'api/monitors/alarm/id:%i/command:status.json' % self._monitor_id
)
if not status_response:
_LOGGER.warning('Could not get status for monitor %i',
self._monitor_id)
return
if status_response['success'] is False:
_LOGGER.warning('Alarm status API call failed for monitor %i',
self._monitor_id)
return
self._is_recording = status_response['status'] == ZM_STATE_ALARM
@property
def is_recording(self):
"""Return whether the monitor is in alarm mode."""
return self._is_recording

View File

@@ -224,7 +224,7 @@ def async_setup(hass, config):
if not climate.should_poll:
continue
update_coro = hass.loop.create_task(
update_coro = hass.async_add_job(
climate.async_update_ha_state(True))
if hasattr(climate, 'async_update'):
update_tasks.append(update_coro)
@@ -692,18 +692,16 @@ class ClimateDevice(Entity):
def _convert_for_display(self, temp):
"""Convert temperature into preferred units for display purposes."""
if (temp is None or not isinstance(temp, Number) or
self.temperature_unit == self.unit_of_measurement):
if temp is None or not isinstance(temp, Number):
return temp
value = convert_temperature(temp, self.temperature_unit,
self.unit_of_measurement)
if self.temperature_unit != self.unit_of_measurement:
temp = convert_temperature(temp, self.temperature_unit,
self.unit_of_measurement)
# Round in the units appropriate
if self.precision == PRECISION_HALVES:
return round(value * 2) / 2.0
return round(temp * 2) / 2.0
elif self.precision == PRECISION_TENTHS:
return round(value, 1)
return round(temp, 1)
else:
# PRECISION_WHOLE as a fall back
return round(value)
return round(temp)

View File

@@ -16,11 +16,15 @@ _LOGGER = logging.getLogger(__name__)
STATE_MANUAL = "manual"
STATE_BOOST = "boost"
STATE_COMFORT = "comfort"
STATE_LOWERING = "lowering"
HM_STATE_MAP = {
"AUTO_MODE": STATE_AUTO,
"MANU_MODE": STATE_MANUAL,
"BOOST_MODE": STATE_BOOST,
"COMFORT_MODE": STATE_COMFORT,
"LOWERING_MODE": STATE_LOWERING
}
HM_TEMP_MAP = [

View File

@@ -0,0 +1,292 @@
"""
Tado component to create a climate device for each zone.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.tado/
"""
import logging
from homeassistant.const import TEMP_CELSIUS
from homeassistant.components.climate import ClimateDevice
from homeassistant.const import ATTR_TEMPERATURE
from homeassistant.components.tado import DATA_TADO
_LOGGER = logging.getLogger(__name__)
CONST_MODE_SMART_SCHEDULE = 'SMART_SCHEDULE' # Default mytado mode
CONST_MODE_OFF = 'OFF' # Switch off heating in a zone
# When we change the temperature setting, we need an overlay mode
# wait until tado changes the mode automatic
CONST_OVERLAY_TADO_MODE = 'TADO_MODE'
# the user has change the temperature or mode manually
CONST_OVERLAY_MANUAL = 'MANUAL'
# the temperature will be reset after a timespan
CONST_OVERLAY_TIMER = 'TIMER'
OPERATION_LIST = {
CONST_OVERLAY_MANUAL: 'Manual',
CONST_OVERLAY_TIMER: 'Timer',
CONST_OVERLAY_TADO_MODE: 'Tado mode',
CONST_MODE_SMART_SCHEDULE: 'Smart schedule',
CONST_MODE_OFF: 'Off',
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Tado climate platform."""
tado = hass.data[DATA_TADO]
try:
zones = tado.get_zones()
except RuntimeError:
_LOGGER.error("Unable to get zone info from mytado")
return False
climate_devices = []
for zone in zones:
climate_devices.append(create_climate_device(
tado, hass, zone, zone['name'], zone['id']))
if len(climate_devices) > 0:
add_devices(climate_devices, True)
return True
else:
return False
def create_climate_device(tado, hass, zone, name, zone_id):
"""Create a Tado climate device."""
capabilities = tado.get_capabilities(zone_id)
unit = TEMP_CELSIUS
min_temp = float(capabilities['temperatures']['celsius']['min'])
max_temp = float(capabilities['temperatures']['celsius']['max'])
ac_mode = capabilities['type'] != 'HEATING'
data_id = 'zone {} {}'.format(name, zone_id)
device = TadoClimate(tado,
name, zone_id, data_id,
hass.config.units.temperature(min_temp, unit),
hass.config.units.temperature(max_temp, unit),
ac_mode)
tado.add_sensor(data_id, {
'id': zone_id,
'zone': zone,
'name': name,
'climate': device
})
return device
class TadoClimate(ClimateDevice):
"""Representation of a tado climate device."""
def __init__(self, store, zone_name, zone_id, data_id,
min_temp, max_temp, ac_mode,
tolerance=0.3):
"""Initialization of Tado climate device."""
self._store = store
self._data_id = data_id
self.zone_name = zone_name
self.zone_id = zone_id
self.ac_mode = ac_mode
self._active = False
self._device_is_active = False
self._unit = TEMP_CELSIUS
self._cur_temp = None
self._cur_humidity = None
self._is_away = False
self._min_temp = min_temp
self._max_temp = max_temp
self._target_temp = None
self._tolerance = tolerance
self._current_operation = CONST_MODE_SMART_SCHEDULE
self._overlay_mode = CONST_MODE_SMART_SCHEDULE
@property
def name(self):
"""Return the name of the sensor."""
return self.zone_name
@property
def current_humidity(self):
"""Return the current humidity."""
return self._cur_humidity
@property
def current_temperature(self):
"""Return the sensor temperature."""
return self._cur_temp
@property
def current_operation(self):
"""Return current readable operation mode."""
return OPERATION_LIST.get(self._current_operation)
@property
def operation_list(self):
"""List of available operation modes (readable)."""
return list(OPERATION_LIST.values())
@property
def temperature_unit(self):
"""The unit of measurement used by the platform."""
return self._unit
@property
def is_away_mode_on(self):
"""Return true if away mode is on."""
return self._is_away
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self._target_temp
def set_temperature(self, **kwargs):
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None:
return
self._current_operation = CONST_OVERLAY_TADO_MODE
self._overlay_mode = None
self._target_temp = temperature
self._control_heating()
def set_operation_mode(self, readable_operation_mode):
"""Set new operation mode."""
operation_mode = CONST_MODE_SMART_SCHEDULE
for mode, readable in OPERATION_LIST.items():
if readable == readable_operation_mode:
operation_mode = mode
break
self._current_operation = operation_mode
self._overlay_mode = None
self._control_heating()
@property
def min_temp(self):
"""Return the minimum temperature."""
if self._min_temp:
return self._min_temp
else:
# get default temp from super class
return super().min_temp
@property
def max_temp(self):
"""Return the maximum temperature."""
if self._max_temp:
return self._max_temp
else:
# Get default temp from super class
return super().max_temp
def update(self):
"""Update the state of this climate device."""
self._store.update()
data = self._store.get_data(self._data_id)
if data is None:
_LOGGER.debug("Recieved no data for zone %s", self.zone_name)
return
if 'sensorDataPoints' in data:
sensor_data = data['sensorDataPoints']
temperature = float(
sensor_data['insideTemperature']['celsius'])
humidity = float(
sensor_data['humidity']['percentage'])
setting = 0
# temperature setting will not exist when device is off
if 'temperature' in data['setting'] and \
data['setting']['temperature'] is not None:
setting = float(
data['setting']['temperature']['celsius'])
unit = TEMP_CELSIUS
self._cur_temp = self.hass.config.units.temperature(
temperature, unit)
self._target_temp = self.hass.config.units.temperature(
setting, unit)
self._cur_humidity = humidity
if 'tadoMode' in data:
mode = data['tadoMode']
self._is_away = mode == 'AWAY'
if 'setting' in data:
power = data['setting']['power']
if power == 'OFF':
self._current_operation = CONST_MODE_OFF
self._device_is_active = False
else:
self._device_is_active = True
if 'overlay' in data and data['overlay'] is not None:
overlay = True
termination = data['overlay']['termination']['type']
else:
overlay = False
termination = ""
# If you set mode manualy to off, there will be an overlay
# and a termination, but we want to see the mode "OFF"
if overlay and self._device_is_active:
# There is an overlay the device is on
self._overlay_mode = termination
self._current_operation = termination
else:
# There is no overlay, the mode will always be
# "SMART_SCHEDULE"
self._overlay_mode = CONST_MODE_SMART_SCHEDULE
self._current_operation = CONST_MODE_SMART_SCHEDULE
def _control_heating(self):
"""Send new target temperature to mytado."""
if not self._active and None not in (
self._cur_temp, self._target_temp):
self._active = True
_LOGGER.info("Obtained current and target temperature. "
"Tado thermostat active")
if not self._active or self._current_operation == self._overlay_mode:
return
if self._current_operation == CONST_MODE_SMART_SCHEDULE:
_LOGGER.info("Switching mytado.com to SCHEDULE (default) "
"for zone %s", self.zone_name)
self._store.reset_zone_overlay(self.zone_id)
self._overlay_mode = self._current_operation
return
if self._current_operation == CONST_MODE_OFF:
_LOGGER.info("Switching mytado.com to OFF for zone %s",
self.zone_name)
self._store.set_zone_overlay(self.zone_id, CONST_OVERLAY_MANUAL)
self._overlay_mode = self._current_operation
return
_LOGGER.info("Switching mytado.com to %s mode for zone %s",
self._current_operation, self.zone_name)
self._store.set_zone_overlay(
self.zone_id, self._current_operation, self._target_temp)
self._overlay_mode = self._current_operation

View File

@@ -85,11 +85,11 @@ class VeraThermostat(VeraDevice, ClimateDevice):
return self.vera_device.fan_cycle()
@property
def current_power_mwh(self):
"""Current power usage in mWh."""
def current_power_w(self):
"""Current power usage in W."""
power = self.vera_device.power
if power:
return convert(power, float, 0.0) * 1000
return convert(power, float, 0.0)
def update(self):
"""Called by the vera device callback to update state."""

View File

@@ -19,7 +19,6 @@ DEPENDENCIES = ['wink']
STATE_AUX = 'aux'
STATE_ECO = 'eco'
STATE_FAN = 'fan'
SPEED_LOWEST = 'lowest'
SPEED_LOW = 'low'
SPEED_MEDIUM = 'medium'
SPEED_HIGH = 'high'
@@ -400,7 +399,7 @@ class WinkAC(WinkDevice, ClimateDevice):
op_list.append(STATE_COOL)
if 'auto_eco' in modes:
op_list.append(STATE_ECO)
if 'fan_eco' in modes:
if 'fan_only' in modes:
op_list.append(STATE_FAN)
return op_list
@@ -439,9 +438,7 @@ class WinkAC(WinkDevice, ClimateDevice):
def current_fan_mode(self):
"""Return the current fan mode."""
speed = self.wink.current_fan_speed()
if speed <= 0.3 and speed >= 0.0:
return SPEED_LOWEST
elif speed <= 0.5 and speed > 0.3:
if speed <= 0.4 and speed > 0.3:
return SPEED_LOW
elif speed <= 0.8 and speed > 0.5:
return SPEED_MEDIUM
@@ -453,14 +450,12 @@ class WinkAC(WinkDevice, ClimateDevice):
@property
def fan_list(self):
"""List of available fan modes."""
return [SPEED_LOWEST, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
return [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
def set_fan_mode(self, mode):
"""Set fan speed."""
if mode == SPEED_LOWEST:
speed = 0.3
elif mode == SPEED_LOW:
speed = 0.5
if mode == SPEED_LOW:
speed = 0.4
elif mode == SPEED_MEDIUM:
speed = 0.8
elif mode == SPEED_HIGH:

View File

@@ -10,7 +10,6 @@ import logging
from homeassistant.components.climate import DOMAIN
from homeassistant.components.climate import ClimateDevice
from homeassistant.components.zwave import ZWaveDeviceEntity
from homeassistant.components import zwave
from homeassistant.components.zwave import async_setup_platform # noqa # pylint: disable=unused-import
from homeassistant.const import (
TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE)
@@ -33,20 +32,18 @@ DEVICE_MAPPINGS = {
}
def get_device(hass, value, **kwargs):
def get_device(hass, values, **kwargs):
"""Create zwave entity device."""
temp_unit = hass.config.units.temperature_unit
return ZWaveClimate(value, temp_unit)
return ZWaveClimate(values, temp_unit)
class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
"""Representation of a Z-Wave Climate device."""
def __init__(self, value, temp_unit):
def __init__(self, values, temp_unit):
"""Initialize the Z-Wave climate device."""
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._index = value.index
self._node = value.node
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
self._target_temperature = None
self._current_temperature = None
self._current_operation = None
@@ -61,10 +58,11 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
_LOGGER.debug("temp_unit is %s", self._unit)
self._zxt_120 = None
# Make sure that we have values for the key before converting to int
if (value.node.manufacturer_id.strip() and
value.node.product_id.strip()):
specific_sensor_key = (int(value.node.manufacturer_id, 16),
int(value.node.product_id, 16))
if (self.node.manufacturer_id.strip() and
self.node.product_id.strip()):
specific_sensor_key = (
int(self.node.manufacturer_id, 16),
int(self.node.product_id, 16))
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_ZXT_120:
_LOGGER.debug("Remotec ZXT-120 Zwave Thermostat"
@@ -75,86 +73,59 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
def update_properties(self):
"""Callback on data changes for node values."""
# Operation Mode
self._current_operation = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE, member='data')
operation_list = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE,
member='data_items')
if operation_list:
self._operation_list = list(operation_list)
if self.values.mode:
self._current_operation = self.values.mode.data
operation_list = self.values.mode.data_items
if operation_list:
self._operation_list = list(operation_list)
_LOGGER.debug("self._operation_list=%s", self._operation_list)
_LOGGER.debug("self._current_operation=%s", self._current_operation)
# Current Temp
self._current_temperature = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL,
label=['Temperature'], member='data')
device_unit = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL,
label=['Temperature'], member='units')
if device_unit is not None:
self._unit = device_unit
if self.values.temperature:
self._current_temperature = self.values.temperature.data
device_unit = self.values.temperature.units
if device_unit is not None:
self._unit = device_unit
# Fan Mode
self._current_fan_mode = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
member='data')
fan_list = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
member='data_items')
if fan_list:
self._fan_list = list(fan_list)
if self.values.fan_mode:
self._current_fan_mode = self.values.fan_mode.data
fan_list = self.values.fan_mode.data_items
if fan_list:
self._fan_list = list(fan_list)
_LOGGER.debug("self._fan_list=%s", self._fan_list)
_LOGGER.debug("self._current_fan_mode=%s",
self._current_fan_mode)
# Swing mode
if self._zxt_120 == 1:
self._current_swing_mode = (
self.get_value(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION,
index=33,
member='data'))
swing_list = self.get_value(class_id=zwave.const
.COMMAND_CLASS_CONFIGURATION,
index=33,
member='data_items')
if swing_list:
self._swing_list = list(swing_list)
if self.values.zxt_120_swing_mode:
self._current_swing_mode = self.values.zxt_120_swing_mode.data
swing_list = self.values.zxt_120_swing_mode.data_items
if swing_list:
self._swing_list = list(swing_list)
_LOGGER.debug("self._swing_list=%s", self._swing_list)
_LOGGER.debug("self._current_swing_mode=%s",
self._current_swing_mode)
# Set point
temps = []
for value in (
self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT)
.values()):
temps.append((round(float(value.data)), 1))
if value.index == self._index:
if value.data == 0:
_LOGGER.debug("Setpoint is 0, setting default to "
"current_temperature=%s",
self._current_temperature)
self._target_temperature = (
round((float(self._current_temperature)), 1))
break
else:
self._target_temperature = round((float(value.data)), 1)
if self.values.primary.data == 0:
_LOGGER.debug("Setpoint is 0, setting default to "
"current_temperature=%s",
self._current_temperature)
if self._current_temperature is not None:
self._target_temperature = (
round((float(self._current_temperature)), 1))
else:
self._target_temperature = round(
(float(self.values.primary.data)), 1)
# Operating state
self._operating_state = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE,
member='data')
if self.values.operating_state:
self._operating_state = self.values.operating_state.data
# Fan operating state
self._fan_state = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_STATE,
member='data')
@property
def should_poll(self):
"""No polling on Z-Wave."""
return False
if self.values.fan_state:
self._fan_state = self.values.fan_state.data
@property
def current_fan_mode(self):
@@ -213,41 +184,31 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
else:
return
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT,
index=self._index, data=temperature)
self.schedule_update_ha_state()
self.values.primary.data = temperature
def set_fan_mode(self, fan):
"""Set new target fan mode."""
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
index=0, data=bytes(fan, 'utf-8'))
if self.values.fan_mode:
self.values.fan_mode.data = bytes(fan, 'utf-8')
def set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE,
index=0, data=bytes(operation_mode, 'utf-8'))
if self.values.mode:
self.values.mode.data = bytes(operation_mode, 'utf-8')
def set_swing_mode(self, swing_mode):
"""Set new target swing mode."""
if self._zxt_120 == 1:
self.set_value(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION,
index=33, data=bytes(swing_mode, 'utf-8'))
if self.values.zxt_120_swing_mode:
self.values.zxt_120_swing_mode.data = bytes(
swing_mode, 'utf-8')
@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
data = super().device_state_attributes
if self._operating_state:
data[ATTR_OPERATING_STATE] = self._operating_state,
data[ATTR_OPERATING_STATE] = self._operating_state
if self._fan_state:
data[ATTR_FAN_STATE] = self._fan_state
return data
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on."""
return None

View File

@@ -180,7 +180,7 @@ class Configurator(object):
# field validation goes here?
callback(call.data.get(ATTR_FIELDS, {}))
self.hass.async_add_job(callback, call.data.get(ATTR_FIELDS, {}))
def _generate_unique_id(self):
"""Generate a unique configurator ID."""

View File

@@ -166,7 +166,7 @@ def async_setup(hass, config):
if not cover.should_poll:
continue
update_coro = hass.loop.create_task(
update_coro = hass.async_add_job(
cover.async_update_ha_state(True))
if hasattr(cover, 'async_update'):
update_tasks.append(update_coro)

View File

@@ -14,8 +14,8 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = [
'https://github.com/arraylabs/pymyq/archive/v0.0.7.zip'
'#pymyq==0.0.7']
'https://github.com/arraylabs/pymyq/archive/v0.0.8.zip'
'#pymyq==0.0.8']
COVER_SCHEMA = vol.Schema({
vol.Required(CONF_TYPE): cv.string,

View File

@@ -47,6 +47,7 @@ class VeraCover(VeraDevice, CoverDevice):
def set_cover_position(self, position, **kwargs):
"""Move the cover to a specific position."""
self.vera_device.set_level(position)
self.schedule_update_ha_state()
@property
def is_closed(self):
@@ -60,11 +61,14 @@ class VeraCover(VeraDevice, CoverDevice):
def open_cover(self, **kwargs):
"""Open the cover."""
self.vera_device.open()
self.schedule_update_ha_state()
def close_cover(self, **kwargs):
"""Close the cover."""
self.vera_device.close()
self.schedule_update_ha_state()
def stop_cover(self, **kwargs):
"""Stop the cover."""
self.vera_device.stop()
self.schedule_update_ha_state()

View File

@@ -20,64 +20,51 @@ _LOGGER = logging.getLogger(__name__)
SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE
def get_device(value, **kwargs):
def get_device(hass, values, node_config, **kwargs):
"""Create zwave entity device."""
if (value.command_class == zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL
and value.index == 0):
return ZwaveRollershutter(value)
elif (value.command_class == zwave.const.COMMAND_CLASS_SWITCH_BINARY or
value.command_class == zwave.const.COMMAND_CLASS_BARRIER_OPERATOR):
return ZwaveGarageDoor(value)
invert_buttons = node_config.get(zwave.CONF_INVERT_OPENCLOSE_BUTTONS)
if (values.primary.command_class ==
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL
and values.primary.index == 0):
return ZwaveRollershutter(hass, values, invert_buttons)
elif (values.primary.command_class in [
zwave.const.COMMAND_CLASS_SWITCH_BINARY,
zwave.const.COMMAND_CLASS_BARRIER_OPERATOR]):
return ZwaveGarageDoor(values)
return None
class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
"""Representation of an Zwave roller shutter."""
def __init__(self, value):
def __init__(self, hass, values, invert_buttons):
"""Initialize the zwave rollershutter."""
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
# pylint: disable=no-member
self._node = value.node
self._network = hass.data[zwave.ZWAVE_NETWORK]
self._open_id = None
self._close_id = None
self._current_position_id = None
self._current_position = None
self._invert_buttons = invert_buttons
self._workaround = workaround.get_device_mapping(value)
self._workaround = workaround.get_device_mapping(values.primary)
if self._workaround:
_LOGGER.debug("Using workaround %s", self._workaround)
self.update_properties()
@property
def dependent_value_ids(self):
"""List of value IDs a device depends on."""
if not self._node.is_ready:
return None
return [self._current_position_id]
def update_properties(self):
"""Callback on data changes for node values."""
# Position value
if not self._node.is_ready:
if self._current_position_id is None:
self._current_position_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Level'], member='value_id')
if self._open_id is None:
self._open_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Open', 'Up'], member='value_id')
if self._close_id is None:
self._close_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Close', 'Down'], member='value_id')
if self._open_id and self._close_id and \
self._workaround == workaround.WORKAROUND_REVERSE_OPEN_CLOSE:
self._open_id, self._close_id = self._close_id, self._open_id
self._workaround = None
self._current_position = self._node.get_dimmer_level(
self._current_position_id)
self._current_position = self.values.primary.data
if self.values.open and self.values.close and \
self._open_id is None and self._close_id is None:
if self._invert_buttons:
self._open_id = self.values.close.value_id
self._close_id = self.values.open.value_id
else:
self._open_id = self.values.open.value_id
self._close_id = self.values.close.value_id
@property
def is_closed(self):
@@ -104,32 +91,32 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
def open_cover(self, **kwargs):
"""Move the roller shutter up."""
zwave.NETWORK.manager.pressButton(self._open_id)
self._network.manager.pressButton(self._open_id)
def close_cover(self, **kwargs):
"""Move the roller shutter down."""
zwave.NETWORK.manager.pressButton(self._close_id)
self._network.manager.pressButton(self._close_id)
def set_cover_position(self, position, **kwargs):
"""Move the roller shutter to a specific position."""
self._node.set_dimmer(self._value.value_id, position)
self.node.set_dimmer(self.values.primary.value_id, position)
def stop_cover(self, **kwargs):
"""Stop the roller shutter."""
zwave.NETWORK.manager.releaseButton(self._open_id)
self._network.manager.releaseButton(self._open_id)
class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice):
"""Representation of an Zwave garage door device."""
def __init__(self, value):
def __init__(self, values):
"""Initialize the zwave garage door."""
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
self.update_properties()
def update_properties(self):
"""Callback on data changes for node values."""
self._state = self._value.data
self._state = self.values.primary.data
@property
def is_closed(self):
@@ -138,11 +125,11 @@ class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice):
def close_cover(self):
"""Close the garage door."""
self._value.data = False
self.values.primary.data = False
def open_cover(self):
"""Open the garage door."""
self._value.data = True
self.values.primary.data = True
@property
def device_class(self):

View File

@@ -159,13 +159,13 @@ def async_setup(hass, config):
tasks2.append(group.Group.async_create_group(hass, 'living room', [
lights[1], switches[0], 'input_select.living_room_preset',
'rollershutter.living_room_window', media_players[1],
'cover.living_room_window', media_players[1],
'scene.romantic_lights']))
tasks2.append(group.Group.async_create_group(hass, 'bedroom', [
lights[0], switches[1], media_players[0],
'input_slider.noise_allowance']))
tasks2.append(group.Group.async_create_group(hass, 'kitchen', [
lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door']))
lights[2], 'cover.kitchen_window', 'lock.kitchen_door']))
tasks2.append(group.Group.async_create_group(hass, 'doors', [
'lock.front_door', 'lock.kitchen_door',
'garage_door.right_garage_door', 'garage_door.left_garage_door']))
@@ -176,8 +176,8 @@ def async_setup(hass, config):
'device_tracker.demo_paulus']))
tasks2.append(group.Group.async_create_group(hass, 'downstairs', [
'group.living_room', 'group.kitchen',
'scene.romantic_lights', 'rollershutter.kitchen_window',
'rollershutter.living_room_window', 'group.doors',
'scene.romantic_lights', 'cover.kitchen_window',
'cover.living_room_window', 'group.doors',
'thermostat.ecobee',
], view=True))

View File

@@ -553,7 +553,6 @@ class Device(Entity):
# bytes like 00 get truncates to 0, API needs full bytes
oui = '{:02x}:{:02x}:{:02x}'.format(*[int(b, 16) for b in oui_bytes])
url = 'http://api.macvendors.com/' + oui
resp = None
try:
websession = async_get_clientsession(self.hass)
@@ -570,13 +569,9 @@ class Device(Entity):
# in the 'known_devices.yaml' file which only happens
# the first time the device is seen.
return 'unknown'
except (asyncio.TimeoutError, aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError):
except (asyncio.TimeoutError, aiohttp.ClientError):
# same as above
return 'unknown'
finally:
if resp is not None:
yield from resp.release()
@asyncio.coroutine
def async_added_to_hass(self):

View File

@@ -4,19 +4,20 @@ Support for the Automatic platform.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.automatic/
"""
import asyncio
from datetime import timedelta
import logging
import re
import requests
import voluptuous as vol
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, ATTR_ATTRIBUTES)
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.util import datetime as dt_util
from homeassistant.helpers.event import async_track_time_interval
REQUIREMENTS = ['aioautomatic==0.2.1']
_LOGGER = logging.getLogger(__name__)
@@ -24,129 +25,103 @@ CONF_CLIENT_ID = 'client_id'
CONF_SECRET = 'secret'
CONF_DEVICES = 'devices'
SCOPE = 'scope:location scope:vehicle:profile scope:user:profile scope:trip'
DEFAULT_TIMEOUT = 5
ATTR_ACCESS_TOKEN = 'access_token'
ATTR_EXPIRES_IN = 'expires_in'
ATTR_RESULTS = 'results'
ATTR_VEHICLE = 'vehicle'
ATTR_ENDED_AT = 'ended_at'
ATTR_END_LOCATION = 'end_location'
URL_AUTHORIZE = 'https://accounts.automatic.com/oauth/access_token/'
URL_VEHICLES = 'https://api.automatic.com/vehicle/'
URL_TRIPS = 'https://api.automatic.com/trip/'
_VEHICLE_ID_REGEX = re.compile(
(URL_VEHICLES + '(.*)?[/]$').replace('/', r'\/'))
SCOPE = ['location', 'vehicle:profile', 'trip']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_SECRET): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_DEVICES): vol.All(cv.ensure_list, [cv.string])
vol.Optional(CONF_DEVICES, default=None): vol.All(
cv.ensure_list, [cv.string])
})
def setup_scanner(hass, config: dict, see, discovery_info=None):
@asyncio.coroutine
def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Validate the configuration and return an Automatic scanner."""
import aioautomatic
client = aioautomatic.Client(
client_id=config[CONF_CLIENT_ID],
client_secret=config[CONF_SECRET],
client_session=async_get_clientsession(hass),
request_kwargs={'timeout': DEFAULT_TIMEOUT})
try:
AutomaticDeviceScanner(hass, config, see)
except requests.HTTPError as err:
session = yield from client.create_session_from_password(
SCOPE, config[CONF_USERNAME], config[CONF_PASSWORD])
data = AutomaticData(hass, session, config[CONF_DEVICES], async_see)
except aioautomatic.exceptions.AutomaticError as err:
_LOGGER.error(str(err))
return False
yield from data.update()
return True
class AutomaticDeviceScanner(object):
"""A class representing an Automatic device."""
class AutomaticData(object):
"""A class representing an Automatic cloud service connection."""
def __init__(self, hass, config: dict, see) -> None:
def __init__(self, hass, session, devices, async_see):
"""Initialize the automatic device scanner."""
self.hass = hass
self._devices = config.get(CONF_DEVICES, None)
self._access_token_payload = {
'username': config.get(CONF_USERNAME),
'password': config.get(CONF_PASSWORD),
'client_id': config.get(CONF_CLIENT_ID),
'client_secret': config.get(CONF_SECRET),
'grant_type': 'password',
'scope': SCOPE
}
self._headers = None
self._token_expires = dt_util.now()
self.last_results = {}
self.last_trips = {}
self.see = see
self.devices = devices
self.session = session
self.async_see = async_see
self._update_info()
async_track_time_interval(hass, self.update, timedelta(seconds=30))
track_utc_time_change(self.hass, self._update_info,
second=range(0, 60, 30))
def _update_headers(self):
"""Get the access token from automatic."""
if self._headers is None or self._token_expires <= dt_util.now():
resp = requests.post(
URL_AUTHORIZE,
data=self._access_token_payload)
resp.raise_for_status()
json = resp.json()
access_token = json[ATTR_ACCESS_TOKEN]
self._token_expires = dt_util.now() + timedelta(
seconds=json[ATTR_EXPIRES_IN])
self._headers = {
'Authorization': 'Bearer {}'.format(access_token)
}
def _update_info(self, now=None) -> None:
@asyncio.coroutine
def update(self, now=None):
"""Update the device info."""
import aioautomatic
_LOGGER.debug('Updating devices %s', now)
self._update_headers()
response = requests.get(URL_VEHICLES, headers=self._headers)
try:
vehicles = yield from self.session.get_vehicles()
except aioautomatic.exceptions.AutomaticError as err:
_LOGGER.error(str(err))
return False
response.raise_for_status()
for vehicle in vehicles:
name = vehicle.display_name
if name is None:
name = ' '.join(filter(None, (
str(vehicle.year), vehicle.make, vehicle.model)))
self.last_results = [item for item in response.json()[ATTR_RESULTS]
if self._devices is None or item[
'display_name'] in self._devices]
if self.devices is not None and name not in self.devices:
continue
response = requests.get(URL_TRIPS, headers=self._headers)
self.hass.async_add_job(self.update_vehicle(vehicle, name))
if response.status_code == 200:
for trip in response.json()[ATTR_RESULTS]:
vehicle_id = _VEHICLE_ID_REGEX.match(
trip[ATTR_VEHICLE]).group(1)
if vehicle_id not in self.last_trips:
self.last_trips[vehicle_id] = trip
elif self.last_trips[vehicle_id][ATTR_ENDED_AT] < trip[
ATTR_ENDED_AT]:
self.last_trips[vehicle_id] = trip
@asyncio.coroutine
def update_vehicle(self, vehicle, name):
"""Updated the specified vehicle's data."""
import aioautomatic
for vehicle in self.last_results:
dev_id = vehicle.get('id')
host_name = vehicle.get('display_name')
kwargs = {
'dev_id': vehicle.id,
'host_name': name,
'mac': vehicle.id,
ATTR_ATTRIBUTES: {
'fuel_level': vehicle.fuel_level_percent,
}
}
attrs = {
'fuel_level': vehicle.get('fuel_level_percent')
}
trips = []
try:
# Get the most recent trip for this vehicle
trips = yield from self.session.get_trips(
vehicle=vehicle.id, limit=1)
except aioautomatic.exceptions.AutomaticError as err:
_LOGGER.error(str(err))
kwargs = {
'dev_id': dev_id,
'host_name': host_name,
'mac': dev_id,
ATTR_ATTRIBUTES: attrs
}
if trips:
end_location = trips[0].end_location
kwargs['gps'] = (end_location.lat, end_location.lon)
kwargs['gps_accuracy'] = end_location.accuracy_m
if dev_id in self.last_trips:
end_location = self.last_trips[dev_id][ATTR_END_LOCATION]
kwargs['gps'] = (end_location['lat'], end_location['lon'])
kwargs['gps_accuracy'] = end_location['accuracy_m']
self.see(**kwargs)
yield from self.async_see(**kwargs)

View File

@@ -15,7 +15,7 @@ from homeassistant.components.device_tracker import (
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
REQUIREMENTS = ['fritzconnection==0.6']
REQUIREMENTS = ['fritzconnection==0.6.3']
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)

View File

@@ -18,23 +18,24 @@ from homeassistant.components.device_tracker import ( # NOQA
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
URL = '/api/locative'
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup an endpoint for the Locative application."""
"""Set up an endpoint for the Locative application."""
hass.http.register_view(LocativeView(see))
return True
class LocativeView(HomeAssistantView):
"""View to handle locative requests."""
"""View to handle Locative requests."""
url = '/api/locative'
url = URL
name = 'api:locative'
def __init__(self, see):
"""Initialize Locative url endpoints."""
"""Initialize Locative URL endpoints."""
self.see = see
@asyncio.coroutine
@@ -51,7 +52,6 @@ class LocativeView(HomeAssistantView):
return res
@asyncio.coroutine
# pylint: disable=too-many-return-statements
def _handle(self, hass, data):
"""Handle locative request."""
if 'latitude' not in data or 'longitude' not in data:

View File

@@ -14,6 +14,7 @@ import requests
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.exceptions import HomeAssistantError
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
@@ -31,6 +32,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
class InvalidLuciTokenError(HomeAssistantError):
"""When an invalid token is detected."""
pass
def get_scanner(hass, config):
"""Validate the configuration and return a Luci scanner."""
scanner = LuciDeviceScanner(config[DOMAIN])
@@ -46,8 +53,9 @@ class LuciDeviceScanner(DeviceScanner):
def __init__(self, config):
"""Initialize the scanner."""
host = config[CONF_HOST]
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
self.host = config[CONF_HOST]
self.username = config[CONF_USERNAME]
self.password = config[CONF_PASSWORD]
self.parse_api_pattern = re.compile(r"(?P<param>\w*) = (?P<value>.*);")
@@ -55,12 +63,15 @@ class LuciDeviceScanner(DeviceScanner):
self.last_results = {}
self.token = _get_token(host, username, password)
self.host = host
self.refresh_token()
self.mac2name = None
self.success_init = self.token is not None
def refresh_token(self):
"""Get a new token."""
self.token = _get_token(self.host, self.username, self.password)
def scan_devices(self):
"""Scan for new devices and return a list with found device IDs."""
self._update_info()
@@ -98,8 +109,15 @@ class LuciDeviceScanner(DeviceScanner):
_LOGGER.info('Checking ARP')
url = 'http://{}/cgi-bin/luci/rpc/sys'.format(self.host)
result = _req_json_rpc(url, 'net.arptable',
params={'auth': self.token})
try:
result = _req_json_rpc(url, 'net.arptable',
params={'auth': self.token})
except InvalidLuciTokenError:
_LOGGER.info('Refreshing token')
self.refresh_token()
return False
if result:
self.last_results = []
for device_entry in result:
@@ -116,6 +134,7 @@ class LuciDeviceScanner(DeviceScanner):
def _req_json_rpc(url, method, *args, **kwargs):
"""Perform one JSON RPC operation."""
data = json.dumps({'method': method, 'params': args})
try:
res = requests.post(url, data=data, timeout=5, **kwargs)
except requests.exceptions.Timeout:
@@ -139,6 +158,10 @@ def _req_json_rpc(url, method, *args, **kwargs):
"Failed to authenticate, "
"please check your username and password")
return
elif res.status_code == 403:
_LOGGER.error('Luci responded with a 403 Invalid token')
raise InvalidLuciTokenError
else:
_LOGGER.error('Invalid response from luci: %s', res)

View File

@@ -0,0 +1,85 @@
"""
Support for GPS tracking MQTT enabled devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.mqtt_json/
"""
import asyncio
import json
import logging
import voluptuous as vol
import homeassistant.components.mqtt as mqtt
from homeassistant.core import callback
from homeassistant.components.mqtt import CONF_QOS
from homeassistant.components.device_tracker import PLATFORM_SCHEMA
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_DEVICES, ATTR_GPS_ACCURACY, ATTR_LATITUDE,
ATTR_LONGITUDE, ATTR_BATTERY_LEVEL)
DEPENDENCIES = ['mqtt']
_LOGGER = logging.getLogger(__name__)
GPS_JSON_PAYLOAD_SCHEMA = vol.Schema({
vol.Required(ATTR_LATITUDE): vol.Coerce(float),
vol.Required(ATTR_LONGITUDE): vol.Coerce(float),
vol.Optional(ATTR_GPS_ACCURACY, default=None): vol.Coerce(int),
vol.Optional(ATTR_BATTERY_LEVEL, default=None): vol.Coerce(str),
}, extra=vol.ALLOW_EXTRA)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(mqtt.SCHEMA_BASE).extend({
vol.Required(CONF_DEVICES): {cv.string: mqtt.valid_subscribe_topic},
})
@asyncio.coroutine
def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Setup the MQTT tracker."""
devices = config[CONF_DEVICES]
qos = config[CONF_QOS]
dev_id_lookup = {}
@callback
def async_tracker_message_received(topic, payload, qos):
"""MQTT message received."""
dev_id = dev_id_lookup[topic]
try:
data = GPS_JSON_PAYLOAD_SCHEMA(json.loads(payload))
except vol.MultipleInvalid:
_LOGGER.error('Skipping update for following data '
'because of missing or malformatted data: %s',
payload)
return
except ValueError:
_LOGGER.error('Error parsing JSON payload: %s', payload)
return
kwargs = _parse_see_args(dev_id, data)
hass.async_add_job(
async_see(**kwargs))
for dev_id, topic in devices.items():
dev_id_lookup[topic] = dev_id
yield from mqtt.async_subscribe(
hass, topic, async_tracker_message_received, qos)
return True
def _parse_see_args(dev_id, data):
"""Parse the payload location parameters, into the format see expects."""
kwargs = {
'gps': (data[ATTR_LATITUDE], data[ATTR_LONGITUDE]),
'dev_id': dev_id
}
if ATTR_GPS_ACCURACY in data:
kwargs[ATTR_GPS_ACCURACY] = data[ATTR_GPS_ACCURACY]
if ATTR_BATTERY_LEVEL in data:
kwargs['battery'] = data[ATTR_BATTERY_LEVEL]
return kwargs

View File

@@ -16,41 +16,43 @@ _LOGGER = logging.getLogger(__name__)
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the MySensors tracker."""
def mysensors_callback(gateway, node_id):
def mysensors_callback(gateway, msg):
"""Callback for mysensors platform."""
node = gateway.sensors[node_id]
node = gateway.sensors[msg.node_id]
if node.sketch_name is None:
_LOGGER.info('No sketch_name: node %s', node_id)
_LOGGER.debug('No sketch_name: node %s', msg.node_id)
return
pres = gateway.const.Presentation
set_req = gateway.const.SetReq
for child in node.children.values():
position = child.values.get(set_req.V_POSITION)
if child.type != pres.S_GPS or position is None:
continue
try:
latitude, longitude, _ = position.split(',')
except ValueError:
_LOGGER.error('Payload for V_POSITION %s is not of format '
'latitude,longitude,altitude', position)
continue
name = '{} {} {}'.format(
node.sketch_name, node_id, child.id)
attr = {
mysensors.ATTR_CHILD_ID: child.id,
mysensors.ATTR_DESCRIPTION: child.description,
mysensors.ATTR_DEVICE: gateway.device,
mysensors.ATTR_NODE_ID: node_id,
}
see(
dev_id=slugify(name),
host_name=name,
gps=(latitude, longitude),
battery=node.battery_level,
attributes=attr
)
child = node.children.get(msg.child_id)
if child is None:
return
position = child.values.get(set_req.V_POSITION)
if child.type != pres.S_GPS or position is None:
return
try:
latitude, longitude, _ = position.split(',')
except ValueError:
_LOGGER.error('Payload for V_POSITION %s is not of format '
'latitude,longitude,altitude', position)
return
name = '{} {} {}'.format(
node.sketch_name, msg.node_id, child.id)
attr = {
mysensors.ATTR_CHILD_ID: child.id,
mysensors.ATTR_DESCRIPTION: child.description,
mysensors.ATTR_DEVICE: gateway.device,
mysensors.ATTR_NODE_ID: msg.node_id,
}
see(
dev_id=slugify(name),
host_name=name,
gps=(latitude, longitude),
battery=node.battery_level,
attributes=attr
)
gateways = hass.data.get(mysensors.MYSENSORS_GATEWAYS)

View File

@@ -1,15 +1,8 @@
"""
Tracks devices by sending a ICMP ping.
Tracks devices by sending a ICMP echo request (ping).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.ping/
device_tracker:
- platform: ping
count: 2
hosts:
host_one: pc.local
host_two: 192.168.2.25
"""
import logging
import subprocess
@@ -18,14 +11,12 @@ from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, DEFAULT_SCAN_INTERVAL, SOURCE_TYPE_ROUTER)
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant import util
from homeassistant import const
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = []
_LOGGER = logging.getLogger(__name__)
@@ -37,7 +28,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
class Host:
class Host(object):
"""Host object with ping detection."""
def __init__(self, ip_address, dev_id, hass, config):
@@ -53,8 +44,10 @@ class Host:
self.ip_address]
def ping(self):
"""Send ICMP ping and return True if success."""
pinger = subprocess.Popen(self._ping_cmd, stdout=subprocess.PIPE)
"""Send an ICMP echo request and return True if success."""
pinger = subprocess.Popen(self._ping_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL)
try:
pinger.communicate()
return pinger.returncode == 0
@@ -70,7 +63,7 @@ class Host:
return True
failed += 1
_LOGGER.debug("ping KO on ip=%s failed=%d", self.ip_address, failed)
_LOGGER.debug("No response from %s failed=%d", self.ip_address, failed)
def setup_scanner(hass, config, see, discovery_info=None):

View File

@@ -19,7 +19,7 @@ from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pysnmp==4.3.4']
REQUIREMENTS = ['pysnmp==4.3.5']
CONF_COMMUNITY = 'community'
CONF_AUTHKEY = 'authkey'
@@ -125,7 +125,10 @@ class SnmpScanner(DeviceScanner):
for resrow in restable:
for _, val in resrow:
mac = binascii.hexlify(val.asOctets()).decode('utf-8')
try:
mac = binascii.hexlify(val.asOctets()).decode('utf-8')
except AttributeError:
continue
_LOGGER.debug("Found MAC %s", mac)
mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)])
devices.append({'mac': mac})

View File

@@ -105,8 +105,6 @@ class TadoDeviceScanner(DeviceScanner):
_LOGGER.debug("Requesting Tado")
last_results = []
response = None
tado_json = None
try:
with async_timeout.timeout(10, loop=self.hass.loop):
@@ -127,14 +125,10 @@ class TadoDeviceScanner(DeviceScanner):
tado_json = yield from response.json()
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Cannot load Tado data")
return False
finally:
if response is not None:
yield from response.release()
# Without a home_id, we fetched an URL where the mobile devices can be
# found under the mobileDevices key.
if 'mobileDevices' in tado_json:

View File

@@ -101,7 +101,6 @@ class UPCDeviceScanner(DeviceScanner):
@asyncio.coroutine
def async_login(self):
"""Login into firmware and get first token."""
response = None
try:
# get first token
with async_timeout.timeout(10, loop=self.hass.loop):
@@ -109,7 +108,8 @@ class UPCDeviceScanner(DeviceScanner):
"http://{}/common_page/login.html".format(self.host)
)
yield from response.text()
yield from response.text()
self.token = response.cookies['sessionToken'].value
# login
@@ -119,18 +119,12 @@ class UPCDeviceScanner(DeviceScanner):
})
# successfull?
if data is not None:
return True
return False
return data is not None
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Can not load login page from %s", self.host)
return False
finally:
if response is not None:
yield from response.release()
@asyncio.coroutine
def _async_ws_function(self, function, additional_form=None):
"""Execute a command on UPC firmware webservice."""
@@ -142,8 +136,7 @@ class UPCDeviceScanner(DeviceScanner):
if additional_form:
form_data.update(additional_form)
redirects = True if function != CMD_DEVICES else False
response = None
redirects = function != CMD_DEVICES
try:
with async_timeout.timeout(10, loop=self.hass.loop):
response = yield from self.websession.post(
@@ -163,10 +156,6 @@ class UPCDeviceScanner(DeviceScanner):
self.token = response.cookies['sessionToken'].value
return (yield from response.text())
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error on %s", function)
self.token = None
finally:
if response is not None:
yield from response.release()

View File

@@ -13,7 +13,7 @@ from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['python-digitalocean==1.10.1']
REQUIREMENTS = ['python-digitalocean==1.11']
_LOGGER = logging.getLogger(__name__)

View File

@@ -10,16 +10,18 @@ import asyncio
import json
from datetime import timedelta
import logging
import os
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_START
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.discovery import async_load_platform, async_discover
import homeassistant.util.dt as dt_util
REQUIREMENTS = ['netdisco==0.9.2']
REQUIREMENTS = ['netdisco==1.0.0rc3']
DOMAIN = 'discovery'
@@ -27,11 +29,15 @@ SCAN_INTERVAL = timedelta(seconds=300)
SERVICE_NETGEAR = 'netgear_router'
SERVICE_WEMO = 'belkin_wemo'
SERVICE_HASS_IOS_APP = 'hass_ios'
SERVICE_IKEA_TRADFRI = 'ikea_tradfri'
SERVICE_HASSIO = 'hassio'
SERVICE_HANDLERS = {
SERVICE_HASS_IOS_APP: ('ios', None),
SERVICE_NETGEAR: ('device_tracker', None),
SERVICE_WEMO: ('wemo', None),
SERVICE_IKEA_TRADFRI: ('tradfri', None),
SERVICE_HASSIO: ('hassio', None),
'philips_hue': ('light', 'hue'),
'google_cast': ('media_player', 'cast'),
'panasonic_viera': ('media_player', 'panasonic_viera'),
@@ -44,10 +50,10 @@ SERVICE_HANDLERS = {
'denonavr': ('media_player', 'denonavr'),
'samsung_tv': ('media_player', 'samsungtv'),
'yeelight': ('light', 'yeelight'),
'flux_led': ('light', 'flux_led'),
'apple_tv': ('media_player', 'apple_tv'),
'frontier_silicon': ('media_player', 'frontier_silicon'),
'openhome': ('media_player', 'openhome'),
'bose_soundtouch': ('media_player', 'soundtouch'),
}
CONF_IGNORE = 'ignore'
@@ -105,7 +111,7 @@ def async_setup(hass, config):
hass, component, platform, info, config)
@asyncio.coroutine
def scan_devices(_):
def scan_devices(now):
"""Scan for devices."""
results = yield from hass.loop.run_in_executor(
None, _discover, netdisco)
@@ -116,7 +122,16 @@ def async_setup(hass, config):
async_track_point_in_utc_time(hass, scan_devices,
dt_util.utcnow() + SCAN_INTERVAL)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, scan_devices)
@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
if 'HASSIO' in os.environ:
hass.async_add_job(new_service_found(SERVICE_HASSIO, {}))
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, schedule_first)
return True

View File

@@ -218,7 +218,7 @@ def async_setup(hass, config: dict):
if not fan.should_poll:
continue
update_coro = hass.loop.create_task(
update_coro = hass.async_add_job(
fan.async_update_ha_state(True))
if hasattr(fan, 'async_update'):
update_tasks.append(update_coro)

View File

@@ -153,7 +153,7 @@ def setup(hass, config):
sw_path = "service_worker.js"
hass.http.register_static_path("/service_worker.js",
os.path.join(STATIC_PATH, sw_path), 0)
os.path.join(STATIC_PATH, sw_path), False)
hass.http.register_static_path("/robots.txt",
os.path.join(STATIC_PATH, "robots.txt"))
hass.http.register_static_path("/static", STATIC_PATH)

View File

@@ -63,7 +63,7 @@
window.Polymer = {
lazyRegister: true,
useNativeCSSProperties: true,
dom: 'shady',
dom: 'shadow',
suppressTemplateNotifications: true,
suppressBindingNotifications: true,
};

View File

@@ -2,19 +2,21 @@
FINGERPRINTS = {
"compatibility.js": "83d9c77748dafa9db49ae77d7f3d8fb0",
"core.js": "1f7f88d8f5dada08bce1d935cfa5f33e",
"frontend.html": "418f6ef8354ce71f1b9594ee2068ebef",
"mdi.html": "65413cdf82f822bd6480e577852f0292",
"core.js": "5d08475f03adb5969bd31855d5ca0cfd",
"frontend.html": "1533f44c55927e814294de757cd7eada",
"mdi.html": "1cc8593d3684f7f6f3b3854403216f77",
"micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a",
"panels/ha-panel-config.html": "412b3e24515ffa1ee8074ce974cf4057",
"panels/ha-panel-dev-event.html": "91347dedf3b4fa9b49ccf4c0a28a03c4",
"panels/ha-panel-config.html": "39f00f769faa63ee61f1fe6fc85d67f7",
"panels/ha-panel-dev-event.html": "2db9c218065ef0f61d8d08db8093cad2",
"panels/ha-panel-dev-info.html": "61610e015a411cfc84edd2c4d489e71d",
"panels/ha-panel-dev-service.html": "153aad076f98bbd626466bac50986874",
"panels/ha-panel-dev-state.html": "90f3bede9602241552ef7bb7958198c6",
"panels/ha-panel-dev-template.html": "c249a4fc18a3a6994de3d6330cfe6cbb",
"panels/ha-panel-history.html": "fdaa4d2402d49d4c8bd64a1708ab7a50",
"panels/ha-panel-dev-service.html": "415552027cb083badeff5f16080410ed",
"panels/ha-panel-dev-state.html": "d70314913b8923d750932367b1099750",
"panels/ha-panel-dev-template.html": "567fbf86735e1b891e40c2f4060fec9b",
"panels/ha-panel-hassio.html": "1d954cfe5f47c4be3cf4f6f5db9a83b2",
"panels/ha-panel-history.html": "89062c48c76206cad1cec14ddbb1cbb1",
"panels/ha-panel-iframe.html": "d920f0aa3c903680f2f8795e2255daab",
"panels/ha-panel-logbook.html": "2af1feb30b37427f481d5437a438a3f2",
"panels/ha-panel-map.html": "e10704a3469e44d1714eac9ed8e4b6a0",
"panels/ha-panel-logbook.html": "6dd6a16f52117318b202e60f98400163",
"panels/ha-panel-map.html": "31c592c239636f91e07c7ac232a5ebc4",
"panels/ha-panel-zwave.html": "a81f82b48439da80286798558f414a2e",
"websocket_test.html": "575de64b431fe11c3785bf96d7813450"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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