Compare commits

...

795 Commits
0.34 ... 0.38

Author SHA1 Message Date
Robbie Trencheny
e0f1c8ac67 Merge pull request #5833 from home-assistant/release-038
Release 0.38
2017-02-11 14:38:12 -08:00
Paulus Schoutsen
d8a34877d4 Version bump to 0.38 2017-02-10 20:55:52 -08:00
Marcelo Moreira de Mello
3fb70afb14 Avoid traceback for Amcrest cameras/firmware that does not have the software_information API call (#5865)
* Avoid traceback for Amcrest cameras/firmware that does not have the software_information API call

* Make lint happy
2017-02-10 20:55:34 -08:00
Johan Bloemberg
bb043c47f8 Rflink update and small refactor. (#5789)
* Use same pattern for device defaults in both platforms.

* Update Rflink that passes loop downstream.

* Update requirements.
2017-02-10 20:55:34 -08:00
Paulus Schoutsen
849ae9903c Recorder run can be None (#5854) 2017-02-10 20:55:34 -08:00
Johann Kellerman
66088377e1 [recorder] Run end model changed in session scope (#5858) 2017-02-10 20:55:34 -08:00
Paulus Schoutsen
0e6ab3ace6 Update frontend (#5855) 2017-02-10 20:55:34 -08:00
Pascal Vizeli
eaa6392535 Fix check_config script. (#5853) 2017-02-10 20:55:34 -08:00
Petr Vraník
a071cd21f2 version bump (#5846)
Add an optional extended description…
2017-02-10 20:55:33 -08:00
Teemu R
0f6aed16a2 bump python-yeelight version (#5850)
Add an optional extended description…
2017-02-10 20:55:33 -08:00
Christian Brædstrup
cee389f621 D-Link switch version bump of external library (#5843) 2017-02-10 20:55:33 -08:00
Andrey
2b62e9f008 Fix zwave helper getter not to fail on some None results. (#5845) 2017-02-10 20:55:33 -08:00
Adam Mills
3d7b79f523 [recorder] Add tests for full schema migration (#5831)
* [recorder] Add tests for full schema migration

* Remove leftover code

* Fix duplicate creation of sqlalchemy Index object

* It's that kind of day...

* Improve models_original docstring
2017-02-10 20:55:33 -08:00
Philipp Schmitt
7bf7c727d1 Refactoring and JSON decode error handling (#5826)
* Refactoring and JSON decode error handling

* Catch ValueError instead of simplejson.scanner.JSONDecodeError
2017-02-10 20:55:33 -08:00
Pascal Vizeli
3f87d28616 Update aiohttp 1.3.1 (#5838) 2017-02-10 20:55:33 -08:00
Pierre Ståhl
7259082de5 Reuse default aiohttp session (#5836) 2017-02-10 20:55:33 -08:00
Pierre Ståhl
60f85b1e09 Handle connection errors when connecting to Apple TVs (#5829)
* Handle connection errors when connecting to Apple TVs

Also bump pyatv to 0.1.2 which fixes a request leak.

* Fix pylint error

* Fix import order
2017-02-10 20:55:33 -08:00
Erik Eriksson
0fdf1391e2 Don't thow exception if connection to server is lost (#5775) 2017-02-10 20:55:33 -08:00
Paulus Schoutsen
e5256ccf1f Merge remote-tracking branch 'origin/master' into dev 2017-02-09 08:54:40 -08:00
John Arild Berentsen
298575f7cb Adding helper for get and set values (#5743)
* cleanup

* Update __init__.py

* Update __init__.py

* Update __init__.py
2017-02-09 13:40:35 +01:00
Paulus Schoutsen
c550a316a4 Make device sun light trigger async (#5823) 2017-02-09 00:10:53 -08:00
Paulus Schoutsen
4398b8b5c6 Deprecate event decorators (#5822) 2017-02-09 00:10:38 -08:00
Pascal Vizeli
2cbed9cd96 Move signal handling out of core to bootstrap (#5815)
* Move signal handling out of core to bootstrap

* Fix tests
2017-02-08 21:58:45 -08:00
Paulus Schoutsen
7eb4bdc37b Upgrade aiohttp to 1.3 (#5821) 2017-02-08 21:27:36 -08:00
Paulus Schoutsen
ebceca36ec Update frontend (#5820) 2017-02-08 20:55:15 -08:00
Paulus Schoutsen
03fe5b04b5 Remove non-working webfont preload (#5819) 2017-02-08 20:31:36 -08:00
Duoxilian
7fa08059dc Support away_mode as permanent hold and hold_mode as temporary hold. (#5725)
* Support away_mode as permanent hold and hold_mode as temporary hold.

* Add comments to explain code better. Remove indefinite hold preference
to be consistent with 'away_mode'.
2017-02-08 20:04:09 -08:00
John Arild Berentsen
fdcf5fe233 Bugfixes (#5740)
* wrong data for lock alarm_type

* missing whitespace

* Not possible to set codes starting with 0
2017-02-08 19:59:47 -08:00
Johann Kellerman
415500de23 [recorder] Protect against running in the event loop (#5812) 2017-02-08 19:58:43 -08:00
William Scanlon
628b169393 Fixed call to object_id() (#5814) 2017-02-08 19:57:58 -08:00
Sean Dague
49f2540730 Enhancements to ARWN platform (#5816)
* Fix arwn platform discover_sensors

The discover_sensors function can return either singletons or a list
of sensors. However the consumer was always expecting a list. This
fixes it to work in both cases.

* Add custom icons to arwn sensors.

This adds some custom icons for different kinds of weather sensors
that the arwn platform returns. Makes it a little easier to see what's
going on.
2017-02-08 19:56:44 -08:00
Teagan Glenn
76db4cc099 Change medium state for filtering (#5817) 2017-02-08 19:55:48 -08:00
Pascal Vizeli
d29b7f6910 Ffmpeg update 1.4 (#5813)
* Pump ffmpeg version

* update entity

* next 1.4
2017-02-08 23:18:23 +01:00
Johann Kellerman
612aa1cf21 Initial (#5811) 2017-02-08 13:16:39 -08:00
Andreas Cambitsis
4f20a2d3ea Upgrade Russound integration to v0.1.7 (#5756)
* Bumped up version to use 0.1.7 of Russound integration module.
Fixed bug arising from not supporting TURN_ON state (fixes issue https://github.com/home-assistant/home-assistant/issues/5012)
Implemented state support in 0.1.7 such that component state is returned from the actual AMP. (Still uses polling model though).
Tested it with home-assitant users @laf (original developer of the module) and @hofsta.  Works fine with their Russounds.

* Made styling / compliance changes and updated correct version of russound module on requirements_all.txt.

* Changed handling of properties to be compliant with https://github.com/home-assistant/home-assistant/issues/4210
(Specifcailly added member variables for state, volume and source to cache these values, and introduced Update() method to set their values).

Now returns None if the selected source index that is returned from russound is greater than the length of the specified source list in the yaml config.
Removed unnecesary comment.

* Removed blank line after docstring.

* Removed updated() in class init and added True paramter to add_devices in setup_platform.

* Dropped the no longer needed self.update()
2017-02-08 15:53:59 -05:00
Teemu R
061985bc65 Add available property and typing hints (#5593)
* light.demo: add available property, add typing hints

* light.demo: keep all lights available, fix init ordering

* Fix issues raised during review

* Update demo.py
2017-02-08 12:13:07 -08:00
Andrey
4b15946a9b Make sure workaround_component is not none (#5808) 2017-02-08 21:11:36 +02:00
Pascal Vizeli
881d53339b [Image_Processing][Breaking Change] Cleanup Base face class add support for microsoft face detect (#5802)
* [Image_Processing] Cleanup Base face class add support for microsoft face detect

* fix lint

* add unittest for micosoft detect

* fix test
2017-02-08 09:19:40 -08:00
Pascal Vizeli
3f82ef64a1 Move core service from core to components (#5787)
* Move core servcie from core to components

* add new handler for signals/exception

* Static persistent id

* Move unittest

* fix coro/callback

* Add more unittest for new services

* Address comments

* Update __init__.py
2017-02-08 09:17:52 -08:00
Daniel Høyer Iversen
08efe2bf6d Improve warning message in template rendering (#5806)
* improve warning message when template is none

* improve error message when template is none

* improve error message when template is none

* improve error message when template is none
2017-02-08 09:07:43 -08:00
Johann Kellerman
db6c166abe Update sma.py (#5807) 2017-02-08 09:07:03 -08:00
George.M
8951e1bdc0 The word router was misspelt (#5803)
fixed as this message is user facing
2017-02-08 12:28:45 +01:00
Pierre Ståhl
250523c1d8 Add discovery suppport to Apple TV (#5801)
Add an optional extended description…
2017-02-08 12:17:23 +01:00
Philipp Schmitt
2dab6cbb0e Mailgun notify service (#5782)
* Mailgun notify service

* Update dependency to version 1.3

- The provided credentials (including the domain) are now checked during
startup, as requested by @balloob
- The domain name is now optional
- There's a new config item "sandbox" which indicates whether to use the
sandboxed domain in case the domain is not set

* Fix a few lint issues

* Disable lint check no-value-for-parameter
2017-02-07 22:22:19 -08:00
Johan Bloemberg
0e6dd39c15 Add support for fluxled discovery. (#5784)
* Add support for fluxled discovery.

* Make use of device type/protocol auto detection.
2017-02-07 21:49:36 -08:00
Johann Kellerman
490ef6afad WIP: [component/recorder] Refactoring & better handling of SQLAlchemy Sessions (#5607)
* Refactor recorder and Sessions

* Cover #4352

* NO_reset_on_return

* contextmanager

* coverage
2017-02-07 21:47:41 -08:00
Paulus Schoutsen
bdebe5d53c Update frontend (#5800) 2017-02-07 21:30:24 -08:00
Adam Mills
ecfe8e0a9a Formalize supported_features as entity property (#5794)
* Formalize supported_features as entity property

* Remove extra emulated_hue conditions

* Generate log message in executor
2017-02-07 20:42:45 -08:00
Jeff Wilson
4fa4d7347f Fix climate.set_fan_mode yaml (#5799) 2017-02-07 20:38:16 -08:00
Andrey
1b54218d46 Create a file for zwave workarounds. (#5798)
* Create a file for zwave workarounds. Add sensor->binary_sensor for fgfs101 (#2)

* Don't use default None
2017-02-07 20:37:11 -08:00
Robbie Trencheny
b8a0792424 Bump netdisco to 0.8.2 2017-02-07 14:11:51 -08:00
Josh Wright
35f6dbc9dc Update python-nest dependency version (#5795)
The nest-cam changes have now been merged into the upstream library, so
there is no need to track a specific branch.

Updating to 3.1.0 also fixes a structure parsing bug I was experiencing.
2017-02-07 22:55:49 +01:00
Fabian Affolter
12bc7c7316 Upgrade pyasn1 to 0.2.2 (#5796) 2017-02-07 22:55:21 +01:00
Pascal Vizeli
acdda1f42b Revert last change on TTS cache load for more speed (#5797) 2017-02-07 22:54:52 +01:00
Martin Hjelmare
45a7c27280 Add mysensors device tracker and platform discovery (#5781)
* Add mysensors device_tracker platform

* Add discovery of device_tracker platforms

* Enable discovery of device_tracker platforms that are not
  DeviceScanner.
* Update signature of setup_scanner function in all affected platforms.
* Add test.
* Use discovery for mysensors device_tracker platform.

* Remove gps accuracy

* Small change to core like schema

* fix depency
2017-02-07 20:47:11 +01:00
Fabian Affolter
c7fd28c10f MQTT discovery (#5724)
* Change implementation

* Re-write

* Remove unused consts

* Update discovery.py

* Add tests

* fix other tests

* Fix check_config script test

* Lint

* Lint
2017-02-07 09:13:24 -08:00
Robbie Trencheny
45507cd9d1 TTS ID3 support (#5773)
* Add support for writing ID3 tags to the file for improved display in media players

* Lint and async fixes

* Use mutagen instead of taglib

* Fix tests

* Add fallback for album

* Requested changes

* move import

* Fix album name

* Change default options handling

* Move to member function / minor fix

* fix style

* fix lint

* change mutagen handling

* fix lint / add name to bytesio

* Update __init__.py

* Fix test, some cleanups

* Add mutagen exeption handling, fix tests

* fix mutagen taging
2017-02-07 12:07:11 +01:00
Trevor
063c0e8f44 Add icon_template to template sensor (#5766)
* Add icon_template to template sensor

* Update test_template.py

* Update test_template.py again

* Update template.py

* Update test_template.py

* Update test_template.py
2017-02-07 01:51:44 -08:00
Trevor
f0da576315 Fix Hue groups with same names (#5737)
* Revert "Fix hue lightgroups not syncing state (#5702)"

* Use light_id in unique_id for Hue groups

* Make sure HueLight unique_id is unique

* Update hue.py

* Update hue.py

* Update hue.py
2017-02-07 01:51:05 -08:00
andrey-git
f774538e66 Check config before restarting (#5609)
* Check config before restarting.

* Make check_config on restart async

* don't check if notification service exists

* Use .communicate()

* Reduce the number of notifications. Add tests.
2017-02-07 01:19:08 -08:00
Hermann Kraus
51810620fb Check for command topics when determinig the capabilities of an MQTT light. (#5770)
Previous code used the state topic which is obviously wrong:
- The state topic is already used to select optimistic mode.
- A light with only the state topic but no command topic would still announce the capability.
2017-02-07 01:13:00 -08:00
Pierre Ståhl
c7e282257a Initial support for Apple TV (#5698)
* Initial support for Apple TV

* hash_wip

* Add media_play support to Apple TV
2017-02-07 00:55:19 -08:00
Jesse Osiecki
9a2c84ee8a Added error checking to the MIMEImage encoding for smtp.py (#5753)
* Added error checking to the MIMEImage encoding for smtp.py

Added fallback to file attachment rather than inline image for images
without a known MIME

* PEP8 reqs to fix previous commit
2017-02-07 00:43:03 -08:00
Alessandro Mogavero
86da4f511d Improve Sky Hub error handling (#5762)
* Added error handling in function _get_skyhub_data

* Error line split for readability
2017-02-07 00:28:33 -08:00
Pascal Vizeli
48161697f8 Sonos fix favorite, coordinator, cleanup update (#5778)
* Sonos fix favorite, coordinator, cleanup update

* Bugfix snapshot restore
2017-02-07 00:27:55 -08:00
Stéphane Bidoul (ACSONE)
7927a6b588 add min and max jinja filters (#5765) 2017-02-07 00:25:47 -08:00
ray0711
1546ec7778 Fix brightness slider for mqtt template lights (#5780)
Add the missing supported_features declaration.
2017-02-07 09:52:07 +02:00
Marcelo Moreira de Mello
305d2612cf Fix attribute error for media_player/roku if roku device is unreachable and shows a persistent notification (#5785) (#5786) 2017-02-06 22:00:20 -08:00
Andrzej
34c7bac9b4 Update package.py (#5783) 2017-02-06 22:16:17 +01:00
Pascal Vizeli
2b124a008c Migrate lock component to async (#5748) 2017-02-06 21:25:34 +01:00
Alberto Arias Maestro
32dc276c53 Add support for position on wink cover (#5751)
Add support for position property for wink covers and fix state when stopped in the middle.
2017-02-06 15:43:36 +02:00
Adrián López
4cc711357a Allow to use data for enhanced messages (#5763)
Add notification data field to the message send to Facebook.
Allows to construct richer messages like cards, quick replies, attach
images, videos, etc
2017-02-06 14:01:41 +01:00
andrey-git
26a3ecc9d0 Merge pull request #5718 from benvm/modbus_write_register_array
Modbus write_register doesn't accept list
2017-02-06 13:55:48 +02:00
Johan Bloemberg
264bdc9d56 Make brightness display work for rgb devices. (#5675)
* Make brightness display work for rgb devices.

Non rgbw devices return 255 for getWarmWhite255. This is part 2 to make the brightness slider work for these devices.

https://github.com/Danielhiversen/flux_led/pull/25

* Query brightness property which return WW level or RGB brightness equivalent.

https://github.com/Danielhiversen/flux_led/pull/25
2017-02-06 11:32:51 +01:00
Daniel Høyer Iversen
150c8ac11c flux led o.13 (#5774) 2017-02-06 11:32:37 +01:00
Yannic-HAW
4a341ff55f - added send packet service to send ir packets directly from scripts (no need to define a switch for each command) (#5768)
- changed ip suffix of SERVICE_LEARN to use _ instead of . as ip seperator to avoid errors if used in yaml
2017-02-06 07:53:58 +01:00
Martin Hjelmare
0d89f2bc69 Update mysensors sensors (#5764)
* Add V_DIRECTION type for S_WIND.
* Add more predefined units of measurement.
2017-02-05 21:07:30 +01:00
Pascal Vizeli
2a139d6bc7 Add unittest for multible entities in one line (#5759) 2017-02-05 14:24:38 +01:00
Fabian Affolter
91bebca0b6 Upgrade pysnmp to 4.3.3 (#5757) 2017-02-05 11:22:32 +01:00
Philipp Schmitt
573fc651dc Store the key file in the config dir (#5732)
* webostv: Store the key file in the config dir

* Update the pylgtv source to use the repo by @TheRealLink

* Add missing config parameter
2017-02-05 10:39:04 +01:00
Fabian Affolter
d88c903537 Upgrade pyasn1 to 0.2.1 (#5755) 2017-02-05 10:16:57 +01:00
Martin Hjelmare
82c99f81fc Fix validation of serial port on windows (#5749)
* Fix validation of serial port on windows

* Use pyserial to check serial ports.
* Check that persistence file ends with either `.json` or `.pickle`.

* Change fix to not rely on pyserial

* Use generator expr instead of list comprehension
2017-02-04 21:54:20 -08:00
Fabian Affolter
02dfd9660e Upgrade zeroconf to 0.18.0 (#5746) 2017-02-04 20:52:11 +01:00
Fabian Affolter
6164b61e14 Upgrade psutil to 5.1.2 (#5745) 2017-02-04 20:51:49 +01:00
Fabian Affolter
4cb20ce6d9 Upgrade astral to 1.3.4 (#5744) 2017-02-04 20:51:22 +01:00
Joeboyc2
0ea81c1269 Change CONF_DEFAULT_COLOR CV type (#5700)
* Change CONF_DEFAULT_COLOR CV type

Changed  vol.Optional(CONF_DEFAULT_COLOR, default=DEFAULT_COLOR) from cv.string to cv.ensure_list

This allows the optional parameter default_color to be picked up correctly and to function

the option needs to be specifed as follows:

    default_color: [0,255,0]

Solution provided by @scossa2020 in issue #5338
https://github.com/home-assistant/home-assistant/issues/5338

* Update hyperion.py
2017-02-03 23:36:25 -08:00
Fabian Affolter
b29c167dde Upgrade psutil to 5.1.1 (#5736) 2017-02-03 09:09:14 -08:00
William Scanlon
25a68f3ce9 Update python-wink version (#5734) 2017-02-03 16:37:00 +01:00
Fabian Affolter
c0dcef6c3e Add wind bearing (#5730) 2017-02-03 09:44:07 +01:00
Fabian Affolter
6786f83c26 Upgrade pyowm to 2.6.1 (#5729) 2017-02-03 09:43:29 +01:00
Philipp Schmitt
7506569db9 Support for the Orange Livebox Play TV appliance (#5533)
* Support for the Orage Livebox Play TV appliance

* Add liveboxplaytv to coveragerc

* Minor refactoring

* Update requirements

* Adjust comments

* Fix alignment

* Fix some coding-style issues highlighted by Travis CI

* The livebox play TV does not support playing media

* Lint: shorten line

* Remove unused callback function

* Remove redundant backslash

* Implement changes requested by balloob

* Don't error out if channel name or media url could not be retrieved

* Support current program (media title property)

* Remove unnecessary check

* Clean up: Remove another unnecessary check, _CONFIGURING variable and _playing attribute

* Update liveboxplaytv dependency to version 1.4.4

* Fix liveboxplaytv requirement

* Improve media state (support for playing and pause state)

* Update liveboxplaytv.py
2017-02-02 23:52:52 -08:00
Fabian Affolter
f9ede73a55 Add moon sensor (#5726)
* Add moon sensor

* Update moon.py
2017-02-02 23:43:03 -08:00
Joseph Piron
4aa7f030e8 Adds average load to systemmonitor (#5686)
* Adds average load to systemmonitor

* split the values in 3 sensors

* hound ok
2017-02-02 23:33:15 -08:00
Wolf-Bastian Pöttner
e831a2705e Add support for FRITZ!DECT wireless switches based on fritzhome (#5541) 2017-02-02 23:29:18 -08:00
Ryan Kraus
537355924f Alert Component (#5201) 2017-02-03 07:20:51 +02:00
miniconfig
dbc2f6b9cd Merge pull request #5658 from miniconfig/openevse
Added new sensor component to monitor OpenEVSE chargers.
2017-02-02 23:52:11 -05:00
Adam Mills
6a64e79d7b [recorder] Index events time_fired to improve logbook performance (#5633)
* Index events time_fired to improve logbook perf.

* Updated implementation to track schema versions

* Added tests for schema migration support logic

* Rename check_schema to migrate_schema
2017-02-02 22:04:14 -05:00
miniconfig
4b62a0d924 Updated openvse sensor component to use new structure of openevsewifi library. 2017-02-02 21:46:35 -05:00
Valentin Alexeev
dfb991ce19 Bump pwaqi to 1.4 to fix a typo in the underlying library. (#5716) 2017-02-02 12:59:52 -08:00
Pascal Vizeli
f63874eb8c Migrate cover to async. (#5717) 2017-02-02 12:39:13 -08:00
Colin O'Dell
bc65452efb QNAP Sensor (#5666)
* Implement the QNAP sensor

* Add sensors immediately

* Remove unnecessary check

* Use CONF_SSL instead of CONF_PROTOCOL
2017-02-02 12:29:04 -08:00
Pascal Vizeli
2fc3dfff67 Migrate fan component to async. (#5723)
* Migrate fan component to async.

* Fix lint
2017-02-02 12:07:00 -08:00
Daniel Høyer Iversen
574384f446 update miflora lib and allow specification of bluetooth adapter (#5720) 2017-02-02 17:08:10 +01:00
Daniel Høyer Iversen
ee551e2a9c up rfxtrx lib (#5721) 2017-02-02 16:18:16 +01:00
Duoxilian
219337a574 Cleanup climate and ecobee (#5616)
* Remove redundant input validation which is already accomplished through
defined schemata.

* Rely on defined state attributes for hold mode.

* Remove misleading comment. This comment seems to assume that sleep
mode is a hold; it is a schedule instead. The code snippets in the
comment could never work.

* Remove use of constants for hold mode. Will be made irrelevant
by a planned change by nordlead2005.
2017-02-02 15:28:32 +01:00
Philipp Schmitt
ff65c2a114 Support for Nuki.io smart locks (#5715)
* Support for Nuki.io smart locks

* Update requirements and add lock.nuki to .coveragerc

* lint: Re-organize imports

* Schedule a state update instead of calling directly update_ha_state

* Remove update requests altogether

* Make sure there is no IO inside properties

* Fix: nuki lock are all initialized as "lock.unnamed_device"

* Update pynuki to 1.2 to avoid an extra REST API call for each lock init
2017-02-02 15:15:27 +01:00
Ben Van Mechelen
0ea9d935af Modbus write_register accept list 2017-02-02 10:23:13 +01:00
William Scanlon
80a794e587 Wink AC and addidtional sensor support (#5670)
* Added door bell sensors

* Initial support for AC units.

* Added new device service

* Quirky Aros AC unit support

* Use super() everywhere and error checking for token request.

* Ignore camera sensors during setup of alarms.

* Added manufacturer/device attributes to all wink devices.

* Fixed style errors

* Fixed remaining lint errors.
2017-02-01 22:43:12 -08:00
Trevor
b5f285a789 Fix OwnTracks state names (#5454)
* Fix OwnTracks state names (#5453)

* Update owntracks.py

* Update tests
2017-02-01 22:06:02 -08:00
Paulus Schoutsen
6e94f0d7cd Upgrade zeroconf dep" (#5706) 2017-02-01 21:48:30 -08:00
Pascal Vizeli
eefb603f17 Cleanup media_player universal platform / blocking platform. (#5671)
* Cleanup media_player universal platform / blocking platform.

* fix comments

* fix unittest

* fix lint

* fix unittest lint

* fix coro

* fix test

* fix tests part 2
2017-02-01 21:45:19 -08:00
Pascal Vizeli
10a104271e Cleanup climate platform for async update_ha_state / migrate generic thermostat (#5679)
* Cleanup climate from blocking stuff / migrate generic

* Migrate generic thermostat

* fix tests

* fix lint
2017-02-01 21:44:05 -08:00
Paulus Schoutsen
686c8466a0 Merge pull request #5707 from home-assistant/release-0-37-1
0.37.1
2017-02-01 21:34:52 -08:00
Paulus Schoutsen
181943e139 Version bump to 0.37.1 2017-02-01 21:19:45 -08:00
Trevor
e05c1bc160 Fix hue lightgroups not syncing state (#5702) 2017-02-01 21:19:28 -08:00
Johan Bloemberg
96745abf5d Prevent infinite loop in crossconfigured mqtt event streams (#5624)
* Prevent events about MQTT messages received to cause infinite loop when two HA instances are crossconfigured for mqtt_eventstream.

* Fix linting

* Publish all MQTT received events except incoming from eventstream. Also make it configurable.
2017-02-01 21:19:28 -08:00
John Arild Berentsen
17c4f4d391 [lock.zwave] Bugfix Zwave lock (#5619)
* Bugfix state

* remove debug
2017-02-01 21:19:28 -08:00
Pascal Vizeli
9ed8ee1261 Bugfix sonso source input (#5699) 2017-02-01 21:19:28 -08:00
Pascal Vizeli
df7ca22656 Fix bug for UNREACH devices / Variable handling and update. (#5689)
* Fix bug for UNREACH devices / Variable handling and update.

* fix track_time

* update after data after creation

* add message output

* change unreach

* change unreach code

* Revert "change unreach code"

This reverts commit f58430de3c.

* update pyhomematic
2017-02-01 21:19:28 -08:00
Hugo Dupras
9716cd3f48 Hotfix for netatmo cameras (#5644)
* Fix for missing netatmo tags in 0.37

Also fix issue with SSL certificate for vpn_url

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Netatmo welcome: vpn_url can be empty

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* add config floag to disable SSL verification for vpn_url

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Import CONF_VERIFY_SSL from const
2017-02-01 21:19:28 -08:00
Pascal Vizeli
6ee7878236 Bugfix async blocking loop with xml parser. (#5694) 2017-02-01 21:19:28 -08:00
Pascal Vizeli
bc7fd5611e Bugfix sonos group coordinator (#5691)
* Bugfix sonos group coordinator

* Fix tests
2017-02-01 21:19:28 -08:00
Erik Eriksson
27a91b357e Upgraded tellduslive (#5664) 2017-02-01 21:19:28 -08:00
Marcelo Moreira de Mello
26d0fd772b Fixes issue #5627 by bumping external Amcrest module to version 1.1.4 (#5662)
Add an optional extended description…
2017-02-01 21:19:27 -08:00
Daniel Høyer Iversen
a168bf64b6 bug fix in hue (#5623) 2017-02-01 21:19:27 -08:00
David-Leon Pohl
db09ef0a6f Fixes: Pilight Switch rejects alphanumeric IDs #5119 (#5601) 2017-02-01 21:19:27 -08:00
Trevor
647a93801c Fix hue lightgroups not syncing state (#5702) 2017-02-01 21:06:11 -08:00
Johann Kellerman
2fcaf8bda6 [sensor/sma] handle units correctly (#5657) 2017-02-01 21:01:06 -08:00
Johan Bloemberg
ae1f59970d Prevent infinite loop in crossconfigured mqtt event streams (#5624)
* Prevent events about MQTT messages received to cause infinite loop when two HA instances are crossconfigured for mqtt_eventstream.

* Fix linting

* Publish all MQTT received events except incoming from eventstream. Also make it configurable.
2017-02-01 21:00:05 -08:00
John Arild Berentsen
68d6bcd3ed [lock.zwave] Bugfix Zwave lock (#5619)
* Bugfix state

* remove debug
2017-02-01 20:57:57 -08:00
Pascal Vizeli
b2180fba63 Bugfix sonso source input (#5699) 2017-02-02 00:02:24 +01:00
Pascal Vizeli
b5b1d72ab6 Fix bug for UNREACH devices / Variable handling and update. (#5689)
* Fix bug for UNREACH devices / Variable handling and update.

* fix track_time

* update after data after creation

* add message output

* change unreach

* change unreach code

* Revert "change unreach code"

This reverts commit f58430de3c.

* update pyhomematic
2017-02-01 23:55:16 +01:00
Daniel Høyer Iversen
2a5ccff82e up rfxtrx lib (#5687) 2017-02-01 19:47:18 +01:00
Paulus Schoutsen
bdc62730bd Update frontend (#5696) 2017-02-01 09:03:35 -08:00
Hugo Dupras
676519d0cb Hotfix for netatmo cameras (#5644)
* Fix for missing netatmo tags in 0.37

Also fix issue with SSL certificate for vpn_url

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Netatmo welcome: vpn_url can be empty

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* add config floag to disable SSL verification for vpn_url

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Import CONF_VERIFY_SSL from const
2017-02-01 17:33:32 +01:00
Pascal Vizeli
a72d32b9af Bugfix async blocking loop with xml parser. (#5694) 2017-02-01 17:20:52 +01:00
Pascal Vizeli
32f8622bba Bugfix sonos group coordinator (#5691)
* Bugfix sonos group coordinator

* Fix tests
2017-02-01 16:53:02 +01:00
Johan Bloemberg
cabc4dff03 Use same pattern for device defaults in both platforms. (#5682) 2017-02-01 10:42:50 +01:00
Paulus Schoutsen
987f59e8d8 Update frontend (#5685) 2017-02-01 00:47:02 -08:00
Johan Bloemberg
7be3414785 Only set LANG=C.UTF8 during install. (#5648) 2017-01-31 19:30:53 -08:00
Paulus Schoutsen
8334bc908c Update frontend (#5683) 2017-01-31 19:29:07 -08:00
Michaël Arnauts
8247acb7b9 Move docker installation scripts to virtualization/Docker path. Splits out openalpr to seperate script. (#5676) 2017-01-31 19:02:06 -08:00
Erik Eriksson
89ec752064 Upgraded tellduslive (#5664) 2017-01-31 11:08:11 -08:00
Mathew Peterson
d65f07860c Refactors script/setup_docker_prereqs (#5506)
* Refactors script/setup_docker_prereqs

Refactors script/setup_docker_prereqs to allow toggling of packages
to being installed

* Adds support for openalpr to Docker

* Updates Dockerfile

Comments ENV directives in order to preserve cache.

* Fixes incorrect position of echo

* Fixes telldus installer by updating apt before pkg install
2017-01-31 18:11:51 +01:00
Johan Bloemberg
bbda2a72f4 Rflink 433Mhz gateway platform and components (#4547)
* Initial sketches of rflink component.

* Add requirement.

* Properly load configuration.

* Bump rflink for graceful parse errors and protocol callback.

* Cleanup, documentation and linting.

* More documentation, first sensor implementation (temp & hum).

* Add brightness/dim support for newkaku protocol.

* Use separate class for dimmables.

* Make sure non-dimmable newkaku devices are turned on.

* Move some code around, add switches. Support loading from config.

* Fix bug in ignoring devices.

* Fix initial state assumption.

* Improve reliability on invalid conditions.

* Allow configuration of group for new devices.

* Sensor icons.

* Fix parsing negative numbers.

* Correct icon.

* Allow sending commands serial.

* Pluralize.

* Allow adding sensors from config.

* Fix ignoring devices and bugs in previous commit.

* Share know devices so devices from configuration don't get added as lights.

* Lookup unit from value_key.

* Remove debug.

* Start implementing event protocol in place of packet protocol.

- Added first test suite for sensors.
- This currently breaks light and switch.

* Refactor switch component to fit new rflink changes. Add test suite.

* Fix style.

* Refactor and test lights. Bring coverage to 100%.

* Use non-broken and production tested rflink module.

* Update requirements.

* Bump for logging.

* Improve readability.

* Do not use global variable but keep known device state in intended place.

* Improve docs.

* Make icon support generic.

* Disable overriding icons in config, as it belongs in customization. Only keep custom icon for entities that are able to detect a icon based on the thing they represent (sensors in this case).

* Implement configuration schema, overall refactor of magic values.

* Fix bug in config/test wait_for_ack.

* Small refactors.

* Move command logic into separate class.

* Convert command sending logic to class based pattern instead of using the event bus.

* Start not using bus for rflink event propagation to platforms.

* Do not use event bus for all entity types.

* Fire an event on the bus for every switch incoming rflink command.

* Resolve lint errors, remove some old code.

* Known devices no longer need to be registered separately.

* Log bus events.

* Event callback is a..... callback.

* Use full entity id for events.

* Move event sending to entity.

* Log incoming events.

* Make firing events optional inline with rfxtrx.

* Add foundation for signal repetition.

* Add signal repetition config and tests.

* Make plain switchable type explicitly configurable.

* Enable default entity settings for automatically added entities as well.

* Prevent default configuration leaking accross entities.

* Make sure device defaults don't get overwritten by defaults further down.

* Don't let fast state switching and repetitions turn your house into a disco.

* Make repetitions more responsive.

* Disable on/off fallback on dimmables as it currently doesn't play nice with repetitions.

* Use rflink that allows send_command_ack to be safely cancelled.

* Reduce duplication and make repeat work for non-ack.

* Implement reconnection logic.

* Improve reconnection logic.

* Also cancel repetitions when entity state is changed due to external command.

* Update requirements.

* Fix linting.

* Fix spelling.

* Don't lie.

* Fix lint.

* Support for automatically creating protocol translation (fixes spaces in device names).

* Returned support for dimmable and on/off entity.

* Duplicate code to fix linting issues with inheritance.

* Allow overriding unit of measurement from config.
2017-01-31 08:11:52 -08:00
Johan Bloemberg
9925b2a8e0 Load mqtt_eventstream before any other component. (#5629)
This ensures that initial state changes for components are broadcasted on the MQTT bus.
2017-01-31 08:02:28 -08:00
Michaël Arnauts
b080ae154c Add devices detected by ping as SOURCE_TYPE_ROUTER instead of GPS (#5625) 2017-01-31 08:01:14 -08:00
Adrián López
14f8bc26d1 Voice command API.AI. First import (#5462)
* Voice command API.AI. First import

* Fixes suggested by hound

* Fixing comments

* Fix pylint and pydocstyle errors

* Change how speech is defined

Also clean some unused constants, remove card type (not used), define
a message when action is not defined and improve the message when
action is unknown.

* Change how speech is defined

Clean some constants.
Improve error messages.
Delete card type, not used.

* Tests for new Api.ai component

* Use async_add_job to python compatibility. New test to measure response time

* Add async_action option to choose between waiting or not for the action to execute

* Travis-ci needs more time

* Removed timeout tests

* Removed timeout tests

* Added apiai to .coveragerc as specified by PR doc
2017-01-31 07:54:54 -08:00
Pascal Vizeli
88d9d787a6 Add unittests for FFmpeg and spliting binary sensor (#5659)
* Spliting ffmpeg binary sensor and move service to component.

* unittests for component

* add unittest for binary_sensor

* exclude camera for tests
2017-01-31 07:48:03 -08:00
Paulus Schoutsen
9ae574c7d9 Update frontend (#5669) 2017-01-31 07:39:01 -08:00
Daniel Høyer Iversen
29816f3041 Vlc (#5665)
* vlc default name
2017-01-31 13:51:02 +01:00
Teemu R
5f0138f8e4 new yeelight backend lib, new features (#5296)
* initial yeelight based on python-yeelight

* adapt yeelight's discovery code & suppress exceptions on set_default

* Support flash & code cleanups

Adds simple pulse for flashing, needs to be refined.
This commit also includes changing transition from seconds to milliseconds,
and cleans up the code quite a bit.

* cleanup code, adjust default transition to 350

* bump required version to 0.0.13

* Cleaning up and marking todos, ready to be reviewed

* Renamed back to yeelight.
* Removed effect support for now until we have some sane effects available.
* Add "breath" notification for flash, currently hidden behind a False check due to unknown issue not accepting it.
* TODO/open points are marked as such.

* Fix a typo in rgb calculation

* yeelight_<bulbtype>_<mac> for autodetected bulbs

hostname from mdns seems to vary

* Lint fixes, add music mode, fix flash

* Flash transforms now to red and back
* Fix lint warnings
* Add initial music mode.

* remove unused mode logging, move set_mode to turn_on

* Add save_on_change configuration variable

* yeelight: check if music mode is on before enabling it.

* Fix linting, bump required python-yeelight version

* More linting fixes, use import when needed instead of saving the module handle

* Use OR instead of + for features assignment

* Fix color temperature support, convert non-rgb values to rgb values in rgb()

* Fix typo on duration, thanks @qzapwy for noticing

* yeelight: fix issues from review, behave when not available

* Implement available()
* Fix transition to take seconds instead of milliseconds
* Fix default configuration for detected bulbs
* Cache values fetched in update()
* Add return values for methods

* yeelight: kwarg-given transition overrides config, slight cleanups

* change settings back to optional, request update when calling add_devices

* As future version of python-yeelight will wrap exceptions, we can handle broken connections more nicely.

* bump yeelight library version

* Remove unused import

* set the default only when settings are changed and not, e.g., when turned on by automation

* update comment & fix linting
2017-01-31 10:01:11 +01:00
Teemu R
2c31e3ea8c Cleanup modes & available, bump version requirement (#5606)
* Cleanup modes & available, bump version requirement

* check for Noneness on available
2017-01-31 09:23:05 +01:00
Marcelo Moreira de Mello
976cd545fe Fixes issue #5627 by bumping external Amcrest module to version 1.1.4 (#5662)
Add an optional extended description…
2017-01-31 09:19:49 +01:00
Adam Mills
7f3ee8a83c [docker] cec install path fixed upstream (#5651) 2017-01-30 21:27:20 -05:00
miniconfig
495b0667e9 Added new sensor component to monitor OpenEVSE chargers equipped with the WiFi Kit. 2017-01-30 15:29:56 -05:00
Daniel Høyer Iversen
3b32afda01 bug fix in hue (#5623) 2017-01-30 09:20:35 -08:00
Daniel Høyer Iversen
3a1607500e Add set_options_service for input select (#5630) 2017-01-30 09:17:57 -08:00
Matt N
1bf3eba603 yarn setup_js_dev was deleted (#5639) 2017-01-30 09:15:26 -08:00
Fabian Affolter
ab019b9747 Upgrade slacker to 0.9.40 (#5650) 2017-01-30 09:14:00 -08:00
Paulus Schoutsen
55992468b0 Update frontend (#5652)
* Return empty result when history date is in future

* Update frontend
2017-01-30 09:12:07 -08:00
Fabian Affolter
87764a51ba Use device_state_attributes() for platforms (#5649)
* Use device_state_attributes() for platforms

* Update test

* Fix lint issue
2017-01-30 16:16:49 +01:00
Pascal Vizeli
ca558f6485 Add unittest for cleanup not validate ssl stuff. (#5643) 2017-01-30 13:09:36 +01:00
Paulus Schoutsen
f1d1f7d032 Update frontend (#5642) 2017-01-30 00:57:13 -08:00
Paulus Schoutsen
c8ff1094f8 Update frontend (#5640) 2017-01-30 00:20:51 -08:00
Paulus Schoutsen
318f3c9625 Update frontend (#5638)
* Ensure fonts get loaded quicker

* Update frontend
2017-01-29 23:26:35 -08:00
Paulus Schoutsen
da4f402ebe Update frontend (#5637) 2017-01-29 18:43:39 -08:00
Paulus Schoutsen
b5047bbaad Prioritize core.js (#5636)
* Prioritize loading app core script

* change app skeleton to be small
2017-01-29 18:36:48 -08:00
Pascal Vizeli
847a5a064d Aiohttp client unittest (#5635)
* add test for cleanup

* add test for mjpeg stream
2017-01-29 16:15:40 -08:00
Pascal Vizeli
261ffbbfea Move part of image_processing tests (#5634)
* Move part of image_processing tests

* fix lint
2017-01-29 14:40:37 -08:00
Martin Hjelmare
24f828d7eb Fix mysensors RGB and W light turn on (#5608)
* RGBW light needs a white value defined.
* Log error if V_RGB is not 6 characters and V_RGBW not 8 characters.
2017-01-29 12:59:13 +01:00
Kyle Hendricks
fddab7f2b4 Fixes the AsusWRT ip neigh regex to handle the possible IPv6 "router" flag (#5605)
See the last line here: http://linux-ip.net/gl/ip-cref/ip-cref-node62.html
2017-01-29 09:37:59 +01:00
David-Leon Pohl
a9325ea663 Fixes: Pilight Switch rejects alphanumeric IDs #5119 (#5601) 2017-01-28 17:49:16 -08:00
Fabian Affolter
5ae5d9b576 Upgrade rpi-rf to 0.9.6 (#5611) 2017-01-28 17:05:24 -08:00
Paulus Schoutsen
e32f933cb6 Version bump to 0.38.0.dev0 2017-01-28 15:13:59 -08:00
Paulus Schoutsen
46aa2e7ce1 Merge pull request #5530 from home-assistant/dev
0.37
2017-01-28 15:13:43 -08:00
Paulus Schoutsen
8bcb26b90c Version bump to 0.37 2017-01-28 15:13:17 -08:00
Johann Kellerman
b48a7e4007 Bugfix customize (#5613) 2017-01-28 23:52:30 +01:00
Robbie Trencheny
0f939d6906 Merge pull request #5580 from fakezeta/Added_notify.twilio_call
Added notify.twilio_call component for Voice calling with Twilio
2017-01-28 13:57:18 -08:00
Robbie Trencheny
b7bb31cb95 Allow both http and https URLs 2017-01-28 12:46:34 -08:00
Robbie Trencheny
379ae11405 Minor style fixes 2017-01-28 12:45:52 -08:00
Robbie Trencheny
8f418831a1 Update Twilio SDK version 2017-01-28 12:45:32 -08:00
andrey-git
1fb372ffdb Apply new customize format to Zwave (#5603) 2017-01-28 22:29:51 +02:00
Robbie Trencheny
405b2fdfa0 Correct project year, update license 2017-01-28 12:29:09 -08:00
Robbie Trencheny
1aa1074054 Update LICENSE.md and CLA.md to reflect the new Apache 2.0 license 2017-01-28 12:12:34 -08:00
Paulus Schoutsen
b0d07a414b Token tweaks (#5599)
* Base status code on auth when entity not found

* Also allow previous camera token

* Fix tests

* Address comments
2017-01-28 11:51:35 -08:00
Fabian Affolter
e1412a223c Update docstring (quotes, links, content) (#5602) 2017-01-28 16:02:19 +01:00
Teemu R
72bc8fc5bf eq3btsmart: add reporting for availability (#5594)
* eq3btsmart: add reporting for availability

* Update eq3btsmart.py
2017-01-27 23:52:46 -08:00
jeremydk
b7aba525ca Emulated Hue "host-ip" fails to bind when running in docker without --net=host (#5550)
* UPNP changes to allow a separate advertised IP and Port.

* Fixing lint.

* UPNP changes to allow a separate advertised IP and Port.

* Fixing lint.

* Update __init__.py

* Moved logic for advertised ip and port into config class.

* Commenting change for clarity.

* Spacing changes for PEP8

* Spacing Changes for PEP8

* Style Changes
2017-01-27 23:42:37 -08:00
Craig J. Ward
6ede1c08ca Insteon config (#5595)
* only check for devices when not defined in config

* lint

* performance fix
2017-01-27 23:31:36 -08:00
Paulus Schoutsen
b4c3de3215 Fix switch.tplink doing I/O in event bus (#5589)
* Fix switch.tplink doing I/O in event bus

* Update tplink.py
2017-01-27 22:45:57 -08:00
fakezeta
564aad0ab8 Removed Regexp and added error logging 2017-01-28 01:35:54 +01:00
fakezeta
549c3b2c84 Minor changes to pass lint check 2017-01-28 01:06:01 +01:00
fakezeta
db85e2bc2a Checking message if valid url or a string for TTS 2017-01-28 00:51:30 +01:00
Duoxilian
b732174def Hold mode (#5586)
* Initial commit of hold_mode feature.

* Added deprecation warning for climate.away_mode

* Add tests to demo environment.
2017-01-27 08:57:18 -08:00
Pascal Vizeli
1d4e967106 sonos set coordinator after join/unjoin (#5584)
* sonos set coordinator after join/unjoin

* fix unittest
2017-01-27 15:37:16 +01:00
Lukas
5a7a84fad1 Device Tracker for Linksys Access Points (#4933)
* Implementation for Linksys Access Points

* update .coveragerc

* update requirements

* add default timeout of 10sec

* address lint issues
2017-01-26 23:27:29 -08:00
Teemu R
86dfa7ad25 [switch.flux] Allow disabling setting the brightness (#5407)
* flux: allow disabling setting the brightness

* Use separate boolean for disabling brightness adjustment

* Update flux.py
2017-01-26 23:08:08 -08:00
Paulus Schoutsen
4aab72fe7c Update MDI 2017-01-26 22:59:32 -08:00
Paulus Schoutsen
5948b5e33a Update frontend 2017-01-26 22:59:13 -08:00
Pascal Vizeli
4831f57834 Bugfix sonos / refactor of sonos function for TTS (#5571)
* Bugfix sonos / refactor of sonos function for TTS

* fix unittest

* update service yaml

* restore group of a coordinator

* use group function to evaluate

* fix state flooting

* fix comments
2017-01-26 22:50:36 -08:00
Jan Losinski
41218e5a37 [switch.pilight] Implement echo config option (#5056)
* Implement echo config option for pilight

Pilight switches can get a receive code configured. If so they act on
received codes. In the current implementation "act on" means not only
to set the internal state, but also to send the code again. If the
receiver is directly parred with the switch, to act even if HA is not
running, this causes it to receive the signal twice because the HA
sends it again.

In my case this causes a dimmer to start dimming until I hit the switch
again.

This implements a "echo" argument for the receive codes that let the
user choose if a received signal should result in any sending or not.
If not, only the status of pilight will be updated.

The echo option defaults to True, as this was the default since now.
Simply set it to halse to disable this behaviour.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Add documentation to set_state in switch/pilight.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2017-01-26 22:40:14 -08:00
nordlead2005
837994196e Added forecast support to DarkSky (#5264)
* Added forecast support to DarkSky

	modified:   homeassistant/components/sensor/darksky.py
	modified:   tests/components/sensor/test_darksky.py

* Fix async_volume_up / async_volume_down (#5249)

async_volume_up / async_volume_down should be async versions of
volume_up / volume_down, not a async version of the default variants of
volume_up / volume_down.

The previous code always called into the mediaplayers set_volume_level,
and never into volume_up / volume_down.

Signed-off-by: Anton Lundin <glance@acc.umu.se>

* adding a default icon "blind" to a PowerView blinds scene. (#5210)

* adding a default icon "blind" to a PowerView blinds scene.

* Adding icon property to define blind icon. Removed it from the state attributes dict.

* fixing lint error

* Added forecast support to DarkSky

	modified:   homeassistant/components/sensor/darksky.py
	modified:   tests/components/sensor/test_darksky.py

* Use SHA hash to make token harder to guess (#5258)

* Use SHA hash to make token harder to guess

Use hashlib SHA256 to encode object id instead of using it directly.

* Cache access token

Instead of generating a token on the fly cache it in the constructor.

* Fix lint

* Bugfix async device_tracker see callback (#5259)

* Add support for NAD receivers (#5191)

* Add support for NAD receivers

* remove self.update() in various methods

* remove setting attributes in various methods

* Change import to hass style

* Updated Config Validation, extended daily forecast to all supported types

* Fix style errors from previous commit, fix test since adding daily for all supported types

* Removed temperature from daily as it isn't supported

* Added forecast support to DarkSky

	modified:   homeassistant/components/sensor/darksky.py
	modified:   tests/components/sensor/test_darksky.py

* Updated Config Validation, extended daily forecast to all supported types

* Fix style errors from previous commit, fix test since adding daily for all supported types

* Removed temperature from daily as it isn't supported

* Revert "Bugfix camera streams (#5306)"

This reverts commit 4b43537801.

Revert "Version bump for kodi dependency (#5307)"

This reverts commit 6abad6b76e.

Revert "Add HMWIOSwitch to sensor, binary (#5304)"

This reverts commit 2c3f55acc4.

Revert "Remove GTFS default name & string change"

This reverts commit 6000c59bb5.

Revert "Update pyhomematic 1.19 & small cleanups (#5299)"

This reverts commit a30711f1a0.

Revert "[sensor] Add Dublin bus RTPI sensor (#5257)"

This reverts commit 1219ca3c3b.

Revert "Bugfix group reload (#5292)"

This reverts commit baa8e53e66.

Revert "Support for TrackR device trackers (#5010)"

This reverts commit f7a1d63d52.

Revert "Bump pywemo version."

This reverts commit dc937cc8cf.

Revert "Upgrade to voluptuous to 0.9.3 (#5288)"

This reverts commit d12decc471.

Revert "Upgrade distro to 1.0.2 (#5291)"

This reverts commit 64800fd48c.

Revert "Don't build Adafruit_BBIO - doesn't work on all platforms. (#5281)"

This reverts commit 9a3c0c8cd3.

Revert "Convert flic to synchronous platform. (#5276)"

This reverts commit eb9b95c292.

Revert "Upgrade to aiohttp 1.2 (#4964)"

This reverts commit e68e29e03e.

Revert "Fix TCP sensor to correctly use value_template (#5211)"

This reverts commit 1cf9ae5a01.

Revert "Cleanup language support on TTS (#5255)"

This reverts commit 3f3a3bcc8a.

Revert "Add last triggered to script (#5261)"

This reverts commit 467cb18625.

Revert "Bump flux_led version and make use of PyPi package (#5267)"

This reverts commit 34a9fb01ac.

Revert "Add support for NAD receivers (#5191)"

This reverts commit 3b59e169f1.

Revert "Bugfix async device_tracker see callback (#5259)"

This reverts commit 71fddd26eb.

Revert "Use SHA hash to make token harder to guess (#5258)"

This reverts commit 922308bc1f.

* Revert "Revert "Bugfix camera streams (#5306)""

This reverts commit 2ee8c44021.

* Update darksky.py
2017-01-26 22:32:45 -08:00
Johann Kellerman
f2870c3103 [core.config] Support customize in packages (#5543)
* Support customize in packages

* GMT

* Update test_config.py
2017-01-26 22:26:49 -08:00
Adam Mills
923431110a [*.zwave] Refactor of zwave value_changed (#5512)
* Refactor of zwave value_changed

* Rename update_properties to update

* Revert "Rename update_properties to update"

This reverts commit 723578e7d4.
2017-01-26 22:21:33 -08:00
John Arild Berentsen
d0538fe3aa [lock.zwave] Add set, get and clear usercodes for zwave locks (#5489)
* Add set, get and clear usercodes for zwave locks

* Fix CRLF
2017-01-26 21:45:04 -08:00
Robbie Trencheny
295a232374 Allow a protocol in the http.base_url parameter (#5557) 2017-01-26 21:43:45 -08:00
Johann Kellerman
3f2fdb97a0 check_config: Add support for packages (#5574) 2017-01-26 21:42:14 -08:00
Daniel Høyer Iversen
a465a45588 Fix for assumed state in command_line (#5578)
* Bug fix for assumed state in command_line

* command line

* command line

* command line

* command line test
2017-01-26 21:41:30 -08:00
fakezeta
e4f0b0a57f Updated requirements_all.txt 2017-01-27 01:12:28 +01:00
fakezeta
86b2db54be Merge remote-tracking branch 'home-assistant/dev' into Added_notify.twilio_call 2017-01-27 00:51:56 +01:00
fakezeta
3f13bdb1f7 Modified .coveragerc 2017-01-27 00:48:58 +01:00
fakezeta
61ad11fcd7 Added notify.twilio_call component for Voice calling with Twilio 2017-01-27 00:32:01 +01:00
John Mihalic
fb49c588e5 Handle Squeezebox issues (#5566)
* Handle Squeezebox issues

* Fix double logging
2017-01-26 18:30:42 -05:00
Pascal Vizeli
36e47473c5 pump ffmpeg version 1.2 to fix close bug (#5579) 2017-01-27 00:22:31 +01:00
Robbie Trencheny
7136fd0f0a Add an Amazon Polly TTS platform (#5169)
* Remove SPEED_MED from fan

* Add an Amazon Polly TTS platform

* Update boto library version for notify.aws_* platforms to match the tts.amazon_polly req

* Improve log line and add docstring to function

* Simplify config logic

* Remove duplicate logic

* Don't know how this got in here...

* initial options work

* Remove stale config option and only allow supported languages

* Make requested changes

* Polly is only supported in some regions

* Allow filename to contain underscores (for amazon_polly platform name), remove unnecessary default_lang, other small things

* Add options dict to service description
2017-01-26 23:22:47 +01:00
Pascal Vizeli
636e7aa31e [microsoft_face] Small error fixes (#5577)
* [microsoft_face] Small error fixes

* add array to exclusion
2017-01-26 23:18:15 +01:00
Pascal Vizeli
eadf67bd9a Update ha-ffmpeg version 1.1 (#5573) 2017-01-26 18:12:30 +01:00
Hugo Dupras
4fe54e1cbb Update Netatmo library to fix missing local_url (#5570) 2017-01-26 15:08:21 +01:00
John Mihalic
111b482be4 Update neurio library req. & fix keyerror (#5565) 2017-01-26 13:07:50 +01:00
Robbie Trencheny
2a897574a8 Merge pull request #5564 from home-assistant/readme-grammar-fix
Fix grammar in README
2017-01-25 17:39:32 -08:00
Robbie Trencheny
068369c008 Fix grammar in README 2017-01-25 15:35:52 -08:00
Robbie Trencheny
f25b34cafe Merge pull request #5558 from andrey-git/sonos
Fix empty image when Sonos is doing nothing.
2017-01-25 13:41:41 -08:00
Daniel Perna
7e7f9f6670 Merge pull request #5555 from jannau/max_shutter_contact
homematic: add MAX shutter contact class
2017-01-25 22:32:35 +01:00
Andrey
58e063a1b6 Fix empty image when Sonos is doing nothing. 2017-01-25 23:15:30 +02:00
Robbie Trencheny
7f5d6eb841 Add a is_coordinator attribute to Sonos (#5556) 2017-01-25 13:03:36 -08:00
Janne Grunau
0048267f3e homematic: add MAX shutter contact class 2017-01-25 21:15:04 +01:00
Gianluca Barbaro
9cad9c19f8 Update keyboard_remote.py (#5535)
* Update keyboard_remote.py

I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound)
I changed the way the component refers to the keyboard: not by "descriptor", but by name.
The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. 
For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration.
Now it searches every time for the right input file.
The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure.
I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices.

I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names.

* Update keyboard_remote.py

That should do the trick.
You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated.
But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update.

* Update keyboard_remote.py

* Update keyboard_remote.py

* Update keyboard_remote.py

* Unwrap logger error
2017-01-25 13:58:34 -05:00
Pascal Vizeli
c3a55e7d82 Fix upc lint error (#5554) 2017-01-25 18:46:37 +01:00
Paulus Schoutsen
42d33ae26d Disable typing travis build 2017-01-25 09:32:39 -08:00
Johann Kellerman
d500ddac9a [script] Fix dodgy bash syntax for bootstrap (#5552) 2017-01-25 09:21:09 -08:00
Pascal Vizeli
393c7f2cf1 [device.upc_connect] Discount on STOP. (#5553)
* [device.upc_connect] Discount on STOP.

* close session it self

* Update upc_connect.py
2017-01-25 09:20:31 -08:00
thecynic
6015274ee2 Add initial support for Lutron RadioRA 2 using pylutron (#5337)
Signed-off-by: Dima Zavin <thecynic@gmail.com>
2017-01-25 09:11:37 +01:00
Oleksii Serdiuk
ca1dc202f9 ASUSWRT: Add IPv6 support when parsing neighbors (#5536)
* ASUSWRT: Add IPv6 support when parsing neighbors

The regex for IPv6 should cover most cases, but it doesn't validate
whether IP is correct. It also might fail for some edge cases.

Also, ignore 'duid xx:xx:xx:xx:xx:xx:xx:xx:xx:xx' line in leases.

Closes #2814 - ASUSWRT doesn't support ipv6

* Update asuswrt.py
2017-01-25 00:02:39 -08:00
Alessandro Mogavero
c7ff7af39d Sky hub (#5509)
* Added new platform sky_hub

* added env to virtual environment gitingore

* Removed unuseful imports

* BT home hub 5 renamed to sky hub in the comments

* Added sky_hub to .coveragerc

* Added example configuration in sky_hub docstring

* sky_hub made compliant with test style standards

* homehub functions renamed to skyhub

* Update .gitignore

* Update .coveragerc
2017-01-25 00:02:08 -08:00
Paulus Schoutsen
264310074f Update Wink requirement 2017-01-24 22:13:22 -08:00
Stu Gott
191d7b0a50 Add support for Wemo CoffeeMaker devices (#5505)
* Add support for Wemo CoffeeMaker devices

* Add CoffeeMaker to WEMO_MODEL_DISPATCH
2017-01-24 22:10:10 -08:00
Nick Touran
43e46154c6 Added new Washington State DOT sensor. (#5496)
* Added new Washington State DOT sensor.

* Minor changes from review for WSDOT.

* Update wsdot.py
2017-01-24 22:08:19 -08:00
Martin Hjelmare
a09a772f43 Add more validation for mysensors (#5493)
* Move isdevice validator under helpers.config_validation.
* Check that all persistence files are set and are unique if any is set
  by user. This is necessary to avoid file name clashes.
* Check that a set persistence file has an existing and writable
  directory.
* Check that a device is either a valid device file, "mqtt", or a valid
  domain name or ip address.
2017-01-24 22:04:44 -08:00
Pascal Vizeli
b57f5728c5 [image_processing/microsoft_face_identify] face recognition for automation (#5472)
* [image_processing/microsoft_face_verify] face recognition for automation

* Add platform for microsoft face identify

* add unittest for demo

* Add unittest for platform
2017-01-24 21:50:10 -08:00
Robbie Trencheny
c355def154 Merge pull request #5348 from freol35241/dev
Remove throttle decorator from miflora platform
2017-01-24 21:39:42 -08:00
happyleavesaoc
3d081c2564 bump dep version; add name conf (#5451) 2017-01-24 21:36:18 -08:00
snagytx
59cad0f6ef add a small sleep before reading the rpi-gpio sensor (#5446)
* add a small sleep before reading the sensor

The read of the sensor might be incorrect if it's read too soon after the setup_input call. It might be isolated to my case where I rely on the the PI internal PULL, but once I added this sleep I never get false initial read. If you think this change is appropriate please merge it.

* Update rpi_gpio.py

* Update rpi_gpio.py

* Update rpi_gpio.py
2017-01-24 21:35:12 -08:00
Jan Harkes
6adc5c318e Add missing dependency in emulated_hue component. (#5394)
* Add missing dependency in emulated_hue component.

On first startup after upgrade to 0.36, the emulated_hue componented failed to
start because the http component had installed the modules it depends on, in
this particular case 'aiohttp_cors' was missing.

* Include dependencies for the emulated_hue web server

Emulated_hue uses it's own 'web-server' component to handle hue related
discovery and config, so we need to make sure the required http modules are
made available before we are initialized.

We don't have to depend on the home-assistant http/api component because we do
not need to have the frontend to be initialized to handle emulated_hue, so we
can just import in the same set of requirements as the http component.

* Fix linting error
2017-01-24 21:29:34 -08:00
Robbie Trencheny
88e0bb6733 Merge pull request #5492 from kellerza/gen_requirements
[script] gen_requirement: Raise an error if REQUIREMENT not pinned
2017-01-24 21:23:22 -08:00
William Scanlon
47bbfc309c Support for python-wink 1.0.0 (#5534) 2017-01-24 21:11:18 -08:00
Robbie Trencheny
794852f76f Merge pull request #5497 from armills/test-port-fixes
Fix network tests to use get_test_instance_port
2017-01-24 21:10:00 -08:00
Robbie Trencheny
75c52ff9c4 Merge pull request #5545 from fabaff/waqi-pm10
[sensor.waqi] Add missing particle value and refactor attributes
2017-01-24 21:07:46 -08:00
Paulus Schoutsen
d8e5e60a08 Merge remote-tracking branch 'origin/master' into dev 2017-01-24 20:59:34 -08:00
Robbie Trencheny
9bd5378fe4 [component/ios] Discover notify.ios when iOS component loads (#5548) 2017-01-25 06:57:48 +02:00
Paulus Schoutsen
2cf2dcd9ba Emulated_hue: default type to Google [Breaking change] (#5549) 2017-01-24 20:25:08 -08:00
Johann Kellerman
e53b2fe121 [script] Only bootstrap frontend if npm installed (#5507)
* [scripts] Only bootstrap frontend if npm installed

* Message if not frontend dev possible

* yarn
2017-01-24 16:20:18 -08:00
Fabian Affolter
4462431c78 Add missing particle value and refactor attributes 2017-01-25 00:52:19 +01:00
David McNett
eb1e8ebc18 New version of anthemav python library (#5544) 2017-01-25 00:20:59 +01:00
Pascal Vizeli
92858554e6 Bugfix endless aiohttp streamreader (#5540) 2017-01-24 20:43:36 +01:00
Pascal Vizeli
c972e90580 Bugfix mjpeg camera (#5539) 2017-01-24 20:25:51 +01:00
Fabian Affolter
b2ecaa189a [binary_sensor.arest] Fix name for sensor and shorten logger messages (#5460)
* Fix name for sensor and shorten logger messages

* Use variable as name if none is given
2017-01-24 19:54:14 +01:00
ecksun
f5062b06a9 media_player.kodi: Add SSL config option (#5531)
This readds support for https for kodi, resolves issue #5527
2017-01-24 18:20:18 +01:00
Fabian Affolter
cd260d89cb Add location to attributes and option to show position on the map (sensor.iss) (#5465)
[sensor.iss] Add location to attributes and option to show position on the map
2017-01-24 10:02:17 +01:00
Aaron Polley
38d9dc996b Piglow support (#5302)
* Add support for Piglow

* Updated coverage and requirements

* Add support for Piglow

* Updated coverage and requirements

* Fix linting errors

* Fix linting errors

* Remove trailing whitespace

* Shorter lines

* Remove trailing whitespace

* Update piglow.py

* Pinned piglow version

* Remove unused method

* Remove unused imports

* Fix lint errors

* Update requirements all

* Updated Piglow to allow the component name to be changed

* Fix imports

* Pass in name

* The piglow platform now fails if it cannot detect the piglow device

* Tidy subprocess import
2017-01-24 09:41:33 +01:00
Robbie Trencheny
be2c9ccee2 Merge pull request #5529 from robbiet480/update-hdmi-cec
Update pyCEC version
2017-01-23 23:34:02 -08:00
Robbie Trencheny
dd0110e06d Update pyCEC version 2017-01-23 23:33:16 -08:00
Fabian Affolter
64fc6a08d3 Fix typos (#5522) 2017-01-23 13:25:38 -08:00
Fabian Affolter
a83b61ad58 Allow direct messaging to user (#5521) 2017-01-23 13:24:45 -08:00
andrey-git
b7e477fbba Copy val in config.py before modifying (#5520)
* Copy val before modifying

* Bad line location
2017-01-23 13:24:13 -08:00
Fabian Affolter
318b0f4f36 Upgrade beautifulsoup4 to 4.5.3 (#5519) 2017-01-23 13:23:41 -08:00
Petr Vraník
900868708e check cec message length when asking physical address (#5516)
* cec client object

* cec command structure

* autodetect source

* volume support and native source select

* switch device

* media player device

* detecting of state

* friendly names

* hdmi cec properties

* presence detection

* simplified callbacks

* stable names

* renamed methods

* code cleanup

* name with vendor

* fixed standby call name

* fake standby/poweron

* domain switch

* domain switch

* async updating

* update separated

* cec -> hass event bridge

* fixed name generation

* code cleanup

* code cleanup

* icon constants

* code cleanup

* do not register unavailable devices

* discovery of deevices

* code cleanup

* cec device discovery

* moved method implementation into child

* service descriptions

* service descriptions

* service descriptions

* changed entity init sequence

* logging cleanup

* add remove as job

* closing cec, no service schemas

* correct iterate over dictionary

* Volume by commands

* threading

* logging minimized

* get load out of main thread

* naming cleanup

* get load out of main thread

* optimized discovery

* async where possible

* cleanup logging, constructors first

* pydoc

* formatting

* no async_update from out of loop
no hiding entities
removed redundant device_state_attributes
async updating presence

* no async

* working async cec

* cec in thirdparty lib

* cec initialized oudsice

* working without SIGSEGV

* rollbacked file changed by mistake

* sending of commands

* working with ha

* using hass loop and device driven updates

* version up

* version up

* Command types in pycec, cleanup for HA integration

* Removed media player, state moved to switch

* service descriptions

* requirements: pyCEC

* line width to 79

* doc

* doc

* overindentation solved

* HDMI to uppercase

* minimal dependency on cec

* removed unwanted line

* doc wording

* margin 79

* line continuation indent

* imperative doc

* lint: indentation

* fixed overindented

* fixed overindented

* fixed overindented

* fixed overindented

* order of imports

* PEP8

* keep signature of overriding

* removed redundant blank line

* fixed update call method (#4)

* Preparation for merge to upstream (#5)

* newer version of pyCEC
* updated services.yaml
* fixed lint scrpt to operate only on python files

* pycec version up

* update services

* no coverage report

* exclude non python files from lint

* lint only on python files

* Dev (#6)

* reordered
* sending nonserialized data through hass.data
* code formatting
* code formatting
* import order

* Dev (#7)

* newer version of pyCEC
* updated services.yaml
* fixed lint scrpt to operate only on python files

* pycec version up

* update services

* no coverage report

* exclude non python files from lint

* lint only on python files

* reordered

* sending nonserialized data through hass.data

* import order

* fixed object handling

* code formatting

* Backwards compatibility of hdmi_cec (#10)

* services:
power_on
standby
active_source

* new version of pyCEC (#12)

* newer version of pyCEC

* devices config (#13)

* getting device name from config

* shutdown fix (#14)


* correct call on shutdown

* remove misplaced annotations (#15)

* Preparation for merge to upstream (#5)

* newer version of pyCEC
* updated services.yaml
* reordered
* sending nonserialized data through hass.data
* services:
power_on
standby
active_source
* code formatting
* getting device name from config
* correct call on shutdown

* pyCEC version 0.3.6 (#18)

* newer version of pyCEC
* updated services.yaml
* sending nonserialized data through hass.data
* services:
** power_on
** standby
** active_source
* getting device name from config
* correct call on shutdown
* fork new thread on multicore machines
* support both config schemas: original and new (#16)
* volume press and release support (#17)

* support for media_player (#21)

* accept hexadecimal format of commands
* support for media player
* platform customization
* type constants

* Dev (#23)

* accept hexadecimal format of commands
* support for media player
* platform customization

* TCP CEC support (#24)

* accept hexadecimal format of commands
* support for media player
* platform customization
* preparing tcp support

* volume handling (#25)

* Incorporated CR remarks (#26)

* cleanup imports
* cleanup and enhance services description
* removed unwanted file

* implemented CR remarks (#27)

* pyCEC v0.4.6
* pined dependency version
* tighten service schemas

* requirements (#28)

* incorporate remarks from users (#32)

* home-assistant-31 make mute schema better (#31)

* pycec-30 pyCEC version up (#30)

* pycec-30 pyCEC version up (#30)

* home-assistant-30 OSD display name from configuration (#30) (#33)

* Home assistant 29 (#34)

* home-assistant-29 counting from 0 (#29)

* Home assistant 31 (#35)

* home-assistant-31 add support for mute-on and mute-off (#31)

* home-assistant-31 pyCEC version up (#31)

* Home assistant 31 (#36)

* home-assistant-31 Limit OSD name to 13 chars (#31)

* home-assistant-31 Limit OSD name to 13 chars moved to CEC adapter (#31)

* home-assistant-31 version up (#31)

* home-assistant-31 formatting (#31)

* formatting

* service description

* service description

* single attribute for volume

* fixed mute on -> mute off

* moved config constant from core into component

* check cec message length when asking physical address (#38) (#38)

* cec turn on/turn off commands instead of power

* cec turn on/turn off commands instead of power
2017-01-23 13:22:39 -08:00
Fabian Affolter
d5119a0520 Use device_state_attributes (#5518) 2017-01-23 13:21:12 -08:00
Robbie Trencheny
9bc9e7fbc4 Dont set a speed when fan turns on (#5514) 2017-01-23 13:20:54 -08:00
Adam Mills
f54f68903d Fixes for rest tests (#5495)
* Fixes for rest tests

* Linter fixes

* Alternate test_setup_missing_config implementation
2017-01-23 10:17:29 -05:00
Adam Mills
8217a42960 Don't start test thread as daemon and wait until patching is done (#5494) 2017-01-23 07:03:55 +02:00
Robbie Trencheny
9442131373 Add organization docs 2017-01-22 16:21:20 -08:00
andrey-git
addc2c4340 Allow easier customization of whole domain, entity lists, globs. (#5215) 2017-01-22 21:19:50 +02:00
Johann Kellerman
699c615d23 Add url 2017-01-22 18:34:00 +02:00
Marcelo Moreira de Mello
ab19577322 Bump amcrest version and refactored to simplify code (#5499)
* Bumped version to Amcrest 1.1.3 and rebase self.base_url()

* Refactored amcrest camera and amcrest sensor to simply object reference
2017-01-22 14:22:25 +01:00
Daniel Perna
59b0491d29 Homematic fixes + device support (#5503)
* Using new MaxShutterContact class

* Skip unnecessary function calls

* Added casting for VALUE

* Some renaming to clarify what's happening

* Bumped dependency version

* Bumped version in requirements
2017-01-22 14:20:49 +01:00
Fabian Affolter
75adb7ff46 Upgrade Sphinx to 1.5.2 (#5502) 2017-01-22 11:36:29 +01:00
Adam Mills
df361dc1e1 Fix network tests to use get_test_instance_port 2017-01-21 22:15:05 -05:00
Johann Kellerman
7df51dc545 gen_requirement: Raise an error if REQUIREMENT not pinned 2017-01-22 01:37:08 +02:00
Matthew Garrett
2992cd35be Add support for Leviton Decora Bluetooth dimmer switches (#5434)
* Add support for Leviton Decora Bluetooth dimmer switches

Add support for the Decora Bluetooth smart dimmer switches from Leviton.

* Update decora.py
2017-01-21 14:14:08 -08:00
Petr Vraník
06361b1ed1 implementing users remarks (#5481)
* cec client object

* cec command structure

* autodetect source

* volume support and native source select

* switch device

* media player device

* detecting of state

* friendly names

* hdmi cec properties

* presence detection

* simplified callbacks

* stable names

* renamed methods

* code cleanup

* name with vendor

* fixed standby call name

* fake standby/poweron

* domain switch

* domain switch

* async updating

* update separated

* cec -> hass event bridge

* fixed name generation

* code cleanup

* code cleanup

* icon constants

* code cleanup

* do not register unavailable devices

* discovery of deevices

* code cleanup

* cec device discovery

* moved method implementation into child

* service descriptions

* service descriptions

* service descriptions

* changed entity init sequence

* logging cleanup

* add remove as job

* closing cec, no service schemas

* correct iterate over dictionary

* Volume by commands

* threading

* logging minimized

* get load out of main thread

* naming cleanup

* get load out of main thread

* optimized discovery

* async where possible

* cleanup logging, constructors first

* pydoc

* formatting

* no async_update from out of loop
no hiding entities
removed redundant device_state_attributes
async updating presence

* no async

* working async cec

* cec in thirdparty lib

* cec initialized oudsice

* working without SIGSEGV

* rollbacked file changed by mistake

* sending of commands

* working with ha

* using hass loop and device driven updates

* version up

* version up

* Command types in pycec, cleanup for HA integration

* Removed media player, state moved to switch

* service descriptions

* requirements: pyCEC

* line width to 79

* doc

* doc

* overindentation solved

* HDMI to uppercase

* minimal dependency on cec

* removed unwanted line

* doc wording

* margin 79

* line continuation indent

* imperative doc

* lint: indentation

* fixed overindented

* fixed overindented

* fixed overindented

* fixed overindented

* order of imports

* PEP8

* keep signature of overriding

* removed redundant blank line

* fixed update call method (#4)

* Preparation for merge to upstream (#5)

* newer version of pyCEC
* updated services.yaml
* fixed lint scrpt to operate only on python files

* pycec version up

* update services

* no coverage report

* exclude non python files from lint

* lint only on python files

* Dev (#6)

* reordered
* sending nonserialized data through hass.data
* code formatting
* code formatting
* import order

* Dev (#7)

* newer version of pyCEC
* updated services.yaml
* fixed lint scrpt to operate only on python files

* pycec version up

* update services

* no coverage report

* exclude non python files from lint

* lint only on python files

* reordered

* sending nonserialized data through hass.data

* import order

* fixed object handling

* code formatting

* Backwards compatibility of hdmi_cec (#10)

* services:
power_on
standby
active_source

* new version of pyCEC (#12)

* newer version of pyCEC

* devices config (#13)

* getting device name from config

* shutdown fix (#14)


* correct call on shutdown

* remove misplaced annotations (#15)

* Preparation for merge to upstream (#5)

* newer version of pyCEC
* updated services.yaml
* reordered
* sending nonserialized data through hass.data
* services:
power_on
standby
active_source
* code formatting
* getting device name from config
* correct call on shutdown

* pyCEC version 0.3.6 (#18)

* newer version of pyCEC
* updated services.yaml
* sending nonserialized data through hass.data
* services:
** power_on
** standby
** active_source
* getting device name from config
* correct call on shutdown
* fork new thread on multicore machines
* support both config schemas: original and new (#16)
* volume press and release support (#17)

* support for media_player (#21)

* accept hexadecimal format of commands
* support for media player
* platform customization
* type constants

* Dev (#23)

* accept hexadecimal format of commands
* support for media player
* platform customization

* TCP CEC support (#24)

* accept hexadecimal format of commands
* support for media player
* platform customization
* preparing tcp support

* volume handling (#25)

* Incorporated CR remarks (#26)

* cleanup imports
* cleanup and enhance services description
* removed unwanted file

* implemented CR remarks (#27)

* pyCEC v0.4.6
* pined dependency version
* tighten service schemas

* requirements (#28)

* incorporate remarks from users (#32)

* home-assistant-31 make mute schema better (#31)

* pycec-30 pyCEC version up (#30)

* pycec-30 pyCEC version up (#30)

* home-assistant-30 OSD display name from configuration (#30) (#33)

* Home assistant 29 (#34)

* home-assistant-29 counting from 0 (#29)

* Home assistant 31 (#35)

* home-assistant-31 add support for mute-on and mute-off (#31)

* home-assistant-31 pyCEC version up (#31)

* Home assistant 31 (#36)

* home-assistant-31 Limit OSD name to 13 chars (#31)

* home-assistant-31 Limit OSD name to 13 chars moved to CEC adapter (#31)

* home-assistant-31 version up (#31)

* home-assistant-31 formatting (#31)

* formatting

* service description

* service description

* single attribute for volume

* fixed mute on -> mute off

* moved config constant from core into component
2017-01-21 14:13:46 -08:00
Craig J. Ward
a89a4f39dc only check for devices when not defined in config (#5490)
* only check for devices when not defined in config

* lint
2017-01-21 14:00:56 -08:00
Greg Dowling
74989f7941 Bump pyvera version (add RFX device support) (#5487) 2017-01-21 12:26:57 -08:00
Stefan Jonasson
ed12f9e237 Tellstick light fix (#5482)
* Fixed crash when lights objects was inited

* Fixed initial value for tellstick lights

* Fixed problem with lights not working with the turn_on action.
2017-01-21 12:24:48 -08:00
Fabian Affolter
13d801e1c6 Upgrade sqlalchemy to 1.1.5 (#5484) 2017-01-21 12:24:23 -08:00
Fabian Affolter
f13774269d Upgrade speedtest-cli to 1.0.2 (#5483) 2017-01-21 18:26:17 +01:00
joopert
56ed00a1db update nad library version (#5478) 2017-01-21 13:24:38 +01:00
Dale Higgs
a6f341f06a Add exit code to check_config script (#5471) 2017-01-21 10:39:50 +03:00
Lupin Demid
ccd2588cf7 added speed and emotion to Yandex tts (#5431)
* Added speed and emotion  parameters. Refactored test's

* Fixed float point arfs in url and added test for float point values
2017-01-21 08:36:28 +01:00
Pascal Vizeli
2fff8a5a11 [TTS] options support for service calls (#5436)
* [TTS] Support now options like voice and age on service.

* Add unittest
2017-01-21 08:35:18 +01:00
Robbie Trencheny
074f9315d7 Fan improvements (#5457)
* Remove SPEED_MED from fan

* Correctly use the oscillation on/off payloads for MQTT fan

* Add set_direction service documentation

* Correct function name for Wink fans

* Check for existence of the correct topic

* Enable set fan speed in emulated_hue

* features -> functions

* Final emulated_hue fan fixes

* Fix linting issues

* Revert to supported features instead of supported functions

* Fix logic

* Add a test for emulated_hue fan support
2017-01-20 22:21:28 -08:00
Robbie Trencheny
2efd7d4e4a Hue improvements (#5474)
* Allow automatic removal of all Hue entities from emulated_hue

* Allow disabling of Hue groups

* Only add device state attributes if they need to be there
2017-01-20 22:20:07 -08:00
Paulus Schoutsen
14309401d0 Update frontend 2017-01-20 22:10:44 -08:00
Pascal Vizeli
b2203f7f41 [ffmpeg] Use new 1.0 version / migrate all asyncio (#5464)
* [ffmpeg] Use new 1.0 version / migrate all asyncio

* fix lint

* fix import

* Add new service to binary_sensors

* fix lint
2017-01-20 21:56:22 -08:00
Robbie Trencheny
dec2ddb393 Merge pull request #5405 from xhostplus/roku-idle
Updated Roku IDLE state
2017-01-20 21:54:04 -08:00
Robbie Trencheny
58b698400e Set Roku name to the device name instead of the serial number (#5475) 2017-01-20 21:28:29 -08:00
Robbie Trencheny
26f6a9ee20 Bump requirements 2017-01-20 20:24:00 -08:00
Robbie Trencheny
6b0a6b87de Use is_screensaver 2017-01-20 20:23:20 -08:00
Robbie Trencheny
db97ad4485 Merge pull request #5473 from stu-gott/tts-cache
TTS: Invalidate broken file cache entries
2017-01-20 17:44:32 -08:00
Stu Gott
ec4b148a71 TTS: Invalidate broken file cache entries
If a cached file cannot be read by the TTS component, then it should
be removed from the file cache--or it will remain broken.
2017-01-20 20:09:03 -05:00
John Arild Berentsen
41ee798b0f [WIP][ZWave][Lock] Further improvements to zwave lock platform (#5400)
* Further improvements to zwave lock platform

* Add missing notification

* Some improvements
2017-01-20 13:01:36 -08:00
Martin Vacula
f4d2d69a5d Beaglebone Black binary sensor (#5422)
* Configuration parameters defined

* Edge detection added

* Example configuration added and tipo corrected

* Comments updated, lint update

* Added check for input pull_mode

* Too long line

* trailing white space

* Configuration parameters defined

* Edge detection added

* Example configuration added and tipo corrected

* Comments updated, lint update

* Added check for input pull_mode

* Too long line

* trailing white space

* pylint disable import error

* read_input() changed to return boolean value, according changes in binary sensor

* example configuration in docstring changed to mention direct web link, pylint update

* read_input() updated according review
2017-01-20 12:55:28 -08:00
Petr Vraník
067e11ea5c HDMI CEC - support for devices and commands (#4781)
* cec client object

* cec command structure

* autodetect source

* volume support and native source select

* switch device

* media player device

* detecting of state

* friendly names

* hdmi cec properties

* presence detection

* simplified callbacks

* stable names

* renamed methods

* code cleanup

* name with vendor

* fixed standby call name

* fake standby/poweron

* domain switch

* domain switch

* async updating

* update separated

* cec -> hass event bridge

* fixed name generation

* code cleanup

* code cleanup

* icon constants

* code cleanup

* do not register unavailable devices

* discovery of deevices

* code cleanup

* cec device discovery

* moved method implementation into child

* service descriptions

* service descriptions

* service descriptions

* changed entity init sequence

* logging cleanup

* add remove as job

* closing cec, no service schemas

* correct iterate over dictionary

* Volume by commands

* threading

* logging minimized

* get load out of main thread

* naming cleanup

* get load out of main thread

* optimized discovery

* async where possible

* cleanup logging, constructors first

* pydoc

* formatting

* no async_update from out of loop
no hiding entities
removed redundant device_state_attributes
async updating presence

* no async

* working async cec

* cec in thirdparty lib

* cec initialized oudsice

* working without SIGSEGV

* rollbacked file changed by mistake

* sending of commands

* working with ha

* using hass loop and device driven updates

* version up

* version up

* Command types in pycec, cleanup for HA integration

* Removed media player, state moved to switch

* service descriptions

* requirements: pyCEC

* line width to 79

* doc

* doc

* overindentation solved

* HDMI to uppercase

* minimal dependency on cec

* removed unwanted line

* doc wording

* margin 79

* line continuation indent

* imperative doc

* lint: indentation

* fixed overindented

* fixed overindented

* fixed overindented

* fixed overindented

* order of imports

* PEP8

* keep signature of overriding

* removed redundant blank line

* fixed update call method (#4)

* Preparation for merge to upstream (#5)

* newer version of pyCEC
* updated services.yaml
* fixed lint scrpt to operate only on python files

* pycec version up

* update services

* no coverage report

* exclude non python files from lint

* lint only on python files

* Dev (#6)

* reordered
* sending nonserialized data through hass.data
* code formatting
* code formatting
* import order

* Dev (#7)

* newer version of pyCEC
* updated services.yaml
* fixed lint scrpt to operate only on python files

* pycec version up

* update services

* no coverage report

* exclude non python files from lint

* lint only on python files

* reordered

* sending nonserialized data through hass.data

* import order

* fixed object handling

* code formatting

* Backwards compatibility of hdmi_cec (#10)

* services:
power_on
standby
active_source

* new version of pyCEC (#12)

* newer version of pyCEC

* devices config (#13)

* getting device name from config

* shutdown fix (#14)


* correct call on shutdown

* remove misplaced annotations (#15)

* Preparation for merge to upstream (#5)

* newer version of pyCEC
* updated services.yaml
* reordered
* sending nonserialized data through hass.data
* services:
power_on
standby
active_source
* code formatting
* getting device name from config
* correct call on shutdown

* pyCEC version 0.3.6 (#18)

* newer version of pyCEC
* updated services.yaml
* sending nonserialized data through hass.data
* services:
** power_on
** standby
** active_source
* getting device name from config
* correct call on shutdown
* fork new thread on multicore machines
* support both config schemas: original and new (#16)
* volume press and release support (#17)

* support for media_player (#21)

* accept hexadecimal format of commands
* support for media player
* platform customization
* type constants

* Dev (#23)

* accept hexadecimal format of commands
* support for media player
* platform customization

* TCP CEC support (#24)

* accept hexadecimal format of commands
* support for media player
* platform customization
* preparing tcp support

* volume handling (#25)

* Incorporated CR remarks (#26)

* cleanup imports
* cleanup and enhance services description
* removed unwanted file

* implemented CR remarks (#27)

* pyCEC v0.4.6
* pined dependency version
* tighten service schemas

* requirements (#28)
2017-01-20 12:39:18 -08:00
Colin O'Dell
cb47d16282 Don't use Debian's httpredir for backports (#5392)
Hopefully this solves https://github.com/home-assistant/home-assistant/pull/5322#issuecomment-273041585
2017-01-20 12:30:09 -08:00
Adam Mills
5ff9dfa440 Use voluptuous for cast ignore-cec (#5468) 2017-01-20 12:21:27 -08:00
Adam Mills
a7e5c847fb Kodi supports volume stepping (#5467) 2017-01-20 20:52:55 +01:00
Daniel Høyer Iversen
72d63517ba Merge pull request #5420 from MrMep/patch-4
Update generic_thermostat.py
2017-01-20 18:16:40 +01:00
John Arild Berentsen
c41cf7c308 Bugfix Zwave Light: Use only supported features for devices (#5370)
* Use only supported features for devices

* Changes

* Holy Macarony!
2017-01-20 13:35:41 +01:00
Pascal Vizeli
8496975de8 Fix if none data is present for a sensor. (#5415) 2017-01-20 09:07:03 +01:00
Daniel Høyer Iversen
f669680b1e fix issue ##5398 in yr sensor (#5459) 2017-01-19 23:58:15 -08:00
Fabian Affolter
2ed0e76e7c Add elevation to as_dict and use unified style for quoting (#5448) 2017-01-19 23:55:29 -08:00
Adam Mills
1f6f9a1677 Filter new entities from logbook (#5402) 2017-01-19 23:30:47 -08:00
Ryan Kraus
5dd45efac3 Updated ISY component to not overwrite state_attributes. (#5433)
* Updated ISY component to not overwrite state_attributes.

The ISY component included an ISYDevice base class that is used by all
of the isy994 platforms. This still overwrote the state_attributes
property instead of the more appropriate device_state_attributes
property. This was also repeated in the isy994 light platform. Both of
these were addressed. This also fixes issue #5428.

* Removed custom state attributes from ISY lights.

The brightness attribute need not be manually reported by the isy994
light platform.

* Removed ISY Node cleanup.

The ISY entities don’t really need to unsubscribe themselves while hass
is shutting down. Because these updates are not sent in a thread, there
is no negative impact from shutting down without unsubscribing. This
greatly speeds up hass shutdown.

* Removed unused attribute from isy994 light platform.

* Cleaned up ISY994 light entity class.

1) Removed the state property. This property is set in the Entity base
class and shouldn’t be overridden here.
2) Set the brightness property. This is the proper way of setting the
brightness for the Light base class.
3) Removed properties that are now unused because of these changes.
2017-01-19 22:22:33 -08:00
Pascal Vizeli
fe6a8f3367 Remove old openalpr component (#5406)
* Remove old openalpr component

* update region support
2017-01-19 22:21:25 -08:00
Daniel Høyer Iversen
d1ec422eab Merge pull request #5447 from home-assistant/rfxtrx
update rfxtrx lib
2017-01-20 07:17:42 +01:00
happyleavesaoc
f17efc2168 log formats match (#5456) 2017-01-19 21:31:44 -08:00
Paulus Schoutsen
887a33c7d1 Persist emulated hue IDs (#5435) 2017-01-19 21:27:10 -08:00
HerrHofrat
dbcad34b47 Updated valid station id list (#5449) 2017-01-19 23:33:31 +01:00
Robbie Trencheny
738292f817 Ignore import error on avion 2017-01-19 13:25:35 -08:00
Robbie Trencheny
a74258db09 Block Avion from auto installing because only supported on Linux (cc #5414) 2017-01-19 13:14:48 -08:00
Daniel Hoyer Iversen
62b785c040 update rfxtrx lib 2017-01-19 22:08:55 +01:00
Gianluca Barbaro
64c9cd805a Update generic_thermostat.py 2017-01-19 21:26:12 +01:00
Matthew Garrett
3b25b5a6da Add support for Avion Bluetooth dimmer switches (#5414)
GE sell a range of Bluetooth dimmer switches based on Avi-on technology.
Add a module for controlling them. There's also a set of smart switches that
speak the same protocol, but I don't have any of those to test support with.
2017-01-19 21:07:37 +01:00
David McNett
1a82adb054 New platform media_player/anthemav (#5146)
* Initial commit of anthemav platform.  It loads but has no purpose.

* Now presents a card in the UI but the values aren't real

* Mute and volume polling/setting work now

* Source lists and selection works now.

* Reduce debug logging verbosity

* Support power on/off and skip polling for details if power is off

* Add some static tables to decode numerics from telnet commands

* Add stub for unsupported media_play

* New style anthemav uses native asyncio structure

* Add device callback for asyncio

* This is ugly but it works

* Simplify async setup and abstract class data retrieval

* Implement commands (power on and power off for now)

* Add support for scan_interval and set default to 120 seconds

* Pass-through to package handlers for volume and input selection

* Slight restructuring to satisfy anthemav 0.9

* Load anthemav package from pypi now that it's registered

* Proper app_name from a/v info

* Mispelled word

* media_player/anthemav initial commit of platform requirements

* Philio 3-in-1 Gen 4 zwave sensor needs the no-off-event workaround. (#5120)

* Add print_config_parameter service to Z-Wave (#5121)

* Add print_config_param service to z-wave

* Add print_config_parameter service to z-wave

* Add print_config_parameter service to z-wave

* Fix typos

* Fix typos

* Fix typo

* Conform to Python/project style requirements

* Making pylint happy

* Bring pip requirements in agreement with the code

* Bungled previous update

* Remove unnecessady SCAN_INTERVAL logic

I was unawre that this is performed as part of the normal platform behavior and
it's unnecessary for a platform to independently implement this logic.

* Refactor code based on @armills PR requests

* Re-add media_play stub to avoid traceback

* Align with platform reqirements

* Remove references to SCAN_INTERVAL and clean up _lookup logic

* Add DEFAULT_PORT assignment

* Code style changes and removal of vestigial structures

* CONF_NAME handling changes to allow local override to default from device

* Address PR feedback from @balloob

* Remove media_play function override

It's no longer necesary for the platform to implement a stub media_play
function override now that the Add SUPPORT_PLAY flag #5181 issue has been
resolved and merged into the dev branch.

* Rename callback function to async_ for clarity

* Use async routines for platform methods

* Convert update callback to coroutine for conformity

Underlying anthemav library now properly supports coroutine callbacks instead
of normal functions.  Converted the platform callback to a coroutine for
conformance with async operation for the device.

Special thanks to @pvizeli and @armills for their invaluable remedial Python
instruction!

* Further callback refinements

Altered the nature of callback handling based on suggestions from @pvizeli

* True not needed for local push update_ha_state

* Small style fix
2017-01-19 20:07:01 +01:00
Gianluca Barbaro
6ef9714dc1 Update generic_thermostat.py 2017-01-19 19:46:58 +01:00
Pascal Vizeli
3da25c227f lint v2 (#5444) 2017-01-19 19:33:15 +01:00
Pascal Vizeli
11083cf04b Fix lint (#5443) 2017-01-19 19:18:32 +01:00
Pascal Vizeli
8da398c0bd Proxy aiohttp websession / more rebust. (#5419) 2017-01-19 09:55:27 -08:00
Pascal Vizeli
9799631797 [camera/mjpeg] Support still image for thumbmail (#5440) 2017-01-19 09:53:08 -08:00
Touliloup
909978b0d1 [Device Tracker] Xiaomi Mi Router token refresh (#5437)
Device token is refreshed if not anymore valid (for example after router reboot).
Token refresh will only be tried once per update.
2017-01-19 15:05:37 +01:00
Gianluca Barbaro
a87d653077 Update generic_thermostat.py
As suggested, I added a callback on the heater switch state change.
Still it doesn't solve the problem.
2017-01-19 10:57:45 +01:00
Adam Mills
216ac14b3d Fix test for async media player volume helpers (#5432) 2017-01-18 19:15:51 -08:00
Pascal Vizeli
5299c92352 Bugfix volume up/down (#5426) 2017-01-18 22:52:11 +01:00
Gianluca Barbaro
72dca1da09 Update generic_thermostat.py 2017-01-18 12:39:16 +01:00
Gianluca Barbaro
b7bf07eaca Update generic_thermostat.py
After _control_heating() is executed, current_operation() is correctly called but _is_device_active() still reports the old state if the heater switch, at least in my case. The resulting climate state is incorrect until the next refresh, which apparently occurs only when _sensor_changed() gets called (it can be minutes after).

I think the state of the heater switch should be forced to update at the end of  _control_heating(), but I don't know how to do that...
A simple sleep() fixes it, but obviously is just a temporary workaround, I'm not really expecting this PR to be actually committed, unless there's no other solution.
2017-01-18 12:20:39 +01:00
Alex
3267aa8c08 WIP fritz install dependencies fix (#5399)
* updated fritzconnection dependency to 0.6 from pypi

* updated requirements_all for new dependencies of fritz platform
2017-01-18 11:01:31 +01:00
Adam Mills
eb06023aa5 Fix universal mp service call wth no child (#5411) 2017-01-17 22:15:37 -08:00
Pascal Vizeli
2a362fd1ff Asyncio notify component migration (#5377)
* Async migrate notify/platform

* convert group to async

* fix unittest
2017-01-17 22:08:03 -08:00
Paulus Schoutsen
f7ac644c11 Make SMTP tests fast 2017-01-17 22:00:23 -08:00
Paulus Schoutsen
6cd57ac02f Fix Yamaha doing I/O in event loop (#5387) 2017-01-17 21:53:03 -08:00
Paulus Schoutsen
283bcf367b Ignore python-eq3bt from auto-building 2017-01-17 21:48:33 -08:00
Paulus Schoutsen
50b326c7fc Upgrade somecomfort (#5413) 2017-01-17 21:04:56 -08:00
Fabian Affolter
4c52380519 Sync logger messages with Mi-Flora and link to docs (#5391) 2017-01-17 23:41:09 +01:00
Fabian Affolter
bfc0a6a17c Use constants (#5390) 2017-01-17 23:40:34 +01:00
Pascal Vizeli
cfc936761b Make upc more robust (#5404)
* Make upc more robust

* update unittest

* add test for parse error
2017-01-17 23:35:02 +01:00
Bill Nelson
d31f00f672 Updated Roku IDLE state 2017-01-17 13:53:35 -08:00
Whytey
298c1654f8 New zabbix (#5297)
* Hopefully a clean branch for merging

* Remove scan_interval, use defaults for now

* Fix code style error
2017-01-17 00:41:37 -08:00
R1chardTM
321a8be339 Move Nest sensors configuration to Nest component (#4983)
* Move Nest sensor config to Nest Component

* Ensure Nest Protect sensors are added without specified sensor config

* Fix pylint warnings

* Remove support for empty monitored condion list

* Remove scan interval

* Remove scan interval import

* Add Nest sensors by default with opt-out
2017-01-17 00:12:15 -08:00
anpetrov
d240ea56d8 Add Skybeacon BLE temperature/humidity sensor (#5183)
Signed-off-by: Andrey Petrov <andrey.petrov@gmail.com>
2017-01-17 00:01:11 -08:00
Johan Bloemberg
915a91dc1b DSMR: TCP, reconnecting and V4 CRC support (#5164)
* Add support for TCP connection.

* Implement reconnect logic.

* Actually register connect loop task and fix error handling.

* Fix lint, configure upstream requirement.

* Revert debug logging.

* Explicitly catch connection errors.

* Test reconnect on setup and reconnect after disconnect.

* Style.
2017-01-16 23:56:00 -08:00
Job Vermeulen
40ba4fd872 Tado device tracker support (#5046)
* Added tado device tracker

* Added tado device tracker to .converagerc

* Updated docs

* Code formatting and removed unused import

* Code formatting and removed unused import

* Respected the lint line length

* Respect pydocstyle rules

* Respect the lint line limit length

* Fixed reviewer feedback

* Changed the tracker to support async

* Respect the New line end of file rule

* Update .coveragerc
2017-01-16 23:15:11 -08:00
martst
e4a45fa857 Improved x10 state monitoring (#5115)
* Improved x10 state monitoring

* Improved x10 state monitoring

* Use update mthod to fetch state change

* Use update mthod to fetch state change

* Use update function to fetch status

* remove temp file

* Add doc string to update method
2017-01-16 23:11:02 -08:00
Giel Janssens
c72f8b1a06 Netatmo Presence (#5122)
* Netatmo Presence

* Travis

* Remove def camera_stream
2017-01-16 23:10:18 -08:00
Anton Lundin
fd5c2ad08f Denon improvements (#5251)
* denonavr: Expose input as title when in non playing modes

Signed-off-by: Anton Lundin <glance@acc.umu.se>

* denon: Pop from the intended end of the list

Pop from front of list, so we start with NSE0, then NSE1X and so on.

Signed-off-by: Anton Lundin <glance@acc.umu.se>

* denonavr: Don't provide broken media_url's

Only return a media_url if we're in a state that might provide one.

Signed-off-by: Anton Lundin <glance@acc.umu.se>

* denonavr: Only expose player support when in a player mode

This changes so the denonavr only exposes the media player support, when
in a mode that supports media playing.

Signed-off-by: Anton Lundin <glance@acc.umu.se>
2017-01-16 23:04:50 -08:00
Nick Touran
784b87eb2f Add listing and selection of available MPD playlists (#5237)
* Add listing and selection of available MPD playlists through input source UI.

* MPD support updating playlist list on the fly as well as at turn-on.

* Added no_throttle to force playlist update on mpd power cycle.

* Added kwargs signature to get Throttle working right.
2017-01-16 23:01:57 -08:00
Christiaan Blom
ef274c6914 New Discord notification component (#5330)
* Initial commit of discord notification component

* Fixed error where script added extra entries to .coveragerc

* Cleaned up code

* Compliance to PEP8

* removed dependencies

* readded dependencies

* changed name of client id to token for configuration

* Changes for Hound

* Incorporated Review Feedback

* Review feedback

* Updated requirements file

* Check compliance
2017-01-16 22:58:38 -08:00
Marcelo Moreira de Mello
b915cf776b Introduced Amcrest camera sensors and bump Amcrest module version (#5310)
* Introduced Amcrest camera sensors

* Makes script/gen_requirements_all.py happy

* Bump Amcrest version across all components

* - Adjusted scan_interval to 10 seconds

- Filtering HTTPError and ConnectTimeout exceptions

- Removed @Throttle decorator
2017-01-16 22:57:25 -08:00
Jesse Newland
41a6c35ea2 Install phantomjs in Docker container (#5368) 2017-01-16 22:55:42 -08:00
Tom Dickman
65bf30643a Use timezone aware timestamp in flux_update (#5378) 2017-01-16 22:55:05 -08:00
Teemu R
d62b1fc808 Make initial flux update directly when turning on (#5266) 2017-01-16 22:53:34 -08:00
Adam Mills
bae38ac17b Include .ignore file for search utilities (#5290)
* Include .ignore file for search utilities

This instructs search utilities to ignore generated html/js files.

* Panels has no js files
2017-01-16 22:52:53 -08:00
Bryce Edwards
8e17bf43e0 added upnp_bind_multicast option to emulated_hue component (#5381) 2017-01-16 22:52:21 -08:00
Tom Dickman
59f74896a0 Updated abreviation for miles in darksky sensor (#5382) 2017-01-16 22:45:44 -08:00
Paulus Schoutsen
48607d15e5 Merge pull request #5386 from home-assistant/Bug_#5374
fix bug #5374
2017-01-16 22:44:04 -08:00
Daniel Høyer Iversen
51dcd3de6d fix bug #5374 2017-01-17 07:35:09 +01:00
Paulus Schoutsen
2625fee861 Fix script release (#5345) 2017-01-16 22:25:38 -08:00
Paulus Schoutsen
4d96b12424 Merge pull request #5384 from home-assistant/release-0-36-1
Release 0 36 1
2017-01-16 22:24:12 -08:00
Paulus Schoutsen
4603cf4f38 Bump release to 0.36.1 2017-01-16 22:09:17 -08:00
Paulus Schoutsen
28e5659eee Fix load_yaml default value (#5383) 2017-01-16 22:08:57 -08:00
Paulus Schoutsen
7511a5842d Fix load_yaml default value (#5383) 2017-01-16 22:08:47 -08:00
Daniel Høyer Iversen
836eed2a15 fix bug in flux_led (#5373) 2017-01-16 22:08:24 -08:00
Daniel Høyer Iversen
372b54534a Fix python-nest release number (#5369)
* fix_#5365

* fix_#5365
2017-01-16 22:08:24 -08:00
Marcelo Moreira de Mello
7bad0171ec Fixes #5357 which especify absolute path to save cookie used by USPS sensor. (#5358)
Traceback (most recent call last):
  File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/helpers/entity_component.py", line 151, in _async_setup_platform
    entity_platform.add_entities, discovery_info
  File "/usr/local/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/local/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/local/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/local/lib/python3.5/concurrent/futures/thread.py", line 55, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/components/sensor/usps.py", line 48, in setup_platform
    add_devices([USPSSensor(session, config.get(CONF_UPDATE_INTERVAL))])
  File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/components/sensor/usps.py", line 58, in __init__
    self._profile = myusps.get_profile(session)
  File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 100, in wrapped
    _login(*args)
  File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 90, in _login
    _save_cookies(session.cookies, session.auth.cookie_path)
  File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 41, in _save_cookies
    with open(filename, 'wb') as handle:
PermissionError: [Errno 13] Permission denied: './usps_cookies.pickle'
2017-01-16 22:08:24 -08:00
Pascal Vizeli
7af92d0bca Bugfix upc with aiohttp 1.2 (cookies) (#5362) 2017-01-16 22:08:24 -08:00
Teemu R
63bf453c60 Eq3bt bump version, expose away attribute (#5353)
* eq3bt: read away ends attr

* eq3bt: bump version to fix missing __init__, expose away_ends attribute.
2017-01-16 22:08:24 -08:00
Pascal Vizeli
8a95cc4104 Bugfix timedelta v2 (#5349)
* Bugfix timedelta v2

* fix volvo

* fix lint
2017-01-16 22:08:24 -08:00
Heiko Rothe
29e2613c75 Fixed the lannouncer platform get_service method (#5352) 2017-01-16 22:08:24 -08:00
Daniel Høyer Iversen
0bbb16626c fix bug in flux_led (#5373) 2017-01-16 21:52:12 -08:00
Duoxilian
d8560a244c Made target temperature sensitive to auto mode (#5312)
* Made target temperature sensitive to auto mode

* Used current_operation instead of operation_mode

* When not in auto_mode, the temperature is sent to set_temperature

* Low and high targets are switched in the call to set_temperature.

* Missed on current_operation. Use STATE_AUTO.

* Remove incorrectly checked in directory.

* Updated set_temperature based on Martin's feedback.

* Use ATTR_TEMPERATURE from const.py
2017-01-17 02:58:34 +01:00
Adam Mills
bd3117a0e7 Reserve a test port for broken api to fix race (#5371)
* Reserve a test port for broken api to fix race

* I cheated.
2017-01-16 12:56:47 -08:00
Daniel Høyer Iversen
196897fdfc Fix python-nest release number (#5369)
* fix_#5365

* fix_#5365
2017-01-16 18:03:26 +01:00
Marcelo Moreira de Mello
62f26fb701 Fixes #5357 which especify absolute path to save cookie used by USPS sensor. (#5358)
Traceback (most recent call last):
  File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/helpers/entity_component.py", line 151, in _async_setup_platform
    entity_platform.add_entities, discovery_info
  File "/usr/local/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/local/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/local/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/local/lib/python3.5/concurrent/futures/thread.py", line 55, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/components/sensor/usps.py", line 48, in setup_platform
    add_devices([USPSSensor(session, config.get(CONF_UPDATE_INTERVAL))])
  File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/components/sensor/usps.py", line 58, in __init__
    self._profile = myusps.get_profile(session)
  File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 100, in wrapped
    _login(*args)
  File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 90, in _login
    _save_cookies(session.cookies, session.auth.cookie_path)
  File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 41, in _save_cookies
    with open(filename, 'wb') as handle:
PermissionError: [Errno 13] Permission denied: './usps_cookies.pickle'
2017-01-16 17:36:35 +01:00
whhsw
887c586aae Add station parameter to waqi sensor (#5239)
* Add station parameter to waqi sensor

* Update waqi.py

* Update waqi.py

* Update waqi.py

* add back 'waqi' prefix and add station_name prop

* Update waqi.py
2017-01-16 12:44:09 +01:00
Pascal Vizeli
f08e2648ae Bugfix upc with aiohttp 1.2 (cookies) (#5362) 2017-01-16 11:06:23 +01:00
Teemu R
7a1d4b96ef Eq3bt bump version, expose away attribute (#5353)
* eq3bt: read away ends attr

* eq3bt: bump version to fix missing __init__, expose away_ends attribute.
2017-01-16 06:48:36 +01:00
Pascal Vizeli
2e3d5302bf Bugfix timedelta v2 (#5349)
* Bugfix timedelta v2

* fix volvo

* fix lint
2017-01-15 23:53:37 +01:00
Gianluca Barbaro
6b91d9a75c Update keyboard_remote.py (#5341)
Now it fires events in case the keyboard disconnects and/or disconnects
2017-01-15 14:05:41 -08:00
Heiko Rothe
ad23613cdc Fixed the lannouncer platform get_service method (#5352) 2017-01-15 13:45:54 -08:00
freol35241
c8cf952e21 Remove import of datetime module 2017-01-15 21:10:02 +01:00
freol35241
01d9e6cdfe Removing throttle decorator
Removing redundant throttle decorator on update method. This ensures the existing 'cache-value' config option is respected. Also, UPDATE_INTERVAL is renamed to DEFAULT_UPDATE_INTERVAL for clarity.
2017-01-15 20:53:46 +01:00
Colin O'Dell
8200827a19 Add support for direct MJPEG streams from Amcrest cameras (#5217)
* Add support for direct MJPEG streams from Amcrest cameras

The previous implementation relied on using snapshots from the camera. However,
some Amcrest models cannot keep up with the large number of requests and
instead timeout, resulting in no video stream. These cameras do provide MJPEG
streams though, so this commit adds the option to use these instead of creating
one from snapshots.

Unfortunately, some cameras on newer firmwares do not support MJPEG streams at
high resolution - only at low resolution.

By providing users with both a `resolution` and `stream_source` option, we can
allow them to choose whichever combination works for their particular model
and firmware version.

* Close the stream instead of releasing it

* Close stream without creating a task

* Handle client aborts

* fix lint
2017-01-15 20:37:24 +01:00
Fabian Affolter
633c1408fb Upgrade pylast to 1.7.0 (#5344) 2017-01-15 09:37:17 -08:00
Paulus Schoutsen
82b84f480b Fix script release (#5345) 2017-01-15 09:16:46 -08:00
Paulus Schoutsen
bcfc30264d version bump to 0.37 2017-01-15 08:40:20 -08:00
Paulus Schoutsen
c21172dd36 Merge branch 'dev' 2017-01-15 08:39:56 -08:00
Paulus Schoutsen
24bc035e22 ps - version bump to 0.36 2017-01-15 08:38:41 -08:00
Paulus Schoutsen
36da5d9adb Merge pull request #5245 from home-assistant/dev
0.36
2017-01-15 08:36:53 -08:00
Pascal Vizeli
d7d428119b Bugfix stack trace from api (#5332) 2017-01-15 08:36:24 -08:00
Pascal Vizeli
c458ee29f2 Rename log message / handle cancellederror on image proxy (#5331) 2017-01-15 08:35:58 -08:00
Lupin Demid
85a84549eb Yandex tts component (#5342)
* Added Yandex SpeechKit TTS

* Added test stub

* Added two test and added property for yandex tts

* Copy all test from voice rss

* Added test vith different speaker and  code style changes

* Added new line to end of file

* Url format replaced with url_params
2017-01-15 15:43:10 +01:00
Fabian Affolter
2465aea63b Fix link (#5343) 2017-01-15 14:53:07 +01:00
Zac Hatfield Dodds
e00e6f9db6 Bom weather platform (#5153)
* Fix typo

* Auto-config for `sensor.bom`

Deprecate (but still support) the old two-part station ID, and move to a
single `station` identifier.  Any combination of these, including none,
is valid; most results in downloading and caching the station map to
work out any missing info.

* Add `weather.bom` platform

Very similar to `sensor.bom`, but supporting the lovely new `weather`
component interface.  Easier to configure, and does not support the
deprecated config options.

* Review improvements to BOM weather

Largely around better input validation.
2017-01-15 12:12:50 +01:00
Daniel Høyer Iversen
9fff634b9d Merge pull request #5334 from MrMep/patch-1
Update vlc.py
2017-01-15 10:42:13 +01:00
Adam Mills
e7c157e766 Tests for async volume up/down (#5265) 2017-01-15 08:27:56 +01:00
Martin Hjelmare
9db1aa7629 Add discovery notify support and mysensors notify (#5219)
* Add mysensors notify platform

* Make add_devices optional in platform callback function.
* Use new argument structure for all existing mysensors platforms.
* Add notify platform.
* Update mysensors gateway.

* Refactor notify setup

* Enable discovery of notify platforms.
* Update and add tests for notify component and some platforms.
* Continue setup of notify platforms if a platform fails setup.
* Remove notify tests that check platform config. These tests are not
  needed when config validation is used.
* Add config validation to APNS notify platform.
* Use discovery to set up mysensors notify platform.

* Add discovery_info to get_service and update tests

* Add discovery_info as keyword argument to the get_service function
  signature and update all notify platforms.
* Update existing notify tests to check config validation using test
  helper.
* Add removed tests back in that checked config in apns, command_line
  and file platforms, but use config validation test helper to verify
  config.
* Add a test for notify file to increase coverage.
* Fix some PEP issues.

* Fix comments and use more constants

* Move apns notify service under notify domain
2017-01-15 03:53:14 +01:00
Gianluca Barbaro
d998cba6a2 Update vlc.py
Added default value for "arguments"
2017-01-15 01:35:46 +01:00
Gianluca Barbaro
8013963784 Update vlc.py 2017-01-14 23:31:17 +01:00
Gianluca Barbaro
7436a96978 Update vlc.py 2017-01-14 22:32:50 +01:00
Gianluca Barbaro
3d9b2b5ed0 Update vlc.py
Added support for additional optional configuration

arguments:

to send to vlc.
It is useful for special configurations of VLC.
For example, I have two sound cards on my server, so I defined two vlc media players:

media_player:
- platform: vlc
  name: speaker_1
  arguments: '--alsa-audio-device=hw:1,0'
- platform: vlc
  name: speaker_2
  arguments: '--alsa-audio-device=hw:0,0'

This way, by specifying the corresponding entity_id, I can send the output to the desired speaker.
It is also useful for TTS.
2017-01-14 22:30:24 +01:00
Thibault Cohen
3b9fb6ccf5 Improve InfluxDB (#5238)
* Revert #4791 and fixes #4696

* Update influxDB based on PR comments

* Add migration script

* Update influxdb_migrator based on PR comments

* Add override_measurement option to influxdb_migrator

* Rename value field to state when data is string type

* Fix influxdb cloning query
2017-01-14 09:52:47 -08:00
Colin O'Dell
e3418f633c Add ffmpeg to Docker from jessie-backports (#5322) 2017-01-14 09:43:40 -08:00
Fabian Affolter
bf3e5b460e Clean-up (#5327) 2017-01-14 09:42:45 -08:00
Colin O'Dell
03a6aa48e0 Copy openzwave config to ensure it exists (fixes #5328) (#5329) 2017-01-14 09:41:38 -08:00
Fabian Affolter
2aa996b558 Update name (#5324) 2017-01-14 17:08:48 +01:00
Fabian Affolter
ef4a9bf354 Add timeout to requests, remove pylint disable, and docsstrings (#5326) 2017-01-14 17:08:21 +01:00
Michaël Arnauts
d4eabaf844 Remove build dirs from docker image to keep the layers small (#5243)
* Remove build dirs from docker image to keep the layers small

* Create setup_docker_prereqs script to prepare docker env

* Add documentation for required packages, drop colorlog and cython in first step of Dockerfile since it will be installed later on anyway. Drop libglib2.0-dev and libbluetooth-dev

* Also remove early install of colorlog and cython in Dockerfile.dev

* Re-add libglib2.0-dev and libbluetooth-dev for Bluetooth LE
2017-01-14 07:41:41 -08:00
Pascal Vizeli
7ed83306ea Add warning to openalpr (#5315)
* Add warning to openalpr

* fix lint

* update comment
2017-01-14 15:36:20 +01:00
Fabian Affolter
2e7ae1d5fe Update the link to the docs (#5321) 2017-01-14 15:16:42 +01:00
joopert
f2a42d767e fix hass.loop.run_in_executor in mediaplayer init (#5320) 2017-01-14 14:54:00 +01:00
Michaël Arnauts
c3783bf49b Bugfix for ping component now DEFAULT_SCAN_INTERVAL is a timedelta (#5318) 2017-01-14 11:55:29 +01:00
Pascal Vizeli
b817c7d0c2 Bugfix camera fake image (#5314)
* Bugfix camera fake image

* add logger
2017-01-14 11:53:00 +01:00
Pascal Vizeli
c2492d1493 Component "Image processing" (#5166)
* Init new component for image processing.

* Add demo platform

* address comments

* add unittest v1 for demo

* Add unittest for alpr

* Add openalpr local test

* Add openalpr cloud platform

* Add unittest openalpr cloud platform

* Update stale docstring

* Address paulus comments

* Update stale docstring

* Add coro to function

* Add coro to cloud
2017-01-14 08:18:03 +01:00
Teemu R
5bba9a63a5 switch.tplink: bump to the newest release of pyhs100 (#5308) 2017-01-13 22:20:47 -08:00
Matthew Garrett
d6747d6aaf Add support for Zengge Bluetooth bulbs (#5196)
* Add support for Zengge Bluetooth bulbs

Adds support for the Zengge Bluetooth RGBW bulbs. These are sold under a
number of brands, including Flux. The bulbs do not support full RGBW
control - they turn off the RGB LEDs when white is enabled, and vice versa.

* Update zengge.py
2017-01-13 22:15:43 -08:00
William Scanlon
0da8418f3f Wink fan support (#5174)
* Initial commit for Wink fan support

* Added fan to discovery list

* Raise NotImplementedError and fixed is_on

* Added speed property

* Update __init__.py
2017-01-13 22:08:13 -08:00
Johann Kellerman
9f765836f8 [core] Add 'packages' to the config (#5140)
* Initial

* Merge dicts and lists

* feedback

* Move to homeassistant

* feedback

* increase_coverage

* kick_the_hound
2017-01-13 22:01:47 -08:00
Teemu R
d58b901a78 eq3btsmart: support modes and clean up the code to use climatedevice's features (#4959)
* eq3btsmart: support modes and clean up the code to use climatedevice's features

* eq3btsmart: re-add device state attributes

adds reporting for is_locked, valve, window_open and low_battery,
exposing everything the device reports currently.

* eq3btsmart: bump version req

* eq3btsmart: fix a typo in mode name, report unknown when not initialized

* eq3btsmart: depend on newly forked python-eq3bt lib, pythonify states
2017-01-13 21:34:35 -08:00
Touliloup
394b52b9e8 Xiaomi device tracker (#5283)
* [Device Tracker] Xiaomi Mi Router integration as device tracker

    This device tracker allow to track device connected to Xiaomi Router.
    Parameter: host, username (default admin) and password.

* [Device Tracker] Addition of Xiaomi device tracker file in coverage
2017-01-13 21:24:58 -08:00
Johann Kellerman
b67cce7215 Add correct line numbers for yaml include directives (#5303) 2017-01-13 21:13:17 -08:00
Pascal Vizeli
0cf3c22da0 Bugfix media_player volume_ up and down (#5282)
* Bugfix media_player volume_ up and down

* fix lint

* make a coro
2017-01-13 21:09:02 -08:00
Martin Rowan
a7cb9bdfff Fixed bootstrap to upgrade pip if mininum version not present. As parameter --only-binary in requirements.txt doesn't work on pip < 7.0.0 so install fails. This is to simplify the setup of the development environment when using pyvenv. (#5301) 2017-01-13 21:03:50 -08:00
Dan Cinnamon
5f7d53c06b Bump pyenvisalink to version 2.0 (#5311) 2017-01-13 21:02:33 -08:00
Pascal Vizeli
4b43537801 Bugfix camera streams (#5306)
* fix mjpeg streams

* fix trow error on close by frontend

* fix ffmpeg
2017-01-13 15:57:38 -08:00
Adam Mills
6abad6b76e Version bump for kodi dependency (#5307)
Catches timeout exceptions
2017-01-14 00:16:38 +01:00
Robbie Trencheny
6000c59bb5 Remove GTFS default name & string change 2017-01-13 14:02:00 -08:00
Pascal Vizeli
2c3f55acc4 Add HMWIOSwitch to sensor, binary (#5304) 2017-01-13 22:39:42 +01:00
Pascal Vizeli
a30711f1a0 Update pyhomematic 1.19 & small cleanups (#5299) 2017-01-13 21:22:09 +01:00
Thom Troy
1219ca3c3b [sensor] Add Dublin bus RTPI sensor (#5257) 2017-01-13 19:15:46 +02:00
Pascal Vizeli
baa8e53e66 Bugfix group reload (#5292)
* Bugfix group / hit update

* try to fix round 2

* Convert it to coro

* Don't check statemachine, check unsub listener.
2017-01-13 03:29:20 -08:00
William Scanlon
f7a1d63d52 Support for TrackR device trackers (#5010)
* Support for TrackR device trackers

* Change small style for hass
2017-01-13 00:16:05 +01:00
Pascal Bach
d12decc471 Upgrade to voluptuous to 0.9.3 (#5288) 2017-01-12 23:56:37 +01:00
Pascal Bach
64800fd48c Upgrade distro to 1.0.2 (#5291) 2017-01-12 23:16:32 +01:00
Greg Dowling
9a3c0c8cd3 Don't build Adafruit_BBIO - doesn't work on all platforms. (#5281)
* Don't build Adafruit_BBIO - doesn't work on all platforms.

* Disable pylint import warning on BBIO.
2017-01-12 08:31:30 -08:00
Greg Dowling
d4a54acda0 Merge pull request #5280 from home-assistant/bump-pywemo-version
Bump pywemo version.
2017-01-11 23:26:06 +00:00
pavoni
dc937cc8cf Bump pywemo version. 2017-01-11 23:10:24 +00:00
Adam Mills
eb9b95c292 Convert flic to synchronous platform. (#5276)
* Convert flic to synchronous platform.

pyflic is a synchronous library

* Move pyflic event loop to dedicated thread.
2017-01-11 22:59:39 +01:00
Paulus Schoutsen
e68e29e03e Upgrade to aiohttp 1.2 (#4964)
* Upgrade to aiohttp 1.2

* Clean up emulated_hue tests
2017-01-11 21:25:02 +01:00
Andrew Williams
1cf9ae5a01 Fix TCP sensor to correctly use value_template (#5211)
* Fix TCP sensor to correctly use value_template

* Fix TCP component tests

* Update tcp.py
2017-01-11 17:26:29 +01:00
Pascal Vizeli
3f3a3bcc8a Cleanup language support on TTS (#5255)
* Cleanup language support on TTS

* change to default_language & address comments

* Cleanup not needed code / comment from paulus
2017-01-11 16:31:16 +01:00
Daniel Høyer Iversen
467cb18625 Add last triggered to script (#5261)
* Add last triggered to script

* Add tests for script last_triggered
2017-01-11 16:23:05 +01:00
Robbie Trencheny
82d037a828 Merge pull request #5117 from gopalkildoliya/dev
Notify component for Facebook Messenger
2017-01-10 23:16:43 -08:00
Marcelo Moreira de Mello
34a9fb01ac Bump flux_led version and make use of PyPi package (#5267)
* Bump flux_led version and make use of PyPi package

* Makes script/gen_requirements_all.py happy
2017-01-11 06:45:46 +01:00
joopert
3a4b4380a1 Add support for NAD receivers (#5191)
* Add support for NAD receivers

* remove self.update() in various methods

* remove setting attributes in various methods

* Change import to hass style
2017-01-10 22:32:43 +01:00
Pascal Vizeli
6b00f7ff28 Bugfix async device_tracker see callback (#5259) 2017-01-10 17:19:51 +01:00
Valentin Alexeev
f75e13f55e Use SHA hash to make token harder to guess (#5258)
* Use SHA hash to make token harder to guess

Use hashlib SHA256 to encode object id instead of using it directly.

* Cache access token

Instead of generating a token on the fly cache it in the constructor.

* Fix lint
2017-01-10 16:01:04 +01:00
sander76
6845a0974d adding a default icon "blind" to a PowerView blinds scene. (#5210)
* adding a default icon "blind" to a PowerView blinds scene.

* Adding icon property to define blind icon. Removed it from the state attributes dict.

* fixing lint error
2017-01-10 13:21:15 +01:00
Anton Lundin
7e1629a962 Fix async_volume_up / async_volume_down (#5249)
async_volume_up / async_volume_down should be async versions of
volume_up / volume_down, not a async version of the default variants of
volume_up / volume_down.

The previous code always called into the mediaplayers set_volume_level,
and never into volume_up / volume_down.

Signed-off-by: Anton Lundin <glance@acc.umu.se>
2017-01-10 10:58:39 +01:00
andrey-git
0b685a5b1e Expose supported_features of mqtt_json (#5250)
* Expose supported_features of mqtt_json

* Remove whitespace
2017-01-10 00:40:52 +01:00
Johann Kellerman
1f31dfe5d3 [recorder] Include & Exclude domain fix & unit tests (#5213)
* Tests & domain fix

* incl/excl combined
2017-01-09 21:53:30 +01:00
Fabian Affolter
6be19e8997 Update pytz to 2016.10 (#5247) 2017-01-09 21:50:38 +01:00
Johann Kellerman
e6a9b6404f [sensor/sma] SMA Solar Inverter sensor (#5118)
* Initial

* Rebase ensure_list

* timedelta & remove prints
2017-01-09 21:35:47 +01:00
Fabian Affolter
dd7cafd5e3 Upgrade TwitterAPI to 2.4.3 (#5244) 2017-01-09 21:34:18 +01:00
Adam Mills
c7249a3e3a Build libcec for Docker image (#5230)
* Build libcec for Docker image

* Update development dockerfile as well

* Dynamically load python paths for current version
2017-01-09 17:49:11 +01:00
Pascal Vizeli
bb02fc707c [device_traker/upc] New UPC connect box platform (#5100) 2017-01-09 18:08:37 +02:00
Michaël Arnauts
3ed7c1c6ad Add Lannouncer tts notify component (#5187)
* Add Lannouncer notify component

* Send message by opening a raw TCP socket instead of using requests. Cleanup of method validation.

* Use 'return' instead of 'return None'
2017-01-09 14:10:46 +01:00
Terry Carlin
ee055651cd Update insteon_local.py (#5236)
Correct typo
2017-01-09 12:11:15 +01:00
St. John Johnson
9fdefa5a1d Fix #5188 by Closing the stream instead of Releasing it (#5235)
* Fix #5188 by Closing the stream instead of Releasing it

Closing just terminates the connection, release attempts to download all the contents before closing.  Since the MJPEG stream is infinite, it loads and loads content until the python script runs out of memory.

Source: 50b1d30f41/aiohttp/client_reqrep.py (L668-L672)

* Update mjpeg.py
2017-01-09 10:35:38 +01:00
Paulus Schoutsen
f87016afe6 Update MDI 2017-01-09 01:38:27 +01:00
Paulus Schoutsen
b685e6e2b5 Update frontend 2017-01-09 01:38:04 +01:00
Paulus Schoutsen
5983dc232f Update frontend 2017-01-09 01:14:13 +01:00
Adam Mills
469472914b Add SUPPORT_PLAY flag (#5181)
* Add SUPPORT_PLAY flag

* Add SUPPPORT_PLAY to existing media players

* Leave usage of new flag to device devs
2017-01-09 01:09:30 +01:00
Craig J. Ward
a3971d7ad1 Insteon local (#5088)
* platform set-up begin components

* lights seem to be getting set up properly, not sure why they aren't being added...

* typo

* Dependencies line

* toggle working

* toggle working

* added the switch to insteon_local

First commit hope to test tonight or in the morning

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* move dependency declaration before import?

* Move dependencies in Switch

* Update insteon_local.py

* wait for response

* switched the while to an if

switched the while 'cmd2' not in resp: to an if 'cmd2' not in resp: this seems to have the updater working

* Switched the while sleep loop to an if

switched the wile cmd2 not ins resp to be if cmd2 not in resp seems to be working.

* Update insteon_local.py

* import statement

Updated the import statement to import the instance of the insteon_local component not the hub Instance.

* updated import and the device assignment

update the import to import the instance of the insteon_local component not the hub.

* more changes to support the import change

* more changes to support the import change

* change to hass.data and add loop logic

* &&

* Update insteon_local.py

* Update insteon_local.py

* logic fixes and throttle

* reduce polling time

* brightness support

* import util

* hound fixes

* requirements file

* more hound fixes

* newline

* newline weirdness

* lint fixes

* more lint fixes

* switch state

* Update insteon_local.py

* log cmd2 for debugging

* assume success

* remove check for none

* fix comments

* fix comments again

* fix comments, add fixed version of lib, add support for timeout, add support for port, handle invalid login and connection problems

* fix logging exception

* fix hounceci-bot errors

* fix hounceci-bot errors

* requirements fix

* unique-id changes

* make dimmer off use saved ramp rate

* configurator working for lights

* configurator working for switches?

* configurator working for switches?

* include model names and fix lint errors

* lint fix

* fix exception order

* lint fixes

* fix lint errors

* update to use insteon local 0.38

* fix device id

* move status check to library

* move status check to library

* add SKU to setup

* lint fixes

* requirements

* linting
2017-01-09 00:33:35 +01:00
markferry
2b14d407c0 onkyo: fix selecting sources with only one name (#5221) 2017-01-08 23:59:26 +01:00
happyleavesaoc
81f988cf9e date fix (#5227) 2017-01-08 23:50:42 +01:00
happyleavesaoc
f643149d24 usps sensor: better delivery handling (#5202)
* better delivery handling

* bump dep version
2017-01-08 14:35:14 +01:00
dasos
fd50201407 Squeezebox JSON-RPC (#5084)
* Refactor of Squeezebox connection code

* Refactor of Squeezebox connection code

* Typos

* Make Python 3.4 friendly

* Addressing comments

* Improving docstring

* Using discovered port

* Style better

* Accept new disco object

* Revert "Accept new disco object"

* Make it obvious that port isn't discovered yet

* Flake8. ;)
2017-01-08 14:32:15 +01:00
Jan Losinski
469aad5fc8 Add teardown method to pilight tests (#5195)
* Add teardown method to pilight tests

This is necessary to stop the HomeAssistant instance that was started
in the setUp method. Without this there happen random test failures.

This is necessary to stabilize the tests for PR #5045.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Update test_pilight.py
2017-01-08 14:23:01 +01:00
Pascal Vizeli
a65388e778 Bugfix segfault with new helper track_time_interval (#5222)
* Bugfix sigfault with new helper track_time_interval

* Add none init also to sunrise/sunset for consistance
2017-01-08 14:06:15 +01:00
Lewis Juggins
41ef6228be [device_tracker] Use home zone GPS coordinates for router based devices. (#4852) 2017-01-07 23:09:07 +02:00
Pascal Vizeli
ca4a857532 Improve aiohttp default clientsession close with connector (#5208) 2017-01-07 21:11:19 +01:00
Lewis Juggins
81922b88a2 [calendar] Fix scan interval (#5205) 2017-01-07 11:24:25 +01:00
Paulus Schoutsen
aa1e4c564c Fix tests closing properly (#5203) 2017-01-07 01:47:25 +01:00
Pascal Vizeli
2b991e2f32 [new] component rest_command (#5055)
* New component rest_command

* add unittests

* change handling like other command

* change unittest

* address @balloob comments
2017-01-06 23:42:53 +01:00
Pascal Vizeli
1719d88602 Bugfix default values to timedelta (#5193)
* Bugfix default values to timedelta

* fix unittests
2017-01-06 00:16:12 +01:00
Jared J
c959637ebe Add Yeelight auto discovery, color temp (#5145)
* Add Yeelight auto discovery, color temp

* Fixing linting issues
2017-01-05 23:39:28 +01:00
Ryan Kraus
db623040a4 Re-enabled Weather Sensors for the ISY component. (#5148)
* Re-enabled Weather Sensors for the ISY component.

As the ISY component had been updated to support newer components and
have better UOM support, the support for ISY’s weather module was
dropped. This adds that support back into Home Assistant.

* Cleanup of the ISY Weather support.

Cleaned up the for loops used to generate nodes representing weather
data from the ISY. Moved the weather_node named tuple to the top of the
file so that it can be more easily identified.

* Update isy994.py
2017-01-05 23:33:52 +01:00
Michaël Arnauts
ba29ba0fc3 Universal media_player returns ATTR_MEDIA_POSITION and ATTR_MEDIA_POSITION_UPDATED_AT from it's active child now. (#5184) 2017-01-05 23:15:26 +01:00
Adam Mills
93d462b010 Fix for async device_tracker (#5192) 2017-01-05 23:10:43 +01:00
Pascal Vizeli
50a8ec7335 Bugfix aiohttp connector pool close on shutdown (#5190)
* Bugfix aiohttp connector pool close on shutdown

* fix circular import

* remove lint disable
2017-01-05 23:09:04 +01:00
Nick Touran
a36ca62445 Support longer-than-60-second scan_interval and interval_seconds (#5147)
* Update scan_interval and interval_seconds max to 1 day vs. 60 seconds

* Format fixes

* Add docstring on unittest.

* Added and implemented new async_track_time_interval helper.

* Format fixes, removed unused import.

* Undid whoops on unsub_polling.

* Updated unit tests for scan_interval.

* Added unit test for track_time_interval.

* Allow other forms of time interval input for scan_interval and interval_seconds
2017-01-05 23:05:16 +01:00
Paulus Schoutsen
f88b5a9c5e Update frontend 2017-01-05 22:28:48 +01:00
webworxshop
51446e0772 Add support for customised Kankun SP3 Wifi switches. (#5089)
* Add support for customised Kankun SP3 Wifi switches.

* Add config validation for Kankun SP3 wifi switch.

* Update kankun.py
2017-01-05 22:16:00 +01:00
MrMep
95ddef31fe Update keyboard_remote.py (#5112)
* Update keyboard_remote.py

I changed os.path.isFile() to os.path.exists: as far as I know isFile doesn't work with device files. At least on my Ubuntu it wasn't working.

Then I added some error control in case the keyboard disconnects: with bluetooth keyboards this happen often due to battery saving. Now it reconnects automatically when the keyboard wakes up.

We could fire an event to hass when the keyboard connects-disconnects, maybe I'll do this later.

We should also manage errors due to permissions problems on the device file, or at least give some info in the docs about how to allow HA to grab control over an system input file.

I'm sorry if my coding isn't up to some standard practice I'm not aware of: I'm new to HA and to python itself, I'm just trying to be of help.
Gianluca

* Update keyboard_remote.py

I changed some other few things.

Not the component gets loaded even if the keyboard is disconnected. When it connects, it starts to fire events when keys are pressed.

I also added a sleep(0.1) that reduces a lot the load on the CPU, but without many consequences on key pressed detection.
2017-01-05 22:03:05 +01:00
Adam Mills
276a29c8f4 Convert Kodi platform to async (#5167)
* Convert Kodi platform to async

* Remove unnecessary async_update_ha_state
2017-01-05 21:01:13 +01:00
Martin Vacula
6b682d0d81 Beaglebone black GPIO control by switch v2 (#4908)
* Added support for BBB GPIO

* Requirements updated

* unnecessary pylint statement removed

* Changed according arduino switch

* typo corrected

* Hound errors solved

* lint error

* Update bbb_gpio.py
2017-01-05 20:53:48 +01:00
Johann Kellerman
cbda516af9 ensure_list validator - Allow None to return an empty list (#5133) 2017-01-05 21:33:22 +02:00
Michaël Arnauts
cb85128304 Speeds up lint and test in docker by keeping the cache between invocations. (#5177)
* Add a volume to store the tox cache on the host. This gives quite some speed boost when running lint_docker and test_docker.

* Only map .tox directory for cache.
2017-01-05 09:45:14 +01:00
happyleavesaoc
74aa8194d7 USPS sensor (#5180)
* usps sensor

* Update usps.py
2017-01-05 09:44:15 +01:00
Sander de Leeuw
497a1c84b5 Fixed invalid response when sending a test-request from Locative iOS app (#5179) 2017-01-05 10:05:39 +02:00
Daniel Høyer Iversen
2f907696f3 [WIP] Spread the traffic to yr.no servers and remove yr.no from the default… (#5171)
* Spread the traffic to yr.no servers and remove yr.no from the default config

* use unique address for yr.no

* update yr tests

* Wait 10 min extra before requesting new data

* Update config.py

* Update yr.py
2017-01-04 23:20:30 +01:00
Brent Hughes
4ef7e08553 Rewrite influxdb metrics to be more consistent (#4791)
* Updated to make all metrics consistent

* Updated existing test for new format

* Updated checks on lists and dictionarys
2017-01-04 22:36:54 +01:00
Matthew Garrett
ff0788324c Add support for Tikteck Bluetooth bulbs (#4843)
* Add support for Tikteck Bluetooth bulbs

Adds support for the Tikteck RGBW BLE bulbs. These don't provide "true" RGBW
support - at a certain point in RGB space, the white LEDs turn on. Each bulb
has a specific key that needs to be extracted from the Android app.

* Update tikteck.py
2017-01-04 22:31:31 +01:00
doudz
9f65b8fef5 add offline tts using pico (#5005)
* add offline tts using pico

* Update picotts.py

* Update picotts.py

* Update picotts.py

* Update picotts.py

* Update picotts.py

* Update picotts.py

* Update picotts.py

* Update .coveragerc
2017-01-04 22:05:41 +01:00
Daniel Høyer Iversen
67ab1f69d8 user agent header (#5172)
* user agent in header

* update user agent info

* Use user-agent from lib
2017-01-04 21:15:50 +01:00
Michaël Arnauts
5e8e2a8312 Ping device tracker (#5176)
* Ping device tracker

* Style fixes
2017-01-04 21:06:09 +01:00
Dan Smith
6ed3c69604 Bump uvcclient to 0.10.0 (#5175)
This brings fixes for newer versions of unifi-video and a few
fixes.
2017-01-04 19:55:16 +01:00
Gopal
d09dcc4b03 Timeout and Constant added 2017-01-04 18:23:29 +05:30
Florian Holzapfel
3f7a629079 fix #5157 (#5173) 2017-01-04 13:16:52 +01:00
Oliver
8e61fab579 Pushed to another version of denonavr library with fixes for AVR-nonX devices (#5156) 2017-01-04 00:02:44 +01:00
Magnus Ihse Bursie
d9614cff46 Improve async/multithreaded behavior of tellstick code (#4989)
* Refactor tellstick code for increased readability. Especially highlight if "device" is a telldus core device or a HA entity.

* Refactor Tellstick object model for increased clarity.

* Update comments. Unify better with sensors. Fix typo bug. Add debug logging.

* Refactor tellstick code for increased readability. Especially highlight if "device" is a telldus core device or a HA entity.

* Refactor Tellstick object model for increased clarity.

* Update comments. Unify better with sensors. Fix typo bug. Add debug logging.

* Fix lint issues.

* Remove global variable according to hint from balloob.

* Better async/threading behavior for tellstick.

* Fix lint/style checks.

* Use hass.async_add_job
2017-01-03 23:54:11 +01:00
Adam Mills
2a7a419ff3 Async support in media player component (#4995)
* Async support in media player component

* Removed redundant new tests

* Update to new 'reduce coroutine' standards

* Remove extra create_task
2017-01-03 23:51:23 +01:00
Nathan Henrie
b78cf4772d Add ability to set rpi_rf tx_repeats attribute (#5070)
* Add ability to set rpi_rf `tx_repeats` attribute

Uses the current `rpi_rf` default of 10
Closes home-assistant/home-assistant#5069

* Use `signal_repetitions` instead of `repeats` for consistency
2017-01-03 23:46:30 +01:00
Daniel Høyer Iversen
ebfb2c9b26 Broadlink fix (#5065)
* Broadlink fix

* style fix

* style fix

* typo

* restructure

* Update broadlink.py

* Update broadlink.py

* Add support for more devices

* fix library version

* fix library version

* fix library version

* fix library version

* fix library version

* Update broadlink.py

* lib version

* remove lower

* remove lower

* refactor

* refactor

* refactor

* authorization

* authorization

* refactor

* lib version

* lib version
2017-01-03 23:45:11 +01:00
Thibault Cohen
e17ce4f374 Improve Sharp Aquos TV component - Fixes #4973 (#5108) 2017-01-03 23:38:21 +01:00
Giannie
67b74abf8d Allow selection of bluetooth device to use (#5104)
Adding the device_id key to the bevice tracker component allows selecting the bluetooth device to use in case the default device does not have BLE Capabilities
2017-01-03 23:34:51 +01:00
Brandon Weeks
c14a5fa7c1 Upgrade samsungctl to 0.6.0 (#5126) 2017-01-03 23:28:23 +01:00
Matthew Garrett
2970196f61 Add support for limiting which entities are recorded (#4733) 2017-01-04 00:19:28 +02:00
Johann Kellerman
4692ea85b7 [mqtt] Embedded MQTT server fix (#5132)
* Embedded MQTT server fix and schema

* feedback
2017-01-03 23:17:56 +01:00
Adam Mills
018d786f36 Kodi cleanup and config update (#5098) 2017-01-03 21:41:42 +01:00
Mike Hennessy
254eb4e88a Change zeroconf to use local ip not base_url (#5151)
Rather than rely on base_url being unconfigured and therefore be
set as the host ip in the api component, get the local ip directly.

Use server port from the http component instead of the api component
where it will be None if base_url is set.

Change the exception catch meant to check for ipv4 vs ipv6 to wrap the
address encoding only instead of the zeroconf service info declaration.
2017-01-03 21:39:42 +01:00
Paulus Schoutsen
9eed03108f Run tests on Python 3.6 (#5162)
* Run tests on Python 3.6

* Fix dsmr test

* Fix async util tests

* Fix rest sensor test
2017-01-03 21:33:48 +01:00
eieste
fdd3fa7d80 Apple TouchBar icon Support (mask-icon) (#5159)
* Update index.html

Add Apple TouchBar icon Color Support

* Add svg icon

* Change path to SVG icon

* Rename mask-icon.svg to home-assistant-icon.svg

* Remove useless whitespace

* Update index.html
2017-01-03 21:32:40 +01:00
Daniel Høyer Iversen
6ec500f2e7 issue #5101 (#5161) 2017-01-03 21:13:02 +01:00
andrey-git
a0a9f26312 Keep previous brightness of dimming zwave light upon turning it on/off (#5160)
* Keep previous brightness of dimming zwave light upon turning it on/off

* Fix initialization

* Use value 255 to set dimmer to previous value
2017-01-03 20:31:44 +01:00
Adam Mills
21f59a0732 Merge pull request #5150 from Zac-HD/fix-contrib-link
Fix link to pull request advice for contributors
2017-01-03 14:24:53 -05:00
Brandon Weeks
52f6fe3e06 Add version test for monkey_patch_asyncio() (#5127)
* Add version test for monkey_patch_asyncio()

* Update __main__.py
2017-01-03 17:47:33 +01:00
Dan Smith
f71027a9c7 Nx584 maint (#5149)
* Update nx584 requirement to 0.4

There have been a few bug fixes to nx584 since 0.2, so this just updates
our requirement to pull in the newer version.

* Fix nx584 if no partitions are found

If we succeed in our connection to the panel but find no
configured partitions, then we will fall through to the bypass
probe and fail because 'zones' is never initialized. This fixes
both exception paths and adds a test that would poke it.
2017-01-03 08:19:33 +01:00
Zac-HD
328ff6027b Fix link to pull request advice for contributors 2017-01-03 14:26:24 +11:00
Michaël Arnauts
c864ea60c9 Improve development workflow in docker (#5079)
* Allow bower install of frontend components as root. Needed for frontend development in docker since everything runs as root in the docker image.

* Improve development workflow in docker

* Use LANG=C.UTF-8 in tox. Fixes installation of libraries with UTF-8 in it's readme.

* Install mysqlclient psycopg2 uvloop after requirements_all.txt again, but with a --no-cache-dir this time. Allows bootstrap_frontend to be executed in a different path like the other scripts.
2017-01-02 22:04:09 +01:00
Pascal Vizeli
b2371c6614 Update device_traker for async platforms (#5102)
Async DeviceScanner object, migrate to async, cleanups
2017-01-02 21:50:42 +02:00
John Arild Berentsen
9c6a985c56 [zwave]Use schedule_ha_state and add debug message (#5143)
* Use schedule_ha_state and add debug message

* Logger not defined
2017-01-02 18:55:56 +01:00
John Arild Berentsen
5c006cd2d2 Prevent Homeassistant to create a segfault with OZW (#5085) 2017-01-02 18:53:46 +01:00
andrey-git
a7cc7ce476 Clean up DEFAULT_DEBUG constant in zwave (#5138)
Nice 👍
2017-01-02 18:45:44 +01:00
John Arild Berentsen
23f16bb68f Catch RuntimeError (#5134) 2017-01-02 18:45:10 +01:00
Greg Dowling
8a463ef7a4 Merge pull request #5142 from home-assistant/bump_loop_energy
Bump pyloopenergy to catch SSL exception.
2017-01-02 15:02:08 +00:00
pavoni
9af1e0ccf3 Bump pyloopenergy to catch SSL exception. 2017-01-02 14:15:38 +00:00
jtscott
a8a98f2585 [climate] Fix typo in services.yaml (#5136) 2017-01-02 10:17:52 +00:00
andrey-git
7fbf68df35 Add print_config_parameter service to Z-Wave (#5121)
* Add print_config_param service to z-wave

* Add print_config_parameter service to z-wave

* Add print_config_parameter service to z-wave

* Fix typos

* Fix typos

* Fix typo
2017-01-01 21:10:45 +01:00
Mark
01be70cda9 Philio 3-in-1 Gen 4 zwave sensor needs the no-off-event workaround. (#5120) 2017-01-01 20:40:34 +01:00
Gopal
e89aa6b2d6 File added to coveragerc 2016-12-31 10:11:30 +05:30
Gopal
227fb29cab Facebook notify updated 2016-12-31 09:59:44 +05:30
Felix
d04ee66669 Fixes moldindicator sensor after switch to asyncio (#5038) 2016-12-30 11:16:34 +01:00
Thibault Cohen
9e66755baf Fix proximity issue (#5109) 2016-12-30 08:51:37 +01:00
Pascal Vizeli
0ecd185f0d Bugfix close async log handler (#5086) 2016-12-29 17:27:58 +01:00
Jesse Newland
a2f17cccbb Replace dots in Alexa built-in intent slots w/ underscores (#5092)
* Replace dots in built-in intent slots w/ underscores

* Add a built-in intent test
2016-12-29 17:26:23 +01:00
Gopal
6892048de0 Facebook Notify Started 2016-12-29 18:19:11 +05:30
Johann Kellerman
aaff8d8602 Qwikswitch library PyPi update (#5099) 2016-12-29 11:08:11 +02:00
Brent Hughes
cf714f42df Added Ledenet protocol support to flux_led (#5097)
* Added Ledenet protocol support to flux_led

* Made the protocol config lowercase
2016-12-28 23:21:29 -06:00
Johann Kellerman
f0b1874d2d Fix up docstring for tests (#5090) 2016-12-28 20:04:59 +02:00
Magnus Ihse Bursie
98efbbc129 Fix typo. (#5087) 2016-12-28 08:12:10 +01:00
Magnus Ihse Bursie
d8ff22870a Include flake8-docstrings to test requirements to better mimic tox -e lint (#4926) 2016-12-28 07:26:27 +02:00
John Arild Berentsen
fee47f35b9 Improvements to zwave lock platform (#5066) 2016-12-27 22:08:35 +01:00
Brian Torres-Gil
7b6503c017 Ecobee service fix and new 'resume program' service (#4510)
* ecobee_set_fan_min_on_time: fix issue using 'entity_id' field and add service field help text

* climate.ecobee: add 'resume_program' service

* Add default value for resume_all and correct entity_id field name reference
2016-12-27 21:56:26 +01:00
Michaël Arnauts
c77b4a4806 Update icons from materialdesignicons.com (#5081) 2016-12-27 21:36:07 +01:00
andrey-git
4728fa8da6 Allow to specify TTS language in the service call. (#5047)
* Allow to specify TTS language in the service call.

* Allow to specify TTS language in the service call.

* Respect 79 char limit

* Fix "Too many blank lines"

* Fix "Too many blank lines"

* Fix "Too many blank lines"

* Change language to be optional parameter of *get_tts_audio

* Change language to be optional parameter of *get_tts_audio

* Respect 79 char limit

* Don't pass "None

* Use default of "None" for TTS language

* Use default of "None" for TTS language

* Don't pass "None"

* Change TTS cache key to be hash_lang_engine

* Change language from demo to en

* Fix wrong replace
2016-12-27 17:01:22 +01:00
Daniel Høyer Iversen
68865ec27b upgrade miflora (#5075)
Add an optional extended description…
2016-12-27 09:24:05 +01:00
Fabian Affolter
c5f70e8be3 Upgrade speedtest-cli to 1.0.1 (#5073) 2016-12-26 16:41:18 +01:00
Fabian Affolter
ec89accd29 Upgrade psutil to 5.0.1 (#5072) 2016-12-26 16:31:44 +01:00
Fabian Affolter
ac1063266c Upgrade pyowm to 2.6.0 (#5071) 2016-12-26 16:31:26 +01:00
Hydreliox
5b619a94ad Add sensor for International Space Station (#4968)
* Add sensor for International Space Station

* Change two sensors to one with attributes.

* Fix due to comments in HA PR. Thanks !

* Update Requirement
2016-12-26 16:02:11 +01:00
Pascal Vizeli
244cdf43d0 Async reduce coro (#5001)
* Asyncio coro reduce

* Replace comments
2016-12-26 14:10:23 +01:00
Pascal Vizeli
22d1bf0acd Async migrate climate (#5026)
* Async migrate climate

* Change update handling
2016-12-26 14:09:15 +01:00
Johan Bloemberg
43e5d28643 Fix and test for prefixed MAC addresses. (#5052)
* Fix and test for prefixed MAC addresses.

* Fix style.

* Don't commit old code.

* Fix style.
2016-12-26 14:02:12 +01:00
abmantis
e5dfcf7310 Emulated hue: add support for cover open/close (#5057)
* add support for cover open/close

* fix action compare; reduce line width
2016-12-26 14:00:43 +01:00
abmantis
1c1b04718f emulated_hue: fix alexa "device not responding" (#5058)
* emulated_hue: fix alexa "device not responding"

we need to set the brightness to 100 for devices that only turn on

* emulated_hue: dont override brightness for scenes/scripts

* emulated_hue: python and semi-colons

* emulated_hue: fix output brightness level
2016-12-26 13:58:32 +01:00
Paulus Schoutsen
ce24ef0c20 Merge branch 'master' into dev 2016-12-23 07:08:33 +01:00
Paulus Schoutsen
308d71c448 Merge pull request #5032 from home-assistant/release-0-35-3
0.35.3
2016-12-23 07:05:54 +01:00
Josh Nichols
203c1cfc96 Nest fixes (#5011)
* Updated Nest API to have logical names

* Fix NoneType not having replace method in NestSensor constructor

* Move name setting to constructor, in case zone.name causes IO.

* normalize is_online to online

* Updated python-nest API

* push is_* helpers down to python-nest, and use inheritence to implement rather than checking class name

* Update python-nest
2016-12-22 20:25:43 +01:00
Josh Nichols
6c50f53696 Nest fixes (#5011)
* Updated Nest API to have logical names

* Fix NoneType not having replace method in NestSensor constructor

* Move name setting to constructor, in case zone.name causes IO.

* normalize is_online to online

* Updated python-nest API

* push is_* helpers down to python-nest, and use inheritence to implement rather than checking class name

* Update python-nest
2016-12-22 20:22:07 +01:00
John Mihalic
5e1e5992af Update pyHik requirement version (#5040) 2016-12-22 18:45:05 +01:00
Pascal Vizeli
9bf4a53fbb Bugfix async log handle re-close bug (#5034)
* Bugfix async log handle re-close bug

* Check on running thread on async_close

* Fix now on right place
2016-12-22 16:09:16 +01:00
Pascal Vizeli
334b3b8636 Bugfix async log handle re-close bug (#5034)
* Bugfix async log handle re-close bug

* Check on running thread on async_close

* Fix now on right place
2016-12-22 16:08:01 +01:00
Pascal Vizeli
f18a88c2d4 Bugfix create a task from a task in component update (#5033) 2016-12-21 15:12:26 +01:00
Pascal Vizeli
9a16054867 Bugfix create a task from a task in component update (#5033) 2016-12-21 15:11:14 +01:00
Pascal Vizeli
35b4da0aa2 Bugfix voicerss post api (#5021)
* Bugfix voicerss post api

* fix unittest

* Add cache to service description
2016-12-21 11:49:56 +01:00
pvizeli
61fc4ca8fe Version bump to 0.35.3 2016-12-21 11:47:38 +01:00
Pascal Vizeli
4c9347eb2a Fix spell media_player service (#5030)
Add an optional extended description…
2016-12-21 11:39:59 +01:00
Pascal Vizeli
25469dd8ee Bugfix voicerss post api (#5021)
* Bugfix voicerss post api

* fix unittest

* Add cache to service description
2016-12-21 10:22:12 +01:00
Johann Kellerman
b170f4c399 Spread seconds (#5025) 2016-12-21 08:42:23 +01:00
Johann Kellerman
a8b3900913 device_tracker (#5023)
2
2016-12-21 08:40:44 +01:00
Daniel Høyer Iversen
39bdd5310b Merge pull request #5022 from home-assistant/broadlink_bug_fix
Solve some bugs in the broadlink switch
2016-12-21 05:24:16 +01:00
Daniel Høyer Iversen
133c03ee57 style fix 2016-12-20 21:16:18 +01:00
Daniel Høyer Iversen
f224ee7229 Solve some bugs in the bradlink switch 2016-12-20 21:05:54 +01:00
Petr Vraník
1aea3e0d51 script/lint only on python files (#5018) 2016-12-20 13:06:53 +02:00
Daniel Perna
877efac630 Add missing support for HMIP-PSM (#5013) 2016-12-20 11:39:29 +01:00
Hugo Dupras
ee6fb93018 Hotfix for Netatmo Camera (#4998)
Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>
2016-12-19 07:47:38 -08:00
Daniel Høyer Iversen
08591aacc9 Merge pull request #5000 from home-assistant/rfxtrx_lib_0.14
rfxtrx lib upgrade
2016-12-19 11:54:58 +01:00
Daniel Hoyer Iversen
2cb67eca46 rfxtrx lib upgrade 2016-12-19 11:35:00 +01:00
Daniel Høyer Iversen
00b80d4fe1 Support for broadlink sp (#4961)
* initial support for broadlink sp

* style fix

* style

* bug fix
2016-12-18 17:59:08 -08:00
Paulus Schoutsen
53dde0e4e1 Unbreak dev 2016-12-18 17:35:42 -08:00
Paulus Schoutsen
4778ec4f94 Merge branch 'master' into dev 2016-12-18 15:02:02 -08:00
Paulus Schoutsen
3ea984ca25 Version bump to 0.35.2 2016-12-18 15:00:37 -08:00
Paulus Schoutsen
1258c4c680 Base url: Fix external port different from internal port (#4990)
* Base url: Fix external port different from internal port

* Add base_url example to new config
2016-12-18 15:00:16 -08:00
Paulus Schoutsen
ed0d14c902 Base url: Fix external port different from internal port (#4990)
* Base url: Fix external port different from internal port

* Add base_url example to new config
2016-12-18 14:59:45 -08:00
Paulus Schoutsen
75dd391118 Merge pull request #4988 from home-assistant/release-0-35-1
0.35.1
2016-12-18 14:11:10 -08:00
Paulus Schoutsen
76a9eba744 Version bump to 0.35.1 2016-12-18 13:00:35 -08:00
Paulus Schoutsen
31fe1d28e8 Gracefully exit with async logger (#4965)
* Gracefully exit with async logger

* Lint
2016-12-18 13:00:21 -08:00
Pascal Vizeli
a4a38c8a00 Bugfix TTS clear cache (#4974)
* Bugfix TTS base url with certificate

* fix lint

* remove base_url stuff fix only clear_cache stuff

* cleanup
2016-12-18 13:00:21 -08:00
Paulus Schoutsen
3b74cc606e Allow setting base url (#4985) 2016-12-18 13:00:21 -08:00
Pascal Vizeli
b750319de4 Bugfix wait in automation (#4984) 2016-12-18 13:00:21 -08:00
andrey-git
744d00a36e Fix non-radio Sonos album-art by using track_info['album_art'] before checking other options. (#4958)
* Fix Sonos album art for non-radio streams

* Revert "Fix Sonos album art for non-radio streams"

This reverts commit d71502d18f.

* Fix Sonos album art for non-radio streams

* Move art existance check into _format_media_image_url
2016-12-18 12:58:59 -08:00
Pascal Vizeli
a6d995e394 Bugfix wait in automation (#4984) 2016-12-18 12:57:31 -08:00
Paulus Schoutsen
f8af6e7863 Allow setting base url (#4985) 2016-12-18 12:56:07 -08:00
Pascal Vizeli
fec33347fb Bugfix TTS clear cache (#4974)
* Bugfix TTS base url with certificate

* fix lint

* remove base_url stuff fix only clear_cache stuff

* cleanup
2016-12-18 11:26:40 -08:00
Nick Sabinske
44eaca5985 Add support for the Sonarr URL Base setting (#4975)
* Add support for the Sonarr URL Base setting

For those of us who have sonarr hidden behind reverse proxy under a path, we need to be able to pass the path

Adds urlbase: XXX to the sonarr yaml... Match it to what you have set in Sonarr (Settings>General>URL Base)

* Fix line lengths

* Fix trailing white space caused by last change

* Removing use of len()
2016-12-18 10:05:05 -08:00
Thibault Cohen
18cf6f6f99 Add HydroQuebec support (#4840) 2016-12-18 12:23:10 +01:00
Fabian Affolter
9f298a92f4 Remove and update docstrings (#4969) 2016-12-18 11:39:33 +01:00
Paulus Schoutsen
01e6bd2c92 Gracefully exit with async logger (#4965)
* Gracefully exit with async logger

* Lint
2016-12-18 00:14:59 -08:00
Paulus Schoutsen
a76684f203 Version bump to 0.36.0.dev0 2016-12-17 14:07:44 -08:00
Paulus Schoutsen
9bc16157af Merge pull request #4875 from home-assistant/dev
0.35
2016-12-17 14:07:28 -08:00
Paulus Schoutsen
35d7f2b8bb Version bump to 0.35.0 2016-12-17 14:07:13 -08:00
Georgi Kirichkov
7390f82e1f Updates TP-Link dependency (#4914)
* Updates TP-Link switches dependent module

Refactors code to use the new module API

* Set TP-Link Switch name from the device settings

If no name has been set in the configuration file the name set on the device will be used

* Removes default name for TP-Link switch

Fallback to device alias now works properly

* Removes logging

* Updates comment to denote support for HS200 switch
2016-12-17 13:49:43 -08:00
Paulus Schoutsen
cc9e5de503 Only report slowness warning once per entity (#4962) 2016-12-17 13:00:08 -08:00
Pascal Vizeli
50c8224365 Bugfix async log handler (#4954)
* Bugfix async log handler

* fix boostrap test

* Use hass.data for store handler and cleanup on async_stop

* Update bootstrap.py
2016-12-17 12:21:52 -08:00
Erik Eriksson
b08b376aa7 eliqonline lib upgrade (#4948) 2016-12-17 12:14:04 -08:00
Fabian Affolter
60ef0153a2 Upgrade Sphinx to 1.5.1 (#4957) 2016-12-17 19:30:54 +01:00
Fabian Affolter
44c4b25f2b Upgrade astral to 1.3.3 (#4956) 2016-12-17 19:29:36 +01:00
Fabian Affolter
4abcaea4b7 Upgrade python-telegram-bot to 5.3.0 (#4955) 2016-12-17 19:29:24 +01:00
Albert Lee
831cad4220 Use Wake-on-LAN to turn on LG webOS TV (#4808) 2016-12-16 23:24:35 -08:00
Daniel Perna
6c524594c1 Fixing issue #4899 (#4947) 2016-12-16 22:34:13 -08:00
Paulus Schoutsen
78f6cfd1eb Update coverage 2016-12-16 22:03:45 -08:00
Pascal Vizeli
6d6abab358 Async logging file handler (#4901)
* Async logging file handler

* add time rotation handle

* new layout

* address paulus comments

* fix lint
2016-12-16 15:51:06 -08:00
Ashura
326cc83a17 [media_player.braviatv] Add turn on capabilities. (#4938) 2016-12-16 17:41:31 +00:00
Pascal Vizeli
8358ab56ea Bugfix asyncio wait (#4946) 2016-12-16 08:36:50 -08:00
Albert Lee
32dc518971 Use Wake-on-LAN to turn on Panasonic Viera TV (#4809) 2016-12-16 08:16:46 -08:00
Paulus Schoutsen
b318a033bb Cast fix (#4939)
* Update frontend

* Fix exception on cast startup
2016-12-16 00:10:56 -08:00
Pascal Vizeli
a0b2105ea0 Add voicerss for TTS (#4916)
* Add voicerss for TTS

* add unittests

* fix tests

* fix status bug in google/voicerss

* remove ssl
2016-12-16 00:10:48 -08:00
Nolan Gilley
9f9b87692a add manual option to prevent scheduled tests. (#4906) 2016-12-15 22:55:51 -08:00
Roi Dayan
5c4f04e9fc Fix webostv component to accept any custom sources (#4915)
Updated the schema check to accept any string
Search custom sources in app title and app id
The makes the short list redundant and thus removed
Tested by adding livetv, netflix, youtube, makovod and others
This is also compatible with the list that was supported till now
so current users won't see any difference.

Signed-off-by: Roi Dayan <roi.dayan@gmail.com>
2016-12-15 22:51:08 -08:00
Erik Eriksson
757f6278eb initialize self._last_brightness (#4917) 2016-12-15 22:35:53 -08:00
Adam Mills
b9dcc2777b Setup DarkSky platform when offline during init (#4919)
* Setup DarkSky platform when offline during init

* Fail setup_platform if fetch was unsuccessful
2016-12-15 22:27:37 -08:00
Magnus Ihse Bursie
103fffa0f4 Add support for new netdisco detection of Samsung Smart TV. (#4925) 2016-12-15 22:20:00 -08:00
Fabian Affolter
7748867732 Avoid TypeError for state (#4897) 2016-12-15 22:14:59 -08:00
Paulus Schoutsen
02517ae5ec Fix synologydsm (#4895) 2016-12-15 22:13:38 -08:00
Fabian Affolter
2a31bb48c6 Clean-up (#4894) 2016-12-15 22:12:33 -08:00
Magas
5b70ada7b4 Panasonic viera fix (#4888)
* Removed return False so the Panasonic Viera TV can be added even if it doesn't connect

* Removed return False so the Panasonic Viera TV can be added even if it doesn't connect

* Removed return False so the Panasonic Viera TV can be added even if it doesn't connect

* Remove try/except to connect to the TV

* Update panasonic_viera.py

* Update panasonic_viera.py
2016-12-15 22:11:58 -08:00
Albert Lee
7b45cf8e59 Expose media volume as emulated Hue brightness (#4869)
* Allow virtual Hue bridge to set volume level of media_player entities
* Show correct states in all lights view
2016-12-15 21:47:23 -08:00
Daniel Høyer Iversen
394d53e748 Broadlink sensor and switch (#4834)
* Broadlink sensor and switch

* broadlink logging

* Use async

* style

* style
2016-12-15 21:42:00 -08:00
Hugo Dupras
c125c4af4f Fix for GTFS sensor (#4828)
* Fix for GTFS sensor

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* GTFS fix

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>
2016-12-15 21:40:33 -08:00
Joe Rocklin
f90b89bc74 Add nest hvac state (#4810)
* Add basic property details for Nest hvac_state

* Add the hvac_state sensor

* Update requirements and remove trailing whitespace

Clean up the multiline docstring

Adding a space between summary and description

* Removing the hvac_state as a property on the nest climate

* Update nest.py
2016-12-15 21:39:59 -08:00
Daniel Høyer Iversen
ceac9eab94 Bug fix for #4903 (#4927) 2016-12-15 21:35:47 -08:00
joopert
7bb0abdf09 kodi fanart fix basic auth (#4930) 2016-12-15 21:35:01 -08:00
Pascal Vizeli
1d60760e21 Protect add_job (#4932) 2016-12-15 21:30:09 -08:00
Pascal Vizeli
43d18daebd Homematic faster update with async (#4929) 2016-12-15 21:26:13 +01:00
Lewis Juggins
1a7895b1d8 [media_player.sonos] Bugfix, initalise source_name. (#4911) 2016-12-15 11:46:18 +00:00
Daniel Høyer Iversen
c2f31bbb38 Merge pull request #4924 from home-assistant/flux_lib
Update flux led lib
2016-12-15 08:26:24 +01:00
Daniel Høyer Iversen
a7e75dd01e Merge pull request #4907 from home-assistant/rpi_camera
Bug in rpi_camera
2016-12-15 08:09:23 +01:00
Daniel Hoyer Iversen
58ea3c25df Update flux led lib 2016-12-15 07:58:58 +01:00
Pascal Vizeli
6d2de67620 TTS add google language list for config check (#4912)
* Add config check for language

* update default

* move language from component to platform

* fix lint
2016-12-14 22:32:20 +01:00
Valentin Alexeev
a359d21799 [media_player.sonos] Source selection from favorites (#4804) 2016-12-14 18:05:03 +00:00
Daniel Høyer Iversen
be552a59c9 Bug in rpi_camera 2016-12-14 18:45:05 +01:00
Paulus Schoutsen
832f9737a8 Fix hue groups on older hubs (#4884) 2016-12-13 23:46:27 -08:00
Paulus Schoutsen
da6bdf275e Update frontend 2016-12-13 23:30:08 -08:00
Marcelo Moreira de Mello
7ca025f653 Fixes issues #4844 to avoid traceback when self.rest.data is None (#4886)
6-12-09 18:12:30 homeassistant.core: Error doing job: Task exception was never retrieved
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/tasks.py", line 237, in _step
    result = next(coro)
  File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py", line 386, in _update_entity_states
    yield from update_coro
  File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/helpers/entity.py", line 240, in async_update_ha_state
    self._attr_setter('entity_picture', str, ATTR_ENTITY_PICTURE, attr)
  File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/helpers/entity.py", line 307, in _attr_setter
    value = getattr(self, name)
  File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/components/sensor/wunderground.py", line 176, in entity_picture
    url = self.rest.data['icon_url']
TypeError: 'NoneType' object is not subscriptable
2016-12-13 23:01:14 -08:00
Erik Eriksson
570cfc60c5 bugfix: is_on is a property (#4889) 2016-12-13 22:58:43 -08:00
Oliver
dc551b825f Added a volume set option and autodiscovery functions to Denon AVR rece… (#4845)
* Added Volume Set option and autodiscovery functions to Denon AVR receivers

* Corrected issues in SSDP discovery and in case no host could be discovered

* Corrected discovery handling / added denonavr to discovery platform

* No needless discoveries anymore / add_devices() with list instead of loop
2016-12-13 20:04:40 -08:00
Erik Eriksson
6da3e23436 Update __init__.py (#4877)
Cleaner exit by not throwing exception if server was not set during initialization of component (ref https://github.com/home-assistant/home-assistant/pull/4866)
2016-12-13 08:57:33 -08:00
Pascal Vizeli
e4b6395250 Migrate REST switch to async (#4517)
* Migrate REST switch to async

* Update rest.py

* Address comments from paulus
2016-12-13 08:55:13 -08:00
Audun Ytterdal
72bd9fb5c7 Remove libtelldus-core-dev from Dockerfile (#4878)
Remove unnecessary  libtelldus-core-dev from Dockerfile . Ref https://github.com/home-assistant/home-assistant/pull/4680#issuecomment-266006310
2016-12-13 08:53:42 -08:00
Pascal Vizeli
2dec38d8d4 TTS Component / Google speech platform (#4837)
* TTS Component / Google speech platform

* Change file backend handling / cache

* Use mimetype / rename Provider function / allow cache on service call

* Add a memcache for faster response

* Add demo platform

* First version of unittest

* Address comments

* improve error handling / address comments

* Add google unittest & check http response code

* Change url param handling

* add test for other language

* Change hash to sha256 for same hash on every os/hardware

* add unittest for receive demo data

* add test for error cases

* Test case load from file to mem over aiohttp server

* Use cache SpeechManager level, address other comments

* Add service for clear cache

* Update service.yaml

* add support for spliting google message
2016-12-12 23:23:08 -08:00
John Mihalic
acb841a1f4 Add Hikvision binary sensor component (#4825)
* Add Hikvision binary sensor component

* Simplify customize configuration

* Add delay attribute

* Remove use of threading timer, fix delay functionality
2016-12-12 23:10:16 -08:00
Paulus Schoutsen
eeb8bc3913 Fix dev tag detection in release script (#4873) 2016-12-12 22:18:20 -08:00
Erik Eriksson
12f790c7cf Display error message instead of exception (#4866)
* Display error message instead of exception

Display error message in log instead of stack trace.
(Usually happens when a server is already running at the same port.)

* Update __init__.py

Better error handling when reading SSL certificate

* Update __init__.py

* Update __init__.py
2016-12-12 22:02:24 -08:00
Erik Eriksson
dbb4e4c3fa [tellduslive] Upgrade requirement (#4865) 2016-12-12 20:05:38 +02:00
Paulus Schoutsen
d51e62d0a3 Merge pull request #4863 from home-assistant/master
Backmerge point releases
2016-12-11 22:51:30 -08:00
Paulus Schoutsen
ab92a91ac5 Merge branch 'dev' into master 2016-12-11 22:49:06 -08:00
Paulus Schoutsen
cfa36f3546 Merge pull request #4862 from home-assistant/release-0-34-5
0.34.5
2016-12-11 22:46:19 -08:00
Paulus Schoutsen
96d8fbe513 Version bump to 0.34.5 2016-12-11 22:26:48 -08:00
Paulus Schoutsen
1e9d91be0e Fix Plex from doing I/O inside event loop (#4857) 2016-12-11 22:26:30 -08:00
Paulus Schoutsen
2402897f47 Fix Nest doing I/O inside event loop (#4855) 2016-12-11 22:26:30 -08:00
Josh Nichols
b857d5dad0 Bump python-nest to fix issue with Nest Cam without activity zones (#4820)
* Bump python-nest to fix issue with Nest Cam without activity zones

* bump to include fix python-nest dependency with hvac_state

* regenerate requirements_all.txt
2016-12-11 22:26:30 -08:00
R1chardTM
d17753009a Fix python-nest version bump (#4799)
* Fix python-nest version bump

* Change SHA so version in HASS and dependency are the same
2016-12-11 22:26:30 -08:00
Marcelo Moreira de Mello
3467020dbf Added resolution support to Amcrest cameras (#4860)
* Added resolution support to Amcrest cameras

* Ordered alphabetically DEFAULT_ options
2016-12-11 21:46:19 -08:00
Adam Mills
4114884cdc Flic: Support use of queued events within timeout (#4822)
* Flic: Support use of queued events within timeout

* Linter fixes
2016-12-11 21:43:59 -08:00
John Mihalic
d7ccf07922 Add media position support and trailer type to Emby (#4792)
* Add media position support and trailer type to Emby

* Adjustments to mitigate TypeError

* Simplify media_position property

* Update handling when data isn't available

* Update emby.py
2016-12-11 21:43:53 -08:00
Erik Eriksson
2a7fa5afc3 Telldus Live: (#4645)
- Implemented support for covers and dimmable lights.
- Removed global object, use hass.data.
- Disabled polling via update.
- Inherit from common TelldusLiveEntity device.
- Configurable polling interval
- Use https API endpoint
- Use tellduslive package
2016-12-11 21:39:37 -08:00
Anton Lundin
04aa4e898a Improve denon media_player (#4836)
* Add debug level logging of messages in denon

* Added media stop for Denon AVR Media Player

* Sort source list

* Rework input selection for Denon AVR

This reworks the input selection, adding more modes and making it so
that the media controls are only announced in modes where they actually
makes sense.

* Added real media info for Denon AVR Media modes

* Read more configuration from denon devices

This reads network name, and overrides the local name with that.

This also reads the source names and reconfigures the input list to
those names, and also reads the source deleted list and removes the
inputs that are set to deleted in the device.

* Discover and handle max volume in Denon media player

* Rework source discovery in Denon media player

This uses SSFUN as authorative source for which sources that we should
present.
2016-12-11 21:04:36 -08:00
Michaël Arnauts
b156ae7812 Add support for Hue LightGroups (#4744)
* Add support for Hue LightGroup entity

* Don't filter on LightGroup and add properties for a group

* Reuse code from HueLight in HueLightGroup

* Remove HueLightGroup and add is_group variable to HueLight

* Make linter happy

* Update light or lightgroup state when a new state is available

* Use schedule_update_ha_state() to schedule the state update. Drop new_lightgroups and use new_lights instead.

* code style fix
2016-12-11 17:59:30 -08:00
David-Leon Pohl
48928d1f9e Fix config validation (#4853) (#4854) 2016-12-11 17:38:33 -08:00
Paulus Schoutsen
df98d5b3c1 Fix Nest doing I/O inside event loop (#4855) 2016-12-11 17:34:26 -08:00
Paulus Schoutsen
f4b5c439a1 Fix Plex from doing I/O inside event loop (#4857) 2016-12-11 17:34:13 -08:00
Jeff Wilson
ecc514b7e4 Use current mode to determine which temperature attributes to use (#4858) 2016-12-11 15:48:47 -08:00
IoTGuy
6edb54052f adding sensehat plugin (#4775)
* adding sensehat plugin

* added

* fix PR

* requirement updated

* Update sensehat.py
2016-12-11 15:46:55 -08:00
Marcelo Moreira de Mello
4d2480bbd1 Added support to language codes on Weather Underground (#4815)
* Added supported to language codes to Weather Underground

* Removed unecessary None assigments
2016-12-11 15:43:42 -08:00
Daniel Høyer Iversen
2708e193ec vlc media player (#4800)
* vlc media player

* Update vlc.py
2016-12-11 14:59:12 -08:00
Jean-Philippe Bouillot
c3923b2768 Netatmo improving Battery info (#4724)
* Improving Battery info

Improving battery status info (to reflect NetAtmo API documentation and change deprecated DeviceList for WeatherStationData

* Fixes from previous update

Fix the hound issue.
add battery_lvl, WindAngle_value, GustAngle_value, rf_status_lvl, wifi_status_lvl
2016-12-11 14:47:27 -08:00
devdelay
080c4efb00 Ecobee detect Smart Away (#4769)
* Ecobee autoAway Event

* Update ecobee.py

Checking if event['running'] true is pointless because if false event['type'] will equal template and when true type will only be 'hold' or 'autoAway' so I've removed this check from the statement
2016-12-11 14:46:10 -08:00
Pascal Vizeli
99f1ea9b59 Migrate alarm control panel to async (#4807)
* Merge alarm control panel to async

* fix lint
2016-12-11 14:39:20 -08:00
Johann Kellerman
46cad514d4 Revert "[device_tracker] Don't clear GPS coordinates when using two device trackers." (#4851) 2016-12-11 19:18:11 +02:00
Lewis Juggins
e0552ad899 [device_tracker] Don't clear GPS coordinates if no GPS seen (#4848) 2016-12-11 15:13:43 +02:00
Daniel Høyer Iversen
5c99dd0e3d Merge pull request #4846 from lwis/gpslogger
[device_tracker.gpslogger] Add additional activity attribute.
2016-12-11 11:07:55 +01:00
Lewis Juggins
cdf9464698 [device_tracker.gpslogger] Add additional activity attribute. 2016-12-11 09:06:29 +00:00
Stefan Jonasson
7ba25f3526 Fixed crash during light objects initizilation (#4835)
* Fixed crash when lights objects was inited
2016-12-11 09:54:50 +01:00
Daniel Høyer Iversen
ee5b9e7291 Configurable scan options for nmap (#4838) 2016-12-10 19:53:25 +02:00
r-jordan
167260bcc6 [climate.generic_thermostat] Make tolerance work both ways (#4830) 2016-12-10 10:36:35 +00:00
Josh Nichols
64de1c9777 Bump python-nest to fix issue with Nest Cam without activity zones (#4820)
* Bump python-nest to fix issue with Nest Cam without activity zones

* bump to include fix python-nest dependency with hvac_state

* regenerate requirements_all.txt
2016-12-09 11:04:40 -08:00
Sören Oldag
1547045f2c Flic: Support ignoring individual click types. (#4827) 2016-12-09 08:52:14 -08:00
Keaton Taylor
d02899216d Prevent emulated hue discovery by hue component (#4819)
* Prevent emulated hue discovery

Test for “HASS Bridge” in discovery info, pass if found, else try and
setup the bridge.

* Solved coding error

Duplicate commands and return false added for component.
2016-12-09 08:45:14 -08:00
Albert Lee
0aac4d64e1 Add away mode for Radio Thermostat/3M Filtrete (#4793) 2016-12-08 23:26:02 -08:00
Pascal Vizeli
0bf9e6d4bb Bugfix error on automation reload (#4823) 2016-12-08 23:24:03 -08:00
Jan Losinski
f78246e686 Pilight receive match fix for bug 4637 (#4639)
* Pilight: dont protocol as list in COMMAND_SCHEMA

As described in bug #4637 the protocol should not be wrapped in a list
in the spec of COMMAND_SCHEMA because this causes the component to
never successfully match any received rf code.

As pointed ot in PR #4639 the easiest way to do this, is to not derive
COMMAND_SCHEMA from RF_CODE_SCHEMA and specify protocol as simple
string there.

This fixes bug #4637.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Pilight: Add "unitcode" to command schema.

This adds "unitcode" to the COMMAND_SCHEMA. It is used for example in
the brennenstuhl protocol of pilight.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2016-12-08 23:19:14 -08:00
Paulus Schoutsen
c90a1b9760 Update frontend 2016-12-08 22:31:32 -08:00
Lewis Juggins
14446c5731 [media_player.sonos] Add stop support. (#4788) 2016-12-08 08:36:37 +00:00
Albert Lee
2e2b764dbe Add exception handling when turning on Onkyo receivers (#4813) 2016-12-07 21:46:42 -08:00
R1chardTM
695f062e29 Fix python-nest version bump (#4799)
* Fix python-nest version bump

* Change SHA so version in HASS and dependency are the same
2016-12-07 21:45:43 -08:00
Keaton Taylor
194b268ae3 Get entity name from entity.name (#4798)
Grabbing the ATTR_FRIENDLY_NAME directly produces an error. Instead
grab from entity.name.
2016-12-07 21:45:18 -08:00
Pascal Vizeli
8295fc8b4c Pararell execute state restore by domain (#4801)
* Pararell execute state restore per domain

* fix spell
2016-12-07 08:37:35 -08:00
Jan Losinski
d0dcd1bb73 Scene: add support for input_select (#4674)
This adds support for the scene component to handle input_select
devices and set their options.

This fixes bug #4673

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2016-12-07 05:33:41 -08:00
Nolan Gilley
82ad8b0a8f round $ to 2 decimals (#4786) 2016-12-07 09:15:48 +01:00
Paulus Schoutsen
91a9da8f0c Merge pull request #4796 from home-assistant/release-0-34-4
0.34.4
2016-12-06 22:44:57 -08:00
Paulus Schoutsen
e3415c4e22 Version bump to 0.34.4 2016-12-06 22:35:43 -08:00
Paulus Schoutsen
9bca3f3103 Update after service calls (#4795)
* Update after service calls

* Service update: wrap async_update in create_task
2016-12-06 22:35:24 -08:00
Pascal Vizeli
7c3ae884df Migrate remote to async (#4678)
* Migrate remote to async

* add coro

* remove sync from init since only used in harmony

* import ATTR from remote

* remove unused sync stuff from tests
2016-12-06 22:35:24 -08:00
Adam Mills
8a4aace789 Fix incorrect caching of /api/error_log (#4789) 2016-12-06 22:35:24 -08:00
Josh Nichols
0e74cd833d Updated python-nest dependency (#4785)
* Updated python-nest dependency

This sha fixes two issues:

- min and max temperatures not being set when temperature isn't locked
- fixes error when setting farenheit with a .5

* gen requirements all

* Add fix for https://github.com/home-assistant/home-assistant/issues/4731
2016-12-06 22:35:24 -08:00
Paulus Schoutsen
5e2911f071 Fix Kodi auth (#4770) 2016-12-06 22:35:24 -08:00
Paulus Schoutsen
7dacc4a7bb Fix default auth influxdb (#4771) 2016-12-06 22:35:24 -08:00
Paulus Schoutsen
98fe50d5ad Update after service calls (#4795)
* Update after service calls

* Service update: wrap async_update in create_task
2016-12-06 22:30:47 -08:00
Paulus Schoutsen
37e3c2a133 Contributing: add skip step 0 2016-12-06 21:48:44 -08:00
Paulus Schoutsen
76b79019ce Add faster reviews link to contributing 2016-12-06 21:48:02 -08:00
Adam Mills
c40ddf18c7 Fix incorrect caching of /api/error_log (#4789) 2016-12-06 21:03:49 -08:00
Josh Nichols
9a3fe691b1 Updated python-nest dependency (#4785)
* Updated python-nest dependency

This sha fixes two issues:

- min and max temperatures not being set when temperature isn't locked
- fixes error when setting farenheit with a .5

* gen requirements all

* Add fix for https://github.com/home-assistant/home-assistant/issues/4731
2016-12-06 20:31:07 -08:00
Audun Ytterdal
8826e6a8d0 Add support for telldus in the Docker image. (#4680)
* Add support for telldus in the Docker image. Start with -v /tmp/TelldusClient:/tmp/TelldusClient -v /tmp/TelldusEvents:/tmp/TelldusEvents

* Merged telldus install with the others

* Clean up indenting

* Stream apt-key
2016-12-06 09:01:47 -08:00
Paulus Schoutsen
860a12cffb Fix Kodi auth (#4770) 2016-12-06 07:43:11 -08:00
Fabian Affolter
76ff934bd3 Move details to docs, update doc strings, and use consts (#4777) 2016-12-06 15:49:59 +01:00
Lewis Juggins
d968e1d011 Add test to ensure device_tracker records state correctly. (#4776) 2016-12-06 15:01:24 +02:00
Paulus Schoutsen
fa0dbaf065 Fix default auth influxdb (#4771) 2016-12-05 23:39:22 -08:00
Paulus Schoutsen
4d0f19496a Merge remote-tracking branch 'origin/master' into dev 2016-12-05 23:35:36 -08:00
Paulus Schoutsen
0cc9555d14 Merge pull request #4774 from home-assistant/release-0-34-3
0.34.3
2016-12-05 23:35:09 -08:00
Paulus Schoutsen
d712a3dc38 Version bump to 0.34.3 2016-12-05 22:42:55 -08:00
rubund
84446bed14 Fix broken EnOcean support (#4710)
* ensure_list

* CONF_ID is not required configuration for enocean lights

* Use vol.All(cv.ensure_list, [vol.Coerce(int)]) as suggested in pull request review

* Fix line too long
2016-12-05 22:42:32 -08:00
Jeff Wilson
e92b15f966 Set hue-bridgeid in UPNP response (#4740) 2016-12-05 22:42:23 -08:00
Paulus Schoutsen
a458ce8069 Fix websocket async (#4752)
* Ensure we write to websocket from inside event loop

* Inline service call helper
2016-12-05 22:42:13 -08:00
dasos
5e492db9a3 Fix connection check (#4732)
* Fix connection check

* Release instead

* Remove if

* Update hook.py
2016-12-05 22:42:02 -08:00
Albert Lee
bc646070c8 Match uppercase MAC addresses in asuswrt 'arp -n' output (#4742) (#4764) 2016-12-05 22:20:21 -08:00
Paulus Schoutsen
64290d74f0 Update frontend 2016-12-05 21:40:45 -08:00
dasos
a11b68c560 Fix connection check (#4732)
* Fix connection check

* Release instead

* Remove if

* Update hook.py
2016-12-05 21:37:05 -08:00
Caleb
8ca2345fd4 Pyunifi dep (#4754)
* change unifi dependency to pyunifi

* Change dependency to fix #4336

* Run gen_requirements_all.py script

* Changed import statement to reflect new package

* Updated test_unifiy.py with different module

* Update requirements_all.txt
2016-12-05 21:35:54 -08:00
Hugo Dupras
8c628071f3 Add support for Netatmo tags (#4761)
* Add support for Netatmo Welcome Tags

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Add size parameter for WelcomeData

* minor fixes

* Add Throttling mechanism for update event

This will prevent to reach the API limit

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Change scan interval for Netatmo Binary sensors

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Minor fixes

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Update netatmo.py
2016-12-05 21:35:33 -08:00
Sören Oldag
81d38c3463 Add flic smart button component (#4681)
* Added component for flic smart buttons.

* Apply home-assistant coding styles.

* Fixed flic configuration.

* Made logging for scanning for new buttons less verbose.

* Fixed flic event data.

* Follow async conventions.

* Added new requirements to requirements_all.txt.

* Added flic component to .coveragerc

* Updated flic threshold configuration key names.

* Flic devices are now removed when they disconnect.

* Include review feedback.

* Fixed stopping of clients in flic component when home assistant is stopped.

* Updated flic component by integrating input of #4738.

Use library method to determine click type. Merge three click events into single one with click_type parameter.

* Use a single client for both handling click events and scanning for new buttons.

* Renamed flic ‘auto_scan’ configuration variable to ‘discovery’ using HA constants.
2016-12-05 21:12:24 -08:00
Adam Mills
776455030f Fix media_image_urls for universal media player (#4765)
* Fix media_image_urls for universal media player

* Linter fixes
2016-12-05 18:07:04 -08:00
Daniel Høyer Iversen
8afd30b7d4 fix setting battery in device_tracker (#4756) 2016-12-05 18:04:04 -08:00
Paulus Schoutsen
b60f5714fc Fix websocket async (#4752)
* Ensure we write to websocket from inside event loop

* Inline service call helper
2016-12-05 18:03:06 -08:00
John Arild Berentsen
fa8bc0a36c Add Verisure smartcam capture service (#4559)
* Add verisure capture as service

* docstyle
2016-12-05 17:51:58 -08:00
Martin J. Laubach
1ae8256ffd Add sensor for reading Austrian ZAMG weather conditions (#4347)
* Add sensor for reading ZAMG weather conditions

* Add to coveragerc; Correct some doc style problems

* More doc fixes

* More doc fixes

* Lose license and whatever.

* Don't return UNKNOWN for unknown variables

* Verify that the configured station id is actually one in the data set.

Don't warn about unknown stations, this cannot happen any more as the configuration parser now checks that.
This could still happen if the data set is incomplete though ...

* Clean up imports

* Clarify comment on throttling interval

* Base zamg sensor on Entity, not WeatherEntity, and delete unused code

* Fix formatting nits from flake8

* Use ATTR_FRIENDLY_NAME, clean up imports, remove unnecessary indirection.

* Use {}.format() instead of "" %

* Re-add unit of measurement that got lost somehow

* Use guard clauses instead of if-matroshka.
Wrap requests.get() in try/except for RequestException.

* Huh, how did this happen? White space corrections...

* Add sensor for reading ZAMG weather conditions

* Add to coveragerc; Correct some doc style problems

* More doc fixes

* More doc fixes

* Verify that the configured station id is actually one in the data set.

Don't warn about unknown stations, this cannot happen any more as the configuration parser now checks that.
This could still happen if the data set is incomplete though ...

* Lose license and whatever.

* Don't return UNKNOWN for unknown variables

* Clean up imports

* Clarify comment on throttling interval

* Base zamg sensor on Entity, not WeatherEntity, and delete unused code

* Fix formatting nits from flake8

* Use ATTR_FRIENDLY_NAME, clean up imports, remove unnecessary indirection.

* Use {}.format() instead of "" %

* Re-add unit of measurement that got lost somehow

* Use guard clauses instead of if-matroshka.
Wrap requests.get() in try/except for RequestException.

* Huh, how did this happen? White space corrections...

* Precipitation actually is a float, good it rained today

* Logger needs no module visibility

* Do not name sensors with _ to be in line with the other weather sensor platforms.

* Remove manually set friendly_name

* comment format police

* Less comments

* Update zamg.py
2016-12-05 17:50:50 -08:00
Jeff Wilson
b3253403aa Set hue-bridgeid in UPNP response (#4740) 2016-12-05 17:39:40 -08:00
Daniel Høyer Iversen
308744d8a0 Add additional attributes to GPSLogger (#4755) 2016-12-05 19:33:51 +00:00
Paulus Schoutsen
13006cee68 Device tracker attributes (#4753) 2016-12-05 19:32:17 +00:00
rubund
e21382cd3e Fix broken EnOcean support (#4710)
* ensure_list

* CONF_ID is not required configuration for enocean lights

* Use vol.All(cv.ensure_list, [vol.Coerce(int)]) as suggested in pull request review

* Fix line too long
2016-12-05 08:15:36 -08:00
Daniel Høyer Iversen
71fc446425 Merge pull request #4719 from home-assistant/device_tracker
device tracker
2016-12-05 16:32:29 +01:00
Fabian Affolter
03d19ec2f1 Netdata sensor (#4743)
* Added netdata sensor

* Typo

* Add netdata sensor

* Improvement of the work done by @ezar
2016-12-05 11:19:20 +01:00
Daniel Hoyer Iversen
5a7e446646 device tracker 2016-12-05 09:12:13 +01:00
Paulus Schoutsen
2b3caa716a Cast progress (#4735)
* add progress to google cast

* Add progress to media player demo
2016-12-04 15:30:55 -08:00
Paulus Schoutsen
6574dd8439 Merge branch 'release-0-34-2' into dev 2016-12-04 15:20:48 -08:00
Paulus Schoutsen
6d56519297 Merge pull request #4736 from home-assistant/release-0-34-2
0.34.2
2016-12-04 15:19:56 -08:00
Paulus Schoutsen
60bcb12a48 Version bump to 0.34.2 2016-12-04 15:08:38 -08:00
Lukas
58509f8bba [0.34] bugfix influxdb node_id (#4712)
* Bugfix for #4709 - do not convert node_id to float

* Update influxdb.py
2016-12-04 15:08:25 -08:00
Lukas
2099d023ef [0.34] bugfix influxdb node_id (#4712)
* Bugfix for #4709 - do not convert node_id to float

* Update influxdb.py
2016-12-04 15:08:14 -08:00
Paulus Schoutsen
db6a6fa4cb Lint 2016-12-04 14:45:25 -08:00
Josh Nichols
d89bfcdaa5 Make sure all nest platforms require discovery info (#4734) 2016-12-04 14:34:01 -08:00
Josh Nichols
64b1179c13 Make sure all nest platforms require discovery info (#4734) 2016-12-04 14:33:50 -08:00
Paulus Schoutsen
840e27adec Fix Nest interpreting Celsius temperature as Fahrenheit (#4729) 2016-12-04 14:32:59 -08:00
Paulus Schoutsen
31a8537ab5 Update frontend 2016-12-04 14:32:43 -08:00
Paulus Schoutsen
87dab37b8a Fix Nest interpreting Celsius temperature as Fahrenheit (#4729) 2016-12-04 13:49:46 -08:00
Paulus Schoutsen
3a2cdd3de0 Merge pull request #4728 from home-assistant/release-0-34-1
0.34.1
2016-12-04 11:18:46 -08:00
Paulus Schoutsen
2009e98497 Version bump to 0.34.1 2016-12-04 11:03:16 -08:00
Paulus Schoutsen
b354a18bf3 Fix CORS when static resources registered (#4727) 2016-12-04 11:02:33 -08:00
hexa-
1cd3cd8d77 Revert "Update reference to correct tplink switch" (#4722) 2016-12-04 11:02:33 -08:00
Pascal Vizeli
d9556392bc Protect hm thread for hangs on events (#4717) 2016-12-04 11:02:33 -08:00
Paulus Schoutsen
695fb412cd Re-org emulated_hue and fix google home (#4708) 2016-12-04 11:02:33 -08:00
Josh Nichols
93322b0251 Updated python-nest to fix a camera bug when loading images (#4701) 2016-12-04 11:02:33 -08:00
Paulus Schoutsen
9b9b625ac4 Fix synology dsm doing I/O inside loop (#4699) 2016-12-04 11:02:33 -08:00
Pascal Vizeli
0ae6585a90 Bugfix sonos hosts (#4698) 2016-12-04 11:02:33 -08:00
Paulus Schoutsen
cffc7ac4d8 Update netdisco to 0.8 (#4723) 2016-12-04 10:59:18 -08:00
Paulus Schoutsen
a9be6c36f1 Re-org emulated_hue and fix google home (#4708) 2016-12-04 10:57:48 -08:00
Paulus Schoutsen
1b35f0878e Fix CORS when static resources registered (#4727) 2016-12-04 10:57:24 -08:00
Paulus Schoutsen
93872590b6 Fix synology dsm doing I/O inside loop (#4699) 2016-12-04 09:54:49 -08:00
Paulus Schoutsen
b2a15e17d3 MQTT Automation: parse payload as JSON (#4703) 2016-12-04 09:53:05 -08:00
Johan Bloemberg
9bf13231f7 Actually test calling async macvendor lookup and fix it. (#4718) 2016-12-04 09:51:40 -08:00
hexa-
c8c6bee539 Revert "Update reference to correct tplink switch" (#4722) 2016-12-04 09:50:43 -08:00
Pascal Vizeli
b5c2be8ffa Protect hm thread for hangs on events (#4717) 2016-12-04 15:31:24 +01:00
DaveSergeant
4d35f2805f New support Digital Loggers relays (#4684)
* initial commit

Previous work included with no history.  Sorry, I was figuring out how to use git, branches and deal with open source projects.  At this point this is a working switch but with the shortcomings of each of the 8 ports causes a network query.  This needs to be rewritten so that the SwitchDevice is part of a larger device group that is only queried once, saving traffic and preventing the small device from timing out.

* Device polls independent of switches now

Used anel_pwrctrl.py as a basis to extract the per-switch polling out to per-device so it can be trottled properly.  Likewise, no longer touching the device independently for relay status AND relay name.  Getting them both from the same statuslist() return.

* Final comments and tweaks

Lowered cycle and update time since the device update is working so well now.  Effectively no timeouts anymore.

* Added dlipower to requirements

homeassistant.components.switch.digitalloggers

* Tox fixes

pydocstyle updates

* More tox errors

* Yet more tox

Removed useful future TODO and helpful details on the structure of the statuslocal list.
Good catch on not initializing .update(), though it worked.

* Blank line fix

* Added file to .coveragerc
2016-12-03 23:40:22 -08:00
Marcelo Moreira de Mello
53c1b93b61 Added persistent_notification in case of error during Unifi device_tracker setup (#4682) 2016-12-03 22:11:52 -08:00
Thibault Cohen
c25aa56751 Add Sharp AquosTV component (#4679) 2016-12-03 22:09:49 -08:00
Pascal Vizeli
e8c9dcf0fe Migrate remote to async (#4678)
* Migrate remote to async

* add coro

* remove sync from init since only used in harmony

* import ATTR from remote

* remove unused sync stuff from tests
2016-12-03 22:08:24 -08:00
William Scanlon
ca63e44227 Wink hub sensor (#4704) 2016-12-03 21:39:48 -08:00
Johan Bloemberg
776e53a7f0 Dsmr hourly gas usage. (#4609)
* Hourly rate of Gas consumption. Use proper unknown state.

* Import unknown state constant.

* doh

* Cleanup device add.

* Fix lint.

* Add test for derivative calculation.

* Remove conflict.

* Document and move calculation into update call.
2016-12-03 20:45:42 -08:00
Dan
a099430834 Universal source list (#4086)
* Add source_list to universal media player

* Expanded attirubte and command support for UMP

Added support to the universal media player
for the following:
    Volume Set
    Current Source
    Set Source
    Current Volume

The goal is to facilitate a single-card media player
that includes source selection and setting the volume
of the receiver.

Example setup:
```
media_player:
  - platform: universal
    name: Media Center
    children:
      - media_player.kodi
      - media_player.cast
    commands:
      select_source:
        service: media_player.select_source
        data:
          entity_id: media_player.receiver
      volume_set:
        service: media_player.volume_set
        data:
          entity_id: media_player.receiver
      volume_mute:
        service: media_player.volume_mute
        data:
          entity_id: media_player.receiver
      turn_on:
        service: homeassistant.turn_on
        data:
          entity_id: media_player.receiver
      turn_off:
        service: homeassistant.turn_off
        data:
          entity_id: media_player.receiver
    attributes:
      state: media_player.receiver
      is_volume_muted: media_player.receiver|is_volume_muted
      volume_level: media_player.receiver|volume_level
      source: media_player.receiver|source
      source_list: media_player.receiver|source_list
```

* Remove print statements

* Change service call back to use call_from_config

* Modified service calls to use template data

* linting fixes

* Add tests

* linting fices

* More pylinting
2016-12-03 20:09:28 -08:00
Pascal Vizeli
7746ecd98e Migrate weather to async (#4677) 2016-12-03 17:58:43 -08:00
Josh Nichols
10d1496f5a Updated python-nest to fix a camera bug when loading images (#4701) 2016-12-03 17:55:14 -08:00
Sebastian von Minckwitz
cf0ff54d14 Add option to hide the group card switch (#4631)
* Add option to hide the group card switch

* Disallow control of hidden group switches

* Revert "Disallow control of hidden group switches"

This reverts commit 75e5ddfe30.

* Changed hide_switch to control
2016-12-03 17:50:11 -08:00
Magnus Ihse Bursie
97cc76b43e Remove global variable from tellstick code (#4700)
* Refactor tellstick code for increased readability. Especially highlight if "device" is a telldus core device or a HA entity.

* Refactor Tellstick object model for increased clarity.

* Update comments. Unify better with sensors. Fix typo bug. Add debug logging.

* Refactor tellstick code for increased readability. Especially highlight if "device" is a telldus core device or a HA entity.

* Refactor Tellstick object model for increased clarity.

* Update comments. Unify better with sensors. Fix typo bug. Add debug logging.

* Fix lint issues.

* Remove global variable according to hint from balloob.
2016-12-03 17:27:55 -08:00
Jacob Minnis
c89e6ec915 Updated email message headers to have 'Date' and 'Message-Id' fields (#4693) (#4695) 2016-12-03 16:56:42 -08:00
Pascal Vizeli
efdf51b542 Bugfix sonos hosts (#4698) 2016-12-04 00:31:27 +01:00
Paulus Schoutsen
bbb251c0cf Version bump to 0.35.0dev0 2016-12-03 12:17:16 -08:00
632 changed files with 38463 additions and 7547 deletions

View File

@@ -5,6 +5,7 @@ omit =
homeassistant/__main__.py
homeassistant/scripts/*.py
homeassistant/helpers/typing.py
homeassistant/helpers/signal.py
# omit pieces of code that rely on external devices being present
homeassistant/components/apcupsd.py
@@ -13,6 +14,9 @@ omit =
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
homeassistant/components/bbb_gpio.py
homeassistant/components/*/bbb_gpio.py
homeassistant/components/bloomsky.py
homeassistant/components/*/bloomsky.py
@@ -34,12 +38,18 @@ omit =
homeassistant/components/insteon_hub.py
homeassistant/components/*/insteon_hub.py
homeassistant/components/insteon_local.py
homeassistant/components/*/insteon_local.py
homeassistant/components/ios.py
homeassistant/components/*/ios.py
homeassistant/components/isy994.py
homeassistant/components/*/isy994.py
homeassistant/components/lutron.py
homeassistant/components/*/lutron.py
homeassistant/components/modbus.py
homeassistant/components/*/modbus.py
@@ -107,25 +117,30 @@ omit =
homeassistant/components/knx.py
homeassistant/components/*/knx.py
homeassistant/components/ffmpeg.py
homeassistant/components/*/ffmpeg.py
homeassistant/components/zoneminder.py
homeassistant/components/*/zoneminder.py
homeassistant/components/mochad.py
homeassistant/components/*/mochad.py
homeassistant/components/zabbix.py
homeassistant/components/*/zabbix.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/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/rest.py
homeassistant/components/browser.py
homeassistant/components/camera/amcrest.py
homeassistant/components/camera/bloomsky.py
homeassistant/components/camera/ffmpeg.py
homeassistant/components/camera/foscam.py
homeassistant/components/camera/mjpeg.py
homeassistant/components/camera/rpi_camera.py
@@ -152,19 +167,26 @@ omit =
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/gpslogger.py
homeassistant/components/device_tracker/icloud.py
homeassistant/components/device_tracker/linksys_ap.py
homeassistant/components/device_tracker/luci.py
homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/device_tracker/ping.py
homeassistant/components/device_tracker/sky_hub.py
homeassistant/components/device_tracker/snmp.py
homeassistant/components/device_tracker/swisscom.py
homeassistant/components/device_tracker/thomson.py
homeassistant/components/device_tracker/tomato.py
homeassistant/components/device_tracker/tado.py
homeassistant/components/device_tracker/tplink.py
homeassistant/components/device_tracker/trackr.py
homeassistant/components/device_tracker/ubus.py
homeassistant/components/device_tracker/volvooncall.py
homeassistant/components/device_tracker/xiaomi.py
homeassistant/components/discovery.py
homeassistant/components/downloader.py
homeassistant/components/emoncms_history.py
homeassistant/components/emulated_hue/upnp.py
homeassistant/components/fan/mqtt.py
homeassistant/components/feedreader.py
homeassistant/components/foursquare.py
@@ -173,16 +195,25 @@ omit =
homeassistant/components/joaoapps_join.py
homeassistant/components/keyboard.py
homeassistant/components/keyboard_remote.py
homeassistant/components/light/avion.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/decora.py
homeassistant/components/light/flux_led.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
homeassistant/components/light/lifx.py
homeassistant/components/light/limitlessled.py
homeassistant/components/light/osramlightify.py
homeassistant/components/light/tikteck.py
homeassistant/components/light/x10.py
homeassistant/components/light/yeelight.py
homeassistant/components/light/piglow.py
homeassistant/components/light/zengge.py
homeassistant/components/lirc.py
homeassistant/components/lock/nuki.py
homeassistant/components/media_player/anthemav.py
homeassistant/components/media_player/apple_tv.py
homeassistant/components/media_player/aquostv.py
homeassistant/components/media_player/braviatv.py
homeassistant/components/media_player/cast.py
homeassistant/components/media_player/cmus.py
@@ -193,11 +224,14 @@ omit =
homeassistant/components/media_player/emby.py
homeassistant/components/media_player/firetv.py
homeassistant/components/media_player/gpmdp.py
homeassistant/components/media_player/hdmi_cec.py
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/lg_netcast.py
homeassistant/components/media_player/liveboxplaytv.py
homeassistant/components/media_player/mpchc.py
homeassistant/components/media_player/mpd.py
homeassistant/components/media_player/nad.py
homeassistant/components/media_player/onkyo.py
homeassistant/components/media_player/panasonic_viera.py
homeassistant/components/media_player/pandora.py
@@ -210,17 +244,22 @@ omit =
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/vlc.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/notify/aws_lambda.py
homeassistant/components/notify/aws_sns.py
homeassistant/components/notify/aws_sqs.py
homeassistant/components/notify/discord.py
homeassistant/components/notify/facebook.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/gntp.py
homeassistant/components/notify/group.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/joaoapps_join.py
homeassistant/components/notify/kodi.py
homeassistant/components/notify/lannouncer.py
homeassistant/components/notify/llamalab_automate.py
homeassistant/components/notify/mailgun.py
homeassistant/components/notify/matrix.py
homeassistant/components/notify/message_bird.py
homeassistant/components/notify/nfandroidtv.py
@@ -237,17 +276,20 @@ omit =
homeassistant/components/notify/telegram.py
homeassistant/components/notify/telstra.py
homeassistant/components/notify/twilio_sms.py
homeassistant/components/notify/twilio_call.py
homeassistant/components/notify/twitter.py
homeassistant/components/notify/xmpp.py
homeassistant/components/nuimo_controller.py
homeassistant/components/openalpr.py
homeassistant/components/remote/harmony.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/sensor/amcrest.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/arwn.py
homeassistant/components/sensor/bbox.py
homeassistant/components/sensor/bitcoin.py
homeassistant/components/sensor/bom.py
homeassistant/components/sensor/broadlink.py
homeassistant/components/sensor/dublin_bus_transport.py
homeassistant/components/sensor/coinmarketcap.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/cups.py
@@ -271,6 +313,7 @@ omit =
homeassistant/components/sensor/haveibeenpwned.py
homeassistant/components/sensor/hddtemp.py
homeassistant/components/sensor/hp_ilo.py
homeassistant/components/sensor/hydroquebec.py
homeassistant/components/sensor/imap.py
homeassistant/components/sensor/imap_email_content.py
homeassistant/components/sensor/influxdb.py
@@ -280,19 +323,25 @@ omit =
homeassistant/components/sensor/mhz19.py
homeassistant/components/sensor/miflora.py
homeassistant/components/sensor/mqtt_room.py
homeassistant/components/sensor/netdata.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nut.py
homeassistant/components/sensor/nzbget.py
homeassistant/components/sensor/ohmconnect.py
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openevse.py
homeassistant/components/sensor/openexchangerates.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/pi_hole.py
homeassistant/components/sensor/plex.py
homeassistant/components/sensor/pvoutput.py
homeassistant/components/sensor/qnap.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/sensor/scrape.py
homeassistant/components/sensor/sensehat.py
homeassistant/components/sensor/serial_pm.py
homeassistant/components/sensor/skybeacon.py
homeassistant/components/sensor/sma.py
homeassistant/components/sensor/snmp.py
homeassistant/components/sensor/sonarr.py
homeassistant/components/sensor/speedtest.py
@@ -309,18 +358,24 @@ omit =
homeassistant/components/sensor/transmission.py
homeassistant/components/sensor/twitch.py
homeassistant/components/sensor/uber.py
homeassistant/components/sensor/usps.py
homeassistant/components/sensor/vasttrafik.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/xbox_live.py
homeassistant/components/sensor/yweather.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/zamg.py
homeassistant/components/switch/acer_projector.py
homeassistant/components/switch/anel_pwrctrl.py
homeassistant/components/switch/arest.py
homeassistant/components/switch/broadlink.py
homeassistant/components/switch/digitalloggers.py
homeassistant/components/switch/dlink.py
homeassistant/components/switch/edimax.py
homeassistant/components/switch/fritzdect.py
homeassistant/components/switch/hdmi_cec.py
homeassistant/components/switch/hikvisioncam.py
homeassistant/components/switch/hook.py
homeassistant/components/switch/kankun.py
homeassistant/components/switch/mystrom.py
homeassistant/components/switch/netio.py
homeassistant/components/switch/orvibo.py
@@ -332,7 +387,10 @@ omit =
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
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/openweathermap.py
homeassistant/components/zeroconf.py

6
.ignore Normal file
View File

@@ -0,0 +1,6 @@
# Patterns matched in this file will be ignored by supported search utilities
# Ignore generated html and javascript files
/homeassistant/components/frontend/www_static/*.html
/homeassistant/components/frontend/www_static/*.js
/homeassistant/components/frontend/www_static/panels/*.html

View File

@@ -8,13 +8,16 @@ matrix:
env: TOXENV=requirements
- python: "3.4.2"
env: TOXENV=lint
- python: "3.5"
env: TOXENV=typing
# - python: "3.5"
# env: TOXENV=typing
- python: "3.5"
env: TOXENV=py35
allow_failures:
- python: "3.5"
env: TOXENV=typing
- python: "3.6"
env: TOXENV=py36
# allow_failures:
# - python: "3.5"
# env: TOXENV=typing
cache:
directories:
- $HOME/.cache/pip

39
CLA.md Normal file
View File

@@ -0,0 +1,39 @@
# Contributor License Agreement
```
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the Apache 2.0 license; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the Apache 2.0 license; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it) is maintained indefinitely
and may be redistributed consistent with this project or the open
source license(s) involved.
```
## Attribution
The text of this license is available under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). It is based on the Linux [Developer Certificate Of Origin](http://elinux.org/Developer_Certificate_Of_Origin), but is modified to explicitly use the Apache 2.0 license
and not mention sign-off.
## Signing
To sign this CLA you must first submit a pull request to a repository under the Home Assistant organization.
## Adoption
This Contributor License Agreement (CLA) was first announced on January 21st, 2017 in [this][cla-blog] blog post and adopted January 28th, 2017.
[cla-blog]: https://home-assistant.io/blog/2017/01/21/home-assistant-governance/

80
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,80 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [safety@home-assistant.io][email]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available [here][version].
## Adoption
This Code of Conduct was first adopted January 21st, 2017 and announced in [this][coc-blog] blog post.
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
[email]: mailto:safety@home-assistant.io
[coc-blog]: https://home-assistant.io/blog/2017/01/21/home-assistant-governance/

View File

@@ -1,13 +1,14 @@
# Contributing to Home Assistant
Everybody is invited and welcome to contribute to Home Assistant. There is a lot to do...if you are not a developer perhaps you would like to help with the documentation on [home-assistant.io](https://home-assistant.io/)? If you are a developer and have devices in your home which aren't working with Home Assistant yet, why not spent a couple of hours and help to integrate them?
Everybody is invited and welcome to contribute to Home Assistant. There is a lot to do...if you are not a developer perhaps you would like to help with the documentation on [home-assistant.io](https://home-assistant.io/)? If you are a developer and have devices in your home which aren't working with Home Assistant yet, why not spent a couple of hours and help to integrate them?
The process is straight-forward.
- Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) by Kubernetes (but skip step 0)
- Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant).
- Write the code for your device, notification service, sensor, or IoT thing.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should take a peak at the [developer documentation](https://home-assistant.io/developers/) to get more details.
Still interested? Then you should take a peak at the [developer documentation](https://home-assistant.io/developers/) to get more details.

View File

@@ -1,28 +1,29 @@
FROM python:3.5
MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
# Uncomment any of the following lines to disable the installation.
#ENV INSTALL_TELLSTICK no
#ENV INSTALL_OPENALPR no
#ENV INSTALL_FFMPEG no
#ENV INSTALL_OPENZWAVE no
#ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no
VOLUME /config
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN pip3 install --no-cache-dir colorlog cython
# For the nmap tracker, bluetooth tracker, Z-Wave
RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo libglib2.0-dev bluetooth libbluetooth-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY script/build_python_openzwave script/build_python_openzwave
RUN script/build_python_openzwave && \
mkdir -p /usr/local/share/python-openzwave && \
ln -sf /usr/src/app/build/python-openzwave/openzwave/config /usr/local/share/python-openzwave/config
# Copy build scripts
COPY virtualization/Docker/ virtualization/Docker/
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 mysqlclient psycopg2 uvloop
pip3 install --no-cache-dir mysqlclient psycopg2 uvloop
# Copy source
COPY . .
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]

20
LICENSE
View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Paulus Schoutsen
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

194
LICENSE.md Normal file
View File

@@ -0,0 +1,194 @@
Apache License
==============
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -26,7 +26,8 @@ Examples of devices Home Assistant can interface with:
`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/>`__ and any SNMP
`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/>`__
@@ -86,7 +87,7 @@ 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/>`__ how to reach us.
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

View File

@@ -356,7 +356,8 @@ def try_to_restart() -> None:
def main() -> int:
"""Start Home Assistant."""
monkey_patch_asyncio()
if sys.version_info[:3] < (3, 5, 3):
monkey_patch_asyncio()
validate_python()

View File

@@ -20,11 +20,13 @@ import homeassistant.loader as loader
import homeassistant.util.package as pkg_util
from homeassistant.util.async import (
run_coroutine_threadsafe, run_callback_threadsafe)
from homeassistant.util.logging import AsyncHandler
from homeassistant.util.yaml import clear_secret_cache
from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
event_decorators, service, config_per_platform, extract_domain_configs)
from homeassistant.helpers.signal import async_register_signal_handling
_LOGGER = logging.getLogger(__name__)
@@ -394,6 +396,10 @@ def async_from_config_dict(config: Dict[str, Any],
if not loader.PREPARED:
yield from hass.loop.run_in_executor(None, loader.prepare, hass)
# Merge packages
conf_util.merge_packages_config(
config, core_config.get(conf_util.CONF_PACKAGES, {}))
# Make a copy because we are mutating it.
# Use OrderedDict in case original one was one.
# Convert values to dictionaries if they are None
@@ -430,6 +436,7 @@ def async_from_config_dict(config: Dict[str, Any],
yield from hass.async_stop_track_tasks()
async_register_signal_handling(hass)
return hass
@@ -503,8 +510,10 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
Async friendly.
"""
logging.basicConfig(level=logging.INFO)
fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) "
"[%(name)s] %(message)s%(reset)s")
fmt = ("%(asctime)s %(levelname)s (%(threadName)s) "
"[%(name)s] %(message)s")
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
datefmt = '%y-%m-%d %H:%M:%S'
# suppress overly verbose logs from libraries that aren't helpful
logging.getLogger("requests").setLevel(logging.WARNING)
@@ -514,8 +523,8 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
try:
from colorlog import ColoredFormatter
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
fmt,
datefmt='%y-%m-%d %H:%M:%S',
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
'DEBUG': 'cyan',
@@ -528,6 +537,10 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
except ImportError:
pass
# AsyncHandler allready exists?
if hass.data.get(core.DATA_ASYNCHANDLER):
return
# Log errors to a file if we have write access to file or config dir
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
err_path_exists = os.path.isfile(err_log_path)
@@ -545,11 +558,13 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
err_log_path, mode='w', delay=True)
err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
err_handler.setFormatter(
logging.Formatter('%(asctime)s %(name)s: %(message)s',
datefmt='%y-%m-%d %H:%M:%S'))
err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt))
async_handler = AsyncHandler(hass.loop, err_handler)
hass.data[core.DATA_ASYNCHANDLER] = async_handler
logger = logging.getLogger('')
logger.addHandler(err_handler)
logger.addHandler(async_handler)
logger.setLevel(logging.INFO)
else:
@@ -597,7 +612,7 @@ def async_log_exception(ex, domain, config, hass):
message += '{}.'.format(humanize_error(config, ex))
domain_config = config.get(domain, config)
message += " (See {}:{}). ".format(
message += " (See {}, line {}). ".format(
getattr(domain_config, '__config_file__', '?'),
getattr(domain_config, '__line__', '?'))

View File

@@ -12,14 +12,19 @@ import itertools as it
import logging
import homeassistant.core as ha
import homeassistant.config as conf_util
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.loader import get_component
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE)
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
RESTART_EXIT_CODE)
_LOGGER = logging.getLogger(__name__)
SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config'
SERVICE_CHECK_CONFIG = 'check_config'
def is_on(hass, entity_id=None):
@@ -75,6 +80,21 @@ def toggle(hass, entity_id=None, **service_data):
hass.services.call(ha.DOMAIN, SERVICE_TOGGLE, service_data)
def stop(hass):
"""Stop Home Assistant."""
hass.services.call(ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP)
def restart(hass):
"""Stop Home Assistant."""
hass.services.call(ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART)
def check_config(hass):
"""Check the config files."""
hass.services.call(ha.DOMAIN, SERVICE_CHECK_CONFIG)
def reload_core_config(hass):
"""Reload the core config."""
hass.services.call(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG)
@@ -84,7 +104,7 @@ def reload_core_config(hass):
def async_setup(hass, config):
"""Setup general services related to Home Assistant."""
@asyncio.coroutine
def handle_turn_service(service):
def async_handle_turn_service(service):
"""Method to handle calls to homeassistant.turn_on/off."""
entity_ids = extract_entity_ids(hass, service)
@@ -122,18 +142,37 @@ def async_setup(hass, config):
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service)
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service)
ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TOGGLE, handle_turn_service)
ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service)
@asyncio.coroutine
def handle_reload_config(call):
"""Service handler for reloading core config."""
from homeassistant.exceptions import HomeAssistantError
from homeassistant import config as conf_util
def async_handle_core_service(call):
"""Service handler for handling core services."""
if call.service == SERVICE_HOMEASSISTANT_STOP:
hass.async_add_job(hass.async_stop())
return
try:
yield from conf_util.async_check_ha_config_file(hass)
except HomeAssistantError:
return
if call.service == SERVICE_HOMEASSISTANT_RESTART:
hass.async_add_job(hass.async_stop(RESTART_EXIT_CODE))
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service)
@asyncio.coroutine
def async_handle_reload_config(call):
"""Service handler for reloading core config."""
try:
conf = yield from conf_util.async_hass_config_yaml(hass)
except HomeAssistantError as err:
@@ -144,6 +183,6 @@ def async_setup(hass, config):
hass, conf.get(ha.DOMAIN) or {})
hass.services.async_register(
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, handle_reload_config)
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config)
return True

View File

@@ -4,6 +4,8 @@ Component to interface with an alarm control panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel/
"""
import asyncio
from datetime import timedelta
import logging
import os
@@ -19,7 +21,7 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
DOMAIN = 'alarm_control_panel'
SCAN_INTERVAL = 30
SCAN_INTERVAL = timedelta(seconds=30)
ATTR_CHANGED_BY = 'changed_by'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
@@ -42,36 +44,6 @@ ALARM_SERVICE_SCHEMA = vol.Schema({
})
def setup(hass, config):
"""Track states and offer events for sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)
def alarm_service_handler(service):
"""Map services to methods on Alarm."""
target_alarms = component.extract_from_service(service)
code = service.data.get(ATTR_CODE)
method = SERVICE_TO_METHOD[service.service]
for alarm in target_alarms:
getattr(alarm, method)(code)
if alarm.should_poll:
alarm.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, alarm_service_handler,
descriptions.get(service),
schema=ALARM_SERVICE_SCHEMA)
return True
def alarm_disarm(hass, code=None, entity_id=None):
"""Send the alarm the command for disarm."""
data = {}
@@ -116,6 +88,53 @@ def alarm_trigger(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data)
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
yield from component.async_setup(config)
@asyncio.coroutine
def async_alarm_service_handler(service):
"""Map services to methods on Alarm."""
target_alarms = component.async_extract_from_service(service)
code = service.data.get(ATTR_CODE)
method = "async_{}".format(SERVICE_TO_METHOD[service.service])
for alarm in target_alarms:
yield from getattr(alarm, method)(code)
update_tasks = []
for alarm in target_alarms:
if not alarm.should_poll:
continue
update_coro = hass.loop.create_task(
alarm.async_update_ha_state(True))
if hasattr(alarm, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
for service in SERVICE_TO_METHOD:
hass.services.async_register(
DOMAIN, service, async_alarm_service_handler,
descriptions.get(service), schema=ALARM_SERVICE_SCHEMA)
return True
# pylint: disable=no-self-use
class AlarmControlPanel(Entity):
"""An abstract class for alarm control devices."""
@@ -134,18 +153,50 @@ class AlarmControlPanel(Entity):
"""Send disarm command."""
raise NotImplementedError()
def async_alarm_disarm(self, code=None):
"""Send disarm command.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_disarm, code)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
raise NotImplementedError()
def async_alarm_arm_home(self, code=None):
"""Send arm home command.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_arm_home, code)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
raise NotImplementedError()
def async_alarm_arm_away(self, code=None):
"""Send arm away command.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_arm_away, code)
def alarm_trigger(self, code=None):
"""Send alarm trigger command."""
raise NotImplementedError()
def async_alarm_trigger(self, code=None):
"""Send alarm trigger command.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_trigger, code)
@property
def state_attributes(self):
"""Return the state attributes."""

View File

@@ -56,11 +56,6 @@ class AlarmDotCom(alarm.AlarmControlPanel):
self._password = password
self._state = STATE_UNKNOWN
@property
def should_poll(self):
"""No polling needed."""
return True
def update(self):
"""Fetch the latest state."""
self._state = self._alarm.state

View File

@@ -5,6 +5,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.concord232/
"""
import datetime
from datetime import timedelta
import logging
import requests
@@ -25,7 +26,7 @@ DEFAULT_HOST = 'localhost'
DEFAULT_NAME = 'CONCORD232'
DEFAULT_PORT = 5007
SCAN_INTERVAL = 1
SCAN_INTERVAL = timedelta(seconds=1)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
@@ -71,11 +72,6 @@ class Concord232Alarm(alarm.AlarmControlPanel):
self._alarm.last_partition_update = datetime.datetime.now()
self.update()
@property
def should_poll(self):
"""Polling needed."""
return True
@property
def name(self):
"""Return the name of the device."""
@@ -126,7 +122,3 @@ class Concord232Alarm(alarm.AlarmControlPanel):
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._alarm.arm('auto')
def alarm_trigger(self, code=None):
"""Alarm trigger command."""
raise NotImplementedError()

View File

@@ -97,7 +97,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
def _update_callback(self, partition):
"""Update HA state, if needed."""
if partition is None or int(partition) == self._partition_number:
self.hass.async_add_job(self.update_ha_state)
self.hass.async_add_job(self.async_update_ha_state())
@property
def code_format(self):

View File

@@ -116,7 +116,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
self._state = STATE_ALARM_DISARMED
self._state_ts = dt_util.utcnow()
self.update_ha_state()
self.schedule_update_ha_state()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
@@ -125,7 +125,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
self._state = STATE_ALARM_ARMED_HOME
self._state_ts = dt_util.utcnow()
self.update_ha_state()
self.schedule_update_ha_state()
if self._pending_time:
track_point_in_time(
@@ -139,7 +139,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
self._state = STATE_ALARM_ARMED_AWAY
self._state_ts = dt_util.utcnow()
self.update_ha_state()
self.schedule_update_ha_state()
if self._pending_time:
track_point_in_time(
@@ -151,7 +151,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
self._pre_trigger_state = self._state
self._state = STATE_ALARM_TRIGGERED
self._state_ts = dt_util.utcnow()
self.update_ha_state()
self.schedule_update_ha_state()
if self._trigger_time:
track_point_in_time(

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
STATE_UNKNOWN, CONF_NAME, CONF_HOST, CONF_PORT)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pynx584==0.2']
REQUIREMENTS = ['pynx584==0.4']
_LOGGER = logging.getLogger(__name__)
@@ -62,11 +62,6 @@ class NX584Alarm(alarm.AlarmControlPanel):
self._alarm.list_zones()
self._state = STATE_UNKNOWN
@property
def should_poll(self):
"""Polling needed."""
return True
@property
def name(self):
"""Return the name of the device."""
@@ -91,9 +86,11 @@ class NX584Alarm(alarm.AlarmControlPanel):
_LOGGER.error('Unable to connect to %(host)s: %(reason)s',
dict(host=self._url, reason=ex))
self._state = STATE_UNKNOWN
zones = []
except IndexError:
_LOGGER.error('nx584 reports no partitions')
self._state = STATE_UNKNOWN
zones = []
bypassed = False
for zone in zones:
@@ -122,7 +119,3 @@ class NX584Alarm(alarm.AlarmControlPanel):
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._alarm.arm('exit')
def alarm_trigger(self, code=None):
"""Alarm trigger command."""
raise NotImplementedError()

View File

@@ -61,11 +61,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
else:
self._state = STATE_UNKNOWN
@property
def should_poll(self):
"""Poll the SimpliSafe API."""
return True
@property
def name(self):
"""Return the name of the device."""
@@ -104,7 +99,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
return
self.simplisafe.set_state('off')
_LOGGER.info('SimpliSafe alarm disarming')
self.update()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
@@ -112,7 +106,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
return
self.simplisafe.set_state('home')
_LOGGER.info('SimpliSafe alarm arming home')
self.update()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
@@ -120,7 +113,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
return
self.simplisafe.set_state('away')
_LOGGER.info('SimpliSafe alarm arming away')
self.update()
def _validate_code(self, code, state):
"""Validate given code."""

View File

@@ -84,18 +84,15 @@ class VerisureAlarm(alarm.AlarmControlPanel):
hub.my_pages.alarm.set(code, 'DISARMED')
_LOGGER.info('verisure alarm disarming')
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
hub.my_pages.alarm.set(code, 'ARMED_HOME')
_LOGGER.info('verisure alarm arming home')
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
hub.my_pages.alarm.set(code, 'ARMED_AWAY')
_LOGGER.info('verisure alarm arming away')
hub.my_pages.alarm.wait_while_pending()
self.update()

View File

@@ -0,0 +1,75 @@
"""
Interfaces with Wink Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.wink/
"""
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import (STATE_UNKNOWN,
STATE_ALARM_DISARMED,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY)
from homeassistant.components.wink import WinkDevice, DOMAIN
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['wink']
STATE_ALARM_PRIVACY = 'Private'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink platform."""
import pywink
for camera in pywink.get_cameras():
# get_cameras returns multiple device types.
# Only add those that aren't sensors.
try:
camera.capability()
except AttributeError:
_id = camera.object_id() + camera.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkCameraDevice(camera, hass)])
class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel):
"""Representation a Wink camera alarm."""
def __init__(self, wink, hass):
"""Initialize the Wink alarm."""
super().__init__(wink, hass)
@property
def state(self):
"""Return the state of the device."""
wink_state = self.wink.state()
if wink_state == "away":
state = STATE_ALARM_ARMED_AWAY
elif wink_state == "home":
state = STATE_ALARM_DISARMED
elif wink_state == "night":
state = STATE_ALARM_ARMED_HOME
else:
state = STATE_UNKNOWN
return state
def alarm_disarm(self, code=None):
"""Send disarm command."""
self.wink.set_mode("home")
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self.wink.set_mode("night")
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self.wink.set_mode("away")
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'private': self.wink.private()
}

View File

@@ -0,0 +1,275 @@
"""
Support for repeating alerts when conditions are met.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alert/
"""
import asyncio
from datetime import datetime, timedelta
import logging
import os
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (CONF_ENTITY_ID, STATE_IDLE, CONF_NAME,
CONF_STATE, STATE_ON, STATE_OFF,
SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE, ATTR_ENTITY_ID)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers import service, event
from homeassistant.util.async import run_callback_threadsafe
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'alert'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_CAN_ACK = 'can_acknowledge'
CONF_NOTIFIERS = 'notifiers'
CONF_REPEAT = 'repeat'
CONF_SKIP_FIRST = 'skip_first'
ALERT_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_STATE, default=STATE_ON): cv.string,
vol.Required(CONF_REPEAT): vol.All(cv.ensure_list, [vol.Coerce(float)]),
vol.Required(CONF_CAN_ACK, default=True): cv.boolean,
vol.Required(CONF_SKIP_FIRST, default=False): cv.boolean,
vol.Required(CONF_NOTIFIERS): cv.ensure_list})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: ALERT_SCHEMA,
}),
}, extra=vol.ALLOW_EXTRA)
ALERT_SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_on(hass, entity_id):
"""Return if the alert is firing and not acknowledged."""
return hass.states.is_state(entity_id, STATE_ON)
def turn_on(hass, entity_id):
"""Reset the alert."""
run_callback_threadsafe(hass.loop, async_turn_on, hass, entity_id)
@callback
def async_turn_on(hass, entity_id):
"""Async reset the alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_add_job(
hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data))
def turn_off(hass, entity_id):
"""Acknowledge alert."""
run_callback_threadsafe(hass.loop, async_turn_off, hass, entity_id)
@callback
def async_turn_off(hass, entity_id):
"""Async acknowledge the alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_add_job(
hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data))
def toggle(hass, entity_id):
"""Toggle acknowledgement of alert."""
run_callback_threadsafe(hass.loop, async_toggle, hass, entity_id)
@callback
def async_toggle(hass, entity_id):
"""Async toggle acknowledgement of alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_add_job(
hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data))
@asyncio.coroutine
def async_setup(hass, config):
"""Setup alert component."""
alerts = config.get(DOMAIN)
all_alerts = {}
@asyncio.coroutine
def async_handle_alert_service(service_call):
"""Handle calls to alert services."""
alert_ids = service.extract_entity_ids(hass, service_call)
for alert_id in alert_ids:
alert = all_alerts[alert_id]
if service_call.service == SERVICE_TURN_ON:
yield from alert.async_turn_on()
elif service_call.service == SERVICE_TOGGLE:
yield from alert.async_toggle()
else:
yield from alert.async_turn_off()
# setup alerts
for entity_id, alert in alerts.items():
entity = Alert(hass, entity_id,
alert[CONF_NAME], alert[CONF_ENTITY_ID],
alert[CONF_STATE], alert[CONF_REPEAT],
alert[CONF_SKIP_FIRST], alert[CONF_NOTIFIERS],
alert[CONF_CAN_ACK])
all_alerts[entity.entity_id] = entity
# read descriptions
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
descriptions = descriptions.get(DOMAIN, {})
# setup service calls
hass.services.async_register(
DOMAIN, SERVICE_TURN_OFF, async_handle_alert_service,
descriptions.get(SERVICE_TURN_OFF), schema=ALERT_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_alert_service,
descriptions.get(SERVICE_TURN_ON), schema=ALERT_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handle_alert_service,
descriptions.get(SERVICE_TOGGLE), schema=ALERT_SERVICE_SCHEMA)
tasks = [alert.async_update_ha_state() for alert in all_alerts.values()]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
return True
class Alert(ToggleEntity):
"""Representation of an alert."""
def __init__(self, hass, entity_id, name, watched_entity_id, state,
repeat, skip_first, notifiers, can_ack):
"""Initialize the alert."""
self.hass = hass
self._name = name
self._alert_state = state
self._skip_first = skip_first
self._notifiers = notifiers
self._can_ack = can_ack
self._delay = [timedelta(minutes=val) for val in repeat]
self._next_delay = 0
self._firing = False
self._ack = False
self._cancel = None
self.entity_id = ENTITY_ID_FORMAT.format(entity_id)
event.async_track_state_change(hass, watched_entity_id,
self.watched_entity_change)
@property
def name(self):
"""Return the name of the alert."""
return self._name
@property
def should_poll(self):
"""HASS need not poll these entities."""
return False
@property
def state(self):
"""Return the alert status."""
if self._firing:
if self._ack:
return STATE_OFF
return STATE_ON
return STATE_IDLE
@property
def hidden(self):
"""Hide the alert when it is not firing."""
return not self._can_ack or not self._firing
@asyncio.coroutine
def watched_entity_change(self, entity, from_state, to_state):
"""Determine if the alert should start or stop."""
_LOGGER.debug('Watched entity (%s) has changed.', entity)
if to_state.state == self._alert_state and not self._firing:
yield from self.begin_alerting()
if to_state.state != self._alert_state and self._firing:
yield from self.end_alerting()
@asyncio.coroutine
def begin_alerting(self):
"""Begin the alert procedures."""
_LOGGER.debug('Beginning Alert: %s', self._name)
self._ack = False
self._firing = True
self._next_delay = 0
if not self._skip_first:
yield from self._notify()
else:
yield from self._schedule_notify()
self.hass.async_add_job(self.async_update_ha_state)
@asyncio.coroutine
def end_alerting(self):
"""End the alert procedures."""
_LOGGER.debug('Ending Alert: %s', self._name)
self._cancel()
self._ack = False
self._firing = False
self.hass.async_add_job(self.async_update_ha_state)
@asyncio.coroutine
def _schedule_notify(self):
"""Schedule a notification."""
delay = self._delay[self._next_delay]
next_msg = datetime.now() + delay
self._cancel = \
event.async_track_point_in_time(self.hass, self._notify, next_msg)
self._next_delay = min(self._next_delay + 1, len(self._delay) - 1)
@asyncio.coroutine
def _notify(self, *args):
"""Send the alert notification."""
if not self._firing:
return
if not self._ack:
_LOGGER.info('Alerting: %s', self._name)
for target in self._notifiers:
yield from self.hass.services.async_call(
'notify', target, {'message': self._name})
yield from self._schedule_notify()
@asyncio.coroutine
def async_turn_on(self):
"""Async Unacknowledge alert."""
_LOGGER.debug('Reset Alert: %s', self._name)
self._ack = False
yield from self.async_update_ha_state()
@asyncio.coroutine
def async_turn_off(self):
"""Async Acknowledge alert."""
_LOGGER.debug('Acknowledged Alert: %s', self._name)
self._ack = True
yield from self.async_update_ha_state()
@asyncio.coroutine
def async_toggle(self):
"""Async toggle alert."""
if self._ack:
return self.async_turn_on()
return self.async_turn_off()

View File

@@ -203,11 +203,12 @@ class AlexaResponse(object):
self.reprompt = None
self.session_attributes = {}
self.should_end_session = True
self.variables = {}
if intent is not None and 'slots' in intent:
self.variables = {key: value['value'] for key, value
in intent['slots'].items() if 'value' in value}
else:
self.variables = {}
for key, value in intent['slots'].items():
if 'value' in value:
underscored_key = key.replace('.', '_')
self.variables[underscored_key] = value['value']
def add_card(self, card_type, title, content):
"""Add a card to the response."""

View File

@@ -133,6 +133,9 @@ class APIEventStream(HomeAssistantView):
except asyncio.TimeoutError:
yield from to_write.put(STREAM_PING_PAYLOAD)
except asyncio.CancelledError:
_LOGGER.debug('STREAM %s ABORT', id(stop_obj))
finally:
_LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj))
unsub_stream()

View File

@@ -0,0 +1,172 @@
"""
Support for API.AI webhook.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/apiai/
"""
import asyncio
import copy
import logging
import voluptuous as vol
from homeassistant.const import PROJECT_NAME, HTTP_BAD_REQUEST
from homeassistant.helpers import template, script, config_validation as cv
from homeassistant.components.http import HomeAssistantView
_LOGGER = logging.getLogger(__name__)
INTENTS_API_ENDPOINT = '/api/apiai'
CONF_INTENTS = 'intents'
CONF_SPEECH = 'speech'
CONF_ACTION = 'action'
CONF_ASYNC_ACTION = 'async_action'
DEFAULT_CONF_ASYNC_ACTION = False
DOMAIN = 'apiai'
DEPENDENCIES = ['http']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
CONF_INTENTS: {
cv.string: {
vol.Optional(CONF_SPEECH): cv.template,
vol.Optional(CONF_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_ASYNC_ACTION,
default=DEFAULT_CONF_ASYNC_ACTION): cv.boolean
}
}
}
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Activate API.AI component."""
intents = config[DOMAIN].get(CONF_INTENTS, {})
hass.http.register_view(ApiaiIntentsView(hass, intents))
return True
class ApiaiIntentsView(HomeAssistantView):
"""Handle API.AI requests."""
url = INTENTS_API_ENDPOINT
name = 'api:apiai'
def __init__(self, hass, intents):
"""Initialize API.AI view."""
super().__init__()
self.hass = hass
intents = copy.deepcopy(intents)
template.attach(hass, intents)
for name, intent in intents.items():
if CONF_ACTION in intent:
intent[CONF_ACTION] = script.Script(
hass, intent[CONF_ACTION], "Apiai intent {}".format(name))
self.intents = intents
@asyncio.coroutine
def post(self, request):
"""Handle API.AI."""
data = yield from request.json()
_LOGGER.debug('Received Apiai request: %s', data)
req = data.get('result')
if req is None:
_LOGGER.error('Received invalid data from Apiai: %s', data)
return self.json_message('Expected result value not received',
HTTP_BAD_REQUEST)
action_incomplete = req['actionIncomplete']
if action_incomplete:
return None
# use intent to no mix HASS actions with this parameter
intent = req.get('action')
parameters = req.get('parameters')
# contexts = req.get('contexts')
response = ApiaiResponse(parameters)
# Default Welcome Intent
# Maybe is better to handle this in api.ai directly?
#
# if intent == 'input.welcome':
# response.add_speech(
# "Hello, and welcome to the future. How may I help?")
# return self.json(response)
if intent == "":
_LOGGER.warning('Received intent with empty action')
response.add_speech(
"You have not defined an action in your api.ai intent.")
return self.json(response)
config = self.intents.get(intent)
if config is None:
_LOGGER.warning('Received unknown intent %s', intent)
response.add_speech(
"Intent '%s' is not yet configured within Home Assistant." %
intent)
return self.json(response)
speech = config.get(CONF_SPEECH)
action = config.get(CONF_ACTION)
async_action = config.get(CONF_ASYNC_ACTION)
if action is not None:
# API.AI expects a response in less than 5s
if async_action:
# Do not wait for the action to be executed.
# Needed if the action will take longer than 5s to execute
self.hass.async_add_job(action.async_run(response.parameters))
else:
# Wait for the action to be executed so we can use results to
# render the answer
yield from action.async_run(response.parameters)
# pylint: disable=unsubscriptable-object
if speech is not None:
response.add_speech(speech)
return self.json(response)
class ApiaiResponse(object):
"""Help generating the response for API.AI."""
def __init__(self, parameters):
"""Initialize the response."""
self.speech = None
self.parameters = {}
# Parameter names replace '.' and '-' for '_'
for key, value in parameters.items():
underscored_key = key.replace('.', '_').replace('-', '_')
self.parameters[underscored_key] = value
def add_speech(self, text):
"""Add speech to the response."""
assert self.speech is None
if isinstance(text, template.Template):
text = text.async_render(self.parameters)
self.speech = text
def as_dict(self):
"""Return response in an API.AI valid dict."""
return {
'speech': self.speech,
'displayText': self.speech,
'source': PROJECT_NAME,
}

View File

@@ -166,7 +166,9 @@ def async_setup(hass, config):
for entity in component.async_extract_from_service(service_call):
tasks.append(entity.async_trigger(
service_call.data.get(ATTR_VARIABLES), True))
yield from asyncio.wait(tasks, loop=hass.loop)
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def turn_onoff_service_handler(service_call):
@@ -175,7 +177,9 @@ def async_setup(hass, config):
method = 'async_{}'.format(service_call.service)
for entity in component.async_extract_from_service(service_call):
tasks.append(getattr(entity, method)())
yield from asyncio.wait(tasks, loop=hass.loop)
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def toggle_service_handler(service_call):
@@ -186,7 +190,9 @@ def async_setup(hass, config):
tasks.append(entity.async_turn_off())
else:
tasks.append(entity.async_turn_on())
yield from asyncio.wait(tasks, loop=hass.loop)
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def reload_service_handler(service_call):

View File

@@ -4,6 +4,8 @@ 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
"""
import json
import voluptuous as vol
from homeassistant.core import callback
@@ -31,13 +33,20 @@ def async_trigger(hass, config, action):
def mqtt_automation_listener(msg_topic, msg_payload, qos):
"""Listen for MQTT messages."""
if payload is None or payload == msg_payload:
data = {
'platform': 'mqtt',
'topic': msg_topic,
'payload': msg_payload,
'qos': qos,
}
try:
data['payload_json'] = json.loads(msg_payload)
except ValueError:
pass
hass.async_run_job(action, {
'trigger': {
'platform': 'mqtt',
'topic': msg_topic,
'payload': msg_payload,
'qos': qos,
}
'trigger': data
})
return mqtt.async_subscribe(hass, topic, mqtt_automation_listener)

View File

@@ -64,10 +64,19 @@ def async_trigger(hass, config, action):
call_action()
return
@callback
def clear_listener():
"""Clear all unsub listener."""
nonlocal async_remove_state_for_cancel
nonlocal async_remove_state_for_listener
async_remove_state_for_listener = None
async_remove_state_for_cancel = None
@callback
def state_for_listener(now):
"""Fire on state changes after a delay and calls action."""
async_remove_state_for_cancel()
clear_listener()
call_action()
@callback
@@ -77,6 +86,7 @@ def async_trigger(hass, config, action):
return
async_remove_state_for_listener()
async_remove_state_for_cancel()
clear_listener()
async_remove_state_for_listener = async_track_point_in_utc_time(
hass, state_for_listener, dt_util.utcnow() + time_delta)

View File

@@ -0,0 +1,74 @@
"""
Support for controlling GPIO pins of a Beaglebone Black.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/bbb_gpio/
"""
import logging
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
REQUIREMENTS = ['Adafruit_BBIO==1.0.0']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'bbb_gpio'
# pylint: disable=no-member
def setup(hass, config):
"""Set up the BeagleBone Black GPIO component."""
# pylint: disable=import-error
import Adafruit_BBIO.GPIO as GPIO
def cleanup_gpio(event):
"""Stuff to do before stopping."""
GPIO.cleanup()
def prepare_gpio(event):
"""Stuff to do when home assistant starts."""
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio)
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio)
return True
# noqa: F821
def setup_output(pin):
"""Setup a GPIO as output."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.setup(pin, GPIO.OUT)
def setup_input(pin, pull_mode):
"""Setup a GPIO as input."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.setup(pin, GPIO.IN, # noqa: F821
GPIO.PUD_DOWN if pull_mode == 'DOWN' # noqa: F821
else GPIO.PUD_UP) # noqa: F821
def write_output(pin, value):
"""Write a value to a GPIO."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.output(pin, value)
def read_input(pin):
"""Read a value from a GPIO."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
return GPIO.input(pin) is GPIO.HIGH
def edge_detect(pin, event_callback, bounce):
"""Add detection for RISING and FALLING events."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.add_event_detect(
pin, GPIO.BOTH, callback=event_callback, bouncetime=bounce)

View File

@@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor/
"""
import asyncio
from datetime import timedelta
import logging
import voluptuous as vol
@@ -15,7 +16,7 @@ from homeassistant.const import (STATE_ON, STATE_OFF)
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
DOMAIN = 'binary_sensor'
SCAN_INTERVAL = 30
SCAN_INTERVAL = timedelta(seconds=30)
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SENSOR_CLASSES = [

View File

@@ -30,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the aREST binary sensor."""
"""Set up the aREST binary sensor."""
resource = config.get(CONF_RESOURCE)
pin = config.get(CONF_PIN)
sensor_class = config.get(CONF_SENSOR_CLASS)
@@ -38,13 +38,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
try:
response = requests.get(resource, timeout=10).json()
except requests.exceptions.MissingSchema:
_LOGGER.error('Missing resource or schema in configuration. '
'Add http:// to your URL.')
_LOGGER.error("Missing resource or schema in configuration. "
"Add http:// to your URL")
return False
except requests.exceptions.ConnectionError:
_LOGGER.error('No route to device at %s. '
'Please check the IP address in the configuration file.',
resource)
_LOGGER.error("No route to device at %s", resource)
return False
arest = ArestData(resource, pin)
@@ -67,10 +65,10 @@ class ArestBinarySensor(BinarySensorDevice):
self.update()
if self._pin is not None:
request = requests.get('{}/mode/{}/i'.format
(self._resource, self._pin), timeout=10)
request = requests.get(
'{}/mode/{}/i'.format(self._resource, self._pin), timeout=10)
if request.status_code is not 200:
_LOGGER.error("Can't set mode. Is device offline?")
_LOGGER.error("Can't set mode of %s", self._resource)
@property
def name(self):
@@ -109,5 +107,4 @@ class ArestData(object):
self._resource, self._pin), timeout=10)
self.data = {'state': response.json()['return_value']}
except requests.exceptions.ConnectionError:
_LOGGER.error("No route to device '%s'. Is device offline?",
self._resource)
_LOGGER.error("No route to device '%s'", self._resource)

View File

@@ -0,0 +1,89 @@
"""
Support for binary sensor using Beaglebone Black GPIO.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.bbb_gpio/
"""
import logging
import voluptuous as vol
import homeassistant.components.bbb_gpio as bbb_gpio
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import (DEVICE_DEFAULT_NAME, CONF_NAME)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['bbb_gpio']
CONF_PINS = 'pins'
CONF_BOUNCETIME = 'bouncetime'
CONF_INVERT_LOGIC = 'invert_logic'
CONF_PULL_MODE = 'pull_mode'
DEFAULT_BOUNCETIME = 50
DEFAULT_INVERT_LOGIC = False
DEFAULT_PULL_MODE = 'UP'
PIN_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE):
vol.In(['UP', 'DOWN'])
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PINS, default={}):
vol.Schema({cv.string: PIN_SCHEMA}),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Beaglebone Black GPIO devices."""
pins = config.get(CONF_PINS)
binary_sensors = []
for pin, params in pins.items():
binary_sensors.append(BBBGPIOBinarySensor(pin, params))
add_devices(binary_sensors)
class BBBGPIOBinarySensor(BinarySensorDevice):
"""Represent a binary sensor that uses Beaglebone Black GPIO."""
def __init__(self, pin, params):
"""Initialize the Beaglebone Black binary sensor."""
self._pin = pin
self._name = params.get(CONF_NAME) or DEVICE_DEFAULT_NAME
self._bouncetime = params.get(CONF_BOUNCETIME)
self._pull_mode = params.get(CONF_PULL_MODE)
self._invert_logic = params.get(CONF_INVERT_LOGIC)
bbb_gpio.setup_input(self._pin, self._pull_mode)
self._state = bbb_gpio.read_input(self._pin)
def read_gpio(pin):
"""Read state from GPIO."""
self._state = bbb_gpio.read_input(self._pin)
self.schedule_update_ha_state()
bbb_gpio.edge_detect(self._pin, read_gpio, self._bouncetime)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return the state of the entity."""
return self._state != self._invert_logic

View File

@@ -4,6 +4,7 @@ Support for custom shell commands to retrieve values.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.command_line/
"""
from datetime import timedelta
import logging
import voluptuous as vol
@@ -22,7 +23,7 @@ DEFAULT_NAME = 'Binary Command Sensor'
DEFAULT_PAYLOAD_ON = 'ON'
DEFAULT_PAYLOAD_OFF = 'OFF'
SCAN_INTERVAL = 60
SCAN_INTERVAL = timedelta(seconds=60)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COMMAND): cv.string,

View File

@@ -27,7 +27,7 @@ DEFAULT_NAME = 'Alarm'
DEFAULT_PORT = '5007'
DEFAULT_SSL = False
SCAN_INTERVAL = 1
SCAN_INTERVAL = datetime.timedelta(seconds=1)
ZONE_TYPES_SCHEMA = vol.Schema({
cv.positive_int: vol.In(SENSOR_CLASSES),

View File

@@ -29,7 +29,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Digital Ocean droplet sensor."""
"""Set up the Digital Ocean droplet sensor."""
digital_ocean = get_component('digital_ocean')
droplets = config.get(CONF_DROPLETS)
@@ -68,7 +68,7 @@ class DigitalOceanBinarySensor(BinarySensorDevice):
return DEFAULT_SENSOR_CLASS
@property
def state_attributes(self):
def device_state_attributes(self):
"""Return the state attributes of the Digital Ocean droplet."""
return {
ATTR_CREATED_AT: self.data.created_at,

View File

@@ -20,7 +20,7 @@ DEPENDENCIES = ['enocean']
DEFAULT_NAME = 'EnOcean binary sensor'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ID): cv.string,
vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_SENSOR_CLASS, default=None): SENSOR_CLASSES_SCHEMA,
})

View File

@@ -1,224 +0,0 @@
"""
Provides a binary sensor which is a collection of ffmpeg tools.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ffmpeg/
"""
import logging
from os import path
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DOMAIN)
from homeassistant.components.ffmpeg import (
get_binary, run_test, CONF_INPUT, CONF_OUTPUT, CONF_EXTRA_ARGUMENTS)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_NAME,
ATTR_ENTITY_ID)
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
SERVICE_RESTART = 'ffmpeg_restart'
FFMPEG_SENSOR_NOISE = 'noise'
FFMPEG_SENSOR_MOTION = 'motion'
MAP_FFMPEG_BIN = [
FFMPEG_SENSOR_NOISE,
FFMPEG_SENSOR_MOTION
]
CONF_TOOL = 'tool'
CONF_PEAK = 'peak'
CONF_DURATION = 'duration'
CONF_RESET = 'reset'
CONF_CHANGES = 'changes'
CONF_REPEAT = 'repeat'
CONF_REPEAT_TIME = 'repeat_time'
DEFAULT_NAME = 'FFmpeg'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_TOOL): vol.In(MAP_FFMPEG_BIN),
vol.Required(CONF_INPUT): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string,
vol.Optional(CONF_OUTPUT): cv.string,
vol.Optional(CONF_PEAK, default=-30): vol.Coerce(int),
vol.Optional(CONF_DURATION, default=1):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_RESET, default=10):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_CHANGES, default=10):
vol.All(vol.Coerce(float), vol.Range(min=0, max=99)),
vol.Optional(CONF_REPEAT, default=0):
vol.All(vol.Coerce(int), vol.Range(min=0)),
vol.Optional(CONF_REPEAT_TIME, default=0):
vol.All(vol.Coerce(int), vol.Range(min=0)),
})
SERVICE_RESTART_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def restart(hass, entity_id=None):
"""Restart a ffmpeg process on entity."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_RESTART, data)
# list of all ffmpeg sensors
DEVICES = []
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Create the binary sensor."""
from haffmpeg import SensorNoise, SensorMotion
# check source
if not run_test(hass, config.get(CONF_INPUT)):
return
# generate sensor object
if config.get(CONF_TOOL) == FFMPEG_SENSOR_NOISE:
entity = FFmpegNoise(SensorNoise, config)
else:
entity = FFmpegMotion(SensorMotion, config)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, entity.shutdown_ffmpeg)
# add to system
add_entities([entity])
DEVICES.append(entity)
# exists service?
if hass.services.has_service(DOMAIN, SERVICE_RESTART):
return
descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))
# register service
def _service_handle_restart(service):
"""Handle service binary_sensor.ffmpeg_restart."""
entity_ids = service.data.get('entity_id')
if entity_ids:
_devices = [device for device in DEVICES
if device.entity_id in entity_ids]
else:
_devices = DEVICES
for device in _devices:
device.restart_ffmpeg()
hass.services.register(DOMAIN, SERVICE_RESTART,
_service_handle_restart,
descriptions.get(SERVICE_RESTART),
schema=SERVICE_RESTART_SCHEMA)
class FFmpegBinarySensor(BinarySensorDevice):
"""A binary sensor which use ffmpeg for noise detection."""
def __init__(self, ffobj, config):
"""Constructor for binary sensor noise detection."""
self._state = False
self._config = config
self._name = config.get(CONF_NAME)
self._ffmpeg = ffobj(get_binary(), self._callback)
self._start_ffmpeg(config)
def _callback(self, state):
"""HA-FFmpeg callback for noise detection."""
self._state = state
self.schedule_update_ha_state()
def _start_ffmpeg(self, config):
"""Start a FFmpeg instance."""
raise NotImplementedError
def shutdown_ffmpeg(self, event):
"""For STOP event to shutdown ffmpeg."""
self._ffmpeg.close()
def restart_ffmpeg(self):
"""Restart ffmpeg with new config."""
self._ffmpeg.close()
self._start_ffmpeg(self._config)
@property
def is_on(self):
"""True if the binary sensor is on."""
return self._state
@property
def should_poll(self):
"""Return True if entity has to be polled for state."""
return False
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def available(self):
"""Return True if entity is available."""
return self._ffmpeg.is_running
class FFmpegNoise(FFmpegBinarySensor):
"""A binary sensor which use ffmpeg for noise detection."""
def _start_ffmpeg(self, config):
"""Start a FFmpeg instance."""
# init config
self._ffmpeg.set_options(
time_duration=config.get(CONF_DURATION),
time_reset=config.get(CONF_RESET),
peak=config.get(CONF_PEAK),
)
# run
self._ffmpeg.open_sensor(
input_source=config.get(CONF_INPUT),
output_dest=config.get(CONF_OUTPUT),
extra_cmd=config.get(CONF_EXTRA_ARGUMENTS),
)
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return "sound"
class FFmpegMotion(FFmpegBinarySensor):
"""A binary sensor which use ffmpeg for noise detection."""
def _start_ffmpeg(self, config):
"""Start a FFmpeg instance."""
# init config
self._ffmpeg.set_options(
time_reset=config.get(CONF_RESET),
time_repeat=config.get(CONF_REPEAT_TIME),
repeat=config.get(CONF_REPEAT),
changes=config.get(CONF_CHANGES),
)
# run
self._ffmpeg.open_sensor(
input_source=config.get(CONF_INPUT),
extra_cmd=config.get(CONF_EXTRA_ARGUMENTS),
)
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return "motion"

View File

@@ -0,0 +1,127 @@
"""
Provides a binary sensor which is a collection of ffmpeg tools.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ffmpeg_motion/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.ffmpeg import (
FFmpegBase, DATA_FFMPEG, CONF_INPUT, CONF_EXTRA_ARGUMENTS,
CONF_INITIAL_STATE)
from homeassistant.const import CONF_NAME
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
CONF_RESET = 'reset'
CONF_CHANGES = 'changes'
CONF_REPEAT = 'repeat'
CONF_REPEAT_TIME = 'repeat_time'
DEFAULT_NAME = 'FFmpeg Motion'
DEFAULT_INIT_STATE = True
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_INPUT): cv.string,
vol.Optional(CONF_INITIAL_STATE, default=DEFAULT_INIT_STATE): cv.boolean,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string,
vol.Optional(CONF_RESET, default=10):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_CHANGES, default=10):
vol.All(vol.Coerce(float), vol.Range(min=0, max=99)),
vol.Inclusive(CONF_REPEAT, 'repeat'):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Inclusive(CONF_REPEAT_TIME, 'repeat'):
vol.All(vol.Coerce(int), vol.Range(min=1)),
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Create the binary sensor."""
manager = hass.data[DATA_FFMPEG]
# check source
if not manager.async_run_test(config.get(CONF_INPUT)):
return
# generate sensor object
entity = FFmpegMotion(hass, manager, config)
# add to system
manager.async_register_device(entity)
yield from async_add_devices([entity])
class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice):
"""A binary sensor which use ffmpeg for noise detection."""
def __init__(self, hass, config):
"""Constructor for binary sensor noise detection."""
super().__init__(config.get(CONF_INITIAL_STATE))
self._state = False
self._config = config
self._name = config.get(CONF_NAME)
@callback
def _async_callback(self, state):
"""HA-FFmpeg callback for noise detection."""
self._state = state
self.hass.async_add_job(self.async_update_ha_state())
@property
def is_on(self):
"""True if the binary sensor is on."""
return self._state
@property
def name(self):
"""Return the name of the entity."""
return self._name
class FFmpegMotion(FFmpegBinarySensor):
"""A binary sensor which use ffmpeg for noise detection."""
def __init__(self, hass, manager, config):
"""Initialize ffmpeg motion binary sensor."""
from haffmpeg import SensorMotion
super().__init__(hass, config)
self.ffmpeg = SensorMotion(
manager.binary, hass.loop, self._async_callback)
def async_start_ffmpeg(self):
"""Start a FFmpeg instance.
This method must be run in the event loop and returns a coroutine.
"""
# init config
self.ffmpeg.set_options(
time_reset=self._config.get(CONF_RESET),
time_repeat=self._config.get(CONF_REPEAT_TIME, 0),
repeat=self._config.get(CONF_REPEAT, 0),
changes=self._config.get(CONF_CHANGES),
)
# run
return self.ffmpeg.open_sensor(
input_source=self._config.get(CONF_INPUT),
extra_cmd=self._config.get(CONF_EXTRA_ARGUMENTS),
)
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return "motion"

View File

@@ -0,0 +1,96 @@
"""
Provides a binary sensor which is a collection of ffmpeg tools.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ffmpeg_noise/
"""
import asyncio
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA
from homeassistant.components.binary_sensor.ffmpeg_motion import (
FFmpegBinarySensor)
from homeassistant.components.ffmpeg import (
DATA_FFMPEG, CONF_INPUT, CONF_OUTPUT, CONF_EXTRA_ARGUMENTS,
CONF_INITIAL_STATE)
from homeassistant.const import CONF_NAME
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
CONF_PEAK = 'peak'
CONF_DURATION = 'duration'
CONF_RESET = 'reset'
DEFAULT_NAME = 'FFmpeg Noise'
DEFAULT_INIT_STATE = True
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_INPUT): cv.string,
vol.Optional(CONF_INITIAL_STATE, default=DEFAULT_INIT_STATE): cv.boolean,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string,
vol.Optional(CONF_OUTPUT): cv.string,
vol.Optional(CONF_PEAK, default=-30): vol.Coerce(int),
vol.Optional(CONF_DURATION, default=1):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_RESET, default=10):
vol.All(vol.Coerce(int), vol.Range(min=1)),
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Create the binary sensor."""
manager = hass.data[DATA_FFMPEG]
# check source
if not manager.async_run_test(config.get(CONF_INPUT)):
return
# generate sensor object
entity = FFmpegNoise(hass, manager, config)
# add to system
manager.async_register_device(entity)
yield from async_add_devices([entity])
class FFmpegNoise(FFmpegBinarySensor):
"""A binary sensor which use ffmpeg for noise detection."""
def __init__(self, hass, manager, config):
"""Initialize ffmpeg noise binary sensor."""
from haffmpeg import SensorNoise
super().__init__(hass, config)
self.ffmpeg = SensorNoise(
manager.binary, hass.loop, self._async_callback)
def async_start_ffmpeg(self):
"""Start a FFmpeg instance.
This method must be run in the event loop and returns a coroutine.
"""
# init config
self.ffmpeg.set_options(
time_duration=self._config.get(CONF_DURATION),
time_reset=self._config.get(CONF_RESET),
peak=self._config.get(CONF_PEAK),
)
# run
return self.ffmpeg.open_sensor(
input_source=self._config.get(CONF_INPUT),
output_dest=self._config.get(CONF_OUTPUT),
extra_cmd=self._config.get(CONF_EXTRA_ARGUMENTS),
)
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return "sound"

View File

@@ -0,0 +1,241 @@
"""Contains functionality to use flic buttons as a binary sensor."""
import logging
import threading
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_HOST, CONF_PORT, CONF_DISCOVERY, CONF_TIMEOUT,
EVENT_HOMEASSISTANT_STOP)
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_TYPES = [CLICK_TYPE_SINGLE, CLICK_TYPE_DOUBLE, CLICK_TYPE_HOLD]
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"
# 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_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)])
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Setup the flic platform."""
import pyflic
# Initialize flic client responsible for
# connecting to buttons and retrieving events
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
discovery = config.get(CONF_DISCOVERY)
try:
client = pyflic.FlicClient(host, port)
except ConnectionRefusedError:
_LOGGER.error("Failed to connect to flic server.")
return
def new_button_callback(address):
"""Setup newly verified button as device in home assistant."""
setup_button(hass, config, add_entities, client, address)
client.on_new_verified_button = new_button_callback
if discovery:
start_scanning(config, add_entities, client)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
lambda event: client.close())
# Start the pyflic event handling thread
threading.Thread(target=client.handle_events).start()
def get_info_callback(items):
"""Add entities for already verified buttons."""
addresses = items["bd_addr_of_verified_buttons"] or []
for address in addresses:
setup_button(hass, config, add_entities, client, address)
# Get addresses of already verified buttons
client.get_info(get_info_callback)
def start_scanning(config, add_entities, client):
"""Start a new flic client for scanning & connceting to new buttons."""
import pyflic
scan_wizard = pyflic.ScanWizard()
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)
elif result != pyflic.ScanWizardResult.WizardFailedTimeout:
_LOGGER.warning("Failed to connect to button (%s). Reason: %s",
address, result)
# Restart scan wizard
start_scanning(config, add_entities, client)
scan_wizard.on_completed = scan_completed_callback
client.add_scan_wizard(scan_wizard)
def setup_button(hass, config, add_entities, client, address):
"""Setup single button device."""
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)
add_entities([button])
class FlicButton(BinarySensorDevice):
"""Representation of a flic button."""
def __init__(self, hass, client, address, timeout, ignored_click_types):
"""Initialize the flic button."""
import pyflic
self._hass = hass
self._address = address
self._timeout = timeout
self._is_down = False
self._ignored_click_types = ignored_click_types or []
self._hass_click_types = {
pyflic.ClickType.ButtonClick: CLICK_TYPE_SINGLE,
pyflic.ClickType.ButtonSingleClick: CLICK_TYPE_SINGLE,
pyflic.ClickType.ButtonDoubleClick: CLICK_TYPE_DOUBLE,
pyflic.ClickType.ButtonHold: CLICK_TYPE_HOLD,
}
self._channel = self._create_channel()
client.add_connection_channel(self._channel)
def _create_channel(self):
"""Create a new connection channel to the button."""
import pyflic
channel = pyflic.ButtonConnectionChannel(self._address)
channel.on_button_up_or_down = self._on_up_down
# If all types of clicks should be ignored, skip registering callbacks
if set(self._ignored_click_types) == set(CLICK_TYPES):
return channel
if CLICK_TYPE_DOUBLE in self._ignored_click_types:
# Listen to all but double click type events
channel.on_button_click_or_hold = self._on_click
elif CLICK_TYPE_HOLD in self._ignored_click_types:
# Listen to all but hold click type events
channel.on_button_single_or_double_click = self._on_click
else:
# Listen to all click type events
channel.on_button_single_or_double_click_or_hold = self._on_click
return channel
@property
def name(self):
"""Return the name of the device."""
return "flic_%s" % self.address.replace(":", "")
@property
def address(self):
"""Return the bluetooth address of the device."""
return self._address
@property
def is_on(self):
"""Return true if sensor is on."""
return self._is_down
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def state_attributes(self):
"""Return device specific state attributes."""
attr = super(FlicButton, self).state_attributes
attr["address"] = self.address
return attr
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")
if time_diff > self._timeout:
_LOGGER.warning(
"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.",
click_type, self.address, time_string)
return False
def _on_up_down(self, channel, click_type, was_queued, time_diff):
"""Update device state, if event was not queued."""
import pyflic
if was_queued and self._queued_event_check(click_type, time_diff):
return
self._is_down = click_type == pyflic.ClickType.ButtonDown
self.schedule_update_ha_state()
def _on_click(self, channel, click_type, was_queued, time_diff):
"""Fire click event, if event was not queued."""
# Return if click event was queued beyond allowed timeout
if was_queued and self._queued_event_check(click_type, time_diff):
return
# Return if click event is in ignored click types
hass_click_type = self._hass_click_types[click_type]
if hass_click_type in self._ignored_click_types:
return
self._hass.bus.fire(EVENT_NAME, {
EVENT_DATA_NAME: self.name,
EVENT_DATA_ADDRESS: self.address,
EVENT_DATA_QUEUED_TIME: time_diff,
EVENT_DATA_TYPE: hass_click_type
})
def _connection_status_changed(self, channel,
connection_status, disconnect_reason):
"""Remove device, if button disconnects."""
import pyflic
if connection_status == pyflic.ConnectionStatus.Disconnected:
_LOGGER.info("Button (%s) disconnected. Reason: %s",
self.address, disconnect_reason)
self.remove()

View File

@@ -0,0 +1,262 @@
"""
Support for Hikvision event stream events represented as binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.hikvision/
"""
import logging
from datetime import timedelta
import voluptuous as vol
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.util.dt import utcnow
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_HOST, CONF_PORT, CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_SSL, EVENT_HOMEASSISTANT_STOP, ATTR_LAST_TRIP_TIME, CONF_CUSTOMIZE)
REQUIREMENTS = ['pyhik==0.0.7', 'pydispatcher==2.0.5']
_LOGGER = logging.getLogger(__name__)
CONF_IGNORED = 'ignored'
CONF_DELAY = 'delay'
DEFAULT_PORT = 80
DEFAULT_IGNORED = False
DEFAULT_DELAY = 0
ATTR_DELAY = 'delay'
SENSOR_CLASS_MAP = {
'Motion': 'motion',
'Line Crossing': 'motion',
'IO Trigger': None,
'Field Detection': 'motion',
'Video Loss': None,
'Tamper Detection': 'motion',
'Shelter Alarm': None,
'Disk Full': None,
'Disk Error': None,
'Net Interface Broken': 'connectivity',
'IP Conflict': 'connectivity',
'Illegal Access': None,
'Video Mismatch': None,
'Bad Video': None,
'PIR Alarm': 'motion',
'Face Detection': 'motion',
}
CUSTOMIZE_SCHEMA = vol.Schema({
vol.Optional(CONF_IGNORED, default=DEFAULT_IGNORED): cv.boolean,
vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.positive_int
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=None): cv.string,
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_SSL, default=False): cv.boolean,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_CUSTOMIZE, default={}):
vol.Schema({cv.string: CUSTOMIZE_SCHEMA}),
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Setup Hikvision binary sensor devices."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
customize = config.get(CONF_CUSTOMIZE)
if config.get(CONF_SSL):
protocol = "https"
else:
protocol = "http"
url = '{}://{}'.format(protocol, host)
data = HikvisionData(hass, url, port, name, username, password)
if data.sensors is None:
_LOGGER.error('Hikvision event stream has no data, unable to setup.')
return False
entities = []
for sensor in data.sensors:
# Build sensor name, then parse customize config.
sensor_name = sensor.replace(' ', '_')
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))
add_entities(entities)
class HikvisionData(object):
"""Hikvision camera event stream object."""
def __init__(self, hass, url, port, name, username, password):
"""Initialize the data oject."""
from pyhik.hikvision import HikCamera
self._url = url
self._port = port
self._name = name
self._username = username
self._password = password
# Establish camera
self._cam = HikCamera(self._url, self._port,
self._username, self._password)
if self._name is None:
self._name = self._cam.get_name
# Start event stream
self._cam.start_stream()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.stop_hik)
def stop_hik(self, event):
"""Shutdown Hikvision subscriptions and subscription thread on exit."""
self._cam.disconnect()
@property
def sensors(self):
"""Return list of available sensors and their states."""
return self._cam.current_event_states
@property
def cam_id(self):
"""Return camera id."""
return self._cam.get_id
@property
def name(self):
"""Return camera name."""
return self._name
class HikvisionBinarySensor(BinarySensorDevice):
"""Representation of a Hikvision binary sensor."""
def __init__(self, hass, sensor, cam, delay):
"""Initialize the binary_sensor."""
from pydispatch import dispatcher
self._hass = hass
self._cam = cam
self._name = self._cam.name + ' ' + sensor
self._id = self._cam.cam_id + '.' + sensor
self._sensor = sensor
if delay is None:
self._delay = 0
else:
self._delay = delay
self._timer = None
# Form signal for dispatcher
signal = 'ValueChanged.{}'.format(self._cam.cam_id)
dispatcher.connect(self._update_callback,
signal=signal,
sender=self._sensor)
def _sensor_state(self):
"""Extract sensor state."""
return self._cam.sensors[self._sensor][0]
def _sensor_last_update(self):
"""Extract sensor last update time."""
return self._cam.sensors[self._sensor][3]
@property
def name(self):
"""Return the name of the Hikvision sensor."""
return self._name
@property
def unique_id(self):
"""Return an unique ID."""
return '{}.{}'.format(self.__class__, self._id)
@property
def is_on(self):
"""Return true if sensor is on."""
return self._sensor_state()
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
try:
return SENSOR_CLASS_MAP[self._sensor]
except KeyError:
# Sensor must be unknown to us, add as generic
return None
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_state_attributes(self):
"""Return the state attributes."""
attr = {}
attr[ATTR_LAST_TRIP_TIME] = self._sensor_last_update()
if self._delay != 0:
attr[ATTR_DELAY] = self._delay
return attr
def _update_callback(self, signal, sender):
"""Update the sensor's state, if needed."""
_LOGGER.debug('Dispatcher callback, signal: %s, sender: %s',
signal, sender)
if sender is not self._sensor:
return
if self._delay > 0 and not self.is_on:
# Set timer to wait until updating the state
def _delay_update(now):
"""Timer callback for sensor update."""
_LOGGER.debug('%s Called delayed (%ssec) update.',
self._name, self._delay)
self.schedule_update_ha_state()
self._timer = None
if self._timer is not None:
self._timer()
self._timer = None
self._timer = track_point_in_utc_time(
self._hass, _delay_update,
utcnow() + timedelta(seconds=self._delay))
elif self._delay > 0 and self.is_on:
# For delayed sensors kill any callbacks on true events and update
if self._timer is not None:
self._timer()
self._timer = None
self.schedule_update_ha_state()
else:
self.schedule_update_ha_state()

View File

@@ -17,6 +17,7 @@ DEPENDENCIES = ['homematic']
SENSOR_TYPES_CLASS = {
"Remote": None,
"ShutterContact": "opening",
"MaxShutterContact": "opening",
"IPShutterContact": "opening",
"Smoke": "smoke",
"SmokeV2": "smoke",

View File

@@ -0,0 +1,131 @@
"""
Support for International Space Station data sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.iss/
"""
import logging
from datetime import timedelta
import requests
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, ATTR_LONGITUDE, ATTR_LATITUDE)
from homeassistant.util import Throttle
REQUIREMENTS = ['pyiss==1.0.1']
_LOGGER = logging.getLogger(__name__)
ATTR_ISS_NEXT_RISE = 'next_rise'
ATTR_ISS_NUMBER_PEOPLE_SPACE = 'number_of_people_in_space'
CONF_SHOW_ON_MAP = 'show_on_map'
DEFAULT_NAME = 'ISS'
DEFAULT_SENSOR_CLASS = 'visible'
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_SHOW_ON_MAP, default=False): cv.boolean,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the ISS sensor."""
if None in (hass.config.latitude, hass.config.longitude):
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
return False
try:
iss_data = IssData(hass.config.latitude, hass.config.longitude)
iss_data.update()
except requests.exceptions.HTTPError as error:
_LOGGER.error(error)
return False
name = config.get(CONF_NAME)
show_on_map = config.get(CONF_SHOW_ON_MAP)
add_devices([IssBinarySensor(iss_data, name, show_on_map)], True)
class IssBinarySensor(BinarySensorDevice):
"""Implementation of the ISS binary sensor."""
def __init__(self, iss_data, name, show):
"""Initialize the sensor."""
self.iss_data = iss_data
self._state = None
self._name = name
self._show_on_map = show
self.update()
@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.iss_data.is_above if self.iss_data else False
@property
def sensor_class(self):
"""Return the class of this sensor."""
return DEFAULT_SENSOR_CLASS
@property
def device_state_attributes(self):
"""Return the state attributes."""
if self.iss_data:
attrs = {
ATTR_ISS_NUMBER_PEOPLE_SPACE:
self.iss_data.number_of_people_in_space,
ATTR_ISS_NEXT_RISE: self.iss_data.next_rise,
}
if self._show_on_map:
attrs[ATTR_LONGITUDE] = self.iss_data.position.get('longitude')
attrs[ATTR_LATITUDE] = self.iss_data.position.get('latitude')
else:
attrs['long'] = self.iss_data.position.get('longitude')
attrs['lat'] = self.iss_data.position.get('latitude')
return attrs
def update(self):
"""Get the latest data from ISS API and updates the states."""
self.iss_data.update()
class IssData(object):
"""Get data from the ISS API."""
def __init__(self, latitude, longitude):
"""Initialize the data object."""
self.is_above = None
self.next_rise = None
self.number_of_people_in_space = None
self.position = None
self.latitude = latitude
self.longitude = longitude
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data from the ISS API."""
import pyiss
try:
iss = pyiss.ISS()
self.is_above = iss.is_ISS_above(self.latitude, self.longitude)
self.next_rise = iss.next_rise(self.latitude, self.longitude)
self.number_of_people_in_space = iss.number_of_people_in_space()
self.position = iss.current_location()
except requests.exceptions.HTTPError as error:
_LOGGER.error(error)
return False

View File

@@ -36,7 +36,10 @@ PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the MQTT binary sensor."""
"""Set up the MQTT binary sensor."""
if discovery_info is not None:
config = PLATFORM_SCHEMA(discovery_info)
value_template = config.get(CONF_VALUE_TEMPLATE)
if value_template is not None:
value_template.hass = hass

View File

@@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
map_sv_types, devices, add_devices, MySensorsBinarySensor))
map_sv_types, devices, MySensorsBinarySensor, add_devices))
class MySensorsBinarySensor(

View File

@@ -7,15 +7,10 @@ https://home-assistant.io/components/binary_sensor.nest/
from itertools import chain
import logging
import voluptuous as vol
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.binary_sensor import (BinarySensorDevice)
from homeassistant.components.sensor.nest import NestSensor
from homeassistant.const import (CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS)
from homeassistant.components.nest import (
DATA_NEST, is_thermostat, is_camera)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_MONITORED_CONDITIONS
from homeassistant.components.nest import DATA_NEST
DEPENDENCIES = ['nest']
@@ -43,51 +38,47 @@ _BINARY_TYPES_DEPRECATED = [
_VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \
+ CAMERA_BINARY_TYPES
_VALID_BINARY_SENSOR_TYPES_WITH_DEPRECATED = _VALID_BINARY_SENSOR_TYPES \
+ _BINARY_TYPES_DEPRECATED
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS):
vol.All(cv.ensure_list,
[vol.In(_VALID_BINARY_SENSOR_TYPES_WITH_DEPRECATED)])
})
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Nest binary sensors."""
nest = hass.data[DATA_NEST]
conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_BINARY_SENSOR_TYPES)
if discovery_info is None:
return
for variable in conf:
nest = hass.data[DATA_NEST]
# Add all available binary sensors if no Nest binary sensor config is set
if discovery_info == {}:
conditions = _VALID_BINARY_SENSOR_TYPES
else:
conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {})
for variable in conditions:
if variable in _BINARY_TYPES_DEPRECATED:
wstr = (variable + " is no a longer supported "
"monitored_conditions. See "
"https://home-assistant.io/components/binary_sensor.nest/ "
"for valid options, or remove monitored_conditions "
"entirely to get a reasonable default")
"for valid options.")
_LOGGER.error(wstr)
sensors = []
device_chain = chain(nest.devices(),
nest.protect_devices(),
nest.camera_devices())
device_chain = chain(nest.thermostats(),
nest.smoke_co_alarms(),
nest.cameras())
for structure, device in device_chain:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conf
for variable in conditions
if variable in BINARY_TYPES]
sensors += [NestBinarySensor(structure, device, variable)
for variable in conf
for variable in conditions
if variable in CLIMATE_BINARY_TYPES
and is_thermostat(device)]
and device.is_thermostat]
if is_camera(device):
if device.is_camera:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conf
for variable in conditions
if variable in CAMERA_BINARY_TYPES]
for activity_zone in device.activity_zones:
sensors += [NestActivityZoneSensor(structure,
@@ -115,13 +106,14 @@ class NestActivityZoneSensor(NestBinarySensor):
def __init__(self, structure, device, zone):
"""Initialize the sensor."""
super(NestActivityZoneSensor, self).__init__(structure, device, None)
super(NestActivityZoneSensor, self).__init__(structure, device, "")
self.zone = zone
self._name = "{} {} activity".format(self._name, self.zone.name)
@property
def name(self):
"""Return the name of the nest, if any."""
return "{} {} activity".format(self._name, self.zone.name)
return self._name
def update(self):
"""Retrieve latest state."""

View File

@@ -1,19 +1,19 @@
"""
Support for the Netatmo binary sensors.
The binary sensors based on events seen by the NetatmoCamera
The binary sensors based on events seen by the Netatmo cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.netatmo/
https://home-assistant.io/components/binary_sensor.netatmo/.
"""
import logging
import voluptuous as vol
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.netatmo import WelcomeData
from homeassistant.components.netatmo import CameraData
from homeassistant.loader import get_component
from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_TIMEOUT
from homeassistant.const import CONF_TIMEOUT, CONF_OFFSET
from homeassistant.helpers import config_validation as cv
DEPENDENCIES = ["netatmo"]
@@ -22,22 +22,40 @@ _LOGGER = logging.getLogger(__name__)
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
WELCOME_SENSOR_TYPES = {
"Someone known": "motion",
"Someone unknown": "motion",
"Motion": "motion",
}
PRESENCE_SENSOR_TYPES = {
"Outdoor motion": "motion",
"Outdoor human": "motion",
"Outdoor animal": "motion",
"Outdoor vehicle": "motion"
}
TAG_SENSOR_TYPES = {
"Tag Vibration": 'vibration',
"Tag Open": 'opening'
}
CONF_HOME = 'home'
CONF_CAMERAS = 'cameras'
CONF_WELCOME_SENSORS = 'welcome_sensors'
CONF_PRESENCE_SENSORS = 'presence_sensors'
CONF_TAG_SENSORS = 'tag_sensors'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOME): cv.string,
vol.Optional(CONF_TIMEOUT): cv.positive_int,
vol.Optional(CONF_OFFSET): cv.positive_int,
vol.Optional(CONF_CAMERAS, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_MONITORED_CONDITIONS, default=SENSOR_TYPES.keys()):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
vol.Optional(
CONF_WELCOME_SENSORS, default=WELCOME_SENSOR_TYPES.keys()):
vol.All(cv.ensure_list, [vol.In(WELCOME_SENSOR_TYPES)]),
vol.Optional(
CONF_PRESENCE_SENSORS, default=PRESENCE_SENSOR_TYPES.keys()):
vol.All(cv.ensure_list, [vol.In(PRESENCE_SENSOR_TYPES)]),
})
@@ -47,46 +65,82 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
netatmo = get_component('netatmo')
home = config.get(CONF_HOME, None)
timeout = config.get(CONF_TIMEOUT, 15)
offset = config.get(CONF_OFFSET, 90)
module_name = None
import lnetatmo
try:
data = WelcomeData(netatmo.NETATMO_AUTH, home)
data = CameraData(netatmo.NETATMO_AUTH, home)
if data.get_camera_names() == []:
return None
except lnetatmo.NoDevice:
return None
sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES)
welcome_sensors = config.get(
CONF_WELCOME_SENSORS, WELCOME_SENSOR_TYPES)
presence_sensors = config.get(
CONF_PRESENCE_SENSORS, PRESENCE_SENSOR_TYPES)
tag_sensors = config.get(CONF_TAG_SENSORS, TAG_SENSOR_TYPES)
for camera_name in data.get_camera_names():
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
camera_name not in config[CONF_CAMERAS]:
continue
for variable in sensors:
add_devices([WelcomeBinarySensor(data, camera_name, home, timeout,
variable)])
camera_type = data.get_camera_type(camera=camera_name, home=home)
if camera_type == "NACamera":
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
camera_name not in config[CONF_CAMERAS]:
continue
for variable in welcome_sensors:
add_devices([NetatmoBinarySensor(data, camera_name,
module_name, home, timeout,
offset, camera_type,
variable)])
if camera_type == "NOC":
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
camera_name not in config[CONF_CAMERAS]:
continue
for variable in presence_sensors:
add_devices([NetatmoBinarySensor(data, camera_name,
module_name, home, timeout,
offset, camera_type,
variable)])
for module_name in data.get_module_names(camera_name):
for variable in tag_sensors:
camera_type = None
add_devices([NetatmoBinarySensor(data, camera_name,
module_name, home,
timeout, offset,
camera_type,
variable)])
class WelcomeBinarySensor(BinarySensorDevice):
"""Represent a single binary sensor in a Netatmo Welcome device."""
class NetatmoBinarySensor(BinarySensorDevice):
"""Represent a single binary sensor in a Netatmo Camera device."""
def __init__(self, data, camera_name, home, timeout, sensor):
def __init__(self, data, camera_name, module_name, home,
timeout, offset, camera_type, sensor):
"""Setup for access to the Netatmo camera events."""
self._data = data
self._camera_name = camera_name
self._module_name = module_name
self._home = home
self._timeout = timeout
self._offset = offset
if home:
self._name = home + ' / ' + camera_name
else:
self._name = camera_name
if module_name:
self._name += ' / ' + module_name
self._sensor_name = sensor
self._name += ' ' + sensor
camera_id = data.welcomedata.cameraByName(camera=camera_name,
camera_id = data.camera_data.cameraByName(camera=camera_name,
home=home)['id']
self._unique_id = "Welcome_binary_sensor {0} - {1}".format(self._name,
self._unique_id = "Netatmo_binary_sensor {0} - {1}".format(self._name,
camera_id)
self._cameratype = camera_type
self.update()
@property
@@ -102,7 +156,12 @@ class WelcomeBinarySensor(BinarySensorDevice):
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return SENSOR_TYPES.get(self._sensor_name)
if self._cameratype == "NACamera":
return WELCOME_SENSOR_TYPES.get(self._sensor_name)
elif self._cameratype == "NOC":
return PRESENCE_SENSOR_TYPES.get(self._sensor_name)
else:
return TAG_SENSOR_TYPES.get(self._sensor_name)
@property
def is_on(self):
@@ -112,22 +171,53 @@ class WelcomeBinarySensor(BinarySensorDevice):
def update(self):
"""Request an update from the Netatmo API."""
self._data.update()
self._data.welcomedata.updateEvent(home=self._data.home)
self._data.update_event()
if self._sensor_name == "Someone known":
self._state =\
self._data.welcomedata.someoneKnownSeen(self._home,
if self._cameratype == "NACamera":
if self._sensor_name == "Someone known":
self._state =\
self._data.camera_data.someoneKnownSeen(self._home,
self._camera_name,
self._timeout*60)
elif self._sensor_name == "Someone unknown":
self._state =\
self._data.welcomedata.someoneUnknownSeen(self._home,
elif self._sensor_name == "Someone unknown":
self._state =\
self._data.camera_data.someoneUnknownSeen(
self._home, self._camera_name, self._timeout*60)
elif self._sensor_name == "Motion":
self._state =\
self._data.camera_data.motionDetected(self._home,
self._camera_name,
self._timeout*60)
elif self._sensor_name == "Motion":
elif self._cameratype == "NOC":
if self._sensor_name == "Outdoor motion":
self._state =\
self._data.camera_data.outdoormotionDetected(
self._home, self._camera_name, self._offset)
elif self._sensor_name == "Outdoor human":
self._state =\
self._data.camera_data.humanDetected(self._home,
self._camera_name,
self._offset)
elif self._sensor_name == "Outdoor animal":
self._state =\
self._data.camera_data.animalDetected(self._home,
self._camera_name,
self._offset)
elif self._sensor_name == "Outdoor vehicle":
self._state =\
self._data.camera_data.carDetected(self._home,
self._camera_name,
self._offset)
if self._sensor_name == "Tag Vibration":
self._state =\
self._data.welcomedata.motionDetected(self._home,
self._camera_name,
self._timeout*60)
self._data.camera_data.moduleMotionDetected(self._home,
self._module_name,
self._camera_name,
self._timeout*60)
elif self._sensor_name == "Tag Open":
self._state =\
self._data.camera_data.moduleOpened(self._home,
self._module_name,
self._camera_name)
else:
return None

View File

@@ -16,7 +16,7 @@ from homeassistant.components.binary_sensor import (
from homeassistant.const import (CONF_HOST, CONF_PORT)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pynx584==0.2']
REQUIREMENTS = ['pynx584==0.4']
_LOGGER = logging.getLogger(__name__)

View File

@@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for port_num, port_name in ports.items():
binary_sensors.append(RPiGPIOBinarySensor(
port_name, port_num, pull_mode, bouncetime, invert_logic))
add_devices(binary_sensors)
add_devices(binary_sensors, True)
class RPiGPIOBinarySensor(BinarySensorDevice):
@@ -65,9 +65,9 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
self._pull_mode = pull_mode
self._bouncetime = bouncetime
self._invert_logic = invert_logic
self._state = None
rpi_gpio.setup_input(self._port, self._pull_mode)
self._state = rpi_gpio.read_input(self._port)
def read_gpio(port):
"""Read state from GPIO."""
@@ -90,3 +90,7 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
def is_on(self):
"""Return the state of the entity."""
return self._state != self._invert_logic
def update(self):
"""Update the GPIO state."""
self._state = rpi_gpio.read_input(self._port)

View File

@@ -1,9 +0,0 @@
# Describes the format for available binary_sensor services
ffmpeg_restart:
description: Send a restart command to a ffmpeg based sensor (party mode).
fields:
entity_id:
description: Name(s) of entites that will restart. Platform dependent.
example: 'binary_sensor.ffmpeg_noise'

View File

@@ -118,7 +118,8 @@ class BinarySensorTemplate(BinarySensorDevice):
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
# Common during HA startup - so just a warning
_LOGGER.warning(ex)
_LOGGER.warning('Could not render template %s,'
' the state is unknown.', self._name)
return
_LOGGER.error(ex)
_LOGGER.error('Could not render template %s: %s', self._name, ex)
self._state = False

View File

@@ -110,7 +110,7 @@ class ThresholdSensor(BinarySensorDevice):
return self._sensor_class
@property
def state_attributes(self):
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
return {
ATTR_ENTITY_ID: self._entity_id,

View File

@@ -4,11 +4,13 @@ Support for Wink binary sensors.
For more details about this platform, please refer to the documentation at
at https://home-assistant.io/components/binary_sensor.wink/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.wink import WinkDevice
from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['wink']
@@ -18,11 +20,14 @@ SENSOR_TYPES = {
"brightness": "light",
"vibration": "vibration",
"loudness": "sound",
"noise": "sound",
"capturing_audio": "sound",
"liquid_detected": "moisture",
"motion": "motion",
"presence": "occupancy",
"co_detected": "gas",
"smoke_detected": "smoke"
"smoke_detected": "smoke",
"capturing_video": None
}
@@ -31,14 +36,54 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
import pywink
for sensor in pywink.get_sensors():
if sensor.capability() in SENSOR_TYPES:
add_devices([WinkBinarySensorDevice(sensor, hass)])
_id = sensor.object_id() + sensor.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
if sensor.capability() in SENSOR_TYPES:
add_devices([WinkBinarySensorDevice(sensor, hass)])
for key in pywink.get_keys():
add_devices([WinkBinarySensorDevice(key, hass)])
_id = key.object_id() + key.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkBinarySensorDevice(key, hass)])
for sensor in pywink.get_smoke_and_co_detectors():
add_devices([WinkBinarySensorDevice(sensor, hass)])
_id = sensor.object_id() + sensor.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkSmokeDetector(sensor, hass)])
for hub in pywink.get_hubs():
_id = hub.object_id() + hub.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkHub(hub, hass)])
for remote in pywink.get_remotes():
_id = remote.object_id() + remote.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkRemote(remote, hass)])
for button in pywink.get_buttons():
_id = button.object_id() + button.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkButton(button, hass)])
for gang in pywink.get_gangs():
_id = gang.object_id() + gang.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkGang(gang, hass)])
for door_bell_sensor in pywink.get_door_bells():
_id = door_bell_sensor.object_id() + door_bell_sensor.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkBinarySensorDevice(door_bell_sensor, hass)])
for camera_sensor in pywink.get_cameras():
_id = camera_sensor.object_id() + camera_sensor.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
try:
if camera_sensor.capability() in SENSOR_TYPES:
add_devices([WinkBinarySensorDevice(camera_sensor, hass)])
except AttributeError:
_LOGGER.info("Device isn't a sensor, skipping.")
class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity):
@@ -47,35 +92,104 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity):
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink, hass)
wink = get_component('wink')
self._unit_of_measurement = self.wink.UNIT
self.capability = self.wink.capability()
try:
self._unit_of_measurement = self.wink.unit()
except AttributeError:
self._unit_of_measurement = None
try:
self.capability = self.wink.capability()
except AttributeError:
self.capability = None
@property
def is_on(self):
"""Return true if the binary sensor is on."""
if self.capability == "loudness":
state = self.wink.loudness_boolean()
elif self.capability == "vibration":
state = self.wink.vibration_boolean()
elif self.capability == "brightness":
state = self.wink.brightness_boolean()
elif self.capability == "liquid_detected":
state = self.wink.liquid_boolean()
elif self.capability == "motion":
state = self.wink.motion_boolean()
elif self.capability == "presence":
state = self.wink.presence_boolean()
elif self.capability == "co_detected":
state = self.wink.co_detected_boolean()
elif self.capability == "smoke_detected":
state = self.wink.smoke_detected_boolean()
else:
state = self.wink.state()
return state
return self.wink.state()
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return SENSOR_TYPES.get(self.capability)
class WinkSmokeDetector(WinkBinarySensorDevice):
"""Representation of a Wink Smoke detector."""
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink, hass)
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'test_activated': self.wink.test_activated()
}
class WinkHub(WinkBinarySensorDevice):
"""Representation of a Wink Hub."""
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink, hass)
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'update needed': self.wink.update_needed(),
'firmware version': self.wink.firmware_version()
}
class WinkRemote(WinkBinarySensorDevice):
"""Representation of a Wink Lutron Connected bulb remote."""
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink, hass)
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'button_on_pressed': self.wink.button_on_pressed(),
'button_off_pressed': self.wink.button_off_pressed(),
'button_up_pressed': self.wink.button_up_pressed(),
'button_down_pressed': self.wink.button_down_pressed()
}
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return None
class WinkButton(WinkBinarySensorDevice):
"""Representation of a Wink Relay button."""
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink, hass)
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'pressed': self.wink.pressed(),
'long_pressed': self.wink.long_pressed()
}
class WinkGang(WinkBinarySensorDevice):
"""Representation of a Wink Relay gang."""
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink, hass)
@property
def is_on(self):
"""Return true if the gang is connected."""
return self.wink.state()

View File

@@ -8,8 +8,8 @@ import logging
import datetime
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_time
from homeassistant.helpers.entity import Entity
from homeassistant.components import zwave
from homeassistant.components.zwave import workaround
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDevice)
@@ -17,19 +17,6 @@ from homeassistant.components.binary_sensor import (
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
PHILIO = 0x013c
PHILIO_SLIM_SENSOR = 0x0002
PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0)
WENZHOU = 0x0118
WENZHOU_SLIM_SENSOR_MOTION = (WENZHOU, PHILIO_SLIM_SENSOR, 0)
WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event'
DEVICE_MAPPINGS = {
PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
WENZHOU_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Z-Wave platform for binary sensors."""
@@ -40,43 +27,32 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
value = node.values[discovery_info[zwave.const.ATTR_VALUE_ID]]
value.set_change_verified(False)
# 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),
value.index)
device_mapping = workaround.get_device_mapping(value)
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)
add_devices([
ZWaveTriggerSensor(value, "motion",
hass, re_arm_multiplier * 8)
])
return
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
re_arm_multiplier = (zwave.get_config_value(value.node,
9) or 4)
add_devices([
ZWaveTriggerSensor(value, "motion",
hass, re_arm_multiplier * 8)
])
return
if workaround.get_device_component_mapping(value) == DOMAIN:
add_devices([ZWaveBinarySensor(value, None)])
return
if value.command_class == zwave.const.COMMAND_CLASS_SENSOR_BINARY:
add_devices([ZWaveBinarySensor(value, None)])
class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity, Entity):
class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
"""Representation of a binary sensor within Z-Wave."""
def __init__(self, value, sensor_class):
"""Initialize the sensor."""
self._sensor_type = sensor_class
# pylint: disable=import-error
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
@property
def is_on(self):
"""Return True if the binary sensor is on."""
@@ -92,31 +68,25 @@ class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity, Entity):
"""No polling needed."""
return False
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id or \
self._value.node == value.node:
self.schedule_update_ha_state()
class ZWaveTriggerSensor(ZWaveBinarySensor, Entity):
class ZWaveTriggerSensor(ZWaveBinarySensor):
"""Representation of a stateless sensor within Z-Wave."""
def __init__(self, sensor_value, sensor_class, hass, re_arm_sec=60):
def __init__(self, value, sensor_class, hass, re_arm_sec=60):
"""Initialize the sensor."""
super(ZWaveTriggerSensor, self).__init__(sensor_value, sensor_class)
super(ZWaveTriggerSensor, self).__init__(value, sensor_class)
self._hass = hass
self.re_arm_sec = re_arm_sec
self.invalidate_after = dt_util.utcnow() + datetime.timedelta(
seconds=self.re_arm_sec)
# If it's active make sure that we set the timeout tracker
if sensor_value.data:
if value.data:
track_point_in_time(
self._hass, self.async_update_ha_state,
self.invalidate_after)
def value_changed(self, value):
"""Called when a value has changed on the network."""
"""Called when a value for this entity's node has changed."""
if self._value.value_id == value.value_id:
self.schedule_update_ha_state()
if value.data:

View File

@@ -6,6 +6,8 @@ https://home-assistant.io/components/calendar/
"""
import logging
from datetime import timedelta
import re
from homeassistant.components.google import (CONF_OFFSET,
@@ -20,6 +22,7 @@ from homeassistant.util import dt
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=60)
DOMAIN = 'calendar'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
@@ -27,7 +30,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
def setup(hass, config):
"""Track states and offer events for calendars."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, 60, DOMAIN)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, DOMAIN)
component.setup(config)
@@ -144,10 +147,10 @@ class CalendarEventDevice(Entity):
def _get_date(date):
"""Get the dateTime from date or dateTime as a local."""
if 'date' in date:
return dt.as_utc(dt.dt.datetime.combine(
dt.parse_date(date['date']), dt.dt.time()))
return dt.start_of_local_day(dt.dt.datetime.combine(
dt.parse_date(date['date']), dt.dt.time.min))
else:
return dt.parse_datetime(date['dateTime'])
return dt.as_local(dt.parse_datetime(date['dateTime']))
start = _get_date(self.data.event['start'])
end = _get_date(self.data.event['end'])

View File

@@ -66,7 +66,7 @@ class GoogleCalendarData(object):
"""Get the latest data."""
service = self.calendar_service.get()
params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS)
params['timeMin'] = dt.utcnow().isoformat('T')
params['timeMin'] = dt.start_of_local_day().isoformat('T')
params['calendarId'] = self.calendar_id
if self.search:
params['q'] = self.search

View File

@@ -6,18 +6,31 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera/
"""
import asyncio
import collections
from datetime import timedelta
import logging
import hashlib
from random import SystemRandom
import aiohttp
from aiohttp import web
import async_timeout
from homeassistant.core import callback
from homeassistant.const import ATTR_ENTITY_PICTURE
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED
from homeassistant.helpers.event import async_track_time_interval
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'camera'
DEPENDENCIES = ['http']
SCAN_INTERVAL = 30
SCAN_INTERVAL = timedelta(seconds=30)
ENTITY_ID_FORMAT = DOMAIN + '.{}'
STATE_RECORDING = 'recording'
@@ -26,17 +39,63 @@ STATE_IDLE = 'idle'
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?token={1}'
TOKEN_CHANGE_INTERVAL = timedelta(minutes=5)
_RND = SystemRandom()
@asyncio.coroutine
def async_get_image(hass, entity_id, timeout=10):
"""Fetch a image from a camera entity."""
websession = async_get_clientsession(hass)
state = hass.states.get(entity_id)
if state is None:
raise HomeAssistantError(
"No entity '{0}' for grab a image".format(entity_id))
url = "{0}{1}".format(
hass.config.api.base_url,
state.attributes.get(ATTR_ENTITY_PICTURE)
)
response = None
try:
with async_timeout.timeout(timeout, loop=hass.loop):
response = yield from websession.get(url)
if response.status != 200:
raise HomeAssistantError("Error {0} on {1}".format(
response.status, url))
image = yield from response.read()
return image
except (asyncio.TimeoutError, aiohttp.errors.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):
"""Setup the camera component."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
hass.http.register_view(CameraImageView(component.entities))
hass.http.register_view(CameraMjpegStream(component.entities))
yield from component.async_setup(config)
@callback
def update_tokens(time):
"""Update tokens of the entities."""
for entity in component.entities.values():
entity.async_update_token()
hass.async_add_job(entity.async_update_ha_state())
async_track_time_interval(hass, update_tokens, TOKEN_CHANGE_INTERVAL)
return True
@@ -46,11 +105,8 @@ class Camera(Entity):
def __init__(self):
"""Initialize a camera."""
self.is_streaming = False
@property
def access_token(self):
"""Access token for this camera."""
return str(id(self))
self.access_tokens = collections.deque([], 2)
self.async_update_token()
@property
def should_poll(self):
@@ -60,7 +116,7 @@ class Camera(Entity):
@property
def entity_picture(self):
"""Return a link to the camera feed as entity picture."""
return ENTITY_IMAGE_URL.format(self.entity_id, self.access_token)
return ENTITY_IMAGE_URL.format(self.entity_id, self.access_tokens[-1])
@property
def is_recording(self):
@@ -81,15 +137,12 @@ class Camera(Entity):
"""Return bytes of camera image."""
raise NotImplementedError()
@asyncio.coroutine
def async_camera_image(self):
"""Return bytes of camera image.
This method must be run in the event loop.
This method must be run in the event loop and returns a coroutine.
"""
image = yield from self.hass.loop.run_in_executor(
None, self.camera_image)
return image
return self.hass.loop.run_in_executor(None, self.camera_image)
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
@@ -131,8 +184,14 @@ class Camera(Entity):
yield from response.drain()
yield from asyncio.sleep(.5)
except (asyncio.CancelledError, ConnectionResetError):
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
yield from response.write_eof()
if response is not None:
yield from response.write_eof()
@property
def state(self):
@@ -148,7 +207,7 @@ class Camera(Entity):
def state_attributes(self):
"""Camera state attributes."""
attr = {
'access_token': self.access_token,
'access_token': self.access_tokens[-1],
}
if self.model:
@@ -159,6 +218,13 @@ class Camera(Entity):
return attr
@callback
def async_update_token(self):
"""Update the used token."""
self.access_tokens.append(
hashlib.sha256(
_RND.getrandbits(256).to_bytes(32, 'little')).hexdigest())
class CameraView(HomeAssistantView):
"""Base CameraView."""
@@ -175,10 +241,11 @@ class CameraView(HomeAssistantView):
camera = self.entities.get(entity_id)
if camera is None:
return web.Response(status=404)
status = 404 if request[KEY_AUTHENTICATED] else 401
return web.Response(status=status)
authenticated = (request[KEY_AUTHENTICATED] or
request.GET.get('token') == camera.access_token)
request.GET.get('token') in camera.access_tokens)
if not authenticated:
return web.Response(status=401)
@@ -201,12 +268,16 @@ class CameraImageView(CameraView):
@asyncio.coroutine
def handle(self, request, camera):
"""Serve camera image."""
image = yield from camera.async_camera_image()
try:
image = yield from camera.async_camera_image()
if image is None:
return web.Response(status=500)
if image is None:
return web.Response(status=500)
return web.Response(body=image)
return web.Response(body=image)
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
class CameraMjpegStream(CameraView):

View File

@@ -4,8 +4,10 @@ This component provides basic support for Amcrest IP cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.amcrest/
"""
import asyncio
import logging
import aiohttp
import voluptuous as vol
import homeassistant.loader as loader
@@ -13,36 +15,60 @@ from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
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)
REQUIREMENTS = ['amcrest==1.0.0']
REQUIREMENTS = ['amcrest==1.1.4']
_LOGGER = logging.getLogger(__name__)
DEFAULT_PORT = 80
CONF_RESOLUTION = 'resolution'
CONF_STREAM_SOURCE = 'stream_source'
DEFAULT_NAME = 'Amcrest Camera'
DEFAULT_PORT = 80
DEFAULT_RESOLUTION = 'high'
DEFAULT_STREAM_SOURCE = 'mjpeg'
NOTIFICATION_ID = 'amcrest_notification'
NOTIFICATION_TITLE = 'Amcrest Camera Setup'
RESOLUTION_LIST = {
'high': 0,
'low': 1,
}
STREAM_SOURCE_LIST = {
'mjpeg': 0,
'snapshot': 1
}
CONTENT_TYPE_HEADER = 'Content-Type'
TIMEOUT = 5
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION):
vol.All(vol.In(RESOLUTION_LIST)),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE):
vol.All(vol.In(STREAM_SOURCE_LIST)),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up an Amcrest IP Camera."""
from amcrest import AmcrestCamera
data = AmcrestCamera(
camera = AmcrestCamera(
config.get(CONF_HOST), config.get(CONF_PORT),
config.get(CONF_USERNAME), config.get(CONF_PASSWORD))
config.get(CONF_USERNAME), config.get(CONF_PASSWORD)).camera
persistent_notification = loader.get_component('persistent_notification')
try:
data.camera.current_time
camera.current_time
# pylint: disable=broad-except
except Exception as ex:
_LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
@@ -54,25 +80,53 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
notification_id=NOTIFICATION_ID)
return False
add_devices([AmcrestCam(config, data)])
add_devices([AmcrestCam(hass, config, camera)])
return True
class AmcrestCam(Camera):
"""An implementation of an Amcrest IP camera."""
def __init__(self, device_info, data):
def __init__(self, hass, device_info, camera):
"""Initialize an Amcrest camera."""
super(AmcrestCam, self).__init__()
self._camera = camera
self._base_url = self._camera.get_base_url()
self._hass = hass
self._name = device_info.get(CONF_NAME)
self._data = data
self._resolution = RESOLUTION_LIST[device_info.get(CONF_RESOLUTION)]
self._stream_source = STREAM_SOURCE_LIST[
device_info.get(CONF_STREAM_SOURCE)
]
self._token = self._auth = aiohttp.BasicAuth(
device_info.get(CONF_USERNAME),
password=device_info.get(CONF_PASSWORD)
)
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 = self._data.camera.snapshot()
response = self._camera.snapshot(channel=self._resolution)
return response.data
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Return an MJPEG stream."""
# The snapshot implementation is handled by the parent class
if self._stream_source == STREAM_SOURCE_LIST['snapshot']:
yield from super().handle_async_mjpeg_stream(request)
return
# Otherwise, stream an MJPEG image stream directly from the camera
websession = async_get_clientsession(self.hass)
streaming_url = '{0}mjpg/video.cgi?channel=0&subtype={1}'.format(
self._base_url, self._resolution)
stream_coro = websession.get(
streaming_url, auth=self._token, timeout=TIMEOUT)
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):
"""Return the name of this camera."""

View File

@@ -12,10 +12,9 @@ from aiohttp import web
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import (
async_run_test, get_binary, CONF_INPUT, CONF_EXTRA_ARGUMENTS)
DATA_FFMPEG, CONF_INPUT, CONF_EXTRA_ARGUMENTS)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
from homeassistant.util.async import run_coroutine_threadsafe
DEPENDENCIES = ['ffmpeg']
@@ -33,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a FFmpeg Camera."""
if not async_run_test(hass, config.get(CONF_INPUT)):
if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)):
return
yield from async_add_devices([FFmpegCamera(hass, config)])
@@ -44,20 +43,17 @@ class FFmpegCamera(Camera):
def __init__(self, hass, config):
"""Initialize a FFmpeg camera."""
super().__init__()
self._manager = hass.data[DATA_FFMPEG]
self._name = config.get(CONF_NAME)
self._input = config.get(CONF_INPUT)
self._extra_arguments = config.get(CONF_EXTRA_ARGUMENTS)
def camera_image(self):
"""Return bytes of camera image."""
return run_coroutine_threadsafe(
self.async_camera_image(), self.hass.loop).result()
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
from haffmpeg import ImageSingleAsync, IMAGE_JPEG
ffmpeg = ImageSingleAsync(get_binary(), loop=self.hass.loop)
from haffmpeg import ImageFrame, IMAGE_JPEG
ffmpeg = ImageFrame(self._manager.binary, loop=self.hass.loop)
image = yield from ffmpeg.get_image(
self._input, output_format=IMAGE_JPEG,
@@ -67,9 +63,9 @@ class FFmpegCamera(Camera):
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
from haffmpeg import CameraMjpegAsync
from haffmpeg import CameraMjpeg
stream = CameraMjpegAsync(get_binary(), loop=self.hass.loop)
stream = CameraMjpeg(self._manager.binary, loop=self.hass.loop)
yield from stream.open_camera(
self._input, extra_cmd=self._extra_arguments)
@@ -84,9 +80,15 @@ class FFmpegCamera(Camera):
if not data:
break
response.write(data)
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
self.hass.async_add_job(stream.close())
yield from response.write_eof()
yield from stream.close()
if response is not None:
yield from response.write_eof()
@property
def name(self):

View File

@@ -9,8 +9,6 @@ import logging
from contextlib import closing
import aiohttp
from aiohttp import web
from aiohttp.web_exceptions import HTTPGatewayTimeout
import async_timeout
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
@@ -20,18 +18,21 @@ from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_AUTHENTICATION,
HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_stream)
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
CONF_MJPEG_URL = 'mjpeg_url'
CONF_STILL_IMAGE_URL = 'still_image_url'
CONTENT_TYPE_HEADER = 'Content-Type'
DEFAULT_NAME = 'Mjpeg Camera'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MJPEG_URL): cv.url,
vol.Optional(CONF_STILL_IMAGE_URL): cv.url,
vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION):
vol.In([HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -70,6 +71,7 @@ class MjpegCamera(Camera):
self._username = device_info.get(CONF_USERNAME)
self._password = device_info.get(CONF_PASSWORD)
self._mjpeg_url = device_info[CONF_MJPEG_URL]
self._still_image_url = device_info.get(CONF_STILL_IMAGE_URL)
self._auth = None
if self._username and self._password:
@@ -78,6 +80,37 @@ class MjpegCamera(Camera):
self._username, password=self._password
)
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
# DigestAuth is not supported
if self._authentication == HTTP_DIGEST_AUTHENTICATION or \
self._still_image_url is None:
image = yield from self.hass.loop.run_in_executor(
None, self.camera_image)
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(
self._still_image_url, auth=self._auth)
image = yield from response.read()
return image
except asyncio.TimeoutError:
_LOGGER.error('Timeout getting camera image')
except (aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError) 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:
@@ -103,32 +136,9 @@ class MjpegCamera(Camera):
# connect to stream
websession = async_get_clientsession(self.hass)
stream = None
response = None
try:
with async_timeout.timeout(10, loop=self.hass.loop):
stream = yield from websession.get(self._mjpeg_url,
auth=self._auth)
stream_coro = websession.get(self._mjpeg_url, auth=self._auth)
response = web.StreamResponse()
response.content_type = stream.headers.get(CONTENT_TYPE_HEADER)
yield from response.prepare(request)
while True:
data = yield from stream.content.read(102400)
if not data:
break
response.write(data)
except asyncio.TimeoutError:
raise HTTPGatewayTimeout()
finally:
if stream is not None:
self.hass.async_add_job(stream.release())
if response is not None:
yield from response.write_eof()
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):

View File

@@ -26,7 +26,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a Nest Cam."""
if discovery_info is None:
return
camera_devices = hass.data[nest.DATA_NEST].camera_devices()
camera_devices = hass.data[nest.DATA_NEST].cameras()
cameras = [NestCamera(structure, device)
for structure, device in camera_devices]
add_devices(cameras, True)
@@ -42,7 +43,7 @@ class NestCamera(Camera):
self.device = device
self._location = None
self._name = None
self._is_online = None
self._online = None
self._is_streaming = None
self._is_video_history_enabled = False
# Default to non-NestAware subscribed, but will be fixed during update
@@ -75,7 +76,7 @@ class NestCamera(Camera):
"""Cache value from Python-nest."""
self._location = self.device.where
self._name = self.device.name
self._is_online = self.device.is_online
self._online = self.device.online
self._is_streaming = self.device.is_streaming
self._is_video_history_enabled = self.device.is_video_history_enabled

View File

@@ -1,15 +1,16 @@
"""
Support for the Netatmo Welcome camera.
Support for the Netatmo cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.netatmo/
https://home-assistant.io/components/camera.netatmo/.
"""
import logging
import requests
import voluptuous as vol
from homeassistant.components.netatmo import WelcomeData
from homeassistant.const import CONF_VERIFY_SSL
from homeassistant.components.netatmo import CameraData
from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
from homeassistant.loader import get_component
from homeassistant.helpers import config_validation as cv
@@ -22,6 +23,7 @@ CONF_HOME = 'home'
CONF_CAMERAS = 'cameras'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
vol.Optional(CONF_HOME): cv.string,
vol.Optional(CONF_CAMERAS, default=[]):
vol.All(cv.ensure_list, [cv.string]),
@@ -30,41 +32,46 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup access to Netatmo Welcome cameras."""
"""Setup access to Netatmo cameras."""
netatmo = get_component('netatmo')
home = config.get(CONF_HOME)
verify_ssl = config.get(CONF_VERIFY_SSL, True)
import lnetatmo
try:
data = WelcomeData(netatmo.NETATMO_AUTH, home)
data = CameraData(netatmo.NETATMO_AUTH, home)
for camera_name in data.get_camera_names():
camera_type = data.get_camera_type(camera=camera_name, home=home)
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
camera_name not in config[CONF_CAMERAS]:
continue
add_devices([WelcomeCamera(data, camera_name, home)])
add_devices([NetatmoCamera(data, camera_name, home,
camera_type, verify_ssl)])
except lnetatmo.NoDevice:
return None
class WelcomeCamera(Camera):
"""Representation of the images published from Welcome camera."""
class NetatmoCamera(Camera):
"""Representation of the images published from a Netatmo camera."""
def __init__(self, data, camera_name, home):
def __init__(self, data, camera_name, home, camera_type, verify_ssl):
"""Setup for access to the Netatmo camera images."""
super(WelcomeCamera, self).__init__()
super(NetatmoCamera, self).__init__()
self._data = data
self._camera_name = camera_name
self._verify_ssl = verify_ssl
if home:
self._name = home + ' / ' + camera_name
else:
self._name = camera_name
camera_id = data.welcomedata.cameraByName(camera=camera_name,
camera_id = data.camera_data.cameraByName(camera=camera_name,
home=home)['id']
self._unique_id = "Welcome_camera {0} - {1}".format(self._name,
camera_id)
self._vpnurl, self._localurl = self._data.welcomedata.cameraUrls(
self._vpnurl, self._localurl = self._data.camera_data.cameraUrls(
camera=camera_name
)
self._cameratype = camera_type
def camera_image(self):
"""Return a still image response from the camera."""
@@ -72,22 +79,43 @@ class WelcomeCamera(Camera):
if self._localurl:
response = requests.get('{0}/live/snapshot_720.jpg'.format(
self._localurl), timeout=10)
else:
elif self._vpnurl:
response = requests.get('{0}/live/snapshot_720.jpg'.format(
self._vpnurl), timeout=10)
self._vpnurl), timeout=10, verify=self._verify_ssl)
else:
_LOGGER.error('Welcome VPN url is None')
self._data.update()
(self._vpnurl, self._localurl) = \
self._data.camera_data.cameraUrls(camera=self._camera_name)
return None
except requests.exceptions.RequestException as error:
_LOGGER.error('Welcome VPN url changed: %s', error)
_LOGGER.error('Welcome url changed: %s', error)
self._data.update()
(self._vpnurl, self._localurl) = \
self._data.welcomedata.cameraUrls(camera=self._camera_name)
self._data.camera_data.cameraUrls(camera=self._camera_name)
return None
return response.content
@property
def name(self):
"""Return the name of this Netatmo Welcome device."""
"""Return the name of this Netatmo camera device."""
return self._name
@property
def brand(self):
"""Camera brand."""
return "Netatmo"
@property
def model(self):
"""Camera model."""
if self._cameratype == "NOC":
return "Presence"
elif self._cameratype == "NACamera":
return "Welcome"
else:
return None
@property
def unique_id(self):
"""Return the unique ID for this sensor."""

View File

@@ -39,7 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_FILE_PATH): cv.string,
vol.Optional(CONF_HORIZONTAL_FLIP, default=DEFAULT_HORIZONTAL_FLIP):
vol.All(vol.Coerce(int), vol.Range(min=0, max=1)),
vol.Optional(CONF_IMAGE_HEIGHT, default=DEFAULT_HORIZONTAL_FLIP):
vol.Optional(CONF_IMAGE_HEIGHT, default=DEFAULT_IMAGE_HEIGHT):
vol.Coerce(int),
vol.Optional(CONF_IMAGE_QUALITY, default=DEFAULT_IMAGE_QUALITIY):
vol.All(vol.Coerce(int), vol.Range(min=0, max=100)),

View File

@@ -10,8 +10,6 @@ import logging
import voluptuous as vol
import aiohttp
from aiohttp import web
from aiohttp.web_exceptions import HTTPGatewayTimeout
import async_timeout
from homeassistant.const import (
@@ -20,7 +18,8 @@ from homeassistant.const import (
from homeassistant.components.camera import (
Camera, PLATFORM_SCHEMA)
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_create_clientsession)
async_get_clientsession, async_create_clientsession,
async_aiohttp_proxy_stream)
import homeassistant.helpers.config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
@@ -253,34 +252,10 @@ class SynologyCamera(Camera):
'cameraId': self._camera_id,
'format': 'mjpeg'
}
stream = None
response = None
try:
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
stream = yield from self._websession.get(
streaming_url,
params=streaming_payload
)
response = web.StreamResponse()
response.content_type = stream.headers.get(CONTENT_TYPE_HEADER)
stream_coro = self._websession.get(
streaming_url, params=streaming_payload)
yield from response.prepare(request)
while True:
data = yield from stream.content.read(102400)
if not data:
break
response.write(data)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", streaming_url)
raise HTTPGatewayTimeout()
finally:
if stream is not None:
self.hass.async_add_job(stream.release())
if response is not None:
yield from response.write_eof()
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):

View File

@@ -14,7 +14,7 @@ from homeassistant.const import CONF_PORT
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['uvcclient==0.9.0']
REQUIREMENTS = ['uvcclient==0.10.0']
_LOGGER = logging.getLogger(__name__)

View File

@@ -4,15 +4,18 @@ Provides functionality to interact with climate devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/climate/
"""
import asyncio
from datetime import timedelta
import logging
import os
import functools as ft
from numbers import Number
import voluptuous as vol
from homeassistant.helpers.entity_component import EntityComponent
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.util.temperature import convert as convert_temperature
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
@@ -23,12 +26,13 @@ from homeassistant.const import (
DOMAIN = "climate"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
SCAN_INTERVAL = 60
SCAN_INTERVAL = timedelta(seconds=60)
SERVICE_SET_AWAY_MODE = "set_away_mode"
SERVICE_SET_AUX_HEAT = "set_aux_heat"
SERVICE_SET_TEMPERATURE = "set_temperature"
SERVICE_SET_FAN_MODE = "set_fan_mode"
SERVICE_SET_HOLD_MODE = "set_hold_mode"
SERVICE_SET_OPERATION_MODE = "set_operation_mode"
SERVICE_SET_SWING_MODE = "set_swing_mode"
SERVICE_SET_HUMIDITY = "set_humidity"
@@ -53,6 +57,7 @@ ATTR_CURRENT_HUMIDITY = "current_humidity"
ATTR_HUMIDITY = "humidity"
ATTR_MAX_HUMIDITY = "max_humidity"
ATTR_MIN_HUMIDITY = "min_humidity"
ATTR_HOLD_MODE = "hold_mode"
ATTR_OPERATION_MODE = "operation_mode"
ATTR_OPERATION_LIST = "operation_list"
ATTR_SWING_MODE = "swing_mode"
@@ -90,6 +95,10 @@ SET_FAN_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_FAN_MODE): cv.string,
})
SET_HOLD_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_HOLD_MODE): cv.string,
})
SET_OPERATION_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_OPERATION_MODE): cv.string,
@@ -116,6 +125,18 @@ def set_away_mode(hass, away_mode, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_AWAY_MODE, data)
def set_hold_mode(hass, hold_mode, entity_id=None):
"""Set new hold mode."""
data = {
ATTR_HOLD_MODE: hold_mode
}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_SET_HOLD_MODE, data)
def set_aux_heat(hass, aux_heat, entity_id=None):
"""Turn all or specified climate devices auxillary heater on."""
data = {
@@ -185,69 +206,95 @@ def set_swing_mode(hass, swing_mode, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_SWING_MODE, data)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Setup climate devices."""
component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)
yield from component.async_setup(config)
descriptions = load_yaml_config_file(
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file,
os.path.join(os.path.dirname(__file__), 'services.yaml'))
def away_mode_set_service(service):
@asyncio.coroutine
def _async_update_climate(target_climate):
"""Update climate entity after service stuff."""
update_tasks = []
for climate in target_climate:
if not climate.should_poll:
continue
update_coro = hass.loop.create_task(
climate.async_update_ha_state(True))
if hasattr(climate, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
@asyncio.coroutine
def async_away_mode_set_service(service):
"""Set away mode on target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
away_mode = service.data.get(ATTR_AWAY_MODE)
if away_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
return
for climate in target_climate:
if away_mode:
climate.turn_away_mode_on()
yield from climate.async_turn_away_mode_on()
else:
climate.turn_away_mode_off()
yield from climate.async_turn_away_mode_off()
if climate.should_poll:
climate.update_ha_state(True)
yield from _async_update_climate(target_climate)
hass.services.register(
DOMAIN, SERVICE_SET_AWAY_MODE, away_mode_set_service,
hass.services.async_register(
DOMAIN, SERVICE_SET_AWAY_MODE, async_away_mode_set_service,
descriptions.get(SERVICE_SET_AWAY_MODE),
schema=SET_AWAY_MODE_SCHEMA)
def aux_heat_set_service(service):
@asyncio.coroutine
def async_hold_mode_set_service(service):
"""Set hold mode on target climate devices."""
target_climate = component.async_extract_from_service(service)
hold_mode = service.data.get(ATTR_HOLD_MODE)
for climate in target_climate:
yield from climate.async_set_hold_mode(hold_mode)
yield from _async_update_climate(target_climate)
hass.services.async_register(
DOMAIN, SERVICE_SET_HOLD_MODE, async_hold_mode_set_service,
descriptions.get(SERVICE_SET_HOLD_MODE),
schema=SET_HOLD_MODE_SCHEMA)
@asyncio.coroutine
def async_aux_heat_set_service(service):
"""Set auxillary heater on target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
aux_heat = service.data.get(ATTR_AUX_HEAT)
if aux_heat is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_AUX_HEAT, ATTR_AUX_HEAT)
return
for climate in target_climate:
if aux_heat:
climate.turn_aux_heat_on()
yield from climate.async_turn_aux_heat_on()
else:
climate.turn_aux_heat_off()
yield from climate.async_turn_aux_heat_off()
if climate.should_poll:
climate.update_ha_state(True)
yield from _async_update_climate(target_climate)
hass.services.register(
DOMAIN, SERVICE_SET_AUX_HEAT, aux_heat_set_service,
hass.services.async_register(
DOMAIN, SERVICE_SET_AUX_HEAT, async_aux_heat_set_service,
descriptions.get(SERVICE_SET_AUX_HEAT),
schema=SET_AUX_HEAT_SCHEMA)
def temperature_set_service(service):
@asyncio.coroutine
def async_temperature_set_service(service):
"""Set temperature on the target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
for climate in target_climate:
kwargs = {}
@@ -261,106 +308,83 @@ def setup(hass, config):
else:
kwargs[value] = temp
climate.set_temperature(**kwargs)
if climate.should_poll:
climate.update_ha_state(True)
yield from climate.async_set_temperature(**kwargs)
hass.services.register(
DOMAIN, SERVICE_SET_TEMPERATURE, temperature_set_service,
yield from _async_update_climate(target_climate)
hass.services.async_register(
DOMAIN, SERVICE_SET_TEMPERATURE, async_temperature_set_service,
descriptions.get(SERVICE_SET_TEMPERATURE),
schema=SET_TEMPERATURE_SCHEMA)
def humidity_set_service(service):
@asyncio.coroutine
def async_humidity_set_service(service):
"""Set humidity on the target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
humidity = service.data.get(ATTR_HUMIDITY)
if humidity is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_HUMIDITY, ATTR_HUMIDITY)
return
for climate in target_climate:
climate.set_humidity(humidity)
yield from climate.async_set_humidity(humidity)
if climate.should_poll:
climate.update_ha_state(True)
yield from _async_update_climate(target_climate)
hass.services.register(
DOMAIN, SERVICE_SET_HUMIDITY, humidity_set_service,
hass.services.async_register(
DOMAIN, SERVICE_SET_HUMIDITY, async_humidity_set_service,
descriptions.get(SERVICE_SET_HUMIDITY),
schema=SET_HUMIDITY_SCHEMA)
def fan_mode_set_service(service):
@asyncio.coroutine
def async_fan_mode_set_service(service):
"""Set fan mode on target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
fan = service.data.get(ATTR_FAN_MODE)
if fan is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_FAN_MODE, ATTR_FAN_MODE)
return
for climate in target_climate:
climate.set_fan_mode(fan)
yield from climate.async_set_fan_mode(fan)
if climate.should_poll:
climate.update_ha_state(True)
yield from _async_update_climate(target_climate)
hass.services.register(
DOMAIN, SERVICE_SET_FAN_MODE, fan_mode_set_service,
hass.services.async_register(
DOMAIN, SERVICE_SET_FAN_MODE, async_fan_mode_set_service,
descriptions.get(SERVICE_SET_FAN_MODE),
schema=SET_FAN_MODE_SCHEMA)
def operation_set_service(service):
@asyncio.coroutine
def async_operation_set_service(service):
"""Set operating mode on the target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
operation_mode = service.data.get(ATTR_OPERATION_MODE)
if operation_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_OPERATION_MODE, ATTR_OPERATION_MODE)
return
for climate in target_climate:
climate.set_operation_mode(operation_mode)
yield from climate.async_set_operation_mode(operation_mode)
if climate.should_poll:
climate.update_ha_state(True)
yield from _async_update_climate(target_climate)
hass.services.register(
DOMAIN, SERVICE_SET_OPERATION_MODE, operation_set_service,
hass.services.async_register(
DOMAIN, SERVICE_SET_OPERATION_MODE, async_operation_set_service,
descriptions.get(SERVICE_SET_OPERATION_MODE),
schema=SET_OPERATION_MODE_SCHEMA)
def swing_set_service(service):
@asyncio.coroutine
def async_swing_set_service(service):
"""Set swing mode on the target climate devices."""
target_climate = component.extract_from_service(service)
target_climate = component.async_extract_from_service(service)
swing_mode = service.data.get(ATTR_SWING_MODE)
if swing_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_SWING_MODE, ATTR_SWING_MODE)
return
for climate in target_climate:
climate.set_swing_mode(swing_mode)
yield from climate.async_set_swing_mode(swing_mode)
if climate.should_poll:
climate.update_ha_state(True)
yield from _async_update_climate(target_climate)
hass.services.register(
DOMAIN, SERVICE_SET_SWING_MODE, swing_set_service,
hass.services.async_register(
DOMAIN, SERVICE_SET_SWING_MODE, async_swing_set_service,
descriptions.get(SERVICE_SET_SWING_MODE),
schema=SET_SWING_MODE_SCHEMA)
return True
@@ -421,6 +445,10 @@ class ClimateDevice(Entity):
if self.operation_list:
data[ATTR_OPERATION_LIST] = self.operation_list
is_hold = self.current_hold_mode
if is_hold is not None:
data[ATTR_HOLD_MODE] = is_hold
swing_mode = self.current_swing_mode
if swing_mode is not None:
data[ATTR_SWING_MODE] = swing_mode
@@ -492,6 +520,11 @@ class ClimateDevice(Entity):
"""Return true if away mode is on."""
return None
@property
def current_hold_mode(self):
"""Return the current hold mode, e.g., home, away, temp."""
return None
@property
def is_aux_heat_on(self):
"""Return true if aux heater."""
@@ -521,38 +554,122 @@ class ClimateDevice(Entity):
"""Set new target temperature."""
raise NotImplementedError()
def async_set_temperature(self, **kwargs):
"""Set new target temperature.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.set_temperature, **kwargs))
def set_humidity(self, humidity):
"""Set new target humidity."""
raise NotImplementedError()
def async_set_humidity(self, humidity):
"""Set new target humidity.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.set_humidity, humidity)
def set_fan_mode(self, fan):
"""Set new target fan mode."""
raise NotImplementedError()
def async_set_fan_mode(self, fan):
"""Set new target fan mode.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.set_fan_mode, fan)
def set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
raise NotImplementedError()
def async_set_operation_mode(self, operation_mode):
"""Set new target operation mode.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.set_operation_mode, operation_mode)
def set_swing_mode(self, swing_mode):
"""Set new target swing operation."""
raise NotImplementedError()
def async_set_swing_mode(self, swing_mode):
"""Set new target swing operation.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.set_swing_mode, swing_mode)
def turn_away_mode_on(self):
"""Turn away mode on."""
raise NotImplementedError()
def async_turn_away_mode_on(self):
"""Turn away mode on.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.turn_away_mode_on)
def turn_away_mode_off(self):
"""Turn away mode off."""
raise NotImplementedError()
def async_turn_away_mode_off(self):
"""Turn away mode off.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.turn_away_mode_off)
def set_hold_mode(self, hold_mode):
"""Set new target hold mode."""
raise NotImplementedError()
def async_set_hold_mode(self, hold_mode):
"""Set new target hold mode.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.set_hold_mode, hold_mode)
def turn_aux_heat_on(self):
"""Turn auxillary heater on."""
raise NotImplementedError()
def async_turn_aux_heat_on(self):
"""Turn auxillary heater on.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.turn_aux_heat_on)
def turn_aux_heat_off(self):
"""Turn auxillary heater off."""
raise NotImplementedError()
def async_turn_aux_heat_off(self):
"""Turn auxillary heater off.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.turn_aux_heat_off)
@property
def min_temp(self):
"""Return the minimum temperature."""

View File

@@ -12,11 +12,11 @@ from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo climate devices."""
add_devices([
DemoClimate("HeatPump", 68, TEMP_FAHRENHEIT, None, 77, "Auto Low",
None, None, "Auto", "heat", None, None, None),
DemoClimate("Hvac", 21, TEMP_CELSIUS, True, 22, "On High",
DemoClimate("HeatPump", 68, TEMP_FAHRENHEIT, None, None, 77,
"Auto Low", None, None, "Auto", "heat", None, None, None),
DemoClimate("Hvac", 21, TEMP_CELSIUS, True, None, 22, "On High",
67, 54, "Off", "cool", False, None, None),
DemoClimate("Ecobee", None, TEMP_CELSIUS, None, 23, "Auto Low",
DemoClimate("Ecobee", None, TEMP_CELSIUS, None, None, 23, "Auto Low",
None, None, "Auto", "auto", None, 24, 21)
])
@@ -25,7 +25,7 @@ class DemoClimate(ClimateDevice):
"""Representation of a demo climate device."""
def __init__(self, name, target_temperature, unit_of_measurement,
away, current_temperature, current_fan_mode,
away, hold, current_temperature, current_fan_mode,
target_humidity, current_humidity, current_swing_mode,
current_operation, aux, target_temp_high, target_temp_low):
"""Initialize the climate device."""
@@ -34,6 +34,7 @@ class DemoClimate(ClimateDevice):
self._target_humidity = target_humidity
self._unit_of_measurement = unit_of_measurement
self._away = away
self._hold = hold
self._current_temperature = current_temperature
self._current_humidity = current_humidity
self._current_fan_mode = current_fan_mode
@@ -106,6 +107,11 @@ class DemoClimate(ClimateDevice):
"""Return if away mode is on."""
return self._away
@property
def current_hold_mode(self):
"""Return hold mode setting."""
return self._hold
@property
def is_aux_heat_on(self):
"""Return true if away mode is on."""
@@ -129,27 +135,27 @@ class DemoClimate(ClimateDevice):
kwargs.get(ATTR_TARGET_TEMP_LOW) is not None:
self._target_temperature_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
self._target_temperature_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
self.update_ha_state()
self.schedule_update_ha_state()
def set_humidity(self, humidity):
"""Set new target temperature."""
self._target_humidity = humidity
self.update_ha_state()
self.schedule_update_ha_state()
def set_swing_mode(self, swing_mode):
"""Set new target temperature."""
self._current_swing_mode = swing_mode
self.update_ha_state()
self.schedule_update_ha_state()
def set_fan_mode(self, fan):
"""Set new target temperature."""
self._current_fan_mode = fan
self.update_ha_state()
self.schedule_update_ha_state()
def set_operation_mode(self, operation_mode):
"""Set new target temperature."""
self._current_operation = operation_mode
self.update_ha_state()
self.schedule_update_ha_state()
@property
def current_swing_mode(self):
@@ -164,19 +170,24 @@ class DemoClimate(ClimateDevice):
def turn_away_mode_on(self):
"""Turn away mode on."""
self._away = True
self.update_ha_state()
self.schedule_update_ha_state()
def turn_away_mode_off(self):
"""Turn away mode off."""
self._away = False
self.update_ha_state()
self.schedule_update_ha_state()
def set_hold_mode(self, hold):
"""Update hold mode on."""
self._hold = hold
self.schedule_update_ha_state()
def turn_aux_heat_on(self):
"""Turn away auxillary heater on."""
self._aux = True
self.update_ha_state()
self.schedule_update_ha_state()
def turn_aux_heat_off(self):
"""Turn auxillary heater off."""
self._aux = False
self.update_ha_state()
self.schedule_update_ha_state()

View File

@@ -11,10 +11,10 @@ import voluptuous as vol
from homeassistant.components import ecobee
from homeassistant.components.climate import (
DOMAIN, STATE_COOL, STATE_HEAT, STATE_IDLE, ClimateDevice,
DOMAIN, STATE_COOL, STATE_HEAT, STATE_AUTO, STATE_IDLE, ClimateDevice,
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH)
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_OFF, STATE_ON, TEMP_FAHRENHEIT)
ATTR_ENTITY_ID, STATE_OFF, STATE_ON, ATTR_TEMPERATURE, TEMP_FAHRENHEIT)
from homeassistant.config import load_yaml_config_file
import homeassistant.helpers.config_validation as cv
@@ -22,16 +22,25 @@ _CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
ATTR_FAN_MIN_ON_TIME = 'fan_min_on_time'
ATTR_RESUME_ALL = 'resume_all'
DEFAULT_RESUME_ALL = False
DEPENDENCIES = ['ecobee']
SERVICE_SET_FAN_MIN_ON_TIME = 'ecobee_set_fan_min_on_time'
SERVICE_RESUME_PROGRAM = 'ecobee_resume_program'
SET_FAN_MIN_ON_TIME_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_FAN_MIN_ON_TIME): vol.Coerce(int),
})
RESUME_PROGRAM_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_RESUME_ALL, default=DEFAULT_RESUME_ALL): cv.boolean,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Ecobee Thermostat Platform."""
@@ -48,20 +57,35 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
def fan_min_on_time_set_service(service):
"""Set the minimum fan on time on the target thermostats."""
entity_id = service.data.get('entity_id')
entity_id = service.data.get(ATTR_ENTITY_ID)
fan_min_on_time = service.data[ATTR_FAN_MIN_ON_TIME]
if entity_id:
target_thermostats = [device for device in devices
if device.entity_id == entity_id]
if device.entity_id in entity_id]
else:
target_thermostats = devices
fan_min_on_time = service.data[ATTR_FAN_MIN_ON_TIME]
for thermostat in target_thermostats:
thermostat.set_fan_min_on_time(str(fan_min_on_time))
thermostat.update_ha_state(True)
thermostat.schedule_update_ha_state(True)
def resume_program_set_service(service):
"""Resume the program on the target thermostats."""
entity_id = service.data.get(ATTR_ENTITY_ID)
resume_all = service.data.get(ATTR_RESUME_ALL)
if entity_id:
target_thermostats = [device for device in devices
if device.entity_id in entity_id]
else:
target_thermostats = devices
for thermostat in target_thermostats:
thermostat.resume_program(resume_all)
thermostat.schedule_update_ha_state(True)
descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))
@@ -71,6 +95,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
descriptions.get(SERVICE_SET_FAN_MIN_ON_TIME),
schema=SET_FAN_MIN_ON_TIME_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_RESUME_PROGRAM, resume_program_set_service,
descriptions.get(SERVICE_RESUME_PROGRAM),
schema=RESUME_PROGRAM_SCHEMA)
class Thermostat(ClimateDevice):
"""A thermostat class for Ecobee."""
@@ -116,12 +145,30 @@ class Thermostat(ClimateDevice):
@property
def target_temperature_low(self):
"""Return the lower bound temperature we try to reach."""
return int(self.thermostat['runtime']['desiredHeat'] / 10)
if self.current_operation == STATE_AUTO:
return int(self.thermostat['runtime']['desiredHeat'] / 10)
else:
return None
@property
def target_temperature_high(self):
"""Return the upper bound temperature we try to reach."""
return int(self.thermostat['runtime']['desiredCool'] / 10)
if self.current_operation == STATE_AUTO:
return int(self.thermostat['runtime']['desiredCool'] / 10)
else:
return None
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
if self.current_operation == STATE_AUTO:
return None
if self.current_operation == STATE_HEAT:
return int(self.thermostat['runtime']['desiredHeat'] / 10)
elif self.current_operation == STATE_COOL:
return int(self.thermostat['runtime']['desiredCool'] / 10)
else:
return None
@property
def desired_fan_mode(self):
@@ -136,6 +183,34 @@ class Thermostat(ClimateDevice):
else:
return STATE_OFF
@property
def current_hold_mode(self):
"""Return current hold mode."""
events = self.thermostat['events']
if any((event['holdClimateRef'] == 'away' and
int(event['endDate'][0:4])-int(event['startDate'][0:4]) <= 1)
or event['type'] == 'autoAway'
for event in events):
# away hold is auto away or a temporary hold from away climate
hold = 'away'
elif any(event['holdClimateRef'] == 'away' and
int(event['endDate'][0:4])-int(event['startDate'][0:4]) > 1
for event in events):
# a permanent away is not considered a hold, but away_mode
hold = None
elif any(event['holdClimateRef'] == 'home' or
event['type'] == 'autoHome'
for event in events):
# home mode is auto home or any home hold
hold = 'home'
elif any(event['type'] == 'hold' and event['running']
for event in events):
hold = 'temp'
# temperature hold is any other hold not based on climate
else:
hold = None
return hold
@property
def current_operation(self):
"""Return current operation."""
@@ -189,24 +264,24 @@ class Thermostat(ClimateDevice):
"fan_min_on_time": self.fan_min_on_time
}
def is_vacation_on(self):
"""Return true if vacation mode is on."""
events = self.thermostat['events']
return any(event['type'] == 'vacation' and event['running']
for event in events)
@property
def is_away_mode_on(self):
"""Return true if away mode is on."""
mode = self.mode
events = self.thermostat['events']
for event in events:
if event['running']:
mode = event['holdClimateRef']
break
return 'away' in mode
return any(event['holdClimateRef'] == 'away' and
int(event['endDate'][0:4])-int(event['startDate'][0:4]) > 1
for event in events)
def turn_away_mode_on(self):
"""Turn away on."""
if self.hold_temp:
self.data.ecobee.set_climate_hold(self.thermostat_index,
"away", "indefinite")
else:
self.data.ecobee.set_climate_hold(self.thermostat_index, "away")
self.data.ecobee.set_climate_hold(self.thermostat_index,
"away", 'indefinite')
self.update_without_throttle = True
def turn_away_mode_off(self):
@@ -214,28 +289,69 @@ class Thermostat(ClimateDevice):
self.data.ecobee.resume_program(self.thermostat_index)
self.update_without_throttle = True
def set_hold_mode(self, hold_mode):
"""Set hold mode (away, home, temp)."""
hold = self.current_hold_mode
if hold == hold_mode:
# no change, so no action required
return
elif hold_mode == 'away':
self.data.ecobee.set_climate_hold(self.thermostat_index,
"away", self.hold_preference())
elif hold_mode == 'home':
self.data.ecobee.set_climate_hold(self.thermostat_index,
"home", self.hold_preference())
elif hold_mode == 'temp':
self.set_temp_hold(int(self.current_temperature))
else:
self.data.ecobee.resume_program(self.thermostat_index)
self.update_without_throttle = True
def set_auto_temp_hold(self, heat_temp, cool_temp):
"""Set temperature hold in auto mode."""
self.data.ecobee.set_hold_temp(self.thermostat_index, cool_temp,
heat_temp, self.hold_preference())
_LOGGER.debug("Setting ecobee hold_temp to: heat=%s, is=%s, "
"cool=%s, is=%s", heat_temp, isinstance(
heat_temp, (int, float)), cool_temp,
isinstance(cool_temp, (int, float)))
self.update_without_throttle = True
def set_temp_hold(self, temp):
"""Set temperature hold in modes other than auto."""
# Set arbitrary range when not in auto mode
if self.current_operation == STATE_HEAT:
heat_temp = temp
cool_temp = temp + 20
elif self.current_operation == STATE_COOL:
heat_temp = temp - 20
cool_temp = temp
self.data.ecobee.set_hold_temp(self.thermostat_index, cool_temp,
heat_temp, self.hold_preference())
_LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, "
"cool=%s, is=%s", heat_temp, isinstance(
heat_temp, (int, float)), cool_temp,
isinstance(cool_temp, (int, float)))
self.update_without_throttle = True
def set_temperature(self, **kwargs):
"""Set new target temperature."""
if kwargs.get(ATTR_TARGET_TEMP_LOW) is not None and \
kwargs.get(ATTR_TARGET_TEMP_HIGH) is not None:
high_temp = int(kwargs.get(ATTR_TARGET_TEMP_LOW))
low_temp = int(kwargs.get(ATTR_TARGET_TEMP_HIGH))
low_temp = kwargs.get(ATTR_TARGET_TEMP_LOW)
high_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH)
temp = kwargs.get(ATTR_TEMPERATURE)
if self.hold_temp:
self.data.ecobee.set_hold_temp(self.thermostat_index, low_temp,
high_temp, "indefinite")
_LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, "
"high=%s, is=%s", low_temp, isinstance(
low_temp, (int, float)), high_temp,
isinstance(high_temp, (int, float)))
if self.current_operation == STATE_AUTO and low_temp is not None \
and high_temp is not None:
self.set_auto_temp_hold(int(low_temp), int(high_temp))
elif temp is not None:
self.set_temp_hold(int(temp))
else:
self.data.ecobee.set_hold_temp(self.thermostat_index, low_temp,
high_temp)
_LOGGER.debug("Setting ecobee temp to: low=%s, is=%s, "
"high=%s, is=%s", low_temp, isinstance(
low_temp, (int, float)), high_temp,
isinstance(high_temp, (int, float)))
self.update_without_throttle = True
_LOGGER.error(
'Missing valid arguments for set_temperature in %s', kwargs)
def set_operation_mode(self, operation_mode):
"""Set HVAC mode (auto, auxHeatOnly, cool, heat, off)."""
@@ -248,20 +364,21 @@ class Thermostat(ClimateDevice):
fan_min_on_time)
self.update_without_throttle = True
# Home and Sleep mode aren't used in UI yet:
def resume_program(self, resume_all):
"""Resume the thermostat schedule program."""
self.data.ecobee.resume_program(self.thermostat_index,
str(resume_all).lower())
self.update_without_throttle = True
# def turn_home_mode_on(self):
# """ Turns home mode on. """
# self.data.ecobee.set_climate_hold(self.thermostat_index, "home")
# def turn_home_mode_off(self):
# """ Turns home mode off. """
# self.data.ecobee.resume_program(self.thermostat_index)
# def turn_sleep_mode_on(self):
# """ Turns sleep mode on. """
# self.data.ecobee.set_climate_hold(self.thermostat_index, "sleep")
# def turn_sleep_mode_off(self):
# """ Turns sleep mode off. """
# self.data.ecobee.resume_program(self.thermostat_index)
def hold_preference(self):
"""Return user preference setting for hold time."""
# Values returned from thermostat are 'useEndTime4hour',
# 'useEndTime2hour', 'nextTransition', 'indefinite', 'askMe'
default = self.thermostat['settings']['holdAction']
if default == 'nextTransition':
return default
# add further conditions if other hold durations should be
# supported; note that this should not include 'indefinite'
# as an indefinite away hold is interpreted as away_mode
else:
return 'nextTransition'

View File

@@ -8,18 +8,28 @@ import logging
import voluptuous as vol
from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA
from homeassistant.components.climate import (
ClimateDevice, PLATFORM_SCHEMA, PRECISION_HALVES,
STATE_AUTO, STATE_ON, STATE_OFF,
)
from homeassistant.const import (
CONF_MAC, TEMP_CELSIUS, CONF_DEVICES, ATTR_TEMPERATURE)
from homeassistant.util.temperature import convert
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['bluepy_devices==0.2.0']
REQUIREMENTS = ['python-eq3bt==0.1.5']
_LOGGER = logging.getLogger(__name__)
ATTR_MODE = 'mode'
ATTR_MODE_READABLE = 'mode_readable'
STATE_BOOST = "boost"
STATE_AWAY = "away"
STATE_MANUAL = "manual"
ATTR_STATE_WINDOW_OPEN = "window_open"
ATTR_STATE_VALVE = "valve"
ATTR_STATE_LOCKED = "is_locked"
ATTR_STATE_LOW_BAT = "low_battery"
ATTR_STATE_AWAY_END = "away_end"
DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_MAC): cv.string,
@@ -48,10 +58,25 @@ class EQ3BTSmartThermostat(ClimateDevice):
def __init__(self, _mac, _name):
"""Initialize the thermostat."""
from bluepy_devices.devices import eq3btsmart
# we want to avoid name clash with this module..
import eq3bt as eq3
self.modes = {eq3.Mode.Open: STATE_ON,
eq3.Mode.Closed: STATE_OFF,
eq3.Mode.Auto: STATE_AUTO,
eq3.Mode.Manual: STATE_MANUAL,
eq3.Mode.Boost: STATE_BOOST,
eq3.Mode.Away: STATE_AWAY}
self.reverse_modes = {v: k for k, v in self.modes.items()}
self._name = _name
self._thermostat = eq3btsmart.EQ3BTSmartThermostat(_mac)
self._thermostat = eq3.Thermostat(_mac)
@property
def available(self) -> bool:
"""Return if thermostat is available."""
return self.current_operation is not None
@property
def name(self):
@@ -63,6 +88,11 @@ class EQ3BTSmartThermostat(ClimateDevice):
"""Return the unit of measurement that is used."""
return TEMP_CELSIUS
@property
def precision(self):
"""Return eq3bt's precision 0.5."""
return PRECISION_HALVES
@property
def current_temperature(self):
"""Can not report temperature, so return target_temperature."""
@@ -81,24 +111,56 @@ class EQ3BTSmartThermostat(ClimateDevice):
self._thermostat.target_temperature = temperature
@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
return {
ATTR_MODE: self._thermostat.mode,
ATTR_MODE_READABLE: self._thermostat.mode_readable,
}
def current_operation(self):
"""Current mode."""
if self._thermostat.mode < 0:
return None
return self.modes[self._thermostat.mode]
@property
def operation_list(self):
"""List of available operation modes."""
return [x for x in self.modes.values()]
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
self._thermostat.mode = self.reverse_modes[operation_mode]
def turn_away_mode_off(self):
"""Away mode off turns to AUTO mode."""
self.set_operation_mode(STATE_AUTO)
def turn_away_mode_on(self):
"""Set away mode on."""
self.set_operation_mode(STATE_AWAY)
@property
def is_away_mode_on(self):
"""Return if we are away."""
return self.current_operation == STATE_AWAY
@property
def min_temp(self):
"""Return the minimum temperature."""
return convert(self._thermostat.min_temp, TEMP_CELSIUS,
self.unit_of_measurement)
return self._thermostat.min_temp
@property
def max_temp(self):
"""Return the maximum temperature."""
return convert(self._thermostat.max_temp, TEMP_CELSIUS,
self.unit_of_measurement)
return self._thermostat.max_temp
@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
dev_specific = {
ATTR_STATE_LOCKED: self._thermostat.locked,
ATTR_STATE_LOW_BAT: self._thermostat.low_battery,
ATTR_STATE_VALVE: self._thermostat.valve_state,
ATTR_STATE_WINDOW_OPEN: self._thermostat.window_open,
ATTR_STATE_AWAY_END: self._thermostat.away_end,
}
return dev_specific
def update(self):
"""Update the data from the thermostat."""

View File

@@ -4,17 +4,19 @@ Adds support for generic thermostat units.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.generic_thermostat/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.components import switch
from homeassistant.components.climate import (
STATE_HEAT, STATE_COOL, STATE_IDLE, ClimateDevice, PLATFORM_SCHEMA)
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_TEMPERATURE)
from homeassistant.helpers import condition
from homeassistant.helpers.event import track_state_change
from homeassistant.helpers.event import async_track_state_change
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -48,7 +50,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup the generic thermostat."""
name = config.get(CONF_NAME)
heater_entity_id = config.get(CONF_HEATER)
@@ -60,7 +63,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
min_cycle_duration = config.get(CONF_MIN_DUR)
tolerance = config.get(CONF_TOLERANCE)
add_devices([GenericThermostat(
yield from async_add_devices([GenericThermostat(
hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp,
target_temp, ac_mode, min_cycle_duration, tolerance)])
@@ -86,11 +89,14 @@ class GenericThermostat(ClimateDevice):
self._target_temp = target_temp
self._unit = hass.config.units.temperature_unit
track_state_change(hass, sensor_entity_id, self._sensor_changed)
async_track_state_change(
hass, sensor_entity_id, self._async_sensor_changed)
async_track_state_change(
hass, heater_entity_id, self._async_switch_changed)
sensor_state = hass.states.get(sensor_entity_id)
if sensor_state:
self._update_temp(sensor_state)
self._async_update_temp(sensor_state)
@property
def should_poll(self):
@@ -127,14 +133,15 @@ class GenericThermostat(ClimateDevice):
"""Return the temperature we try to reach."""
return self._target_temp
def set_temperature(self, **kwargs):
@asyncio.coroutine
def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None:
return
self._target_temp = temperature
self._control_heating()
self.update_ha_state()
self._async_control_heating()
yield from self.async_update_ha_state()
@property
def min_temp(self):
@@ -156,16 +163,25 @@ class GenericThermostat(ClimateDevice):
# Get default temp from super class
return ClimateDevice.max_temp.fget(self)
def _sensor_changed(self, entity_id, old_state, new_state):
@asyncio.coroutine
def _async_sensor_changed(self, entity_id, old_state, new_state):
"""Called when temperature changes."""
if new_state is None:
return
self._update_temp(new_state)
self._control_heating()
self.schedule_update_ha_state()
self._async_update_temp(new_state)
self._async_control_heating()
yield from self.async_update_ha_state()
def _update_temp(self, state):
@callback
def _async_switch_changed(self, entity_id, old_state, new_state):
"""Called when heater switch changes state."""
if new_state is None:
return
self.hass.async_add_job(self.async_update_ha_state())
@callback
def _async_update_temp(self, state):
"""Update thermostat with latest state from sensor."""
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@@ -175,7 +191,8 @@ class GenericThermostat(ClimateDevice):
except ValueError as ex:
_LOGGER.error('Unable to update from sensor: %s', ex)
def _control_heating(self):
@callback
def _async_control_heating(self):
"""Check if we need to turn heating on or off."""
if not self._active and None not in (self._cur_temp,
self._target_temp):
@@ -191,31 +208,37 @@ class GenericThermostat(ClimateDevice):
current_state = STATE_ON
else:
current_state = STATE_OFF
long_enough = condition.state(self.hass, self.heater_entity_id,
current_state,
self.min_cycle_duration)
long_enough = condition.state(
self.hass, self.heater_entity_id, current_state,
self.min_cycle_duration)
if not long_enough:
return
if self.ac_mode:
too_hot = self._cur_temp - self._target_temp > self._tolerance
is_cooling = self._is_device_active
if too_hot and not is_cooling:
_LOGGER.info('Turning on AC %s', self.heater_entity_id)
switch.turn_on(self.hass, self.heater_entity_id)
elif not too_hot and is_cooling:
_LOGGER.info('Turning off AC %s', self.heater_entity_id)
switch.turn_off(self.hass, self.heater_entity_id)
if is_cooling:
too_cold = self._target_temp - self._cur_temp > self._tolerance
if too_cold:
_LOGGER.info('Turning off AC %s', self.heater_entity_id)
switch.async_turn_off(self.hass, self.heater_entity_id)
else:
too_hot = self._cur_temp - self._target_temp > self._tolerance
if too_hot:
_LOGGER.info('Turning on AC %s', self.heater_entity_id)
switch.async_turn_on(self.hass, self.heater_entity_id)
else:
too_cold = self._target_temp - self._cur_temp > self._tolerance
is_heating = self._is_device_active
if too_cold and not is_heating:
_LOGGER.info('Turning on heater %s', self.heater_entity_id)
switch.turn_on(self.hass, self.heater_entity_id)
elif not too_cold and is_heating:
_LOGGER.info('Turning off heater %s', self.heater_entity_id)
switch.turn_off(self.hass, self.heater_entity_id)
if is_heating:
too_hot = self._cur_temp - self._target_temp > self._tolerance
if too_hot:
_LOGGER.info('Turning off heater %s',
self.heater_entity_id)
switch.async_turn_off(self.hass, self.heater_entity_id)
else:
too_cold = self._target_temp - self._cur_temp > self._tolerance
if too_cold:
_LOGGER.info('Turning on heater %s', self.heater_entity_id)
switch.async_turn_on(self.hass, self.heater_entity_id)
@property
def _is_device_active(self):

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['evohomeclient==0.2.5',
'somecomfort==0.3.2']
'somecomfort==0.4.1']
_LOGGER = logging.getLogger(__name__)

View File

@@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
}
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
map_sv_types, devices, add_devices, MySensorsHVAC))
map_sv_types, devices, MySensorsHVAC, add_devices))
class MySensorsHVAC(mysensors.MySensorsDeviceEntity, ClimateDevice):
@@ -135,7 +135,7 @@ class MySensorsHVAC(mysensors.MySensorsDeviceEntity, ClimateDevice):
if self.gateway.optimistic:
# optimistically assume that switch has changed state
self._values[value_type] = value
self.update_ha_state()
self.schedule_update_ha_state()
def set_fan_mode(self, fan):
"""Set new target temperature."""
@@ -145,7 +145,7 @@ class MySensorsHVAC(mysensors.MySensorsDeviceEntity, ClimateDevice):
if self.gateway.optimistic:
# optimistically assume that switch has changed state
self._values[set_req.V_HVAC_SPEED] = fan
self.update_ha_state()
self.schedule_update_ha_state()
def set_operation_mode(self, operation_mode):
"""Set new target temperature."""
@@ -156,7 +156,7 @@ class MySensorsHVAC(mysensors.MySensorsDeviceEntity, ClimateDevice):
if self.gateway.optimistic:
# optimistically assume that switch has changed state
self._values[set_req.V_HVAC_FLOW_STATE] = operation_mode
self.update_ha_state()
self.schedule_update_ha_state()
def update(self):
"""Update the controller with the latest value from a sensor."""

View File

@@ -31,15 +31,16 @@ STATE_HEAT_COOL = 'heat-cool'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Nest thermostat."""
_LOGGER.debug("Setting up nest thermostat")
if discovery_info is None:
return
_LOGGER.debug("Setting up nest thermostat")
temp_unit = hass.config.units.temperature_unit
add_devices(
[NestThermostat(structure, device, temp_unit)
for structure, device in hass.data[DATA_NEST].devices()],
for structure, device in hass.data[DATA_NEST].thermostats()],
True
)
@@ -85,6 +86,8 @@ class NestThermostat(ClimateDevice):
self._eco_temperature = None
self._is_locked = None
self._locked_temperature = None
self._min_temperature = None
self._max_temperature = None
@property
def name(self):
@@ -152,8 +155,8 @@ class NestThermostat(ClimateDevice):
"""Set new target temperature."""
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
if target_temp_low is not None and target_temp_high is not None:
if self._mode == STATE_HEAT_COOL:
if self._mode == STATE_HEAT_COOL:
if target_temp_low is not None and target_temp_high is not None:
temp = (target_temp_low, target_temp_high)
else:
temp = kwargs.get(ATTR_TEMPERATURE)
@@ -203,18 +206,12 @@ class NestThermostat(ClimateDevice):
@property
def min_temp(self):
"""Identify min_temp in Nest API or defaults if not available."""
if self._is_locked:
return self._locked_temperature[0]
else:
return None
return self._min_temperature
@property
def max_temp(self):
"""Identify max_temp in Nest API or defaults if not available."""
if self._is_locked:
return self._locked_temperature[1]
else:
return None
return self._max_temperature
def update(self):
"""Cache value from Python-nest."""
@@ -228,8 +225,10 @@ class NestThermostat(ClimateDevice):
self._away = self.structure.away == 'away'
self._eco_temperature = self.device.eco_temperature
self._locked_temperature = self.device.locked_temperature
self._min_temperature = self.device.min_temperature
self._max_temperature = self.device.max_temperature
self._is_locked = self.device.is_locked
if self.device.temperature == 'C':
if self.device.temperature_scale == 'C':
self._temperature_scale = TEMP_CELSIUS
else:
self._temperature_scale = TEMP_FAHRENHEIT

View File

@@ -111,7 +111,6 @@ class NetatmoThermostat(ClimateDevice):
temp = None
self._data.thermostatdata.setthermpoint(mode, temp, endTimeOffset=None)
self._away = True
self.update_ha_state()
def turn_away_mode_off(self):
"""Turn away off."""
@@ -119,7 +118,6 @@ class NetatmoThermostat(ClimateDevice):
temp = None
self._data.thermostatdata.setthermpoint(mode, temp, endTimeOffset=None)
self._away = False
self.update_ha_state()
def set_temperature(self, endTimeOffset=DEFAULT_TIME_OFFSET, **kwargs):
"""Set new target temperature for 2 hours."""
@@ -131,7 +129,6 @@ class NetatmoThermostat(ClimateDevice):
mode, temperature, endTimeOffset)
self._target_temperature = temperature
self._away = False
self.update_ha_state()
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):

View File

@@ -23,10 +23,19 @@ ATTR_FAN = 'fan'
ATTR_MODE = 'mode'
CONF_HOLD_TEMP = 'hold_temp'
CONF_AWAY_TEMPERATURE_HEAT = 'away_temperature_heat'
CONF_AWAY_TEMPERATURE_COOL = 'away_temperature_cool'
DEFAULT_AWAY_TEMPERATURE_HEAT = 60
DEFAULT_AWAY_TEMPERATURE_COOL = 85
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_HOLD_TEMP, default=False): cv.boolean,
vol.Optional(CONF_AWAY_TEMPERATURE_HEAT,
default=DEFAULT_AWAY_TEMPERATURE_HEAT): vol.Coerce(float),
vol.Optional(CONF_AWAY_TEMPERATURE_COOL,
default=DEFAULT_AWAY_TEMPERATURE_COOL): vol.Coerce(float),
})
@@ -45,12 +54,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return False
hold_temp = config.get(CONF_HOLD_TEMP)
away_temps = [
config.get(CONF_AWAY_TEMPERATURE_HEAT),
config.get(CONF_AWAY_TEMPERATURE_COOL)
]
tstats = []
for host in hosts:
try:
tstat = radiotherm.get_thermostat(host)
tstats.append(RadioThermostat(tstat, hold_temp))
tstats.append(RadioThermostat(tstat, hold_temp, away_temps))
except OSError:
_LOGGER.exception("Unable to connect to Radio Thermostat: %s",
host)
@@ -61,7 +74,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class RadioThermostat(ClimateDevice):
"""Representation of a Radio Thermostat."""
def __init__(self, device, hold_temp):
def __init__(self, device, hold_temp, away_temps):
"""Initialize the thermostat."""
self.device = device
self.set_time()
@@ -71,7 +84,10 @@ class RadioThermostat(ClimateDevice):
self._name = None
self._fmode = None
self._tmode = None
self.hold_temp = hold_temp
self._hold_temp = hold_temp
self._away = False
self._away_temps = away_temps
self._prev_temp = None
self.update()
self._operation_list = [STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_OFF]
@@ -113,6 +129,11 @@ class RadioThermostat(ClimateDevice):
"""Return the temperature we try to reach."""
return self._target_temperature
@property
def is_away_mode_on(self):
"""Return true if away mode is on."""
return self._away
def update(self):
"""Update the data from the thermostat."""
self._current_temperature = self.device.temp['raw']
@@ -138,7 +159,7 @@ class RadioThermostat(ClimateDevice):
self.device.t_cool = round(temperature * 2.0) / 2.0
elif self._current_operation == STATE_HEAT:
self.device.t_heat = round(temperature * 2.0) / 2.0
if self.hold_temp:
if self._hold_temp or self._away:
self.device.hold = 1
else:
self.device.hold = 0
@@ -162,3 +183,23 @@ class RadioThermostat(ClimateDevice):
self.device.t_cool = round(self._target_temperature * 2.0) / 2.0
elif operation_mode == STATE_HEAT:
self.device.t_heat = round(self._target_temperature * 2.0) / 2.0
def turn_away_mode_on(self):
"""Turn away on.
The RTCOA app simulates away mode by using a hold.
"""
away_temp = None
if not self._away:
self._prev_temp = self._target_temperature
if self._current_operation == STATE_HEAT:
away_temp = self._away_temps[0]
elif self._current_operation == STATE_COOL:
away_temp = self._away_temps[1]
self._away = True
self.set_temperature(temperature=away_temp)
def turn_away_mode_off(self):
"""Turn away off."""
self._away = False
self.set_temperature(temperature=self._prev_temp)

View File

@@ -22,6 +22,18 @@ set_away_mode:
description: New value of away mode
example: true
set_hold_mode:
description: Turn hold mode for climate device
fields:
entity_id:
description: Name(s) of entities to change
example: 'climate.kitchen'
hold_mode:
description: New value of hold mode
example: 'away'
set_temperature:
description: Set target temperature of climate device
@@ -66,7 +78,7 @@ set_fan_mode:
description: Name(s) of entities to change
example: 'climate.nest'
fan:
fan_mode:
description: New value of fan mode
example: On Low
@@ -76,7 +88,7 @@ set_operation_mode:
fields:
entity_id:
description: Name(s) of entities to change
example: 'climet.nest'
example: 'climate.nest'
operation_mode:
description: New value of operation mode
@@ -94,3 +106,27 @@ set_swing_mode:
swing_mode:
description: New value of swing mode
example: 1
ecobee_set_fan_min_on_time:
description: Set the minimum fan on time
fields:
entity_id:
description: Name(s) of entities to change
example: 'climate.kitchen'
fan_min_on_time:
description: New value of fan min on time
example: 5
ecobee_resume_program:
description: Resume the programmed schedule
fields:
entity_id:
description: Name(s) of entities to change
example: 'climate.kitchen'
resume_all:
description: Resume all events and return to the scheduled program. This default to false which removes only the top event.
example: true

View File

@@ -4,7 +4,7 @@ Support for Wink thermostats.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.wink/
"""
from homeassistant.components.wink import WinkDevice
from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.components.climate import (
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
@@ -13,12 +13,16 @@ from homeassistant.components.climate import (
from homeassistant.const import (
TEMP_CELSIUS, STATE_ON,
STATE_OFF, STATE_UNKNOWN)
from homeassistant.loader import get_component
DEPENDENCIES = ['wink']
STATE_AUX = 'aux'
STATE_ECO = 'eco'
STATE_FAN = 'fan'
SPEED_LOWEST = 'lowest'
SPEED_LOW = 'low'
SPEED_MEDIUM = 'medium'
SPEED_HIGH = 'high'
ATTR_EXTERNAL_TEMPERATURE = "external_temperature"
ATTR_SMART_TEMPERATURE = "smart_temperature"
@@ -30,8 +34,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink thermostat."""
import pywink
temp_unit = hass.config.units.temperature_unit
add_devices(WinkThermostat(thermostat, hass, temp_unit)
for thermostat in pywink.get_thermostats())
for climate in pywink.get_thermostats():
_id = climate.object_id() + climate.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkThermostat(climate, hass, temp_unit)])
for climate in pywink.get_air_conditioners():
_id = climate.object_id() + climate.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkAC(climate, hass, temp_unit)])
# pylint: disable=abstract-method,too-many-public-methods, too-many-branches
@@ -41,7 +51,6 @@ class WinkThermostat(WinkDevice, ClimateDevice):
def __init__(self, wink, hass, temp_unit):
"""Initialize the Wink device."""
super().__init__(wink, hass)
wink = get_component('wink')
self._config_temp_unit = temp_unit
@property
@@ -329,3 +338,131 @@ class WinkThermostat(WinkDevice, ClimateDevice):
else:
return_value = maximum
return return_value
class WinkAC(WinkDevice, ClimateDevice):
"""Representation of a Wink air conditioner."""
def __init__(self, wink, hass, temp_unit):
"""Initialize the Wink device."""
super().__init__(wink, hass)
self._config_temp_unit = temp_unit
@property
def temperature_unit(self):
"""Return the unit of measurement."""
# The Wink API always returns temp in Celsius
return TEMP_CELSIUS
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
data = {}
target_temp_high = self.target_temperature_high
target_temp_low = self.target_temperature_low
if target_temp_high is not None:
data[ATTR_TARGET_TEMP_HIGH] = self._convert_for_display(
self.target_temperature_high)
if target_temp_low is not None:
data[ATTR_TARGET_TEMP_LOW] = self._convert_for_display(
self.target_temperature_low)
data["total_consumption"] = self.wink.toatl_consumption()
data["schedule_enabled"] = self.wink.toatl_consumption()
return data
@property
def current_temperature(self):
"""Return the current temperature."""
return self.wink.current_temperature()
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
if not self.wink.is_on():
current_op = STATE_OFF
elif self.wink.current_mode() == 'cool_only':
current_op = STATE_COOL
elif self.wink.current_mode() == 'auto_eco':
current_op = STATE_ECO
elif self.wink.current_mode() == 'fan_only':
current_op = STATE_FAN
else:
current_op = STATE_UNKNOWN
return current_op
@property
def operation_list(self):
"""List of available operation modes."""
op_list = ['off']
modes = self.wink.modes()
if 'cool_only' in modes:
op_list.append(STATE_COOL)
if 'auto_eco' in modes:
op_list.append(STATE_ECO)
if 'fan_eco' in modes:
op_list.append(STATE_FAN)
return op_list
def set_temperature(self, **kwargs):
"""Set new target temperature."""
target_temp = kwargs.get(ATTR_TEMPERATURE)
self.wink.set_temperature(target_temp)
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
if operation_mode == STATE_COOL:
self.wink.set_operation_mode('cool_only')
elif operation_mode == STATE_ECO:
self.wink.set_operation_mode('auto_eco')
elif operation_mode == STATE_OFF:
self.wink.set_operation_mode('off')
elif operation_mode == STATE_FAN:
self.wink.set_operation_mode('fan_only')
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self.wink.current_max_set_point()
@property
def target_temperature_low(self):
"""Only supports cool."""
return None
@property
def target_temperature_high(self):
"""Only supports cool."""
return None
@property
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:
return SPEED_LOW
elif speed <= 0.8 and speed > 0.5:
return SPEED_MEDIUM
elif speed <= 1.0 and speed > 0.8:
return SPEED_HIGH
else:
return STATE_UNKNOWN
@property
def fan_list(self):
"""List of available fan modes."""
return [SPEED_LOWEST, 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
elif mode == SPEED_MEDIUM:
speed = 0.8
elif mode == SPEED_HIGH:
speed = 1.0
self.wink.set_ac_fan_speed(speed)

View File

@@ -52,8 +52,6 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
def __init__(self, value, temp_unit):
"""Initialize the Z-Wave climate device."""
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._index = value.index
self._node = value.node
@@ -71,9 +69,6 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
_LOGGER.debug("temp_unit is %s", self._unit)
self._zxt_120 = None
self.update_properties()
# register listener
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
# 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()):
@@ -85,56 +80,55 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
" workaround")
self._zxt_120 = 1
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id or \
self._value.node == value.node:
self.update_properties()
self.schedule_update_ha_state()
_LOGGER.debug("Value changed on network %s", value)
def update_properties(self):
"""Callback on data change for the registered node/value pair."""
"""Callback on data changes for node values."""
# Operation Mode
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE).values():
self._current_operation = value.data
self._operation_list = list(value.data_items)
_LOGGER.debug("self._operation_list=%s", self._operation_list)
_LOGGER.debug("self._current_operation=%s",
self._current_operation)
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)
_LOGGER.debug("self._operation_list=%s", self._operation_list)
_LOGGER.debug("self._current_operation=%s", self._current_operation)
# Current Temp
for value in (
self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL)
.values()):
if value.label == 'Temperature':
self._current_temperature = round((float(value.data)), 1)
self._unit = value.units
self._current_temperature = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL,
label=['Temperature'], member='data')
self._unit = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SENSOR_MULTILEVEL,
label=['Temperature'], member='units')
# Fan Mode
for value in (
self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE)
.values()):
self._current_fan_mode = value.data
self._fan_list = list(value.data_items)
_LOGGER.debug("self._fan_list=%s", self._fan_list)
_LOGGER.debug("self._current_fan_mode=%s",
self._current_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)
_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:
for value in (
self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION)
.values()):
if value.command_class == \
zwave.const.COMMAND_CLASS_CONFIGURATION and \
value.index == 33:
self._current_swing_mode = value.data
self._swing_list = list(value.data_items)
_LOGGER.debug("self._swing_list=%s", self._swing_list)
_LOGGER.debug("self._current_swing_mode=%s",
self._current_swing_mode)
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)
_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 (
@@ -152,19 +146,16 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
break
else:
self._target_temperature = round((float(value.data)), 1)
# Operating state
for value in (
self._node.get_values(
class_id=zwave.const
.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE).values()):
self._operating_state = value.data
self._operating_state = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE,
member='data')
# Fan operating state
for value in (
self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_STATE)
.values()):
self._fan_state = value.data
self._fan_state = self.get_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_STATE,
member='data')
@property
def should_poll(self):
@@ -228,50 +219,29 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice):
else:
return
for value in (self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT)
.values()):
if value.index == self._index:
if self._zxt_120:
# ZXT-120 responds only to whole int
value.data = round(temperature, 0)
self._target_temperature = temperature
self.update_ha_state()
else:
value.data = temperature
self.update_ha_state()
break
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT,
index=self._index, data=temperature)
self.update_ha_state()
def set_fan_mode(self, fan):
"""Set new target fan mode."""
for value in (self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE).
values()):
if value.command_class == \
zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE and \
value.index == 0:
value.data = bytes(fan, 'utf-8')
break
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_FAN_MODE,
index=0, data=bytes(fan, 'utf-8'))
def set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE).values():
if value.command_class == \
zwave.const.COMMAND_CLASS_THERMOSTAT_MODE and value.index == 0:
value.data = bytes(operation_mode, 'utf-8')
break
self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE,
index=0, data=bytes(operation_mode, 'utf-8'))
def set_swing_mode(self, swing_mode):
"""Set new target swing mode."""
if self._zxt_120 == 1:
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION).values():
if value.command_class == \
zwave.const.COMMAND_CLASS_CONFIGURATION and \
value.index == 33:
value.data = bytes(swing_mode, 'utf-8')
break
self.set_value(
class_id=zwave.const.COMMAND_CLASS_CONFIGURATION,
index=33, data=bytes(swing_mode, 'utf-8'))
@property
def device_state_attributes(self):

View File

@@ -4,8 +4,11 @@ Support for Cover devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover/
"""
import os
import asyncio
from datetime import timedelta
import functools as ft
import logging
import os
import voluptuous as vol
@@ -23,7 +26,7 @@ from homeassistant.const import (
DOMAIN = 'cover'
SCAN_INTERVAL = 15
SCAN_INTERVAL = timedelta(seconds=15)
GROUP_NAME_ALL_COVERS = 'all covers'
ENTITY_ID_ALL_COVERS = group.ENTITY_ID_FORMAT.format('all_covers')
@@ -52,17 +55,17 @@ COVER_SET_COVER_TILT_POSITION_SCHEMA = COVER_SERVICE_SCHEMA.extend({
})
SERVICE_TO_METHOD = {
SERVICE_OPEN_COVER: {'method': 'open_cover'},
SERVICE_CLOSE_COVER: {'method': 'close_cover'},
SERVICE_OPEN_COVER: {'method': 'async_open_cover'},
SERVICE_CLOSE_COVER: {'method': 'async_close_cover'},
SERVICE_SET_COVER_POSITION: {
'method': 'set_cover_position',
'method': 'async_set_cover_position',
'schema': COVER_SET_COVER_POSITION_SCHEMA},
SERVICE_STOP_COVER: {'method': 'stop_cover'},
SERVICE_OPEN_COVER_TILT: {'method': 'open_cover_tilt'},
SERVICE_CLOSE_COVER_TILT: {'method': 'close_cover_tilt'},
SERVICE_STOP_COVER_TILT: {'method': 'stop_cover_tilt'},
SERVICE_STOP_COVER: {'method': 'async_stop_cover'},
SERVICE_OPEN_COVER_TILT: {'method': 'async_open_cover_tilt'},
SERVICE_CLOSE_COVER_TILT: {'method': 'async_close_cover_tilt'},
SERVICE_STOP_COVER_TILT: {'method': 'async_stop_cover_tilt'},
SERVICE_SET_COVER_TILT_POSITION: {
'method': 'set_cover_tilt_position',
'method': 'async_set_cover_tilt_position',
'schema': COVER_SET_COVER_TILT_POSITION_SCHEMA},
}
@@ -123,33 +126,53 @@ def stop_cover_tilt(hass, entity_id=None):
hass.services.call(DOMAIN, SERVICE_STOP_COVER_TILT, data)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for covers."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_COVERS)
component.setup(config)
def handle_cover_service(service):
yield from component.async_setup(config)
@asyncio.coroutine
def async_handle_cover_service(service):
"""Handle calls to the cover services."""
covers = component.async_extract_from_service(service)
method = SERVICE_TO_METHOD.get(service.service)
params = service.data.copy()
params.pop(ATTR_ENTITY_ID, None)
if method:
for cover in component.extract_from_service(service):
getattr(cover, method['method'])(**params)
# call method
for cover in covers:
yield from getattr(cover, method['method'])(**params)
if cover.should_poll:
cover.update_ha_state(True)
update_tasks = []
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
for cover in covers:
if not cover.should_poll:
continue
update_coro = hass.loop.create_task(
cover.async_update_ha_state(True))
if hasattr(cover, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
for service_name in SERVICE_TO_METHOD:
schema = SERVICE_TO_METHOD[service_name].get(
'schema', COVER_SERVICE_SCHEMA)
hass.services.register(DOMAIN, service_name, handle_cover_service,
descriptions.get(service_name), schema=schema)
hass.services.async_register(
DOMAIN, service_name, async_handle_cover_service,
descriptions.get(service_name), schema=schema)
return True
@@ -207,30 +230,94 @@ class CoverDevice(Entity):
"""Open the cover."""
raise NotImplementedError()
def async_open_cover(self, **kwargs):
"""Open the cover.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.open_cover, **kwargs))
def close_cover(self, **kwargs):
"""Close cover."""
raise NotImplementedError()
def async_close_cover(self, **kwargs):
"""Close cover.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.close_cover, **kwargs))
def set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
pass
def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.set_cover_position, **kwargs))
def stop_cover(self, **kwargs):
"""Stop the cover."""
pass
def async_stop_cover(self, **kwargs):
"""Stop the cover.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.stop_cover, **kwargs))
def open_cover_tilt(self, **kwargs):
"""Open the cover tilt."""
pass
def async_open_cover_tilt(self, **kwargs):
"""Open the cover tilt.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.open_cover_tilt, **kwargs))
def close_cover_tilt(self, **kwargs):
"""Close the cover tilt."""
pass
def async_close_cover_tilt(self, **kwargs):
"""Close the cover tilt.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.close_cover_tilt, **kwargs))
def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
pass
def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.set_cover_tilt_position, **kwargs))
def stop_cover_tilt(self, **kwargs):
"""Stop the cover."""
pass
def async_stop_cover_tilt(self, **kwargs):
"""Stop the cover.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, ft.partial(self.stop_cover_tilt, **kwargs))

View File

@@ -149,7 +149,7 @@ class DemoCover(CoverDevice):
if self._position in (100, 0, self._set_position):
self.stop_cover()
self.update_ha_state()
self.schedule_update_ha_state()
def _listen_cover_tilt(self):
"""Listen for changes in cover tilt."""
@@ -167,4 +167,4 @@ class DemoCover(CoverDevice):
if self._tilt_position in (100, 0, self._set_tilt_position):
self.stop_cover_tilt()
self.update_ha_state()
self.schedule_update_ha_state()

View File

@@ -199,8 +199,7 @@ class GaradgetCover(CoverDevice):
def _check_state(self, now):
"""Check the state of the service during an operation."""
self.update()
self.update_ha_state()
self.schedule_update_ha_state(True)
def close_cover(self):
"""Close the cover."""

View File

@@ -151,7 +151,7 @@ class MqttCover(CoverDevice):
if self._optimistic:
# Optimistically assume that cover has changed state.
self._state = False
self.update_ha_state()
self.schedule_update_ha_state()
def close_cover(self, **kwargs):
"""Move the cover down."""
@@ -160,7 +160,7 @@ class MqttCover(CoverDevice):
if self._optimistic:
# Optimistically assume that cover has changed state.
self._state = True
self.update_ha_state()
self.schedule_update_ha_state()
def stop_cover(self, **kwargs):
"""Stop the device."""

View File

@@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
})
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
map_sv_types, devices, add_devices, MySensorsCover))
map_sv_types, devices, MySensorsCover, add_devices))
class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice):
@@ -75,7 +75,7 @@ class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice):
self._values[set_req.V_DIMMER] = 100
else:
self._values[set_req.V_LIGHT] = STATE_ON
self.update_ha_state()
self.schedule_update_ha_state()
def close_cover(self, **kwargs):
"""Move the cover down."""
@@ -88,7 +88,7 @@ class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice):
self._values[set_req.V_DIMMER] = 0
else:
self._values[set_req.V_LIGHT] = STATE_OFF
self.update_ha_state()
self.schedule_update_ha_state()
def set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
@@ -99,7 +99,7 @@ class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice):
if self.gateway.optimistic:
# Optimistically assume that cover has changed state.
self._values[set_req.V_DIMMER] = position
self.update_ha_state()
self.schedule_update_ha_state()
def stop_cover(self, **kwargs):
"""Stop the device."""

View File

@@ -0,0 +1,46 @@
"""
Support for Tellstick covers using Tellstick Net.
This platform uses the Telldus Live online service.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.tellduslive/
"""
import logging
from homeassistant.components.cover import CoverDevice
from homeassistant.components.tellduslive import TelldusLiveEntity
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup covers."""
if discovery_info is None:
return
add_devices(TelldusLiveCover(hass, cover) for cover in discovery_info)
class TelldusLiveCover(TelldusLiveEntity, CoverDevice):
"""Representation of a cover."""
@property
def is_closed(self):
"""Return the current position of the cover."""
return self.device.is_down
def close_cover(self, **kwargs):
"""Close the cover."""
self.device.down()
self.changed()
def open_cover(self, **kwargs):
"""Open the cover."""
self.device.up()
self.changed()
def stop_cover(self, **kwargs):
"""Stop the cover."""
self.device.stop()
self.changed()

View File

@@ -6,7 +6,7 @@ https://home-assistant.io/components/cover.wink/
"""
from homeassistant.components.cover import CoverDevice
from homeassistant.components.wink import WinkDevice
from homeassistant.components.wink import WinkDevice, DOMAIN
DEPENDENCIES = ['wink']
@@ -15,10 +15,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink cover platform."""
import pywink
add_devices(WinkCoverDevice(shade, hass) for shade in
pywink.get_shades())
add_devices(WinkCoverDevice(door, hass) for door in
pywink.get_garage_doors())
for shade in pywink.get_shades():
_id = shade.object_id() + shade.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkCoverDevice(shade, hass)])
for door in pywink.get_garage_doors():
_id = door.object_id() + door.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkCoverDevice(door, hass)])
class WinkCoverDevice(WinkDevice, CoverDevice):
@@ -26,7 +30,7 @@ class WinkCoverDevice(WinkDevice, CoverDevice):
def __init__(self, wink, hass):
"""Initialize the cover."""
WinkDevice.__init__(self, wink, hass)
super().__init__(wink, hass)
def close_cover(self):
"""Close the shade."""
@@ -36,13 +40,17 @@ class WinkCoverDevice(WinkDevice, CoverDevice):
"""Open the shade."""
self.wink.set_state(1)
def set_cover_position(self, position, **kwargs):
"""Move the roller shutter to a specific position."""
self.wink.set_state(float(position)/100)
@property
def current_cover_position(self):
"""Return the current position of roller shutter."""
return int(self.wink.state()*100)
@property
def is_closed(self):
"""Return if the cover is closed."""
state = self.wink.state()
if state == 0:
return True
elif state == 1:
return False
else:
return None
return bool(state == 0)

View File

@@ -52,18 +52,13 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
def __init__(self, value):
"""Initialize the zwave rollershutter."""
import libopenzwave
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
# pylint: disable=no-member
self._lozwmgr = libopenzwave.PyManager()
self._lozwmgr.create()
self._node = value.node
self._open_id = None
self._close_id = None
self._current_position = None
self._workaround = None
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
if (value.node.manufacturer_id.strip() and
value.node.product_id.strip()):
specific_sensor_key = (int(value.node.manufacturer_id, 16),
@@ -74,23 +69,18 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
_LOGGER.debug("Controller without positioning feedback")
self._workaround = 1
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id or \
self._value.node == value.node:
self.update_properties()
self.update_ha_state()
_LOGGER.debug("Value changed on network %s", value)
def update_properties(self):
"""Callback on data change for the registered node/value pair."""
"""Callback on data changes for node values."""
# Position value
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and \
value.label == 'Level':
self._current_position = value.data
self._current_position = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Level'], member='data')
self._open_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Open', 'Up'], member='value_id')
self._close_id = self.get_value(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL,
label=['Close', 'Down'], member='value_id')
@property
def is_closed(self):
@@ -116,27 +106,11 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
def open_cover(self, **kwargs):
"""Move the roller shutter up."""
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and value.label == \
'Open' or value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and value.label == \
'Up':
self._lozwmgr.pressButton(value.value_id)
break
zwave.NETWORK.manager.pressButton(self._open_id)
def close_cover(self, **kwargs):
"""Move the roller shutter down."""
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and value.label == \
'Down' or value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and value.label == \
'Close':
self._lozwmgr.pressButton(value.value_id)
break
zwave.NETWORK.manager.pressButton(self._close_id)
def set_cover_position(self, position, **kwargs):
"""Move the roller shutter to a specific position."""
@@ -144,15 +118,7 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice):
def stop_cover(self, **kwargs):
"""Stop the roller shutter."""
for value in self._node.get_values(
class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL).values():
if value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and value.label == \
'Open' or value.command_class == \
zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and value.label == \
'Down':
self._lozwmgr.releaseButton(value.value_id)
break
zwave.NETWORK.manager.releaseButton(self._open_id)
class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice):
@@ -160,24 +126,12 @@ class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice):
def __init__(self, value):
"""Initialize the zwave garage door."""
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._state = value.data
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id:
self._state = value.data
self.update_ha_state()
_LOGGER.debug("Value changed on network %s", value)
@property
def is_closed(self):
"""Return the current position of Zwave garage door."""
return not self._state
return not self._value.data
def close_cover(self):
"""Close the garage door."""

View File

@@ -23,12 +23,14 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
'cover',
'device_tracker',
'fan',
'image_processing',
'light',
'lock',
'media_player',
'notify',
'sensor',
'switch',
'tts',
]

View File

@@ -4,6 +4,7 @@ Provides functionality to turn on lights based on the states.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/device_sun_light_trigger/
"""
import asyncio
import logging
from datetime import timedelta
@@ -12,8 +13,8 @@ import voluptuous as vol
from homeassistant.core import callback
import homeassistant.util.dt as dt_util
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
from homeassistant.helpers.event import track_point_in_time
from homeassistant.helpers.event_decorators import track_state_change
from homeassistant.helpers.event import (
async_track_point_in_time, async_track_state_change)
from homeassistant.loader import get_component
import homeassistant.helpers.config_validation as cv
@@ -42,20 +43,20 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""The triggers to turn lights on or off based on device presence."""
logger = logging.getLogger(__name__)
device_tracker = get_component('device_tracker')
group = get_component('group')
light = get_component('light')
sun = get_component('sun')
disable_turn_off = config[DOMAIN].get(CONF_DISABLE_TURN_OFF)
light_group = config[DOMAIN].get(CONF_LIGHT_GROUP,
light.ENTITY_ID_ALL_LIGHTS)
light_profile = config[DOMAIN].get(CONF_LIGHT_PROFILE)
device_group = config[DOMAIN].get(CONF_DEVICE_GROUP,
device_tracker.ENTITY_ID_ALL_DEVICES)
conf = config[DOMAIN]
disable_turn_off = conf.get(CONF_DISABLE_TURN_OFF)
light_group = conf.get(CONF_LIGHT_GROUP, light.ENTITY_ID_ALL_LIGHTS)
light_profile = conf.get(CONF_LIGHT_PROFILE)
device_group = conf.get(CONF_DEVICE_GROUP,
device_tracker.ENTITY_ID_ALL_DEVICES)
device_entity_ids = group.get_entity_ids(hass, device_group,
device_tracker.DOMAIN)
@@ -74,6 +75,8 @@ def setup(hass, config):
"""Calculate the time when to start fading lights in when sun sets.
Returns None if no next_setting data available.
Async friendly.
"""
next_setting = sun.next_setting(hass)
if not next_setting:
@@ -81,22 +84,26 @@ def setup(hass, config):
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
def async_turn_on_before_sunset(light_id):
"""Helper function to turn on lights.
Speed is slow if there are devices home and the light is not on yet.
"""
"""Helper function to turn on lights."""
if not device_tracker.is_on(hass) or light.is_on(hass, light_id):
return
light.async_turn_on(hass, light_id,
transition=LIGHT_TRANSITION_TIME.seconds,
profile=light_profile)
def async_turn_on_factory(light_id):
"""Factory to generate turn on callbacks."""
@callback
def async_turn_on_light(now):
"""Turn on specific light."""
async_turn_on_before_sunset(light_id)
return async_turn_on_light
# Track every time sun rises so we can schedule a time-based
# pre-sun set event
@track_state_change(sun.ENTITY_ID, sun.STATE_BELOW_HORIZON,
sun.STATE_ABOVE_HORIZON)
@callback
def schedule_lights_at_sun_set(hass, entity, old_state, new_state):
def schedule_light_turn_on(entity, old_state, new_state):
"""The moment sun sets we want to have all the lights on.
We will schedule to have each light start after one another
@@ -106,35 +113,23 @@ def setup(hass, config):
if not start_point:
return
def async_turn_on_factory(light_id):
"""Lambda can keep track of function parameters.
No local parameters. If we put the lambda directly in the below
statement only the last light will be turned on.
"""
@callback
def async_turn_on_light(now):
"""Turn on specific light."""
async_turn_on_before_sunset(light_id)
return async_turn_on_light
for index, light_id in enumerate(light_ids):
track_point_in_time(hass, async_turn_on_factory(light_id),
start_point + index * LIGHT_TRANSITION_TIME)
async_track_point_in_time(
hass, async_turn_on_factory(light_id),
start_point + index * LIGHT_TRANSITION_TIME)
async_track_state_change(hass, sun.ENTITY_ID, schedule_light_turn_on,
sun.STATE_BELOW_HORIZON, sun.STATE_ABOVE_HORIZON)
# If the sun is already above horizon schedule the time-based pre-sun set
# event.
if sun.is_on(hass):
schedule_lights_at_sun_set(hass, None, None, None)
schedule_light_turn_on(None, None, None)
@track_state_change(device_entity_ids, STATE_NOT_HOME, STATE_HOME)
@callback
def check_light_on_dev_state_change(hass, entity, old_state, new_state):
def check_light_on_dev_state_change(entity, old_state, new_state):
"""Handle tracked device state changes."""
# pylint: disable=unused-variable
lights_are_on = group.is_on(hass, light_group)
light_needed = not (lights_are_on or sun.is_on(hass))
# These variables are needed for the elif check
@@ -164,17 +159,25 @@ def setup(hass, config):
# will all the following then, break.
break
if not disable_turn_off:
@track_state_change(device_group, STATE_HOME, STATE_NOT_HOME)
@callback
def turn_off_lights_when_all_leave(hass, entity, old_state, new_state):
"""Handle device group state change."""
# pylint: disable=unused-variable
if not group.is_on(hass, light_group):
return
async_track_state_change(
hass, device_entity_ids, check_light_on_dev_state_change,
STATE_NOT_HOME, STATE_HOME)
logger.info(
"Everyone has left but there are lights on. Turning them off")
light.async_turn_off(hass, light_ids)
if disable_turn_off:
return True
@callback
def turn_off_lights_when_all_leave(entity, old_state, new_state):
"""Handle device group state change."""
if not group.is_on(hass, light_group):
return
logger.info(
"Everyone has left but there are lights on. Turning them off")
light.async_turn_off(hass, light_ids)
async_track_state_change(
hass, device_group, turn_off_lights_when_all_leave,
STATE_HOME, STATE_NOT_HOME)
return True

View File

@@ -8,7 +8,7 @@ import asyncio
from datetime import timedelta
import logging
import os
from typing import Any, Sequence, Callable
from typing import Any, List, Sequence, Callable
import aiohttp
import async_timeout
@@ -24,6 +24,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import GPSType, ConfigType, HomeAssistantType
import homeassistant.helpers.config_validation as cv
import homeassistant.util as util
@@ -50,10 +51,10 @@ CONF_TRACK_NEW = 'track_new_devices'
DEFAULT_TRACK_NEW = True
CONF_CONSIDER_HOME = 'consider_home'
DEFAULT_CONSIDER_HOME = 180
DEFAULT_CONSIDER_HOME = timedelta(seconds=180)
CONF_SCAN_INTERVAL = 'interval_seconds'
DEFAULT_SCAN_INTERVAL = 12
DEFAULT_SCAN_INTERVAL = timedelta(seconds=12)
CONF_AWAY_HIDE = 'hide_if_away'
DEFAULT_AWAY_HIDE = False
@@ -69,12 +70,16 @@ ATTR_LOCATION_NAME = 'location_name'
ATTR_GPS = 'gps'
ATTR_BATTERY = 'battery'
ATTR_ATTRIBUTES = 'attributes'
ATTR_SOURCE_TYPE = 'source_type'
SOURCE_TYPE_GPS = 'gps'
SOURCE_TYPE_ROUTER = 'router'
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SCAN_INTERVAL): cv.positive_int, # seconds
vol.Optional(CONF_SCAN_INTERVAL): cv.time_period,
vol.Optional(CONF_TRACK_NEW, default=DEFAULT_TRACK_NEW): cv.boolean,
vol.Optional(CONF_CONSIDER_HOME,
default=timedelta(seconds=DEFAULT_CONSIDER_HOME)): vol.All(
default=DEFAULT_CONSIDER_HOME): vol.All(
cv.time_period, cv.positive_timedelta)
})
@@ -121,8 +126,7 @@ def async_setup(hass: HomeAssistantType, config: ConfigType):
return False
else:
conf = conf[0] if len(conf) > 0 else {}
consider_home = conf.get(CONF_CONSIDER_HOME,
timedelta(seconds=DEFAULT_CONSIDER_HOME))
consider_home = conf.get(CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME)
track_new = conf.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
devices = yield from async_load_config(yaml_path, hass, consider_home)
@@ -142,23 +146,35 @@ def async_setup(hass: HomeAssistantType, config: ConfigType):
if platform is None:
return
_LOGGER.info("Setting up %s.%s", DOMAIN, p_type)
try:
if hasattr(platform, 'get_scanner'):
scanner = None
setup = None
if hasattr(platform, 'async_get_scanner'):
scanner = yield from platform.async_get_scanner(
hass, {DOMAIN: p_config})
elif hasattr(platform, 'get_scanner'):
scanner = yield from hass.loop.run_in_executor(
None, platform.get_scanner, hass, {DOMAIN: p_config})
elif hasattr(platform, 'async_setup_scanner'):
setup = yield from platform.async_setup_scanner(
hass, p_config, tracker.async_see, disc_info)
elif hasattr(platform, 'setup_scanner'):
setup = yield from hass.loop.run_in_executor(
None, platform.setup_scanner, hass, p_config, tracker.see,
disc_info)
else:
raise HomeAssistantError("Invalid device_tracker platform.")
if scanner is None:
_LOGGER.error('Error setting up platform %s', p_type)
return
if scanner:
yield from async_setup_scanner_platform(
hass, p_config, scanner, tracker.async_see)
return
ret = yield from hass.loop.run_in_executor(
None, platform.setup_scanner, hass, p_config, tracker.see)
if not ret:
if not setup:
_LOGGER.error('Error setting up platform %s', p_type)
return
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error setting up platform %s', p_type)
@@ -178,6 +194,13 @@ def async_setup(hass: HomeAssistantType, config: ConfigType):
discovery.async_listen(
hass, DISCOVERY_PLATFORMS.keys(), async_device_tracker_discovered)
@asyncio.coroutine
def async_platform_discovered(platform, info):
"""Callback to load a platform."""
yield from async_setup_platform(platform, {}, disc_info=info)
discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered)
# Clean up stale devices
async_track_utc_time_change(
hass, tracker.async_update_stale, second=range(0, 60, 5))
@@ -223,17 +246,19 @@ class DeviceTracker(object):
def see(self, mac: str=None, dev_id: str=None, host_name: str=None,
location_name: str=None, gps: GPSType=None, gps_accuracy=None,
battery: str=None, attributes: dict=None):
battery: str=None, attributes: dict=None,
source_type: str=SOURCE_TYPE_GPS):
"""Notify the device tracker that you see a device."""
self.hass.add_job(
self.async_see(mac, dev_id, host_name, location_name, gps,
gps_accuracy, battery, attributes)
gps_accuracy, battery, attributes, source_type)
)
@asyncio.coroutine
def async_see(self, mac: str=None, dev_id: str=None, host_name: str=None,
location_name: str=None, gps: GPSType=None,
gps_accuracy=None, battery: str=None, attributes: dict=None):
gps_accuracy=None, battery: str=None, attributes: dict=None,
source_type: str=SOURCE_TYPE_GPS):
"""Notify the device tracker that you see a device.
This method is a coroutine.
@@ -251,7 +276,8 @@ class DeviceTracker(object):
if device:
yield from device.async_seen(host_name, location_name, gps,
gps_accuracy, battery, attributes)
gps_accuracy, battery, attributes,
source_type)
if device.track:
yield from device.async_update_ha_state()
return
@@ -266,7 +292,8 @@ class DeviceTracker(object):
self.mac_to_dev[mac] = device
yield from device.async_seen(host_name, location_name, gps,
gps_accuracy, battery, attributes)
gps_accuracy, battery, attributes,
source_type)
if device.track:
yield from device.async_update_ha_state()
@@ -282,7 +309,7 @@ class DeviceTracker(object):
list(self.group.tracking) + [device.entity_id])
# lookup mac vendor string to be stored in config
device.set_vendor_for_mac()
yield from device.set_vendor_for_mac()
# update known_devices.yaml
self.hass.async_add_job(
@@ -371,6 +398,10 @@ class Device(Entity):
self.away_hide = hide_if_away
self.vendor = vendor
self.source_type = None
self._attributes = {}
@property
def name(self):
"""Return the name of the entity."""
@@ -389,7 +420,9 @@ class Device(Entity):
@property
def state_attributes(self):
"""Return the device state attributes."""
attr = {}
attr = {
ATTR_SOURCE_TYPE: self.source_type
}
if self.gps:
attr[ATTR_LATITUDE] = self.gps[0]
@@ -399,12 +432,13 @@ class Device(Entity):
if self.battery:
attr[ATTR_BATTERY] = self.battery
if self.attributes:
for key, value in self.attributes.items():
attr[key] = value
return attr
@property
def device_state_attributes(self):
"""Return device state attributes."""
return self._attributes
@property
def hidden(self):
"""If device should be hidden."""
@@ -413,20 +447,27 @@ class Device(Entity):
@asyncio.coroutine
def async_seen(self, host_name: str=None, location_name: str=None,
gps: GPSType=None, gps_accuracy=0, battery: str=None,
attributes: dict=None):
attributes: dict=None, source_type: str=SOURCE_TYPE_GPS):
"""Mark the device as seen."""
self.source_type = source_type
self.last_seen = dt_util.utcnow()
self.host_name = host_name
self.location_name = location_name
self.gps_accuracy = gps_accuracy or 0
self.battery = battery
self.attributes = attributes
if battery:
self.battery = battery
if attributes:
self._attributes.update(attributes)
self.gps = None
if gps is not None:
try:
self.gps = float(gps[0]), float(gps[1])
self.gps_accuracy = gps_accuracy or 0
except (ValueError, TypeError, IndexError):
self.gps = None
self.gps_accuracy = 0
_LOGGER.warning('Could not parse gps value for %s: %s',
self.dev_id, gps)
@@ -451,7 +492,7 @@ class Device(Entity):
return
elif self.location_name:
self._state = self.location_name
elif self.gps is not None:
elif self.gps is not None and self.source_type == SOURCE_TYPE_GPS:
zone_state = zone.async_active_zone(
self.hass, self.gps[0], self.gps[1], self.gps_accuracy)
if zone_state is None:
@@ -460,9 +501,9 @@ class Device(Entity):
self._state = STATE_HOME
else:
self._state = zone_state.name
elif self.stale():
self._state = STATE_NOT_HOME
self.gps = None
self.last_update_home = False
else:
self._state = STATE_HOME
@@ -480,13 +521,18 @@ class Device(Entity):
if not self.mac:
return None
if '_' in self.mac:
_, mac = self.mac.split('_', 1)
else:
mac = self.mac
# prevent lookup of invalid macs
if not len(self.mac.split(':')) == 6:
if not len(mac.split(':')) == 6:
return 'unknown'
# we only need the first 3 bytes of the mac for a lookup
# this improves somewhat on privacy
oui_bytes = self.mac.split(':')[0:3]
oui_bytes = mac.split(':')[0:3]
# 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
@@ -516,6 +562,34 @@ class Device(Entity):
yield from resp.release()
class DeviceScanner(object):
"""Device scanner object."""
hass = None # type: HomeAssistantType
def scan_devices(self) -> List[str]:
"""Scan for devices."""
raise NotImplementedError()
def async_scan_devices(self) -> Any:
"""Scan for devices.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(None, self.scan_devices)
def get_device_name(self, mac: str) -> str:
"""Get device name from mac."""
raise NotImplementedError()
def async_get_device_name(self, mac: str) -> Any:
"""Get device name from mac.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(None, self.get_device_name, mac)
def load_config(path: str, hass: HomeAssistantType, consider_home: timedelta):
"""Load devices from YAML configuration file."""
return run_coroutine_threadsafe(
@@ -572,26 +646,39 @@ def async_setup_scanner_platform(hass: HomeAssistantType, config: ConfigType,
This method is a coroutine.
"""
interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
scanner.hass = hass
# Initial scan of each mac we also tell about host name for config
seen = set() # type: Any
def device_tracker_scan(now: dt_util.dt.datetime):
@asyncio.coroutine
def async_device_tracker_scan(now: dt_util.dt.datetime):
"""Called when interval matches."""
found_devices = scanner.scan_devices()
found_devices = yield from scanner.async_scan_devices()
for mac in found_devices:
if mac in seen:
host_name = None
else:
host_name = scanner.get_device_name(mac)
host_name = yield from scanner.async_get_device_name(mac)
seen.add(mac)
hass.add_job(async_see_device(mac=mac, host_name=host_name))
async_track_utc_time_change(
hass, device_tracker_scan, second=range(0, 60, interval))
kwargs = {
'mac': mac,
'host_name': host_name,
'source_type': SOURCE_TYPE_ROUTER
}
hass.async_add_job(device_tracker_scan, None)
zone_home = hass.states.get(zone.ENTITY_ID_HOME)
if zone_home:
kwargs['gps'] = [zone_home.attributes[ATTR_LATITUDE],
zone_home.attributes[ATTR_LONGITUDE]]
kwargs['gps_accuracy'] = 0
hass.async_add_job(async_see_device(**kwargs))
async_track_time_interval(hass, async_device_tracker_scan, interval)
hass.async_add_job(async_device_tracker_scan, None)
def update_config(path: str, dev_id: str, device: Device):

View File

@@ -14,7 +14,8 @@ import voluptuous as vol
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
from homeassistant.components.device_tracker import (DOMAIN, PLATFORM_SCHEMA)
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
@@ -46,7 +47,7 @@ def get_scanner(hass, config):
Device = namedtuple("Device", ["mac", "ip", "last_update"])
class ActiontecDeviceScanner(object):
class ActiontecDeviceScanner(DeviceScanner):
"""This class queries a an actiontec router for connected devices."""
def __init__(self, config):

View File

@@ -12,7 +12,8 @@ from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
@@ -42,7 +43,7 @@ def get_scanner(hass, config):
return scanner if scanner.success_init else None
class ArubaDeviceScanner(object):
class ArubaDeviceScanner(DeviceScanner):
"""This class queries a Aruba Access Point for connected devices."""
def __init__(self, config):

View File

@@ -14,7 +14,8 @@ from datetime import timedelta
import voluptuous as vol
from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
@@ -70,10 +71,12 @@ _ARP_REGEX = re.compile(
_IP_NEIGH_CMD = 'ip neigh'
_IP_NEIGH_REGEX = re.compile(
r'(?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3})\s' +
r'\w+\s' +
r'\w+\s' +
r'(\w+\s(?P<mac>(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))))?\s' +
r'(?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3}|'
r'([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{1,4}){1,7})\s'
r'\w+\s'
r'\w+\s'
r'(\w+\s(?P<mac>(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))))?\s'
r'\s?(router)?'
r'(?P<status>(\w+))')
_NVRAM_CMD = 'nvram get client_info_tmp'
@@ -97,7 +100,7 @@ def get_scanner(hass, config):
AsusWrtResult = namedtuple('AsusWrtResult', 'neighbors leases arp nvram')
class AsusWrtDeviceScanner(object):
class AsusWrtDeviceScanner(DeviceScanner):
"""This class queries a router running ASUSWRT firmware."""
# Eighth attribute needed for mode (AP mode vs router mode)
@@ -286,8 +289,10 @@ class AsusWrtDeviceScanner(object):
# match mac addresses to IP addresses in ARP table
for arp in result.arp:
if match.group('mac').lower() in arp.decode('utf-8'):
arp_match = _ARP_REGEX.search(arp.decode('utf-8'))
if match.group('mac').lower() in \
arp.decode('utf-8').lower():
arp_match = _ARP_REGEX.search(
arp.decode('utf-8').lower())
if not arp_match:
_LOGGER.warning('Could not parse arp row: %s', arp)
continue
@@ -320,6 +325,8 @@ class AsusWrtDeviceScanner(object):
else:
for lease in result.leases:
if lease.startswith(b'duid '):
continue
match = _LEASES_REGEX.search(lease.decode('utf-8'))
if not match:

View File

@@ -11,8 +11,8 @@ import requests
import voluptuous as vol
from homeassistant.components.device_tracker import (PLATFORM_SCHEMA,
ATTR_ATTRIBUTES)
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, ATTR_ATTRIBUTES)
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_utc_time_change
@@ -49,7 +49,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config: dict, see):
def setup_scanner(hass, config: dict, see, discovery_info=None):
"""Validate the configuration and return an Automatic scanner."""
try:
AutomaticDeviceScanner(hass, config, see)

View File

@@ -9,7 +9,7 @@ import logging
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.components.device_tracker import DOMAIN, DeviceScanner
from homeassistant.util import Throttle
REQUIREMENTS = ['pybbox==0.0.5-alpha']
@@ -29,7 +29,7 @@ def get_scanner(hass, config):
Device = namedtuple('Device', ['mac', 'name', 'ip', 'last_update'])
class BboxDeviceScanner(object):
class BboxDeviceScanner(DeviceScanner):
"""This class scans for devices connected to the bbox."""
def __init__(self, config):

View File

@@ -1,19 +1,12 @@
"""Tracking for bluetooth low energy devices."""
import logging
from datetime import timedelta
import voluptuous as vol
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.components.device_tracker import (
YAML_DEVICES,
CONF_TRACK_NEW,
CONF_SCAN_INTERVAL,
DEFAULT_SCAN_INTERVAL,
PLATFORM_SCHEMA,
load_config,
DEFAULT_TRACK_NEW
YAML_DEVICES, CONF_TRACK_NEW, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
PLATFORM_SCHEMA, load_config
)
import homeassistant.util as util
import homeassistant.util.dt as dt_util
import homeassistant.helpers.config_validation as cv
@@ -24,13 +17,15 @@ REQUIREMENTS = ['gattlib==0.20150805']
BLE_PREFIX = 'BLE_'
MIN_SEEN_NEW = 5
CONF_SCAN_DURATION = "scan_duration"
CONF_BLUETOOTH_DEVICE = "device_id"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SCAN_DURATION, default=10): cv.positive_int
vol.Optional(CONF_SCAN_DURATION, default=10): cv.positive_int,
vol.Optional(CONF_BLUETOOTH_DEVICE, default="hci0"): cv.string
})
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the Bluetooth LE Scanner."""
# pylint: disable=import-error
from gattlib import DiscoveryService
@@ -60,7 +55,7 @@ def setup_scanner(hass, config, see):
"""Discover Bluetooth LE devices."""
_LOGGER.debug("Discovering Bluetooth LE devices")
try:
service = DiscoveryService()
service = DiscoveryService(ble_dev_id)
devices = service.discover(duration)
_LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
except RuntimeError as error:
@@ -70,6 +65,7 @@ def setup_scanner(hass, config, see):
yaml_path = hass.config.path(YAML_DEVICES)
duration = config.get(CONF_SCAN_DURATION)
ble_dev_id = config.get(CONF_BLUETOOTH_DEVICE)
devs_to_track = []
devs_donot_track = []
@@ -88,14 +84,13 @@ def setup_scanner(hass, config, see):
# if track new devices is true discover new devices
# on every scan.
track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
DEFAULT_TRACK_NEW)
track_new = config.get(CONF_TRACK_NEW)
if not devs_to_track and not track_new:
_LOGGER.warning("No Bluetooth LE devices to track!")
return False
interval = util.convert(config.get(CONF_SCAN_INTERVAL), int,
DEFAULT_SCAN_INTERVAL)
interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
def update_ble(now):
"""Lookup Bluetooth LE devices and update status."""
@@ -115,8 +110,7 @@ def setup_scanner(hass, config, see):
_LOGGER.info("Discovered Bluetooth LE device %s", address)
see_device(address, devs[address], new_device=True)
track_point_in_utc_time(hass, update_ble,
now + timedelta(seconds=interval))
track_point_in_utc_time(hass, update_ble, now + interval)
update_ble(dt_util.utcnow())

View File

@@ -1,6 +1,5 @@
"""Tracking for bluetooth devices."""
import logging
from datetime import timedelta
import voluptuous as vol
@@ -22,7 +21,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the Bluetooth Scanner."""
# pylint: disable=import-error
import bluetooth
@@ -83,8 +82,7 @@ def setup_scanner(hass, config, see):
see_device((mac, result))
except bluetooth.BluetoothError:
_LOGGER.exception('Error looking up bluetooth device!')
track_point_in_utc_time(hass, update_bluetooth,
now + timedelta(seconds=interval))
track_point_in_utc_time(hass, update_bluetooth, now + interval)
update_bluetooth(dt_util.utcnow())

View File

@@ -16,7 +16,8 @@ import requests
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST
from homeassistant.util import Throttle
@@ -40,7 +41,7 @@ def get_scanner(hass, config):
return scanner if scanner.success_init else None
class BTHomeHub5DeviceScanner(object):
class BTHomeHub5DeviceScanner(DeviceScanner):
"""This class queries a BT Home Hub 5."""
def __init__(self, config):

View File

@@ -10,7 +10,8 @@ from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, \
CONF_PORT
from homeassistant.util import Throttle
@@ -39,7 +40,7 @@ def get_scanner(hass, config):
return scanner if scanner.success_init else None
class CiscoDeviceScanner(object):
class CiscoDeviceScanner(DeviceScanner):
"""This class queries a wireless router running Cisco IOS firmware."""
def __init__(self, config):

View File

@@ -13,7 +13,8 @@ import requests
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
@@ -41,7 +42,7 @@ def get_scanner(hass, config):
return None
class DdWrtDeviceScanner(object):
class DdWrtDeviceScanner(DeviceScanner):
"""This class queries a wireless router running DD-WRT firmware."""
def __init__(self, config):

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