Compare commits

..

769 Commits
0.7.3 ... 0.8

Author SHA1 Message Date
Paulus Schoutsen
8e44ed0090 Merge pull request #617 from balloob/dev
0.8.0.rc1
2015-11-16 00:03:20 -08:00
Paulus Schoutsen
5fb6076f6e Version bump to 0.8.0 2015-11-16 00:03:00 -08:00
Fabian Affolter
4ea6def1bd Update docstrings and link to docs 2015-11-16 07:53:31 +01:00
Paulus Schoutsen
314185ffb8 Merge pull request #629 from rmkraus/dev
Adding updater to default list of components
2015-11-15 17:30:15 -08:00
Ryan Kraus
8aafb89a64 Merge remote-tracking branch 'balloob/dev' into dev 2015-11-15 20:04:21 -05:00
Ryan Kraus
d01ed9788f Added updater to set of default components 2015-11-15 20:03:45 -05:00
Paulus Schoutsen
b4e2f4f0be Merge pull request #625 from balloob/enhancement_pushbullet
Enhancement: targeted notifications in pushbullet
2015-11-15 16:41:18 -08:00
Tom Duijf
f4d8325084 Pushbullet; code cleanup & better errors on config typos 2015-11-16 00:29:04 +00:00
Tom Duijf
cc5dec3c59 Processed feedback from PR comments 2015-11-15 23:46:16 +00:00
Ryan Kraus
ec5a93a0fd Merge remote-tracking branch 'balloob/dev' into dev 2015-11-15 18:16:49 -05:00
Paulus Schoutsen
aabda1b7b0 Merge pull request #628 from rmkraus/dev
Small tweaks to daemon management.
2015-11-15 14:46:58 -08:00
Tom Duijf
aee4411cfb <type>.<name> split only on first separator 2015-11-15 22:44:51 +00:00
Ryan Kraus
135eb0a0ac Fixed hass daemon management
1) Changed signal to exit hass to SIGTERM
2) Updated initd script to send SIGTERM
3) Updated systemd script to never send SIGKILL.
2015-11-15 17:43:38 -05:00
Ryan Kraus
01daac066a Merge remote-tracking branch 'balloob/dev' into dev 2015-11-15 17:37:57 -05:00
Ryan Kraus
5d96ca133d Merge pull request #627 from rmkraus/update_notify
Updater component
2015-11-15 17:36:55 -05:00
Ryan Kraus
3cda1aacff Fixed typo in updater entity attributes.
Left some quotes in there. My bad.
2015-11-15 17:34:06 -05:00
Ryan Kraus
243130c133 Using ATTR_FRIENDLY_NAME in updater component. 2015-11-15 17:32:05 -05:00
Paulus Schoutsen
0d74b628b0 Merge pull request #623 from balloob/lib-clean-on-upgrade
Lib clean on upgrade
2015-11-15 14:30:51 -08:00
Ryan Kraus
c314101dde Updater suggestions from Paulus
1) Moved error checking into get_newest_version function.
2) Fixed import formatting mistake.
2015-11-15 17:30:42 -05:00
Paulus Schoutsen
0f68dc6b7b Add tests for version upgrade 2015-11-15 14:28:50 -08:00
Ryan Kraus
919c20a263 Implemented suggestions from Paulus for updater
1) Better Error handling when making PyPI requests.
2) More efficient event scheduling.
3) ENTITY_ID in constant
3) friendly_name from constant
2015-11-15 17:23:56 -05:00
Ryan Kraus
9b5385c565 Merge remote-tracking branch 'balloob/dev' into update_notify
Conflicts:
	homeassistant/components/frontend/version.py
	homeassistant/components/frontend/www_static/frontend.html
