Compare commits

...

607 Commits

Author SHA1 Message Date
Paulus Schoutsen 50bf147d73 Merge pull request #1365 from balloob/dev
0.14
2016-02-27 16:21:54 -08:00
Paulus Schoutsen 3bdf7eabbf Version bump to 0.14.0 2016-02-27 16:13:40 -08:00
Paulus Schoutsen 4e750a4d72 Merge pull request #1417 from fabaff/freegeoip
Catch 503 responses by freegeoip.net (Fixes #1378)
2016-02-27 16:12:47 -08:00
Paulus Schoutsen a1cd120ed4 Merge pull request #1422 from balloob/rename-command-line-platforms
Make names command line platform consistent
2016-02-27 16:03:00 -08:00
Paulus Schoutsen be55ee059e Make names command line platform consistent 2016-02-27 15:56:35 -08:00
Fabian Affolter eb8228237e Catch connection issue with freegeoip.net (Fixes #1378) 2016-02-27 23:58:36 +01:00
Paulus Schoutsen afb51d14b8 Merge pull request #1302 from persandstrom/vsure_maintenace
updated verisure reconnect
2016-02-27 14:50:24 -08:00
Paulus Schoutsen bca4dee30e Merge pull request #1393 from persandstrom/throttle_fix
Throttle should work on a single method
2016-02-27 14:49:56 -08:00
Paulus Schoutsen 9e4ddc405d Update frontend 2016-02-27 14:34:52 -08:00
Paulus Schoutsen 73ec049d1c Do not expect Hue light to have a brightness 2016-02-27 14:27:16 -08:00
Paulus Schoutsen efeb5c5290 Merge pull request #1419 from justincmoy/sendgrid_initial
Add SendGrid notify component
2016-02-27 14:24:07 -08:00
Paulus Schoutsen 498a3f78d4 Merge pull request #1413 from fabaff/issue
Templates for PRs and issue
2016-02-27 14:19:49 -08:00
Per Sandström 562db5ea4c Throttle for two methonds in same class 2016-02-27 23:18:56 +01:00
Fabian Affolter cd21142d5b Update with comments from PR 2016-02-27 23:17:20 +01:00
Justin Moy 9b371a8c66 Add SendGrid notify component 2016-02-27 15:00:52 -07:00
Per Sandström d7e3c6a442 verisure refactoring and fix reconnect 2016-02-27 21:50:19 +01:00
Paulus Schoutsen f4b5b3f88f Merge pull request #1411 from shaftoe/sonos_decorator
ADD only_if_coordinator decorator to sonos
2016-02-27 09:05:54 -08:00
Paulus Schoutsen a0c58487ca Merge pull request #1415 from kfgoode/patch-1
Update demo.py
2016-02-27 08:50:13 -08:00
Fabian Affolter f8f0ed860c Sync with template content 2016-02-27 12:53:06 +01:00
Fabian Affolter f99a536b98 Add initial templates 2016-02-27 10:03:55 +01:00
Karen Goode 02faefdab3 Update demo.py 2016-02-26 18:43:50 -08:00
Karen Goode 2a01b09d31 Update demo.py 2016-02-26 18:25:39 -08:00
Karen Goode 712ba65d26 Update demo.py 2016-02-26 18:12:03 -08:00
Alexander Fortin 124a9b7a81 ADD only_if_coordinator decorator to sonos
Currently we interact with players regardless of thir coordinator
role, hence soco.exceptions.SoCoSlaveException are thrown. The use
of the decorator for each interactive method should address this
2016-02-27 00:40:34 +01:00
Karen Goode cbd69583bd Update demo.py
Update to demo site to include input_selects, notify, inline camera and two views.
2016-02-26 15:23:44 -08:00
Paulus Schoutsen 2ec141b5d4 Merge pull request #1403 from stjohnjohnson/MQTTLightScale
Add scale support for MQTT Lights.
2016-02-26 15:19:08 -08:00
Fabian Affolter 2609852d6e Modify docstrings to match PEP257 2016-02-26 23:52:54 +01:00
St. John Johnson e525b6e0a5 Add brightness scale support for MQTT Lights.
Converts 0..255 values that HA expects into a device 0..SCALE value
Example:
  HA considers "hall light" at 25 brightness or 10% of 255
  Device considers "hall light" at 100 brightness or 10% of 1000

This allows our existing MQTT devices to not change their data format to be used in HA
2016-02-26 10:38:07 -08:00
Daniel Høyer Iversen 278fdc0983 Merge pull request #1385 from balloob/rfxtrx
improve rfxtrx sensor
2016-02-26 10:49:10 +01:00
Greg Dowling 6dc49df2df Merge pull request #1405 from balloob/wemo_insight_updates
Fix typo in Wemo state_detail attribute.
2016-02-26 08:46:54 +00:00
pavoni b2d32114c8 Fix typo. 2016-02-26 08:28:17 +00:00
Paulus Schoutsen 6512b7d701 Merge pull request #1380 from w1ll1am23/wink_binary_sensor
Moved Wink binary sensors to a binary sensor class
2016-02-25 18:19:38 -08:00
Paulus Schoutsen 782713ee4f Merge pull request #1398 from GreenTurtwig/steam
Update Steam sensor to show currently played game
2016-02-25 18:14:54 -08:00
Paulus Schoutsen dd45b5e0b1 Merge pull request #1402 from kk7ds/fix-mfi-connect-exception
Fix mFi error handling in setup_platform
2016-02-25 17:06:03 -08:00
Greg Dowling f92802fd5c Merge pull request #1401 from balloob/wemo_insight_updates
Wemo updates
2016-02-26 00:23:17 +00:00
pavoni ddbceebd65 Cast standby_state to int before testing. 2016-02-26 00:16:33 +00:00
Dan Smith 37e5b98919 Fix mFi error handling in setup_platform
The exception we were catching incorrectly referenced the client variable
in local scope instead of the module. Also, if we fail to connect we can
get a requests exception, so catch and handle that as well.
2016-02-25 15:52:13 -08:00
pavoni 86973226b1 Remove ghost comment. 2016-02-25 23:13:20 +00:00
pavoni bbf8897a51 Add LightSwitch to discovery, default unnown devices to switches for compatability. 2016-02-25 23:02:36 +00:00
pavoni 72144945b4 Move standby from state to attribute, add optimistic switch status. 2016-02-25 22:46:14 +00:00
William Scanlon 5a64ef2c98 Moved Wink binary sensors to a binary sensor class 2016-02-25 14:03:02 -05:00
Rowan Hine 393df2da49 Update steam_online.py 2016-02-25 17:46:51 +00:00
Rowan Hine a54986159c Update Steam sensor to show currently played game 2016-02-25 17:05:00 +00:00
Daniel 8ffa3684e3 rfxtrx refactor 2016-02-25 17:40:24 +01:00
Paulus Schoutsen dc02370b43 Merge pull request #1355 from deisi/deutsche_bahn
New deutsche_bahn component
2016-02-25 08:31:47 -08:00
Daniel 23db6e753f refactor rfxtrx code 2016-02-25 13:08:16 +01:00
Paulus Schoutsen 35aa9aa863 Merge pull request #1383 from kk7ds/virtual-binary-sensor
Template binary sensor
2016-02-24 22:15:01 -08:00
Paulus Schoutsen 57ac6cd76f Merge branch 'extract-entity-picture' into dev
Conflicts:
	homeassistant/components/sensor/steam_online.py
2016-02-24 22:00:58 -08:00
Dan Smith a7519bb38b Add a template binary_sensor platform 2016-02-24 20:09:13 -08:00
Dan Smith db4bf7319f Add float() operator to templates 2016-02-24 20:07:46 -08:00
Paulus Schoutsen 621a6e0ea0 Merge pull request #1395 from MartinHjelmare/fix-mysensors-switch-light
Fix mysensors switch & light types
2016-02-24 19:56:36 -08:00
Paulus Schoutsen caa1b73035 Merge pull request #1394 from MartinHjelmare/use-get_component-mysensors
Use get_component instead of importing component for mysensors
2016-02-24 19:55:03 -08:00
MartinHjelmare 9c538d11c2 Fix mysensors switch & light types
* Move S_LIGHT and V_LIGHT from light back to switch platform, to avoid
  double devices showing.
* Remove MySensorsLightPlain class from light platform, since it's not
  needed anymore.
* A light switch with only a switch, ie no dimmer or
  RGB controls, will show as a regular switch device.
2016-02-25 00:48:29 +01:00
MartinHjelmare 2790ee0b02 Use get_component instead of importing component 2016-02-25 00:24:31 +01:00
Paulus Schoutsen eb681e721c Merge pull request #1387 from stefan-jonasson/z_wave_workaround_fix
Fixed the workaround for philio slim multi sensor.
2016-02-24 14:45:27 -08:00
Stefan Jonasson db06d5a03d Fixed the z-wave trigger sensor workaround 2016-02-24 22:44:49 +01:00
Dan Smith eb5f208a09 Add a few more sensor classes
This adds hot, cold, and moving sensor_class values for things that we
may want to support.
2016-02-24 10:38:21 -08:00
Malte Deiseroth b096004449 New deutsche_bahn component
Uses the (schiene)[https://pypi.python.org/pypi/schiene/0.14] API to communicate with the webserver of bahn.de
and pulls iformation about a specific connection from the (bahn.de)[http://www.bahn.de/p/view/index.shtml]
webpage. The departure time of the next train for the given connection is shown.
In case of delay, the delay is also shown. Additional `ATTRIBUTES` are used to
inform about e.g. the type of the train, price and if it is ontime.

Usage:

    sensor:
      platform: deutsche_bahn
        from: name_of_start_station
	  to: name_of_final_station

Problems:
I'm testing it for quite some time, but I have never seen the `ATTRIBUTES` in case
of a delayed train. The `ATTRIBUTES` are directly passed from the `schiene` API. So this
usecase has not been tested yet.

deutsche_bahn ist not supporting the `schiene` api unlike in the swiss_public_transport case.
It's not guaranteed that `schiene` will work forever, infact it can happen that Bahn AG will
intentionally brake the API at some point. In the past Bahn AG has not allways been very supportive
to the opensource community.
2016-02-24 17:35:00 +01:00
Paulus Schoutsen c2729211ad Merge pull request #1390 from flyte/tcp-component-tests
Bring TCP component test coverage to 100%
2016-02-24 07:33:34 -08:00
Flyte 8840461af3 Bring TCP component test coverage to 100% 2016-02-24 13:28:39 +00:00
Fabian Affolter 5406028ace Modify docstrings to match PEP257 2016-02-24 10:47:35 +01:00
Fabian Affolter 4563c54a3e Add link to docs and modify docstrings to match PEP257 2016-02-24 10:38:06 +01:00
Stefan Jonasson 04b48e0683 Moved the workaround for philio slim multi sensor.
Changed the key matching to utilize integer values. It does not report always return the hex marker '0x' in the identifier strings. This Re closes issue #1349
2016-02-24 10:28:49 +01:00
Fabian Affolter 3f82b9d6b0 Add link to docs and modify docstrings to match PEP257 2016-02-24 10:07:54 +01:00
Paulus Schoutsen 2b839de854 Merge pull request #1372 from balloob/rfxtrx_bug_fix
Fix issue #1301
2016-02-23 23:40:17 -08:00
Paulus Schoutsen 967a751da1 Add entity_picture property 2016-02-23 22:41:24 -08:00
Paulus Schoutsen 8dc6443d14 Merge pull request #1364 from GreenTurtwig/steam
Added a Steam sensor
2016-02-23 21:31:27 -08:00
Dan Smith 537808f9be Merge pull request #1382 from kk7ds/uvc-ignore-aircams
Filter out AirCam models in UVC camera platform
2016-02-23 15:39:58 -08:00
Paulus Schoutsen 49e588deb3 Merge pull request #1381 from fabaff/template-helpers
Template helpers
2016-02-23 13:38:19 -08:00
Fabian Affolter 6b4dd64cfe Add break to fix length of lines 2016-02-23 22:06:45 +01:00
Fabian Affolter a47d6c944e Update docstrings to match PEP257 2016-02-23 21:31:38 +01:00
Fabian Affolter 582394bc3b Modify import of template and PEP257 2016-02-23 21:19:22 +01:00
Fabian Affolter 213cc920d0 Move template to helpers 2016-02-23 21:19:10 +01:00
Dan Smith 05a1e11db2 Filter out AirCam models in UVC camera platform
The older (unsupported AirCam) models behave differently and also apparently
suffer some under the last release of the NVR that supported them. Since they
are EOL and not supported by current software, filter them out so we don't
break while trying to extract an image from them.
2016-02-23 12:01:51 -08:00
Rowan Hine f8240a9cda Changed to use dictionary 2016-02-23 18:02:48 +00:00
Paulus Schoutsen d6a14a1767 Merge pull request #1379 from jaharkes/wemo_switch
Fix dispatching for regular WeMo switch devices.
2016-02-23 07:22:25 -08:00
Jan Harkes 9b0b3c474e Fix dispatching of WeMo switch devices.
I only have WeMo Link and Insight devices and assumed model names of other
devices were fairly straightforward.

But it looks like the regular WeMo switch uses 'Socket' as the model name.
2016-02-23 10:05:14 -05:00
Rowan Hine 532d807771 Add Steam sensor 2016-02-23 09:35:27 +00:00
Fabian Affolter 60d579af84 Update/add docstrings (PEP257) 2016-02-23 06:23:04 +01:00
Paulus Schoutsen c64da761f1 Merge pull request #1359 from balloob/template-helpers
New template helpers now, utcnow, distance, closest
2016-02-22 19:20:49 -08:00
Dan Smith 2d3d674295 Merge pull request #1376 from kk7ds/uvc-passwords-fixes-tests
UVC Fixes, features, and tests
2016-02-22 15:17:57 -08:00
Dan Smith 590512916a Add tests for camera.uvc and fix bugs found in the process
This adds tests for the uvc camera module. It's a good thing too,
because I found a few bugs which are fixed here as well:

 - Graceful handling of non-integer port
 - Failure to take the first host that works when probing host,internalHost
 - Failure to detect if neither of them actually work

This also converts the code to only call add_devices once with a listcomp.
2016-02-22 14:37:57 -08:00
Dan Smith d398832112 Make UVC cameras honor the local camera password store, if set
The NVR tells us the admin username, but not the password for the
camera. Right now, we assume the default password, which obviously
doesn't work for people that have changed it. The uvcclient library
provides a way to set the cached admin password for a camera, which
is stored in a client-specific location. We can utilize that to
grab the password, falling back to the default if it's unset. With
this, people just need to run a command per camera to set the
admin password on their systems, if it has changed.
2016-02-22 13:17:53 -08:00
Paulus Schoutsen 88dc7a08c4 Merge pull request #1346 from stefan-jonasson/add_for_to_conditions
Added the "for" param to the conditions as well
2016-02-22 07:49:35 -08:00
Paulus Schoutsen 7cc3b8d7b1 Merge pull request #1371 from davidedmundson/dev
Fix service documentation appearing for notifications
2016-02-22 07:37:08 -08:00
Paulus Schoutsen 09ff394abc Merge pull request #1369 from t30/test-command_rollershutter
Added command_rollershutter component
2016-02-22 07:36:14 -08:00
Paulus Schoutsen b294383f20 Merge pull request #1368 from Gerto/dev
Don't set Bitcoin wallet if not setup in configuration
2016-02-22 07:35:24 -08:00
Fabian Affolter 5bd0b5ab99 Update docstring 2016-02-22 14:43:12 +01:00
Fabian Affolter 7d9e882d52 Update docstrings to match PEP257 2016-02-22 14:42:11 +01:00
Daniel 1f842140ef Fix issue #1301 2016-02-22 13:45:01 +01:00
David Edmundson 8d5a164346 Fix service documentation appearing for notifications
We want the descriptions for notify.notify regardless of the name of the
exact notify service being called.
2016-02-22 11:14:05 +00:00
Gert cbe27d8f1a Don't set wallet if not setup in configuration
Because of problems with the Wallet part of python blockchain library (see #1242 ) , the entire Bitcoin module isn't working currently.
This change does not fix those problems but at least makes the sensor work again for people who don't need Wallet-related functionality.

It also just seems better practice to not set a wallet and call "wallet.get_balance()" when not wallet is set in configuration.
2016-02-22 11:44:00 +01:00
t30 066b569cfe Added command_rollershutter component 2016-02-22 10:43:29 +00:00
Fabian Affolter fd3ea95b82 Add docstrings, fix typos, and update docsstring for PEP257 2016-02-22 10:11:46 +01:00
Stefan Jonasson e4485dcf3d Updated structure, added more tests 2016-02-22 09:40:27 +01:00
Paulus Schoutsen 7a0c99a1d5 Update frontend 2016-02-21 23:14:36 -08:00
Paulus Schoutsen 7c15f6a4b3 Merge pull request #1332 from jaharkes/wemo_leds
Wemo leds
2016-02-21 20:52:55 -08:00
Jan Harkes afe564fb3f Move WeMo discovery into a common component 2016-02-21 22:23:00 -05:00
Paulus Schoutsen 82e11e237e Merge pull request #1268 from flyte/tcp-component-pr
Add generic TCP socket component
2016-02-21 19:21:20 -08:00
Jan Harkes 4dad40fffb Add support for WeMo LED lights. 2016-02-21 21:57:53 -05:00
Jan Harkes 368ad93eb6 Only add discovered switches instead of any WeMo device. 2016-02-21 21:57:53 -05:00
Paulus Schoutsen 92be572374 Merge pull request #1351 from tpatja/zwave_binary_sensor
ZWave binary sensor support
2016-02-21 13:57:06 -08:00
Paulus Schoutsen c77266c544 Merge pull request #1357 from w1ll1am23/update_wink_for_binary_switch_fix
Updated wink version for binary switch fix
2016-02-21 13:50:59 -08:00
Paulus Schoutsen aa748e3e48 Merge pull request #1358 from andythigpen/script-improvements
Script improvements
2016-02-21 13:49:49 -08:00
Paulus Schoutsen 3d8e9b4261 Lint fixes 2016-02-21 13:09:49 -08:00
Paulus Schoutsen 7c6dcdb082 Catch an extra error that could break util.convert 2016-02-21 11:23:16 -08:00
Paulus Schoutsen c3bb6d32aa Add closest template helper 2016-02-21 11:13:40 -08:00
Paulus Schoutsen 9f5f13644a Add template multiply test 2016-02-21 11:12:37 -08:00
Paulus Schoutsen 7805d2800c Expose current time object instead of method to retrieve (thanks @fabaff) 2016-02-21 09:32:43 -08:00
Andrew Thigpen 7dd529356a Add toggle support for scripts. 2016-02-21 11:22:38 -06:00
Paulus Schoutsen 070cee48a8 Merge pull request #1353 from balloob/assume-state
Add assumed_state to Group, MQTT Switch, MQTT Light
2016-02-21 09:19:54 -08:00
Andrew Thigpen b961b5037f Only turn on scripts that are not currently running.
Prevents an errant API call from advancing a currently executing delay
step before it should.
2016-02-21 11:11:35 -06:00
William Scanlon 3da554a198 Update wink version 2016-02-21 12:01:32 -05:00
Teemu Patja d1a4dc77d1 Disable pylint warning 2016-02-21 10:01:03 +02:00
Paulus Schoutsen ff9568ad26 Merge pull request #1348 from LinuxChristian/dev
Added support for d-link W215 smart plug
2016-02-20 23:49:57 -08:00
Christian Fredborg Braedstrup 2f66528595 Added support for d-link W215 smart plug 2016-02-21 08:34:07 +01:00
Paulus Schoutsen 9ad2cf7b7a Adjust template rounding tests 2016-02-20 21:59:16 -08:00
Paulus Schoutsen 6ac54b20c7 Add template distance helper 2016-02-20 21:58:53 -08:00
Paulus Schoutsen 6847dac582 Expose current time in templates
Fixes #1282
2016-02-20 20:58:01 -08:00
Paulus Schoutsen 7f81122af6 Update frontend with latest version 2016-02-20 20:46:40 -08:00
Paulus Schoutsen 1eae74be58 Add assumed_state to group 2016-02-20 19:11:02 -08:00
Paulus Schoutsen 443b39bccd MQTT Switch to expose assumed_state if optimistic 2016-02-20 17:17:30 -08:00
Paulus Schoutsen 8f70630790 MQTT Light to expose assumed_state if optimistic 2016-02-20 17:17:22 -08:00
Teemu Patja 5053c807c0 ZWave binary sensor support
Treat ZWave binary sensors as binary_sensor components instead of
regular sensors.
2016-02-20 23:57:59 +02:00
Stefan Jonasson 6e7ca9505c Removed unused import 2016-02-20 22:25:41 +01:00
Stefan Jonasson f3c95adaca Fixed now => utcnow
Fixed added time patch to unittest
2016-02-20 22:15:49 +01:00
Paulus Schoutsen 6532eae3d5 Merge pull request #1343 from balloob/random-cleanup
Random cleanup
2016-02-20 12:16:54 -08:00
Stefan Jonasson ff6e071dff added the for param to the conditions as well 2016-02-20 12:52:42 +01:00
Paulus Schoutsen cd6d44ece3 Properly clean up Home Assistant instances in tests 2016-02-20 01:05:52 -08:00
Paulus Schoutsen 22b47ce9c6 Merge pull request #1314 from balloob/fix_own_tracks_mobile_beacon
Fix own tracks mobile beacon race condition
2016-02-20 00:27:07 -08:00
Paulus Schoutsen f4e0f1d895 Update frontend 2016-02-20 00:11:07 -08:00
Paulus Schoutsen 8a1fa82205 Add sensor class to bloomsky binary_sensor 2016-02-20 00:08:02 -08:00
Paulus Schoutsen fb9d8c79b5 Bloomsky main component to load its platforms 2016-02-19 23:22:23 -08:00
Paulus Schoutsen f5f52010d1 Extract bloomsky binary_sensor 2016-02-19 23:21:56 -08:00
Paulus Schoutsen 1bfea626ff Handle circular setup dependency 2016-02-19 23:20:14 -08:00
Paulus Schoutsen 4c538c718b Clean up binary_sensor 2016-02-19 22:23:25 -08:00
Paulus Schoutsen 63a27f1943 Add discover helper method to discovery component 2016-02-19 22:23:01 -08:00
Paulus Schoutsen 70aa605396 Merge pull request #1340 from MartinHjelmare/mysensors-binary-sensor
Add mysensors binary sensor
2016-02-19 20:14:42 -08:00
MartinHjelmare 08aaea5444 Add mysensors binary sensor
* Add mysensors binary sensor.
* Add discovery platforms to binary_sensor base component.
* Replace device_state_attributes with state_attributes in
	binary_sensor base class.
* Fix docstrings.
* Add discovery of binary sensor to mysensors component.
* Add child.type as argument to mysensors device_class.
* Move binary sensor types from sensor to binary_sensor module.
* Fix binary_sensor attribute tests. Use state_attributes instead of
	device_state_attributes.
2016-02-20 04:28:17 +01:00
Paulus Schoutsen 2d0721abe8 Merge pull request #1338 from kk7ds/unifi-tracker
Add Ubiquiti Unifi device tracker
2016-02-19 18:41:01 -08:00
Paulus Schoutsen 4128d083e6 Merge pull request #1339 from MartinHjelmare/fix-mysensors-temp
Fix temperature unit
2016-02-19 18:18:35 -08:00
MartinHjelmare 52131a3335 Fix temperature unit
* Specify temperature unit according to gateway setting, to avoid
	converting to Fahrenheit when already Fahrenheit.
* Fix docstrings in wrapper class.
2016-02-20 02:11:15 +01:00
Dan Smith 27f456ca70 Add Ubiquiti Unifi device tracker
Ubiquiti's Unifi WAP infrastructure has a central controller (like mfi and uvc)
that can be queried for client status. This adds a device_tracker module that
can report the state of any client connected to the controller.
2016-02-19 15:24:00 -08:00
Fabian Affolter f9385eb87a UPdate docstring 2016-02-19 23:58:45 +01:00
Paulus Schoutsen 1c8c16f85f Merge pull request #1220 from stefan-jonasson/command_line.py
New notification platform "command line"
2016-02-19 14:15:40 -08:00
Stefan Jonasson c85875ddf4 removed command_line from coveragerc 2016-02-19 22:34:04 +01:00
Stefan Jonasson fa6d9adcba Added some unittests for the command_line notification 2016-02-19 22:30:38 +01:00
Flyte 4e6b755b26 Add tests for TCP component. 2016-02-19 17:41:51 +00:00
Greg Dowling d019b7f6b8 Merge pull request #1334 from balloob/add_for_to_automation
Add `for` to state automation.
2016-02-19 16:58:04 +00:00
pavoni a75833cf2b Remove debug. 2016-02-19 16:41:05 +00:00
Paulus Schoutsen f20ea41538 Merge pull request #1322 from w1ll1am23/nest_weather_component
Added support for nest current weather conditions
2016-02-19 08:24:07 -08:00
Paulus Schoutsen 97e1f0be98 Add neurio to .coveragerc 2016-02-19 08:13:11 -08:00
pavoni 85bf9a49ea Remove traces. 2016-02-19 15:39:44 +00:00
pavoni 9e48b88154 Add for delay to state automation. 2016-02-19 15:28:17 +00:00
William Scanlon 14afd1e409 Removed debugging logging 2016-02-19 08:26:56 -05:00
pavoni ee62120fe5 Revise race condition test. 2016-02-19 10:19:14 +00:00
pavoni 7bd4e58b9d Add tests for race condition. 2016-02-19 09:43:59 +00:00
Paulus Schoutsen 0417803b7c Merge pull request #1331 from balloob/tox-coveralls-pt2
Travis: only run coveralls on success
2016-02-18 23:30:38 -08:00
Paulus Schoutsen d65c1c2b0d Travis: only run coveralls on success 2016-02-18 23:23:05 -08:00
Paulus Schoutsen 1841f3994f Merge pull request #1250 from balloob/chore/remove-simple-alarm
Remove simple_alarm component
2016-02-18 22:26:58 -08:00
Paulus Schoutsen 5f3cb58d8e Remove simple_alarm component 2016-02-18 22:20:59 -08:00
Paulus Schoutsen e8da63db28 Merge pull request #1330 from balloob/isort-test
Fix imports (using isort)
2016-02-18 21:57:48 -08:00
Paulus Schoutsen e80309c03c Fix imports (using isort) 2016-02-18 21:27:50 -08:00
Paulus Schoutsen 233a2a2878 Merge pull request #1308 from kk7ds/binary-sensor-class
Add binary sensor class
2016-02-18 21:09:17 -08:00
Paulus Schoutsen e677fc876b Merge pull request #1319 from shaftoe/sonosconfig
Fix media_player/sonos configuration mismatch
2016-02-18 20:33:39 -08:00
Paulus Schoutsen 560173e13b Merge pull request #1327 from justyns/zwaveremote
Return early from setup_platform when zwave NETWORK is not configured
2016-02-18 20:27:43 -08:00
Dan Smith d57df2ddde Update the sensor classes in the demo binary_sensor
This updates the two demo sensors we have so they show contextual icons
in the UI.
2016-02-18 17:59:58 -08:00
Justyn Shull 9a740bff2a Return early from setup_platform when zwave NETWORK is not configured 2016-02-18 18:12:00 -06:00
Paulus Schoutsen acea32949b Merge pull request #1326 from balloob/bloomsky-tweak
Fix bloomsky component importing
2016-02-18 14:46:05 -08:00
Paulus Schoutsen 30e1569b21 Fix bloomsky component importing 2016-02-18 13:10:25 -08:00
William Scanlon d7c4b50e3e Added support for nest current weather conditions 2016-02-18 16:04:32 -05:00
Dan Smith d93883f153 Make nx584 expose zone types (sensor classes)
With this, plus https://github.com/balloob/home-assistant-polymer/pull/32,
I can have nx584 sensors use a proper icon in the UI.
2016-02-18 12:20:35 -08:00
Dan Smith 2d932f89fc Add sensor_class to binary_sensor
This adds a 'sensor_class' property and attribute, which should be either
None or one of several defined SENSOR_CLASSES to indicate contextual
information about what the sensor is measuring.
2016-02-18 12:20:35 -08:00
Greg Dowling 182fcb5f03 Merge pull request #1320 from balloob/bump-pywemo-version
Bump pywemo version.
2016-02-18 19:40:47 +00:00
pavoni 98d18c3060 Bump pywemo version. 2016-02-18 19:32:30 +00:00
Alexander Fortin 6578928e3c Fix media_player/sonos configuration mismatch
* From configuration.yaml is easy to provide iterable elements like lists,
  this adds the possibility to provide a list of Sonos hosts using a yaml
  and still supports the comma separated string version
* Remove superfluous host reassignment
2016-02-18 18:57:50 +01:00
Flyte c1d39a2fce Remove unnecessary top-level TCP component. Fix order of inheritance on TCP BinarySensor. 2016-02-18 17:25:02 +00:00
pavoni 9f7ce23e80 Fix suspect race condition in leave region.
Add safely check for double beacon entry. Remove battery for beacons.
Disable lint warning.
2016-02-18 12:52:40 +00:00
Paulus Schoutsen d39e1be388 Merge pull request #1312 from balloob/tox-coveralls
Fix coveralls
2016-02-17 23:37:17 -08:00
Paulus Schoutsen 61b0d02a88 Fix coveralls 2016-02-17 23:26:55 -08:00
Paulus Schoutsen 8eb396a435 Merge pull request #1309 from kk7ds/graphite-debug
More graphite hardening work
2016-02-17 20:30:04 -08:00
Paulus Schoutsen 70f05f30d5 Merge pull request #1243 from MartinHjelmare/refactor-mysensors-light
Refactor mysensors light
2016-02-17 20:27:52 -08:00
MartinHjelmare 9ac53b502f Clean up and fix
* Add check if V_LIGHT is in values before sending message in
	_turn_on_light.
* Replace super calls with self.
* Remove not needed init method in child classes.
* Remove turn_on method in parent class and add update_ha_state
	to _turn_on_light, _turn_on_dimmer and _turn_on_rgb_or_w.
2016-02-18 02:04:06 +01:00
MartinHjelmare 6e8c79d531 Change refactor structure
* Make a flatter one level inheritance, with MySensorsLight as parent
	with four children, one per light type.
* Break out helper methods. One per plain light, dimmer and RGB/RGBW
	children and per update, turn_on and turn_off, nine in total. Put
	these in the parent.
* Call the helper methods as needed from the child methods update,
	turn_on and turn_off.
* Change name of MySensorsLightLight to MySensorsLightPlain.
* Fix module docstrings according to pep257.
* Change name of color util method from rgb_hex_to_list to
	rgb_hex_to_rgb_list.
* Add unit tests for rgb_hex_to_rgb_list.
2016-02-18 02:04:06 +01:00
MartinHjelmare 03423cc3a9 Refactor mysensors light
* Add a light entity class per V_LIGHT, V_DIMMER, V_RGB and V_RGBW.
    Make these classes inherit each other up to MySensorsLight class.
* Map the entity classes to their S_TYPE in a dict.
* Check if an entity class map or just an entity class have been passed
    to pf_callback_factory before using the entity_class variable in
    homeassistant/components/mysensors.py.
* Add rgb_hex_to_list function in homeassistant/util/color.py.
2016-02-18 02:04:06 +01:00
Dan Smith cd6780baf4 More graphite hardening work
This adds verbose debugging which can be turned on to figure out what is
going on. It also adds a broad exception handler in the worker thread
to avoid dying. If you're running this such that stderr doesn't go to a
log, it can be easy to miss the thread's death.

I wrote all this to try to diagnose #1283, which seems to maybe have
healed itself. But since I have it, I figure we might as well keep it
in case we have trouble in the future.
2016-02-17 15:11:24 -08:00
Paulus Schoutsen bb37708716 Merge pull request #1296 from kk7ds/honeywell-no-fan
Fix #1287 for honeywell US systems with no fan
2016-02-17 12:18:10 -08:00
Per Sandström 0bacc7be07 Merge pull request #1300 from persandstrom/vsure0.5.1
update vsure to 0.5.1
2016-02-17 19:34:03 +01:00
Per Sandström 99ac4524b9 update vsure to 0.5.1 2016-02-17 19:28:26 +01:00
Flyte 348b7abe7d Change TCP component to use Jinja2 instead of regex 2016-02-17 18:12:36 +00:00
Flyte cf93644d54 Move generic tcp sensor entity to specific sensor component 2016-02-17 17:26:53 +00:00
Paulus Schoutsen 4d8487e7ba Merge pull request #1298 from kk7ds/fix-alarmdotcom-dep
Fix alarmdotcom requirement 0.0.7 removed
2016-02-17 08:52:10 -08:00
Dan Smith 96dde18ae3 Fix alarmdotcom requirement 0.0.7 removed
Looks like alarmdotcom just released 0.1.1 and deleted 0.0.7 which is
breaking our build.
2016-02-17 08:41:46 -08:00
Dan Smith 9aa4028718 Fix #1287 for honeywell US systems with no fan
This bumps the somecomfort requirement to 0.2.1 to pull in a change
that makes handling no-fan systems graceful. Adds a test that should
prove it gives us what we want.

If no fan, then fan is always idle and fanmode is None.
2016-02-17 08:16:02 -08:00
Paulus Schoutsen 966b83f648 Merge pull request #1293 from w1ll1am23/Wink_lock_fix
Fixed issue with Wink locks lock, and unlock methods
2016-02-17 08:00:51 -08:00
William Scanlon b2366ce68e Added optional parameter to lock and unlock methods 2016-02-17 05:50:36 -05:00
Paulus Schoutsen bd3f0c101d Merge pull request #1290 from balloob/cleanup-camera
Cleanup camera
2016-02-16 23:59:10 -08:00
Paulus Schoutsen ab9ac80ee0 Update frontend with new camera UI 2016-02-16 23:52:26 -08:00
Paulus Schoutsen aea0598805 Clean up camera component 2016-02-16 23:52:05 -08:00
Dan Smith b7c4370e2b Merge pull request #1271 from kk7ds/nx584-sensors
Add nx584 as a sensor platform
2016-02-16 20:40:15 -08:00
Dan Smith 22865e5d96 Add nx584 as a sensor platform
This allows you to get every door, window, smoke, etc zone from your security
panel into HA. This uses the live eventing feature of pynx584, which means you
get instantaneous signaling into HA when a door opens or something happens,
which is handy for automating lights on when doors open after dark, etc.

Requires update to pynx584 0.2
2016-02-16 20:24:32 -08:00
Paulus Schoutsen c66511e0cf Merge pull request #1275 from balloob/rfxtrxSignalRep
Rfxtrx signal rep
2016-02-16 20:23:55 -08:00
Paulus Schoutsen 79dbd14568 Merge pull request #1280 from molobrakos/tellduslive
added assumed_state property + improved error handling
2016-02-16 13:51:14 -08:00
Paulus Schoutsen 5756cff8a4 Merge pull request #1281 from molobrakos/eliqonline
improve error handling
2016-02-16 11:24:36 -08:00
Erik 44a39636b1 improve error handling 2016-02-16 18:23:08 +01:00
Erik 307b2c629b catch exception and log 2016-02-16 17:46:25 +01:00
Paulus Schoutsen bbfa63ee79 Merge pull request #1269 from turbokongen/rfxtrx-newprotocols
Add assumed_state for rfxtrx switch and light.
2016-02-16 08:19:27 -08:00
Erik 008d65677b add assumed state property 2016-02-16 17:18:49 +01:00
Paulus Schoutsen 68973dbc4d Merge pull request #1276 from balloob/fix_efergy_key_error
Catch KeyError as well as ValueError when handling efergy errors.
2016-02-16 07:33:12 -08:00
pavoni 9a6c99264e Catch KeyError as well as ValueError when handling efergy errors. 2016-02-16 12:05:00 +00:00
Paulus Schoutsen 1d7aea7120 Merge pull request #1272 from infamy/dev
initial commit of the neurio_energy sensor
2016-02-15 22:08:35 -08:00
infamy 16865b82d3 initial commit of the neurio_energy sensor 2016-02-15 15:54:07 -08:00
Daniel Høyer Iversen 2ba3df2257 Merge pull request #1264 from balloob/rfxtrxLight
Rfxtrx light
2016-02-15 20:59:49 +01:00
Daniel aec269050f Add signal repetition to rfxtrx 2016-02-15 19:52:39 +01:00
Daniel b93ebe1936 Add signal repetition to rfxtrx 2016-02-15 19:45:33 +01:00
Daniel 0010cadd39 Add signal repetition to rfxtrx 2016-02-15 19:41:40 +01:00
Paulus Schoutsen 75f2b4bc0e Merge pull request #1265 from flyte/tox-execute-all-lint
Execute all lint tests even if flake8 reports errors.
2016-02-15 10:21:43 -08:00
John Arild Berentsen a7ce9ba49e CI fix 2016-02-15 19:09:53 +01:00
John Arild Berentsen f429a6c4ff Add assumed_state for rfxtrx switch and light. 2016-02-15 18:59:11 +01:00
Flyte 3d83eea5f7 Add tcp component. 2016-02-15 16:04:14 +00:00
Flyte a3e8994fcc Execute all lint tests even if flake8 reports errors. 2016-02-15 10:00:46 +00:00
Daniel eacfac6fa8 Support on-off device in rfxtrx light 2016-02-15 10:10:29 +01:00
Daniel 0d45470ea6 Support on-off device in rfxtrx light 2016-02-15 10:05:47 +01:00
Paulus Schoutsen bca3207e0c Update frontend with assumed_state and delete state support 2016-02-15 01:02:15 -08:00
Paulus Schoutsen de7a14074e Merge pull request #1247 from balloob/feature/entity-assumed-state
Add assumed_state property to entity
2016-02-14 23:59:35 -08:00
Paulus Schoutsen cdc93ab670 Merge pull request #1262 from balloob/chore/tests-cleanup
More testing cleanup
2016-02-14 23:37:10 -08:00
Paulus Schoutsen c287520432 MQTT Light test - switch order 2016-02-14 23:16:54 -08:00
Paulus Schoutsen 68803a46b6 Thread pool tweaks 2016-02-14 23:01:49 -08:00
Paulus Schoutsen 8d366a7367 Tests: Mock get_local_ip by default too 2016-02-14 22:01:30 -08:00
Paulus Schoutsen 23b116803b Merge pull request #1261 from kk7ds/graphite-gaierror
Some graphite error handling improvements
2016-02-14 17:40:40 -08:00
Dan Smith 3610f40a6a Handle EVENT_STATE_CHANGED with no new_state in graphite
I noticed some events that came in with new_state=None. Make graphite
defensive about this.
2016-02-14 15:57:03 -08:00
Dan Smith 366595fd90 Catch socket.gaierror in graphite driver
If you specify a name that can't be looked up in DNS, socket.connect()
throws socket.gaierror. We should catch and log that situation properly.
2016-02-14 15:44:11 -08:00
Paulus Schoutsen 00486dccea Merge pull request #1260 from balloob/chore/tests-cleanup
Chore/tests cleanup
2016-02-14 15:21:23 -08:00
Paulus Schoutsen 09ab3e95c0 Tests should all use test HA 2016-02-14 15:08:23 -08:00
Stefan Jonasson fa8857dfc5 Changed process communication to use stdin for the message because of security concerns. 2016-02-14 22:22:11 +01:00
Paulus Schoutsen bade0e0d71 Make tests pass flake8 2016-02-14 13:07:21 -08:00
Paulus Schoutsen dd2aec0a08 Restructure tests to ensure unique ports 2016-02-14 12:54:16 -08:00
Paulus Schoutsen 64430f26f3 Merge pull request #1257 from kk7ds/handle-uvc-errors-better
Misc UVC improvements
2016-02-14 12:27:25 -08:00
Paulus Schoutsen 4eecdbdab1 Merge pull request #1259 from kk7ds/exclude-venv
Exclude venv directories from linting
2016-02-14 12:25:25 -08:00
Dan Smith 92a11819b4 Exclude venv directories from linting
Exclude venv/ (a common virtualenv name) and bin/,lib/ if the local
directory is configured as a venv.
2016-02-14 20:20:12 +00:00
Paulus Schoutsen dabb8d5bbc Merge pull request #1246 from balloob/feature/remove-state-fire-event
Have remove state fire state_changed event
2016-02-14 10:33:59 -08:00
Sean Dague 8c67a924bc Merge pull request #895 from sdague/tox
add tox infrastructure for running tests locally and in travis
2016-02-14 13:13:13 -05:00
Sean Dague 97c0f5bb5a convert testing infrastructure to tox
This converts the testing infrastructure to tox for both locally
testing and travis. This is nearly equivalent to the previous testing
with the only exception that linting fails with the first tool to fail
and won't process all of them.

Slightly tricky thing is that tox resets *all* of the environment for
it's subprocess runs by default. A couple of the dependencies we have
will not install in non UTF8 locales: temper-python & XBee.
2016-02-14 13:04:42 -05:00
Paulus Schoutsen aeb87b0245 Merge pull request #1248 from balloob/chore/reorg-frontend-api
Move bootstrap endpoint from api to frontend
2016-02-14 09:43:53 -08:00
Paulus Schoutsen 6be2ec7fea Merge pull request #1249 from balloob/chore/remove-usage-attr_friendly_name
Remove usage of ATTR_FRIENDLY_NAME within components/platforms
2016-02-14 09:43:39 -08:00
Dan Smith 885b61a750 Implement brand and model for UVC cameras 2016-02-14 17:27:29 +00:00
Dan Smith 0ba7fb40a4 Improve UVC performance by not logging in on each image fetch
This makes the UVC camera_image() method not log into the camera on
every single image fetch, which reduces load on hass and the camera,
and lowers the latency to get an actual image fetched.
2016-02-14 17:27:29 +00:00
Dan Smith 263839a336 Handle connection errors talking to UVC cameras during image fetch
This requires uvcclient==0.6 which breaks out exceptions for us.

Fixes #1244
2016-02-14 17:27:26 +00:00
Paulus Schoutsen 3ced457089 Merge pull request #1255 from miniconfig/splunk-fix-attributes
Converted state.attributes to dict in Splunk component
2016-02-14 07:51:48 -08:00
miniconfig 01df1f8458 Converted state.attributes to dict. Fixes Issue #1252 2016-02-14 10:47:46 -05:00
Paulus Schoutsen b29f2f6d6f Remove usage of ATTR_FRIENDLY_NAME within components/platforms 2016-02-14 00:21:20 -08:00
Paulus Schoutsen cafa4043b3 Move bootstrap endpoint from api to frontend 2016-02-14 00:04:08 -08:00
Paulus Schoutsen 8bea5c06de Add assumed_state property to entity 2016-02-13 23:42:11 -08:00
Paulus Schoutsen 06de73ff80 Allow removing a state via API + remote StateMachine 2016-02-13 23:00:38 -08:00
Paulus Schoutsen ada2fb4ec0 Fire event when we remove a state 2016-02-13 22:57:40 -08:00
Paulus Schoutsen 39bbfd14d9 Merge pull request #1240 from persandstrom/dict_size_change_during_iteration
Fixed intermittent error during setup
2016-02-13 22:28:46 -08:00
Per Sandström 9e816cfd3f lock _states to prevent size change during iteration 2016-02-14 06:34:00 +01:00
Paulus Schoutsen e170484f16 Change helpers.extract_domain_configs from generator to list bc concurrency 2016-02-13 21:20:49 -08:00
Paulus Schoutsen 9204c44eec Merge pull request #1241 from kk7ds/update-honeywell-somecomfort
Update honeywell somecomfort
2016-02-13 19:44:58 -08:00
Dan Smith 0fbd947426 Test Honeywell Round thermostat
This includes two changes to the round code:
 - Return True on setup success
 - Break out the default away temp into a constant
2016-02-14 01:05:18 +00:00
Dan Smith 5921e65d83 Allow specifying location and/or thermostat for Honeywell US
This lets you optionally only add thermostats by location or
specific device id, instead of all the thermostats in your
account. This would be helpful if you have two devices in different
houses (i.e vacation home), etc.
2016-02-13 23:13:38 +00:00
Dan Smith c51dd64bd8 Convert Honeywell platform to use somecomfort library 2016-02-13 23:13:33 +00:00
Paulus Schoutsen 278033cbf9 Merge pull request #1235 from flyte/docker-colorlog
Add colorlog to docker images
2016-02-13 14:55:07 -08:00
Flyte 9233449551 Add colorlog to Dockerfile 2016-02-13 20:53:15 +00:00
Paulus Schoutsen 680f450278 Merge pull request #1236 from kk7ds/ci-tweak
Make it easier to run cibuild locally
2016-02-13 11:18:40 -08:00
Dan Smith 66c2fa1270 Make it easier to run cibuild locally
Just treat lack of travis environment as "run everything"
2016-02-13 18:30:34 +00:00
Paulus Schoutsen bf0b453677 Merge branch 'hotfix/state-as-number' into dev
Conflicts:
	homeassistant/const.py
	tests/helpers/test_state.py
2016-02-13 08:32:06 -08:00
Paulus Schoutsen 29b6782b42 Merge pull request #1229 from balloob/hotfix/state-as-number
0.13.1 / Fix: state_as_number always return float
2016-02-13 08:23:14 -08:00
Paulus Schoutsen d2df485bea Version bump to 0.13.1 2016-02-13 08:17:47 -08:00
Paulus Schoutsen 77b141a355 Update Wink to 0.6 2016-02-13 08:17:38 -08:00
Fabian Affolter 00afaac54c Update for file header, docstrings, and PEP8/PEP257 2016-02-13 14:19:11 +01:00
Fabian Affolter 34b91cf6ce Remove config details (already covered in docs) 2016-02-13 09:38:56 +01:00
Fabian Affolter 9dc055e537 Add link to docs 2016-02-13 09:35:31 +01:00
Paulus Schoutsen 6d6cf886f3 Merge pull request #1222 from turbokongen/verisure-mouse
Support for Mousedetectors connected in Verisure systems
2016-02-13 00:22:34 -08:00
Paulus Schoutsen 1571b33e4a Fix: state_as_number always return float 2016-02-13 00:08:32 -08:00
John Arild Berentsen a29be5455c Fix unit and wrong errorhandling 2016-02-13 09:05:18 +01:00
Paulus Schoutsen 4210291e5b Version bump to 0.14.dev0 2016-02-12 21:55:40 -08:00
Paulus Schoutsen e9fa1f1f83 Merge pull request #1182 from balloob/dev
0.13
2016-02-12 21:55:24 -08:00
Paulus Schoutsen 724d5bfe9d Version bump to 0.13 2016-02-12 21:51:34 -08:00
Paulus Schoutsen 938c9888a6 Merge pull request #918 from happyleavesaoc/snapcast
snapcast media player
2016-02-12 19:31:07 -08:00
Paulus Schoutsen b91e4cfb4a Merge pull request #1195 from persandstrom/kodi_thumbnails
kodi thumbnails
2016-02-12 19:03:22 -08:00
Paulus Schoutsen 7dd51034cd Merge pull request #1213 from MartinHjelmare/mysensors-light
Add mysensors light platform
2016-02-12 19:01:41 -08:00
Paulus Schoutsen 2f60ff224f Merge pull request #1223 from MartinHjelmare/update-mysensors-version
Update pymysensors version to 0.5
2016-02-12 18:55:06 -08:00
Paulus Schoutsen fc3a37cba2 Merge pull request #1224 from MartinHjelmare/fix-mysensors-version-error
Fix error in update method for V_STATUS
2016-02-12 18:54:12 -08:00
Paulus Schoutsen 223b7f85ee Merge pull request #1225 from nkgilley/ecobee-fix
fix ecobee sensor unique id.
2016-02-12 18:53:58 -08:00
happyleavesaoc 13b0beee31 snapcast 2016-02-12 18:35:32 -05:00
nkgilley@gmail.com ba530e5a16 fix ecobee sensor unique id. 2016-02-12 17:24:00 -05:00
Per SandstrÃom aafd36d2ce change url to host 2016-02-12 21:19:27 +00:00
MartinHjelmare 0492f0abd0 Fix error in update method for V_STATUS
* Remove check against V_STATUS to avoid error when using version 1.4
    of mysensors. V_LIGHT has the same integer value so V_STATUS is not
    needed.
2016-02-12 22:05:31 +01:00
MartinHjelmare 940799d0da Update pymysensors version to 0.5 2016-02-12 21:59:24 +01:00
John Arild Berentsen c584b6b28d Support for Mousedetectors connected in Verisure systems 2016-02-12 21:41:01 +01:00
Stefan Jonasson 5a03ddd7e0 Removed "" and changed the call to check_call to make it race the appropriate error 2016-02-12 19:31:28 +01:00
Paulus Schoutsen 1ba60dc898 Update .coveragerc 2016-02-12 09:01:48 -08:00
Paulus Schoutsen 75d3d25969 Merge pull request #1129 from nkgilley/speedtest
Speedtest.net component
2016-02-12 08:59:19 -08:00
Paulus Schoutsen 06bd812b7b Make state unknown if None returned 2016-02-12 08:58:07 -08:00
nkgilley@gmail.com 7cb57583e2 fix line too long 2016-02-12 11:44:24 -05:00
nkgilley@gmail.com 3e3f5db2a5 use a windows & linux compatible regex 2016-02-12 11:30:55 -05:00
nkgilley@gmail.com 88fe28ea1b add update service 2016-02-12 09:55:28 -05:00
Stefan Jonasson 10a20f802b Updated coverage 2016-02-12 11:26:21 +01:00
Stefan Jonasson 9521dad263 Added a command line notification platform that could be used for all kind of custom notifications 2016-02-12 11:25:26 +01:00
Paulus Schoutsen 8857c48c17 Merge pull request #1206 from turbokongen/rfxtrx-newprotocols
Supporting electricity sensors with ELEC2/3 protocol from latest pyRFXtrx (0.4)
2016-02-11 23:30:40 -08:00
Paulus Schoutsen 3c582d1e3c Merge pull request #1143 from balloob/rfxtrx
Rfxtrx
2016-02-11 23:30:34 -08:00
Fabian Affolter 19d12716ef Update docstrings 2016-02-12 08:21:39 +01:00
Fabian Affolter 57446cfb08 Add link to docs 2016-02-12 07:55:20 +01:00
Paulus Schoutsen ff6cb2b452 Update frontend icons + proximity icon 2016-02-11 22:21:06 -08:00
Paulus Schoutsen a3ec7998b1 Update frontend with garage door support 2016-02-11 21:55:50 -08:00
Paulus Schoutsen d892716bfa Merge pull request #1218 from kk7ds/influx-tests
Add tests for influxdb and fix a bug
2016-02-11 21:45:37 -08:00
Dan Smith 61e2da8827 Add tests for influxdb and fix a bug
This adds tests for the influxdb component. It also fixes a bug,
where username and password are required, but not gracefully
handled if they're missing from config.
2016-02-12 05:34:13 +00:00
Paulus Schoutsen abc039a3d0 Check coverage for statsd and splunk 2016-02-11 20:22:01 -08:00
Paulus Schoutsen 7ba7747ce8 Merge pull request #1217 from jaharkes/nest_heating
Nest binary sensor fixes
2016-02-11 20:15:02 -08:00
Jan Harkes b04ff7207c Make sure Nest is setup before the binary sensors. 2016-02-11 21:28:08 -05:00
Dan Smith 72235c48fb Merge pull request #1216 from kk7ds/fix-dan-broke-splunk
Re-allow splunk to report string states
2016-02-11 18:26:24 -08:00
Jan Harkes 3f7ff2b1d4 The Nest binary sensor can also track when heating system is running. 2016-02-11 21:24:51 -05:00
Dan Smith 484b7b64d7 Re-allow splunk to report string states
Splunk *can* take string states, so un-fix that wrong fix.
2016-02-12 02:20:15 +00:00
Paulus Schoutsen 7241762bcc Merge pull request #1209 from kk7ds/abstract-numeric-state
Abstract numeric state
2016-02-11 18:16:57 -08:00
Dan Smith 0a904acd4d Add some tests for splunk
This also fixes issue #1214, and I think another bug. The splunk
code will just take the value of state.state and try to serialize
it to json if it can't make it into a number. It did this before
I generalized that code. Since json.dumps() will fail on most anything
complicated, I think the right thing to do is *not* try to do that.
2016-02-12 01:45:30 +00:00
Dan Smith 76df759f4c Add simple statsd tests
These are not very amazing, but at least exercise the code a little
to make sure I didn't break anything. Hopefully they're useful in the
future too.
2016-02-12 01:45:30 +00:00
Dan Smith 4a2b956493 Convert statsd, influx, splunk, and graphite to use state_as_number()
Fixes #1205
2016-02-12 01:45:25 +00:00
Dan Smith 3aa34deaa2 Add state_as_number() helper
This adds state_as_number(), a helper method that tries to interpret
state as a number, for cases we can predict. It's a generalization of
what is copy-and-paste-ed into multiple other places.
2016-02-12 00:41:32 +00:00
nkgilley@gmail.com b00cad7095 fix travisci errors. 2016-02-11 19:27:05 -05:00
MartinHjelmare 2cf061c768 Add mysensors light platform
* Make light controllable independently with types V_LIGHT, V_DIMMER,
    V_RGB and V_RGBW. V_RGBW is not implemented in the frontend yet.
* Add discovery for light platform.
* Add optimistic mode config setting for switch and light
    to allow feedback state from actuator.
* Move S_LIGHT, V_LIGHT, V_STATUS types from switch to light platform.
* Change node update logging to debug from info level.
* Fix some inaccurate comments.
2016-02-12 01:22:35 +01:00
nkgilley@gmail.com e837e97c9d use track_time_change 2016-02-11 19:09:51 -05:00
Paulus Schoutsen df8afe51f4 Merge pull request #1212 from MartinHjelmare/fix-validate-config
Fix validate config in mysensors
2016-02-11 12:39:16 -08:00
Greg Dowling c8e6f89302 Merge pull request #1210 from balloob/fix_owntracks_passive_zone
Handle passive zones correctly.
2016-02-11 20:35:21 +00:00
pavoni 2e75a58372 Fix outtracks bug with passive zones. 2016-02-11 20:28:02 +00:00
MartinHjelmare ae2fd149a5 Fix validate config in mysensors
* Add check of port in config.
2016-02-11 21:03:13 +01:00
Greg Dowling ee62c2cc2b Merge pull request #1208 from kk7ds/static-wemos
Support manually-defined WeMo devices
2016-02-11 18:46:58 +00:00
Dan Smith 4cfa14c29d Support manually-defined WeMo devices
This is extremely useful if you want to support wemos that are on
another subnet or across a VPN. It also lets you sidestep the discovery
process, which is problematic for a lot of people and situations.

In order for this to work, we need to bump the pywemo requirement to
0.3.10, which includes my changes to make this possible.

WeMo devices can be manually configured by adding a static section to
the config, like this:

  switch:
    platform: wemo
    static:
       - 192.168.100.5
       - 192.168.100.6
2016-02-11 17:23:20 +00:00
Paulus Schoutsen 4ce1a67c13 Merge pull request #1077 from xrolfex/wink_garage_door_support
Wink Garage Door Support
2016-02-11 07:59:57 -08:00
Dan Smith 962463c1ab Merge pull request #1196 from kk7ds/honeywell-enumerate
Make Honeywell module enumerate all available thermostats
2016-02-11 07:53:05 -08:00
Paulus Schoutsen 74f06b6862 Merge pull request #1190 from flyte/apcupsd-component
Add APCUPSd component
2016-02-11 07:35:43 -08:00
Dan Smith d3d7d458e1 Make Honeywell module enumerate all available thermostats
This extends the HoneywellUSThermostat functionality to find and add
all thermostats in your account. So, we add a new config element called
'region' that is the primary trigger for this, and remove the 'id'
trigger since it was never in a released version.

This does a few extra things:
 - It names the thermostat what you have it named in your account,
   which is not something we get to know *unless* we emumerate.
 - It makes all thermostats on a given account use the same session,
   and thus we have to avoid doing an explicit login every time we
   refresh our data. That was causing some rate-limiting on their
   side when I was debugging, so this is probably good. Now, we use
   their existing keepalive pinger to determine if we're still logged
   in and only re-login if we need to.
2016-02-11 15:26:41 +00:00
Eric Rolf cca6b0c287 Test Fix. 2016-02-11 10:16:57 -05:00
Eric Rolf 23e3b8d2f2 Fixed Style Issue 2016-02-11 10:00:12 -05:00
Eric Rolf be9a2a043e Refactored Method Names. 2016-02-11 09:57:56 -05:00
Dan Smith cc4fa6cd38 Merge pull request #1194 from kk7ds/add-graphite-feeder
Add graphite feeder component
2016-02-11 06:36:19 -08:00
John Arild Berentsen 4d15367956 Supporting electricity sensors with ELEC2/3 protocol from latest pyRFXtrx (0.4) 2016-02-11 15:35:05 +01:00
Eric Rolf 175b49236c Fixed style attribute with redefined built in method names. 2016-02-11 09:20:47 -05:00
Eric Rolf fd0afaa204 Fixed Test Case Logic 2016-02-11 09:12:28 -05:00
Eric Rolf 034cec7152 Fixed Demo Test Cases 2016-02-11 09:06:35 -05:00
Eric Rolf f464d591c9 Update python wink requirement 2016-02-11 08:49:07 -05:00
Eric Rolf 06cb97adee Merge branch 'wink_garage_door_support' of https://github.com/xrolfex/home-assistant into wink_garage_door_support 2016-02-11 08:39:20 -05:00
Eric Rolf cab46b91e3 Updated Requirements All. 2016-02-11 08:38:05 -05:00
Eric Rolf 0da09b85de refactored test case 2016-02-11 08:37:17 -05:00
Eric Rolf 95d9bc48ea Updated Demo 2016-02-11 08:37:17 -05:00
Eric Rolf 6b962a2207 Updated coveragec, cleaned up constants, added test for demo. 2016-02-11 08:37:16 -05:00
Eric Rolf 18b3d3df57 Forgot to refactor demo. 2016-02-11 08:37:16 -05:00
Eric Rolf 5f6977acda Refactor Method Name For Open and Close. 2016-02-11 08:37:16 -05:00
Eric Rolf 89f6ef9f6c Updated requirements_all.txt 2016-02-11 08:37:16 -05:00
Eric Rolf d2ad0620ee Wink Garage Door Support 2016-02-11 08:37:16 -05:00
Eric Rolf aa13392983 refactored test case 2016-02-11 08:30:33 -05:00
Eric Rolf 6cbf19934f Updated Demo 2016-02-11 08:23:04 -05:00
Daniel f938134069 updated rfxtrx lib 2016-02-11 14:15:51 +01:00
Eric Rolf 7cdcb800a9 Updated coveragec, cleaned up constants, added test for demo. 2016-02-11 07:41:42 -05:00
Flyte 91fb2764cc Use a cache object to reduce the frequency of calls to APCUPSd 2016-02-11 07:33:53 +00:00
Paulus Schoutsen 82c5e2cf3c Merge pull request #1177 from Theb-1/dev-notify-rest
REST notify component
2016-02-10 23:05:34 -08:00
Flyte bb8981b611 Add apcupsd component. 2016-02-11 06:33:23 +00:00
Paulus Schoutsen b350f22a77 Update frontend to fix color picker 2016-02-10 22:18:08 -08:00
Paulus Schoutsen 40da28a0c7 Merge pull request #1192 from i-c/add-delay-to-launchtl-load
Add delay to launchtl load
2016-02-10 21:39:36 -08:00
Paulus Schoutsen 3bdb50510a Merge pull request #1202 from huanga/change_nest_battery-level_unit
Changing battery level unit to "V" instead of "%"
2016-02-10 21:33:10 -08:00
Dan Smith 7478c36b27 Add graphite feeder component
Like recorder, this component listens to all events and reports any
that it can to a graphite installation. This makes it easy to use
graphite for all your data collection and analysis. If you run
carbon-cache (the backend for graphite) on the local machine, no
configuration is required other than enabling the component.

For more info on graphite: http://graphite.wikidot.com/
2016-02-11 05:28:40 +00:00
Paulus Schoutsen b1f2c90bd0 Add MQTT service description 2016-02-10 21:23:27 -08:00
Ian Copp e53785f30c Fix __main__.py permissions change 2016-02-10 21:20:56 -08:00
Paulus Schoutsen 1a38354ed5 Merge pull request #1138 from flyte/mqtt-publish-template
Add template support to mqtt.publish service payload.
2016-02-10 21:13:02 -08:00
Andy Huang 02609d0ab5 Changing battery level unit to "V" instead of "%" as the API reports output voltage, not percentage.
This value matches with Settings > Technical Information's Battery information.
2016-02-10 21:12:43 -08:00
Paulus Schoutsen ce4f5ff29c Merge pull request #1197 from maxdrift/expose-baud-rate-mysensors-component
Expose baud rate config for MySensors component
2016-02-10 20:51:18 -08:00
Paulus Schoutsen 5190cc74c5 Merge pull request #1200 from MartinHjelmare/mysensors-unavailable
Use entity property available in mysensors
2016-02-10 20:45:48 -08:00
Paulus Schoutsen e83f8da342 Merge pull request #1185 from balloob/perf-states
Make State class more immutable
2016-02-10 18:53:02 -08:00
Paulus Schoutsen ddaeeba68b Merge pull request #1186 from balloob/remove-deprecated-methods
Remove deprecated methods from core
2016-02-10 18:52:57 -08:00
MartinHjelmare 75775a561b Use entity property available 2016-02-11 03:27:02 +01:00
Fabian Affolter 058315720f Fix typo 2016-02-10 23:59:34 +01:00
Flyte 4e0c7f8a3d Create additional mqtt helper function for using template payload. 2016-02-10 22:38:33 +00:00
Riccardo Massari c705ca4288 Expose baud rate config for mysensors component 2016-02-10 23:16:41 +01:00
Per SandstrÃom 15ad48a7a0 kodi thumbnails 2016-02-10 19:48:41 +00:00
Ian Copp c9c15c4cf7 Fix comment phrasing
Somehow I left out the "some". Whoops.
2016-02-10 11:32:56 -08:00
Ian Copp 3046bfce7b Add small time delay to restart-osx command 2016-02-10 11:29:25 -08:00
Flyte d52e2019c0 Update mqtt.publish() function to use template_payload. Reorganise publish service. Use mqtt.publish() in tests. 2016-02-10 11:11:02 +00:00
Paulus Schoutsen af8f6bcaba Remove deprecated methods from core 2016-02-09 23:59:31 -08:00
Daniel cdf0e80773 Improve the robustness of the rfxtrx module, and solve issue #1116 2016-02-10 08:44:34 +01:00
Paulus Schoutsen b0948bef5f Make State class immutable 2016-02-09 23:27:01 -08:00
Paulus Schoutsen 70a528c04b Merge pull request #1184 from balloob/group-of-groups
Support expanding nested groups
2016-02-09 22:56:30 -08:00
Theb-1 d796625098 flake8 fix 2016-02-09 22:51:44 -08:00
Paulus Schoutsen dc44ef7356 Support expanding nested groups 2016-02-09 22:43:07 -08:00
Theb-1 c5c4085ad4 upper/arg list/response improvements 2016-02-09 22:27:54 -08:00
Paulus Schoutsen 09b3aba51b Merge pull request #1136 from turbokongen/verisure-locks
Added support for lock connected to Verisure system.
2016-02-09 21:20:52 -08:00
Paulus Schoutsen 6f3aefde64 Merge pull request #1179 from roqeer/patch-1
Add name property to DHT sensor
2016-02-09 21:14:23 -08:00
Theb-1 b3a1491482 pylint: ignore 'too-many-arguments' 2016-02-09 21:12:33 -08:00
Theb-1 b7ff79da24 Fixes and updates
Remove setting defaults twice
Add timeout
Add optional title and target
2016-02-09 20:48:17 -08:00
roqeer 4bf4d94344 Changed to dict lookup with default value
Corrected on request
2016-02-09 22:09:44 +01:00
roqeer 3e26af5ff1 Correct bad-indentation 2016-02-09 19:09:48 +01:00
roqeer c1270cf0bb Add name property to DHT sensor
Add name to distinguish between multiple connected DHT sensors
2016-02-09 18:58:04 +01:00
Flyte 26fc637ab5 Add payload_template to mqtt 'publish' service call. 2016-02-09 15:41:31 +00:00
Fabian Affolter 66c5d96b43 Upgrade influxdb to 2.12.0 2016-02-09 16:32:05 +01:00
turbokongen@hotmail.com 41f908ed39 Added support for lock connected to Verisure system. 2016-02-09 13:17:05 +01:00
Theb-1 0f5487b95a Add REST notify component 2016-02-08 22:24:11 -08:00
Paulus Schoutsen 23c5159f6c Update frontend to fix map CSS 2016-02-08 21:58:41 -08:00
Paulus Schoutsen 4840dd297a Add unique ID to ecobee sensor 2016-02-08 20:39:09 -08:00
Dan Smith 4605742bb7 Merge pull request #1176 from kk7ds/mfi-tests
Add mFi tests
2016-02-08 19:07:48 -08:00
Paulus Schoutsen f222340c8e Merge pull request #1174 from balloob/wink-light
Wink light to inherit from light
2016-02-08 19:05:39 -08:00
Dan Smith b17df44402 Add tests for mFi switches 2016-02-09 03:03:26 +00:00
Dan Smith 895ddc8433 Add tests for mFi sensors
Note that some of the indirection here is so that I can reuse
a few things for mFi switch tests to follow.
2016-02-09 02:59:34 +00:00
Per Sandström d867d26612 Merge pull request #1168 from persandstrom/sigterm_handler
fix sigterm crash
2016-02-08 19:27:26 +01:00
Per SandstrÃom 564e328698 fix sigterm crash 2016-02-08 18:09:46 +00:00
Paulus Schoutsen 160b811ddf Wink light to inherit from light 2016-02-08 08:53:22 -08:00
Paulus Schoutsen 2e164e519a Merge pull request #1160 from stjohnjohnson/live-camera-stream
Fixes #1062 - If supported, directly stream the MJPEG from the remote camera
2016-02-07 14:43:00 -08:00
Paulus Schoutsen 779188ad27 Merge pull request #1167 from kk7ds/mfi-improvements
Mfi improvements
2016-02-07 14:40:47 -08:00
Paulus Schoutsen 3f6349d663 Merge pull request #1170 from balloob/fix_vera_dimmer_bug
Refactor VeraLight to inherit from Light, rather then VeraSwitch.
2016-02-07 14:39:16 -08:00
pavoni ac0dc10377 Refactor VeraLight to inherit from Light, rather then VeraSwitch. 2016-02-07 21:45:15 +00:00
Dan Smith 0a7db98b0e Round mFi sensor values to reasonable levels of precision
Most of the mFi sensors are able to reasonably provide accurate
readings to a tenth of a unit or so. This patch rounds them for
better display in the UI. Normally, I would expect this to be a view
action instead of altering the actual data emitted, but since these
values are reasonable for sensor precision, we're not really losing
anything. I followed the model from the openweathermap component, which
rounds for readability in the backend.
2016-02-07 20:51:00 +00:00
Dan Smith 8f690ff077 Add support for mPort input sensors
The mPort device has input pins that can be configured as digital or
analog inputs. We should support those as sensors.
2016-02-07 20:49:02 +00:00
Dan Smith 951fa603ff Support mPort voltage output switch types
An mPort device has a voltage output port that, if configured,
we should support like a switch.
2016-02-07 20:48:57 +00:00
Dan Smith c113997609 Tweak mFi switch behavior to avoid false states
When we update the mFi server for the state of a switch, the new
state is not always reported immediately if we update right after
the action (the server is not RESTful).

This patch adds some internal target-state handling to report the
desired state on the next poll, allowing any subsequent polls to
override that state.

Also, bump the version requirement for mficlient to 0.2.2 to absorb
a bug fix.
2016-02-07 20:48:51 +00:00
St. John Johnson f700635445 Add support for mjpeg component to proxy it's own stream 2016-02-07 12:02:52 -08:00
Paulus Schoutsen d49fae86e4 Update frontend with entity picture fix 2016-02-07 11:33:01 -08:00
Paulus Schoutsen 64611ab2be Merge pull request #1157 from balloob/device-state-attributes
Clean up state_attributes vs device_state_attributes
2016-02-07 10:16:34 -08:00
Paulus Schoutsen 27dc2f61fb Merge pull request #1163 from balloob/migration-5-fix
Fix recorder migration 5
2016-02-07 10:16:24 -08:00
Paulus Schoutsen cd25c8f72d Clean up some query stuff 2016-02-07 10:07:27 -08:00
Paulus Schoutsen 9ad1d290af Fix migration 5 2016-02-07 10:07:08 -08:00
Fabian Affolter 90ef81d8d5 Merge pull request #1161 from fabaff/bitcoin
Add icon
2016-02-07 15:37:40 +01:00
Fabian Affolter 02efe903ab Add icon 2016-02-07 12:43:02 +01:00
Fabian Affolter 0bb63bf3f0 Move configuration details to docs and add link 2016-02-07 12:36:29 +01:00
Fabian Affolter e23db5d972 Some small changes 2016-02-07 12:03:01 +01:00
Fabian Affolter e311f89056 Move details to docs and update docstrings/comments 2016-02-07 12:00:35 +01:00
Fabian Affolter 757946293e Move configuration details to docs 2016-02-07 11:52:17 +01:00
Paulus Schoutsen 98c6e56ea4 Merge pull request #1159 from nickwaring/proximity
Proximity component
2016-02-07 00:54:47 -08:00
Nick Waring cd0cef6403 Component to track the proximity of devices to a zone 2016-02-07 08:52:32 +00:00
Nick Waring 0d2891ebcc Test file for the proximity component 2016-02-07 08:51:21 +00:00
Paulus Schoutsen fb6aded2e1 Update frontend with new dependencies 2016-02-07 00:15:09 -08:00
Paulus Schoutsen 8b7cfc831d Merge pull request #1133 from balloob/cast-fix
Fix Chromecast discovery
2016-02-06 23:06:23 -08:00
Paulus Schoutsen 987be65d55 Update frontend to support default_view 2016-02-06 23:01:44 -08:00
Paulus Schoutsen f08b77dc4c Clean up state_attributes vs device_state_attributes 2016-02-06 22:34:24 -08:00
Paulus Schoutsen 681b84e1bd Update frontend with FF url-sync fix 2016-02-06 22:07:30 -08:00
Paulus Schoutsen 4103d7463b Merge pull request #1155 from balloob/fix-kb-interrupt
Fix Ctrl+C with Subprocesses
2016-02-06 20:12:27 -08:00
Paulus Schoutsen 428750eeda Merge pull request #1151 from kk7ds/add-mfi
Add support for Ubiquiti mFi sensors and switches
2016-02-06 20:11:53 -08:00
Paulus Schoutsen 0ae36e1d28 Merge pull request #1140 from kk7ds/add-nx584-alarm
Add Caddx/GE/Interlogix NetworX alarm panel support
2016-02-06 20:09:25 -08:00
Philip Lundrigan d2e8721918 Merge pull request #1124 from philipbl/fix_influx
Fix InfluxDB field type conflict
2016-02-06 20:49:20 -07:00
Philip Lundrigan bbdc196127 Use entity_id attribute 2016-02-06 20:33:43 -07:00
Paulus Schoutsen a417156d84 Merge pull request #1154 from balloob/fix-time-utils
Fixed time zone conversion with no TZ specified
2016-02-06 19:10:14 -08:00
Dan Smith 3575ddb6ef Add Caddx/GE/Interlogix NetworX alarm panel support
This adds support for NetworX-based alarm panels and should work for
any such panel equipped with a NX584 serial interface module. This
includes NX-4/6/8/8E, where the NX8E has this interface built-in.

It requires the pynx584 module, and requires running the server
component somewhere that has connectivity to the panel via serial,
which may include a serial-over-lan connection.
2016-02-07 03:06:58 +00:00
Dan Smith ffc4822f50 Add support for Ubiquiti mFi switchable devices
This adds support for mFi devices that are swichable,
such as the mFi Outlet device.
2016-02-07 03:02:49 +00:00
Dan Smith a147304be9 Add support for Ubiquiti mFi sensors
This adds support for sensors based on Ubiquiti's mFi platform.
All ports/sensors are detected from the mFi controller and exposed.
2016-02-07 02:58:04 +00:00
Ryan Kraus a001780afb Fix Ctrl+C with Subprocesses
Added KeyboardInterrupt handling back to block_till_stopped method.
This is because Keyboard Interrupts are sent to both the parent and
child process in no particular order so both need to handle the
interrupt.
2016-02-06 21:50:06 -05:00
Ryan Kraus 7a00bf8696 Fixed time zone conversion with no TZ specified
Using .replace to set the current time zone appears to not handle
things correctly. The proper way to do this is apparently .localize.
2016-02-06 21:31:07 -05:00
Paulus Schoutsen 7eef831ff3 Merge pull request #1141 from kk7ds/add-uvc-cameras
Add Ubiquiti Unifi Video Camera support
2016-02-06 18:21:44 -08:00
Paulus Schoutsen 9fde97efed Merge pull request #1152 from balloob/bump-pyvera-version
Bump pyvera, pywemo, add available for wemo
2016-02-06 16:43:42 -08:00
MartinHjelmare d773ad1ecb Fix mysensors version errors
* The application of the version check in unit_of_measurement was
	messed up after the last refactor. Fix that again.
* An error could occur in device_state_attributes if there was a
	mismatch between used value_type in the device and mysensors
	version in config. Add try... except to handle that.

Bump pyvera version.

Bump pywemo version.

Add unavailable status before properly initialised for maker and insight.
2016-02-07 00:28:12 +00:00
Dan Smith cab1100a51 Add Ubiquiti Unifi Video Camera support
This adds support for Ubiquiti's UniFi Video cameras via their
NVR device (or service). By configuring just the address of the
NVR and a valid API key, all cameras are discovered and enabled,
including direct-to-camera image snapshot-based video support.
2016-02-06 22:57:44 +00:00
Dan Smith 3616d7a7ea Fix alarm service handler state updates
This changes the service handler dispatch code to always
call update_ha_state(), and cleans up the alarm platforms that
were calling it themselves.
2016-02-06 21:11:28 +00:00
Paulus Schoutsen d38ad57b7d Merge pull request #1142 from kk7ds/add-honeywellus-thermostat
Add Honeywell US thermostat support
2016-02-06 13:05:53 -08:00
Paulus Schoutsen b3e966665a Merge pull request #1150 from bradsk88/master
Updating to python-wink 0.5.0
2016-02-06 13:04:42 -08:00
bradsk88 6e69737e88 Updating to python-wink 0.5.0
Major bugfix.  ``` get_bulbs ``` and similar methods were always returning empty lists.

Better unit tests have been added to https://github.com/bradsk88/python-wink to avoid this regression.
2016-02-06 14:22:46 -06:00
Dan Smith 062fe79b3f Add Honeywell US thermostat support
This adds support for the US variant of the Honeywell connected
thermostat. The interface is super simple, so this doesn't add
any external dependencies. It supports basic temperature, setpoint,
and control.

Issue #998 notes that the existing honeywell module doesn't work
for US models, which is because they are totally different. In order
to indicate to the honeywell platform module that the thermostat
is a US-type, we key off of whether or not the thermostat id is
provided. This is something that US people have (and require to
identify one of potentially multiple thermostats in their account)
and EU people will not.
2016-02-06 20:14:40 +00:00
Paulus Schoutsen af0a44d976 Merge pull request #1144 from MartinHjelmare/fix-mysensors-errors
Fix mysensors version errors
2016-02-06 11:35:49 -08:00
Paulus Schoutsen 43613f000d Merge pull request #1147 from balloob/debug-flag
Fix Interactive Debuggers
2016-02-06 09:56:12 -08:00
Paulus Schoutsen dde80850a6 Merge pull request #1112 from balloob/switch-template
First cut of switch.template
2016-02-06 09:12:13 -08:00
Ryan Kraus 11a2b8888b Fixes for issue #1114
1. Moved RESTART_EXIT_CODE to constants so it can safely be used by
__main__.py.
2. Allowed __main__/main to return the desired exit code.
3. Forwarded the child processes exit code to the parent process to be
duplicated.
4. Added —debug flag to pass command to force Home Assistant to run in
only one process. A warning is printed to STDERR to indicate HASS is in
debug mode. Another is printed if HASS requests a restart in debug
mode. A restart request in debug mode will quit.
5. Added an argument to __main__/main/setup_and_run_hass to indicate
that it is running in the top process. This tells it to return the exit
code rather than exiting.
2016-02-06 09:48:36 -05:00
MartinHjelmare b700ec4faa Fix mysensors version errors
* The application of the version check in unit_of_measurement was
	messed up after the last refactor. Fix that again.
* An error could occur in device_state_attributes if there was a
	mismatch between used value_type in the device and mysensors
	version in config. Add try... except to handle that.
2016-02-06 10:57:42 +01:00
Fabian Affolter 614034d196 Update docstrings 2016-02-06 08:23:30 +01:00
Paulus Schoutsen c9d145cb13 Merge pull request #1120 from haraldnagel/dev
Add BloomSky weather station support
2016-02-05 20:06:53 -08:00
Harald Nagel b6a32098d1 Add BloomSky weather station support 2016-02-06 01:37:32 +00:00
Flyte 4cf85294db Add template support to mqtt.publish service payload. 2016-02-05 21:47:27 +00:00
Eric Rolf 6fc68e9c8a Forgot to refactor demo. 2016-02-05 15:16:56 -05:00
Eric Rolf ec88733b57 Refactor Method Name For Open and Close. 2016-02-05 15:10:53 -05:00
Eric Rolf 7ef2075520 Updated requirements_all.txt 2016-02-05 14:20:06 -05:00
Eric Rolf fbd0dbf8ee Wink Garage Door Support 2016-02-05 12:53:57 -05:00
Paulus Schoutsen 2ba237eac8 Merge pull request #1134 from fabaff/icons
Add icon
2016-02-05 07:33:31 -08:00
Fabian Affolter 6bf4532608 Add icon 2016-02-05 13:08:17 +01:00
pavoni 2622cf2e53 Use available, remove state, improve true,false tests. 2016-02-05 11:18:50 +00:00
Paulus Schoutsen a5db23afa4 Mock util.location by default 2016-02-04 22:26:02 -08:00
Paulus Schoutsen 2c4166b5f2 Update splunk.py 2016-02-04 22:06:27 -08:00
Paulus Schoutsen 8be9aaba4f Fix Chromecast discovery 2016-02-04 21:36:37 -08:00
Paulus Schoutsen 1b16d76c40 Merge pull request #1128 from miniconfig/splunk
Added a new component to log state changes to a Splunk instance using…
2016-02-04 20:58:12 -08:00
Paulus Schoutsen 1a6539ad41 Merge pull request #1110 from lukas-hetzenecker/feature-zwave-poll-and-scene
Z-Wave: Scene activation & Polling
2016-02-04 20:51:52 -08:00
Paulus Schoutsen 96066e94ab Merge pull request #1122 from molobrakos/tellduslive
reworked telldus live support
2016-02-04 20:46:23 -08:00
nkgilley@gmail.com 78e758925b remove commented lines. 2016-02-04 18:42:38 -05:00
nkgilley@gmail.com 19fc48f4a0 use raw regex 2016-02-04 18:39:09 -05:00
nkgilley@gmail.com d469970e5a Speedtest.net component 2016-02-04 18:21:37 -05:00
Erik 50a9b3a7c0 reworked telldus live support 2016-02-05 00:07:12 +01:00
miniconfig ab837f9070 Added a new component to log state changes to a Splunk instance using the HTTP Event Collector 2016-02-04 16:13:55 -05:00
Paulus Schoutsen 6149e509c3 Merge pull request #1127 from molobrakos/timedate
provide default icon
2016-02-04 13:04:09 -08:00
Erik f3b74079e0 provide default icon 2016-02-04 21:55:22 +01:00
Philip Lundrigan c580953bd8 Fix MQTT sensor 2016-02-04 12:38:48 -07:00
Philip Lundrigan fc3741911c Fix problem with field type conflict in influxdb 2016-02-04 12:38:48 -07:00
Lukas Hetzenecker 2589e78e84 Z-Wave: This small refactor adds the following features:
* The poll interval got fixed and the poll intensity gets configurable in the settings
* Activated scenes now fire an event
2016-02-04 19:54:43 +01:00
pavoni ced380f0cd Remove unneeded entity_id check and blank lines. 2016-02-04 17:24:38 +00:00
Paulus Schoutsen a33f1c61e5 Merge pull request #1108 from lukas-hetzenecker/regression-zwave-light
Z-Wave Regression: Fix state attributes of lights and switches.
2016-02-03 21:25:13 -08:00
Lukas Hetzenecker 8cf5ca0ba8 Regression: The device specific attributes of ZWave devices got lost.
Light and ZWaveDeviceEntity both have overwritten the property state_attributes
This includes the device specific attributes in the state_attributes again
2016-02-04 02:12:33 +01:00
pavoni b20d3f8b3a Update docstrings. 2016-02-03 23:23:19 +00:00
Paulus Schoutsen 1f34b3586e Merge pull request #1117 from philipbl/influx_ssl
Add ability to specify SSL for InfluxDB connection
2016-02-03 13:33:08 -08:00
Paulus Schoutsen 37dadd1ae0 Merge pull request #974 from sdague/domains
Add recording of domain to state tables
2016-02-03 12:26:31 -08:00
Paulus Schoutsen fac8d4b969 Merge pull request #1115 from sdague/test
add pytest-timeout to test runs
2016-02-03 12:24:35 -08:00
Sean Dague efcba8f1ca add pytest-timeout to test runs
This adds a default 30 second timeout on every test method so that
deadlocks or broken threads are move obvious in travis. It also passes
-v by default to make things a little more verbose on where things
fail when they are failing.
2016-02-03 15:13:30 -05:00
Sean Dague abc253c4c5 implement get_significant_states
This adds a new function to history module which returns significant
states. For most domains this is the list of state changes. For the
thermostat domain this also includes attribute changes, so that
changes in the current_temperature are exposed to the graphing layer.

Closes #881
2016-02-03 15:05:43 -05:00
Sean Dague 3d00735341 Add recording of domain to state tables
Some domains, like thermostat, need all state records, not just state
change ones, to provide accurate graphs. This introduces a new db
migration which adds a 'domain' column to all states so that is a fast
query.

Indexes were added to help with query performance.

This includes a data migration which post-date populates domain. On
large HA dbs this might take real time, as it has to touch every state
row. 100 MB db (91k states) updated in a couple of seconds on my
reasonably fast server. Be forewarned.

This is part of bug #881
2016-02-03 15:05:43 -05:00
Paulus Schoutsen ce75c590b1 Merge pull request #1109 from lukas-hetzenecker/bugfix-zwave-fibaro-wall-plug
Z-Wave: Bugfix for Fibaro Wall Plug component
2016-02-03 08:34:06 -08:00
pavoni 6e6c3c5cd5 Tidy. 2016-02-03 14:30:58 +00:00
pavoni 5521096c02 Add actions. 2016-02-03 14:29:25 +00:00
Fabian Affolter 356013118d Update docstrings 2016-02-03 15:13:53 +01:00
pavoni 9a9dbcfaea Refactor, support template logic values, add tests. 2016-02-03 13:16:13 +00:00
Lukas Hetzenecker 1c33e01b99 styleguide fix 2016-02-03 13:03:01 +01:00
Lukas Hetzenecker d09837fef6 Zwave: This is a bugfix for the Fibaro Wall Plug component
As discussed here ( https://www.domoticz.com/forum/viewtopic.php?f=6&t=5661 ) this components reports two different power consumption values.
Unfortunately only one of them is correct. Both of them map to the exactly same object id.
This bugfix gets rid of the incorrect one.
2016-02-03 12:44:11 +01:00
Paulus Schoutsen f5e736d271 Merge pull request #1105 from balloob/disable-location-in-tests
Disable location queries
2016-02-02 21:38:20 -08:00
Paulus Schoutsen 61630783f1 Disable location queries 2016-02-02 21:33:59 -08:00
Paulus Schoutsen 077797ac4f Merge pull request #1086 from carlosmgr/dev
update SSH for aruba device tracker
2016-02-02 18:33:49 -08:00
Paulus Schoutsen b14f7f7ed0 Merge pull request #1099 from balloob/sensor_template_startup_error
Sensor template startup error
2016-02-02 18:32:09 -08:00
carlosmgr 3d695405b7 pep8 2 time 2016-02-03 00:03:50 +00:00
carlosmgr 55932b048e fix pep8 2016-02-02 23:54:32 +00:00
Fabian Affolter b19fbd8e72 Update docstrings 2016-02-03 00:35:53 +01:00
Fabian Affolter 635369ad65 Update docstrings 2016-02-03 00:23:59 +01:00
Fabian Affolter bd8881cbe1 Add icon 2016-02-03 00:12:01 +01:00
Fabian Affolter 847e92f57a Update docstrings 2016-02-03 00:01:26 +01:00
Fabian Affolter 5cea8fda9f Update link to docs and add docstrings 2016-02-02 23:55:44 +01:00
carlosmgr 7f87df20c2 ssh aruba 2016-02-02 22:40:04 +00:00
carlosmgr e91c8e4143 ssh aruba.py
req pexpect
2016-02-02 21:49:11 +00:00
carlosmgr cd00ff8b56 Merge remote-tracking branch 'upstream/dev' into dev 2016-02-02 20:07:31 +00:00
Philip Lundrigan 018329b12b Add ability to specify ssl 2016-02-02 12:58:38 -07:00
pavoni a955f3db08 WIP commit - template state working, on / off still to do. 2016-02-02 19:25:17 +00:00
pavoni d344defc7e Switch to warning. 2016-02-02 16:26:17 +00:00
Paulus Schoutsen 2da422fd77 Merge pull request #1098 from balloob/media-player-test
Clean up and test media player
2016-02-02 08:09:40 -08:00
pavoni 93a38d39ef Add test for missimng attributes. 2016-02-02 14:15:06 +00:00
pavoni 3aad223c95 Change to warning if attributes missing. 2016-02-02 14:14:29 +00:00
Ryan Kraus 1a5d18fd66 Merge pull request #1012 from balloob/restart-service
Restart service
2016-02-02 07:15:40 -05:00
Paulus Schoutsen e7e540d4bb Clean up and test media player 2016-02-02 00:31:36 -08:00
Paulus Schoutsen 35613d7fbf Merge pull request #1097 from molobrakos/add-test
add test for unsafe yaml
2016-02-02 00:22:19 -08:00
Erik 00d1cab091 add test for unsafe yaml 2016-02-02 08:41:18 +01:00
Paulus Schoutsen 26efaa91a3 Merge pull request #1090 from balloob/fix_sensor_template_entity_id
Fix bug in sensor.template entity_id
2016-02-01 21:21:57 -08:00
Paulus Schoutsen 3c37ecc477 Merge pull request #1091 from TangoAlpha/dev
Improvements to device detection
2016-02-01 21:21:15 -08:00
Paulus Schoutsen 274aaabd93 Merge pull request #1094 from balloob/cast-multi-room
No longer ignore ports for Chromecasts
2016-02-01 21:16:50 -08:00
Paulus Schoutsen c8bfd27182 No longer ignore ports for Chromecasts 2016-02-01 21:07:33 -08:00
Tim 08ab7dba2c Fix whitespace 2016-02-02 00:21:15 +00:00
Tim 54cc35d729 Merge branch 'dev' of https://github.com/TangoAlpha/home-assistant into dev 2016-02-02 00:17:03 +00:00
Tim 031e7a4013 New liffylights release improves device detection
Increase device polling to 30 seconds
2016-02-02 00:15:38 +00:00
Paulus Schoutsen 41165695f0 Merge pull request #1071 from deisi/onewireupdate
Little improvement for the onewire platform
2016-02-01 15:26:22 -08:00
pavoni 9c33af60f2 Fix unreachable code! 2016-02-01 18:38:11 +00:00
pavoni 7c1241c1f8 Add another test, revise another. Improve coverage. 2016-02-01 18:30:39 +00:00
Tim 9caa4752a4 New liffylights release improves device detection
Increase device polling to 30 seconds
2016-02-01 18:29:43 +00:00
Malte Deiseroth cb2e75befd removed trailing whitespace 2016-02-01 19:24:08 +01:00
pavoni d54e10e54a Improve test coverage of error conditions. 2016-02-01 18:18:51 +00:00
pavoni 95748a6880 Generate entity id correctly, was using friendly_name. 2016-02-01 17:45:18 +00:00
Paulus Schoutsen 10a41a22dc Merge pull request #1084 from molobrakos/tellduslive
handle situation where no name is set yet for the sensor
2016-02-01 08:55:39 -08:00
Erik ac0b6ca50c handle situation where no name is set yet for the sensor 2016-02-01 17:42:08 +01:00
Paulus Schoutsen a0f6f3ac22 Merge pull request #1085 from molobrakos/yaml_safe_load
use yaml safe loader
2016-02-01 08:15:32 -08:00
Paulus Schoutsen d9aff0c76d Merge pull request #1089 from balloob/yr
Added and fixed yr tests
2016-02-01 08:11:18 -08:00
Daniel 5005b20122 Added and fixed yr tests 2016-02-01 15:50:17 +01:00
carlosmgr b3ef2bd2d9 Update aruba.py 2016-02-01 12:23:20 +00:00
Fabian Affolter e29a2fa45a Fix typo 2016-02-01 11:49:44 +01:00
Fabian Affolter 395743005a Add link to docs, new docstrings, and update docstrings 2016-02-01 11:47:09 +01:00
Fabian Affolter 8de56bc8e2 Fix docstrings 2016-02-01 11:31:27 +01:00
Paulus Schoutsen 79b6269aa2 Merge pull request #1083 from balloob/fix-requirements
Fix gen_requirements_all
2016-02-01 00:00:51 -08:00
Paulus Schoutsen 525b206e1b Fix gen_requirements_all 2016-01-31 23:55:02 -08:00
Paulus Schoutsen 2f4e40db27 Merge pull request #1076 from MartinHjelmare/add-entity-online-prop
Fix 'unavailable' entity
2016-01-31 17:26:12 -08:00
MartinHjelmare 38c9f7a37a Fix 'unavailable' entity
* Report friendly_name, icon and customized attributes for 'unavailable'
  entities.
2016-02-01 02:16:56 +01:00
Paulus Schoutsen c725f7883a Merge pull request #1063 from stefan-jonasson/samsungtv
Added a new media_player platform for controlling Samsung TVs
2016-01-31 16:57:11 -08:00
Paulus Schoutsen 10f79ab45d Merge pull request #1073 from MartinHjelmare/add-entity-online-prop
Add new 'available' property to entity.py
2016-01-31 15:59:28 -08:00
MartinHjelmare 455593017d Add new 'available' property to entity.py
* Add 'available' property. Return True by default.
* Use new property in update_ha_state(). If available is False, set
  state to 'unavailable', through constant.
* Add STATE_UNAVAILABLE constant 'unavailable' in const.py.
* Fix docstrings in entity.py and const.py, according to PEP257.
  Ignore D203 and D105.
2016-01-31 23:58:19 +01:00
Malte Deiseroth d6b19aae48 - check for reasonable temperature values
- round temperature to one digit
2016-01-31 22:56:48 +01:00
Paulus Schoutsen 6519333e1d Merge pull request #1070 from balloob/input-select
Add input_select component
2016-01-31 13:44:32 -08:00
Paulus Schoutsen 41919e7339 Update frontend for input_select 2016-01-31 13:39:50 -08:00
Stefan Jonasson 1c10f218de Fixed duplicate import statements and made use of the config_helper 2016-01-31 22:17:00 +01:00
Paulus Schoutsen 96710ad410 Add input_select component 2016-01-31 12:52:51 -08:00
Paulus Schoutsen c95c3d9198 Update frontend with weblink support 2016-01-31 12:00:45 -08:00
Stefan Jonasson 5719743ec7 Fixed .coveragerc and requirements_all.txt 2016-01-31 20:02:51 +01:00
Per Sandström e2e8d4276f Merge pull request #1069 from persandstrom/vsure0.5.0
vsure 0.5.0
2016-01-31 19:33:09 +01:00
Per Sandström 3f03fefd35 vsure 0.5.0 2016-01-31 19:23:53 +01:00
Stefan Jonasson 2dab815f90 Fixes imports, styles and other misstates 2016-01-31 19:12:00 +01:00
Paulus Schoutsen 6a4b63f807 Merge pull request #1056 from flavio/scsgate
Add support for the SCSGate device
2016-01-31 09:34:07 -08:00
Flavio Castelli 8eef978241 Add support for the SCSGate device
Support the SCSGate device. This will allow home-assistant to interact
with BTicino/Legrand MyHome system.

Signed-off-by: Flavio Castelli <flavio@castelli.me>
2016-01-31 18:30:43 +01:00
Paulus Schoutsen 1789a08d21 Merge pull request #1065 from kennedyshead/weblink
Weblink component
2016-01-31 09:20:17 -08:00
magnusknutas de4dab74b1 Adding weblink component
Adding weblink component tests
2016-01-31 18:13:04 +01:00
Paulus Schoutsen 16b1529d14 Merge pull request #1059 from balloob/entity-component-enhancements
Add tests and custom interval for entity component
2016-01-31 09:03:55 -08:00
Paulus Schoutsen 0b8e097705 Remove unused environment util 2016-01-31 08:58:30 -08:00
Paulus Schoutsen b21be63220 Merge pull request #1066 from TangoAlpha/dev
Update for new liffylights release
2016-01-31 08:32:51 -08:00
Tim e6a8746dba Update requirements_all 2016-01-31 15:07:06 +00:00
Tim 1974eda51d Update for new liffylights release
Fix incorrect packet timeout/ack code causing flooding when no bulbs were online, which consumed all WorkerPool threads
2016-01-31 13:31:12 +00:00
Stefan Jonasson bd475f5db1 Added a new media_player platform for controlling Samsung TVs with a lan interface.
Configured like this
 media_player:
  platform: samsungtv
  host: <IP OF TV>
  name: <Name of the tv>
2016-01-31 10:06:39 +01:00
Paulus Schoutsen fce8815ab4 Support custom interval for platforms 2016-01-31 01:01:23 -08:00
Paulus Schoutsen 90e17fc77f Add tests for entity component 2016-01-31 01:01:23 -08:00
Paulus Schoutsen 6418634f3a Merge pull request #1049 from balloob/new-tests
Adding more unit tests
2016-01-30 23:32:34 -08:00
Ryan Kraus a230d00ed0 Added test for Introduction component
This test may seem useless, but it is good to ensure that default
components don’t ever crash HASS.
2016-01-30 22:50:56 -05:00
Ryan Kraus 5fdbe5fd9a More tests for Binary Command Sensor
1. Added a test for detecting STATE_OFF
2. Fixed tests for detecting STATE_ON
2016-01-30 22:41:29 -05:00
Ryan Kraus 283d621e90 Added tests for Binary Command Sensor 2016-01-30 22:32:25 -05:00
Ryan Kraus 2d0004f46a Another test for for command sensor
Added a test for command sensors with bad configurations.
2016-01-30 22:16:22 -05:00
Ryan Kraus 6a08f14120 Additional tests for Command Sensor.
1. Moved template testing out of main test.
2. Added test for bad command.
2016-01-30 22:13:42 -05:00
Ryan Kraus 97e867052d Added tests for command sensor
Added tests to create and check basic functionality of command sensor.
2016-01-30 22:01:10 -05:00
Ryan Kraus 2651021461 Added test for entity customization
Added test for entity customization from configuration. Processes a
sample configuration to hide an entity, creates the entity, updates ha
state, and then verifies customization made it through.
2016-01-30 21:27:00 -05:00
Erik 4b253d17ba use yaml safe loader 2016-01-31 00:46:08 +01:00
Paulus Schoutsen b7722ec452 Allow usage of words domain, service, call_id in service data 2016-01-30 15:18:26 -08:00
magnusknutas fd6086a5d6 Testing logbook service 2016-01-30 15:18:26 -08:00
Paulus Schoutsen 3e35bc06fc Merge pull request #1055 from TangoAlpha/dev
Update to new release of liffylights
2016-01-30 12:56:22 -08:00
Tim f76dee8a05 Update to new release of liffylights 2016-01-30 20:48:57 +00:00
Ryan Kraus 56ac4281c7 Better tear down of util/package tests
Explicitly removed temp directory at the end of util/package unit tests.
2016-01-30 14:39:17 -05:00
Ryan Kraus b8e149fe7d Cleaned up universal MP tests
Changed all assertEquals in universal media player tests to have
assumed value first.
2016-01-30 14:38:27 -05:00
Ryan Kraus 4a8f55e630 Revised package util tests
The package util tests were revised to pull the external library
pyhelloworld3 from an internal source rather than external. This speeds
up tests, makes tests more reliable, and removes dependency on internet
connection.
2016-01-30 14:08:32 -05:00
Ryan Kraus de61bcb80e Additional testing for logger component
Added an additional test for the logger component the validates the
filtering logic of the filters that were created during setup.
2016-01-30 13:23:35 -05:00
Ryan Kraus 4cc9606bcc Added test for logger component. 2016-01-30 13:03:46 -05:00
Ryan Kraus 8ac763c6f6 Added test for universal mp service routing.
Added tests to ensure that the Universal Media Player is routing
service calls correctly.
2016-01-30 12:26:28 -05:00
Ryan Kraus 6a75b524cb Removed unused private method from universal media player
The universal media player contained a private method that was replaced
by the update method. It was meant to be removed and wasn’t. This
commit removed that method.
2016-01-30 11:57:46 -05:00
Ryan Kraus c1d057407b Fixed typo in universal media player test. 2016-01-30 11:53:15 -05:00
Ryan Kraus c396dbb570 Added tests to check setup and config of universal media player. 2016-01-30 07:18:37 -05:00
Ryan Kraus 0631f5c59d Added tests for package utilities 2016-01-30 06:44:22 -05:00
Paulus Schoutsen 10f9c049bb Version bump to 0.13.0.dev0 2016-01-29 22:38:01 -08:00
Ryan Kraus 106c53abf1 Revised HASS Core test
Changed the HASS Core test that tested KeyboardInterrupt handling to
now test SIGTERM handling. KeyboardInterrupts are no longer handled in
the HASS application process as they are handled in the HASS parent
process. SIGTERM is the proper way to now stop HASS.
2016-01-29 22:42:39 -05:00
Ryan Kraus b56369855a Cleaned up restart handling in __main__.py
1. Fixed logged message about SIGTERM binding failure.
2. Set to only restart HASS with an exit code of 100.
3. Fixed typo in comment.
2016-01-29 22:11:11 -05:00
Ryan Kraus a41b66bb94 Cleaned up block_till_stop in core.py
1. Removed handling of KeyboardInterrupt. This will no longer happen
now that HASS is run in a subprocess. The KeyboardInterrupt will not be
sent to the parent process which will send a SIGTERM to the HASS
process.
2. Fixed logger warning about not being able to bind to SIGTERM.
3. Removed check for Windows OSs when binding to SIGTERM. This check
was originally put in place when HASS was binding to SIGQUIT. SIGTERM
exists in NT OSs, so the check is no longer required.
3. Now returning exit code of 100 when requesting a restart. This will
allow the parent process to only restart HASS if it is specifically
requested and not just on any encountered crash.
2016-01-29 22:02:39 -05:00
Ryan Kraus 3534c975f3 Added missing CONF_ICON constant 2016-01-26 22:46:01 -05:00
Ryan Kraus 519abbbfa2 Better handling of second KeyboardInterrupt
Now the second KeyboardInterrupt will be cleanly handled by the parent
process.
2016-01-26 22:41:57 -05:00
Ryan Kraus b596fa33d6 Implemented restart service
Implemented an OS and environment safe restart service. This works by
running Home Assistant in a child process. If the child process
terminates with an exit code > 0, HASS is restarted. SIGTERM and
KeyboardInterrupts to the parent process are forwarded to the child
process. KeyboardInterrupts will only be forwarded once. The second
KeyboardInterrupt will be handled by the parent.
2016-01-26 22:39:59 -05:00
401 changed files with 19291 additions and 7698 deletions
+28 -7
View File
@@ -6,10 +6,17 @@ omit =
# omit pieces of code that rely on external devices being present
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
homeassistant/components/bloomsky.py
homeassistant/components/*/bloomsky.py
homeassistant/components/insteon_hub.py
homeassistant/components/*/insteon_hub.py
@@ -32,6 +39,9 @@ omit =
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/wemo.py
homeassistant/components/*/wemo.py
homeassistant/components/wink.py
homeassistant/components/*/wink.py
@@ -53,10 +63,16 @@ omit =
homeassistant/components/rpi_gpio.py
homeassistant/components/*/rpi_gpio.py
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
homeassistant/components/binary_sensor/arest.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/browser.py
homeassistant/components/camera/*
homeassistant/components/camera/bloomsky.py
homeassistant/components/camera/foscam.py
homeassistant/components/camera/generic.py
homeassistant/components/camera/mjpeg.py
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
@@ -73,9 +89,8 @@ omit =
homeassistant/components/device_tracker/ubus.py
homeassistant/components/discovery.py
homeassistant/components/downloader.py
homeassistant/components/garage_door/wink.py
homeassistant/components/ifttt.py
homeassistant/components/statsd.py
homeassistant/components/influxdb.py
homeassistant/components/keyboard.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/hue.py
@@ -89,24 +104,29 @@ omit =
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/mpd.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/samsungtv.py
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/nma.py
homeassistant/components/notify/pushbullet.py
homeassistant/components/notify/pushetta.py
homeassistant/components/notify/pushover.py
homeassistant/components/notify/rest.py
homeassistant/components/notify/sendgrid.py
homeassistant/components/notify/slack.py
homeassistant/components/notify/smtp.py
homeassistant/components/notify/syslog.py
homeassistant/components/notify/telegram.py
homeassistant/components/notify/twitter.py
homeassistant/components/notify/xmpp.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/bitcoin.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/deutsche_bahn.py
homeassistant/components/sensor/dht.py
homeassistant/components/sensor/dweet.py
homeassistant/components/sensor/efergy.py
@@ -114,10 +134,13 @@ omit =
homeassistant/components/sensor/forecast.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/rest.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/sensor/steam_online.py
homeassistant/components/sensor/speedtest.py
homeassistant/components/sensor/swiss_public_transport.py
homeassistant/components/sensor/systemmonitor.py
homeassistant/components/sensor/temper.py
@@ -128,19 +151,17 @@ omit =
homeassistant/components/sensor/worldclock.py
homeassistant/components/switch/arest.py
homeassistant/components/switch/edimax.py
homeassistant/components/switch/dlink.py
homeassistant/components/switch/hikvisioncam.py
homeassistant/components/switch/mystrom.py
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wemo.py
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/honeywell.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
+31
View File
@@ -0,0 +1,31 @@
**Home Assistant release (`hass --version`):**
**Python release (`python3 --version`):**
**Component/platform:**
**Description of problem:**
**Expected:**
**Problem-relevant `configuration.yaml` entries and steps to reproduce:**
```yaml
```
1.
2.
3.
**Traceback (if applicable):**
```bash
```
**Additional info:**
+28
View File
@@ -0,0 +1,28 @@
**Description:**
**Related issue (if applicable):** #
**Example entry for `configuration.yaml` (if applicable):**
```yaml
```
**Checklist:**
- [ ] Local tests with `tox` ran successfully.
- [ ] No CI failures. **Your PR cannot be merged unless CI is green!**
- [ ] [Fork is up to date][fork] and was rebased on the `dev` branch before creating the PR.
- If code communicates with devices:
- [ ] 3rd party library/libraries for communication is/are added as dependencies via the `REQUIREMENTS` variable ([example][ex-requir]).
- [ ] 3rd party dependencies are imported inside functions that use them ([example][ex-import]).
- [ ] `requirements_all.txt` is up-to-date, `script/gen_requirements_all.py` ran and the updated file is included in the PR.
- [ ] New files were added to `.coveragerc`.
- If the code does not depend on external Python module:
- [ ] Tests to verify that the code works are included.
- [ ] [Commits will be squashed][squash] when the PR is ready to be merged.
[fork]: http://stackoverflow.com/a/7244456
[squash]: https://github.com/ginatrapani/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit
[ex-requir]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16
[ex-import]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51
+15 -11
View File
@@ -1,15 +1,19 @@
sudo: false
language: python
matrix:
fast_finish: true
include:
- python: "3.4"
env: TOXENV=py34
- python: "3.4"
env: TOXENV=requirements
- python: "3.5"
env: TOXENV=lint
- python: "3.5"
env: TOXENV=py35
cache:
directories:
- $HOME/.cache/pip
# - "$HOME/virtualenv/python$TRAVIS_PYTHON_VERSION"
python:
- 3.4
- 3.5
install:
- "true"
script:
- script/cibuild
matrix:
fast_finish: true
install: pip install -U tox coveralls
language: python
script: tox
after_success: coveralls
+20 -4
View File
@@ -6,7 +6,7 @@ The process is straight-forward.
- Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant).
- Write the code for your device, notification service, sensor, or IoT thing.
- Check it with ``pylint`` and ``flake8``.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should read the next sections and get more details.
@@ -17,12 +17,13 @@ For help on building your component, please see the [developer documentation](ht
After you finish adding support for your device:
- Check that all dependencies are included via the `REQUIREMENTS` variable in your platform/component and only imported inside functions that use them.
- Add any new dependencies to `requirements_all.txt` if needed. Use `script/gen_requirements_all.py`.
- Update the `.coveragerc` file to exclude your platform if there are no tests available.
- Update the `.coveragerc` file to exclude your platform if there are no tests available or your new code uses a 3rd party library for communication with the device/service/sensor.
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/balloob/home-assistant.io).
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `./script/lint`.
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `tox` or `script/lint`.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
- Check for comments and suggestions on your Pull Request and keep an eye on the [Travis output](https://travis-ci.org/balloob/home-assistant/).
- Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/balloob/home-assistant/).
If you add a platform for an existing component, there is usually no need for updating the frontend. Only if you've added a new component that should show up in the frontend, there are more steps needed:
@@ -66,6 +67,21 @@ The frontend is composed of [Polymer](https://www.polymer-project.org) web-compo
When you are done with development and ready to commit your changes, run `build_frontend`, set `development=0` in your config and validate that everything still works.
## Testing your code
To test your code before submission, used the `tox` tool.
```shell
> pip install -U tox
> tox
```
This will run unit tests against python 3.4 and 3.5 (if both are available locally), as well as run a set of tests which validate `pep8` and `pylint` style of the code.
You can optionally run tests on only one tox target using the `-e` option to select an environment.
For instance `tox -e lint` will run the linters only, `tox -e py34` will run unit tests only on python 3.4.
### Notes on PyLint and PEP8 validation
In case a PyLint warning cannot be avoided, add a comment to disable the PyLint check for that line. This can be done using the format `# pylint: disable=YOUR-ERROR-NAME`. Example of an unavoidable PyLint warning is if you do not use the passed in datetime if you're listening for time change.
+2
View File
@@ -6,6 +6,8 @@ VOLUME /config
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN pip3 install --no-cache-dir colorlog
# For the nmap tracker
RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools && \
+13 -14
View File
@@ -1,6 +1,5 @@
"""
custom_components.example
~~~~~~~~~~~~~~~~~~~~~~~~~
Example of a custom component.
Example component to target an entity_id to:
- turn it on at 7AM in the morning
@@ -37,21 +36,21 @@ import homeassistant.components as core
from homeassistant.components import device_tracker
from homeassistant.components import light
# The domain of your component. Should be equal to the name of your component
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "example"
# List of component names (string) your component depends upon
# List of component names (string) your component depends upon.
# We depend on group because group will be loaded after all the components that
# initialize devices have been setup.
DEPENDENCIES = ['group', 'device_tracker', 'light']
# Configuration key for the entity id we are targetting
# Configuration key for the entity id we are targeting.
CONF_TARGET = 'target'
# Variable for storing configuration parameters
# Variable for storing configuration parameters.
TARGET_ID = None
# Name of the service that we expose
# Name of the service that we expose.
SERVICE_FLASH = 'flash'
# Shortcut for the logger
@@ -59,16 +58,16 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Setup example component. """
"""Setup example component."""
global TARGET_ID
# Validate that all required config options are given
# Validate that all required config options are given.
if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER):
return False
TARGET_ID = config[DOMAIN][CONF_TARGET]
# Validate that the target entity id exists
# Validate that the target entity id exists.
if hass.states.get(TARGET_ID) is None:
_LOGGER.error("Target entity id %s does not exist",
TARGET_ID)
@@ -78,13 +77,13 @@ def setup(hass, config):
TARGET_ID = None
return False
# Tell the bootstrapper that we initialized successfully
# Tell the bootstrapper that we initialized successfully.
return True
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
def track_devices(hass, entity_id, old_state, new_state):
""" Called when the group.all devices change state. """
"""Called when the group.all devices change state."""
# If the target id is not set, return
if not TARGET_ID:
return
@@ -94,7 +93,7 @@ def track_devices(hass, entity_id, old_state, new_state):
core.turn_on(hass, TARGET_ID)
# If all people leave the house and the entity is on, turn it off
# If all people leave the house and the entity is on, turn it off.
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
@@ -116,7 +115,7 @@ def wake_up(hass, now):
@track_state_change(light.ENTITY_ID_ALL_LIGHTS, STATE_ON, STATE_OFF)
def all_lights_off(hass, entity_id, old_state, new_state):
""" If all lights turn off, turn off. """
"""If all lights turn off, turn off."""
if not TARGET_ID:
return
+8 -8
View File
@@ -1,7 +1,7 @@
"""
custom_components.hello_world
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Implements the bare minimum that a component should implement.
The "hello world" custom component.
This component implements the bare minimum that a component should implement.
Configuration:
@@ -11,18 +11,18 @@ configuration.yaml file.
hello_world:
"""
# The domain of your component. Should be equal to the name of your component
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "hello_world"
# List of component names (string) your component depends upon
# List of component names (string) your component depends upon.
DEPENDENCIES = []
def setup(hass, config):
""" Setup our skeleton component. """
"""Setup our skeleton component."""
# States are in the format DOMAIN.OBJECT_ID
# States are in the format DOMAIN.OBJECT_ID.
hass.states.set('hello_world.Hello_World', 'Works!')
# return boolean to indicate that initialization was successful
# Return boolean to indicate that initialization was successfully.
return True
+11 -15
View File
@@ -1,6 +1,6 @@
"""
custom_components.mqtt_example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example of a custom MQTT component.
Shows how to communicate with MQTT. Follows a topic on MQTT and updates the
state of an entity to the last message received on that topic.
@@ -15,45 +15,41 @@ configuration.yaml file.
mqtt_example:
topic: home-assistant/mqtt_example
"""
import homeassistant.loader as loader
# The domain of your component. Should be equal to the name of your component
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "mqtt_example"
# List of component names (string) your component depends upon
# List of component names (string) your component depends upon.
DEPENDENCIES = ['mqtt']
CONF_TOPIC = 'topic'
DEFAULT_TOPIC = 'home-assistant/mqtt_example'
def setup(hass, config):
""" Setup our mqtt_example component. """
"""Setup the MQTT example component."""
mqtt = loader.get_component('mqtt')
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
entity_id = 'mqtt_example.last_message'
# Listen to a message on MQTT
# Listen to a message on MQTT.
def message_received(topic, payload, qos):
""" A new MQTT message has been received. """
"""A new MQTT message has been received."""
hass.states.set(entity_id, payload)
mqtt.subscribe(hass, topic, message_received)
hass.states.set(entity_id, 'No messages')
# Service to publish a message on MQTT
# Service to publish a message on MQTT.
def set_state_service(call):
""" Service to send a message. """
"""Service to send a message."""
mqtt.publish(hass, topic, call.data.get('new_state'))
# Register our service with Home Assistant
# Register our service with Home Assistant.
hass.services.register(DOMAIN, 'set_state', set_state_service)
# return boolean to indicate that initialization was successful
# Return boolean to indicate that initialization was successfully.
return True
+105 -35
View File
@@ -1,13 +1,18 @@
""" Starts home assistant. """
from __future__ import print_function
import sys
import os
import argparse
import os
import signal
import sys
import threading
import time
from multiprocessing import Process
from homeassistant import bootstrap
import homeassistant.config as config_util
from homeassistant.const import __version__, EVENT_HOMEASSISTANT_START
from homeassistant import bootstrap
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, RESTART_EXIT_CODE, __version__)
def validate_python():
@@ -73,6 +78,11 @@ def get_arguments():
'--demo-mode',
action='store_true',
help='Start Home Assistant in demo mode')
parser.add_argument(
'--debug',
action='store_true',
help='Start Home Assistant in debug mode. Runs in single process to '
'enable use of interactive debuggers.')
parser.add_argument(
'--open-ui',
action='store_true',
@@ -204,35 +214,11 @@ def uninstall_osx():
print("Home Assistant has been uninstalled.")
def main():
""" Starts Home Assistant. """
validate_python()
args = get_arguments()
config_dir = os.path.join(os.getcwd(), args.config)
ensure_config_path(config_dir)
# os x launchd functions
if args.install_osx:
install_osx()
return
if args.uninstall_osx:
uninstall_osx()
return
if args.restart_osx:
uninstall_osx()
install_osx()
return
# daemon functions
if args.pid_file:
check_pid(args.pid_file)
if args.daemon:
daemonize()
if args.pid_file:
write_pid(args.pid_file)
def setup_and_run_hass(config_dir, args, top_process=False):
"""
Setup HASS and run. Block until stopped. Will assume it is running in a
subprocess unless top_process is set to true.
"""
if args.demo_mode:
config = {
'frontend': {},
@@ -259,7 +245,91 @@ def main():
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, open_browser)
hass.start()
hass.block_till_stopped()
exit_code = int(hass.block_till_stopped())
if not top_process:
sys.exit(exit_code)
return exit_code
def run_hass_process(hass_proc):
""" Runs a child hass process. Returns True if it should be restarted. """
requested_stop = threading.Event()
hass_proc.daemon = True
def request_stop(*args):
""" request hass stop, *args is for signal handler callback """
requested_stop.set()
hass_proc.terminate()
try:
signal.signal(signal.SIGTERM, request_stop)
except ValueError:
print('Could not bind to SIGTERM. Are you running in a thread?')
hass_proc.start()
try:
hass_proc.join()
except KeyboardInterrupt:
request_stop()
try:
hass_proc.join()
except KeyboardInterrupt:
return False
return (not requested_stop.isSet() and
hass_proc.exitcode == RESTART_EXIT_CODE,
hass_proc.exitcode)
def main():
""" Starts Home Assistant. """
validate_python()
args = get_arguments()
config_dir = os.path.join(os.getcwd(), args.config)
ensure_config_path(config_dir)
# os x launchd functions
if args.install_osx:
install_osx()
return 0
if args.uninstall_osx:
uninstall_osx()
return 0
if args.restart_osx:
uninstall_osx()
# A small delay is needed on some systems to let the unload finish.
time.sleep(0.5)
install_osx()
return 0
# daemon functions
if args.pid_file:
check_pid(args.pid_file)
if args.daemon:
daemonize()
if args.pid_file:
write_pid(args.pid_file)
# Run hass in debug mode if requested
if args.debug:
sys.stderr.write('Running in debug mode. '
'Home Assistant will not be able to restart.\n')
exit_code = setup_and_run_hass(config_dir, args, top_process=True)
if exit_code == RESTART_EXIT_CODE:
sys.stderr.write('Home Assistant requested a '
'restart in debug mode.\n')
return exit_code
# Run hass as child process. Restart if necessary.
keep_running = True
while keep_running:
hass_proc = Process(target=setup_and_run_hass, args=(config_dir, args))
keep_running, exit_code = run_hass_process(hass_proc)
return exit_code
if __name__ == "__main__":
main()
sys.exit(main())
+57 -48
View File
@@ -1,37 +1,31 @@
"""
homeassistant.bootstrap
~~~~~~~~~~~~~~~~~~~~~~~
Provides methods to bootstrap a home assistant instance.
"""Provides methods to bootstrap a home assistant instance."""
Each method will return a tuple (bus, statemachine).
After bootstrapping you can add your own components or
start by calling homeassistant.start_home_assistant(bus)
"""
from collections import defaultdict
import logging
import logging.handlers
import os
import shutil
import sys
from collections import defaultdict
from threading import RLock
import homeassistant.core as core
import homeassistant.util.dt as date_util
import homeassistant.util.package as pkg_util
import homeassistant.util.location as loc_util
import homeassistant.config as config_util
import homeassistant.loader as loader
import homeassistant.components as core_components
import homeassistant.components.group as group
import homeassistant.config as config_util
import homeassistant.core as core
import homeassistant.loader as loader
import homeassistant.util.dt as date_util
import homeassistant.util.location as loc_util
import homeassistant.util.package as pkg_util
from homeassistant.const import (
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
TEMP_CELCIUS, TEMP_FAHRENHEIT, __version__)
from homeassistant.helpers import event_decorators, service
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
__version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, CONF_CUSTOMIZE,
TEMP_CELCIUS, TEMP_FAHRENHEIT)
_LOGGER = logging.getLogger(__name__)
_SETUP_LOCK = RLock()
_CURRENT_SETUP = []
ATTR_COMPONENT = 'component'
@@ -78,42 +72,57 @@ def _handle_requirements(hass, component, name):
def _setup_component(hass, domain, config):
""" Setup a component for Home Assistant. """
"""Setup a component for Home Assistant."""
# pylint: disable=too-many-return-statements
if domain in hass.config.components:
return True
component = loader.get_component(domain)
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
if dep not in hass.config.components]
with _SETUP_LOCK:
# It might have been loaded while waiting for lock
if domain in hass.config.components:
return True
if missing_deps:
_LOGGER.error(
'Not initializing %s because not all dependencies loaded: %s',
domain, ", ".join(missing_deps))
return False
if not _handle_requirements(hass, component, domain):
return False
try:
if not component.setup(hass, config):
_LOGGER.error('component %s failed to initialize', domain)
if domain in _CURRENT_SETUP:
_LOGGER.error('Attempt made to setup %s during setup of %s',
domain, domain)
return False
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error during setup of component %s', domain)
return False
hass.config.components.append(component.DOMAIN)
component = loader.get_component(domain)
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
if dep not in hass.config.components]
# Assumption: if a component does not depend on groups
# it communicates with devices
if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []):
hass.pool.add_worker()
if missing_deps:
_LOGGER.error(
'Not initializing %s because not all dependencies loaded: %s',
domain, ", ".join(missing_deps))
return False
hass.bus.fire(
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN})
if not _handle_requirements(hass, component, domain):
return False
return True
_CURRENT_SETUP.append(domain)
try:
if not component.setup(hass, config):
_LOGGER.error('component %s failed to initialize', domain)
return False
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error during setup of component %s', domain)
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.DOMAIN not in getattr(component, 'DEPENDENCIES', []):
hass.pool.add_worker()
hass.bus.fire(
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN})
return True
def prepare_setup_platform(hass, config, domain, platform_name):
@@ -21,7 +21,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
verisure.DISCOVER_SENSORS: 'verisure'
verisure.DISCOVER_ALARMS: 'verisure'
}
SERVICE_TO_METHOD = {
@@ -61,6 +61,8 @@ def setup(hass, config):
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'))
@@ -9,18 +9,16 @@ https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import (
STATE_UNKNOWN,
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/Xorso/pyalarmdotcom'
'/archive/0.0.7.zip'
'#pyalarmdotcom==0.0.7']
'/archive/0.1.1.zip'
'#pyalarmdotcom==0.1.1']
DEFAULT_NAME = 'Alarm.com'
@@ -57,10 +55,12 @@ class AlarmDotCom(alarm.AlarmControlPanel):
@property
def should_poll(self):
""" No polling needed. """
return True
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
@@ -88,7 +88,6 @@ class AlarmDotCom(alarm.AlarmControlPanel):
# Open another session to alarm.com to fire off the command
_alarm = Alarmdotcom(self._username, self._password, timeout=10)
_alarm.disarm()
self.update_ha_state()
def alarm_arm_home(self, code=None):
""" Send arm home command. """
@@ -98,7 +97,6 @@ class AlarmDotCom(alarm.AlarmControlPanel):
# Open another session to alarm.com to fire off the command
_alarm = Alarmdotcom(self._username, self._password, timeout=10)
_alarm.arm_stay()
self.update_ha_state()
def alarm_arm_away(self, code=None):
""" Send arm away command. """
@@ -108,7 +106,6 @@ class AlarmDotCom(alarm.AlarmControlPanel):
# Open another session to alarm.com to fire off the command
_alarm = Alarmdotcom(self._username, self._password, timeout=10)
_alarm.arm_away()
self.update_ha_state()
def _validate_code(self, code, state):
""" Validate given code. """
@@ -1,13 +1,14 @@
"""
homeassistant.components.alarm_control_panel.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has two fake alarm control panels.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import homeassistant.components.alarm_control_panel.manual as manual
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo alarm control panels. """
"""Setup the Demo alarm control panel platform."""
add_devices([
manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10),
])
@@ -6,15 +6,15 @@ Support for manual alarms.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.manual/
"""
import logging
import datetime
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.helpers.event import track_point_in_time
import homeassistant.util.dt as dt_util
import logging
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.util.dt as dt_util
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED)
from homeassistant.helpers.event import track_point_in_time
_LOGGER = logging.getLogger(__name__)
@@ -7,11 +7,11 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.mqtt/
"""
import logging
import homeassistant.components.mqtt as mqtt
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.components.mqtt as mqtt
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
@@ -0,0 +1,106 @@
"""
homeassistant.components.alarm_control_panel.nx584
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for NX584 alarm control panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.nx584/
"""
import logging
import requests
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
REQUIREMENTS = ['pynx584==0.2']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Setup nx584. """
host = config.get('host', 'localhost:5007')
try:
add_devices([NX584Alarm(hass, host, config.get('name', 'NX584'))])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to NX584: %s', str(ex))
return False
class NX584Alarm(alarm.AlarmControlPanel):
""" NX584-based alarm panel. """
def __init__(self, hass, host, name):
from nx584 import client
self._hass = hass
self._host = host
self._name = name
self._alarm = client.Client('http://%s' % host)
# Do an initial list operation so that we will try to actually
# talk to the API and trigger a requests exception for setup_platform()
# to catch
self._alarm.list_zones()
@property
def should_poll(self):
""" Polling needed. """
return True
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def code_format(self):
""" Characters if code is defined. """
return '[0-9]{4}([0-9]{2})?'
@property
def state(self):
""" Returns the state of the device. """
try:
part = self._alarm.list_partitions()[0]
zones = self._alarm.list_zones()
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to %(host)s: %(reason)s',
dict(host=self._host, reason=ex))
return STATE_UNKNOWN
except IndexError:
_LOGGER.error('nx584 reports no partitions')
return STATE_UNKNOWN
bypassed = False
for zone in zones:
if zone['bypassed']:
_LOGGER.debug('Zone %(zone)s is bypassed, '
'assuming HOME',
dict(zone=zone['number']))
bypassed = True
break
if not part['armed']:
return STATE_ALARM_DISARMED
elif bypassed:
return STATE_ALARM_ARMED_HOME
else:
return STATE_ALARM_ARMED_AWAY
def alarm_disarm(self, code=None):
""" Send disarm command. """
self._alarm.disarm(code)
def alarm_arm_home(self, code=None):
""" Send arm home command. """
self._alarm.arm('home')
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()
@@ -8,12 +8,12 @@ https://home-assistant.io/components/verisure/
"""
import logging
import homeassistant.components.verisure as verisure
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.verisure import HUB as hub
from homeassistant.const import (
STATE_UNKNOWN,
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
@@ -21,18 +21,13 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
alarms = []
alarms.extend([
VerisureAlarm(value)
for value in verisure.ALARM_STATUS.values()
if verisure.SHOW_ALARM
])
if int(hub.config.get('alarm', '1')):
hub.update_alarms()
alarms.extend([
VerisureAlarm(value.id)
for value in hub.alarm_status.values()
])
add_devices(alarms)
@@ -40,9 +35,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureAlarm(alarm.AlarmControlPanel):
""" Represents a Verisure alarm status. """
def __init__(self, alarm_status):
self._id = alarm_status.id
def __init__(self, device_id):
self._id = device_id
self._state = STATE_UNKNOWN
self._digits = int(hub.config.get('code_digits', '4'))
@property
def name(self):
@@ -56,41 +52,41 @@ class VerisureAlarm(alarm.AlarmControlPanel):
@property
def code_format(self):
""" Four digit code required. """
return '^\\d{%s}$' % verisure.CODE_DIGITS
""" code format as regex """
return '^\\d{%s}$' % self._digits
def update(self):
""" Update alarm status """
verisure.update_alarm()
hub.update_alarms()
if verisure.ALARM_STATUS[self._id].status == 'unarmed':
if hub.alarm_status[self._id].status == 'unarmed':
self._state = STATE_ALARM_DISARMED
elif verisure.ALARM_STATUS[self._id].status == 'armedhome':
elif hub.alarm_status[self._id].status == 'armedhome':
self._state = STATE_ALARM_ARMED_HOME
elif verisure.ALARM_STATUS[self._id].status == 'armed':
elif hub.alarm_status[self._id].status == 'armed':
self._state = STATE_ALARM_ARMED_AWAY
elif verisure.ALARM_STATUS[self._id].status != 'pending':
elif hub.alarm_status[self._id].status != 'pending':
_LOGGER.error(
'Unknown alarm state %s',
verisure.ALARM_STATUS[self._id].status)
hub.alarm_status[self._id].status)
def alarm_disarm(self, code=None):
""" Send disarm command. """
verisure.MY_PAGES.alarm.set(code, 'DISARMED')
hub.my_pages.alarm.set(code, 'DISARMED')
_LOGGER.info('verisure alarm disarming')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_home(self, code=None):
""" Send arm home command. """
verisure.MY_PAGES.alarm.set(code, 'ARMED_HOME')
hub.my_pages.alarm.set(code, 'ARMED_HOME')
_LOGGER.info('verisure alarm arming home')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_away(self, code=None):
""" Send arm away command. """
verisure.MY_PAGES.alarm.set(code, 'ARMED_AWAY')
hub.my_pages.alarm.set(code, 'ARMED_AWAY')
_LOGGER.info('verisure alarm arming away')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
+10 -12
View File
@@ -1,7 +1,5 @@
"""
components.alexa
~~~~~~~~~~~~~~~~
Component to offer a service end point for an Alexa skill.
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/
@@ -10,8 +8,8 @@ import enum
import logging
from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY
from homeassistant.util import template
from homeassistant.helpers.service import call_from_config
from homeassistant.helpers import template
DOMAIN = 'alexa'
DEPENDENCIES = ['http']
@@ -28,7 +26,7 @@ CONF_ACTION = 'action'
def setup(hass, config):
""" Activate Alexa component. """
"""Activate Alexa component."""
_CONFIG.update(config[DOMAIN].get(CONF_INTENTS, {}))
hass.http.register_path('POST', API_ENDPOINT, _handle_alexa, True)
@@ -37,7 +35,7 @@ def setup(hass, config):
def _handle_alexa(handler, path_match, data):
""" Handle Alexa. """
"""Handle Alexa."""
_LOGGER.debug('Received Alexa request: %s', data)
req = data.get('request')
@@ -99,19 +97,19 @@ def _handle_alexa(handler, path_match, data):
class SpeechType(enum.Enum):
""" Alexa speech types. """
"""Alexa speech types."""
plaintext = "PlainText"
ssml = "SSML"
class CardType(enum.Enum):
""" Alexa card types. """
"""Alexa card types."""
simple = "Simple"
link_account = "LinkAccount"
class AlexaResponse(object):
""" Helps generating the response for Alexa. """
"""Helps generating the response for Alexa."""
def __init__(self, hass, intent=None):
self.hass = hass
@@ -154,7 +152,7 @@ class AlexaResponse(object):
}
def add_reprompt(self, speech_type, text):
""" Add repromopt if user does not answer. """
"""Add reprompt if user does not answer."""
assert self.reprompt is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
@@ -165,7 +163,7 @@ class AlexaResponse(object):
}
def as_dict(self):
""" Returns response in an Alexa valid dict. """
"""Returns response in an Alexa valid dict."""
response = {
'shouldEndSession': self.should_end_session
}
@@ -188,5 +186,5 @@ class AlexaResponse(object):
}
def _render(self, template_string):
""" Render a response, adding data from intent if available. """
"""Render a response, adding data from intent if available."""
return template.render(self.hass, template_string, self.variables)
+84
View File
@@ -0,0 +1,84 @@
"""
homeassistant.components.apcupsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets up and provides access to the status output of APCUPSd via its Network
Information Server (NIS).
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/apcupsd/
"""
import logging
from datetime import timedelta
from homeassistant.util import Throttle
DOMAIN = "apcupsd"
REQUIREMENTS = ("apcaccess==0.0.4",)
CONF_HOST = "host"
CONF_PORT = "port"
CONF_TYPE = "type"
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 3551
KEY_STATUS = "STATUS"
VALUE_ONLINE = "ONLINE"
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
DATA = None
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Use config values to set up a function enabling status retrieval. """
global DATA
host = config[DOMAIN].get(CONF_HOST, DEFAULT_HOST)
port = config[DOMAIN].get(CONF_PORT, DEFAULT_PORT)
DATA = APCUPSdData(host, port)
# It doesn't really matter why we're not able to get the status, just that
# we can't.
# pylint: disable=broad-except
try:
DATA.update(no_throttle=True)
except Exception:
_LOGGER.exception("Failure while testing APCUPSd status retrieval.")
return False
return True
class APCUPSdData(object):
"""
Stores the data retrieved from APCUPSd for each entity to use, acts as the
single point responsible for fetching updates from the server.
"""
def __init__(self, host, port):
from apcaccess import status
self._host = host
self._port = port
self._status = None
self._get = status.get
self._parse = status.parse
@property
def status(self):
""" Get latest update if throttle allows. Return status. """
self.update()
return self._status
def _get_status(self):
""" Get the status from APCUPSd and parse it into a dict. """
return self._parse(self._get(host=self._host, port=self._port))
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs):
"""
Fetch the latest status from APCUPSd and store it in self._status.
"""
self._status = self._get_status()
+58 -61
View File
@@ -1,31 +1,27 @@
"""
homeassistant.components.api
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides a Rest API for Home Assistant.
Rest API for Home Assistant.
For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/
"""
import re
import logging
import threading
import json
import logging
import re
import threading
import homeassistant.core as ha
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import TrackStates
import homeassistant.remote as rem
from homeassistant.util import template
from homeassistant.bootstrap import ERROR_LOG_FILENAME
from homeassistant.const import (
URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES, URL_API_STREAM,
URL_API_EVENT_FORWARD, URL_API_STATES_ENTITY, URL_API_COMPONENTS,
URL_API_CONFIG, URL_API_BOOTSTRAP, URL_API_ERROR_LOG, URL_API_LOG_OUT,
URL_API_TEMPLATE, EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL,
HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND,
HTTP_UNPROCESSABLE_ENTITY, HTTP_HEADER_CONTENT_TYPE,
CONTENT_TYPE_TEXT_PLAIN)
CONTENT_TYPE_TEXT_PLAIN, EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED,
HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_HEADER_CONTENT_TYPE, HTTP_NOT_FOUND,
HTTP_OK, HTTP_UNPROCESSABLE_ENTITY, MATCH_ALL, URL_API, URL_API_COMPONENTS,
URL_API_CONFIG, URL_API_ERROR_LOG, URL_API_EVENT_FORWARD, URL_API_EVENTS,
URL_API_LOG_OUT, URL_API_SERVICES, URL_API_STATES, URL_API_STATES_ENTITY,
URL_API_STREAM, URL_API_TEMPLATE)
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import TrackStates
from homeassistant.helpers import template
DOMAIN = 'api'
DEPENDENCIES = ['http']
@@ -37,7 +33,7 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Register the API with the HTTP interface. """
"""Register the API with the HTTP interface."""
# /api - for validation purposes
hass.http.register_path('GET', URL_API, _handle_get_api)
@@ -48,10 +44,6 @@ def setup(hass, config):
# /api/config
hass.http.register_path('GET', URL_API_CONFIG, _handle_get_api_config)
# /api/bootstrap
hass.http.register_path(
'GET', URL_API_BOOTSTRAP, _handle_get_api_bootstrap)
# /states
hass.http.register_path('GET', URL_API_STATES, _handle_get_api_states)
hass.http.register_path(
@@ -63,6 +55,9 @@ def setup(hass, config):
hass.http.register_path(
'PUT', re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
_handle_post_state_entity)
hass.http.register_path(
'DELETE', re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
_handle_delete_state_entity)
# /events
hass.http.register_path('GET', URL_API_EVENTS, _handle_get_api_events)
@@ -101,12 +96,12 @@ def setup(hass, config):
def _handle_get_api(handler, path_match, data):
""" Renders the debug interface. """
"""Renders the debug interface."""
handler.write_json_message("API running.")
def _handle_get_api_stream(handler, path_match, data):
""" Provide a streaming interface for the event bus. """
"""Provide a streaming interface for the event bus."""
gracefully_closed = False
hass = handler.server.hass
wfile = handler.wfile
@@ -119,7 +114,7 @@ def _handle_get_api_stream(handler, path_match, data):
restrict = restrict.split(',')
def write_message(payload):
""" Writes a message to the output. """
"""Writes a message to the output."""
with write_lock:
msg = "data: {}\n\n".format(payload)
@@ -132,7 +127,7 @@ def _handle_get_api_stream(handler, path_match, data):
block.set()
def forward_events(event):
""" Forwards events to the open request. """
"""Forwards events to the open request."""
nonlocal gracefully_closed
if block.is_set() or event.event_type == EVENT_TIME_CHANGED:
@@ -176,29 +171,17 @@ def _handle_get_api_stream(handler, path_match, data):
def _handle_get_api_config(handler, path_match, data):
""" Returns the Home Assistant config. """
"""Returns the Home Assistant configuration."""
handler.write_json(handler.server.hass.config.as_dict())
def _handle_get_api_bootstrap(handler, path_match, data):
""" Returns all data needed to bootstrap Home Assistant. """
hass = handler.server.hass
handler.write_json({
'config': hass.config.as_dict(),
'states': hass.states.all(),
'events': _events_json(hass),
'services': _services_json(hass),
})
def _handle_get_api_states(handler, path_match, data):
""" Returns a dict containing all entity ids and their state. """
"""Returns a dict containing all entity ids and their state."""
handler.write_json(handler.server.hass.states.all())
def _handle_get_api_states_entity(handler, path_match, data):
""" Returns the state of a specific entity. """
"""Returns the state of a specific entity."""
entity_id = path_match.group('entity_id')
state = handler.server.hass.states.get(entity_id)
@@ -210,7 +193,7 @@ def _handle_get_api_states_entity(handler, path_match, data):
def _handle_post_state_entity(handler, path_match, data):
""" Handles updating the state of an entity.
"""Handles updating the state of an entity.
This handles the following paths:
/api/states/<entity_id>
@@ -240,13 +223,29 @@ def _handle_post_state_entity(handler, path_match, data):
location=URL_API_STATES_ENTITY.format(entity_id))
def _handle_delete_state_entity(handler, path_match, data):
"""Handle request to delete an entity from state machine.
This handles the following paths:
/api/states/<entity_id>
"""
entity_id = path_match.group('entity_id')
if handler.server.hass.states.remove(entity_id):
handler.write_json_message(
"Entity not found", HTTP_NOT_FOUND)
else:
handler.write_json_message(
"Entity removed", HTTP_OK)
def _handle_get_api_events(handler, path_match, data):
""" Handles getting overview of event listeners. """
handler.write_json(_events_json(handler.server.hass))
"""Handles getting overview of event listeners."""
handler.write_json(events_json(handler.server.hass))
def _handle_api_post_events_event(handler, path_match, event_data):
""" Handles firing of an event.
"""Handles firing of an event.
This handles the following paths:
/api/events/<event_type>
@@ -258,6 +257,7 @@ def _handle_api_post_events_event(handler, path_match, event_data):
if event_data is not None and not isinstance(event_data, dict):
handler.write_json_message(
"event_data should be an object", HTTP_UNPROCESSABLE_ENTITY)
return
event_origin = ha.EventOrigin.remote
@@ -276,13 +276,13 @@ def _handle_api_post_events_event(handler, path_match, event_data):
def _handle_get_api_services(handler, path_match, data):
""" Handles getting overview of services. """
handler.write_json(_services_json(handler.server.hass))
"""Handles getting overview of services."""
handler.write_json(services_json(handler.server.hass))
# pylint: disable=invalid-name
def _handle_post_api_services_domain_service(handler, path_match, data):
""" Handles calling a service.
"""Handles calling a service.
This handles the following paths:
/api/services/<domain>/<service>
@@ -298,8 +298,7 @@ def _handle_post_api_services_domain_service(handler, path_match, data):
# pylint: disable=invalid-name
def _handle_post_api_event_forward(handler, path_match, data):
""" Handles adding an event forwarding target. """
"""Handles adding an event forwarding target."""
try:
host = data['host']
api_password = data['api_password']
@@ -332,8 +331,7 @@ def _handle_post_api_event_forward(handler, path_match, data):
def _handle_delete_api_event_forward(handler, path_match, data):
""" Handles deleting an event forwarding target. """
"""Handles deleting an event forwarding target."""
try:
host = data['host']
except KeyError:
@@ -356,26 +354,25 @@ def _handle_delete_api_event_forward(handler, path_match, data):
def _handle_get_api_components(handler, path_match, data):
""" Returns all the loaded components. """
"""Returns all the loaded components."""
handler.write_json(handler.server.hass.config.components)
def _handle_get_api_error_log(handler, path_match, data):
""" Returns the logged errors for this session. """
"""Returns the logged errors for this session."""
handler.write_file(handler.server.hass.config.path(ERROR_LOG_FILENAME),
False)
def _handle_post_api_log_out(handler, path_match, data):
""" Log user out. """
"""Log user out."""
handler.send_response(HTTP_OK)
handler.destroy_session()
handler.end_headers()
def _handle_post_api_template(handler, path_match, data):
""" Log user out. """
"""Log user out."""
template_string = data.get('template', '')
try:
@@ -390,13 +387,13 @@ def _handle_post_api_template(handler, path_match, data):
return
def _services_json(hass):
""" Generate services data to JSONify. """
def services_json(hass):
"""Generate services data to JSONify."""
return [{"domain": key, "services": value}
for key, value in hass.services.services.items()]
def _events_json(hass):
""" Generate event data to JSONify. """
def events_json(hass):
"""Generate event data to JSONify."""
return [{"event": key, "listener_count": value}
for key, value in hass.bus.listeners.items()]
+2 -2
View File
@@ -9,9 +9,9 @@ https://home-assistant.io/components/arduino/
"""
import logging
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import validate_config
from homeassistant.const import (EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP)
DOMAIN = "arduino"
REQUIREMENTS = ['PyMata==2.07a']
@@ -6,13 +6,12 @@ Offers numeric state listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#numeric-state-trigger
"""
from functools import partial
import logging
from functools import partial
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers.event import track_state_change
from homeassistant.util import template
from homeassistant.helpers import template
CONF_ENTITY_ID = "entity_id"
CONF_BELOW = "below"
+74 -6
View File
@@ -7,15 +7,47 @@ For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#state-trigger
"""
import logging
from datetime import timedelta
from homeassistant.helpers.event import track_state_change
from homeassistant.const import MATCH_ALL
import homeassistant.util.dt as dt_util
from homeassistant.const import (
EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL)
from homeassistant.components.automation.time import (
CONF_HOURS, CONF_MINUTES, CONF_SECONDS)
from homeassistant.helpers.event import track_state_change, track_point_in_time
CONF_ENTITY_ID = "entity_id"
CONF_FROM = "from"
CONF_TO = "to"
CONF_STATE = "state"
CONF_FOR = "for"
def get_time_config(config):
""" Helper function to extract the time specified in the config """
if CONF_FOR not in config:
return None
hours = config[CONF_FOR].get(CONF_HOURS)
minutes = config[CONF_FOR].get(CONF_MINUTES)
seconds = config[CONF_FOR].get(CONF_SECONDS)
if hours is None and minutes is None and seconds is None:
logging.getLogger(__name__).error(
"Received invalid value for '%s': %s",
config[CONF_FOR], CONF_FOR)
return None
if config.get(CONF_TO) is None and config.get(CONF_STATE) is None:
logging.getLogger(__name__).error(
"For: requires a to: value'%s': %s",
config[CONF_FOR], CONF_FOR)
return None
return timedelta(hours=(hours or 0.0),
minutes=(minutes or 0.0),
seconds=(seconds or 0.0))
def trigger(hass, config, action):
@@ -25,19 +57,47 @@ def trigger(hass, config, action):
if entity_id is None:
logging.getLogger(__name__).error(
"Missing trigger configuration key %s", CONF_ENTITY_ID)
return False
return None
from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
time_delta = get_time_config(config)
if isinstance(from_state, bool) or isinstance(to_state, bool):
logging.getLogger(__name__).error(
'Config error. Surround to/from values with quotes.')
return False
return None
if CONF_FOR in config and time_delta is None:
return None
def state_automation_listener(entity, from_s, to_s):
""" Listens for state changes and calls action. """
action()
def state_for_listener(now):
""" Fires on state changes after a delay and calls action. """
hass.bus.remove_listener(
EVENT_STATE_CHANGED, for_state_listener)
action()
def state_for_cancel_listener(entity, inner_from_s, inner_to_s):
""" Fires on state changes and cancels
for listener if state changed. """
if inner_to_s == to_s:
return
hass.bus.remove_listener(EVENT_TIME_CHANGED, for_time_listener)
hass.bus.remove_listener(
EVENT_STATE_CHANGED, for_state_listener)
if time_delta is not None:
target_tm = dt_util.utcnow() + time_delta
for_time_listener = track_point_in_time(
hass, state_for_listener, target_tm)
for_state_listener = track_state_change(
hass, entity_id, state_for_cancel_listener,
MATCH_ALL, MATCH_ALL)
else:
action()
track_state_change(
hass, entity_id, state_automation_listener, from_state, to_state)
@@ -56,10 +116,18 @@ def if_action(hass, config):
CONF_STATE)
return None
time_delta = get_time_config(config)
if CONF_FOR in config and time_delta is None:
return None
state = str(state)
def if_state():
""" Test if condition. """
return hass.states.is_state(entity_id, state)
is_state = hass.states.is_state(entity_id, state)
return (time_delta is None and is_state or
time_delta is not None and
dt_util.utcnow() - time_delta >
hass.states.get(entity_id).last_changed)
return if_state
+1 -1
View File
@@ -9,9 +9,9 @@ at https://home-assistant.io/components/automation/#sun-trigger
import logging
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.components import sun
from homeassistant.helpers.event import track_sunrise, track_sunset
import homeassistant.util.dt as dt_util
DEPENDENCIES = ['sun']
@@ -10,7 +10,7 @@ import logging
from homeassistant.const import CONF_VALUE_TEMPLATE, EVENT_STATE_CHANGED
from homeassistant.exceptions import TemplateError
from homeassistant.util import template
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
+1 -2
View File
@@ -9,10 +9,9 @@ at https://home-assistant.io/components/automation/#zone-trigger
import logging
from homeassistant.components import zone
from homeassistant.helpers.event import track_state_change
from homeassistant.const import (
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, MATCH_ALL)
from homeassistant.helpers.event import track_state_change
CONF_ENTITY_ID = "entity_id"
CONF_ZONE = "zone"
@@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with binary sensors (sensors which only know two states)
that can be monitored.
@@ -12,17 +10,43 @@ import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (STATE_ON, STATE_OFF)
from homeassistant.components import (bloomsky, mysensors, zwave, wink)
DOMAIN = 'binary_sensor'
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SENSOR_CLASSES = [
None, # Generic on/off
'opening', # Door, window, etc
'motion', # Motion sensor
'gas', # CO, CO2, etc
'smoke', # Smoke detector
'moisture', # Specifically a wetness sensor
'light', # Lightness threshold
'power', # Power, over-current, etc
'safety', # Generic on=unsafe, off=safe
'heat', # On means hot (or too hot)
'cold', # On means cold (or too cold)
'moving', # On means moving, Off means stopped
'sound', # On means sound detected, Off means no sound
'vibration', # On means vibration detected, Off means no vibration
]
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_BINARY_SENSORS: 'bloomsky',
mysensors.DISCOVER_BINARY_SENSORS: 'mysensors',
zwave.DISCOVER_BINARY_SENSORS: 'zwave',
wink.DISCOVER_BINARY_SENSORS: 'wink'
}
def setup(hass, config):
""" Track states and offer events for binary sensors. """
"""Track states and offer events for binary sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
component.setup(config)
@@ -31,19 +55,29 @@ def setup(hass, config):
# pylint: disable=no-self-use
class BinarySensorDevice(Entity):
""" Represents a binary sensor. """
"""Represent a binary sensor."""
@property
def is_on(self):
""" True if the binary sensor is on. """
"""Return True if the binary sensor is on."""
return None
@property
def state(self):
""" Returns the state of the binary sensor. """
"""Return the state of the binary sensor."""
return STATE_ON if self.is_on else STATE_OFF
@property
def friendly_state(self):
""" Returns the friendly state of the binary sensor. """
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return None
@property
def state_attributes(self):
"""Return device specific state attributes."""
attr = {}
if self.sensor_class is not None:
attr['sensor_class'] = self.sensor_class
return attr
@@ -0,0 +1,42 @@
"""
Provides a binary sensor to track online status of a UPS.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.apcupsd/
"""
from homeassistant.components import apcupsd
from homeassistant.components.binary_sensor import BinarySensorDevice
DEPENDENCIES = [apcupsd.DOMAIN]
DEFAULT_NAME = "UPS Online Status"
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Instantiate an OnlineStatus binary sensor entity."""
add_entities((OnlineStatus(config, apcupsd.DATA),))
class OnlineStatus(BinarySensorDevice):
"""Binary sensor to represent UPS online status."""
def __init__(self, config, data):
self._config = config
self._data = data
self._state = None
self.update()
@property
def name(self):
""" The name of the UPS online status sensor. """
return self._config.get("name", DEFAULT_NAME)
@property
def is_on(self):
"""True if the UPS is online, else False."""
return self._state == apcupsd.VALUE_ONLINE
def update(self):
"""
Get the status report from APCUPSd (or cache) and set this entity's
state.
"""
self._state = self._data.status[apcupsd.KEY_STATUS]
@@ -1,18 +1,16 @@
"""
homeassistant.components.binary_sensor.arest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The arest sensor will consume an exposed aREST API of a device.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.arest/
"""
from datetime import timedelta
import logging
from datetime import timedelta
import requests
from homeassistant.util import Throttle
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
@@ -24,8 +22,7 @@ CONF_PIN = 'pin'
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Get the aREST binary sensor. """
"""Get the aREST binary sensor."""
resource = config.get(CONF_RESOURCE)
pin = config.get(CONF_PIN)
@@ -56,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-instance-attributes, too-many-arguments
class ArestBinarySensor(BinarySensorDevice):
""" Implements an aREST binary sensor for a pin. """
"""Implements an aREST binary sensor for a pin."""
def __init__(self, arest, resource, name, pin):
self.arest = arest
@@ -73,23 +70,22 @@ class ArestBinarySensor(BinarySensorDevice):
@property
def name(self):
""" The name of the binary sensor. """
"""The name of the binary sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return bool(self.arest.data.get('state'))
def update(self):
""" Gets the latest data from aREST API. """
"""Gets the latest data from aREST API."""
self.arest.update()
# pylint: disable=too-few-public-methods
class ArestData(object):
""" Class for handling the data retrieval for pins. """
"""Class for handling the data retrieval for pins."""
def __init__(self, resource, pin):
self._resource = resource
self._pin = pin
@@ -97,7 +93,7 @@ class ArestData(object):
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
""" Gets the latest data from aREST device. """
"""Gets the latest data from aREST device."""
try:
response = requests.get('{}/digital/{}'.format(
self._resource, self._pin), timeout=10)
@@ -0,0 +1,74 @@
"""
Support the binary sensors of a BloomSky weather station.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.bloomsky/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.loader import get_component
DEPENDENCIES = ["bloomsky"]
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
"Rain": "moisture",
"Night": None,
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the available BloomSky weather binary sensors."""
logger = logging.getLogger(__name__)
bloomsky = get_component('bloomsky')
sensors = config.get('monitored_conditions', SENSOR_TYPES)
for device in bloomsky.BLOOMSKY.devices.values():
for variable in sensors:
if variable in SENSOR_TYPES:
add_devices([BloomSkySensor(bloomsky.BLOOMSKY,
device,
variable)])
else:
logger.error("Cannot find definition for device: %s", variable)
class BloomSkySensor(BinarySensorDevice):
""" Represents a single binary sensor in a BloomSky device. """
def __init__(self, bs, device, sensor_name):
"""Initialize a BloomSky binary sensor."""
self._bloomsky = bs
self._device_id = device["DeviceID"]
self._sensor_name = sensor_name
self._name = "{} {}".format(device["DeviceName"], sensor_name)
self._unique_id = "bloomsky_binary_sensor {}".format(self._name)
self.update()
@property
def name(self):
"""The name of the BloomSky device and this sensor."""
return self._name
@property
def unique_id(self):
"""Unique ID for this sensor."""
return self._unique_id
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return SENSOR_TYPES.get(self._sensor_name)
@property
def is_on(self):
"""If binary sensor is on."""
return self._state
def update(self):
"""Request an update from the BloomSky API."""
self._bloomsky.refresh_devices()
self._state = \
self._bloomsky.devices[self._device_id]["Data"][self._sensor_name]
@@ -1,16 +1,17 @@
"""
homeassistant.components.binary_sensor.command_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure custom shell commands to turn a value
into a logical value for a binary sensor.
Allows to configure custom shell commands to turn a value into a logical value
for a binary sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.command/
"""
import logging
from datetime import timedelta
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.command_sensor import CommandSensorData
from homeassistant.util import template
from homeassistant.components.sensor.command_line import CommandSensorData
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
@@ -24,8 +25,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Add the Command Sensor. """
"""Add the Command Sensor."""
if config.get('command') is None:
_LOGGER.error('Missing required variable: "command"')
return False
@@ -44,8 +44,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments
class CommandBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that is returning
a value of a shell commands. """
"""
Represents a binary sensor that is returning a value of a shell commands.
"""
def __init__(self, hass, data, name, payload_on,
payload_off, value_template):
self._hass = hass
@@ -59,16 +60,16 @@ class CommandBinarySensor(BinarySensorDevice):
@property
def name(self):
""" The name of the sensor. """
"""The name of the sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return self._state
def update(self):
""" Gets the latest data and updates the state. """
"""Gets the latest data and updates the state."""
self.data.update()
value = self.data.value
+17 -11
View File
@@ -1,37 +1,43 @@
"""
homeassistant.components.binary_sensor.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo binary sensors. """
"""Setup the Demo binary sensor platform."""
add_devices([
DemoBinarySensor('Basement Floor Wet', False),
DemoBinarySensor('Movement Backyard', True),
DemoBinarySensor('Basement Floor Wet', False, 'moisture'),
DemoBinarySensor('Movement Backyard', True, 'motion'),
])
class DemoBinarySensor(BinarySensorDevice):
""" A Demo binary sensor. """
def __init__(self, name, state):
"""A Demo binary sensor."""
def __init__(self, name, state, sensor_class):
self._name = name
self._state = state
self._sensor_type = sensor_class
@property
def sensor_class(self):
"""Return the class of this sensor."""
return self._sensor_type
@property
def should_poll(self):
""" No polling needed for a demo binary sensor. """
"""No polling needed for a demo binary sensor."""
return False
@property
def name(self):
""" Returns the name of the binary sensor. """
"""Return the name of the binary sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""Return true if the binary sensor is on."""
return self._state
+9 -11
View File
@@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a MQTT binary sensor.
For more details about this platform, please refer to the documentation at
@@ -8,10 +6,10 @@ https://home-assistant.io/components/binary_sensor.mqtt/
"""
import logging
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.util import template
import homeassistant.components.mqtt as mqtt
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
@@ -25,7 +23,7 @@ DEPENDENCIES = ['mqtt']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Add MQTT binary sensor. """
"""Add MQTT binary sensor."""
if config.get('state_topic') is None:
_LOGGER.error('Missing required variable: state_topic')
@@ -43,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments, too-many-instance-attributes
class MqttBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that is updated by MQTT. """
"""Represents a binary sensor that is updated by MQTT."""
def __init__(self, hass, name, state_topic, qos, payload_on, payload_off,
value_template):
self._hass = hass
@@ -55,7 +53,7 @@ class MqttBinarySensor(BinarySensorDevice):
self._qos = qos
def message_received(topic, payload, qos):
""" A new MQTT message has been received. """
"""A new MQTT message has been received."""
if value_template is not None:
payload = template.render_with_possible_json_value(
hass, value_template, payload)
@@ -70,15 +68,15 @@ class MqttBinarySensor(BinarySensorDevice):
@property
def should_poll(self):
""" No polling needed. """
"""No polling needed."""
return False
@property
def name(self):
""" The name of the binary sensor. """
"""The name of the binary sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return self._state
@@ -0,0 +1,168 @@
"""
Support for MySensors binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.mysensors/
"""
import logging
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, SENSOR_CLASSES)
from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for sensors."""
# Only act if loaded via mysensors by discovery event.
# Otherwise gateway is not setup.
if discovery_info is None:
return
mysensors = get_component('mysensors')
for gateway in mysensors.GATEWAYS.values():
# 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
set_req = gateway.const.SetReq
map_sv_types = {
pres.S_DOOR: [set_req.V_TRIPPED],
pres.S_MOTION: [set_req.V_TRIPPED],
pres.S_SMOKE: [set_req.V_TRIPPED],
}
if float(gateway.version) >= 1.5:
map_sv_types.update({
pres.S_SPRINKLER: [set_req.V_TRIPPED],
pres.S_WATER_LEAK: [set_req.V_TRIPPED],
pres.S_SOUND: [set_req.V_TRIPPED],
pres.S_VIBRATION: [set_req.V_TRIPPED],
pres.S_MOISTURE: [set_req.V_TRIPPED],
})
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
map_sv_types, devices, add_devices, MySensorsBinarySensor))
class MySensorsBinarySensor(BinarySensorDevice):
"""Represent the value of a MySensors child node."""
# pylint: disable=too-many-arguments,too-many-instance-attributes
def __init__(
self, gateway, node_id, child_id, name, value_type, child_type):
"""
Setup class attributes on instantiation.
Args:
gateway (GatewayWrapper): Gateway object.
node_id (str): Id of node.
child_id (str): Id of child.
name (str): Entity name.
value_type (str): Value type of child. Value is entity state.
child_type (str): Child type of child.
Attributes:
gateway (GatewayWrapper): Gateway object.
node_id (str): Id of node.
child_id (str): Id of child.
_name (str): Entity name.
value_type (str): Value type of child. Value is entity state.
child_type (str): Child type of child.
battery_level (int): Node battery level.
_values (dict): Child values. Non state values set as state attributes.
mysensors (module): Mysensors main component module.
"""
self.gateway = gateway
self.node_id = node_id
self.child_id = child_id
self._name = name
self.value_type = value_type
self.child_type = child_type
self.battery_level = 0
self._values = {}
self.mysensors = get_component('mysensors')
@property
def should_poll(self):
"""MySensor gateway pushes its state to HA."""
return False
@property
def name(self):
"""The name of this entity."""
return self._name
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,
}
set_req = self.gateway.const.SetReq
for value_type, value in self._values.items():
if value_type != self.value_type:
try:
attr[set_req(value_type).name] = value
except ValueError:
_LOGGER.error('value_type %s is not valid for mysensors '
'version %s', value_type,
self.gateway.version)
return attr
@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.value_type in self._values:
return self._values[self.value_type] == STATE_ON
return False
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
pres = self.gateway.const.Presentation
class_map = {
pres.S_DOOR: 'opening',
pres.S_MOTION: 'motion',
pres.S_SMOKE: 'smoke',
}
if float(self.gateway.version) >= 1.5:
class_map.update({
pres.S_SPRINKLER: 'sprinkler',
pres.S_WATER_LEAK: 'leak',
pres.S_SOUND: 'sound',
pres.S_VIBRATION: 'vibration',
pres.S_MOISTURE: 'moisture',
})
if class_map.get(self.child_type) in SENSOR_CLASSES:
return class_map.get(self.child_type)
@property
def available(self):
"""Return True if entity is available."""
return self.value_type in self._values
def update(self):
"""Update the controller with the latest values from a sensor."""
node = self.gateway.sensors[self.node_id]
child = node.children[self.child_id]
for value_type, value in child.values.items():
_LOGGER.debug(
"%s: value_type %s, value = %s", self._name, value_type, value)
if value_type == self.gateway.const.SetReq.V_TRIPPED:
self._values[value_type] = STATE_ON if int(
value) == 1 else STATE_OFF
else:
self._values[value_type] = value
self.battery_level = node.battery_level
@@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.nest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Nest Thermostat Binary Sensors.
For more details about this platform, please refer to the documentation at
@@ -8,15 +6,16 @@ https://home-assistant.io/components/binary_sensor.nest/
"""
import logging
import socket
import homeassistant.components.nest as nest
from homeassistant.components.sensor.nest import NestSensor
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.nest import NestSensor
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',
@@ -26,7 +25,7 @@ BINARY_TYPES = ['fan',
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Setup Nest binary sensors. """
"""Setup Nest binary sensors."""
logger = logging.getLogger(__name__)
try:
@@ -47,9 +46,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class NestBinarySensor(NestSensor, BinarySensorDevice):
""" Represents a Nest binary sensor. """
"""Represents a Nest binary sensor."""
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return bool(getattr(self.device, self.variable))
@@ -0,0 +1,131 @@
"""
Support for exposing nx584 elements as sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.nx584/
"""
import logging
import threading
import time
import requests
from homeassistant.components.binary_sensor import (
SENSOR_CLASSES, BinarySensorDevice)
REQUIREMENTS = ['pynx584==0.2']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup nx584 sensors."""
from nx584 import client as nx584_client
host = config.get('host', 'localhost:5007')
exclude = config.get('exclude_zones', [])
zone_types = config.get('zone_types', {})
if not all(isinstance(zone, int) for zone in exclude):
_LOGGER.error('Invalid excluded zone specified (use zone number)')
return False
if not all(isinstance(zone, int) and ztype in SENSOR_CLASSES
for zone, ztype in zone_types.items()):
_LOGGER.error('Invalid zone_types entry')
return False
try:
client = nx584_client.Client('http://%s' % host)
zones = client.list_zones()
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to NX584: %s', str(ex))
return False
version = [int(v) for v in client.get_version().split('.')]
if version < [1, 1]:
_LOGGER.error('NX584 is too old to use for sensors (>=0.2 required)')
return False
zone_sensors = {
zone['number']: NX584ZoneSensor(
zone,
zone_types.get(zone['number'], 'opening'))
for zone in zones
if zone['number'] not in exclude}
if zone_sensors:
add_devices(zone_sensors.values())
watcher = NX584Watcher(client, zone_sensors)
watcher.start()
else:
_LOGGER.warning('No zones found on NX584')
return True
class NX584ZoneSensor(BinarySensorDevice):
"""Represents a NX584 zone as a sensor."""
def __init__(self, zone, zone_type):
self._zone = zone
self._zone_type = zone_type
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return self._zone_type
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Name of the binary sensor."""
return self._zone['name']
@property
def is_on(self):
"""Return true if the binary sensor is on."""
# True means "faulted" or "open" or "abnormal state"
return self._zone['state']
class NX584Watcher(threading.Thread):
"""Event listener thread to process NX584 events."""
def __init__(self, client, zone_sensors):
super(NX584Watcher, self).__init__()
self.daemon = True
self._client = client
self._zone_sensors = zone_sensors
def _process_zone_event(self, event):
zone = event['zone']
zone_sensor = self._zone_sensors.get(zone)
# pylint: disable=protected-access
if not zone_sensor:
return
zone_sensor._zone['state'] = event['zone_state']
zone_sensor.update_ha_state()
def _process_events(self, events):
for event in events:
if event.get('type') == 'zone_status':
self._process_zone_event(event)
def _run(self):
# Throw away any existing events so we don't replay history
self._client.get_events()
while True:
events = self._client.get_events()
if events:
self._process_events(events)
def run(self):
while True:
try:
self._run()
except requests.exceptions.ConnectionError:
_LOGGER.error('Failed to reach NX584 server')
time.sleep(10)
@@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.rest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rest binary sensor will consume responses sent by an exposed REST API.
For more details about this platform, please refer to the documentation at
@@ -8,10 +6,10 @@ https://home-assistant.io/components/binary_sensor.rest/
"""
import logging
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.util import template
from homeassistant.components.sensor.rest import RestData
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.rest import RestData
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
@@ -41,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments
class RestBinarySensor(BinarySensorDevice):
"""REST binary sensor."""
"""A REST binary sensor."""
def __init__(self, hass, rest, name, value_template):
"""Initialize a REST binary sensor."""
@@ -59,7 +57,7 @@ class RestBinarySensor(BinarySensorDevice):
@property
def is_on(self):
"""Return if the binary sensor is on."""
"""Return true if the binary sensor is on."""
if self.rest.data is None:
return False
@@ -1,16 +1,14 @@
"""
homeassistant.components.binary_sensor.rpi_gpio
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a binary sensor using RPi GPIO.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.rpi_gpio/
"""
import logging
import homeassistant.components.rpi_gpio as rpi_gpio
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import (DEVICE_DEFAULT_NAME)
from homeassistant.const import DEVICE_DEFAULT_NAME
DEFAULT_PULL_MODE = "UP"
DEFAULT_BOUNCETIME = 50
@@ -22,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Raspberry PI GPIO devices. """
"""Sets up the Raspberry PI GPIO devices."""
pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE)
bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME)
@@ -38,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments, too-many-instance-attributes
class RPiGPIOBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that uses Raspberry Pi GPIO. """
"""Represents a binary sensor that uses Raspberry Pi GPIO."""
def __init__(self, name, port, pull_mode, bouncetime, invert_logic):
# pylint: disable=no-member
@@ -52,22 +50,22 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
self._state = rpi_gpio.read_input(self._port)
def read_gpio(port):
""" Reads state from GPIO. """
"""Reads state from GPIO."""
self._state = rpi_gpio.read_input(self._port)
self.update_ha_state()
rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime)
@property
def should_poll(self):
""" No polling needed. """
"""No polling needed."""
return False
@property
def name(self):
""" The name of the sensor. """
"""The name of the sensor."""
return self._name
@property
def is_on(self):
""" Returns the state of the entity. """
"""Returns the state of the entity."""
return self._state != self._invert_logic
@@ -0,0 +1,31 @@
"""
Provides a binary sensor which gets its values from a TCP socket.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tcp/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.tcp import Sensor, DOMAIN, CONF_VALUE_ON
DEPENDENCIES = [DOMAIN]
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Create the binary sensor."""
if not BinarySensor.validate_config(config):
return False
add_entities((BinarySensor(hass, config),))
class BinarySensor(BinarySensorDevice, Sensor):
"""A binary sensor which is on when its state == CONF_VALUE_ON."""
required = (CONF_VALUE_ON,)
@property
def is_on(self):
"""True if the binary sensor is on."""
return self._state == self._config[CONF_VALUE_ON]
@@ -0,0 +1,122 @@
"""
homeassistant.components.binary_sensor.template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for exposing a templated binary_sensor
"""
import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice,
DOMAIN,
SENSOR_CLASSES)
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE
from homeassistant.core import EVENT_STATE_CHANGED
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers import template
from homeassistant.util import slugify
ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_SENSORS = 'sensors'
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup template binary sensors."""
sensors = []
if config.get(CONF_SENSORS) is None:
_LOGGER.error('Missing configuration data for binary_sensor platform')
return False
for device, device_config in config[CONF_SENSORS].items():
if device != slugify(device):
_LOGGER.error('Found invalid key for binary_sensor.template: %s. '
'Use %s instead', device, slugify(device))
continue
if not isinstance(device_config, dict):
_LOGGER.error('Missing configuration data for binary_sensor %s',
device)
continue
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
sensor_class = device_config.get('sensor_class')
value_template = device_config.get(CONF_VALUE_TEMPLATE)
if sensor_class not in SENSOR_CLASSES:
_LOGGER.error('Sensor class is not valid')
continue
if value_template is None:
_LOGGER.error(
'Missing %s for sensor %s', CONF_VALUE_TEMPLATE, device)
continue
sensors.append(
BinarySensorTemplate(
hass,
device,
friendly_name,
sensor_class,
value_template)
)
if not sensors:
_LOGGER.error('No sensors added')
return False
add_devices(sensors)
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):
self._hass = hass
self._device = device
self._name = friendly_name
self._sensor_class = sensor_class
self._template = value_template
self._state = None
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, device,
hass=hass)
_LOGGER.info('Started template sensor %s', device)
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
def _event_listener(self, event):
self.update_ha_state(True)
@property
def should_poll(self):
return False
@property
def sensor_class(self):
return self._sensor_class
@property
def name(self):
return self._name
@property
def is_on(self):
return self._state
def update(self):
try:
value = template.render(self._hass, self._template)
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)
return
_LOGGER.error(ex)
value = 'false'
self._state = value.lower() == 'true'
@@ -0,0 +1,81 @@
"""
Support for Wink sensors.
For more details about this platform, please refer to the documentation at
at https://home-assistant.io/components/sensor.wink/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['python-wink==0.6.2']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
"opened": "opening",
"brightness": "light",
"vibration": "vibration",
"loudness": "sound"
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Sets up the Wink platform."""
import pywink
if discovery_info is None:
token = config.get(CONF_ACCESS_TOKEN)
if token is None:
logging.getLogger(__name__).error(
"Missing wink access_token. "
"Get one at https://winkbearertoken.appspot.com/")
return
pywink.set_bearer_token(token)
for sensor in pywink.get_sensors():
if sensor.capability() in SENSOR_TYPES:
add_devices([WinkBinarySensorDevice(sensor)])
class WinkBinarySensorDevice(BinarySensorDevice, Entity):
"""Represents a Wink sensor."""
def __init__(self, wink):
self.wink = wink
self._unit_of_measurement = self.wink.UNIT
self.capability = self.wink.capability()
@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.capability == "loudness":
return self.wink.loudness_boolean()
elif self.capability == "vibration":
return self.wink.vibration_boolean()
elif self.capability == "brightness":
return self.wink.brightness_boolean()
else:
return self.wink.state()
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return SENSOR_TYPES.get(self.capability)
@property
def unique_id(self):
""" Returns the id of this wink sensor """
return "{}.{}".format(self.__class__, self.wink.device_id())
@property
def name(self):
""" Returns the name of the sensor if any. """
return self.wink.name()
def update(self):
""" Update state of the sensor. """
self.wink.update_state()
@@ -1,21 +1,18 @@
"""
homeassistant.components.binary_sensor.zigbee
Contains functionality to use a ZigBee device as a binary sensor.
"""
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.zigbee/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.zigbee import (
ZigBeeDigitalIn, ZigBeeDigitalInConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
"""Create and add an entity based on the configuration."""
add_entities([
ZigBeeBinarySensor(hass, ZigBeeDigitalInConfig(config))
])
@@ -0,0 +1,131 @@
"""
Interfaces with Z-Wave sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/binary_sensor.zwave/
"""
import logging
import datetime
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_time
from homeassistant.components.zwave import (
ATTR_NODE_ID, ATTR_VALUE_ID,
COMMAND_CLASS_SENSOR_BINARY, NETWORK,
ZWaveDeviceEntity, get_config_value)
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDevice)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
PHILIO = 0x013c
PHILIO_SLIM_SENSOR = 0x0002
PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0)
WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event'
DEVICE_MAPPINGS = {
PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Z-Wave platform for sensors."""
if discovery_info is None or NETWORK is None:
return
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
specific_sensor_key = (int(value.node.manufacturer_id, 16),
int(value.node.product_id, 16),
value.index)
value.set_change_verified(False)
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 = (get_config_value(value.node, 9) or 4)
add_devices([
ZWaveTriggerSensor(value, "motion",
hass, re_arm_multiplier * 8)
])
elif value.command_class == COMMAND_CLASS_SENSOR_BINARY:
add_devices([ZWaveBinarySensor(value, None)])
class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
"""Represents a binary sensor within Z-Wave."""
def __init__(self, value, sensor_class):
self._sensor_type = sensor_class
# pylint: disable=import-error
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
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."""
return self._value.data
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return self._sensor_type
@property
def should_poll(self):
"""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:
self.update_ha_state()
class ZWaveTriggerSensor(ZWaveBinarySensor):
"""
Represents a stateless sensor which triggers events just 'On'
within Z-Wave.
"""
def __init__(self, sensor_value, sensor_class, hass, re_arm_sec=60):
super(ZWaveTriggerSensor, self).__init__(sensor_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:
track_point_in_time(
self._hass, self.update_ha_state,
self.invalidate_after)
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id:
self.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.invalidate_after)
@property
def is_on(self):
"""Return True if movement has happened within the rearm time."""
return self._value.data and \
(self.invalidate_after is None or
self.invalidate_after > dt_util.utcnow())
+90
View File
@@ -0,0 +1,90 @@
"""
homeassistant.components.bloomsky
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for BloomSky weather station.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/bloomsky/
"""
import logging
from datetime import timedelta
import requests
from homeassistant.components import discovery
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
DOMAIN = "bloomsky"
BLOOMSKY = None
_LOGGER = logging.getLogger(__name__)
# The BloomSky only updates every 5-8 minutes as per the API spec so there's
# no point in polling the API more frequently
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=300)
DISCOVER_SENSORS = 'bloomsky.sensors'
DISCOVER_BINARY_SENSORS = 'bloomsky.binary_sensor'
DISCOVER_CAMERAS = 'bloomsky.camera'
# pylint: disable=unused-argument,too-few-public-methods
def setup(hass, config):
""" Setup BloomSky component. """
if not validate_config(
config,
{DOMAIN: [CONF_API_KEY]},
_LOGGER):
return False
api_key = config[DOMAIN][CONF_API_KEY]
global BLOOMSKY
try:
BLOOMSKY = BloomSky(api_key)
except RuntimeError:
return False
for component, discovery_service in (
('camera', DISCOVER_CAMERAS), ('sensor', DISCOVER_SENSORS),
('binary_sensor', DISCOVER_BINARY_SENSORS)):
discovery.discover(hass, discovery_service, component=component,
hass_config=config)
return True
class BloomSky(object):
""" Handle all communication with the BloomSky API. """
# API documentation at http://weatherlution.com/bloomsky-api/
API_URL = "https://api.bloomsky.com/api/skydata"
def __init__(self, api_key):
self._api_key = api_key
self.devices = {}
_LOGGER.debug("Initial bloomsky device load...")
self.refresh_devices()
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def refresh_devices(self):
"""
Uses the API to retreive a list of devices associated with an
account along with all the sensors on the device.
"""
_LOGGER.debug("Fetching bloomsky update")
response = requests.get(self.API_URL,
headers={"Authorization": self._api_key},
timeout=10)
if response.status_code == 401:
raise RuntimeError("Invalid API_KEY")
elif response.status_code != 200:
_LOGGER.error("Invalid HTTP response: %s", response.status_code)
return
# create dictionary keyed off of the device unique id
self.devices.update({
device["DeviceID"]: device for device in response.json()
})
+4 -7
View File
@@ -1,21 +1,18 @@
"""
homeassistant.components.browser
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to launch a webbrowser on the host machine.
Provides functionality to launch a web browser on the host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/browser/
"""
DOMAIN = "browser"
SERVICE_BROWSE_URL = "browse_url"
def setup(hass, config):
""" Listen for browse_url events and open
the url in the default webbrowser. """
"""
Listen for browse_url events and open the url in the default web browser.
"""
import webbrowser
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
+59 -66
View File
@@ -1,8 +1,6 @@
# pylint: disable=too-many-lines
"""
homeassistant.components.camera
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with various cameras.
Component to interface with cameras.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera/
@@ -15,8 +13,8 @@ import requests
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import bloomsky
from homeassistant.const import (
ATTR_ENTITY_PICTURE,
HTTP_NOT_FOUND,
ATTR_ENTITY_ID,
)
@@ -24,34 +22,19 @@ from homeassistant.const import (
DOMAIN = 'camera'
DEPENDENCIES = ['http']
GROUP_NAME_ALL_CAMERAS = 'all_cameras'
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SWITCH_ACTION_RECORD = 'record'
SWITCH_ACTION_SNAPSHOT = 'snapshot'
SERVICE_CAMERA = 'camera_service'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_CAMERAS: 'bloomsky',
}
STATE_RECORDING = 'recording'
DEFAULT_RECORDING_SECONDS = 30
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {}
FILE_DATETIME_FORMAT = '%Y-%m-%d_%H-%M-%S-%f'
DIR_DATETIME_FORMAT = '%Y-%m-%d_%H-%M-%S'
REC_DIR_PREFIX = 'recording-'
REC_IMG_PREFIX = 'recording_image-'
STATE_STREAMING = 'streaming'
STATE_IDLE = 'idle'
CAMERA_PROXY_URL = '/api/camera_proxy_stream/{0}'
CAMERA_STILL_URL = '/api/camera_proxy/{0}'
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?time={1}'
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}'
MULTIPART_BOUNDARY = '--jpegboundary'
MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n'
@@ -59,8 +42,7 @@ MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n'
# pylint: disable=too-many-branches
def setup(hass, config):
""" Track states and offer events for cameras. """
"""Initialize camera component."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
@@ -79,7 +61,7 @@ def setup(hass, config):
# pylint: disable=unused-argument
def _proxy_camera_image(handler, path_match, data):
""" Proxies the camera image via the HA server. """
"""Serve the camera image via the HA server."""
entity_id = path_match.group(ATTR_ENTITY_ID)
camera = component.entities.get(entity_id)
@@ -105,7 +87,8 @@ def setup(hass, config):
# pylint: disable=unused-argument
def _proxy_camera_mjpeg_stream(handler, path_match, data):
"""
Proxies the camera image as an mjpeg stream via the HA server.
Proxy the camera image as an mjpeg stream via the HA server.
This function takes still images from the IP camera and turns them
into an MJPEG stream. This means that HA can return a live video
stream even with only a still image URL available.
@@ -121,33 +104,7 @@ def setup(hass, config):
try:
camera.is_streaming = True
camera.update_ha_state()
handler.request.sendall(bytes('HTTP/1.1 200 OK\r\n', 'utf-8'))
handler.request.sendall(bytes(
'Content-type: multipart/x-mixed-replace; \
boundary=--jpgboundary\r\n\r\n', 'utf-8'))
handler.request.sendall(bytes('--jpgboundary\r\n', 'utf-8'))
# MJPEG_START_HEADER.format()
while True:
img_bytes = camera.camera_image()
if img_bytes is None:
continue
headers_str = '\r\n'.join((
'Content-length: {}'.format(len(img_bytes)),
'Content-type: image/jpeg',
)) + '\r\n\r\n'
handler.request.sendall(
bytes(headers_str, 'utf-8') +
img_bytes +
bytes('\r\n', 'utf-8'))
handler.request.sendall(
bytes('--jpgboundary\r\n', 'utf-8'))
time.sleep(0.5)
camera.mjpeg_stream(handler)
except (requests.RequestException, IOError):
camera.is_streaming = False
@@ -163,36 +120,75 @@ def setup(hass, config):
class Camera(Entity):
""" The base class for camera components. """
"""The base class for camera entities."""
def __init__(self):
"""Initialize a camera."""
self.is_streaming = False
@property
def should_poll(self):
"""No need to poll cameras."""
return False
@property
def entity_picture(self):
"""Return a link to the camera feed as entity picture."""
return ENTITY_IMAGE_URL.format(self.entity_id)
@property
# pylint: disable=no-self-use
def is_recording(self):
""" Returns true if the device is recording. """
"""Return true if the device is recording."""
return False
@property
# pylint: disable=no-self-use
def brand(self):
""" Should return a string of the camera brand. """
"""Camera brand."""
return None
@property
# pylint: disable=no-self-use
def model(self):
""" Returns string of camera model. """
"""Camera model."""
return None
def camera_image(self):
""" Return bytes of camera image. """
"""Return bytes of camera image."""
raise NotImplementedError()
def mjpeg_stream(self, handler):
"""Generate an HTTP MJPEG stream from camera images."""
handler.request.sendall(bytes('HTTP/1.1 200 OK\r\n', 'utf-8'))
handler.request.sendall(bytes(
'Content-type: multipart/x-mixed-replace; \
boundary=--jpgboundary\r\n\r\n', 'utf-8'))
handler.request.sendall(bytes('--jpgboundary\r\n', 'utf-8'))
# MJPEG_START_HEADER.format()
while True:
img_bytes = self.camera_image()
if img_bytes is None:
continue
headers_str = '\r\n'.join((
'Content-length: {}'.format(len(img_bytes)),
'Content-type: image/jpeg',
)) + '\r\n\r\n'
handler.request.sendall(
bytes(headers_str, 'utf-8') +
img_bytes +
bytes('\r\n', 'utf-8'))
handler.request.sendall(
bytes('--jpgboundary\r\n', 'utf-8'))
time.sleep(0.5)
@property
def state(self):
""" Returns the state of the entity. """
"""Camera state."""
if self.is_recording:
return STATE_RECORDING
elif self.is_streaming:
@@ -202,11 +198,8 @@ class Camera(Entity):
@property
def state_attributes(self):
""" Returns optional state attributes. """
attr = {
ATTR_ENTITY_PICTURE: ENTITY_IMAGE_URL.format(
self.entity_id, time.time()),
}
"""Camera state attributes."""
attr = {}
if self.model:
attr['model_name'] = self.model
@@ -0,0 +1,63 @@
"""
homeassistant.components.camera.bloomsky
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for a camera of a BloomSky weather station.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera.bloomsky/
"""
import logging
import requests
from homeassistant.components.camera import Camera
from homeassistant.loader import get_component
DEPENDENCIES = ["bloomsky"]
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" set up access to BloomSky cameras """
bloomsky = get_component('bloomsky')
for device in bloomsky.BLOOMSKY.devices.values():
add_devices_callback([BloomSkyCamera(bloomsky.BLOOMSKY, device)])
class BloomSkyCamera(Camera):
""" Represents the images published from the BloomSky's camera. """
def __init__(self, bs, device):
""" set up for access to the BloomSky camera images """
super(BloomSkyCamera, self).__init__()
self._name = device["DeviceName"]
self._id = device["DeviceID"]
self._bloomsky = bs
self._url = ""
self._last_url = ""
# _last_image will store images as they are downloaded so that the
# frequent updates in home-assistant don't keep poking the server
# to download the same image over and over
self._last_image = ""
self._logger = logging.getLogger(__name__)
def camera_image(self):
""" Update the camera's image if it has changed. """
try:
self._url = self._bloomsky.devices[self._id]["Data"]["ImageURL"]
self._bloomsky.refresh_devices()
# if the url hasn't changed then the image hasn't changed
if self._url != self._last_url:
response = requests.get(self._url, timeout=10)
self._last_url = self._url
self._last_image = response.content
except requests.exceptions.RequestException as error:
self._logger.error("Error getting bloomsky image: %s", error)
return None
return self._last_image
@property
def name(self):
""" The name of this BloomSky device. """
return self._name
+10 -8
View File
@@ -1,29 +1,31 @@
"""
homeassistant.components.camera.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has a fake camera.
Demo camera platform that has a fake camera.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import os
from homeassistant.components.camera import Camera
import homeassistant.util.dt as dt_util
from homeassistant.components.camera import Camera
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo camera. """
"""Setup the Demo camera platform."""
add_devices([
DemoCamera('Demo camera')
])
class DemoCamera(Camera):
""" A Demo camera. """
"""A Demo camera."""
def __init__(self, name):
super().__init__()
self._name = name
def camera_image(self):
""" Return a faked still image response. """
"""Return a faked still image response."""
now = dt_util.utcnow()
image_path = os.path.join(os.path.dirname(__file__),
@@ -33,5 +35,5 @@ class DemoCamera(Camera):
@property
def name(self):
""" Return the name of this device. """
"""Return the name of this camera."""
return self._name
+1 -1
View File
@@ -10,8 +10,8 @@ import logging
import requests
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)
+1 -1
View File
@@ -11,8 +11,8 @@ import logging
import requests
from requests.auth import HTTPBasicAuth
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)
+32 -12
View File
@@ -6,14 +6,17 @@ Support for IP Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.mjpeg/
"""
from contextlib import closing
import logging
from contextlib import closing
import requests
from requests.auth import HTTPBasicAuth
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.const import HTTP_OK
from homeassistant.helpers import validate_config
CONTENT_TYPE_HEADER = 'Content-Type'
_LOGGER = logging.getLogger(__name__)
@@ -41,6 +44,17 @@ class MjpegCamera(Camera):
self._password = device_info.get('password')
self._mjpeg_url = device_info['mjpeg_url']
def camera_stream(self):
""" Return a mjpeg stream image response directly from the camera. """
if self._username and self._password:
return requests.get(self._mjpeg_url,
auth=HTTPBasicAuth(self._username,
self._password),
stream=True)
else:
return requests.get(self._mjpeg_url,
stream=True)
def camera_image(self):
""" Return a still image response from the camera. """
@@ -55,16 +69,22 @@ class MjpegCamera(Camera):
jpg = data[jpg_start:jpg_end + 2]
return jpg
if self._username and self._password:
with closing(requests.get(self._mjpeg_url,
auth=HTTPBasicAuth(self._username,
self._password),
stream=True)) as response:
return process_response(response)
else:
with closing(requests.get(self._mjpeg_url,
stream=True)) as response:
return process_response(response)
with closing(self.camera_stream()) as response:
return process_response(response)
def mjpeg_stream(self, handler):
""" Generate an HTTP MJPEG stream from the camera. """
response = self.camera_stream()
content_type = response.headers[CONTENT_TYPE_HEADER]
handler.send_response(HTTP_OK)
handler.send_header(CONTENT_TYPE_HEADER, content_type)
handler.end_headers()
for chunk in response.iter_content(chunk_size=1024):
if not chunk:
break
handler.wfile.write(chunk)
@property
def name(self):
+153
View File
@@ -0,0 +1,153 @@
"""
homeassistant.components.camera.uvc
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Ubiquiti's UVC cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.uvc/
"""
import logging
import socket
import requests
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
REQUIREMENTS = ['uvcclient==0.8']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Discover cameras on a Unifi NVR. """
if not validate_config({DOMAIN: config}, {DOMAIN: ['nvr', 'key']},
_LOGGER):
return None
addr = config.get('nvr')
key = config.get('key')
try:
port = int(config.get('port', 7080))
except ValueError:
_LOGGER.error('Invalid port number provided')
return False
from uvcclient import nvr
nvrconn = nvr.UVCRemote(addr, port, key)
try:
cameras = nvrconn.index()
except nvr.NotAuthorized:
_LOGGER.error('Authorization failure while connecting to NVR')
return False
except nvr.NvrError:
_LOGGER.error('NVR refuses to talk to me')
return False
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to NVR: %s', str(ex))
return False
# Filter out airCam models, which are not supported in the latest
# version of UnifiVideo and which are EOL by Ubiquiti
cameras = [camera for camera in cameras
if 'airCam' not in nvrconn.get_camera(camera['uuid'])['model']]
add_devices([UnifiVideoCamera(nvrconn,
camera['uuid'],
camera['name'])
for camera in cameras])
return True
class UnifiVideoCamera(Camera):
""" A Ubiquiti Unifi Video Camera. """
def __init__(self, nvr, uuid, name):
super(UnifiVideoCamera, self).__init__()
self._nvr = nvr
self._uuid = uuid
self._name = name
self.is_streaming = False
self._connect_addr = None
self._camera = None
@property
def name(self):
return self._name
@property
def is_recording(self):
caminfo = self._nvr.get_camera(self._uuid)
return caminfo['recordingSettings']['fullTimeRecordEnabled']
@property
def brand(self):
return 'Ubiquiti'
@property
def model(self):
caminfo = self._nvr.get_camera(self._uuid)
return caminfo['model']
def _login(self):
from uvcclient import camera as uvc_camera
from uvcclient import store as uvc_store
caminfo = self._nvr.get_camera(self._uuid)
if self._connect_addr:
addrs = [self._connect_addr]
else:
addrs = [caminfo['host'], caminfo['internalHost']]
store = uvc_store.get_info_store()
password = store.get_camera_password(self._uuid)
if password is None:
_LOGGER.debug('Logging into camera %(name)s with default password',
dict(name=self._name))
password = 'ubnt'
camera = None
for addr in addrs:
try:
camera = uvc_camera.UVCCameraClient(addr,
caminfo['username'],
password)
camera.login()
_LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
dict(name=self._name, addr=addr))
self._connect_addr = addr
break
except socket.error:
pass
except uvc_camera.CameraConnectError:
pass
except uvc_camera.CameraAuthError:
pass
if not self._connect_addr:
_LOGGER.error('Unable to login to camera')
return None
self._camera = camera
return True
def camera_image(self):
from uvcclient import camera as uvc_camera
if not self._camera:
if not self._login():
return
def _get_image(retry=True):
try:
return self._camera.get_snapshot()
except uvc_camera.CameraConnectError:
_LOGGER.error('Unable to contact camera')
except uvc_camera.CameraAuthError:
if retry:
self._login()
return _get_image(retry=False)
else:
_LOGGER.error('Unable to log into camera, unable '
'to get snapshot')
raise
return _get_image()
+2 -2
View File
@@ -11,8 +11,8 @@ the user has submitted configuration information.
"""
import logging
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.const import EVENT_TIME_CHANGED
from homeassistant.helpers.entity import generate_entity_id
DOMAIN = "configurator"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
@@ -141,7 +141,7 @@ class Configurator(object):
state = self.hass.states.get(entity_id)
new_data = state.attributes
new_data = dict(state.attributes)
new_data[ATTR_ERRORS] = error
self.hass.states.set(entity_id, STATE_CONFIGURE, new_data)
+1 -2
View File
@@ -9,10 +9,9 @@ https://home-assistant.io/components/conversation/
import logging
import re
from homeassistant import core
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
DOMAIN = "conversation"
+46 -14
View File
@@ -1,16 +1,15 @@
"""
homeassistant.components.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets up a demo environment that mimics interaction with devices.
For more details about this component, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import time
import homeassistant.core as ha
import homeassistant.bootstrap as bootstrap
import homeassistant.core as ha
import homeassistant.loader as loader
from homeassistant.const import (
CONF_PLATFORM, ATTR_ENTITY_ID)
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
DOMAIN = "demo"
@@ -21,6 +20,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
'binary_sensor',
'camera',
'device_tracker',
'garage_door',
'light',
'lock',
'media_player',
@@ -33,7 +33,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
def setup(hass, config):
""" Setup a demo environment. """
"""Setup a demo environment."""
group = loader.get_component('group')
configurator = loader.get_component('configurator')
@@ -63,14 +63,29 @@ def setup(hass, config):
switches = sorted(hass.states.entity_ids('switch'))
media_players = sorted(hass.states.entity_ids('media_player'))
group.Group(hass, 'living room', [
lights[2], lights[1], switches[0], media_players[1],
lights[1], switches[0], 'input_select.living_room_preset',
'rollershutter.living_room_window', media_players[1],
'scene.romantic_lights'])
group.Group(hass, 'bedroom', [lights[0], switches[1],
media_players[0]])
group.Group(hass, 'Rooms', [
'group.living_room', 'group.bedroom',
group.Group(hass, 'bedroom', [lights[0], switches[1], media_players[0]])
group.Group(hass, 'kitchen', [
lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door'])
group.Group(hass, 'doors', [
'lock.front_door', 'lock.kitchen_door',
'garage_door.right_garage_door', 'garage_door.left_garage_door'])
group.Group(hass, 'automations', [
'input_select.who_cooks', 'input_boolean.notify', ])
group.Group(hass, 'people', [
'device_tracker.demo_anne_therese', 'device_tracker.demo_home_boy',
'device_tracker.demo_paulus'])
group.Group(hass, 'thermostats', [
'thermostat.nest', 'thermostat.thermostat'])
group.Group(hass, 'downstairs', [
'group.living_room', 'group.kitchen',
'scene.romantic_lights', 'rollershutter.kitchen_window',
'rollershutter.living_room_window',
'rollershutter.living_room_window', 'group.doors', 'thermostat.nest',
], view=True)
group.Group(hass, 'Upstairs', [
'thermostat.thermostat', 'group.bedroom',
], view=True)
# Setup scripts
@@ -112,11 +127,28 @@ def setup(hass, config):
}},
]})
# Set up input select
bootstrap.setup_component(
hass, 'input_select',
{'input_select':
{'living_room_preset': {'options': ['Visitors',
'Visitors with kids',
'Home Alone']},
'who_cooks': {'icon': 'mdi:panda',
'initial': 'Anne Therese',
'name': 'Who cooks today',
'options': ['Paulus', 'Anne Therese']}}})
# Set up input boolean
bootstrap.setup_component(
hass, 'input_boolean',
{'input_boolean': {'notify': {'icon': 'mdi:car',
'initial': False,
'name': 'Notify Anne Therese is home'}}})
# Setup configurator
configurator_ids = []
def hue_configuration_callback(data):
""" Fake callback, mark config as done. """
"""Fake callback, mark config as done."""
time.sleep(2)
# First time it is called, pretend it failed.
@@ -10,10 +10,11 @@ https://home-assistant.io/components/device_sun_light_trigger/
import logging
from datetime import timedelta
from homeassistant.helpers.event import track_point_in_time, track_state_change
import homeassistant.util.dt as dt_util
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
from . import light, sun, device_tracker, group
from homeassistant.helpers.event import track_point_in_time, track_state_change
from . import device_tracker, group, light, sun
DOMAIN = "device_sun_light_trigger"
DEPENDENCIES = ['light', 'device_tracker', 'group', 'sun']
@@ -25,7 +25,7 @@ import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.const import (
ATTR_ENTITY_PICTURE, ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE,
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE,
DEVICE_DEFAULT_NAME, STATE_HOME, STATE_NOT_HOME)
DOMAIN = "device_tracker"
@@ -297,14 +297,16 @@ class Device(Entity):
""" State of the device. """
return self._state
@property
def entity_picture(self):
"""Picture of the device."""
return self.config_picture
@property
def state_attributes(self):
""" Device state attributes. """
attr = {}
if self.config_picture:
attr[ATTR_ENTITY_PICTURE] = self.config_picture
if self.gps:
attr[ATTR_LATITUDE] = self.gps[0]
attr[ATTR_LONGITUDE] = self.gps[1]
@@ -8,17 +8,17 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.actiontec/
"""
import logging
from datetime import timedelta
from collections import namedtuple
import re
import threading
import telnetlib
import threading
from collections import namedtuple
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -8,19 +8,19 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.aruba/
"""
import logging
from datetime import timedelta
import re
import threading
import telnetlib
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
REQUIREMENTS = ['pexpect==4.0.1']
_LOGGER = logging.getLogger(__name__)
_DEVICES_REGEX = re.compile(
@@ -44,6 +44,7 @@ def get_scanner(hass, config):
class ArubaDeviceScanner(object):
""" This class queries a Aruba Acces Point for connected devices. """
def __init__(self, config):
self.host = config[CONF_HOST]
self.username = config[CONF_USERNAME]
@@ -93,23 +94,39 @@ class ArubaDeviceScanner(object):
def get_aruba_data(self):
""" Retrieve data from Aruba Access Point and return parsed result. """
try:
telnet = telnetlib.Telnet(self.host)
telnet.read_until(b'User: ')
telnet.write((self.username + '\r\n').encode('ascii'))
telnet.read_until(b'Password: ')
telnet.write((self.password + '\r\n').encode('ascii'))
telnet.read_until(b'#')
telnet.write(('show clients\r\n').encode('ascii'))
devices_result = telnet.read_until(b'#').split(b'\r\n')
telnet.write('exit\r\n'.encode('ascii'))
except EOFError:
_LOGGER.exception("Unexpected response from router")
import pexpect
connect = "ssh {}@{}"
ssh = pexpect.spawn(connect.format(self.username, self.host))
query = ssh.expect(['password:', pexpect.TIMEOUT, pexpect.EOF,
'continue connecting (yes/no)?',
'Host key verification failed.',
'Connection refused',
'Connection timed out'], timeout=120)
if query == 1:
_LOGGER.error("Timeout")
return
except ConnectionRefusedError:
_LOGGER.exception("Connection refused by router," +
" is telnet enabled?")
elif query == 2:
_LOGGER.error("Unexpected response from router")
return
elif query == 3:
ssh.sendline('yes')
ssh.expect('password:')
elif query == 4:
_LOGGER.error("Host key Changed")
return
elif query == 5:
_LOGGER.error("Connection refused by server")
return
elif query == 6:
_LOGGER.error("Connection timed out")
return
ssh.sendline(self.password)
ssh.expect('#')
ssh.sendline('show clients')
ssh.expect('#')
devices_result = ssh.before.split(b'\r\n')
ssh.sendline('exit')
devices = {}
for device in devices_result:
@@ -119,5 +136,5 @@ class ArubaDeviceScanner(object):
'ip': match.group('ip'),
'mac': match.group('mac').upper(),
'name': match.group('name')
}
}
return devices
@@ -8,15 +8,15 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.asuswrt/
"""
import logging
from datetime import timedelta
import re
import threading
import telnetlib
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -8,15 +8,16 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.ddwrt/
"""
import logging
from datetime import timedelta
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -10,10 +10,10 @@ https://home-assistant.io/components/device_tracker.fritz/
import logging
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
REQUIREMENTS = ['fritzconnection==0.4.6']
@@ -7,9 +7,9 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.icloud/
"""
import logging
import re
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.event import track_utc_time_change
_LOGGER = logging.getLogger(__name__)
@@ -9,9 +9,8 @@ https://home-assistant.io/components/device_tracker.locative/
import logging
from functools import partial
from homeassistant.const import (
HTTP_UNPROCESSABLE_ENTITY, STATE_NOT_HOME)
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, STATE_NOT_HOME
_LOGGER = logging.getLogger(__name__)
@@ -7,17 +7,18 @@ presence.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.luci/
"""
import logging
import json
from datetime import timedelta
import logging
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -7,8 +7,9 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.mqtt/
"""
import logging
from homeassistant import util
import homeassistant.components.mqtt as mqtt
from homeassistant import util
DEPENDENCIES = ['mqtt']
@@ -8,12 +8,12 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.netgear/
"""
import logging
from datetime import timedelta
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -7,16 +7,16 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.nmap_scanner/
"""
import logging
from datetime import timedelta
from collections import namedtuple
import subprocess
import re
import subprocess
from collections import namedtuple
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOSTS
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle, convert
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -62,7 +62,7 @@ def setup_scanner(hass, config, see):
see_beacons(dev_id, kwargs)
def owntracks_event_update(topic, payload, qos):
# pylint: disable=too-many-branches
# pylint: disable=too-many-branches, too-many-statements
""" MQTT event (geofences) received. """
# Docs on available data:
@@ -92,10 +92,14 @@ def setup_scanner(hass, config, see):
if zone is None:
if data['t'] == 'b':
# Not a HA zone, and a beacon so assume mobile
MOBILE_BEACONS_ACTIVE[dev_id].append(location)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location not in beacons:
beacons.append(location)
_LOGGER.info("Added beacon %s", location)
else:
# Normal region
kwargs['location_name'] = location
if not zone.attributes.get('passive'):
kwargs['location_name'] = location
regions = REGIONS_ENTERED[dev_id]
if location not in regions:
@@ -107,27 +111,30 @@ def setup_scanner(hass, config, see):
see_beacons(dev_id, kwargs)
elif data['event'] == 'leave':
regions = REGIONS_ENTERED[dev_id]
if location in regions:
regions.remove(location)
new_region = regions[-1] if regions else None
with LOCK:
regions = REGIONS_ENTERED[dev_id]
if location in regions:
regions.remove(location)
new_region = regions[-1] if regions else None
if new_region:
# Exit to previous region
zone = hass.states.get("zone.{}".format(new_region))
kwargs['location_name'] = new_region
_set_gps_from_zone(kwargs, zone)
_LOGGER.info("Exit from to %s", new_region)
if new_region:
# Exit to previous region
zone = hass.states.get("zone.{}".format(new_region))
if not zone.attributes.get('passive'):
kwargs['location_name'] = new_region
_set_gps_from_zone(kwargs, zone)
_LOGGER.info("Exit to %s", new_region)
else:
_LOGGER.info("Exit to GPS")
else:
_LOGGER.info("Exit to GPS")
see(**kwargs)
see_beacons(dev_id, kwargs)
see(**kwargs)
see_beacons(dev_id, kwargs)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location in beacons:
beacons.remove(location)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location in beacons:
beacons.remove(location)
_LOGGER.info("Remove beacon %s", location)
else:
_LOGGER.error(
@@ -139,6 +146,8 @@ def setup_scanner(hass, config, see):
""" Set active beacons to the current location """
kwargs = kwargs_param.copy()
# the battery state applies to the tracking device, not the beacon
kwargs.pop('battery', None)
for beacon in MOBILE_BEACONS_ACTIVE[dev_id]:
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
kwargs['host_name'] = beacon
@@ -7,15 +7,15 @@ through SNMP.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.snmp/
"""
import logging
from datetime import timedelta
import threading
import binascii
import logging
import threading
from datetime import timedelta
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
@@ -8,15 +8,15 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.thomson/
"""
import logging
from datetime import timedelta
import re
import threading
import telnetlib
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
@@ -7,18 +7,18 @@ presence.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.tomato/
"""
import logging
import json
from datetime import timedelta
import logging
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -9,15 +9,16 @@ https://home-assistant.io/components/device_tracker.tplink/
"""
import base64
import logging
from datetime import timedelta
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -7,17 +7,18 @@ presence.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.ubus/
"""
import logging
import json
from datetime import timedelta
import logging
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -0,0 +1,79 @@
"""
homeassistant.components.device_tracker.unifi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Device tracker platform that supports scanning a Unifi WAP controller
"""
import logging
import urllib
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.helpers import validate_config
# Unifi package doesn't list urllib3 as a requirement
REQUIREMENTS = ['urllib3', 'unifi==1.2.4']
_LOGGER = logging.getLogger(__name__)
CONF_PORT = 'port'
def get_scanner(hass, config):
""" Sets up unifi device_tracker """
from unifi.controller import Controller
if not validate_config(config, {DOMAIN: [CONF_USERNAME,
CONF_PASSWORD]},
_LOGGER):
_LOGGER.error('Invalid configuration')
return False
this_config = config[DOMAIN]
host = this_config.get(CONF_HOST, 'localhost')
username = this_config.get(CONF_USERNAME)
password = this_config.get(CONF_PASSWORD)
try:
port = int(this_config.get(CONF_PORT, 8443))
except ValueError:
_LOGGER.error('Invalid port (must be numeric like 8443)')
return False
try:
ctrl = Controller(host, username, password, port, 'v4')
except urllib.error.HTTPError as ex:
_LOGGER.error('Failed to connect to unifi: %s', ex)
return False
return UnifiScanner(ctrl)
class UnifiScanner(object):
"""Provide device_tracker support from Unifi WAP client data."""
def __init__(self, controller):
self._controller = controller
self._update()
def _update(self):
try:
clients = self._controller.get_clients()
except urllib.error.HTTPError as ex:
_LOGGER.error('Failed to scan clients: %s', ex)
clients = []
self._clients = {client['mac']: client for client in clients}
def scan_devices(self):
""" Scans for devices. """
self._update()
return self._clients.keys()
def get_device_name(self, mac):
""" Returns the name (if known) of the device.
If a name has been set in Unifi, then return that, else
return the hostname if it has been detected.
"""
client = self._clients.get(mac, {})
name = client.get('name') or client.get('hostname')
_LOGGER.debug('Device %s name %s', mac, name)
return name
+25 -10
View File
@@ -1,6 +1,4 @@
"""
homeassistant.components.discovery
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Starts a service to scan in intervals for new devices.
Will emit EVENT_PLATFORM_DISCOVERED whenever a new service has been discovered.
@@ -13,8 +11,8 @@ import threading
from homeassistant import bootstrap
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_PLATFORM_DISCOVERED,
ATTR_SERVICE, ATTR_DISCOVERED)
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
EVENT_PLATFORM_DISCOVERED)
DOMAIN = "discovery"
REQUIREMENTS = ['netdisco==0.5.2']
@@ -29,7 +27,7 @@ SERVICE_SONOS = 'sonos'
SERVICE_PLEX = 'plex_mediaserver'
SERVICE_HANDLERS = {
SERVICE_WEMO: "switch",
SERVICE_WEMO: "wemo",
SERVICE_CAST: "media_player",
SERVICE_HUE: "light",
SERVICE_NETGEAR: 'device_tracker',
@@ -39,24 +37,41 @@ SERVICE_HANDLERS = {
def listen(hass, service, callback):
"""
Setup listener for discovery of specific service.
"""Setup listener for discovery of specific service.
Service can be a string or a list/tuple.
"""
if isinstance(service, str):
service = (service,)
else:
service = tuple(service)
def discovery_event_listener(event):
""" Listens for discovery events. """
"""Listen for discovery events."""
if event.data[ATTR_SERVICE] in service:
callback(event.data[ATTR_SERVICE], event.data[ATTR_DISCOVERED])
callback(event.data[ATTR_SERVICE], event.data.get(ATTR_DISCOVERED))
hass.bus.listen(EVENT_PLATFORM_DISCOVERED, discovery_event_listener)
def discover(hass, service, discovered=None, component=None, hass_config=None):
"""Fire discovery event.
Can ensure a component is loaded.
"""
if component is not None:
bootstrap.setup_component(hass, component, hass_config)
data = {
ATTR_SERVICE: service
}
if discovered is not None:
data[ATTR_DISCOVERED] = discovered
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, data)
def setup(hass, config):
""" Starts a discovery service. """
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -6,8 +6,8 @@ Provides functionality to download files.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/downloader/
"""
import os
import logging
import os
import re
import threading
+9 -29
View File
@@ -1,40 +1,20 @@
"""
homeassistant.components.ecobee
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ecobee Component
This component adds support for Ecobee3 Wireless Thermostats.
You will need to setup developer access to your thermostat,
and create and API key on the ecobee website.
The first time you run this component you will see a configuration
component card in Home Assistant. This card will contain a PIN code
that you will need to use to authorize access to your thermostat. You
can do this at https://www.ecobee.com/consumerportal/index.html
Click My Apps, Add application, Enter Pin and click Authorize.
After authorizing the application click the button in the configuration
card. Now your thermostat and sensors should shown in home-assistant.
You can use the optional hold_temp parameter to set whether or not holds
are set indefintely or until the next scheduled event.
ecobee:
api_key: asdfasdfasdfasdfasdfaasdfasdfasdfasdf
hold_temp: True
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ecobee component
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/ecobee/
"""
from datetime import timedelta
import logging
import os
from datetime import timedelta
from homeassistant.loader import get_component
from homeassistant import bootstrap
from homeassistant.util import Throttle
from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED, CONF_API_KEY)
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, EVENT_PLATFORM_DISCOVERED)
from homeassistant.loader import get_component
from homeassistant.util import Throttle
DOMAIN = "ecobee"
DISCOVER_THERMOSTAT = "ecobee.thermostat"
@@ -82,7 +62,7 @@ def request_configuration(network, hass, config):
def setup_ecobee(hass, network, config):
""" Setup ecobee thermostat """
""" Setup Ecobee thermostat. """
# If ecobee has a PIN then it needs to be configured.
if network.pin is not None:
request_configuration(network, hass, config)
+19 -4
View File
@@ -11,6 +11,7 @@ import logging
from . import version, mdi_version
import homeassistant.util as util
from homeassistant.const import URL_ROOT, HTTP_OK
from homeassistant.components import api
DOMAIN = 'frontend'
DEPENDENCIES = ['api']
@@ -25,21 +26,23 @@ FRONTEND_URLS = [
re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)'),
]
URL_API_BOOTSTRAP = "/api/bootstrap"
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
def setup(hass, config):
""" Setup serving the frontend. """
if 'http' not in hass.config.components:
_LOGGER.error('Dependency http is not loaded')
return False
for url in FRONTEND_URLS:
hass.http.register_path('GET', url, _handle_get_root, False)
hass.http.register_path('GET', '/service_worker.js',
_handle_get_service_worker, False)
# Bootstrap API
hass.http.register_path(
'GET', URL_API_BOOTSTRAP, _handle_get_api_bootstrap)
# Static files
hass.http.register_path(
'GET', re.compile(r'/static/(?P<file>[a-zA-Z\._\-0-9/]+)'),
@@ -54,6 +57,18 @@ def setup(hass, config):
return True
def _handle_get_api_bootstrap(handler, path_match, data):
""" Returns all data needed to bootstrap Home Assistant. """
hass = handler.server.hass
handler.write_json({
'config': hass.config.as_dict(),
'states': hass.states.all(),
'events': api.events_json(hass),
'services': api.services_json(hass),
})
def _handle_get_root(handler, path_match, data):
""" Renders the frontend. """
handler.send_response(HTTP_OK)
@@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by update_mdi script """
VERSION = "a2605736c8d959d50c4bcbba1e6a6aa5"
VERSION = "2f4adc5d3ad6d2f73bf69ed29b7594fd"
+1 -1
View File
@@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "1e89871aaae43c91b2508f52bc161b69"
VERSION = "a4d021cb50ed079fcfda7369ed2f0d4a"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
!function(e){function t(r){if(n[r])return n[r].exports;var s=n[r]={exports:{},id:r,loaded:!1};return e[r].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([/*!*************************************!*\
!*** ./src/service-worker/index.js ***!
\*************************************/
function(e,t,n){"use strict";var r="0.10",s="/",c=["/","/logbook","/history","/map","/devService","/devState","/devEvent","/devInfo","/states"],i=["/static/favicon-192x192.png"];self.addEventListener("install",function(e){e.waitUntil(caches.open(r).then(function(e){return e.addAll(i.concat(s))}))}),self.addEventListener("activate",function(e){}),self.addEventListener("message",function(e){}),self.addEventListener("fetch",function(e){var t=e.request.url.substr(e.request.url.indexOf("/",8));i.includes(t)&&e.respondWith(caches.open(r).then(function(t){return t.match(e.request)})),c.includes(t)&&e.respondWith(caches.open(r).then(function(t){return t.match(s).then(function(n){var r=fetch(e.request).then(function(e){return t.put(s,e.clone()),e});return n||r})}))})}]);
function(e,t,n){"use strict";var r="0.10",s="/",c=["/","/logbook","/history","/map","/devService","/devState","/devEvent","/devInfo","/states"],i=["/static/favicon-192x192.png"];self.addEventListener("install",function(e){e.waitUntil(caches.open(r).then(function(e){return e.addAll(i.concat(s))}))}),self.addEventListener("activate",function(e){}),self.addEventListener("message",function(e){}),self.addEventListener("fetch",function(e){var t=e.request.url.substr(e.request.url.indexOf("/",8));i.includes(t)&&e.respondWith(caches.open(r).then(function(t){return t.match(e.request)})),c.includes(t)&&e.respondWith(caches.open(r).then(function(t){return t.match(s).then(function(n){return n||fetch(e.request).then(function(e){return t.put(s,e.clone()),e})})}))})}]);
//# sourceMappingURL=service_worker.js.map
@@ -0,0 +1,105 @@
"""
Component to interface with garage doors that can be controlled remotely.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/garage_door/
"""
import logging
import os
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
ATTR_ENTITY_ID)
from homeassistant.components import (group, wink)
DOMAIN = 'garage_door'
SCAN_INTERVAL = 30
GROUP_NAME_ALL_GARAGE_DOORS = 'all garage doors'
ENTITY_ID_ALL_GARAGE_DOORS = group.ENTITY_ID_FORMAT.format('all_garage_doors')
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_GARAGE_DOORS: 'wink'
}
_LOGGER = logging.getLogger(__name__)
def is_closed(hass, entity_id=None):
"""Returns if the garage door is closed based on the statemachine."""
entity_id = entity_id or ENTITY_ID_ALL_GARAGE_DOORS
return hass.states.is_state(entity_id, STATE_CLOSED)
def close_door(hass, entity_id=None):
"""Closes all or specified garage door."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_CLOSE, data)
def open_door(hass, entity_id=None):
"""Open all or specified garage door."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_OPEN, data)
def setup(hass, config):
"""Track states and offer events for garage door."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_GARAGE_DOORS)
component.setup(config)
def handle_garage_door_service(service):
"""Handles calls to the garage door services."""
target_locks = component.extract_from_service(service)
for item in target_locks:
if service.service == SERVICE_CLOSE:
item.close_door()
else:
item.open_door()
if item.should_poll:
item.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service,
descriptions.get(SERVICE_OPEN))
hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service,
descriptions.get(SERVICE_CLOSE))
return True
class GarageDoorDevice(Entity):
"""Represents a garage door."""
# pylint: disable=no-self-use
@property
def is_closed(self):
"""Return true if door is closed."""
return None
def close_door(self):
"""Close the garage door."""
raise NotImplementedError()
def open_door(self):
"""Open the garage door."""
raise NotImplementedError()
@property
def state(self):
"""Returns the state of the garage door."""
closed = self.is_closed
if closed is None:
return STATE_UNKNOWN
return STATE_CLOSED if closed else STATE_OPEN
@@ -0,0 +1,49 @@
"""
Demo garage door platform that has two fake doors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import STATE_CLOSED, STATE_OPEN
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup demo garage door platform."""
add_devices_callback([
DemoGarageDoor('Left Garage Door', STATE_CLOSED),
DemoGarageDoor('Right Garage Door', STATE_OPEN)
])
class DemoGarageDoor(GarageDoorDevice):
"""Provides a demo garage door."""
def __init__(self, name, state):
self._name = name
self._state = state
@property
def should_poll(self):
"""No polling needed for a demo garage door."""
return False
@property
def name(self):
"""Return the name of the device if any."""
return self._name
@property
def is_closed(self):
"""Return true if garage door is closed."""
return self._state == STATE_CLOSED
def close_door(self, **kwargs):
"""Close the garage door."""
self._state = STATE_CLOSED
self.update_ha_state()
def open_door(self, **kwargs):
"""Open the garage door."""
self._state = STATE_OPEN
self.update_ha_state()
@@ -0,0 +1,65 @@
"""
Support for Wink garage doors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/garage_door.wink/
"""
import logging
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.2']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Sets up the Wink garage door platform."""
import pywink
if discovery_info is None:
token = config.get(CONF_ACCESS_TOKEN)
if token is None:
logging.getLogger(__name__).error(
"Missing wink access_token. "
"Get one at https://winkbearertoken.appspot.com/")
return
pywink.set_bearer_token(token)
add_devices(WinkGarageDoorDevice(door) for door in
pywink.get_garage_doors())
class WinkGarageDoorDevice(GarageDoorDevice):
"""Represents a Wink garage door."""
def __init__(self, wink):
self.wink = wink
@property
def unique_id(self):
"""Returns the id of this wink garage door."""
return "{}.{}".format(self.__class__, self.wink.device_id())
@property
def name(self):
"""Returns the name of the garage door if any."""
return self.wink.name()
def update(self):
"""Update the state of the garage door."""
self.wink.update_state()
@property
def is_closed(self):
"""Returns true if door is closed."""
return self.wink.state() == 0
def close_door(self):
"""Closes the door."""
self.wink.set_state(0)
def open_door(self):
"""Open the door."""
self.wink.set_state(1)
+134
View File
@@ -0,0 +1,134 @@
"""
Component that records all events and state changes and feeds the data to
a Graphite installation.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/graphite/
"""
import logging
import queue
import socket
import threading
import time
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED)
from homeassistant.helpers import state
DOMAIN = "graphite"
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
"""Setup the Graphite feeder."""
graphite_config = config.get('graphite', {})
host = graphite_config.get('host', 'localhost')
prefix = graphite_config.get('prefix', 'ha')
try:
port = int(graphite_config.get('port', 2003))
except ValueError:
_LOGGER.error('Invalid port specified')
return False
GraphiteFeeder(hass, host, port, prefix)
return True
class GraphiteFeeder(threading.Thread):
"""Feeds data to Graphite."""
def __init__(self, hass, host, port, prefix):
super(GraphiteFeeder, self).__init__(daemon=True)
self._hass = hass
self._host = host
self._port = port
# rstrip any trailing dots in case they think they need it
self._prefix = prefix.rstrip('.')
self._queue = queue.Queue()
self._quit_object = object()
self._we_started = False
hass.bus.listen_once(EVENT_HOMEASSISTANT_START,
self.start_listen)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
self.shutdown)
hass.bus.listen(EVENT_STATE_CHANGED, self.event_listener)
_LOGGER.debug('Graphite feeding to %s:%i initialized',
self._host, self._port)
def start_listen(self, event):
"""Start event-processing thread."""
_LOGGER.debug('Event processing thread started')
self._we_started = True
self.start()
def shutdown(self, event):
"""Signal shutdown of processing event."""
_LOGGER.debug('Event processing signaled exit')
self._queue.put(self._quit_object)
def event_listener(self, event):
"""Queue an event for processing."""
if self.is_alive() or not self._we_started:
_LOGGER.debug('Received event')
self._queue.put(event)
else:
_LOGGER.error('Graphite feeder thread has died, not '
'queuing event!')
def _send_to_graphite(self, data):
"""Send data to Graphite."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((self._host, self._port))
sock.sendall(data.encode('ascii'))
sock.send('\n'.encode('ascii'))
sock.close()
def _report_attributes(self, entity_id, new_state):
"""Report the attributes."""
now = time.time()
things = dict(new_state.attributes)
try:
things['state'] = state.state_as_number(new_state)
except ValueError:
pass
lines = ['%s.%s.%s %f %i' % (self._prefix,
entity_id, key.replace(' ', '_'),
value, now)
for key, value in things.items()
if isinstance(value, (float, int))]
if not lines:
return
_LOGGER.debug('Sending to graphite: %s', lines)
try:
self._send_to_graphite('\n'.join(lines))
except socket.gaierror:
_LOGGER.error('Unable to connect to host %s', self._host)
except socket.error:
_LOGGER.exception('Failed to send data to graphite')
def run(self):
"""Run the process to export the data."""
while True:
event = self._queue.get()
if event == self._quit_object:
_LOGGER.debug('Event processing thread stopped')
self._queue.task_done()
return
elif (event.event_type == EVENT_STATE_CHANGED and
event.data.get('new_state')):
_LOGGER.debug('Processing STATE_CHANGED event for %s',
event.data['entity_id'])
try:
self._report_attributes(event.data['entity_id'],
event.data['new_state'])
# pylint: disable=broad-except
except Exception:
# Catch this so we can avoid the thread dying and
# make it visible.
_LOGGER.exception('Failed to process STATE_CHANGED event')
else:
_LOGGER.warning('Processing unexpected event type %s',
event.event_type)
self._queue.task_done()
+70 -34
View File
@@ -7,13 +7,13 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/group/
"""
import homeassistant.core as ha
from homeassistant.helpers.event import track_state_change
from homeassistant.helpers.entity import (
Entity, split_entity_id, generate_entity_id)
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED,
STATE_UNKNOWN, CONF_NAME, CONF_ICON)
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME,
STATE_NOT_HOME, STATE_OFF, STATE_ON, STATE_OPEN, STATE_UNKNOWN,
ATTR_ASSUMED_STATE, )
from homeassistant.helpers.entity import (
Entity, generate_entity_id, split_entity_id)
from homeassistant.helpers.event import track_state_change
DOMAIN = 'group'
@@ -71,7 +71,7 @@ def expand_entity_ids(hass, entity_ids):
if domain == DOMAIN:
found_ids.extend(
ent_id for ent_id
in get_entity_ids(hass, entity_id)
in expand_entity_ids(hass, get_entity_ids(hass, entity_id))
if ent_id not in found_ids)
else:
@@ -145,6 +145,7 @@ class Group(Entity):
self.tracking = []
self.group_on = None
self.group_off = None
self._assumed_state = False
if entity_ids is not None:
self.update_tracked_entity_ids(entity_ids)
@@ -183,6 +184,11 @@ class Group(Entity):
data[ATTR_VIEW] = True
return data
@property
def assumed_state(self):
"""Return True if unable to access real state of entity."""
return self._assumed_state
def update_tracked_entity_ids(self, entity_ids):
""" Update the tracked entity IDs. """
self.stop()
@@ -208,47 +214,77 @@ class Group(Entity):
def update(self):
""" Query all the tracked states and determine current group state. """
self._state = STATE_UNKNOWN
self._update_group_state()
def _state_changed_listener(self, entity_id, old_state, new_state):
""" Listener to receive state changes of tracked entities. """
self._update_group_state(new_state)
self.update_ha_state()
@property
def _tracking_states(self):
"""States that the group is tracking."""
states = []
for entity_id in self.tracking:
state = self.hass.states.get(entity_id)
if state is not None:
self._process_tracked_state(state)
states.append(state)
def _state_changed_listener(self, entity_id, old_state, new_state):
""" Listener to receive state changes of tracked entities. """
self._process_tracked_state(new_state)
self.update_ha_state()
return states
def _process_tracked_state(self, tr_state):
""" Updates group state based on a new state of a tracked entity. """
def _update_group_state(self, tr_state=None):
"""Update group state.
Optionally you can provide the only state changed since last update
allowing this method to take shortcuts.
"""
# pylint: disable=too-many-branches
# To store current states of group entities. Might not be needed.
states = None
gr_state, gr_on, gr_off = self._state, self.group_on, self.group_off
# We have not determined type of group yet
if self.group_on is None:
self.group_on, self.group_off = _get_group_on_off(tr_state.state)
if gr_on is None:
if tr_state is None:
states = self._tracking_states
if self.group_on is not None:
# New state of the group is going to be based on the first
# state that we can recognize
self._state = tr_state.state
for state in states:
gr_on, gr_off = \
_get_group_on_off(state.state)
if gr_on is not None:
break
else:
gr_on, gr_off = _get_group_on_off(tr_state.state)
if gr_on is not None:
self.group_on, self.group_off = gr_on, gr_off
# We cannot determine state of the group
if gr_on is None:
return
# There is already a group state
cur_gr_state = self._state
group_on, group_off = self.group_on, self.group_off
if tr_state is None or (gr_state == gr_on and
tr_state.state == gr_off):
if states is None:
states = self._tracking_states
# if cur_gr_state = OFF and tr_state = ON: set ON
# if cur_gr_state = ON and tr_state = OFF: research
# else: ignore
if any(state.state == gr_on for state in states):
self._state = gr_on
else:
self._state = gr_off
if cur_gr_state == group_off and tr_state.state == group_on:
self._state = group_on
elif tr_state.state in (gr_on, gr_off):
self._state = tr_state.state
elif cur_gr_state == group_on and tr_state.state == group_off:
if tr_state is None or self._assumed_state and \
not tr_state.attributes.get(ATTR_ASSUMED_STATE):
if states is None:
states = self._tracking_states
# Set to off if no other states are on
if not any(self.hass.states.is_state(ent_id, group_on)
for ent_id in self.tracking
if tr_state.entity_id != ent_id):
self._state = group_off
self._assumed_state = any(state.attributes.get(ATTR_ASSUMED_STATE)
for state in states)
elif tr_state.attributes.get(ATTR_ASSUMED_STATE):
self._assumed_state = True
+64 -17
View File
@@ -7,17 +7,19 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/history/
"""
import re
from collections import defaultdict
from datetime import timedelta
from itertools import groupby
from collections import defaultdict
import homeassistant.util.dt as dt_util
import homeassistant.components.recorder as recorder
import homeassistant.util.dt as dt_util
from homeassistant.const import HTTP_BAD_REQUEST
DOMAIN = 'history'
DEPENDENCIES = ['recorder', 'http']
SIGNIFICANT_DOMAINS = ('thermostat',)
URL_HISTORY_PERIOD = re.compile(
r'/api/history/period(?:/(?P<date>\d{4}-\d{1,2}-\d{1,2})|)')
@@ -35,6 +37,37 @@ def last_5_states(entity_id):
return recorder.query_states(query, (entity_id, ))
def get_significant_states(start_time, end_time=None, entity_id=None):
"""Return states changes during UTC period start_time - end_time.
Significant states are all states where there is a state change,
as well as all states from certain domains (for instance
thermostat so that we get current temperature in our graphs).
"""
where = """
(domain in ({}) or last_changed=last_updated)
AND last_updated > ?
""".format(",".join(["'%s'" % x for x in SIGNIFICANT_DOMAINS]))
data = [start_time]
if end_time is not None:
where += "AND last_updated < ? "
data.append(end_time)
if entity_id is not None:
where += "AND entity_id = ? "
data.append(entity_id.lower())
query = ("SELECT * FROM states WHERE {} "
"ORDER BY entity_id, last_updated ASC").format(where)
states = recorder.query_states(query, data)
return states_to_json(states, start_time, entity_id)
def state_changes_during_period(start_time, end_time=None, entity_id=None):
"""
Return states changes during UTC period start_time - end_time.
@@ -55,20 +88,7 @@ def state_changes_during_period(start_time, end_time=None, entity_id=None):
states = recorder.query_states(query, data)
result = defaultdict(list)
entity_ids = [entity_id] if entity_id is not None else None
# Get the states at the start time
for state in get_states(start_time, entity_ids):
state.last_changed = start_time
result[state.entity_id].append(state)
# Append all changes to it
for entity_id, group in groupby(states, lambda state: state.entity_id):
result[entity_id].extend(group)
return result
return states_to_json(states, start_time, entity_id)
def get_states(utc_point_in_time, entity_ids=None, run=None):
@@ -100,6 +120,33 @@ def get_states(utc_point_in_time, entity_ids=None, run=None):
return recorder.query_states(query, where_data)
def states_to_json(states, start_time, entity_id):
"""Converts SQL results into JSON friendly data structure.
This takes our state list and turns it into a JSON friendly data
structure {'entity_id': [list of states], 'entity_id2': [list of states]}
We also need to go back and create a synthetic zero data point for
each list of states, otherwise our graphs won't start on the Y
axis correctly.
"""
result = defaultdict(list)
entity_ids = [entity_id] if entity_id is not None else None
# Get the states at the start time
for state in get_states(start_time, entity_ids):
state.last_changed = start_time
state.last_updated = start_time
result[state.entity_id].append(state)
# Append all changes to it
for entity_id, group in groupby(states, lambda state: state.entity_id):
result[entity_id].extend(group)
return result
def get_state(utc_point_in_time, entity_id, run=None):
""" Return a state at a specific point in time. """
states = get_states(utc_point_in_time, (entity_id,), run)
@@ -152,4 +199,4 @@ def _api_history_period(handler, path_match, data):
entity_id = data.get('filter_entity_id')
handler.write_json(
state_changes_during_period(start_time, end_time, entity_id).values())
get_significant_states(start_time, end_time, entity_id).values())
+13 -12
View File
@@ -6,30 +6,31 @@ This module provides an API and a HTTP interface for debug purposes.
For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/
"""
from datetime import timedelta
import gzip
from http import cookies
from http.server import SimpleHTTPRequestHandler, HTTPServer
import json
import logging
import os
from socketserver import ThreadingMixIn
import ssl
import threading
import time
from urllib.parse import urlparse, parse_qs
from datetime import timedelta
from http import cookies
from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn
from urllib.parse import parse_qs, urlparse
import homeassistant.bootstrap as bootstrap
import homeassistant.core as ha
from homeassistant.const import (
SERVER_PORT, CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT_PLAIN,
HTTP_HEADER_HA_AUTH, HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_ACCEPT_ENCODING,
HTTP_HEADER_CONTENT_ENCODING, HTTP_HEADER_VARY, HTTP_HEADER_CONTENT_LENGTH,
HTTP_HEADER_CACHE_CONTROL, HTTP_HEADER_EXPIRES, HTTP_OK, HTTP_UNAUTHORIZED,
HTTP_NOT_FOUND, HTTP_METHOD_NOT_ALLOWED, HTTP_UNPROCESSABLE_ENTITY)
import homeassistant.remote as rem
import homeassistant.util as util
import homeassistant.util.dt as date_util
import homeassistant.bootstrap as bootstrap
from homeassistant.const import (
CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT_PLAIN, HTTP_HEADER_ACCEPT_ENCODING,
HTTP_HEADER_CACHE_CONTROL, HTTP_HEADER_CONTENT_ENCODING,
HTTP_HEADER_CONTENT_LENGTH, HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_EXPIRES,
HTTP_HEADER_HA_AUTH, HTTP_HEADER_VARY, HTTP_METHOD_NOT_ALLOWED,
HTTP_NOT_FOUND, HTTP_OK, HTTP_UNAUTHORIZED, HTTP_UNPROCESSABLE_ENTITY,
SERVER_PORT)
DOMAIN = "http"
+1
View File
@@ -7,6 +7,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/ifttt/
"""
import logging
import requests
from homeassistant.helpers import validate_config
+28 -31
View File
@@ -1,18 +1,15 @@
"""
homeassistant.components.influxdb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
InfluxDB component which allows you to send data to an Influx database.
A component which allows you to send data to an Influx database.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/influxdb/
"""
import logging
import homeassistant.util as util
from homeassistant.const import EVENT_STATE_CHANGED, STATE_UNKNOWN
from homeassistant.helpers import state as state_helper
from homeassistant.helpers import validate_config
from homeassistant.const import (EVENT_STATE_CHANGED, STATE_ON, STATE_OFF,
STATE_UNLOCKED, STATE_LOCKED, STATE_UNKNOWN)
from homeassistant.components.sun import (STATE_ABOVE_HORIZON,
STATE_BELOW_HORIZON)
_LOGGER = logging.getLogger(__name__)
@@ -22,22 +19,27 @@ DEPENDENCIES = []
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 8086
DEFAULT_DATABASE = 'home_assistant'
DEFAULT_SSL = False
DEFAULT_VERIFY_SSL = False
REQUIREMENTS = ['influxdb==2.11.0']
REQUIREMENTS = ['influxdb==2.12.0']
CONF_HOST = 'host'
CONF_PORT = 'port'
CONF_DB_NAME = 'database'
CONF_USERNAME = 'username'
CONF_PASSWORD = 'password'
CONF_SSL = 'ssl'
CONF_VERIFY_SSL = 'verify_ssl'
def setup(hass, config):
""" Setup the InfluxDB component. """
"""Setup the InfluxDB component."""
from influxdb import InfluxDBClient, exceptions
if not validate_config(config, {DOMAIN: ['host']}, _LOGGER):
if not validate_config(config, {DOMAIN: ['host',
CONF_USERNAME,
CONF_PASSWORD]}, _LOGGER):
return False
conf = config[DOMAIN]
@@ -47,40 +49,35 @@ def setup(hass, config):
database = util.convert(conf.get(CONF_DB_NAME), str, DEFAULT_DATABASE)
username = util.convert(conf.get(CONF_USERNAME), str)
password = util.convert(conf.get(CONF_PASSWORD), str)
ssl = util.convert(conf.get(CONF_SSL), bool, DEFAULT_SSL)
verify_ssl = util.convert(conf.get(CONF_VERIFY_SSL), bool,
DEFAULT_VERIFY_SSL)
try:
influx = InfluxDBClient(host=host, port=port, username=username,
password=password, database=database)
password=password, database=database,
ssl=ssl, verify_ssl=verify_ssl)
influx.query("select * from /.*/ LIMIT 1;")
except exceptions.InfluxDBClientError as exc:
_LOGGER.error("Database host is not accessible due to '%s', please "
"check your entries in the configuration file and that"
" the database exists and is READ/WRITE.", exc)
"check your entries in the configuration file and that "
"the database exists and is READ/WRITE.", exc)
return False
def influx_event_listener(event):
""" Listen for new messages on the bus and sends them to Influx. """
"""Listen for new messages on the bus and sends them to Influx."""
state = event.data.get('new_state')
if state is None:
if state is None or state.state in (STATE_UNKNOWN, ''):
return
if state.state in (STATE_ON, STATE_LOCKED, STATE_ABOVE_HORIZON):
_state = 1
elif state.state in (STATE_OFF, STATE_UNLOCKED, STATE_UNKNOWN,
STATE_BELOW_HORIZON):
_state = 0
else:
try:
_state = state_helper.state_as_number(state)
except ValueError:
_state = state.state
if _state == '':
return
try:
_state = float(_state)
except ValueError:
pass
measurement = state.attributes.get('unit_of_measurement', state.domain)
measurement = state.attributes.get('unit_of_measurement')
if measurement in (None, ''):
measurement = state.entity_id
json_body = [
{
+6 -6
View File
@@ -9,9 +9,9 @@ at https://home-assistant.io/components/input_boolean/
import logging
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.helpers.entity_component import EntityComponent
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
DOMAIN = 'input_boolean'
@@ -41,7 +41,7 @@ def turn_off(hass, entity_id):
def setup(hass, config):
"""Set up input booleans."""
""" Set up input boolean. """
if not isinstance(config.get(DOMAIN), dict):
_LOGGER.error('Expected %s config to be a dictionary', DOMAIN)
return False
@@ -68,7 +68,7 @@ def setup(hass, config):
return False
def toggle_service(service):
"""Handle a calls to the input boolean services."""
""" Handle a calls to the input boolean services. """
target_inputs = component.extract_from_service(service)
for input_b in target_inputs:
@@ -86,10 +86,10 @@ def setup(hass, config):
class InputBoolean(ToggleEntity):
"""Represent a boolean input within Home Assistant."""
""" Represent a boolean input. """
def __init__(self, object_id, name, state, icon):
"""Initialize a boolean input."""
""" Initialize a boolean input. """
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._name = name
self._state = state
+140
View File
@@ -0,0 +1,140 @@
"""
homeassistant.components.input_select
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to offer a way to select an option from a list.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/input_select/
"""
import logging
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
DOMAIN = 'input_select'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
_LOGGER = logging.getLogger(__name__)
CONF_NAME = 'name'
CONF_INITIAL = 'initial'
CONF_ICON = 'icon'
CONF_OPTIONS = 'options'
ATTR_OPTION = 'option'
ATTR_OPTIONS = 'options'
SERVICE_SELECT_OPTION = 'select_option'
def select_option(hass, entity_id, option):
""" Set input_select to False. """
hass.services.call(DOMAIN, SERVICE_SELECT_OPTION, {
ATTR_ENTITY_ID: entity_id,
ATTR_OPTION: option,
})
def setup(hass, config):
""" Set up input select. """
if not isinstance(config.get(DOMAIN), dict):
_LOGGER.error('Expected %s config to be a dictionary', DOMAIN)
return False
component = EntityComponent(_LOGGER, DOMAIN, hass)
entities = []
for object_id, cfg in config[DOMAIN].items():
if object_id != slugify(object_id):
_LOGGER.warning("Found invalid key for boolean input: %s. "
"Use %s instead", object_id, slugify(object_id))
continue
if not cfg:
_LOGGER.warning("No configuration specified for %s", object_id)
continue
name = cfg.get(CONF_NAME)
options = cfg.get(CONF_OPTIONS)
if not isinstance(options, list) or len(options) == 0:
_LOGGER.warning('Key %s should be a list of options', CONF_OPTIONS)
continue
options = [str(val) for val in options]
state = cfg.get(CONF_INITIAL)
if state not in options:
state = options[0]
icon = cfg.get(CONF_ICON)
entities.append(InputSelect(object_id, name, state, options, icon))
if not entities:
return False
def select_option_service(call):
""" Handle a calls to the input select services. """
target_inputs = component.extract_from_service(call)
for input_select in target_inputs:
input_select.select_option(call.data.get(ATTR_OPTION))
hass.services.register(DOMAIN, SERVICE_SELECT_OPTION,
select_option_service)
component.add_entities(entities)
return True
class InputSelect(Entity):
""" Represent a select input. """
# pylint: disable=too-many-arguments
def __init__(self, object_id, name, state, options, icon):
""" Initialize a select input. """
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._name = name
self._current_option = state
self._options = options
self._icon = icon
@property
def should_poll(self):
""" If entity should be polled. """
return False
@property
def name(self):
""" Name of the select input. """
return self._name
@property
def icon(self):
""" Icon to be used for this entity. """
return self._icon
@property
def state(self):
""" State of the component. """
return self._current_option
@property
def state_attributes(self):
""" State attributes. """
return {
ATTR_OPTIONS: self._options,
}
def select_option(self, option):
""" Select new option. """
if option not in self._options:
_LOGGER.warning('Invalid option: %s (possible options: %s)',
option, ', '.join(self._options))
return
self._current_option = option
self.update_ha_state()
+9 -8
View File
@@ -1,19 +1,20 @@
"""
homeassistant.components.insteon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
homeassistant.components.insteon_hub
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Insteon Hub.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/insteon/
https://home-assistant.io/components/insteon_hub/
"""
import logging
import homeassistant.bootstrap as bootstrap
from homeassistant.helpers import validate_config
from homeassistant.loader import get_component
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY, ATTR_DISCOVERED,
ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME,
EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.loader import get_component
DOMAIN = "insteon_hub"
REQUIREMENTS = ['insteon_hub==0.4.5']
+5 -6
View File
@@ -11,13 +11,12 @@ import logging
from urllib.parse import urlparse
from homeassistant import bootstrap
from homeassistant.loader import get_component
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_HOST, CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
CONF_HOST, CONF_USERNAME, CONF_PASSWORD, EVENT_PLATFORM_DISCOVERED,
EVENT_HOMEASSISTANT_STOP, ATTR_SERVICE, ATTR_DISCOVERED,
ATTR_FRIENDLY_NAME)
from homeassistant.loader import get_component
DOMAIN = "isy994"
REQUIREMENTS = ['PyISY==1.0.5']
@@ -147,7 +146,7 @@ class ISYDeviceABC(ToggleEntity):
@property
def state_attributes(self):
""" Returns the state attributes for the node. """
attr = {ATTR_FRIENDLY_NAME: self.name}
attr = {}
for name, prop in self._attrs.items():
attr[name] = getattr(self, prop)
attr = self._attr_filter(attr)

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