Compare commits

..

313 Commits

Author SHA1 Message Date
Paulus Schoutsen d0bcec12b9 Merge pull request #1001 from balloob/dev
0.12
2016-01-29 22:37:38 -08:00
Paulus Schoutsen 9fc62c1851 Version bump to 0.12 2016-01-29 22:33:46 -08:00
Paulus Schoutsen 23d88cd4ad Merge pull request #1047 from haraldnagel/dev
Fix #1046 by correcting platform name
2016-01-29 22:14:21 -08:00
Harald Nagel 86f433067c Fix #1046 by correcting platform name 2016-01-30 06:05:09 +00:00
Paulus Schoutsen 75232c43ce Merge pull request #1044 from w1ll1am23/googlevoice
Google Voice SMS notification support
2016-01-29 19:32:23 -08:00
William Scanlon 01a743c7d4 Log if target == NULL 2016-01-29 22:09:59 -05:00
William Scanlon 28f4283b40 Validate target 2016-01-29 22:04:02 -05:00
William Scanlon 375faa9c91 Fixed style error 2016-01-29 21:53:06 -05:00
Paulus Schoutsen ef132e4583 Add tests for color util 2016-01-29 18:44:21 -08:00
Paulus Schoutsen 05cbe54db3 Remove owntracks from coverage 2016-01-29 18:02:42 -08:00
Paulus Schoutsen ae7697b900 Merge pull request #1037 from balloob/owntracks_mobile_beacons
Owntracks - mobile ibeacon tracking.
2016-01-29 18:01:31 -08:00
Martin Hjelmare a8f7bc2324 Merge pull request #1034 from MartinHjelmare/refactor-s_v_types
Mysensors: Refactor s_types and v_types mapping
2016-01-30 01:04:06 +01:00
MartinHjelmare 9db0987e53 Refactor s_types and v_types mapping
To be able to handle new platforms with multiple used v_types but
single states, a new more strict mapping was required. Each s_type is
now mapped to its corresponding v_type(s) in a dict of lists.
2016-01-30 00:55:13 +01:00
pavoni 515307b404 Remove dev/null from build script. 2016-01-29 22:11:01 +00:00
pavoni 03e7ac2a0e Fix typo. 2016-01-29 22:11:00 +00:00
pavoni c5cdf6d7cf Copy kwargs, remove traces. 2016-01-29 22:11:00 +00:00
pavoni a3abd8bb08 Revise tracked beacon name. 2016-01-29 22:11:00 +00:00
pavoni 5a7e380396 Revise beacon name. Add debug. 2016-01-29 22:11:00 +00:00
pavoni 09ef2e1b8c Add docstring, remove trace. 2016-01-29 22:11:00 +00:00
pavoni 80d2f35cc5 First cut mobile beacon tracking. 2016-01-29 22:11:00 +00:00
William Scanlon ad57f27989 Added test exclude in .coveragerc 2016-01-29 16:54:56 -05:00
William Scanlon a6720f54b3 Fixed style errors 2016-01-29 16:48:01 -05:00
William Scanlon aaf75c7e45 Added target support for googlevoice 2016-01-29 14:14:27 -05:00
Martin Hjelmare c5de42e7b5 Merge pull request #1033 from MartinHjelmare/refactor-unit-of-measurement
Mysensors: Refactor unit of measurement
2016-01-29 18:21:15 +01:00
Paulus Schoutsen d6bb6a0777 Merge pull request #1020 from kennedyshead/dev
Adding service for logbook
2016-01-29 09:19:34 -08:00
Paulus Schoutsen f87c7d6732 Merge pull request #1023 from flyte/zigbee-pr
Add ZigBee integration and components.
2016-01-29 09:18:10 -08:00
MartinHjelmare 575e97a051 Refactor unit of measurement
* Add dict with map of units to v_types.
* Avoid numerous if, elif branches and return statements.
* Add custom unit decoupled from other units.
2016-01-29 18:11:58 +01:00
Flyte 902077d78b Catch ZigBee exceptions when communicating with the devices and log appropriate messages. 2016-01-29 16:45:49 +00:00
Flyte c17a4fca80 ZigBee: Ensure correct entity types are used for each component. 2016-01-29 12:00:53 +00:00
Flyte 241a768983 ZigBee: Remove extraneous update_ha_state() usage and use it more appropriately for initial state update. 2016-01-29 11:33:15 +00:00
Flyte 72cca0a91a ZigBee: Handle case in which Serial port is unable to open 2016-01-29 10:48:15 +00:00
Greg Dowling dd7a7f4c75 Merge pull request #1036 from balloob/owntracks_beacon2
Improve owntracks events, add support for stationary ibeacons.
2016-01-29 09:47:20 +00:00
pavoni 468a8a1013 Improve owntracks events, add support for stationary ibeacons. 2016-01-29 09:39:00 +00:00
magnusknutas 24d84dbb42 Added entity_id and domain 2016-01-29 08:13:46 +01:00
Paulus Schoutsen 893a14e8db Merge pull request #1035 from balloob/fix-imports
Fix imports and rename insteon to insteon_hub
2016-01-28 21:53:21 -08:00
Paulus Schoutsen cbc6323438 Fix imports 2016-01-28 21:45:26 -08:00
Paulus Schoutsen d6db00b55a Merge pull request #1022 from haraldnagel/dev
Move Insteon API KEY into configuration.yaml
2016-01-28 21:36:35 -08:00
Harald Nagel eebb736bf8 Add ability to control dimmable sources 2016-01-29 04:36:40 +00:00
Harald Nagel f192a15a8f Remove unnecessary try around import 2016-01-29 02:35:04 +00:00
William Scanlon 9ec44fbe32 Updated googlevoicesms version 2016-01-28 21:19:15 -05:00
William Scanlon ffc06e8bcb pygooglevoice-sms support 2016-01-28 21:07:59 -05:00
William Scanlon 9c7b2ce9fd pygooglevoice-sms support 2016-01-28 21:07:16 -05:00
Paulus Schoutsen 1de7dcdb5f Merge pull request #1025 from molobrakos/yr-position
allow monitoring weather at other location than home
2016-01-28 08:49:48 -08:00
Erik 9e7886b909 allow monitoring weather at other location than home (such as summer house) 2016-01-28 17:16:02 +01:00
Flyte 4045fb6862 Fix import-before-install of xbee-helper dependency. 2016-01-28 13:44:37 +00:00
Flyte dda4f84150 Add zigbee components. 2016-01-28 13:18:29 +00:00
magnusknutas fda88f8fda logbook.log the name of the game 2016-01-28 09:51:05 +01:00
Harald Nagel 3c05c8d1db Use validate_config, watch for import exception 2016-01-28 05:55:36 +00:00
Harald Nagel 21ec435430 Move Insteon API KEY into configuration.yaml 2016-01-28 03:01:32 +00:00
magnusknutas 24893bc28d Less complex service setup 2016-01-27 18:03:37 +01:00
magnusknutas 403b9cbe3e And with template support 2016-01-27 17:37:15 +01:00
magnusknutas 8b0d19835c Cleanup 2016-01-27 17:32:57 +01:00
magnusknutas c31522eea9 Logbook service 2016-01-27 17:27:55 +01:00
Paulus Schoutsen ee33aa73e1 Merge pull request #1017 from fabaff/psutil-upgrade
Upgrade psutil to 3.4.2
2016-01-27 08:08:21 -08:00
Paulus Schoutsen 47498e4aa9 Merge pull request #1016 from fabaff/cpuinfo-upgrade
Upgrade py-cpuinfo to 0.1.8
2016-01-27 08:07:55 -08:00
Fabian Affolter ef4661f1e6 Upgrade psutil to 3.4.2 2016-01-27 12:26:38 +01:00
Fabian Affolter b4070cfb78 Upgrade py-cpuinfo to 0.1.8 2016-01-27 11:43:49 +01:00
Fabian Affolter c750f16275 Update docstings 2016-01-27 09:34:14 +01:00
Fabian Affolter 2248c271fa Add link to docs 2016-01-27 09:25:44 +01:00
Fabian Affolter 87b33d5098 Move configuration details to docs 2016-01-27 09:23:44 +01:00
Fabian Affolter cf7c06d307 Update docstring and fix typos 2016-01-27 09:21:21 +01:00
Fabian Affolter 70d95cb6aa Add link to docs 2016-01-27 09:08:04 +01:00
Fabian Affolter 2c151c6db9 Update docstring 2016-01-27 09:01:08 +01:00
Fabian Affolter 3fc5a60634 Add link to docs 2016-01-27 09:00:01 +01:00
Fabian Affolter 94d2f23cfc Add link to docs and remove configuration details 2016-01-27 08:08:04 +01:00
Fabian Affolter 4bd102ddf5 Add twitter docs 2016-01-27 08:08:04 +01:00
Fabian Affolter 232aa792f1 Add link to docs and update docstrings 2016-01-27 08:08:04 +01:00
Paulus Schoutsen ce9f76a0be Add onewire to coverage 2016-01-26 23:07:01 -08:00
Paulus Schoutsen f3f592cdec Merge pull request #982 from stefan-jonasson/dev
Updated the entity names for all z-wave devices.
2016-01-26 22:51:34 -08:00
Stefan Jonasson 1d2cd0811b Updated Imports.
ZWave modules now import the domain from the base domain.
2016-01-27 07:37:41 +01:00
Paulus Schoutsen 886b581d2a Merge pull request #1002 from kennedyshead/dev
Running periodic tasks
2016-01-26 21:42:06 -08:00
Paulus Schoutsen 88387d3123 Merge branch 'pr/78' into dev
Conflicts:
	homeassistant/const.py
2016-01-26 21:07:53 -08:00
Martin Hjelmare 14cd27aaa7 Merge pull request #1011 from MartinHjelmare/fix-unit-of-measurement
Fix unit of measurement
2016-01-27 03:40:50 +01:00
MartinHjelmare 2e175b88bc Fix unit of measurement
* Add version check in unit_of_measurement, to avoid error.
* Remove not needed metric check in unit_of_measurement.
* Add more value types to unit_of_measurement.
* Fix unit prefix
* Remove unused SCAN_INTERVAL variable.
2016-01-27 03:18:59 +01:00
Paulus Schoutsen 537a7789fd Merge pull request #1005 from fabaff/astral-upgrade
Updgrade to astral 0.9
2016-01-26 17:48:14 -08:00
Paulus Schoutsen 80d1ab78dd Merge pull request #1006 from fabaff/influxdb-upgrade
Upgrade influxdb to 2.11.0
2016-01-26 17:47:59 -08:00
Paulus Schoutsen 821238f889 Merge pull request #1007 from fabaff/telegram-upgrade
Upgrade python-telegram-bot to 3.2.0
2016-01-26 17:47:42 -08:00
Paulus Schoutsen 33a9ec0106 Merge pull request #1008 from fabaff/blockchain-upgrade
Upgrade blockchain to 1.2.1
2016-01-26 17:47:14 -08:00
Paulus Schoutsen a8f0f313c8 Merge pull request #1009 from fabaff/docstrings
Update docstrings
2016-01-26 17:46:56 -08:00
Martin Hjelmare 6796219f37 Merge pull request #1010 from MartinHjelmare/fix-device-attr
Fix device_state_attributes
2016-01-27 02:43:34 +01:00
MartinHjelmare 1dae22a465 Fix device_state_attributes
* Return a dict with keys as the names of the enum members instead of
	the member values.
* This fixes a TypeError: unorderable types: int() < str()
2016-01-27 02:33:21 +01:00
Fabian Affolter 7aba78f96e Update docstrings 2016-01-27 00:08:06 +01:00
Fabian Affolter 06cf07b097 Upgrade blockchain to 1.2.1 2016-01-26 23:53:30 +01:00
Fabian Affolter 271af2c608 Upgrade python-telegram-bot to 3.2.0 2016-01-26 23:47:06 +01:00
Fabian Affolter 8c6ce217e6 Upgrade influxdb to 2.11.0 2016-01-26 23:45:39 +01:00
Fabian Affolter 5410a0c8f6 Updgrade to astral 0.9 2016-01-26 23:34:25 +01:00
Stefan Jonasson 1a422ecd5a Moved common z-wave node functionality to a common z-wave class 2016-01-26 22:11:27 +01:00
magnusknutas 6add5e387b Remove unused import 2016-01-26 21:41:12 +01:00
magnusknutas ca070a36e3 Dont wrap '/int' parameters with tuple 2016-01-26 21:38:07 +01:00
magnusknutas 2c3a6e7905 Remove not needed log warning 2016-01-26 21:30:00 +01:00
magnusknutas 0a4e857901 And a test just to see if nothing works if not providing any vars 2016-01-26 21:21:54 +01:00
magnusknutas d4b444823c Tests for time and a fix for wrong access of _LOGGER 2016-01-26 21:11:30 +01:00
magnusknutas 2382dffbf4 pushed test code :) fixed now 2016-01-26 20:49:51 +01:00
magnusknutas 33b0f4d05d Fixes bug in time trigger and adds test for ex. /two 2016-01-26 20:43:29 +01:00
Stefan Jonasson 5b6371ecda Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	homeassistant/components/light/zwave.py
#	homeassistant/components/switch/zwave.py
2016-01-26 20:31:42 +01:00
magnusknutas 6f1f8ffea0 Merge remote-tracking branch 'upstream/dev' into dev 2016-01-26 20:23:48 +01:00
magnusknutas 1f6f2de9c6 Sorry, I was wrong they are needed 2016-01-26 19:37:53 +01:00
Martin Hjelmare b13e48bd71 Merge pull request #999 from MartinHjelmare/fix-not-adding-devices
Fix adding devices on the fly
2016-01-26 19:30:31 +01:00
magnusknutas 9a79ecf2d3 Remove warnings no longer needed (I think :D) 2016-01-26 19:25:41 +01:00
magnusknutas ebd475b380 Unused import was the problem 2016-01-26 18:50:25 +01:00
magnusknutas cff77a175d Year periodic test (just to trigger travis but should maybe be here anyway) 2016-01-26 18:44:41 +01:00
magnusknutas 726637b867 New and improved handling of the matching! Kudos to @balloob 2016-01-26 18:37:19 +01:00
MartinHjelmare 9117fa6eb8 Fix adding devices on the fly
Devices were not added without persistence enabled and restart of HA.
Node id was added to defaultdict(list) by mistake when checking if list
of defaultdict was empty.

* Fix adding devices in mysensors_callback.
* Change devices to regular dict.
2016-01-26 18:12:13 +01:00
magnusknutas 6ae57b5aaf Some warnings if input is wrong for periodic tasks 2016-01-26 17:35:12 +01:00
magnusknutas e8e2814313 Warnings for not divisible settings 2016-01-26 17:30:55 +01:00
magnusknutas a918be517d test past midnight support for hours 2016-01-26 17:13:18 +01:00
magnusknutas ec5d88b98e hourly periodic task 2016-01-26 17:06:50 +01:00
magnusknutas 13fbefcdf8 Running periodic tasks 2016-01-26 10:28:31 +01:00
Stefan Jonasson f4c9540a1b Updated teh entity_id generation
Entity ids will now look like this:
sensor.philio_technology_corporation_psm021_slim_multisensor_doorwindow_sensor_7
sensor.philio_technology_corporation_psm021_slim_multisensor_luminance_7
sensor.philio_technology_corporation_psm021_slim_multisensor_motion_sensor_7
sensor.philio_technology_corporation_psm021_slim_multisensor_tamper_sensor_7
sensor.philio_technology_corporation_psm021_slim_multisensor_temperature_7

sensor.philio_technology_corporation_psm021_slim_multisensor_doorwindow_sensor_8
sensor.philio_technology_corporation_psm021_slim_multisensor_luminance_8
sensor.philio_technology_corporation_psm021_slim_multisensor_motion_sensor_8
sensor.philio_technology_corporation_psm021_slim_multisensor_tamper_sensor_8
sensor.philio_technology_corporation_psm021_slim_multisensor_temperature_8

And if there is several values of the same type in a node the instance id will be appended like this (three switches in the same device):
switch.fibaro_system_unknown_type0202_id1002_switch_9
switch.fibaro_system_unknown_type0202_id1002_switch_9_2
switch.fibaro_system_unknown_type0202_id1002_switch_9_3
2016-01-26 09:33:26 +01:00
Fabian Affolter e72f61ce73 Update docstring 2016-01-26 09:11:04 +01:00
Paulus Schoutsen 4c056db3bb Fix Chrome url sync 2016-01-25 23:56:43 -08:00
Paulus Schoutsen c10a86d1bf Merge branch 'master' into dev
Conflicts:
	homeassistant/const.py
2016-01-25 22:58:06 -08:00
Paulus Schoutsen b97de5cef6 Merge pull request #1000 from balloob/view-url-sync
Update frontend with view url sync
2016-01-25 22:54:00 -08:00
Paulus Schoutsen 4233c0bc66 Update frontend with view url sync 2016-01-25 22:47:13 -08:00
Paulus Schoutsen b13008201e Fix lint issue 2016-01-25 21:31:21 -08:00
Paulus Schoutsen 125ad8630d Add views to demo 2016-01-25 21:27:36 -08:00
Paulus Schoutsen 5c3ad5d4c0 Update frontend 2016-01-25 17:57:33 -08:00
Paulus Schoutsen 414aa8563d Merge pull request #930 from TangoAlpha/dev
Add LIFX bulb support
2016-01-25 17:51:47 -08:00
happyleavesaoc d08a181c72 Merge pull request #994 from happyleavesaoc/orvibo_bump
Orvibo version bump
2016-01-25 19:34:21 -05:00
happyleavesaoc 80c6bf6744 bump orvibo version 2016-01-25 16:40:27 -05:00
Tim b1ba792715 Brightness from HA overrides brightness from HSV conversion 2016-01-25 21:19:27 +00:00
Per Sandström b99f6c1a46 Merge pull request #995 from persandstrom/vsure0.4.8
update python-verisure to version 0.4.8
2016-01-25 22:04:02 +01:00
Tim 1397f9e588 Fix logging typo 2016-01-25 20:58:10 +00:00
Tim bb7f92330d Update liffylights version 2016-01-25 20:53:21 +00:00
Per Sandström 7cbd780748 update vsure version 2016-01-25 21:39:41 +01:00
Tim 784fea2d56 Update for new liffylights version 2016-01-25 19:08:48 +00:00
Tim 97f0425252 Update requirements_all 2016-01-25 15:20:23 +00:00
Tim 11120a8743 Fix liffylights import 2016-01-25 15:19:50 +00:00
Tim 8009542b3e Add extra debugging
Add duration to power on
2016-01-25 13:30:52 +00:00
Tim 32cfa6998c Update requirements_all 2016-01-25 12:13:24 +00:00
Tim d10a5cf5e9 Update to v0.8.3 of liffylights 2016-01-25 11:54:41 +00:00
Tim 4089a7a0d3 Update requirements_all 2016-01-25 10:02:03 +00:00
Tim 5a926913d3 Update to v0.6 of liffylights 2016-01-25 10:00:15 +00:00
Paulus Schoutsen 6c91831baa Hide groups when they are views or auto defined 2016-01-24 23:45:06 -08:00
Tim 069dafa3a5 Update requirements_all 2016-01-25 07:38:37 +00:00
Paulus Schoutsen 4abc5c97cd Merge pull request #988 from auchter/thermostat-fan
Add service to control a thermostat's fan
2016-01-24 23:24:32 -08:00
Paulus Schoutsen bb3dd47088 Merge pull request #992 from balloob/sections
Add support for sections in the frontend [Fixes #100705168]
2016-01-24 22:58:27 -08:00
Paulus Schoutsen f6c53896e3 Allow groups to be used as views 2016-01-24 22:54:45 -08:00
Paulus Schoutsen ad2e2d916b Merge pull request #979 from rmkraus/automation-decorator
Automation Decorator for custom components
2016-01-24 21:22:19 -08:00
Ryan Kraus 8406f81811 Removed decorator callback
The decorator callback was not actually necessary so it was removed and
replaced with a partial function instead.
2016-01-25 00:14:16 -05:00
Ryan Kraus bcdfc555e0 Removed service decorator from event decorators 2016-01-24 23:09:09 -05:00
Ryan Kraus 3b89102338 Fixed lint issue from merge
extract_entity_ids from the service helpers was overwriting the service
decorator with one of its attributes. This was fixed.
2016-01-24 23:00:43 -05:00
Ryan Kraus 60dd2d441d Merge remote-tracking branch 'balloob/dev' into automation-decorator
# Conflicts:
#	homeassistant/helpers/service.py
#	tests/helpers/test_service.py
2016-01-24 22:51:00 -05:00
Ryan Kraus 5830da63b1 Moved service decorator to service helpers
Moved the service decorator to the service helpers module and moved the
associated tests.
2016-01-24 22:46:30 -05:00
Tim 50561ffe97 Fix long line 2016-01-25 03:39:13 +00:00
Tim 74e8446556 Bump version of liffylights 2016-01-25 03:34:24 +00:00
Tim d94db5388c Add preliminary support for transition time 2016-01-25 03:32:55 +00:00
Ryan Kraus f66aeb2e73 Added event helper tests
1. Added tests for all event decorators
2. Added tests for sunrise and sunset event helpers
2016-01-24 22:23:56 -05:00
Ryan Kraus 54b82ecd91 Lint fixes and additions to event decorators
1. service decorator was overwriting the function name with one of its
arguments.
2. Accidentally left an extra argument in track_sunrise.
3. Added track_utc_time_change decorator.
2016-01-24 21:06:15 -05:00
Ryan Kraus 2fa98167c2 Updated example.py component
Cleaned up example.py to better handle failed loads.
2016-01-24 20:05:40 -05:00
Ryan Kraus 57725136c0 Many updates regarding event decorators
1. Added HASS to the arguments for callbacks that are created with
event decorators.
2. Added a service decorator.
3. Updated example.py in the example config to use the event decorators.
2016-01-24 19:52:22 -05:00
Ryan Kraus 40dbeb0b60 Another revision on event decorators
This revision of event decorators removes much of the complexity. The
decorated functions are no longer wrapped with a class that tracks
last_run, etc. Bootstrap now gives hass to the event_decorators module
before initializing components so the decorators no longer require
activation.
2016-01-24 17:46:05 -05:00
Ryan Kraus ef92940ffb A few lint fixes to event decorators. 2016-01-24 16:45:35 -05:00
Erik e7865c1d67 Merge pull request #990 from molobrakos/squeezebox-fix
bugfix for squeezebox cover art regression
2016-01-24 21:56:21 +01:00
Ryan Kraus 02e634c6a2 Fixed bugs to allow HA to boot again
1) helpers/event should not import the sun component unless it is
requested. This prevents circular import.
2) fixed import typo in bootstrap
2) bootstrap cannot import event_decorators until it is needed because
this leads to a circular import.
2016-01-24 15:55:47 -05:00
Ryan Kraus 0f937cad74 Initial pass at event decorators
Created event decorators for custom components. Decorators were created
for the events: track_state_change, track_sunrise, track_sunset, and
track_time_change.
2016-01-24 15:28:09 -05:00
Ryan Kraus 81dd1515ae Moved sunrise/sunset tracking to helpers
The automation component contained some very handy and generic
functions for tracking sunset and sunrise. This was moved to
helpers/event.py.
2016-01-24 15:07:09 -05:00
Ryan Kraus a65d0f0549 Reverting Automation decorator in favor of a new approach. 2016-01-24 14:44:48 -05:00
Michael Auchter 881c82c2df nest: implement fan control 2016-01-24 12:47:21 -06:00
Michael Auchter df94c909f7 thermostat: add service to control fan mode 2016-01-24 12:47:21 -06:00
Michael Auchter a0ed469aa2 thermostat: move fan attribute up to thermostat 2016-01-24 12:47:20 -06:00
Michael Auchter afa4fc4ef5 thermostat: split up services 2016-01-24 12:47:20 -06:00
Paulus Schoutsen df450c3d1f Merge pull request #987 from balloob/update-pynetgear
Update version pynetgear
2016-01-24 09:46:23 -08:00
Paulus Schoutsen dc5d652d31 Update version pynetgear 2016-01-24 09:43:06 -08:00
Paulus Schoutsen e4fe19fff0 Merge pull request #986 from turbokongen/dev
Small errorlog fix.
2016-01-24 08:54:54 -08:00
John Arild Berentsen f6f3f54228 flake8 complaint fix 2016-01-24 16:43:24 +01:00
John Arild Berentsen 6df67d2852 Send correct command to pyrfxtrx
Although it seems to work with send_on, it throws an logged error. Using correct command against pyrfxtrx removes this error.
2016-01-24 16:37:38 +01:00
Tim 2411d1f2c8 Fix wrongly generated requirements 2016-01-24 10:07:56 +00:00
Tim 9f6a1c75fa Fix wrongly generated requirements 2016-01-24 10:01:23 +00:00
Stefan Jonasson 1842e5909e Updated the entity names for all z-wave devices.
Z-Wave have a unique node id, this is now added to the entity name.

