Compare commits

..

136 Commits

Author SHA1 Message Date
Paulus Schoutsen 8f18cb34d6 Release 0.18 #1854
0.18
2016-04-20 18:43:54 -07:00
Paulus Schoutsen b6e956a3d0 Version bump to 0.18 2016-04-20 18:43:21 -07:00
Paulus Schoutsen 3e0f64e4f6 Update frontend 2016-04-20 18:13:25 -07:00
Dennis Karpienski 934cd876b4 fixed some issues with webos (#1856)
* fixed some issues

* fixed linter
2016-04-20 14:09:50 -07:00
Christoph Wagner fbfdf5a286 Removed redundant Throttle decorator (#1862) 2016-04-20 00:20:30 -07:00
Paulus Schoutsen 7d4b11f4ec Fix wrong merge 2016-04-19 22:59:29 -07:00
Paulus Schoutsen 4abbbf0f50 Merge remote-tracking branch 'origin/master' into dev
Conflicts:
	homeassistant/bootstrap.py
	homeassistant/components/frontend/version.py
	homeassistant/const.py
2016-04-19 22:53:26 -07:00
Paulus Schoutsen 0c58a1a499 Sort .coveragerc 2016-04-19 21:45:29 -07:00
Paulus Schoutsen bdc39ff905 Update frontend 2016-04-19 21:39:44 -07:00
Paulus Schoutsen 9d391becc1 Add mysensors tcp ethernet gateway (#1861)
* Bump version of pymysensors to 0.6, which includes the tcp gateway.
* Update requirements_all.txt.
* Replace CONF_PORT with CONF_DEVICE and ATTR_PORT with ATTR_DEVICE.
* Add tcp_port in config.
* Try to guess if tcp or serial gateway is configured, by validating
	device name as an ip address. If successful setup tcp gateway, if it
	fails, setup serial gateway.
* Update device_state_attributes to show correct device, ethernet or
	serial.
2016-04-19 21:00:56 -07:00
Paulus Schoutsen 2e79e9d5bb Correct celcius to celsius (#1860) 2016-04-19 20:30:44 -07:00
Jon Evans 9090672146 Pass format through to temper_device so that Fahrenheit works (#1858) 2016-04-19 20:00:03 -07:00
Dennis Karpienski 46274d4393 add media_player webos tv platform (#1853)
* added webos tv platform

* moved coverage entries
2016-04-19 08:53:58 -07:00
Paulus Schoutsen 177d0c20d9 Add bluepy package dependency to Dockerfile 2016-04-19 08:49:05 -07:00
Chema García b9fd4919b3 Set name to hyperion assets (#1822)
* set name to hyperion assets

by specifying the name in the configuration:

light kodi_xxx:
  name: "Kodi XXX"
  platform: hyperion
  host: 192.168.1.222
  port: 19444

* Update hyperion.py

fix extra space
2016-04-19 08:19:27 -07:00
Manoj c1ca13d613 Add bluetooth device tracker (#1830)
This tracker discovers new devices on boot and tracks bluetooth devices
periodically based on interval_seconds value. Devices discovered are
stored with 'BT_' as the prefix for device mac.

Requires PyBluez
2016-04-19 08:18:46 -07:00
Alexander Fortin c0a7b0f474 Add feedreader component (#1836) 2016-04-19 08:14:36 -07:00
Markus Peter e61ffff646 Support for EQ3 Bluetooth Smart Thermostats (#1839)
* Initial Support for EQ3 Bluetooth Smart Radiator Thermostats

* tox runs successfully

* Moved device specific stuff to bluepy_devices library

* lint fix
2016-04-19 08:12:27 -07:00
Daniel Høyer Iversen 16fe7dd937 rfxtrx config validation 2016-04-19 07:13:58 -07:00
Greg Dowling 5b0ab5c118 Merge pull request #1842 from home-assistant/add_imperial_meter
Add support for imperial gas meter and user specified gas calorific value.
2016-04-18 12:10:07 +01:00
Dan 276c2070be Do not setup or configure a hue bridge twice (#1848)
Tracks previously configured bridges by IP and prevents duplicates
from being configured
2016-04-17 22:41:31 -07:00
Paulus Schoutsen c89e9d87e8 Catch platform setup error (#1847) 2016-04-17 22:07:53 -07:00
Brad Johnson 09693bf16c Upgrading to python-wink 0.7.4 and improving RGB color support in HA (#1832) 2016-04-17 19:07:21 -07:00
Jan Harkes bb439129e6 Bump pywemo version to 0.4.2 2016-04-17 21:02:31 -04:00
Jan-Preben Mossin 5931bac695 Only add visible sonos devices
Some Sonos devices (e.g SUB) does not have
a upnp media renderer, but are discovered as sonos
devices. Creating a SonosDevice object from such a
device will fail.
2016-04-17 16:45:16 -07:00
John Arild Berentsen b45c0cd735 Zwave import fixes for stability (#1845) 2016-04-17 14:46:51 -07:00
Paulus Schoutsen ea38742067 Update bootstrap frontend script 2016-04-17 13:59:11 -07:00
Flavio Castelli b5f1c1332a Sonos: better handling of offline players (#1829)
When a sonos player goes offline an endless stream of
exceptions is raised. That happens because homeassistant keeps trying
to refresh its status.

This can happen even when a sonos player has gone offline **before**
homeassistant is started. The sonos players take some time before
realizing one of their mates is no longer online, leading to the same
stream of exceptions inside of homeassistant.

Three types of exceptions of can be raised:

  - `requests.packages.urllib3.exceptions.MaxRetryError`
  - `requests.packages.urllib3.exceptions.NewConnectionError`
  - `TimeoutError`

It's not possible to handle all of them with a single `except` block
because they are raised in a random order and after some delays. That
means a 2nd or 3rd exception can take place while handling the 1st one.

The only solution is to check whether a a player is actually reachable
by attempting to connect to a service that must be running on it.

Also all the players in a 'unknown' state should not be polled by
homeassistant (despite of their brand).

I'm going to upstream the `_is_reachable` method I added to the
`sonos.py` file into `SoCo`. In the meantime we must ship this piece
of code with homeassistant.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
2016-04-17 13:17:13 -07:00
Dan Cinnamon a482c4b2a1 Allow mpd to handle the play_media service. (#1831)
Even though there's not really a place in the UI for it, it will now be possible to set a playlist as part of a script, and ultimately a scene.
2016-04-17 12:34:33 -07:00
deisi e8bf47ff59 Fix for the issue #1323 (#1835) 2016-04-17 12:32:04 -07:00
Paulus Schoutsen ecf411c4c5 Merge pull request #1838 from MartinHjelmare/fix-influxdb
Fix field type conflict in influxdb
2016-04-17 10:43:47 -07:00
pavoni 31f061f4d3 Add support for imperial meter and user soecified calorific value. 2016-04-17 18:34:40 +01:00
icovada b29dedaecf SCSGATE: Actually cycle through all devices to register (#1841)
* SCSGATE: Actually cycle through all devices to register

Modify _activate_next_device function to actually cycle through self-devices_to_register instead of simply checking whether there are any and onl registering one

* Deleted extra white lines
2016-04-17 10:17:42 -07:00
MartinHjelmare ca2d969198 Fix field type conflict in influxdb
* Add STATE_UNAVAILABLE to states that are ignored when writing to the
  database. This will avoid a field type error for string if the field
  already contains a different type, eg integer.
* Add test for ignored states for influxdb.
* Clean up influxdb tests.
2016-04-16 20:25:39 +02:00
Paulus Schoutsen 37a28c799f Merge pull request #1833 from home-assistant/iso8601
Use standardised datetime format
2016-04-16 10:06:33 -07:00
Paulus Schoutsen 1cfbeabaec Update frontend 2016-04-16 10:05:29 -07:00
Paulus Schoutsen 68d92c3196 Use standardised datetime format 2016-04-16 01:46:50 -07:00
Paulus Schoutsen e7520ef401 Update frontend + mdi 2016-04-14 22:57:41 -07:00
Paulus Schoutsen f5ee3e6e13 Merge pull request #1821 from home-assistant/service-validations
Service validations
2016-04-13 22:38:14 -07:00
Alex Harvey 0abedd8b46 fix for plex not detecting plex players after power up (#1824) 2016-04-13 22:33:30 -07:00
Paulus Schoutsen abd376cbba Merge pull request #1816 from JshWright/feature/accurate-away-temps-nest
Improve Nest target temp display
2016-04-13 22:01:01 -07:00
Øystein Hansen 8b986b0b97 mysensors.py Prevent sensor name mixup (#1802)
Change the sensor name schema so nodes are not mixed up. The dot that was pressent before was ignored by home-assistant.
Before name:  <skeatchname> <node_id><child_id>  eg. wall 107
New name   :  <skeatchname> <node_id> <child_id>  eg. wall 10 7
This will result in a Home Assistant Entity ID of: sensor.wall_10_7
2016-04-13 21:22:40 -07:00
Dennis Karpienski f1d8667d7e Yamaha AVR Input Select (#1796)
* added select input for yamaha

* set source_list to none on init
2016-04-13 21:13:12 -07:00
Jan Harkes 298a1c2af7 Service validation for alarm_control_panel component. 2016-04-13 13:45:11 -04:00
Fabian Affolter 9d71a8c4b9 Merge pull request #1819 from fabaff/typos
Update file header and fix typos
2016-04-13 19:35:06 +02:00
Jan Harkes 5cdaee7ebb Service validation for browser component. 2016-04-13 12:57:47 -04:00
Jan Harkes 7c9729b9c1 Service validation for conversation component. 2016-04-13 12:48:39 -04:00
Jan Harkes 003bd24976 Service validation for downloader component. 2016-04-13 12:35:08 -04:00
Jan Harkes 652fe7e0f2 Service validation for garage_door component. 2016-04-13 12:35:08 -04:00
Jan Harkes 49b002dc53 Service validation for ifttt component. 2016-04-13 12:35:07 -04:00
Jan Harkes 7e0d9bc709 Service validation for input_boolean component. 2016-04-13 12:35:07 -04:00
Jan Harkes bfdbbbddac Service validation for input_select component. 2016-04-13 12:35:07 -04:00
Jan Harkes 620d7a92f0 Service validation for input_slider component. 2016-04-13 12:35:07 -04:00
Jan Harkes 8b40346db3 Service validation for keyboard component. 2016-04-13 12:35:07 -04:00
Jan Harkes 5c520b0d35 Service validation for lock component. 2016-04-13 12:35:07 -04:00
Jan Harkes d6f3123937 Service validation for logbook component. 2016-04-13 12:35:07 -04:00
Jan Harkes 7ffc254a87 Service validation for notify component. 2016-04-13 12:35:07 -04:00
Jan Harkes 40e36126bc Service validation for rollershutter component. 2016-04-13 12:35:07 -04:00
Jan Harkes ad6f5d3b1d Service validation for scene component. 2016-04-13 12:35:07 -04:00
Jan Harkes 567d1065b2 Service validation for script component. 2016-04-13 12:35:07 -04:00
Jan Harkes d90f31bf6e Config and service validation for shell_command component. 2016-04-13 12:35:07 -04:00
Jan Harkes 1deaf2fe8f Service validation for switch component. 2016-04-13 12:35:07 -04:00
Jan Harkes 730514cea8 Service validation for the thermostat component. 2016-04-13 12:35:07 -04:00
Fabian Affolter 85901d60c4 Update file header and fix typos 2016-04-13 13:50:28 +02:00
Robbie Trencheny 05469e6d9b Update pyGTFS version 2016-04-13 00:40:12 -07:00
Paulus Schoutsen 1f96c4ad88 Update submodule 2016-04-12 23:57:07 -07:00
Robbie Trencheny c121c9aa39 Fix links in README.rst 2016-04-12 21:37:10 -07:00
Paulus Schoutsen 3ed104529c Merge pull request #1811 from fabaff/moving
Update links
2016-04-12 21:16:01 -07:00
Jan Harkes 0e10f7ced9 Ignore tests/config/deps/ for both git and flake8.
Sometimes py.test leave some packages around in tests/config/deps.
Make sure these do not accidentally get pulled into a commit or
cause a local tox run to fail.
2016-04-12 18:46:34 -04:00
Jan Harkes 4aa43bbf7a New version of betamax breaks the yr.no sensor test.
Pin to betamax-0.5.1 for now.
2016-04-12 18:46:34 -04:00
Josh Wright 1685bbe1f7 Fix copy/paste logic error 2016-04-12 18:45:57 -04:00
Josh Wright 942d722dcf Improve target temperature selection logic
When picking which of the high/low temperatures to display as the
"target", it makes more sense to take the outside temperature into
consideration, rather than the current temperature of the thermostat.

If the temperature outside is less than the temperature of the
thermostat, then we are far more likely to end up in "heating" mode
eventually, and vice versa, regardless of where the current inside
temp falls in the range between high and low.
2016-04-12 18:25:24 -04:00
Josh Wright c4a71fbfa7 Include away temps in target temps
The target temperature methods were not taking the 'away' status
into account. leading to misleading target temps being reported.
2016-04-12 18:15:24 -04:00
Jan Harkes ebf45012fb Ignore tests/config/deps/ for both git and flake8.
Sometimes py.test leave some packages around in tests/config/deps.
Make sure these do not accidentally get pulled into a commit or
cause a local tox run to fail.
2016-04-12 14:58:02 -04:00
Jan Harkes 0bf49aea6f New version of betamax breaks the yr.no sensor test.
Pin to betamax-0.5.1 for now.
2016-04-12 14:43:04 -04:00
Daniel Høyer Iversen cd80e41b32 Allow negative elevation in yr config 2016-04-12 07:54:03 -07:00
Fabian Affolter 4ecd8d5f11 Update links 2016-04-12 14:18:18 +02:00
Paulus Schoutsen 5b8a2b00df Update frontend 2016-04-12 00:24:19 -07:00
Alexander Fortin 24569e6169 Make owntracks.py pylint clean (#1808)
* refactor validation logic into validate_payload function
* add debugging messages to validation
2016-04-11 22:02:47 -07:00
Fabian Affolter 4a16d8064d Enable certificate verification and allow non-encrypted communication (#1763) 2016-04-11 21:55:17 -07:00
Jan Harkes 9d848731d9 Nest config validation (#1810)
* Config validation for Nest platforms.
2016-04-11 21:52:19 -07:00
Jan Harkes 656e187729 Some fixes for yr config validation. (#1809)
The setup_platform function is trying to get CONF_LATITUDE and CONF_LONGITUDE,
but the validation schema was not accepting these.

Also moved CONF_MONITORED_CONDITIONS and CONF_ELEVATION to homeassistant.const
because they are used in other places.
2016-04-11 21:44:39 -07:00
Jan Harkes 241735c924 Change local library path from {config_dir}/lib to {config_dir}/deps. (#1799)
Just on the off chance that someone who happens to run as root and also
doesn't correctly parse "just remove config /lib and restart".
2016-04-11 20:07:50 -07:00
Paulus Schoutsen 3de51bf75d Merge pull request #1807 from balloob/release-0.17.3
0.17.3
2016-04-11 19:56:41 -07:00
Paulus Schoutsen 4023021b21 Version bump to 0.17.3 2016-04-11 19:48:37 -07:00
Jan Harkes 2c665ca3e4 Do not propagate api password (#1797)
* Do not propagate API password in service requests.

It makes service validation fail. The choice is to either handle it as an
optional key in every service handler and make sure it doesn't end up in event
stream and notifications, or to strip it as early as possible.

* Some places still need a forwarded api password.

- Event forwarding/remote api uses the local api password to
  authenticate against the remote instance.
- The generated index.html at '/' embeds the api password.
2016-04-11 19:48:16 -07:00
Jan Harkes 4857117dda Do not propagate api password (#1797)
* Do not propagate API password in service requests.

It makes service validation fail. The choice is to either handle it as an
optional key in every service handler and make sure it doesn't end up in event
stream and notifications, or to strip it as early as possible.

* Some places still need a forwarded api password.

- Event forwarding/remote api uses the local api password to
  authenticate against the remote instance.
- The generated index.html at '/' embeds the api password.
2016-04-11 19:37:15 -07:00
Josh Wright 80d6e9f08f Use constant time comparison for http authentication (#1804)
In order to prevent a potential timing attack, it's important to make
sure the password check takes the same amount of time, regardless of
how many characters in the candidate password match the real password.

This commit does increase the verbosity of the authentication check.
Generally it is a good idea for authentication logic to be very clear,
even if that requires some extra verbosity.
2016-04-11 19:36:25 -07:00
Robbie Trencheny 0018d2b3f5 Fix flake8 error on zeroconf.py 2016-04-10 18:55:54 -07:00
Robbie Trencheny 5d8cd6d49d Merge pull request #1790 from robbiet480/upnp
UPnP port mapping component
2016-04-10 18:47:47 -07:00
Robbie Trencheny ed6958f477 Change needs_auth ZeroConf key 2016-04-10 18:46:57 -07:00
Robbie Trencheny 27d624fc4a Merge remote-tracking branch 'upstream/dev' into upnp 2016-04-10 18:24:04 -07:00
Paulus Schoutsen 948aa6838d Remove stale print 2016-04-10 18:22:25 -07:00
Robbie Trencheny 6f149d414a Disable import-error on upnp 2016-04-10 18:19:32 -07:00
Robbie Trencheny f1e46e63c0 Merge pull request #1733 from robbiet480/http-zeroconf
ZeroConf component
2016-04-10 18:17:06 -07:00
Robbie Trencheny a3959d5e01 Update netdisco dependency to 0.6.4 (deja vu all over again!) 2016-04-10 18:10:31 -07:00
Robbie Trencheny eca1631f1b Update netdisco dependency to 0.6.3 2016-04-10 18:05:30 -07:00
Robbie Trencheny f30b406334 Merge remote-tracking branch 'upstream/dev' into http-zeroconf 2016-04-10 18:04:56 -07:00
Robbie Trencheny 1de45ebe8b Fix api_password conditional and close zeroconf when we shut down 2016-04-10 17:59:21 -07:00
Robbie Trencheny 5b51f682ca Remove unnecessary disable=no-name-in-module 2016-04-10 17:49:07 -07:00
Robbie Trencheny 41c3f695b4 UPnP port mapping component 2016-04-10 17:44:57 -07:00
Paulus Schoutsen 197388a9b2 Prevent device tracker error 2016-04-10 17:35:33 -07:00
Dennis Karpienski a862e994c7 Thinkingcleaner support (#1784)
* added first implementation of thinking cleaner

* fix lock release

* fixed tox flaws

* updated coveragerc

* fixed lock

* changed update lock

* fixed codestyle
2016-04-10 16:59:53 -07:00
Markus Peter fec45033bc Expanded homematic component with MAX! support via homegear (#1783)
* Expanded homematic component with MAX! support via homegear
Also multithreading fixes

* fixed tox errors

* incorporate changes suggested by balloob

* replaced HomematicConfig Container Class with namedtuple

* fixed lint errors
2016-04-10 16:26:08 -07:00
Robbie Trencheny e1ffdcc5f1 Use hass.config.api instead of hass.http 2016-04-10 16:09:52 -07:00
Robbie Trencheny 085d90ed67 Revert all http.py changes 2016-04-10 16:08:00 -07:00
Daniel Høyer Iversen 769d958464 Config validation for rfxtrx sensor (#1780) 2016-04-10 16:05:32 -07:00
Robbie Trencheny e70338dfe1 Block zeroconf from tests 2016-04-10 16:03:40 -07:00
Robbie Trencheny beac69ad17 Final clean up, flake8, pylint, change a variable name, remove unnecessary imports 2016-04-10 16:02:07 -07:00
Robbie Trencheny c33c2c01d2 Break Zeroconf into its own component 2016-04-10 15:34:04 -07:00
Paulus Schoutsen 4f834ba3f1 Make all entity_ids lowercase (#1786) 2016-04-10 15:20:20 -07:00
Paulus Schoutsen 6d65b0bbd7 Update frontend 2016-04-10 15:18:44 -07:00
Daniel Høyer Iversen d3493c7e5a Config validation of yr sensor (#1767) 2016-04-10 10:43:05 -07:00
Josh Wright 24257fe4a3 Don't round values in Thermostat internal state (#1782)
Since all values coming out of the Thermostat component pass though the
_convert_for_display() method (which handles any necessary rounding),
there is no need to round values that only exist in the internal state
of the thermostat device. It serves no purpose and risks rounding
errors/precision loss.
2016-04-10 10:41:13 -07:00
Paulus Schoutsen 3d98b8b5b3 Update frontend 2016-04-10 01:43:40 -07:00
Paulus Schoutsen 988dd2ca83 Update frontend 2016-04-10 01:33:01 -07:00
Joel Asher Friedman 9808c0e3fd mqtt garage door component (#1742) 2016-04-09 20:31:53 -07:00
OpenDave15 91b1ebaeb7 Allow use of pynetgear 3.3 port parameter. (#1777)
* Allow use of pynetgear 3.3 port parameter.

* Fix lint problem
2016-04-09 20:29:06 -07:00
Paulus Schoutsen 1e86044590 Update netdisco to 0.6.2 2016-04-09 20:24:34 -07:00
Paulus Schoutsen c98b56a807 Merge pull request #1778 from balloob/hotfix/0.17.2
Hotfix/0.17.2
2016-04-09 16:23:20 -07:00
Paulus Schoutsen fa0be21342 Version bump to 0.17.2 2016-04-09 16:16:05 -07:00
Jan Harkes 7be29468d5 Make yaml config parser errors look less like bugs. (#1776)
Instead of nested tracebacks, show a simpler error message.

    Config directory: /home/user/.homeassistant
    ERROR:homeassistant.util.yaml:duplicate key: "script"
      in "/home/user/.homeassistant/configuration.yaml", line 95, column 0
      in "/home/user/.homeassistant/configuration.yaml", line 108, column 0
2016-04-09 16:15:49 -07:00
Paulus Schoutsen 9924351a42 Prevent device tracker from creating invalid YAML (#1774) 2016-04-09 16:15:49 -07:00
Jan Harkes 73859f59f0 Make yaml config parser errors look less like bugs. (#1776)
Instead of nested tracebacks, show a simpler error message.

    Config directory: /home/user/.homeassistant
    ERROR:homeassistant.util.yaml:duplicate key: "script"
      in "/home/user/.homeassistant/configuration.yaml", line 95, column 0
      in "/home/user/.homeassistant/configuration.yaml", line 108, column 0
2016-04-09 15:25:01 -07:00
Paulus Schoutsen b87e2437aa Prevent device tracker from creating invalid YAML (#1774) 2016-04-09 13:38:51 -07:00
Paulus Schoutsen a41514ca50 0.17.1 (#1771)
* We need to allow extra keys on top level componenet config

fixes #1756

* Add comment about location of hass (fixes #1723)

* Fix for MQTT config validation on the protocol field. (#1765)

* Update frontend with weblink fix

* Fix for light service validation. (#1770)

Incorrect validation tested if passed value was a list instead of
a member of the list.

* Accept group without entities in configuration. (#1768)

* Accept group without entities in configuration.

People seem to use these as placeholders for future expansion of their
home automation dreams, and we used to accept them.  We still have to
specify at least one of 'name', 'view' or 'icon' so that the group is
parsed as a dictionary.

* Also accept empty entities: key in a group.

* Additional fix for empty entities value in a group config.

* Version bump to 0.17.1
2016-04-09 09:55:52 -07:00
Jan Harkes 446d367aeb Accept group without entities in configuration. (#1768)
* Accept group without entities in configuration.

People seem to use these as placeholders for future expansion of their
home automation dreams, and we used to accept them.  We still have to
specify at least one of 'name', 'view' or 'icon' so that the group is
parsed as a dictionary.

* Also accept empty entities: key in a group.

* Additional fix for empty entities value in a group config.
2016-04-09 09:24:18 -07:00
Paulus Schoutsen 982f8f41ae Update frontend with weblink fix 2016-04-09 09:09:17 -07:00
Jan Harkes 7a25ae3e2c Fix for light service validation. (#1770)
Incorrect validation tested if passed value was a list instead of
a member of the list.
2016-04-09 09:07:13 -07:00
Jan Harkes 3aa4727b18 Fix for MQTT config validation on the protocol field. (#1765) 2016-04-09 09:03:41 -07:00
Paulus Schoutsen b3bad10dab Merge pull request #1758 from turbokongen/patch-1
We need to allow extra keys on top level componenet config
2016-04-09 09:02:55 -07:00
Fabian Affolter c210242c80 Add comment about location of hass (fixes #1723) 2016-04-09 10:55:42 +02:00
John Arild Berentsen f36cfcdbd9 We need to allow extra keys on top level componenet config
fixes #1756
2016-04-09 10:50:46 +02:00
Paulus Schoutsen ff47cffe8a Version bump to 0.18.0.dev0 2016-04-08 21:43:29 -07:00
Robbie Trencheny 27aabd961c Remove unnecessary variable 2016-04-06 13:51:43 -07:00
Robbie Trencheny 86dad0c045 Add ZeroConf support to http.py 2016-04-06 13:51:26 -07:00
178 changed files with 2992 additions and 1036 deletions
+45 -37
View File
@@ -5,18 +5,18 @@ omit =
homeassistant/__main__.py
# omit pieces of code that rely on external devices being present
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
homeassistant/components/bloomsky.py
homeassistant/components/*/bloomsky.py
homeassistant/components/ecobee.py
homeassistant/components/*/ecobee.py
homeassistant/components/insteon_hub.py
homeassistant/components/*/insteon_hub.py
@@ -26,33 +26,6 @@ omit =
homeassistant/components/modbus.py
homeassistant/components/*/modbus.py
homeassistant/components/tellstick.py
homeassistant/components/*/tellstick.py
homeassistant/components/tellduslive.py
homeassistant/components/*/tellduslive.py
homeassistant/components/vera.py
homeassistant/components/*/vera.py
homeassistant/components/ecobee.py
homeassistant/components/*/ecobee.py
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/wemo.py
homeassistant/components/*/wemo.py
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
homeassistant/components/mysensors.py
homeassistant/components/*/mysensors.py
@@ -65,6 +38,36 @@ omit =
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
homeassistant/components/tellduslive.py
homeassistant/components/*/tellduslive.py
homeassistant/components/tellstick.py
homeassistant/components/*/tellstick.py
homeassistant/components/*/thinkingcleaner.py
homeassistant/components/vera.py
homeassistant/components/*/vera.py
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/*/webostv.py
homeassistant/components/wemo.py
homeassistant/components/*/wemo.py
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/binary_sensor/arest.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/browser.py
@@ -76,6 +79,7 @@ omit =
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
homeassistant/components/device_tracker/bluetooth_tracker.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/icloud.py
@@ -89,6 +93,7 @@ omit =
homeassistant/components/device_tracker/ubus.py
homeassistant/components/discovery.py
homeassistant/components/downloader.py
homeassistant/components/feedreader.py
homeassistant/components/garage_door/wink.py
homeassistant/components/ifttt.py
homeassistant/components/keyboard.py
@@ -103,17 +108,17 @@ omit =
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/mpd.py
homeassistant/components/media_player/onkyo.py
homeassistant/components/media_player/panasonic_viera.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/samsungtv.py
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/onkyo.py
homeassistant/components/media_player/panasonic_viera.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/notify/gntp.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/message_bird.py
homeassistant/components/notify/nma.py
@@ -140,10 +145,10 @@ omit =
homeassistant/components/sensor/forecast.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/gtfs.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/nzbget.py
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nzbget.py
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/rest.py
@@ -169,10 +174,13 @@ omit =
homeassistant/components/switch/rest.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/thermostat/eq3btsmart.py
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py
homeassistant/components/upnp.py
homeassistant/components/zeroconf.py
[report]
+2 -2
View File
@@ -23,6 +23,6 @@ If the code does not interact with devices:
[fork]: http://stackoverflow.com/a/7244456
[squash]: https://github.com/ginatrapani/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit
[ex-requir]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16
[ex-import]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51
[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16
[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51
+1
View File
@@ -8,6 +8,7 @@ config/custom_components/*
!config/custom_components/hello_world.py
!config/custom_components/mqtt_example.py
tests/config/deps
tests/config/home-assistant.log
# Hide sublime text stuff
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "homeassistant/components/frontend/www_static/home-assistant-polymer"]
path = homeassistant/components/frontend/www_static/home-assistant-polymer
url = https://github.com/balloob/home-assistant-polymer.git
url = https://github.com/home-assistant/home-assistant-polymer.git
+9 -9
View File
@@ -4,10 +4,10 @@ Everybody is invited and welcome to contribute to Home Assistant. There is a lot
The process is straight-forward.
- Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant).
- Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant).
- Write the code for your device, notification service, sensor, or IoT thing.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should read the next sections and get more details.
@@ -20,20 +20,20 @@ After you finish adding support for your device:
- Check that all dependencies are included via the `REQUIREMENTS` variable in your platform/component and only imported inside functions that use them.
- Add any new dependencies to `requirements_all.txt` if needed. Use `script/gen_requirements_all.py`.
- Update the `.coveragerc` file to exclude your platform if there are no tests available or your new code uses a 3rd party library for communication with the device/service/sensor.
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/balloob/home-assistant.io).
- 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/home-assistant/home-assistant.io).
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `tox` or `script/lint`.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
- Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/balloob/home-assistant/).
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
- Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/home-assistant/home-assistant/).
If you add a platform for an existing component, there is usually no need for updating the frontend. Only if you've added a new component that should show up in the frontend, there are more steps needed:
- 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 file [`home-assistant-icons.html`](https://github.com/home-assistant/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`.
Since you've updated `home-assistant-icons.html`, you've made changes to the frontend:
- Run `build_frontend`. This will build a new version of the frontend. Make sure you add the changed files `frontend.py` and `frontend.html` to the commit.
- Run `script/build_frontend`. This will build a new version of the frontend. Make sure you add the changed files `frontend.py` and `frontend.html` to the commit.
### Setting states
@@ -46,11 +46,11 @@ A state can have several attributes that will help the frontend in displaying yo
- `unit_of_measurement`: this will be appended to the state in the interface
- `hidden`: This is a suggestion to the frontend on if the state should be hidden
These attributes are defined in [homeassistant.components](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/__init__.py#L25).
These attributes are defined in [homeassistant.components](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/__init__.py#L25).
### Proper Visibility Handling
Generally, when creating a new entity for Home Assistant you will want it to be a class that inherits the [homeassistant.helpers.entity.Entity](https://github.com/balloob/home-assistant/blob/master/homeassistant/helpers/entity.py) class. If this is done, visibility will be handled for you.
Generally, when creating a new entity for Home Assistant you will want it to be a class that inherits the [homeassistant.helpers.entity.Entity](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/helpers/entity.py) class. If this is done, visibility will be handled for you.
You can set a suggestion for your entity's visibility by setting the hidden property by doing something similar to the following.
```python
+2 -2
View File
@@ -8,9 +8,9 @@ WORKDIR /usr/src/app
RUN pip3 install --no-cache-dir colorlog cython
# For the nmap tracker
# For the nmap tracker, bluetooth tracker, Z-Wave
RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo && \
apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo libglib2.0-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY script/build_python_openzwave script/build_python_openzwave
+8 -8
View File
@@ -1,4 +1,4 @@
Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/balloob/home-assistant|
Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/home-assistant/home-assistant|
===========================================================================================================
Home Assistant is a home automation platform running on Python 3. The
@@ -88,11 +88,11 @@ If you run into issues while using Home Assistant or during development
of a component, check the `Home Assistant help
section <https://home-assistant.io/help/>`__ how to reach us.
.. |Build Status| image:: https://travis-ci.org/balloob/home-assistant.svg?branch=master
:target: https://travis-ci.org/balloob/home-assistant
.. |Coverage Status| image:: https://img.shields.io/coveralls/balloob/home-assistant.svg
:target: https://coveralls.io/r/balloob/home-assistant?branch=master
.. |Join the chat at https://gitter.im/balloob/home-assistant| image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/balloob/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |screenshot-states| image:: https://raw.github.com/balloob/home-assistant/master/docs/screenshots.png
.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master
:target: https://travis-ci.org/home-assistant/home-assistant
.. |Coverage Status| image:: https://img.shields.io/coveralls/home-assistant/home-assistant.svg
:target: https://coveralls.io/r/home-assistant/home-assistant?branch=master
.. |Join the chat at https://gitter.im/home-assistant/home-assistant| image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/home-assistant/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |screenshot-states| image:: https://raw.github.com/home-assistant/home-assistant/master/docs/screenshots.png
:target: https://home-assistant.io/demo/
+1 -1
View File
@@ -7,7 +7,7 @@ homeassistant:
latitude: 32.87336
longitude: 117.22743
# C for Celcius, F for Fahrenheit
# C for Celsius, F for Fahrenheit
temperature_unit: C
# Pick yours from here:
+2 -2
View File
@@ -14,7 +14,7 @@ To use the mqtt_example component you will need to add the following to your
configuration.yaml file.
mqtt_example:
topic: home-assistant/mqtt_example
topic: "home-assistant/mqtt_example"
"""
import homeassistant.loader as loader
@@ -29,7 +29,7 @@ DEFAULT_TOPIC = 'home-assistant/mqtt_example'
def setup(hass, config):
"""Setup the MQTT example component."""
"""Setup the MQTT example component."""
mqtt = loader.get_component('mqtt')
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
entity_id = 'mqtt_example.last_message'
+1 -1
View File
@@ -31,7 +31,7 @@ def validate_python():
def ensure_config_path(config_dir):
"""Validate the configuration directory."""
import homeassistant.config as config_util
lib_dir = os.path.join(config_dir, 'lib')
lib_dir = os.path.join(config_dir, 'deps')
# Test if configuration directory exists
if not os.path.isdir(config_dir):
+15 -5
View File
@@ -21,7 +21,8 @@ import homeassistant.util.package as pkg_util
from homeassistant.const import (
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
TEMP_CELCIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__)
TEMP_CELSIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
event_decorators, service, config_per_platform, extract_domain_configs)
from homeassistant.helpers.entity import Entity
@@ -64,7 +65,7 @@ def _handle_requirements(hass, component, name):
return True
for req in component.REQUIREMENTS:
if not pkg_util.install_package(req, target=hass.config.path('lib')):
if not pkg_util.install_package(req, target=hass.config.path('deps')):
_LOGGER.error('Not initializing %s because could not install '
'dependency %s', name, req)
return False
@@ -210,7 +211,7 @@ def prepare_setup_platform(hass, config, domain, platform_name):
def mount_local_lib_path(config_dir):
"""Add local library to Python Path."""
sys.path.insert(0, os.path.join(config_dir, 'lib'))
sys.path.insert(0, os.path.join(config_dir, 'deps'))
# pylint: disable=too-many-branches, too-many-statements, too-many-arguments
@@ -293,7 +294,10 @@ def from_config_file(config_path, hass=None, verbose=False, daemon=False,
enable_logging(hass, verbose, daemon, log_rotate_days)
config_dict = config_util.load_yaml_config_file(config_path)
try:
config_dict = config_util.load_yaml_config_file(config_path)
except HomeAssistantError:
return None
return from_config_dict(config_dict, hass, enable_log=False,
skip_pip=skip_pip)
@@ -368,10 +372,16 @@ def process_ha_config_upgrade(hass):
_LOGGER.info('Upgrading config directory from %s to %s', conf_version,
__version__)
# This was where dependencies were installed before v0.18
# Probably should keep this around until ~v0.20.
lib_path = hass.config.path('lib')
if os.path.isdir(lib_path):
shutil.rmtree(lib_path)
lib_path = hass.config.path('deps')
if os.path.isdir(lib_path):
shutil.rmtree(lib_path)
with open(version_path, 'wt') as outp:
outp.write(__version__)
@@ -430,7 +440,7 @@ def process_ha_core_config(hass, config):
if info.use_fahrenheit:
hac.temperature_unit = TEMP_FAHRENHEIT
else:
hac.temperature_unit = TEMP_CELCIUS
hac.temperature_unit = TEMP_CELSIUS
if hac.location_name is None:
hac.location_name = info.city
@@ -7,12 +7,15 @@ https://home-assistant.io/components/alarm_control_panel/
import logging
import os
import voluptuous as vol
from homeassistant.components import verisure
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, 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.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@@ -38,6 +41,11 @@ ATTR_TO_PROPERTY = [
ATTR_CODE_FORMAT
]
ALARM_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string,
})
def setup(hass, config):
"""Track states and offer events for sensors."""
@@ -51,10 +59,7 @@ def setup(hass, config):
"""Map services to methods on Alarm."""
target_alarms = component.extract_from_service(service)
if ATTR_CODE not in service.data:
code = None
else:
code = service.data[ATTR_CODE]
code = service.data.get(ATTR_CODE)
method = SERVICE_TO_METHOD[service.service]
@@ -68,8 +73,8 @@ def setup(hass, config):
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, alarm_service_handler,
descriptions.get(service))
descriptions.get(service),
schema=ALARM_SERVICE_SCHEMA)
return True
@@ -51,8 +51,6 @@ def _platform_validator(method, schema):
if not hasattr(platform, schema):
return config
print('validating config', method, config)
return getattr(platform, schema)(config)
return validator
+3 -3
View File
@@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
def trigger(hass, config, action):
"""Listen for state changes based on configuration."""
if CONF_AFTER in config:
after = dt_util.parse_time_str(config[CONF_AFTER])
after = dt_util.parse_time(config[CONF_AFTER])
if after is None:
_error_time(config[CONF_AFTER], CONF_AFTER)
return False
@@ -62,13 +62,13 @@ def if_action(hass, config):
return None
if before is not None:
before = dt_util.parse_time_str(before)
before = dt_util.parse_time(before)
if before is None:
_error_time(before, CONF_BEFORE)
return None
if after is not None:
after = dt_util.parse_time_str(after)
after = dt_util.parse_time(after)
if after is None:
_error_time(after, CONF_AFTER)
return None
@@ -6,10 +6,9 @@ https://home-assistant.io/components/binary_sensor.mysensors/
"""
import logging
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, SENSOR_CLASSES)
from homeassistant.components.binary_sensor import (SENSOR_CLASSES,
BinarySensorDevice)
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__)
@@ -101,8 +100,13 @@ class MySensorsBinarySensor(BinarySensorDevice):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
address = getattr(self.gateway, 'server_address', None)
if address:
device = '{}:{}'.format(address[0], address[1])
else:
device = self.gateway.port
attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,
+14 -18
View File
@@ -4,12 +4,14 @@ Support for Nest Thermostat Binary Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.nest/
"""
import logging
import socket
import voluptuous as vol
import homeassistant.components.nest as nest
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.nest import NestSensor
from homeassistant.const import (
CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS
)
DEPENDENCIES = ['nest']
BINARY_TYPES = ['fan',
@@ -23,25 +25,19 @@ BINARY_TYPES = ['fan',
'hvac_emer_heat_state',
'online']
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): nest.DOMAIN,
vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS): [vol.In(BINARY_TYPES)],
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Nest binary sensors."""
logger = logging.getLogger(__name__)
try:
for structure in nest.NEST.structures:
for device in structure.devices:
for variable in config['monitored_conditions']:
if variable in BINARY_TYPES:
add_devices([NestBinarySensor(structure,
device,
variable)])
else:
logger.error('Nest sensor type: "%s" does not exist',
variable)
except socket.error:
logger.error(
"Connection error logging into the nest web service."
)
for structure, device in nest.devices():
add_devices([NestBinarySensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]])
class NestBinarySensor(NestSensor, BinarySensorDevice):
@@ -49,8 +49,7 @@ class VeraBinarySensor(VeraDevice, BinarySensorDevice):
last_tripped = self.vera_device.last_trip
if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time)
attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
else:
attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped
@@ -10,7 +10,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
@@ -8,11 +8,7 @@ import logging
import datetime
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_time
from homeassistant.components.zwave import (
ATTR_NODE_ID, ATTR_VALUE_ID,
COMMAND_CLASS_SENSOR_BINARY, NETWORK,
ZWaveDeviceEntity, get_config_value)
from homeassistant.components import zwave
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDevice)
@@ -36,11 +32,11 @@ DEVICE_MAPPINGS = {
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Z-Wave platform for sensors."""
if discovery_info is None or NETWORK is None:
if discovery_info is None or zwave.NETWORK is None:
return
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
value.set_change_verified(False)
# Make sure that we have values for the key before converting to int
@@ -53,18 +49,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
re_arm_multiplier = (get_config_value(value.node, 9) or 4)
re_arm_multiplier = (zwave.get_config_value(value.node,
9) or 4)
add_devices([
ZWaveTriggerSensor(value, "motion",
hass, re_arm_multiplier * 8)
])
return
if value.command_class == COMMAND_CLASS_SENSOR_BINARY:
if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
add_devices([ZWaveBinarySensor(value, None)])
class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
"""Representation of a binary sensor within Z-Wave."""
def __init__(self, value, sensor_class):
@@ -74,7 +71,7 @@ class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
+10 -3
View File
@@ -4,10 +4,18 @@ Provides functionality to launch a web browser on the host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/browser/
"""
import voluptuous as vol
DOMAIN = "browser"
SERVICE_BROWSE_URL = "browse_url"
ATTR_URL = 'url'
ATTR_URL_DEFAULT = 'https://www.google.com'
SERVICE_BROWSE_URL_SCHEMA = vol.Schema({
vol.Required(ATTR_URL, default=ATTR_URL_DEFAULT): vol.Url,
})
def setup(hass, config):
"""Listen for browse_url events."""
@@ -15,8 +23,7 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
lambda service:
webbrowser.open(
service.data.get(
'url', 'https://www.google.com')))
webbrowser.open(service.data[ATTR_URL]),
schema=SERVICE_BROWSE_URL_SCHEMA)
return True
+10 -7
View File
@@ -8,9 +8,12 @@ import logging
import re
import warnings
import voluptuous as vol
from homeassistant import core
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
import homeassistant.helpers.config_validation as cv
DOMAIN = "conversation"
@@ -18,6 +21,10 @@ SERVICE_PROCESS = "process"
ATTR_TEXT = "text"
SERVICE_PROCESS_SCHEMA = vol.Schema({
vol.Required(ATTR_TEXT): vol.All(cv.string, vol.Lower),
})
REGEX_TURN_COMMAND = re.compile(r'turn (?P<name>(?: |\w)+) (?P<command>\w+)')
REQUIREMENTS = ['fuzzywuzzy==0.8.0']
@@ -32,11 +39,7 @@ def setup(hass, config):
def process(service):
"""Parse text into commands."""
if ATTR_TEXT not in service.data:
logger.error("Received process service call without a text")
return
text = service.data[ATTR_TEXT].lower()
text = service.data[ATTR_TEXT]
match = REGEX_TURN_COMMAND.match(text)
if not match:
@@ -67,6 +70,6 @@ def setup(hass, config):
logger.error(
'Got unsupported command %s from text %s', command, text)
hass.services.register(DOMAIN, SERVICE_PROCESS, process)
hass.services.register(DOMAIN, SERVICE_PROCESS, process,
schema=SERVICE_PROCESS_SCHEMA)
return True
@@ -94,7 +94,7 @@ def setup(hass, config):
yaml_path = hass.config.path(YAML_DEVICES)
conf = config.get(DOMAIN, {})
if isinstance(conf, list):
if isinstance(conf, list) and len(conf) > 0:
conf = conf[0]
consider_home = timedelta(
seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int,
@@ -204,6 +204,7 @@ class DeviceTracker(object):
return
# If no device can be found, create it
dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
device = Device(
self.hass, self.consider_home, self.home_range, self.track_new,
dev_id, mac, (host_name or dev_id).replace('_', ' '))
@@ -0,0 +1,91 @@
"""Tracking for bluetooth devices."""
import logging
from datetime import timedelta
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.components.device_tracker import (
YAML_DEVICES,
CONF_TRACK_NEW,
CONF_SCAN_INTERVAL,
DEFAULT_SCAN_INTERVAL,
load_config,
)
import homeassistant.util as util
import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pybluez==0.22']
BT_PREFIX = 'BT_'
def setup_scanner(hass, config, see):
"""Setup the Bluetooth Scanner."""
# pylint: disable=import-error
import bluetooth
def see_device(device):
"""Mark a device as seen."""
see(mac=BT_PREFIX + device[0], host_name=device[1])
def discover_devices():
"""Discover bluetooth devices."""
result = bluetooth.discover_devices(duration=8,
lookup_names=True,
flush_cache=True,
lookup_class=False)
_LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
return result
yaml_path = hass.config.path(YAML_DEVICES)
devs_to_track = []
devs_donot_track = []
# Load all known devices.
# We just need the devices so set consider_home and home range
# to 0
for device in load_config(yaml_path, hass, 0, 0):
# check if device is a valid bluetooth device
if device.mac and device.mac[:3].upper() == BT_PREFIX:
if device.track:
devs_to_track.append(device.mac[3:])
else:
devs_donot_track.append(device.mac[3:])
# if track new devices is true discover new devices
# on startup.
track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
len(devs_to_track) == 0)
if track_new:
for dev in discover_devices():
if dev[0] not in devs_to_track and \
dev[0] not in devs_donot_track:
devs_to_track.append(dev[0])
see_device(dev)
if not devs_to_track:
_LOGGER.warning("No bluetooth devices to track!")
return False
interval = util.convert(config.get(CONF_SCAN_INTERVAL), int,
DEFAULT_SCAN_INTERVAL)
def update_bluetooth(now):
"""Lookup bluetooth device and update status."""
try:
for mac in devs_to_track:
_LOGGER.debug("Scanning " + mac)
result = bluetooth.lookup_name(mac, timeout=5)
if not result:
# Could not lookup device name
continue
see_device((mac, result))
except bluetooth.BluetoothError:
_LOGGER.exception('Error looking up bluetooth device!')
track_point_in_utc_time(hass, update_bluetooth,
now + timedelta(seconds=interval))
update_bluetooth(dt_util.utcnow())
return True
@@ -12,7 +12,9 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
REQUIREMENTS = ['fritzconnection==0.4.6']
REQUIREMENTS = ['https://github.com/deisi/fritzconnection/archive/'
'b5c14515e1c8e2652b06b6316a7f3913df942841.zip'
'#fritzconnection==0.4.6']
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -9,14 +9,15 @@ import threading
from datetime import timedelta
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, \
CONF_PORT
from homeassistant.util import Throttle
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pynetgear==0.3.2']
REQUIREMENTS = ['pynetgear==0.3.3']
def get_scanner(hass, config):
@@ -25,12 +26,13 @@ def get_scanner(hass, config):
host = info.get(CONF_HOST)
username = info.get(CONF_USERNAME)
password = info.get(CONF_PASSWORD)
port = info.get(CONF_PORT)
if password is not None and host is None:
_LOGGER.warning('Found username or password but no host')
return None
scanner = NetgearDeviceScanner(host, username, password)
scanner = NetgearDeviceScanner(host, username, password, port)
return scanner if scanner.success_init else None
@@ -38,7 +40,7 @@ def get_scanner(hass, config):
class NetgearDeviceScanner(object):
"""Queries a Netgear wireless router using the SOAP-API."""
def __init__(self, host, username, password):
def __init__(self, host, username, password, port):
"""Initialize the scanner."""
import pynetgear
@@ -49,8 +51,10 @@ class NetgearDeviceScanner(object):
self._api = pynetgear.Netgear()
elif username is None:
self._api = pynetgear.Netgear(password, host)
else:
elif port is None:
self._api = pynetgear.Netgear(password, host, username)
else:
self._api = pynetgear.Netgear(password, host, username, port)
_LOGGER.info("Logging in")
@@ -34,21 +34,33 @@ def setup_scanner(hass, config, see):
"""Setup an OwnTracks tracker."""
max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY)
def owntracks_location_update(topic, payload, qos):
"""MQTT message received."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typelocation
def validate_payload(payload, data_type):
"""Validate OwnTracks payload."""
try:
data = json.loads(payload)
except ValueError:
# If invalid JSON
_LOGGER.error(
'Unable to parse payload as JSON: %s', payload)
return
_LOGGER.error('Unable to parse payload as JSON: %s', payload)
return None
if not isinstance(data, dict) or data.get('_type') != data_type:
_LOGGER.debug('Skipping %s update for following data '
'because of missing or malformatted data: %s',
data_type, data)
return None
if max_gps_accuracy is not None and \
convert(data.get('acc'), float, 0.0) > max_gps_accuracy:
_LOGGER.debug('Skipping %s update because expected GPS '
'accuracy %s is not met: %s',
data_type, max_gps_accuracy, data)
return None
return data
if (not isinstance(data, dict) or data.get('_type') != 'location') or (
max_gps_accuracy is not None and
convert(data.get('acc'), float, 0.0) > max_gps_accuracy):
def owntracks_location_update(topic, payload, qos):
"""MQTT message received."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typelocation
data = validate_payload(payload, 'location')
if not data:
return
dev_id, kwargs = _parse_see_args(topic, data)
@@ -65,24 +77,16 @@ def setup_scanner(hass, config, see):
see_beacons(dev_id, kwargs)
def owntracks_event_update(topic, payload, qos):
# pylint: disable=too-many-branches, too-many-statements
"""MQTT event (geofences) received."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typetransition
try:
data = json.loads(payload)
except ValueError:
# If invalid JSON
_LOGGER.error(
'Unable to parse payload as JSON: %s', payload)
return
if not isinstance(data, dict) or data.get('_type') != 'transition':
data = validate_payload(payload, 'transition')
if not data:
return
if data.get('desc') is None:
_LOGGER.error(
"Location missing from `enter/exit` message - "
"Location missing from `Entering/Leaving` message - "
"please turn `Share` on in OwnTracks app")
return
# OwnTracks uses - at the start of a beacon zone
@@ -93,16 +97,16 @@ def setup_scanner(hass, config, see):
dev_id, kwargs = _parse_see_args(topic, data)
if data['event'] == 'enter':
def enter_event():
"""Execute enter event."""
zone = hass.states.get("zone.{}".format(location))
with LOCK:
if zone is None:
if data['t'] == 'b':
# Not a HA zone, and a beacon so assume mobile
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location not in beacons:
beacons.append(location)
_LOGGER.info("Added beacon %s", location)
if zone is None and data['t'] == 'b':
# Not a HA zone, and a beacon so assume mobile
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location not in beacons:
beacons.append(location)
_LOGGER.info("Added beacon %s", location)
else:
# Normal region
regions = REGIONS_ENTERED[dev_id]
@@ -114,7 +118,8 @@ def setup_scanner(hass, config, see):
see(**kwargs)
see_beacons(dev_id, kwargs)
elif data['event'] == 'leave':
def leave_event():
"""Execute leave event."""
with LOCK:
regions = REGIONS_ENTERED[dev_id]
if location in regions:
@@ -146,6 +151,10 @@ def setup_scanner(hass, config, see):
beacons.remove(location)
_LOGGER.info("Remove beacon %s", location)
if data['event'] == 'enter':
enter_event()
elif data['event'] == 'leave':
leave_event()
else:
_LOGGER.error(
'Misformatted mqtt msgs, _type=transition, event=%s',
+1 -1
View File
@@ -15,7 +15,7 @@ from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED)
DOMAIN = "discovery"
REQUIREMENTS = ['netdisco==0.6.1']
REQUIREMENTS = ['netdisco==0.6.4']
SCAN_INTERVAL = 300 # seconds
+9 -6
View File
@@ -10,8 +10,10 @@ import re
import threading
import requests
import voluptuous as vol
from homeassistant.helpers import validate_config
import homeassistant.helpers.config_validation as cv
from homeassistant.util import sanitize_filename
DOMAIN = "downloader"
@@ -21,6 +23,11 @@ SERVICE_DOWNLOAD_FILE = "download_file"
ATTR_URL = "url"
ATTR_SUBDIR = "subdir"
SERVICE_DOWNLOAD_FILE_SCHEMA = vol.Schema({
vol.Required(ATTR_URL): vol.Url,
vol.Optional(ATTR_SUBDIR): cv.string,
})
CONF_DOWNLOAD_DIR = 'download_dir'
@@ -48,10 +55,6 @@ def setup(hass, config):
def download_file(service):
"""Start thread to download file specified in the URL."""
if ATTR_URL not in service.data:
logger.error("Service called but 'url' parameter not specified.")
return
def do_download():
"""Download the file."""
try:
@@ -127,7 +130,7 @@ def setup(hass, config):
threading.Thread(target=do_download).start()
hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE,
download_file)
hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE, download_file,
schema=SERVICE_DOWNLOAD_FILE_SCHEMA)
return True
+80
View File
@@ -0,0 +1,80 @@
"""RSS/Atom feed reader for Home Assistant."""
from datetime import datetime
from logging import getLogger
import voluptuous as vol
from homeassistant.helpers.event import track_utc_time_change
REQUIREMENTS = ['feedparser==5.2.1']
_LOGGER = getLogger(__name__)
DOMAIN = "feedreader"
EVENT_FEEDREADER = "feedreader"
# pylint: disable=no-value-for-parameter
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
'urls': [vol.Url()],
}
}, extra=vol.ALLOW_EXTRA)
# pylint: disable=too-few-public-methods
class FeedManager(object):
"""Abstraction over feedparser module."""
def __init__(self, url, hass):
"""Initialize the FeedManager object, poll every hour."""
self._url = url
self._feed = None
self._hass = hass
# Initialize last entry timestamp as epoch time
self._last_entry_timestamp = datetime.utcfromtimestamp(0).timetuple()
_LOGGER.debug('Loading feed %s', self._url)
self._update()
track_utc_time_change(hass, lambda now: self._update(),
minute=0, second=0)
def _log_no_entries(self):
"""Send no entries log at debug level."""
_LOGGER.debug('No new entries in feed %s', self._url)
def _update(self):
"""Update the feed and publish new entries in the event bus."""
import feedparser
_LOGGER.info('Fetching new data from feed %s', self._url)
self._feed = feedparser.parse(self._url,
etag=None if not self._feed
else self._feed.get('etag'),
modified=None if not self._feed
else self._feed.get('modified'))
if not self._feed:
_LOGGER.error('Error fetching feed data from %s', self._url)
else:
if self._feed.bozo != 0:
_LOGGER.error('Error parsing feed %s', self._url)
# Using etag and modified, if there's no new data available,
# the entries list will be empty
elif len(self._feed.entries) > 0:
_LOGGER.debug('Entries available in feed %s', self._url)
self._publish_new_entries()
self._last_entry_timestamp = \
self._feed.entries[0].published_parsed
else:
self._log_no_entries()
def _publish_new_entries(self):
"""Publish new entries to the event bus."""
new_entries = False
for entry in self._feed.entries:
# Consider only entries newer then the latest parsed one
if entry.published_parsed > self._last_entry_timestamp:
new_entries = True
entry.update({'feed_url': self._url})
self._hass.bus.fire(EVENT_FEEDREADER, entry)
if not new_entries:
self._log_no_entries()
def setup(hass, config):
"""Setup the feedreader component."""
urls = config.get(DOMAIN)['urls']
feeds = [FeedManager(url, hass) for url in urls]
return len(feeds) > 0
@@ -1,2 +1,2 @@
"""DO NOT MODIFY. Auto-generated by update_mdi script."""
VERSION = "df49e6b7c930eb39b42ff1909712e95e"
VERSION = "af8a531f1c2e477c07c4b3394bd1ce13"
+1 -1
View File
@@ -1,2 +1,2 @@
"""DO NOT MODIFY. Auto-generated by build_frontend script."""
VERSION = "4062ab87999fd5e382074ba9d7a880d7"
VERSION = "ffd8a1bde5ba13f300c3d6ad32036526"
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long
@@ -7,10 +7,13 @@ at https://home-assistant.io/components/garage_door/
import logging
import os
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
ATTR_ENTITY_ID)
@@ -29,6 +32,10 @@ DISCOVERY_PLATFORMS = {
wink.DISCOVER_GARAGE_DOORS: 'wink'
}
GARAGE_DOOR_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
_LOGGER = logging.getLogger(__name__)
@@ -73,10 +80,11 @@ def setup(hass, config):
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service,
descriptions.get(SERVICE_OPEN))
descriptions.get(SERVICE_OPEN),
schema=GARAGE_DOOR_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service,
descriptions.get(SERVICE_CLOSE))
descriptions.get(SERVICE_CLOSE),
schema=GARAGE_DOOR_SERVICE_SCHEMA)
return True
@@ -0,0 +1,139 @@
"""
Support for MQTT garage doors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/garage_door.mqtt/
"""
import logging
import voluptuous as vol
from homeassistant.const import (STATE_OPEN, STATE_CLOSED, SERVICE_OPEN,
SERVICE_CLOSE)
import homeassistant.components.mqtt as mqtt
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import (
CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE)
from homeassistant.components.mqtt import (
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
CONF_STATE_OPEN = 'state_open'
CONF_STATE_CLOSED = 'state_closed'
CONF_SERVICE_OPEN = 'service_open'
CONF_SERVICE_CLOSE = 'service_close'
DEFAULT_NAME = 'MQTT Garage Door'
DEFAULT_OPTIMISTIC = False
DEFAULT_RETAIN = False
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string,
vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string,
vol.Optional(CONF_SERVICE_OPEN, default=SERVICE_OPEN): cv.string,
vol.Optional(CONF_SERVICE_CLOSE, default=SERVICE_CLOSE): cv.string
})
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Add MQTT Garage Door."""
add_devices_callback([MqttGarageDoor(
hass,
config[CONF_NAME],
config.get(CONF_STATE_TOPIC),
config[CONF_COMMAND_TOPIC],
config[CONF_QOS],
config[CONF_RETAIN],
config[CONF_STATE_OPEN],
config[CONF_STATE_CLOSED],
config[CONF_SERVICE_OPEN],
config[CONF_SERVICE_CLOSE],
config[CONF_OPTIMISTIC],
config.get(CONF_VALUE_TEMPLATE))])
# pylint: disable=too-many-arguments, too-many-instance-attributes
class MqttGarageDoor(GarageDoorDevice):
"""Representation of a MQTT garage door."""
def __init__(self, hass, name, state_topic, command_topic, qos, retain,
state_open, state_closed, service_open, service_close,
optimistic, value_template):
"""Initialize the garage door."""
self._hass = hass
self._name = name
self._state_topic = state_topic
self._command_topic = command_topic
self._qos = qos
self._retain = retain
self._state_open = state_open
self._state_closed = state_closed
self._service_open = service_open
self._service_close = service_close
self._optimistic = optimistic or state_topic is None
self._state = False
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
if value_template is not None:
payload = template.render_with_possible_json_value(
hass, value_template, payload)
if payload == self._state_open:
self._state = True
self.update_ha_state()
elif payload == self._state_closed:
self._state = False
self.update_ha_state()
if self._state_topic is None:
# Force into optimistic mode.
self._optimistic = True
else:
mqtt.subscribe(hass, self._state_topic, message_received,
self._qos)
@property
def name(self):
"""Return the name of the garage door if any."""
return self._name
@property
def is_opened(self):
"""Return true if door is closed."""
return self._state
@property
def is_closed(self):
"""Return true if door is closed."""
return self._state is False
@property
def assumed_state(self):
"""Return true if we do optimistic updates."""
return self._optimistic
def close_door(self):
"""Close the door."""
mqtt.publish(self.hass, self._command_topic, self._service_close,
self._qos, self._retain)
if self._optimistic:
# Optimistically assume that door has changed state.
self._state = False
self.update_ha_state()
def open_door(self):
"""Open the door."""
mqtt.publish(self.hass, self._command_topic, self._service_open,
self._qos, self._retain)
if self._optimistic:
# Optimistically assume that door has changed state.
self._state = True
self.update_ha_state()
+1 -1
View File
@@ -9,7 +9,7 @@ import logging
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
def setup_platform(hass, config, add_devices, discovery_info=None):
+2 -2
View File
@@ -39,7 +39,7 @@ def _conf_preprocess(value):
return value
_SINGLE_GROUP_CONFIG = vol.Schema(vol.All(_conf_preprocess, {
vol.Required(CONF_ENTITIES): cv.entity_ids,
vol.Optional(CONF_ENTITIES): vol.Any(None, cv.entity_ids),
CONF_VIEW: bool,
CONF_NAME: str,
CONF_ICON: cv.icon,
@@ -145,7 +145,7 @@ def setup(hass, config):
"""Setup all groups found definded in the configuration."""
for object_id, conf in config.get(DOMAIN, {}).items():
name = conf.get(CONF_NAME, object_id)
entity_ids = conf[CONF_ENTITIES]
entity_ids = conf.get(CONF_ENTITIES) or []
icon = conf.get(CONF_ICON)
view = conf.get(CONF_VIEW)
+1 -1
View File
@@ -182,7 +182,7 @@ def _api_history_period(handler, path_match, data):
one_day = timedelta(seconds=86400)
if date_str:
start_date = dt_util.date_str_to_date(date_str)
start_date = dt_util.parse_date(date_str)
if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)
+22 -7
View File
@@ -5,6 +5,7 @@ For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/
"""
import gzip
import hmac
import json
import logging
import ssl
@@ -27,7 +28,7 @@ from homeassistant.const import (
HTTP_HEADER_CONTENT_LENGTH, HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_EXPIRES,
HTTP_HEADER_HA_AUTH, HTTP_HEADER_VARY, HTTP_METHOD_NOT_ALLOWED,
HTTP_NOT_FOUND, HTTP_OK, HTTP_UNAUTHORIZED, HTTP_UNPROCESSABLE_ENTITY,
SERVER_PORT)
SERVER_PORT, URL_ROOT, URL_API_EVENT_FORWARD)
DOMAIN = "http"
@@ -200,12 +201,26 @@ class RequestHandler(SimpleHTTPRequestHandler):
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
return
self.authenticated = (self.server.api_password is None or
self.headers.get(HTTP_HEADER_HA_AUTH) ==
self.server.api_password or
data.get(DATA_API_PASSWORD) ==
self.server.api_password or
self.verify_session())
if self.verify_session():
# The user has a valid session already
self.authenticated = True
elif self.server.api_password is None:
# No password is set, so everyone is authenticated
self.authenticated = True
elif hmac.compare_digest(self.headers.get(HTTP_HEADER_HA_AUTH, ''),
self.server.api_password):
# A valid auth header has been set
self.authenticated = True
elif hmac.compare_digest(data.get(DATA_API_PASSWORD, ''),
self.server.api_password):
# A valid password has been specified
self.authenticated = True
else:
self.authenticated = False
# we really shouldn't need to forward the password from here
if url.path not in [URL_ROOT, URL_API_EVENT_FORWARD]:
data.pop(DATA_API_PASSWORD, None)
if '_METHOD' in data:
method = data.pop('_METHOD')
+12 -4
View File
@@ -7,8 +7,10 @@ https://home-assistant.io/components/ifttt/
import logging
import requests
import voluptuous as vol
from homeassistant.helpers import validate_config
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -23,6 +25,13 @@ ATTR_VALUE3 = 'value3'
REQUIREMENTS = ['pyfttt==0.3']
SERVICE_TRIGGER_SCHEMA = vol.Schema({
vol.Required(ATTR_EVENT): cv.string,
vol.Optional(ATTR_VALUE1): cv.string,
vol.Optional(ATTR_VALUE2): cv.string,
vol.Optional(ATTR_VALUE3): cv.string,
})
def trigger(hass, event, value1=None, value2=None, value3=None):
"""Trigger a Maker IFTTT recipe."""
@@ -44,12 +53,10 @@ def setup(hass, config):
def trigger_service(call):
"""Handle IFTTT trigger service calls."""
event = call.data.get(ATTR_EVENT)
event = call.data[ATTR_EVENT]
value1 = call.data.get(ATTR_VALUE1)
value2 = call.data.get(ATTR_VALUE2)
value3 = call.data.get(ATTR_VALUE3)
if event is None:
return
try:
import pyfttt as pyfttt
@@ -57,6 +64,7 @@ def setup(hass, config):
except requests.exceptions.RequestException:
_LOGGER.exception("Error communicating with IFTTT")
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service)
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service,
schema=SERVICE_TRIGGER_SCHEMA)
return True
+5 -3
View File
@@ -7,7 +7,8 @@ https://home-assistant.io/components/influxdb/
import logging
import homeassistant.util as util
from homeassistant.const import EVENT_STATE_CHANGED, STATE_UNKNOWN
from homeassistant.const import (EVENT_STATE_CHANGED, STATE_UNAVAILABLE,
STATE_UNKNOWN)
from homeassistant.helpers import state as state_helper
from homeassistant.helpers import validate_config
@@ -70,8 +71,9 @@ def setup(hass, config):
def influx_event_listener(event):
"""Listen for new messages on the bus and sends them to Influx."""
state = event.data.get('new_state')
if state is None or state.state in (STATE_UNKNOWN, '') \
or state.entity_id in blacklist:
if state is None or state.state in (
STATE_UNKNOWN, '', STATE_UNAVAILABLE) or \
state.entity_id in blacklist:
return
try:
+11 -2
View File
@@ -6,8 +6,11 @@ at https://home-assistant.io/components/input_boolean/
"""
import logging
import voluptuous as vol
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
@@ -22,6 +25,10 @@ CONF_NAME = "name"
CONF_INITIAL = "initial"
CONF_ICON = "icon"
TOGGLE_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_on(hass, entity_id):
"""Test if input_boolean is True."""
@@ -75,8 +82,10 @@ def setup(hass, config):
else:
input_b.turn_off()
hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service,
schema=TOGGLE_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service,
schema=TOGGLE_SERVICE_SCHEMA)
component.add_entities(entities)
+11 -2
View File
@@ -6,7 +6,10 @@ at https://home-assistant.io/components/input_select/
"""
import logging
import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
@@ -25,6 +28,11 @@ ATTR_OPTIONS = 'options'
SERVICE_SELECT_OPTION = 'select_option'
SERVICE_SELECT_OPTION_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_OPTION): cv.string,
})
def select_option(hass, entity_id, option):
"""Set input_select to False."""
@@ -79,10 +87,11 @@ def setup(hass, config):
target_inputs = component.extract_from_service(call)
for input_select in target_inputs:
input_select.select_option(call.data.get(ATTR_OPTION))
input_select.select_option(call.data[ATTR_OPTION])
hass.services.register(DOMAIN, SERVICE_SELECT_OPTION,
select_option_service)
select_option_service,
schema=SERVICE_SELECT_OPTION_SCHEMA)
component.add_entities(entities)
+11 -2
View File
@@ -6,7 +6,10 @@ at https://home-assistant.io/components/input_slider/
"""
import logging
import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
@@ -29,6 +32,11 @@ ATTR_STEP = 'step'
SERVICE_SELECT_VALUE = 'select_value'
SERVICE_SELECT_VALUE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_VALUE): vol.Coerce(int),
})
def select_value(hass, entity_id, value):
"""Set input_slider to value."""
@@ -81,10 +89,11 @@ def setup(hass, config):
target_inputs = component.extract_from_service(call)
for input_slider in target_inputs:
input_slider.select_value(call.data.get(ATTR_VALUE))
input_slider.select_value(call.data[ATTR_VALUE])
hass.services.register(DOMAIN, SERVICE_SELECT_VALUE,
select_value_service)
select_value_service,
schema=SERVICE_SELECT_VALUE_SCHEMA)
component.add_entities(entities)
+16 -7
View File
@@ -4,6 +4,8 @@ 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 voluptuous as vol
from homeassistant.const import (
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PLAY_PAUSE,
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
@@ -12,6 +14,8 @@ from homeassistant.const import (
DOMAIN = "keyboard"
REQUIREMENTS = ['pyuserinput==0.1.9']
TAP_KEY_SCHEMA = vol.Schema({})
def volume_up(hass):
"""Press the keyboard button for volume up."""
@@ -52,26 +56,31 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
lambda service:
keyboard.tap_key(keyboard.volume_up_key))
keyboard.tap_key(keyboard.volume_up_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
lambda service:
keyboard.tap_key(keyboard.volume_down_key))
keyboard.tap_key(keyboard.volume_down_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE,
lambda service:
keyboard.tap_key(keyboard.volume_mute_key))
keyboard.tap_key(keyboard.volume_mute_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
lambda service:
keyboard.tap_key(keyboard.media_play_pause_key))
keyboard.tap_key(keyboard.media_play_pause_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
lambda service:
keyboard.tap_key(keyboard.media_next_track_key))
keyboard.tap_key(keyboard.media_next_track_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK,
lambda service:
keyboard.tap_key(keyboard.media_prev_track_key))
keyboard.tap_key(keyboard.media_prev_track_key),
schema=TAP_KEY_SCHEMA)
return True
+2 -2
View File
@@ -92,8 +92,8 @@ LIGHT_TURN_ON_SCHEMA = vol.Schema({
ATTR_XY_COLOR: vol.All(vol.ExactSequence((cv.small_float, cv.small_float)),
vol.Coerce(tuple)),
ATTR_COLOR_TEMP: vol.All(int, vol.Range(min=154, max=500)),
ATTR_FLASH: [FLASH_SHORT, FLASH_LONG],
ATTR_EFFECT: [EFFECT_COLORLOOP, EFFECT_RANDOM, EFFECT_WHITE],
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
ATTR_EFFECT: vol.In([EFFECT_COLORLOOP, EFFECT_RANDOM, EFFECT_WHITE]),
})
LIGHT_TURN_OFF_SCHEMA = vol.Schema({
+6 -1
View File
@@ -32,6 +32,9 @@ PHUE_CONFIG_FILE = "phue.conf"
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
# Track previously setup bridges
_CONFIGURED_BRIDGES = {}
def _find_host_from_config(hass, filename=PHUE_CONFIG_FILE):
"""Attempt to detect host based on existing configuration."""
@@ -68,7 +71,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
return False
# Only act if we are not already configuring this host
if host in _CONFIGURING:
if host in _CONFIGURING or \
socket.gethostbyname(host) in _CONFIGURED_BRIDGES:
return
setup_bridge(host, hass, add_devices_callback, filename, allow_unreachable)
@@ -142,6 +146,7 @@ def setup_bridge(host, hass, add_devices_callback, filename,
if new_lights:
add_devices_callback(new_lights)
_CONFIGURED_BRIDGES[socket.gethostbyname(host)] = True
update_lights()
+5 -4
View File
@@ -19,7 +19,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup a Hyperion server remote."""
host = config.get(CONF_HOST, None)
port = config.get("port", 19444)
device = Hyperion(host, port)
device = Hyperion(config.get('name', host), host, port)
if device.setup():
add_devices_callback([device])
return True
@@ -30,11 +30,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
class Hyperion(Light):
"""Representation of a Hyperion remote."""
def __init__(self, host, port):
def __init__(self, name, host, port):
"""Initialize the light."""
self._host = host
self._port = port
self._name = host
self._name = name
self._is_available = True
self._rgb_color = [255, 255, 255]
@@ -75,7 +75,8 @@ class Hyperion(Light):
"""Get the hostname of the remote."""
response = self.json_request({"command": "serverinfo"})
if response:
self._name = response["info"]["hostname"]
if self._name == self._host:
self._name = response["info"]["hostname"]
return True
return False
+2
View File
@@ -209,6 +209,8 @@ class LIFXLight(Light):
brightness = self._bri
if ATTR_COLOR_TEMP in kwargs:
# pylint: disable=fixme
# TODO: Use color_temperature_mired_to_kelvin from util.color
kelvin = int(((TEMP_MAX - TEMP_MIN) *
(kwargs[ATTR_COLOR_TEMP] - TEMP_MIN_HASS) /
(TEMP_MAX_HASS - TEMP_MIN_HASS)) + TEMP_MIN)
+11 -6
View File
@@ -6,8 +6,8 @@ https://home-assistant.io/components/light.mysensors/
"""
import logging
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light)
from homeassistant.components.light import (ATTR_BRIGHTNESS, ATTR_RGB_COLOR,
Light)
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
from homeassistant.loader import get_component
from homeassistant.util.color import rgb_hex_to_rgb_list
@@ -100,15 +100,20 @@ class MySensorsLight(Light):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
device_attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
address = getattr(self.gateway, 'server_address', None)
if address:
device = '{}:{}'.format(address[0], address[1])
else:
device = self.gateway.port
attr = {
self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,
}
for value_type, value in self._values.items():
device_attr[self.gateway.const.SetReq(value_type).name] = value
return device_attr
attr[self.gateway.const.SetReq(value_type).name] = value
return attr
@property
def available(self):
+1 -2
View File
@@ -72,8 +72,7 @@ class VeraLight(VeraDevice, Light):
last_tripped = self.vera_device.last_trip
if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time)
attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
else:
attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped
+42 -8
View File
@@ -6,10 +6,14 @@ https://home-assistant.io/components/light.wink/
"""
import logging
from homeassistant.components.light import ATTR_BRIGHTNESS, Light
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, \
Light, ATTR_RGB_COLOR
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.util import color as color_util
from homeassistant.util.color import \
color_temperature_mired_to_kelvin as mired_to_kelvin
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
@@ -35,7 +39,11 @@ class WinkLight(Light):
"""Representation of a Wink light."""
def __init__(self, wink):
"""Initialize the light."""
"""
Initialize the light.
:type wink: pywink.devices.standard.bulb.WinkBulb
"""
self.wink = wink
@property
@@ -63,15 +71,41 @@ class WinkLight(Light):
"""True if connection == True."""
return self.wink.available
@property
def xy_color(self):
"""Current bulb color in CIE 1931 (XY) color space."""
if not self.wink.supports_xy_color():
return None
return self.wink.color_xy()
@property
def color_temp(self):
"""Current bulb color in degrees Kelvin."""
if not self.wink.supports_temperature():
return None
return color_util.color_temperature_kelvin_to_mired(
self.wink.color_temperature_kelvin())
# pylint: disable=too-few-public-methods
def turn_on(self, **kwargs):
"""Turn the switch on."""
brightness = kwargs.get(ATTR_BRIGHTNESS)
rgb_color = kwargs.get(ATTR_RGB_COLOR)
color_temp_mired = kwargs.get(ATTR_COLOR_TEMP)
if brightness is not None:
self.wink.set_state(True, brightness=brightness / 255)
else:
self.wink.set_state(True)
state_kwargs = {
}
if rgb_color:
state_kwargs['color_xy'] = color_util.color_RGB_to_xy(*rgb_color)
if color_temp_mired:
state_kwargs['color_kelvin'] = mired_to_kelvin(color_temp_mired)
if brightness:
state_kwargs['brightness'] = brightness / 255.0
self.wink.set_state(True, **state_kwargs)
def turn_off(self):
"""Turn the switch off."""
@@ -79,4 +113,4 @@ class WinkLight(Light):
def update(self):
"""Update state of the light."""
self.wink.update_state()
self.wink.update_state(require_desired_state_fulfilled=True)
+9 -11
View File
@@ -9,25 +9,23 @@ https://home-assistant.io/components/light.zwave/
from threading import Timer
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN, Light
from homeassistant.components.zwave import (
ATTR_NODE_ID, ATTR_VALUE_ID, COMMAND_CLASS_SWITCH_MULTILEVEL, GENRE_USER,
NETWORK, TYPE_BYTE, ZWaveDeviceEntity)
from homeassistant.components import zwave
from homeassistant.const import STATE_OFF, STATE_ON
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Find and add Z-Wave lights."""
if discovery_info is None or NETWORK is None:
if discovery_info is None or zwave.NETWORK is None:
return
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
if value.command_class != COMMAND_CLASS_SWITCH_MULTILEVEL:
if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL:
return
if value.type != TYPE_BYTE:
if value.type != zwave.TYPE_BYTE:
return
if value.genre != GENRE_USER:
if value.genre != zwave.GENRE_USER:
return
value.set_change_verified(False)
@@ -42,7 +40,7 @@ def brightness_state(value):
return 255, STATE_OFF
class ZwaveDimmer(ZWaveDeviceEntity, Light):
class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
"""Representation of a Z-Wave dimmer."""
# pylint: disable=too-many-arguments
@@ -51,7 +49,7 @@ class ZwaveDimmer(ZWaveDeviceEntity, Light):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._brightness, self._state = brightness_state(value)
+13 -7
View File
@@ -8,10 +8,13 @@ from datetime import timedelta
import logging
import os
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED,
STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK)
@@ -33,6 +36,11 @@ DISCOVERY_PLATFORMS = {
verisure.DISCOVER_LOCKS: 'verisure'
}
LOCK_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string,
})
_LOGGER = logging.getLogger(__name__)
@@ -75,10 +83,7 @@ def setup(hass, config):
"""Handle calls to the lock services."""
target_locks = component.extract_from_service(service)
if ATTR_CODE not in service.data:
code = None
else:
code = service.data[ATTR_CODE]
code = service.data.get(ATTR_CODE)
for item in target_locks:
if service.service == SERVICE_LOCK:
@@ -92,10 +97,11 @@ def setup(hass, config):
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service,
descriptions.get(SERVICE_UNLOCK))
descriptions.get(SERVICE_UNLOCK),
schema=LOCK_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service,
descriptions.get(SERVICE_LOCK))
descriptions.get(SERVICE_LOCK),
schema=LOCK_SERVICE_SCHEMA)
return True
+1 -1
View File
@@ -9,7 +9,7 @@ import logging
from homeassistant.components.lock import LockDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
def setup_platform(hass, config, add_devices, discovery_info=None):
+18 -10
View File
@@ -9,6 +9,8 @@ import re
from datetime import timedelta
from itertools import groupby
import voluptuous as vol
import homeassistant.util.dt as dt_util
from homeassistant.components import recorder, sun
from homeassistant.const import (
@@ -18,6 +20,7 @@ from homeassistant.core import DOMAIN as HA_DOMAIN
from homeassistant.core import State
from homeassistant.helpers.entity import split_entity_id
from homeassistant.helpers import template
import homeassistant.helpers.config_validation as cv
DOMAIN = "logbook"
DEPENDENCIES = ['recorder', 'http']
@@ -39,6 +42,13 @@ ATTR_MESSAGE = 'message'
ATTR_DOMAIN = 'domain'
ATTR_ENTITY_ID = 'entity_id'
LOG_MESSAGE_SCHEMA = vol.Schema({
vol.Required(ATTR_NAME): cv.string,
vol.Required(ATTR_MESSAGE): cv.string,
vol.Optional(ATTR_DOMAIN): cv.slug,
vol.Optional(ATTR_ENTITY_ID): cv.entity_id,
})
def log_entry(hass, name, message, domain=None, entity_id=None):
"""Add an entry to the logbook."""
@@ -58,19 +68,17 @@ def setup(hass, config):
"""Listen for download events to download files."""
def log_message(service):
"""Handle sending notification message service calls."""
message = service.data.get(ATTR_MESSAGE)
name = service.data.get(ATTR_NAME)
domain = service.data.get(ATTR_DOMAIN, None)
entity_id = service.data.get(ATTR_ENTITY_ID, None)
if not message or not name:
return
message = service.data[ATTR_MESSAGE]
name = service.data[ATTR_NAME]
domain = service.data.get(ATTR_DOMAIN)
entity_id = service.data.get(ATTR_ENTITY_ID)
message = template.render(hass, message)
log_entry(hass, name, message, domain, entity_id)
hass.http.register_path('GET', URL_LOGBOOK, _handle_get_logbook)
hass.services.register(DOMAIN, 'log', log_message)
hass.services.register(DOMAIN, 'log', log_message,
schema=LOG_MESSAGE_SCHEMA)
return True
@@ -79,7 +87,7 @@ def _handle_get_logbook(handler, path_match, data):
date_str = path_match.group('date')
if date_str:
start_date = dt_util.date_str_to_date(date_str)
start_date = dt_util.parse_date(date_str)
if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)
@@ -114,7 +122,7 @@ class Entry(object):
def as_dict(self):
"""Convert entry to a dict to be used within JSON."""
return {
'when': dt_util.datetime_to_str(self.when),
'when': self.when,
'name': self.name,
'message': self.message,
'domain': self.domain,
@@ -61,6 +61,7 @@ ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name'
ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands'
ATTR_INPUT_SOURCE = 'source'
ATTR_INPUT_SOURCE_LIST = 'source_list'
MEDIA_TYPE_MUSIC = 'music'
MEDIA_TYPE_TVSHOW = 'tvshow'
@@ -94,6 +95,7 @@ SERVICE_TO_METHOD = {
SERVICE_MEDIA_PAUSE: 'media_pause',
SERVICE_MEDIA_NEXT_TRACK: 'media_next_track',
SERVICE_MEDIA_PREVIOUS_TRACK: 'media_previous_track',
SERVICE_SELECT_SOURCE: 'select_source'
}
ATTR_TO_PROPERTY = [
@@ -116,6 +118,7 @@ ATTR_TO_PROPERTY = [
ATTR_APP_NAME,
ATTR_SUPPORTED_MEDIA_COMMANDS,
ATTR_INPUT_SOURCE,
ATTR_INPUT_SOURCE_LIST,
]
# Service call validation schemas
@@ -473,6 +476,11 @@ class MediaPlayerDevice(Entity):
"""Name of the current input source."""
return None
@property
def source_list(self):
"""List of available input sources."""
return None
@property
def supported_media_commands(self):
"""Flag media commands that are supported."""
@@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
])
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/hqdefault.jpg'
YOUTUBE_PLAYER_SUPPORT = \
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
@@ -208,7 +208,8 @@ class DemoMusicPlayer(AbstractDemoPlayer):
@property
def media_image_url(self):
"""Return the image url of current playing media."""
return 'https://graph.facebook.com/107771475912710/picture'
return 'https://graph.facebook.com/v2.5/107771475912710/' \
'picture?type=large'
@property
def media_title(self):
@@ -287,7 +288,7 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
@property
def media_image_url(self):
"""Return the image url of current playing media."""
return 'https://graph.facebook.com/HouseofCards/picture'
return 'https://graph.facebook.com/v2.5/HouseofCards/picture?width=400'
@property
def media_title(self):
+16 -3
View File
@@ -10,14 +10,16 @@ import socket
from homeassistant.components.media_player import (
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON,
SUPPORT_VOLUME_SET, MediaPlayerDevice)
SUPPORT_VOLUME_SET, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_PLAYLIST,
MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['python-mpd2==0.5.5']
SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \
SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
SUPPORT_PLAY_MEDIA
# pylint: disable=unused-argument
@@ -64,7 +66,7 @@ class MpdDevice(MediaPlayerDevice):
"""Representation of a MPD server."""
# MPD confuses pylint
# pylint: disable=no-member, abstract-method
# pylint: disable=no-member, too-many-public-methods, abstract-method
def __init__(self, server, port, location, password):
"""Initialize the MPD device."""
import mpd
@@ -203,3 +205,14 @@ class MpdDevice(MediaPlayerDevice):
def media_previous_track(self):
"""Service to send the MPD the command for previous track."""
self.client.previous()
def play_media(self, media_type, media_id):
"""Send the media player the command for playing a playlist."""
_LOGGER.info(str.format("Playing playlist: {0}", media_id))
if media_type == MEDIA_TYPE_PLAYLIST:
self.client.clear()
self.client.load(media_id)
self.client.play()
else:
_LOGGER.error(str.format("Invalid media type. Expected: {0}",
MEDIA_TYPE_PLAYLIST))
@@ -18,6 +18,7 @@ from homeassistant.const import (
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING,
STATE_UNKNOWN)
from homeassistant.loader import get_component
from homeassistant.helpers.event import (track_utc_time_change)
REQUIREMENTS = ['plexapi==1.1.0']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
@@ -113,6 +114,7 @@ def setup_plexserver(host, token, hass, add_devices_callback):
plex_clients = {}
plex_sessions = {}
track_utc_time_change(hass, lambda now: update_devices(), second=30)
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():
+27 -7
View File
@@ -6,6 +6,7 @@ https://home-assistant.io/components/media_player.sonos/
"""
import datetime
import logging
import socket
from homeassistant.components.media_player import (
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
@@ -13,7 +14,7 @@ from homeassistant.components.media_player import (
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
from homeassistant.const import (
STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN)
STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, STATE_OFF)
REQUIREMENTS = ['SoCo==0.11.1']
@@ -36,11 +37,13 @@ SUPPORT_SONOS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Sonos platform."""
import soco
import socket
if discovery_info:
add_devices([SonosDevice(hass, soco.SoCo(discovery_info))])
return True
player = soco.SoCo(discovery_info)
if player.is_visible:
add_devices([SonosDevice(hass, player)])
return True
return False
players = None
hosts = config.get('hosts', None)
@@ -138,9 +141,14 @@ class SonosDevice(MediaPlayerDevice):
"""Retrieve latest state."""
self._name = self._player.get_speaker_info()['zone_name'].replace(
' (R)', '').replace(' (L)', '')
self._status = self._player.get_current_transport_info().get(
'current_transport_state')
self._trackinfo = self._player.get_current_track_info()
if self.available:
self._status = self._player.get_current_transport_info().get(
'current_transport_state')
self._trackinfo = self._player.get_current_track_info()
else:
self._status = STATE_OFF
self._trackinfo = {}
@property
def volume_level(self):
@@ -253,3 +261,15 @@ class SonosDevice(MediaPlayerDevice):
def play_media(self, media_type, media_id):
"""Send the play_media command to the media player."""
self._player.play_uri(media_id)
@property
def available(self):
"""Return True if player is reachable, False otherwise."""
try:
sock = socket.create_connection(
address=(self._player.ip_address, 1443),
timeout=3)
sock.close()
return True
except socket.error:
return False
@@ -0,0 +1,262 @@
"""
Support for interface with an LG WebOS TV.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.webostv/
"""
import logging
from datetime import timedelta
from urllib.parse import urlparse
import homeassistant.util as util
from homeassistant.components.media_player import (
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP,
SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL,
MediaPlayerDevice)
from homeassistant.const import (
CONF_HOST, STATE_OFF, STATE_PLAYING, STATE_PAUSED, STATE_UNKNOWN)
from homeassistant.loader import get_component
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/TheRealLink/pylgtv'
'/archive/v0.1.2.zip'
'#pylgtv==0.1.2']
SUPPORT_WEBOSTV = SUPPORT_PAUSE | SUPPORT_VOLUME_STEP | \
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | \
SUPPORT_NEXT_TRACK | SUPPORT_TURN_OFF | \
SUPPORT_SELECT_SOURCE | SUPPORT_PLAY_MEDIA
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the LG WebOS TV platform."""
if discovery_info is not None:
host = urlparse(discovery_info[1]).hostname
else:
host = config.get(CONF_HOST, None)
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
setup_tv(host, hass, add_devices)
def setup_tv(host, hass, add_devices):
"""Setup a phue bridge based on host parameter."""
from pylgtv import WebOsClient
from pylgtv import PyLGTVPairException
client = WebOsClient(host)
if not client.is_registered():
if host in _CONFIGURING:
# Try to pair.
try:
client.register()
except PyLGTVPairException:
_LOGGER.warning(
'Connected to LG WebOS TV at %s but not paired.', host)
return
except ConnectionRefusedError:
_LOGGER.error('Unable to connect to host %s.', host)
return
else:
# Not registered, request configuration.
_LOGGER.warning('LG WebOS TV at %s needs to be paired.', host)
request_configuration(host, hass, add_devices)
return
# If we came here and configuring this host, mark as done.
if client.is_registered() and host in _CONFIGURING:
request_id = _CONFIGURING.pop(host)
configurator = get_component('configurator')
configurator.request_done(request_id)
add_devices([LgWebOSDevice(host)])
def request_configuration(host, hass, add_devices):
"""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 pair, please try again.')
return
# pylint: disable=unused-argument
def lgtv_configuration_callback(data):
"""The actions to do when our configuration callback is called."""
setup_tv(host, hass, add_devices)
_CONFIGURING[host] = configurator.request_config(
hass, 'LG WebOS TV', lgtv_configuration_callback,
description='Click start and accept the pairing request on your tv.',
description_image='/static/images/config_webos.png',
submit_caption='Start pairing request'
)
# pylint: disable=abstract-method
# pylint: disable=too-many-instance-attributes
class LgWebOSDevice(MediaPlayerDevice):
"""Representation of a LG WebOS TV."""
# pylint: disable=too-many-public-methods
def __init__(self, host):
"""Initialize the webos device."""
from pylgtv import WebOsClient
self._client = WebOsClient(host)
self._name = 'LG WebOS TV Remote'
# Assume that the TV is not muted
self._muted = False
# Assume that the TV is in Play mode
self._playing = True
self._volume = 0
self._current_source = None
self._current_source_id = None
self._source_list = None
self._source_label_list = None
self._state = STATE_UNKNOWN
self._app_list = None
self.update()
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update(self):
"""Retrieve the latest data."""
try:
self._state = STATE_PLAYING
self._muted = self._client.get_muted()
self._volume = self._client.get_volume()
self._current_source_id = self._client.get_input()
self._source_list = {}
self._source_label_list = []
self._app_list = {}
for app in self._client.get_apps():
self._app_list[app['id']] = app
for source in self._client.get_inputs():
self._source_list[source['label']] = source
self._app_list[source['appId']] = source
self._source_label_list.append(source['label'])
if source['appId'] == self._current_source_id:
self._current_source = source['label']
except ConnectionRefusedError:
self._state = STATE_OFF
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._muted
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
return self._volume / 100.0
@property
def source(self):
"""Return the current input source."""
return self._current_source
@property
def source_list(self):
"""List of available input sources."""
return self._source_label_list
@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_CHANNEL
@property
def media_image_url(self):
"""Image url of current playing media."""
return self._app_list[self._current_source_id]['icon']
@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
return SUPPORT_WEBOSTV
def turn_off(self):
"""Turn off media player."""
self._client.power_off()
def volume_up(self):
"""Volume up the media player."""
self._client.volume_up()
def volume_down(self):
"""Volume down media player."""
self._client.volume_down()
def set_volume_level(self, volume):
"""Set volume level, range 0..1."""
tv_volume = volume * 100
self._client.set_volume(tv_volume)
def mute_volume(self, mute):
"""Send mute command."""
self._muted = mute
self._client.set_mute(mute)
def media_play_pause(self):
"""Simulate play pause media player."""
if self._playing:
self.media_pause()
else:
self.media_play()
def select_source(self, source):
"""Select input source."""
self._current_source_id = self._source_list[source]['appId']
self._current_source = self._source_list[source]['label']
self._client.set_input(self._source_list[source]['id'])
def media_play(self):
"""Send play command."""
self._playing = True
self._state = STATE_PLAYING
self._client.play()
def media_pause(self):
"""Send media pause command to media player."""
self._playing = False
self._state = STATE_PAUSED
self._client.pause()
def media_next_track(self):
"""Send next track command."""
self._client.fast_forward()
def media_previous_track(self):
"""Send the previous track command."""
self._client.rewind()
@@ -8,13 +8,15 @@ import logging
from homeassistant.components.media_player import (
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
SUPPORT_SELECT_SOURCE, MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_ON
REQUIREMENTS = ['rxv==0.1.11']
_LOGGER = logging.getLogger(__name__)
SUPPORT_YAMAHA = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE
def setup_platform(hass, config, add_devices, discovery_info=None):
@@ -34,6 +36,8 @@ class YamahaDevice(MediaPlayerDevice):
self._muted = False
self._volume = 0
self._pwstate = STATE_OFF
self._current_source = None
self._source_list = None
self.update()
self._name = name
@@ -45,6 +49,8 @@ class YamahaDevice(MediaPlayerDevice):
self._pwstate = STATE_OFF
self._muted = self._receiver.mute
self._volume = (self._receiver.volume/100) + 1
self._current_source = self._receiver.input
self._source_list = list(self._receiver.inputs().keys())
@property
def name(self):
@@ -66,6 +72,16 @@ class YamahaDevice(MediaPlayerDevice):
"""Boolean if volume is currently muted."""
return self._muted
@property
def source(self):
"""Return the current input source."""
return self._current_source
@property
def source_list(self):
"""List of available input sources."""
return self._source_list
@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
@@ -89,3 +105,7 @@ class YamahaDevice(MediaPlayerDevice):
"""Turn the media player on."""
self._receiver.on = True
self._volume = (self._receiver.volume/100) + 1
def select_source(self, source):
"""Select input source."""
self._receiver.input = source
+4 -4
View File
@@ -1,5 +1,5 @@
"""
Support for MQTT message handling..
Support for MQTT message handling.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/mqtt/
@@ -90,7 +90,7 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_CERTIFICATE): cv.isfile,
vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL):
[PROTOCOL_31, PROTOCOL_311],
vol.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])),
vol.Optional(CONF_EMBEDDED): _HBMQTT_CONFIG_SCHEMA,
}),
}, extra=vol.ALLOW_EXTRA)
@@ -102,13 +102,13 @@ MQTT_BASE_PLATFORM_SCHEMA = vol.Schema({
vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
})
# Sensor type platforms subscribe to mqtt events
# Sensor type platforms subscribe to MQTT events
MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
})
# Switch type platforms publish to mqtt and may subscribe
# Switch type platforms publish to MQTT and may subscribe
MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
+6 -1
View File
@@ -1,4 +1,9 @@
"""MQTT server."""
"""
Support for a local MQTT broker.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/mqtt/#use-the-embedded-broker
"""
import asyncio
import logging
import tempfile
+35 -23
View File
@@ -5,33 +5,36 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors/
"""
import logging
import socket
import homeassistant.bootstrap as bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, TEMP_CELCIUS,
CONF_OPTIMISTIC)
from homeassistant.const import (ATTR_DISCOVERED, ATTR_SERVICE,
CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS)
from homeassistant.helpers import validate_config
CONF_GATEWAYS = 'gateways'
CONF_PORT = 'port'
CONF_DEVICE = 'device'
CONF_DEBUG = 'debug'
CONF_PERSISTENCE = 'persistence'
CONF_PERSISTENCE_FILE = 'persistence_file'
CONF_VERSION = 'version'
CONF_BAUD_RATE = 'baud_rate'
CONF_TCP_PORT = 'tcp_port'
DEFAULT_VERSION = '1.4'
DEFAULT_BAUD_RATE = 115200
DEFAULT_TCP_PORT = 5003
DOMAIN = 'mysensors'
DEPENDENCIES = []
REQUIREMENTS = [
'https://github.com/theolind/pymysensors/archive/'
'f0c928532167fb24823efa793ec21ca646fd37a6.zip#pymysensors==0.5']
'cc5d0b325e13c2b623fa934f69eea7cd4555f110.zip#pymysensors==0.6']
_LOGGER = logging.getLogger(__name__)
ATTR_NODE_ID = 'node_id'
ATTR_CHILD_ID = 'child_id'
ATTR_PORT = 'port'
ATTR_DEVICE = 'device'
GATEWAYS = None
@@ -49,30 +52,39 @@ DISCOVERY_COMPONENTS = [
]
def setup(hass, config):
def setup(hass, config): # pylint: disable=too-many-locals
"""Setup the MySensors component."""
if not validate_config(config,
{DOMAIN: [CONF_GATEWAYS]},
_LOGGER):
return False
if not all(CONF_PORT in gateway
if not all(CONF_DEVICE in gateway
for gateway in config[DOMAIN][CONF_GATEWAYS]):
_LOGGER.error('Missing required configuration items '
'in %s: %s', DOMAIN, CONF_PORT)
'in %s: %s', DOMAIN, CONF_DEVICE)
return False
import mysensors.mysensors as mysensors
version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION))
is_metric = (hass.config.temperature_unit == TEMP_CELCIUS)
is_metric = (hass.config.temperature_unit == TEMP_CELSIUS)
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
def setup_gateway(port, persistence, persistence_file, version, baud_rate):
def setup_gateway(device, persistence_file, baud_rate, tcp_port):
"""Return gateway after setup of the gateway."""
gateway = mysensors.SerialGateway(port, event_callback=None,
persistence=persistence,
persistence_file=persistence_file,
protocol_version=version,
baud=baud_rate)
try:
socket.inet_aton(device)
# valid ip address
gateway = mysensors.TCPGateway(
device, event_callback=None, persistence=persistence,
persistence_file=persistence_file, protocol_version=version,
port=tcp_port)
except OSError:
# invalid ip address
gateway = mysensors.SerialGateway(
device, event_callback=None, persistence=persistence,
persistence_file=persistence_file, protocol_version=version,
baud=baud_rate)
gateway.metric = is_metric
gateway.debug = config[DOMAIN].get(CONF_DEBUG, False)
optimistic = config[DOMAIN].get(CONF_OPTIMISTIC, False)
@@ -93,22 +105,22 @@ def setup(hass, config):
return gateway
# Setup all ports from config
# Setup all devices from config
global GATEWAYS
GATEWAYS = {}
conf_gateways = config[DOMAIN][CONF_GATEWAYS]
if isinstance(conf_gateways, dict):
conf_gateways = [conf_gateways]
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
for index, gway in enumerate(conf_gateways):
port = gway[CONF_PORT]
device = gway[CONF_DEVICE]
persistence_file = gway.get(
CONF_PERSISTENCE_FILE,
hass.config.path('mysensors{}.pickle'.format(index + 1)))
baud_rate = gway.get(CONF_BAUD_RATE, DEFAULT_BAUD_RATE)
GATEWAYS[port] = setup_gateway(
port, persistence, persistence_file, version, baud_rate)
tcp_port = gway.get(CONF_TCP_PORT, DEFAULT_TCP_PORT)
GATEWAYS[device] = setup_gateway(
device, persistence_file, baud_rate, tcp_port)
for (component, discovery_service) in DISCOVERY_COMPONENTS:
# Ensure component is loaded
@@ -139,7 +151,7 @@ def pf_callback_factory(map_sv_types, devices, add_devices, entity_class):
if key in devices:
devices[key].update_ha_state(True)
continue
name = '{} {}.{}'.format(
name = '{} {} {}'.format(
gateway.sensors[node_id].sketch_name, node_id, child.id)
if isinstance(entity_class, dict):
device_class = entity_class[child.type]
+18 -2
View File
@@ -4,6 +4,9 @@ Support for Nest thermostats.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/thermostat.nest/
"""
import logging
import socket
import voluptuous as vol
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
@@ -20,14 +23,27 @@ CONFIG_SCHEMA = vol.Schema({
})
}, extra=vol.ALLOW_EXTRA)
_LOGGER = logging.getLogger(__name__)
def devices():
"""Generator returning list of devices and their location."""
try:
for structure in NEST.structures:
for device in structure.devices:
yield (structure, device)
except socket.error:
_LOGGER.error("Connection error logging into the nest web service.")
# pylint: disable=unused-argument
def setup(hass, config):
"""Setup the Nest thermostat component."""
global NEST
username = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
conf = config[DOMAIN]
username = conf[CONF_USERNAME]
password = conf[CONF_PASSWORD]
import nest
+13 -9
View File
@@ -8,11 +8,13 @@ from functools import partial
import logging
import os
import voluptuous as vol
import homeassistant.bootstrap as bootstrap
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers import config_per_platform, template
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
DOMAIN = "notify"
@@ -32,6 +34,13 @@ ATTR_DATA = 'data'
SERVICE_NOTIFY = "notify"
NOTIFY_SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_MESSAGE): cv.template,
vol.Optional(ATTR_TITLE, default=ATTR_TITLE_DEFAULT): cv.string,
vol.Optional(ATTR_TARGET): cv.string,
vol.Optional(ATTR_DATA): dict, # nobody seems to be using this (yet)
})
_LOGGER = logging.getLogger(__name__)
@@ -71,13 +80,7 @@ def setup(hass, config):
def notify_message(notify_service, call):
"""Handle sending notification message service calls."""
message = call.data.get(ATTR_MESSAGE)
if message is None:
_LOGGER.error(
'Received call to %s without attribute %s',
call.service, ATTR_MESSAGE)
return
message = call.data[ATTR_MESSAGE]
title = template.render(
hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT))
@@ -91,7 +94,8 @@ def setup(hass, config):
service_call_handler = partial(notify_message, notify_service)
service_notify = p_config.get(CONF_NAME, SERVICE_NOTIFY)
hass.services.register(DOMAIN, service_notify, service_call_handler,
descriptions.get(SERVICE_NOTIFY))
descriptions.get(SERVICE_NOTIFY),
schema=NOTIFY_SERVICE_SCHEMA)
success = True
return success
+2 -2
View File
@@ -44,12 +44,12 @@ class FileNotificationService(BaseNotificationService):
if os.stat(self.filepath).st_size == 0:
title = '{} notifications (Log started: {})\n{}\n'.format(
kwargs.get(ATTR_TITLE),
dt_util.strip_microseconds(dt_util.utcnow()),
dt_util.utcnow().isoformat(),
'-' * 80)
file.write(title)
if self.add_timestamp == 1:
text = '{} {}\n'.format(dt_util.utcnow(), message)
text = '{} {}\n'.format(dt_util.utcnow().isoformat(), message)
file.write(text)
else:
text = '{}\n'.format(message)
@@ -0,0 +1,62 @@
"""
LG WebOS TV notification service.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.webostv/
"""
import logging
from homeassistant.components.notify import (BaseNotificationService, DOMAIN)
from homeassistant.const import (CONF_HOST, CONF_NAME)
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)
def get_service(hass, config):
"""Return the notify service."""
if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_HOST, CONF_NAME]},
_LOGGER):
return None
host = config.get(CONF_HOST, None)
if not host:
_LOGGER.error('No host provided.')
return None
from pylgtv import WebOsClient
from pylgtv import PyLGTVPairException
client = WebOsClient(host)
try:
client.register()
except PyLGTVPairException:
_LOGGER.error('Pairing failed.')
return None
except ConnectionRefusedError:
_LOGGER.error('Host unreachable.')
return None
return LgWebOSNotificationService(client)
# pylint: disable=too-few-public-methods
class LgWebOSNotificationService(BaseNotificationService):
"""Implement the notification service for LG WebOS TV."""
def __init__(self, client):
"""Initialize the service."""
self._client = client
def send_message(self, message="", **kwargs):
"""Send a message to the tv."""
from pylgtv import PyLGTVPairException
try:
self._client.send_message(message)
except PyLGTVPairException:
_LOGGER.error('Pairing failed.')
except ConnectionRefusedError:
_LOGGER.error('Host unreachable.')
+15 -9
View File
@@ -10,7 +10,10 @@ from homeassistant.components.notify import (
ATTR_TITLE, DOMAIN, BaseNotificationService)
from homeassistant.helpers import validate_config
REQUIREMENTS = ['sleekxmpp==1.3.1', 'dnspython3==1.12.0']
REQUIREMENTS = ['sleekxmpp==1.3.1',
'dnspython3==1.12.0',
'pyasn1==0.1.9',
'pyasn1-modules==0.0.8']
_LOGGER = logging.getLogger(__name__)
@@ -22,20 +25,23 @@ def get_service(hass, config):
_LOGGER):
return None
return XmppNotificationService(config['sender'],
config['password'],
config['recipient'])
return XmppNotificationService(
config.get('sender'),
config.get('password'),
config.get('recipient'),
config.get('tls', True))
# pylint: disable=too-few-public-methods
class XmppNotificationService(BaseNotificationService):
"""Implement the notification service for Jabber (XMPP)."""
def __init__(self, sender, password, recipient):
def __init__(self, sender, password, recipient, tls):
"""Initialize the service."""
self._sender = sender
self._password = password
self._recipient = recipient
self._tls = tls
def send_message(self, message="", **kwargs):
"""Send a message to a user."""
@@ -43,10 +49,10 @@ class XmppNotificationService(BaseNotificationService):
data = "{}: {}".format(title, message) if title else message
send_message(self._sender + '/home-assistant', self._password,
self._recipient, data)
self._recipient, self._tls, data)
def send_message(sender, password, recipient, message):
def send_message(sender, password, recipient, use_tls, message):
"""Send a message over XMPP."""
import sleekxmpp
@@ -59,11 +65,11 @@ def send_message(sender, password, recipient, message):
logging.basicConfig(level=logging.ERROR)
self.use_tls = True
self.use_tls = use_tls
self.use_ipv6 = False
self.add_event_handler('failed_auth', self.check_credentials)
self.add_event_handler('session_start', self.start)
self.connect()
self.connect(use_tls=self.use_tls, use_ssl=False)
self.process()
def start(self, event):
+1 -1
View File
@@ -478,7 +478,7 @@ class Recorder(threading.Thread):
def _adapt_datetime(datetimestamp):
"""Turn a datetime into an integer for in the DB."""
return dt_util.as_utc(datetimestamp.replace(microsecond=0)).timestamp()
return dt_util.as_utc(datetimestamp).timestamp()
def _verify_instance():
+12 -6
View File
@@ -38,16 +38,22 @@ _LOGGER = logging.getLogger(__name__)
RFXOBJECT = None
def _validate_packetid(value):
def validate_packetid(value):
"""Validate that value is a valid packet id for rfxtrx."""
if get_rfx_object(value):
return value
else:
raise vol.Invalid('invalid packet id for {}'.format(value))
# Share between rfxtrx platforms
VALID_DEVICE_ID = vol.All(cv.string, vol.Lower)
VALID_SENSOR_DEVICE_ID = vol.All(VALID_DEVICE_ID,
vol.truth(lambda val:
val.startswith('sensor_')))
DEVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_NAME): cv.string,
vol.Required(ATTR_PACKETID): _validate_packetid,
vol.Required(ATTR_PACKETID): validate_packetid,
vol.Optional(ATTR_FIREEVENT, default=False): cv.boolean,
})
@@ -65,7 +71,7 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(ATTR_DEBUG, default=False): cv.boolean,
vol.Optional(ATTR_DUMMY, default=False): cv.boolean,
}),
})
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
@@ -182,7 +188,7 @@ def apply_received_command(event):
# Check if entity exists or previously added automatically
if device_id in RFX_DEVICES:
_LOGGER.debug(
"EntityID: %s light_update. Command: %s",
"EntityID: %s device_update. Command: %s",
device_id,
event.values['Command']
)
@@ -251,7 +257,7 @@ class RfxtrxDevice(Entity):
@property
def is_on(self):
"""Return true if light is on."""
"""Return true if device is on."""
return self._state
@property
@@ -260,7 +266,7 @@ class RfxtrxDevice(Entity):
return True
def turn_off(self, **kwargs):
"""Turn the light off."""
"""Turn the device off."""
self._send_command("turn_off")
def _send_command(self, command, brightness=0):
@@ -7,10 +7,13 @@ https://home-assistant.io/components/rollershutter/
import os
import logging
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.components import group
from homeassistant.const import (
SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_STOP,
@@ -33,6 +36,10 @@ _LOGGER = logging.getLogger(__name__)
ATTR_CURRENT_POSITION = 'current_position'
ROLLERSHUTTER_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_open(hass, entity_id=None):
"""Return if the roller shutter is open based on the statemachine."""
@@ -85,14 +92,16 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_MOVE_UP,
handle_rollershutter_service,
descriptions.get(SERVICE_MOVE_UP))
descriptions.get(SERVICE_MOVE_UP),
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MOVE_DOWN,
handle_rollershutter_service,
descriptions.get(SERVICE_MOVE_DOWN))
descriptions.get(SERVICE_MOVE_DOWN),
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_STOP,
handle_rollershutter_service,
descriptions.get(SERVICE_STOP))
descriptions.get(SERVICE_STOP),
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
return True
+9 -1
View File
@@ -7,9 +7,12 @@ https://home-assistant.io/components/scene/
import logging
from collections import namedtuple
import voluptuous as vol
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, CONF_PLATFORM)
from homeassistant.helpers import extract_domain_configs
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@@ -19,6 +22,10 @@ STATE = 'scening'
CONF_ENTITIES = "entities"
SCENE_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
SceneConfig = namedtuple('SceneConfig', ['name', 'states'])
@@ -61,7 +68,8 @@ def setup(hass, config):
for scene in target_scenes:
scene.activate()
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service,
schema=SCENE_SERVICE_SCHEMA)
return True
+13 -5
View File
@@ -109,6 +109,11 @@ CONFIG_SCHEMA = vol.Schema({
vol.Required(DOMAIN): {cv.slug: _SCRIPT_ENTRY_SCHEMA}
}, extra=vol.ALLOW_EXTRA)
SCRIPT_SERVICE_SCHEMA = vol.Schema({})
SCRIPT_TURN_ONOFF_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_on(hass, entity_id):
"""Return if the switch is on based on the statemachine."""
@@ -149,7 +154,8 @@ def setup(hass, config):
alias = cfg.get(CONF_ALIAS, object_id)
script = Script(object_id, alias, cfg[CONF_SEQUENCE])
component.add_entities((script,))
hass.services.register(DOMAIN, object_id, service_handler)
hass.services.register(DOMAIN, object_id, service_handler,
schema=SCRIPT_SERVICE_SCHEMA)
def turn_on_service(service):
"""Call a service to turn script on."""
@@ -168,10 +174,12 @@ def setup(hass, config):
for script in component.extract_from_service(service):
script.toggle()
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service)
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service,
schema=SCRIPT_TURN_ONOFF_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service,
schema=SCRIPT_TURN_ONOFF_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service,
schema=SCRIPT_TURN_ONOFF_SCHEMA)
return True
+5 -6
View File
@@ -98,12 +98,11 @@ class SCSGate:
from scsgate.tasks import GetStatusTask
with self._devices_to_register_lock:
if len(self._devices_to_register) == 0:
return
_, device = self._devices_to_register.popitem()
self._devices[device.scs_id] = device
self._device_being_registered = device.scs_id
self._reactor.append_task(GetStatusTask(target=device.scs_id))
while len(self._devices_to_register) != 0:
_, device = self._devices_to_register.popitem()
self._devices[device.scs_id] = device
self._device_being_registered = device.scs_id
self._reactor.append_task(GetStatusTask(target=device.scs_id))
def is_device_registered(self, device_id):
"""Check whether a device is already registered or not."""
+2 -2
View File
@@ -7,13 +7,13 @@ https://home-assistant.io/components/sensor.apcupsd/
import logging
from homeassistant.components import apcupsd
from homeassistant.const import TEMP_CELCIUS
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
DEPENDENCIES = [apcupsd.DOMAIN]
DEFAULT_NAME = "UPS Status"
SPECIFIC_UNITS = {
"ITEMP": TEMP_CELCIUS
"ITEMP": TEMP_CELSIUS
}
_LOGGER = logging.getLogger(__name__)
+2 -2
View File
@@ -4,7 +4,7 @@ Demo platform that has a couple of fake sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
from homeassistant.const import ATTR_BATTERY_LEVEL, TEMP_CELCIUS
from homeassistant.const import ATTR_BATTERY_LEVEL, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
@@ -12,7 +12,7 @@ from homeassistant.helpers.entity import Entity
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo sensors."""
add_devices([
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS, 12),
DemoSensor('Outside Temperature', 15.6, TEMP_CELSIUS, 12),
DemoSensor('Outside Humidity', 54, '%', None),
])
+2 -2
View File
@@ -7,7 +7,7 @@ https://home-assistant.io/components/sensor.forecast/
import logging
from datetime import timedelta
from homeassistant.const import CONF_API_KEY, TEMP_CELCIUS
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@@ -64,7 +64,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if 'units' in config:
units = config['units']
elif hass.config.temperature_unit == TEMP_CELCIUS:
elif hass.config.temperature_unit == TEMP_CELSIUS:
units = 'si'
else:
units = 'us'
+1 -1
View File
@@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ["https://github.com/robbiet480/pygtfs/archive/"
"6b40d5fb30fd410cfaf637c901b5ed5a08c33e4c.zip#"
"432414b720c580fb2667a0a48f539118a2d95969.zip#"
"pygtfs==0.1.2"]
ICON = "mdi:train"
+14 -2
View File
@@ -8,12 +8,13 @@ import logging
from homeassistant.helpers.entity import Entity
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.util import convert
_LOGGER = logging.getLogger(__name__)
DOMAIN = "loopenergy"
REQUIREMENTS = ['pyloopenergy==0.0.7']
REQUIREMENTS = ['pyloopenergy==0.0.10']
def setup_platform(hass, config, add_devices, discovery_info=None):
@@ -24,6 +25,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
elec_secret = config.get('electricity_secret')
gas_serial = config.get('gas_serial')
gas_secret = config.get('gas_secret')
gas_type = config.get('gas_type', 'metric')
gas_calorific = convert(config.get('gas_calorific'), float, 39.11)
if not (elec_serial and elec_secret):
_LOGGER.error(
@@ -39,11 +42,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"serial and secret tokens")
return None
if gas_type not in ['imperial', 'metric']:
_LOGGER.error(
"Configuration Error, 'gas_type' "
"can only be 'imperial' or 'metric' ")
return None
# pylint: disable=too-many-function-args
controller = pyloopenergy.LoopEnergy(
elec_serial,
elec_secret,
gas_serial,
gas_secret
gas_secret,
gas_type,
gas_calorific
)
def stop_loopenergy(event):
+2 -2
View File
@@ -9,7 +9,7 @@ import logging
import requests
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, TEMP_CELCIUS
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import Entity
@@ -96,7 +96,7 @@ class MfiSensor(Entity):
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
if self._port.tag == 'temperature':
return TEMP_CELCIUS
return TEMP_CELSIUS
elif self._port.tag == 'active_pwr':
return 'Watts'
elif self._port.model == 'Input Digital':
+2 -2
View File
@@ -8,7 +8,7 @@ import logging
import homeassistant.components.modbus as modbus
from homeassistant.const import (
STATE_OFF, STATE_ON, TEMP_CELCIUS, TEMP_FAHRENHEIT)
STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
@@ -97,7 +97,7 @@ class ModbusSensor(Entity):
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
if self._unit == "C":
return TEMP_CELCIUS
return TEMP_CELSIUS
elif self._unit == "F":
return TEMP_FAHRENHEIT
else:
+9 -4
View File
@@ -6,8 +6,8 @@ https://home-assistant.io/components/sensor.mysensors/
"""
import logging
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, TEMP_CELCIUS, TEMP_FAHRENHEIT)
from homeassistant.const import (ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON,
TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
@@ -132,7 +132,7 @@ class MySensorsSensor(Entity):
"""Return the unit of measurement of this entity."""
set_req = self.gateway.const.SetReq
unit_map = {
set_req.V_TEMP: (TEMP_CELCIUS
set_req.V_TEMP: (TEMP_CELSIUS
if self.gateway.metric else TEMP_FAHRENHEIT),
set_req.V_HUM: '%',
set_req.V_DIMMER: '%',
@@ -157,8 +157,13 @@ class MySensorsSensor(Entity):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
address = getattr(self.gateway, 'server_address', None)
if address:
device = '{}:{}'.format(address[0], address[1])
else:
device = self.gateway.port
attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,
+32 -38
View File
@@ -4,12 +4,13 @@ Support for Nest Thermostat Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.nest/
"""
import logging
import socket
import voluptuous as vol
import homeassistant.components.nest as nest
from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
TEMP_CELSIUS, CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS
)
DEPENDENCIES = ['nest']
SENSOR_TYPES = ['humidity',
@@ -19,49 +20,42 @@ SENSOR_TYPES = ['humidity',
'last_connection',
'battery_level']
WEATHER_VARIABLES = ['weather_condition', 'weather_temperature',
'weather_humidity',
'wind_speed', 'wind_direction']
JSON_VARIABLE_NAMES = {'weather_humidity': 'humidity',
'weather_temperature': 'temperature',
'weather_condition': 'condition',
'wind_speed': 'kph',
'wind_direction': 'direction'}
WEATHER_VARS = {'weather_humidity': 'humidity',
'weather_temperature': 'temperature',
'weather_condition': 'condition',
'wind_speed': 'kph',
'wind_direction': 'direction'}
SENSOR_UNITS = {'humidity': '%', 'battery_level': 'V',
'kph': 'kph', 'temperature': '°C'}
SENSOR_TEMP_TYPES = ['temperature', 'target']
_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + \
list(WEATHER_VARS.keys())
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): nest.DOMAIN,
vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS): [vol.In(_VALID_SENSOR_TYPES)],
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Nest Sensor."""
logger = logging.getLogger(__name__)
try:
for structure in nest.NEST.structures:
for device in structure.devices:
for variable in config['monitored_conditions']:
if variable in SENSOR_TYPES:
add_devices([NestBasicSensor(structure,
device,
variable)])
elif variable in SENSOR_TEMP_TYPES:
add_devices([NestTempSensor(structure,
device,
variable)])
elif variable in WEATHER_VARIABLES:
json_variable = JSON_VARIABLE_NAMES.get(variable, None)
add_devices([NestWeatherSensor(structure,
device,
json_variable)])
else:
logger.error('Nest sensor type: "%s" does not exist',
variable)
except socket.error:
logger.error(
"Connection error logging into the nest web service."
)
for structure, device in nest.devices():
sensors = [NestBasicSensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]
if variable in SENSOR_TYPES]
sensors += [NestTempSensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]
if variable in SENSOR_TEMP_TYPES]
sensors += [NestWeatherSensor(structure, device,
WEATHER_VARS[variable])
for variable in config[CONF_MONITORED_CONDITIONS]
if variable in WEATHER_VARS]
add_devices(sensors)
class NestSensor(Entity):
@@ -109,7 +103,7 @@ class NestTempSensor(NestSensor):
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return TEMP_CELCIUS
return TEMP_CELSIUS
@property
def state(self):
+2 -2
View File
@@ -9,7 +9,7 @@ from datetime import timedelta
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import (
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, TEMP_CELCIUS)
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS)
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@@ -22,7 +22,7 @@ REQUIREMENTS = [
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {
'temperature': ['Temperature', TEMP_CELCIUS, 'mdi:thermometer'],
'temperature': ['Temperature', TEMP_CELSIUS, 'mdi:thermometer'],
'co2': ['CO2', 'ppm', 'mdi:cloud'],
'pressure': ['Pressure', 'mbar', 'mdi:gauge'],
'noise': ['Noise', 'dB', 'mdi:volume-high'],
+2 -2
View File
@@ -9,7 +9,7 @@ import os
import time
from glob import glob
from homeassistant.const import STATE_UNKNOWN, TEMP_CELCIUS
from homeassistant.const import STATE_UNKNOWN, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
BASE_DIR = '/sys/bus/w1/devices/'
@@ -84,7 +84,7 @@ class OneWire(Entity):
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return TEMP_CELCIUS
return TEMP_CELSIUS
def update(self):
"""Get the latest data from the device."""
@@ -7,7 +7,7 @@ https://home-assistant.io/components/sensor.openweathermap/
import logging
from datetime import timedelta
from homeassistant.const import CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS, TEMP_FAHRENHEIT
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@@ -106,7 +106,7 @@ class OpenWeatherMapSensor(Entity):
if self.type == 'weather':
self._state = data.get_detailed_status()
elif self.type == 'temperature':
if self.temp_unit == TEMP_CELCIUS:
if self.temp_unit == TEMP_CELSIUS:
self._state = round(data.get_temperature('celsius')['temp'],
1)
elif self.temp_unit == TEMP_FAHRENHEIT:
-6
View File
@@ -5,23 +5,18 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.rest/
"""
import logging
from datetime import timedelta
import requests
from homeassistant.const import CONF_VALUE_TEMPLATE, STATE_UNKNOWN
from homeassistant.helpers.entity import Entity
from homeassistant.helpers import template
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'REST Sensor'
DEFAULT_METHOD = 'GET'
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
# pylint: disable=unused-variable
def setup_platform(hass, config, add_devices, discovery_info=None):
@@ -96,7 +91,6 @@ class RestData(object):
self._verify_ssl = verify_ssl
self.data = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data from REST service with GET method."""
try:
+38 -6
View File
@@ -6,18 +6,21 @@ https://home-assistant.io/components/sensor.rfxtrx/
"""
import logging
from collections import OrderedDict
import voluptuous as vol
import homeassistant.components.rfxtrx as rfxtrx
from homeassistant.const import TEMP_CELCIUS
from homeassistant.const import TEMP_CELSIUS
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify
from homeassistant.components.rfxtrx import (
ATTR_PACKETID, ATTR_NAME, ATTR_DATA_TYPE)
ATTR_AUTOMATIC_ADD, ATTR_PACKETID, ATTR_NAME,
CONF_DEVICES, ATTR_DATA_TYPE)
DEPENDENCIES = ['rfxtrx']
DATA_TYPES = OrderedDict([
('Temperature', TEMP_CELCIUS),
('Temperature', TEMP_CELSIUS),
('Humidity', '%'),
('Barometer', ''),
('Wind direction', ''),
@@ -26,19 +29,48 @@ DATA_TYPES = OrderedDict([
('Total usage', 'W')])
_LOGGER = logging.getLogger(__name__)
DEVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_NAME, default=None): cv.string,
vol.Required(ATTR_PACKETID): rfxtrx.validate_packetid,
vol.Optional(ATTR_DATA_TYPE, default=None):
vol.In(list(DATA_TYPES.keys())),
})
def _valid_device(value):
"""Validate a dictionary of devices definitions."""
config = OrderedDict()
for key, device in value.items():
try:
key = rfxtrx.VALID_SENSOR_DEVICE_ID(key)
config[key] = DEVICE_SCHEMA(device)
if not config[key][ATTR_NAME]:
config[key][ATTR_NAME] = key
except vol.MultipleInvalid as ex:
raise vol.Invalid('Rfxtrx sensor {} is invalid: {}'
.format(key, ex))
return config
PLATFORM_SCHEMA = vol.Schema({
vol.Required("platform"): rfxtrx.DOMAIN,
vol.Required(CONF_DEVICES): vol.All(dict, _valid_device),
vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean,
}, extra=vol.ALLOW_EXTRA)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup the RFXtrx platform."""
from RFXtrx import SensorEvent
sensors = []
for device_id, entity_info in config.get('devices', {}).items():
for device_id, entity_info in config['devices'].items():
if device_id in rfxtrx.RFX_DEVICES:
continue
_LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME])
event = rfxtrx.get_rfx_object(entity_info[ATTR_PACKETID])
new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME],
entity_info.get(ATTR_DATA_TYPE, None))
entity_info[ATTR_DATA_TYPE])
rfxtrx.RFX_DEVICES[slugify(device_id)] = new_sensor
sensors.append(new_sensor)
@@ -62,7 +94,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
return
# Add entity if not exist and the automatic_add is True
if config.get('automatic_add', True):
if config[ATTR_AUTOMATIC_ADD]:
pkt_id = "".join("{0:02x}".format(x) for x in event.data)
entity_name = "%s : %s" % (device_id, pkt_id)
_LOGGER.info(
@@ -23,6 +23,8 @@ ATTR_TARGET = 'Destination'
ATTR_REMAINING_TIME = 'Remaining time'
ICON = 'mdi:bus'
TIME_STR_FORMAT = "%H:%M"
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
@@ -126,10 +128,10 @@ class PublicTransportData(object):
try:
self.times = [
dt_util.datetime_to_time_str(
dt_util.as_local(dt_util.utc_from_timestamp(
item['from']['departureTimestamp']))
)
dt_util.as_local(
dt_util.utc_from_timestamp(
item['from']['departureTimestamp'])).strftime(
TIME_STR_FORMAT)
for item in connections
]
self.times.append(
@@ -131,9 +131,9 @@ class SystemMonitorSensor(Entity):
elif self.type == 'ipv6_address':
self._state = psutil.net_if_addrs()[self.argument][1][1]
elif self.type == 'last_boot':
self._state = dt_util.datetime_to_date_str(
dt_util.as_local(
dt_util.utc_from_timestamp(psutil.boot_time())))
self._state = dt_util.as_local(
dt_util.utc_from_timestamp(psutil.boot_time())
).date().isoformat()
elif self.type == 'since_last_boot':
self._state = dt_util.utcnow() - dt_util.utc_from_timestamp(
psutil.boot_time())
@@ -10,7 +10,7 @@ from datetime import datetime
from homeassistant.components import tellduslive
from homeassistant.const import (
ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, TEMP_CELCIUS)
ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, TEMP_CELSIUS)
from homeassistant.helpers.entity import Entity
ATTR_LAST_UPDATED = "time_last_updated"
@@ -27,7 +27,7 @@ SENSOR_TYPE_WINDGUST = "wgust"
SENSOR_TYPE_WATT = "watt"
SENSOR_TYPES = {
SENSOR_TYPE_TEMP: ['Temperature', TEMP_CELCIUS, "mdi:thermometer"],
SENSOR_TYPE_TEMP: ['Temperature', TEMP_CELSIUS, "mdi:thermometer"],
SENSOR_TYPE_HUMIDITY: ['Humidity', '%', "mdi:water"],
SENSOR_TYPE_RAINRATE: ['Rain rate', 'mm', "mdi:water"],
SENSOR_TYPE_RAINTOTAL: ['Rain total', 'mm', "mdi:water"],
+2 -2
View File
@@ -8,7 +8,7 @@ import logging
from collections import namedtuple
import homeassistant.util as util
from homeassistant.const import TEMP_CELCIUS
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit'])
@@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensor_value_descriptions = {
tellcore_constants.TELLSTICK_TEMPERATURE:
DatatypeDescription(
'temperature', config.get('temperature_scale', TEMP_CELCIUS)),
'temperature', config.get('temperature_scale', TEMP_CELSIUS)),
tellcore_constants.TELLSTICK_HUMIDITY:
DatatypeDescription('humidity', '%'),
+4 -2
View File
@@ -6,7 +6,7 @@ https://home-assistant.io/components/sensor.temper/
"""
import logging
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME, TEMP_FAHRENHEIT
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
@@ -56,7 +56,9 @@ class TemperSensor(Entity):
def update(self):
"""Retrieve latest state."""
try:
self.current_value = self.temper_device.get_temperature()
format_str = ('fahrenheit' if self.temp_unit == TEMP_FAHRENHEIT
else 'celsius')
self.current_value = self.temper_device.get_temperature(format_str)
except IOError:
_LOGGER.error('Failed to get temperature due to insufficient '
'permissions. Try running with "sudo"')
@@ -0,0 +1,114 @@
"""Support for ThinkingCleaner."""
import logging
from datetime import timedelta
import homeassistant.util as util
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/TheRealLink/pythinkingcleaner'
'/archive/v0.0.2.zip'
'#pythinkingcleaner==0.0.2']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
SENSOR_TYPES = {
'battery': ['Battery', '%', 'mdi:battery'],
'state': ['State', None, None],
'capacity': ['Capacity', None, None],
}
STATES = {
'st_base': 'On homebase: Not Charging',
'st_base_recon': 'On homebase: Reconditioning Charging',
'st_base_full': 'On homebase: Full Charging',
'st_base_trickle': 'On homebase: Trickle Charging',
'st_base_wait': 'On homebase: Waiting',
'st_plug': 'Plugged in: Not Charging',
'st_plug_recon': 'Plugged in: Reconditioning Charging',
'st_plug_full': 'Plugged in: Full Charging',
'st_plug_trickle': 'Plugged in: Trickle Charging',
'st_plug_wait': 'Plugged in: Waiting',
'st_stopped': 'Stopped',
'st_clean': 'Cleaning',
'st_cleanstop': 'Stopped with cleaning',
'st_clean_spot': 'Spot cleaning',
'st_clean_max': 'Max cleaning',
'st_delayed': 'Delayed cleaning will start soon',
'st_dock': 'Searching Homebase',
'st_pickup': 'Roomba picked up',
'st_remote': 'Remote control driving',
'st_wait': 'Waiting for command',
'st_off': 'Off',
'st_error': 'Error',
'st_locate': 'Find me!',
'st_unknown': 'Unknown state',
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the ThinkingCleaner platform."""
from pythinkingcleaner import Discovery
discovery = Discovery()
devices = discovery.discover()
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():
"""Update all devices."""
for device_object in devices:
device_object.update()
dev = []
for device in devices:
for type_name in SENSOR_TYPES.keys():
dev.append(ThinkingCleanerSensor(device, type_name,
update_devices))
add_devices(dev)
class ThinkingCleanerSensor(Entity):
"""ThinkingCleaner Sensor."""
def __init__(self, tc_object, sensor_type, update_devices):
"""Initialize the ThinkingCleaner."""
self.type = sensor_type
self._tc_object = tc_object
self._update_devices = update_devices
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return self._tc_object.name + ' ' + SENSOR_TYPES[self.type][0]
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return SENSOR_TYPES[self.type][2]
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit_of_measurement
def update(self):
"""Update the sensor."""
self._update_devices()
if self.type == 'battery':
self._state = self._tc_object.battery
elif self.type == 'state':
self._state = STATES[self._tc_object.status]
elif self.type == 'capacity':
self._state = self._tc_object.capacity

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