Compare commits

...

1290 Commits

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: https://github.com/KeepSafe/aiohttp/blob/50b1d30f4128abd895532022c726e97993987c7c/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
Paulus Schoutsen 94b719e150 Merge pull request #4626 from home-assistant/dev
0.34
2016-12-03 12:17:02 -08:00
Paulus Schoutsen 69d3a3dd32 Version bump to 0.34 2016-12-03 12:16:18 -08:00
Paulus Schoutsen 4904653b70 Yarl has been fixed (#4694) 2016-12-03 11:59:05 -08:00
Fabian Affolter dddf4d1460 Style 0.34 (#4689)
* Minor style updates

* Minor style updates

* Update validation and logger messages

* Update ordering

* Fix lint issue

* Fix line too long

* Update ordering

* update logger messages
2016-12-03 20:46:04 +01:00
GadgetReactor 9a6c9cff30 Update reference to correct tplink switch (#4670) 2016-12-03 11:38:14 -08:00
Paulus Schoutsen d3b62e1fe1 Requirements use zip instead of git (#4692) 2016-12-03 10:18:00 -08:00
Paulus Schoutsen f63a79ee8f Remove not dev related scripts (#4690) 2016-12-03 09:59:20 -08:00
Paulus Schoutsen 898ba56d9f Fix aiohttp build (#4691) 2016-12-03 09:49:10 -08:00
Josh Nichols 64a5bff5b2 Nest further improvements (#4655)
* Further improvements on nest platform

- fix binary sensor
- add deprecations for monitored_conditions
- better names for sensors (includes device type)

* lint

* Remove unused weather sensor

* Fix to python-nest to a specific commit

* lint

* lint

* lint

* lint
2016-12-03 09:26:47 -08:00
Paulus Schoutsen af7de8d5ae Merge remote-tracking branch 'origin/master' into dev 2016-12-03 09:11:47 -08:00
Pascal Vizeli 754d98bcd5 Cleanups on homematic climate (#4685) 2016-12-03 14:06:08 +01:00
Paulus Schoutsen 4874030b70 Have api_streams sensor also monitor websocket connections (#4668) 2016-12-02 18:17:46 -08:00
Paulus Schoutsen 84c89686a9 Update __init__.py 2016-12-02 09:13:39 -08:00
Brent Hughes 48fd8f1f63 InfluxDB: Fixed attributes that are lists causing invalid syntax (#4642)
* Fixed attributes that are lists cuasing invalid influx syntax

* Added bool and fixed mixed data type issue

* Fixed changing nearly all data types to float causing some worse influxdb errors. whoops

* Added line to end of file
2016-12-01 23:02:58 -08:00
Lewis Juggins 83a108b20a Sonos specify IP for event subscription (#4177) 2016-12-01 22:22:03 -08:00
Alberto Arias Maestro b0a800cc6d Update commands to match the strings in pynx584 (#4623)
The command string don't match the ones pynx584. See source code:

https://github.com/kk7ds/pynx584/blob/master/nx584/api.py#L68
2016-12-01 22:20:44 -08:00
Matt N 1f5f4e7a89 zoneminder: Support excluding archived events (#4445) 2016-12-01 22:17:38 -08:00
Fabian Affolter b1fbada02d Update throttle and add more attributes (#4644) 2016-12-01 22:15:48 -08:00
Lewis Juggins 08909ed420 (InfluxDB) Configuration for a default measurement value for events without a unit. (#4632) 2016-12-01 22:13:55 -08:00
Nick Touran ec8969351d Prevent Pandora component from crashing or hanging during shutdown. (#4255)
* Prevent Pandora component from crashing or hanging during shutdown.

* Update pandora.py

* Update pandora.py
2016-12-01 22:06:23 -08:00
Javier González Calleja 801a69be3a Extending efergy component for get the amount of energy consumed (#4202)
* Extending efergy component for get the amount of energy consumed

* Changing units from kW to kWh

* Chaning units for Instant Consumption from kWh to kW

* Adding timeout for get and removing pylint config

* Update efergy.py
2016-12-01 22:00:17 -08:00
Fabian Affolter 51e20c92f9 WIP Fix pylint and PEP257 issues (tests) (#4120)
* Fix pylint and PEP257 issues

* More PEP257 fixes
2016-12-01 21:45:19 -08:00
Russell Cloran 443553ff16 Handle IPv6 in zeroconf (#4052) 2016-12-01 21:43:33 -08:00
Pascal Vizeli 2e6a48ff5f WIP: Migrate scene to async + homeassistant scene async (#4665)
* Migrate scene to async + homeassistant scene async

* fix lint

* Update state.py

* Fix tests
2016-12-01 21:38:12 -08:00
Paulus Schoutsen 49cfe38cca Demo platform to group climate instead of thermostat 2016-12-01 21:11:13 -08:00
Pascal Vizeli 8a042586f1 Migrate sensor to async (#4663) 2016-12-01 18:31:55 -08:00
Johan Bloemberg 08f8e540e3 Macvendor (#4468)
* Add MAC vendor lookup for device_tracker.

* Test vendor mac lookup and fix device attribute.

* Generate requirements.

* Style.

* Use hyphen instead of underscore to satisfy 'idna'.

https://github.com/kjd/idna/issues/17

* Resort imports.

* Refactor macvendor to use macvendors.com API instead of netaddr library.

* Test vendor lookup using macvendors.com api.

* Remove debugging.

* Correct description.

* No longer needed.

* Device tracker is now an async component. Fix ddwrt tests.

* Fix linting.

* Add test case for error conditions.

* There is no reason to retry failes vendor loopups as they won't be saved to the file anyways at that point.

* Sorry, bad assumption, this only made things worse.

* Wait for async parts during setup component to complete before asserting results.

* Fix linting.

* Is generated when running 'coverage html'.

* Undo isort.

* Make aioclient_mock exception more generic.

* Only lookup mac vendor string with adding new device to known_devices.yaml.

* Undo isort.

* Revert unneeded change.

* Adjust to use new websession pattern.

* Always make sure to cleanup response.

* Use correct function to release response.

* Fix tests.
2016-12-01 18:30:41 -08:00
Brandon Weeks f09b888a8a Fixes #3511 - handle multiple return values (#4659) 2016-12-01 18:28:52 -08:00
lichtteil 279f82acc4 Mutate values for light color temperature and white value (#4660)
* Mutate values for light color temperature and white value

* Fix lenght of line

* Fix under-indented line

* Fix cgl
2016-12-01 18:26:53 -08:00
iandday de6c5a503b Remote Component and Harmony Platform (#4254)
* Initial Harmony device support, working current activity sensor and switch for each activity
TODO: add new device per hub to send device specific activity

 Changes to be committed:
	new file:   homeassistant/components/harmony.py
	new file:   homeassistant/components/sensor/harmony.py
	new file:   homeassistant/components/switch/harmony.py

* ready for beta, I think

 Changes to be committed:
	modified:   homeassistant/components/harmony.py
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py

*  Changes to be committed:
	modified:   homeassistant/components/harmony.py
	new file:   homeassistant/components/remote/__init__.py
	new file:   homeassistant/components/remote/harmony.py
	new file:   homeassistant/components/remote/services.yaml
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py
Implemented remote component and harmony platform

* streamlined harmony support

* typo

* Initial Harmony device support, working current activity sensor and switch for each activity
TODO: add new device per hub to send device specific activity

 Changes to be committed:
	new file:   homeassistant/components/harmony.py
	new file:   homeassistant/components/sensor/harmony.py
	new file:   homeassistant/components/switch/harmony.py

* ready for beta, I think

 Changes to be committed:
	modified:   homeassistant/components/harmony.py
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py

*  Changes to be committed:
	modified:   homeassistant/components/harmony.py
	new file:   homeassistant/components/remote/__init__.py
	new file:   homeassistant/components/remote/harmony.py
	new file:   homeassistant/components/remote/services.yaml
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py
Implemented remote component and harmony platform

* streamlined harmony support

* typo

* reworked token generation

* delete

* Initial Harmony device support, working current activity sensor and switch for each activity
TODO: add new device per hub to send device specific activity

 Changes to be committed:
	new file:   homeassistant/components/harmony.py
	new file:   homeassistant/components/sensor/harmony.py
	new file:   homeassistant/components/switch/harmony.py

* Initial Harmony device support, working current activity sensor and switch for each activity
TODO: add new device per hub to send device specific activity

 Changes to be committed:
	new file:   homeassistant/components/harmony.py
	new file:   homeassistant/components/sensor/harmony.py
	new file:   homeassistant/components/switch/harmony.py

* ready for beta, I think

 Changes to be committed:
	modified:   homeassistant/components/harmony.py
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py

* ready for beta, I think

 Changes to be committed:
	modified:   homeassistant/components/harmony.py
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py

*  Changes to be committed:
	modified:   homeassistant/components/harmony.py
	new file:   homeassistant/components/remote/__init__.py
	new file:   homeassistant/components/remote/harmony.py
	new file:   homeassistant/components/remote/services.yaml
	modified:   homeassistant/components/sensor/harmony.py
	modified:   homeassistant/components/switch/harmony.py
Implemented remote component and harmony platform

* streamlined harmony support

* typo

* reworked token generation

* delete

* readded after rebase

* cleaning up style errors

* modified .coveragerc

* moved import statements

* added more debug logging

* Added URL encoding of token received from Logitech

* Corrected import for python 3

* new pyharmony version

* new pyharmony version

* remote tests

* only write config file if not present or sync service is called

* more tests

* more tests

* bumped pyharmony version to work with new auth

* bumped pyharmony version to work with new auth

* style corrections

* harmony local auth and remote demo platform

* style fix

* PR refinements and permission issues

* forgot a blank line

* removed sync test from test_init

* removed sync test from test_init

* visual indent

* send_command test in demo platform
2016-12-01 12:48:08 -08:00
Jesse Newland 898f89ffc7 Make trusted_networks iterable (#4649) 2016-12-01 12:28:59 -08:00
Jan Losinski 5c807c6bd9 MPD: Reconnect mpd client afetr OSError (#4651)
If the mpd client ran into an socket timeout, the socket will raise an
OSError on every further request. This adds OSError to the list of
excptions, that causes a client reconnect.

This fixes #4650

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2016-12-01 12:28:31 -08:00
Jan Losinski dd84b4e237 Mpd: Use "file" instead "id" for media_content_id (#4653)
In media_content_id() the "id" of the current song was returned. as
stated in bug #4652 the id is only the Tracklist-Id in the current
tracklist and is omitted if the track is not part of a tracklist (what
caused the bug in the first place).

To match the semantics described in the dockstring, to return a "Content
ID", this chooses the filename of the current song as id and returns
it.

It also uses get() instead of [] to prevent KeyError.

This fixes bug #4652

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2016-12-01 12:20:42 -08:00
John Mihalic 6dfae7a259 Add support for NUT (Network UPS Tools) sensor. (#4551)
* Add support for NUT (Network UPS Tools) sensor.

* Address comments

* Fix issues

* Fix issues 2

* Fix unhandled exception
2016-12-01 08:58:16 +01:00
Johann Kellerman c6c8cd4f51 Yr.no: New aiohttp client needs params to form websession URL (#4634)
* Yr.no: New aiohttp client needs params to form websession URL
* Support params in aiohttp mocking
2016-12-01 08:20:21 +02:00
Pascal Vizeli bde7176b3c Migrate light component to async (#4635) 2016-11-30 13:33:38 -08:00
William Scanlon 4c03d670c1 Wink PubNub v4 (#4561)
* PubNub v4

* Updated to pubnubsub-handler 0.0.5

* Updated requirements_all.txt
2016-11-30 13:12:26 -08:00
Johan Bloemberg 406afbb369 Philips controls (#4441)
* Add channel switching for philips tvs.

* Disable track buttons when not watching tv.

* Undo isort config.

* Yes it does.

* Just testing some assumption on hound's flake8 behaviour.

* Revert "Just testing some assumption on hound's flake8 behaviour."

This reverts commit ff9940b39e.

* poke
2016-11-30 13:07:57 -08:00
Marcelo Moreira de Mello 9c6609cb79 Added support to Amcrest camera (#4573)
* Introduced support to Amcrest IP Cameras

* Fixed lint issues

* Fixed requirements test

* * Implemented test to verify crendentials during camera setup

* Added persistent_notification in case of error when during Amcrest setup
2016-11-30 13:07:17 -08:00
Paulus Schoutsen e5504b39ec Close aiohttp responses (#4624)
* Close aiohttp responses

* Update generic.py
2016-11-30 13:05:58 -08:00
Paulus Schoutsen b1ef5042f9 Make updater more robust (#4625) 2016-11-30 13:03:09 -08:00
Paulus Schoutsen b35fa4f1c1 Finish all tasks before setup phase is done (#4606) 2016-11-30 13:02:45 -08:00
Martin Hjelmare 71da9d2f50 Fix mysensors ir switch overwriting devices (#4612) 2016-11-30 13:02:18 -08:00
Fabian Affolter 86388f5af2 Upgrade Sphinx to 1.4.9 (#4641) 2016-11-30 14:21:00 +01:00
Pascal Vizeli 17f0fb69bd Homematic update with HomematicIP/HomematicWired support and multible… (#4568)
* Homematic update with HomematicIP/HomematicWired support and multible connections

* fix bug in virtualkey service

* create new service & cleanups

* fix lint

* Pump pyhomematic 0.1.18
2016-11-29 20:53:02 +01:00
DaveSergeant 2d02baf3d0 Default dimmable brightness to 255 from 100 (#4621)
* Default dimmable brightness to 255 from 100

Full brightness for ISY dimmers is 255. The current 100 value turns dimmer switches on to just under half brightness.  Probably just an oversight from the Sept implementation.

* Brightness change for turn_on, ramp for turn_off.

Per discussion with Teagan42 and jbcodemonkey, the brightness should rightfully be None and not an explicit value.  There is a continuing issue that the ISY modules don't respect HA's brightness customization values.  A new issue will be opened for this.
Additionally, turn_off was using ISY's fastoff() which didn't respect the ramping time.  The default behavior should just be off().
2016-11-29 09:50:12 -07:00
Lewis Juggins 66473120ab Add test for delay on automations (#4630) 2016-11-29 08:45:04 -08:00
Christian Brædstrup 6ddbb4d568 Improved exception handling for D-Link switch (#4633) 2016-11-29 08:40:51 -08:00
Valentin Alexeev 154c69a454 Bump version of pwaqi module to 1.3. Fixes #4595. (#4610) 2016-11-28 23:11:21 -08:00
Charles Spirakis ad4ec49f9c Update color names to follow w3.org list. (#4374)
The color names -> rgb dictionary now follows the
color names listed in the w3.org site for css3,
section 4.3. Extended color keywords:

https://www.w3.org/TR/2010/PR-css3-color-20101028/#svg-color
2016-11-28 22:59:46 -08:00
Fabian Affolter e8367f245a Update ordering and sync logger messages (#4615) 2016-11-28 20:50:42 +01:00
Fabian Affolter 4bc37bd661 Add timeout to request, update ordering, make dev info message shorter, and (#4613)
update the other logger messages
2016-11-28 20:49:01 +01:00
Pascal Vizeli b4841a17a6 Hotfix device_tracker yaml config (#4611) 2016-11-28 18:43:47 +01:00
Valentin Alexeev 3b9d5cdf73 DuneHD media player (#4588)
* Implement WAQI sensor

* Corrections based on CI check.

* Updated requirements_all.txt for pwaqi==1.2

* Require latest version of pwaqi

* Initial implementation of DuneHD media player component based on pdunehd.

* Major: avoid update() in property fetch,
Major: implement source support,
Major: single device per media player instance,
Major: support for volume / mute controls

* Pythonify pdunehd.
Support media_title.

* Fix pylint.

* Further pylint.

* docstring

* Formatting and indentation.

* Change indentation to spaces.

* Update coverage and recorded requirements before PR.

* Further pylint / fake8 / pydocstyle fixes.

* Implement next / prev track,
Properly decode blu-ray playback,
Attempt to decode media title

* Fix play / pause
Linting

* Update requirements.
Fix lint.

* Fix lint and syntax error

* Yet more linting.

* Yet more linting.

* Fix lint: line too long.

* Force update of HA state.
2016-11-27 23:42:57 -08:00
Daniel Perna 77d568dc47 Fixed incorrect event-order (#4605) 2016-11-27 22:29:21 -08:00
Paulus Schoutsen 9db1ff8cd4 Update frontend 2016-11-27 22:27:02 -08:00
Oliver 248a90b71d Added denon media player controls via denonavr library (#4580)
* Added denonavr module again

* Edited requirements_all.txt

* Edited .coveragerc

* Fixed error with AUX1 input source in library

* Adding device should not fail on connection timeout

* Changed method to select source

* Update requirements_all.txt
2016-11-27 22:13:22 -08:00
Mark King d8c4af9c81 TEMPer component: reset devices on address change (#4596)
Fixes https://github.com/home-assistant/home-assistant/issues/4389

The USB address of these devices periodically changes, causing
home-assistant to fail to read the temperature data. This PR fixes this
by re-reading the available devices on failure. I've been running this
for several days and for the first time have consistent temperature
data without having to restart home-assistant.
2016-11-27 22:01:13 -08:00
Fabian Affolter 1e6c660f59 Threshold sensor (#4216)
* Add threshold sensor

* New config requirement, update async, other changes, and update tests

* Update threshold.py
2016-11-27 21:55:26 -08:00
Harris Borawski 44a508e86c Add exception handling to Sonarr (#4569)
* Add exception handling to request call to prevent
failure in setup_platform if host is down

* update for comments

* update test for state being none

* remove unused import
2016-11-27 21:11:49 -08:00
Bjarni Ivarsson 92c6cee2a1 Support for media_position property on media_player (#4172)
* Added support for media_position property to media_player + implementation for sonos.

* Pla yback progress now updates without needed state transitions in HA.

* Linting fixes

* media_position_update_at property is now a datetime.

* Minor fix.

* Linting fixes.
2016-11-27 17:45:49 -08:00
Paulus Schoutsen d4bc8e23af Update frontend 2016-11-27 17:21:11 -08:00
Antoine Bertin f0db698f75 Light effects (#4538)
* Add support for light effects

* Move PLATFORM_SCHEMA changes in light to mqtt_template

* Add effect validation

* Add unittests

* Add light effect to demo and unittests

* Use cv.string for config validation

* Use cv.ensure_list for config validation

* Fix typo

* Remove unused exception management for effect
2016-11-27 17:15:28 -08:00
Pascal Vizeli cf57db919e Refactory aiohttp clientsession handling in HA (#4602)
* Refactory aiohttp clientsession handling in HA

* remove from core / update platforms / rename file
2016-11-27 16:26:46 -08:00
Josh Nichols 84b12ab007 Nest Cam support (#4292)
* start nestcam support

* start nestcam support

* introduce a access_token_cache_file

* Bare minimum to get nest thermostat loading

* occaisonally the image works

* switch to nest-aware interval for testing

* Add Nest Aware awareness

* remove duplicate error logging line

* Fix nest protect support

* address baloobot

* fix copy pasta

* fix more baloobot

* last baloobot thing for now?

* Use streaming status to determine online or not. online from nest means its on the network

* Fix temperature scale for climate

* Add support for eco mode

* Fix auto mode for nest climate

* update update current_operation and set_operation mode to use constant when possible. try to get setting something working

* remove stale comment

* unused-argument already disabled globally

* Add eco to the end, instead of after off

* Simplify conditional when the hass mode is the same as the nest one

* away_temperature became eco_temperature, and works with eco mode

* Update min/max temp based on locked temperature

* Forgot to set locked stuff during construction

* Cache image instead of throttling (which returns none), respect NestAware subscription

* Fix _time_between_snapshots before the first update

* WIP pin authorization

* Add some more logging

* Working configurator, woo. Fix some hound errors

* Updated pin workflow

* Deprecate more sensors

* Don't update during access of name

* Don't update during access of name

* Add camera brand

* Fix up some syntastic errors

* Fix ups ome hound errors

* Maybe fix some more?

* Move snapshot simulator url checking down into python-nest

* Rename _ready_to_update_camera_image to _ready_for_snapshot

* More fixes

* Set the next time a snapshot can be taken when one is taken to simplify logic

* Add a FIXME about update not getting called

* Call update during constructor, so values get set at least once

* Fix up names

* Remove todo about eco, since that's pretty nest

* thanks hound

* Fix temperature being off for farenheight.

* Fix some lint errors, which includes using a git version of python-nest with updated code

* generate requirements_all.py

* fix pylint

* Update nestcam before adding

* Fix polling of NestCamera

* Lint
2016-11-27 16:18:47 -08:00
Johan Bloemberg 601193b1d2 Expose isort preferences for tools. (#4481)
* Expose isort preferences for tools.

* Adhere to pylints sorted imports requirement.

* More documentation, set typing in between stdlib and 3rd party.
2016-11-27 14:33:30 -08:00
Sean Dague 038b1c1fc6 precision properties for climate components (#4562)
This lets components declare their precision for temperatures. If
nothing is declared, we assume 0.1 C and whole integer precision in
F. Currently this supports only WHOLE, HALVES, and TENTHS for
precision, but adding other precision levels is pretty straight
forward.

This also uses proliphix as an example of changing the precision for a
platform.

Closes bug #4350
2016-11-27 14:19:12 -08:00
Paulus Schoutsen 0d734303a4 HTTP: Fix registering views after start (#4604) 2016-11-27 14:01:12 -08:00
Lewis Juggins ff4cb23f2a Update nginx docs (#4603) 2016-11-27 13:49:21 -08:00
Paulus Schoutsen e94b4ec006 Tweak services return result (#4600)
* Tweak services return result

* Lint
2016-11-27 12:33:02 -08:00
Paulus Schoutsen be91207830 Upgrade HBMQTT (#4599) 2016-11-27 12:21:20 -08:00
Ron Klinkien ecf285105c Fixed unit_of_measurement functionality for knx sensor (#4594) 2016-11-27 12:21:05 -08:00
Paulus Schoutsen 767f3d58ff Add websocket_api as frontend dependency 2016-11-27 12:13:01 -08:00
Lewis Juggins 34097cda24 Allow generic thermostat tolerance to be customisable to determine the temperature difference required to turn switch on. (#4585) 2016-11-27 09:31:00 +00:00
Michaël Arnauts 0ce3703e30 Remove fixed throttle for binary_sensor.command_line and sensor.command_line since the scan_interval is configured trough YAML since #1059 (#4586)
* Remove fixed throttle for binary_sensor.command_line and sensor.command_line since the scan_interval is configured trough YAML since #1059

* Clean up imports

* Add SCAN_INTERVAL=60 to put default scan_inteval back to 60
2016-11-27 00:29:49 -08:00
Paulus Schoutsen 464e843186 Update frontend 2016-11-26 23:44:20 -08:00
Paulus Schoutsen 5d2b7a6e0b Add ping to websockets API (#4592) 2016-11-26 23:22:34 -08:00
Paulus Schoutsen 914a868fbd Add websocket API (#4582)
* Add websocket API

* Add identifiers to interactions

* Allow unsubscribing event listeners

* Add support for fetching data

* Clean up handling code websockets api

* Lint

* Add Home Assistant version to auth messages

* Py.test be less verbose in tox
2016-11-26 18:23:28 -08:00
Paulus Schoutsen 03e0c7c71c Prevent edimax from doing I/O in event loop (#4584) 2016-11-26 10:10:29 -08:00
Paulus Schoutsen 32ffd006fa Reorganize HTTP component (#4575)
* Move HTTP to own folder

* Break HTTP into middlewares

* Lint

* Split tests per middleware

* Clean up HTTP tests

* Make HomeAssistantViews more stateless

* Lint

* Make HTTP setup async
2016-11-25 13:04:06 -08:00
Fabian Affolter 58b85b2e0e Upgrade speedtest-cli to 1.0.0 (#4578) 2016-11-25 12:30:53 -08:00
Marcelo Moreira de Mello 61653a517d #4421 - Forced icons to be displayed via SSL to avoid Mixed Content warnings (#4544)
* #4421 - Forced icons to be displayed via SSL to avoid Mixed Content warnings

* Fixed houndci-bot whitespace

* Using regex to replace http:// for https://

* Created assert test to verify https translation
2016-11-25 20:03:12 +00:00
Vlad Korniev 2a7bc0e55c Advanced Ip filtering (#4424)
* Added IP Bans configuration

* Fixing warnings

* Added ban enabled option and unit tests

* Fixed py34 tox

* http: requested changes fix

* Requested changes fix
2016-11-24 21:52:10 -08:00
Lewis Juggins 95b439fbd5 Upgrade aiohttp to 1.1.5 (#4213) 2016-11-24 21:37:56 -08:00
Paulus Schoutsen 1872481f47 Merge pull request #4572 from home-assistant/release-0-33-4
0.33.4
2016-11-24 15:37:38 -08:00
Paulus Schoutsen 44b6d23e0f Version bump to 0.33.4 2016-11-24 14:57:12 -08:00
Paulus Schoutsen 58eb0ec52a Set executor pool size to 10 (#4571) 2016-11-24 14:56:59 -08:00
Paulus Schoutsen febe16d700 Set executor pool size to 10 (#4571) 2016-11-24 14:56:33 -08:00
Pascal Vizeli 8c56091af7 Hotfix executor pool size (#4552) 2016-11-24 14:53:46 -08:00
Paulus Schoutsen eacdce9ed9 Track tasks only during shutdown and tests (#4428)
* Track tasks only when needed

* Tweak async_block_till_done
2016-11-24 14:49:29 -08:00
Paulus Schoutsen 42c99b0ccb Pass hass object to ServiceRegistry constructor (#4570) 2016-11-24 14:02:39 -08:00
Jon Caruana 2a6c0cfc17 LiteJet: Unit tests and new trigger options held_more_than and held_less_than. (#4473)
* LiteJet: Unit tests and new trigger options held_more_than and held_less_than.
* Unit tests for the LiteJet component and associated platforms. Coverage is almost 100% -- just misses one line.
* The automation LiteJet trigger returns an empty "removal" function to ensure the automation base is happy with it. The pylitejet library doesn't actually support a real removal.
* The automation LiteJet trigger can detect hold time and act appropriately to support things like short tap or long hold.

* LiteJet: Fix indent in unit test source code.

* LiteJet: Fix test_include_switches_* unit tests on Python 3.5

* LiteJet: Remove wait for state existence from unit tests. Recent fixes to discovery make this no longer necessary.
2016-11-24 09:52:15 -08:00
Fabian Affolter 84040892df Remove globally disable pylint issue (#4565) 2016-11-24 12:25:01 +01:00
Fabian Affolter 345008c673 Fix docstring (#4564) 2016-11-24 10:15:00 +01:00
Matt N 14d1494cd2 systemmonitor: Support monitoring removable network interfaces (#4462) 2016-11-24 10:14:38 +01:00
Marcel030nl f1d11e77ed Update pvoutput.py (#4557)
This addition could be usefull when working with the template sensor using the data of this sensor.
2016-11-24 09:58:38 +01:00
Fabian Affolter b1b8715f7d Minor comment updates and ordering (#4554) 2016-11-24 00:27:31 +01:00
Fabian Affolter b6d559da1f Add timeout to requests, use consts, and add link to docs (#4555) 2016-11-24 00:26:59 +01:00
Fabian Affolter 475c412ae4 Minor changes (switch.hook) (#4553)
* Use string formatting, add link to docs, and pylint

* Extent platform for validation
2016-11-24 00:21:48 +01:00
Pascal Vizeli c04a002c55 Hotfix executor pool size (#4552) 2016-11-23 09:52:03 -08:00
dasos 5013a82655 Hook Smart Home support (#4392)
* Support for Hook (hooksmarthome.com)

* Linting

* Add asyncio

* Move to aiohttp

* Yield more
2016-11-23 14:52:14 +00:00
Johan Bloemberg 05181bf232 0.4 release upstream. (#4545) 2016-11-23 10:44:37 +00:00
Marcelo Moreira de Mello c22a73e1d0 Removed raise statement to don't pollute the user log. (#4536)
* Removed raise statement to don't polute the user log.
Only the error message should be displayed.

Nov 22 11:28:32 tchellopi hass[20138]: 16-11-22 11:28:32 ERROR (MainThread) [homeassistant.core] Error doing job: Task exception was never retrieved
Nov 22 11:28:32 tchellopi hass[20138]: Traceback (most recent call last):
Nov 22 11:28:32 tchellopi hass[20138]: File "/usr/local/lib/python3.5/asyncio/tasks.py", line 241, in _step
Nov 22 11:28:32 tchellopi hass[20138]: result = coro.throw(exc)
Nov 22 11:28:32 tchellopi hass[20138]: File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/helpers/entity_component.py", line 386, in _update_entity_states
Nov 22 11:28:32 tchellopi hass[20138]: yield from update_coro
Nov 22 11:28:32 tchellopi hass[20138]: File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/helpers/entity.py", line 213, in async_update_ha_state
Nov 22 11:28:32 tchellopi hass[20138]: yield from self.hass.loop.run_in_executor(None, self.update)
Nov 22 11:28:32 tchellopi hass[20138]: File "/usr/local/lib/python3.5/asyncio/futures.py", line 361, in __iter__
Nov 22 11:28:32 tchellopi hass[20138]: yield self  # This tells Task to wait for completion.
Nov 22 11:28:32 tchellopi hass[20138]: File "/usr/local/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
Nov 22 11:28:32 tchellopi hass[20138]: future.result()
Nov 22 11:28:32 tchellopi hass[20138]: File "/usr/local/lib/python3.5/asyncio/futures.py", line 274, in result
Nov 22 11:28:32 tchellopi hass[20138]: raise self._exception
Nov 22 11:28:32 tchellopi hass[20138]: File "/usr/local/lib/python3.5/concurrent/futures/thread.py", line 55, in run
Nov 22 11:28:32 tchellopi hass[20138]: result = self.fn(*self.args, **self.kwargs)
Nov 22 11:28:32 tchellopi hass[20138]: File "/home/hass/.homeassistant/custom_components/sensor/wunderground.py", line 187, in update
Nov 22 11:28:32 tchellopi hass[20138]: self.rest.update()
Nov 22 11:28:32 tchellopi hass[20138]: File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/util/__init__.py", line 296, in wrapper
Nov 22 11:28:32 tchellopi hass[20138]: result = method(*args, **kwargs)
Nov 22 11:28:32 tchellopi hass[20138]: File "/home/hass/.homeassistant/custom_components/sensor/wunderground.py", line 222, in update
Nov 22 11:28:32 tchellopi hass[20138]: ["description"])
Nov 22 11:28:32 tchellopi hass[20138]: ValueError: you must supply a key

* Updated unittest since we are just printing the error instead raising
2016-11-22 23:41:51 -08:00
Charles Blonde c9b353f7a7 Add Bose SoundTouch device support - v2 (#4523)
* Add Bose SoundTouch device support

* Update soundtouch.py
2016-11-22 23:22:52 -08:00
Johan Bloemberg 64cfc4ff02 DSMR sensor (#4309)
* Initial implemenation of DSMR component.

* Fix linting

* Remove protocol V2.2 support until merged upstream.

* Generate requirements using script.

* Use updated dsmr-parser with protocol 2.2 support.

* Add tests.

* Isort and input validation.

* Add entities for gas and actual meter reading. Error handling. Use Throttle.

* Implement non-blocking serial reader.

* Improve logging.

* Merge entities into one, add icons, fix tests for asyncio.

* Add error logging for serial reader.

* Refactoring and documentation.

- refactor asyncio reader task to make sure it stops with HA
- document general principle of this component
- refactor entity reading to be more clear
- remove cruft from split entity implementation

* Use `port` configuration key.

* DSMR V2.2 seems to conflict in explaining which tariff is high and low.

http://www.netbeheernederland.nl/themas/hotspot/hotspot-documenten/?dossierid=11010056&title=Slimme%20meter&onderdeel=Documenten
> DSMR v2.2 Final P1
>> 6.1: table vs table note

    Meter Reading electricity delivered to client normal tariff) in 0,01 kWh - 1-0:1.8.1.255
    Meter Reading electricity delivered to client (low tariff) in 0,01 kWh - 1-0:1.8.2.255

    Note: Tariff code 1 is used for low tariff and tariff code 2 is used for normal tariff.

* Refactor to use asyncio.Protocol instead of loop+queue.

* Fix requirements

* Close transport when HA stops.

* Cleanup.

* Include as dependency for testing (until merged upstream.)

* Fix style.

* Update setup.cfg
2016-11-22 23:03:39 -08:00
Valentin Alexeev bb46009efa World Air Quality Index sensor (#4434)
* Implement WAQI sensor

* Corrections based on CI check.

* Updated requirements_all.txt for pwaqi==1.2

* Require latest version of pwaqi

* Fix lint: single argument for .exception and no more pass statement.

* Further lint fixes.

* pydocstyle fix

* Implement rate throttle.
Data on WAQI is usually updated once an hour - make it refresh every thirty minutes.

* Implement schema validation with voluptuous.
Change exception handling scope.
Move messages to debug().

* Fix lint (empty indented line).

* Sort lines correctly.

* Fix last lint issue.

* Provide additional sensor data as received from WAQI.
Easier-to-read throttle timing.

* Additional object attributes to be unrolled later.
2016-11-22 22:59:27 -08:00
Paulus Schoutsen 3f9250415f Skip broken tests (#4543) 2016-11-22 22:58:14 -08:00
Pascal Vizeli c294a534d0 Migrate binary_sensor to async (#4516) 2016-11-22 22:47:43 -08:00
Harris Borawski 85d6970df8 Add Sensor for Sonarr (#4496)
* Add sonarr sensor and tests for sensor

* Fixed some linting errors and removed unused import

* Add SSL option for those who use SSL from within Sonarr

* Add requirements to all requirements, and sensor to coveragerc

* remove unused variable

* move methods to functions, and other lint fixes

* linting fixes

* linting is clean now

* Remove double requirement

* fix linting for docstrings, this should probably be a part of the script/lint and not just travis
2016-11-22 22:32:45 -08:00
dainok 260a619a40 Added GPSLogger API (#4089)
* Added GPSLogger API, check https://goo.gl/eJnKw5 for details.

* Switched to debug severity and added to coveragerc

* Switched to debug severity for logs

* Updated .coveragerc

* Update .coveragerc

* Merged from sfiorini

* Merged from sfiorini

* Update .coveragerc
2016-11-22 22:19:57 -08:00
Michaël Arnauts 0c6ef3b7f9 Try to register a Chromecast anyway, even if it could not be detected by get_chromecasts(), since it might be on a other network. Fixes #4469. (#4470) 2016-11-22 22:16:01 -08:00
Thomas Friedel 0c47434aad Change Osram to use Github lightify dep (#4256)
* used MindrustUK's version ( https://github.com/MindrustUK/python-lightify/commits/master/osramlightify.py ) from Oct 2, 2016 and changed the REQUIRMENTS line to use the fixed lightify component with thread safety fixes

* reformatted long lines

* updated osramlightify requirements in requirements_all.txt

* ran script gen_requirements_all.py

* rerun requirements gen script on linux

* fixed some inspection warnings

* zip file points to a specific commit

* no requests to lights in properties, instead instance variables are update in update method

* regenerated requirements_all.txt

* removed call to update from is_on() property
2016-11-22 22:10:45 -08:00
Magnus Ihse Bursie 1d8a1df2c4 Refactor tellstick code (#4460)
* 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.
2016-11-22 21:48:22 -08:00
Aaron Morris 65b85ec6c0 Fix missing space in error message between "accuracy" and "is" (#4542) 2016-11-22 20:45:06 -08:00
Paulus Schoutsen b6b9da7e6e Merge pull request #4541 from home-assistant/release-0-33-3
0.33.3
2016-11-22 20:35:55 -08:00
Paulus Schoutsen d18f2684fb Version bump to 0.33.3 2016-11-22 19:39:15 -08:00
Paulus Schoutsen e93b079ef4 Fix platform discovery when platform discovered during discovery of a (#4529)
component
2016-11-22 19:38:34 -08:00
Paulus Schoutsen 356ad6e468 Bump netdisco (#4539) 2016-11-22 19:36:39 -08:00
Johann Kellerman 8f35212dd6 Yr.no update entities every hour (#4521) 2016-11-22 19:35:49 -08:00
Johann Kellerman 0827a26642 Yr.no update entities every hour (#4521) 2016-11-22 19:28:31 -08:00
Paulus Schoutsen b4756e6dda Bump netdisco (#4539) 2016-11-22 18:36:10 -08:00
Paulus Schoutsen 4cc192e445 Disable broken google offset test (#4540) 2016-11-22 18:34:48 -08:00
mnestor 962e5315ab Mock call to google servers (#4532)
* Fix for #4520

* mock call to do_auth to prevent call to google servers
2016-11-22 18:19:32 -08:00
Paulus Schoutsen 2c7e895105 Entity and climate: do not convert temperature unnecessary (#4522)
* Climate: more consistent units

* Prevent unnecessary conversion in entity component

* int -> round

* Disable Google tests because they connect to the internet

* Remove default conversion rounding F->C

* Add rounding of temp to weather comp

* Fix equality

* Maintain precision when converting temp in entity

* Revert "Disable Google tests because they connect to the internet"

This reverts commit b60485dc19.
2016-11-22 17:38:04 -08:00
Paulus Schoutsen 00019b9ff0 Fix warning in test 2016-11-22 12:48:35 -08:00
Pascal Vizeli 8e776b4dc0 Fix wrong name handling in rfxtrx sensor (#4531) 2016-11-22 12:47:37 -08:00
mnestor ce13b0989d Fix for #4520 (#4526)
* Fix for #4520

* fix lint
2016-11-22 10:15:39 -08:00
Paulus Schoutsen c81735cc84 Fix platform discovery when platform discovered during discovery of a (#4529)
component
2016-11-22 08:21:08 -08:00
Fabian Affolter 5d18759146 Upgrade miflora to 0.1.13 (fixes #4479) (#4524) 2016-11-22 15:41:37 +01:00
Malte Franken 9cdcfae8f3 New config parameter for min_max sensor to specify number of digits for rounding mean value (#4237)
* new config parameter to specify number of digits for rounding average value

* fixed two `line too long` errors

* added three new tests for the mean sensor including test for precision of mean value
2016-11-22 15:36:29 +01:00
Gilles Margerie 547d93f631 Added source selection for Denon AVR Media Player (#4304)
* Added source selection for Denon AVR Media Player

* Update denon.py

* Update denon.py

* Update denon.py

* Update denon.py

* Update denon.py

slight format update (space issue and new line)

* Further update regarding formatting

* Updated the source name with lowercase

* Update denon.py
2016-11-21 23:45:17 -08:00
Paulus Schoutsen d841ddc50b Merge pull request #4519 from home-assistant/release-0-33-2
0.33.2
2016-11-21 20:42:21 -08:00
Paulus Schoutsen 40b5824230 Skip google calendar offset test (#4520) 2016-11-21 20:16:50 -08:00
Paulus Schoutsen 86f3e2455d Skip google calendar offset test (#4520) 2016-11-21 20:16:34 -08:00
Paulus Schoutsen 9a065cc536 Version bump to 0.33.2 2016-11-21 19:40:19 -08:00
Richard Cox 8e4dbcaf21 Fixing 'Unknown' status for Nest Protect devices (#4475)
* Fixing 'Unknown' status for Nest Protect devices

* Fixing bad formatting
2016-11-21 19:39:38 -08:00
Richard Cox 6863d2e0af Fixing 'Unknown' status for Nest Protect devices (#4475)
* Fixing 'Unknown' status for Nest Protect devices

* Fixing bad formatting
2016-11-21 19:39:23 -08:00
John Arild Berentsen c23809488b Neato Fixes (#4490)
* Fix, switch state. Move constants to hub

* Responsiveness

* Whitespace

* Delay was not needed as commands does not return until done.
2016-11-21 19:36:54 -08:00
John Arild Berentsen 248f5c0209 Neato Fixes (#4490)
* Fix, switch state. Move constants to hub

* Responsiveness

* Whitespace

* Delay was not needed as commands does not return until done.
2016-11-21 19:36:44 -08:00
Jack Chapple e5aa40fa5d Fixes #4500 (#4502) 2016-11-21 19:35:49 -08:00
Jack Chapple 1f573b46a4 Fixes #4500 (#4502) 2016-11-21 19:35:36 -08:00
hexa- 0647bb7f6b switch.tplink: expect daily stats to be empty (#4504)
Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
2016-11-21 19:34:58 -08:00
hexa- a73fbbaf7a switch.tplink: expect daily stats to be empty (#4504)
Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
2016-11-21 19:34:48 -08:00
Pascal Vizeli 755f5b61b7 Bugfix discovery use wrong time async (#4515)
* Bugfix discovery use wrong time async

* fix lint
2016-11-21 19:33:32 -08:00
Pascal Vizeli 6869c7401e Bugfix device_tracker init tracker scan (#4514) 2016-11-21 19:33:32 -08:00
Pascal Vizeli 835577b2bc Bugfix discovery use wrong time async (#4515)
* Bugfix discovery use wrong time async

* fix lint
2016-11-21 19:33:08 -08:00
Pascal Vizeli 859d0d5ad6 Bugfix device_tracker init tracker scan (#4514) 2016-11-21 19:32:21 -08:00
Fabian Affolter aed797f438 Upgrade freesms to 0.1.1 (#4491) 2016-11-21 17:32:05 +01:00
Fabian Affolter eb8093934f Upgrade python-hpilo to 3.9 (#4482) 2016-11-21 17:31:14 +01:00
Fabian Affolter 608b482906 Upgrade sqlalchemy to 1.1.4 (#4486) 2016-11-21 17:29:06 +01:00
Fabian Affolter 7207c2cca1 Upgrade sendgrid to 3.6.3 (#4485) 2016-11-21 17:28:31 +01:00
Fabian Affolter ed1d0b4197 Upgrade astral to 1.3.2 (#4505) 2016-11-21 17:27:48 +01:00
Fabian Affolter 63461e9007 Upgrade slacker to 0.9.30 (#4484) 2016-11-21 17:27:15 +01:00
Fabian Affolter 40a2145558 Upgrade yahoo-finance to 1.4.0 (#4483) 2016-11-21 17:25:43 +01:00
Sean Dague d883b18751 Merge pull request #4503 from sdague/pyvera_bump
Bump pyvera to 0.2.21
2016-11-21 06:09:54 -05:00
Sean Dague b8e462cf5b Bump pyvera to 0.2.21
pyvera 0.2.21 fixes the fact that use of requests.get was not using a
timeout. Some times (after a few days of use) the pyvera poll loop
would hang indefinitely on a requests.get of the event interface. This
would cause the pyvera thread to hang completely. It would also
prevent graceful shutdown, as pyvera does a thread join.

The new version uses a timeout, so that we won't lock up any more.
2016-11-21 06:06:17 -05:00
Paulus Schoutsen 11df7becd3 Merge pull request #4492 from home-assistant/release-0-33-1
0.33.1
2016-11-20 13:58:01 -08:00
Paulus Schoutsen 99f5db8c02 Version bump to 0.33.1 2016-11-20 12:11:07 -08:00
John Arild Berentsen 19b08a975a ZWave lights: Not use super() (#4476)
* Not use super

* Review changes
2016-11-20 12:10:50 -08:00
John Arild Berentsen 123f4acfc1 ZWave lights: Not use super() (#4476)
* Not use super

* Review changes
2016-11-20 11:49:54 -08:00
Paulus Schoutsen 0f90426023 Version bump to 0.34.0.dev0 2016-11-19 16:06:42 -08:00
Paulus Schoutsen 8b6a94b0f5 Merge pull request #4446 from home-assistant/dev
0.33
2016-11-19 16:06:26 -08:00
Paulus Schoutsen 0a333230c1 Version bump to 0.33 2016-11-19 16:05:56 -08:00
Paulus Schoutsen 455e1df7cb Fix typo 2016-11-19 16:05:33 -08:00
Matt N f71396c293 Fix nmap_tracker documentation link (#4471) 2016-11-19 15:31:45 -08:00
Paulus Schoutsen d930c399fe Better locking while setting up components + discovery (#4463) 2016-11-19 08:18:33 -08:00
Bjarni Ivarsson f3748ce535 Sonos line-in and tv source fixes + Sonos discovery fix. (#4440)
* Fixes line-in and tv sources on Sonos + Sonos discovery fixes.

* Style fix.
2016-11-19 15:29:00 +00:00
John Arild Berentsen 8beefcfc69 Switch did not update (#4466) 2016-11-19 15:52:42 +01:00
John Arild Berentsen 93747f2766 switch base cover did not appear (#4454)
Thanks for testing the PR @emilhetty 👍
2016-11-19 12:33:08 +01:00
John Arild Berentsen 7af438fa2f Hound for zwave climate (#4465) 2016-11-19 10:19:22 +01:00
Fabian Affolter 2b5fcd737b PVOutput sensor (#4203)
* Add PVOutput sensor

* Remove attributes

* Revert `verify_ssl` back to true
2016-11-19 10:04:03 +01:00
John Arild Berentsen 2b320f23fc Hound comments (#4464) 2016-11-19 09:46:02 +01:00
John Arild Berentsen 679d500e61 Neato refactor and support for sensors (#4319)
* Imporvements to neato

* Review changes
2016-11-19 00:14:40 -08:00
mnestor 613615433a Google Calendar round 2 (#4161)
* Google Calendar round 2

* Add google back to .coveragerc

* Update __init__.py
2016-11-18 22:29:20 -08:00
Fabian Affolter f70ff66d11 Upgrade batinfo to 0.4.2 (#4452) 2016-11-18 22:04:15 -08:00
Paulus Schoutsen d2bbc6ef70 Upgrade linter (#4461) 2016-11-18 21:47:59 -08:00
Paulus Schoutsen 37e28428c1 Merge remote-tracking branch 'origin/master' into dev 2016-11-18 18:39:11 -08:00
Pascal Vizeli c56f99baaf Async migration device_tracker (#4406)
* Async migration device_tracker

* change location stuff to async

* address paulus comments

* fix lint & add async discovery listener

* address paulus comments v2

* fix tests

* fix test_mqtt

* fix test_init

* fix gps_acc

* fix lint

* change async_update_stale to callback
2016-11-18 23:35:08 +01:00
Erik Eriksson 265232af98 only check heater status if present (#4459) 2016-11-18 14:12:51 -08:00
Fabian Affolter e6c4113c5b Fix lint issues for 0.33 (#4451)
* Fix PEP257 issues

* Fix ident

* Fix lint issues

* Update docstrings

* Fix indent

* Fix indent

* Fix lint issues

* Fix lint issue

* Again lint
2016-11-18 23:05:03 +01:00
Igor Shults c86e1b31b3 Fix typo in OWM (#4458) 2016-11-18 22:54:46 +01:00
Lewis Juggins 5912316496 pywebpush update to 0.6.1 (#4449) 2016-11-18 13:03:44 -08:00
John Arild Berentsen 58f0655298 ZWave Light: Use Configurable refresh (#4437)
* Use Configurable refresh

* Use super instead of object
2016-11-18 21:59:01 +01:00
John Arild Berentsen 43a93fb345 ZWave: Fix missing battery_level, node_id and location (#4422)
* Fix missing battery_level, node_id and location

* use super instead of object
2016-11-18 21:42:30 +01:00
Daniel Høyer Iversen 36b338051b Merge pull request #4450 from home-assistant/flux_led_lib_09
Upgrade flux led lib
2016-11-18 14:18:12 +01:00
Daniel Hoyer Iversen fc566309c1 Upgrade flux led lib 2016-11-18 13:20:51 +01:00
Sean Dague 23ce9949b1 Merge pull request #4447 from sdague/proliphix
bump proliphix library version
2016-11-18 06:56:04 -05:00
Sean Dague 275c80183c bump proliphix library version
This fixes an upstream bug with daylight savings time handling
2016-11-18 05:05:05 -05:00
Sean Dague cd1655f43b create light.hue_activate_scene service (#4425)
* create light.hue_activate_scene service

This creates a light.hue_activate_scene service that takes group_name
and scene_name, and calls phue's bridge.run_scene with those
parameters. This allows calling hue bridge stored scene names by name
during automation.

This only currently works reliably in 1 hue hub configurations (which
is most of them). Phue will be further enhanced to display warnings
when it can't figure out what to do with the parameters passed in to HA.

* Update hue.py
2016-11-17 22:14:06 -08:00
jnimmo 1a117d0bea Add keypress & output control services to Envisalink component (#3932)
* Add keypress & output control services to Envisalink component

Add services to allow sending custom keypresses and activating
programmable outputs on an alarm control panel.
Implemented for the Envisalink alarm, and moving to new version of
pyenvisalink to support this.

Replicated the service handler mapping code from Cover component into
Alarm Control Panel to allow handling alternative schemas if required
by new services.

* Update requirements_all.txt

* Updated services.yaml

* Removed requirement to enter code in HA UI

Incorporated changes suggested by @sriram
https://github.com/srirams/home-assistant/commit/2f8deb70cb5f3621a69b6b9
acb72f8e29123650c

Including pending state for exit/entry delay

Clarified services to use the code passed to them as a first priority,
otherwise use the code from configuration

Swapped back to using NotImplementedError for the service definitions

* - Add support for alarm_keypress to manual alarm (functions like a standard alarm keypad where entering the code disarms or arms the alarm)
- Add tests for alarm_keypress to manual alarm
- Style corrections (too many returns, comment & whitespace issues)

* Removed alarm_output_control service as unable to incorporate in the demo/test in a meaningful way

* Add keypress & output control services to Envisalink component

Add services to allow sending custom keypresses and activating
programmable outputs on an alarm control panel.
Implemented for the Envisalink alarm, and moving to new version of
pyenvisalink to support this.

Replicated the service handler mapping code from Cover component into
Alarm Control Panel to allow handling alternative schemas if required
by new services.

* Update requirements_all.txt

* Updated services.yaml

* Removed requirement to enter code in HA UI

Incorporated changes suggested by @sriram
https://github.com/srirams/home-assistant/commit/2f8deb70cb5f3621a69b6b9
acb72f8e29123650c

Including pending state for exit/entry delay

Clarified services to use the code passed to them as a first priority,
otherwise use the code from configuration

Swapped back to using NotImplementedError for the service definitions

* - Add support for alarm_keypress to manual alarm (functions like a standard alarm keypad where entering the code disarms or arms the alarm)
- Add tests for alarm_keypress to manual alarm
- Style corrections (too many returns, comment & whitespace issues)

* Removed alarm_output_control service as unable to incorporate in the demo/test in a meaningful way

* Moved the Alarm_Keypress service into Envisalink component out of the generic

* Update envisalink.py

* Update services.yaml
2016-11-17 22:13:22 -08:00
Fabian Affolter 944bb8474f Change validation to optional (#4400) 2016-11-17 22:09:57 -08:00
Magnus Ihse Bursie 779f520c56 Make UI more responsive to power off for Samsung Smart TV (#4438) 2016-11-17 22:00:18 -08:00
Magnus Ihse Bursie 82ed7b6b08 Fix so shell script adheres to posix standards. (#4439) 2016-11-17 21:59:53 -08:00
Paulus Schoutsen af77341494 Add sensor to show how many clients are connected. (#4430)
* Add sensor to show how many clients are connected.

* Lint

* Fix tests
2016-11-17 21:54:47 -08:00
Paulus Schoutsen 23fb8c4cdd Convert script component to async (#4427) 2016-11-17 21:50:01 -08:00
Paulus Schoutsen 726bc5b670 Do not report on shutting down errors (#4431)
* Do not report on shutting down errors

* Lint
2016-11-17 12:02:43 -08:00
Open Home Automation b615b3349f Fix for Miflora 2.6.6 firmware (#4436) 2016-11-17 08:40:21 -08:00
Paulus Schoutsen 0f59bb208c Migrate callbacks to use schedule_update_ha_state (#4426)
* Migrate callbacks to use schedule_update_ha_state

* Migrate MQTT sensor callback to async

* Migrate wemo to not update inside schedule_update_ha_state

* Make MQTT switch async

* Fix nx584 test

* Migrate tellstick callback

* Migrate vera callback

* Alarm control panel - manual: use async callbacks

* Run the switch rest tests that work
2016-11-17 07:34:46 -08:00
Paulus Schoutsen 38d201a54a Increase logging level of errors while doing jobs (#4429) 2016-11-16 23:01:14 -08:00
Pascal Vizeli c8bc1e3c5d change add_job to use call_soon_threadsafe (#4410)
* change add_job to use call_soon_threadsafe

* address comments from paulus

* Tweak core tests

* Fix tests Python 3.4.2
2016-11-16 20:00:08 -08:00
Lewis Juggins a862bc4edc Fix DLink async I/O (#4301) 2016-11-16 18:55:58 -08:00
Magnus Ihse Bursie b0e3d5a576 Better handling of accented characters in slugify (#4399) (#4423)
* Better handling of accented characters in slugify (#4399)

* Update __init__.py
2016-11-16 15:05:10 -08:00
Paulus Schoutsen f006b00dc1 Fix spelling schedule_update_ha_state (#4415) 2016-11-16 08:26:29 -08:00
bestlibre 1fff6ce438 Deduplicate MQTT_PUBLISH_SCHEMA definition (#4411) 2016-11-16 08:19:00 -08:00
Sean Dague c06c82905a dynamically fetch yamaha media playback support (#4385)
This makes it so that media playback support for inputs is dynamically
fetched from the receiver, instead of assuming that all playback
commands work for all inputs.

Tests are added for this, using a FakeYamaha class, which has some
sample data stubbed in for key methods that need to be called. We also
include an example of the desc.xml needed to dynamically parse these
features for these tests (as this is done in platform init).
2016-11-15 21:56:40 -08:00
Paulus Schoutsen 2b86d89bb4 Fix tplink test 2016-11-15 21:26:03 -08:00
Sean Dague 7bdb79bd54 bump phue to 0.9 (#4404)
This increases the phue library to 0.9, which includes some basic
Scene support that could be consumed from home assistant.
2016-11-15 21:14:54 -08:00
Pascal Vizeli 41aaeb715a Convert switch to AsnycIO (#4382)
* Convert switch to AsnycIO

* Move update entity to service

* use time better for faster handling

* Change to suggestion from paulus

* Use new shedule_update_ha_state

* fix lint

* minimize executor calls
2016-11-15 21:06:50 -08:00
Fabian Affolter 5d8a465c18 Add timeout to requests (#4398) 2016-11-15 21:02:17 -08:00
John Arild Berentsen c6f5a5443f Make zwave climate entities contain it's respective setpoints (#4357)
* Make zwave entities contain it's respective setpoints

* Add fan state
2016-11-15 13:14:29 +01:00
Fabian Affolter d6cb102f63 Merge pull request #4144 from dasos/squeezebox_name_fix
Squeezebox name
2016-11-15 10:06:10 +01:00
Fabian Affolter edde76e544 Fix validation and use consts (mqtt) (#4396) 2016-11-15 08:33:42 +01:00
Fabian Affolter d5fff2f94a Fix validation and use consts (mqtt) 2016-11-15 08:21:44 +01:00
bestlibre 0e0ba28249 support for last will and birth message for mqtt (#4381) 2016-11-14 22:18:33 -08:00
Paulus Schoutsen 6745e83a6c Merge pull request #4394 from home-assistant/release-0-32-4
0.32.4
2016-11-14 22:04:43 -08:00
Paulus Schoutsen 44bc057fdb Version bump to 0.32.4 2016-11-14 21:34:40 -08:00
hexa- 96b8d8fcfa http: reimplement X-Forwarded-For parsing (#4355)
This feature needs to be enabled through the `http.use_x_forwarded_for` option,
satisfying security concerns of spoofed remote addresses in untrusted network
environments.

The testsuite was enhanced to explicitly test the functionality of the
header.

Fixes #4265.

Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
2016-11-14 21:33:34 -08:00
Sean Dague fc2df34206 Pin versions on linters for tests
The linters really need to specify an exact version, because when
either flake8 or pylint release a new version, a whole lot of new
issues are caught, causing failures on the code unrelated to the
patches being pushed.

Pinning is a best practice for linters. This allows patches which move
forward the linter version to happen with any code fixes required for
it to pass.
2016-11-14 21:32:02 -08:00
Paulus Schoutsen 09c29737de Fix device tracker sending invalid event data 2016-11-14 21:31:17 -08:00
Paulus Schoutsen 4c01b47945 device_tracker.see should not call async methods (#4377) 2016-11-14 21:31:06 -08:00
Paulus Schoutsen 7aaf3a46db Fix device tracker sending invalid event data (#4393) 2016-11-14 21:28:57 -08:00
Paulus Schoutsen d774ba46c7 Fix device tracker sending invalid event data 2016-11-14 20:59:29 -08:00
Lewis Juggins 4c37ee8884 Handle live content better in Kodi (#4388) 2016-11-14 20:11:22 -08:00
Paulus Schoutsen 7f5f458074 Faster async entity update on component. (#4384) 2016-11-14 18:54:38 -08:00
Paulus Schoutsen 479457d6ec device_tracker.see should not call async methods (#4377) 2016-11-14 18:35:58 -08:00
Paulus Schoutsen 7e73d27dd1 Do not serve HTTP requests while stopping (#4378) 2016-11-14 18:33:53 -08:00
Martin Wood e7ffec87ac Squeezebox name fix #4019 2016-11-14 21:46:05 +00:00
Robbie Trencheny 2d47b187c5 notify.html5: decode bytes values in registration data (#4379)
Occassionally the values of `keys` and `p256h` are bytes objects instead of
strings. As JSON by default does not serialize bytes objects let's decode
bytes objects to unicode strings.

Resolves the registration issue mentioned in #4012.

Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
2016-11-14 11:35:31 -08:00
Sean Dague fe2103dedb Merge pull request #4386 from sdague/lint
Pin versions on linters for tests
2016-11-14 13:58:50 -05:00
Sean Dague 7bf5d1c662 Pin versions on linters for tests
The linters really need to specify an exact version, because when
either flake8 or pylint release a new version, a whole lot of new
issues are caught, causing failures on the code unrelated to the
patches being pushed.

Pinning is a best practice for linters. This allows patches which move
forward the linter version to happen with any code fixes required for
it to pass.
2016-11-14 13:50:27 -05:00
pvizeli cb24282040 Faster async entity update on component. 2016-11-14 14:18:04 +01:00
Fabian Affolter bd9429d3af Upgrade sendgrid to 3.6.2 (#4370) 2016-11-14 09:35:08 +01:00
Martin Weinelt d7a005ad0f notify.html5: decode bytes values in registration data
Occassionally the values of `keys` and `p256h` are bytes objects instead of
strings. As JSON by default does not serialize bytes objects let's decode
bytes objects to unicode strings.

Resolves the registration issue mentioned in #4012.

Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
2016-11-14 05:14:18 +01:00
Paulus Schoutsen 2e2a996a8e Do not serve HTTP requests while stopping 2016-11-13 18:12:50 -08:00
Paulus Schoutsen 0364498dee Add .hound.yml 2016-11-13 11:34:09 -08:00
Daniel Høyer Iversen c5fdd4392a Merge pull request #4373 from home-assistant/flux_led_color_bright
support color and brightness in flux_led light
2016-11-13 20:33:48 +01:00
Daniel Hoyer Iversen 895454b6c3 support color and brightness in flux_led light 2016-11-13 11:10:27 +01:00
Nathan Henrie 2109b7a1b9 Use entity_id for backend, friendly name for frontend (#4343)
* Use entity_id for backend, friendly name for frontend

Closes https://github.com/home-assistant/home-assistant/issues/3434

Command line switches had the option to set a `friendly_name` reportedly
for use in the front end. However, if set, it was also being used as the
`entity_id`.

This did not seem like obvious behavior to me. This PR changes the
behavior so the entity_id is the object_id, which must already be
unique, and is an obvious place to have a very predictable slug (even if
long or unsightly), and the friendly name (if set) is used for the
display.

Example:

```yaml
switch:
  platform: command_line
  switches:
    rf_kitchen_light_one:
      command_on: switch_command on kitchen
      command_off: switch_command off kitchen
      command_state: query_command kitchen
      value_template: '{{ value == "online" }}'
      friendly_name: "Beautiful bright kitchen light!"
```

If you were using in an automation or from dev tools, would use:
`switch.rf_kitchen_light_one`, but your front end would still show `Beautiful
bright kitchen light!`

* Add new arg to test_assumed_state_should_be_true_if_command_state_is_false

* Import ENTITY_ID _FORMAT from existing, rename device_name to object_id

* Rename `device_name` to `object_id`

* Test that `entity_id` and `name` are set as expected
2016-11-12 22:46:23 -08:00
Pascal Vizeli 71a305ea45 Hotfix deadlock on platform setup (#4354)
* Hotfix deadlock on platform setup

* fix wrong import
2016-11-12 16:19:13 -08:00
hexa- e73634e6c7 http: reimplement X-Forwarded-For parsing (#4355)
This feature needs to be enabled through the `http.use_x_forwarded_for` option,
satisfying security concerns of spoofed remote addresses in untrusted network
environments.

The testsuite was enhanced to explicitly test the functionality of the
header.

Fixes #4265.

Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
2016-11-12 16:14:39 -08:00
Pascal Vizeli 3d47ad5018 Use hass aiohttp connector for ssl connection (#4344) 2016-11-12 16:00:31 -08:00
Erik Eriksson c823ea9f2a Don't fail if component name is None. Fixes (#4345)
https://github.com/home-assistant/home-assistant/issues/4326

Might fix https://github.com/home-assistant/home-assistant/issues/4326
2016-11-12 14:16:27 -08:00
Fabian Affolter 75bcb1ff0f Upgrade schiene to 0.18 (#4359) 2016-11-12 12:30:05 -08:00
Sean Dague 1663cc9084 Fix typo in generic thermostat (#4348)
It looks like a copy / paste error was made when doing the min/max
code. This fixes that.
2016-11-11 17:42:58 +01:00
Christian Brædstrup 17cfcc981d D-Link switch version bump of external library (#4351) 2016-11-11 17:38:12 +01:00
Hugo Dupras 60fabaec24 Add timeout for Netatmo binary sensor (#4280)
* Add time limit for Netatmo binary sensor

* Change limit to timeout

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

* Update requirements_all.txt
2016-11-10 23:04:11 -08:00
Marcelo Moreira de Mello 5e44934e7e Added some extra options to Weather Underground component (#4306)
* Added some extra options to Weather Underground component

* Added Location and Elevation options

* Fixed if statement

* Fixed lint

* Updated tests including  elevation and location

* Update wunderground.py
2016-11-10 23:01:20 -08:00
Lewis Juggins 01a6c1c1c8 Add strptime template function (#3950) 2016-11-10 22:57:44 -08:00
Jeffrey Lin cd1b0ac67d Added NVRAM-based MAC to IP mapping as backup to ARP tables (#4189) 2016-11-10 22:46:58 -08:00
Paulus Schoutsen 2bfded7153 MQTT.Server will use HASS eventloop (#3429) 2016-11-10 22:45:38 -08:00
Javier González Calleja 20af5cb5b4 Daily consumption information for HS110 (#4206)
* Add daily consumption information for HS110

* Fixing code review

* Fixing code review

* Fixing code review
2016-11-10 22:43:16 -08:00
Paulus Schoutsen 080f56e0f5 Merge pull request #4342 from home-assistant/release-0-32-3
0.32.3
2016-11-10 21:59:39 -08:00
Paulus Schoutsen 173e15e733 Version bump to 0.32.3 2016-11-10 21:50:05 -08:00
Paulus Schoutsen 72407c2f95 Make yr compatible with 0.32 2016-11-10 21:49:56 -08:00
Paulus Schoutsen 1b79722b69 Fix KNX async I/O (#4267) 2016-11-10 21:43:50 -08:00
Pascal Vizeli cc5233103c Fix rest switch default template (#4331) 2016-11-10 21:43:50 -08:00
Daniel Høyer Iversen 2feea1d1eb Add support for rgb light in led flux, fixes issue #4303 (#4332) 2016-11-10 21:43:50 -08:00
Johann Kellerman 2c39c39d52 Improve async generic camera's error handling (#4316)
* Handle errors

* Feedback

* DisconnectedError
2016-11-10 21:43:48 -08:00
Paulus Schoutsen 6e6b1ef7ab fix panasonic viera doing I/O in event loop (#4341) 2016-11-10 21:42:41 -08:00
Pascal Vizeli 55ddaf1ee7 Synology SSL fix & Error handling (#4325)
* Synology SSL fix & Error handling

* change handling for cookies/ssl

* fix use not deprecated functions

* fix lint

* change verify

* fix connector close to coro

* fix force close

* not needed since websession close connector too

* fix params

* fix lint
2016-11-10 21:42:37 -08:00
Pascal Vizeli 6860d9b096 Update SoCo to 0.12 (#4337)
* Update SoCo to 0.12

* fix req
2016-11-10 21:41:28 -08:00
Sean Dague 3e1cc4282e Fix "argument of type 'NoneType' is not iterable" during discovery (#4279)
* Fix "argument of type 'NoneType' is not iterable" during discovery

When yamaha receivers are dynamically discovered, there config is
empty, which means that we need to set zone_ignore to [] otherwise the
iteration over receivers fails.

* Bump rxv library version to fix play_status bug

rxv version 0.3 will issue the play_status command even for sources
that don't support it, causing stack traces during updates when
receivers are on HDMI inputs.

This was fixed in rxv 0.3.1. Bump to fix bug #4226.

* Don't discovery receivers that we've already configured

The discovery component doesn't know anything about already configured
receivers. This means that specifying a receiver manually will make it
show up twice if you have the discovery component enabled.

This puts a platform specific work around here that ensures that if
the media_player is found, we ignore the discovery system.
2016-11-10 21:41:28 -08:00
Jan Losinski 200bdb30ff Change pilight systemcode validation to integer (#4286)
* Change pilight systemcode validation to integer

According to the pilight code the systemcode should be an integer and
not a string (it is an int in the pilight code). Passing this as a
string caused errors from pilight:
"ERROR: elro_800_switch: insufficient number of arguments"

This fixes #4282

* Change pilight unit-id to positive integer

According to the pilight code the unit of an entity is also evrywhere
handled as an integer. So converting and passing this as string causes
pilight not to work.

This fixes #4282

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2016-11-10 21:41:28 -08:00
Paulus Schoutsen eb17ba970c Increase update delay (#4321) 2016-11-10 21:41:28 -08:00
Paulus Schoutsen ffe4c425af Fix Tellstick doing I/O inside event loop (#4268) 2016-11-10 21:41:28 -08:00
Jesse Newland a18fdbfbb8 Fix alarm.com I/O inside properties (#4307)
* Fix alarm.com I/O inside properties

* First line should end with a period

* Not needed

* Fetch state on init
2016-11-10 21:41:28 -08:00
Lewis Juggins 58600f25b3 Fix OWM async I/O (#4298) 2016-11-10 21:41:28 -08:00
Pascal Vizeli 749fc583ea Fix rest switch default template (#4331) 2016-11-10 21:32:08 -08:00
Daniel Høyer Iversen b07d887d77 Add support for rgb light in led flux, fixes issue #4303 (#4332) 2016-11-10 21:30:52 -08:00
Johann Kellerman 9bb94a4512 Improve async generic camera's error handling (#4316)
* Handle errors

* Feedback

* DisconnectedError
2016-11-10 21:28:22 -08:00
Paulus Schoutsen e76d553513 fix panasonic viera doing I/O in event loop (#4341) 2016-11-10 21:17:44 -08:00
Pascal Vizeli 844799a1f7 Synology SSL fix & Error handling (#4325)
* Synology SSL fix & Error handling

* change handling for cookies/ssl

* fix use not deprecated functions

* fix lint

* change verify

* fix connector close to coro

* fix force close

* not needed since websession close connector too

* fix params

* fix lint
2016-11-10 21:04:47 -08:00
Pascal Vizeli e005ebe989 Update SoCo to 0.12 (#4337)
* Update SoCo to 0.12

* fix req
2016-11-10 21:01:42 -08:00
Sean Dague e9d19c1dcc Fix "argument of type 'NoneType' is not iterable" during discovery (#4279)
* Fix "argument of type 'NoneType' is not iterable" during discovery

When yamaha receivers are dynamically discovered, there config is
empty, which means that we need to set zone_ignore to [] otherwise the
iteration over receivers fails.

* Bump rxv library version to fix play_status bug

rxv version 0.3 will issue the play_status command even for sources
that don't support it, causing stack traces during updates when
receivers are on HDMI inputs.

This was fixed in rxv 0.3.1. Bump to fix bug #4226.

* Don't discovery receivers that we've already configured

The discovery component doesn't know anything about already configured
receivers. This means that specifying a receiver manually will make it
show up twice if you have the discovery component enabled.

This puts a platform specific work around here that ensures that if
the media_player is found, we ignore the discovery system.
2016-11-10 20:44:38 -08:00
Jan Losinski 7d2ab4fce6 Change pilight systemcode validation to integer (#4286)
* Change pilight systemcode validation to integer

According to the pilight code the systemcode should be an integer and
not a string (it is an int in the pilight code). Passing this as a
string caused errors from pilight:
"ERROR: elro_800_switch: insufficient number of arguments"

This fixes #4282

* Change pilight unit-id to positive integer

According to the pilight code the unit of an entity is also evrywhere
handled as an integer. So converting and passing this as string causes
pilight not to work.

This fixes #4282

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2016-11-10 13:14:40 -08:00
Pascal Vizeli ba2ea35089 Add logging to platform/component setup (#4300)
* Add timeout to platform/component

* Revert "Add timeout to platform/component"

This reverts commit 280a311e48.

* Add logging data

* Change log message with paulus comments
2016-11-10 18:46:31 +01:00
Erik Eriksson ade62faa38 Don't fail if component name is None. Fixes (#4334)
https://github.com/home-assistant/home-assistant/issues/4326

Might fix https://github.com/home-assistant/home-assistant/issues/4326
2016-11-10 08:46:32 -08:00
Finbarr Brady ee322dbbdc Cisco IOS device tracker support (#4193) 2016-11-09 22:36:57 +02:00
John Arild Berentsen 0d4141bf13 Add missing Index labels (#4328) 2016-11-09 19:11:24 +01:00
Christopher Viel d404ac8978 Add support for off script to WOL switch (#4258) 2016-11-09 08:44:30 -08:00
Pascal Vizeli 71da21dcc8 Change pending task sheduler to time based cleanup (#4324)
* Change pending task sheduler to time based cleanup

* update unittest
2016-11-09 08:41:17 -08:00
Paulus Schoutsen 04dbc992ec Increase update delay (#4321) 2016-11-09 07:21:58 -08:00
Paulus Schoutsen 6d0e08cf7d Fix KNX async I/O (#4267) 2016-11-08 21:00:33 -08:00
Paulus Schoutsen 1e0025acae Fix Tellstick doing I/O inside event loop (#4268) 2016-11-08 20:25:19 -08:00
Pascal Vizeli 8fc853ba11 Add more unittest for async_add_job (#4320)
* Add more unittest for async_add_job

* fix test

* lint
2016-11-08 20:01:05 -08:00
sustah 8cbb8f6527 Update dlink.py (#4317)
corrected "total consumption" units from W to kWh
2016-11-08 19:58:27 -08:00
Jesse Newland 4f86c9ecda Fix alarm.com I/O inside properties (#4307)
* Fix alarm.com I/O inside properties

* First line should end with a period

* Not needed

* Fetch state on init
2016-11-08 19:57:46 -08:00
Lewis Juggins 9561fed650 Fix Dark Sky async I/O (#4299) 2016-11-08 19:46:44 -08:00
Lewis Juggins 67b599475e Fix OWM async I/O (#4298) 2016-11-08 18:57:56 -08:00
Pascal Vizeli 114ece1848 Fix possible sigterm / unittest / Fix all lazy test (#4297)
* replace weakref with a list

* add unittest

* fix lint

* fix handling

* fix unittest

* change code style

* fix lazy tests
2016-11-08 10:24:50 +01:00
Fabian Affolter c05815cced Upgrade sqlalchemy to 1.1.3 (#4277) 2016-11-07 23:08:17 -08:00
Pascal Vizeli 2e0c185740 Async cleanup part 3 (#4302) 2016-11-07 22:31:40 -08:00
Johann Kellerman 231ef40f53 iOS links (#4295) 2016-11-08 00:07:24 +01:00
Fabian Affolter b4159c7dc9 Upgrade python-digitalocean to 1.10.1 (#4276) 2016-11-06 23:49:25 -08:00
Fabian Affolter 8cc5fc1369 Upgrade psutil to 5.0.0 (#4275) 2016-11-06 23:49:11 -08:00
Paulus Schoutsen fc3235fb6d Merge pull request #4271 from home-assistant/release-0-32-2
Release 0 32 2
2016-11-06 23:40:06 -08:00
David-Leon Pohl d129df93dd Hotfix #4272 (#4273) 2016-11-06 23:34:45 -08:00
David-Leon Pohl 67336a111b Hotfix #4272 (#4273) 2016-11-06 23:34:32 -08:00
Paulus Schoutsen 0af1a96f14 Lint 2016-11-06 23:24:25 -08:00
andyat 272899ec96 Fix setting temperature in Celsius on radiotherm CT50 (#4270) 2016-11-06 23:21:08 -08:00
andyat 7d28d9d6b4 Fix setting temperature in Celsius on radiotherm CT50 (#4270) 2016-11-06 23:18:06 -08:00
Paulus Schoutsen 6a92e27e2f Version bump to 0.32.2 2016-11-06 23:12:37 -08:00
Paulus Schoutsen faceb4c1dc Sequential updates for non-async entities 2016-11-06 23:12:20 -08:00
Paulus Schoutsen 6d5f00098a Move Honeywell I/O out of event loop (#4244) 2016-11-06 23:09:31 -08:00
Pascal Vizeli 618a86a37c Set executor to 15 and help to reduce flooting async core with updates (#4252)
* Set executor to 15 and help to reduce flooting async core with udpates

* fix typing

* if it a executor, wait

* address comments from paulus

* add space for style :)

* fix spell

* Update entity_component.py

* Update entity_component.py
2016-11-06 22:28:03 -08:00
Paulus Schoutsen 880ef8af48 Remove broken disable verify ssl synology (#4269) 2016-11-06 22:17:56 -08:00
William Scanlon 95124c7ddb Revert "Catch AttributeError on Wink PubNub update" (#4263) 2016-11-06 20:05:42 -08:00
William Scanlon 0aba227300 Catch AttributeError (#4253) 2016-11-06 16:04:57 -08:00
Martin Hjelmare 734bd75fd3 Fix mysensors overwriting gateway in GATEWAYS (#4013)
GATEWAYS was a dict, so would overwrite item if key was the same. This
would happen when using multiple MQTT gateways, since the device id is
the same (`mqtt`).

* Fix by changing GATEWAYS from dict into list.
* Use hass data to store mysensors gateways instead of having GATEWAYS
  be a global.
2016-11-06 10:49:43 -08:00
Frantz 0c5e077091 Updated netdisco to 0.7.6 (#4250) 2016-11-06 10:43:13 -08:00
Nicolas Graziano 1ed2f8ae91 Update braviarc to 0.3.6 (#4246)
Add HDMI sources.
Sources ordered.
2016-11-06 09:27:55 -08:00
Paulus Schoutsen a343c20404 Async gather wait (#4247)
* Fix config validation for input_*, script

* Allow scheduling coroutines

* Validate entity ids when entity ids set by platform

* Async: gather -> wait

* Script/Group: use async_add_job instead of create_task
2016-11-06 09:26:40 -08:00
Antoine Bertin d4e8b831a0 Add mqtt_template light component (#4233)
* Add mqtt_template component

* Docstring copy paste party on overriden methods

* pep8 E501 🌟

* Add missing docstrings on unittests
2016-11-06 09:09:01 -08:00
Paulus Schoutsen 98f41d6b84 Tweak block_till_done (#4245) 2016-11-06 08:43:32 -08:00
Paulus Schoutsen 7774a03a55 Move Honeywell I/O out of event loop (#4244) 2016-11-06 07:53:54 -08:00
Fabian Affolter c35e5c9997 Upgrade astral to 1.3 (#4238) 2016-11-06 07:36:16 -08:00
Fabian Affolter 5d862e426e Upgrade fuzzywuzzy to 0.14.0 (#4240) 2016-11-06 07:36:03 -08:00
William Scanlon bab8d574fe Wink Thermostat support and NoneType error fixes (#4175) 2016-11-06 07:27:15 -08:00
Fabian Affolter c980d26aae Upgrade distro to 1.0.1 (#4239) 2016-11-06 14:00:41 +02:00
Paulus Schoutsen 08f75f7935 Merge pull request #4235 from home-assistant/release-0-32-1
0.32.1
2016-11-05 17:09:10 -07:00
Brent Hughes 1ad14b8227 Updated Emulated_Hue to send request info as variables to scripts (#4010)
* Updated Emulated_Hue to send request info as variables to scripts

* Updated tests to not use the old mqtt

* Updated test to actualy use and validate the script variables

* Fixed the removal of time in a recent merge

* fixed test to not use a timer
2016-11-05 17:08:54 -07:00
Pascal Vizeli 382ac5c3b5 Async cleanups with new handling and executor (#4234) 2016-11-06 01:01:03 +01:00
Paulus Schoutsen af297aa0dc Version bump to 0.32.1 2016-11-05 17:00:06 -07:00
Paulus Schoutsen 20e1b3eae0 Fix radiotherm I/O inside properties (#4227) 2016-11-05 16:59:52 -07:00
Paulus Schoutsen 28861221ae Remove chunked encoding (#4230) 2016-11-05 16:59:52 -07:00
Pascal Vizeli f367c49fb9 Sonos fix for slow update (#4232)
* Sonos fix for slow update

* fix auto update on discovery

* fix unittest
2016-11-05 16:59:52 -07:00
Pascal Vizeli ad8645baf4 Sonos fix for slow update (#4232)
* Sonos fix for slow update

* fix auto update on discovery

* fix unittest
2016-11-05 16:58:29 -07:00
Paulus Schoutsen 62785c2431 More async tests (#4223)
* Annotate test callbacks to be async

* Convert device_sun_light_trigger to be async
2016-11-05 16:36:20 -07:00
Paulus Schoutsen 22c3d014aa Remove chunked encoding (#4230) 2016-11-05 15:29:22 -07:00
Paulus Schoutsen 3f3127a290 Fix radiotherm I/O inside properties (#4227) 2016-11-05 13:28:11 -07:00
Danijel Stojnic 88fc64c8a0 Add Map support for Locative component (#4174)
* Add Map support for Locative component

The Locative App on the mobile is sending an HTTP request to the
server where also the GPS location is sent.
But the GPS location was not passed to the event device_tracker.see.

Use the passed GPS location from Locative and pass it to the
device_tracker.see event.

With this the device is then also shown on the HA Map component.

* Use existing constants for latitude and longitude

Use the existing constants from homeassistant.consts:

ATTR_LATITUDE for 'latitude'
ATTR_LONGITUDE for 'longitude'

* Reuse the "yield from self.hass.loop.run_in_executor" again

* Use variable gps_location
2016-11-05 13:05:15 -07:00
Samuel Bétrisey 1463fc4fe0 Add Swisscom Internet-Box device tracker (#4123)
* Add Swisscom Internet-Box device tracker

* Add Swisscom device tracker to .coveragerc

* Add timeout to requests
Fix formatting and add missing comments to pass the lint test

* Remove authentication which was not required

I realised that there was no need to be authenticated to get the
connected devices. Thanks Swisscom :/

* Moving config to a PLATFORM_SCHEMA and using voluptuous
2016-11-05 13:04:44 -07:00
Pascal Vizeli ece58ce78f Remove ThreadPool with async executor (#4154)
* Remove ThreadPool with async executor

* Fix zigbee

* update unittest

* fix remote api

* add pending task to remote

* fix lint

* remove unused import

* remove old stuff for lazy tests

* fix bug and add a exception handler to executor

* change executor handling

* change to wait from gather

* fix unittest
2016-11-05 09:27:55 -07:00
Paulus Schoutsen b67f1fed52 Version bump to 0.33.0.dev0 2016-11-05 08:53:13 -07:00
Paulus Schoutsen 4770888d22 Merge pull request #4166 from home-assistant/dev
0.32
2016-11-05 08:52:57 -07:00
Paulus Schoutsen 1d0f3b930f Version bump to 0.32.0 2016-11-05 08:40:32 -07:00
Paulus Schoutsen 22e2262f8e Merge remote-tracking branch 'origin/master' into dev 2016-11-05 08:40:04 -07:00
William Scanlon 53d1a040d4 Stop Octoprint from logging errors during startup (#4220)
* Fix log errors

* Remove discovery code
2016-11-05 07:55:59 -07:00
Fabian Affolter d7d71c97e2 Make the wind details more robust (weather.openweathermap) (#4215)
* Make the wind details more robust

* Return None if values is not available
2016-11-05 09:15:59 +01:00
Paulus Schoutsen c15fd4323e Disable insteon hub (#4221) 2016-11-04 23:38:27 -07:00
Pascal Vizeli 91227d9a2e Refactory nest component/platforms (#4219)
* Refactory nest component/platforms
2016-11-04 20:22:47 -04:00
jbcodemonkey a3db0ec231 add dimmer slide control to imported isy lights (#4152)
Supported attribute added and checks appear to pass. 🐬
2016-11-04 15:28:22 -06:00
Paulus Schoutsen 18e965c3cd Fix flaky group notify test (#4212) 2016-11-03 22:56:55 -07:00
Paulus Schoutsen 4cc417677e Add link to issue in warning slow entity update (#4211) 2016-11-03 22:45:01 -07:00
Paulus Schoutsen 525d735f21 Warn if fetching properties takes too long (#4208)
* Warn if fetching properties takes too long

* Update entity.py
2016-11-03 21:58:25 -07:00
Paulus Schoutsen e88b98f5fa Clean up tests (#4209) 2016-11-03 21:58:18 -07:00
Lewis Juggins 6f68752d1e Speed up Sonos tests (#4196) 2016-11-03 21:23:37 -07:00
Fabian Affolter 61a0976752 Use port instead of url and fix PEP257 issues (#4192) 2016-11-03 18:43:42 -07:00
Pascal Vizeli d7b3c9c38e Fix log owntrack log flooting (#4198) 2016-11-03 18:42:22 -07:00
jgriff2 a01939c6e9 Fix Synology Camera SSL certificate option (#4201) [BREAKING CHANGE]
* Fix Synology SSL config

* Revert "Fix Synology SSL config"

This reverts commit b8dc2a92abee6249b3dd42c99d0786820ebbeb72.

* Revert "Fix Synology SSL config"

This reverts commit 805e87f3af300a1b7627bb5df0792285fcf38901.

* Fix Synology SSL config
2016-11-03 18:41:32 -07:00
Fabian Affolter c128919b5f Remove globally disabled pylint warnings (#4204) 2016-11-03 18:40:43 -07:00
Pascal Vizeli e5d69feb93 Fix blocking/stack trace with empty list (#4191) 2016-11-03 18:33:18 -07:00
Paulus Schoutsen ee5f228309 Make services yield (#4187)
* Make services yield

* Disable pylint abstract-method check

* add input_select

* add input_slider

* change to async vers.

* fix lint

* yield on add_entities as other components does
2016-11-03 18:32:14 -07:00
John Arild Berentsen 15dde7925a Prevent multiple instances of device initialzed (#4179) 2016-11-03 13:08:23 +01:00
Pascal Vizeli fcf318cf53 Bugfix windows have a other default loop now (#4195)
* Bugfix windows have a other default loop now

* fix handling with 3.4.2 that not support ensure_future

* make the same as ensure_future does

* fix spell

* fix lazy test
2016-11-03 11:07:47 +01:00
Pascal Vizeli c2a5f63b1f Bugfix async Yr.no (#4190) 2016-11-03 11:09:03 +02:00
Fabian Affolter 79fa2d4175 CUPS sensor (#4142)
* Add CUPS sensor

* Use CupsData

* Fix requirement
2016-11-03 09:31:50 +01:00
Erik Eriksson 214a18f08c Support for Dovado routers (#4176)
* Implemented support for the Dovado router

* Update .coveragerc
2016-11-02 21:20:21 -07:00
Ferry van Zeelst ded2ea8b19 Synology DSM sensor (#4156)
* Added Synology DSM Sensor

* Fixed balloobbot's comments

* Fixed mistake (should have run lint and flake8 before committing

* Fixed update mechanisme according to balloobs feedback

* Requesting retest as test failure isn't related to changes made
2016-11-02 21:17:29 -07:00
Paulus Schoutsen 1d100dcac9 Bugfix/frontend group urls (#4185)
* Remove unnecessary sleeps

* Frontend: fix serving index when refreshing view page.
2016-11-02 21:15:23 -07:00
Bart274 a3ae96440b Update the icloud device_tracker (#4081)
* Update the icloud device_tracker

* addressed @kellerza 's comments

* GMTT config needs an entity_id

* renamed services

* fix cookiedir and clean up keep_alive function

* fix travis errors

* forgot a self.

* update devices after initializing the API

* changed wording

* addressed changes from @kellerza

* Syntax error solved

* Update icloud.py

* Only use account of username instead of whole username as default for account name

* use slugify instead of slug for schema

* remove Google Maps Travel Time

* Add comment from original tracker back
2016-11-02 21:07:23 -07:00
devdelay 0235626f40 yet another command_line sensor update (#4184) 2016-11-02 21:00:32 -07:00
Paulus Schoutsen d7dd7df5e7 Update frontend 2016-11-02 20:39:42 -07:00
Nicolas Graziano 1e28851280 Media player BraviaTv : Try to connect only if tv is not in off state. (#4140)
When HA is restart with the TV in off state there was error log every 10s until the TV is set ON.
2016-11-02 19:51:53 -07:00
bestlibre df68de8032 Influxdb sensor state set to unknown if query return no points (#4148)
* Influxdb sensor state set to unknown if query return no points

* Update influxdb.py
2016-11-02 19:50:18 -07:00
Johann Kellerman f3595f790a Async version of Yr.no (#4158)
* initial

* feedback

* More feedback. Still need to fix match_url

* url_match

* split_lines
2016-11-02 19:34:12 -07:00
Johann Kellerman 0d14920758 Component setup error messages with markdown (#3919)
* Remove_dev_link_async

* callback
2016-11-02 19:31:09 -07:00
Paulus Schoutsen 2940fb72fb EntityComponent.add_entities now converts generators to a list (#4183) 2016-11-02 19:24:25 -07:00
Daniel Perna 8e0838adeb Added support for Philips TVs with jointSPACE API (#4157)
* Added support for Philips Tvs with JointSpace API

* Flake + Lint fixes

* Lint be like "lol fu"

* Changes as requested by reviewers, except lib-requirement

* Switched to library-usage

* lint... newline-bingo...
2016-11-02 19:19:53 -07:00
Paulus Schoutsen 4e820ea30a Move mocks to async_start (#4182) 2016-11-02 19:16:59 -07:00
Pascal Vizeli 26490109ac Change event loop on windows (#4075)
* Change event loop on windows

* fix

* split PR

* remove set event loop

* Add paulus suggestion

* fix missing import

* revert stuff from PR Splitting

* fix event loop on test
2016-11-02 21:53:52 +01:00
Georgi Kirichkov e4a713207d Fixes in TP-Link Switch logging 0 values on init (#4026)
* Fixes in TP-Link Switch logging 0 values on init

On init of component the emeter would log to influxdb and possibly other inputs a 0 value, instead of not logging anything.
Initial polling should circumvent that behavior and avoid logging inconsistencies.

* Refactors update call in __init__
2016-11-02 12:23:43 -07:00
Greg Dowling cc0d0a38d7 Get temparature units from vera controller. (#4130)
Alrighty 👯‍♀️
2016-11-02 14:20:44 +01:00
Erik Eriksson afde5a6b26 extracted logic into an external package. monitor more attributes. support for more than one vehicle (#4170) 2016-11-01 22:01:00 -07:00
Lewis Juggins a5fb284717 Add new_device_discovered event (#4132) 2016-11-01 21:52:27 -07:00
Johann Kellerman e487a09190 Remove None value before writing known_devices (#4098)
* Remove None

* Replace null
2016-11-01 21:51:31 -07:00
Jan Losinski 52eb816c62 Introduce a send_delay for pilight component (#4051)
* Add a method to throttle calls to services

This adds CallRateDelayThrottle. This is a class that provides an
decorator to throttle calls to services. Instead of the Throttle in
homeassistant.util it does this by delaying all subsequent calls
instead of just dropping them. Dropping of calls would be bad if we
call services to actual change the state of a connected hardware (like
rf controlled power plugs).

Ihe delay is done by rescheduling the call using
track_point_in_utc_time from homeassistant.helpers.event so it should
not block the mainloop at all.

* Add unittests for CallRateDelayThrottle

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

* Introduce a send_delay for pilight component

If pilight is used with a "pilight USB Nano" between the daemon and the
hardware, we must use a delay between sending multiple signals.
Otherwise the hardware will just skip random codes. We hit this
condition for example, if we switch a group of pilight switches on or
off. Without the delay, random switch signals will not be transmitted by
the RF transmitter.

As this seems not necessary, if the transmitter is directly connected
via GPIO, we introduce a optional configuration to set the delay.

* Add unittests for pilight send_delay handling

This adds an unittest to test the delayed calls to the send_code
service.
2016-11-01 21:50:27 -07:00
Jason Carter 90d894a499 Garadget (#4031)
* Initial attempt at implementation

* Adding Garadget cover component

* Updating Device to be Required

* Updating .coveragerc to exclude from testing

* Updating code review items

* Updating per 2nd code review

* Updating configuration to be more like command-line
2016-11-01 21:49:27 -07:00
Jon Caruana ba13951fff Add LiteJet (a lighting control system) component (#4125)
* Initial submission of LiteJet integration.

* Add LiteJet switch pressed automation trigger. (State changes are too slow to catch a press-release.)
Add LiteJet scene, replacing commented out code that treated these as lights.
Include LiteJet numbers in the device state so that it is easy to lookup entity -> number.

* Fix missing global.

* Allow light's brightness to be set explicitly.

* Support optional 'ignore' key to ignore prefixes of loads, switches, and scenes that weren't configured for use in the LiteJet system.

* Fix lint errors and warnings.

* Cleanup header comments.
Default to not creating LiteJet switches as these are generally not useful.

* Lint fixes.

* Fixes from pull request feedback.

* Use hass.data instead of globals for data storage.

* Fix lint warnings.
2016-11-01 20:44:25 -07:00
Sean Dague 2a7b7ebd6a Merge pull request #3985 from postlund/yamaha_additions
Improve support for Yamaha receiver
2016-11-01 21:50:03 -04:00
Bjarni Ivarsson df7d9c3bb2 Fallback to read volume and mute state from speaker. (#4173) 2016-11-01 15:12:18 -07:00
Bjarni Ivarsson c549ea115d Sonos responsiveness improvements + enhancements (#4063)
* Sonos responsiveness improvements (async_ coroutines, event based updating, album art caching) + Better radio station information

* Docstring fixes.

* Docstring fixes.

* Updated SoCo dependency + fixed file permissions.

* Only fetch speaker info if needed.

* PEP8 fixes

* Fixed SoCoMock.get_speaker_info to get test to pass.

* Regenerated requirements_all.txt + async fetching of album art with caching + added http_session to HomeAssistant object.

* Unit test fixed.

* Add blank line as per flake8

* Fixed media image proxy unit test.

* Removed async stuff.

* Removed last remnants of async stuff.
2016-11-01 10:42:38 -07:00
Fabian Affolter dad54bb993 Update to make the sample file validate (#4168) 2016-11-01 15:11:42 +01:00
Fabian Affolter 0211cf29eb Change behavior to be more natural and fix test (#4150) 2016-11-01 10:43:48 +01:00
Nicholas Sideras 1d9ac5f8b3 Update __init__.py (#4155)
Changed manifest.json to respect Android screen rotate lock.
2016-10-31 13:04:54 -07:00
Paulus Schoutsen 7f699b4261 Lazy initialise the worker pool (#4110)
* Lazy initialise the worker pool

* Minimize pool initialization in core tests

* Fix tests on Python 3.4

* Remove passing in thread count to mock HASS

* Tests: Allow pool by default for threaded, disable for async

* Remove JobPriority for thread pool

* Fix wrong block_till_done

* EmulatedHue: Remove unused test code

* Zigbee: do not touch hass.pool

* Init loop in add_job

* Fix core test

* Fix random sensor test
2016-10-31 08:47:29 -07:00
Paulus Schoutsen a1e910f1cf Disable rest switch tests 2016-10-31 08:22:49 -07:00
Fabian Affolter b4899ec469 Allow multiple symbols (sensor.yahoo_finance) (#4126)
* Allow multiple symbols

* Update test
2016-10-31 13:31:09 +01:00
John 06de7053ce Add Emby Server media_player component (#3862)
* Add Emby Server media_player component

* Code cleanup, move to request sessions, generate UUID per session

* Make media image fetch more robust

* Allow for http or https

* Cleanup some Keyerror conditions found through more testing

* Move EmbyRemote to pip, update requirements

* Code cleanup, add SSL config option
2016-10-31 13:29:08 +01:00
Fabian Affolter 4484a7a94b Use voluptuous for Pilight switch (#3819)
* Migrate to voluptuous

* Add protocol

* Update
2016-10-31 13:18:47 +01:00
Jared Beckham a89e635bf3 Added tests for REST switches (#4016)
* Added tests for REST switches

* Remove REST switch from test coverage exclusions
2016-10-31 13:14:23 +01:00
Daniel Høyer Iversen 3ab056ba69 Merge pull request #4149 from home-assistant/flux_led_library
flux led lib
2016-10-31 11:56:00 +01:00
Daniel 5ba815ab21 flux led lib 2016-10-31 09:23:34 +01:00
Fabian Affolter 274e9799b3 Add random number sensor (#4139) 2016-10-31 00:01:25 -07:00
Jared Beckham 5ce9aea65d Added tests for REST sensors (#4115) 2016-10-30 21:51:03 -07:00
Michael 705814cb08 Catch all errors when doing mqtt message unicode-decode. (#4143)
* catch all errors when doing mqtt message unicode-decode.

* added AttributeError and UnicodeDecodeError to exception when decoding an mqtt message payload
2016-10-30 23:17:41 +01:00
Alok Saboo 8e695d1eb0 Fixed typo (#4145) 2016-10-30 23:13:27 +01:00
Fabian Affolter be272ac64a Disable too-many-* (#4107)
* Disable too-many-* and too-few-public-methods

* Remove globally disabled pylint warnings
2016-10-30 22:18:53 +01:00
Fabian Affolter b910a9917d Migrate to async (sensor.statistics) (#4138)
* Migrate to async

* Add async_ prefix and remove stale print
2016-10-30 18:56:26 +01:00
Fabian Affolter 9649097b32 Migrate to async (sensor.min_max) (#4136)
* Migrate to async

* Add async_ prefix
2016-10-30 16:45:53 +01:00
Fabian Affolter 5e76a51db4 Migrate to async (#4135) 2016-10-30 15:23:47 +01:00
Fabian Affolter 27abac85b6 Migrate to async (sensor.time_date) (#4100)
* Migrate to async

* Update acc. #4114
2016-10-30 15:21:23 +01:00
Fabian Affolter 9f2aae1357 Maintenance 2nd (#4106)
* Add link to docs

* Fix link

* Update line breaks

* Update ordering

* Align vera platofrm to only use add_devices
(instead od add_devices_callback)

* Remove line break

* Use consts

* Update ordering

* Update ordering

* Use const, create default name, use string formatting

* Update ordering

* Use const

* Update import style

* Update ordering and line breaks

* update line breaks

* Set default port

* Set defaults and update ordering

* Update ordering

* Minor style updates

* Update ordering, defaults, line breaks, and readability

* Use constants

* Add line breaks

* use string formatting

* Update line breaks

* Update logger
2016-10-30 09:58:34 +01:00
Adam Mills e6ece4bf6d Fix initialization of zwave color bulbs (#4085)
* Fix initialization of zwave color bulbs

Zwave values can be added to the node in any order. This change allows
proper initialization when the multilevel value is added before the
color value.

* Fix incorrect rename of color command class
2016-10-29 17:14:28 -07:00
Hydreliox aea2d1b317 Add support for Yeelight Wifi bulbs (#4065)
* Add support for Yeelight Wifi bulbs

* Fix cache property in instance
2016-10-29 17:03:26 -07:00
Pierre Ståhl 33e46b484f Add service to change visibility of a group (#3998) 2016-10-29 16:54:26 -07:00
wokar 3f6a5564ad lg_netcast platform fails to load if no channels defined (#4083)
* fixes loading of lg_netcast platform if no channels are defined

* turned list comprehension into for loop
2016-10-29 16:52:53 -07:00
Erik Eriksson 3317b4916b OSError is alias for IOException and base class for many other exceptions - no need to catch redundant exceptions if OSError already present in except-clause (#4111) 2016-10-29 15:33:56 -07:00
Pascal Vizeli 9c0455e3dc Allow update entities on add_entities callback (#4114)
* Allow udpate entities on add_entities callback

* fix wrong position

* update force_update to update_before_add

* add unittest for update_befor_add

* fix unittest

* change mocking
2016-10-29 15:33:11 -07:00
Fabian Affolter 5d43d3eb1c Fix error message (#4122) 2016-10-29 15:30:23 -07:00
Paulus Schoutsen 4163e55dbd Introducing hass.data (#4121)
* Hello hass.data

* Migrate setup_component to hass.data
2016-10-29 14:51:17 -07:00
Paulus Schoutsen 3cc4fdaa34 Fix HTTP static file singular (#4118) 2016-10-29 14:45:31 -07:00
Pascal Vizeli edeb31d74e Fix bug with aioHTTP and none authentification (#4116) 2016-10-29 22:47:46 +02:00
Fabian Affolter 54d19e3c53 Maintenance (sensor.currencylayer, sensor.fixer) (#4103)
* Add new const (base)

* Use constant

* Remove second error message, use const, add attribution, add link
to docs, remove unused vars, and a little simplification

* Add quote

* Use const

* Add attribution, simplify the code, and use consts
2016-10-29 13:27:02 -07:00
Fabian Affolter 892f455aee Maintenance (sensor.bitcoin, sensor.yahoo_finance) (#4104)
* Add attribution

* Update ordering

* Update ordering
2016-10-29 13:21:09 -07:00
Fabian Affolter 942d630762 Maintenance zoneminder (#4102)
* Add timeout to requests, fix typos, and defaults

* Clean-up
2016-10-29 13:10:42 -07:00
Paulus Schoutsen 9ea1101aba Fix bootstrap circular imports (#4108)
* Fix bootstrap circular imports

* fix test

* Lint
2016-10-29 12:54:47 -07:00
Pascal Vizeli 08a65a3b31 Async input_*/zone migration (#4095)
* Async input_*

* Async zone component

* rename service callback
2016-10-29 12:19:27 -07:00
Fabian Affolter d4b3f56d53 Maintenance (#4101)
* UPdate ordering, fix typos, and align logger messages

* Update import style, fix PEP257 issue, and align logger messages

* Updaate import style and align logger messages

* Update import style and align logger messages

* Update ordering

* Update import style and ordering

* Update quotes

* Make logger messages more clear

* Fix indentation
2016-10-29 09:12:43 -07:00
Paulus Schoutsen 5a2b4a5376 Core Async improvements (#4087)
* Clean up HomeAssistant.start

* Add missing pieces to remote HA constructor

* Make HomeAssistant constructor async safe

* Code cleanup

* Init websession lazy
2016-10-29 08:57:59 -07:00
Sean Dague 9d836a115a Add zone_ignore option for yamaha. (#4091)
* Add zone_ignore option for yamaha.

We attempt to discover all zones for yamaha receivers. There are times
when users may want to suppress some zones from showing up. When a
Zone isn't actually connected to speakers, or on some newer receivers
where Zone_4 is an HDMI only zone, that doesn't support even basic
media_player UI.

This provide a mechanism for users to do that.

Fixes #4088

* Update yamaha.py
2016-10-28 19:18:31 -07:00
Fabian Affolter bf92aedd38 Add hddtemp sensor (#4092) 2016-10-28 19:06:24 -07:00
devdelay 230c3815f2 Update command_line sensor to use STATE_UNKNOWN (#4093) 2016-10-28 19:03:40 -07:00
Pascal Vizeli 9afe066ec8 Fix name in openalpr cloud api (#4097) 2016-10-28 19:01:14 -07:00
Pascal Vizeli 66541a6a19 Update ha-ffmpeg to version 0.15 (#4096) 2016-10-29 00:12:53 +02:00
Pascal Vizeli 825ee3612d fix some comments spell (#4082)
* fix some comments

* fix in an executor

* address paulus comments
2016-10-28 21:26:52 +02:00
Abhishek Anand 65bd7d2326 Generalized REST switch to enable templating and configurable timeout. (#3329)
* successfully tested the "remote temperature mode" switch for the radio thermostat

* removed logging and interpreted None as Off.

* turn_off value is also templated now -- can depend on state

Also, undid accidental removal of error logging.

* ensured backward compatibility of config file

if value_template is not provided, the update function behaves as before

* ran autopep8 --in-place

* fixed another complaint of tox

* addressed the comments of balloob

* undid acccidental log.error to log.info

* timeout : 50 -> 10

* added a timeout parameter

* removed the stray '-', better names for the failure case

* string comparisons after .lower(), as suggested by balloob

* addressed balloob's latest requests

* making flake happy

* value_template --> is_on_template in config file

* moved CONF_IS_ON_TEMPLATE to local file

* null checks

* addressed flake error

* properly comparing template text when is_on is not a template.
2016-10-27 22:34:22 -07:00
John Arild Berentsen d8c1013b09 Zwave climate, add operating state to attributes (#4069)
* Zwave climate, add operating state to attributes

* Reversed assisgnment
2016-10-27 22:25:17 -07:00
Fabian Affolter 02d1dc6247 Upgrade psutil to 4.4.2 (#4079) 2016-10-27 22:22:43 -07:00
Paulus Schoutsen 726d950522 Update aiohttp.py 2016-10-27 21:45:35 -07:00
Pascal Vizeli 3324995e70 Async clientsession / fix stuff on aiohttp and camera platform (#4084)
* add websession

* convert to websession

* convert camera to async

* fix lint

* fix spell

* add import

* create task to loop

* fix test

* update aiohttp

* fix tests part 2

* Update aiohttp.py
2016-10-27 21:40:10 -07:00
Fabian Affolter 85747fe2ef Upgrade python-telegram-bot to 5.2.0 (#4080) 2016-10-27 21:28:09 -07:00
Pascal Vizeli 09db875ace Fix async bug in automation (#4078) 2016-10-27 18:26:55 +02:00
bestlibre 7d407756c3 Converting unit_of_measurement variable to optional, to be consistent with other sensors (#4076) 2016-10-27 08:50:36 -07:00
Richard Cox 91d682d02c Adding ssl option to zoneminder (#4074) 2016-10-27 15:54:03 +02:00
Benoit BESSET b75c103db4 fixed Up/Down (#4064) 2016-10-27 12:10:38 +02:00
Richard Cox bba323d226 [media_player/onkyo] host should be optional (#4073) 2016-10-27 10:33:35 +02:00
Paulus Schoutsen 7564d539c1 Lint 2016-10-27 00:37:02 -07:00
Paulus Schoutsen 33439aaa22 Update frontend 2016-10-27 00:21:55 -07:00
Pascal Vizeli d5368f6f78 Async bootstrap / component init (#3991)
* Async bootstrap

* Adress comments

* Fix tests

* More fixes

* Tests fixes
2016-10-27 00:16:23 -07:00
Simon Szustkowski d9999f36e8 Added a ThingSpeak component (#4027)
* Added a ThingSpeak component

* Forgot a colon. Fixed it

* Some config variables are better required

* New requirements created by the script

* Updated the .coveragerc

* Fixed small linting errors

* Removed unneccessary validation

* Even more linting error fixes

* Changed the way the component listens to state changes

* Removed unneccessary declaration of 'state' variable, referring to new_state instead
2016-10-26 23:56:51 -07:00
Pierre Ståhl 235e1a0885 Minor improvements to RPi camera platform (#4059)
* Try to create output file instead of checking write permissions

* Kill raspistill process during shutdown
2016-10-26 23:51:13 -07:00
Paulus Schoutsen 541fec0534 Sort .coveragerc alphabetically. 2016-10-26 23:50:11 -07:00
bestlibre b3ad7989ae Influxdb sensor (#4060)
* Influxdb sensor with voluptuous configuration validation

* Adding sensor to coveragerc since there is no test for now
2016-10-26 23:48:57 -07:00
Sean Dague 3d897e0e52 Add discovery for yamaha component (#4061)
This uses the discovery code from netdisco/ha to discover yamaha
receivers. The old discovery code remains if discovery is turned of in
HA, at least for now. Though it probably is worth turning that off in
the future.
2016-10-26 23:46:44 -07:00
Alok Saboo c6d5987109 Create Currencylayer exchange rate sensor (#4062)
* Added Currencylayer exchange rate sensor

* Updated .coveragerc to include currencylayer

* Update currencylayer.py

* Added Conf_name
2016-10-26 23:46:13 -07:00
Matthew Treinish 5d3956ea98 Cleanup use of MQTT in emulated_hue tests (#4068)
* Use unix newlines on test_emulated_hue

This commit switches the test_emulated_hue module to use unix newlines
instead of the DOS style that were there before. (using dos2unix on
the file) This makes it consistent with the other files in the repo.

* Cleanup emulated_hue tests

Previously these tests relied on the mqtt light platform as test devices
to control with the emulated hue. However, this was pretty heavyweight
and required running an MQTT broker in the tests. Instead this commit
switches it to use the demo light platform which is strictly in memory.

Fixes #3549
2016-10-26 23:33:43 -07:00
Marcelo Moreira de Mello 4fb0b27310 Wunderground sensor with alerts exceeds API limits (#4070)
* Fixes issue #4067 - Wunderground sensor with alerts exceeds API limits

 To avoid hitting the max limit of 500 calls per day, this patch keeps weather conditions being updated each 5 minutes
  and weather advisories each 15 minutes.

 This formula will result the following:

   conditions -> 300 seconds -> 5 minutes -> 12 req/h -> 288 req/day
   alerts -> 900 seconds -> 15 minutes -> 4 req/h -> 96 req/day

* Using timedelta in minutes instead seconds
2016-10-26 23:31:49 -07:00
Paulus Schoutsen 4833e992fb Pin cython==0.24.1 (#4057) 2016-10-25 23:38:32 -07:00
Paulus Schoutsen fe3aed0f0c Update .coveragerc 2016-10-25 23:32:58 -07:00
Paulus Schoutsen 57402bcb43 Update .coveragerc 2016-10-25 23:30:43 -07:00
Scott O'Neil 7f48c00793 Adding timer setting functionality to sonos component (#3941)
* Adding timer setting functionality to sonos component

* Adding clear sleep timer for Sonos
2016-10-25 23:22:17 -07:00
Paulus Schoutsen f58647849a Fix Z-Wave: Pin cython in Dockerfile (#4055) 2016-10-25 23:17:34 -07:00
Marcelo Moreira de Mello 1f468fc94d If no weather advisories were issued, state should return 0 instead Unknown (#4029)
* If no weather advisories were issued, state should return 0 instead Unknown

* Updated to keep on the same if statement

* Revert "Updated to keep on the same if statement"

This reverts commit 0e6a94aa0f.
2016-10-25 22:49:51 -07:00
Bjarni Ivarsson 961c02f72a Sonos improvements (#3997)
* Sonos improvements: media_* properties delegate to coordinator if speaker is a slave, media_image_url and media_title now works for radio streams, source selection/list takes speaker model into account, commands on slaves delegate to coordinator.

* Fixed failing unit tests.
2016-10-26 00:37:47 +02:00
Robbie Trencheny 79da1ec0d9 Merge pull request #4037 from home-assistant/remove-deprecated-things
Remove deprecated things
2016-10-25 14:28:01 -07:00
Robbie Trencheny fe174402d2 Remove more deprecated things 2016-10-25 14:16:08 -07:00
Matthew Treinish 1b2dfb8ed1 Handle FreeBSD version in updater component (#4048) 2016-10-25 22:38:22 +02:00
Pierre Ståhl 297a6f6f03 Improve support for Yamaha receiver
* Playback (play, pause, stop, next, previous)

* Media title, artist and album
2016-10-25 20:53:04 +02:00
Per Sandström 0dfcf40d37 [WIP] Config validation error line numbers (#3976)
Config validation error line numbers
2016-10-25 20:13:32 +02:00
Fabian Affolter d308ea69ce Upgrade yahoo-finance to 1.3.2 (#4040) 2016-10-25 08:36:20 -07:00
Sean Dague a8c5c995a0 Merge pull request #4042 from sdague/typo
fix typos in script module strings
2016-10-25 07:40:11 -04:00
Sean Dague 86b318e992 fix typos in script module strings
It looks like some copy / paste in docstrings, clean them up for
posterity.
2016-10-25 07:20:40 -04:00
Bart274 53ea926292 Fix for see service attributes (#4023) 2016-10-25 10:59:20 +02:00
Robbie Trencheny 2b1f4123db Update requirements_all.txt 2016-10-24 22:36:04 -07:00
Robbie Trencheny b36e346ccb Fix helpers.state tests 2016-10-24 22:33:54 -07:00
Adam Mills 89e8fb4066 Configurator support for entity_picture (#4028) 2016-10-24 22:28:34 -07:00
David-Leon Pohl e2d23d902a Unittests for ddwrt device tracker and bugfix (#3996)
* BUG Message data cannot be changed thus use voluptuous to ensure format

* Pilight daemon expects JSON serializable data

Thus dict is needed and not a mapping proxy.

* Add explanation why dict as message data is needed

* Use more obvious voluptuous validation scheme

* Pylint:  Trailing whitespace

* Pilight sensor component

* Python 3.4 compatibility

* D202

* Use pytest-caplog and no unittest.TestCase

* Fix setup/teardown of unittests

* Activate coverage testing

* Bugfix whitelist filter and use bugfixed pilight library

* Use newest pilight library that has a bugfix

* Add unittest for pilight hub component

* PEP257 for docstrings

* Bugfix setting device name from host name and small cleanup

- Init with connection error handling is more clear
- Comments clean-up

* PEP257

* New unittest with full coverage

* Upload missing testfixtures

* D209

* Handle double quotes in reply

* Formatting
2016-10-24 22:18:24 -07:00
Fabian Affolter 2604dd89a6 Add test (#3999) 2016-10-24 22:01:38 -07:00
Robbie Trencheny 044b9caa76 Remove garage_door, hvac, rollershutter and thermostat components/platforms 2016-10-24 22:00:43 -07:00
Daniel Perna 1707cdf9f3 Added support for Notifications for Android TV / FireTV (#3978)
* Added support for Notifications for Android TV / FireTV

* Silly me forgot to commit coverage

* Fixed pylint

* Fixed flake8

* Fixed another flake8 -.-

* Changed option 'ip' to 'host' like most other platforms do
2016-10-24 21:59:09 -07:00
Fabian Affolter 4c86721e70 Update tests, rename variable, and change conversion (#3546) 2016-10-24 21:53:03 -07:00
Matthew Treinish 0ff500ca25 Add mochad component (#3970)
This commit adds a new component for communicating with mochad[1] a
socket interface for the CM15A and CM19A USB X10 controllers. This
commit leverages the pymochad library to interface with a mochad socket
either on a local or remote machine. Mochad is added as as a generic
platform because it supports multiple different classes of device,
however in this patch only the switch device implemented as a starting
point. Future patches will include other devices types. (although
that's dependent on someone gaining access to those)

[1] https://sourceforge.net/projects/mochad/
2016-10-24 21:49:49 -07:00
Robbie Trencheny 23f54b07c7 Dont load notify.ios if no devices exist. Thanks @arsaboo for catching this. 2016-10-24 18:46:47 -07:00
Robbie Trencheny f25ddef4d7 More iOS HTTP Async updates 2016-10-24 17:31:45 -07:00
Robbie Trencheny 7158919346 Missed a wsgi->http on iOS component 2016-10-24 15:03:51 -07:00
Fabian Affolter 627517cbbc Upgrade psutil to 4.4.0 (#4032) 2016-10-24 22:24:33 +02:00
Fabian Affolter 4ecfc7d066 Upgrade sqlalchemy to 1.1.2 (#4003) 2016-10-24 22:00:22 +02:00
Fabian Affolter 72751b95b5 Upgrade slacker to 0.9.29 (#4000) 2016-10-24 22:00:02 +02:00
Fabian Affolter fc3b7907ed Upgrade python-telegram-bot to 5.1.1 (#4001) 2016-10-24 21:59:48 +02:00
Paulus Schoutsen f26a7fc6bb Update http 2016-10-24 00:09:20 -07:00
Robbie Trencheny 0c563f7b14 Minor updater... updates (#4020)
* Enable updater in dev versions

* Code clarity

* Add log line about being on the current version already

* Remove dev check test
2016-10-24 00:01:56 -07:00
Paulus Schoutsen 519d9f2fd0 async HTTP component (#3914)
* Migrate WSGI to asyncio

* Rename wsgi -> http

* Python 3.4 compat

* Move linting to Python 3.4

* lint

* Lint

* Fix Python 3.4 mock_open + binary data

* Surpress logging aiohttp.access

* Spelling

* Sending files is a coroutine

* More callback annotations and naming fixes

* Fix ios
2016-10-23 23:48:01 -07:00
Paulus Schoutsen 3701ac292c Merge pull request #4017 from home-assistant/release-0-31-1
0.31.1
2016-10-23 21:57:38 -07:00
Lewis Juggins 1db18478d2 Exclude dirs/files prefixed with . (#3986) 2016-10-23 21:08:56 -07:00
Robbie Trencheny 3230869f74 Fix a spelling problem on user-facing error 2016-10-23 20:34:31 -07:00
Robbie Trencheny 9aa88819a5 Fix a spelling problem on user-facing error 2016-10-23 20:33:49 -07:00
Paulus Schoutsen 3e92318cb2 Version bump to 0.31.1 2016-10-23 19:20:30 -07:00
Johann Kellerman f0a38dded6 Catch UnicodeDecodeError Error (#4007)
* Catch UnicodeDecodeError Error

Error for #3933

* Forgot (exc)

* catch...

* Tests by @lwis

* Docstring

* Create open
2016-10-23 19:20:13 -07:00
Robbie Trencheny c32f47aea6 iOS component hotfixes (#4015)
* iOS component hot fixes around component/platform loading, logging, and more

* Load device_tracker and zeroconf in deps instead of bootstraping

* Change conditional check on status code
2016-10-23 19:18:13 -07:00
Robbie Trencheny 626763a7c3 iOS component hotfixes (#4015)
* iOS component hot fixes around component/platform loading, logging, and more

* Load device_tracker and zeroconf in deps instead of bootstraping

* Change conditional check on status code
2016-10-23 19:17:34 -07:00
Johann Kellerman 5df8477536 Catch UnicodeDecodeError Error (#4007)
* Catch UnicodeDecodeError Error 

Error for #3933

* Forgot (exc)

* catch...

* Tests by @lwis

* Docstring

* Create open
2016-10-23 18:55:06 -07:00
Fabian Affolter 1f89e6ddba Upgrade pytz to 2016.7 (#4002) 2016-10-23 20:51:41 +02:00
Lewis Juggins 13ab2be5f6 Exclude dirs/files prefixed with . (#3986) 2016-10-23 16:47:06 +02:00
Robbie Trencheny 2bc84af87e Version bump to 0.32.0.dev0 2016-10-22 15:23:01 -07:00
934 changed files with 65506 additions and 19423 deletions
+99 -18
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
@@ -28,15 +32,24 @@ omit =
homeassistant/components/envisalink.py
homeassistant/components/*/envisalink.py
homeassistant/components/google.py
homeassistant/components/*/google.py
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
@@ -95,29 +108,39 @@ omit =
homeassistant/components/netatmo.py
homeassistant/components/*/netatmo.py
homeassistant/components/neato.py
homeassistant/components/*/neato.py
homeassistant/components/homematic.py
homeassistant/components/*/homematic.py
homeassistant/components/switch/pilight.py
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
@@ -128,6 +151,7 @@ omit =
homeassistant/components/climate/knx.py
homeassistant/components/climate/proliphix.py
homeassistant/components/climate/radiotherm.py
homeassistant/components/cover/garadget.py
homeassistant/components/cover/homematic.py
homeassistant/components/cover/rpi_gpio.py
homeassistant/components/cover/scsgate.py
@@ -136,58 +160,82 @@ omit =
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
homeassistant/components/device_tracker/bbox.py
homeassistant/components/device_tracker/bluetooth_tracker.py
homeassistant/components/device_tracker/bluetooth_le_tracker.py
homeassistant/components/device_tracker/bluetooth_tracker.py
homeassistant/components/device_tracker/bt_home_hub_5.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/cisco_ios.py
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
homeassistant/components/garage_door/rpi_gpio.py
homeassistant/components/garage_door/wink.py
homeassistant/components/hdmi_cec.py
homeassistant/components/ifttt.py
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
homeassistant/components/media_player/denon.py
homeassistant/components/media_player/denonavr.py
homeassistant/components/media_player/directv.py
homeassistant/components/media_player/dunehd.py
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
homeassistant/components/media_player/philips_js.py
homeassistant/components/media_player/pioneer.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/roku.py
@@ -196,19 +244,25 @@ 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
homeassistant/components/notify/nma.py
homeassistant/components/notify/pushbullet.py
homeassistant/components/notify/pushetta.py
@@ -222,21 +276,28 @@ 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
homeassistant/components/sensor/currencylayer.py
homeassistant/components/sensor/darksky.py
homeassistant/components/sensor/deutsche_bahn.py
homeassistant/components/sensor/dht.py
homeassistant/components/sensor/dovado.py
homeassistant/components/sensor/dte_energy_bridge.py
homeassistant/components/sensor/efergy.py
homeassistant/components/sensor/eliqonline.py
@@ -250,33 +311,45 @@ omit =
homeassistant/components/sensor/gpsd.py
homeassistant/components/sensor/gtfs.py
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
homeassistant/components/sensor/lastfm.py
homeassistant/components/sensor/linux_battery.py
homeassistant/components/sensor/loopenergy.py
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/rest.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
homeassistant/components/sensor/steam_online.py
homeassistant/components/sensor/supervisord.py
homeassistant/components/sensor/swiss_hydrological_data.py
homeassistant/components/sensor/swiss_public_transport.py
homeassistant/components/sensor/synologydsm.py
homeassistant/components/sensor/systemmonitor.py
homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/temper.py
@@ -285,32 +358,40 @@ 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/worldclock.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/xbox_live.py
homeassistant/components/sensor/yweather.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/neato.py
homeassistant/components/switch/netio.py
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/pilight.py
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/rpi_rf.py
homeassistant/components/switch/tplink.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/thermostat/eq3btsmart.py
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.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
+1
View File
@@ -62,6 +62,7 @@ pip-log.txt
.coverage
.tox
nosetests.xml
htmlcov/
# Translations
*.mo
+2
View File
@@ -0,0 +1,2 @@
python:
enabled: true
+6
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
+11 -8
View File
@@ -2,19 +2,22 @@ sudo: false
matrix:
fast_finish: true
include:
- python: "3.4"
- python: "3.4.2"
env: TOXENV=py34
- python: "3.4"
- python: "3.4.2"
env: TOXENV=requirements
- python: "3.5"
- 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
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
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/
+3 -2
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.
+14 -13
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
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
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.
+3 -2
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
+22 -58
View File
@@ -1,7 +1,7 @@
homeassistant:
# Omitted values in this section will be auto detected using freegeoip.io
# Location required to calculate the time the sun rises and sets.
# Location required to calculate the time the sun rises and sets.
# Coordinates are also used for location for weather related components.
# Google Maps can be used to determine more precise GPS coordinates.
latitude: 32.87336
@@ -43,12 +43,10 @@ device_tracker:
username: admin
password: PASSWORD
chromecast:
switch:
platform: wemo
thermostat:
climate:
platform: nest
# Required: username and password that are used to login to the Nest thermostat.
username: myemail@mydomain.com
@@ -79,7 +77,6 @@ group:
entities:
- group.awesome_people
- group.climate
kitchen:
name: Kitchen
entities:
@@ -92,52 +89,23 @@ group:
- input_boolean.notify_home
- camera.demo_camera
example:
simple_alarm:
# Which light/light group has to flash when a known device comes home
known_light: light.Bowl
# Which light/light group has to flash red when light turns on while no one home
unknown_light: group.living_room
browser:
keyboard:
# https://home-assistant.io/getting-started/automation/
automation:
- alias: 'Rule 1 Light on in the evening'
trigger:
- platform: sun
- alias: Turn on light when sun sets
trigger:
platform: sun
event: sunset
offset: "-01:00:00"
- platform: state
condition:
condition: state
entity_id: group.all_devices
state: home
condition:
- platform: state
entity_id: group.all_devices
state: home
- platform: time
after: "16:00:00"
before: "23:00:00"
action:
service: homeassistant.turn_on
entity_id: group.living_room
state: 'home'
action:
service: light.turn_on
- alias: 'Rule 2 - Away Mode'
trigger:
- platform: state
entity_id: group.all_devices
state: 'not_home'
condition: use_trigger_values
action:
service: light.turn_off
entity_id: group.all_lights
# Sensors need to be added into the configuration.yaml as sensor:, sensor 2:, sensor 3:, etc.
# Each sensor label should be unique or your sensors might not load correctly.
# Another way to do is to collect all entries under one "sensor:"
# sensor:
# - platform: mqtt
@@ -154,34 +122,30 @@ sensor:
arg: '/'
- type: 'disk_use_percent'
arg: '/home'
- type: 'disk_use'
arg: '/home'
sensor 2:
platform: forecast
api_key: <register on Forecast.io for your PRIVATE API>
monitored_conditions:
- summary
- precip_type
- precip_intensity
- temperature
platform: cpuspeed
script:
# Turns on the bedroom lights and then the living room lights 1 minute later
wakeup:
alias: Wake Up
sequence:
# alias is optional
- event: LOGBOOK_ENTRY
event_data:
name: Paulus
message: is waking up
entity_id: device_tracker.paulus
domain: light
- alias: Bedroom lights on
execute_service: light.turn_on
service_data:
service: light.turn_on
data:
entity_id: group.bedroom
brightness: 100
- delay:
# supports seconds, milliseconds, minutes, hours, etc.
minutes: 1
- alias: Living room lights on
execute_service: light.turn_on
service_data:
service: light.turn_on
data:
entity_id: group.living_room
scene:
+10 -4
View File
@@ -14,6 +14,7 @@ from homeassistant.const import (
__version__,
EVENT_HOMEASSISTANT_START,
REQUIRED_PYTHON_VER,
REQUIRED_PYTHON_VER_WIN,
RESTART_EXIT_CODE,
)
from homeassistant.util.async import run_callback_threadsafe
@@ -44,8 +45,7 @@ def monkey_patch_asyncio():
See https://bugs.python.org/issue26617 for details of the Python
bug.
"""
# pylint: disable=no-self-use, too-few-public-methods, protected-access
# pylint: disable=bare-except
# pylint: disable=no-self-use, protected-access, bare-except
import asyncio.tasks
class IgnoreCalls:
@@ -64,7 +64,12 @@ def monkey_patch_asyncio():
def validate_python() -> None:
"""Validate we're running the right Python version."""
if sys.version_info[:3] < REQUIRED_PYTHON_VER:
if sys.platform == "win32" and \
sys.version_info[:3] < REQUIRED_PYTHON_VER_WIN:
print("Home Assistant requires at least Python {}.{}.{}".format(
*REQUIRED_PYTHON_VER_WIN))
sys.exit(1)
elif sys.version_info[:3] < REQUIRED_PYTHON_VER:
print("Home Assistant requires at least Python {}.{}.{}".format(
*REQUIRED_PYTHON_VER))
sys.exit(1)
@@ -351,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()
+303 -118
View File
@@ -1,11 +1,10 @@
"""Provides methods to bootstrap a home assistant instance."""
import asyncio
import logging
import logging.handlers
import os
import sys
from collections import defaultdict
from threading import RLock
from collections import OrderedDict
from types import ModuleType
from typing import Any, Optional, Dict
@@ -19,44 +18,61 @@ import homeassistant.config as conf_util
import homeassistant.core as core
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__)
_SETUP_LOCK = RLock()
_CURRENT_SETUP = []
ATTR_COMPONENT = 'component'
ERROR_LOG_FILENAME = 'home-assistant.log'
_PERSISTENT_PLATFORMS = set()
_PERSISTENT_VALIDATION = set()
_PERSISTENT_ERRORS = {}
HA_COMPONENT_URL = '[{}](https://home-assistant.io/components/{}/)'
def setup_component(hass: core.HomeAssistant, domain: str,
config: Optional[Dict]=None) -> bool:
"""Setup a component and all its dependencies."""
return run_coroutine_threadsafe(
async_setup_component(hass, domain, config), loop=hass.loop).result()
@asyncio.coroutine
def async_setup_component(hass: core.HomeAssistant, domain: str,
config: Optional[Dict]=None) -> bool:
"""Setup a component and all its dependencies.
This method is a coroutine.
"""
if domain in hass.config.components:
_LOGGER.debug('Component %s already set up.', domain)
return True
_ensure_loader_prepared(hass)
if not loader.PREPARED:
yield from hass.loop.run_in_executor(None, loader.prepare, hass)
if config is None:
config = defaultdict(dict)
config = {}
components = loader.load_order_component(domain)
# OrderedSet is empty if component or dependencies could not be resolved
if not components:
_async_persistent_notification(hass, domain, True)
return False
for component in components:
if not _setup_component(hass, component, config):
res = yield from _async_setup_component(hass, component, config)
if not res:
_LOGGER.error('Component %s failed to setup', component)
_async_persistent_notification(hass, component, True)
return False
return True
@@ -64,7 +80,10 @@ def setup_component(hass: core.HomeAssistant, domain: str,
def _handle_requirements(hass: core.HomeAssistant, component,
name: str) -> bool:
"""Install the requirements for a component."""
"""Install the requirements for a component.
This method needs to run in an executor.
"""
if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'):
return True
@@ -72,70 +91,109 @@ def _handle_requirements(hass: core.HomeAssistant, component,
if not pkg_util.install_package(req, target=hass.config.path('deps')):
_LOGGER.error('Not initializing %s because could not install '
'dependency %s', name, req)
_async_persistent_notification(hass, name)
return False
return True
def _setup_component(hass: core.HomeAssistant, domain: str, config) -> bool:
"""Setup a component for Home Assistant."""
# pylint: disable=too-many-return-statements,too-many-branches
# pylint: disable=too-many-statements
@asyncio.coroutine
def _async_setup_component(hass: core.HomeAssistant,
domain: str, config) -> bool:
"""Setup a component for Home Assistant.
This method is a coroutine.
"""
# pylint: disable=too-many-return-statements
if domain in hass.config.components:
return True
with _SETUP_LOCK:
# It might have been loaded while waiting for lock
if domain in hass.config.components:
return True
setup_lock = hass.data.get('setup_lock')
if setup_lock is None:
setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop)
if domain in _CURRENT_SETUP:
_LOGGER.error('Attempt made to setup %s during setup of %s',
domain, domain)
return False
setup_progress = hass.data.get('setup_progress')
if setup_progress is None:
setup_progress = hass.data['setup_progress'] = []
config = prepare_setup_component(hass, config, domain)
if domain in setup_progress:
_LOGGER.error('Attempt made to setup %s during setup of %s',
domain, domain)
_async_persistent_notification(hass, domain, True)
return False
try:
# Used to indicate to discovery that a setup is ongoing and allow it
# to wait till it is done.
did_lock = False
if not setup_lock.locked():
yield from setup_lock.acquire()
did_lock = True
setup_progress.append(domain)
config = yield from async_prepare_setup_component(hass, config, domain)
if config is None:
return False
component = loader.get_component(domain)
_CURRENT_SETUP.append(domain)
if component is None:
_async_persistent_notification(hass, domain)
return False
async_comp = hasattr(component, 'async_setup')
try:
result = component.setup(hass, config)
if result is False:
_LOGGER.error('component %s failed to initialize', domain)
return False
elif result is not True:
_LOGGER.error('component %s did not return boolean if setup '
'was successful. Disabling component.', domain)
loader.set_component(domain, None)
return False
_LOGGER.info("Setting up %s", domain)
if async_comp:
result = yield from component.async_setup(hass, config)
else:
result = yield from hass.loop.run_in_executor(
None, component.setup, hass, config)
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error during setup of component %s', domain)
_async_persistent_notification(hass, domain, True)
return False
if result is False:
_LOGGER.error('component %s failed to initialize', domain)
_async_persistent_notification(hass, domain, True)
return False
elif result is not True:
_LOGGER.error('component %s did not return boolean if setup '
'was successful. Disabling component.', domain)
_async_persistent_notification(hass, domain, True)
loader.set_component(domain, None)
return False
finally:
_CURRENT_SETUP.remove(domain)
hass.config.components.append(component.DOMAIN)
# Assumption: if a component does not depend on groups
# it communicates with devices
if 'group' not in getattr(component, 'DEPENDENCIES', []) and \
hass.pool.worker_count <= 10:
hass.pool.add_worker()
hass.bus.fire(
hass.bus.async_fire(
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}
)
return True
finally:
setup_progress.remove(domain)
if did_lock:
setup_lock.release()
def prepare_setup_component(hass: core.HomeAssistant, config: dict,
domain: str):
"""Prepare setup of a component and return processed config."""
return run_coroutine_threadsafe(
async_prepare_setup_component(hass, config, domain), loop=hass.loop
).result()
@asyncio.coroutine
def async_prepare_setup_component(hass: core.HomeAssistant, config: dict,
domain: str):
"""Prepare setup of a component and return processed config.
This method is a coroutine.
"""
# pylint: disable=too-many-return-statements
component = loader.get_component(domain)
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
@@ -151,7 +209,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
try:
config = component.CONFIG_SCHEMA(config)
except vol.Invalid as ex:
log_exception(ex, domain, config, hass)
async_log_exception(ex, domain, config, hass)
return None
elif hasattr(component, 'PLATFORM_SCHEMA'):
@@ -161,7 +219,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
try:
p_validated = component.PLATFORM_SCHEMA(p_config)
except vol.Invalid as ex:
log_exception(ex, domain, config, hass)
async_log_exception(ex, domain, config, hass)
continue
# Not all platform components follow same pattern for platforms
@@ -171,8 +229,8 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
platforms.append(p_validated)
continue
platform = prepare_setup_platform(hass, config, domain,
p_name)
platform = yield from async_prepare_setup_platform(
hass, config, domain, p_name)
if platform is None:
continue
@@ -180,10 +238,11 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
# Validate platform specific schema
if hasattr(platform, 'PLATFORM_SCHEMA'):
try:
# pylint: disable=no-member
p_validated = platform.PLATFORM_SCHEMA(p_validated)
except vol.Invalid as ex:
log_exception(ex, '{}.{}'.format(domain, p_name),
p_validated, hass)
async_log_exception(ex, '{}.{}'.format(domain, p_name),
p_validated, hass)
continue
platforms.append(p_validated)
@@ -195,7 +254,9 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
if key not in filter_keys}
config[domain] = platforms
if not _handle_requirements(hass, component, domain):
res = yield from hass.loop.run_in_executor(
None, _handle_requirements, hass, component, domain)
if not res:
return None
return config
@@ -204,7 +265,22 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
platform_name: str) -> Optional[ModuleType]:
"""Load a platform and makes sure dependencies are setup."""
_ensure_loader_prepared(hass)
return run_coroutine_threadsafe(
async_prepare_setup_platform(hass, config, domain, platform_name),
loop=hass.loop
).result()
@asyncio.coroutine
def async_prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
platform_name: str) \
-> Optional[ModuleType]:
"""Load a platform and makes sure dependencies are setup.
This method is a coroutine.
"""
if not loader.PREPARED:
yield from hass.loop.run_in_executor(None, loader.prepare, hass)
platform_path = PLATFORM_FORMAT.format(domain, platform_name)
@@ -213,13 +289,7 @@ def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
# Not found
if platform is None:
_LOGGER.error('Unable to find platform %s', platform_path)
_PERSISTENT_PLATFORMS.add(platform_path)
message = ('Unable to find the following platforms: ' +
', '.join(list(_PERSISTENT_PLATFORMS)) +
'(please check your configuration)')
persistent_notification.create(
hass, message, 'Invalid platforms', 'platform_errors')
_async_persistent_notification(hass, platform_path)
return None
# Already loaded
@@ -228,20 +298,23 @@ def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
# Load dependencies
for component in getattr(platform, 'DEPENDENCIES', []):
if not setup_component(hass, component, config):
res = yield from async_setup_component(hass, component, config)
if not res:
_LOGGER.error(
'Unable to prepare setup for platform %s because '
'dependency %s could not be initialized', platform_path,
component)
_async_persistent_notification(hass, platform_path, True)
return None
if not _handle_requirements(hass, platform, platform_path):
res = yield from hass.loop.run_in_executor(
None, _handle_requirements, hass, platform, platform_path)
if not res:
return None
return platform
# pylint: disable=too-many-branches, too-many-statements, too-many-arguments
def from_config_dict(config: Dict[str, Any],
hass: Optional[core.HomeAssistant]=None,
config_dir: Optional[str]=None,
@@ -261,15 +334,56 @@ def from_config_dict(config: Dict[str, Any],
hass.config.config_dir = config_dir
mount_local_lib_path(config_dir)
@asyncio.coroutine
def _async_init_from_config_dict(future):
try:
re_hass = yield from async_from_config_dict(
config, hass, config_dir, enable_log, verbose, skip_pip,
log_rotate_days)
future.set_result(re_hass)
# pylint: disable=broad-except
except Exception as exc:
future.set_exception(exc)
# run task
future = asyncio.Future(loop=hass.loop)
hass.async_add_job(_async_init_from_config_dict(future))
hass.loop.run_until_complete(future)
return future.result()
@asyncio.coroutine
def async_from_config_dict(config: Dict[str, Any],
hass: core.HomeAssistant,
config_dir: Optional[str]=None,
enable_log: bool=True,
verbose: bool=False,
skip_pip: bool=False,
log_rotate_days: Any=None) \
-> Optional[core.HomeAssistant]:
"""Try to configure Home Assistant from a config dict.
Dynamically loads required components and its dependencies.
This method is a coroutine.
"""
hass.async_track_tasks()
setup_lock = hass.data.get('setup_lock')
if setup_lock is None:
setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop)
yield from setup_lock.acquire()
core_config = config.get(core.DOMAIN, {})
try:
conf_util.process_ha_core_config(hass, core_config)
yield from conf_util.async_process_ha_core_config(hass, core_config)
except vol.Invalid as ex:
log_exception(ex, 'homeassistant', core_config, hass)
async_log_exception(ex, 'homeassistant', core_config, hass)
return None
conf_util.process_ha_config_upgrade(hass)
yield from hass.loop.run_in_executor(
None, conf_util.process_ha_config_upgrade, hass)
if enable_log:
enable_logging(hass, verbose, log_rotate_days)
@@ -279,42 +393,50 @@ def from_config_dict(config: Dict[str, Any],
_LOGGER.warning('Skipping pip installation of required modules. '
'This may cause issues.')
_ensure_loader_prepared(hass)
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.
# Convert it to defaultdict so components can always have config dict
# Use OrderedDict in case original one was one.
# Convert values to dictionaries if they are None
config = defaultdict(
dict, {key: value or {} for key, value in config.items()})
new_config = OrderedDict()
for key, value in config.items():
new_config[key] = value or {}
config = new_config
# Filter out the repeating and common config section [homeassistant]
components = set(key.split(' ')[0] for key in config.keys()
if key != core.DOMAIN)
# Setup in a thread to avoid blocking
def component_setup():
"""Set up a component."""
if not core_components.setup(hass, config):
_LOGGER.error('Home Assistant core failed to initialize. '
'Further initialization aborted.')
return hass
# setup components
# pylint: disable=not-an-iterable
res = yield from core_components.async_setup(hass, config)
if not res:
_LOGGER.error('Home Assistant core failed to initialize. '
'Further initialization aborted.')
return hass
persistent_notification.setup(hass, config)
yield from persistent_notification.async_setup(hass, config)
_LOGGER.info('Home Assistant core initialized')
_LOGGER.info('Home Assistant core initialized')
# Give event decorators access to HASS
event_decorators.HASS = hass
service.HASS = hass
# Give event decorators access to HASS
event_decorators.HASS = hass
service.HASS = hass
# Setup the components
for domain in loader.load_order_components(components):
_setup_component(hass, domain, config)
# Setup the components
for domain in loader.load_order_components(components):
yield from _async_setup_component(hass, domain, config)
hass.loop.run_until_complete(
hass.loop.run_in_executor(None, component_setup)
)
setup_lock.release()
yield from hass.async_stop_track_tasks()
async_register_signal_handling(hass)
return hass
@@ -331,40 +453,78 @@ def from_config_file(config_path: str,
if hass is None:
hass = core.HomeAssistant()
@asyncio.coroutine
def _async_init_from_config_file(future):
try:
re_hass = yield from async_from_config_file(
config_path, hass, verbose, skip_pip, log_rotate_days)
future.set_result(re_hass)
# pylint: disable=broad-except
except Exception as exc:
future.set_exception(exc)
# run task
future = asyncio.Future(loop=hass.loop)
hass.loop.create_task(_async_init_from_config_file(future))
hass.loop.run_until_complete(future)
return future.result()
@asyncio.coroutine
def async_from_config_file(config_path: str,
hass: core.HomeAssistant,
verbose: bool=False,
skip_pip: bool=True,
log_rotate_days: Any=None):
"""Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter.
This method is a coroutine.
"""
# Set config dir to directory holding config file
config_dir = os.path.abspath(os.path.dirname(config_path))
hass.config.config_dir = config_dir
mount_local_lib_path(config_dir)
yield from hass.loop.run_in_executor(
None, mount_local_lib_path, config_dir)
enable_logging(hass, verbose, log_rotate_days)
try:
config_dict = conf_util.load_yaml_config_file(config_path)
config_dict = yield from hass.loop.run_in_executor(
None, conf_util.load_yaml_config_file, config_path)
except HomeAssistantError:
return None
finally:
clear_secret_cache()
return from_config_dict(config_dict, hass, enable_log=False,
skip_pip=skip_pip)
hass = yield from async_from_config_dict(
config_dict, hass, enable_log=False, skip_pip=skip_pip)
return hass
def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
log_rotate_days=None) -> None:
"""Setup the logging."""
"""Setup the logging.
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)
logging.getLogger("urllib3").setLevel(logging.WARNING)
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
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',
@@ -377,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)
@@ -394,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:
@@ -406,43 +572,62 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
'Unable to setup error log %s (access denied)', err_log_path)
def _ensure_loader_prepared(hass: core.HomeAssistant) -> None:
"""Ensure Home Assistant loader is prepared."""
if not loader.PREPARED:
loader.prepare(hass)
def log_exception(ex, domain, config, hass=None):
def log_exception(ex, domain, config, hass):
"""Generate log exception for config validation."""
run_callback_threadsafe(
hass.loop, async_log_exception, ex, domain, config, hass).result()
@core.callback
def _async_persistent_notification(hass: core.HomeAssistant, component: str,
link: Optional[bool]=False):
"""Print a persistent notification.
This method must be run in the event loop.
"""
_PERSISTENT_ERRORS[component] = _PERSISTENT_ERRORS.get(component) or link
_lst = [HA_COMPONENT_URL.format(name.replace('_', '-'), name)
if link else name for name, link in _PERSISTENT_ERRORS.items()]
message = ('The following components and platforms could not be set up:\n'
'* ' + '\n* '.join(list(_lst)) + '\nPlease check your config')
persistent_notification.async_create(
hass, message, 'Invalid config', 'invalid_config')
@core.callback
def async_log_exception(ex, domain, config, hass):
"""Generate log exception for config validation.
This method must be run in the event loop.
"""
message = 'Invalid config for [{}]: '.format(domain)
if hass is not None:
_PERSISTENT_VALIDATION.add(domain)
message = ('The following platforms contain invalid configuration: ' +
', '.join(list(_PERSISTENT_VALIDATION)) +
' (please check your configuration)')
persistent_notification.create(
hass, message, 'Invalid config', 'invalid_config')
_async_persistent_notification(hass, domain, True)
if 'extra keys not allowed' in ex.error_message:
message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\
.format(ex.path[-1], domain, domain,
'->'.join('%s' % m for m in ex.path))
'->'.join(str(m) for m in ex.path))
else:
message += '{}.'.format(humanize_error(config, ex))
if hasattr(config, '__line__'):
message += " (See {}:{})".format(
config.__config_file__, config.__line__ or '?')
domain_config = config.get(domain, config)
message += " (See {}, line {}). ".format(
getattr(domain_config, '__config_file__', '?'),
getattr(domain_config, '__line__', '?'))
if domain != 'homeassistant':
message += (' Please check the docs at '
message += ('Please check the docs at '
'https://home-assistant.io/components/{}/'.format(domain))
_LOGGER.error(message)
def mount_local_lib_path(config_dir: str) -> str:
"""Add local library to Python Path."""
"""Add local library to Python Path.
Async friendly.
"""
deps_dir = os.path.join(config_dir, 'deps')
if deps_dir not in sys.path:
sys.path.insert(0, os.path.join(config_dir, 'deps'))
+67 -16
View File
@@ -7,18 +7,24 @@ Component design guidelines:
format "<DOMAIN>.<OBJECT_ID>".
- Each component should publish services only under its own domain.
"""
import asyncio
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):
@@ -74,14 +80,31 @@ 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)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Setup general services related to Home Assistant."""
def handle_turn_service(service):
@asyncio.coroutine
def async_handle_turn_service(service):
"""Method to handle calls to homeassistant.turn_on/off."""
entity_ids = extract_entity_ids(hass, service)
@@ -96,6 +119,8 @@ def setup(hass, config):
by_domain = it.groupby(sorted(entity_ids),
lambda item: ha.split_entity_id(item)[0])
tasks = []
for domain, ent_ids in by_domain:
# We want to block for all calls and only return when all calls
# have been processed. If a service does not exist it causes a 10
@@ -111,27 +136,53 @@ def setup(hass, config):
# ent_ids is a generator, convert it to a list.
data[ATTR_ENTITY_ID] = list(ent_ids)
hass.services.call(domain, service.service, data, blocking)
tasks.append(hass.services.async_call(
domain, service.service, data, blocking))
hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service)
hass.services.register(ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service)
hass.services.register(ha.DOMAIN, SERVICE_TOGGLE, handle_turn_service)
yield from asyncio.wait(tasks, loop=hass.loop)
def handle_reload_config(call):
"""Service handler for reloading core config."""
from homeassistant.exceptions import HomeAssistantError
from homeassistant import config as conf_util
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service)
@asyncio.coroutine
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:
path = conf_util.find_config_file(hass.config.config_dir)
conf = conf_util.load_yaml_config_file(path)
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:
_LOGGER.error(err)
return
conf_util.process_ha_core_config(hass, conf.get(ha.DOMAIN) or {})
yield from conf_util.async_process_ha_core_config(
hass, conf.get(ha.DOMAIN) or {})
hass.services.register(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG,
handle_reload_config)
hass.services.async_register(
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config)
return True
@@ -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."""
@@ -39,11 +39,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
add_devices([AlarmDotCom(hass, name, code, username, password)])
add_devices([AlarmDotCom(hass, name, code, username, password)], True)
# pylint: disable=too-many-arguments, too-many-instance-attributes
# pylint: disable=abstract-method
class AlarmDotCom(alarm.AlarmControlPanel):
"""Represent an Alarm.com status."""
@@ -56,11 +54,11 @@ class AlarmDotCom(alarm.AlarmControlPanel):
self._code = str(code) if code else None
self._username = username
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
@property
def name(self):
@@ -75,11 +73,11 @@ class AlarmDotCom(alarm.AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
if self._alarm.state == 'Disarmed':
if self._state == 'Disarmed':
return STATE_ALARM_DISARMED
elif self._alarm.state == 'Armed Stay':
elif self._state == 'Armed Stay':
return STATE_ALARM_ARMED_HOME
elif self._alarm.state == 'Armed Away':
elif self._state == 'Armed Away':
return STATE_ALARM_ARMED_AWAY
else:
return STATE_UNKNOWN
@@ -4,23 +4,20 @@ Support for Concord232 alarm control panels.
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
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PORT,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_UNKNOWN)
CONF_HOST, CONF_NAME, CONF_PORT, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
import requests
import voluptuous as vol
REQUIREMENTS = ['concord232==0.14']
_LOGGER = logging.getLogger(__name__)
@@ -29,17 +26,17 @@ DEFAULT_HOST = 'localhost'
DEFAULT_NAME = 'CONCORD232'
DEFAULT_PORT = 5007
SCAN_INTERVAL = timedelta(seconds=1)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
})
SCAN_INTERVAL = 1
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup concord232 platform."""
"""Set up the Concord232 alarm control panel platform."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
@@ -49,7 +46,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
try:
add_devices([Concord232Alarm(hass, url, name)])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to Concord232: %s', str(ex))
_LOGGER.error("Unable to connect to Concord232: %s", str(ex))
return False
@@ -57,7 +54,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
"""Represents the Concord232-based alarm panel."""
def __init__(self, hass, url, name):
"""Initalize the concord232 alarm panel."""
"""Initialize the Concord232 alarm panel."""
from concord232 import client as concord232_client
self._state = STATE_UNKNOWN
@@ -68,18 +65,13 @@ class Concord232Alarm(alarm.AlarmControlPanel):
try:
client = concord232_client.Client(self._url)
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to Concord232: %s', str(ex))
_LOGGER.error("Unable to connect to Concord232: %s", str(ex))
self._alarm = client
self._alarm.partitions = self._alarm.list_partitions()
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."""
@@ -100,16 +92,16 @@ class Concord232Alarm(alarm.AlarmControlPanel):
try:
part = self._alarm.list_partitions()[0]
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to %(host)s: %(reason)s',
_LOGGER.error("Unable to connect to %(host)s: %(reason)s",
dict(host=self._url, reason=ex))
newstate = STATE_UNKNOWN
except IndexError:
_LOGGER.error('concord232 reports no partitions')
_LOGGER.error("Concord232 reports no partitions")
newstate = STATE_UNKNOWN
if part['arming_level'] == "Off":
if part['arming_level'] == 'Off':
newstate = STATE_ALARM_DISARMED
elif "Home" in part['arming_level']:
elif 'Home' in part['arming_level']:
newstate = STATE_ALARM_ARMED_HOME
else:
newstate = STATE_ALARM_ARMED_AWAY
@@ -130,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()
@@ -4,26 +4,48 @@ Support for Envisalink-based alarm control panels (Honeywell/DSC).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.envisalink/
"""
from os import path
import logging
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.envisalink import (EVL_CONTROLLER,
EnvisalinkDevice,
PARTITION_SCHEMA,
CONF_CODE,
CONF_PANIC,
CONF_PARTITIONNAME,
SIGNAL_PARTITION_UPDATE,
SIGNAL_KEYPAD_UPDATE)
import homeassistant.helpers.config_validation as cv
from homeassistant.config import load_yaml_config_file
from homeassistant.components.envisalink import (
EVL_CONTROLLER, EnvisalinkDevice, PARTITION_SCHEMA, CONF_CODE, CONF_PANIC,
CONF_PARTITIONNAME, SIGNAL_PARTITION_UPDATE, SIGNAL_KEYPAD_UPDATE)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN, STATE_ALARM_TRIGGERED)
STATE_UNKNOWN, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, ATTR_ENTITY_ID)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['envisalink']
_LOGGER = logging.getLogger(__name__)
DEVICES = []
SERVICE_ALARM_KEYPRESS = 'envisalink_alarm_keypress'
ATTR_KEYPRESS = 'keypress'
ALARM_KEYPRESS_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_KEYPRESS): cv.string
})
def alarm_keypress_handler(service):
"""Map services to methods on Alarm."""
entity_ids = service.data.get(ATTR_ENTITY_ID)
keypress = service.data.get(ATTR_KEYPRESS)
_target_devices = [device for device in DEVICES
if device.entity_id in entity_ids]
for device in _target_devices:
EnvisalinkAlarm.alarm_keypress(device, keypress)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Perform the setup for Envisalink alarm panels."""
_configured_partitions = discovery_info['partitions']
_code = discovery_info[CONF_CODE]
@@ -38,74 +60,105 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
_panic_type,
EVL_CONTROLLER.alarm_state['partition'][part_num],
EVL_CONTROLLER)
add_devices_callback([_device])
DEVICES.append(_device)
add_devices(DEVICES)
# Register Envisalink specific services
descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))
hass.services.register(alarm.DOMAIN, SERVICE_ALARM_KEYPRESS,
alarm_keypress_handler,
descriptions.get(SERVICE_ALARM_KEYPRESS),
schema=ALARM_KEYPRESS_SCHEMA)
return True
class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
"""Represents the Envisalink-based alarm panel."""
"""Representation of an Envisalink-based alarm panel."""
# pylint: disable=too-many-arguments
def __init__(self, partition_number, alarm_name,
code, panic_type, info, controller):
def __init__(self, partition_number, alarm_name, code, panic_type, info,
controller):
"""Initialize the alarm panel."""
from pydispatch import dispatcher
self._partition_number = partition_number
self._code = code
self._panic_type = panic_type
_LOGGER.debug('Setting up alarm: ' + alarm_name)
_LOGGER.debug("Setting up alarm: %s", alarm_name)
EnvisalinkDevice.__init__(self, alarm_name, info, controller)
dispatcher.connect(self._update_callback,
signal=SIGNAL_PARTITION_UPDATE,
sender=dispatcher.Any)
dispatcher.connect(self._update_callback,
signal=SIGNAL_KEYPAD_UPDATE,
sender=dispatcher.Any)
dispatcher.connect(
self._update_callback, signal=SIGNAL_PARTITION_UPDATE,
sender=dispatcher.Any)
dispatcher.connect(
self._update_callback, signal=SIGNAL_KEYPAD_UPDATE,
sender=dispatcher.Any)
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):
"""The characters if code is defined."""
return self._code
"""Regex for code format or None if no code is required."""
if self._code:
return None
else:
return '^\\d{4,6}$'
@property
def state(self):
"""Return the state of the device."""
state = STATE_UNKNOWN
if self._info['status']['alarm']:
return STATE_ALARM_TRIGGERED
state = STATE_ALARM_TRIGGERED
elif self._info['status']['armed_away']:
return STATE_ALARM_ARMED_AWAY
state = STATE_ALARM_ARMED_AWAY
elif self._info['status']['armed_stay']:
return STATE_ALARM_ARMED_HOME
state = STATE_ALARM_ARMED_HOME
elif self._info['status']['exit_delay']:
state = STATE_ALARM_PENDING
elif self._info['status']['entry_delay']:
state = STATE_ALARM_PENDING
elif self._info['status']['alpha']:
return STATE_ALARM_DISARMED
else:
return STATE_UNKNOWN
state = STATE_ALARM_DISARMED
return state
def alarm_disarm(self, code=None):
"""Send disarm command."""
if self._code:
if code:
EVL_CONTROLLER.disarm_partition(str(code),
self._partition_number)
else:
EVL_CONTROLLER.disarm_partition(str(self._code),
self._partition_number)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
if self._code:
if code:
EVL_CONTROLLER.arm_stay_partition(str(code),
self._partition_number)
else:
EVL_CONTROLLER.arm_stay_partition(str(self._code),
self._partition_number)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
if self._code:
if code:
EVL_CONTROLLER.arm_away_partition(str(code),
self._partition_number)
else:
EVL_CONTROLLER.arm_away_partition(str(self._code),
self._partition_number)
def alarm_trigger(self, code=None):
"""Alarm trigger command. Will be used to trigger a panic alarm."""
if self._code:
EVL_CONTROLLER.panic_alarm(self._panic_type)
EVL_CONTROLLER.panic_alarm(self._panic_type)
def alarm_keypress(self, keypress=None):
"""Send custom keypress."""
if keypress:
EVL_CONTROLLER.keypresses_to_partition(self._partition_number,
keypress)
@@ -50,8 +50,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
)])
# pylint: disable=too-many-arguments, too-many-instance-attributes
# pylint: disable=abstract-method
class ManualAlarm(alarm.AlarmControlPanel):
"""
Represents an alarm status.
@@ -118,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."""
@@ -127,11 +125,11 @@ 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(
self._hass, self.update_ha_state,
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
def alarm_arm_away(self, code=None):
@@ -141,11 +139,11 @@ 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(
self._hass, self.update_ha_state,
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
def alarm_trigger(self, code=None):
@@ -153,15 +151,15 @@ 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(
self._hass, self.update_ha_state,
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
track_point_in_time(
self._hass, self.update_ha_state,
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time + self._trigger_time)
def _validate_code(self, code, state):
@@ -55,8 +55,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
config.get(CONF_CODE))])
# pylint: disable=too-many-arguments, too-many-instance-attributes
# pylint: disable=abstract-method
class MqttAlarm(alarm.AlarmControlPanel):
"""Representation of a MQTT alarm status."""
@@ -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:
@@ -117,12 +114,8 @@ class NX584Alarm(alarm.AlarmControlPanel):
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._alarm.arm('home')
self._alarm.arm('stay')
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()
self._alarm.arm('exit')
@@ -41,3 +41,14 @@ alarm_trigger:
code:
description: An optional code to trigger the alarm control panel with
example: 1234
envisalink_alarm_keypress:
description: Send custom keypresses to the alarm
fields:
entity_id:
description: Name of the alarm control panel to trigger
example: 'alarm_control_panel.downstairs'
keypress:
description: 'String to send to the alarm panel (1-6 characters)'
example: '*71'
@@ -41,7 +41,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices([SimpliSafeAlarm(name, username, password, code)])
# pylint: disable=abstract-method
class SimpliSafeAlarm(alarm.AlarmControlPanel):
"""Representation a SimpliSafe alarm."""
@@ -62,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."""
@@ -105,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."""
@@ -113,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."""
@@ -121,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."""
@@ -28,7 +28,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(alarms)
# pylint: disable=abstract-method
class VerisureAlarm(alarm.AlarmControlPanel):
"""Represent a Verisure alarm status."""
@@ -85,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()
@@ -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()
}
+275
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()
+26 -22
View File
@@ -4,6 +4,7 @@ Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import copy
import enum
import logging
@@ -12,6 +13,7 @@ from datetime import datetime
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import HTTP_BAD_REQUEST
from homeassistant.helpers import template, script, config_validation as cv
from homeassistant.components.http import HomeAssistantView
@@ -20,7 +22,7 @@ import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
INTENTS_API_ENDPOINT = '/api/alexa'
FLASH_BRIEFINGS_API_ENDPOINT = '/api/alexa/flash_briefings/<briefing_id>'
FLASH_BRIEFINGS_API_ENDPOINT = '/api/alexa/flash_briefings/{briefing_id}'
CONF_ACTION = 'action'
CONF_CARD = 'card'
@@ -102,8 +104,8 @@ def setup(hass, config):
intents = config[DOMAIN].get(CONF_INTENTS, {})
flash_briefings = config[DOMAIN].get(CONF_FLASH_BRIEFINGS, {})
hass.wsgi.register_view(AlexaIntentsView(hass, intents))
hass.wsgi.register_view(AlexaFlashBriefingView(hass, flash_briefings))
hass.http.register_view(AlexaIntentsView(hass, intents))
hass.http.register_view(AlexaFlashBriefingView(hass, flash_briefings))
return True
@@ -116,7 +118,7 @@ class AlexaIntentsView(HomeAssistantView):
def __init__(self, hass, intents):
"""Initialize Alexa view."""
super().__init__(hass)
super().__init__()
intents = copy.deepcopy(intents)
template.attach(hass, intents)
@@ -128,9 +130,10 @@ class AlexaIntentsView(HomeAssistantView):
self.intents = intents
@asyncio.coroutine
def post(self, request):
"""Handle Alexa."""
data = request.json
data = yield from request.json()
_LOGGER.debug('Received Alexa request: %s', data)
@@ -147,7 +150,7 @@ class AlexaIntentsView(HomeAssistantView):
return None
intent = req.get('intent')
response = AlexaResponse(self.hass, intent)
response = AlexaResponse(request.app['hass'], intent)
if req_type == 'LaunchRequest':
response.add_speech(
@@ -176,7 +179,7 @@ class AlexaIntentsView(HomeAssistantView):
action = config.get(CONF_ACTION)
if action is not None:
action.run(response.variables)
yield from action.async_run(response.variables)
# pylint: disable=unsubscriptable-object
if speech is not None:
@@ -200,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."""
@@ -218,8 +222,8 @@ class AlexaResponse(object):
self.card = card
return
card["title"] = title.render(self.variables)
card["content"] = content.render(self.variables)
card["title"] = title.async_render(self.variables)
card["content"] = content.async_render(self.variables)
self.card = card
def add_speech(self, speech_type, text):
@@ -229,7 +233,7 @@ class AlexaResponse(object):
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
if isinstance(text, template.Template):
text = text.render(self.variables)
text = text.async_render(self.variables)
self.speech = {
'type': speech_type.value,
@@ -244,7 +248,7 @@ class AlexaResponse(object):
self.reprompt = {
'type': speech_type.value,
key: text.render(self.variables)
key: text.async_render(self.variables)
}
def as_dict(self):
@@ -279,11 +283,11 @@ class AlexaFlashBriefingView(HomeAssistantView):
def __init__(self, hass, flash_briefings):
"""Initialize Alexa view."""
super().__init__(hass)
super().__init__()
self.flash_briefings = copy.deepcopy(flash_briefings)
template.attach(hass, self.flash_briefings)
# pylint: disable=too-many-branches
@callback
def get(self, request, briefing_id):
"""Handle Alexa Flash Briefing request."""
_LOGGER.debug('Received Alexa flash briefing request for: %s',
@@ -292,7 +296,7 @@ class AlexaFlashBriefingView(HomeAssistantView):
if self.flash_briefings.get(briefing_id) is None:
err = 'No configured Alexa flash briefing was found for: %s'
_LOGGER.error(err, briefing_id)
return self.Response(status=404)
return b'', 404
briefing = []
@@ -300,13 +304,13 @@ class AlexaFlashBriefingView(HomeAssistantView):
output = {}
if item.get(CONF_TITLE) is not None:
if isinstance(item.get(CONF_TITLE), template.Template):
output[ATTR_TITLE_TEXT] = item[CONF_TITLE].render()
output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render()
else:
output[ATTR_TITLE_TEXT] = item.get(CONF_TITLE)
if item.get(CONF_TEXT) is not None:
if isinstance(item.get(CONF_TEXT), template.Template):
output[ATTR_MAIN_TEXT] = item[CONF_TEXT].render()
output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render()
else:
output[ATTR_MAIN_TEXT] = item.get(CONF_TEXT)
@@ -315,7 +319,7 @@ class AlexaFlashBriefingView(HomeAssistantView):
if item.get(CONF_AUDIO) is not None:
if isinstance(item.get(CONF_AUDIO), template.Template):
output[ATTR_STREAM_URL] = item[CONF_AUDIO].render()
output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render()
else:
output[ATTR_STREAM_URL] = item.get(CONF_AUDIO)
@@ -323,7 +327,7 @@ class AlexaFlashBriefingView(HomeAssistantView):
if isinstance(item.get(CONF_DISPLAY_URL),
template.Template):
output[ATTR_REDIRECTION_URL] = \
item[CONF_DISPLAY_URL].render()
item[CONF_DISPLAY_URL].async_render()
else:
output[ATTR_REDIRECTION_URL] = item.get(CONF_DISPLAY_URL)
+133 -86
View File
@@ -7,7 +7,9 @@ https://home-assistant.io/developers/api/
import asyncio
import json
import logging
import queue
from aiohttp import web
import async_timeout
import homeassistant.core as ha
import homeassistant.remote as rem
@@ -21,7 +23,7 @@ from homeassistant.const import (
URL_API_STATES, URL_API_STATES_ENTITY, URL_API_STREAM, URL_API_TEMPLATE,
__version__)
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import TrackStates
from homeassistant.helpers.state import AsyncTrackStates
from homeassistant.helpers import template
from homeassistant.components.http import HomeAssistantView
@@ -36,20 +38,20 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
"""Register the API with the HTTP interface."""
hass.wsgi.register_view(APIStatusView)
hass.wsgi.register_view(APIEventStream)
hass.wsgi.register_view(APIConfigView)
hass.wsgi.register_view(APIDiscoveryView)
hass.wsgi.register_view(APIStatesView)
hass.wsgi.register_view(APIEntityStateView)
hass.wsgi.register_view(APIEventListenersView)
hass.wsgi.register_view(APIEventView)
hass.wsgi.register_view(APIServicesView)
hass.wsgi.register_view(APIDomainServicesView)
hass.wsgi.register_view(APIEventForwardingView)
hass.wsgi.register_view(APIComponentsView)
hass.wsgi.register_view(APIErrorLogView)
hass.wsgi.register_view(APITemplateView)
hass.http.register_view(APIStatusView)
hass.http.register_view(APIEventStream)
hass.http.register_view(APIConfigView)
hass.http.register_view(APIDiscoveryView)
hass.http.register_view(APIStatesView)
hass.http.register_view(APIEntityStateView)
hass.http.register_view(APIEventListenersView)
hass.http.register_view(APIEventView)
hass.http.register_view(APIServicesView)
hass.http.register_view(APIDomainServicesView)
hass.http.register_view(APIEventForwardingView)
hass.http.register_view(APIComponentsView)
hass.http.register_view(APIErrorLogView)
hass.http.register_view(APITemplateView)
return True
@@ -60,6 +62,7 @@ class APIStatusView(HomeAssistantView):
url = URL_API
name = "api:status"
@ha.callback
def get(self, request):
"""Retrieve if API is running."""
return self.json_message('API running.')
@@ -71,12 +74,15 @@ class APIEventStream(HomeAssistantView):
url = URL_API_STREAM
name = "api:stream"
@asyncio.coroutine
def get(self, request):
"""Provide a streaming interface for the event bus."""
# pylint: disable=no-self-use
hass = request.app['hass']
stop_obj = object()
to_write = queue.Queue()
to_write = asyncio.Queue(loop=hass.loop)
restrict = request.args.get('restrict')
restrict = request.GET.get('restrict')
if restrict:
restrict = restrict.split(',') + [EVENT_HOMEASSISTANT_STOP]
@@ -96,38 +102,43 @@ class APIEventStream(HomeAssistantView):
else:
data = json.dumps(event, cls=rem.JSONEncoder)
to_write.put(data)
yield from to_write.put(data)
def stream():
"""Stream events to response."""
unsub_stream = self.hass.bus.listen(MATCH_ALL, forward_events)
response = web.StreamResponse()
response.content_type = 'text/event-stream'
yield from response.prepare(request)
try:
_LOGGER.debug('STREAM %s ATTACHED', id(stop_obj))
unsub_stream = hass.bus.async_listen(MATCH_ALL, forward_events)
# Fire off one message so browsers fire open event right away
to_write.put(STREAM_PING_PAYLOAD)
try:
_LOGGER.debug('STREAM %s ATTACHED', id(stop_obj))
while True:
try:
payload = to_write.get(timeout=STREAM_PING_INTERVAL)
# Fire off one message so browsers fire open event right away
yield from to_write.put(STREAM_PING_PAYLOAD)
if payload is stop_obj:
break
while True:
try:
with async_timeout.timeout(STREAM_PING_INTERVAL,
loop=hass.loop):
payload = yield from to_write.get()
msg = "data: {}\n\n".format(payload)
_LOGGER.debug('STREAM %s WRITING %s', id(stop_obj),
msg.strip())
yield msg.encode("UTF-8")
except queue.Empty:
to_write.put(STREAM_PING_PAYLOAD)
except GeneratorExit:
if payload is stop_obj:
break
finally:
_LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj))
unsub_stream()
return self.Response(stream(), mimetype='text/event-stream')
msg = "data: {}\n\n".format(payload)
_LOGGER.debug('STREAM %s WRITING %s', id(stop_obj),
msg.strip())
response.write(msg.encode("UTF-8"))
yield from response.drain()
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()
class APIConfigView(HomeAssistantView):
@@ -136,9 +147,10 @@ class APIConfigView(HomeAssistantView):
url = URL_API_CONFIG
name = "api:config"
@ha.callback
def get(self, request):
"""Get current configuration."""
return self.json(self.hass.config.as_dict())
return self.json(request.app['hass'].config.as_dict())
class APIDiscoveryView(HomeAssistantView):
@@ -148,12 +160,14 @@ class APIDiscoveryView(HomeAssistantView):
url = URL_API_DISCOVERY_INFO
name = "api:discovery"
@ha.callback
def get(self, request):
"""Get discovery info."""
needs_auth = self.hass.config.api.api_password is not None
hass = request.app['hass']
needs_auth = hass.config.api.api_password is not None
return self.json({
'base_url': self.hass.config.api.base_url,
'location_name': self.hass.config.location_name,
'base_url': hass.config.api.base_url,
'location_name': hass.config.location_name,
'requires_api_password': needs_auth,
'version': __version__
})
@@ -165,53 +179,62 @@ class APIStatesView(HomeAssistantView):
url = URL_API_STATES
name = "api:states"
@ha.callback
def get(self, request):
"""Get current states."""
return self.json(self.hass.states.all())
return self.json(request.app['hass'].states.async_all())
class APIEntityStateView(HomeAssistantView):
"""View to handle EntityState requests."""
url = "/api/states/<entity(exist=False):entity_id>"
url = "/api/states/{entity_id}"
name = "api:entity-state"
@ha.callback
def get(self, request, entity_id):
"""Retrieve state of entity."""
state = self.hass.states.get(entity_id)
state = request.app['hass'].states.get(entity_id)
if state:
return self.json(state)
else:
return self.json_message('Entity not found', HTTP_NOT_FOUND)
@asyncio.coroutine
def post(self, request, entity_id):
"""Update state of entity."""
hass = request.app['hass']
try:
new_state = request.json['state']
except KeyError:
data = yield from request.json()
except ValueError:
return self.json_message('Invalid JSON specified',
HTTP_BAD_REQUEST)
new_state = data.get('state')
if not new_state:
return self.json_message('No state specified', HTTP_BAD_REQUEST)
attributes = request.json.get('attributes')
force_update = request.json.get('force_update', False)
attributes = data.get('attributes')
force_update = data.get('force_update', False)
is_new_state = self.hass.states.get(entity_id) is None
is_new_state = hass.states.get(entity_id) is None
# Write state
self.hass.states.set(entity_id, new_state, attributes, force_update)
hass.states.async_set(entity_id, new_state, attributes, force_update)
# Read the state back for our response
resp = self.json(self.hass.states.get(entity_id))
if is_new_state:
resp.status_code = HTTP_CREATED
status_code = HTTP_CREATED if is_new_state else 200
resp = self.json(hass.states.get(entity_id), status_code)
resp.headers.add('Location', URL_API_STATES_ENTITY.format(entity_id))
return resp
@ha.callback
def delete(self, request, entity_id):
"""Remove entity."""
if self.hass.states.remove(entity_id):
if request.app['hass'].states.async_remove(entity_id):
return self.json_message('Entity removed')
else:
return self.json_message('Entity not found', HTTP_NOT_FOUND)
@@ -223,20 +246,23 @@ class APIEventListenersView(HomeAssistantView):
url = URL_API_EVENTS
name = "api:event-listeners"
@ha.callback
def get(self, request):
"""Get event listeners."""
return self.json(events_json(self.hass))
return self.json(async_events_json(request.app['hass']))
class APIEventView(HomeAssistantView):
"""View to handle Event requests."""
url = '/api/events/<event_type>'
url = '/api/events/{event_type}'
name = "api:event"
@asyncio.coroutine
def post(self, request, event_type):
"""Fire events."""
event_data = request.json
body = yield from request.text()
event_data = json.loads(body) if body else None
if event_data is not None and not isinstance(event_data, dict):
return self.json_message('Event data should be a JSON object',
@@ -251,7 +277,8 @@ class APIEventView(HomeAssistantView):
if state:
event_data[key] = state
self.hass.bus.fire(event_type, event_data, ha.EventOrigin.remote)
request.app['hass'].bus.async_fire(event_type, event_data,
ha.EventOrigin.remote)
return self.json_message("Event {} fired.".format(event_type))
@@ -262,24 +289,30 @@ class APIServicesView(HomeAssistantView):
url = URL_API_SERVICES
name = "api:services"
@ha.callback
def get(self, request):
"""Get registered services."""
return self.json(services_json(self.hass))
return self.json(async_services_json(request.app['hass']))
class APIDomainServicesView(HomeAssistantView):
"""View to handle DomainServices requests."""
url = "/api/services/<domain>/<service>"
url = "/api/services/{domain}/{service}"
name = "api:domain-services"
@asyncio.coroutine
def post(self, request, domain, service):
"""Call a service.
Returns a list of changed states.
"""
with TrackStates(self.hass) as changed_states:
self.hass.services.call(domain, service, request.json, True)
hass = request.app['hass']
body = yield from request.text()
data = json.loads(body) if body else None
with AsyncTrackStates(hass) as changed_states:
yield from hass.services.async_call(domain, service, data, True)
return self.json(changed_states)
@@ -291,11 +324,15 @@ class APIEventForwardingView(HomeAssistantView):
name = "api:event-forward"
event_forwarder = None
@asyncio.coroutine
def post(self, request):
"""Setup an event forwarder."""
data = request.json
if data is None:
hass = request.app['hass']
try:
data = yield from request.json()
except ValueError:
return self.json_message("No data received.", HTTP_BAD_REQUEST)
try:
host = data['host']
api_password = data['api_password']
@@ -311,21 +348,25 @@ class APIEventForwardingView(HomeAssistantView):
api = rem.API(host, api_password, port)
if not api.validate_api():
valid = yield from hass.loop.run_in_executor(
None, api.validate_api)
if not valid:
return self.json_message("Unable to validate API.",
HTTP_UNPROCESSABLE_ENTITY)
if self.event_forwarder is None:
self.event_forwarder = rem.EventForwarder(self.hass)
self.event_forwarder = rem.EventForwarder(hass)
self.event_forwarder.connect(api)
self.event_forwarder.async_connect(api)
return self.json_message("Event forwarding setup.")
@asyncio.coroutine
def delete(self, request):
"""Remove event forwarer."""
data = request.json
if data is None:
"""Remove event forwarder."""
try:
data = yield from request.json()
except ValueError:
return self.json_message("No data received.", HTTP_BAD_REQUEST)
try:
@@ -342,7 +383,7 @@ class APIEventForwardingView(HomeAssistantView):
if self.event_forwarder is not None:
api = rem.API(host, None, port)
self.event_forwarder.disconnect(api)
self.event_forwarder.async_disconnect(api)
return self.json_message("Event forwarding cancelled.")
@@ -353,9 +394,10 @@ class APIComponentsView(HomeAssistantView):
url = URL_API_COMPONENTS
name = "api:components"
@ha.callback
def get(self, request):
"""Get current loaded components."""
return self.json(self.hass.config.components)
return self.json(request.app['hass'].config.components)
class APIErrorLogView(HomeAssistantView):
@@ -364,9 +406,12 @@ class APIErrorLogView(HomeAssistantView):
url = URL_API_ERROR_LOG
name = "api:error-log"
@asyncio.coroutine
def get(self, request):
"""Serve error log."""
return self.file(request, self.hass.config.path(ERROR_LOG_FILENAME))
resp = yield from self.file(
request, request.app['hass'].config.path(ERROR_LOG_FILENAME))
return resp
class APITemplateView(HomeAssistantView):
@@ -375,23 +420,25 @@ class APITemplateView(HomeAssistantView):
url = URL_API_TEMPLATE
name = "api:template"
@asyncio.coroutine
def post(self, request):
"""Render a template."""
try:
tpl = template.Template(request.json['template'], self.hass)
return tpl.render(request.json.get('variables'))
except TemplateError as ex:
data = yield from request.json()
tpl = template.Template(data['template'], request.app['hass'])
return tpl.async_render(data.get('variables'))
except (ValueError, TemplateError) as ex:
return self.json_message('Error rendering template: {}'.format(ex),
HTTP_BAD_REQUEST)
def services_json(hass):
def async_services_json(hass):
"""Generate services data to JSONify."""
return [{"domain": key, "services": value}
for key, value in hass.services.services.items()]
for key, value in hass.services.async_services().items()]
def events_json(hass):
def async_events_json(hass):
"""Generate event data to JSONify."""
return [{"event": key, "listener_count": value}
for key, value in hass.bus.listeners.items()]
for key, value in hass.bus.async_listeners().items()]
+172
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,
}
+50 -40
View File
@@ -11,8 +11,7 @@ import os
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.bootstrap import async_prepare_setup_platform
from homeassistant import config as conf_util
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
@@ -25,7 +24,6 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.loader import get_platform
from homeassistant.util.dt import utcnow
import homeassistant.helpers.config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
DOMAIN = 'automation'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
@@ -68,6 +66,7 @@ def _platform_validator(config):
return getattr(platform, 'TRIGGER_SCHEMA')(config)
_TRIGGER_SCHEMA = vol.All(
cv.ensure_list,
[
@@ -144,42 +143,56 @@ def reload(hass):
hass.services.call(DOMAIN, SERVICE_RELOAD)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Setup the automation."""
component = EntityComponent(_LOGGER, DOMAIN, hass,
group_name=GROUP_NAME_ALL_AUTOMATIONS)
success = run_coroutine_threadsafe(
_async_process_config(hass, config, component), hass.loop).result()
success = yield from _async_process_config(hass, config, component)
if not success:
return False
descriptions = conf_util.load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
descriptions = yield from hass.loop.run_in_executor(
None, conf_util.load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml')
)
@callback
@asyncio.coroutine
def trigger_service_handler(service_call):
"""Handle automation triggers."""
tasks = []
for entity in component.async_extract_from_service(service_call):
hass.loop.create_task(entity.async_trigger(
tasks.append(entity.async_trigger(
service_call.data.get(ATTR_VARIABLES), True))
@callback
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def turn_onoff_service_handler(service_call):
"""Handle automation turn on/off service calls."""
tasks = []
method = 'async_{}'.format(service_call.service)
for entity in component.async_extract_from_service(service_call):
hass.loop.create_task(getattr(entity, method)())
tasks.append(getattr(entity, method)())
@callback
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def toggle_service_handler(service_call):
"""Handle automation toggle service calls."""
tasks = []
for entity in component.async_extract_from_service(service_call):
if entity.is_on:
hass.loop.create_task(entity.async_turn_off())
tasks.append(entity.async_turn_off())
else:
hass.loop.create_task(entity.async_turn_on())
tasks.append(entity.async_turn_on())
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def reload_service_handler(service_call):
@@ -187,24 +200,24 @@ def setup(hass, config):
conf = yield from component.async_prepare_reload()
if conf is None:
return
hass.loop.create_task(_async_process_config(hass, conf, component))
yield from _async_process_config(hass, conf, component)
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service_handler,
descriptions.get(SERVICE_TRIGGER),
schema=TRIGGER_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TRIGGER, trigger_service_handler,
descriptions.get(SERVICE_TRIGGER), schema=TRIGGER_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_RELOAD, reload_service_handler,
descriptions.get(SERVICE_RELOAD),
schema=RELOAD_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_RELOAD, reload_service_handler,
descriptions.get(SERVICE_RELOAD), schema=RELOAD_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service_handler,
descriptions.get(SERVICE_TOGGLE),
schema=SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, toggle_service_handler,
descriptions.get(SERVICE_TOGGLE), schema=SERVICE_SCHEMA)
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF):
hass.services.register(DOMAIN, service, turn_onoff_service_handler,
descriptions.get(service),
schema=SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, service, turn_onoff_service_handler,
descriptions.get(service), schema=SERVICE_SCHEMA)
return True
@@ -212,8 +225,6 @@ def setup(hass, config):
class AutomationEntity(ToggleEntity):
"""Entity to show status of entity."""
# pylint: disable=abstract-method
# pylint: disable=too-many-arguments, too-many-instance-attributes
def __init__(self, name, async_attach_triggers, cond_func, async_action,
hidden):
"""Initialize an automation entity."""
@@ -260,7 +271,7 @@ class AutomationEntity(ToggleEntity):
return
yield from self.async_enable()
self.hass.loop.create_task(self.async_update_ha_state())
yield from self.async_update_ha_state()
@asyncio.coroutine
def async_turn_off(self, **kwargs) -> None:
@@ -271,8 +282,6 @@ class AutomationEntity(ToggleEntity):
self._async_detach_triggers()
self._async_detach_triggers = None
self._enabled = False
# It's important that the update is finished before this method
# ends because async_remove depends on it.
yield from self.async_update_ha_state()
@asyncio.coroutine
@@ -284,7 +293,7 @@ class AutomationEntity(ToggleEntity):
if skip_condition or self._cond_func(variables):
yield from self._async_action(self.entity_id, variables)
self._last_triggered = utcnow()
self.hass.loop.create_task(self.async_update_ha_state())
yield from self.async_update_ha_state()
@asyncio.coroutine
def async_remove(self):
@@ -346,8 +355,10 @@ def _async_process_config(hass, config, component):
tasks.append(entity.async_enable())
entities.append(entity)
yield from asyncio.gather(*tasks, loop=hass.loop)
hass.loop.create_task(component.async_add_entities(entities))
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
if entities:
yield from component.async_add_entities(entities)
return len(entities) > 0
@@ -362,7 +373,7 @@ def _async_get_action(hass, config, name):
_LOGGER.info('Executing %s', name)
logbook.async_log_entry(
hass, name, 'has been triggered', DOMAIN, entity_id)
hass.loop.create_task(script_obj.async_run(variables))
yield from script_obj.async_run(variables)
return action
@@ -395,9 +406,8 @@ def _async_process_trigger(hass, config, trigger_configs, name, action):
removes = []
for conf in trigger_configs:
platform = yield from hass.loop.run_in_executor(
None, prepare_setup_platform, hass, config, DOMAIN,
conf.get(CONF_PLATFORM))
platform = yield from async_prepare_setup_platform(
hass, config, DOMAIN, conf.get(CONF_PLATFORM))
if platform is None:
return None
@@ -0,0 +1,98 @@
"""
Trigger an automation when a LiteJet switch is released.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/automation.litejet/
"""
import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import CONF_PLATFORM
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_utc_time
DEPENDENCIES = ['litejet']
_LOGGER = logging.getLogger(__name__)
CONF_NUMBER = 'number'
CONF_HELD_MORE_THAN = 'held_more_than'
CONF_HELD_LESS_THAN = 'held_less_than'
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'litejet',
vol.Required(CONF_NUMBER): cv.positive_int,
vol.Optional(CONF_HELD_MORE_THAN):
vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_HELD_LESS_THAN):
vol.All(cv.time_period, cv.positive_timedelta)
})
def async_trigger(hass, config, action):
"""Listen for events based on configuration."""
number = config.get(CONF_NUMBER)
held_more_than = config.get(CONF_HELD_MORE_THAN)
held_less_than = config.get(CONF_HELD_LESS_THAN)
pressed_time = None
cancel_pressed_more_than = None
@callback
def call_action():
"""Call action with right context."""
hass.async_run_job(action, {
'trigger': {
CONF_PLATFORM: 'litejet',
CONF_NUMBER: number,
CONF_HELD_MORE_THAN: held_more_than,
CONF_HELD_LESS_THAN: held_less_than
},
})
# held_more_than and held_less_than: trigger on released (if in time range)
# held_more_than: trigger after pressed with calculation
# held_less_than: trigger on released with calculation
# neither: trigger on pressed
@callback
def pressed_more_than_satisfied(now):
"""Handle the LiteJet's switch's button pressed >= held_more_than."""
call_action()
def pressed():
"""Handle the press of the LiteJet switch's button."""
nonlocal cancel_pressed_more_than, pressed_time
nonlocal held_less_than, held_more_than
pressed_time = dt_util.utcnow()
if held_more_than is None and held_less_than is None:
call_action()
if held_more_than is not None and held_less_than is None:
cancel_pressed_more_than = track_point_in_utc_time(
hass,
pressed_more_than_satisfied,
dt_util.utcnow() + held_more_than)
def released():
"""Handle the release of the LiteJet switch's button."""
nonlocal cancel_pressed_more_than, pressed_time
nonlocal held_less_than, held_more_than
# pylint: disable=not-callable
if cancel_pressed_more_than is not None:
cancel_pressed_more_than()
cancel_pressed_more_than = None
held_time = dt_util.utcnow() - pressed_time
if held_less_than is not None and held_time < held_less_than:
if held_more_than is None or held_time > held_more_than:
call_action()
hass.data['litejet_system'].on_switch_pressed(number, pressed)
hass.data['litejet_system'].on_switch_released(number, released)
def async_remove():
"""Remove all subscriptions used for this trigger."""
return
return async_remove
+15 -6
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)
@@ -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)
+74
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)
@@ -4,6 +4,8 @@ Component to interface with binary sensors.
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
@@ -14,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 = [
@@ -39,13 +41,13 @@ SENSOR_CLASSES = [
SENSOR_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(SENSOR_CLASSES))
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for binary sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)
yield from component.async_setup(config)
return True
@@ -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)
@@ -54,7 +52,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensor_class, pin)])
# pylint: disable=too-many-instance-attributes, too-many-arguments
class ArestBinarySensor(BinarySensorDevice):
"""Implement an aREST binary sensor for a pin."""
@@ -68,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):
@@ -93,7 +90,6 @@ class ArestBinarySensor(BinarySensorDevice):
self.arest.update()
# pylint: disable=too-few-public-methods
class ArestData(object):
"""Class for handling the data retrieval for pins."""
@@ -111,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)
@@ -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
@@ -4,8 +4,8 @@ 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/
"""
import logging
from datetime import timedelta
import logging
import voluptuous as vol
@@ -23,7 +23,7 @@ DEFAULT_NAME = 'Binary Command Sensor'
DEFAULT_PAYLOAD_ON = 'ON'
DEFAULT_PAYLOAD_OFF = 'OFF'
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
SCAN_INTERVAL = timedelta(seconds=60)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COMMAND): cv.string,
@@ -53,7 +53,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
value_template)])
# pylint: disable=too-many-arguments, too-many-instance-attributes
class CommandBinarySensor(BinarySensorDevice):
"""Represent a command line binary sensor."""
@@ -5,20 +5,16 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.concord232/
"""
import datetime
import logging
import requests
import voluptuous as vol
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, SENSOR_CLASSES)
from homeassistant.const import (CONF_HOST, CONF_PORT)
import homeassistant.helpers.config_validation as cv
import requests
import voluptuous as vol
REQUIREMENTS = ['concord232==0.14']
_LOGGER = logging.getLogger(__name__)
@@ -27,9 +23,12 @@ CONF_EXCLUDE_ZONES = 'exclude_zones'
CONF_ZONE_TYPES = 'zone_types'
DEFAULT_HOST = 'localhost'
DEFAULT_NAME = 'Alarm'
DEFAULT_PORT = '5007'
DEFAULT_SSL = False
SCAN_INTERVAL = datetime.timedelta(seconds=1)
ZONE_TYPES_SCHEMA = vol.Schema({
cv.positive_int: vol.In(SENSOR_CLASSES),
})
@@ -42,14 +41,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_ZONE_TYPES, default={}): ZONE_TYPES_SCHEMA,
})
SCAN_INTERVAL = 1
DEFAULT_NAME = "Alarm"
# pylint: disable=too-many-locals
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Concord232 binary sensor platform."""
"""Set up the Concord232 binary sensor platform."""
from concord232 import client as concord232_client
host = config.get(CONF_HOST)
@@ -59,24 +53,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensors = []
try:
_LOGGER.debug('Initializing Client.')
client = concord232_client.Client('http://{}:{}'
.format(host, port))
_LOGGER.debug("Initializing Client")
client = concord232_client.Client('http://{}:{}'.format(host, port))
client.zones = client.list_zones()
client.last_zone_update = datetime.datetime.now()
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to Concord232: %s', str(ex))
_LOGGER.error("Unable to connect to Concord232: %s", str(ex))
return False
for zone in client.zones:
_LOGGER.info('Loading Zone found: %s', zone['name'])
_LOGGER.info("Loading Zone found: %s", zone['name'])
if zone['number'] not in exclude:
sensors.append(Concord232ZoneSensor(
hass,
client,
zone,
zone_types.get(zone['number'], get_opening_type(zone))))
sensors.append(
Concord232ZoneSensor(
hass, client, zone, zone_types.get(zone['number'],
get_opening_type(zone)))
)
add_devices(sensors)
@@ -84,16 +77,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
def get_opening_type(zone):
"""Helper function to try to guess sensor type frm name."""
if "MOTION" in zone["name"]:
return "motion"
if "KEY" in zone["name"]:
return "safety"
if "SMOKE" in zone["name"]:
return "smoke"
if "WATER" in zone["name"]:
return "water"
return "opening"
"""Helper function to try to guess sensor type from name."""
if 'MOTION' in zone['name']:
return 'motion'
if 'KEY' in zone['name']:
return 'safety'
if 'SMOKE' in zone['name']:
return 'smoke'
if 'WATER' in zone['name']:
return 'water'
return 'opening'
class Concord232ZoneSensor(BinarySensorDevice):
@@ -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,
@@ -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,
})
@@ -35,7 +35,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
class EnvisalinkBinarySensor(EnvisalinkDevice, BinarySensorDevice):
"""Representation of an Envisalink binary sensor."""
# pylint: disable=too-many-arguments
def __init__(self, zone_number, zone_name, zone_type, info, controller):
"""Initialize the binary_sensor."""
from pydispatch import dispatcher
@@ -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(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.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"
@@ -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"
@@ -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"
@@ -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()
@@ -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()
@@ -7,7 +7,8 @@ https://home-assistant.io/components/binary_sensor.homematic/
import logging
from homeassistant.const import STATE_UNKNOWN
from homeassistant.components.binary_sensor import BinarySensorDevice
import homeassistant.components.homematic as homematic
from homeassistant.components.homematic import HMDevice
from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__)
@@ -16,6 +17,7 @@ DEPENDENCIES = ['homematic']
SENSOR_TYPES_CLASS = {
"Remote": None,
"ShutterContact": "opening",
"MaxShutterContact": "opening",
"IPShutterContact": "opening",
"Smoke": "smoke",
"SmokeV2": "smoke",
@@ -32,14 +34,16 @@ def setup_platform(hass, config, add_callback_devices, discovery_info=None):
if discovery_info is None:
return
homematic = get_component("homematic")
return homematic.setup_hmdevice_discovery_helper(
hass,
HMBinarySensor,
discovery_info,
add_callback_devices
)
class HMBinarySensor(homematic.HMDevice, BinarySensorDevice):
class HMBinarySensor(HMDevice, BinarySensorDevice):
"""Representation of a binary Homematic device."""
@property
@@ -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
@@ -8,6 +8,7 @@ import logging
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.components.mqtt as mqtt
from homeassistant.components.binary_sensor import (
BinarySensorDevice, SENSOR_CLASSES)
@@ -35,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
@@ -51,7 +55,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
)])
# pylint: disable=too-many-arguments, too-many-instance-attributes
class MqttBinarySensor(BinarySensorDevice):
"""Representation a binary sensor that is updated by MQTT."""
@@ -67,17 +70,18 @@ class MqttBinarySensor(BinarySensorDevice):
self._payload_off = payload_off
self._qos = qos
@callback
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
if value_template is not None:
payload = value_template.render_with_possible_json_value(
payload = value_template.async_render_with_possible_json_value(
payload)
if payload == self._payload_on:
self._state = True
self.update_ha_state()
hass.async_add_job(self.async_update_ha_state())
elif payload == self._payload_off:
self._state = False
self.update_ha_state()
hass.async_add_job(self.async_update_ha_state())
mqtt.subscribe(hass, self._state_topic, message_received, self._qos)
@@ -22,7 +22,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if discovery_info is None:
return
for gateway in mysensors.GATEWAYS.values():
gateways = hass.data.get(mysensors.MYSENSORS_GATEWAYS)
if not gateways:
return
for gateway in gateways:
# Define the S_TYPES and V_TYPES that the platform should handle as
# states. Map them in a dict of lists.
pres = gateway.const.Presentation
@@ -43,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(
+97 -26
View File
@@ -4,40 +4,88 @@ Support for Nest Thermostat Binary Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.nest/
"""
import voluptuous as vol
from itertools import chain
import logging
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)
import homeassistant.components.nest as nest
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_MONITORED_CONDITIONS
from homeassistant.components.nest import DATA_NEST
DEPENDENCIES = ['nest']
BINARY_TYPES = ['fan',
'hvac_ac_state',
'hvac_aux_heater_state',
'hvac_heater_state',
'hvac_heat_x2_state',
'hvac_heat_x3_state',
'hvac_alt_heat_state',
'hvac_alt_heat_x2_state',
'hvac_emer_heat_state',
'online']
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(BINARY_TYPES)]),
})
BINARY_TYPES = ['online']
CLIMATE_BINARY_TYPES = ['fan',
'is_using_emergency_heat',
'is_locked',
'has_leaf']
CAMERA_BINARY_TYPES = [
'motion_detected',
'sound_detected',
'person_detected']
_BINARY_TYPES_DEPRECATED = [
'hvac_ac_state',
'hvac_aux_heater_state',
'hvac_heater_state',
'hvac_heat_x2_state',
'hvac_heat_x3_state',
'hvac_alt_heat_state',
'hvac_alt_heat_x2_state',
'hvac_emer_heat_state']
_VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \
+ CAMERA_BINARY_TYPES
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Nest binary sensors."""
for structure, device in nest.devices():
add_devices([NestBinarySensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]])
if discovery_info is None:
return
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.")
_LOGGER.error(wstr)
sensors = []
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 conditions
if variable in BINARY_TYPES]
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in CLIMATE_BINARY_TYPES
and device.is_thermostat]
if device.is_camera:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in CAMERA_BINARY_TYPES]
for activity_zone in device.activity_zones:
sensors += [NestActivityZoneSensor(structure,
device,
activity_zone)]
add_devices(sensors, True)
class NestBinarySensor(NestSensor, BinarySensorDevice):
@@ -46,4 +94,27 @@ class NestBinarySensor(NestSensor, BinarySensorDevice):
@property
def is_on(self):
"""True if the binary sensor is on."""
return bool(getattr(self.device, self.variable))
return self._state
def update(self):
"""Retrieve latest state."""
self._state = bool(getattr(self.device, self.variable))
class NestActivityZoneSensor(NestBinarySensor):
"""Represents a Nest binary sensor for activity in a zone."""
def __init__(self, structure, device, zone):
"""Initialize the sensor."""
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 self._name
def update(self):
"""Retrieve latest state."""
self._state = self.device.has_ongoing_motion_in_zone(self.zone.zone_id)
+129 -33
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
from homeassistant.const import CONF_TIMEOUT, CONF_OFFSET
from homeassistant.helpers import config_validation as cv
DEPENDENCIES = ["netatmo"]
@@ -22,21 +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)]),
})
@@ -45,45 +64,83 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup access to Netatmo binary sensor."""
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,
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, 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
@@ -99,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):
@@ -109,19 +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":
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.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._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.someoneKnownSeen(self._home,
self._camera_name)
elif self._sensor_name == "Someone unknown":
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.welcomedata.someoneUnknownSeen(self._home,
self._camera_name)
elif self._sensor_name == "Motion":
self._state =\
self._data.welcomedata.motionDetected(self._home,
self._camera_name)
self._data.camera_data.moduleOpened(self._home,
self._module_name,
self._camera_name)
else:
return None
@@ -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__)
@@ -123,7 +123,7 @@ class NX584Watcher(threading.Thread):
if not zone_sensor:
return
zone_sensor._zone['state'] = event['zone_state']
zone_sensor.update_ha_state()
zone_sensor.schedule_update_ha_state()
def _process_events(self, events):
for event in events:
@@ -58,11 +58,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(devices)
# pylint: disable=too-many-instance-attributes
class OctoPrintBinarySensor(BinarySensorDevice):
"""Representation an OctoPrint binary sensor."""
# pylint: disable=too-many-arguments
def __init__(self, api, condition, sensor_type, sensor_name, unit,
endpoint, group, tool=None):
"""Initialize a new OctoPrint sensor."""
@@ -41,7 +41,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-variable, too-many-locals
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the REST binary sensor."""
name = config.get(CONF_NAME)
@@ -76,7 +75,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
hass, rest, name, sensor_class, value_template)])
# pylint: disable=too-many-arguments
class RestBinarySensor(BinarySensorDevice):
"""Representation of a REST binary sensor."""
@@ -51,10 +51,9 @@ 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)
# pylint: disable=too-many-arguments, too-many-instance-attributes
class RPiGPIOBinarySensor(BinarySensorDevice):
"""Represent a binary sensor that uses Raspberry Pi GPIO."""
@@ -66,14 +65,14 @@ 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."""
self._state = rpi_gpio.read_input(self._port)
self.update_ha_state()
self.schedule_update_ha_state()
rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime)
@@ -91,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)
@@ -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'
@@ -25,7 +25,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(dev)
# pylint: disable=too-many-instance-attributes
class IsInBedBinarySensor(sleepiq.SleepIQSensor, BinarySensorDevice):
"""Implementation of a SleepIQ presence sensor."""
@@ -63,14 +63,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
_LOGGER.error('No sensors added')
return False
hass.loop.create_task(async_add_devices(sensors))
yield from async_add_devices(sensors, True)
return True
class BinarySensorTemplate(BinarySensorDevice):
"""A virtual binary sensor that triggers from another sensor."""
# pylint: disable=too-many-arguments
def __init__(self, hass, device, friendly_name, sensor_class,
value_template, entity_ids):
"""Initialize the Template binary sensor."""
@@ -82,12 +81,10 @@ class BinarySensorTemplate(BinarySensorDevice):
self._template = value_template
self._state = None
self._async_render()
@callback
def template_bsensor_state_listener(entity, old_state, new_state):
"""Called when the target device changes state."""
hass.loop.create_task(self.async_update_ha_state(True))
hass.async_add_job(self.async_update_ha_state, True)
async_track_state_change(
hass, entity_ids, template_bsensor_state_listener)
@@ -115,17 +112,14 @@ class BinarySensorTemplate(BinarySensorDevice):
@asyncio.coroutine
def async_update(self):
"""Update the state from the template."""
self._async_render()
def _async_render(self):
"""Render the state from the template."""
try:
self._state = self._template.async_render().lower() == 'true'
except TemplateError as ex:
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
@@ -0,0 +1,128 @@
"""
Support for monitoring if a sensor value is below/above a threshold.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.threshold/
"""
import asyncio
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, SENSOR_CLASSES_SCHEMA)
from homeassistant.const import (
CONF_NAME, CONF_ENTITY_ID, CONF_TYPE, STATE_UNKNOWN, CONF_SENSOR_CLASS,
ATTR_ENTITY_ID)
from homeassistant.core import callback
from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__)
ATTR_SENSOR_VALUE = 'sensor_value'
ATTR_THRESHOLD = 'threshold'
ATTR_TYPE = 'type'
CONF_LOWER = 'lower'
CONF_THRESHOLD = 'threshold'
CONF_UPPER = 'upper'
DEFAULT_NAME = 'Threshold'
SENSOR_TYPES = [CONF_LOWER, CONF_UPPER]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_THRESHOLD): vol.Coerce(float),
vol.Required(CONF_TYPE): vol.In(SENSOR_TYPES),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_SENSOR_CLASS, default=None): SENSOR_CLASSES_SCHEMA,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Threshold sensor."""
entity_id = config.get(CONF_ENTITY_ID)
name = config.get(CONF_NAME)
threshold = config.get(CONF_THRESHOLD)
limit_type = config.get(CONF_TYPE)
sensor_class = config.get(CONF_SENSOR_CLASS)
yield from async_add_devices(
[ThresholdSensor(hass, entity_id, name, threshold, limit_type,
sensor_class)], True)
return True
class ThresholdSensor(BinarySensorDevice):
"""Representation of a Threshold sensor."""
def __init__(self, hass, entity_id, name, threshold, limit_type,
sensor_class):
"""Initialize the Threshold sensor."""
self._hass = hass
self._entity_id = entity_id
self.is_upper = limit_type == 'upper'
self._name = name
self._threshold = threshold
self._sensor_class = sensor_class
self._deviation = False
self.sensor_value = 0
@callback
# pylint: disable=invalid-name
def async_threshold_sensor_state_listener(
entity, old_state, new_state):
"""Called when the sensor changes state."""
if new_state.state == STATE_UNKNOWN:
return
try:
self.sensor_value = float(new_state.state)
except ValueError:
_LOGGER.error("State is not numerical")
hass.async_add_job(self.async_update_ha_state, True)
async_track_state_change(
hass, entity_id, async_threshold_sensor_state_listener)
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return true if sensor is on."""
return self._deviation
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def sensor_class(self):
"""Return the sensor class of the sensor."""
return self._sensor_class
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
return {
ATTR_ENTITY_ID: self._entity_id,
ATTR_SENSOR_VALUE: self.sensor_value,
ATTR_THRESHOLD: self._threshold,
ATTR_TYPE: CONF_UPPER if self.is_upper else CONF_LOWER,
}
@asyncio.coroutine
def async_update(self):
"""Get the latest data and updates the states."""
if self.is_upper:
self._deviation = bool(self.sensor_value > self._threshold)
else:
self._deviation = bool(self.sensor_value < self._threshold)
@@ -4,8 +4,11 @@ A sensor that monitors trands in other components.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.trend/
"""
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 (
@@ -72,7 +75,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class SensorTrend(BinarySensorDevice):
"""Representation of a trend Sensor."""
# pylint: disable=too-many-arguments, too-many-instance-attributes
def __init__(self, hass, device_id, friendly_name,
target_entity, attribute, sensor_class, invert):
"""Initialize the sensor."""
@@ -88,13 +90,12 @@ class SensorTrend(BinarySensorDevice):
self.from_state = None
self.to_state = None
self.update()
@callback
def trend_sensor_state_listener(entity, old_state, new_state):
"""Called when the target device changes state."""
self.from_state = old_state
self.to_state = new_state
self.update_ha_state(True)
hass.async_add_job(self.async_update_ha_state(True))
track_state_change(hass, target_entity,
trend_sensor_state_listener)
@@ -119,7 +120,8 @@ class SensorTrend(BinarySensorDevice):
"""No polling needed."""
return False
def update(self):
@asyncio.coroutine
def async_update(self):
"""Get the latest data and update the states."""
if self.from_state is None or self.to_state is None:
return
@@ -16,9 +16,9 @@ DEPENDENCIES = ['vera']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Perform the setup for Vera controller devices."""
add_devices_callback(
add_devices(
VeraBinarySensor(device, VERA_CONTROLLER)
for device in VERA_DEVICES['binary_sensor'])
@@ -45,10 +45,10 @@ class WemoBinarySensor(BinarySensorDevice):
_LOGGER.info(
'Subscription update for %s',
_device)
self.update()
if not hasattr(self, 'hass'):
self.update()
return
self.update_ha_state(True)
self.schedule_update_ha_state()
@property
def should_poll(self):
+146 -41
View File
@@ -4,12 +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 json
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']
@@ -19,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
}
@@ -32,59 +36,160 @@ 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)])
_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)])
_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)])
_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):
"""Representation of a Wink binary sensor."""
def __init__(self, wink):
def __init__(self, wink, hass):
"""Initialize the Wink binary sensor."""
super().__init__(wink)
wink = get_component('wink')
self._unit_of_measurement = self.wink.UNIT
self.capability = self.wink.capability()
def _pubnub_update(self, message, channel):
if 'data' in message:
json_data = json.dumps(message.get('data'))
else:
json_data = message
self.wink.pubnub_update(json.loads(json_data))
self.update_ha_state()
super().__init__(wink, hass)
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()
+22 -52
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,39 +68,33 @@ 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.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.update_ha_state,
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.update_ha_state()
self.schedule_update_ha_state()
if value.data:
# only allow this value to be true for re_arm secs
self.invalidate_after = dt_util.utcnow() + datetime.timedelta(
seconds=self.re_arm_sec)
track_point_in_time(
self._hass, self.update_ha_state,
self._hass, self.async_update_ha_state,
self.invalidate_after)
@property
+1 -1
View File
@@ -33,7 +33,7 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
# pylint: disable=unused-argument,too-few-public-methods
# pylint: disable=unused-argument
def setup(hass, config):
"""Setup BloomSky component."""
api_key = config[DOMAIN][CONF_API_KEY]
@@ -0,0 +1,186 @@
"""
Support for Google Calendar event device sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/calendar/
"""
import logging
from datetime import timedelta
import re
from homeassistant.components.google import (CONF_OFFSET,
CONF_DEVICE_ID,
CONF_NAME)
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers.config_validation import time_period_str
from homeassistant.helpers.entity import Entity, generate_entity_id
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.template import DATE_STR_FORMAT
from homeassistant.util import dt
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=60)
DOMAIN = 'calendar'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
def setup(hass, config):
"""Track states and offer events for calendars."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, DOMAIN)
component.setup(config)
return True
DEFAULT_CONF_TRACK_NEW = True
DEFAULT_CONF_OFFSET = '!!'
# pylint: disable=too-many-instance-attributes
class CalendarEventDevice(Entity):
"""A calendar event device."""
# Classes overloading this must set data to an object
# with an update() method
data = None
# pylint: disable=too-many-arguments
def __init__(self, hass, data):
"""Create the Calendar Event Device."""
self._name = data.get(CONF_NAME)
self.dev_id = data.get(CONF_DEVICE_ID)
self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET)
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT,
self.dev_id,
hass=hass)
self._cal_data = {
'all_day': False,
'offset_time': dt.dt.timedelta(),
'message': '',
'start': None,
'end': None,
'location': '',
'description': '',
}
self.update()
def offset_reached(self):
"""Have we reached the offset time specified in the event title."""
if self._cal_data['start'] is None or \
self._cal_data['offset_time'] == dt.dt.timedelta():
return False
return self._cal_data['start'] + self._cal_data['offset_time'] <= \
dt.now(self._cal_data['start'].tzinfo)
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def device_state_attributes(self):
"""State Attributes for HA."""
start = self._cal_data.get('start', None)
end = self._cal_data.get('end', None)
start = start.strftime(DATE_STR_FORMAT) if start is not None else None
end = end.strftime(DATE_STR_FORMAT) if end is not None else None
return {
'message': self._cal_data.get('message', ''),
'all_day': self._cal_data.get('all_day', False),
'offset_reached': self.offset_reached(),
'start_time': start,
'end_time': end,
'location': self._cal_data.get('location', None),
'description': self._cal_data.get('description', None),
}
@property
def state(self):
"""Return the state of the calendar event."""
start = self._cal_data.get('start', None)
end = self._cal_data.get('end', None)
if start is None or end is None:
return STATE_OFF
now = dt.now()
if start <= now and end > now:
return STATE_ON
if now >= end:
self.cleanup()
return STATE_OFF
def cleanup(self):
"""Cleanup any start/end listeners that were setup."""
self._cal_data = {
'all_day': False,
'offset_time': 0,
'message': '',
'start': None,
'end': None,
'location': None,
'description': None
}
def update(self):
"""Search for the next event."""
if not self.data or not self.data.update():
# update cached, don't do anything
return
if not self.data.event:
# we have no event to work on, make sure we're clean
self.cleanup()
return
def _get_date(date):
"""Get the dateTime from date or dateTime as a local."""
if 'date' in date:
return dt.start_of_local_day(dt.dt.datetime.combine(
dt.parse_date(date['date']), dt.dt.time.min))
else:
return dt.as_local(dt.parse_datetime(date['dateTime']))
start = _get_date(self.data.event['start'])
end = _get_date(self.data.event['end'])
summary = self.data.event['summary']
# check if we have an offset tag in the message
# time is HH:MM or MM
reg = '{}([+-]?[0-9]{{0,2}}(:[0-9]{{0,2}})?)'.format(self._offset)
search = re.search(reg, summary)
if search and search.group(1):
time = search.group(1)
if ':' not in time:
if time[0] == '+' or time[0] == '-':
time = '{}0:{}'.format(time[0], time[1:])
else:
time = '0:{}'.format(time)
offset_time = time_period_str(time)
summary = (summary[:search.start()] + summary[search.end():]) \
.strip()
else:
offset_time = dt.dt.timedelta() # default it
# cleanup the string so we don't have a bunch of double+ spaces
self._cal_data['message'] = re.sub(' +', '', summary).strip()
self._cal_data['offset_time'] = offset_time
self._cal_data['location'] = self.data.event.get('location', '')
self._cal_data['description'] = self.data.event.get('description', '')
self._cal_data['start'] = start
self._cal_data['end'] = end
self._cal_data['all_day'] = 'date' in self.data.event['start']
+82
View File
@@ -0,0 +1,82 @@
"""
Demo platform that has two fake binary sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import homeassistant.util.dt as dt_util
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo binary sensor platform."""
calendar_data_future = DemoGoogleCalendarDataFuture()
calendar_data_current = DemoGoogleCalendarDataCurrent()
add_devices([
DemoGoogleCalendar(hass, calendar_data_future, {
CONF_NAME: 'Future Event',
CONF_DEVICE_ID: 'future_event',
}),
DemoGoogleCalendar(hass, calendar_data_current, {
CONF_NAME: 'Current Event',
CONF_DEVICE_ID: 'current_event',
}),
])
class DemoGoogleCalendarData(object):
"""Setup base class for data."""
# pylint: disable=no-self-use
def update(self):
"""Return true so entity knows we have new data."""
return True
class DemoGoogleCalendarDataFuture(DemoGoogleCalendarData):
"""Setup future data event."""
def __init__(self):
"""Set the event to a future event."""
one_hour_from_now = dt_util.now() \
+ dt_util.dt.timedelta(minutes=30)
self.event = {
'start': {
'dateTime': one_hour_from_now.isoformat()
},
'end': {
'dateTime': (one_hour_from_now + dt_util.dt.
timedelta(minutes=60)).isoformat()
},
'summary': 'Future Event',
}
class DemoGoogleCalendarDataCurrent(DemoGoogleCalendarData):
"""Create a current event we're in the middle of."""
def __init__(self):
"""Set the event data."""
middle_of_event = dt_util.now() \
- dt_util.dt.timedelta(minutes=30)
self.event = {
'start': {
'dateTime': middle_of_event.isoformat()
},
'end': {
'dateTime': (middle_of_event + dt_util.dt.
timedelta(minutes=60)).isoformat()
},
'summary': 'Current Event',
}
class DemoGoogleCalendar(CalendarEventDevice):
"""A Demo binary sensor."""
def __init__(self, hass, calendar_data, data):
"""The same as a google calendar but without the api calls."""
self.data = calendar_data
super().__init__(hass, data)
@@ -0,0 +1,79 @@
"""
Support for Google Calendar Search binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.google_calendar/
"""
# pylint: disable=import-error
import logging
from datetime import timedelta
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import (CONF_CAL_ID, CONF_ENTITIES,
CONF_TRACK, TOKEN_FILE,
GoogleCalendarService)
from homeassistant.util import Throttle, dt
DEFAULT_GOOGLE_SEARCH_PARAMS = {
'orderBy': 'startTime',
'maxResults': 1,
'singleEvents': True,
}
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, disc_info=None):
"""Setup the calendar platform for event devices."""
if disc_info is None:
return
if not any([data[CONF_TRACK] for data in disc_info[CONF_ENTITIES]]):
return
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
add_devices([GoogleCalendarEventDevice(hass, calendar_service,
disc_info[CONF_CAL_ID], data)
for data in disc_info[CONF_ENTITIES] if data[CONF_TRACK]])
# pylint: disable=too-many-instance-attributes
class GoogleCalendarEventDevice(CalendarEventDevice):
"""A calendar event device."""
def __init__(self, hass, calendar_service, calendar, data):
"""Create the Calendar event device."""
self.data = GoogleCalendarData(calendar_service, calendar,
data.get('search', None))
super().__init__(hass, data)
class GoogleCalendarData(object):
"""Class to utilize calendar service object to get next event."""
def __init__(self, calendar_service, calendar_id, search=None):
"""Setup how we are going to search the google calendar."""
self.calendar_service = calendar_service
self.calendar_id = calendar_id
self.search = search
self.event = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data."""
service = self.calendar_service.get()
params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS)
params['timeMin'] = dt.start_of_local_day().isoformat('T')
params['calendarId'] = self.calendar_id
if self.search:
params['q'] = self.search
events = service.events() # pylint: disable=no-member
result = events.list(**params).execute()
items = result.get('items', [])
self.event = items[0] if len(items) == 1 else None
return True
+158 -57
View File
@@ -5,17 +5,32 @@ Component to interface with cameras.
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 time
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
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'
@@ -24,18 +39,63 @@ STATE_IDLE = 'idle'
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?token={1}'
TOKEN_CHANGE_INTERVAL = timedelta(minutes=5)
_RND = SystemRandom()
# pylint: disable=too-many-branches
def setup(hass, config):
@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.wsgi.register_view(CameraImageView(hass, component.entities))
hass.wsgi.register_view(CameraMjpegStream(hass, component.entities))
hass.http.register_view(CameraImageView(component.entities))
hass.http.register_view(CameraMjpegStream(component.entities))
component.setup(config)
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
@@ -45,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):
@@ -59,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):
@@ -80,33 +137,61 @@ class Camera(Entity):
"""Return bytes of camera image."""
raise NotImplementedError()
def mjpeg_stream(self, response):
"""Generate an HTTP MJPEG stream from camera images."""
def stream():
"""Stream images as mjpeg stream."""
try:
last_image = None
while True:
img_bytes = self.camera_image()
def async_camera_image(self):
"""Return bytes of camera image.
if img_bytes is not None and img_bytes != last_image:
yield bytes(
'--jpegboundary\r\n'
'Content-Type: image/jpeg\r\n'
'Content-Length: {}\r\n\r\n'.format(
len(img_bytes)), 'utf-8') + img_bytes + b'\r\n'
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(None, self.camera_image)
last_image = img_bytes
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from camera images.
time.sleep(0.5)
except GeneratorExit:
pass
This method must be run in the event loop.
"""
response = web.StreamResponse()
return response(
stream(),
content_type=('multipart/x-mixed-replace; '
'boundary=--jpegboundary')
)
response.content_type = ('multipart/x-mixed-replace; '
'boundary=--jpegboundary')
yield from response.prepare(request)
def write(img_bytes):
"""Write image to stream."""
response.write(bytes(
'--jpegboundary\r\n'
'Content-Type: image/jpeg\r\n'
'Content-Length: {}\r\n\r\n'.format(
len(img_bytes)), 'utf-8') + img_bytes + b'\r\n')
last_image = None
try:
while True:
img_bytes = yield from self.async_camera_image()
if not img_bytes:
break
if img_bytes is not None and img_bytes != last_image:
write(img_bytes)
# Chrome seems to always ignore first picture,
# print it twice.
if last_image is None:
write(img_bytes)
last_image = img_bytes
yield from response.drain()
yield from asyncio.sleep(.5)
except (asyncio.CancelledError, ConnectionResetError):
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
if response is not None:
yield from response.write_eof()
@property
def state(self):
@@ -122,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:
@@ -133,33 +218,43 @@ 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."""
requires_auth = False
def __init__(self, hass, entities):
def __init__(self, entities):
"""Initialize a basic camera view."""
super().__init__(hass)
self.entities = entities
@asyncio.coroutine
def get(self, request, entity_id):
"""Start a get request."""
camera = self.entities.get(entity_id)
if camera is None:
return self.Response(status=404)
status = 404 if request[KEY_AUTHENTICATED] else 401
return web.Response(status=status)
authenticated = (request.authenticated or
request.args.get('token') == camera.access_token)
authenticated = (request[KEY_AUTHENTICATED] or
request.GET.get('token') in camera.access_tokens)
if not authenticated:
return self.Response(status=401)
return web.Response(status=401)
return self.handle(camera)
response = yield from self.handle(request, camera)
return response
def handle(self, camera):
@asyncio.coroutine
def handle(self, request, camera):
"""Hanlde the camera request."""
raise NotImplementedError()
@@ -167,25 +262,31 @@ class CameraView(HomeAssistantView):
class CameraImageView(CameraView):
"""Camera view to serve an image."""
url = "/api/camera_proxy/<entity(domain=camera):entity_id>"
url = "/api/camera_proxy/{entity_id}"
name = "api:camera:image"
def handle(self, camera):
@asyncio.coroutine
def handle(self, request, camera):
"""Serve camera image."""
response = camera.camera_image()
try:
image = yield from camera.async_camera_image()
if response is None:
return self.Response(status=500)
if image is None:
return web.Response(status=500)
return self.Response(response)
return web.Response(body=image)
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
class CameraMjpegStream(CameraView):
"""Camera View to serve an MJPEG stream."""
url = "/api/camera_proxy_stream/<entity(domain=camera):entity_id>"
url = "/api/camera_proxy_stream/{entity_id}"
name = "api:camera:stream"
def handle(self, camera):
@asyncio.coroutine
def handle(self, request, camera):
"""Serve camera image."""
return camera.mjpeg_stream(self.Response)
yield from camera.handle_async_mjpeg_stream(request)
+133
View File
@@ -0,0 +1,133 @@
"""
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
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.1.4']
_LOGGER = logging.getLogger(__name__)
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
camera = AmcrestCamera(
config.get(CONF_HOST), config.get(CONF_PORT),
config.get(CONF_USERNAME), config.get(CONF_PASSWORD)).camera
persistent_notification = loader.get_component('persistent_notification')
try:
camera.current_time
# pylint: disable=broad-except
except Exception as ex:
_LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
persistent_notification.create(
hass, 'Error: {}<br />'
'You will need to restart hass after fixing.'
''.format(ex),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
add_devices([AmcrestCam(hass, config, camera)])
return True
class AmcrestCam(Camera):
"""An implementation of an Amcrest IP camera."""
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._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._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."""
return self._name
+45 -19
View File
@@ -4,13 +4,15 @@ Support for Cameras with FFmpeg as decoder.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.ffmpeg/
"""
import asyncio
import logging
import voluptuous as vol
from aiohttp import web
from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import (
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
@@ -27,42 +29,66 @@ 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 a FFmpeg Camera."""
if not run_test(config.get(CONF_INPUT)):
if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)):
return
add_devices([FFmpegCamera(config)])
yield from async_add_devices([FFmpegCamera(hass, config)])
class FFmpegCamera(Camera):
"""An implementation of an FFmpeg camera."""
def __init__(self, config):
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):
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
from haffmpeg import ImageSingle, IMAGE_JPEG
ffmpeg = ImageSingle(get_binary())
from haffmpeg import ImageFrame, IMAGE_JPEG
ffmpeg = ImageFrame(self._manager.binary, loop=self.hass.loop)
return ffmpeg.get_image(self._input, output_format=IMAGE_JPEG,
extra_cmd=self._extra_arguments)
image = yield from ffmpeg.get_image(
self._input, output_format=IMAGE_JPEG,
extra_cmd=self._extra_arguments)
return image
def mjpeg_stream(self, response):
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
from haffmpeg import CameraMjpeg
stream = CameraMjpeg(get_binary())
stream.open_camera(self._input, extra_cmd=self._extra_arguments)
return response(
stream,
mimetype='multipart/x-mixed-replace;boundary=ffserver',
direct_passthrough=True
)
stream = CameraMjpeg(self._manager.binary, loop=self.hass.loop)
yield from stream.open_camera(
self._input, extra_cmd=self._extra_arguments)
response = web.StreamResponse()
response.content_type = 'multipart/x-mixed-replace;boundary=ffserver'
yield from response.prepare(request)
try:
while True:
data = yield from stream.read(102400)
if not data:
break
response.write(data)
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
yield from stream.close()
if response is not None:
yield from response.write_eof()
@property
def name(self):
@@ -36,7 +36,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices([FoscamCamera(config)])
# pylint: disable=too-many-instance-attributes
class FoscamCamera(Camera):
"""An implementation of a Foscam IP camera."""
+50 -14
View File
@@ -4,10 +4,13 @@ Support for IP Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.generic/
"""
import asyncio
import logging
import aiohttp
import async_timeout
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
from requests.auth import HTTPDigestAuth
import voluptuous as vol
from homeassistant.const import (
@@ -15,7 +18,9 @@ from homeassistant.const import (
HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.exceptions import TemplateError
from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
_LOGGER = logging.getLogger(__name__)
@@ -35,13 +40,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
@asyncio.coroutine
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a generic IP Camera."""
add_devices([GenericCamera(hass, config)])
yield from async_add_devices([GenericCamera(hass, config)])
# pylint: disable=too-many-instance-attributes
class GenericCamera(Camera):
"""A generic implementation of an IP camera."""
@@ -49,6 +54,7 @@ class GenericCamera(Camera):
"""Initialize a generic camera."""
super().__init__()
self.hass = hass
self._authentication = device_info.get(CONF_AUTHENTICATION)
self._name = device_info.get(CONF_NAME)
self._still_image_url = device_info[CONF_STILL_IMAGE_URL]
self._still_image_url.hass = hass
@@ -58,10 +64,10 @@ class GenericCamera(Camera):
password = device_info.get(CONF_PASSWORD)
if username and password:
if device_info[CONF_AUTHENTICATION] == HTTP_DIGEST_AUTHENTICATION:
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
self._auth = HTTPDigestAuth(username, password)
else:
self._auth = HTTPBasicAuth(username, password)
self._auth = aiohttp.BasicAuth(username, password=password)
else:
self._auth = None
@@ -69,9 +75,15 @@ class GenericCamera(Camera):
self._last_image = None
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."""
try:
url = self._still_image_url.render()
url = self._still_image_url.async_render()
except TemplateError as err:
_LOGGER.error('Error parsing template %s: %s',
self._still_image_url, err)
@@ -80,16 +92,40 @@ class GenericCamera(Camera):
if url == self._last_url and self._limit_refetch:
return self._last_image
kwargs = {'timeout': 10, 'auth': self._auth}
# aiohttp don't support DigestAuth yet
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
def fetch():
"""Read image from a URL."""
try:
response = requests.get(url, timeout=10, auth=self._auth)
return response.content
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
return self._last_image
try:
response = requests.get(url, **kwargs)
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
return None
self._last_image = yield from self.hass.loop.run_in_executor(
None, fetch)
# async
else:
response = None
try:
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(10, loop=self.hass.loop):
response = yield from websession.get(
url, auth=self._auth)
self._last_image = yield from response.read()
except asyncio.TimeoutError:
_LOGGER.error('Timeout getting camera image')
return self._last_image
except (aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError) as err:
_LOGGER.error('Error getting new camera image: %s', err)
return self._last_image
finally:
if response is not None:
self.hass.async_add_job(response.release())
self._last_url = url
self._last_image = response.content
return self._last_image
@property
+69 -21
View File
@@ -4,9 +4,12 @@ Support for IP Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.mjpeg/
"""
import asyncio
import logging
from contextlib import closing
import aiohttp
import async_timeout
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
import voluptuous as vol
@@ -15,17 +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, 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,
@@ -34,10 +41,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
@asyncio.coroutine
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a MJPEG IP Camera."""
add_devices([MjpegCamera(config)])
yield from async_add_devices([MjpegCamera(hass, config)])
def extract_image_from_mjpeg(stream):
@@ -52,11 +60,10 @@ def extract_image_from_mjpeg(stream):
return jpg
# pylint: disable=too-many-instance-attributes
class MjpegCamera(Camera):
"""An implementation of an IP camera that is reachable over a URL."""
def __init__(self, device_info):
def __init__(self, hass, device_info):
"""Initialize a MJPEG camera."""
super().__init__()
self._name = device_info.get(CONF_NAME)
@@ -64,33 +71,74 @@ 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)
def camera_stream(self):
"""Return a MJPEG stream image response directly from the camera."""
self._auth = None
if self._username and self._password:
if self._authentication == HTTP_BASIC_AUTHENTICATION:
self._auth = aiohttp.BasicAuth(
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:
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
auth = HTTPDigestAuth(self._username, self._password)
else:
auth = HTTPBasicAuth(self._username, self._password)
return requests.get(self._mjpeg_url,
auth=auth,
stream=True, timeout=10)
req = requests.get(
self._mjpeg_url, auth=auth, stream=True, timeout=10)
else:
return requests.get(self._mjpeg_url, stream=True, timeout=10)
req = requests.get(self._mjpeg_url, stream=True, timeout=10)
def camera_image(self):
"""Return a still image response from the camera."""
with closing(self.camera_stream()) as response:
return extract_image_from_mjpeg(response.iter_content(1024))
with closing(req) as response:
return extract_image_from_mjpeg(response.iter_content(102400))
def mjpeg_stream(self, response):
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
stream = self.camera_stream()
return response(
stream.iter_content(chunk_size=1024),
mimetype=stream.headers[CONTENT_TYPE_HEADER],
direct_passthrough=True
)
# aiohttp don't support DigestAuth -> Fallback
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
yield from super().handle_async_mjpeg_stream(request)
return
# connect to stream
websession = async_get_clientsession(self.hass)
stream_coro = websession.get(self._mjpeg_url, auth=self._auth)
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):
+109
View File
@@ -0,0 +1,109 @@
"""
Support for Nest Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.nest/
"""
import logging
from datetime import timedelta
import requests
import homeassistant.components.nest as nest
from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera)
from homeassistant.util.dt import utcnow
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['nest']
NEST_BRAND = 'Nest'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({})
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].cameras()
cameras = [NestCamera(structure, device)
for structure, device in camera_devices]
add_devices(cameras, True)
class NestCamera(Camera):
"""Representation of a Nest Camera."""
def __init__(self, structure, device):
"""Initialize a Nest Camera."""
super(NestCamera, self).__init__()
self.structure = structure
self.device = device
self._location = None
self._name = 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
self._time_between_snapshots = timedelta(seconds=30)
self._last_image = None
self._next_snapshot_at = None
@property
def name(self):
"""Return the name of the nest, if any."""
return self._name
@property
def should_poll(self):
"""Nest camera should poll periodically."""
return True
@property
def is_recording(self):
"""Return true if the device is recording."""
return self._is_streaming
@property
def brand(self):
"""Return the brand of the camera."""
return NEST_BRAND
# This doesn't seem to be getting called regularly, for some reason
def update(self):
"""Cache value from Python-nest."""
self._location = self.device.where
self._name = self.device.name
self._online = self.device.online
self._is_streaming = self.device.is_streaming
self._is_video_history_enabled = self.device.is_video_history_enabled
if self._is_video_history_enabled:
# NestAware allowed 10/min
self._time_between_snapshots = timedelta(seconds=6)
else:
# Otherwise, 2/min
self._time_between_snapshots = timedelta(seconds=30)
def _ready_for_snapshot(self, now):
return (self._next_snapshot_at is None or
now > self._next_snapshot_at)
def camera_image(self):
"""Return a still image response from the camera."""
now = utcnow()
if self._ready_for_snapshot(now):
url = self.device.snapshot_url
try:
response = requests.get(url)
except requests.exceptions.RequestException as error:
_LOGGER.error("Error getting camera image: %s", error)
return None
self._next_snapshot_at = now + self._time_between_snapshots
self._last_image = response.content
return self._last_image
+45 -17
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."""
+24 -9
View File
@@ -12,7 +12,8 @@ import shutil
import voluptuous as vol
from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_NAME, CONF_FILE_PATH)
from homeassistant.const import (CONF_NAME, CONF_FILE_PATH,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -35,10 +36,10 @@ DEFAULT_TIMELAPSE = 1000
DEFAULT_VERTICAL_FLIP = 0
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_FILE_PATH): cv.isfile,
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)),
@@ -53,6 +54,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def kill_raspistill(*args):
"""Kill any previously running raspistill process.."""
subprocess.Popen(['killall', 'raspistill'],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Raspberry Camera."""
if shutil.which("raspistill") is None:
@@ -75,11 +83,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
}
)
if not os.access(setup_config[CONF_FILE_PATH], os.W_OK):
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, kill_raspistill)
try:
# Try to create an empty file (or open existing) to ensure we have
# proper permissions.
open(setup_config[CONF_FILE_PATH], 'a').close()
add_devices([RaspberryCamera(setup_config)])
except PermissionError:
_LOGGER.error("File path is not writable")
return False
add_devices([RaspberryCamera(setup_config)])
except FileNotFoundError:
_LOGGER.error("Could not create output file (missing directory?)")
return False
class RaspberryCamera(Camera):
@@ -93,9 +110,7 @@ class RaspberryCamera(Camera):
self._config = device_info
# Kill if there's raspistill instance
subprocess.Popen(['killall', 'raspistill'],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT)
kill_raspistill()
cmd_args = [
'raspistill', '--nopreview', '-o', device_info[CONF_FILE_PATH],
+170 -130
View File
@@ -4,28 +4,32 @@ Support for Synology Surveillance Station Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.synology/
"""
import asyncio
import logging
import voluptuous as vol
import requests
import aiohttp
import async_timeout
from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_URL, CONF_WHITELIST)
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL)
from homeassistant.components.camera import (
Camera, PLATFORM_SCHEMA)
from homeassistant.helpers.aiohttp_client import (
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
_LOGGER = logging.getLogger(__name__)
# pylint: disable=too-many-locals
DEFAULT_NAME = 'Synology Camera'
DEFAULT_STREAM_ID = '0'
TIMEOUT = 5
CONF_CAMERA_NAME = 'camera_name'
CONF_STREAM_ID = 'stream_id'
CONF_VALID_CERT = 'valid_cert'
QUERY_CGI = 'query.cgi'
QUERY_API = 'SYNO.API.Info'
@@ -38,6 +42,7 @@ WEBAPI_PATH = '/webapi/'
AUTH_PATH = 'auth.cgi'
CAMERA_PATH = 'camera.cgi'
STREAMING_PATH = 'SurveillanceStation/videoStreaming.cgi'
CONTENT_TYPE_HEADER = 'Content-Type'
SYNO_API_URL = '{0}{1}{2}'
@@ -47,175 +52,210 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_URL): cv.string,
vol.Optional(CONF_WHITELIST, default=[]): cv.ensure_list,
vol.Optional(CONF_VALID_CERT, default=True): cv.boolean,
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
})
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 a Synology IP Camera."""
verify_ssl = config.get(CONF_VERIFY_SSL)
websession_init = async_get_clientsession(hass, verify_ssl)
# Determine API to use for authentication
syno_api_url = SYNO_API_URL.format(config.get(CONF_URL),
WEBAPI_PATH,
QUERY_CGI)
query_payload = {'api': QUERY_API,
'method': 'Query',
'version': '1',
'query': 'SYNO.'}
query_req = requests.get(syno_api_url,
params=query_payload,
verify=config.get(CONF_VALID_CERT),
timeout=TIMEOUT)
query_resp = query_req.json()
auth_path = query_resp['data'][AUTH_API]['path']
camera_api = query_resp['data'][CAMERA_API]['path']
camera_path = query_resp['data'][CAMERA_API]['path']
streaming_path = query_resp['data'][STREAMING_API]['path']
syno_api_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, QUERY_CGI)
query_payload = {
'api': QUERY_API,
'method': 'Query',
'version': '1',
'query': 'SYNO.'
}
query_req = None
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
query_req = yield from websession_init.get(
syno_api_url,
params=query_payload
)
query_resp = yield from query_req.json()
auth_path = query_resp['data'][AUTH_API]['path']
camera_api = query_resp['data'][CAMERA_API]['path']
camera_path = query_resp['data'][CAMERA_API]['path']
streaming_path = query_resp['data'][STREAMING_API]['path']
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", syno_api_url)
return False
finally:
if query_req is not None:
yield from query_req.release()
# Authticate to NAS to get a session id
syno_auth_url = SYNO_API_URL.format(config.get(CONF_URL),
WEBAPI_PATH,
auth_path)
session_id = get_session_id(config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
syno_auth_url,
config.get(CONF_VALID_CERT))
syno_auth_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, auth_path)
session_id = yield from get_session_id(
hass,
websession_init,
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
syno_auth_url
)
# init websession
websession = async_create_clientsession(
hass, verify_ssl, cookies={'id': session_id})
# Use SessionID to get cameras in system
syno_camera_url = SYNO_API_URL.format(config.get(CONF_URL),
WEBAPI_PATH,
camera_api)
camera_payload = {'api': CAMERA_API,
'method': 'List',
'version': '1'}
camera_req = requests.get(syno_camera_url,
params=camera_payload,
verify=config.get(CONF_VALID_CERT),
timeout=TIMEOUT,
cookies={'id': session_id})
camera_resp = camera_req.json()
syno_camera_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, camera_api)
camera_payload = {
'api': CAMERA_API,
'method': 'List',
'version': '1'
}
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
camera_req = yield from websession.get(
syno_camera_url,
params=camera_payload
)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", syno_camera_url)
return False
camera_resp = yield from camera_req.json()
cameras = camera_resp['data']['cameras']
yield from camera_req.release()
# add cameras
devices = []
for camera in cameras:
if not config.get(CONF_WHITELIST):
camera_id = camera['id']
snapshot_path = camera['snapshot_path']
add_devices([SynologyCamera(config,
camera_id,
camera['name'],
snapshot_path,
streaming_path,
camera_path,
auth_path)])
device = SynologyCamera(
hass,
websession,
config,
camera_id,
camera['name'],
snapshot_path,
streaming_path,
camera_path,
auth_path
)
devices.append(device)
yield from async_add_devices(devices)
def get_session_id(username, password, login_url, valid_cert):
@asyncio.coroutine
def get_session_id(hass, websession, username, password, login_url):
"""Get a session id."""
auth_payload = {'api': AUTH_API,
'method': 'Login',
'version': '2',
'account': username,
'passwd': password,
'session': 'SurveillanceStation',
'format': 'sid'}
auth_req = requests.get(login_url,
params=auth_payload,
verify=valid_cert,
timeout=TIMEOUT)
auth_resp = auth_req.json()
return auth_resp['data']['sid']
auth_payload = {
'api': AUTH_API,
'method': 'Login',
'version': '2',
'account': username,
'passwd': password,
'session': 'SurveillanceStation',
'format': 'sid'
}
auth_req = None
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
auth_req = yield from websession.get(
login_url,
params=auth_payload
)
auth_resp = yield from auth_req.json()
return auth_resp['data']['sid']
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", login_url)
return False
finally:
if auth_req is not None:
yield from auth_req.release()
# pylint: disable=too-many-instance-attributes
class SynologyCamera(Camera):
"""An implementation of a Synology NAS based IP camera."""
# pylint: disable=too-many-arguments
def __init__(self, config, camera_id, camera_name,
snapshot_path, streaming_path, camera_path, auth_path):
def __init__(self, hass, websession, config, camera_id,
camera_name, snapshot_path, streaming_path, camera_path,
auth_path):
"""Initialize a Synology Surveillance Station camera."""
super().__init__()
self.hass = hass
self._websession = websession
self._name = camera_name
self._username = config.get(CONF_USERNAME)
self._password = config.get(CONF_PASSWORD)
self._synology_url = config.get(CONF_URL)
self._api_url = config.get(CONF_URL) + 'webapi/'
self._login_url = config.get(CONF_URL) + '/webapi/' + 'auth.cgi'
self._camera_name = config.get(CONF_CAMERA_NAME)
self._stream_id = config.get(CONF_STREAM_ID)
self._valid_cert = config.get(CONF_VALID_CERT)
self._camera_id = camera_id
self._snapshot_path = snapshot_path
self._streaming_path = streaming_path
self._camera_path = camera_path
self._auth_path = auth_path
self._session_id = get_session_id(self._username,
self._password,
self._login_url,
self._valid_cert)
def get_sid(self):
"""Get a session id."""
auth_payload = {'api': AUTH_API,
'method': 'Login',
'version': '2',
'account': self._username,
'passwd': self._password,
'session': 'SurveillanceStation',
'format': 'sid'}
auth_req = requests.get(self._login_url,
params=auth_payload,
verify=self._valid_cert,
timeout=TIMEOUT)
auth_resp = auth_req.json()
self._session_id = auth_resp['data']['sid']
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."""
image_url = SYNO_API_URL.format(self._synology_url,
WEBAPI_PATH,
self._camera_path)
image_payload = {'api': CAMERA_API,
'method': 'GetSnapshot',
'version': '1',
'cameraId': self._camera_id}
image_url = SYNO_API_URL.format(
self._synology_url, WEBAPI_PATH, self._camera_path)
image_payload = {
'api': CAMERA_API,
'method': 'GetSnapshot',
'version': '1',
'cameraId': self._camera_id
}
try:
response = requests.get(image_url,
params=image_payload,
timeout=TIMEOUT,
verify=self._valid_cert,
cookies={'id': self._session_id})
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
response = yield from self._websession.get(
image_url,
params=image_payload
)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", image_url)
return None
return response.content
image = yield from response.read()
yield from response.release()
def camera_stream(self):
return image
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Return a MJPEG stream image response directly from the camera."""
streaming_url = SYNO_API_URL.format(self._synology_url,
WEBAPI_PATH,
self._streaming_path)
streaming_payload = {'api': STREAMING_API,
'method': 'Stream',
'version': '1',
'cameraId': self._camera_id,
'format': 'mjpeg'}
response = requests.get(streaming_url,
payload=streaming_payload,
stream=True,
timeout=TIMEOUT,
cookies={'id': self._session_id})
return response
streaming_url = SYNO_API_URL.format(
self._synology_url, WEBAPI_PATH, self._streaming_path)
def mjpeg_steam(self, response):
"""Generate an HTTP MJPEG Stream from the Synology NAS."""
stream = self.camera_stream()
return response(
stream.iter_content(chunk_size=1024),
mimetype=stream.headers['CONTENT_TYPE_HEADER'],
direct_passthrough=True
)
streaming_payload = {
'api': STREAMING_API,
'method': 'Stream',
'version': '1',
'cameraId': self._camera_id,
'format': 'mjpeg'
}
stream_coro = self._websession.get(
streaming_url, params=streaming_payload)
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):
+1 -1
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__)
+233 -103
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,11 +57,17 @@ 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"
ATTR_SWING_LIST = "swing_list"
# The degree of precision for each platform
PRECISION_WHOLE = 1
PRECISION_HALVES = 0.5
PRECISION_TENTHS = 0.1
CONVERTIBLE_ATTRIBUTE = [
ATTR_TEMPERATURE,
ATTR_TARGET_TEMP_LOW,
@@ -85,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,
@@ -111,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 = {
@@ -123,7 +149,6 @@ def set_aux_heat(hass, aux_heat, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_AUX_HEAT, data)
# pylint: disable=too-many-arguments
def set_temperature(hass, temperature=None, entity_id=None,
target_temp_high=None, target_temp_low=None,
operation_mode=None):
@@ -181,70 +206,95 @@ def set_swing_mode(hass, swing_mode, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_SWING_MODE, data)
# pylint: disable=too-many-branches
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 = {}
@@ -258,113 +308,90 @@ 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
class ClimateDevice(Entity):
"""Representation of a climate device."""
# pylint: disable=too-many-public-methods,no-self-use
# pylint: disable=no-self-use
@property
def state(self):
"""Return the current state."""
@@ -373,6 +400,14 @@ class ClimateDevice(Entity):
else:
return STATE_UNKNOWN
@property
def precision(self):
"""Return the precision of the system."""
if self.unit_of_measurement == TEMP_CELSIUS:
return PRECISION_TENTHS
else:
return PRECISION_WHOLE
@property
def state_attributes(self):
"""Return the optional state attributes."""
@@ -410,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
@@ -481,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."""
@@ -510,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."""
@@ -564,16 +692,18 @@ class ClimateDevice(Entity):
def _convert_for_display(self, temp):
"""Convert temperature into preferred units for display purposes."""
if temp is None or not isinstance(temp, Number):
if (temp is None or not isinstance(temp, Number) or
self.temperature_unit == self.unit_of_measurement):
return temp
value = convert_temperature(temp, self.temperature_unit,
self.unit_of_measurement)
if self.unit_of_measurement is TEMP_CELSIUS:
decimal_count = 1
# Round in the units appropriate
if self.precision == PRECISION_HALVES:
return round(value * 2) / 2.0
elif self.precision == PRECISION_TENTHS:
return round(value, 1)
else:
# Users of fahrenheit generally expect integer units.
decimal_count = 0
return round(value, decimal_count)
# PRECISION_WHOLE as a fall back
return round(value)
+25 -16
View File
@@ -12,22 +12,20 @@ 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)
])
# pylint: disable=too-many-arguments, too-many-public-methods
class DemoClimate(ClimateDevice):
"""Representation of a demo climate device."""
# pylint: disable=too-many-instance-attributes
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."""
@@ -36,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
@@ -108,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."""
@@ -131,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):
@@ -166,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()
+171 -55
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,8 +95,12 @@ 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)
# pylint: disable=too-many-public-methods, abstract-method
class Thermostat(ClimateDevice):
"""A thermostat class for Ecobee."""
@@ -117,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):
@@ -137,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."""
@@ -190,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):
@@ -215,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)."""
@@ -249,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'
+80 -18
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,
@@ -42,16 +52,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(devices)
# pylint: disable=too-many-instance-attributes, import-error, abstract-method
# pylint: disable=import-error
class EQ3BTSmartThermostat(ClimateDevice):
"""Representation of a eQ-3 Bluetooth Smart thermostat."""
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."""
@@ -4,27 +4,29 @@ 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__)
DEPENDENCIES = ['switch', 'sensor']
TOL_TEMP = 0.3
DEFAULT_TOLERANCE = 0.3
DEFAULT_NAME = 'Generic Thermostat'
CONF_NAME = 'name'
DEFAULT_NAME = 'Generic Thermostat'
CONF_HEATER = 'heater'
CONF_SENSOR = 'target_sensor'
CONF_MIN_TEMP = 'min_temp'
@@ -32,6 +34,7 @@ CONF_MAX_TEMP = 'max_temp'
CONF_TARGET_TEMP = 'target_temp'
CONF_AC_MODE = 'ac_mode'
CONF_MIN_DUR = 'min_cycle_duration'
CONF_TOLERANCE = 'tolerance'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@@ -42,11 +45,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_MIN_DUR): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_TOLERANCE, default=DEFAULT_TOLERANCE): vol.Coerce(float),
vol.Optional(CONF_TARGET_TEMP): vol.Coerce(float),
})
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)
@@ -56,25 +61,26 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
target_temp = config.get(CONF_TARGET_TEMP)
ac_mode = config.get(CONF_AC_MODE)
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)])
target_temp, ac_mode, min_cycle_duration, tolerance)])
# pylint: disable=too-many-instance-attributes, abstract-method
class GenericThermostat(ClimateDevice):
"""Representation of a GenericThermostat device."""
# pylint: disable=too-many-arguments
def __init__(self, hass, name, heater_entity_id, sensor_entity_id,
min_temp, max_temp, target_temp, ac_mode, min_cycle_duration):
min_temp, max_temp, target_temp, ac_mode, min_cycle_duration,
tolerance):
"""Initialize the thermostat."""
self.hass = hass
self._name = name
self.heater_entity_id = heater_entity_id
self.ac_mode = ac_mode
self.min_cycle_duration = min_cycle_duration
self._tolerance = tolerance
self._active = False
self._cur_temp = None
@@ -83,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):
@@ -124,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):
@@ -147,22 +157,31 @@ class GenericThermostat(ClimateDevice):
def max_temp(self):
"""Return the maximum temperature."""
# pylint: disable=no-member
if self._min_temp:
if self._max_temp:
return self._max_temp
else:
# 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.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)
@@ -172,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):
@@ -188,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 > TOL_TEMP
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 > TOL_TEMP
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):
@@ -56,7 +56,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class HeatmiserV3Thermostat(ClimateDevice):
"""Representation of a HeatmiserV3 thermostat."""
# pylint: disable=too-many-instance-attributes, abstract-method
def __init__(self, heatmiser, device, name, serport):
"""Initialize the thermostat."""
self.heatmiser = heatmiser
@@ -5,10 +5,11 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.homematic/
"""
import logging
import homeassistant.components.homematic as homematic
from homeassistant.components.climate import ClimateDevice, STATE_AUTO
from homeassistant.components.homematic import HMDevice
from homeassistant.util.temperature import convert
from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN, ATTR_TEMPERATURE
from homeassistant.loader import get_component
DEPENDENCIES = ['homematic']
@@ -29,15 +30,16 @@ def setup_platform(hass, config, add_callback_devices, discovery_info=None):
if discovery_info is None:
return
homematic = get_component("homematic")
return homematic.setup_hmdevice_discovery_helper(
hass,
HMThermostat,
discovery_info,
add_callback_devices
)
# pylint: disable=abstract-method
class HMThermostat(homematic.HMDevice, ClimateDevice):
class HMThermostat(HMDevice, ClimateDevice):
"""Representation of a Homematic thermostat."""
@property
@@ -95,13 +97,9 @@ class HMThermostat(homematic.HMDevice, ClimateDevice):
def set_temperature(self, **kwargs):
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
if not self.available:
if not self.available or temperature is None:
return None
if temperature is None:
return
if self.current_operation == STATE_AUTO:
return self._hmdevice.actionNodeData('MANU_MODE', temperature)
self._hmdevice.set_temperature(temperature)
def set_operation_mode(self, operation_mode):
@@ -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__)
@@ -100,7 +100,6 @@ def _setup_us(username, password, config, add_devices):
class RoundThermostat(ClimateDevice):
"""Representation of a Honeywell Round Connected thermostat."""
# pylint: disable=too-many-instance-attributes, abstract-method
def __init__(self, device, zone_id, master, away_temp):
"""Initialize the thermostat."""
self.device = device
@@ -197,7 +196,6 @@ class RoundThermostat(ClimateDevice):
self._is_dhw = False
# pylint: disable=abstract-method
class HoneywellUSThermostat(ClimateDevice):
"""Representation of a Honeywell US Thermostat."""
@@ -225,7 +223,6 @@ class HoneywellUSThermostat(ClimateDevice):
@property
def current_temperature(self):
"""Return the current temperature."""
self._device.refresh()
return self._device.current_temperature
@property
@@ -276,3 +273,7 @@ class HoneywellUSThermostat(ClimateDevice):
"""Set the system mode (Cool, Heat, etc)."""
if hasattr(self._device, ATTR_SYSTEM_MODE):
self._device.system_mode = operation_mode
def update(self):
"""Update the state."""
self._device.refresh()
+13 -6
View File
@@ -56,6 +56,8 @@ class KNXThermostat(KNXMultiAddressDevice, ClimateDevice):
self._unit_of_measurement = TEMP_CELSIUS # KNX always used celsius
self._away = False # not yet supported
self._is_fan_on = False # not yet supported
self._current_temp = None
self._target_temp = None
@property
def should_poll(self):
@@ -70,16 +72,12 @@ class KNXThermostat(KNXMultiAddressDevice, ClimateDevice):
@property
def current_temperature(self):
"""Return the current temperature."""
from knxip.conversion import knx2_to_float
return knx2_to_float(self.value('temperature'))
return self._current_temp
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
from knxip.conversion import knx2_to_float
return knx2_to_float(self.value('setpoint'))
return self._target_temp
def set_temperature(self, **kwargs):
"""Set new target temperature."""
@@ -94,3 +92,12 @@ class KNXThermostat(KNXMultiAddressDevice, ClimateDevice):
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
raise NotImplementedError()
def update(self):
"""Update KNX climate."""
from knxip.conversion import knx2_to_float
super().update()
self._current_temp = knx2_to_float(self.value('temperature'))
self._target_temp = knx2_to_float(self.value('setpoint'))
+10 -6
View File
@@ -24,7 +24,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors climate."""
if discovery_info is None:
return
for gateway in mysensors.GATEWAYS.values():
gateways = hass.data.get(mysensors.MYSENSORS_GATEWAYS)
if not gateways:
return
for gateway in gateways:
if float(gateway.protocol_version) < 1.5:
continue
pres = gateway.const.Presentation
@@ -34,10 +39,9 @@ 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))
# pylint: disable=too-many-arguments, too-many-public-methods
class MySensorsHVAC(mysensors.MySensorsDeviceEntity, ClimateDevice):
"""Representation of a MySensorsHVAC hvac."""
@@ -131,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."""
@@ -141,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."""
@@ -152,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."""
+89 -73
View File
@@ -5,14 +5,17 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.nest/
"""
import logging
import voluptuous as vol
import homeassistant.components.nest as nest
from homeassistant.components.nest import DATA_NEST
from homeassistant.components.climate import (
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
PLATFORM_SCHEMA, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
ATTR_TEMPERATURE)
from homeassistant.const import (
TEMP_CELSIUS, CONF_SCAN_INTERVAL, STATE_ON, STATE_OFF, STATE_UNKNOWN)
TEMP_CELSIUS, TEMP_FAHRENHEIT,
CONF_SCAN_INTERVAL, STATE_ON, STATE_OFF, STATE_UNKNOWN)
DEPENDENCIES = ['nest']
_LOGGER = logging.getLogger(__name__)
@@ -22,15 +25,26 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.All(vol.Coerce(int), vol.Range(min=1)),
})
STATE_ECO = 'eco'
STATE_HEAT_COOL = 'heat-cool'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the 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 nest.devices()])
add_devices(
[NestThermostat(structure, device, temp_unit)
for structure, device in hass.data[DATA_NEST].thermostats()],
True
)
# pylint: disable=abstract-method,too-many-public-methods
class NestThermostat(ClimateDevice):
"""Representation of a Nest thermostat."""
@@ -54,98 +68,95 @@ class NestThermostat(ClimateDevice):
if self.device.can_heat and self.device.can_cool:
self._operation_list.append(STATE_AUTO)
self._operation_list.append(STATE_ECO)
# feature of device
self._has_fan = self.device.has_fan
# data attributes
self._away = None
self._location = None
self._name = None
self._humidity = None
self._target_temperature = None
self._temperature = None
self._temperature_scale = None
self._mode = None
self._fan = None
self._eco_temperature = None
self._is_locked = None
self._locked_temperature = None
self._min_temperature = None
self._max_temperature = None
@property
def name(self):
"""Return the name of the nest, if any."""
location = self.device.where
name = self.device.name
if location is None:
return name
else:
if name == '':
return location.capitalize()
else:
return location.capitalize() + '(' + name + ')'
return self._name
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
if self.device.has_humidifier or self.device.has_dehumidifier:
# Move these to Thermostat Device and make them global
return {
"humidity": self.device.humidity,
"target_humidity": self.device.target_humidity,
}
else:
# No way to control humidity not show setting
return {}
return self._temperature_scale
@property
def current_temperature(self):
"""Return the current temperature."""
return self.device.temperature
return self._temperature
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
if self.device.mode == 'cool':
return STATE_COOL
elif self.device.mode == 'heat':
return STATE_HEAT
elif self.device.mode == 'range':
if self._mode in [STATE_HEAT, STATE_COOL, STATE_OFF, STATE_ECO]:
return self._mode
elif self._mode == STATE_HEAT_COOL:
return STATE_AUTO
elif self.device.mode == 'off':
return STATE_OFF
else:
return STATE_UNKNOWN
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
if self.device.mode != 'range' and not self.is_away_mode_on:
return self.device.target
if self._mode != STATE_HEAT_COOL and not self.is_away_mode_on:
return self._target_temperature
else:
return None
@property
def target_temperature_low(self):
"""Return the lower bound temperature we try to reach."""
if self.is_away_mode_on and self.device.away_temperature[0]:
# away_temperature is always a low, high tuple
return self.device.away_temperature[0]
if self.device.mode == 'range':
return self.device.target[0]
if (self.is_away_mode_on or self._mode == STATE_ECO) and \
self._eco_temperature[0]:
# eco_temperature is always a low, high tuple
return self._eco_temperature[0]
if self._mode == STATE_HEAT_COOL:
return self._target_temperature[0]
else:
return None
@property
def target_temperature_high(self):
"""Return the upper bound temperature we try to reach."""
if self.is_away_mode_on and self.device.away_temperature[1]:
# away_temperature is always a low, high tuple
return self.device.away_temperature[1]
if self.device.mode == 'range':
return self.device.target[1]
if (self.is_away_mode_on or self._mode == STATE_ECO) and \
self._eco_temperature[1]:
# eco_temperature is always a low, high tuple
return self._eco_temperature[1]
if self._mode == STATE_HEAT_COOL:
return self._target_temperature[1]
else:
return None
@property
def is_away_mode_on(self):
"""Return if away mode is on."""
return self.structure.away
return self._away
def set_temperature(self, **kwargs):
"""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.device.mode == 'range':
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)
@@ -154,14 +165,11 @@ class NestThermostat(ClimateDevice):
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
if operation_mode == STATE_HEAT:
self.device.mode = 'heat'
elif operation_mode == STATE_COOL:
self.device.mode = 'cool'
if operation_mode in [STATE_HEAT, STATE_COOL, STATE_OFF, STATE_ECO]:
device_mode = operation_mode
elif operation_mode == STATE_AUTO:
self.device.mode = 'range'
elif operation_mode == STATE_OFF:
self.device.mode = 'off'
device_mode = STATE_HEAT_COOL
self.device.mode = device_mode
@property
def operation_list(self):
@@ -179,9 +187,9 @@ class NestThermostat(ClimateDevice):
@property
def current_fan_mode(self):
"""Return whether the fan is on."""
if self.device.has_fan:
if self._has_fan:
# Return whether the fan is on
return STATE_ON if self.device.fan else STATE_AUTO
return STATE_ON if self._fan else STATE_AUTO
else:
# No Fan available so disable slider
return None
@@ -198,21 +206,29 @@ class NestThermostat(ClimateDevice):
@property
def min_temp(self):
"""Identify min_temp in Nest API or defaults if not available."""
temp = self.device.away_temperature.low
if temp is None:
return super().min_temp
else:
return temp
return self._min_temperature
@property
def max_temp(self):
"""Identify max_temp in Nest API or defaults if not available."""
temp = self.device.away_temperature.high
if temp is None:
return super().max_temp
else:
return temp
return self._max_temperature
def update(self):
"""Python-nest has its own mechanism for staying up to date."""
pass
"""Cache value from Python-nest."""
self._location = self.device.where
self._name = self.device.name
self._humidity = self.device.humidity,
self._temperature = self.device.temperature
self._mode = self.device.mode
self._target_temperature = self.device.target
self._fan = self.device.fan
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_scale == 'C':
self._temperature_scale = TEMP_CELSIUS
else:
self._temperature_scale = TEMP_FAHRENHEIT
@@ -54,7 +54,6 @@ def setup_platform(hass, config, add_callback_devices, discovery_info=None):
return None
# pylint: disable=abstract-method
class NetatmoThermostat(ClimateDevice):
"""Representation a Netatmo thermostat."""
@@ -112,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."""
@@ -120,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."""
@@ -132,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):
+12 -3
View File
@@ -7,12 +7,13 @@ https://home-assistant.io/components/climate.proliphix/
import voluptuous as vol
from homeassistant.components.climate import (
STATE_COOL, STATE_HEAT, STATE_IDLE, ClimateDevice, PLATFORM_SCHEMA)
PRECISION_TENTHS, STATE_COOL, STATE_HEAT, STATE_IDLE,
ClimateDevice, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, TEMP_FAHRENHEIT, ATTR_TEMPERATURE)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['proliphix==0.4.0']
REQUIREMENTS = ['proliphix==0.4.1']
ATTR_FAN = 'fan'
@@ -36,7 +37,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices([ProliphixThermostat(pdp)])
# pylint: disable=abstract-method
class ProliphixThermostat(ClimateDevice):
"""Representation a Proliphix thermostat."""
@@ -61,6 +61,15 @@ class ProliphixThermostat(ClimateDevice):
"""Return the name of the thermostat."""
return self._name
@property
def precision(self):
"""Return the precision of the system.
Proliphix temperature values are passed back and forth in the
API as tenths of degrees F (i.e. 690 for 69 degrees).
"""
return PRECISION_TENTHS
@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
+59 -15
View File
@@ -6,7 +6,6 @@ https://home-assistant.io/components/climate.radiotherm/
"""
import datetime
import logging
from urllib.error import URLError
import voluptuous as vol
@@ -24,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),
})
@@ -46,24 +54,27 @@ 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))
except (URLError, OSError):
tstats.append(RadioThermostat(tstat, hold_temp, away_temps))
except OSError:
_LOGGER.exception("Unable to connect to Radio Thermostat: %s",
host)
add_devices(tstats)
# pylint: disable=abstract-method
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 +82,12 @@ class RadioThermostat(ClimateDevice):
self._current_temperature = None
self._current_operation = STATE_IDLE
self._name = None
self.hold_temp = hold_temp
self._fmode = None
self._tmode = None
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]
@@ -89,8 +105,8 @@ class RadioThermostat(ClimateDevice):
def device_state_attributes(self):
"""Return the device specific state attributes."""
return {
ATTR_FAN: self.device.fmode['human'],
ATTR_MODE: self.device.tmode['human']
ATTR_FAN: self._fmode,
ATTR_MODE: self._tmode,
}
@property
@@ -113,14 +129,22 @@ 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']
self._name = self.device.name['raw']
if self.device.tmode['human'] == 'Cool':
self._fmode = self.device.fmode['human']
self._tmode = self.device.tmode['human']
if self._tmode == 'Cool':
self._target_temperature = self.device.t_cool['raw']
self._current_operation = STATE_COOL
elif self.device.tmode['human'] == 'Heat':
elif self._tmode == 'Heat':
self._target_temperature = self.device.t_heat['raw']
self._current_operation = STATE_HEAT
else:
@@ -132,10 +156,10 @@ class RadioThermostat(ClimateDevice):
if temperature is None:
return
if self._current_operation == STATE_COOL:
self.device.t_cool = temperature
self.device.t_cool = round(temperature * 2.0) / 2.0
elif self._current_operation == STATE_HEAT:
self.device.t_heat = temperature
if self.hold_temp:
self.device.t_heat = round(temperature * 2.0) / 2.0
if self._hold_temp or self._away:
self.device.hold = 1
else:
self.device.hold = 0
@@ -156,6 +180,26 @@ class RadioThermostat(ClimateDevice):
elif operation_mode == STATE_AUTO:
self.device.tmode = 3
elif operation_mode == STATE_COOL:
self.device.t_cool = self._target_temperature
self.device.t_cool = round(self._target_temperature * 2.0) / 2.0
elif operation_mode == STATE_HEAT:
self.device.t_heat = self._target_temperature
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)
+38 -2
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
+11 -3
View File
@@ -8,7 +8,10 @@ import logging
from homeassistant.util import convert
from homeassistant.components.climate import ClimateDevice
from homeassistant.const import TEMP_FAHRENHEIT, ATTR_TEMPERATURE
from homeassistant.const import (
TEMP_FAHRENHEIT,
TEMP_CELSIUS,
ATTR_TEMPERATURE)
from homeassistant.components.vera import (
VeraDevice, VERA_DEVICES, VERA_CONTROLLER)
@@ -28,7 +31,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
device in VERA_DEVICES['climate'])
# pylint: disable=abstract-method
class VeraThermostat(VeraDevice, ClimateDevice):
"""Representation of a Vera Thermostat."""
@@ -95,7 +97,13 @@ class VeraThermostat(VeraDevice, ClimateDevice):
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_FAHRENHEIT
vera_temp_units = (
self.vera_device.vera_controller.temperature_units)
if vera_temp_units == 'F':
return TEMP_FAHRENHEIT
return TEMP_CELSIUS
@property
def current_temperature(self):
+468
View File
@@ -0,0 +1,468 @@
"""
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, DOMAIN
from homeassistant.components.climate import (
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
ATTR_TEMPERATURE,
ATTR_CURRENT_HUMIDITY)
from homeassistant.const import (
TEMP_CELSIUS, STATE_ON,
STATE_OFF, STATE_UNKNOWN)
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"
ATTR_ECO_TARGET = "eco_target"
ATTR_OCCUPIED = "occupied"
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink thermostat."""
import pywink
temp_unit = hass.config.units.temperature_unit
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
class WinkThermostat(WinkDevice, ClimateDevice):
"""Representation of a Wink thermostat."""
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)
if self.external_temperature:
data[ATTR_EXTERNAL_TEMPERATURE] = self._convert_for_display(
self.external_temperature)
if self.smart_temperature:
data[ATTR_SMART_TEMPERATURE] = self.smart_temperature
if self.occupied:
data[ATTR_OCCUPIED] = self.occupied
if self.eco_target:
data[ATTR_ECO_TARGET] = self.eco_target
current_humidity = self.current_humidity
if current_humidity is not None:
data[ATTR_CURRENT_HUMIDITY] = current_humidity
return data
@property
def current_temperature(self):
"""Return the current temperature."""
return self.wink.current_temperature()
@property
def current_humidity(self):
"""Return the current humidity."""
if self.wink.current_humidity() is not None:
# The API states humidity will be a float 0-1
# the only example API response with humidity listed show an int
# This will address both possibilities
if self.wink.current_humidity() < 1:
return self.wink.current_humidity() * 100
else:
return self.wink.current_humidity()
@property
def external_temperature(self):
"""Return the current external temperature."""
return self.wink.current_external_temperature()
@property
def smart_temperature(self):
"""Return the current average temp of all remote sensor."""
return self.wink.current_smart_temperature()
@property
def eco_target(self):
"""Return status of eco target (Is the termostat in eco mode)."""
return self.wink.eco_target()
@property
def occupied(self):
"""Return status of if the thermostat has detected occupancy."""
return self.wink.occupied()
@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_hvac_mode() == 'cool_only':
current_op = STATE_COOL
elif self.wink.current_hvac_mode() == 'heat_only':
current_op = STATE_HEAT
elif self.wink.current_hvac_mode() == 'aux':
current_op = STATE_HEAT
elif self.wink.current_hvac_mode() == 'auto':
current_op = STATE_AUTO
elif self.wink.current_hvac_mode() == 'eco':
current_op = STATE_ECO
else:
current_op = STATE_UNKNOWN
return current_op
@property
def target_humidity(self):
"""Return the humidity we try to reach."""
target_hum = None
if self.wink.current_humidifier_mode() == 'on':
if self.wink.current_humidifier_set_point() is not None:
target_hum = self.wink.current_humidifier_set_point() * 100
elif self.wink.current_dehumidifier_mode() == 'on':
if self.wink.current_dehumidifier_set_point() is not None:
target_hum = self.wink.current_dehumidifier_set_point() * 100
else:
target_hum = None
return target_hum
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
if self.current_operation != STATE_AUTO and not self.is_away_mode_on:
if self.current_operation == STATE_COOL:
return self.wink.current_max_set_point()
elif self.current_operation == STATE_HEAT:
return self.wink.current_min_set_point()
else:
return None
else:
return None
@property
def target_temperature_low(self):
"""Return the lower bound temperature we try to reach."""
if self.current_operation == STATE_AUTO:
return self.wink.current_min_set_point()
return None
@property
def target_temperature_high(self):
"""Return the higher bound temperature we try to reach."""
if self.current_operation == STATE_AUTO:
return self.wink.current_max_set_point()
return None
@property
def is_away_mode_on(self):
"""Return if away mode is on."""
return self.wink.away()
@property
def is_aux_heat_on(self):
"""Return true if aux heater."""
if self.wink.current_hvac_mode() == 'aux' and self.wink.is_on():
return True
elif self.wink.current_hvac_mode() == 'aux' and not self.wink.is_on():
return False
else:
return None
def set_temperature(self, **kwargs):
"""Set new target temperature."""
target_temp = kwargs.get(ATTR_TEMPERATURE)
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
if target_temp is not None:
if self.current_operation == STATE_COOL:
target_temp_high = target_temp
if self.current_operation == STATE_HEAT:
target_temp_low = target_temp
if target_temp_low is not None:
target_temp_low = target_temp_low
if target_temp_high is not None:
target_temp_high = target_temp_high
self.wink.set_temperature(target_temp_low, target_temp_high)
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
if operation_mode == STATE_HEAT:
self.wink.set_operation_mode('heat_only')
elif operation_mode == STATE_COOL:
self.wink.set_operation_mode('cool_only')
elif operation_mode == STATE_AUTO:
self.wink.set_operation_mode('auto')
elif operation_mode == STATE_OFF:
self.wink.set_operation_mode('off')
elif operation_mode == STATE_AUX:
self.wink.set_operation_mode('aux')
elif operation_mode == STATE_ECO:
self.wink.set_operation_mode('eco')
@property
def operation_list(self):
"""List of available operation modes."""
op_list = ['off']
modes = self.wink.hvac_modes()
if 'cool_only' in modes:
op_list.append(STATE_COOL)
if 'heat_only' in modes or 'aux' in modes:
op_list.append(STATE_HEAT)
if 'auto' in modes:
op_list.append(STATE_AUTO)
if 'eco' in modes:
op_list.append(STATE_ECO)
return op_list
def turn_away_mode_on(self):
"""Turn away on."""
self.wink.set_away_mode()
def turn_away_mode_off(self):
"""Turn away off."""
self.wink.set_away_mode(False)
@property
def current_fan_mode(self):
"""Return whether the fan is on."""
if self.wink.current_fan_mode() == 'on':
return STATE_ON
elif self.wink.current_fan_mode() == 'auto':
return STATE_AUTO
else:
# No Fan available so disable slider
return None
@property
def fan_list(self):
"""List of available fan modes."""
if self.wink.has_fan():
return self.wink.fan_modes()
return None
def set_fan_mode(self, fan):
"""Turn fan on/off."""
self.wink.set_fan_mode(fan.lower())
def turn_aux_heat_on(self):
"""Turn auxillary heater on."""
self.set_operation_mode(STATE_AUX)
def turn_aux_heat_off(self):
"""Turn auxillary heater off."""
self.set_operation_mode(STATE_AUTO)
@property
def min_temp(self):
"""Return the minimum temperature."""
minimum = 7 # Default minimum
min_min = self.wink.min_min_set_point()
min_max = self.wink.min_max_set_point()
return_value = minimum
if self.current_operation == STATE_HEAT:
if min_min:
return_value = min_min
else:
return_value = minimum
elif self.current_operation == STATE_COOL:
if min_max:
return_value = min_max
else:
return_value = minimum
elif self.current_operation == STATE_AUTO:
if min_min and min_max:
return_value = min(min_min, min_max)
else:
return_value = minimum
else:
return_value = minimum
return return_value
@property
def max_temp(self):
"""Return the maximum temperature."""
maximum = 35 # Default maximum
max_min = self.wink.max_min_set_point()
max_max = self.wink.max_max_set_point()
return_value = maximum
if self.current_operation == STATE_HEAT:
if max_min:
return_value = max_min
else:
return_value = maximum
elif self.current_operation == STATE_COOL:
if max_max:
return_value = max_max
else:
return_value = maximum
elif self.current_operation == STATE_AUTO:
if max_min and max_max:
return_value = min(max_min, max_max)
else:
return_value = maximum
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)

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