This is a change will break old configs.  The Z-Wave entity_id will have to be updated in configurations.

The reason for this change is that the old behavior created duplicate entity_ids if you have several similar devices. The order devices is included is not consistent so when the service was restarted the entity ids could change and the configuration for entities will be invalid.

There was also inconsistencies between the naming for sensors and switches.
(cherry picked from commit 33a78df)
2016-01-24 10:20:51 +01:00
John Arild Berentsen 6f31aacb90 Merge pull request #1 from balloob/dev
Updating base
2016-01-24 09:58:51 +01:00
Paulus Schoutsen e541b9ba77 Merge pull request #981 from balloob/ordered-yaml
Load YAML config into an ordered dict
2016-01-23 23:04:41 -08:00
Paulus Schoutsen 53484e46a3 Move generate_entity_id to entity helpers 2016-01-23 23:00:46 -08:00
Paulus Schoutsen de79a46d43 Move extract_entity_id to service helpers 2016-01-23 22:57:14 -08:00
Paulus Schoutsen bc19ef66bf Move split_entity_id to helpers 2016-01-23 22:49:49 -08:00
Paulus Schoutsen de08f0afaa Load YAML config into an ordered dict 2016-01-23 22:41:01 -08:00
Tim 706bbeae16 Add to .coveragerc 2016-01-24 02:17:52 +00:00
Paulus Schoutsen e5497d89f4 Merge pull request #980 from balloob/upgrade-cast
Upgrade PyChromecast version
2016-01-23 18:15:12 -08:00
Paulus Schoutsen 048ec0aa66 Merge pull request #964 from balloob/passive-zones
Allow passive zones
2016-01-23 18:15:06 -08:00
Paulus Schoutsen 03ed85b0a7 Merge pull request #971 from trollkarlen/smtp-fix
smtp: make smtp component thread safe
2016-01-23 18:08:03 -08:00
Paulus Schoutsen 9d92707fd7 Merge pull request #973 from turbokongen/dev
Added support for Dimming with lights in Rfxtrx module
2016-01-23 18:03:37 -08:00
Paulus Schoutsen 90c392e270 Upgrade PyChromecast version 2016-01-23 17:29:40 -08:00
Tim 6cb6cbfefd Update requirements 2016-01-24 01:18:18 +00:00
Tim 6d2bca0fd1 Import 3rd party library inside method 2016-01-24 01:13:51 +00:00
Tim 99286391e1 Fixes for lint 2016-01-24 01:00:02 +00:00
Tim 711f2da496 Update liffylights version 2016-01-24 00:43:50 +00:00
Ryan Kraus fbd68b6f89 Created automation decorator prototype
Created an initial iteration of an Automation decorator.
2016-01-23 19:39:59 -05:00
Paulus Schoutsen 85ebd0ab59 Merge pull request #977 from sdague/dev
fix typo in log message
2016-01-23 14:27:37 -08:00
Tim 58034219b6 Bump version liffylights 2016-01-23 22:23:46 +00:00
Tim 17f5a466d9 Separate LIFX code and HA component 2016-01-23 22:14:57 +00:00
Sean Dague 90ca6a0998 fix typo in log message
The plex component logs an htts url, which is confusing to people, as
they think something is broken, when it is not.

Closes #959
2016-01-23 16:06:50 -05:00
Erik ec2b433733 should be _id 2016-01-23 18:55:43 +01:00
Erik 492c4b7f00 style 2016-01-23 18:14:03 +01:00
Erik b3beb9f3c9 style 2016-01-23 18:08:54 +01:00
Erik 837e7affa7 only query artwork by track_id if id is available (7.7 vs 7.9 version issue?) 2016-01-23 17:48:14 +01:00
turbokongen 6d527842dd Another flake8 fix too long line 2016-01-23 12:14:00 +01:00
turbokongen e30915eb2c flake8 complaint fix 2016-01-23 12:08:21 +01:00
turbokongen 43e2b58f20 Fixing of various test errors 2016-01-23 12:00:03 +01:00
turbokongen d3c6c892a8 Small intendation fix 2016-01-23 11:50:09 +01:00
turbokongen a63edcf505 Added support for Dimming with lights in Rfxtrx module 2016-01-23 11:18:11 +01:00
Robert Marklund 0e7088ce3b smtp: make smtp component thread safe
Also fix so it does not require ip, port, username and password when using local smtp server.
Add debug config.

Signed-off-by: Robert Marklund <robbelibobban@gmail.com>
2016-01-23 10:43:10 +01:00
Paulus Schoutsen 0042e7725d Merge pull request #967 from HydrelioxGitHub/NetAtmo-icons
Add icons to NetAtmo sensors
2016-01-22 12:56:43 -08:00
hydreliox 613f8d0bd2 Add icons to NetAtmo sensors 2016-01-22 21:15:07 +01:00
Greg Dowling 61ca1ab2c1 Merge pull request #948 from balloob/sensor_template
First draft of sensor.template.
2016-01-22 16:49:49 +00:00
pavoni ad62591f43 Change error state to be 'error' rather than 'unknown', trace error. 2016-01-22 16:30:02 +00:00
Paulus Schoutsen 5ba33bc40e Merge pull request #950 from michaelkuty/dev
Add statsd component.
2016-01-22 07:41:04 -08:00
Michael Kutý 143b08d661 Add statsd component. 2016-01-22 16:18:15 +01:00
pavoni 87a9fd8252 Handle race condition on startup. 2016-01-22 11:30:04 +00:00
pavoni b1f7b5c6d7 Tidy, add test for ValueException logic. 2016-01-22 09:37:20 +00:00
Paulus Schoutsen bb97af1504 Allow passive zones 2016-01-22 00:00:36 -08:00
Paulus Schoutsen 9a092654e9 Merge pull request #962 from FreekingDean/dev
Adding Support for Insteon Hub
2016-01-21 23:31:16 -08:00
Dean d59b98ee2b Added Insteon Support 2016-01-22 02:28:36 -05:00
Paulus Schoutsen 6bbbbd9e17 Update mdi icons 2016-01-21 23:07:43 -08:00
Paulus Schoutsen 9fbedd8b5f Merge pull request #963 from balloob/scrip-service-helper
Script: use config service helper
2016-01-21 22:55:28 -08:00
Paulus Schoutsen a91163877f Script: use config service helper 2016-01-21 22:47:25 -08:00
Paulus Schoutsen 0acce86596 Merge pull request #958 from balloob/itunes-updates
iTunes Updates
2016-01-21 18:44:55 -08:00
William Scanlon c95b03f240 Fixed style 2016-01-21 18:32:10 -08:00
William Scanlon 56ce3e5f5a Wink siren support 2016-01-21 18:32:10 -08:00
pavoni 8b5751ad44 Merge branch 'dev' into sensor_template 2016-01-21 23:17:53 +00:00
pavoni c615272c06 Tidy. 2016-01-21 23:17:19 +00:00
Jon Maddox a3b8122707 not using these 2016-01-21 17:54:26 -05:00
Jon Maddox 682e3460e0 use custom material icons to represent speaker state 2016-01-21 17:45:30 -05:00
Jon Maddox 71dbd10b39 let port be optional 2016-01-21 17:18:52 -05:00
Paulus Schoutsen 7f143bcdf9 Merge pull request #957 from trollkarlen/yr.no-fix
yr.no: fix some bugs in the component
2016-01-21 13:33:36 -08:00
Robert Marklund f19a46dcfe yr.no: fix some bugs in the component
Someone forgot some in:s and ()
Also added windGust sensor.

Signed-off-by: Robert Marklund <robbelibobban@gmail.com>
2016-01-21 22:04:18 +01:00
Paulus Schoutsen 625f69443a Merge pull request #953 from trollkarlen/kodi-fix
kodi: remove traceback on state check
2016-01-21 12:18:28 -08:00
Robert Marklund 0bdd293572 kodi: remove traceback on state check
If the computer is sleeping that are running xbmc then
the traceback is printed in the log all the time.
Keep the traceback in debug mode of the module if needed.

Signed-off-by: Robert Marklund <robbelibobban@gmail.com>
2016-01-21 20:59:58 +01:00
pavoni f8072aae68 Merge branch 'dev' into sensor_template 2016-01-21 18:34:13 +00:00
pavoni 92afcae9be Add test. 2016-01-21 18:31:44 +00:00
pavoni a6f37c032b Revise to not need dependencies (or quoting)! 2016-01-21 17:35:33 +00:00
pavoni e66d15b71d First drafy of sensor.template. 2016-01-21 16:31:23 +00:00
Paulus Schoutsen 1098194a89 Merge pull request #937 from trollkarlen/sonos-multicast
sonos: add hosts and interface_addr to sonos config
2016-01-21 07:30:40 -08:00
Paulus Schoutsen 66d23cd15f Merge pull request #945 from zmrow/fix-exception-in-openweather
Fix #863 - Added a check for good pull from OWM and return if the object is None
2016-01-20 19:57:57 -08:00
zmrowicki@hotmail.com c3a1193ef9 Fix #863 - Added a check for good pull from OWM and return if the object is None 2016-01-20 19:36:33 -08:00
Paulus Schoutsen ec2d9af8dc Merge pull request #943 from trollkarlen/influxdb-fix
influxdb: fix the need of admin to run
2016-01-20 19:22:40 -08:00
Paulus Schoutsen f0e44728d7 Merge pull request #944 from HydrelioxGitHub/twitter_notify
Add Twitter as a notification platform
2016-01-20 19:21:38 -08:00
hydreliox f67747456f Clean some errors 2016-01-21 02:14:11 +01:00
hydreliox f6017a17b2 Add Twitter as a notification platform 2016-01-21 02:02:32 +01:00
Robert Marklund 89aa3cbc62 influxdb: fix the need of admin to run
Use select statment to show if db exits instead of
'SHOW DATABASES' which cant be run by a non admin user.
See https://github.com/influxdata/influxdb/issues/4785 for more info.

Also influxdb dont like empty writes('') so ignore state changes of that kind,
this happens on startup of home assistant.

Signed-off-by: Robert Marklund <robbelibobban@gmail.com>
2016-01-21 00:19:16 +01:00
Robert Marklund 543190dfb0 sonos: add hosts and interface_addr to sonos config
Config can now specify one or more hosts to connect and
also a interface_addr to multicast on if multiple interfaces exists.