2015-11-15 17:00:35 -05:00
Ryan Kraus
7dacf01baa Updating fronted to latest version. 2015-11-15 16:56:33 -05:00
Tom Duijf
9b4650afd4 Added comments 2015-11-15 20:49:42 +00:00
Tom Duijf
0b0fd2490d Pushbullet; styling, requirements, example 2015-11-15 19:14:10 +00:00
Tom Duijf
3a85bebbf6 pushbullet; styling and minor fixed before PR 2015-11-15 18:57:16 +00:00
Paulus Schoutsen
18f1de10a5 Merge pull request #624 from leoc/feature-zwave-meter-sensor
Add Zwave `meter` command class
2015-11-15 10:16:57 -08:00
Paulus Schoutsen
869d6df65e Merge pull request #618 from nkgilley/camera-fix
add exception handling to generic camera requests function.
2015-11-15 10:02:20 -08:00
Tom Duijf
0a586bd919 Initial commit of pushbullet enhancement 2015-11-15 17:50:36 +00:00
Fabian Affolter
a98b1b0ebc Update link to docs 2015-11-15 18:50:06 +01:00
Paulus Schoutsen
511028612c Merge pull request #593 from leoc/feature-zwave-switches
Implement zwave switches
2015-11-15 09:30:06 -08:00
Paulus Schoutsen
e3efce5ded Merge pull request #622 from happyleavesaoc/s20
s20 switch support
2015-11-15 09:28:40 -08:00
Arthur Andersen
340ee171b5 [Zwave] Add zwave polling interval configuration 2015-11-15 17:50:14 +01:00
Arthur Andersen
773da3f755 [Zwave] Add Meter command class 2015-11-15 17:50:11 +01:00
happyleaves
12bdc39274 don't update state in turn_on/off 2015-11-15 08:59:18 -05:00
Paulus Schoutsen
700b7ba591 Remove unused import in notify 2015-11-15 02:20:35 -08:00
Paulus Schoutsen
295f27d259 Only delete lib dir in config upgrade if exists 2015-11-15 02:16:52 -08:00
Ryan Kraus
4463b69245 Added friendly name to updater component. 2015-11-15 05:15:36 -05:00
Paulus Schoutsen
71e4283a2e Remove lib directory in version upgrade 2015-11-15 02:05:46 -08:00
Paulus Schoutsen
6135b87b1d Log error if unable to install package 2015-11-15 02:05:13 -08:00
Paulus Schoutsen
04bb7ed58f Have Notify platform install platform dependencies 2015-11-15 02:04:57 -08:00
Ryan Kraus
dfa9880176 Created updater component 2015-11-15 05:00:24 -05:00
Paulus Schoutsen
88f3a5a50a Update to new version frontend 2015-11-15 00:51:12 -08:00
Paulus Schoutsen
e2c530b85d Script: new attribute if can cancel 2015-11-14 15:38:07 -08:00
Arthur Andersen
56c5d345a4 [Zwave] Update HA state on value change 2015-11-14 23:14:08 +01:00
happyleaves
86b9ae9566 addressed comments 2015-11-14 16:14:25 -05:00
Nolan Gilley
df264f2ec0 remove unnecessary else 2015-11-14 15:49:39 -05:00
happyleaves
aabcad59d8 rename platform to orvibo 2015-11-14 15:05:22 -05:00
happyleaves
70fef3c5b5 fixed names 2015-11-14 14:25:53 -05:00
happyleaves
57ec58e255 fixed requirements 2015-11-14 14:19:47 -05:00
happyleaves
cf8e23adbc s20 switch support 2015-11-14 14:14:02 -05:00
Paulus Schoutsen
eabf9087f3 Merge pull request #621 from fabaff/cleanup-glances
Glances sensor cleanup
2015-11-14 10:51:57 -08:00
Nolan Gilley
9acb341b96 remove break 2015-11-14 10:51:07 -05:00
Fabian Affolter
5275ca9ce7 Fix typo 2015-11-14 15:25:52 +01:00
Fabian Affolter
646618a25e Improve error messages, use constants, and fix docstrings 2015-11-14 15:23:20 +01:00
Paulus Schoutsen
bc48e4f98e Merge pull request #619 from nkgilley/mqtt-light-color-fix
Remove rgb color if it's not an rgb bulb.
2015-11-14 00:51:18 -08:00
Nolan Gilley
776324807e last PR was dumb. this fix is better. 2015-11-13 14:58:49 -05:00
Nolan Gilley
d68a4b52f1 Remove rgb color if it's not an rgb bulb. 2015-11-13 14:32:47 -05:00
Nolan Gilley
85e0db6ade add exception handling to generic camera requests function. 2015-11-13 13:55:22 -05:00
Fabian Affolter
d993f4014e Add link to docs 2015-11-13 08:29:54 +01:00
Paulus Schoutsen
7ebda9c3c6 Fix MQTT light test 2015-11-12 23:08:26 -08:00
Paulus Schoutsen
16e948d032 Merge branch 'pr/552' into dev 2015-11-12 23:04:05 -08:00
Paulus Schoutsen
41d0f95d9a Move core light test to correct dir 2015-11-12 23:03:56 -08:00
Paulus Schoutsen
bfaaf39e9e Merge pull request #613 from Xorso/squeezebox_fix
Fixing bug when connecting to squeezebox and it is a float
2015-11-12 22:45:03 -08:00
Paulus Schoutsen
16904452b8 Merge pull request #614 from persandstrom/asuswrt_not_loading
ASUSWRT more logging and more robust
2015-11-12 11:43:49 -08:00
Per Sandström
158d9e27ff more robust and more logging 2015-11-12 20:10:25 +01:00
Fabian Affolter
b652dd47cd Update file header and docstrings 2015-11-12 18:04:48 +01:00
Daren Lord
2812fae721 Fixing bug when connecting to squeezebox and it is a float 2015-11-11 16:21:42 -07:00
Tom Duijf
6da88108fe Merge pull request #612 from balloob/dt_snmp_fix
Fix memory issue in SNMP device tracker
2015-11-11 23:30:43 +01:00
Tom Duijf
5503c12cfd Fixes memory consumption issue 2015-11-11 21:54:33 +00:00
hexxter
329d63ac11 next online unittest test ;) 2015-11-11 20:52:41 +01:00
hexxter
698e30bd2b more self.hass.pool.block_till_done() 2015-11-11 20:40:21 +01:00
hexxter
90063ea7f8 check the default value only checkable local. I removed it. 2015-11-11 12:44:59 +01:00
hexxter
0c52b143ae now saved 2015-11-11 12:38:10 +01:00
hexxter
8f12b997f8 more unittests 2015-11-11 12:32:24 +01:00
Arthur Andersen
877926cfee [Zwave] Fix docstring 2015-11-11 10:24:00 +01:00
Arthur Andersen
19649390d3 [Zwave] Add binary switch component 2015-11-11 10:24:00 +01:00
Paulus Schoutsen
50d19bb1b4 Merge pull request #592 from leoc/feature-zwave-lights
Implement zwave light support
2015-11-11 00:11:51 -08:00
Arthur Andersen
665436cd91 [Zwave] Use threading.Timer for value refresh delay 2015-11-10 19:59:45 +01:00
Paulus Schoutsen
1a3410119e Merge pull request #606 from fabaff/pushetta
Pushetta notification platform
2015-11-10 09:27:16 -08:00
Fabian Affolter
bf2bcb6dcf Use _LOGGER.error instead of _LOGGER.exception 2015-11-10 18:20:10 +01:00
Fabian Affolter
8371b08676 Add pushetta notify platform 2015-11-10 14:17:28 +01:00
Fabian Affolter
e8a0d54fdd Add pushetta 2015-11-10 14:17:28 +01:00
Fabian Affolter
f4a82c6f6b Add pushetta 2015-11-10 14:17:28 +01:00
Paulus Schoutsen
963c4bb70e Update frontend with new card style 2015-11-10 00:56:56 -08:00
Paulus Schoutsen
ec2e0cc77d Compile new version frontend 2015-11-10 00:27:41 -08:00
Paulus Schoutsen
0c0ccb361d Merge branch 'dev-tool-info' into dev 2015-11-10 00:25:33 -08:00
Paulus Schoutsen
994fc32f25 Upgrade frontend with about page 2015-11-10 00:25:19 -08:00
Paulus Schoutsen
d68263d5c4 Another LimitlessLED color fix 2015-11-09 21:55:49 -08:00
Paulus Schoutsen
27b001df2b Add dev info to frontend urls 2015-11-09 21:48:36 -08:00
Paulus Schoutsen
dafc0ced6b Update limitlessled with lists for colors 2015-11-09 16:55:10 -08:00
Paulus Schoutsen
3ec2555c66 Merge pull request #601 from balloob/package-install-global
Check global installed packages
2015-11-09 12:45:51 -08:00
Fabian Affolter
bfa8e58879 Update link to docs (Jekyll 3 update) 2015-11-09 18:33:11 +01:00
Paulus Schoutsen
2d9a785c18 Merge pull request #600 from balloob/cleanup-notify
Clean up notifiy component
2015-11-09 07:32:13 -08:00
Fabian Affolter
97f9f8aa49 Update link to docs (Jekyll 3 update) 2015-11-09 13:12:18 +01:00
Fabian Affolter
64d5ca4da0 Add link to docs and update some docstrings 2015-11-09 08:25:46 +01:00
Paulus Schoutsen
4fb301b7a9 Check global installed packages 2015-11-08 22:55:22 -08:00
Paulus Schoutsen
fda65a4934 Update coveragerc 2015-11-08 22:33:43 -08:00
Paulus Schoutsen
98b4c27211 Style fixes 2015-11-08 22:21:02 -08:00
Paulus Schoutsen
3b3f5fe6fe Clean up notifiy component 2015-11-08 22:15:34 -08:00
Paulus Schoutsen
1c9c5ce1bd Merge pull request #599 from ryanturner/dev
Initial implementation of mjpeg camera
2015-11-08 22:13:49 -08:00
Ryan Turner
a36b315927 Fixed indentations hopefully 2015-11-09 00:11:11 -06:00
Ryan Turner
f3352546c6 More lint fixes 2015-11-09 00:00:31 -06:00
Ryan Turner
3a6aa8f3d1 Fixed line length issues to make lint happy. Still bummed that I decreased test coverage :( 2015-11-08 23:51:01 -06:00
Ryan Turner
dfa81b0117 Changed camera.mjpeg to use Response and Closing; cleaned up a number of code-clarity issues near that 2015-11-08 23:41:21 -06:00
Paulus Schoutsen
3947691347 Style fixes + rename honeywell 2015-11-08 20:56:11 -08:00
Ryan Turner
8541fdb112 Fixed style issue related to failing build 2015-11-08 22:26:27 -06:00
Ryan Turner
e078ab53ca Initial implementation of mjpeg camera 2015-11-08 22:15:06 -06:00
Paulus Schoutsen
0665af7f0f Merge branch 'pr/579' into dev
Conflicts:
	requirements_all.txt
2015-11-08 20:10:30 -08:00
Paulus Schoutsen
6dfb8f5737 Merge pull request #591 from balloob/error-log
Expose API to view error log
2015-11-08 20:06:56 -08:00
Paulus Schoutsen
1be2be0886 Merge pull request #590 from balloob/light-rgb
Light: base color now in RGB instead of XY
2015-11-08 20:06:49 -08:00
Paulus Schoutsen
3a095f53a8 Merge pull request #596 from badele/dev
Minor change for logger component
2015-11-08 19:57:58 -08:00
Paulus Schoutsen
ffce252a12 Merge pull request #598 from SEJeff/update-foscam-snapshot
Make a single request to get the foscam camera image
2015-11-08 19:57:21 -08:00
Paulus Schoutsen
7f4c13c382 Upgrade Wink version to v0.1.1 2015-11-08 19:41:22 -08:00
Jeff Schroeder
0f292e8fa6 Remove unused import for re 2015-11-08 20:37:29 -06:00
badele
e63d0c51e0 Change log severity 2015-11-08 19:02:51 +01:00
badele
ebaecdb9d6 Fix flake & pylint 2015-11-08 11:29:56 +01:00
badele
fd50693ca7 Minor change to documentation for logger component 2015-11-08 11:26:36 +01:00
badele
ebc95aca51 Add log info in the rfxtrx component 2015-11-08 11:15:03 +01:00
Jeff Schroeder
137cadb59c Make a single request to get the foscam camera image
This uses the `snapPicture2` command, which is documented in their
cgi sdk to return raw jpeg data instead of html containing the image
2015-11-07 20:18:46 -06:00
Arthur Andersen
84f81480bb [Zwave] Add light zwave component 2015-11-07 15:58:28 +01:00
Arthur Andersen
5565e418f8 [Zwave] Add type and genre to value filter 2015-11-07 15:57:46 +01:00
Arthur Andersen
5b4fc4f346 [Zwave] Add check for missing discovery_service 2015-11-07 15:57:28 +01:00
Paulus Schoutsen
e4c3d47dbf Expose API to view error log 2015-11-07 01:44:02 -08:00
Paulus Schoutsen
95320f39b3 Light: base color now in RGB instead of XY 2015-11-07 01:25:33 -08:00
Paulus Schoutsen
0c97280479 Merge pull request #587 from badele/logfilter
Add logger filter feature
2015-11-06 21:59:56 -08:00
Paulus Schoutsen
3e339c7304 Update vincenty version in setup.py 2015-11-06 21:58:18 -08:00
badele
6f06f48ac6 Ensure the component is loaded first 2015-11-06 22:57:03 +01:00
badele
aeacbad4a0 Fix pull request from balloob comments 2015-11-06 22:51:33 +01:00
sander
f60f3fa4a2 Removed unused self._sensorid. 2015-11-06 08:37:22 +01:00
sander
e49dc94d4b slightly better update method. 2015-11-05 09:58:35 +01:00
sander
26a6438e93 learning the alphabet ;-) 2015-11-05 09:37:05 +01:00
Paulus Schoutsen
cae71a73a1 Update frontend 2015-11-05 00:03:01 -08:00
Paulus Schoutsen
df7f6e1235 Remove alarm sensor from demo platform 2015-11-04 23:53:22 -08:00
badele
a31f7d2816 Fix flake & pylint 2015-11-04 22:08:15 +01:00
badele
c52c982510 Add logger feature 2015-11-04 21:30:02 +01:00
sander
ea06d946e6 modified .converagerc and requirements_all.txt 2015-11-04 21:15:56 +01:00
Paulus Schoutsen
bd798b8c55 Merge pull request #582 from balloob/mdi-icons
Icons for everyone!
2015-11-04 09:08:34 -08:00
Paulus Schoutsen
ac7456b73b Merge pull request #577 from happyleavesaoc/limitlessled_additions
LimitlessLED effects
2015-11-04 09:02:43 -08:00
Fabian Affolter
d69b08ecf5 Update with comments from PR 579 2015-11-03 11:46:03 +01:00
Paulus Schoutsen
be6dd20236 Update frontend with new icons 2015-11-03 00:20:59 -08:00
Paulus Schoutsen
4d069323f4 Add icon support to entity 2015-11-03 00:20:48 -08:00
Paulus Schoutsen
77f4fc8c22 Frontend: Add materialdesignicons 2015-11-03 00:20:20 -08:00
Paulus Schoutsen
72b4212b19 Demo uses device tracker demo platform 2015-11-03 00:19:28 -08:00
Fabian Affolter
5fce381b89 Remove empty point 2015-11-03 08:50:27 +01:00
happyleaves
7b968f6a6b re-fix conditionals 2015-11-02 18:11:58 -05:00
happyleaves
4d958c6d18 style fix 2015-11-02 18:08:17 -05:00
happyleaves
566712023d consolidate conditionals 2015-11-02 18:08:17 -05:00
happyleaves
3cd89f8474 add disco, white effects 2015-11-02 18:08:17 -05:00
Fabian Affolter
218a05356a Add docstrings 2015-11-02 21:00:53 +01:00
hexxter
186f68cce3 not working mqtt light unittest 2015-11-02 20:16:36 +01:00
hexxter
168eb8e5a2 mqtt light test is working more test should be written 2015-11-02 17:02:34 +01:00
Paulus Schoutsen
c6b5a04312 Allow more static files to be fingerprinted 2015-11-02 00:03:53 -08:00
Paulus Schoutsen
728cd8bb5e Upgrade Vincenty to latest version 2015-11-02 00:03:53 -08:00
Stefan Jonasson
d873ab0262 Merge pull request #569 from stefan-jonasson/dev
Fix for Philio Zwave devices which don't send an off event.
2015-11-02 08:15:57 +01:00
Daniel Høyer Iversen
0ff6a460c2 Update rfxtrx.py 2015-11-01 14:20:11 +01:00
Daniel Høyer Iversen
77539a5b89 revert prev commit 2015-11-01 12:51:09 +01:00
Daniel Høyer Iversen
92b05389f2 Update rfxtrx.py 2015-11-01 12:41:21 +01:00
Daniel Høyer Iversen
82aec895a0 Minor bug in rfxtrx 2015-11-01 12:40:41 +01:00
Stefan Jonasson
ec732becfc Fixed the get_config_value method when the zwave node was changed while reading it. 2015-10-31 23:34:19 +01:00
Stefan Jonasson
c4261ae2e0 Z-Wave workaround - Added a default value if we did not get any config value. 2015-10-31 23:03:40 +01:00
Stefan Jonasson
cae8932b18 Z-Wave workaround - Connected to the timeout to the configured node value "9. Turn Off Light Time" 2015-10-31 21:23:33 +01:00
sander
efacd66bec linting and flakeing.. 2015-10-31 20:35:23 +01:00
hexxter
31826ab263 redesigned mqtt light an first steps with the unittest system 2015-10-31 19:26:03 +01:00
Paulus Schoutsen
6bb95f5c9b Merge pull request #560 from pavoni/add-vera-dimmer
Add vera dimmer
2015-10-30 21:47:02 -07:00
Paulus Schoutsen
46761d827f Merge pull request #570 from nkgilley/actiontec-fix
Actiontec device_tracker bugfix and removed home_interval
2015-10-30 21:18:28 -07:00
Paulus Schoutsen
288db9a57f Merge pull request #559 from balloob/light_ct_color
Color temperature support for light component and hue platform
2015-10-30 19:11:24 -07:00
Tom Duijf
b76471c4b3 :( .. pyliny 2015-10-30 19:15:38 +00:00
Tom Duijf
194c6343ac Minor corrections to light and light/demo 2015-10-30 19:01:42 +00:00
Stefan Jonasson
2ad647bb09 Style fixes 2015-10-30 15:30:08 +01:00
Stefan Jonasson
a56173676e Fixed the workaround match logic 2015-10-30 15:28:06 +01:00
Nolan Gilley
e961dd5f95 increase valid for time to 60 since I was having some issues. removed deprecated lines. 2015-10-30 07:00:35 -04:00
pavoni
0269be5813 Update pyvera version 2015-10-30 09:39:30 +00:00
pavoni
031d5ce255 Fix style issues, update pyvera version. 2015-10-30 09:37:16 +00:00
sander
85bb828149 changed requirements to the latest evohome version. 2015-10-29 21:17:10 +01:00
Paulus Schoutsen
c7a0b5800c Update links in introduction component 2015-10-29 00:23:05 -07:00
Tom Duijf
f456d2ff23 styling fix 2015-10-28 23:16:25 +00:00
Tom Duijf
6bad702db4 Renamed to color_temp, removed capabilities (not needed afterall) 2015-10-28 23:12:16 +00:00
Nolan Gilley
bcb2451752 fix pylint warning 2015-10-28 17:47:13 -04:00
Nolan Gilley
8eeca94517 removed home_interval option since it was added to the main device_tracker component 2015-10-28 17:43:41 -04:00
Nolan Gilley
3b37a7b737 bugfix for actiontec device tracker 2015-10-28 17:20:15 -04:00
Stefan Jonasson
d578bf3494 Removed extra pylint hint from a previous merge 2015-10-28 22:17:17 +01:00
Stefan Jonasson
8cb046a4a9 Merge remote-tracking branch 'origin/dev' into dev 2015-10-28 22:16:00 +01:00
Stefan Jonasson
30de5af445 Fix for Philio Zwave devices which don't send an off event. 2015-10-28 22:08:50 +01:00
Stefan Jonasson
f74a6ed4c9 Merge remote-tracking branch 'origin/dev' into dev
Conflicts:
	homeassistant/components/script.py
	homeassistant/helpers/entity_component.py
2015-10-28 21:43:46 +01:00
Stefan Jonasson
f3500542dd Added pylint hint 2015-10-28 21:42:42 +01:00
Paulus Schoutsen
2eb65c8eb8 Version bump to 0.8.0.dev0 2015-10-28 12:45:57 -07:00
Paulus Schoutsen
f8bb807707 Merge pull request #568 from balloob/dev
0.7.7
2015-10-28 12:45:42 -07:00
Paulus Schoutsen
5f40115605 Version bump to 0.7.7 2015-10-28 12:43:04 -07:00
Paulus Schoutsen
776616bcac Merge pull request #562 from pavoni/efergy_error_handling
Log request exceptions in Efergy sensor
2015-10-28 12:41:42 -07:00
Paulus Schoutsen
a98cb798f7 Merge pull request #567 from balloob/fix-script-regression
Fix script regression
2015-10-28 12:37:50 -07:00
Paulus Schoutsen
12495c717e Fix script regression 2015-10-28 12:24:33 -07:00
Stefan Jonasson
10c95b4352 Added pylint hint 2015-10-28 20:17:17 +01:00
Stefan Jonasson
48bfc98acb Fixed entity name 2015-10-28 19:52:09 +01:00
Stefan Jonasson
de027609d8 Fixed entity_id for the script component. Alias now does not override the entity_id
Fixed issue: #561
2015-10-28 12:27:58 +01:00
pavoni
16a3511c0a Catch request exceptions and log a warning 2015-10-28 10:57:10 +00:00
Paulus Schoutsen
0a36c96a55 Fill in service info for thermostat 2015-10-27 19:51:50 -07:00
pavoni
6ef0d089ea Add VeraLight class based on VeraSwitch - add dimmer support 2015-10-27 23:18:46 +00:00
Fabian Affolter
b3b2f2e326 Fix pylint and flake issues 2015-10-28 00:18:03 +01:00
Fabian Affolter
bef0b2b01e Make pins optional 2015-10-27 23:51:16 +01:00
Tom Duijf
805aecd6f9 pylint & flake cleanup 2015-10-27 22:49:45 +00:00
Tom Duijf
e4d33bc6d4 Included ct_color in code coverage 2015-10-27 22:45:35 +00:00
Tom Duijf
e25503bc4a Hue device capabilities. Color temperature support for light component and hue platform 2015-10-27 22:34:49 +00:00
root
4fcd27e905 light/mqtt to .coveragerc 2015-10-27 16:52:43 +01:00
Fabian Affolter
52b1080ccd Catch invalid chat ids 2015-10-27 14:26:44 +01:00
root
0128357024 Merge remote-tracking branch 'upstream/dev' into dev 2015-10-27 07:52:04 +01:00
root
c5f8095f53 Merge remote-tracking branch 'upstream/master' into dev 2015-10-27 07:51:21 +01:00
Paulus Schoutsen
ab48a94d2a Version bump to 0.7.7.dev0 2015-10-26 21:32:07 -07:00
Paulus Schoutsen
27c6c27db6 Merge pull request #554 from balloob/dev
0.7.6-rc1
2015-10-26 21:31:41 -07:00
Paulus Schoutsen
55c0ee6b32 Version bump to 0.7.6 2015-10-26 21:27:50 -07:00
Paulus Schoutsen
6b881ce1cd Merge pull request #547 from krzynio/dev
Add new OpenWRT presence detection routine based on ubus instead of luci
2015-10-26 21:26:13 -07:00
Paulus Schoutsen
9ab9d0e383 Update netdisco requirement 2015-10-26 20:49:20 -07:00
pavoni
dbc05450a0 Bump requirements_all.txt version, remove pylint disable 2015-10-26 16:32:12 +00:00
root
b66e4f1e15 two different demo lights on without RGB and one with RGB support.
and code cleanup more pylint aligned
2015-10-26 15:05:01 +01:00
Krzysztof Koziarek
649124d162 Added ubus.py to .coveragerc 2015-10-26 11:55:20 +01:00
pavoni
49f4d92c62 Add dimmer as switch 2015-10-26 10:51:23 +00:00
Krzysztof Koziarek
fbb73dd5da fixed pylint warnings 2015-10-26 11:50:09 +01:00
Krzysztof Koziarek
c9f1dce6a2 Coding style fixes 2015-10-26 11:32:00 +01:00
pavoni
ef6c209c6f Revise for clarity, disable pylink check 2015-10-26 09:03:57 +00:00
pavoni
0826ae2742 Revise pywemo version, update discovery.device_from_description parameters 2015-10-26 08:37:13 +00:00
Paulus Schoutsen
18747f8ae1 Update some docs 2015-10-25 23:12:10 -07:00
Paulus Schoutsen
06c8c1b168 Update to latest version frontend 2015-10-25 23:12:10 -07:00
Paulus Schoutsen
160fb6fcc8 Merge pull request #553 from balloob/download_relative
Download - relative / absolute path
2015-10-25 21:06:33 -07:00
Paulus Schoutsen
f1aa685cf2 Add version to config API 2015-10-25 21:00:22 -07:00
Paulus Schoutsen
da259d75a2 Update frontend with imprpved charts 2015-10-25 19:01:51 -07:00
Tom Duijf
9104ca815d Indentation 2015-10-26 00:13:47 +00:00
Tom Duijf
52193611cd Check for relative path 2015-10-26 00:10:32 +00:00
Paulus Schoutsen
a2256f6c97 Update version frontend 2015-10-25 15:39:50 -07:00
Paulus Schoutsen
004bad7f00 Merge pull request #551 from balloob/mp_plex_discovery
Media_player/plex discovery
2015-10-25 15:38:42 -07:00
root
a8c2cc4c33 rework for flake8 errors done 2015-10-25 23:38:24 +01:00
root
538f8545f7 fix a bug after the pylint rework 2015-10-25 23:04:43 +01:00
root
7cfce94dfb pylint rework for light/mqtt 2015-10-25 22:58:07 +01:00
Paulus Schoutsen
e490388843 Merge pull request #544 from MakeMeASandwich/hyperion
Hyperion ambilight remote support
2015-10-25 14:34:22 -07:00
Fabian Affolter
fb8edca942 Add link to docs, fix typo, and update log output 2015-10-25 22:21:25 +01:00
root
469d0619ba mqtt light component 2015-10-25 21:48:01 +01:00
Tom Duijf
bc8c5766d4 Logic fixes 2015-10-25 17:54:48 +00:00
Tom Duijf
5b25d9ccd6 flake8,pylint and other code cleanup 2015-10-25 17:00:54 +00:00
Fabian Affolter
0d05930765 Update 2015-10-25 15:58:58 +01:00
Fabian Affolter
f93282d636 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
78ad2686d4 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
3c36d13e8d Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
77ba0c0393 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
6a3316ed12 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
415d650860 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
c3c248bc0a Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
bf027fcd48 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
5c79fc0ae3 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
0aaf280bf5 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
55de563511 Add link to docs 2015-10-25 15:58:58 +01:00
Fabian Affolter
1a018e3ee7 Remove configuration details 2015-10-25 15:58:58 +01:00
Fabian Affolter
b023348795 Add link to docs 2015-10-25 15:58:58 +01:00
Tom Duijf
847d9736aa Configurator works, config saving basic implementation 2015-10-25 13:34:34 +00:00
Tom Duijf
884525df33 Basic discovery works, added plex logo for configurator. Missing configurator support for fields. Todo: config save on successful connect 2015-10-25 13:34:34 +00:00
Tom Duijf
6a82504e5e further discovery integration into plex 2015-10-25 13:34:34 +00:00
Tom Duijf
db7e46abd1 Intermediate save 2015-10-25 13:33:47 +00:00
Tom Duijf
8e9cafd29d Updated requirements_all.txt. Added placeholder to the empty deviceClass filter. Will remove this if deemed unneeded, later 2015-10-25 13:32:15 +00:00
Tom Duijf
a236b87ccf new attempt for PR 2015-10-25 13:32:15 +00:00
MakeMeASandwich
e379e3d902 Merge branch 'dev' of https://github.com/balloob/home-assistant into hyperion 2015-10-25 11:29:02 +01:00
MakeMeASandwich
1be48f54c0 light/hyperion: close sockets, report setup success 2015-10-25 11:08:59 +01:00
Paulus Schoutsen
96181a555a Allow pipes in command sensors and services 2015-10-24 12:40:36 -07:00
Paulus Schoutsen
e461ceae36 discovery: update Netdisco requirement 2015-10-24 12:18:48 -07:00
Paulus Schoutsen
83e6c24f18 Re-enable Z-Wave for Docker 2015-10-24 11:36:34 -07:00
Krzysztof Koziarek
50fbd83b3d corrected flake8 warnings 2015-10-24 11:20:57 +02:00
Fabian Affolter
649275044a Remove configuration details 2015-10-24 09:13:54 +02:00
Fabian Affolter
0f81fc60ad Remove configuration details 2015-10-24 09:10:31 +02:00
Paulus Schoutsen
ade8681511 Exclude rest switch from coverage 2015-10-23 23:44:17 -07:00
Paulus Schoutsen
7a699fd819 Merge pull request #539 from nkgilley/forecastio-units
Forecastio: Added support for specifying unit system in the configuration file.
2015-10-23 22:54:31 -07:00
Paulus Schoutsen
b0c0659acc Merge branch 'clean-up-heat-control' into dev
Conflicts:
	homeassistant/components/thermostat/heat_control.py
2015-10-23 22:51:00 -07:00
Paulus Schoutsen
80a9422a9a Merge pull request #545 from MakeMeASandwich/denon
Refactor denon remote
2015-10-23 22:40:37 -07:00
Fabian Affolter
060cbaf66b Add return value 2015-10-24 00:38:19 +02:00
Fabian Affolter
c19120e012 Check import 2015-10-24 00:34:49 +02:00
Fabian Affolter
1e0e48fcd7 Use logger the same as other platforms do 2015-10-24 00:29:47 +02:00
Fabian Affolter
f2fda2914a Fix continuation 2015-10-24 00:29:02 +02:00
Fabian Affolter
7e3483ab03 Remove configuration details 2015-10-24 00:24:14 +02:00
Fabian Affolter
bffce11a9a Remove configuration details 2015-10-24 00:20:18 +02:00
Fabian Affolter
8a895390ef Merge pull request #534 from bachp/dev
Add simple REST switch
2015-10-23 23:52:40 +02:00
Fabian Affolter
97f81ad7a6 Add more details 2015-10-23 23:48:57 +02:00
Pascal Bach
67d5581a1b Add simple REST switch
The switch can get the state via GET and set the state via POST
on a given REST resource.
It is not able to control arbitrary urls but it allows controlling
switches exposed via "real" REST.
2015-10-23 23:40:14 +02:00
Fabian Affolter
3406b41b0c Fix return value 2015-10-23 23:08:27 +02:00
Fabian Affolter
2e7912157b Remove configuration details 2015-10-23 23:00:20 +02:00
Fabian Affolter
5dbdf82ec7 Fix typo 2015-10-23 22:58:00 +02:00
Fabian Affolter
9f4a3f4aea Use the logger the same way as other platforms do 2015-10-23 22:57:07 +02:00
Fabian Affolter
4f3b3a9e34 Use the logger the same way as other platforms 2015-10-23 22:53:58 +02:00
Fabian Affolter
3ea167203f Remove configuration details 2015-10-23 22:48:30 +02:00
Fabian Affolter
756cbe1b08 Remove configuration details 2015-10-23 22:41:13 +02:00
Fabian Affolter
87e55820e7 Add link docs 2015-10-23 22:39:04 +02:00
Fabian Affolter
f828ee044d UPdate docstring 2015-10-23 22:34:23 +02:00
Fabian Affolter
a8e2f9cbb7 Remove configuration details 2015-10-23 22:34:02 +02:00
Fabian Affolter
2e3f462474 Update docstring 2015-10-23 22:32:36 +02:00
Fabian Affolter
3f6780d9be Remove configuration details 2015-10-23 22:31:37 +02:00
Fabian Affolter
e21921823e Update docstring 2015-10-23 22:29:22 +02:00
Fabian Affolter
f9b2e0058e Fix typo 2015-10-23 19:01:38 +02:00
Fabian Affolter
a155587693 Remove configuration details 2015-10-23 19:01:19 +02:00
Fabian Affolter
0e145ec130 Remove configuration details 2015-10-23 18:39:50 +02:00
Fabian Affolter
44b08a06e7 Remove configuration details 2015-10-23 18:26:36 +02:00
Fabian Affolter
75f737144a Remove configuration details 2015-10-23 18:24:07 +02:00
Fabian Affolter
170742b0a7 Remove configuration details 2015-10-23 18:15:12 +02:00
Fabian Affolter
6115be7c42 Remove configuration details 2015-10-23 18:13:45 +02:00
Fabian Affolter
84a9a300d6 Fix link 2015-10-23 18:13:28 +02:00
Fabian Affolter
55718aac66 Remove configuration details 2015-10-23 18:10:32 +02:00
MakeMeASandwich
b6e6512367 media_player/denon: refactor
* connect only if necessary
* do not throw errors if offline
2015-10-23 17:35:08 +02:00
Krzysztof Koziarek
29c9c5a7ec Add new OpenWRT presence detection routine based on ubus instead of luci 2015-10-23 17:01:42 +02:00
Nolan Gilley
dd787ea5ce remove suggestion for uk unit system. change default to use si or us based on default temperature. added more sensor types. 2015-10-23 10:10:44 -04:00
Paulus Schoutsen
c2d75efb4d Add missing docstring to heat control thermo 2015-10-22 22:14:40 -07:00
Paulus Schoutsen
3d972abdab Clean up the heat control thermostat 2015-10-22 22:04:37 -07:00
Fabian Affolter
7801489149 Remove configuration details 2015-10-21 23:05:54 +02:00
Fabian Affolter
0fda89e983 Remove configuration details 2015-10-21 23:05:38 +02:00
Fabian Affolter
07a75c5eeb Remove configuration details 2015-10-21 23:05:13 +02:00
Fabian Affolter
0b7c407519 Remove configuration details 2015-10-21 22:54:42 +02:00
Fabian Affolter
3c34f3dac2 Remove configuration details 2015-10-21 22:53:26 +02:00
Fabian Affolter
d45074f9dc Remove configuration details 2015-10-21 22:53:09 +02:00
sander
863955e1bd got the basics working 2015-10-21 21:48:21 +02:00
Fabian Affolter
4ff1b0fdb2 Add link to docs 2015-10-21 21:26:16 +02:00
Fabian Affolter
ba13f13442 Remove configuration details 2015-10-21 21:12:25 +02:00
Fabian Affolter
e615755eb9 Remove configuration details 2015-10-21 21:11:51 +02:00
Fabian Affolter
352d3532e7 Remove configuration details 2015-10-21 21:11:18 +02:00
Fabian Affolter
7b60f6ca77 Remove configuration details 2015-10-21 19:43:24 +02:00
Fabian Affolter
a2e8fcbc77 Remove newline 2015-10-21 19:37:34 +02:00
Fabian Affolter
e10fd0d28b Remove configuration details 2015-10-21 19:36:52 +02:00
sander
f376061e23 Revert "had to change to let this work on windows."
This reverts commit 6c106a87f1.
2015-10-21 19:00:23 +02:00
sander
076b3db5e8 first try 2015-10-21 19:00:15 +02:00
Fabian Affolter
89964ad793 Remove configuration details 2015-10-21 16:46:31 +02:00
Fabian Affolter
bddd02bd58 Remove configuration details 2015-10-21 16:45:45 +02:00
Fabian Affolter
cfb3384ee3 Add link to docs 2015-10-21 16:38:33 +02:00
Fabian Affolter
490e9ee95d Add link to docs 2015-10-21 16:37:41 +02:00
Fabian Affolter
0d0eb7e7c0 Add link to docs 2015-10-21 16:10:47 +02:00
Fabian Affolter
3d4af8c229 Remove configuration details 2015-10-21 10:56:32 +02:00
Fabian Affolter
7e23c241da Update docstring 2015-10-21 10:49:25 +02:00
Fabian Affolter
7ec1424825 Add link to docs 2015-10-21 10:47:31 +02:00
Fabian Affolter
9162149598 Add link to docs 2015-10-21 10:47:12 +02:00
Fabian Affolter
da31b54d06 Add link to docs 2015-10-21 10:45:08 +02:00
Fabian Affolter
b20a757454 Remove configuration details 2015-10-21 10:44:29 +02:00
Fabian Affolter
761f225c86 Update link 2015-10-20 22:20:58 +02:00
Fabian Affolter
5580309d98 Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
52b4c3b5a2 Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
aed61cecff Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
ef129639bd Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
f945a3a692 Move configuration details to docs 2015-10-20 22:20:58 +02:00
Fabian Affolter
5e56eae28f Move configuration details to docs 2015-10-20 22:20:58 +02:00
Fabian Affolter
72ad1387f0 Move configuration details to docs 2015-10-20 22:20:58 +02:00
Fabian Affolter
02cfc70ad5 Add link to component 2015-10-20 22:20:58 +02:00
Fabian Affolter
5a21b677a1 Add link to component 2015-10-20 22:20:58 +02:00
Fabian Affolter
c1a73d250a Add link to component 2015-10-20 22:20:58 +02:00
Fabian Affolter
8c544a89c9 Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
c473d426e3 Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
f5a62f8381 Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
0e8e4a73fe Remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
74700e4b10 Add link to doc and remove configuration details 2015-10-20 22:20:58 +02:00
Fabian Affolter
4d5c9581bf Add link to docs 2015-10-20 22:20:58 +02:00
Fabian Affolter
f45e0eabe3 Add link to docs 2015-10-20 22:20:58 +02:00
Fabian Affolter
bbed4a262c Fix typo 2015-10-20 22:20:44 +02:00
Fabian Affolter
f8590f7d1d Include resource in error message 2015-10-20 22:20:44 +02:00
Nolan Gilley
293ed275ab Added support for specifying units in the configuration file. If no units are specified in the config file it will use location to determine the units. 2015-10-20 14:29:22 -04:00
MakeMeASandwich
2e9ee28637 light/hyperion: use RGB, clean code 2015-10-20 17:30:23 +02:00
Paulus Schoutsen
73cb23f599 Merge pull request #535 from toddeye/radiotherm-dev
radiotherm platform bug fix and configuration parameter
2015-10-19 23:28:10 -07:00
Todd Ingarfield
661f4c594e formatting 2015-10-19 16:54:42 -05:00
Todd Ingarfield
9464e2a13b Add hass configuration parameter hold_temp & config documentation 2015-10-19 16:18:45 -05:00
Todd Ingarfield
27d5248937 Fix configuration for multiple hosts and add example configuration.yaml section 2015-10-19 15:33:23 -05:00
MakeMeASandwich
c5a7e3abd1 Merge branch 'dev' of https://github.com/balloob/home-assistant into dev 2015-10-19 20:09:38 +02:00
Paulus Schoutsen
0a6424a81d Merge pull request #529 from persandstrom/lms_pause
bugfix, LMS pause
2015-10-18 13:01:43 -07:00
Per Sandström
5b7389de55 bugfix, 1 = force pause 2015-10-18 21:05:30 +02:00
Paulus Schoutsen
0fe4e0330a Merge pull request #528 from fabaff/arest-sensor
Add pins feature to arest sensor
2015-10-18 10:32:57 -07:00
Fabian Affolter
40b095b866 Add option to use pins 2015-10-18 18:05:25 +02:00
MakeMeASandwich
7141a99927 fix flake warning 2015-10-18 09:10:41 +02:00
Fabian Affolter
f01d2b1263 Allow to overwrite the device name 2015-10-18 00:42:02 +02:00
Fabian Affolter
3c6420c538 Allow to overwrite the device name 2015-10-18 00:41:12 +02:00
MakeMeASandwich
e3304caf06 add hyperion light support 2015-10-17 19:36:52 +02:00
Fabian Affolter
91a1fb0240 Remove wrong file 2015-10-16 09:12:37 +02:00
Paulus Schoutsen
6fd32e83c8 Update .coveragerc 2015-10-15 13:50:06 -07:00
Paulus Schoutsen
b41caa093c Merge pull request #503 from toddeye/radiotherm-platform
Add Radio Thermostat platform
2015-10-15 13:45:52 -07:00
Paulus Schoutsen
806c71c803 Merge pull request #522 from balloob/script-cleanup
Script clean up
2015-10-15 13:44:46 -07:00
Todd Ingarfield
b0bafa32b7 fixed typo in requirements_all.txt 2015-10-15 11:44:19 -05:00
Fabian Affolter
332ac794a4 Modify the import style 2015-10-15 18:22:42 +02:00
Paulus Schoutsen
5dfd0d2502 Fix another manual alarm regression 2015-10-15 08:39:38 -07:00
Todd Ingarfield
3d838c307f merged upstream and fixed conflict 2015-10-15 10:13:02 -05:00
Paulus Schoutsen
2dd77f9477 Merge pull request #523 from fabaff/cpuinfo
CPU Speed sensor
2015-10-15 07:33:18 -07:00
sander
6c106a87f1 had to change to let this work on windows. 2015-10-15 15:02:09 +02:00
Fabian Affolter
1279bf7c68 Add py-cpuinfo 2015-10-15 12:13:35 +02:00
Fabian Affolter
323d301072 Add cpuspeed sensor 2015-10-15 12:13:16 +02:00
Fabian Affolter
b1815075ac Add cpuspeed sensor 2015-10-15 12:13:04 +02:00
Paulus Schoutsen
7ba4263284 Fix regression manual alarm 2015-10-14 23:38:42 -07:00
Paulus Schoutsen
347597ebdc Base Script on entity 2015-10-14 23:15:48 -07:00
Todd Ingarfield
ddeb84cb9c Removed name and netdisco functions, implemented update method to caches values, radiotherm lib to coveragerc and requirements_all.txt 2015-10-14 11:11:33 -05:00
Todd Ingarfield
b2e39884f9 Removed name and netdisco functions, implemented update method to caches values, radiotherm lib to coveragerc and requirements_all.txt 2015-10-14 11:02:07 -05:00
Fabian Affolter
8d99c4a0cc Move configuration details to docs 2015-10-14 10:39:51 +02:00
Fabian Affolter
80e4f2f51f Add link to doc 2015-10-14 10:39:51 +02:00
Fabian Affolter
6064fffc8e Move configuration details to docs 2015-10-14 10:39:51 +02:00
Fabian Affolter
7da354c4c5 Move configuration details to docs 2015-10-14 10:39:51 +02:00
Paulus Schoutsen
49de153ecf Add alarm component to demo component 2015-10-13 23:21:47 -07:00
Paulus Schoutsen
716376081d Add tests for MQTT alarm 2015-10-13 23:08:12 -07:00
Paulus Schoutsen
d37b70556d manual alarm: Test disarm with invalid code 2015-10-13 22:41:35 -07:00
Paulus Schoutsen
32bb950b5f Add tests for manual alarm control panel platform 2015-10-13 22:36:21 -07:00
Fabian Affolter
0e89418cbe Move configuration desciption to docs 2015-10-13 23:44:27 +02:00
Fabian Affolter
fe032be352 Upgrade psutil to 3.2.2 2015-10-13 23:42:27 +02:00
Fabian Affolter
64a78d7b4f Upgrade psutil to 3.2.2 2015-10-13 23:41:46 +02:00
Fabian Affolter
24e4b9e012 Move configuration description to docs 2015-10-13 23:08:56 +02:00
Fabian Affolter
a44a39003d Move configuration description to docs 2015-10-13 23:07:26 +02:00
Fabian Affolter
f019b4f697 Move configuration details to docs 2015-10-13 22:56:12 +02:00
Fabian Affolter
e353dae3a6 Move vonfiguration details to docs 2015-10-13 22:45:36 +02:00
Fabian Affolter
fb84c0ce6b Move configuration details to docs 2015-10-13 22:41:53 +02:00
Fabian Affolter
fd382871a1 Move configuration details to docs 2015-10-13 22:30:21 +02:00
Fabian Affolter
405025a00b Remove configuration details 2015-10-13 22:26:32 +02:00
Fabian Affolter
912ddbb4fc Add link to docs 2015-10-13 22:26:27 +02:00
Fabian Affolter
91138b8679 Move configuration details to docs 2015-10-13 22:16:26 +02:00
Fabian Affolter
893b9fc8ac Move configuration details to docs 2015-10-13 22:08:11 +02:00
Fabian Affolter
185ba000dd Add link to docs 2015-10-13 21:09:11 +02:00
Fabian Affolter
771118caaf Add link to docs 2015-10-13 21:08:51 +02:00
Fabian Affolter
17d9df0da5 Add link to docs 2015-10-13 21:08:34 +02:00
Fabian Affolter
e067398134 Add link to docs 2015-10-13 21:08:18 +02:00
Fabian Affolter
cb69ac30ec Add link to docs 2015-10-13 21:07:53 +02:00
Fabian Affolter
241ff45c5e Add link to docs 2015-10-13 21:07:40 +02:00
Fabian Affolter
c5c2f0c5f3 Add link to docs 2015-10-13 21:07:24 +02:00
Fabian Affolter
0874cb364f Move configuration details to docs 2015-10-13 21:00:44 +02:00
Fabian Affolter
44418b509c Move configuration details to docs 2015-10-13 21:00:28 +02:00
Fabian Affolter
403889bbeb Move configuration details to docs 2015-10-13 20:55:45 +02:00
Fabian Affolter
6ca50d8b5c Move configuration details to docs 2015-10-13 20:55:15 +02:00
Fabian Affolter
0369a9ee0d Move configuration details to docs 2015-10-13 20:54:48 +02:00
Fabian Affolter
5a6ff9a69a Move configuration details to docs 2015-10-13 20:54:15 +02:00
Fabian Affolter
796cce78bc Move configuration details to docs 2015-10-13 20:52:30 +02:00
Fabian Affolter
77430c0687 Move configuration details to docs 2015-10-13 20:52:08 +02:00
Fabian Affolter
c33942d6e2 Move configuration details to docs 2015-10-13 20:51:30 +02:00
Fabian Affolter
3b91f89173 Move configuration details to docs 2015-10-13 20:50:53 +02:00
Fabian Affolter
c74f46794e Move configuration details to docs 2015-10-13 20:50:15 +02:00
Fabian Affolter
966fd8f24d Move configuration details to docs 2015-10-13 20:49:28 +02:00
Fabian Affolter
8253fdfc13 Add newline 2015-10-13 20:49:14 +02:00
Fabian Affolter
cb7b5f8d15 Move configuration details to docs 2015-10-13 20:45:29 +02:00
Fabian Affolter
b0d8eaeda9 Move configuration details to docs 2015-10-13 20:44:18 +02:00
Fabian Affolter
62cfb8aeb2 Move configuration details to docs 2015-10-13 20:42:05 +02:00
Fabian Affolter
47448d1dc0 Add link to docs 2015-10-13 20:40:59 +02:00
Fabian Affolter
a583525110 Move configuration details to docs 2015-10-13 20:01:23 +02:00
Paulus Schoutsen
38e1cef30e Update frontend for style fix 2015-10-13 08:58:15 -07:00
Paulus Schoutsen
e2f187879c Merge pull request #511 from wind-rider/geofancy
Geofancy
2015-10-13 00:05:17 -07:00
Paulus Schoutsen
925cde200f Merge pull request #514 from balloob/scene-turn-off-remove
Remove turning off scenes
2015-10-12 23:43:29 -07:00
Paulus Schoutsen
383efee470 Scene turn off for frontend 2015-10-12 23:40:09 -07:00
Paulus Schoutsen
d5eb90160f Merge pull request #461 from sfam/manual-alarm
Add manual alarm
2015-10-12 23:13:37 -07:00
Paulus Schoutsen
2f946cc6de Merge pull request #516 from mKeRix/dev
Fix for newest tplink firmware, fixes #415
2015-10-12 23:10:30 -07:00
sfam
1b7ce2146c replace sleeps with track_point_in_time 2015-10-13 00:56:24 +00:00
Heiko Rothe
021a374a6a Merge branch 'dev' of https://github.com/mKeRix/home-assistant into dev 2015-10-12 22:44:20 +02:00
Heiko Rothe
bbec34d0e6 Merge remote-tracking branch 'refs/remotes/balloob/dev' into dev 2015-10-12 22:43:55 +02:00
Heiko Rothe
a6cb19b27d Fixed an issue with the initiation of the new attributes 2015-10-12 22:42:45 +02:00
Hans Bakker
b74e70d4e0 Fixes based on balloob's comments 2015-10-12 20:58:24 +02:00
Paulus Schoutsen
5cd283e999 Merge pull request #513 from balloob/component-command
Add shell_command component
2015-10-12 08:43:32 -07:00
Heiko Rothe
2f2bd7a616 Fixed pylint and pep8 violations 2015-10-12 09:18:55 +02:00
Paulus Schoutsen
cddc87b0ab Remove turn off from scene 2015-10-11 23:51:59 -07:00
Paulus Schoutsen
d6bbc67112 Add tests for scene 2015-10-11 23:48:17 -07:00
Paulus Schoutsen
6d77b15e44 Few more tests 2015-10-11 21:41:44 -07:00
Paulus Schoutsen
916c453d2b Add test for shell command 2015-10-11 21:30:17 -07:00
Paulus Schoutsen
7786b52d93 Add shell_command component 2015-10-11 20:11:30 -07:00
Paulus Schoutsen
90d4a2c0b8 Update frontend version 2015-10-11 20:10:32 -07:00
Paulus Schoutsen
b6d26597c0 Automation - state platfor: Flag if user makes config error 2015-10-11 18:30:25 -07:00
Paulus Schoutsen
bf1970b78c Make thermostat more robust 2015-10-11 18:16:55 -07:00
Hans Bakker
1eb3610a11 Style fixes 2015-10-12 00:28:39 +02:00
Paulus Schoutsen
f081f7c4ff Merge pull request #494 from andythigpen/mysensors-update
Update to latest mysensors library.
2015-10-11 15:14:23 -07:00
Hans Bakker
6a969208e9 Initial commit for Geofancy device tracker. 2015-10-12 00:14:05 +02:00
Andrew Thigpen
384b3d0d17 Update to latest mysensors library.
* Adds JSON persistence support.
* Adds documentation comments for configuration options.
2015-10-11 17:05:08 -05:00
Paulus Schoutsen
ad5b650661 Merge pull request #504 from happyleavesaoc/dev
Amazon Fire TV device support
2015-10-11 14:54:55 -07:00
Paulus Schoutsen
b05f2e3221 Fix style issue 2015-10-11 11:04:16 -07:00
Paulus Schoutsen
dcfc91e71c Fix throttle applied to methods 2015-10-11 10:42:42 -07:00
Todd Ingarfield
6c1c243000 start away mode 2015-10-11 11:42:24 -05:00
Paulus Schoutsen
c2117b3eaf Merge pull request #507 from gsabbe/dev
asuswrt gives a traceback when ipv6 is enabled on the router
2015-10-11 09:26:11 -07:00
Todd Ingarfield
84c72ebf63 Add support for multiple thermostats (via hass-config) and auto-discovery via ratiotherm module 2015-10-11 09:28:25 -05:00
Guillaume SABBE
a1e5bea3ab When IPv6 is enabled, dnsmasq has a configuration line with the DUID.
This looks like this
61072 b8:27:eb:e1:4e:4d 192.168.0.4 domotycoon *
61072 b8:27:eb:b8:10:6b 192.168.0.5 pimonitor *
duid 00:03:00:01:ac:22:0b:e9:98:50
When using match.group() without testing if match != None, you get a traceback.
2015-10-11 15:21:53 +02:00
Hans Bakker
bee5c0adfb Merge branch 'dev' of https://github.com/balloob/home-assistant 2015-10-10 23:40:59 +02:00
happyleavesaoc
7ca21f577d fixed merge conflict 2015-10-10 16:53:55 -04:00
happyleavesaoc
168516f5da addressed PR comments 2015-10-10 16:45:13 -04:00
Paulus Schoutsen
94df5acbf3 Version bump to 0.7.6.dev0 2015-10-10 11:45:25 -07:00
Paulus Schoutsen
853a9fd4cd Merge pull request #506 from balloob/dev
0.7.5rc1
2015-10-10 11:45:06 -07:00
Paulus Schoutsen
6a18205d2e Update to version 0.7.5 2015-10-10 11:39:29 -07:00
Paulus Schoutsen
3a3b8bbb45 Fix packaging issues 2015-10-10 10:33:09 -07:00
Todd Ingarfield
37278aab20 add set_time and begin discovery 2015-10-10 11:36:34 -05:00
Fabian Affolter
d3c4722529 Add some other components 2015-10-09 23:45:36 +02:00
Fabian Affolter
c3de67041a Add plex 2015-10-09 23:40:06 +02:00
Fabian Affolter
f07d07432d Add telegram 2015-10-09 23:38:28 +02:00
Fabian Affolter
47f994b867 Move configuration details to docs 2015-10-09 23:33:59 +02:00
Fabian Affolter
a8a172c8b7 Add link to docs and remove configuration details from file header 2015-10-09 23:24:26 +02:00
happyleavesaoc
d4d91bfdbb Amazon Fire TV device support 2015-10-09 17:06:35 -04:00
Fabian Affolter
7432bbd70c Merge pull request #500 from balloob/arest-fix
Throttle per instance (fixes arest)
2015-10-09 22:46:55 +02:00
Todd Ingarfield
a3d295d885 Correct formatting 2015-10-09 11:38:39 -05:00
Todd Ingarfield
0cf909cce9 Correct ci failed tests 2015-10-09 11:34:14 -05:00
Todd Ingarfield
fc1cf49fd3 added REQUIREMENTS for radiotherm python module 2015-10-09 10:49:54 -05:00
Todd Ingarfield
e5d68d8a1e set name of device through hass config 2015-10-09 10:43:14 -05:00
Fabian Affolter
8fc2f5fe36 Update and equalize comments 2015-10-09 17:41:07 +02:00
Paulus Schoutsen
c2c18bdbd5 Merge pull request #501 from fabaff/telegram
Telegram notifications
2015-10-09 07:40:40 -07:00
Fabian Affolter
f8efe3f00f Update link to docs 2015-10-09 14:48:58 +02:00
Fabian Affolter
db53e46705 Add link to docs and remove configuration details 2015-10-09 14:44:59 +02:00
Fabian Affolter
526a163563 Update link 2015-10-09 14:41:35 +02:00
Fabian Affolter
e29f857f43 Update header (docstring) 2015-10-09 14:40:48 +02:00
Fabian Affolter
9f6ce868e2 Add telegram 2015-10-09 14:13:05 +02:00
Fabian Affolter
3ef5e7c161 Add telegram 2015-10-09 14:12:49 +02:00
Fabian Affolter
fe5bb89a68 Add telegram notifier 2015-10-09 14:04:29 +02:00
Paulus Schoutsen
be8089bcde Cleanup arest 2015-10-08 23:50:04 -07:00
Paulus Schoutsen
47fc1deecb Fix throttle to work on instance-level 2015-10-08 23:49:55 -07:00
Paulus Schoutsen
8a04e1f5f4 Device tracker configuration fix
Fixes #498
2015-10-08 22:19:15 -07:00
Paulus Schoutsen
9f33b8f541 DDWRT - match multiple output variants
Fixes #481
2015-10-08 22:15:12 -07:00
Paulus Schoutsen
0624725e21 Ignore nmap style issue - pylint bug 2015-10-08 21:45:51 -07:00
Paulus Schoutsen
dc5f0ef314 NMap: fix hostname resolver
Fixes #482
2015-10-08 21:01:38 -07:00
Paulus Schoutsen
cb2943c247 Merge pull request #499 from balloob/handle-states-for-media-player
Prioritize play_media over state change
2015-10-08 18:07:27 -07:00
Jon Maddox
45f0911640 move play_media to the top so it catches first 2015-10-08 20:37:59 -04:00
Todd Ingarfield
4ac9e9fc4c initial commit 2015-10-08 17:48:03 -05:00
Fabian Affolter
28b107ffa9 Move details from header to docs 2015-10-09 00:27:29 +02:00
Paulus Schoutsen
e0149c4ee4 Merge pull request #488 from balloob/itunes-play-media
iTunes play_media
2015-10-08 12:48:35 -07:00
Paulus Schoutsen
455a5916fd Merge pull request #496 from tomduijf/local_www
Allowing custom/local files (images, etc) to be used in the webinterface
2015-10-08 12:46:23 -07:00
Tom Duijf
cbf94aae55 Merge remote-tracking branch 'upstream/dev' into local_www 2015-10-08 19:32:28 +00:00
Paulus Schoutsen
39ced09727 Merge pull request #493 from tomduijf/dev_tracker_snmp
device_tracker snmp
2015-10-08 12:29:15 -07:00
Tom Duijf
ad417bfdfb Merge remote-tracking branch 'upstream/dev' into local_www 2015-10-08 15:06:01 +00:00
Tom Duijf
f682fd7c1f Merge remote-tracking branch 'upstream/dev' into dev_tracker_snmp 2015-10-08 15:04:15 +00:00
Tom Duijf
ee23c0fe14 cleaner logging 2015-10-08 14:54:20 +00:00
Tom Duijf
5322789c14 Ability to store icons/pictures in config_dir/www for e.g. device_tracker pictures 2015-10-08 14:10:33 +00:00
Paulus Schoutsen
05cec772d0 Merge pull request #495 from kennedyshead/dev
Fix for KeyError in kodi.py
2015-10-08 06:59:26 -07:00
magnusknutas
75c3e42064 Removes log for cleanup 2015-10-08 14:00:23 +02:00
magnusknutas
61c955779b Logging with info 2015-10-08 13:55:01 +02:00
magnusknutas
a015df7b01 Test for media_content_id KeyError 2015-10-08 13:41:58 +02:00
Tom Duijf
721c1d0f54 styling fix for flake 2015-10-08 10:24:55 +00:00
Tom Duijf
fe37a6aecc Merge remote-tracking branch 'upstream/dev' into dev_tracker_snmp 2015-10-08 10:01:24 +00:00
Tom Duijf
85bf6cb568 Added pylint disables 2015-10-08 10:01:10 +00:00
Fabian Affolter
9f10ab5e7a Update logger output 2015-10-08 11:10:05 +02:00
Fabian Affolter
3b7f6d3b67 Update docstrings 2015-10-08 11:09:00 +02:00
Fabian Affolter
d8aefb5d55 Update docstrings 2015-10-08 11:08:47 +02:00
Fabian Affolter
06cac7f9ef Update docstrings 2015-10-08 11:08:32 +02:00
Fabian Affolter
6d3f18d094 Update docstrings 2015-10-08 11:08:17 +02:00
Tom Duijf
050f90d07a merge with upstream 2015-10-08 08:24:38 +00:00
Fabian Affolter
bf9b179441 Update docstrings 2015-10-08 10:23:19 +02:00
Tom Duijf
4f0f7eff5e Merge remote-tracking branch 'upstream/dev' into dev 2015-10-08 08:22:14 +00:00
Tom Duijf
4edbdab4c0 Merge remote-tracking branch 'upstream/master' into dev 2015-10-08 08:09:56 +00:00
Tom Duijf
729f59625e Merge branch 'dev' into dev_tracker_snmp 2015-10-08 08:05:03 +00:00
Tom Duijf
213a1fe4ba Various fixes, CI validation 2015-10-08 08:00:30 +00:00
Paulus Schoutsen
c1899609a4 Merge branch 'pr/483' into dev
Conflicts:
	.coveragerc
2015-10-08 00:28:52 -07:00
Paulus Schoutsen
1b4ef3856a Merge pull request #471 from alanbowman/blinkstick_support
[WIP] Add blinkstick support
2015-10-08 00:05:42 -07:00
Paulus Schoutsen
4673a82c90 Merge pull request #490 from CCOSTAN/patch-3
Added # comment for Sensor
2015-10-07 23:52:45 -07:00
Tom Duijf
ae6f651c7d styling and version for requirement 2015-10-07 23:22:29 +00:00
Tom Duijf
7cb0f805ee fixed loop 2015-10-07 22:17:49 +00:00
Tom Duijf
d556e5979a Updated misc files and code styling 2015-10-07 21:45:24 +00:00
Fabian Affolter
d149f9d64c Update doc string (Fix #491) 2015-10-07 23:28:56 +02:00
Tom Duijf
9377b647f5 removed debug logging 2015-10-07 21:05:27 +00:00
Tom Duijf
469f35d25f various fixes, initial working version 2015-10-07 21:04:34 +00:00
Carlo Costanzo
17865c78c4 Added # comment for Sensor
Comments for unique sensor labels.
2015-10-07 17:02:07 -04:00
badele
a5dae78155 Refactoring the rfxtrx components 2015-10-07 19:57:40 +02:00
badele
46f5ef54a1 Refactoring test instance type 2015-10-07 19:15:50 +02:00
badele
496e4cf784 Exclude rfxtrx component files 2015-10-07 19:07:19 +02:00
badele
11fc521e60 Replace REQUIREMENTS by DEPENDENCIES variable 2015-10-07 19:04:03 +02:00
Tom Duijf
a58382e763 Fixed b/octet to mac adress conversion 2015-10-07 16:57:01 +00:00
Alan Bowman
9d4aa7e519 Update tests for RGB color support 2015-10-07 13:58:21 +01:00
Jon Maddox
ffbaf0cd5a simpler 2015-10-07 02:13:13 -04:00
Jon Maddox
3b58e8628d style 2015-10-07 02:02:25 -04:00
Jon Maddox
c2fe977778 style 2015-10-07 01:55:15 -04:00
Jon Maddox
85338887b4 wrap it 2015-10-07 01:42:50 -04:00
Jon Maddox
9a3c76c263 these are required 2015-10-07 01:41:57 -04:00
Jon Maddox
6ab4b80486 Merge branch 'dev' into itunes-play-media 2015-10-07 01:41:21 -04:00
Paulus Schoutsen
5e0a4c316f Merge pull request #487 from balloob/media-player-play-media
Media Player play_media function
2015-10-06 22:40:36 -07:00
Jon Maddox
26939ce554 style 2015-10-07 01:37:40 -04:00
Jon Maddox
c83324d4cf nope 2015-10-07 01:34:37 -04:00
Jon Maddox
dbcc3a76ea style 2015-10-07 01:29:55 -04:00
Jon Maddox
faa3e98921 module level play_media 2015-10-07 01:28:58 -04:00
Jon Maddox
1c4ac6017d fix typo while were in here 2015-10-07 01:21:41 -04:00
Jon Maddox
25a690691b import it from the right place 2015-10-07 01:11:19 -04:00
Jon Maddox
bb997deb85 COMMMMAAAAAAAAAAAA 2015-10-07 01:06:27 -04:00
Jon Maddox
6c4b2fd638 derp 2015-10-07 01:01:25 -04:00
Jon Maddox
c4f8017a3f silence warning 2015-10-07 00:56:36 -04:00
Jon Maddox
6afb846d04 avoid key errors 2015-10-07 00:56:14 -04:00
Jon Maddox
ad549be353 support play_media for state restoration (for scenes) 2015-10-07 00:39:38 -04:00
Jon Maddox
9012ba53fd add play_media service to tests 2015-10-06 23:18:24 -04:00
Jon Maddox
bdb42bf4a2 support play_media 2015-10-06 23:12:48 -04:00
Jon Maddox
1b22f71a19 implement play_media 2015-10-06 23:12:41 -04:00
Jon Maddox
e84ddb036f return what playlist is playing 2015-10-06 23:12:30 -04:00
Jon Maddox
4be33bb15b add a way to play a playlist with the client 2015-10-06 23:12:20 -04:00
Jon Maddox
d17174d43d play_media as a service 2015-10-06 23:11:21 -04:00
Jon Maddox
e64846e2fd add ability to support play_media 2015-10-06 23:11:09 -04:00
Jon Maddox
d454cad5a6 add a play_media function 2015-10-06 23:10:39 -04:00
Jon Maddox
dcf52332ca add new properties for Channel or Playlist 2015-10-06 23:09:53 -04:00
Jon Maddox
87599df41b add some new media types 2015-10-06 23:00:29 -04:00
Tom Duijf
e535f50e03 Merge branch 'master' into dev_tracker_snmp 2015-10-06 21:30:36 +00:00
Tom Duijf
df7fbf664e Added constants needed for snmp 2015-10-06 21:27:04 +00:00
Tom Duijf
0fb9e1b16c Initial commit of snmp device tracker 2015-10-06 21:26:32 +00:00
Alan Bowman
047cff6596 Add blinkstick support 2015-10-06 11:10:16 +01:00
badele
32f1791c5a Check flake & pylint style 2015-10-06 08:44:15 +02:00
Paulus Schoutsen
3b49d1e876 Update version to 0.7.5dev0 2015-10-05 22:31:21 -07:00
Paulus Schoutsen
4d1dce2519 Merge branch 'dev' 2015-10-05 22:18:45 -07:00
Paulus Schoutsen
01d097b9b0 Bump version to 0.7.4 2015-10-05 22:18:34 -07:00
Alan Bowman
6d53944fa1 Support RGB colors 2015-10-05 13:25:09 +01:00
Paulus Schoutsen
7f60f1e662 Merge pull request #478 from balloob/dev
0.7.4rc1
2015-10-04 11:30:36 -07:00
Paulus Schoutsen
bc6c285945 Update zone doc 2015-10-04 01:40:38 -07:00
Paulus Schoutsen
d4d8c9ae65 Update frontend version 2015-10-04 01:40:38 -07:00
Paulus Schoutsen
9292891836 Update documentation 2015-10-04 01:40:38 -07:00
Paulus Schoutsen
035df68d6c Merge pull request #479 from balloob/balloob-patch-1
Tweak caching on CI
2015-10-03 11:41:14 -07:00
Paulus Schoutsen
c611be96ad Another try, caching is enabled before activating virtualenv 2015-10-03 11:36:39 -07:00
Paulus Schoutsen
d46720ee2c Tweak caching on CI 2015-10-03 11:31:28 -07:00
Paulus Schoutsen
c1f464f478 Fix style issue 2015-10-03 11:26:57 -07:00
Paulus Schoutsen
8c5759e460 Improve Logbook device tracker locations handling 2015-10-03 11:20:22 -07:00
Paulus Schoutsen
8490d6126a OwnTracks robustness improvement 2015-10-03 10:29:00 -07:00
badele
7f71706f08 Log RFXCOM events 2015-10-03 11:26:18 +02:00
Paulus Schoutsen
8b5b580287 Merge pull request #477 from fabaff/worldclock
Worldclock sensor
2015-10-03 00:03:14 -07:00
Paulus Schoutsen
d35f5b9f97 Tests for MQTT sensor/switch 2015-10-02 23:57:26 -07:00
Paulus Schoutsen
6de64d7695 Cache pip in Travis 2015-10-02 16:49:10 -07:00
Fabian Affolter
7f1da8b7bc Add worldclock sensor 2015-10-02 23:49:32 +02:00
Fabian Affolter
58ac4be24c Add worldclock sensor 2015-10-02 23:49:00 +02:00
Paulus Schoutsen
b2919c6504 Fix gps accuracy issue 2015-10-02 13:49:55 -07:00
badele
db509ccf18 Add a light & switch rfxtrx sender capability 2015-10-02 22:39:30 +02:00
Paulus Schoutsen
3863d2985a Merge pull request #475 from toddeye/group-state-openclose
Add STATE_OPEN/STATE_CLOSED to groupable states
2015-10-02 09:30:27 -07:00
Paulus Schoutsen
0180c056e1 Add away mode to heat control 2015-10-02 08:57:38 -07:00
Paulus Schoutsen
e6cd9a6dc7 Merge pull request #459 from auchter/limitlessled-white
Add support for white LimitlessLED devices and multiple bridges
2015-10-02 08:55:34 -07:00
Todd Ingarfield
33028dd143 Add STATE_OPEN/STATE_CLOSED to groupable states 2015-10-02 10:53:36 -05:00
Paulus Schoutsen
9bdfa89b7c More robust geofence checking 2015-10-02 08:16:53 -07:00
Paulus Schoutsen
42b80868d4 Update netdisco dependency 2015-10-02 06:48:46 -07:00
Fabian Affolter
707ca4b752 Update docstrings 2015-10-02 13:42:06 +02:00
Fabian Affolter
c7d2a09097 Update docstring 2015-10-02 13:41:51 +02:00
Fabian Affolter
fb9f83f8ad Update docstrings 2015-10-02 13:17:18 +02:00
Michael Auchter
52ebb2fb3b limitlessled: Add support for White Limitless LED bulbs
LimitlessLED bulbs actually come in three flavors: RGB, RGBW, and White. The
ledcontroller library used to control these bulbs only supports RGBW and White
bulbs. This changelist adds support for the White bulb variant.

The White bulbs are a bit annoying in that they don't support absolute
brightness or color temperature adjustments; they only support a relative
"increase" or "decrease" adjustment. This, along with the unreliable, one-way
communication medium that requires repeats to be "sure" that the bulb received a
command, makes implementing brightness control difficult. So, for now, these
bulbs are more limited than the RGBW variants and only support On/Off control.
2015-10-01 22:38:50 -05:00
Michael Auchter
ea7ca48ba2 limitlessled: Add support for previous configuration format
Quick hack that preserves functionality of existing configuration formats to
ease upgrades.
2015-10-01 22:38:50 -05:00
Michael Auchter
ab80af099c limitlessled: Add support for multiple bridges
This adds support for a controlling multiple Limitless LED bridges.
2015-10-01 22:38:50 -05:00
Michael Auchter
34531895a0 limitlessled: Use LedControllerPool
This change is in preparation for adapting this component to support multiple
LimitlessLED bridges. Ultimately LedControllerPool helps to maintain the
mandatory 100ms pauses across multiple controllers so messages are reliably
received.
2015-10-01 22:38:50 -05:00
Michael Auchter
645cd89406 limitlessled: fix docstring 2015-10-01 22:38:49 -05:00
Paulus Schoutsen
cc5217d818 Merge pull request #463 from adrienbrault/plex
Finish plex implementation
2015-10-01 18:30:24 -07:00
Adrien Brault
e454806669 Finish plex implementation 2015-10-01 21:14:29 +02:00
Paulus Schoutsen
726557b2f6 Sensor.rest: verify SSL by default 2015-09-30 23:17:08 -07:00
Paulus Schoutsen
c7e22e6910 Merge pull request #467 from adrienbrault/sensor-path-nossl
Allow to skip ssl and specify variable path for rest sensor
2015-09-30 23:14:24 -07:00
Adrien Brault
f66a020bfc Allow to skip ssl and specify variable path for rest sensor 2015-10-01 05:54:31 +02:00
Paulus Schoutsen
64a73f6b67 Update pywemo dependency 2015-09-30 00:12:00 -07:00
Paulus Schoutsen
ad7f034805 MQTT: Auto provide cloudmqtt cert 2015-09-30 00:09:35 -07:00
Paulus Schoutsen
76674d4de9 MQTT: Allow certificates 2015-09-30 00:09:07 -07:00
Paulus Schoutsen
0dc9f2a9f8 Move MQTT to own folder 2015-09-29 23:55:16 -07:00
Paulus Schoutsen
ce47b58a8b Report MQTT connect issues 2015-09-29 23:34:17 -07:00
Paulus Schoutsen
5d71d5560e update rpi_gpio comment 2015-09-29 23:11:32 -07:00
Paulus Schoutsen
1dc9bfdf73 Update config zones 2015-09-29 23:08:37 -07:00
badele
cc47e39006 Add send capability 2015-09-29 22:47:22 +02:00
Paulus Schoutsen
2eb36c18bd Add geofencing to automation 2015-09-29 00:18:52 -07:00
badele
d64f0ddd41 Refactoring the code for pylint & flake test 2015-09-29 08:20:25 +02:00
Paulus Schoutsen
5ad27d8cdb Add support for zones to Home Assistant 2015-09-28 23:13:13 -07:00
Paulus Schoutsen
68c2b539ee More flexible domain config extraction 2015-09-28 23:09:05 -07:00
sfam
e57b3ae847 add manual alarm 2015-09-28 23:36:46 +00:00
Paulus Schoutsen
755234369d New frontend build 2015-09-27 22:05:03 -07:00
Paulus Schoutsen
0a34e8de02 Fix services.yaml in packaging 2015-09-27 21:56:15 -07:00
Paulus Schoutsen
52ed25fc21 Merge pull request #394 from pavoni/hue-scenes
Fuzzy match for float attributes for Philips Hue scenes
2015-09-27 21:41:59 -07:00
Paulus Schoutsen
9e866680d4 Merge pull request #456 from balloob/service-fields
Service fields
2015-09-27 21:07:48 -07:00
pavoni
80c89d218b Avoid throwing an exception when a wemo device with attributes isn't found 2015-09-27 21:05:45 -07:00
Paulus Schoutsen
4f1bf7b2bf Merge pull request #443 from CCOSTAN/patch-2
Added some additional examples.
2015-09-27 20:48:01 -07:00
Paulus Schoutsen
e557e355db Merge pull request #458 from AnthemisFoundry/fix_vera_dict
Fix Vera bug
2015-09-27 18:26:47 -07:00
badele
174aeacd76 Fix duplicate devices insertion 2015-09-27 23:51:19 +02:00
pavoni
e7320fe969 Default dict if parent class returned None 2015-09-27 17:06:49 +01:00
badele
321a603bfe Add a light & switch rfxtrx support 2015-09-27 11:13:49 +02:00
Paulus Schoutsen
4e3bd5f2a9 Add service descriptions 2015-09-26 23:17:04 -07:00
Paulus Schoutsen
9a6b2c1831 Add utf-8 encoding to const file 2015-09-26 06:57:22 -07:00
sfam
ca0b6ebd99 Merge pull request #397 from sfam/dev
Add MQTT alarm
2015-09-25 23:55:47 +01:00
sfam
47cd0b20a0 Merge branch 'persandstrom-sfam-dev' into dev 2015-09-25 17:02:37 +00:00
sfam
98d051f870 Merge branch 'sfam-dev' of https://github.com/persandstrom/home-assistant into persandstrom-sfam-dev 2015-09-25 17:02:20 +00:00
Carlo Costanzo
5f98705100 Changed the automation example
Changed the automation example to match the examples on the website.
2015-09-25 11:03:16 -04:00
pavoni
63bf4db969 Remove trace 2015-09-25 15:51:09 +01:00
pavoni
3ec00ce4fe Fix format errors 2015-09-25 15:49:56 +01:00
Paulus Schoutsen
74a0e47ba6 Update frontend with badge fixes 2015-09-25 07:44:58 -07:00
pavoni
476e4f0517 Add doc strings 2015-09-25 13:37:47 +01:00
pavoni
61fb8271e5 Change scene matching to use fuzzy logic for float values, if requested 2015-09-25 13:26:43 +01:00
Per Sandström
5cf9bd7223 updates to support ui 2015-09-25 06:23:04 +02:00
Paulus Schoutsen
9f986c55e6 Merge pull request #435 from toddeye/notify-smtp-retry
Added retry logic if the SMTP connection is disconnected by the server.
2015-09-24 17:15:05 -07:00
sfam
94eb54ff00 Merge branch 'dev' of https://github.com/balloob/home-assistant into dev 2015-09-24 21:19:21 +00:00
Per Sandström
f28b392f1a Merge branch 'dev' of https://github.com/sfam/home-assistant into sfam-dev 2015-09-24 22:21:51 +02:00
Carlo Costanzo
fa71d5fac9 Update configuration.yaml.example 2015-09-24 15:57:23 -04:00
Carlo Costanzo
21fd53b05d Added some additional examples.
- Added an eample of Groups within Groups.
- Took away the Automation 2 and used the new -Alias format.
- Added a second sensor to demonstrate sensor:, sensor 2: format.
2015-09-24 15:56:26 -04:00
Todd Ingarfield
b0b3c2f73f formatting correction 2015-09-24 11:20:25 -05:00
Paulus Schoutsen
d660d2b3dc Update frontend (group toggle updates) 2015-09-24 09:04:22 -07:00
Todd Ingarfield
a89bfcf342 removed exception attributes 2015-09-24 10:55:24 -05:00
Todd Ingarfield
a42347e6e7 corrected formating and style issues 2015-09-24 10:47:19 -05:00
Paulus Schoutsen
faee3e8447 Merge pull request #360 from fabaff/rest-sensor
Rest sensor
2015-09-23 23:53:09 -07:00
Paulus Schoutsen
3625646c34 Fix reproduce_state 2015-09-23 23:35:08 -07:00
Paulus Schoutsen
5a562f3db8 Update frontend 2015-09-23 23:32:41 -07:00
Paulus Schoutsen
19705ab40a Hide auto groups from logbook 2015-09-23 23:20:20 -07:00
Paulus Schoutsen
20bf9f7ea1 Update frontend with group toggle 2015-09-23 23:20:20 -07:00
Paulus Schoutsen
6399c873f9 Add gps location to device tracker demo 2015-09-23 23:20:20 -07:00
Paulus Schoutsen
4be1053f1c Merge pull request #437 from balloob/handle-play-states
Support media_player Play States When Resolving/Reproducing State
2015-09-23 22:52:38 -07:00
Jon Maddox
efdd0c9e8a don't break the chain 2015-09-24 01:35:08 -04:00
Jon Maddox
8d42e42230 style 2015-09-24 00:38:18 -04:00
Paulus Schoutsen
4b6878f91c Restrict data from stream API 2015-09-23 21:35:23 -07:00
Jon Maddox
90f35b35cd moar derp 2015-09-24 00:20:17 -04:00
Jon Maddox
082920abe0 moar constants 2015-09-24 00:20:05 -04:00
Jon Maddox
4a8bbc52e0 derp 2015-09-24 00:15:36 -04:00
Jon Maddox
1674c8309a Support playing, pausing states for media players when reproducing state
This allows the state helper to call the correct service call for
media_players when attempting to resolve state.
2015-09-24 00:06:05 -04:00
Paulus Schoutsen
62f016e7d2 Filter api password from arguments 2015-09-23 20:56:34 -07:00
Stefan Jonasson
34e5ecb8ab Merge pull request #433 from stefan-jonasson/fix_telldus_libary_cleanup
Telldus libary version update + added callback cleanup
2015-09-23 12:15:31 +02:00
Stefan Jonasson
8f95885e3a Codestyle cleanup 2015-09-23 11:47:53 +02:00
Stefan Jonasson
94db1ac142 Codestyle cleanup 2015-09-23 11:46:55 +02:00
Stefan Jonasson
f48e65096a Removed logging. 2015-09-23 11:38:47 +02:00
Stefan Jonasson
3244975489 Removed logging. 2015-09-23 11:37:45 +02:00
Stefan Jonasson
10327795e9 Added more logging. 2015-09-23 11:34:20 +02:00
Stefan Jonasson
bcbb8edd59 Added more logging. 2015-09-23 11:30:46 +02:00
Stefan Jonasson
86270e1a37 Added more logging. 2015-09-23 11:27:25 +02:00
Stefan Jonasson
de7a34b648 Added more logging. 2015-09-23 11:25:08 +02:00
Stefan Jonasson
82a06279de Added more logging. 2015-09-23 11:22:32 +02:00
Stefan Jonasson
62af1fcc57 Added more logging. 2015-09-23 11:19:27 +02:00
Stefan Jonasson
6afe99dcc7 Added more logging. 2015-09-23 11:14:47 +02:00
Stefan Jonasson
b6bf398859 Added callback logging. 2015-09-23 11:07:37 +02:00
Stefan Jonasson
48df06d1c0 Added callback logging. 2015-09-23 10:18:45 +02:00
Stefan Jonasson
b4ca691822 Removed the check for callback_dispatcher 2015-09-23 09:52:58 +02:00
Stefan Jonasson
16c2827465 Removed the check for callback_dispatcher 2015-09-23 09:50:12 +02:00
Stefan Jonasson
e90fd3d654 Removed the check for callback_dispatcher 2015-09-23 09:43:16 +02:00
Stefan Jonasson
7d0ff6884c Added the req consts 2015-09-23 09:32:11 +02:00
Stefan Jonasson
a9ea8972dd Updated required tellcore version 2015-09-23 08:29:57 +02:00
Stefan Jonasson
a0c1202ad6 Try to make the connection to the tellcore library more stable 2015-09-23 08:26:40 +02:00
Fabian Affolter
1bf45c8f33 Merge branch 'rest-sensor' of github.com:fabaff/home-assistant into rest-sensor 2015-09-23 01:25:53 +02:00
Fabian Affolter
c5094438de Add post option, correction_factor, and decimal_places 2015-09-23 01:17:28 +02:00
Fabian Affolter
60d45ebf79 Add return value 2015-09-23 01:17:28 +02:00
Fabian Affolter
5df2a1cf76 Add new checks and move var check to setup 2015-09-23 01:17:28 +02:00
Fabian Affolter
f5b2fa6fbe Remove left-over 2015-09-23 01:17:28 +02:00
Fabian Affolter
03b2ced24e Add rest sensor 2015-09-23 01:17:28 +02:00
Fabian Affolter
6c18f264f3 Add rest sensor 2015-09-23 01:17:28 +02:00
sfam
cdc371c3ee merge requires_code and code_format properties 2015-09-22 21:40:45 +00:00
Heiko Rothe
1553844279 Added support for the newest tp-link firmware
Currently this seemingly only applies to the Archer C9
2015-09-22 22:48:43 +02:00
Paulus Schoutsen
826b3be087 Update coveragerc 2015-09-22 13:25:18 -07:00
Paulus Schoutsen
f5000d401b Merge pull request #430 from CCOSTAN/patch-1
Minor comment about weather components & LONG:LAT.
2015-09-22 13:11:24 -07:00
Carlo Costanzo
7443f4faf8 Minor comment about weather components & LONG:LAT.
Super Small edit adding in a # about the weather related information.
2015-09-22 15:53:16 -04:00
Heiko Rothe
582ed1fc8d Merge remote-tracking branch 'balloob/dev' into dev 2015-09-22 19:48:39 +02:00
Paulus Schoutsen
3158db9553 Update tile provider 2015-09-21 23:19:42 -07:00
Paulus Schoutsen
46a0173e31 Add demo device tracker platform 2015-09-21 22:46:08 -07:00
Paulus Schoutsen
7e511bcacf Fix iPhone map issues 2015-09-21 22:23:17 -07:00
Paulus Schoutsen
d7fd2ccdaf Merge pull request #316 from fabaff/systemd
Systemd service unit file
2015-09-21 19:45:12 -07:00
Todd Ingarfield
b2999ae325 Added retry logic if the SMTP connection is disconnected by the server. 2015-09-21 18:54:30 -05:00
Per Sandström
5033c1fcb7 Merge branch 'dev' of https://github.com/sfam/home-assistant into sfam-dev 2015-09-21 21:18:46 +02:00
Jeff Schroeder
e492be299b Merge pull request #417 from miniconfig/plex-dev
Plex media player component
2015-09-21 11:44:33 -05:00
miniconfig
03e7281406 Moved plexapi import into setup_platform().
Changed CONTRIBUTING.md to refer to requirements_all.txt instead of requirements.txt
2015-09-21 11:59:55 -04:00
miniconfig
16d75b2981 Added plexapi library to requirements_all.txt 2015-09-21 11:45:52 -04:00
miniconfig
cc7784889a Pylint errors 2015-09-21 11:11:38 -04:00
miniconfig
d267f0a04c Removed references to the frontend device parameter in the directions and added some clarification.
Fixed plexapi version number.
2015-09-21 10:59:34 -04:00
miniconfig
a8e0ca6d3f Fixed various property methods to make sure they all had a fall through return and removed unnecessary "else" statements 2015-09-21 10:44:24 -04:00
Stefan Jonasson
f8175adbdc Merge pull request #420 from stefan-jonasson/dev
Fixed Pylint issue
2015-09-21 13:03:16 +02:00
Stefan Jonasson
6437f6f6b4 Desperate try to fix travis ci reporting a unused-argument 2015-09-21 12:57:11 +02:00
Paulus Schoutsen
27bbfbae62 Fix compilation issue frontend 2015-09-21 00:42:23 -07:00
Stefan Jonasson
2785c373fb E302 expected 2 blank lines, found 1 2015-09-21 08:26:14 +02:00
Paulus Schoutsen
acddae3747 Initial support for maps in frontend 2015-09-20 23:14:58 -07:00
Stefan Jonasson
d3e9a22759 Added pylint hint! 2015-09-21 08:14:11 +02:00
Paulus Schoutsen
ca698ff063 remove debug statement 2015-09-20 20:24:31 -07:00
Paulus Schoutsen
a866d515f7 Make owntracks more robust 2015-09-20 20:09:53 -07:00
Paulus Schoutsen
3af4f267b3 Lint script would incorrectly report success 2015-09-20 19:56:10 -07:00
Paulus Schoutsen
bd61555698 discovery: Update to netdisco 0.4.1 2015-09-20 19:46:33 -07:00
miniconfig
5027acfda1 Fixed additional pylint and flake issues 2015-09-20 16:13:26 -04:00
stefan-jonasson
f0991d63d1 Merge pull request #416 from stefan-jonasson/dev
Fix states not updating after command was sent!
2015-09-20 22:00:12 +02:00
Stefan Jonasson
34f36479c6 Fix states not updating after command was sent! 2015-09-20 21:29:38 +02:00
Paulus Schoutsen
506c88dbaf Fix owntracks bugs 2015-09-20 12:13:51 -07:00
Paulus Schoutsen
98a1addc18 Merge pull request #413 from balloob/owntracks
initial owntracks support
2015-09-20 12:00:11 -07:00
Paulus Schoutsen
30492cc685 Fix tests and linting 2015-09-20 11:46:01 -07:00
Paulus Schoutsen
0d09e2e1df Attempt to fix CI scripts 2015-09-20 11:00:35 -07:00
Paulus Schoutsen
81085c7467 Merge pull request #414 from stefan-jonasson/telldus_callback_fix
Telldus callback fix
2015-09-20 10:25:34 -07:00
Paulus Schoutsen
19d40612e6 Add home_range to device tracker 2015-09-20 09:35:03 -07:00
miniconfig
48306ddbf6 Fixed Requirements URL 2015-09-20 08:19:21 -04:00
Stefan Jonasson
a60a9202a5 cleanup 2015-09-20 14:17:32 +02:00
Stefan Jonasson
ab81231e6d Changed flow so we got one callback per platorm instead of per device which caused race conditions in the telldus library. 2015-09-20 14:11:42 +02:00
Paulus Schoutsen
68286dcef8 initial owntracks support 2015-09-20 00:27:50 -07:00
Paulus Schoutsen
6e96f915f6 Merge pull request #411 from stefan-jonasson/tellstick_callbacks
Fix for issue: #204
2015-09-19 22:54:48 -07:00
Paulus Schoutsen
620a7eadf4 Add release script 2015-09-19 21:33:24 -07:00
Paulus Schoutsen
2332548cf4 Update version to 0.7.4dev 2015-09-19 21:19:45 -07:00
Stefan Jonasson
60d8266ce0 Fix for issue:
Tellstick switches status changes aren't realtime #204
2015-09-20 00:57:04 +02:00
sfam
e29deb0202 add a code_format property on alarm object and a optional code for its MQTT platform 2015-09-19 22:22:37 +00:00
miniconfig
64741a95b8 Added requirements 2015-09-19 14:16:57 -04:00
miniconfig
a24b38aacc Initial version of plex media player component 2015-09-19 13:48:45 -04:00
sfam
35eed93443 add a requires_code property on alarm object 2015-09-19 17:32:37 +00:00
sfam
fc946da5db Add MQTT alarm 2015-09-18 15:30:34 +00:00
pavoni
6abaebb248 More consistant naming 2015-09-18 14:40:00 +01:00
pavoni
c01e9bea2b Fix inconsistant naming 2015-09-18 14:38:15 +01:00
Fabian Affolter
5ce4ade737 Add user and change config dir 2015-09-18 14:18:49 +02:00
Fabian Affolter
9ce8f385d2 Rename service unit file 2015-09-18 14:15:42 +02:00
Fabian Affolter
d7464aea86 Initial systemd service unit file 2015-09-18 14:14:10 +02:00
Fabian Affolter
071952462c initial systemd service unit file 2015-09-18 13:49:46 +02:00
pavoni
ab79b8a541 First cut of write after set for scenes 2015-09-18 12:34:24 +01:00
pavoni
3a3374ed4b Remove incorrect change 2015-09-18 12:34:02 +01:00
pavoni
4d53fa0173 First draft of read_after_set for scenes 2015-09-18 12:21:08 +01:00
pavoni
5369d8c61c Merge remote-tracking branch 'upstream/dev' into dev 2015-09-18 08:51:54 +01:00
pavoni
408f0cff78 Merge remote-tracking branch 'upstream/dev' into dev 2015-09-15 14:30:01 +01:00
Fabian Affolter
b0441aadc4 Add new checks and move var check to setup 2015-09-14 10:06:40 +02:00
Fabian Affolter
7e066e11ad Remove left-over 2015-09-14 08:55:20 +02:00
Fabian Affolter
246184507c Add rest sensor 2015-09-13 22:41:37 +02:00
Fabian Affolter
3f3b475d76 Add rest sensor 2015-09-13 22:41:16 +02:00
Paulus Schoutsen
6e458114f4 Bump version 0.7.2 2015-09-09 19:38:04 -07:00
Paulus Schoutsen
73797dad2d Merge pull request #341 from balloob/dev
0.7.2rc1
2015-09-09 19:36:47 -07:00
238 changed files with 12167 additions and 6800 deletions

View File

@@ -26,6 +26,9 @@ omit =
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
homeassistant/components/rfxtrx.py
homeassistant/components/*/rfxtrx.py
homeassistant/components/ifttt.py
homeassistant/components/browser.py
homeassistant/components/camera/*
@@ -33,43 +36,56 @@ omit =
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/geofancy.py
homeassistant/components/device_tracker/luci.py
homeassistant/components/device_tracker/ubus.py
homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/device_tracker/owntracks.py
homeassistant/components/device_tracker/thomson.py
homeassistant/components/device_tracker/tomato.py
homeassistant/components/device_tracker/tplink.py
homeassistant/components/device_tracker/snmp.py
homeassistant/components/discovery.py
homeassistant/components/downloader.py
homeassistant/components/keyboard.py
homeassistant/components/light/hue.py
homeassistant/components/light/mqtt.py
homeassistant/components/light/limitlessled.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/hyperion.py
homeassistant/components/media_player/cast.py
homeassistant/components/media_player/denon.py
homeassistant/components/media_player/firetv.py
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/mpd.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/sonos.py
homeassistant/components/notify/file.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/slack.py
homeassistant/components/notify/smtp.py
homeassistant/components/notify/syslog.py
homeassistant/components/notify/telegram.py
homeassistant/components/notify/xmpp.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/bitcoin.py
homeassistant/components/sensor/command_sensor.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/dht.py
homeassistant/components/sensor/efergy.py
homeassistant/components/sensor/forecast.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/mysensors.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/rfxtrx.py
homeassistant/components/switch/orvibo.py
homeassistant/components/sensor/rest.py
homeassistant/components/sensor/rpi_gpio.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/sensor/swiss_public_transport.py
@@ -77,14 +93,18 @@ omit =
homeassistant/components/sensor/temper.py
homeassistant/components/sensor/time_date.py
homeassistant/components/sensor/transmission.py
homeassistant/components/sensor/worldclock.py
homeassistant/components/switch/arest.py
homeassistant/components/switch/command_switch.py
homeassistant/components/switch/edimax.py
homeassistant/components/switch/hikvisioncam.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/rpi_gpio.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wemo.py
homeassistant/components/thermostat/honeywell.py
homeassistant/components/thermostat/nest.py
homeassistant/components/thermostat/radiotherm.py
[report]

View File

@@ -1,5 +1,8 @@
sudo: false
language: python
cache:
directories:
- $HOME/virtualenv/python3.4.2/
python:
- "3.4"
install:

View File

@@ -17,19 +17,19 @@ For help on building your component, please see the [developer documentation](ht
After you finish adding support for your device:
- Update the supported devices in the `README.md` file.
- Add any new dependencies to `requirements.txt`.
- Update the `.coveragerc` file.
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io).
- Make sure all your code passes Pylint and flake8 (PEP8 and some more) validation. To generate reports, run `pylint homeassistant > pylint.txt` and `flake8 homeassistant --exclude bower_components,external > flake8.txt`.
- Add a link to the website of your device/service/component in the "examples" listing of the `README.md` file.
- Add any new dependencies to `requirements_all.txt` if needed. There is no ordering right now, so just add it to the end of the file.
- Update the `.coveragerc` file to exclude your platform if there are no tests available.
- 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`.
- 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/).
If you've added a component:
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:
- Update the file [`home-assistant-icons.html`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/frontend/www_static/polymer/resources/home-assistant-icons.html) with an icon for your domain ([pick one from this list](https://www.polymer-project.org/1.0/components/core-elements/demo.html#core-icon)).
- Update the demo component with two states that it provides
- Add your component to home-assistant.conf.example
- Update the demo component with two states that it provides.
- Add your component to `home-assistant.conf.example`.
Since you've updated `home-assistant-icons.html`, you've made changes to the frontend:

View File

@@ -10,11 +10,10 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Open Z-Wave disabled because broken
#RUN apt-get update && \
# apt-get install -y cython3 libudev-dev && \
# apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
# pip3 install cython && \
# scripts/build_python_openzwave
RUN apt-get update && \
apt-get install -y cython3 libudev-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
pip3 install "cython<0.23" && \
script/build_python_openzwave
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]

View File

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

View File

@@ -16,10 +16,11 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat
Examples of devices it can interface it:
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), and [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/)
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Kodi (XBMC)](http://kodi.tv/), and iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api))
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv))
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
* Interaction with [IFTTT](https://ifttt.com/)
* Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org).
* [See full list of supported devices](https://home-assistant.io/components/)
@@ -29,8 +30,8 @@ Built home automation on top of your devices:
* Turn on the lights when people get home after sun set
* Turn on lights slowly during sun set to compensate for less light
* Turn off all lights and devices when everybody leaves the house
* Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects
* Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), and [Jabber (XMPP)](http://xmpp.org)
* Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects like [OwnTracks](http://owntracks.org/)
* Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), [Telegram](https://telegram.org/), and [Jabber (XMPP)](http://xmpp.org)
The system is built modular so support for other devices or actions can be implemented easily. See also the [section on architecture](https://home-assistant.io/developers/architecture.html) and the [section on creating your own components](https://home-assistant.io/developers/creating_components.html).

View File

@@ -1,7 +1,9 @@
homeassistant:
# Omitted values in this section will be auto detected using freegeoip.net
# Location required to calculate the time the sun rises and sets
# Location required to calculate the time the sun rises and sets.
# Cooridinates are also used for location for weather related components.
# Google Maps can be used to determine more precise GPS cooridinates.
latitude: 32.87336
longitude: 117.22743
@@ -68,11 +70,18 @@ device_sun_light_trigger:
# A comma separated list of states that have to be tracked as a single group
# Grouped states should share the same type of states (ON/OFF or HOME/NOT_HOME)
# You can also have groups within groups.
group:
Home:
- group.living_room
- group.kitchen
living_room:
- light.Bowl
- light.Ceiling
- light.TV_back_light
kitchen:
- light.fan_bulb_1
- light.fan_bulb_2
children:
- device_tracker.child_1
- device_tracker.child_2
@@ -94,28 +103,39 @@ browser:
keyboard:
automation:
platform: state
alias: Sun starts shining
- alias: 'Rule 1 Light on in the evening'
trigger:
- platform: sun
event: sunset
offset: "-01:00:00"
- platform: state
entity_id: group.all_devices
state: home
condition:
- platform: state
entity_id: group.all_devices
state: home
- platform: time
after: "16:00:00"
before: "23:00:00"
action:
service: homeassistant.turn_on
entity_id: group.living_room
state_entity_id: sun.sun
# Next two are optional, omit to match all
state_from: below_horizon
state_to: above_horizon
- alias: 'Rule 2 - Away Mode'
execute_service: light.turn_off
service_entity_id: group.living_room
trigger:
- platform: state
entity_id: group.all_devices
state: 'not_home'
automation 2:
platform: time
alias: Beer o Clock
condition: use_trigger_values
action:
service: light.turn_off
entity_id: group.all_lights
time_hours: 16
time_minutes: 0
time_seconds: 0
execute_service: notify.notify
service_data:
message: It's 4, time for beer!
# Sensors need to be added into the configuration.yaml as sensor:, sensor 2:, sensor 3:, etc.
# Each sensor label should be unique or your sensors might not load correctly.
sensor:
platform: systemmonitor
@@ -135,6 +155,23 @@ sensor:
- type: 'process'
arg: 'octave-cli'
sensor 2:
platform: forecast
api_key: <register on Forecast.io for your PRIVATE API>
monitored_conditions:
- summary
- precip_type
- precip_intensity
- temperature
- dew_point
- wind_speed
- wind_bearing
- cloud_cover
- humidity
- pressure
- visibility
- ozone
script:
# Turns on the bedroom lights and then the living room lights 1 minute later
wakeup:

View File

@@ -9,11 +9,12 @@ After bootstrapping you can add your own components or
start by calling homeassistant.start_home_assistant(bus)
"""
import os
import sys
from collections import defaultdict
import logging
import logging.handlers
from collections import defaultdict
import os
import shutil
import sys
import homeassistant.core as core
import homeassistant.util.dt as date_util
@@ -25,7 +26,7 @@ import homeassistant.components as core_components
import homeassistant.components.group as group
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
__version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, CONF_CUSTOMIZE,
TEMP_CELCIUS, TEMP_FAHRENHEIT)
@@ -34,6 +35,7 @@ _LOGGER = logging.getLogger(__name__)
ATTR_COMPONENT = 'component'
PLATFORM_FORMAT = '{}.{}'
ERROR_LOG_FILENAME = 'home-assistant.log'
def setup_component(hass, domain, config=None):
@@ -167,6 +169,7 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
hass.config.config_dir = config_dir
mount_local_lib_path(config_dir)
process_ha_config_upgrade(hass)
process_ha_core_config(hass, config.get(core.DOMAIN, {}))
if enable_log:
@@ -186,8 +189,8 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
dict, {key: value or {} for key, value in config.items()})
# Filter out the repeating and common config section [homeassistant]
components = (key for key in config.keys()
if ' ' not in key and key != core.DOMAIN)
components = set(key.split(' ')[0] for key in config.keys()
if key != core.DOMAIN)
if not core_components.setup(hass, config):
_LOGGER.error('Home Assistant core failed to initialize. '
@@ -252,7 +255,7 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
"Colorlog package not found, console coloring disabled")
# Log errors to a file if we have write access to file or config dir
err_log_path = hass.config.path('home-assistant.log')
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
err_path_exists = os.path.isfile(err_log_path)
# Check if we can write to the error log if it exists or that
@@ -280,6 +283,31 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
'Unable to setup error log %s (access denied)', err_log_path)
def process_ha_config_upgrade(hass):
""" Upgrade config if necessary. """
version_path = hass.config.path('.HA_VERSION')
try:
with open(version_path, 'rt') as inp:
conf_version = inp.readline().strip()
except FileNotFoundError:
# Last version to not have this file
conf_version = '0.7.7'
if conf_version == __version__:
return
_LOGGER.info('Upgrading config directory from %s to %s', conf_version,
__version__)
lib_path = hass.config.path('lib')
if os.path.isdir(lib_path):
shutil.rmtree(lib_path)
with open(version_path, 'wt') as outp:
outp.write(__version__)
def process_ha_core_config(hass, config):
""" Processes the [homeassistant] section from the config. """
hac = hass.config
@@ -297,11 +325,15 @@ def process_ha_core_config(hass, config):
else:
_LOGGER.error('Received invalid time zone %s', time_zone_str)
for key, attr in ((CONF_LATITUDE, 'latitude'),
(CONF_LONGITUDE, 'longitude'),
(CONF_NAME, 'location_name')):
for key, attr, typ in ((CONF_LATITUDE, 'latitude', float),
(CONF_LONGITUDE, 'longitude', float),
(CONF_NAME, 'location_name', str)):
if key in config:
setattr(hac, attr, config[key])
try:
setattr(hac, attr, typ(config[key]))
except ValueError:
_LOGGER.error('Received invalid %s value for %s: %s',
typ.__name__, key, attr)
set_time_zone(config.get(CONF_TIME_ZONE))

View File

@@ -1,7 +1,6 @@
"""
homeassistant.components
~~~~~~~~~~~~~~~~~~~~~~~~
This package contains components that can be plugged into Home Assistant.
Component design guidelines:
@@ -12,7 +11,6 @@ Each component that tracks states should create state entity names in the
format "<DOMAIN>.<OBJECT_ID>".
Each component should publish services only under its own domain.
"""
import itertools as it
import logging

View File

@@ -1,15 +1,18 @@
"""
homeassistant.components.alarm_control_panel
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with a alarm control panel.
"""
import logging
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
import os
from homeassistant.components import verisure
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
DOMAIN = 'alarm_control_panel'
DEPENDENCIES = []
@@ -26,12 +29,15 @@ SERVICE_TO_METHOD = {
SERVICE_ALARM_DISARM: 'alarm_disarm',
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
}
ATTR_CODE = 'code'
ATTR_CODE_FORMAT = 'code_format'
ATTR_TO_PROPERTY = [
ATTR_CODE,
ATTR_CODE_FORMAT
]
@@ -48,61 +54,98 @@ def setup(hass, config):
target_alarms = component.extract_from_service(service)
if ATTR_CODE not in service.data:
return
code = service.data[ATTR_CODE]
code = None
else:
code = service.data[ATTR_CODE]
method = SERVICE_TO_METHOD[service.service]
for alarm in target_alarms:
getattr(alarm, method)(code)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, alarm_service_handler)
hass.services.register(DOMAIN, service, alarm_service_handler,
descriptions.get(service))
return True
def alarm_disarm(hass, code, entity_id=None):
def alarm_disarm(hass, code=None, entity_id=None):
""" Send the alarm the command for disarm. """
data = {ATTR_CODE: code}
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
def alarm_arm_home(hass, code, entity_id=None):
def alarm_arm_home(hass, code=None, entity_id=None):
""" Send the alarm the command for arm home. """
data = {ATTR_CODE: code}
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
def alarm_arm_away(hass, code, entity_id=None):
def alarm_arm_away(hass, code=None, entity_id=None):
""" Send the alarm the command for arm away. """
data = {ATTR_CODE: code}
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
def alarm_trigger(hass, code=None, entity_id=None):
""" Send the alarm the command for trigger. """
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data)
# pylint: disable=no-self-use
class AlarmControlPanel(Entity):
""" ABC for alarm control devices. """
def alarm_disarm(self, code):
@property
def code_format(self):
""" regex for code format or None if no code is required. """
return None
def alarm_disarm(self, code=None):
""" Send disarm command. """
raise NotImplementedError()
def alarm_arm_home(self, code):
def alarm_arm_home(self, code=None):
""" Send arm home command. """
raise NotImplementedError()
def alarm_arm_away(self, code):
def alarm_arm_away(self, code=None):
""" Send arm away command. """
raise NotImplementedError()
def alarm_trigger(self, code=None):
""" Send alarm trigger command. """
raise NotImplementedError()
@property
def state_attributes(self):
""" Return the state attributes. """
state_attr = {
ATTR_CODE_FORMAT: self.code_format,
}
return state_attr

View File

@@ -0,0 +1,149 @@
"""
homeassistant.components.alarm_control_panel.manual
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
DEFAULT_ALARM_NAME = 'HA Alarm'
DEFAULT_PENDING_TIME = 60
DEFAULT_TRIGGER_TIME = 120
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the manual alarm platform. """
add_devices([ManualAlarm(
hass,
config.get('name', DEFAULT_ALARM_NAME),
config.get('code'),
config.get('pending_time', DEFAULT_PENDING_TIME),
config.get('trigger_time', DEFAULT_TRIGGER_TIME),
)])
# pylint: disable=too-many-arguments, too-many-instance-attributes
# pylint: disable=abstract-method
class ManualAlarm(alarm.AlarmControlPanel):
"""
Represents an alarm status.
When armed, will be pending for 'pending_time', after that armed.
When triggered, will be pending for 'trigger_time'. After that will be
triggered for 'trigger_time', after that we return to disarmed.
"""
def __init__(self, hass, name, code, pending_time, trigger_time):
self._state = STATE_ALARM_DISARMED
self._hass = hass
self._name = name
self._code = str(code) if code else None
self._pending_time = datetime.timedelta(seconds=pending_time)
self._trigger_time = datetime.timedelta(seconds=trigger_time)
self._state_ts = None
@property
def should_poll(self):
""" No polling needed. """
return False
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def state(self):
""" Returns the state of the device. """
if self._state in (STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY) and \
self._pending_time and self._state_ts + self._pending_time > \
dt_util.utcnow():
return STATE_ALARM_PENDING
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
if self._state_ts + self._pending_time > dt_util.utcnow():
return STATE_ALARM_PENDING
elif (self._state_ts + self._pending_time +
self._trigger_time) < dt_util.utcnow():
return STATE_ALARM_DISARMED
return self._state
@property
def code_format(self):
""" One or more characters. """
return None if self._code is None else '.+'
def alarm_disarm(self, code=None):
""" Send disarm command. """
if not self._validate_code(code, STATE_ALARM_DISARMED):
return
self._state = STATE_ALARM_DISARMED
self._state_ts = dt_util.utcnow()
self.update_ha_state()
def alarm_arm_home(self, code=None):
""" Send arm home command. """
if not self._validate_code(code, STATE_ALARM_ARMED_HOME):
return
self._state = STATE_ALARM_ARMED_HOME
self._state_ts = dt_util.utcnow()
self.update_ha_state()
if self._pending_time:
track_point_in_time(
self._hass, self.update_ha_state,
self._state_ts + self._pending_time)
def alarm_arm_away(self, code=None):
""" Send arm away command. """
if not self._validate_code(code, STATE_ALARM_ARMED_AWAY):
return
self._state = STATE_ALARM_ARMED_AWAY
self._state_ts = dt_util.utcnow()
self.update_ha_state()
if self._pending_time:
track_point_in_time(
self._hass, self.update_ha_state,
self._state_ts + self._pending_time)
def alarm_trigger(self, code=None):
""" Send alarm trigger command. No code needed. """
self._state = STATE_ALARM_TRIGGERED
self._state_ts = dt_util.utcnow()
self.update_ha_state()
if self._trigger_time:
track_point_in_time(
self._hass, self.update_ha_state,
self._state_ts + self._pending_time)
track_point_in_time(
self._hass, self.update_ha_state,
self._state_ts + self._pending_time + self._trigger_time)
def _validate_code(self, code, state):
""" Validate given code. """
check = self._code is None or code == self._code
if not check:
_LOGGER.warning('Invalid code given for %s', state)
return check

View File

@@ -0,0 +1,127 @@
"""
homeassistant.components.alarm_control_panel.mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This platform enables the possibility to control a MQTT alarm.
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
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "MQTT Alarm"
DEFAULT_QOS = 0
DEFAULT_PAYLOAD_DISARM = "DISARM"
DEFAULT_PAYLOAD_ARM_HOME = "ARM_HOME"
DEFAULT_PAYLOAD_ARM_AWAY = "ARM_AWAY"
DEPENDENCIES = ['mqtt']
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the MQTT platform. """
if config.get('state_topic') is None:
_LOGGER.error("Missing required variable: state_topic")
return False
if config.get('command_topic') is None:
_LOGGER.error("Missing required variable: command_topic")
return False
add_devices([MqttAlarm(
hass,
config.get('name', DEFAULT_NAME),
config.get('state_topic'),
config.get('command_topic'),
config.get('qos', DEFAULT_QOS),
config.get('payload_disarm', DEFAULT_PAYLOAD_DISARM),
config.get('payload_arm_home', DEFAULT_PAYLOAD_ARM_HOME),
config.get('payload_arm_away', DEFAULT_PAYLOAD_ARM_AWAY),
config.get('code'))])
# pylint: disable=too-many-arguments, too-many-instance-attributes
# pylint: disable=abstract-method
class MqttAlarm(alarm.AlarmControlPanel):
""" represents a MQTT alarm status within home assistant. """
def __init__(self, hass, name, state_topic, command_topic, qos,
payload_disarm, payload_arm_home, payload_arm_away, code):
self._state = STATE_UNKNOWN
self._hass = hass
self._name = name
self._state_topic = state_topic
self._command_topic = command_topic
self._qos = qos
self._payload_disarm = payload_disarm
self._payload_arm_home = payload_arm_home
self._payload_arm_away = payload_arm_away
self._code = str(code) if code else None
def message_received(topic, payload, qos):
""" A new MQTT message has been received. """
if payload not in (STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED):
_LOGGER.warning('Received unexpected payload: %s', payload)
return
self._state = payload
self.update_ha_state()
mqtt.subscribe(hass, self._state_topic, message_received, self._qos)
@property
def should_poll(self):
""" No polling needed """
return False
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def state(self):
""" Returns the state of the device. """
return self._state
@property
def code_format(self):
""" One or more characters if code is defined """
return None if self._code is None else '.+'
def alarm_disarm(self, code=None):
""" Send disarm command. """
if not self._validate_code(code, 'disarming'):
return
mqtt.publish(self.hass, self._command_topic,
self._payload_disarm, self._qos)
def alarm_arm_home(self, code=None):
""" Send arm home command. """
if not self._validate_code(code, 'arming home'):
return
mqtt.publish(self.hass, self._command_topic,
self._payload_arm_home, self._qos)
def alarm_arm_away(self, code=None):
""" Send arm away command. """
if not self._validate_code(code, 'arming away'):
return
mqtt.publish(self.hass, self._command_topic,
self._payload_arm_away, self._qos)
def _validate_code(self, code, state):
""" Validate given code. """
check = self._code is None or code == self._code
if not check:
_LOGGER.warning('Wrong code entered for %s', state)
return check

View File

@@ -1,7 +1,10 @@
"""
homeassistant.components.alarm_control_panel.verisure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Interfaces with Verisure alarm control panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/verisure/
"""
import logging
@@ -33,8 +36,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(alarms)
# pylint: disable=abstract-method
class VerisureAlarm(alarm.AlarmControlPanel):
""" represents a Verisure alarm status within home assistant. """
""" Represents a Verisure alarm status. """
def __init__(self, alarm_status):
self._id = alarm_status.id
@@ -51,8 +55,13 @@ class VerisureAlarm(alarm.AlarmControlPanel):
""" Returns the state of the device. """
return self._state
@property
def code_format(self):
""" Four digit code required. """
return '^\\d{4}$'
def update(self):
''' update alarm status '''
""" Update alarm status """
verisure.update()
if verisure.STATUS[self._device][self._id].status == 'unarmed':
@@ -66,21 +75,21 @@ class VerisureAlarm(alarm.AlarmControlPanel):
'Unknown alarm state %s',
verisure.STATUS[self._device][self._id].status)
def alarm_disarm(self, code):
def alarm_disarm(self, code=None):
""" Send disarm command. """
verisure.MY_PAGES.set_alarm_status(
code,
verisure.MY_PAGES.ALARM_DISARMED)
_LOGGER.warning('disarming')
def alarm_arm_home(self, code):
def alarm_arm_home(self, code=None):
""" Send arm home command. """
verisure.MY_PAGES.set_alarm_status(
code,
verisure.MY_PAGES.ALARM_ARMED_HOME)
_LOGGER.warning('arming home')
def alarm_arm_away(self, code):
def alarm_arm_away(self, code=None):
""" Send arm away command. """
verisure.MY_PAGES.set_alarm_status(
code,

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.api
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides a 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
@@ -12,13 +14,14 @@ import json
import homeassistant.core as ha
from homeassistant.helpers.state import TrackStates
import homeassistant.remote as rem
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_CONFIG, URL_API_BOOTSTRAP, URL_API_ERROR_LOG,
EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL,
HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND,
HTTP_UNPROCESSABLE_ENTITY)
HTTP_UNPROCESSABLE_ENTITY, CONTENT_TYPE_TEXT_PLAIN)
DOMAIN = 'api'
@@ -87,6 +90,9 @@ def setup(hass, config):
hass.http.register_path(
'GET', URL_API_COMPONENTS, _handle_get_api_components)
hass.http.register_path('GET', URL_API_ERROR_LOG,
_handle_get_api_error_log)
return True
@@ -103,6 +109,10 @@ def _handle_get_api_stream(handler, path_match, data):
write_lock = threading.Lock()
block = threading.Event()
restrict = data.get('restrict')
if restrict:
restrict = restrict.split(',')
def write_message(payload):
""" Writes a message to the output. """
with write_lock:
@@ -118,7 +128,8 @@ def _handle_get_api_stream(handler, path_match, data):
""" Forwards events to the open request. """
nonlocal gracefully_closed
if block.is_set() or event.event_type == EVENT_TIME_CHANGED:
if block.is_set() or event.event_type == EVENT_TIME_CHANGED or \
restrict and event.event_type not in restrict:
return
elif event.event_type == EVENT_HOMEASSISTANT_STOP:
gracefully_closed = True
@@ -334,6 +345,13 @@ def _handle_get_api_components(handler, path_match, data):
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. """
error_path = handler.server.hass.config.path(ERROR_LOG_FILENAME)
with open(error_path, 'rb') as error_log:
handler.write_file_pointer(CONTENT_TYPE_TEXT_PLAIN, error_log)
def _services_json(hass):
""" Generate services data to JSONify. """
return [{"domain": key, "services": value}

View File

@@ -4,26 +4,8 @@ components.arduino
Arduino component that connects to a directly attached Arduino board which
runs with the Firmata firmware.
Configuration:
To use the Arduino board you will need to add something like the following
to your configuration.yaml file.
arduino:
port: /dev/ttyACM0
Variables:
port
*Required
The port where is your board connected to your Home Assistant system.
If you are using an original Arduino the port will be named ttyACM*. The exact
number can be determined with 'ls /dev/ttyACM*' or check your 'dmesg'/
'journalctl -f' output. Keep in mind that Arduino clones are often using a
different name for the port (e.g. '/dev/ttyUSB*').
A word of caution: The Arduino is not storing states. This means that with
every initialization the pins are set to off/low.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/arduino/
"""
import logging

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to setup simple automation rules via the config file.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/automation/
"""
import logging

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation.event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers event listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#event-trigger
"""
import logging

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation.mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers MQTT listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#mqtt-trigger
"""
import logging

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation.numeric_state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
"""
import logging

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation.state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers state listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#state-trigger
"""
import logging
@@ -28,6 +30,11 @@ def trigger(hass, config, action):
from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
if isinstance(from_state, bool) or isinstance(to_state, bool):
logging.getLogger(__name__).error(
'Config error. Surround to/from values with quotes.')
return False
def state_automation_listener(entity, from_s, to_s):
""" Listens for state changes and calls action. """
action()

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation.sun
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers sun based automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#sun-trigger
"""
import logging
from datetime import timedelta

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.automation.time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers time listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#time-trigger
"""
import logging

View File

@@ -0,0 +1,87 @@
"""
homeassistant.components.automation.zone
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers zone automation rules.
For more details about this automation rule, please refer to the documentation
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)
CONF_ENTITY_ID = "entity_id"
CONF_ZONE = "zone"
CONF_EVENT = "event"
EVENT_ENTER = "enter"
EVENT_LEAVE = "leave"
DEFAULT_EVENT = EVENT_ENTER
def trigger(hass, config, action):
""" Listen for state changes based on `config`. """
entity_id = config.get(CONF_ENTITY_ID)
zone_entity_id = config.get(CONF_ZONE)
if entity_id is None or zone_entity_id is None:
logging.getLogger(__name__).error(
"Missing trigger configuration key %s or %s", CONF_ENTITY_ID,
CONF_ZONE)
return False
event = config.get(CONF_EVENT, DEFAULT_EVENT)
def zone_automation_listener(entity, from_s, to_s):
""" Listens for state changes and calls action. """
if from_s and None in (from_s.attributes.get(ATTR_LATITUDE),
from_s.attributes.get(ATTR_LONGITUDE)) or \
None in (to_s.attributes.get(ATTR_LATITUDE),
to_s.attributes.get(ATTR_LONGITUDE)):
return
from_match = _in_zone(hass, zone_entity_id, from_s) if from_s else None
to_match = _in_zone(hass, zone_entity_id, to_s)
if event == EVENT_ENTER and not from_match and to_match or \
event == EVENT_LEAVE and from_match and not to_match:
action()
track_state_change(
hass, entity_id, zone_automation_listener, MATCH_ALL, MATCH_ALL)
return True
def if_action(hass, config):
""" Wraps action method with zone based condition. """
entity_id = config.get(CONF_ENTITY_ID)
zone_entity_id = config.get(CONF_ZONE)
if entity_id is None or zone_entity_id is None:
logging.getLogger(__name__).error(
"Missing condition configuration key %s or %s", CONF_ENTITY_ID,
CONF_ZONE)
return False
def if_in_zone():
""" Test if condition. """
return _in_zone(hass, zone_entity_id, hass.states.get(entity_id))
return if_in_zone
def _in_zone(hass, zone_entity_id, state):
""" Check if state is in zone. """
if not state or None in (state.attributes.get(ATTR_LATITUDE),
state.attributes.get(ATTR_LONGITUDE)):
return False
zone_state = hass.states.get(zone_entity_id)
return zone_state and zone.in_zone(
zone_state, state.attributes.get(ATTR_LATITUDE),
state.attributes.get(ATTR_LONGITUDE),
state.attributes.get(ATTR_GPS_ACCURACY, 0))

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.browser
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to launch a webbrowser on the host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/browser/
"""
DOMAIN = "browser"

View File

@@ -4,24 +4,8 @@ homeassistant.components.camera
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with various cameras.
The following features are supported:
- Returning recorded camera images and streams
- Proxying image requests via HA for external access
- Converting a still image url into a live video stream
Upcoming features
- Recording
- Snapshot
- Motion Detection Recording(for supported cameras)
- Automatic Configuration(for supported cameras)
- Creation of child entities for supported functions
- Collating motion event images passed via FTP into time based events
- A service for calling camera functions
- Camera movement(panning)
- Zoom
- Light/Nightvision toggling
- Support for more devices
- Expanded documentation
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera/
"""
import requests
import logging
@@ -103,7 +87,10 @@ def setup(hass, config):
if camera:
response = camera.camera_image()
handler.wfile.write(response)
if response is not None:
handler.wfile.write(response)
else:
handler.send_response(HTTP_NOT_FOUND)
else:
handler.send_response(HTTP_NOT_FOUND)
@@ -145,7 +132,8 @@ def setup(hass, config):
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',

View File

@@ -3,51 +3,14 @@ homeassistant.components.camera.foscam
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This component provides basic support for Foscam IP cameras.
As part of the basic support the following features will be provided:
-MJPEG video streaming
To use this component, add the following to your configuration.yaml file.
camera:
platform: foscam
name: Door Camera
ip: 192.168.0.123
port: 88
username: YOUR_USERNAME
password: YOUR_PASSWORD
Variables:
ip
*Required
The IP address of your Foscam device.
username
*Required
The username of a visitor or operator of your camera. Oddly admin accounts
don't seem to have access to take snapshots.
password
*Required
The password for accessing your camera.
name
*Optional
This parameter allows you to override the name of your camera in homeassistant.
port
*Optional
The port that the camera is running on. The default is 88.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.foscam.html
https://home-assistant.io/components/camera.foscam/
"""
import logging
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN
from homeassistant.components.camera import Camera
import requests
import re
_LOGGER = logging.getLogger(__name__)
@@ -76,7 +39,7 @@ class FoscamCamera(Camera):
self._username = device_info.get('username')
self._password = device_info.get('password')
self._snap_picture_url = self._base_url \
+ 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture&usr=' \
+ 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=' \
+ self._username + '&pwd=' + self._password
self._name = device_info.get('name', 'Foscam Camera')
@@ -86,17 +49,9 @@ class FoscamCamera(Camera):
def camera_image(self):
""" Return a still image reponse from the camera. """
# send the request to snap a picture
# Send the request to snap a picture and return raw jpg data
response = requests.get(self._snap_picture_url)
# parse the response to find the image file name
pattern = re.compile('src="[.][.]/(.*[.]jpg)"')
filename = pattern.search(response.content.decode("utf-8")).group(1)
# send request for the image
response = requests.get(self._base_url + filename)
return response.content
@property

View File

@@ -3,43 +3,8 @@ homeassistant.components.camera.generic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for IP Cameras.
This component provides basic support for IP cameras. For the basic support to
work you camera must support accessing a JPEG snapshot via a URL and you will
need to specify the "still_image_url" parameter which should be the location of
the JPEG image.
As part of the basic support the following features will be provided:
- MJPEG video streaming
- Saving a snapshot
- Recording(JPEG frame capture)
To use this component, add the following to your configuration.yaml file.
camera:
platform: generic
name: Door Camera
username: YOUR_USERNAME
password: YOUR_PASSWORD
still_image_url: http://YOUR_CAMERA_IP_AND_PORT/image.jpg
Variables:
still_image_url
*Required
The URL your camera serves the image on, eg. http://192.168.1.21:2112/
name
*Optional
This parameter allows you to override the name of your camera in Home
Assistant.
username
*Optional
The username for accessing your camera.
password
*Optional
The password for accessing your camera.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.generic/
"""
import logging
from requests.auth import HTTPBasicAuth
@@ -77,11 +42,19 @@ class GenericCamera(Camera):
def camera_image(self):
""" Return a still image reponse from the camera. """
if self._username and self._password:
response = requests.get(
self._still_image_url,
auth=HTTPBasicAuth(self._username, self._password))
try:
response = requests.get(
self._still_image_url,
auth=HTTPBasicAuth(self._username, self._password))
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
return None
else:
response = requests.get(self._still_image_url)
try:
response = requests.get(self._still_image_url)
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
return None
return response.content

View File

@@ -0,0 +1,71 @@
"""
homeassistant.components.camera.mjpeg
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for IP Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.mjpeg/
"""
import logging
from requests.auth import HTTPBasicAuth
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN
from homeassistant.components.camera import Camera
import requests
from contextlib import closing
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Adds a mjpeg IP Camera. """
if not validate_config({DOMAIN: config}, {DOMAIN: ['mjpeg_url']},
_LOGGER):
return None
add_devices_callback([MjpegCamera(config)])
# pylint: disable=too-many-instance-attributes
class MjpegCamera(Camera):
"""
A generic implementation of an IP camera that is reachable over a URL.
"""
def __init__(self, device_info):
super().__init__()
self._name = device_info.get('name', 'Mjpeg Camera')
self._username = device_info.get('username')
self._password = device_info.get('password')
self._mjpeg_url = device_info['mjpeg_url']
def camera_image(self):
""" Return a still image response from the camera. """
def process_response(response):
""" Take in a response object, return the jpg from it. """
data = b''
for chunk in response.iter_content(1024):
data += chunk
jpg_start = data.find(b'\xff\xd8')
jpg_end = data.find(b'\xff\xd9')
if jpg_start != -1 and jpg_end != -1:
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)
@property
def name(self):
""" Return the name of this device. """
return self._name

View File

@@ -1,9 +1,10 @@
"""
homeassistant.components.conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to have conversations with Home Assistant.
This is more a proof of concept.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/conversation/
"""
import logging
import re

View File

@@ -10,14 +10,15 @@ import homeassistant.core as ha
import homeassistant.bootstrap as bootstrap
import homeassistant.loader as loader
from homeassistant.const import (
CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME)
CONF_PLATFORM, ATTR_ENTITY_ID)
DOMAIN = "demo"
DEPENDENCIES = ['introduction', 'conversation']
DEPENDENCIES = ['conversation', 'introduction', 'zone']
COMPONENTS_WITH_DEMO_PLATFORM = [
'switch', 'light', 'sensor', 'thermostat', 'media_player', 'notify']
'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor',
'thermostat']
def setup(hass, config):
@@ -33,10 +34,10 @@ def setup(hass, config):
# Setup sun
if not hass.config.latitude:
hass.config.latitude = '32.87336'
hass.config.latitude = 32.87336
if not hass.config.longitude:
hass.config.longitude = '117.22743'
hass.config.longitude = 117.22743
bootstrap.setup_component(hass, 'sun')
@@ -60,7 +61,15 @@ def setup(hass, config):
{'camera': {
'platform': 'generic',
'name': 'IP Camera',
'still_image_url': 'http://194.218.96.92/jpg/image.jpg',
'still_image_url': 'http://home-assistant.io/demo/webcam.jpg',
}})
# Setup alarm_control_panel
bootstrap.setup_component(
hass, 'alarm_control_panel',
{'alarm_control_panel': {
'platform': 'manual',
'name': 'Test Alarm',
}})
# Setup scripts
@@ -102,23 +111,6 @@ def setup(hass, config):
}},
]})
# Setup fake device tracker
hass.states.set("device_tracker.paulus", "home",
{ATTR_ENTITY_PICTURE:
"http://graph.facebook.com/297400035/picture",
ATTR_FRIENDLY_NAME: 'Paulus'})
hass.states.set("device_tracker.anne_therese", "not_home",
{ATTR_FRIENDLY_NAME: 'Anne Therese'})
hass.states.set("group.all_devices", "home",
{
"auto": True,
ATTR_ENTITY_ID: [
"device_tracker.paulus",
"device_tracker.anne_therese"
]
})
# Setup configurator
configurator_ids = []

View File

@@ -1,9 +1,11 @@
"""
homeassistant.components.device_sun_light_trigger
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to turn on lights based on the state of the sun and
devices.
Provides functionality to turn on lights based on
the state of the sun and devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/device_sun_light_trigger/
"""
import logging
from datetime import timedelta

View File

@@ -1,23 +1,13 @@
"""
homeassistant.components.device_tracker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to keep track of devices.
device_tracker:
platform: netgear
# Optional
# How many seconds to wait after not seeing device to consider it not home
consider_home: 180
# Seconds between each scan
interval_seconds: 12
# New found devices auto found
track_new_devices: yes
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/device_tracker/
"""
# pylint: disable=too-many-instance-attributes, too-many-arguments
# pylint: disable=too-many-locals
import csv
from datetime import timedelta
import logging
@@ -25,7 +15,7 @@ import os
import threading
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.components import discovery, group
from homeassistant.components import discovery, group, zone
from homeassistant.config import load_yaml_config_file
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform
@@ -35,10 +25,11 @@ import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.const import (
ATTR_ENTITY_PICTURE, DEVICE_DEFAULT_NAME, STATE_HOME, STATE_NOT_HOME)
ATTR_ENTITY_PICTURE, ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE,
DEVICE_DEFAULT_NAME, STATE_HOME, STATE_NOT_HOME)
DOMAIN = "device_tracker"
DEPENDENCIES = []
DEPENDENCIES = ['zone']
GROUP_NAME_ALL_DEVICES = 'all devices'
ENTITY_ID_ALL_DEVICES = group.ENTITY_ID_FORMAT.format('all_devices')
@@ -52,7 +43,7 @@ CONF_TRACK_NEW = "track_new_devices"
DEFAULT_CONF_TRACK_NEW = True
CONF_CONSIDER_HOME = 'consider_home'
DEFAULT_CONF_CONSIDER_HOME = 180 # seconds
DEFAULT_CONSIDER_HOME = 180 # seconds
CONF_SCAN_INTERVAL = "interval_seconds"
DEFAULT_SCAN_INTERVAL = 12
@@ -60,15 +51,17 @@ DEFAULT_SCAN_INTERVAL = 12
CONF_AWAY_HIDE = 'hide_if_away'
DEFAULT_AWAY_HIDE = False
CONF_HOME_RANGE = 'home_range'
DEFAULT_HOME_RANGE = 100
SERVICE_SEE = 'see'
ATTR_LATITUDE = 'latitude'
ATTR_LONGITUDE = 'longitude'
ATTR_MAC = 'mac'
ATTR_DEV_ID = 'dev_id'
ATTR_HOST_NAME = 'host_name'
ATTR_LOCATION_NAME = 'location_name'
ATTR_GPS = 'gps'
ATTR_BATTERY = 'battery'
DISCOVERY_PLATFORMS = {
discovery.SERVICE_NETGEAR: 'netgear',
@@ -86,7 +79,7 @@ def is_on(hass, entity_id=None):
def see(hass, mac=None, dev_id=None, host_name=None, location_name=None,
gps=None):
gps=None, gps_accuracy=None, battery=None):
""" Call service to notify you see device. """
data = {key: value for key, value in
((ATTR_MAC, mac),
@@ -106,13 +99,19 @@ def setup(hass, config):
os.remove(csv_path)
conf = config.get(DOMAIN, {})
consider_home = util.convert(conf.get(CONF_CONSIDER_HOME), int,
DEFAULT_CONF_CONSIDER_HOME)
if isinstance(conf, list):
conf = conf[0]
consider_home = timedelta(
seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int,
DEFAULT_CONSIDER_HOME))
track_new = util.convert(conf.get(CONF_TRACK_NEW), bool,
DEFAULT_CONF_TRACK_NEW)
home_range = util.convert(conf.get(CONF_HOME_RANGE), int,
DEFAULT_HOME_RANGE)
devices = load_config(yaml_path, hass, timedelta(seconds=consider_home))
tracker = DeviceTracker(hass, consider_home, track_new, devices)
devices = load_config(yaml_path, hass, consider_home, home_range)
tracker = DeviceTracker(hass, consider_home, track_new, home_range,
devices)
def setup_platform(p_type, p_config, disc_info=None):
""" Setup a device tracker platform. """
@@ -158,22 +157,26 @@ def setup(hass, config):
""" Service to see a device. """
args = {key: value for key, value in call.data.items() if key in
(ATTR_MAC, ATTR_DEV_ID, ATTR_HOST_NAME, ATTR_LOCATION_NAME,
ATTR_GPS)}
ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_BATTERY)}
tracker.see(**args)
hass.services.register(DOMAIN, SERVICE_SEE, see_service)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_SEE, see_service,
descriptions.get(SERVICE_SEE))
return True
class DeviceTracker(object):
""" Track devices """
def __init__(self, hass, consider_home, track_new, devices):
def __init__(self, hass, consider_home, track_new, home_range, devices):
self.hass = hass
self.devices = {dev.dev_id: dev for dev in devices}
self.mac_to_dev = {dev.mac: dev for dev in devices if dev.mac}
self.consider_home = timedelta(seconds=consider_home)
self.consider_home = consider_home
self.track_new = track_new
self.home_range = home_range
self.lock = threading.Lock()
for device in devices:
@@ -183,7 +186,7 @@ class DeviceTracker(object):
self.group = None
def see(self, mac=None, dev_id=None, host_name=None, location_name=None,
gps=None):
gps=None, gps_accuracy=None, battery=None):
""" Notify device tracker that you see a device. """
with self.lock:
if mac is None and dev_id is None:
@@ -198,20 +201,21 @@ class DeviceTracker(object):
device = self.devices.get(dev_id)
if device:
device.seen(host_name, location_name, gps)
device.seen(host_name, location_name, gps, gps_accuracy,
battery)
if device.track:
device.update_ha_state()
return
# If no device can be found, create it
device = Device(
self.hass, self.consider_home, self.track_new, dev_id, mac,
(host_name or dev_id).replace('_', ' '))
self.hass, self.consider_home, self.home_range, self.track_new,
dev_id, mac, (host_name or dev_id).replace('_', ' '))
self.devices[dev_id] = device
if mac is not None:
self.mac_to_dev[mac] = device
device.seen(host_name, location_name, gps)
device.seen(host_name, location_name, gps, gps_accuracy, battery)
if device.track:
device.update_ha_state()
@@ -239,19 +243,20 @@ class DeviceTracker(object):
class Device(Entity):
""" Tracked device. """
# pylint: disable=too-many-instance-attributes, too-many-arguments
host_name = None
location_name = None
gps = None
gps_accuracy = 0
last_seen = None
battery = None
# Track if the last update of this device was HOME
last_update_home = False
_state = STATE_NOT_HOME
def __init__(self, hass, consider_home, track, dev_id, mac, name=None,
picture=None, away_hide=False):
def __init__(self, hass, consider_home, home_range, track, dev_id, mac,
name=None, picture=None, away_hide=False):
self.hass = hass
self.entity_id = ENTITY_ID_FORMAT.format(dev_id)
@@ -259,6 +264,8 @@ class Device(Entity):
# detected anymore.
self.consider_home = consider_home
# Distance in meters
self.home_range = home_range
# Device ID
self.dev_id = dev_id
self.mac = mac
@@ -273,6 +280,13 @@ class Device(Entity):
self.config_picture = picture
self.away_hide = away_hide
@property
def gps_home(self):
""" Return if device is within range of home. """
distance = max(
0, self.hass.config.distance(*self.gps) - self.gps_accuracy)
return self.gps is not None and distance <= self.home_range
@property
def name(self):
""" Returns the name of the entity. """
@@ -292,8 +306,12 @@ class Device(Entity):
attr[ATTR_ENTITY_PICTURE] = self.config_picture
if self.gps:
attr[ATTR_LATITUDE] = self.gps[0],
attr[ATTR_LONGITUDE] = self.gps[1],
attr[ATTR_LATITUDE] = self.gps[0]
attr[ATTR_LONGITUDE] = self.gps[1]
attr[ATTR_GPS_ACCURACY] = self.gps_accuracy
if self.battery:
attr[ATTR_BATTERY] = self.battery
return attr
@@ -302,12 +320,23 @@ class Device(Entity):
""" If device should be hidden. """
return self.away_hide and self.state != STATE_HOME
def seen(self, host_name=None, location_name=None, gps=None):
def seen(self, host_name=None, location_name=None, gps=None,
gps_accuracy=0, battery=None):
""" Mark the device as seen. """
self.last_seen = dt_util.utcnow()
self.host_name = host_name
self.location_name = location_name
self.gps = gps
self.gps_accuracy = gps_accuracy or 0
self.battery = battery
if gps is None:
self.gps = None
else:
try:
self.gps = tuple(float(val) for val in gps)
except ValueError:
_LOGGER.warning('Could not parse gps value for %s: %s',
self.dev_id, gps)
self.gps = None
self.update()
def stale(self, now=None):
@@ -321,6 +350,16 @@ class Device(Entity):
return
elif self.location_name:
self._state = self.location_name
elif self.gps is not None:
zone_state = zone.active_zone(self.hass, self.gps[0], self.gps[1],
self.gps_accuracy)
if zone_state is None:
self._state = STATE_NOT_HOME
elif zone_state.entity_id == zone.ENTITY_ID_HOME:
self._state = STATE_HOME
else:
self._state = zone_state.name
elif self.stale():
self._state = STATE_NOT_HOME
self.last_update_home = False
@@ -338,18 +377,18 @@ def convert_csv_config(csv_path, yaml_path):
(util.slugify(row['name']) or DEVICE_DEFAULT_NAME).lower(),
used_ids)
used_ids.add(dev_id)
device = Device(None, None, row['track'] == '1', dev_id,
device = Device(None, None, None, row['track'] == '1', dev_id,
row['device'], row['name'], row['picture'])
update_config(yaml_path, dev_id, device)
return True
def load_config(path, hass, consider_home):
def load_config(path, hass, consider_home, home_range):
""" Load devices from YAML config file. """
if not os.path.isfile(path):
return []
return [
Device(hass, consider_home, device.get('track', False),
Device(hass, consider_home, home_range, device.get('track', False),
str(dev_id).lower(), str(device.get('mac')).upper(),
device.get('name'), device.get('picture'),
device.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE))

View File

@@ -4,41 +4,8 @@ homeassistant.components.device_tracker.actiontec
Device tracker platform that supports scanning an Actiontec MI424WR
(Verizon FIOS) router for device presence.
This device tracker needs telnet to be enabled on the router.
Configuration:
To use the Actiontec tracker you will need to add something like the
following to your configuration.yaml file. If you experience disconnects
you can modify the home_interval variable.
device_tracker:
platform: actiontec
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
# optional:
home_interval: 10
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
home_interval
*Optional
If the home_interval is set then the component will not let a device
be AWAY if it has been HOME in the last home_interval minutes. This is
in addition to the 3 minute wait built into the device_tracker component.
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
@@ -50,20 +17,19 @@ import telnetlib
import homeassistant.util.dt as dt_util
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle, convert
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)
# interval in minutes to exclude devices from a scan while they are home
CONF_HOME_INTERVAL = "home_interval"
_LOGGER = logging.getLogger(__name__)
_LEASES_REGEX = re.compile(
r'(?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3})' +
r'\smac:\s(?P<mac>([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))')
r'\smac:\s(?P<mac>([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))' +
r'\svalid\sfor:\s(?P<timevalid>(-?\d+))' +
r'\ssec')
# pylint: disable=unused-argument
@@ -73,9 +39,7 @@ def get_scanner(hass, config):
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
_LOGGER):
return None
scanner = ActiontecDeviceScanner(config[DOMAIN])
return scanner if scanner.success_init else None
Device = namedtuple("Device", ["mac", "ip", "last_update"])
@@ -91,19 +55,11 @@ class ActiontecDeviceScanner(object):
self.host = config[CONF_HOST]
self.username = config[CONF_USERNAME]
self.password = config[CONF_PASSWORD]
minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0)
self.home_interval = timedelta(minutes=minutes)
self.lock = threading.Lock()
self.last_results = []
# Test the router is accessible
data = self.get_actiontec_data()
self.success_init = data is not None
_LOGGER.info("actiontec scanner initialized")
if self.home_interval:
_LOGGER.info("home_interval set to: %s", self.home_interval)
def scan_devices(self):
"""
@@ -133,27 +89,13 @@ class ActiontecDeviceScanner(object):
return False
with self.lock:
exclude_targets = set()
exclude_target_list = []
now = dt_util.now()
if self.home_interval:
for host in self.last_results:
if host.last_update + self.home_interval > now:
exclude_targets.add(host)
if len(exclude_targets) > 0:
exclude_target_list = [t.ip for t in exclude_targets]
actiontec_data = self.get_actiontec_data()
if not actiontec_data:
return False
self.last_results = []
for client in exclude_target_list:
if client in actiontec_data:
actiontec_data.pop(client)
for name, data in actiontec_data.items():
device = Device(data['mac'], name, now)
self.last_results.append(device)
self.last_results.extend(exclude_targets)
self.last_results = [Device(data['mac'], name, now)
for name, data in actiontec_data.items()
if data['timevalid'] > -60]
_LOGGER.info("actiontec scan successful")
return True
@@ -186,6 +128,7 @@ class ActiontecDeviceScanner(object):
if match is not None:
devices[match.group('ip')] = {
'ip': match.group('ip'),
'mac': match.group('mac').upper()
'mac': match.group('mac').upper(),
'timevalid': int(match.group('timevalid'))
}
return devices

View File

@@ -4,33 +4,8 @@ homeassistant.components.device_tracker.aruba
Device tracker platform that supports scanning a Aruba Access Point for device
presence.
This device tracker needs telnet to be enabled on the router.
Configuration:
To use the Aruba tracker you will need to add something like the following
to your configuration.yaml file. You also need to enable Telnet in the
configuration page of your router.
device_tracker:
platform: aruba
host: YOUR_ACCESS_POINT_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
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

View File

@@ -4,32 +4,8 @@ homeassistant.components.device_tracker.asuswrt
Device tracker platform that supports scanning a ASUSWRT router for device
presence.
This device tracker needs telnet to be enabled on the router.
Configuration:
To use the ASUSWRT tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: asuswrt
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
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
@@ -158,6 +134,10 @@ class AsusWrtDeviceScanner(object):
for lease in leases_result:
match = _LEASES_REGEX.search(lease.decode('utf-8'))
if not match:
_LOGGER.warning("Could not parse lease row: %s", lease)
continue
# For leases where the client doesn't set a hostname, ensure
# it is blank and not '*', which breaks the entity_id down
# the line
@@ -174,6 +154,9 @@ class AsusWrtDeviceScanner(object):
for neighbor in neighbors:
match = _IP_NEIGH_REGEX.search(neighbor.decode('utf-8'))
if not match:
_LOGGER.warning("Could not parse neighbor row: %s", neighbor)
continue
if match.group('ip') in devices:
devices[match.group('ip')]['status'] = match.group('status')
return devices

View File

@@ -4,30 +4,8 @@ homeassistant.components.device_tracker.ddwrt
Device tracker platform that supports scanning a DD-WRT router for device
presence.
Configuration:
To use the DD-WRT tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: ddwrt
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
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
@@ -46,6 +24,7 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
_LOGGER = logging.getLogger(__name__)
_DDWRT_DATA_REGEX = re.compile(r'\{(\w+)::([^\}]*)\}')
_MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})')
# pylint: disable=unused-argument
@@ -77,7 +56,7 @@ class DdWrtDeviceScanner(object):
self.last_results = {}
self.mac2name = None
self.mac2name = {}
# Test the router is accessible
url = 'http://{}/Status_Wireless.live.asp'.format(self.host)
@@ -98,30 +77,33 @@ class DdWrtDeviceScanner(object):
with self.lock:
# if not initialised and not already scanned and not found
if self.mac2name is None or device not in self.mac2name:
if device not in self.mac2name:
url = 'http://{}/Status_Lan.live.asp'.format(self.host)
data = self.get_ddwrt_data(url)
if not data:
return
return None
dhcp_leases = data.get('dhcp_leases', None)
if dhcp_leases:
# remove leading and trailing single quotes
cleaned_str = dhcp_leases.strip().strip('"')
elements = cleaned_str.split('","')
num_clients = int(len(elements)/5)
self.mac2name = {}
for idx in range(0, num_clients):
# this is stupid but the data is a single array
# every 5 elements represents one hosts, the MAC
# is the third element and the name is the first
mac_index = (idx * 5) + 2
if mac_index < len(elements):
mac = elements[mac_index]
self.mac2name[mac] = elements[idx * 5]
return self.mac2name.get(device, None)
if not dhcp_leases:
return None
# remove leading and trailing single quotes
cleaned_str = dhcp_leases.strip().strip('"')
elements = cleaned_str.split('","')
num_clients = int(len(elements)/5)
self.mac2name = {}
for idx in range(0, num_clients):
# this is stupid but the data is a single array
# every 5 elements represents one hosts, the MAC
# is the third element and the name is the first
mac_index = (idx * 5) + 2
if mac_index < len(elements):
mac = elements[mac_index]
self.mac2name[mac] = elements[idx * 5]
return self.mac2name.get(device)
@Throttle(MIN_TIME_BETWEEN_SCANS)
def _update_info(self):
@@ -141,29 +123,25 @@ class DdWrtDeviceScanner(object):
if not data:
return False
if data:
self.last_results = []
active_clients = data.get('active_wireless', None)
if active_clients:
# This is really lame, instead of using JSON the DD-WRT UI
# uses its own data format for some reason and then
# regex's out values so I guess I have to do the same,
# LAME!!!
self.last_results = []
# remove leading and trailing single quotes
clean_str = active_clients.strip().strip("'")
elements = clean_str.split("','")
active_clients = data.get('active_wireless', None)
if not active_clients:
return False
num_clients = int(len(elements)/9)
for idx in range(0, num_clients):
# get every 9th element which is the MAC address
index = idx * 9
if index < len(elements):
self.last_results.append(elements[index])
# This is really lame, instead of using JSON the DD-WRT UI
# uses its own data format for some reason and then
# regex's out values so I guess I have to do the same,
# LAME!!!
return True
# remove leading and trailing single quotes
clean_str = active_clients.strip().strip("'")
elements = clean_str.split("','")
return False
self.last_results.extend(item for item in elements
if _MAC_REGEX.match(item))
return True
def get_ddwrt_data(self, url):
""" Retrieve data from DD-WRT and return parsed result. """

View File

@@ -0,0 +1,49 @@
"""
homeassistant.components.device_tracker.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform for the device tracker.
device_tracker:
platform: demo
"""
import random
from homeassistant.components.device_tracker import DOMAIN
def setup_scanner(hass, config, see):
""" Set up a demo tracker. """
def offset():
""" Return random offset. """
return (random.randrange(500, 2000)) / 2e5 * random.choice((-1, 1))
def random_see(dev_id, name):
""" Randomize a sighting. """
see(
dev_id=dev_id,
host_name=name,
gps=(hass.config.latitude + offset(),
hass.config.longitude + offset()),
gps_accuracy=random.randrange(50, 150),
battery=random.randrange(10, 90)
)
def observe(call=None):
""" Observe three entities. """
random_see('demo_paulus', 'Paulus')
random_see('demo_anne_therese', 'Anne Therese')
observe()
see(
dev_id='demo_home_boy',
host_name='Home Boy',
gps=[hass.config.latitude - 0.00002, hass.config.longitude + 0.00002],
gps_accuracy=20,
battery=53
)
hass.services.register(DOMAIN, 'demo', observe)
return True

View File

@@ -0,0 +1,70 @@
"""
homeassistant.components.device_tracker.geofancy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Geofancy platform for the device tracker.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.geofancy/
"""
from homeassistant.const import (
HTTP_UNPROCESSABLE_ENTITY, HTTP_INTERNAL_SERVER_ERROR)
DEPENDENCIES = ['http']
_SEE = 0
URL_API_GEOFANCY_ENDPOINT = "/api/geofancy"
def setup_scanner(hass, config, see):
""" Set up an endpoint for the Geofancy app. """
# Use a global variable to keep setup_scanner compact when using a callback
global _SEE
_SEE = see
# POST would be semantically better, but that currently does not work
# since Geofancy sends the data as key1=value1&key2=value2
# in the request body, while Home Assistant expects json there.
hass.http.register_path(
'GET', URL_API_GEOFANCY_ENDPOINT, _handle_get_api_geofancy)
return True
def _handle_get_api_geofancy(handler, path_match, data):
""" Geofancy message received. """
if not isinstance(data, dict):
handler.write_json_message(
"Error while parsing Geofancy message.",
HTTP_INTERNAL_SERVER_ERROR)
return
if 'latitude' not in data or 'longitude' not in data:
handler.write_json_message(
"Location not specified.",
HTTP_UNPROCESSABLE_ENTITY)
return
if 'device' not in data or 'id' not in data:
handler.write_json_message(
"Device id or location id not specified.",
HTTP_UNPROCESSABLE_ENTITY)
return
try:
gps_coords = (float(data['latitude']), float(data['longitude']))
except ValueError:
# If invalid latitude / longitude format
handler.write_json_message(
"Invalid latitude / longitude format.",
HTTP_UNPROCESSABLE_ENTITY)
return
# entity id's in Home Assistant must be alphanumerical
device_uuid = data['device']
device_entity_id = device_uuid.replace('-', '')
_SEE(dev_id=device_entity_id, gps=gps_coords, location_name=data['id'])
handler.write_json_message("Geofancy message processed")

View File

@@ -4,33 +4,8 @@ homeassistant.components.device_tracker.luci
Device tracker platform that supports scanning a OpenWRT router for device
presence.
It's required that the luci RPC package is installed on the OpenWRT router:
# opkg install luci-mod-rpc
Configuration:
To use the Luci tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: luci
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.luci/
"""
import logging
import json

View File

@@ -1,15 +1,10 @@
"""
homeassistant.components.device_tracker.mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MQTT platform for the device tracker.
device_tracker:
platform: mqtt
qos: 1
devices:
paulus_oneplus: /location/paulus
annetherese_n4: /location/annetherese
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

View File

@@ -4,30 +4,8 @@ homeassistant.components.device_tracker.netgear
Device tracker platform that supports scanning a Netgear router for device
presence.
Configuration:
To use the Netgear tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: netgear
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
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

View File

@@ -3,26 +3,8 @@ homeassistant.components.device_tracker.nmap
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Device tracker platform that supports scanning a network with nmap.
Configuration:
To use the nmap tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: nmap_tracker
hosts: 192.168.1.1/24
Variables:
hosts
*Required
The IP addresses to scan in the network-prefix notation (192.168.1.1/24) or
the range notation (192.168.1.1-255).
home_interval
*Optional
Number of minutes it will not scan devices that it found in previous results.
This is to save battery.
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
@@ -117,15 +99,18 @@ class NmapDeviceScanner(object):
scanner = PortScanner()
options = "-F --host-timeout 5"
exclude_targets = set()
if self.home_interval:
now = dt_util.now()
for host in self.last_results:
if host.last_update + self.home_interval > now:
exclude_targets.add(host)
if len(exclude_targets) > 0:
target_list = [t.ip for t in exclude_targets]
options += " --exclude {}".format(",".join(target_list))
boundary = dt_util.now() - self.home_interval
last_results = [device for device in self.last_results
if device.last_update > boundary]
if last_results:
# Pylint is confused here.
# pylint: disable=no-member
options += " --exclude {}".format(",".join(device.ip for device
in last_results))
else:
last_results = []
try:
result = scanner.scan(hosts=self.hosts, arguments=options)
@@ -133,18 +118,17 @@ class NmapDeviceScanner(object):
return False
now = dt_util.now()
self.last_results = []
for ipv4, info in result['scan'].items():
if info['status']['state'] != 'up':
continue
name = info['hostnames'][0] if info['hostnames'] else ipv4
name = info['hostnames'][0]['name'] if info['hostnames'] else ipv4
# Mac address only returned if nmap ran as root
mac = info['addresses'].get('mac') or _arp(ipv4)
if mac is None:
continue
device = Device(mac.upper(), name, ipv4, now)
self.last_results.append(device)
self.last_results.extend(exclude_targets)
last_results.append(Device(mac.upper(), name, ipv4, now))
self.last_results = last_results
_LOGGER.info("nmap scan successful")
return True

View File

@@ -0,0 +1,53 @@
"""
homeassistant.components.device_tracker.owntracks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OwnTracks platform for the device tracker.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.owntracks/
"""
import json
import logging
import homeassistant.components.mqtt as mqtt
DEPENDENCIES = ['mqtt']
LOCATION_TOPIC = 'owntracks/+/+'
def setup_scanner(hass, config, see):
""" Set up a OwnTracksks tracker. """
def owntracks_location_update(topic, payload, qos):
""" MQTT message received. """
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typelocation
try:
data = json.loads(payload)
except ValueError:
# If invalid JSON
logging.getLogger(__name__).error(
'Unable to parse payload as JSON: %s', payload)
return
if not isinstance(data, dict) or data.get('_type') != 'location':
return
parts = topic.split('/')
kwargs = {
'dev_id': '{}_{}'.format(parts[1], parts[2]),
'host_name': parts[1],
'gps': (data['lat'], data['lon']),
}
if 'acc' in data:
kwargs['gps_accuracy'] = data['acc']
if 'batt' in data:
kwargs['battery'] = data['batt']
see(**kwargs)
mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1)
return True

View File

@@ -0,0 +1,117 @@
"""
homeassistant.components.device_tracker.snmp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Device tracker platform that supports fetching WiFi associations
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
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)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pysnmp==4.2.5']
CONF_COMMUNITY = "community"
CONF_BASEOID = "baseoid"
# pylint: disable=unused-argument
def get_scanner(hass, config):
""" Validates config and returns an snmp scanner """
if not validate_config(config,
{DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]},
_LOGGER):
return None
scanner = SnmpScanner(config[DOMAIN])
return scanner if scanner.success_init else None
class SnmpScanner(object):
"""
This class queries any SNMP capable Acces Point for connected devices.
"""
def __init__(self, config):
from pysnmp.entity.rfc3413.oneliner import cmdgen
self.snmp = cmdgen.CommandGenerator()
self.host = cmdgen.UdpTransportTarget((config[CONF_HOST], 161))
self.community = cmdgen.CommunityData(config[CONF_COMMUNITY])
self.baseoid = cmdgen.MibVariable(config[CONF_BASEOID])
self.lock = threading.Lock()
self.last_results = []
# Test the router is accessible
data = self.get_snmp_data()
self.success_init = data is not None
def scan_devices(self):
"""
Scans for new devices and return a list containing found device IDs.
"""
self._update_info()
return [client['mac'] for client in self.last_results]
# Supressing no-self-use warning
# pylint: disable=R0201
def get_device_name(self, device):
""" Returns the name of the given device or None if we don't know. """
# We have no names
return None
@Throttle(MIN_TIME_BETWEEN_SCANS)
def _update_info(self):
"""
Ensures the information from the WAP is up to date.
Returns boolean if scanning successful.
"""
if not self.success_init:
return False
with self.lock:
data = self.get_snmp_data()
if not data:
return False
self.last_results = data
return True
def get_snmp_data(self):
""" Fetch mac addresses from WAP via SNMP. """
devices = []
errindication, errstatus, errindex, restable = self.snmp.nextCmd(
self.community, self.host, self.baseoid)
if errindication:
_LOGGER.error("SNMPLIB error: %s", errindication)
return
if errstatus:
_LOGGER.error('SNMP error: %s at %s', errstatus.prettyPrint(),
errindex and restable[-1][int(errindex)-1]
or '?')
return
for resrow in restable:
for _, val in resrow:
mac = binascii.hexlify(val.asOctets()).decode('utf-8')
mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)])
devices.append({'mac': mac})
return devices

View File

@@ -4,32 +4,8 @@ homeassistant.components.device_tracker.thomson
Device tracker platform that supports scanning a THOMSON router for device
presence.
This device tracker needs telnet to be enabled on the router.
Configuration:
To use the THOMSON tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: thomson
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
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

View File

@@ -4,36 +4,8 @@ homeassistant.components.device_tracker.tomato
Device tracker platform that supports scanning a Tomato router for device
presence.
Configuration:
To use the Tomato tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: tomato
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
http_id: ABCDEFG
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
http_id
*Required
The value can be obtained by logging in to the Tomato admin interface and
search for http_id in the page source code.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.tomato/
"""
import logging
import json

View File

@@ -4,30 +4,8 @@ homeassistant.components.device_tracker.tplink
Device tracker platform that supports scanning a TP-Link router for device
presence.
Configuration:
To use the TP-Link tracker you will need to add something like the following
to your configuration.yaml file.
device_tracker:
platform: tplink
host: YOUR_ROUTER_IP
username: YOUR_ADMIN_USERNAME
password: YOUR_ADMIN_PASSWORD
Variables:
host
*Required
The IP address of your router, e.g. 192.168.1.1.
username
*Required
The username of an user with administrative privileges, usually 'admin'.
password
*Required
The password for your given admin account.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.tplink/
"""
import base64
import logging
@@ -54,10 +32,13 @@ def get_scanner(hass, config):
_LOGGER):
return None
scanner = Tplink2DeviceScanner(config[DOMAIN])
scanner = Tplink3DeviceScanner(config[DOMAIN])
if not scanner.success_init:
scanner = TplinkDeviceScanner(config[DOMAIN])
scanner = Tplink2DeviceScanner(config[DOMAIN])
if not scanner.success_init:
scanner = TplinkDeviceScanner(config[DOMAIN])
return scanner if scanner.success_init else None
@@ -156,7 +137,7 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
with self.lock:
_LOGGER.info("Loading wireless clients...")
url = 'http://{}/data/map_access_wireless_client_grid.json'\
url = 'http://{}/data/map_access_wireless_client_grid.json' \
.format(self.host)
referer = 'http://{}'.format(self.host)
@@ -166,7 +147,7 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
b64_encoded_username_password = base64.b64encode(
username_password.encode('ascii')
).decode('ascii')
cookie = 'Authorization=Basic {}'\
cookie = 'Authorization=Basic {}' \
.format(b64_encoded_username_password)
response = requests.post(url, headers={'referer': referer,
@@ -183,7 +164,119 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
self.last_results = {
device['mac_addr'].replace('-', ':'): device['name']
for device in result
}
}
return True
return False
class Tplink3DeviceScanner(TplinkDeviceScanner):
"""
This class queries the Archer C9 router running version 150811 or higher
of TP-Link firmware for connected devices.
"""
def __init__(self, config):
self.stok = ''
self.sysauth = ''
super(Tplink3DeviceScanner, self).__init__(config)
def scan_devices(self):
"""
Scans for new devices and return a list containing found device ids.
"""
self._update_info()
return self.last_results.keys()
# pylint: disable=no-self-use
def get_device_name(self, device):
"""
The TP-Link firmware doesn't save the name of the wireless device.
We are forced to use the MAC address as name here.
"""
return self.last_results.get(device)
def _get_auth_tokens(self):
"""
Retrieves auth tokens from the router.
"""
_LOGGER.info("Retrieving auth tokens...")
url = 'http://{}/cgi-bin/luci/;stok=/login?form=login' \
.format(self.host)
referer = 'http://{}/webpages/login.html'.format(self.host)
# if possible implement rsa encryption of password here
response = requests.post(url,
params={'operation': 'login',
'username': self.username,
'password': self.password},
headers={'referer': referer})
try:
self.stok = response.json().get('data').get('stok')
_LOGGER.info(self.stok)
regex_result = re.search('sysauth=(.*);',
response.headers['set-cookie'])
self.sysauth = regex_result.group(1)
_LOGGER.info(self.sysauth)
return True
except ValueError:
_LOGGER.error("Couldn't fetch auth tokens!")
return False
@Throttle(MIN_TIME_BETWEEN_SCANS)
def _update_info(self):
"""
Ensures the information from the TP-Link router is up to date.
Returns boolean if scanning successful.
"""
with self.lock:
if (self.stok == '') or (self.sysauth == ''):
self._get_auth_tokens()
_LOGGER.info("Loading wireless clients...")
url = 'http://{}/cgi-bin/luci/;stok={}/admin/wireless?form=statistics' \
.format(self.host, self.stok)
referer = 'http://{}/webpages/index.html'.format(self.host)
response = requests.post(url,
params={'operation': 'load'},
headers={'referer': referer},
cookies={'sysauth': self.sysauth})
try:
json_response = response.json()
if json_response.get('success'):
result = response.json().get('data')
else:
if json_response.get('errorcode') == 'timeout':
_LOGGER.info("Token timed out. "
"Relogging on next scan.")
self.stok = ''
self.sysauth = ''
return False
else:
_LOGGER.error("An unknown error happened "
"while fetching data.")
return False
except ValueError:
_LOGGER.error("Router didn't respond with JSON. "
"Check if credentials are correct.")
return False
if result:
self.last_results = {
device['mac'].replace('-', ':'): device['mac']
for device in result
}
return True
return False

View File

@@ -0,0 +1,173 @@
"""
homeassistant.components.device_tracker.ubus
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Device tracker platform that supports scanning a OpenWRT router for device
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 re
import threading
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
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)
_LOGGER = logging.getLogger(__name__)
def get_scanner(hass, config):
""" Validates config and returns a Luci scanner. """
if not validate_config(config,
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
_LOGGER):
return None
scanner = UbusDeviceScanner(config[DOMAIN])
return scanner if scanner.success_init else None
# pylint: disable=too-many-instance-attributes
class UbusDeviceScanner(object):
"""
This class queries a wireless router running OpenWrt firmware
for connected devices. Adapted from Tomato scanner.
Configure your routers' ubus ACL based on following instructions:
http://wiki.openwrt.org/doc/techref/ubus
Read only access will be fine.
To use this class you have to install rpcd-mod-file package
in your OpenWrt router:
opkg install rpcd-mod-file
"""
def __init__(self, config):
host = config[CONF_HOST]
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
self.parse_api_pattern = re.compile(r"(?P<param>\w*) = (?P<value>.*);")
self.lock = threading.Lock()
self.last_results = {}
self.url = 'http://{}/ubus'.format(host)
self.session_id = _get_session_id(self.url, username, password)
self.hostapd = []
self.leasefile = None
self.mac2name = None
self.success_init = self.session_id is not None
def scan_devices(self):
"""
Scans for new devices and return a list containing found device ids.
"""
self._update_info()
return self.last_results
def get_device_name(self, device):
""" Returns the name of the given device or None if we don't know. """
with self.lock:
if self.leasefile is None:
result = _req_json_rpc(self.url, self.session_id,
'call', 'uci', 'get',
config="dhcp", type="dnsmasq")
if result:
values = result["values"].values()
self.leasefile = next(iter(values))["leasefile"]
else:
return
if self.mac2name is None:
result = _req_json_rpc(self.url, self.session_id,
'call', 'file', 'read',
path=self.leasefile)
if result:
self.mac2name = dict()
for line in result["data"].splitlines():
hosts = line.split(" ")
self.mac2name[hosts[1].upper()] = hosts[3]
else:
# Error, handled in the _req_json_rpc
return
return self.mac2name.get(device.upper(), None)
@Throttle(MIN_TIME_BETWEEN_SCANS)
def _update_info(self):
"""
Ensures the information from the Luci router is up to date.
Returns boolean if scanning successful.
"""
if not self.success_init:
return False
with self.lock:
_LOGGER.info("Checking ARP")
if not self.hostapd:
hostapd = _req_json_rpc(self.url, self.session_id,
'list', 'hostapd.*', '')
self.hostapd.extend(hostapd.keys())
self.last_results = []
results = 0
for hostapd in self.hostapd:
result = _req_json_rpc(self.url, self.session_id,
'call', hostapd, 'get_clients')
if result:
results = results + 1
self.last_results.extend(result['clients'].keys())
return bool(results)
def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params):
""" Perform one JSON RPC operation. """
data = json.dumps({"jsonrpc": "2.0",
"id": 1,
"method": rpcmethod,
"params": [session_id,
subsystem,
method,
params]})
try:
res = requests.post(url, data=data, timeout=5)
except requests.exceptions.Timeout:
return
if res.status_code == 200:
response = res.json()
if rpcmethod == "call":
return response["result"][1]
else:
return response["result"]
def _get_session_id(url, username, password):
""" Get authentication token for the given host+username+password. """
res = _req_json_rpc(url, "00000000000000000000000000000000", 'call',
'session', 'login', username=username,
password=password)
return res["ubus_rpc_session"]

View File

@@ -1,7 +1,6 @@
"""
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.
@@ -19,7 +18,7 @@ from homeassistant.const import (
DOMAIN = "discovery"
DEPENDENCIES = []
REQUIREMENTS = ['netdisco==0.4']
REQUIREMENTS = ['netdisco==0.5.1']
SCAN_INTERVAL = 300 # seconds
@@ -28,6 +27,7 @@ SERVICE_HUE = 'philips_hue'
SERVICE_CAST = 'google_cast'
SERVICE_NETGEAR = 'netgear_router'
SERVICE_SONOS = 'sonos'
SERVICE_PLEX = 'plex_mediaserver'
SERVICE_HANDLERS = {
SERVICE_WEMO: "switch",
@@ -35,6 +35,7 @@ SERVICE_HANDLERS = {
SERVICE_HUE: "light",
SERVICE_NETGEAR: 'device_tracker',
SERVICE_SONOS: 'media_player',
SERVICE_PLEX: 'media_player',
}
@@ -88,6 +89,7 @@ def setup(hass, config):
ATTR_DISCOVERED: info
})
# pylint: disable=unused-argument
def start_discovery(event):
""" Start discovering. """
netdisco = DiscoveryService(SCAN_INTERVAL)

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.downloader
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
@@ -42,6 +44,10 @@ def setup(hass, config):
download_path = config[DOMAIN][CONF_DOWNLOAD_DIR]
# If path is relative, we assume relative to HASS config dir
if not os.path.isabs(download_path):
download_path = hass.config.path(download_path)
if not os.path.isdir(download_path):
logger.error(

View File

@@ -8,9 +8,10 @@ import re
import os
import logging
from . import version
from . import version, mdi_version
import homeassistant.util as util
from homeassistant.const import URL_ROOT, HTTP_OK
from homeassistant.config import get_default_config_dir
DOMAIN = 'frontend'
DEPENDENCIES = ['api']
@@ -19,11 +20,13 @@ INDEX_PATH = os.path.join(os.path.dirname(__file__), 'index.html.template')
_LOGGER = logging.getLogger(__name__)
FRONTEND_URLS = [
URL_ROOT, '/logbook', '/history', '/devService', '/devState', '/devEvent']
URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState',
'/devEvent', '/devInfo']
STATES_URL = re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)')
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
def setup(hass, config):
""" Setup serving the frontend. """
@@ -43,6 +46,9 @@ def setup(hass, config):
hass.http.register_path(
'HEAD', re.compile(r'/static/(?P<file>[a-zA-Z\._\-0-9/]+)'),
_handle_get_static, False)
hass.http.register_path(
'GET', re.compile(r'/local/(?P<file>[a-zA-Z\._\-0-9/]+)'),
_handle_get_local, False)
return True
@@ -68,6 +74,7 @@ def _handle_get_root(handler, path_match, data):
template_html = template_html.replace('{{ app_url }}', app_url)
template_html = template_html.replace('{{ auth }}', auth)
template_html = template_html.replace('{{ icons }}', mdi_version.VERSION)
handler.wfile.write(template_html.encode("UTF-8"))
@@ -76,10 +83,24 @@ def _handle_get_static(handler, path_match, data):
""" Returns a static file for the frontend. """
req_file = util.sanitize_path(path_match.group('file'))
# Strip md5 hash out of frontend filename
if re.match(r'^frontend-[A-Za-z0-9]{32}\.html$', req_file):
req_file = "frontend.html"
# Strip md5 hash out
fingerprinted = _FINGERPRINT.match(req_file)
if fingerprinted:
req_file = "{}.{}".format(*fingerprinted.groups())
path = os.path.join(os.path.dirname(__file__), 'www_static', req_file)
handler.write_file(path)
def _handle_get_local(handler, path_match, data):
"""
Returns a static file from the hass.config.path/www for the frontend.
"""
req_file = util.sanitize_path(path_match.group('file'))
path = os.path.join(get_default_config_dir(), 'www', req_file)
if not os.path.isfile(path):
return False
handler.write_file(path)

View File

@@ -46,6 +46,6 @@
</div>
<script src='/static/webcomponents-lite.min.js'></script>
<link rel='import' href='/static/{{ app_url }}' />
<home-assistant auth='{{ auth }}'></home-assistant>
<home-assistant auth='{{ auth }}' icons='{{ icons }}'></home-assistant>
</body>
</html>

View File

@@ -0,0 +1,2 @@
""" DO NOT MODIFY. Auto-generated by update_mdi script """
VERSION = "38EF63D0474411E4B3CF842B2B6CFE1B"

View File

@@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "5f35285bc502e3f69f564240fee04baa"
VERSION = "b75e3c9ebd3de2dae0912a89499127a9"

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,11 @@
"""
homeassistant.components.group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to group devices that can be turned on or off.
"""
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 import generate_entity_id
from homeassistant.helpers.event import track_state_change
@@ -12,7 +13,8 @@ from homeassistant.helpers.entity import Entity
import homeassistant.util as util
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN)
STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED,
STATE_UNKNOWN)
DOMAIN = "group"
DEPENDENCIES = []
@@ -22,7 +24,8 @@ ENTITY_ID_FORMAT = DOMAIN + ".{}"
ATTR_AUTO = "auto"
# List of ON/OFF state tuples for groupable states
_GROUP_TYPES = [(STATE_ON, STATE_OFF), (STATE_HOME, STATE_NOT_HOME)]
_GROUP_TYPES = [(STATE_ON, STATE_OFF), (STATE_HOME, STATE_NOT_HOME),
(STATE_OPEN, STATE_CLOSED)]
def _get_group_on_off(state):

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.history
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provide pre-made queries on top of the recorder component.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/history/
"""
import re
from datetime import timedelta

View File

@@ -1,76 +1,11 @@
"""
homeassistant.components.httpinterface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
homeassistant.components.http
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This module provides an API and a HTTP interface for debug purposes.
By default it will run on port 8123.
All API calls have to be accompanied by an 'api_password' parameter and will
return JSON. If successful calls will return status code 200 or 201.
Other status codes that can occur are:
- 400 (Bad Request)
- 401 (Unauthorized)
- 404 (Not Found)
- 405 (Method not allowed)
The api supports the following actions:
/api - GET
Returns message if API is up and running.
Example result:
{
"message": "API running."
}
/api/states - GET
Returns a list of entities for which a state is available
Example result:
[
{ .. state object .. },
{ .. state object .. }
]
/api/states/<entity_id> - GET
Returns the current state from an entity
Example result:
{
"attributes": {
"next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
},
"entity_id": "weather.sun",
"last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
}
/api/states/<entity_id> - POST
Updates the current state of an entity. Returns status code 201 if successful
with location header of updated resource and as body the new state.
parameter: new_state - string
optional parameter: attributes - JSON encoded object
Example result:
{
"attributes": {
"next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
},
"entity_id": "weather.sun",
"last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
}
/api/events/<event_type> - POST
Fires an event with event_type
optional parameter: event_data - JSON encoded object
Example result:
{
"message": "Event download_file fired."
}
For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/
"""
import json
import threading
import logging
@@ -232,7 +167,12 @@ class RequestHandler(SimpleHTTPRequestHandler):
def log_message(self, fmt, *arguments):
""" Redirect built-in log to HA logging """
_LOGGER.info(fmt, *arguments)
if self.server.no_password_set:
_LOGGER.info(fmt, *arguments)
else:
_LOGGER.info(
fmt, *(arg.replace(self.server.api_password, '*******')
if isinstance(arg, str) else arg for arg in arguments))
def _handle_request(self, method): # pylint: disable=too-many-branches
""" Does some common checks and calls appropriate method. """

View File

@@ -1,23 +1,10 @@
"""
homeassistant.components.ifttt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This component enable you to trigger Maker IFTTT recipes.
Check https://ifttt.com/maker for details.
Configuration:
To use Maker IFTTT you will need to add something like the following to your
config/configuration.yaml.
ifttt:
key: xxxxx-x-xxxxxxxxxxxxx
Variables:
key
*Required
Your api key
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/ifttt/
"""
import logging
import requests

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.introduction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component that will help guide the user taking its first steps.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/introduction/
"""
import logging
@@ -24,13 +26,13 @@ def setup(hass, config=None):
Here are some resources to get started:
- Configuring Home Assistant:
https://home-assistant.io/getting-started/configuration.html
https://home-assistant.io/getting-started/configuration/
- Available components:
https://home-assistant.io/components/
- Troubleshooting your configuration:
https://home-assistant.io/getting-started/troubleshooting-configuration.html
https://home-assistant.io/getting-started/troubleshooting-configuration/
- Getting help:
https://home-assistant.io/help/

View File

@@ -5,7 +5,7 @@ Connects to an ISY-994 controller and loads relevant components to control its
devices. Also contains the base classes for ISY Sensors, Lights, and Switches.
For configuration details please visit the documentation for this component at
https://home-assistant.io/components/isy994.html
https://home-assistant.io/components/isy994/
"""
import logging
from urllib.parse import urlparse

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.keyboard
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to emulate keyboard presses on host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/keyboard/
"""
import logging

View File

@@ -1,65 +1,23 @@
"""
homeassistant.components.light
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to interact with lights.
It offers the following services:
TURN_OFF - Turns one or multiple lights off.
Supports following parameters:
- transition
Integer that represents the time the light should take to transition to
the new state.
- entity_id
String or list of strings that point at entity_ids of lights.
TURN_ON - Turns one or multiple lights on and change attributes.
Supports following parameters:
- transition
Integer that represents the time the light should take to transition to
the new state.
- entity_id
String or list of strings that point at entity_ids of lights.
- profile
String with the name of one of the built-in profiles (relax, energize,
concentrate, reading) or one of the custom profiles defined in
light_profiles.csv in the current working directory.
Light profiles define a xy color and a brightness.
If a profile is given and a brightness or xy color then the profile values
will be overwritten.
- xy_color
A list containing two floats representing the xy color you want the light
to be.
- rgb_color
A list containing three integers representing the xy color you want the
light to be.
- brightness
Integer between 0 and 255 representing how bright you want the light to be.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/light/
"""
import logging
import os
import csv
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import ToggleEntity
import homeassistant.util as util
import homeassistant.util.color as color_util
from homeassistant.components import group, discovery, wink, isy994, zwave
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.components import group, discovery, wink, isy994
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
import homeassistant.util as util
import homeassistant.util.color as color_util
DOMAIN = "light"
@@ -77,6 +35,7 @@ ATTR_TRANSITION = "transition"
# lists holding color values
ATTR_RGB_COLOR = "rgb_color"
ATTR_XY_COLOR = "xy_color"
ATTR_COLOR_TEMP = "color_temp"
# int with value 0 .. 255 representing brightness of the light
ATTR_BRIGHTNESS = "brightness"
@@ -92,6 +51,7 @@ FLASH_LONG = "long"
# Apply an effect to the light, can be EFFECT_COLORLOOP
ATTR_EFFECT = "effect"
EFFECT_COLORLOOP = "colorloop"
EFFECT_WHITE = "white"
LIGHT_PROFILES_FILE = "light_profiles.csv"
@@ -100,11 +60,14 @@ DISCOVERY_PLATFORMS = {
wink.DISCOVER_LIGHTS: 'wink',
isy994.DISCOVER_LIGHTS: 'isy994',
discovery.SERVICE_HUE: 'hue',
zwave.DISCOVER_LIGHTS: 'zwave',
}
PROP_TO_ATTR = {
'brightness': ATTR_BRIGHTNESS,
'color_xy': ATTR_XY_COLOR,
'color_temp': ATTR_COLOR_TEMP,
'rgb_color': ATTR_RGB_COLOR,
'xy_color': ATTR_XY_COLOR,
}
_LOGGER = logging.getLogger(__name__)
@@ -119,8 +82,8 @@ def is_on(hass, entity_id=None):
# pylint: disable=too-many-arguments
def turn_on(hass, entity_id=None, transition=None, brightness=None,
rgb_color=None, xy_color=None, profile=None, flash=None,
effect=None):
rgb_color=None, xy_color=None, color_temp=None, profile=None,
flash=None, effect=None):
""" Turns all or specified light on. """
data = {
key: value for key, value in [
@@ -130,6 +93,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None,
(ATTR_BRIGHTNESS, brightness),
(ATTR_RGB_COLOR, rgb_color),
(ATTR_XY_COLOR, xy_color),
(ATTR_COLOR_TEMP, color_temp),
(ATTR_FLASH, flash),
(ATTR_EFFECT, effect),
] if value is not None
@@ -150,7 +114,7 @@ def turn_off(hass, entity_id=None, transition=None):
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
# pylint: disable=too-many-branches, too-many-locals
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
def setup(hass, config):
""" Exposes light control via statemachine and services. """
@@ -240,32 +204,33 @@ def setup(hass, config):
# ValueError if value could not be converted to float
pass
if ATTR_COLOR_TEMP in dat:
# color_temp should be an int of mirads value
colortemp = dat.get(ATTR_COLOR_TEMP)
# Without this check, a ctcolor with value '99' would work
# These values are based on Philips Hue, may need ajustment later
if isinstance(colortemp, int) and 154 <= colortemp <= 500:
params[ATTR_COLOR_TEMP] = colortemp
if ATTR_RGB_COLOR in dat:
try:
# rgb_color should be a list containing 3 ints
rgb_color = dat.get(ATTR_RGB_COLOR)
if len(rgb_color) == 3:
params[ATTR_XY_COLOR] = \
color_util.color_RGB_to_xy(int(rgb_color[0]),
int(rgb_color[1]),
int(rgb_color[2]))
params[ATTR_RGB_COLOR] = [int(val) for val in rgb_color]
except (TypeError, ValueError):
# TypeError if rgb_color is not iterable
# ValueError if not all values can be converted to int
pass
if ATTR_FLASH in dat:
if dat[ATTR_FLASH] == FLASH_SHORT:
params[ATTR_FLASH] = FLASH_SHORT
if dat.get(ATTR_FLASH) in (FLASH_SHORT, FLASH_LONG):
params[ATTR_FLASH] = dat[ATTR_FLASH]
elif dat[ATTR_FLASH] == FLASH_LONG:
params[ATTR_FLASH] = FLASH_LONG
if ATTR_EFFECT in dat:
if dat[ATTR_EFFECT] == EFFECT_COLORLOOP:
params[ATTR_EFFECT] = EFFECT_COLORLOOP
if dat.get(ATTR_EFFECT) in (EFFECT_COLORLOOP, EFFECT_WHITE):
params[ATTR_EFFECT] = dat[ATTR_EFFECT]
for light in target_lights:
light.turn_on(**params)
@@ -275,11 +240,13 @@ def setup(hass, config):
light.update_ha_state(True)
# Listen for light on and light off service calls
hass.services.register(DOMAIN, SERVICE_TURN_ON,
handle_light_service)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service,
descriptions.get(SERVICE_TURN_ON))
hass.services.register(DOMAIN, SERVICE_TURN_OFF,
handle_light_service)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service,
descriptions.get(SERVICE_TURN_OFF))
return True
@@ -294,10 +261,20 @@ class Light(ToggleEntity):
return None
@property
def color_xy(self):
def xy_color(self):
""" XY color value [float, float]. """
return None
@property
def rgb_color(self):
""" RGB color value [int, int, int] """
return None
@property
def color_temp(self):
""" CT color value in mirads. """
return None
@property
def device_state_attributes(self):
""" Returns device specific state attributes. """
@@ -314,6 +291,12 @@ class Light(ToggleEntity):
if value:
data[attr] = value
if ATTR_RGB_COLOR not in data and ATTR_XY_COLOR in data and \
ATTR_BRIGHTNESS in data:
data[ATTR_RGB_COLOR] = color_util.color_xy_brightness_to_RGB(
data[ATTR_XY_COLOR][0], data[ATTR_XY_COLOR][1],
data[ATTR_BRIGHTNESS])
device_attr = self.device_state_attributes
if device_attr is not None:

View File

@@ -0,0 +1,76 @@
"""
homeassistant.components.light.blinksticklight
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Blinkstick lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.blinksticklight/
"""
import logging
from blinkstick import blinkstick
from homeassistant.components.light import (Light, ATTR_RGB_COLOR)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ["blinkstick==1.1.7"]
DEPENDENCIES = []
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Add device specified by serial number. """
stick = blinkstick.find_by_serial(config['serial'])
add_devices_callback([BlinkStickLight(stick, config['name'])])
class BlinkStickLight(Light):
""" Represents a BlinkStick light. """
def __init__(self, stick, name):
self._stick = stick
self._name = name
self._serial = stick.get_serial()
self._rgb_color = stick.get_color()
@property
def should_poll(self):
""" Polling needed. """
return True
@property
def name(self):
""" The name of the light. """
return self._name
@property
def rgb_color(self):
""" Read back the color of the light. """
return self._rgb_color
@property
def is_on(self):
""" Check whether any of the LEDs colors are non-zero. """
return sum(self._rgb_color) > 0
def update(self):
""" Read back the device state """
self._rgb_color = self._stick.get_color()
def turn_on(self, **kwargs):
""" Turn the device on. """
if ATTR_RGB_COLOR in kwargs:
self._rgb_color = kwargs[ATTR_RGB_COLOR]
else:
self._rgb_color = [255, 255, 255]
self._stick.set_color(red=self._rgb_color[0],
green=self._rgb_color[1],
blue=self._rgb_color[2])
def turn_off(self, **kwargs):
""" Turn the device off """
self._stick.turn_off()

View File

@@ -1,37 +1,40 @@
"""
homeassistant.components.light.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that implements lights.
"""
import random
from homeassistant.components.light import (
Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR)
Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_COLOR_TEMP)
LIGHT_COLORS = [
[0.368, 0.180],
[0.460, 0.470],
[237, 224, 33],
[255, 63, 111],
]
LIGHT_TEMPS = [240, 380]
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return demo lights. """
add_devices_callback([
DemoLight("Bed Light", False),
DemoLight("Ceiling Lights", True, LIGHT_COLORS[0]),
DemoLight("Kitchen Lights", True, LIGHT_COLORS[1])
DemoLight("Ceiling Lights", True, LIGHT_COLORS[0], LIGHT_TEMPS[1]),
DemoLight("Kitchen Lights", True, LIGHT_COLORS[1], LIGHT_TEMPS[0])
])
class DemoLight(Light):
""" Provides a demo switch. """
def __init__(self, name, state, xy=None, brightness=180):
# pylint: disable=too-many-arguments
def __init__(self, name, state, rgb=None, ct=None, brightness=180):
self._name = name
self._state = state
self._xy = xy or random.choice(LIGHT_COLORS)
self._rgb = rgb or random.choice(LIGHT_COLORS)
self._ct = ct or random.choice(LIGHT_TEMPS)
self._brightness = brightness
@property
@@ -50,9 +53,14 @@ class DemoLight(Light):
return self._brightness
@property
def color_xy(self):
""" XY color value. """
return self._xy
def rgb_color(self):
""" rgb color value. """
return self._rgb
@property
def color_temp(self):
""" CT color temperature. """
return self._ct
@property
def is_on(self):
@@ -63,8 +71,11 @@ class DemoLight(Light):
""" Turn the device on. """
self._state = True
if ATTR_XY_COLOR in kwargs:
self._xy = kwargs[ATTR_XY_COLOR]
if ATTR_RGB_COLOR in kwargs:
self._rgb = kwargs[ATTR_RGB_COLOR]
if ATTR_COLOR_TEMP in kwargs:
self._ct = kwargs[ATTR_COLOR_TEMP]
if ATTR_BRIGHTNESS in kwargs:
self._brightness = kwargs[ATTR_BRIGHTNESS]

View File

@@ -2,19 +2,25 @@
homeassistant.components.light.hue
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Hue lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.hue/
"""
import json
import logging
import os
import socket
from datetime import timedelta
from urllib.parse import urlparse
from homeassistant.loader import get_component
import homeassistant.util as util
import homeassistant.util.color as color_util
from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME
from homeassistant.components.light import (
Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT,
EFFECT_COLORLOOP)
Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_COLOR_TEMP,
ATTR_TRANSITION, ATTR_FLASH, FLASH_LONG, FLASH_SHORT,
ATTR_EFFECT, EFFECT_COLORLOOP, ATTR_RGB_COLOR)
REQUIREMENTS = ['phue==0.8']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
@@ -28,21 +34,37 @@ _CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
def _find_host_from_config(hass):
""" Attempt to detect host based on existing configuration. """
path = hass.config.path(PHUE_CONFIG_FILE)
if not os.path.isfile(path):
return None
try:
with open(path) as inp:
return next(json.loads(''.join(inp)).keys().__iter__())
except (ValueError, AttributeError, StopIteration):
# ValueError if can't parse as JSON
# AttributeError if JSON value is not a dict
# StopIteration if no keys
return None
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Gets the Hue lights. """
try:
# pylint: disable=unused-variable
import phue # noqa
except ImportError:
_LOGGER.exception("Error while importing dependency phue.")
return
if discovery_info is not None:
host = urlparse(discovery_info[1]).hostname
else:
host = config.get(CONF_HOST, None)
if host is None:
host = _find_host_from_config(hass)
if host is None:
_LOGGER.error('No host found in configuration')
return False
# Only act if we are not already configuring this host
if host in _CONFIGURING:
return
@@ -123,6 +145,7 @@ def request_configuration(host, hass, add_devices_callback):
return
# pylint: disable=unused-argument
def hue_configuration_callback(data):
""" Actions to do when our configuration callback is called. """
setup_bridge(host, hass, add_devices_callback)
@@ -162,10 +185,15 @@ class HueLight(Light):
return self.info['state']['bri']
@property
def color_xy(self):
def xy_color(self):
""" XY color value. """
return self.info['state'].get('xy')
@property
def color_temp(self):
""" CT color value. """
return self.info['state'].get('ct')
@property
def is_on(self):
""" True if device is on. """
@@ -187,6 +215,12 @@ class HueLight(Light):
if ATTR_XY_COLOR in kwargs:
command['xy'] = kwargs[ATTR_XY_COLOR]
elif ATTR_RGB_COLOR in kwargs:
command['xy'] = color_util.color_RGB_to_xy(
*(int(val) for val in kwargs[ATTR_RGB_COLOR]))
if ATTR_COLOR_TEMP in kwargs:
command['ct'] = kwargs[ATTR_COLOR_TEMP]
flash = kwargs.get(ATTR_FLASH)

View File

@@ -0,0 +1,126 @@
"""
homeassistant.components.light.hyperion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Hyperion remotes.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.hyperion/
"""
import logging
import socket
import json
from homeassistant.const import CONF_HOST
from homeassistant.components.light import (Light, ATTR_RGB_COLOR)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = []
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Sets up a Hyperion server remote """
host = config.get(CONF_HOST, None)
port = config.get("port", 19444)
device = Hyperion(host, port)
if device.setup():
add_devices_callback([device])
return True
else:
return False
class Hyperion(Light):
""" Represents a Hyperion remote """
def __init__(self, host, port):
self._host = host
self._port = port
self._name = host
self._is_available = True
self._rgb_color = [255, 255, 255]
@property
def name(self):
""" Return the hostname of the server. """
return self._name
@property
def rgb_color(self):
""" Last RGB color value set. """
return self._rgb_color
@property
def is_on(self):
""" True if the device is online. """
return self._is_available
def turn_on(self, **kwargs):
""" Turn the lights on. """
if self._is_available:
if ATTR_RGB_COLOR in kwargs:
self._rgb_color = kwargs[ATTR_RGB_COLOR]
self.json_request({"command": "color", "priority": 128,
"color": self._rgb_color})
def turn_off(self, **kwargs):
""" Disconnect the remote. """
self.json_request({"command": "clearall"})
def update(self):
""" Ping the remote. """
# just see if the remote port is open
self._is_available = self.json_request()
def setup(self):
""" Get the hostname of the remote. """
response = self.json_request({"command": "serverinfo"})
if response:
self._name = response["info"]["hostname"]
return True
return False
def json_request(self, request=None, wait_for_response=False):
""" Communicate with the json server. """
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
try:
sock.connect((self._host, self._port))
except OSError:
sock.close()
return False
if not request:
# no communication needed, simple presence detection returns True
sock.close()
return True
sock.send(bytearray(json.dumps(request) + "\n", "utf-8"))
try:
buf = sock.recv(4096)
except socket.timeout:
# something is wrong, assume it's offline
sock.close()
return False
# read until a newline or timeout
buffering = True
while buffering:
if "\n" in str(buf, "utf-8"):
response = str(buf, "utf-8").split("\n")[0]
buffering = False
else:
try:
more = sock.recv(4096)
except socket.timeout:
more = None
if not more:
buffering = False
response = str(buf, "utf-8")
else:
buf += more
sock.close()
return json.loads(response)

View File

@@ -2,6 +2,9 @@
homeassistant.components.light.isy994
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for ISY994 lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/isy994/
"""
import logging

View File

@@ -1,51 +1,74 @@
"""
homeassistant.components.light.limitlessled
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for LimitlessLED bulbs.
Support for LimitlessLED bulbs, also known as...
- EasyBulb
- AppLight
- AppLamp
- MiLight
- LEDme
- dekolight
- iLight
Configuration:
To use limitlessled you will need to add the following to your
configuration.yaml file.
light:
platform: limitlessled
host: 192.168.1.10
group_1_name: Living Room
group_2_name: Bedroom
group_3_name: Office
group_4_name: Kitchen
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.limitlessled/
"""
import logging
from homeassistant.const import DEVICE_DEFAULT_NAME
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS,
ATTR_XY_COLOR)
from homeassistant.util.color import color_RGB_to_xy
ATTR_RGB_COLOR, ATTR_EFFECT,
EFFECT_COLORLOOP, EFFECT_WHITE)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['ledcontroller==1.0.7']
REQUIREMENTS = ['ledcontroller==1.1.0']
COLOR_TABLE = {
'white': [0xFF, 0xFF, 0xFF],
'violet': [0xEE, 0x82, 0xEE],
'royal_blue': [0x41, 0x69, 0xE1],
'baby_blue': [0x87, 0xCE, 0xFA],
'aqua': [0x00, 0xFF, 0xFF],
'royal_mint': [0x7F, 0xFF, 0xD4],
'seafoam_green': [0x2E, 0x8B, 0x57],
'green': [0x00, 0x80, 0x00],
'lime_green': [0x32, 0xCD, 0x32],
'yellow': [0xFF, 0xFF, 0x00],
'yellow_orange': [0xDA, 0xA5, 0x20],
'orange': [0xFF, 0xA5, 0x00],
'red': [0xFF, 0x00, 0x00],
'pink': [0xFF, 0xC0, 0xCB],
'fusia': [0xFF, 0x00, 0xFF],
'lilac': [0xDA, 0x70, 0xD6],
'lavendar': [0xE6, 0xE6, 0xFA],
}
def _distance_squared(rgb1, rgb2):
""" Return sum of squared distances of each color part. """
return sum((val1-val2)**2 for val1, val2 in zip(rgb1, rgb2))
def _rgb_to_led_color(rgb_color):
""" Convert an RGB color to the closest color string and color. """
return sorted((_distance_squared(rgb_color, color), name)
for name, color in COLOR_TABLE.items())[0][1]
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Gets the LimitlessLED lights. """
import ledcontroller
led = ledcontroller.LedController(config['host'])
# Handle old configuration format:
bridges = config.get('bridges', [config])
for bridge_id, bridge in enumerate(bridges):
bridge['id'] = bridge_id
pool = ledcontroller.LedControllerPool([x['host'] for x in bridges])
lights = []
for i in range(1, 5):
if 'group_%d_name' % (i) in config:
lights.append(LimitlessLED(led, i, config['group_%d_name' % (i)]))
for bridge in bridges:
for i in range(1, 5):
name_key = 'group_%d_name' % i
if name_key in bridge:
group_type = bridge.get('group_%d_type' % i, 'rgbw')
lights.append(LimitlessLED.factory(pool, bridge['id'], i,
bridge[name_key],
group_type))
add_devices_callback(lights)
@@ -53,43 +76,32 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
class LimitlessLED(Light):
""" Represents a LimitlessLED light """
def __init__(self, led, group, name):
self.led = led
@staticmethod
def factory(pool, controller_id, group, name, group_type):
''' Construct a Limitless LED of the appropriate type '''
if group_type == 'white':
return WhiteLimitlessLED(pool, controller_id, group, name)
elif group_type == 'rgbw':
return RGBWLimitlessLED(pool, controller_id, group, name)
# pylint: disable=too-many-arguments
def __init__(self, pool, controller_id, group, name, group_type):
self.pool = pool
self.controller_id = controller_id
self.group = group
self.pool.execute(self.controller_id, "set_group_type", self.group,
group_type)
# LimitlessLEDs don't report state, we have track it ourselves.
self.led.off(self.group)
self.pool.execute(self.controller_id, "off", self.group)
self._name = name or DEVICE_DEFAULT_NAME
self._state = False
self._brightness = 100
self._xy_color = color_RGB_to_xy(255, 255, 255)
# Build a color table that maps an RGB color to a color string
# recognized by LedController's set_color method
self._color_table = [(color_RGB_to_xy(*x[0]), x[1]) for x in [
((0xFF, 0xFF, 0xFF), 'white'),
((0xEE, 0x82, 0xEE), 'violet'),
((0x41, 0x69, 0xE1), 'royal_blue'),
((0x87, 0xCE, 0xFA), 'baby_blue'),
((0x00, 0xFF, 0xFF), 'aqua'),
((0x7F, 0xFF, 0xD4), 'royal_mint'),
((0x2E, 0x8B, 0x57), 'seafoam_green'),
((0x00, 0x80, 0x00), 'green'),
((0x32, 0xCD, 0x32), 'lime_green'),
((0xFF, 0xFF, 0x00), 'yellow'),
((0xDA, 0xA5, 0x20), 'yellow_orange'),
((0xFF, 0xA5, 0x00), 'orange'),
((0xFF, 0x00, 0x00), 'red'),
((0xFF, 0xC0, 0xCB), 'pink'),
((0xFF, 0x00, 0xFF), 'fusia'),
((0xDA, 0x70, 0xD6), 'lilac'),
((0xE6, 0xE6, 0xFA), 'lavendar'),
]]
@property
def should_poll(self):
""" No polling needed for a demo light. """
""" No polling needed. """
return False
@property
@@ -97,30 +109,34 @@ class LimitlessLED(Light):
""" Returns the name of the device if any. """
return self._name
@property
def is_on(self):
""" True if device is on. """
return self._state
def turn_off(self, **kwargs):
""" Turn the device off. """
self._state = False
self.pool.execute(self.controller_id, "off", self.group)
self.update_ha_state()
class RGBWLimitlessLED(LimitlessLED):
""" Represents a RGBW LimitlessLED light """
def __init__(self, pool, controller_id, group, name):
super().__init__(pool, controller_id, group, name, 'rgbw')
self._brightness = 100
self._led_color = 'white'
@property
def brightness(self):
return self._brightness
@property
def color_xy(self):
return self._xy_color
def _xy_to_led_color(self, xy_color):
""" Convert an XY color to the closest LedController color string. """
def abs_dist_squared(p_0, p_1):
""" Returns the absolute value of the squared distance """
return abs((p_0[0] - p_1[0])**2 + (p_0[1] - p_1[1])**2)
candidates = [(abs_dist_squared(xy_color, x[0]), x[1]) for x in
self._color_table]
# First candidate in the sorted list is closest to desired color:
return sorted(candidates)[0][1]
@property
def is_on(self):
""" True if device is on. """
return self._state
def rgb_color(self):
return COLOR_TABLE[self._led_color]
def turn_on(self, **kwargs):
""" Turn the device on. """
@@ -129,15 +145,34 @@ class LimitlessLED(Light):
if ATTR_BRIGHTNESS in kwargs:
self._brightness = kwargs[ATTR_BRIGHTNESS]
if ATTR_XY_COLOR in kwargs:
self._xy_color = kwargs[ATTR_XY_COLOR]
if ATTR_RGB_COLOR in kwargs:
self._led_color = _rgb_to_led_color(kwargs[ATTR_RGB_COLOR])
effect = kwargs.get(ATTR_EFFECT)
if effect == EFFECT_COLORLOOP:
self.pool.execute(self.controller_id, "disco", self.group)
elif effect == EFFECT_WHITE:
self.pool.execute(self.controller_id, "white", self.group)
else:
self.pool.execute(self.controller_id, "set_color",
self._led_color, self.group)
# Brightness can be set independently of color
self.pool.execute(self.controller_id, "set_brightness",
self._brightness / 255.0, self.group)
self.led.set_color(self._xy_to_led_color(self._xy_color), self.group)
self.led.set_brightness(self._brightness / 255.0, self.group)
self.update_ha_state()
def turn_off(self, **kwargs):
""" Turn the device off. """
self._state = False
self.led.off(self.group)
class WhiteLimitlessLED(LimitlessLED):
""" Represents a White LimitlessLED light """
def __init__(self, pool, controller_id, group, name):
super().__init__(pool, controller_id, group, name, 'white')
def turn_on(self, **kwargs):
""" Turn the device on. """
self._state = True
self.pool.execute(self.controller_id, "on", self.group)
self.update_ha_state()

View File

@@ -0,0 +1,184 @@
"""
homeassistant.components.light.mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a MQTT light.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.mqtt/
"""
import logging
import homeassistant.util.color as color_util
import homeassistant.components.mqtt as mqtt
from homeassistant.components.light import (Light,
ATTR_BRIGHTNESS, ATTR_RGB_COLOR)
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "MQTT Light"
DEFAULT_QOS = 0
DEFAULT_PAYLOAD_ON = "on"
DEFAULT_PAYLOAD_OFF = "off"
DEFAULT_RGB_PATTERN = "%d,%d,%d"
DEFAULT_OPTIMISTIC = False
DEPENDENCIES = ['mqtt']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Add MQTT Light. """
if config.get('command_topic') is None:
_LOGGER.error("Missing required variable: command_topic")
return False
add_devices_callback([MqttLight(
hass,
config.get('name', DEFAULT_NAME),
{"state_topic": config.get('state_topic'),
"command_topic": config.get('command_topic'),
"brightness_state_topic": config.get('brightness_state_topic'),
"brightness_command_topic":
config.get('brightness_command_topic'),
"rgb_state_topic": config.get('rgb_state_topic'),
"rgb_command_topic": config.get('rgb_command_topic')},
config.get('rgb', None),
config.get('qos', DEFAULT_QOS),
{"on": config.get('payload_on', DEFAULT_PAYLOAD_ON),
"off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)},
config.get('brightness'),
config.get('optimistic', DEFAULT_OPTIMISTIC))])
# pylint: disable=too-many-instance-attributes
class MqttLight(Light):
""" Provides a MQTT light. """
# pylint: disable=too-many-arguments
def __init__(self, hass, name,
topic,
rgb, qos,
payload,
brightness, optimistic):
self._hass = hass
self._name = name
self._topic = topic
self._rgb = rgb
self._qos = qos
self._payload = payload
self._brightness = brightness
self._optimistic = optimistic
self._state = False
self._xy = None
def message_received(topic, payload, qos):
""" A new MQTT message has been received. """
if payload == self._payload["on"]:
self._state = True
elif payload == self._payload["off"]:
self._state = False
self.update_ha_state()
if self._topic["state_topic"] is None:
# force optimistic mode
self._optimistic = True
else:
# Subscribe the state_topic
mqtt.subscribe(self._hass, self._topic["state_topic"],
message_received, self._qos)
def brightness_received(topic, payload, qos):
""" A new MQTT message for the brightness has been received. """
self._brightness = int(payload)
self.update_ha_state()
def rgb_received(topic, payload, qos):
""" A new MQTT message has been received. """
self._rgb = [int(val) for val in payload.split(',')]
self._xy = color_util.color_RGB_to_xy(int(self._rgb[0]),
int(self._rgb[1]),
int(self._rgb[2]))
self.update_ha_state()
if self._topic["brightness_state_topic"] is not None:
mqtt.subscribe(self._hass, self._topic["brightness_state_topic"],
brightness_received, self._qos)
self._brightness = 255
else:
self._brightness = None
if self._topic["rgb_state_topic"] is not None:
mqtt.subscribe(self._hass, self._topic["rgb_state_topic"],
rgb_received, self._qos)
self._xy = [0, 0]
else:
self._xy = None
@property
def brightness(self):
""" Brightness of this light between 0..255. """
return self._brightness
@property
def rgb_color(self):
""" RGB color value. """
return self._rgb
@property
def color_xy(self):
""" RGB color value. """
return self._xy
@property
def should_poll(self):
""" No polling needed for a MQTT light. """
return False
@property
def name(self):
""" Returns the name of the device if any. """
return self._name
@property
def is_on(self):
""" True if device is on. """
return self._state
def turn_on(self, **kwargs):
""" Turn the device on. """
if ATTR_RGB_COLOR in kwargs and \
self._topic["rgb_command_topic"] is not None:
self._rgb = kwargs[ATTR_RGB_COLOR]
rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb)
mqtt.publish(self._hass, self._topic["rgb_command_topic"],
rgb, self._qos)
if ATTR_BRIGHTNESS in kwargs and \
self._topic["brightness_command_topic"] is not None:
self._brightness = kwargs[ATTR_BRIGHTNESS]
mqtt.publish(self._hass, self._topic["brightness_command_topic"],
self._brightness, self._qos)
mqtt.publish(self._hass, self._topic["command_topic"],
self._payload["on"], self._qos)
if self._optimistic:
# optimistically assume that switch has changed state
self._state = True
self.update_ha_state()
def turn_off(self, **kwargs):
""" Turn the device off. """
mqtt.publish(self._hass, self._topic["command_topic"],
self._payload["off"], self._qos)
if self._optimistic:
# optimistically assume that switch has changed state
self._state = False
self.update_ha_state()

View File

@@ -0,0 +1,117 @@
"""
homeassistant.components.light.rfxtrx
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for RFXtrx lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.rfxtrx/
"""
import logging
import homeassistant.components.rfxtrx as rfxtrx
import RFXtrx as rfxtrxmod
from homeassistant.components.light import Light
from homeassistant.util import slugify
DEPENDENCIES = ['rfxtrx']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Setup the RFXtrx platform. """
lights = []
devices = config.get('devices', None)
if devices:
for entity_id, entity_info in devices.items():
if entity_id not in rfxtrx.RFX_DEVICES:
_LOGGER.info("Add %s rfxtrx.light", entity_info['name'])
rfxobject = rfxtrx.get_rfx_object(entity_info['packetid'])
new_light = RfxtrxLight(entity_info['name'], rfxobject, False)
rfxtrx.RFX_DEVICES[entity_id] = new_light
lights.append(new_light)
add_devices_callback(lights)
def light_update(event):
""" Callback for light updates from the RFXtrx gateway. """
if not isinstance(event.device, rfxtrxmod.LightingDevice):
return
# Add entity if not exist and the automatic_add is True
entity_id = slugify(event.device.id_string.lower())
if entity_id not in rfxtrx.RFX_DEVICES:
automatic_add = config.get('automatic_add', False)
if not automatic_add:
return
_LOGGER.info(
"Automatic add %s rfxtrx.light (Class: %s Sub: %s)",
entity_id,
event.device.__class__.__name__,
event.device.subtype
)
pkt_id = "".join("{0:02x}".format(x) for x in event.data)
entity_name = "%s : %s" % (entity_id, pkt_id)
new_light = RfxtrxLight(entity_name, event, False)
rfxtrx.RFX_DEVICES[entity_id] = new_light
add_devices_callback([new_light])
# Check if entity exists or previously added automatically
if entity_id in rfxtrx.RFX_DEVICES:
_LOGGER.debug(
"EntityID: %s light_update. Command: %s",
entity_id,
event.values['Command']
)
if event.values['Command'] == 'On'\
or event.values['Command'] == 'Off':
if event.values['Command'] == 'On':
rfxtrx.RFX_DEVICES[entity_id].turn_on()
else:
rfxtrx.RFX_DEVICES[entity_id].turn_off()
# Subscribe to main rfxtrx events
if light_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS:
rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(light_update)
class RfxtrxLight(Light):
""" Provides a RFXtrx light. """
def __init__(self, name, event, state):
self._name = name
self._event = event
self._state = state
@property
def should_poll(self):
""" No polling needed for a light. """
return False
@property
def name(self):
""" Returns the name of the light if any. """
return self._name
@property
def is_on(self):
""" True if light is on. """
return self._state
def turn_on(self, **kwargs):
""" Turn the light on. """
if hasattr(self, '_event') and self._event:
self._event.device.send_on(rfxtrx.RFXOBJECT.transport)
self._state = True
self.update_ha_state()
def turn_off(self, **kwargs):
""" Turn the light off. """
if hasattr(self, '_event') and self._event:
self._event.device.send_off(rfxtrx.RFXOBJECT.transport)
self._state = False
self.update_ha_state()

View File

@@ -0,0 +1,56 @@
# Describes the format for available light services
turn_on:
description: Turn a light on
fields:
entity_id:
description: Name(s) of entities to turn on
example: 'light.kitchen'
transition:
description: Duration in seconds it takes to get to next state
example: 60
rgb_color:
description: Color for the light in RGB-format
example: '[255, 100, 100]'
xy_color:
description: Color for the light in XY-format
example: '[0.52, 0.43]'
color_temp:
description: Color temperature for the light in mireds (154-500)
example: '250'
brightness:
description: Number between 0..255 indicating brightness
example: 120
profile:
description: Name of a light profile to use
example: relax
flash:
description: If the light should flash
values:
- short
- long
effect:
description: Light effect
values:
- colorloop
turn_off:
description: Turn a light off
fields:
entity_id:
description: Name(s) of entities to turn off
example: 'light.kitchen'
transition:
description: Duration in seconds it takes to get to next state
example: 60

View File

@@ -2,16 +2,21 @@
homeassistant.components.light.tellstick
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Tellstick lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.tellstick/
"""
import logging
# pylint: disable=no-name-in-module, import-error
from homeassistant.components.light import Light, ATTR_BRIGHTNESS
from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.const import (EVENT_HOMEASSISTANT_STOP,
ATTR_FRIENDLY_NAME)
import tellcore.constants as tellcore_constants
REQUIREMENTS = ['tellcore-py==1.0.4']
from tellcore.library import DirectCallbackDispatcher
REQUIREMENTS = ['tellcore-py==1.1.2']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return Tellstick lights. """
@@ -22,13 +27,32 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"Failed to import tellcore")
return []
core = telldus.TelldusCore()
core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher())
switches_and_lights = core.devices()
lights = []
for switch in switches_and_lights:
if switch.methods(tellcore_constants.TELLSTICK_DIM):
lights.append(TellstickLight(switch))
def _device_event_callback(id_, method, data, cid):
""" Called from the TelldusCore library to update one device """
for light_device in lights:
if light_device.tellstick_device.id == id_:
# Execute the update in another thread
light_device.update_ha_state(True)
break
callback_id = core.register_device_event(_device_event_callback)
def unload_telldus_lib(event):
""" Un-register the callback bindings """
if callback_id is not None:
core.unregister_callback(callback_id)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, unload_telldus_lib)
add_devices_callback(lights)
@@ -40,15 +64,15 @@ class TellstickLight(Light):
tellcore_constants.TELLSTICK_UP |
tellcore_constants.TELLSTICK_DOWN)
def __init__(self, tellstick):
self.tellstick = tellstick
self.state_attr = {ATTR_FRIENDLY_NAME: tellstick.name}
def __init__(self, tellstick_device):
self.tellstick_device = tellstick_device
self.state_attr = {ATTR_FRIENDLY_NAME: tellstick_device.name}
self._brightness = 0
@property
def name(self):
""" Returns the name of the switch if any. """
return self.tellstick.name
return self.tellstick_device.name
@property
def is_on(self):
@@ -62,8 +86,9 @@ class TellstickLight(Light):
def turn_off(self, **kwargs):
""" Turns the switch off. """
self.tellstick.turn_off()
self.tellstick_device.turn_off()
self._brightness = 0
self.update_ha_state()
def turn_on(self, **kwargs):
""" Turns the switch on. """
@@ -74,11 +99,12 @@ class TellstickLight(Light):
else:
self._brightness = brightness
self.tellstick.dim(self._brightness)
self.tellstick_device.dim(self._brightness)
self.update_ha_state()
def update(self):
""" Update state of the light. """
last_command = self.tellstick.last_sent_command(
last_command = self.tellstick_device.last_sent_command(
self.last_sent_command_mask)
if last_command == tellcore_constants.TELLSTICK_TURNON:
@@ -88,6 +114,11 @@ class TellstickLight(Light):
elif (last_command == tellcore_constants.TELLSTICK_DIM or
last_command == tellcore_constants.TELLSTICK_UP or
last_command == tellcore_constants.TELLSTICK_DOWN):
last_sent_value = self.tellstick.last_sent_value()
last_sent_value = self.tellstick_device.last_sent_value()
if last_sent_value is not None:
self._brightness = last_sent_value
@property
def should_poll(self):
""" Tells Home Assistant not to poll this entity. """
return False

View File

@@ -1,60 +1,22 @@
"""
homeassistant.components.light.vera
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Vera lights. This component is useful if you wish for switches
connected to your Vera controller to appear as lights in Home Assistant.
All switches will be added as a light unless you exclude them in the config.
Configuration:
To use the Vera lights you will need to add something like the following to
your configuration.yaml file.
light:
platform: vera
vera_controller_url: http://YOUR_VERA_IP:3480/
device_data:
12:
name: My awesome switch
exclude: true
13:
name: Another switch
Variables:
vera_controller_url
*Required
This is the base URL of your vera controller including the port number if not
running on 80. Example: http://192.168.1.21:3480/
device_data
*Optional
This contains an array additional device info for your Vera devices. It is not
required and if not specified all lights configured in your Vera controller
will be added with default values. You should use the id of your vera device
as the key for the device within device_data.
These are the variables for the device_data array:
name
*Optional
This parameter allows you to override the name of your Vera device in the HA
interface, if not specified the value configured for the device in your Vera
will be used.
exclude
*Optional
This parameter allows you to exclude the specified device from Home Assistant,
it should be set to "true" if you want this device excluded.
Support for Vera lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.vera/
"""
import logging
import time
from requests.exceptions import RequestException
from homeassistant.components.switch.vera import VeraSwitch
REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/'
'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip'
'#python-vera==0.1']
from homeassistant.components.light import ATTR_BRIGHTNESS
REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/'
'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip'
'#python-vera==0.1.1']
_LOGGER = logging.getLogger(__name__)
@@ -77,7 +39,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
controller = veraApi.VeraController(base_url)
devices = []
try:
devices = controller.get_devices(['Switch', 'On/Off Switch'])
devices = controller.get_devices([
'Switch',
'On/Off Switch',
'Dimmable Switch'])
except RequestException:
# There was a network related error connecting to the vera controller
_LOGGER.exception("Error communicating with Vera API")
@@ -89,6 +54,28 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
exclude = extra_data.get('exclude', False)
if exclude is not True:
lights.append(VeraSwitch(device, extra_data))
lights.append(VeraLight(device, extra_data))
add_devices_callback(lights)
class VeraLight(VeraSwitch):
""" Represents a Vera Light, including dimmable. """
@property
def state_attributes(self):
attr = super().state_attributes or {}
if self.vera_device.is_dimmable:
attr[ATTR_BRIGHTNESS] = self.vera_device.get_brightness()
return attr
def turn_on(self, **kwargs):
if ATTR_BRIGHTNESS in kwargs and self.vera_device.is_dimmable:
self.vera_device.set_brightness(kwargs[ATTR_BRIGHTNESS])
else:
self.vera_device.switch_on()
self.last_command_send = time.time()
self.is_on_status = True

View File

@@ -2,6 +2,9 @@
homeassistant.components.light.wink
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Wink lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.wink/
"""
import logging
@@ -10,8 +13,8 @@ from homeassistant.components.wink import WinkToggleDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/'
'c2b700e8ca866159566ecf5e644d9c297f69f257.zip'
'#python-wink==0.1']
'9eb39eaba0717922815e673ad1114c685839d890.zip'
'#python-wink==0.1.1']
def setup_platform(hass, config, add_devices_callback, discovery_info=None):

View File

@@ -0,0 +1,126 @@
"""
homeassistant.components.light.zwave
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Z-Wave lights.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.zwave/
"""
# pylint: disable=import-error
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
import homeassistant.components.zwave as zwave
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS)
from threading import Timer
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Find and add Z-Wave lights. """
if discovery_info is None:
return
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL:
return
if value.type != zwave.TYPE_BYTE:
return
if value.genre != zwave.GENRE_USER:
return
value.set_change_verified(False)
add_devices([ZwaveDimmer(value)])
def brightness_state(value):
"""
Returns the brightness and state according to the current data of given
value.
"""
if value.data > 0:
return (value.data / 99) * 255, STATE_ON
else:
return 255, STATE_OFF
class ZwaveDimmer(Light):
""" Provides a Z-Wave dimmer. """
# pylint: disable=too-many-arguments
def __init__(self, value):
self._value = value
self._node = value.node
self._brightness, self._state = brightness_state(value)
# Used for value change event handling
self._refreshing = False
self._timer = None
dispatcher.connect(
self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
def _value_changed(self, value):
""" Called when a value has changed on the network. """
if self._value.value_id != value.value_id:
return
if self._refreshing:
self._refreshing = False
self._brightness, self._state = brightness_state(value)
else:
def _refresh_value():
"""Used timer callback for delayed value refresh."""
self._refreshing = True
self._value.refresh()
if self._timer is not None and self._timer.isAlive():
self._timer.cancel()
self._timer = Timer(2, _refresh_value)
self._timer.start()
self.update_ha_state()
@property
def should_poll(self):
""" No polling needed for a light. """
return False
@property
def name(self):
""" Returns the name of the device if any. """
name = self._node.name or "{}".format(self._node.product_name)
return "{}".format(name or self._value.label)
@property
def brightness(self):
""" Brightness of this light between 0..255. """
return self._brightness
@property
def is_on(self):
""" True if device is on. """
return self._state == STATE_ON
def turn_on(self, **kwargs):
""" Turn the device on. """
if ATTR_BRIGHTNESS in kwargs:
self._brightness = kwargs[ATTR_BRIGHTNESS]
# Zwave multilevel switches use a range of [0, 99] to control
# brightness.
brightness = (self._brightness / 255) * 99
if self._node.set_dimmer(self._value.value_id, brightness):
self._state = STATE_ON
def turn_off(self, **kwargs):
""" Turn the device off. """
if self._node.set_dimmer(self._value.value_id, 0):
self._state = STATE_OFF

View File

@@ -1,8 +1,10 @@
"""
homeassistant.components.logbook
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Parses events and generates a human log.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/logbook/
"""
from datetime import timedelta
from itertools import groupby
@@ -10,7 +12,7 @@ import re
from homeassistant.core import State, DOMAIN as HA_DOMAIN
from homeassistant.const import (
EVENT_STATE_CHANGED, STATE_HOME, STATE_ON, STATE_OFF,
EVENT_STATE_CHANGED, STATE_NOT_HOME, STATE_ON, STATE_OFF,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, HTTP_BAD_REQUEST)
from homeassistant import util
import homeassistant.util.dt as dt_util
@@ -162,10 +164,12 @@ def humanify(events):
to_state = State.from_dict(event.data.get('new_state'))
# if last_changed == last_updated only attributes have changed
# we do not report on that yet.
# if last_changed != last_updated only attributes have changed
# we do not report on that yet. Also filter auto groups.
if not to_state or \
to_state.last_changed != to_state.last_updated:
to_state.last_changed != to_state.last_updated or \
to_state.domain == 'group' and \
to_state.attributes.get('auto', False):
continue
domain = to_state.domain
@@ -218,10 +222,13 @@ def humanify(events):
def _entry_message_from_state(domain, state):
""" Convert a state to a message for the logbook. """
# We pass domain in so we don't have to split entity_id again
# pylint: disable=too-many-return-statements
if domain == 'device_tracker':
return '{} home'.format(
'arrived' if state.state == STATE_HOME else 'left')
if state.state == STATE_NOT_HOME:
return 'is away'
else:
return 'is at {}'.format(state.state)
elif domain == 'sun':
if state.state == sun.STATE_ABOVE_HORIZON:

View File

@@ -0,0 +1,84 @@
"""
homeassistant.components.logger
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component that will help set the level of logging for components.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/logger/
"""
import logging
from collections import OrderedDict
DOMAIN = 'logger'
DEPENDENCIES = []
LOGSEVERITY = {
'CRITICAL': 50,
'FATAL': 50,
'ERROR': 40,
'WARNING': 30,
'WARN': 30,
'INFO': 20,
'DEBUG': 10,
'NOTSET': 0
}
LOGGER_DEFAULT = 'default'
LOGGER_LOGS = 'logs'
class HomeAssistantLogFilter(logging.Filter):
""" A log filter. """
# pylint: disable=no-init,too-few-public-methods
def __init__(self, logfilter):
super().__init__()
self.logfilter = logfilter
def filter(self, record):
# Log with filtered severity
if LOGGER_LOGS in self.logfilter:
for filtername in self.logfilter[LOGGER_LOGS]:
logseverity = self.logfilter[LOGGER_LOGS][filtername]
if record.name.startswith(filtername):
return record.levelno >= logseverity
# Log with default severity
default = self.logfilter[LOGGER_DEFAULT]
return record.levelno >= default
def setup(hass, config=None):
""" Setup the logger component. """
logfilter = dict()
# Set default log severity
logfilter[LOGGER_DEFAULT] = LOGSEVERITY['DEBUG']
if LOGGER_DEFAULT in config.get(DOMAIN):
logfilter[LOGGER_DEFAULT] = LOGSEVERITY[
config.get(DOMAIN)[LOGGER_DEFAULT].upper()
]
# Compute log severity for components
if LOGGER_LOGS in config.get(DOMAIN):
for key, value in config.get(DOMAIN)[LOGGER_LOGS].items():
config.get(DOMAIN)[LOGGER_LOGS][key] = LOGSEVERITY[value.upper()]
logs = OrderedDict(
sorted(
config.get(DOMAIN)[LOGGER_LOGS].items(),
key=lambda t: len(t[0]),
reverse=True
)
)
logfilter[LOGGER_LOGS] = logs
# Set log filter for all log handler
for handler in logging.root.handlers:
handler.addFilter(HomeAssistantLogFilter(logfilter))
return True

View File

@@ -1,12 +1,16 @@
"""
homeassistant.components.media_player
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with various media players.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/media_player/
"""
import logging
import os
from homeassistant.components import discovery
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.const import (
@@ -26,9 +30,11 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
DISCOVERY_PLATFORMS = {
discovery.SERVICE_CAST: 'cast',
discovery.SERVICE_SONOS: 'sonos',
discovery.SERVICE_PLEX: 'plex',
}
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
SERVICE_PLAY_MEDIA = 'play_media'
ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
ATTR_MEDIA_VOLUME_MUTED = 'is_volume_muted'
@@ -44,6 +50,8 @@ ATTR_MEDIA_TRACK = 'media_track'
ATTR_MEDIA_SERIES_TITLE = 'media_series_title'
ATTR_MEDIA_SEASON = 'media_season'
ATTR_MEDIA_EPISODE = 'media_episode'
ATTR_MEDIA_CHANNEL = 'media_channel'
ATTR_MEDIA_PLAYLIST = 'media_playlist'
ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name'
ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands'
@@ -51,6 +59,9 @@ ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands'
MEDIA_TYPE_MUSIC = 'music'
MEDIA_TYPE_TVSHOW = 'tvshow'
MEDIA_TYPE_VIDEO = 'movie'
MEDIA_TYPE_EPISODE = 'episode'
MEDIA_TYPE_CHANNEL = 'channel'
MEDIA_TYPE_PLAYLIST = 'playlist'
SUPPORT_PAUSE = 1
SUPPORT_SEEK = 2
@@ -61,6 +72,7 @@ SUPPORT_NEXT_TRACK = 32
SUPPORT_YOUTUBE = 64
SUPPORT_TURN_ON = 128
SUPPORT_TURN_OFF = 256
SUPPORT_PLAY_MEDIA = 512
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
@@ -74,6 +86,7 @@ SERVICE_TO_METHOD = {
SERVICE_MEDIA_PAUSE: 'media_pause',
SERVICE_MEDIA_NEXT_TRACK: 'media_next_track',
SERVICE_MEDIA_PREVIOUS_TRACK: 'media_previous_track',
SERVICE_PLAY_MEDIA: 'play_media',
}
ATTR_TO_PROPERTY = [
@@ -90,6 +103,8 @@ ATTR_TO_PROPERTY = [
ATTR_MEDIA_SERIES_TITLE,
ATTR_MEDIA_SEASON,
ATTR_MEDIA_EPISODE,
ATTR_MEDIA_CHANNEL,
ATTR_MEDIA_PLAYLIST,
ATTR_APP_ID,
ATTR_APP_NAME,
ATTR_SUPPORTED_MEDIA_COMMANDS,
@@ -178,6 +193,16 @@ def media_previous_track(hass, entity_id=None):
hass.services.call(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data)
def play_media(hass, media_type, media_id, entity_id=None):
""" Send the media player the command for playing media. """
data = {"media_type": media_type, "media_id": media_id}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_PLAY_MEDIA, data)
def setup(hass, config):
""" Track states and offer events for media_players. """
component = EntityComponent(
@@ -186,6 +211,9 @@ def setup(hass, config):
component.setup(config)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
def media_player_service_handler(service):
""" Maps services to methods on MediaPlayerDevice. """
target_players = component.extract_from_service(service)
@@ -199,7 +227,8 @@ def setup(hass, config):
player.update_ha_state(True)
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, media_player_service_handler)
hass.services.register(DOMAIN, service, media_player_service_handler,
descriptions.get(service))
def volume_set_service(service):
""" Set specified volume on the media player. """
@@ -216,7 +245,8 @@ def setup(hass, config):
if player.should_poll:
player.update_ha_state(True)
hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service)
hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service,
descriptions.get(SERVICE_VOLUME_SET))
def volume_mute_service(service):
""" Mute (true) or unmute (false) the media player. """
@@ -233,7 +263,8 @@ def setup(hass, config):
if player.should_poll:
player.update_ha_state(True)
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service)
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service,
descriptions.get(SERVICE_VOLUME_MUTE))
def media_seek_service(service):
""" Seek to a position. """
@@ -250,7 +281,8 @@ def setup(hass, config):
if player.should_poll:
player.update_ha_state(True)
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service)
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service,
descriptions.get(SERVICE_MEDIA_SEEK))
def play_youtube_video_service(service, media_id=None):
""" Plays specified media_id on the media player. """
@@ -266,16 +298,40 @@ def setup(hass, config):
if player.should_poll:
player.update_ha_state(True)
def play_media_service(service):
""" Plays specified media_id on the media player. """
media_type = service.data.get('media_type')
media_id = service.data.get('media_id')
if media_type is None:
return
if media_id is None:
return
for player in component.extract_from_service(service):
player.play_media(media_type, media_id)
if player.should_poll:
player.update_ha_state(True)
hass.services.register(
DOMAIN, "start_fireplace",
lambda service: play_youtube_video_service(service, "eyU3bRy2x44"))
lambda service: play_youtube_video_service(service, "eyU3bRy2x44"),
descriptions.get('start_fireplace'))
hass.services.register(
DOMAIN, "start_epic_sax",
lambda service: play_youtube_video_service(service, "kxopViU98Xo"))
lambda service: play_youtube_video_service(service, "kxopViU98Xo"),
descriptions.get('start_epic_sax'))
hass.services.register(
DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service)
DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service,
descriptions.get(SERVICE_YOUTUBE_VIDEO))
hass.services.register(
DOMAIN, SERVICE_PLAY_MEDIA, play_media_service,
descriptions.get(SERVICE_PLAY_MEDIA))
return True
@@ -361,6 +417,16 @@ class MediaPlayerDevice(Entity):
""" Episode of current playing media. (TV Show only) """
return None
@property
def media_channel(self):
""" Channel currently playing. """
return None
@property
def media_playlist(self):
""" Title of Playlist currently playing. """
return None
@property
def app_id(self):
""" ID of the current running app. """
@@ -421,6 +487,10 @@ class MediaPlayerDevice(Entity):
""" Plays a YouTube media. """
raise NotImplementedError()
def play_media(self, media_type, media_id):
""" Plays a piece of media. """
raise NotImplementedError()
# No need to overwrite these.
@property
def support_pause(self):
@@ -457,6 +527,11 @@ class MediaPlayerDevice(Entity):
""" Boolean if YouTube is supported. """
return bool(self.supported_media_commands & SUPPORT_YOUTUBE)
@property
def support_play_media(self):
""" Boolean if play media command supported. """
return bool(self.supported_media_commands & SUPPORT_PLAY_MEDIA)
def volume_up(self):
""" volume_up media player. """
if self.volume_level < 1:

View File

@@ -3,22 +3,8 @@ homeassistant.components.media_player.chromecast
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to interact with Cast devices on the network.
WARNING: This platform is currently not working due to a changed Cast API.
Configuration:
To use the chromecast integration you will need to add something like the
following to your configuration.yaml file.
media_player:
platform: chromecast
host: 192.168.1.9
Variables:
host
*Optional
Use only if you don't want to scan for devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.cast/
"""
import logging
@@ -90,6 +76,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class CastDevice(MediaPlayerDevice):
""" Represents a Cast device on the network. """
# pylint: disable=abstract-method
# pylint: disable=too-many-public-methods
def __init__(self, host):

View File

@@ -2,42 +2,9 @@
homeassistant.components.media_player.denon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to Denon Network Receivers.
Developed for a Denon DRA-N5, see
http://www.denon.co.uk/chg/product/compactsystems/networkmusicsystems/ceolpiccolo
A few notes:
- As long as this module is active and connected, the receiver does
not seem to accept additional telnet connections.
- Be careful with the volume. 50% or even 100% are very loud.
- To be able to wake up the receiver, activate the "remote" setting
in the receiver's settings.
- Play and pause are supported, toggling is not possible.
- Seeking cannot be implemented as the UI sends absolute positions.
Only seeking via simulated button presses is possible.
Configuration:
To use your Denon you will need to add something like the following to
your config/configuration.yaml:
media_player:
platform: denon
name: Music station
host: 192.168.0.123
Variables:
host
*Required
The ip of the player. Example: 192.168.0.123
name
*Optional
The name of the device.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.denon/
"""
import telnetlib
import logging
@@ -67,13 +34,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
CONF_HOST)
return False
add_devices([
DenonDevice(
config.get('name', 'Music station'),
config.get('host'))
])
return True
denon = DenonDevice(
config.get("name", "Music station"),
config.get("host")
)
if denon.update():
add_devices([denon])
return True
else:
return False
class DenonDevice(MediaPlayerDevice):
@@ -84,28 +53,41 @@ class DenonDevice(MediaPlayerDevice):
def __init__(self, name, host):
self._name = name
self._host = host
self._telnet = telnetlib.Telnet(self._host)
self._pwstate = "PWSTANDBY"
self._volume = 0
self._muted = False
self._mediasource = ""
def query(self, message):
""" Send request and await response from server """
@classmethod
def telnet_request(cls, telnet, command):
""" Executes `command` and returns the response. """
telnet.write(command.encode("ASCII") + b"\r")
return telnet.read_until(b"\r", timeout=0.2).decode("ASCII").strip()
def telnet_command(self, command):
""" Establishes a telnet connection and sends `command`. """
telnet = telnetlib.Telnet(self._host)
telnet.write(command.encode("ASCII") + b"\r")
telnet.read_very_eager() # skip response
telnet.close()
def update(self):
try:
# unspecified command, should be ignored
self._telnet.write("?".encode('UTF-8') + b'\r')
except (EOFError, BrokenPipeError, ConnectionResetError):
self._telnet.open(self._host)
telnet = telnetlib.Telnet(self._host)
except ConnectionRefusedError:
return False
self._telnet.read_very_eager() # skip what is not requested
self._pwstate = self.telnet_request(telnet, "PW?")
# PW? sends also SISTATUS, which is not interesting
telnet.read_until(b"\r", timeout=0.2)
self._telnet.write(message.encode('ASCII') + b'\r')
# timeout 200ms, defined by protocol
resp = self._telnet.read_until(b'\r', timeout=0.2)\
.decode('UTF-8').strip()
volume_str = self.telnet_request(telnet, "MV?")[len("MV"):]
self._volume = int(volume_str) / 60
self._muted = (self.telnet_request(telnet, "MU?") == "MUON")
self._mediasource = self.telnet_request(telnet, "SI?")[len("SI"):]
if message == "PW?":
# workaround; PW? sends also SISTATUS
self._telnet.read_until(b'\r', timeout=0.2)
return resp
telnet.close()
return True
@property
def name(self):
@@ -115,10 +97,9 @@ class DenonDevice(MediaPlayerDevice):
@property
def state(self):
""" Returns the state of the device. """
pwstate = self.query('PW?')
if pwstate == "PWSTANDBY":
if self._pwstate == "PWSTANDBY":
return STATE_OFF
if pwstate == "PWON":
if self._pwstate == "PWON":
return STATE_ON
return STATE_UNKNOWN
@@ -126,17 +107,17 @@ class DenonDevice(MediaPlayerDevice):
@property
def volume_level(self):
""" Volume level of the media player (0..1). """
return int(self.query('MV?')[len('MV'):]) / 60
return self._volume
@property
def is_volume_muted(self):
""" Boolean if volume is currently muted. """
return self.query('MU?') == "MUON"
return self._muted
@property
def media_title(self):
""" Current media source. """
return self.query('SI?')[len('SI'):]
return self._mediasource
@property
def supported_media_commands(self):
@@ -145,24 +126,24 @@ class DenonDevice(MediaPlayerDevice):
def turn_off(self):
""" turn_off media player. """
self.query('PWSTANDBY')
self.telnet_command("PWSTANDBY")
def volume_up(self):
""" volume_up media player. """
self.query('MVUP')
self.telnet_command("MVUP")
def volume_down(self):
""" volume_down media player. """
self.query('MVDOWN')
self.telnet_command("MVDOWN")
def set_volume_level(self, volume):
""" set volume level, range 0..1. """
# 60dB max
self.query('MV' + str(round(volume * 60)).zfill(2))
self.telnet_command("MV" + str(round(volume * 60)).zfill(2))
def mute_volume(self, mute):
""" mute (true) or unmute (false) media player. """
self.query('MU' + ('ON' if mute else 'OFF'))
self.telnet_command("MU" + ("ON" if mute else "OFF"))
def media_play_pause(self):
""" media_play_pause media player. """
@@ -170,22 +151,22 @@ class DenonDevice(MediaPlayerDevice):
def media_play(self):
""" media_play media player. """
self.query('NS9A')
self.telnet_command("NS9A")
def media_pause(self):
""" media_pause media player. """
self.query('NS9B')
self.telnet_command("NS9B")
def media_next_track(self):
""" Send next track command. """
self.query('NS9D')
self.telnet_command("NS9D")
def media_previous_track(self):
self.query('NS9E')
self.telnet_command("NS9E")
def media_seek(self, position):
raise NotImplementedError()
def turn_on(self):
""" turn the media player on. """
self.query('PWON')
self.telnet_command("PWON")

View File

@@ -0,0 +1,190 @@
"""
homeassistant.components.media_player.firetv
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to interact with FireTV devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.firetv/
"""
import logging
import requests
from homeassistant.const import (
STATE_PLAYING, STATE_PAUSED, STATE_IDLE, STATE_OFF,
STATE_UNKNOWN, STATE_STANDBY)
from homeassistant.components.media_player import (
MediaPlayerDevice,
SUPPORT_PAUSE, SUPPORT_VOLUME_SET,
SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK)
SUPPORT_FIRETV = SUPPORT_PAUSE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \
SUPPORT_NEXT_TRACK | SUPPORT_VOLUME_SET
DOMAIN = 'firetv'
DEVICE_LIST_URL = 'http://{0}/devices/list'
DEVICE_STATE_URL = 'http://{0}/devices/state/{1}'
DEVICE_ACTION_URL = 'http://{0}/devices/action/{1}/{2}'
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the FireTV platform. """
host = config.get('host', 'localhost:5556')
device_id = config.get('device', 'default')
try:
response = requests.get(DEVICE_LIST_URL.format(host)).json()
if device_id in response['devices'].keys():
add_devices([
FireTVDevice(
host,
device_id,
config.get('name', 'Amazon Fire TV')
)
])
_LOGGER.info(
'Device %s accessible and ready for control', device_id)
else:
_LOGGER.warn(
'Device %s is not registered with firetv-server', device_id)
except requests.exceptions.RequestException:
_LOGGER.error('Could not connect to firetv-server at %s', host)
class FireTV(object):
""" firetv-server client.
Should a native Python 3 ADB module become available, python-firetv can
support Python 3, it can be added as a dependency, and this class can be
dispensed of.
For now, it acts as a client to the firetv-server HTTP server (which must
be running via Python 2).
"""
def __init__(self, host, device_id):
self.host = host
self.device_id = device_id
@property
def state(self):
""" Get the device state. An exception means UNKNOWN state. """
try:
response = requests.get(
DEVICE_STATE_URL.format(
self.host,
self.device_id
)
).json()
return response.get('state', STATE_UNKNOWN)
except requests.exceptions.RequestException:
_LOGGER.error(
'Could not retrieve device state for %s', self.device_id)
return STATE_UNKNOWN
def action(self, action_id):
""" Perform an action on the device. """
try:
requests.get(
DEVICE_ACTION_URL.format(
self.host,
self.device_id,
action_id
)
)
except requests.exceptions.RequestException:
_LOGGER.error(
'Action request for %s was not accepted for device %s',
action_id, self.device_id)
class FireTVDevice(MediaPlayerDevice):
""" Represents an Amazon Fire TV device on the network. """
def __init__(self, host, device, name):
self._firetv = FireTV(host, device)
self._name = name
self._state = STATE_UNKNOWN
@property
def name(self):
""" Get the device name. """
return self._name
@property
def should_poll(self):
""" Device should be polled. """
return True
@property
def supported_media_commands(self):
""" Flags of media commands that are supported. """
return SUPPORT_FIRETV
@property
def state(self):
""" State of the player. """
return self._state
def update(self):
""" Update device state. """
self._state = {
'idle': STATE_IDLE,
'off': STATE_OFF,
'play': STATE_PLAYING,
'pause': STATE_PAUSED,
'standby': STATE_STANDBY,
'disconnected': STATE_UNKNOWN,
}.get(self._firetv.state, STATE_UNKNOWN)
def turn_on(self):
""" Turns on the device. """
self._firetv.action('turn_on')
def turn_off(self):
""" Turns off the device. """
self._firetv.action('turn_off')
def media_play(self):
""" Send play command. """
self._firetv.action('media_play')
def media_pause(self):
""" Send pause command. """
self._firetv.action('media_pause')
def media_play_pause(self):
""" Send play/pause command. """
self._firetv.action('media_play_pause')
def volume_up(self):
""" Send volume up command. """
self._firetv.action('volume_up')
def volume_down(self):
""" Send volume down command. """
self._firetv.action('volume_down')
def media_previous_track(self):
""" Send previous track command (results in rewind). """
self._firetv.action('media_previous')
def media_next_track(self):
""" Send next track command (results in fast-forward). """
self._firetv.action('media_next')
def media_seek(self, position):
raise NotImplementedError()
def mute_volume(self, mute):
raise NotImplementedError()
def play_youtube(self, media_id):
raise NotImplementedError()
def set_volume_level(self, volume):
raise NotImplementedError()

View File

@@ -1,43 +1,18 @@
"""
homeassistant.components.media_player.itunes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to iTunes-API (https://github.com/maddox/itunes-api)
The iTunes media player will allow you to control your iTunes instance. You
can play/pause/next/previous/mute, adjust volume, etc.
In addition to controlling iTunes, your available AirPlay endpoints will be
added as media players as well. You can then individually address them append
turn them on, turn them off, or adjust their volume.
Configuration:
To use iTunes you will need to add something like the following to
your configuration.yaml file.
media_player:
platform: itunes
name: iTunes
host: http://192.168.1.16
port: 8181
Variables:
name
*Optional
The name of the device.
url
*Required
URL of your running version of iTunes-API. Example: http://192.168.1.50:8181
Provides an interface to iTunes API.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.itunes/
"""
import logging
from homeassistant.components.media_player import (
MediaPlayerDevice, MEDIA_TYPE_MUSIC, SUPPORT_PAUSE, SUPPORT_SEEK,
SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, SUPPORT_PREVIOUS_TRACK,
SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
MediaPlayerDevice, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, SUPPORT_PAUSE,
SUPPORT_SEEK, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON,
SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA,
ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_MEDIA_COMMANDS)
from homeassistant.const import (
STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_ON)
@@ -47,7 +22,8 @@ import requests
_LOGGER = logging.getLogger(__name__)
SUPPORT_ITUNES = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK | \
SUPPORT_PLAY_MEDIA
SUPPORT_AIRPLAY = SUPPORT_VOLUME_SET | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
@@ -118,6 +94,20 @@ class Itunes(object):
""" Skips back and returns the current state. """
return self._command('previous')
def play_playlist(self, playlist_id_or_name):
""" Sets a playlist to be current and returns the current state. """
response = self._request('GET', '/playlists')
playlists = response.get('playlists', [])
found_playlists = \
[playlist for playlist in playlists if
(playlist_id_or_name in [playlist["name"], playlist["id"]])]
if len(found_playlists) > 0:
playlist = found_playlists[0]
path = '/playlists/' + playlist['id'] + '/play'
return self._request('PUT', path)
def artwork_url(self):
""" Returns a URL of the current track's album art. """
return self._base_url + '/artwork'
@@ -141,11 +131,9 @@ class Itunes(object):
path = '/airplay_devices/' + device_id + '/volume'
return self._request('PUT', path, {'level': level})
# pylint: disable=unused-argument
# pylint: disable=abstract-method
# pylint: disable=unused-argument, abstract-method
# pylint: disable=too-many-instance-attributes
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the itunes platform. """
@@ -163,7 +151,6 @@ class ItunesDevice(MediaPlayerDevice):
""" Represents a iTunes-API instance. """
# pylint: disable=too-many-public-methods
def __init__(self, name, host, port, add_devices):
self._name = name
self._host = host
@@ -294,6 +281,11 @@ class ItunesDevice(MediaPlayerDevice):
""" Album of current playing media. (Music track only) """
return self.current_album
@property
def media_playlist(self):
""" Title of the currently playing playlist. """
return self.current_playlist
@property
def supported_media_commands(self):
""" Flags of media commands that are supported. """
@@ -329,6 +321,12 @@ class ItunesDevice(MediaPlayerDevice):
response = self.client.previous()
self.update_state(response)
def play_media(self, media_type, media_id):
""" play_media media player. """
if media_type == MEDIA_TYPE_PLAYLIST:
response = self.client.play_playlist(media_id)
self.update_state(response)
class AirPlayDevice(MediaPlayerDevice):
""" Represents an AirPlay device via an iTunes-API instance. """

View File

@@ -3,35 +3,8 @@ homeassistant.components.media_player.kodi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to the XBMC/Kodi JSON-RPC API
Configuration:
To use the Kodi you will need to add something like the following to
your configuration.yaml file.
media_player:
platform: kodi
name: Kodi
url: http://192.168.0.123/jsonrpc
user: kodi
password: my_secure_password
Variables:
name
*Optional
The name of the device.
url
*Required
The URL of the XBMC/Kodi JSON-RPC API. Example: http://192.168.0.123/jsonrpc
user
*Optional
The XBMC/Kodi HTTP username.
password
*Optional
The XBMC/Kodi HTTP password.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.kodi/
"""
import urllib
import logging
@@ -167,7 +140,7 @@ class KodiDevice(MediaPlayerDevice):
def media_content_id(self):
""" Content ID of current playing media. """
if self._item is not None:
return self._item['uniqueid']
return self._item.get('uniqueid', None)
@property
def media_content_type(self):

View File

@@ -3,35 +3,8 @@ homeassistant.components.media_player.mpd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to interact with a Music Player Daemon.
Configuration:
To use MPD you will need to add something like the following to your
configuration.yaml file.
media_player:
platform: mpd
server: 127.0.0.1
port: 6600
location: bedroom
password: superSecretPassword123
Variables:
server
*Required
IP address of the Music Player Daemon. Example: 192.168.1.32
port
*Optional
Port of the Music Player Daemon, defaults to 6600. Example: 6600
location
*Optional
Location of your Music Player Daemon.
password
*Optional
Password for your Music Player Daemon.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.mpd/
"""
import logging
import socket

View File

@@ -0,0 +1,318 @@
"""
homeassistant.components.media_player.plex
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to the Plex API.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.plex/
"""
import os
import json
import logging
from datetime import timedelta
from urllib.parse import urlparse
from homeassistant.loader import get_component
import homeassistant.util as util
from homeassistant.components.media_player import (
MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
from homeassistant.const import (
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_PLAYING,
STATE_PAUSED, STATE_OFF, STATE_UNKNOWN)
REQUIREMENTS = ['plexapi==1.1.0']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
PLEX_CONFIG_FILE = 'plex.conf'
# Map ip to request id for configuring
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
def config_from_file(filename, config=None):
''' Small configuration file management function'''
if config:
# We're writing configuration
try:
with open(filename, 'w') as fdesc:
fdesc.write(json.dumps(config))
except IOError as error:
_LOGGER.error('Saving config file failed: %s', error)
return False
return True
else:
# We're reading config
if os.path.isfile(filename):
try:
with open(filename, 'r') as fdesc:
return json.loads(fdesc.read())
except IOError as error:
_LOGGER.error('Reading config file failed: %s', error)
# This won't work yet
return False
else:
return {}
# pylint: disable=abstract-method, unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Sets up the plex platform. """
config = config_from_file(hass.config.path(PLEX_CONFIG_FILE))
if len(config):
# Setup a configured PlexServer
host, token = config.popitem()
token = token['token']
# Via discovery
elif discovery_info is not None:
# Parse discovery data
host = urlparse(discovery_info[1]).netloc
_LOGGER.info('Discovered PLEX server: %s', host)
if host in _CONFIGURING:
return
token = None
else:
return
setup_plexserver(host, token, hass, add_devices_callback)
# pylint: disable=too-many-branches
def setup_plexserver(host, token, hass, add_devices_callback):
''' Setup a plexserver based on host parameter'''
import plexapi.server
import plexapi.exceptions
try:
plexserver = plexapi.server.PlexServer('http://%s' % host, token)
except (plexapi.exceptions.BadRequest,
plexapi.exceptions.Unauthorized,
plexapi.exceptions.NotFound) as error:
_LOGGER.info(error)
# No token or wrong token
request_configuration(host, hass, add_devices_callback)
return
# If we came here and configuring this host, mark as done
if host in _CONFIGURING:
request_id = _CONFIGURING.pop(host)
configurator = get_component('configurator')
configurator.request_done(request_id)
_LOGGER.info('Discovery configuration done!')
# Save config
if not config_from_file(
hass.config.path(PLEX_CONFIG_FILE),
{host: {'token': token}}):
_LOGGER.error('failed to save config file')
_LOGGER.info('Connected to: htts://%s', host)
plex_clients = {}
plex_sessions = {}
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():
""" Updates the devices objects. """
try:
devices = plexserver.clients()
except plexapi.exceptions.BadRequest:
_LOGGER.exception("Error listing plex devices")
return
new_plex_clients = []
for device in devices:
# For now, let's allow all deviceClass types
if device.deviceClass in ['badClient']:
continue
if device.machineIdentifier not in plex_clients:
new_client = PlexClient(device, plex_sessions, update_devices,
update_sessions)
plex_clients[device.machineIdentifier] = new_client
new_plex_clients.append(new_client)
else:
plex_clients[device.machineIdentifier].set_device(device)
if new_plex_clients:
add_devices_callback(new_plex_clients)
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_sessions():
""" Updates the sessions objects. """
try:
sessions = plexserver.sessions()
except plexapi.exceptions.BadRequest:
_LOGGER.exception("Error listing plex sessions")
return
plex_sessions.clear()
for session in sessions:
plex_sessions[session.player.machineIdentifier] = session
update_devices()
update_sessions()
def request_configuration(host, hass, add_devices_callback):
""" Request configuration steps from the user. """
configurator = get_component('configurator')
# We got an error if this method is called while we are configuring
if host in _CONFIGURING:
configurator.notify_errors(
_CONFIGURING[host], "Failed to register, please try again.")
return
def plex_configuration_callback(data):
""" Actions to do when our configuration callback is called. """
setup_plexserver(host, data.get('token'), hass, add_devices_callback)
_CONFIGURING[host] = configurator.request_config(
hass, "Plex Media Server", plex_configuration_callback,
description=('Enter the X-Plex-Token'),
description_image="/static/images/config_plex_mediaserver.png",
submit_caption="Confirm",
fields=[{'id': 'token', 'name': 'X-Plex-Token', 'type': ''}]
)
class PlexClient(MediaPlayerDevice):
""" Represents a Plex device. """
# pylint: disable=too-many-public-methods, attribute-defined-outside-init
def __init__(self, device, plex_sessions, update_devices, update_sessions):
self.plex_sessions = plex_sessions
self.update_devices = update_devices
self.update_sessions = update_sessions
self.set_device(device)
def set_device(self, device):
""" Sets the device property. """
self.device = device
@property
def unique_id(self):
""" Returns the id of this plex client """
return "{}.{}".format(
self.__class__, self.device.machineIdentifier or self.device.name)
@property
def name(self):
""" Returns the name of the device. """
return self.device.name or DEVICE_DEFAULT_NAME
@property
def session(self):
""" Returns the session, if any. """
if self.device.machineIdentifier not in self.plex_sessions:
return None
return self.plex_sessions[self.device.machineIdentifier]
@property
def state(self):
""" Returns the state of the device. """
if self.session:
state = self.session.player.state
if state == 'playing':
return STATE_PLAYING
elif state == 'paused':
return STATE_PAUSED
# This is nasty. Need to find a way to determine alive
elif self.device:
return STATE_IDLE
else:
return STATE_OFF
return STATE_UNKNOWN
def update(self):
self.update_devices(no_throttle=True)
self.update_sessions(no_throttle=True)
@property
def media_content_id(self):
""" Content ID of current playing media. """
if self.session is not None:
return self.session.ratingKey
@property
def media_content_type(self):
""" Content type of current playing media. """
if self.session is None:
return None
media_type = self.session.type
if media_type == 'episode':
return MEDIA_TYPE_TVSHOW
elif media_type == 'movie':
return MEDIA_TYPE_VIDEO
return None
@property
def media_duration(self):
""" Duration of current playing media in seconds. """
if self.session is not None:
return self.session.duration
@property
def media_image_url(self):
""" Image url of current playing media. """
if self.session is not None:
return self.session.thumbUrl
@property
def media_title(self):
""" Title of current playing media. """
# find a string we can use as a title
if self.session is not None:
return self.session.title
@property
def media_season(self):
""" Season of curent playing media (TV Show only). """
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.seasons()[0].index
@property
def media_series_title(self):
""" Series title of current playing media (TV Show only). """
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.grandparentTitle
@property
def media_episode(self):
""" Episode of current playing media (TV Show only). """
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.index
@property
def supported_media_commands(self):
""" Flags of media commands that are supported. """
return SUPPORT_PLEX
def media_play(self):
""" media_play media player. """
self.device.play()
def media_pause(self):
""" media_pause media player. """
self.device.pause()
def media_next_track(self):
""" Send next track command. """
self.device.skipNext()
def media_previous_track(self):
""" Send previous track command. """
self.device.skipPrevious()

View File

@@ -1,17 +1,11 @@
"""
homeassistant.components.media_player.sonos
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to Sonos players (via SoCo)
Configuration:
To use SoCo, add something like this to your configuration:
media_player:
platform: sonos
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.sonos/
"""
import logging
import datetime
@@ -56,8 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return True
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-public-methods
# pylint: disable=too-many-instance-attributes, too-many-public-methods
# pylint: disable=abstract-method
class SonosDevice(MediaPlayerDevice):
""" Represents a Sonos device. """
@@ -74,7 +67,7 @@ class SonosDevice(MediaPlayerDevice):
return True
def update_sonos(self, now):
""" Updates state, called by track_utc_time_change """
""" Updates state, called by track_utc_time_change. """
self.update_ha_state(True)
@property
@@ -162,31 +155,31 @@ class SonosDevice(MediaPlayerDevice):
return SUPPORT_SONOS
def turn_off(self):
""" turn_off media player. """
""" Turn off media player. """
self._player.pause()
def volume_up(self):
""" volume_up media player. """
""" Volume up media player. """
self._player.volume += 1
def volume_down(self):
""" volume_down media player. """
""" Volume down media player. """
self._player.volume -= 1
def set_volume_level(self, volume):
""" set volume level, range 0..1. """
""" Set volume level, range 0..1. """
self._player.volume = str(int(volume * 100))
def mute_volume(self, mute):
""" mute (true) or unmute (false) media player. """
""" Mute (true) or unmute (false) media player. """
self._player.mute = mute
def media_play(self):
""" media_play media player. """
""" Send paly command. """
self._player.play()
def media_pause(self):
""" media_pause media player. """
""" Send pause command. """
self._player.pause()
def media_next_track(self):
@@ -202,5 +195,5 @@ class SonosDevice(MediaPlayerDevice):
self._player.seek(str(datetime.timedelta(seconds=int(position))))
def turn_on(self):
""" turn the media player on. """
""" Turn the media player on. """
self._player.play()

View File

@@ -1,39 +1,11 @@
"""
homeassistant.components.media_player.squeezebox
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to the Logitech SqueezeBox API
Configuration:
To use SqueezeBox add something something like the following to your
configuration.yaml file.
media_player:
platform: squeezebox
host: 192.168.1.21
port: 9090
username: user
password: password
Variables:
host
*Required
The host name or address of the Logitech Media Server.
port
*Optional
Telnet port to Logitech Media Server, default 9090.
usermame
*Optional
Username, if password protection is enabled.
password
*Optional
Password, if password protection is enabled.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.squeezebox/
"""
import logging
import telnetlib
import urllib.parse
@@ -201,7 +173,7 @@ class SqueezeBoxDevice(MediaPlayerDevice):
def volume_level(self):
""" Volume level of the media player (0..1). """
if 'mixer volume' in self._status:
return int(self._status['mixer volume']) / 100.0
return int(float(self._status['mixer volume'])) / 100.0
@property
def is_volume_muted(self):
@@ -291,7 +263,7 @@ class SqueezeBoxDevice(MediaPlayerDevice):
def media_pause(self):
""" media_pause media player. """
self._lms.query(self._id, 'pause', '0')
self._lms.query(self._id, 'pause', '1')
self.update_ha_state()
def media_next_track(self):

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