Signed-off-by: Robert Marklund <robbelibobban@gmail.com>
2016-01-20 22:55:08 +01:00
Paulus Schoutsen fdc8c45a69 Merge pull request #940 from balloob/remove_sensor_state
Remove sensor state
2016-01-20 12:52:23 -08:00
pavoni d2d421ca8f Remove ghost debug code. 2016-01-20 20:02:03 +00:00
pavoni 7ad5b3a17b Fix bug related to maker_param types 2016-01-20 19:13:29 +00:00
Paulus Schoutsen c8dd9696b4 Merge pull request #939 from molobrakos/squeezebox-fix
artwork fixes
2016-01-20 09:16:32 -08:00
Erik 58ef69b95d less hacky way of getting unique cover art 2016-01-20 16:31:51 +01:00
pavoni 0de9229d75 More tidying. 2016-01-20 11:25:20 +00:00
pavoni f4c3ac2a62 Tidy 2016-01-20 11:19:00 +00:00
pavoni 6c5ceaf686 Remove sensor state from switch and wemo 2016-01-20 11:06:08 +00:00
Erik 54f65ae87d 1) artwork_url might be a relative url (such as /imageproxy). in that case, join it with the base url. note: urllib.parse.urljoin will handle case when the artwork url is absolute. 2) artwork would not be replaced in the user interface because the url did not change between tracks (http://.../cover.jpg). solved by appending internal hash of the media title to the url to force reload 2016-01-20 10:57:39 +01:00
Paulus Schoutsen 4f2dc3cc2a Merge pull request #934 from balloob/mqtt-protocol
Allow forcing MQTT protocol v3.1
2016-01-19 20:20:04 -08:00
Paulus Schoutsen a4ee2bd8ef Merge pull request #935 from balloob/0.11.1
0.11.1
2016-01-19 12:20:26 -08:00
MartinHjelmare d4629a7efe Fix missing binary sensor types
* Add missing binary sensor types to sensor/mysensors.
* Remove unneeded pylint disable.
2016-01-19 12:13:34 -08:00
Paulus Schoutsen 441ae73344 Merge pull request #936 from MartinHjelmare/fix-mysensors-sensors
Fix missing binary sensor types
2016-01-19 12:11:33 -08:00
MartinHjelmare 9249dc6dd3 Fix missing binary sensor types
* Add missing binary sensor types to sensor/mysensors.
* Remove unneeded pylint disable.
2016-01-19 19:26:40 +01:00
Paulus Schoutsen 4c4539caff Version bump to 0.11.1 2016-01-19 09:12:03 -08:00
Paulus Schoutsen 4c0ff0e0d0 Allow forcing MQTT protocol v3.1 2016-01-19 09:07:54 -08:00
Paulus Schoutsen 1ceee2d6c5 Fix MQTT reconnecting 2016-01-19 09:07:35 -08:00
Paulus Schoutsen cbb74d50ce Enforce entity attribute types 2016-01-19 09:07:27 -08:00
Erik c2d72bbf09 fix issue where sensors and switches were duplicated because of component getting initialized twice. closes #913 2016-01-19 09:06:57 -08:00
Paulus Schoutsen f97ba263c4 Allow forcing MQTT protocol v3.1 2016-01-19 09:00:40 -08:00
Paulus Schoutsen 85df9e98bd Merge pull request #922 from balloob/mqtt-fixes
Fix MQTT reconnecting
2016-01-19 08:45:49 -08:00
Paulus Schoutsen 2faafb9c0f Merge pull request #931 from molobrakos/fix-tellduslive-duplicates
proper initialization
2016-01-18 18:01:12 -08:00
Erik d43101f22a fix issue where sensors and switches were duplicated because of component getting initialized twice. closes #913 2016-01-18 19:41:41 +01:00
Tim 3d23cd10fc Attempt to fix ungrouped-imports pylint error 2016-01-18 18:30:09 +00:00
Tim 66cd8d264e Remove use of warn() 2016-01-18 18:27:46 +00:00
Tim 291910d74e Add LIFX bulb support 2016-01-18 18:10:32 +00:00
Paulus Schoutsen 7ac648d0ab Merge pull request #889 from Bart274/dev
Create command_sensor.py
2016-01-18 07:47:00 -08:00
Bart274 3b42390062 Update command_sensor.py 2016-01-18 13:48:09 +01:00
Bart274 a80917f530 Update command_sensor.py 2016-01-18 09:24:38 +01:00
Paulus Schoutsen 8fbb585874 Fix MQTT reconnecting 2016-01-17 21:39:25 -08:00
Ryan Kraus 027d97321f Merge pull request #911 from rmkraus/service-toggle
Added toggle service
2016-01-17 21:11:35 -05:00
Paulus Schoutsen 62a1c9687e Merge pull request #920 from balloob/enforce-entity-types
Enforce entity attribute types
2016-01-17 18:01:20 -08:00
Paulus Schoutsen a9c6f8c1d9 Enforce entity attribute types 2016-01-17 17:50:20 -08:00
Paulus Schoutsen 06b4fcc2cf Fix lint errors 2016-01-17 16:36:25 -08:00
Paulus Schoutsen 6dc2501116 ps - fix RPi GPIO imports 2016-01-17 16:29:41 -08:00
Paulus Schoutsen 54060f27ef Merge pull request #893 from joshughes/nest_sensor
Add the nest sensor for tracking data from nest
2016-01-17 15:16:25 -08:00
Ryan Kraus 85aa4fdd2e Revised entity toggle to use is_on
The toggle function in the Entity ABC was using state == STATE_ON to
determine whether the entity was on. This was revised to use the is_on
property instead.
2016-01-17 16:59:22 -05:00
Ryan Kraus 0624445627 Added tests for toggle service.
1) Added tests to toggle service.
2) Removed color_util import in light tests. It was not being used.
2016-01-17 16:42:18 -05:00
Joseph Hughes 4ca4941c82 fix pylint errors in sensor/nest 2016-01-16 12:56:38 -07:00
Joseph Hughes 4dbd84ead0 make sure everything inherits from NesSensor 2016-01-16 12:53:42 -07:00
Joseph Hughes 0495776a22 merge upstream dev 2016-01-16 12:52:22 -07:00
Joseph Hughes dd35551047 fix order of inhertiance 2016-01-16 12:47:08 -07:00
Paulus Schoutsen 9f61369156 Version bump to 0.12.0.dev0 2016-01-16 11:23:55 -08:00
Ryan Kraus 342a819fd4 Added toggle service
Added a toggle service to the homeassistant, light, switch, and
media_player domains.
2016-01-16 10:45:05 -05:00
Joseph Hughes 80023f62d9 Merge remote-tracking branch 'upstream/dev' into nest_sensor
# Conflicts:
#	.coveragerc
#	requirements_all.txt
2016-01-15 08:18:16 -07:00
Joseph Hughes 9617288bd5 multiple inheritance for nest binary sensor 2016-01-15 08:16:33 -07:00
Bart274 d40e889d3b Update command_sensor.py
Importing CommandSensorData from the command_sensor in sensors in order not to duplicate code
2016-01-15 09:01:58 +01:00
Bart274 41acc8fa43 Update .coveragerc
This shouldn't be excluded according to @balloob because it's a pure python component
2016-01-15 08:59:11 +01:00
Joseph Hughes 9210c57c2d Fix lint errors and PR comments 2016-01-14 14:32:43 -07:00
Joseph Hughes 313cbda0aa fix multiple PR issues 2016-01-14 14:17:28 -07:00
Joseph Hughes a39148dd38 fix pylint errors for Nest Sensor 2016-01-14 11:37:17 -07:00
Joseph Hughes b8c8c71b78 run requirements_all again for nest sensor 2016-01-14 11:28:28 -07:00
Joseph Hughes 65a3bf2325 fix merge error 2016-01-14 11:01:53 -07:00
Joseph Hughes ac34db3c8a Merge remote-tracking branch 'upstream/dev' into nest_sensor
# Conflicts:
#	.coveragerc
#	requirements_all.txt
2016-01-14 11:00:34 -07:00
Joseph Hughes f2e86ecd8e Merge remote-tracking branch 'upstream/master' into nest_sensor
# Conflicts:
#	homeassistant/const.py
2016-01-14 10:52:19 -07:00
Joseph Hughes 7b993da0de address PR comments for Nest Sensor 2016-01-14 10:48:24 -07:00
Bart274 d0d375d433 Update command_sensor.py 2016-01-14 10:27:11 +01:00
Bart274 d17aa103b4 Update command_sensor.py 2016-01-14 10:13:57 +01:00
Joseph Hughes 0e6a60b086 Add the nest sensor for tracking data from nest 2016-01-13 21:05:47 -07:00
Bart274 c2e8646aed Update .coveragerc 2016-01-13 12:20:37 +01:00
Bart274 e5919c1bfe Update command_sensor.py 2016-01-13 12:19:20 +01:00
Bart274 c8961fcf99 Create command_sensor.py
This adds a binary command_sensor
2016-01-13 11:52:42 +01:00
Malte Deiseroth ef76047ba2 new try to add everythin from the stylecheckers 2015-09-22 13:27:08 +02:00
Malte Deiseroth d475e5362b respect flake8 errors 2015-09-22 12:53:44 +02:00
Malte Deiseroth 3027b4a5a8 respect pylint suggestions 2015-09-22 12:32:45 +02:00
Malte Deiseroth 3829abbd2d updated with remote 2015-09-22 12:18:00 +02:00
Malte Deiseroth e6e3b37a62 merged with current dev 2015-09-22 12:11:57 +02:00
Malte Deiseroth ce501ae627 Improved onewire configuration 2015-09-16 14:17:41 +02:00
deisi b6f954e082 Changed handling of config file
I tried to implement your suggesteions for the default handlig of the device names. I think this way, everything you wanted is in.
2015-09-16 10:18:11 +02:00
deisi b368388714 Update .gitignore 2015-09-16 08:49:12 +02:00
Malte Deiseroth 8842e4e94f gitignore ready for emacs 2015-09-16 08:34:08 +02:00
Malte Deiseroth 0ed608abff little bug 2015-04-07 21:10:16 +02:00
Malte Deiseroth afcf3eaac3 - add ds18S20 1-Wire sensor support
- gitignore emacs backup files
2015-03-29 14:48:17 +02:00
140 changed files with 4461 additions and 1171 deletions
+14 -2
View File
@@ -10,6 +10,9 @@ omit =
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
homeassistant/components/insteon_hub.py
homeassistant/components/*/insteon_hub.py
homeassistant/components/isy994.py
homeassistant/components/*/isy994.py
@@ -32,6 +35,9 @@ omit =
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
@@ -41,6 +47,9 @@ omit =
homeassistant/components/mysensors.py
homeassistant/components/*/mysensors.py
homeassistant/components/nest.py
homeassistant/components/*/nest.py
homeassistant/components/rpi_gpio.py
homeassistant/components/*/rpi_gpio.py
@@ -57,7 +66,6 @@ omit =
homeassistant/components/device_tracker/luci.py
homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/device_tracker/owntracks.py
homeassistant/components/device_tracker/snmp.py
homeassistant/components/device_tracker/thomson.py
homeassistant/components/device_tracker/tomato.py
@@ -66,11 +74,13 @@ omit =
homeassistant/components/discovery.py
homeassistant/components/downloader.py
homeassistant/components/ifttt.py
homeassistant/components/statsd.py
homeassistant/components/influxdb.py
homeassistant/components/keyboard.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
homeassistant/components/light/lifx.py
homeassistant/components/light/limitlessled.py
homeassistant/components/media_player/cast.py
homeassistant/components/media_player/denon.py
@@ -91,7 +101,9 @@ omit =
homeassistant/components/notify/smtp.py
homeassistant/components/notify/syslog.py
homeassistant/components/notify/telegram.py
homeassistant/components/notify/twitter.py
homeassistant/components/notify/xmpp.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/bitcoin.py
homeassistant/components/sensor/cpuspeed.py
@@ -102,6 +114,7 @@ omit =
homeassistant/components/sensor/forecast.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/rest.py
homeassistant/components/sensor/sabnzbd.py
@@ -124,7 +137,6 @@ omit =
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/honeywell.py
homeassistant/components/thermostat/nest.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py
+10
View File
@@ -69,6 +69,16 @@ nosetests.xml
.python-version
# emacs auto backups
*~
*#
*.orig
# venv stuff
pyvenv.cfg
pip-selfcheck.json
venv
# vimmy stuff
*.swp
*.swo
+84 -72
View File
@@ -29,9 +29,13 @@ import time
import logging
from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_ON, STATE_OFF
import homeassistant.loader as loader
from homeassistant.helpers import validate_config
from homeassistant.helpers.event_decorators import \
track_state_change, track_time_change
from homeassistant.helpers.service import service
import homeassistant.components as core
from homeassistant.components import device_tracker
from homeassistant.components import light
# The domain of your component. Should be equal to the name of your component
DOMAIN = "example"
@@ -39,11 +43,14 @@ DOMAIN = "example"
# List of component names (string) your component depends upon
# We depend on group because group will be loaded after all the components that
# initialize devices have been setup.
DEPENDENCIES = ['group']
DEPENDENCIES = ['group', 'device_tracker', 'light']
# Configuration key for the entity id we are targetting
CONF_TARGET = 'target'
# Variable for storing configuration parameters
TARGET_ID = None
# Name of the service that we expose
SERVICE_FLASH = 'flash'
@@ -53,84 +60,89 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Setup example component. """
global TARGET_ID
# Validate that all required config options are given
if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER):
return False
target_id = config[DOMAIN][CONF_TARGET]
TARGET_ID = config[DOMAIN][CONF_TARGET]
# Validate that the target entity id exists
if hass.states.get(target_id) is None:
_LOGGER.error("Target entity id %s does not exist", target_id)
if hass.states.get(TARGET_ID) is None:
_LOGGER.error("Target entity id %s does not exist",
TARGET_ID)
# Tell the bootstrapper that we failed to initialize
# Tell the bootstrapper that we failed to initialize and clear the
# stored target id so our functions don't run.
TARGET_ID = None
return False
# We will use the component helper methods to check the states.
device_tracker = loader.get_component('device_tracker')
light = loader.get_component('light')
def track_devices(entity_id, old_state, new_state):
""" Called when the group.all devices change state. """
# If anyone comes home and the core is not on, turn it on.
if new_state.state == STATE_HOME and not core.is_on(hass, target_id):
core.turn_on(hass, target_id)
# If all people leave the house and the core is on, turn it off
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, target_id):
core.turn_off(hass, target_id)
# Register our track_devices method to receive state changes of the
# all tracked devices group.
hass.states.track_change(
device_tracker.ENTITY_ID_ALL_DEVICES, track_devices)
def wake_up(now):
""" Turn it on in the morning if there are people home and
it is not already on. """
if device_tracker.is_on(hass) and not core.is_on(hass, target_id):
_LOGGER.info('People home at 7AM, turning it on')
core.turn_on(hass, target_id)
# Register our wake_up service to be called at 7AM in the morning
hass.track_time_change(wake_up, hour=7, minute=0, second=0)
def all_lights_off(entity_id, old_state, new_state):
""" If all lights turn off, turn off. """
if core.is_on(hass, target_id):
_LOGGER.info('All lights have been turned off, turning it off')
core.turn_off(hass, target_id)
# Register our all_lights_off method to be called when all lights turn off
hass.states.track_change(
light.ENTITY_ID_ALL_LIGHTS, all_lights_off, STATE_ON, STATE_OFF)
def flash_service(call):
""" Service that will turn the target off for 10 seconds
if on and vice versa. """
if core.is_on(hass, target_id):
core.turn_off(hass, target_id)
time.sleep(10)
core.turn_on(hass, target_id)
else:
core.turn_on(hass, target_id)
time.sleep(10)
core.turn_off(hass, target_id)
# Register our service with HASS.
hass.services.register(DOMAIN, SERVICE_FLASH, flash_service)
# Tells the bootstrapper that the component was successfully initialized
# Tell the bootstrapper that we initialized successfully
return True
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
def track_devices(hass, entity_id, old_state, new_state):
""" Called when the group.all devices change state. """
# If the target id is not set, return
if not TARGET_ID:
return
# If anyone comes home and the entity is not on, turn it on.
if new_state.state == STATE_HOME and not core.is_on(hass, TARGET_ID):
core.turn_on(hass, TARGET_ID)
# If all people leave the house and the entity is on, turn it off
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
@track_time_change(hour=7, minute=0, second=0)
def wake_up(hass, now):
"""
Turn it on in the morning (7 AM) if there are people home and
it is not already on.
"""
if not TARGET_ID:
return
if device_tracker.is_on(hass) and not core.is_on(hass, TARGET_ID):
_LOGGER.info('People home at 7AM, turning it on')
core.turn_on(hass, TARGET_ID)
@track_state_change(light.ENTITY_ID_ALL_LIGHTS, STATE_ON, STATE_OFF)
def all_lights_off(hass, entity_id, old_state, new_state):
""" If all lights turn off, turn off. """
if not TARGET_ID:
return
if core.is_on(hass, TARGET_ID):
_LOGGER.info('All lights have been turned off, turning it off')
core.turn_off(hass, TARGET_ID)
@service(DOMAIN, SERVICE_FLASH)
def flash_service(hass, call):
"""
Service that will turn the target off for 10 seconds if on and vice versa.
"""
if not TARGET_ID:
return
if core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
time.sleep(10)
core.turn_on(hass, TARGET_ID)
else:
core.turn_on(hass, TARGET_ID)
time.sleep(10)
core.turn_off(hass, TARGET_ID)
+6 -1
View File
@@ -24,6 +24,7 @@ import homeassistant.config as config_util
import homeassistant.loader as loader
import homeassistant.components as core_components
import homeassistant.components.group as group
from homeassistant.helpers import event_decorators, service
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
__version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
@@ -199,6 +200,10 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
_LOGGER.info('Home Assistant core initialized')
# give event decorators access to HASS
event_decorators.HASS = hass
service.HASS = hass
# Setup the components
for domain in loader.load_order_components(components):
_setup_component(hass, domain, config)
@@ -223,7 +228,7 @@ 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_config_file(config_path)
config_dict = config_util.load_yaml_config_file(config_path)
return from_config_dict(config_dict, hass, enable_log=False,
skip_pip=skip_pip)
+14 -5
View File
@@ -16,11 +16,11 @@ import itertools as it
import logging
import homeassistant.core as ha
import homeassistant.util as util
from homeassistant.helpers import extract_entity_ids
from homeassistant.helpers.entity import split_entity_id
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.loader import get_component
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE)
_LOGGER = logging.getLogger(__name__)
@@ -36,7 +36,7 @@ def is_on(hass, entity_id=None):
entity_ids = hass.states.entity_ids()
for entity_id in entity_ids:
domain = util.split_entity_id(entity_id)[0]
domain = split_entity_id(entity_id)[0]
module = get_component(domain)
@@ -68,6 +68,14 @@ def turn_off(hass, entity_id=None, **service_data):
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
def toggle(hass, entity_id=None, **service_data):
""" Toggles specified entity. """
if entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.services.call(ha.DOMAIN, SERVICE_TOGGLE, service_data)
def setup(hass, config):
""" Setup general services related to homeassistant. """
@@ -84,7 +92,7 @@ def setup(hass, config):
# Group entity_ids by domain. groupby requires sorted data.
by_domain = it.groupby(sorted(entity_ids),
lambda item: util.split_entity_id(item)[0])
lambda item: split_entity_id(item)[0])
for domain, ent_ids in by_domain:
# We want to block for all calls and only return when all calls
@@ -105,5 +113,6 @@ def setup(hass, config):
hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service)
hass.services.register(ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service)
hass.services.register(ha.DOMAIN, SERVICE_TOGGLE, handle_turn_service)
return True
+2 -12
View File
@@ -9,11 +9,6 @@ https://home-assistant.io/components/arduino/
"""
import logging
try:
from PyMata.pymata import PyMata
except ImportError:
PyMata = None
from homeassistant.helpers import validate_config
from homeassistant.const import (EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP)
@@ -27,18 +22,12 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Setup the Arduino component. """
global PyMata # pylint: disable=invalid-name
if PyMata is None:
from PyMata.pymata import PyMata as PyMata_
PyMata = PyMata_
import serial
if not validate_config(config,
{DOMAIN: ['port']},
_LOGGER):
return False
import serial
global BOARD
try:
BOARD = ArduinoBoard(config[DOMAIN]['port'])
@@ -67,6 +56,7 @@ class ArduinoBoard(object):
""" Represents an Arduino board. """
def __init__(self, port):
from PyMata.pymata import PyMata
self._port = port
self._board = PyMata(self._port, verbose=False)
+3 -41
View File
@@ -10,7 +10,7 @@ import logging
from datetime import timedelta
from homeassistant.components import sun
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.helpers.event import track_sunrise, track_sunset
import homeassistant.util.dt as dt_util
DEPENDENCIES = ['sun']
@@ -47,9 +47,9 @@ def trigger(hass, config, action):
# Do something to call action
if event == EVENT_SUNRISE:
trigger_sunrise(hass, action, offset)
track_sunrise(hass, action, offset)
else:
trigger_sunset(hass, action, offset)
track_sunset(hass, action, offset)
return True
@@ -125,44 +125,6 @@ def if_action(hass, config):
return time_if
def trigger_sunrise(hass, action, offset):
""" Trigger action at next sun rise. """
def next_rise():
""" Returns next sunrise. """
next_time = sun.next_rising_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunrise_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
action()
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
def trigger_sunset(hass, action, offset):
""" Trigger action at next sun set. """
def next_set():
""" Returns next sunrise. """
next_time = sun.next_setting_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunset_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
action()
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
def _parse_offset(raw_offset):
if raw_offset is None:
return timedelta(0)
+4 -5
View File
@@ -8,7 +8,6 @@ at https://home-assistant.io/components/automation/#time-trigger
"""
import logging
from homeassistant.util import convert
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_time_change
@@ -34,9 +33,9 @@ def trigger(hass, config, action):
hours, minutes, seconds = after.hour, after.minute, after.second
elif (CONF_HOURS in config or CONF_MINUTES in config or
CONF_SECONDS in config):
hours = convert(config.get(CONF_HOURS), int)
minutes = convert(config.get(CONF_MINUTES), int)
seconds = convert(config.get(CONF_SECONDS), int)
hours = config.get(CONF_HOURS)
minutes = config.get(CONF_MINUTES)
seconds = config.get(CONF_SECONDS)
else:
_LOGGER.error('One of %s, %s, %s OR %s needs to be specified',
CONF_HOURS, CONF_MINUTES, CONF_SECONDS, CONF_AFTER)
@@ -59,7 +58,7 @@ def if_action(hass, config):
weekday = config.get(CONF_WEEKDAY)
if before is None and after is None and weekday is None:
logging.getLogger(__name__).error(
_LOGGER.error(
"Missing if-condition configuration key %s, %s or %s",
CONF_BEFORE, CONF_AFTER, CONF_WEEKDAY)
return None
@@ -0,0 +1,81 @@
"""
homeassistant.components.binary_sensor.command_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure custom shell commands to turn a value
into a logical value for a binary sensor.
"""
import logging
from datetime import timedelta
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.command_sensor import CommandSensorData
from homeassistant.util import template
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "Binary Command Sensor"
DEFAULT_PAYLOAD_ON = 'ON'
DEFAULT_PAYLOAD_OFF = 'OFF'
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Add the Command Sensor. """
if config.get('command') is None:
_LOGGER.error('Missing required variable: "command"')
return False
data = CommandSensorData(config.get('command'))
add_devices([CommandBinarySensor(
hass,
data,
config.get('name', DEFAULT_NAME),
config.get('payload_on', DEFAULT_PAYLOAD_ON),
config.get('payload_off', DEFAULT_PAYLOAD_OFF),
config.get(CONF_VALUE_TEMPLATE)
)])
# pylint: disable=too-many-arguments
class CommandBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that is returning
a value of a shell commands. """
def __init__(self, hass, data, name, payload_on,
payload_off, value_template):
self._hass = hass
self.data = data
self._name = name
self._state = False
self._payload_on = payload_on
self._payload_off = payload_off
self._value_template = value_template
self.update()
@property
def name(self):
""" The name of the sensor. """
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
return self._state
def update(self):
""" Gets the latest data and updates the state. """
self.data.update()
value = self.data.value
if self._value_template is not None:
value = template.render_with_possible_json_value(
self._hass, self._value_template, value, False)
if value == self._payload_on:
self._state = True
elif value == self._payload_off:
self._state = False
@@ -0,0 +1,55 @@
"""
homeassistant.components.binary_sensor.nest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 homeassistant.components.nest as nest
from homeassistant.components.sensor.nest import NestSensor
from homeassistant.components.binary_sensor import BinarySensorDevice
BINARY_TYPES = ['fan',
'hvac_ac_state',
'hvac_aux_heater_state',
'hvac_heat_x2_state',
'hvac_heat_x3_state',
'hvac_alt_heat_state',
'hvac_alt_heat_x2_state',
'hvac_emer_heat_state',
'online']
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."
)
class NestBinarySensor(NestSensor, BinarySensorDevice):
""" Represents a Nest binary sensor. """
@property
def is_on(self):
""" True if the binary sensor is on. """
return bool(getattr(self.device, self.variable))
@@ -1,7 +1,7 @@
"""
homeassistant.components.binary_sensor.rpi_gpio
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a binary_sensor using RPi GPIO.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a binary sensor using RPi GPIO.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.rpi_gpio/
@@ -0,0 +1,29 @@
"""
homeassistant.components.binary_sensor.zigbee
Contains functionality to use a ZigBee device as a binary sensor.
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.zigbee import (
ZigBeeDigitalIn, ZigBeeDigitalInConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
add_entities([
ZigBeeBinarySensor(hass, ZigBeeDigitalInConfig(config))
])
class ZigBeeBinarySensor(ZigBeeDigitalIn, BinarySensorDevice):
"""
Use multiple inheritance to turn a ZigBeeDigitalIn into a
BinarySensorDevice.
"""
pass
+1 -1
View File
@@ -11,7 +11,7 @@ the user has submitted configuration information.
"""
import logging
from homeassistant.helpers import generate_entity_id
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.const import EVENT_TIME_CHANGED
DOMAIN = "configurator"
+10 -4
View File
@@ -62,10 +62,16 @@ def setup(hass, config):
lights = sorted(hass.states.entity_ids('light'))
switches = sorted(hass.states.entity_ids('switch'))
media_players = sorted(hass.states.entity_ids('media_player'))
group.setup_group(hass, 'living room', [lights[2], lights[1], switches[0],
media_players[1]])
group.setup_group(hass, 'bedroom', [lights[0], switches[1],
media_players[0]])
group.Group(hass, 'living room', [
lights[2], lights[1], switches[0], media_players[1],
'scene.romantic_lights'])
group.Group(hass, 'bedroom', [lights[0], switches[1],
media_players[0]])
group.Group(hass, 'Rooms', [
'group.living_room', 'group.bedroom',
'scene.romantic_lights', 'rollershutter.kitchen_window',
'rollershutter.living_room_window',
], view=True)
# Setup scripts
bootstrap.setup_component(
@@ -229,7 +229,7 @@ class DeviceTracker(object):
""" Initializes group for all tracked devices. """
entity_ids = (dev.entity_id for dev in self.devices.values()
if dev.track)
self.group = group.setup_group(
self.group = group.Group(
self.hass, GROUP_NAME_ALL_DEVICES, entity_ids, False)
def update_stale(self, now):
@@ -15,6 +15,8 @@ from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
REQUIREMENTS = ['fritzconnection==0.4.6']
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
@@ -55,16 +57,8 @@ class FritzBoxScanner(object):
self.password = ''
self.success_init = True
# Try to import the fritzconnection library
try:
# noinspection PyPackageRequirements,PyUnresolvedReferences
import fritzconnection as fc
except ImportError:
_LOGGER.exception("""Failed to import Python library
fritzconnection. Please run
<home-assistant>/setup to install it.""")
self.success_init = False
return
# pylint: disable=import-error
import fritzconnection as fc
# Check for user specific configuration
if CONF_HOST in config.keys():
@@ -19,7 +19,7 @@ from homeassistant.components.device_tracker import DOMAIN
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pynetgear==0.3.1']
REQUIREMENTS = ['pynetgear==0.3.2']
def get_scanner(hass, config):
@@ -8,16 +8,26 @@ https://home-assistant.io/components/device_tracker.owntracks/
"""
import json
import logging
import threading
from collections import defaultdict
import homeassistant.components.mqtt as mqtt
from homeassistant.const import (STATE_HOME, STATE_NOT_HOME)
from homeassistant.const import STATE_HOME
DEPENDENCIES = ['mqtt']
CONF_TRANSITION_EVENTS = 'use_events'
REGIONS_ENTERED = defaultdict(list)
MOBILE_BEACONS_ACTIVE = defaultdict(list)
BEACON_DEV_ID = 'beacon'
LOCATION_TOPIC = 'owntracks/+/+'
EVENT_TOPIC = 'owntracks/+/+/event'
_LOGGER = logging.getLogger(__name__)
LOCK = threading.Lock()
def setup_scanner(hass, config, see):
""" Set up an OwnTracks tracker. """
@@ -31,27 +41,28 @@ def setup_scanner(hass, config, see):
data = json.loads(payload)
except ValueError:
# If invalid JSON
logging.getLogger(__name__).error(
_LOGGER.error(
'Unable to parse payload as JSON: %s', payload)
return
if not isinstance(data, dict) or data.get('_type') != 'location':
return
parts = topic.split('/')
kwargs = {
'dev_id': '{}_{}'.format(parts[1], parts[2]),
'host_name': parts[1],
'gps': (data['lat'], data['lon']),
}
if 'acc' in data:
kwargs['gps_accuracy'] = data['acc']
if 'batt' in data:
kwargs['battery'] = data['batt']
dev_id, kwargs = _parse_see_args(topic, data)
see(**kwargs)
# Block updates if we're in a region
with LOCK:
if REGIONS_ENTERED[dev_id]:
_LOGGER.debug(
"location update ignored - inside region %s",
REGIONS_ENTERED[-1])
return
see(**kwargs)
see_beacons(dev_id, kwargs)
def owntracks_event_update(topic, payload, qos):
# pylint: disable=too-many-branches
""" MQTT event (geofences) received. """
# Docs on available data:
@@ -60,47 +71,111 @@ def setup_scanner(hass, config, see):
data = json.loads(payload)
except ValueError:
# If invalid JSON
logging.getLogger(__name__).error(
_LOGGER.error(
'Unable to parse payload as JSON: %s', payload)
return
if not isinstance(data, dict) or data.get('_type') != 'transition':
return
# check if in "home" fence or other zone
location = ''
if data['event'] == 'enter':
# OwnTracks uses - at the start of a beacon zone
# to switch on 'hold mode' - ignore this
location = data['desc'].lstrip("-")
if location.lower() == 'home':
location = STATE_HOME
if data['desc'].lower() == 'home':
location = STATE_HOME
else:
location = data['desc']
dev_id, kwargs = _parse_see_args(topic, data)
if data['event'] == 'enter':
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
MOBILE_BEACONS_ACTIVE[dev_id].append(location)
else:
# Normal region
kwargs['location_name'] = location
regions = REGIONS_ENTERED[dev_id]
if location not in regions:
regions.append(location)
_LOGGER.info("Enter region %s", location)
_set_gps_from_zone(kwargs, zone)
see(**kwargs)
see_beacons(dev_id, kwargs)
elif data['event'] == 'leave':
location = STATE_NOT_HOME
regions = REGIONS_ENTERED[dev_id]
if location in regions:
regions.remove(location)
new_region = regions[-1] if regions else None
if new_region:
# Exit to previous region
zone = hass.states.get("zone.{}".format(new_region))
kwargs['location_name'] = new_region
_set_gps_from_zone(kwargs, zone)
_LOGGER.info("Exit from to %s", new_region)
else:
_LOGGER.info("Exit to GPS")
see(**kwargs)
see_beacons(dev_id, kwargs)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location in beacons:
beacons.remove(location)
else:
logging.getLogger(__name__).error(
_LOGGER.error(
'Misformatted mqtt msgs, _type=transition, event=%s',
data['event'])
return
parts = topic.split('/')
kwargs = {
'dev_id': '{}_{}'.format(parts[1], parts[2]),
'host_name': parts[1],
'gps': (data['lat'], data['lon']),
'location_name': location,
}
if 'acc' in data:
kwargs['gps_accuracy'] = data['acc']
def see_beacons(dev_id, kwargs_param):
""" Set active beacons to the current location """
see(**kwargs)
kwargs = kwargs_param.copy()
for beacon in MOBILE_BEACONS_ACTIVE[dev_id]:
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
kwargs['host_name'] = beacon
see(**kwargs)
use_events = config.get(CONF_TRANSITION_EVENTS)
mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1)
if use_events:
mqtt.subscribe(hass, EVENT_TOPIC, owntracks_event_update, 1)
else:
mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1)
mqtt.subscribe(hass, EVENT_TOPIC, owntracks_event_update, 1)
return True
def _parse_see_args(topic, data):
""" Parse the OwnTracks location parameters,
into the format see expects. """
parts = topic.split('/')
dev_id = '{}_{}'.format(parts[1], parts[2])
host_name = parts[1]
kwargs = {
'dev_id': dev_id,
'host_name': host_name,
'gps': (data['lat'], data['lon'])
}
if 'acc' in data:
kwargs['gps_accuracy'] = data['acc']
if 'batt' in data:
kwargs['battery'] = data['batt']
return dev_id, kwargs
def _set_gps_from_zone(kwargs, zone):
""" Set the see parameters from the zone parameters """
if zone is not None:
kwargs['gps'] = (
zone.attributes['latitude'],
zone.attributes['longitude'])
kwargs['gps_accuracy'] = zone.attributes['radius']
return kwargs
+2 -8
View File
@@ -11,6 +11,8 @@ import logging
import re
import threading
import requests
from homeassistant.helpers import validate_config
from homeassistant.util import sanitize_filename
@@ -30,14 +32,6 @@ def setup(hass, config):
logger = logging.getLogger(__name__)
try:
import requests
except ImportError:
logger.exception(("Failed to import requests. "
"Did you maybe not execute 'pip install requests'?"))
return False
if not validate_config(config, {DOMAIN: [CONF_DOWNLOAD_DIR]}, logger):
return False
@@ -21,7 +21,9 @@ _LOGGER = logging.getLogger(__name__)
FRONTEND_URLS = [
URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState',
'/devEvent', '/devInfo', '/devTemplate', '/states']
'/devEvent', '/devInfo', '/devTemplate',
re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)'),
]
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
@@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by update_mdi script """
VERSION = "7d76081c37634d36af21f5cc1ca79408"
VERSION = "a2605736c8d959d50c4bcbba1e6a6aa5"
+1 -1
View File
@@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "1003c31441ec44b3db84b49980f736a7"
VERSION = "1e89871aaae43c91b2508f52bc161b69"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+49 -24
View File
@@ -7,20 +7,24 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/group/
"""
import homeassistant.core as ha
from homeassistant.helpers import generate_entity_id
from homeassistant.helpers.event import track_state_change
from homeassistant.helpers.entity import Entity
import homeassistant.util as util
from homeassistant.helpers.entity import (
Entity, split_entity_id, generate_entity_id)
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED,
STATE_UNKNOWN)
STATE_UNKNOWN, CONF_NAME, CONF_ICON)
DOMAIN = "group"
DOMAIN = 'group'
ENTITY_ID_FORMAT = DOMAIN + ".{}"
ENTITY_ID_FORMAT = DOMAIN + '.{}'
ATTR_AUTO = "auto"
CONF_ENTITIES = 'entities'
CONF_VIEW = 'view'
ATTR_AUTO = 'auto'
ATTR_ORDER = 'order'
ATTR_VIEW = 'view'
# List of ON/OFF state tuples for groupable states
_GROUP_TYPES = [(STATE_ON, STATE_OFF), (STATE_HOME, STATE_NOT_HOME),
@@ -62,7 +66,7 @@ def expand_entity_ids(hass, entity_ids):
try:
# If entity_id points at a group, expand it
domain, _ = util.split_entity_id(entity_id)
domain, _ = split_entity_id(entity_id)
if domain == DOMAIN:
found_ids.extend(
@@ -75,7 +79,7 @@ def expand_entity_ids(hass, entity_ids):
found_ids.append(entity_id)
except AttributeError:
# Raised by util.split_entity_id if entity_id is not a string
# Raised by split_entity_id if entity_id is not a string
pass
return found_ids
@@ -104,10 +108,20 @@ def get_entity_ids(hass, entity_id, domain_filter=None):
def setup(hass, config):
""" Sets up all groups found definded in the configuration. """
for name, entity_ids in config.get(DOMAIN, {}).items():
for object_id, conf in config.get(DOMAIN, {}).items():
if not isinstance(conf, dict):
conf = {CONF_ENTITIES: conf}
name = conf.get(CONF_NAME, object_id)
entity_ids = conf.get(CONF_ENTITIES)
icon = conf.get(CONF_ICON)
view = conf.get(CONF_VIEW)
if isinstance(entity_ids, str):
entity_ids = [ent.strip() for ent in entity_ids.split(",")]
setup_group(hass, name, entity_ids)
Group(hass, name, entity_ids, icon=icon, view=view,
object_id=object_id)
return True
@@ -115,14 +129,19 @@ def setup(hass, config):
class Group(Entity):
""" Tracks a group of entity ids. """
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-instance-attributes, too-many-arguments
def __init__(self, hass, name, entity_ids=None, user_defined=True):
def __init__(self, hass, name, entity_ids=None, user_defined=True,
icon=None, view=False, object_id=None):
self.hass = hass
self._name = name
self._state = STATE_UNKNOWN
self.user_defined = user_defined
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, hass=hass)
self._order = len(hass.states.entity_ids(DOMAIN))
self._user_defined = user_defined
self._icon = icon
self._view = view
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, object_id or name, hass=hass)
self.tracking = []
self.group_on = None
self.group_off = None
@@ -144,12 +163,25 @@ class Group(Entity):
def state(self):
return self._state
@property
def icon(self):
return self._icon
@property
def hidden(self):
return not self._user_defined or self._view
@property
def state_attributes(self):
return {
data = {
ATTR_ENTITY_ID: self.tracking,
ATTR_AUTO: not self.user_defined,
ATTR_ORDER: self._order,
}
if not self._user_defined:
data[ATTR_AUTO] = True
if self._view:
data[ATTR_VIEW] = True
return data
def update_tracked_entity_ids(self, entity_ids):
""" Update the tracked entity IDs. """
@@ -220,10 +252,3 @@ class Group(Entity):
for ent_id in self.tracking
if tr_state.entity_id != ent_id):
self._state = group_off
def setup_group(hass, name, entity_ids, user_defined=True):
""" Sets up a group state that is the combined state of
several states. Supports ON/OFF and DEVICE_HOME/DEVICE_NOT_HOME. """
return Group(hass, name, entity_ids, user_defined)
+9 -10
View File
@@ -23,7 +23,7 @@ DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 8086
DEFAULT_DATABASE = 'home_assistant'
REQUIREMENTS = ['influxdb==2.10.0']
REQUIREMENTS = ['influxdb==2.11.0']
CONF_HOST = 'host'
CONF_PORT = 'port'
@@ -51,14 +51,11 @@ def setup(hass, config):
try:
influx = InfluxDBClient(host=host, port=port, username=username,
password=password, database=database)
databases = [i['name'] for i in influx.get_list_database()]
except exceptions.InfluxDBClientError:
_LOGGER.error("Database host is not accessible. "
"Please check your entries in the configuration file.")
return False
if database not in databases:
_LOGGER.error("Database %s doesn't exist", database)
influx.query("select * from /.*/ LIMIT 1;")
except exceptions.InfluxDBClientError as exc:
_LOGGER.error("Database host is not accessible due to '%s', please "
"check your entries in the configuration file and that"
" the database exists and is READ/WRITE.", exc)
return False
def influx_event_listener(event):
@@ -76,6 +73,8 @@ def setup(hass, config):
_state = 0
else:
_state = state.state
if _state == '':
return
try:
_state = float(_state)
except ValueError:
@@ -100,7 +99,7 @@ def setup(hass, config):
try:
influx.write_points(json_body)
except exceptions.InfluxDBClientError:
_LOGGER.exception('Error saving event to InfluxDB')
_LOGGER.exception('Error saving event "%s" to InfluxDB', json_body)
hass.bus.listen(EVENT_STATE_CHANGED, influx_event_listener)
@@ -1,4 +1,6 @@
"""
homeassistant.components.input_boolean
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to keep track of user controlled booleans for within automation.
For more details about this component, please refer to the documentation
+93
View File
@@ -0,0 +1,93 @@
"""
homeassistant.components.insteon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Insteon Hub.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/insteon/
"""
import logging
import homeassistant.bootstrap as bootstrap
from homeassistant.helpers import validate_config
from homeassistant.loader import get_component
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY, ATTR_DISCOVERED,
ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
DOMAIN = "insteon_hub"
REQUIREMENTS = ['insteon_hub==0.4.5']
INSTEON = None
DISCOVER_LIGHTS = "insteon_hub.lights"
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
"""
Setup Insteon Hub component.
This will automatically import associated lights.
"""
if not validate_config(
config,
{DOMAIN: [CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY]},
_LOGGER):
return False
import insteon
username = config[DOMAIN][CONF_USERNAME]
password = config[DOMAIN][CONF_PASSWORD]
api_key = config[DOMAIN][CONF_API_KEY]
global INSTEON
INSTEON = insteon.Insteon(username, password, api_key)
if INSTEON is None:
_LOGGER.error("Could not connect to Insteon service.")
return
comp_name = 'light'
discovery = DISCOVER_LIGHTS
component = get_component(comp_name)
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(
EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery, ATTR_DISCOVERED: {}})
return True
class InsteonToggleDevice(ToggleEntity):
""" Abstract Class for an Insteon node. """
def __init__(self, node):
self.node = node
self._value = 0
@property
def name(self):
""" Returns the name of the node. """
return self.node.DeviceName
@property
def unique_id(self):
""" Returns the id of this insteon node. """
return self.node.DeviceID
def update(self):
""" Update state of the sensor. """
resp = self.node.send_command('get_status', wait=True)
try:
self._value = resp['response']['level']
except KeyError:
pass
@property
def is_on(self):
""" Returns boolean response if the node is on. """
return self._value != 0
def turn_on(self, **kwargs):
self.node.send_command('on')
def turn_off(self, **kwargs):
self.node.send_command('off')
+1 -5
View File
@@ -37,11 +37,7 @@ def setup(hass, config):
Setup ISY994 component.
This will automatically import associated lights, switches, and sensors.
"""
try:
import PyISY
except ImportError:
_LOGGER.error("Error while importing dependency PyISY.")
return False
import PyISY
# pylint: disable=global-statement
# check for required values in configuration file
+1 -9
View File
@@ -6,8 +6,6 @@ Provides functionality to emulate keyboard presses on host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/keyboard/
"""
import logging
from homeassistant.const import (
SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK,
@@ -50,13 +48,7 @@ def media_prev_track(hass):
def setup(hass, config):
""" Listen for keyboard events. """
try:
import pykeyboard
except ImportError:
logging.getLogger(__name__).exception(
"Error while importing dependency PyUserInput.")
return False
import pykeyboard
keyboard = pykeyboard.PyKeyboard()
keyboard.special_key_assignment()
+27 -3
View File
@@ -10,10 +10,12 @@ import logging
import os
import csv
from homeassistant.components import group, discovery, wink, isy994, zwave
from homeassistant.components import (
group, discovery, wink, isy994, zwave, insteon_hub)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
ATTR_ENTITY_ID)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
import homeassistant.util as util
@@ -58,6 +60,7 @@ LIGHT_PROFILES_FILE = "light_profiles.csv"
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_LIGHTS: 'wink',
insteon_hub.DISCOVER_LIGHTS: 'insteon_hub',
isy994.DISCOVER_LIGHTS: 'isy994',
discovery.SERVICE_HUE: 'hue',
zwave.DISCOVER_LIGHTS: 'zwave',
@@ -114,6 +117,18 @@ def turn_off(hass, entity_id=None, transition=None):
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
def toggle(hass, entity_id=None, transition=None):
""" Toggles all or specified light. """
data = {
key: value for key, value in [
(ATTR_ENTITY_ID, entity_id),
(ATTR_TRANSITION, transition),
] if value is not None
}
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
def setup(hass, config):
""" Exposes light control via statemachine and services. """
@@ -165,9 +180,15 @@ def setup(hass, config):
if transition is not None:
params[ATTR_TRANSITION] = transition
service_fun = None
if service.service == SERVICE_TURN_OFF:
service_fun = 'turn_off'
elif service.service == SERVICE_TOGGLE:
service_fun = 'toggle'
if service_fun:
for light in target_lights:
light.turn_off(**params)
getattr(light, service_fun)(**params)
for light in target_lights:
if light.should_poll:
@@ -249,6 +270,9 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service,
descriptions.get(SERVICE_TURN_OFF))
hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_light_service,
descriptions.get(SERVICE_TOGGLE))
return True
@@ -0,0 +1,18 @@
"""
homeassistant.components.light.insteon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Insteon Hub lights.
"""
from homeassistant.components.insteon_hub import (INSTEON, InsteonToggleDevice)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Insteon Hub light platform. """
devs = []
for device in INSTEON.devices:
if device.DeviceCategory == "Switched Lighting Control":
devs.append(InsteonToggleDevice(device))
if device.DeviceCategory == "Dimmable Lighting Control":
devs.append(InsteonToggleDevice(device))
add_devices(devs)
+259
View File
@@ -0,0 +1,259 @@
"""
homeassistant.components.light.lifx
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LIFX platform that implements lights
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.lifx/
"""
# pylint: disable=missing-docstring
import logging
import colorsys
from homeassistant.helpers.event import track_time_change
from homeassistant.components.light import \
(Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_COLOR_TEMP, ATTR_TRANSITION)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['liffylights==0.9.0']
DEPENDENCIES = []
CONF_SERVER = "server" # server address configuration item
CONF_BROADCAST = "broadcast" # broadcast address configuration item
SHORT_MAX = 65535 # short int maximum
BYTE_MAX = 255 # byte maximum
TEMP_MIN = 2500 # lifx minimum temperature
TEMP_MAX = 9000 # lifx maximum temperature
TEMP_MIN_HASS = 154 # home assistant minimum temperature
TEMP_MAX_HASS = 500 # home assistant maximum temperature
class LIFX():
def __init__(self, add_devices_callback,
server_addr=None, broadcast_addr=None):
import liffylights
self._devices = []
self._add_devices_callback = add_devices_callback
self._liffylights = liffylights.LiffyLights(
self.on_device,
self.on_power,
self.on_color,
server_addr,
broadcast_addr)
def find_bulb(self, ipaddr):
bulb = None
for device in self._devices:
if device.ipaddr == ipaddr:
bulb = device
break
return bulb
# pylint: disable=too-many-arguments
def on_device(self, ipaddr, name, power, hue, sat, bri, kel):
bulb = self.find_bulb(ipaddr)
if bulb is None:
bulb = LIFXLight(self._liffylights, ipaddr, name,
power, hue, sat, bri, kel)
self._devices.append(bulb)
self._add_devices_callback([bulb])
# pylint: disable=too-many-arguments
def on_color(self, ipaddr, hue, sat, bri, kel):
bulb = self.find_bulb(ipaddr)
if bulb is not None:
bulb.set_color(hue, sat, bri, kel)
bulb.update_ha_state()
def on_power(self, ipaddr, power):
bulb = self.find_bulb(ipaddr)
if bulb is not None:
bulb.set_power(power)
bulb.update_ha_state()
# pylint: disable=unused-argument
def poll(self, now):
self.probe()
def probe(self, address=None):
self._liffylights.probe(address)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Set up platform. """
server_addr = config.get(CONF_SERVER, None)
broadcast_addr = config.get(CONF_BROADCAST, None)
lifx_library = LIFX(add_devices_callback, server_addr, broadcast_addr)
# register our poll service
track_time_change(hass, lifx_library.poll, second=10)
lifx_library.probe()
def convert_rgb_to_hsv(rgb):
""" Convert HASS RGB values to HSV values. """
red, green, blue = [_ / BYTE_MAX for _ in rgb]
hue, saturation, brightness = colorsys.rgb_to_hsv(red, green, blue)
return [int(hue * SHORT_MAX),
int(saturation * SHORT_MAX),
int(brightness * SHORT_MAX)]
# pylint: disable=too-many-instance-attributes
class LIFXLight(Light):
""" Provides LIFX light. """
# pylint: disable=too-many-arguments
def __init__(self, liffy, ipaddr, name, power, hue,
saturation, brightness, kelvin):
_LOGGER.debug("LIFXLight: %s %s",
ipaddr, name)
self._liffylights = liffy
self._ip = ipaddr
self.set_name(name)
self.set_power(power)
self.set_color(hue, saturation, brightness, kelvin)
@property
def should_poll(self):
""" No polling needed for LIFX light. """
return False
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def ipaddr(self):
""" Returns the ip of the device. """
return self._ip
@property
def rgb_color(self):
""" Returns RGB value. """
_LOGGER.debug("rgb_color: [%d %d %d]",
self._rgb[0], self._rgb[1], self._rgb[2])
return self._rgb
@property
def brightness(self):
""" Returns brightness of this light between 0..255. """
brightness = int(self._bri / (BYTE_MAX + 1))
_LOGGER.debug("brightness: %d",
brightness)
return brightness
@property
def color_temp(self):
""" Returns color temperature. """
temperature = int(TEMP_MIN_HASS + (TEMP_MAX_HASS - TEMP_MIN_HASS) *
(self._kel - TEMP_MIN) / (TEMP_MAX - TEMP_MIN))
_LOGGER.debug("color_temp: %d",
temperature)
return temperature
@property
def is_on(self):
""" True if device is on. """
_LOGGER.debug("is_on: %d",
self._power)
return self._power != 0
def turn_on(self, **kwargs):
""" Turn the device on. """
if ATTR_TRANSITION in kwargs:
fade = kwargs[ATTR_TRANSITION] * 1000
else:
fade = 0
if ATTR_RGB_COLOR in kwargs:
hue, saturation, brightness = \
convert_rgb_to_hsv(kwargs[ATTR_RGB_COLOR])
else:
hue = self._hue
saturation = self._sat
brightness = self._bri
if ATTR_BRIGHTNESS in kwargs:
brightness = kwargs[ATTR_BRIGHTNESS] * (BYTE_MAX + 1)
else:
brightness = self._bri
if ATTR_COLOR_TEMP in kwargs:
kelvin = int(((TEMP_MAX - TEMP_MIN) *
(kwargs[ATTR_COLOR_TEMP] - TEMP_MIN_HASS) /
(TEMP_MAX_HASS - TEMP_MIN_HASS)) + TEMP_MIN)
else:
kelvin = self._kel
_LOGGER.debug("turn_on: %s (%d) %d %d %d %d %d",
self._ip, self._power,
hue, saturation, brightness, kelvin, fade)
if self._power == 0:
self._liffylights.set_power(self._ip, 65535, fade)
self._liffylights.set_color(self._ip, hue, saturation,
brightness, kelvin, fade)
def turn_off(self, **kwargs):
""" Turn the device off. """
if ATTR_TRANSITION in kwargs:
fade = kwargs[ATTR_TRANSITION] * 1000
else:
fade = 0
_LOGGER.debug("turn_off: %s %d",
self._ip, fade)
self._liffylights.set_power(self._ip, 0, fade)
def set_name(self, name):
""" Set name. """
self._name = name
def set_power(self, power):
""" Set power state value. """
_LOGGER.debug("set_power: %d",
power)
self._power = (power != 0)
def set_color(self, hue, sat, bri, kel):
""" Set color state values. """
self._hue = hue
self._sat = sat
self._bri = bri
self._kel = kel
red, green, blue = colorsys.hsv_to_rgb(hue / SHORT_MAX,
sat / SHORT_MAX,
bri / SHORT_MAX)
red = int(red * BYTE_MAX)
green = int(green * BYTE_MAX)
blue = int(blue * BYTE_MAX)
_LOGGER.debug("set_color: %d %d %d %d [%d %d %d]",
hue, sat, bri, kel, red, green, blue)
self._rgb = [red, green, blue]
+17 -2
View File
@@ -9,7 +9,7 @@ https://home-assistant.io/components/light.rfxtrx/
import logging
import homeassistant.components.rfxtrx as rfxtrx
from homeassistant.components.light import Light
from homeassistant.components.light import Light, ATTR_BRIGHTNESS
from homeassistant.util import slugify
from homeassistant.const import ATTR_ENTITY_ID
@@ -112,6 +112,7 @@ class RfxtrxLight(Light):
self._event = event
self._state = datas[ATTR_STATE]
self._should_fire_event = datas[ATTR_FIREEVENT]
self._brightness = 0
@property
def should_poll(self):
@@ -133,12 +134,25 @@ class RfxtrxLight(Light):
""" True if light is on. """
return self._state
@property
def brightness(self):
""" Brightness of this light between 0..255. """
return self._brightness
def turn_on(self, **kwargs):
""" Turn the light on. """
brightness = kwargs.get(ATTR_BRIGHTNESS)
if brightness is None:
self._brightness = 100
else:
self._brightness = ((brightness + 4) * 100 // 255 - 1)
if hasattr(self, '_event') and self._event:
self._event.device.send_on(rfxtrx.RFXOBJECT.transport)
self._event.device.send_dim(rfxtrx.RFXOBJECT.transport,
self._brightness)
self._brightness = (self._brightness * 255 // 100)
self._state = True
self.update_ha_state()
@@ -148,5 +162,6 @@ class RfxtrxLight(Light):
if hasattr(self, '_event') and self._event:
self._event.device.send_off(rfxtrx.RFXOBJECT.transport)
self._brightness = 0
self._state = False
self.update_ha_state()
@@ -55,3 +55,15 @@ turn_off:
transition:
description: Duration in seconds it takes to get to next state
example: 60
toggle:
description: Toggles a light
fields:
entity_id:
description: Name(s) of entities to toggle
example: 'light.kitchen'
transition:
description: Duration in seconds it takes to get to next state
example: 60
+1 -1
View File
@@ -12,7 +12,7 @@ from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.components.wink import WinkToggleDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.4.1']
REQUIREMENTS = ['python-wink==0.4.2']
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
+29
View File
@@ -0,0 +1,29 @@
"""
homeassistant.components.light.zigbee
Contains functionality to use a ZigBee device as a light.
"""
from homeassistant.components.light import Light
from homeassistant.components.zigbee import (
ZigBeeDigitalOut, ZigBeeDigitalOutConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
add_entities([
ZigBeeLight(hass, ZigBeeDigitalOutConfig(config))
])
class ZigBeeLight(ZigBeeDigitalOut, Light):
"""
Use multiple inheritance to turn an instance of ZigBeeDigitalOut into a
Light.
"""
pass
+13 -24
View File
@@ -11,8 +11,10 @@ https://home-assistant.io/components/light.zwave/
from threading import Timer
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS)
import homeassistant.components.zwave as zwave
from homeassistant.components.light import Light, ATTR_BRIGHTNESS, DOMAIN
from homeassistant.components.zwave import (
COMMAND_CLASS_SWITCH_MULTILEVEL, TYPE_BYTE, GENRE_USER, NETWORK,
ATTR_NODE_ID, ATTR_VALUE_ID, ZWaveDeviceEntity)
def setup_platform(hass, config, add_devices, discovery_info=None):
@@ -20,14 +22,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if discovery_info is None:
return
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL:
if value.command_class != COMMAND_CLASS_SWITCH_MULTILEVEL:
return
if value.type != zwave.TYPE_BYTE:
if value.type != TYPE_BYTE:
return
if value.genre != zwave.GENRE_USER:
if value.genre != GENRE_USER:
return
value.set_change_verified(False)
@@ -45,15 +47,14 @@ def brightness_state(value):
return 255, STATE_OFF
class ZwaveDimmer(Light):
class ZwaveDimmer(ZWaveDeviceEntity, Light):
""" Provides a Z-Wave dimmer. """
# pylint: disable=too-many-arguments
def __init__(self, value):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
self._value = value
self._node = value.node
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._brightness, self._state = brightness_state(value)
@@ -86,18 +87,6 @@ class ZwaveDimmer(Light):
self.update_ha_state()
@property
def should_poll(self):
""" No polling needed for a light. """
return False
@property
def name(self):
""" Returns the name of the device if any. """
name = self._node.name or "{}".format(self._node.product_name)
return "{}".format(name or self._value.label)
@property
def brightness(self):
""" Brightness of this light between 0..255. """
@@ -118,10 +107,10 @@ class ZwaveDimmer(Light):
# brightness.
brightness = (self._brightness / 255) * 99
if self._node.set_dimmer(self._value.value_id, brightness):
if self._value.node.set_dimmer(self._value.value_id, brightness):
self._state = STATE_ON
def turn_off(self, **kwargs):
""" Turn the device off. """
if self._node.set_dimmer(self._value.value_id, 0):
if self._value.node.set_dimmer(self._value.value_id, 0):
self._state = STATE_OFF
+1 -1
View File
@@ -11,7 +11,7 @@ import logging
from homeassistant.components.lock import LockDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.4.1']
REQUIREMENTS = ['python-wink==0.4.2']
def setup_platform(hass, config, add_devices, discovery_info=None):
+21 -4
View File
@@ -6,6 +6,7 @@ Parses events and generates a human log.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/logbook/
"""
import logging
from datetime import timedelta
from itertools import groupby
import re
@@ -14,10 +15,10 @@ from homeassistant.core import State, DOMAIN as HA_DOMAIN
from homeassistant.const import (
EVENT_STATE_CHANGED, STATE_NOT_HOME, STATE_ON, STATE_OFF,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, HTTP_BAD_REQUEST)
from homeassistant import util
import homeassistant.util.dt as dt_util
from homeassistant.components import recorder, sun
from homeassistant.helpers.entity import split_entity_id
from homeassistant.util import template
DOMAIN = "logbook"
DEPENDENCIES = ['recorder', 'http']
@@ -28,6 +29,8 @@ QUERY_EVENTS_BETWEEN = """
SELECT * FROM events WHERE time_fired > ? AND time_fired < ?
"""
_LOGGER = logging.getLogger(__name__)
EVENT_LOGBOOK_ENTRY = 'logbook_entry'
GROUP_BY_MINUTES = 15
@@ -54,8 +57,22 @@ def log_entry(hass, name, message, domain=None, entity_id=None):
def setup(hass, config):
""" Listens for download events to download files. """
hass.http.register_path('GET', URL_LOGBOOK, _handle_get_logbook)
# create service handler
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 = 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)
return True
@@ -209,7 +226,7 @@ def humanify(events):
entity_id = event.data.get(ATTR_ENTITY_ID)
if domain is None and entity_id is not None:
try:
domain = util.split_entity_id(str(entity_id))[0]
domain = split_entity_id(str(entity_id))[0]
except IndexError:
pass
@@ -14,10 +14,10 @@ from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.const import (
STATE_OFF, STATE_UNKNOWN, STATE_PLAYING,
STATE_OFF, STATE_UNKNOWN, STATE_PLAYING, STATE_IDLE,
ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE, SERVICE_TURN_OFF, SERVICE_TURN_ON,
SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_SET,
SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_MUTE, SERVICE_TOGGLE,
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK)
@@ -79,6 +79,7 @@ YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
SERVICE_TO_METHOD = {
SERVICE_TURN_ON: 'turn_on',
SERVICE_TURN_OFF: 'turn_off',
SERVICE_TOGGLE: 'toggle',
SERVICE_VOLUME_UP: 'volume_up',
SERVICE_VOLUME_DOWN: 'volume_down',
SERVICE_MEDIA_PLAY_PAUSE: 'media_play_pause',
@@ -131,6 +132,12 @@ def turn_off(hass, entity_id=None):
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
def toggle(hass, entity_id=None):
""" Will toggle specified media player or all. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
def volume_up(hass, entity_id=None):
""" Send the media player the command for volume up. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
@@ -532,6 +539,13 @@ class MediaPlayerDevice(Entity):
""" Boolean if play media command supported. """
return bool(self.supported_media_commands & SUPPORT_PLAY_MEDIA)
def toggle(self):
""" Toggles the power on the media player. """
if self.state in [STATE_OFF, STATE_IDLE]:
self.turn_on()
else:
self.turn_off()
def volume_up(self):
""" volume_up media player. """
if self.volume_level < 1:
@@ -20,7 +20,7 @@ from homeassistant.components.media_player import (
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK,
MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
REQUIREMENTS = ['pychromecast==0.6.14']
REQUIREMENTS = ['pychromecast==0.7.1']
CONF_IGNORE_CEC = 'ignore_cec'
CAST_SPLASH = 'https://home-assistant.io/images/cast/splash.png'
SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
+13 -20
View File
@@ -14,8 +14,7 @@ from homeassistant.components.media_player import (
MediaPlayerDevice, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, SUPPORT_PAUSE,
SUPPORT_SEEK, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON,
SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA,
ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_MEDIA_COMMANDS)
SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA)
from homeassistant.const import (
STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_ON)
@@ -40,7 +39,10 @@ class Itunes(object):
@property
def _base_url(self):
""" Returns the base url for endpoints. """
return self.host + ":" + str(self.port)
if self.port:
return self.host + ":" + str(self.port)
else:
return self.host
def _request(self, method, path, params=None):
""" Makes the actual request and returns the parsed response. """
@@ -380,6 +382,14 @@ class AirPlayDevice(MediaPlayerDevice):
""" Returns the name of the device. """
return self.device_name
@property
def icon(self):
""" Icon to use in the frontend, if any. """
if self.selected is True:
return "mdi:volume-high"
else:
return "mdi:volume-off"
@property
def state(self):
""" Returns the state of the device. """
@@ -405,23 +415,6 @@ class AirPlayDevice(MediaPlayerDevice):
""" Flags of media commands that are supported. """
return SUPPORT_AIRPLAY
@property
def device_state_attributes(self):
""" Return the state attributes. """
state_attr = {}
state_attr[ATTR_SUPPORTED_MEDIA_COMMANDS] = SUPPORT_AIRPLAY
if self.state == STATE_OFF:
state_attr[ATTR_ENTITY_PICTURE] = \
('https://cloud.githubusercontent.com/assets/260/9833073'
'/6eb5c906-5958-11e5-9b4a-472cdf36be16.png')
else:
state_attr[ATTR_ENTITY_PICTURE] = \
('https://cloud.githubusercontent.com/assets/260/9833072'
'/6eb13cce-5958-11e5-996f-e2aaefbc9a24.png')
return state_attr
def set_volume_level(self, volume):
""" set volume level, range 0..1. """
volume = int(volume * 100)
@@ -72,7 +72,8 @@ class KodiDevice(MediaPlayerDevice):
try:
return self._server.Player.GetActivePlayers()
except jsonrpc_requests.jsonrpc.TransportError:
_LOGGER.exception('Unable to fetch kodi data')
_LOGGER.warning('Unable to fetch kodi data')
_LOGGER.debug('Unable to fetch kodi data', exc_info=True)
return None
@property
+4 -9
View File
@@ -9,11 +9,6 @@ https://home-assistant.io/components/media_player.mpd/
import logging
import socket
try:
import mpd
except ImportError:
mpd = None
from homeassistant.const import (
STATE_PLAYING, STATE_PAUSED, STATE_OFF)
@@ -40,10 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
location = config.get('location', 'MPD')
password = config.get('password', None)
global mpd # pylint: disable=invalid-name
if mpd is None:
import mpd as mpd_
mpd = mpd_
import mpd
# pylint: disable=no-member
try:
@@ -82,6 +74,8 @@ class MpdDevice(MediaPlayerDevice):
# pylint: disable=no-member, abstract-method
def __init__(self, server, port, location, password):
import mpd
self.server = server
self.port = port
self._name = location
@@ -95,6 +89,7 @@ class MpdDevice(MediaPlayerDevice):
self.update()
def update(self):
import mpd
try:
self.status = self.client.status()
self.currentsong = self.client.currentsong()
@@ -112,7 +112,7 @@ def setup_plexserver(host, token, hass, add_devices_callback):
{host: {'token': token}}):
_LOGGER.error('failed to save config file')
_LOGGER.info('Connected to: htts://%s', host)
_LOGGER.info('Connected to: http://%s', host)
plex_clients = {}
plex_sessions = {}
+12 -1
View File
@@ -38,12 +38,23 @@ SUPPORT_SONOS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Sonos platform. """
import soco
import socket
if discovery_info:
add_devices([SonosDevice(hass, soco.SoCo(discovery_info))])
return True
players = soco.discover()
players = None
hosts = config.get('hosts', None)
if hosts:
players = []
for host in hosts.split(","):
host = socket.gethostbyname(host)
players.append(soco.SoCo(host))
if not players:
players = soco.discover(interface_addr=config.get('interface_addr',
None))
if not players:
_LOGGER.warning('No Sonos speakers found.')
@@ -201,11 +201,19 @@ class SqueezeBoxDevice(MediaPlayerDevice):
def media_image_url(self):
""" Image url of current playing media. """
if 'artwork_url' in self._status:
return self._status['artwork_url']
return ('http://{server}:{port}/music/current/cover.jpg?'
'player={player}').format(server=self._lms.host,
port=self._lms.http_port,
player=self._id)
media_url = self._status['artwork_url']
elif 'id' in self._status:
media_url = ('/music/{track_id}/cover.jpg').format(
track_id=self._status['id'])
else:
media_url = ('/music/current/cover.jpg?player={player}').format(
player=self._id)
base_url = 'http://{server}:{port}/'.format(
server=self._lms.host,
port=self._lms.http_port)
return urllib.parse.urljoin(base_url, media_url)
@property
def media_title(self):
+125 -102
View File
@@ -24,11 +24,6 @@ DOMAIN = "mqtt"
MQTT_CLIENT = None
DEFAULT_PORT = 1883
DEFAULT_KEEPALIVE = 60
DEFAULT_QOS = 0
DEFAULT_RETAIN = False
SERVICE_PUBLISH = 'publish'
EVENT_MQTT_MESSAGE_RECEIVED = 'mqtt_message_received'
@@ -41,6 +36,16 @@ CONF_KEEPALIVE = 'keepalive'
CONF_USERNAME = 'username'
CONF_PASSWORD = 'password'
CONF_CERTIFICATE = 'certificate'
CONF_PROTOCOL = 'protocol'
PROTOCOL_31 = '3.1'
PROTOCOL_311 = '3.1.1'
DEFAULT_PORT = 1883
DEFAULT_KEEPALIVE = 60
DEFAULT_QOS = 0
DEFAULT_RETAIN = False
DEFAULT_PROTOCOL = PROTOCOL_311
ATTR_TOPIC = 'topic'
ATTR_PAYLOAD = 'payload'
@@ -51,7 +56,7 @@ MAX_RECONNECT_WAIT = 300 # seconds
def publish(hass, topic, payload, qos=None, retain=None):
""" Send an MQTT message. """
"""Publish message to an MQTT topic."""
data = {
ATTR_TOPIC: topic,
ATTR_PAYLOAD: payload,
@@ -66,9 +71,9 @@ def publish(hass, topic, payload, qos=None, retain=None):
def subscribe(hass, topic, callback, qos=DEFAULT_QOS):
""" Subscribe to a topic. """
"""Subscribe to an MQTT topic."""
def mqtt_topic_subscriber(event):
""" Match subscribed MQTT topic. """
"""Match subscribed MQTT topic."""
if _match_topic(topic, event.data[ATTR_TOPIC]):
callback(event.data[ATTR_TOPIC], event.data[ATTR_PAYLOAD],
event.data[ATTR_QOS])
@@ -78,8 +83,7 @@ def subscribe(hass, topic, callback, qos=DEFAULT_QOS):
def setup(hass, config):
""" Get the MQTT protocol service. """
"""Start the MQTT protocol service."""
if not validate_config(config, {DOMAIN: ['broker']}, _LOGGER):
return False
@@ -92,6 +96,12 @@ def setup(hass, config):
username = util.convert(conf.get(CONF_USERNAME), str)
password = util.convert(conf.get(CONF_PASSWORD), str)
certificate = util.convert(conf.get(CONF_CERTIFICATE), str)
protocol = util.convert(conf.get(CONF_PROTOCOL), str, DEFAULT_PROTOCOL)
if protocol not in (PROTOCOL_31, PROTOCOL_311):
_LOGGER.error('Invalid protocol specified: %s. Allowed values: %s, %s',
protocol, PROTOCOL_31, PROTOCOL_311)
return False
# For cloudmqtt.com, secured connection, auto fill in certificate
if certificate is None and 19999 < port < 30000 and \
@@ -102,7 +112,7 @@ def setup(hass, config):
global MQTT_CLIENT
try:
MQTT_CLIENT = MQTT(hass, broker, port, client_id, keepalive, username,
password, certificate)
password, certificate, protocol)
except socket.error:
_LOGGER.exception("Can't connect to the broker. "
"Please check your settings and the broker "
@@ -110,16 +120,16 @@ def setup(hass, config):
return False
def stop_mqtt(event):
""" Stop MQTT component. """
"""Stop MQTT component."""
MQTT_CLIENT.stop()
def start_mqtt(event):
""" Launch MQTT component when Home Assistant starts up. """
"""Launch MQTT component when Home Assistant starts up."""
MQTT_CLIENT.start()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_mqtt)
def publish_service(call):
""" Handle MQTT publish service calls. """
"""Handle MQTT publish service calls."""
msg_topic = call.data.get(ATTR_TOPIC)
payload = call.data.get(ATTR_PAYLOAD)
qos = call.data.get(ATTR_QOS, DEFAULT_QOS)
@@ -137,148 +147,161 @@ def setup(hass, config):
# pylint: disable=too-many-arguments
class MQTT(object):
""" Implements messaging service for MQTT. """
"""Home Assistant MQTT client."""
def __init__(self, hass, broker, port, client_id, keepalive, username,
password, certificate):
password, certificate, protocol):
"""Initialize Home Assistant MQTT client."""
import paho.mqtt.client as mqtt
self.userdata = {
'hass': hass,
'topics': {},
'progress': {},
}
self.hass = hass
self.topics = {}
self.progress = {}
if protocol == PROTOCOL_31:
proto = mqtt.MQTTv31
else:
proto = mqtt.MQTTv311
if client_id is None:
self._mqttc = mqtt.Client(protocol=mqtt.MQTTv311)
self._mqttc = mqtt.Client(protocol=proto)
else:
self._mqttc = mqtt.Client(client_id, protocol=mqtt.MQTTv311)
self._mqttc.user_data_set(self.userdata)
self._mqttc = mqtt.Client(client_id, protocol=proto)
if username is not None:
self._mqttc.username_pw_set(username, password)
if certificate is not None:
self._mqttc.tls_set(certificate)
self._mqttc.on_subscribe = _mqtt_on_subscribe
self._mqttc.on_unsubscribe = _mqtt_on_unsubscribe
self._mqttc.on_connect = _mqtt_on_connect
self._mqttc.on_disconnect = _mqtt_on_disconnect
self._mqttc.on_message = _mqtt_on_message
self._mqttc.on_subscribe = self._mqtt_on_subscribe
self._mqttc.on_unsubscribe = self._mqtt_on_unsubscribe
self._mqttc.on_connect = self._mqtt_on_connect
self._mqttc.on_disconnect = self._mqtt_on_disconnect
self._mqttc.on_message = self._mqtt_on_message
self._mqttc.connect(broker, port, keepalive)
def publish(self, topic, payload, qos, retain):
""" Publish a MQTT message. """
"""Publish a MQTT message."""
self._mqttc.publish(topic, payload, qos, retain)
def start(self):
""" Run the MQTT client. """
"""Run the MQTT client."""
self._mqttc.loop_start()
def stop(self):
""" Stop the MQTT client. """
"""Stop the MQTT client."""
self._mqttc.disconnect()
self._mqttc.loop_stop()
def subscribe(self, topic, qos):
""" Subscribe to a topic. """
if topic in self.userdata['topics']:
"""Subscribe to a topic."""
assert isinstance(topic, str)
if topic in self.topics:
return
result, mid = self._mqttc.subscribe(topic, qos)
_raise_on_error(result)
self.userdata['progress'][mid] = topic
self.userdata['topics'][topic] = None
self.progress[mid] = topic
self.topics[topic] = None
def unsubscribe(self, topic):
""" Unsubscribe from topic. """
"""Unsubscribe from topic."""
result, mid = self._mqttc.unsubscribe(topic)
_raise_on_error(result)
self.userdata['progress'][mid] = topic
self.progress[mid] = topic
def _mqtt_on_connect(self, _mqttc, _userdata, _flags, result_code):
"""On connect callback.
def _mqtt_on_message(mqttc, userdata, msg):
""" Message callback """
userdata['hass'].bus.fire(EVENT_MQTT_MESSAGE_RECEIVED, {
ATTR_TOPIC: msg.topic,
ATTR_QOS: msg.qos,
ATTR_PAYLOAD: msg.payload.decode('utf-8'),
})
Resubscribe to all topics we were subscribed to.
"""
if result_code != 0:
_LOGGER.error('Unable to connect to the MQTT broker: %s', {
1: 'Incorrect protocol version',
2: 'Invalid client identifier',
3: 'Server unavailable',
4: 'Bad username or password',
5: 'Not authorised'
}.get(result_code, 'Unknown reason'))
self._mqttc.disconnect()
return
old_topics = self.topics
def _mqtt_on_connect(mqttc, userdata, flags, result_code):
""" On connect, resubscribe to all topics we were subscribed to. """
if result_code != 0:
_LOGGER.error('Unable to connect to the MQTT broker: %s', {
1: 'Incorrect protocol version',
2: 'Invalid client identifier',
3: 'Server unavailable',
4: 'Bad username or password',
5: 'Not authorised'
}.get(result_code, 'Unknown reason'))
mqttc.disconnect()
return
self.topics = {key: value for key, value in self.topics.items()
if value is None}
old_topics = userdata['topics']
for topic, qos in old_topics.items():
# qos is None if we were in process of subscribing
if qos is not None:
self.subscribe(topic, qos)
userdata['topics'] = {}
userdata['progress'] = {}
def _mqtt_on_subscribe(self, _mqttc, _userdata, mid, granted_qos):
"""Subscribe successful callback."""
topic = self.progress.pop(mid, None)
if topic is None:
return
self.topics[topic] = granted_qos[0]
for topic, qos in old_topics.items():
# qos is None if we were in process of subscribing
if qos is not None:
mqttc.subscribe(topic, qos)
def _mqtt_on_message(self, _mqttc, _userdata, msg):
"""Message received callback."""
self.hass.bus.fire(EVENT_MQTT_MESSAGE_RECEIVED, {
ATTR_TOPIC: msg.topic,
ATTR_QOS: msg.qos,
ATTR_PAYLOAD: msg.payload.decode('utf-8'),
})
def _mqtt_on_unsubscribe(self, _mqttc, _userdata, mid, granted_qos):
"""Unsubscribe successful callback."""
topic = self.progress.pop(mid, None)
if topic is None:
return
self.topics.pop(topic, None)
def _mqtt_on_subscribe(mqttc, userdata, mid, granted_qos):
""" Called when subscribe successful. """
topic = userdata['progress'].pop(mid, None)
if topic is None:
return
userdata['topics'][topic] = granted_qos
def _mqtt_on_disconnect(self, _mqttc, _userdata, result_code):
"""Disconnected callback."""
self.progress = {}
self.topics = {key: value for key, value in self.topics.items()
if value is not None}
# Remove None values from topic list
for key in list(self.topics):
if self.topics[key] is None:
self.topics.pop(key)
def _mqtt_on_unsubscribe(mqttc, userdata, mid, granted_qos):
""" Called when subscribe successful. """
topic = userdata['progress'].pop(mid, None)
if topic is None:
return
userdata['topics'].pop(topic, None)
# When disconnected because of calling disconnect()
if result_code == 0:
return
tries = 0
wait_time = 0
def _mqtt_on_disconnect(mqttc, userdata, result_code):
""" Called when being disconnected. """
# When disconnected because of calling disconnect()
if result_code == 0:
return
while True:
try:
if self._mqttc.reconnect() == 0:
_LOGGER.info('Successfully reconnected to the MQTT server')
break
except socket.error:
pass
tries = 0
wait_time = 0
while True:
try:
if mqttc.reconnect() == 0:
_LOGGER.info('Successfully reconnected to the MQTT server')
break
except socket.error:
pass
wait_time = min(2**tries, MAX_RECONNECT_WAIT)
_LOGGER.warning(
'Disconnected from MQTT (%s). Trying to reconnect in %ss',
result_code, wait_time)
# It is ok to sleep here as we are in the MQTT thread.
time.sleep(wait_time)
tries += 1
wait_time = min(2**tries, MAX_RECONNECT_WAIT)
_LOGGER.warning(
'Disconnected from MQTT (%s). Trying to reconnect in %ss',
result_code, wait_time)
# It is ok to sleep here as we are in the MQTT thread.
time.sleep(wait_time)
tries += 1
def _raise_on_error(result):
""" Raise error if error result. """
"""Raise error if error result."""
if result != 0:
raise HomeAssistantError('Error talking to MQTT: {}'.format(result))
def _match_topic(subscription, topic):
""" Returns if topic matches subscription. """
"""Test if topic matches subscription."""
if subscription.endswith('#'):
return (subscription[:-2] == topic or
topic.startswith(subscription[:-1]))
+5 -15
View File
@@ -1,20 +1,10 @@
"""
homeassistant.components.mqtt_eventstream
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Connect two Home Assistant instances via mqtt.
Connect two Home Assistant instances via MQTT..
Configuration:
To use the mqtt_eventstream component you will need to add the following to
your configuration.yaml file.
If you do not specify a publish_topic you will not forward events to the queue.
If you do not specify a subscribe_topic then you will not receive events from
the remote server.
mqtt_eventstream:
publish_topic: MyServerName
subscribe_topic: OtherHaServerName
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/mqtt_eventstream.html
"""
import json
from homeassistant.core import EventOrigin, State
@@ -38,13 +28,13 @@ DEPENDENCIES = ['mqtt']
def setup(hass, config):
""" Setup our mqtt_eventstream component. """
""" Setup th MQTT eventstream component. """
mqtt = loader.get_component('mqtt')
pub_topic = config[DOMAIN].get('publish_topic', None)
sub_topic = config[DOMAIN].get('subscribe_topic', None)
def _event_publisher(event):
""" Handle events by publishing them on the mqtt queue. """
""" Handle events by publishing them on the MQTT queue. """
if event.origin != EventOrigin.local:
return
if event.event_type == EVENT_TIME_CHANGED:
+17 -45
View File
@@ -1,32 +1,11 @@
"""
homeassistant.components.mysensors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MySensors component that connects to a MySensors gateway via pymysensors
API.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors.html
New features:
New MySensors component.
Updated MySensors Sensor platform.
New MySensors Switch platform. Currently only in optimistic mode (compare
with MQTT).
Multiple gateways are now supported.
Configuration.yaml:
mysensors:
gateways:
- port: '/dev/ttyUSB0'
persistence_file: 'path/mysensors.json'
- port: '/dev/ttyACM1'
persistence_file: 'path/mysensors2.json'
debug: true
persistence: true
version: '1.5'
https://home-assistant.io/components/sensor.mysensors/
"""
import logging
@@ -58,7 +37,6 @@ ATTR_CHILD_ID = 'child_id'
ATTR_PORT = 'port'
GATEWAYS = None
SCAN_INTERVAL = 30
DISCOVER_SENSORS = "mysensors.sensors"
DISCOVER_SWITCHES = "mysensors.switches"
@@ -72,8 +50,6 @@ DISCOVERY_COMPONENTS = [
def setup(hass, config):
"""Setup the MySensors component."""
# pylint: disable=too-many-locals
if not validate_config(config,
{DOMAIN: [CONF_GATEWAYS]},
_LOGGER):
@@ -137,36 +113,32 @@ def setup(hass, config):
return True
def pf_callback_factory(
s_types, v_types, devices, add_devices, entity_class):
def pf_callback_factory(map_sv_types, devices, add_devices, entity_class):
"""Return a new callback for the platform."""
def mysensors_callback(gateway, node_id):
"""Callback for mysensors platform."""
if gateway.sensors[node_id].sketch_name is None:
_LOGGER.info('No sketch_name: node %s', node_id)
return
# previously discovered, just update state with latest info
if node_id in devices:
for entity in devices[node_id]:
entity.update_ha_state(True)
return
# First time we see this node, detect sensors
for child in gateway.sensors[node_id].children.values():
name = '{} {}.{}'.format(
gateway.sensors[node_id].sketch_name, node_id, child.id)
for value_type in child.values.keys():
if child.type not in s_types or value_type not in v_types:
key = node_id, child.id, value_type
if child.type not in map_sv_types or \
value_type not in map_sv_types[child.type]:
continue
if key in devices:
devices[key].update_ha_state(True)
continue
name = '{} {}.{}'.format(
gateway.sensors[node_id].sketch_name, node_id, child.id)
devices[key] = entity_class(
gateway, node_id, child.id, name, value_type)
devices[node_id].append(
entity_class(gateway, node_id, child.id, name, value_type))
if devices[node_id]:
_LOGGER.info('adding new devices: %s', devices[node_id])
add_devices(devices[node_id])
for entity in devices[node_id]:
entity.update_ha_state(True)
_LOGGER.info('Adding new devices: %s', devices[key])
add_devices([devices[key]])
if key in devices:
devices[key].update_ha_state(True)
return mysensors_callback
+37
View File
@@ -0,0 +1,37 @@
"""
homeassistant.components.thermostat.nest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adds 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
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD)
REQUIREMENTS = ['python-nest==2.6.0']
DOMAIN = 'nest'
NEST = None
# pylint: disable=unused-argument
def setup(hass, config):
""" Sets up the nest thermostat. """
global NEST
logger = logging.getLogger(__name__)
username = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
if username is None or password is None:
logger.error("Missing required configuration items %s or %s",
CONF_USERNAME, CONF_PASSWORD)
return
import nest
NEST = nest.Nest(username, password)
return True
@@ -0,0 +1,61 @@
"""
homeassistant.components.notify.googlevoice
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Google Voice SMS platform for notify component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.free_mobile/
"""
import logging
from homeassistant.helpers import validate_config
from homeassistant.components.notify import (
DOMAIN, ATTR_TARGET, BaseNotificationService)
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/w1ll1am23/pygooglevoice-sms/archive/'
'7c5ee9969b97a7992fc86a753fe9f20e3ffa3f7c.zip#'
'pygooglevoice-sms==0.0.1']
def get_service(hass, config):
""" Get the Google Voice SMS notification service. """
if not validate_config({DOMAIN: config},
{DOMAIN: [CONF_USERNAME,
CONF_PASSWORD]},
_LOGGER):
return None
return GoogleVoiceSMSNotificationService(config[CONF_USERNAME],
config[CONF_PASSWORD])
# pylint: disable=too-few-public-methods
class GoogleVoiceSMSNotificationService(BaseNotificationService):
""" Implements notification service for the Google Voice SMS service. """
def __init__(self, username, password):
from googlevoicesms import Voice
self.voice = Voice()
self.username = username
self.password = password
def send_message(self, message="", **kwargs):
""" Send SMS to specified target user cell. """
targets = kwargs.get(ATTR_TARGET)
if not targets:
_LOGGER.info('At least 1 target is required')
return
if not isinstance(targets, list):
targets = [targets]
self.voice.login(self.username, self.password)
for target in targets:
self.voice.send_sms(target, message)
self.voice.logout()
+36 -30
View File
@@ -21,37 +21,38 @@ def get_service(hass, config):
""" Get the mail notification service. """
if not validate_config({DOMAIN: config},
{DOMAIN: ['server', 'port', 'sender', 'username',
'password', 'recipient']},
{DOMAIN: ['recipient']},
_LOGGER):
return None
smtp_server = config['server']
port = int(config['port'])
username = config['username']
password = config['password']
starttls = int(config['starttls'])
smtp_server = config.get('server', 'localhost')
port = int(config.get('port', '25'))
username = config.get('username', None)
password = config.get('password', None)
starttls = int(config.get('starttls', 0))
debug = config.get('debug', 0)
server = None
try:
server = smtplib.SMTP(smtp_server, port)
server = smtplib.SMTP(smtp_server, port, timeout=5)
server.set_debuglevel(debug)
server.ehlo()
if starttls == 1:
server.starttls()
server.ehlo()
if username and password:
try:
server.login(username, password)
try:
server.login(username, password)
except (smtplib.SMTPException, smtplib.SMTPSenderRefused):
_LOGGER.exception("Please check your settings.")
return None
except (smtplib.SMTPException, smtplib.SMTPSenderRefused):
_LOGGER.exception("Please check your settings.")
return None
except smtplib.socket.gaierror:
_LOGGER.exception(
"SMTP server not found. "
"Please check the IP address or hostname of your SMTP server.")
"SMTP server not found (%s:%s). "
"Please check the IP address or hostname of your SMTP server.",
smtp_server, port)
return None
@@ -68,7 +69,7 @@ def get_service(hass, config):
return MailNotificationService(
smtp_server, port, config['sender'], starttls, username, password,
config['recipient'])
config['recipient'], debug)
# pylint: disable=too-few-public-methods, too-many-instance-attributes
@@ -77,7 +78,7 @@ class MailNotificationService(BaseNotificationService):
# pylint: disable=too-many-arguments
def __init__(self, server, port, sender, starttls, username,
password, recipient):
password, recipient, debug):
self._server = server
self._port = port
self._sender = sender
@@ -85,24 +86,26 @@ class MailNotificationService(BaseNotificationService):
self.username = username
self.password = password
self.recipient = recipient
self.debug = debug
self.tries = 2
self.mail = None
self.connect()
def connect(self):
""" Connect/Authenticate to SMTP Server """
self.mail = smtplib.SMTP(self._server, self._port)
self.mail.ehlo_or_helo_if_needed()
mail = smtplib.SMTP(self._server, self._port, timeout=5)
mail.set_debuglevel(self.debug)
mail.ehlo_or_helo_if_needed()
if self.starttls == 1:
self.mail.starttls()
self.mail.ehlo()
self.mail.login(self.username, self.password)
mail.starttls()
mail.ehlo()
if self.username and self.password:
mail.login(self.username, self.password)
return mail
def send_message(self, message="", **kwargs):
""" Send a message to a user. """
mail = self.connect()
subject = kwargs.get(ATTR_TITLE)
msg = MIMEText(message)
@@ -113,10 +116,13 @@ class MailNotificationService(BaseNotificationService):
for _ in range(self.tries):
try:
self.mail.sendmail(self._sender, self.recipient,
msg.as_string())
mail.sendmail(self._sender, self.recipient,
msg.as_string())
break
except smtplib.SMTPException:
_LOGGER.warning('SMTPException sending mail: '
'retrying connection')
self.connect()
mail.quit()
mail = self.connect()
mail.quit()
+1 -1
View File
@@ -16,7 +16,7 @@ from homeassistant.const import CONF_API_KEY
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['python-telegram-bot==2.8.7']
REQUIREMENTS = ['python-telegram-bot==3.2.0']
def get_service(hass, config):
@@ -0,0 +1,59 @@
"""
homeassistant.components.notify.twitter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Twitter platform for notify component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.twitter/
"""
import logging
from homeassistant.helpers import validate_config
from homeassistant.components.notify import (
DOMAIN, BaseNotificationService)
from homeassistant.const import CONF_ACCESS_TOKEN
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['TwitterAPI==2.3.6']
CONF_CONSUMER_KEY = "consumer_key"
CONF_CONSUMER_SECRET = "consumer_secret"
CONF_ACCESS_TOKEN_SECRET = "access_token_secret"
def get_service(hass, config):
""" Get the Twitter notification service. """
if not validate_config({DOMAIN: config},
{DOMAIN: [CONF_CONSUMER_KEY, CONF_CONSUMER_SECRET,
CONF_ACCESS_TOKEN,
CONF_ACCESS_TOKEN_SECRET]},
_LOGGER):
return None
return TwitterNotificationService(config[CONF_CONSUMER_KEY],
config[CONF_CONSUMER_SECRET],
config[CONF_ACCESS_TOKEN],
config[CONF_ACCESS_TOKEN_SECRET])
# pylint: disable=too-few-public-methods
class TwitterNotificationService(BaseNotificationService):
""" Implements notification service for the Twitter service. """
def __init__(self, consumer_key, consumer_secret, access_token_key,
access_token_secret):
from TwitterAPI import TwitterAPI
self.api = TwitterAPI(consumer_key, consumer_secret, access_token_key,
access_token_secret)
def send_message(self, message="", **kwargs):
""" Tweet some message. """
resp = self.api.request('statuses/update', {'status': message})
if resp.status_code != 200:
import json
obj = json.loads(resp.text)
error_message = obj['errors'][0]['message']
error_code = obj['errors'][0]['code']
_LOGGER.error("Error %s : %s (Code %s)", resp.status_code,
error_message,
error_code)
+2 -10
View File
@@ -48,11 +48,7 @@ def setup(hass, config):
subscriber(event)
# Try to load the RFXtrx module
try:
import RFXtrx as rfxtrxmod
except ImportError:
_LOGGER.exception("Failed to import rfxtrx")
return False
import RFXtrx as rfxtrxmod
# Init the rfxtrx module
global RFXOBJECT
@@ -74,11 +70,7 @@ def setup(hass, config):
def get_rfx_object(packetid):
""" Return the RFXObject with the packetid. """
try:
import RFXtrx as rfxtrxmod
except ImportError:
_LOGGER.exception("Failed to import rfxtrx")
return False
import RFXtrx as rfxtrxmod
binarypacket = bytearray.fromhex(packetid)
+8 -9
View File
@@ -1,17 +1,13 @@
"""
homeassistant.components.rpi_gpio
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to control the GPIO pins of a Raspberry Pi.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/rpi_gpio/
"""
# pylint: disable=import-error
import logging
try:
import RPi.GPIO as GPIO
except ImportError:
GPIO = None
from homeassistant.const import (EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP)
REQUIREMENTS = ['RPi.GPIO==0.6.1']
@@ -22,9 +18,7 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=no-member
def setup(hass, config):
""" Sets up the Raspberry PI GPIO component. """
if GPIO is None:
_LOGGER.error('RPi.GPIO not available. rpi_gpio ports ignored.')
return False
import RPi.GPIO as GPIO
def cleanup_gpio(event):
""" Stuff to do before stop home assistant. """
@@ -41,27 +35,32 @@ def setup(hass, config):
def setup_output(port):
""" Setup a GPIO as output. """
import RPi.GPIO as GPIO
GPIO.setup(port, GPIO.OUT)
def setup_input(port, pull_mode):
""" Setup a GPIO as input. """
import RPi.GPIO as GPIO
GPIO.setup(port, GPIO.IN,
GPIO.PUD_DOWN if pull_mode == 'DOWN' else GPIO.PUD_UP)
def write_output(port, value):
""" Write a value to a GPIO. """
import RPi.GPIO as GPIO
GPIO.output(port, value)
def read_input(port):
""" Read a value from a GPIO. """
import RPi.GPIO as GPIO
return GPIO.input(port)
def edge_detect(port, event_callback, bounce):
""" Adds detection for RISING and FALLING events. """
import RPi.GPIO as GPIO
GPIO.add_event_detect(
port,
GPIO.BOTH,
+14 -8
View File
@@ -13,9 +13,10 @@ from itertools import islice
import threading
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity import ToggleEntity, split_entity_id
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.util import slugify, split_entity_id
from homeassistant.helpers.service import call_from_config
from homeassistant.util import slugify
import homeassistant.util.dt as date_util
from homeassistant.const import (
ATTR_ENTITY_ID, EVENT_TIME_CHANGED, STATE_ON, SERVICE_TURN_ON,
@@ -30,7 +31,8 @@ STATE_NOT_RUNNING = 'Not Running'
CONF_ALIAS = "alias"
CONF_SERVICE = "service"
CONF_SERVICE_OLD = "execute_service"
CONF_SERVICE_DATA = "service_data"
CONF_SERVICE_DATA = "data"
CONF_SERVICE_DATA_OLD = "service_data"
CONF_SEQUENCE = "sequence"
CONF_EVENT = "event"
CONF_EVENT_DATA = "event_data"
@@ -194,13 +196,17 @@ class Script(ToggleEntity):
def _call_service(self, action):
""" Calls the service specified in the action. """
conf_service = action.get(CONF_SERVICE, action.get(CONF_SERVICE_OLD))
self._last_action = action.get(CONF_ALIAS, conf_service)
# Backwards compatibility
if CONF_SERVICE not in action and CONF_SERVICE_OLD in action:
action[CONF_SERVICE] = action[CONF_SERVICE_OLD]
if CONF_SERVICE_DATA not in action and CONF_SERVICE_DATA_OLD in action:
action[CONF_SERVICE_DATA] = action[CONF_SERVICE_DATA_OLD]
self._last_action = action.get(CONF_ALIAS, action[CONF_SERVICE])
_LOGGER.info("Executing script %s step %s", self._name,
self._last_action)
domain, service = split_entity_id(conf_service)
data = action.get(CONF_SERVICE_DATA, {})
self.hass.services.call(domain, service, data, True)
call_from_config(self.hass, action, True)
def _fire_event(self, action):
""" Fires an event. """
+11 -19
View File
@@ -13,28 +13,28 @@ from homeassistant.util import Throttle
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['blockchain==1.1.2']
REQUIREMENTS = ['blockchain==1.2.1']
_LOGGER = logging.getLogger(__name__)
OPTION_TYPES = {
'wallet': ['Wallet balance', 'BTC'],
'exchangerate': ['Exchange rate (1 BTC)', ''],
'exchangerate': ['Exchange rate (1 BTC)', None],
'trade_volume_btc': ['Trade volume', 'BTC'],
'miners_revenue_usd': ['Miners revenue', 'USD'],
'btc_mined': ['Mined', 'BTC'],
'trade_volume_usd': ['Trade volume', 'USD'],
'difficulty': ['Difficulty', ''],
'difficulty': ['Difficulty', None],
'minutes_between_blocks': ['Time between Blocks', 'min'],
'number_of_transactions': ['No. of Transactions', ''],
'number_of_transactions': ['No. of Transactions', None],
'hash_rate': ['Hash rate', 'PH/s'],
'timestamp': ['Timestamp', ''],
'mined_blocks': ['Minded Blocks', ''],
'blocks_size': ['Block size', ''],
'timestamp': ['Timestamp', None],
'mined_blocks': ['Minded Blocks', None],
'blocks_size': ['Block size', None],
'total_fees_btc': ['Total fees', 'BTC'],
'total_btc_sent': ['Total sent', 'BTC'],
'estimated_btc_sent': ['Estimated sent', 'BTC'],
'total_btc': ['Total', 'BTC'],
'total_blocks': ['Total Blocks', ''],
'next_retarget': ['Next retarget', ''],
'total_blocks': ['Total Blocks', None],
'next_retarget': ['Next retarget', None],
'estimated_transaction_volume_usd': ['Est. Transaction volume', 'USD'],
'miners_revenue_btc': ['Miners revenue', 'BTC'],
'market_price_usd': ['Market price', 'USD']
@@ -47,16 +47,8 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Get the Bitcoin sensor. """
try:
from blockchain.wallet import Wallet
from blockchain import exchangerates, exceptions
except ImportError:
_LOGGER.exception(
"Unable to import blockchain. "
"Did you maybe not install the 'blockchain' package?")
return False
from blockchain.wallet import Wallet
from blockchain import exchangerates, exceptions
wallet_id = config.get('wallet', None)
password = config.get('password', None)
+1 -9
View File
@@ -10,7 +10,7 @@ import logging
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['py-cpuinfo==0.1.6']
REQUIREMENTS = ['py-cpuinfo==0.1.8']
_LOGGER = logging.getLogger(__name__)
@@ -25,14 +25,6 @@ ATTR_HZ = 'GHz Advertised'
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the CPU speed sensor. """
try:
import cpuinfo # noqa
except ImportError:
_LOGGER.exception(
"Unable to import cpuinfo. "
"Did you maybe not install the 'py-cpuinfo' package?")
return False
add_devices([CpuSpeedSensor(config.get('name', DEFAULT_NAME))])
+3 -10
View File
@@ -20,7 +20,7 @@ REQUIREMENTS = ['http://github.com/mala-zaba/Adafruit_Python_DHT/archive/'
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {
'temperature': ['Temperature', ''],
'temperature': ['Temperature', None],
'humidity': ['Humidity', '%']
}
# Return cached results if last scan was less then this time ago
@@ -31,15 +31,8 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Get the DHT sensor. """
try:
import Adafruit_DHT
except ImportError:
_LOGGER.exception(
"Unable to import Adafruit_DHT. "
"Did you maybe not install the 'Adafruit_DHT' package?")
return False
# pylint: disable=import-error
import Adafruit_DHT
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
unit = hass.config.temperature_unit
+1 -1
View File
@@ -36,7 +36,7 @@ DEPENDENCIES = ['ecobee']
SENSOR_TYPES = {
'temperature': ['Temperature', TEMP_FAHRENHEIT],
'humidity': ['Humidity', '%'],
'occupancy': ['Occupancy', '']
'occupancy': ['Occupancy', None]
}
_LOGGER = logging.getLogger(__name__)
+2 -2
View File
@@ -16,8 +16,8 @@ _LOGGER = logging.getLogger(__name__)
_RESOURCE = 'https://engage.efergy.com/mobile_proxy/'
SENSOR_TYPES = {
'instant_readings': ['Energy Usage', 'kW'],
'budget': ['Energy Budget', ''],
'cost': ['Energy Cost', ''],
'budget': ['Energy Budget', None],
'cost': ['Energy Cost', None],
}
+3 -3
View File
@@ -19,13 +19,13 @@ _LOGGER = logging.getLogger(__name__)
# Sensor types are defined like so:
# Name, si unit, us unit, ca unit, uk unit, uk2 unit
SENSOR_TYPES = {
'summary': ['Summary', '', '', '', '', ''],
'icon': ['Icon', '', '', '', '', ''],
'summary': ['Summary', None, None, None, None, None],
'icon': ['Icon', None, None, None, None, None],
'nearest_storm_distance': ['Nearest Storm Distance',
'km', 'm', 'km', 'km', 'm'],
'nearest_storm_bearing': ['Nearest Storm Bearing',
'°', '°', '°', '°', '°'],
'precip_type': ['Precip', '', '', '', '', ''],
'precip_type': ['Precip', None, None, None, None, None],
'precip_intensity': ['Precip Intensity', 'mm', 'in', 'mm', 'mm', 'mm'],
'precip_probability': ['Precip Probability', '%', '%', '%', '%', '%'],
'temperature': ['Temperature', '°C', '°F', '°C', '°C', '°C'],
+5 -5
View File
@@ -31,11 +31,11 @@ SENSOR_TYPES = {
'swap_use_percent': ['Swap Use', '%'],
'swap_use': ['Swap Use', 'GiB'],
'swap_free': ['Swap Free', 'GiB'],
'processor_load': ['CPU Load', ''],
'process_running': ['Running', ''],
'process_total': ['Total', ''],
'process_thread': ['Thread', ''],
'process_sleeping': ['Sleeping', '']
'processor_load': ['CPU Load', None],
'process_running': ['Running', None],
'process_total': ['Total', None],
'process_thread': ['Thread', None],
'process_sleeping': ['Sleeping', None]
}
_LOGGER = logging.getLogger(__name__)
+78 -58
View File
@@ -7,13 +7,12 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors/
"""
import logging
from collections import defaultdict
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
ATTR_BATTERY_LEVEL,
TEMP_CELCIUS, TEMP_FAHRENHEIT,
TEMP_CELCIUS,
STATE_ON, STATE_OFF)
import homeassistant.components.mysensors as mysensors
@@ -31,42 +30,57 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for gateway in mysensors.GATEWAYS.values():
# Define the S_TYPES and V_TYPES that the platform should handle as
# states.
s_types = [
gateway.const.Presentation.S_TEMP,
gateway.const.Presentation.S_HUM,
gateway.const.Presentation.S_BARO,
gateway.const.Presentation.S_WIND,
gateway.const.Presentation.S_RAIN,
gateway.const.Presentation.S_UV,
gateway.const.Presentation.S_WEIGHT,
gateway.const.Presentation.S_POWER,
gateway.const.Presentation.S_DISTANCE,
gateway.const.Presentation.S_LIGHT_LEVEL,
gateway.const.Presentation.S_IR,
gateway.const.Presentation.S_WATER,
gateway.const.Presentation.S_AIR_QUALITY,
gateway.const.Presentation.S_CUSTOM,
gateway.const.Presentation.S_DUST,
gateway.const.Presentation.S_SCENE_CONTROLLER,
]
not_v_types = [
gateway.const.SetReq.V_ARMED,
gateway.const.SetReq.V_LIGHT,
gateway.const.SetReq.V_LOCK_STATUS,
]
# states. Map them in a defaultdict(list).
pres = gateway.const.Presentation
set_req = gateway.const.SetReq
map_sv_types = {
pres.S_DOOR: [set_req.V_TRIPPED],
pres.S_MOTION: [set_req.V_TRIPPED],
pres.S_SMOKE: [set_req.V_TRIPPED],
pres.S_TEMP: [set_req.V_TEMP],
pres.S_HUM: [set_req.V_HUM],
pres.S_BARO: [set_req.V_PRESSURE, set_req.V_FORECAST],
pres.S_WIND: [set_req.V_WIND, set_req.V_GUST],
pres.S_RAIN: [set_req.V_RAIN, set_req.V_RAINRATE],
pres.S_UV: [set_req.V_UV],
pres.S_WEIGHT: [set_req.V_WEIGHT, set_req.V_IMPEDANCE],
pres.S_POWER: [set_req.V_WATT, set_req.V_KWH],
pres.S_DISTANCE: [set_req.V_DISTANCE],
pres.S_LIGHT_LEVEL: [set_req.V_LIGHT_LEVEL],
pres.S_IR: [set_req.V_IR_SEND, set_req.V_IR_RECEIVE],
pres.S_WATER: [set_req.V_FLOW, set_req.V_VOLUME],
pres.S_CUSTOM: [set_req.V_VAR1,
set_req.V_VAR2,
set_req.V_VAR3,
set_req.V_VAR4,
set_req.V_VAR5],
pres.S_SCENE_CONTROLLER: [set_req.V_SCENE_ON,
set_req.V_SCENE_OFF],
}
if float(gateway.version) < 1.5:
map_sv_types.update({
pres.S_AIR_QUALITY: [set_req.V_DUST_LEVEL],
pres.S_DUST: [set_req.V_DUST_LEVEL],
})
if float(gateway.version) >= 1.5:
s_types.extend([
gateway.const.Presentation.S_COLOR_SENSOR,
gateway.const.Presentation.S_MULTIMETER,
])
not_v_types.extend([gateway.const.SetReq.V_STATUS, ])
v_types = [member for member in gateway.const.SetReq
if member.value not in not_v_types]
map_sv_types.update({
pres.S_COLOR_SENSOR: [set_req.V_RGB],
pres.S_MULTIMETER: [set_req.V_VOLTAGE,
set_req.V_CURRENT,
set_req.V_IMPEDANCE],
pres.S_SPRINKLER: [set_req.V_TRIPPED],
pres.S_WATER_LEAK: [set_req.V_TRIPPED],
pres.S_SOUND: [set_req.V_TRIPPED, set_req.V_LEVEL],
pres.S_VIBRATION: [set_req.V_TRIPPED, set_req.V_LEVEL],
pres.S_MOISTURE: [set_req.V_TRIPPED, set_req.V_LEVEL],
pres.S_AIR_QUALITY: [set_req.V_LEVEL],
pres.S_DUST: [set_req.V_LEVEL],
})
map_sv_types[pres.S_LIGHT_LEVEL].append(set_req.V_LEVEL)
devices = defaultdict(list)
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
s_types, v_types, devices, add_devices, MySensorsSensor))
map_sv_types, devices, add_devices, MySensorsSensor))
class MySensorsSensor(Entity):
@@ -121,33 +135,39 @@ class MySensorsSensor(Entity):
@property
def unit_of_measurement(self):
"""Unit of measurement of this entity."""
# pylint:disable=too-many-return-statements
if self.value_type == self.gateway.const.SetReq.V_TEMP:
return TEMP_CELCIUS if self.gateway.metric else TEMP_FAHRENHEIT
elif self.value_type == self.gateway.const.SetReq.V_HUM or \
self.value_type == self.gateway.const.SetReq.V_DIMMER or \
self.value_type == self.gateway.const.SetReq.V_PERCENTAGE or \
self.value_type == self.gateway.const.SetReq.V_LIGHT_LEVEL:
return '%'
elif self.value_type == self.gateway.const.SetReq.V_WATT:
return 'W'
elif self.value_type == self.gateway.const.SetReq.V_KWH:
return 'kWh'
elif self.value_type == self.gateway.const.SetReq.V_VOLTAGE:
return 'V'
elif self.value_type == self.gateway.const.SetReq.V_CURRENT:
return 'A'
elif self.value_type == self.gateway.const.SetReq.V_IMPEDANCE:
return 'ohm'
elif self.gateway.const.SetReq.V_UNIT_PREFIX in self._values:
return self._values[self.gateway.const.SetReq.V_UNIT_PREFIX]
return None
# HA will convert to degrees F if needed
unit_map = {
self.gateway.const.SetReq.V_TEMP: TEMP_CELCIUS,
self.gateway.const.SetReq.V_HUM: '%',
self.gateway.const.SetReq.V_DIMMER: '%',
self.gateway.const.SetReq.V_LIGHT_LEVEL: '%',
self.gateway.const.SetReq.V_WEIGHT: 'kg',
self.gateway.const.SetReq.V_DISTANCE: 'm',
self.gateway.const.SetReq.V_IMPEDANCE: 'ohm',
self.gateway.const.SetReq.V_WATT: 'W',
self.gateway.const.SetReq.V_KWH: 'kWh',
self.gateway.const.SetReq.V_FLOW: 'm',
self.gateway.const.SetReq.V_VOLUME: 'm3',
self.gateway.const.SetReq.V_VOLTAGE: 'V',
self.gateway.const.SetReq.V_CURRENT: 'A',
}
unit_map_v15 = {
self.gateway.const.SetReq.V_PERCENTAGE: '%',
}
if float(self.gateway.version) >= 1.5:
if self.gateway.const.SetReq.V_UNIT_PREFIX in self._values:
return self._values[
self.gateway.const.SetReq.V_UNIT_PREFIX]
unit_map.update(unit_map_v15)
return unit_map.get(self.value_type)
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
device_attr = dict(self._values)
device_attr.pop(self.value_type, None)
device_attr = {}
for value_type, value in self._values.items():
if value_type != self.value_type:
device_attr[self.gateway.const.SetReq(value_type).name] = value
return device_attr
@property
+111
View File
@@ -0,0 +1,111 @@
"""
homeassistant.components.sensor.nest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 homeassistant.components.nest as nest
from homeassistant.helpers.entity import Entity
from homeassistant.const import TEMP_CELCIUS
DEPENDENCIES = ['nest']
SENSOR_TYPES = ['humidity',
'mode',
'last_ip',
'local_ip',
'last_connection',
'battery_level']
SENSOR_UNITS = {'humidity': '%', 'battery_level': '%'}
SENSOR_TEMP_TYPES = ['temperature',
'target',
'away_temperature[0]',
'away_temperature[1]']
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Setup 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)])
else:
logger.error('Nest sensor type: "%s" does not exist',
variable)
except socket.error:
logger.error(
"Connection error logging into the nest web service."
)
class NestSensor(Entity):
""" Represents a Nest sensor. """
def __init__(self, structure, device, variable):
self.structure = structure
self.device = device
self.variable = variable
@property
def name(self):
""" Returns the name of the nest, if any. """
location = self.device.where
name = self.device.name
if location is None:
return "{} {}".format(name, self.variable)
else:
if name == '':
return "{} {}".format(location.capitalize(), self.variable)
else:
return "{}({}){}".format(location.capitalize(),
name,
self.variable)
class NestBasicSensor(NestSensor):
""" Represents a basic Nest sensor with state. """
@property
def state(self):
""" Returns the state of the sensor. """
return getattr(self.device, self.variable)
@property
def unit_of_measurement(self):
""" Unit the value is expressed in. """
return SENSOR_UNITS.get(self.variable, None)
class NestTempSensor(NestSensor):
""" Represents a Nest Temperature sensor. """
@property
def unit_of_measurement(self):
""" Unit the value is expressed in. """
return TEMP_CELCIUS
@property
def state(self):
""" Returns the state of the sensor. """
temp = getattr(self.device, self.variable)
if temp is None:
return None
return round(temp, 1)
+9 -5
View File
@@ -23,11 +23,11 @@ REQUIREMENTS = [
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {
'temperature': ['Temperature', TEMP_CELCIUS],
'co2': ['CO2', 'ppm'],
'pressure': ['Pressure', 'mbar'],
'noise': ['Noise', 'dB'],
'humidity': ['Humidity', '%']
'temperature': ['Temperature', TEMP_CELCIUS, 'mdi:thermometer'],
'co2': ['CO2', 'ppm', 'mdi:cloud'],
'pressure': ['Pressure', 'mbar', 'mdi:gauge'],
'noise': ['Noise', 'dB', 'mdi:volume-high'],
'humidity': ['Humidity', '%', 'mdi:water-percent']
}
CONF_SECRET_KEY = 'secret_key'
@@ -104,6 +104,10 @@ class NetAtmoSensor(Entity):
def name(self):
return self._name
@property
def icon(self):
return SENSOR_TYPES[self.type][2]
@property
def state(self):
""" Returns the state of the device. """
+100
View File
@@ -0,0 +1,100 @@
"""
homeassistant.components.sensor.onewire
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for DS18B20 One Wire Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.onewire/
"""
from glob import glob
import logging
import os
import time
from homeassistant.const import TEMP_CELCIUS, STATE_UNKNOWN
from homeassistant.helpers.entity import Entity
BASE_DIR = '/sys/bus/w1/devices/'
DEVICE_FOLDERS = glob(os.path.join(BASE_DIR, '28*'))
SENSOR_IDS = []
DEVICE_FILES = []
for device_folder in DEVICE_FOLDERS:
SENSOR_IDS.append(os.path.split(device_folder)[1])
DEVICE_FILES.append(os.path.join(device_folder, 'w1_slave'))
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the one wire Sensors. """
if DEVICE_FILES == []:
_LOGGER.error('No onewire sensor found.')
_LOGGER.error('Check if dtoverlay=w1-gpio,gpiopin=4.')
_LOGGER.error('is in your /boot/config.txt and')
_LOGGER.error('the correct gpiopin number is set.')
return
devs = []
names = SENSOR_IDS
for key in config.keys():
if key == "names":
# only one name given
if isinstance(config['names'], str):
names = [config['names']]
# map names and sensors in given order
elif isinstance(config['names'], list):
names = config['names']
# map names to ids.
elif isinstance(config['names'], dict):
names = []
for sensor_id in SENSOR_IDS:
names.append(config['names'].get(sensor_id, sensor_id))
for device_file, name in zip(DEVICE_FILES, names):
devs.append(OneWire(name, device_file))
add_devices(devs)
class OneWire(Entity):
""" An One wire Sensor. """
def __init__(self, name, device_file):
self._name = name
self._device_file = device_file
self._state = STATE_UNKNOWN
self.update()
def _read_temp_raw(self):
""" Read the temperature as it is returned by the sensor. """
ds_device_file = open(self._device_file, 'r')
lines = ds_device_file.readlines()
ds_device_file.close()
return lines
@property
def name(self):
""" The name of the sensor. """
return self._name
@property
def state(self):
""" Returns the state of the device. """
return self._state
@property
def unit_of_measurement(self):
""" Unit the value is expressed in. """
return TEMP_CELCIUS
def update(self):
""" Gets the latest data from the device. """
lines = self._read_temp_raw()
while lines[0].strip()[-3:] != 'YES':
time.sleep(0.2)
lines = self._read_temp_raw()
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temp = float(temp_string) / 1000.0
self._state = temp
@@ -16,8 +16,8 @@ from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['pyowm==2.3.0']
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {
'weather': ['Condition', ''],
'temperature': ['Temperature', ''],
'weather': ['Condition', None],
'temperature': ['Temperature', None],
'wind_speed': ['Wind speed', 'm/s'],
'humidity': ['Humidity', '%'],
'pressure': ['Pressure', 'mbar'],
@@ -37,15 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
return False
try:
from pyowm import OWM
except ImportError:
_LOGGER.exception(
"Unable to import pyowm. "
"Did you maybe not install the 'PyOWM' package?")
return False
from pyowm import OWM
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
unit = hass.config.temperature_unit
@@ -71,7 +63,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
pass
if forecast == 1:
SENSOR_TYPES['forecast'] = ['Forecast', '']
SENSOR_TYPES['forecast'] = ['Forecast', None]
dev.append(OpenWeatherMapSensor(data, 'forecast', unit))
add_devices(dev)
@@ -165,6 +157,10 @@ class WeatherData(object):
def update(self):
""" Gets the latest data from OpenWeatherMap. """
obs = self.owm.weather_at_coords(self.latitude, self.longitude)
if obs is None:
_LOGGER.warning('Failed to fetch data from OWM')
return
self.data = obs.get_weather()
if self.forecast == 1:
+1 -1
View File
@@ -17,7 +17,7 @@ REQUIREMENTS = ['https://github.com/jamespcole/home-assistant-nzb-clients/'
'#python-sabnzbd==0.1']
SENSOR_TYPES = {
'current_status': ['Status', ''],
'current_status': ['Status', None],
'speed': ['Speed', 'MB/s'],
'queue_size': ['Queue', 'MB'],
'queue_remaining': ['Left', 'MB'],
@@ -12,7 +12,7 @@ import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import Entity
from homeassistant.const import STATE_ON, STATE_OFF
REQUIREMENTS = ['psutil==3.2.2']
REQUIREMENTS = ['psutil==3.4.2']
SENSOR_TYPES = {
'disk_use_percent': ['Disk Use', '%', 'mdi:harddisk'],
'disk_use': ['Disk Use', 'GiB', 'mdi:harddisk'],
@@ -18,7 +18,6 @@ from homeassistant.components import tellduslive
ATTR_LAST_UPDATED = "time_last_updated"
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['tellduslive']
SENSOR_TYPE_TEMP = "temp"
SENSOR_TYPE_HUMIDITY = "humidity"
@@ -43,6 +42,8 @@ SENSOR_TYPES = {
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up Tellstick sensors. """
if discovery_info is None:
return
sensors = tellduslive.NETWORK.get_sensors()
devices = []
+1 -6
View File
@@ -20,12 +20,7 @@ REQUIREMENTS = ['https://github.com/rkabadi/temper-python/archive/'
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return Temper sensors. """
try:
# pylint: disable=no-name-in-module, import-error
from temperusb.temper import TemperHandler
except ImportError:
_LOGGER.error('Failed to import temperusb')
return False
from temperusb.temper import TemperHandler
temp_unit = hass.config.temperature_unit
name = config.get(CONF_NAME, DEVICE_DEFAULT_NAME)
+111
View File
@@ -0,0 +1,111 @@
"""
homeassistant.components.sensor.template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows the creation of a sensor that breaks out state_attributes
from other entities.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.template/
"""
import logging
from homeassistant.helpers.entity import Entity
from homeassistant.core import EVENT_STATE_CHANGED
from homeassistant.const import (
ATTR_FRIENDLY_NAME,
CONF_VALUE_TEMPLATE,
ATTR_UNIT_OF_MEASUREMENT)
from homeassistant.util import template
from homeassistant.exceptions import TemplateError
_LOGGER = logging.getLogger(__name__)
CONF_SENSORS = 'sensors'
STATE_ERROR = 'error'
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the sensors. """
sensors = []
if config.get(CONF_SENSORS) is None:
_LOGGER.error("Missing configuration data for sensor platform")
return False
for device, device_config in config[CONF_SENSORS].items():
if not isinstance(device_config, dict):
_LOGGER.error("Missing configuration data for sensor %s", device)
continue
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
state_template = device_config.get(CONF_VALUE_TEMPLATE)
if state_template is None:
_LOGGER.error(
"Missing %s for sensor %s", CONF_VALUE_TEMPLATE, device)
continue
sensors.append(
SensorTemplate(
hass,
friendly_name,
unit_of_measurement,
state_template)
)
if sensors is None:
_LOGGER.error("No sensors added")
return False
add_devices(sensors)
return True
class SensorTemplate(Entity):
""" Represents a Template Sensor. """
# pylint: disable=too-many-arguments
def __init__(self,
hass,
friendly_name,
unit_of_measurement,
state_template):
self.hass = hass
self._name = friendly_name
self._unit_of_measurement = unit_of_measurement
self._template = state_template
self.update()
def _update_callback(_event):
""" Called when the target device changes state. """
# This can be called before the entity is properly
# initialised, so check before updating state,
if self.entity_id:
self.update_ha_state(True)
self.hass.bus.listen(EVENT_STATE_CHANGED, _update_callback)
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def state(self):
""" Returns the state of the device. """
return self._state
@property
def unit_of_measurement(self):
""" Returns the unit_of_measurement of the device. """
return self._unit_of_measurement
@property
def should_poll(self):
""" Tells Home Assistant not to poll this entity. """
return False
def update(self):
try:
self._state = template.render(self.hass, self._template)
except TemplateError as ex:
self._state = STATE_ERROR
_LOGGER.error(ex)
@@ -15,7 +15,7 @@ from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['transmissionrpc==0.11']
SENSOR_TYPES = {
'current_status': ['Status', ''],
'current_status': ['Status', None],
'download_speed': ['Down Speed', 'MB/s'],
'upload_speed': ['Up Speed', 'MB/s']
}
+2 -2
View File
@@ -67,7 +67,7 @@ class VerisureThermometer(Entity):
return TEMP_CELCIUS # can verisure report in fahrenheit?
def update(self):
''' update sensor '''
""" update sensor """
verisure.update_climate()
@@ -96,5 +96,5 @@ class VerisureHygrometer(Entity):
return "%"
def update(self):
''' update sensor '''
""" update sensor """
verisure.update_climate()
+1 -1
View File
@@ -11,7 +11,7 @@ import logging
from homeassistant.helpers.entity import Entity
from homeassistant.const import CONF_ACCESS_TOKEN, STATE_OPEN, STATE_CLOSED
REQUIREMENTS = ['python-wink==0.4.1']
REQUIREMENTS = ['python-wink==0.4.2']
def setup_platform(hass, config, add_devices, discovery_info=None):
+17 -11
View File
@@ -10,7 +10,9 @@ import logging
import requests
from homeassistant.const import ATTR_ENTITY_PICTURE
from homeassistant.const import (ATTR_ENTITY_PICTURE,
CONF_LATITUDE,
CONF_LONGITUDE)
from homeassistant.helpers.entity import Entity
from homeassistant.util import location, dt as dt_util
@@ -21,10 +23,11 @@ REQUIREMENTS = ['xmltodict']
# Sensor types are defined like so:
SENSOR_TYPES = {
'symbol': ['Symbol', ''],
'symbol': ['Symbol', None],
'precipitation': ['Condition', 'mm'],
'temperature': ['Temperature', '°C'],
'windSpeed': ['Wind speed', 'm/s'],
'windGust': ['Wind gust', 'm/s'],
'pressure': ['Pressure', 'mbar'],
'windDirection': ['Wind direction', '°'],
'humidity': ['Humidity', '%'],
@@ -40,18 +43,21 @@ SENSOR_TYPES = {
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Get the Yr.no sensor. """
if None in (hass.config.latitude, hass.config.longitude):
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
elevation = config.get('elevation')
if None in (latitude, longitude):
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
return False
elevation = config.get('elevation')
if elevation is None:
elevation = location.elevation(hass.config.latitude,
hass.config.longitude)
elevation = location.elevation(latitude,
longitude)
coordinates = dict(lat=hass.config.latitude,
lon=hass.config.longitude, msl=elevation)
coordinates = dict(lat=latitude,
lon=longitude,
msl=elevation)
weather = YrData(coordinates)
@@ -143,11 +149,11 @@ class YrSensor(Entity):
elif self.type == 'symbol' and valid_from < now:
self._state = loc_data[self.type]['@number']
break
elif self.type == ('temperature', 'pressure', 'humidity',
elif self.type in ('temperature', 'pressure', 'humidity',
'dewpointTemperature'):
self._state = loc_data[self.type]['@value']
break
elif self.type == 'windSpeed':
elif self.type in ('windSpeed', 'windGust'):
self._state = loc_data[self.type]['@mps']
break
elif self.type == 'windDirection':
+77
View File
@@ -0,0 +1,77 @@
"""
homeassistant.components.sensor.zigbee
Contains functionality to use a ZigBee device as a sensor.
"""
import logging
from binascii import hexlify
from homeassistant.core import JobPriority
from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
from homeassistant.components import zigbee
DEPENDENCIES = ["zigbee"]
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Uses the 'type' config value to work out which type of ZigBee sensor we're
dealing with and instantiates the relevant classes to handle it.
"""
typ = config.get("type", "").lower()
if not typ:
_LOGGER.exception(
"Must include 'type' when configuring a ZigBee sensor.")
return
try:
sensor_class, config_class = TYPE_CLASSES[typ]
except KeyError:
_LOGGER.exception("Unknown ZigBee sensor type: %s", typ)
return
add_entities([sensor_class(hass, config_class(config))])
class ZigBeeTemperatureSensor(Entity):
"""
Allows usage of an XBee Pro as a temperature sensor.
"""
def __init__(self, hass, config):
self._config = config
self._temp = None
# Get initial state
hass.pool.add_job(
JobPriority.EVENT_STATE, (self.update_ha_state, True))
@property
def name(self):
return self._config.name
@property
def state(self):
return self._temp
@property
def unit_of_measurement(self):
return TEMP_CELCIUS
def update(self, *args):
try:
self._temp = zigbee.DEVICE.get_temperature(self._config.address)
except zigbee.ZIGBEE_TX_FAILURE:
_LOGGER.warning(
"Transmission failure when attempting to get sample from "
"ZigBee device at address: %s", hexlify(self._config.address))
except zigbee.ZIGBEE_EXCEPTION as exc:
_LOGGER.exception(
"Unable to get sample from ZigBee device: %s", exc)
# This must be below the classes to which it refers.
TYPE_CLASSES = {
"temperature": (ZigBeeTemperatureSensor, zigbee.ZigBeeConfig),
"analog": (zigbee.ZigBeeAnalogIn, zigbee.ZigBeeAnalogInConfig)
}
+19 -53
View File
@@ -12,11 +12,15 @@ import datetime
from homeassistant.helpers.event import track_point_in_time
import homeassistant.util.dt as dt_util
import homeassistant.components.zwave as zwave
from homeassistant.components.sensor import DOMAIN
from homeassistant.helpers.entity import Entity
from homeassistant.components.zwave import (
NETWORK, ATTR_NODE_ID, ATTR_VALUE_ID, COMMAND_CLASS_SENSOR_BINARY,
COMMAND_CLASS_SENSOR_MULTILEVEL, COMMAND_CLASS_METER, TYPE_DECIMAL,
COMMAND_CLASS_ALARM, ZWaveDeviceEntity, get_config_value)
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF,
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
STATE_ON, STATE_OFF, TEMP_CELCIUS, TEMP_FAHRENHEIT)
PHILIO = '013c'
PHILIO_SLIM_SENSOR = '0002'
@@ -41,14 +45,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if discovery_info is None:
return
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
value.set_change_verified(False)
# if 1 in groups and (zwave.NETWORK.controller.node_id not in
# if 1 in groups and (NETWORK.controller.node_id not in
# groups[1].associations):
# node.groups[1].add_association(zwave.NETWORK.controller.node_id)
# node.groups[1].add_association(NETWORK.controller.node_id)
specific_sensor_key = (value.node.manufacturer_id,
value.node.product_id,
@@ -58,81 +62,43 @@ 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 = (zwave.get_config_value(value.node, 9) or 4)
re_arm_multiplier = (get_config_value(value.node, 9) or 4)
add_devices([
ZWaveTriggerSensor(value, hass, re_arm_multiplier * 8)
])
# generic Device mappings
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
elif value.command_class == COMMAND_CLASS_SENSOR_BINARY:
add_devices([ZWaveBinarySensor(value)])
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
elif value.command_class == COMMAND_CLASS_SENSOR_MULTILEVEL:
add_devices([ZWaveMultilevelSensor(value)])
elif (value.command_class == zwave.COMMAND_CLASS_METER and
value.type == zwave.TYPE_DECIMAL):
elif (value.command_class == COMMAND_CLASS_METER and
value.type == TYPE_DECIMAL):
add_devices([ZWaveMultilevelSensor(value)])
elif value.command_class == zwave.COMMAND_CLASS_ALARM:
elif value.command_class == COMMAND_CLASS_ALARM:
add_devices([ZWaveAlarmSensor(value)])
class ZWaveSensor(Entity):
class ZWaveSensor(ZWaveDeviceEntity, Entity):
""" Represents a Z-Wave sensor. """
def __init__(self, sensor_value):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
self._value = sensor_value
self._node = sensor_value.node
ZWaveDeviceEntity.__init__(self, sensor_value, DOMAIN)
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
@property
def should_poll(self):
""" False because we will push our own state to HA when changed. """
return False
@property
def unique_id(self):
""" Returns a unique id. """
return "ZWAVE-{}-{}".format(self._node.node_id, self._value.object_id)
@property
def name(self):
""" Returns the name of the device. """
name = self._node.name or "{} {}".format(
self._node.manufacturer_name, self._node.product_name)
return "{} {}".format(name, self._value.label)
@property
def state(self):
""" Returns the state of the sensor. """
return self._value.data
@property
def state_attributes(self):
""" Returns the state attributes. """
attrs = {
zwave.ATTR_NODE_ID: self._node.node_id,
}
battery_level = self._node.get_battery_level()
if battery_level is not None:
attrs[ATTR_BATTERY_LEVEL] = battery_level
location = self._node.location
if location:
attrs[ATTR_LOCATION] = location
return attrs
@property
def unit_of_measurement(self):
return self._value.units
+86
View File
@@ -0,0 +1,86 @@
"""
homeassistant.components.statsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
StatsD component which allows you to send data to many backends.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/statsd/
"""
import logging
import homeassistant.util as util
from homeassistant.const import (EVENT_STATE_CHANGED, STATE_ON, STATE_OFF,
STATE_UNLOCKED, STATE_LOCKED, STATE_UNKNOWN)
from homeassistant.components.sun import (STATE_ABOVE_HORIZON,
STATE_BELOW_HORIZON)
_LOGGER = logging.getLogger(__name__)
DOMAIN = "statsd"
DEPENDENCIES = []
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 8125
DEFAULT_PREFIX = 'hass'
DEFAULT_RATE = 1
REQUIREMENTS = ['python-statsd==1.7.2']
CONF_HOST = 'host'
CONF_PORT = 'port'
CONF_PREFIX = 'prefix'
CONF_RATE = 'rate'
def setup(hass, config):
""" Setup the StatsD component. """
from statsd.compat import NUM_TYPES
import statsd
conf = config[DOMAIN]
host = conf[CONF_HOST]
port = util.convert(conf.get(CONF_PORT), int, DEFAULT_PORT)
sample_rate = util.convert(conf.get(CONF_RATE), int, DEFAULT_RATE)
prefix = util.convert(conf.get(CONF_PREFIX), str, DEFAULT_PREFIX)
statsd_connection = statsd.Connection(
host=host,
port=port,
sample_rate=sample_rate,
disabled=False
)
meter = statsd.Gauge(prefix, statsd_connection)
def statsd_event_listener(event):
""" Listen for new messages on the bus and sends them to StatsD. """
state = event.data.get('new_state')
if state is None:
return
if state.state in (STATE_ON, STATE_LOCKED, STATE_ABOVE_HORIZON):
_state = 1
elif state.state in (STATE_OFF, STATE_UNLOCKED, STATE_UNKNOWN,
STATE_BELOW_HORIZON):
_state = 0
else:
_state = state.state
if _state == '':
return
try:
_state = float(_state)
except ValueError:
pass
if not isinstance(_state, NUM_TYPES):
return
_LOGGER.debug('Sending %s.%s', state.entity_id, _state)
meter.send(state.entity_id, _state)
hass.bus.listen(EVENT_STATE_CHANGED, statsd_event_listener)
return True
+1 -1
View File
@@ -15,7 +15,7 @@ from homeassistant.helpers.event import (
track_point_in_utc_time, track_utc_time_change)
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['astral==0.8.1']
REQUIREMENTS = ['astral==0.9']
DOMAIN = "sun"
ENTITY_ID = "sun.sun"
+12 -8
View File
@@ -15,7 +15,8 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
ATTR_ENTITY_ID)
from homeassistant.components import (
group, discovery, wink, isy994, verisure, zwave, tellduslive, mysensors)
@@ -29,7 +30,6 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
ATTR_TODAY_MWH = "today_mwh"
ATTR_CURRENT_POWER_MWH = "current_power_mwh"
ATTR_SENSOR_STATE = "sensor_state"
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
@@ -47,7 +47,6 @@ DISCOVERY_PLATFORMS = {
PROP_TO_ATTR = {
'current_power_mwh': ATTR_CURRENT_POWER_MWH,
'today_power_mw': ATTR_TODAY_MWH,
'sensor_state': ATTR_SENSOR_STATE
}
_LOGGER = logging.getLogger(__name__)
@@ -71,6 +70,12 @@ def turn_off(hass, entity_id=None):
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
def toggle(hass, entity_id=None):
""" Toggle all or specified switch. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
def setup(hass, config):
""" Track states and offer events for switches. """
component = EntityComponent(
@@ -85,6 +90,8 @@ def setup(hass, config):
for switch in target_switches:
if service.service == SERVICE_TURN_ON:
switch.turn_on()
elif service.service == SERVICE_TOGGLE:
switch.toggle()
else:
switch.turn_off()
@@ -97,6 +104,8 @@ def setup(hass, config):
descriptions.get(SERVICE_TURN_OFF))
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service,
descriptions.get(SERVICE_TURN_ON))
hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_switch_service,
descriptions.get(SERVICE_TOGGLE))
return True
@@ -120,11 +129,6 @@ class SwitchDevice(ToggleEntity):
""" Is the device in standby. """
return None
@property
def sensor_state(self):
""" Is the sensor on or off. """
return None
@property
def device_state_attributes(self):
""" Returns device specific state attributes. """
+1 -6
View File
@@ -27,12 +27,7 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return Edimax Smart Plugs. """
try:
# pylint: disable=no-name-in-module, import-error
from pyedimax.smartplug import SmartPlug
except ImportError:
_LOGGER.error('Failed to import pyedimax')
return False
from pyedimax.smartplug import SmartPlug
# pylint: disable=global-statement
# check for required values in configuration file
+25 -27
View File
@@ -7,7 +7,6 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors.html
"""
import logging
from collections import defaultdict
from homeassistant.components.switch import SwitchDevice
@@ -30,33 +29,30 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for gateway in mysensors.GATEWAYS.values():
# Define the S_TYPES and V_TYPES that the platform should handle as
# states.
s_types = [
gateway.const.Presentation.S_DOOR,
gateway.const.Presentation.S_MOTION,
gateway.const.Presentation.S_SMOKE,
gateway.const.Presentation.S_LIGHT,
gateway.const.Presentation.S_LOCK,
]
v_types = [
gateway.const.SetReq.V_ARMED,
gateway.const.SetReq.V_LIGHT,
gateway.const.SetReq.V_LOCK_STATUS,
]
# states. Map them in a defaultdict(list).
pres = gateway.const.Presentation
set_req = gateway.const.SetReq
map_sv_types = {
pres.S_DOOR: [set_req.V_ARMED],
pres.S_MOTION: [set_req.V_ARMED],
pres.S_SMOKE: [set_req.V_ARMED],
pres.S_LIGHT: [set_req.V_LIGHT],
pres.S_LOCK: [set_req.V_LOCK_STATUS],
}
if float(gateway.version) >= 1.5:
s_types.extend([
gateway.const.Presentation.S_BINARY,
gateway.const.Presentation.S_SPRINKLER,
gateway.const.Presentation.S_WATER_LEAK,
gateway.const.Presentation.S_SOUND,
gateway.const.Presentation.S_VIBRATION,
gateway.const.Presentation.S_MOISTURE,
])
v_types.extend([gateway.const.SetReq.V_STATUS, ])
map_sv_types.update({
pres.S_BINARY: [set_req.V_STATUS, set_req.V_LIGHT],
pres.S_SPRINKLER: [set_req.V_STATUS],
pres.S_WATER_LEAK: [set_req.V_ARMED],
pres.S_SOUND: [set_req.V_ARMED],
pres.S_VIBRATION: [set_req.V_ARMED],
pres.S_MOISTURE: [set_req.V_ARMED],
})
map_sv_types[pres.S_LIGHT].append(set_req.V_STATUS)
devices = defaultdict(list)
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
s_types, v_types, devices, add_devices, MySensorsSwitch))
map_sv_types, devices, add_devices, MySensorsSwitch))
class MySensorsSwitch(SwitchDevice):
@@ -104,8 +100,10 @@ class MySensorsSwitch(SwitchDevice):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
device_attr = dict(self._values)
device_attr.pop(self.value_type, None)
device_attr = {}
for value_type, value in self._values.items():
if value_type != self.value_type:
device_attr[self.gateway.const.SetReq(value_type).name] = value
return device_attr
@property
+1 -1
View File
@@ -11,7 +11,7 @@ import logging
from homeassistant.components.switch import SwitchDevice
DEFAULT_NAME = "Orvibo S20 Switch"
REQUIREMENTS = ['orvibo==1.1.0']
REQUIREMENTS = ['orvibo==1.1.1']
_LOGGER = logging.getLogger(__name__)
@@ -15,11 +15,12 @@ from homeassistant.components import tellduslive
from homeassistant.helpers.entity import ToggleEntity
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['tellduslive']
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Find and return Tellstick switches. """
if discovery_info is None:
return
switches = tellduslive.NETWORK.get_switches()
add_devices([TelldusLiveSwitch(switch["name"],
switch["id"])
+26 -22
View File
@@ -17,6 +17,12 @@ _LOGGER = logging.getLogger(__name__)
_WEMO_SUBSCRIPTION_REGISTRY = None
ATTR_SENSOR_STATE = "sensor_state"
ATTR_SWITCH_MODE = "switch_mode"
MAKER_SWITCH_MOMENTARY = "momentary"
MAKER_SWITCH_TOGGLE = "toggle"
# pylint: disable=unused-argument, too-many-function-args
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
@@ -88,6 +94,26 @@ class WemoSwitch(SwitchDevice):
""" Returns the name of the switch if any. """
return self.wemo.name
@property
def state_attributes(self):
attr = super().state_attributes or {}
if self.maker_params:
# Is the maker sensor on or off.
if self.maker_params['hassensor']:
# Note a state of 1 matches the WeMo app 'not triggered'!
if self.maker_params['sensorstate']:
attr[ATTR_SENSOR_STATE] = STATE_OFF
else:
attr[ATTR_SENSOR_STATE] = STATE_ON
# Is the maker switch configured as toggle(0) or momentary (1).
if self.maker_params['switchmode']:
attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_MOMENTARY
else:
attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_TOGGLE
return attr
@property
def state(self):
""" Returns the state. """
@@ -122,28 +148,6 @@ class WemoSwitch(SwitchDevice):
else:
return True
@property
def sensor_state(self):
""" Is the sensor on or off. """
if self.maker_params and self.has_sensor:
# Note a state of 1 matches the WeMo app 'not triggered'!
if self.maker_params['sensorstate']:
return STATE_OFF
else:
return STATE_ON
@property
def switch_mode(self):
""" Is the switch configured as toggle(0) or momentary (1). """
if self.maker_params:
return self.maker_params['switchmode']
@property
def has_sensor(self):
""" Is the sensor present? """
if self.maker_params:
return self.maker_params['hassensor']
@property
def is_on(self):
""" True if switch is on. """
+2 -1
View File
@@ -11,7 +11,7 @@ import logging
from homeassistant.components.wink import WinkToggleDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.4.1']
REQUIREMENTS = ['python-wink==0.4.2']
def setup_platform(hass, config, add_devices, discovery_info=None):
@@ -32,3 +32,4 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(WinkToggleDevice(switch) for switch in pywink.get_switches())
add_devices(WinkToggleDevice(switch) for switch in
pywink.get_powerstrip_outlets())
add_devices(WinkToggleDevice(switch) for switch in pywink.get_sirens())
+28
View File
@@ -0,0 +1,28 @@
"""
homeassistant.components.switch.zigbee
Contains functionality to use a ZigBee device as a switch.
"""
from homeassistant.components.switch import SwitchDevice
from homeassistant.components.zigbee import (
ZigBeeDigitalOut, ZigBeeDigitalOutConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Create and add an entity based on the configuration.
"""
add_entities([
ZigBeeSwitch(hass, ZigBeeDigitalOutConfig(config))
])
class ZigBeeSwitch(ZigBeeDigitalOut, SwitchDevice):
"""
Use multiple inheritance to turn a ZigBeeDigitalOut into a SwitchDevice.
"""
pass
+13 -25
View File
@@ -6,9 +6,10 @@ Zwave platform that handles simple binary switches.
"""
# Because we do not compile openzwave on CI
# pylint: disable=import-error
import homeassistant.components.zwave as zwave
from homeassistant.components.switch import SwitchDevice
from homeassistant.components.switch import SwitchDevice, DOMAIN
from homeassistant.components.zwave import (
COMMAND_CLASS_SWITCH_BINARY, TYPE_BOOL, GENRE_USER, NETWORK,
ATTR_NODE_ID, ATTR_VALUE_ID, ZWaveDeviceEntity)
# pylint: disable=unused-argument
@@ -17,28 +18,27 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if discovery_info is None:
return
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
if value.command_class != zwave.COMMAND_CLASS_SWITCH_BINARY:
if value.command_class != COMMAND_CLASS_SWITCH_BINARY:
return
if value.type != zwave.TYPE_BOOL:
if value.type != TYPE_BOOL:
return
if value.genre != zwave.GENRE_USER:
if value.genre != GENRE_USER:
return
value.set_change_verified(False)
add_devices([ZwaveSwitch(value)])
class ZwaveSwitch(SwitchDevice):
class ZwaveSwitch(ZWaveDeviceEntity, SwitchDevice):
""" Provides a zwave switch. """
def __init__(self, value):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
self._value = value
self._node = value.node
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._state = value.data
dispatcher.connect(
@@ -50,18 +50,6 @@ class ZwaveSwitch(SwitchDevice):
self._state = value.data
self.update_ha_state()
@property
def should_poll(self):
""" No polling needed for a demo switch. """
return False
@property
def name(self):
""" Returns the name of the device if any. """
name = self._node.name or "{}".format(self._node.product_name)
return "{}".format(name or self._value.label)
@property
def is_on(self):
""" True if device is on. """
@@ -69,8 +57,8 @@ class ZwaveSwitch(SwitchDevice):
def turn_on(self, **kwargs):
""" Turn the device on. """
self._node.set_switch(self._value.value_id, True)
self._value.node.set_switch(self._value.value_id, True)
def turn_off(self, **kwargs):
""" Turn the device off. """
self._node.set_switch(self._value.value_id, False)
self._value.node.set_switch(self._value.value_id, False)
+103 -40
View File
@@ -27,6 +27,7 @@ SCAN_INTERVAL = 60
SERVICE_SET_AWAY_MODE = "set_away_mode"
SERVICE_SET_TEMPERATURE = "set_temperature"
SERVICE_SET_FAN_MODE = "set_fan_mode"
STATE_HEAT = "heat"
STATE_COOL = "cool"
@@ -34,6 +35,7 @@ STATE_IDLE = "idle"
ATTR_CURRENT_TEMPERATURE = "current_temperature"
ATTR_AWAY_MODE = "away_mode"
ATTR_FAN = "fan"
ATTR_MAX_TEMP = "max_temp"
ATTR_MIN_TEMP = "min_temp"
ATTR_TEMPERATURE_LOW = "target_temp_low"
@@ -69,59 +71,100 @@ def set_temperature(hass, temperature, entity_id=None):
hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, data)
def set_fan_mode(hass, fan_mode, entity_id=None):
""" Turn all or specified thermostat fan mode on. """
data = {
ATTR_FAN: fan_mode
}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_SET_FAN_MODE, data)
# pylint: disable=too-many-branches
def setup(hass, config):
""" Setup thermostats. """
component = EntityComponent(_LOGGER, DOMAIN, hass,
SCAN_INTERVAL, DISCOVERY_PLATFORMS)
component.setup(config)
def thermostat_service(service):
""" Handles calls to the services. """
# Convert the entity ids to valid light ids
target_thermostats = component.extract_from_service(service)
if service.service == SERVICE_SET_AWAY_MODE:
away_mode = service.data.get(ATTR_AWAY_MODE)
if away_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
elif away_mode:
for thermostat in target_thermostats:
thermostat.turn_away_mode_on()
else:
for thermostat in target_thermostats:
thermostat.turn_away_mode_off()
elif service.service == SERVICE_SET_TEMPERATURE:
temperature = util.convert(
service.data.get(ATTR_TEMPERATURE), float)
if temperature is None:
return
for thermostat in target_thermostats:
thermostat.set_temperature(convert(
temperature, hass.config.temperature_unit,
thermostat.unit_of_measurement))
for thermostat in target_thermostats:
thermostat.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(
DOMAIN, SERVICE_SET_AWAY_MODE, thermostat_service,
descriptions.get(SERVICE_SET_AWAY_MODE))
def away_mode_set_service(service):
""" Set away mode on target thermostats """
target_thermostats = component.extract_from_service(service)
away_mode = service.data.get(ATTR_AWAY_MODE)
if away_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE)
return
for thermostat in target_thermostats:
if away_mode:
thermostat.turn_away_mode_on()
else:
thermostat.turn_away_mode_off()
thermostat.update_ha_state(True)
hass.services.register(
DOMAIN, SERVICE_SET_TEMPERATURE, thermostat_service,
DOMAIN, SERVICE_SET_AWAY_MODE, away_mode_set_service,
descriptions.get(SERVICE_SET_AWAY_MODE))
def temperature_set_service(service):
""" Set temperature on the target thermostats """
target_thermostats = component.extract_from_service(service)
temperature = util.convert(
service.data.get(ATTR_TEMPERATURE), float)
if temperature is None:
return
for thermostat in target_thermostats:
thermostat.set_temperature(convert(
temperature, hass.config.temperature_unit,
thermostat.unit_of_measurement))
thermostat.update_ha_state(True)
hass.services.register(
DOMAIN, SERVICE_SET_TEMPERATURE, temperature_set_service,
descriptions.get(SERVICE_SET_TEMPERATURE))
def fan_mode_set_service(service):
""" Set fan mode on target thermostats """
target_thermostats = component.extract_from_service(service)
fan_mode = service.data.get(ATTR_FAN)
if fan_mode is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_FAN_MODE, ATTR_FAN)
return
for thermostat in target_thermostats:
if fan_mode:
thermostat.turn_fan_on()
else:
thermostat.turn_fan_off()
thermostat.update_ha_state(True)
hass.services.register(
DOMAIN, SERVICE_SET_FAN_MODE, fan_mode_set_service,
descriptions.get(SERVICE_SET_FAN_MODE))
return True
@@ -164,6 +207,10 @@ class ThermostatDevice(Entity):
if is_away is not None:
data[ATTR_AWAY_MODE] = STATE_ON if is_away else STATE_OFF
is_fan_on = self.is_fan_on
if is_fan_on is not None:
data[ATTR_FAN] = STATE_ON if is_fan_on else STATE_OFF
device_attr = self.device_state_attributes
if device_attr is not None:
@@ -209,6 +256,14 @@ class ThermostatDevice(Entity):
"""
return None
@property
def is_fan_on(self):
"""
Returns if the fan is on
Return None if not available.
"""
return None
def set_temperate(self, temperature):
""" Set new target temperature. """
pass
@@ -221,6 +276,14 @@ class ThermostatDevice(Entity):
""" Turns away mode off. """
pass
def turn_fan_on(self):
""" Turns fan on. """
pass
def turn_fan_off(self):
""" Turns fan off. """
pass
@property
def min_temp(self):
""" Return minimum temperature. """
+19 -24
View File
@@ -11,38 +11,21 @@ import logging
from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL,
STATE_IDLE, STATE_HEAT)
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
from homeassistant.const import (TEMP_CELCIUS)
import homeassistant.components.nest as nest
REQUIREMENTS = ['python-nest==2.6.0']
DEPENDENCIES = ['nest']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the nest thermostat. """
"Setup nest thermostat"
logger = logging.getLogger(__name__)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
if username is None or password is None:
logger.error("Missing required configuration items %s or %s",
CONF_USERNAME, CONF_PASSWORD)
return
try:
import nest
except ImportError:
logger.exception(
"Error while importing dependency nest. "
"Did you maybe not install the python-nest dependency?")
return
napi = nest.Nest(username, password)
try:
add_devices([
NestThermostat(structure, device)
for structure in napi.structures
for structure in nest.NEST.structures
for device in structure.devices
])
except socket.error:
@@ -83,7 +66,6 @@ class NestThermostat(ThermostatDevice):
return {
"humidity": self.device.humidity,
"target_humidity": self.device.target_humidity,
"fan": self.device.fan,
"mode": self.device.mode
}
@@ -160,6 +142,19 @@ class NestThermostat(ThermostatDevice):
""" Turns away off. """
self.structure.away = False
@property
def is_fan_on(self):
""" Returns whether the fan is on """
return self.device.fan
def turn_fan_on(self):
""" Turns fan on """
self.device.fan = True
def turn_fan_off(self):
""" Turns fan off """
self.device.fan = False
@property
def min_temp(self):
""" Identifies min_temp in Nest API or defaults if not available. """
@@ -21,13 +21,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Radio Thermostat. """
try:
import radiotherm
except ImportError:
_LOGGER.exception(
"Unable to import radiotherm. "
"Did you maybe not install the 'radiotherm' package?")
return False
import radiotherm
hosts = []
if CONF_HOST in config:
@@ -22,3 +22,15 @@ set_temperature:
temperature:
description: New target temperature for thermostat
example: 25
set_fan_mode:
description: Turn fan on/off for a thermostat
fields:
entity_id:
description: Name(s) of entities to change
example: 'thermostat.nest'
fan:
description: New value of fan mode
example: true
+1 -1
View File
@@ -28,7 +28,7 @@ DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DEPENDENCIES = ['alarm_control_panel']
REQUIREMENTS = ['vsure==0.4.5']
REQUIREMENTS = ['vsure==0.4.8']
_LOGGER = logging.getLogger(__name__)
+2 -1
View File
@@ -16,7 +16,7 @@ from homeassistant.const import (
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
DOMAIN = "wink"
REQUIREMENTS = ['python-wink==0.4.1']
REQUIREMENTS = ['python-wink==0.4.2']
DISCOVER_LIGHTS = "wink.lights"
DISCOVER_SWITCHES = "wink.switches"
@@ -38,6 +38,7 @@ def setup(hass, config):
for component_name, func_exists, discovery_type in (
('light', pywink.get_bulbs, DISCOVER_LIGHTS),
('switch', lambda: pywink.get_switches or
pywink.get_sirens or
pywink.get_powerstrip_outlets, DISCOVER_SWITCHES),
('sensor', lambda: pywink.get_sensors or
pywink.get_eggtrays, DISCOVER_SENSORS),

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