590 Commits

Author SHA1 Message Date
Khaos Tian
6500912f54 0.2.19 2016-02-22 16:50:52 -08:00
Khaos Tian
2e2c8eb207 Update hap-nodejs dependency for iOS 9.3 2016-02-22 16:50:33 -08:00
Khaos Tian
0b28387cb1 Merge pull request #532 from torarnv/handle-shutdown-signals
Handle SIGINT and SIGTERM to enable clean shutdown of Homebridge
2016-02-18 14:47:41 -08:00
Tor Arne Vestbø
cf80e4f2da Handle SIGINT and SIGTERM to enable clean shutdown of Homebridge
For now we terminate the process, but in the future we may tell the
server to stop, which may possibly include some teardown logic.

Handling these signals also make it easier to put Homebridge inside
a docker container, as docker uses SIGTERM to tell a container process
to stop, and passes SIGINT when attached to the container and receiving
a Ctrl+C.
2016-02-18 00:36:56 +01:00
Nick Farina
57beabf0b4 Add comment 2016-02-17 10:43:54 -08:00
Khaos Tian
287aa6d034 0.2.18 2016-02-17 10:38:08 -08:00
Khaos Tian
1da98bb19f Add the insecure launching option to homebridge cli. 2016-02-17 10:37:11 -08:00
Nick Farina
aea485c33e Version bump; publish v0.2.17 2016-02-16 13:00:22 -08:00
Khaos Tian
386cf34827 Update hap-nodejs to 0.2.3 so the reachability for existing accessories will be correct. 2016-02-15 19:23:38 -08:00
Khaos Tian
d04c20ca2f Merge pull request #522 from leoneleone/patch-1
Update hap-nodejs to v0.2.0
2016-02-12 09:52:13 -08:00
leoneleone
c53f890a8c Update hap-nodejs to v0.2.0 2016-02-12 14:58:35 +00:00
Nick Farina
1a356a1783 Add a fun gif illustrating Why Homebridge 2016-01-23 17:11:25 -08:00
Nick Farina
da8d5fd267 Version bump to v0.2.16 2016-01-22 07:11:30 -08:00
Nick Farina
798275774a Merge pull request #482 from nfarina/update-hap-nodejs
Update hap-nodejs to 0.1.1
2016-01-22 07:10:52 -08:00
Khaos Tian
74e1cf257a Update hap-nodejs to 0.1.1
Addressed #478
2016-01-21 21:20:07 -08:00
Nick Farina
f4aa57314b Merge pull request #467 from rudders/master
Updated README.md to explain Startup Errors.
2016-01-12 20:04:50 -08:00
Adrian Rudman
66505457c7 Updated README.md to explain Startup Errors.
This issue is frequently discussed so let's see if people read the documentation...
2016-01-13 14:30:43 +11:00
Nick Farina
ae24e28ac4 Version bump to v0.2.15
Fix link to searching NPM for plugins—the old "correct" method of
searching for specific keywords doesn't work when the results span
multiple pages (see https://github.com/npm/newww/issues/1630).

This way requires more clicking but actually allows you to discover all
known plugins.
2016-01-07 12:37:24 -08:00
Nick Farina
c8c84c08f2 Bump HAP-NodeJS to v0.0.7; Version bump to v0.2.14 2016-01-06 13:51:47 -08:00
Nick Farina
2213997acc Add note about editing config.json 2015-12-28 16:11:53 -08:00
Nick Farina
3616d61fed Version bump for npm 2015-12-28 09:33:47 -08:00
Nick Farina
e52aff802b Merge pull request #442 from n8henrie/master
Add test for valid username
2015-12-28 09:33:08 -08:00
Nathan Henrie
15bf22805c Add test for valid username
Like a MAC address, a valid username can only contain 6 pairs of
hexadecimal characters, and for homebridge purposes must be
colon-separated. This adds a simple regex test for a proper username,
since I just spent nearly 24h trying to debug an issue that all came
down to not realizing that a MAC address could only have letters A-F
(see https://github.com/nfarina/homebridge/issues/385).
2015-12-27 10:29:21 -07:00
Nick Farina
f5b790ff35 Just make the sudo explicit 2015-11-30 14:01:40 -08:00
Nick Farina
08f8ae53c5 Link to HomeKitTypes 2015-11-28 12:16:40 -08:00
Nick Farina
2f0c60984a Remove notice at top
many plugins have now been extracted!
2015-11-28 08:54:56 -08:00
Nick Farina
507835986b Merge pull request #403 from victorklijmeij/patch-1
correct errors
2015-11-21 11:33:09 -08:00
victorklijmeij
9567c41a58 correct errors
Json was found invalid on jsonlint.com
2015-11-21 20:22:16 +01:00
Nick Farina
ec839b2dbc Tweaks to plugin load error detection 2015-11-11 15:16:29 -08:00
Nick Farina
2d73e70670 Merge pull request #387 from knalli/patch-1
Provide and output proper existing error message
2015-11-11 15:15:35 -08:00
Nick Farina
65b74253df Fix command-line examples 2015-11-09 11:29:22 -08:00
Nick Farina
78b8c265e1 Include repo and bugs in example plugin 2015-11-09 10:59:49 -08:00
Jan Philipp
affe3fde63 Provide and output proper existing error message
If the found plugin / package does not fulfill some specs (i.e. does not contain the keyword `homebridge-plugin`), it will be rejected with an error. Without that change nobody will be notified WHY the plugin has been rejected/skipped.
2015-11-08 17:31:02 +01:00
Nick Farina
e3a5c5a96a More helpful plugin load error logging; Publish v0.2.11 2015-11-05 10:48:32 -08:00
Nick Farina
b3dbe1165c New "Common Issues" section 2015-11-05 06:35:51 -08:00
Nick Farina
0b9e931fe1 Tweak README text 2015-11-05 06:20:33 -08:00
Nick Farina
50efd29b6d Fix (remove) relative links in README; publish v0.2.10 2015-11-04 16:22:49 -08:00
Nick Farina
6d2cb11eae Publish v0.2.9 2015-11-04 15:59:49 -08:00
Nick Farina
7bfc5ee84d Expose User object to Plugin API
Closes #379
Changes #378
2015-11-04 15:59:38 -08:00
Nick Farina
8f1a043121 Publish v0.2.8 2015-11-04 15:52:08 -08:00
Nick Farina
a5de3f820f Don't search the same plugin path twice
Fix part of #379
2015-11-04 15:50:23 -08:00
Nick Farina
8e795f48cf Add note about legacy plugins; publish v0.2.7 2015-11-04 10:29:14 -08:00
Nick Farina
1c4bf23b60 Publish v0.2.6 2015-11-04 10:22:47 -08:00
Nick Farina
988c6ef6d0 Basic info on writing plugins 2015-11-04 10:22:36 -08:00
Nick Farina
52aa6bba2c Publish v0.2.5 2015-11-04 09:48:05 -08:00
Nick Farina
f2ad339a13 CLI setting for custom homebridge "home" path
Fixes #378
2015-11-04 09:47:48 -08:00
Nick Farina
9883eb7189 Add Slack info to README 2015-11-04 09:37:20 -08:00
Nick Farina
974eef3ea8 HAP-NodeJS -> v0.0.3, publish v0.2.3 2015-11-04 06:18:57 -08:00
Nick Farina
752a5b7854 Publish v0.2.2 2015-11-04 04:35:44 -08:00
Nick Farina
15357a5fe8 Fix old-style accessory JSON
Closes #375
2015-11-04 04:35:22 -08:00
Nick Farina
e3178e5fa4 Fix -D flag
Fixes #376
2015-11-04 04:31:52 -08:00
Nick Farina
2470f26451 Better handling of custom plugin paths 2015-11-03 21:14:25 -08:00
Nick Farina
52d51c0fcf README tweaks 2015-11-03 15:31:26 -08:00
Nick Farina
3e7d2aedcc Merge pull request #293 from nfarina/plugin-system
Plugin system
2015-11-03 15:22:53 -08:00
Nick Farina
54cff454dd Merge branch 'master' into plugin-system 2015-11-03 15:12:06 -08:00
Nick Farina
46c4b6f589 Trim down config-sample.json 2015-11-03 15:09:56 -08:00
Nick Farina
abc6ccfae7 Require plugin name during registration 2015-11-03 15:02:52 -08:00
Nick Farina
eda8e91d0f Merge pull request #303 from PJCzx/master
Semi working HTTP garage door implementation
2015-11-03 10:26:28 -08:00
Nick Farina
269a8cf43a Merge pull request #320 from fcarucci/master
LiftMaster: return garage door state
2015-11-03 10:25:41 -08:00
Nick Farina
2df1ebed21 Merge pull request #357 from mifi/master
added serialport library as a dependency
2015-11-03 09:34:18 -08:00
Nick Farina
36ed2ab0ad Merge pull request #366 from chkr1011/SupportForHttpBody
Support for http body
2015-11-03 09:30:14 -08:00
Nick Farina
193810691b Merge pull request #370 from ilcato/patch-2
Virtual device optimization
2015-11-03 08:50:30 -08:00
ilcato
7db78a50b5 Virtual device optimization
Managed Fibaro Virtual Devices push buttons with a single HomeKit Bridged Accessory with multiple Switches
2015-11-03 17:26:04 +01:00
Nick Farina
eabf45526d Guard against multiple WeMo callbacks
Closes #296
2015-11-03 06:37:53 -08:00
Nick Farina
52d3a9a96e First pass at README update. 2015-11-02 15:30:29 -08:00
Christian
a682e1dee5 Updated the example configuration. 2015-11-03 00:05:38 +01:00
Christian
5440694243 Renamed duplicate body variable. 2015-11-03 00:04:46 +01:00
Christian
6011ad3d2e The HTTP accessory now supports a body value. 2015-11-02 23:55:15 +01:00
Nick Farina
15cc98217b Move bugged peerDependencies -> engines 2015-11-02 12:04:18 -08:00
Nick Farina
108602a6b0 Merge pull request #361 from kemathy/master
Http accessory improvement
2015-11-02 11:55:42 -08:00
Nick Farina
98d8158d04 Merge pull request #360 from KraigM/hotfix/PhilipsHueUnreachable
Fixed unreachable PhilipsHue bulbs to show up as off.
2015-11-02 11:55:02 -08:00
Nick Farina
a62137477a Merge pull request #359 from KraigM/hotfix/NestHumidity
Fixed humidity on Nest
2015-11-02 11:54:41 -08:00
Nick Farina
386eac8094 Merge pull request #355 from ilcato/patch-1
Added Fibaro Virtual devices management
2015-11-02 11:52:59 -08:00
Nick Farina
9dcebb7320 Merge pull request #353 from thkl/master
HomeMatic Platform - new Devices and Bugfixing
2015-11-02 11:46:01 -08:00
Kevin Mathy
91044f0d92 Http.js shim modification corrections 2015-11-01 22:00:17 +01:00
Kraig McConaghy
82c62f78ed Fixed unreachable PhilipsHue bulbs to show up as off. If you have a manual switch hooked up to a hue bulb, turning it off the switch makes it unreachable but doesn't mark it as off. 2015-11-01 12:42:03 -06:00
Kraig McConaghy
bc1c879412 Fixed humidity and added change handling of settings (like Celsius/Fahrenheit) 2015-11-01 10:16:54 -06:00
Thomas Kluge
c1cc8be8fa new Devices : Smokedetector 2015-11-01 12:57:29 +01:00
Thomas Kluge
bccdbd1755 Merge remote-tracking branch 'homebridge/master' 2015-11-01 12:54:28 +01:00
Mikael Finstad
9f42689256 added serialport library as a dependency 2015-11-01 02:29:11 +01:00
ilcato
73b61fd730 Added Fibaro Virtual devices management 2015-10-31 20:02:29 +01:00
Nick Farina
90feacf53f Merge pull request #352 from planetk/master
Netatmo: new API and added noise and atmospheric pressure
2015-10-31 10:56:41 -07:00
Nick Farina
127741a737 Merge pull request #351 from pedroserano/patch-1
Fix for Issue 343
2015-10-31 10:56:00 -07:00
Thomas Kluge
5c92157009 removed setup 2015-10-31 16:28:51 +01:00
bwilliot
70f2dc6230 Update PhilipsHue.js
fix issue
2015-10-31 16:03:53 +01:00
Thomas König
1dfa190b18 Add HomeMatic Platform sample 2015-10-31 16:03:53 +01:00
Thomas König
7623bf4204 cleanup formatting 2015-10-31 16:03:53 +01:00
Thomas König
6b36ec9dc4 cleanup formatting 2015-10-31 16:03:53 +01:00
Thomas König
b277b76c39 reintegrated Homematic Platform fork from https://github.com/thkl/homebridge/tree/xmlrpc 2015-10-31 16:03:53 +01:00
stipus
3b19613528 SecuritySystem,Window, WindowCovering, obstruction 2015-10-31 16:03:53 +01:00
Stefan Kuper
48bec8a1bc new API and added noise and atmospheric pressure 2015-10-31 14:56:24 +01:00
pedroserano
cfed5d7a66 Fix for Issue 343
Fix for Issue 343 as described here: https://github.com/nfarina/homebridge/issues/343
2015-10-31 09:22:12 -04:00
Nick Farina
75417fb322 Merge pull request #350 from bwilliot/patch-1
Update PhilipsHue.js
2015-10-31 06:18:20 -07:00
Thomas Kluge
04f48ecbab fixed thermostats 2015-10-31 12:02:06 +01:00
bwilliot
ec015e58a3 Update PhilipsHue.js
fix issue
2015-10-31 11:30:01 +01:00
Kevin Mathy
bee6d03d80 New fields for Http Accessory 2015-10-30 23:02:46 +01:00
Kevin Mathy
255a064c08 Http Accessory improvements : accessory type (switch or light), and ligtht brightness handling toggle 2015-10-30 22:55:53 +01:00
Thomas Kluge
727809e9b4 removed some logs
changed to autodetection if hm channel is supported yet
2015-10-30 18:30:07 +01:00
Thomas Kluge
ea1f75abb0 setup.sh complete installation on a pi2 2015-10-30 18:13:31 +01:00
Thomas Kluge
ead491fb4c switch back from xmlrpc branch to master 2015-10-30 16:50:31 +01:00
Nick Farina
7362b48df7 Merge pull request #346 from tkoenig/master
Homematic Platform
2015-10-29 13:36:33 -07:00
Thomas König
5f82fa6fb6 Add HomeMatic Platform sample 2015-10-29 21:04:32 +01:00
Thomas König
fd49b96d78 cleanup formatting 2015-10-29 20:33:07 +01:00
Thomas König
cbc34897f9 cleanup formatting 2015-10-29 19:15:35 +01:00
Thomas König
98c61bc72a reintegrated Homematic Platform fork from https://github.com/thkl/homebridge/tree/xmlrpc 2015-10-29 19:06:47 +01:00
Nick Farina
fe4c63fd75 Merge pull request #342 from stipus/master
Added SecuritySystem,Window/Covering,obstruction
2015-10-29 04:55:57 -07:00
stipus
2e6a487f80 SecuritySystem,Window, WindowCovering, obstruction 2015-10-29 12:16:05 +01:00
stipus
5cf54d7ff6 Merge pull request #2 from nfarina/master
Update fork from nfarina/homebridge/master
2015-10-29 12:12:10 +01:00
Nick Farina
94c5bd6de0 Merge pull request #337 from KraigM/hotfix/PhilipsHue
Fixed issue on Philips Hue where initial state was not being loaded
2015-10-27 21:56:32 -07:00
Kraig McConaghy
e944d3ed2a Fixed issue on Philips Hue where initial state was not being loaded (quick fix). 2015-10-27 23:55:06 -05:00
Nick Farina
3d6ddfc520 Merge pull request #336 from KraigM/feature/Nest
Enhancements to Nest Platform including support for range temperatures
2015-10-27 21:23:42 -07:00
Kraig McConaghy
528c83bd1a Brought Nest feature up to date with master branch 2015-10-27 23:14:25 -05:00
Kraig McConaghy
9740a50520 Several fixes. Full simulated temperature range support. 2015-10-27 23:07:34 -05:00
Nick Farina
af53950080 Merge pull request #335 from patricks/master
Using the new getStationsData() api call
2015-10-27 16:19:30 -04:00
Patrick
909900d025 Using the new getStationsData() api call, the station name is saved to every module name, so we avoid duplicate accessories UUIDs 2015-10-27 20:41:24 +01:00
Patrick
06c7356aef updated to new netatmo node module 2015-10-27 19:24:43 +01:00
Nick Farina
c591cf6899 Merge pull request #334 from dvinc/philips-hue-read
Philips Hue: Read lightbulb status / update to new HAP-NodeJS Service API
2015-10-27 10:29:50 -04:00
Nick Farina
890f6acff0 Merge pull request #331 from KraigM/feature/Wink
Enhancements to Wink Platform including addition of Locks
2015-10-27 10:20:38 -04:00
Vincent Drevelle
debbc14fef Fix case in hap-nodejs module require statements 2015-10-27 15:07:15 +01:00
Vincent Drevelle
5e9c3a3b62 Merge with upstream 2015-10-27 11:02:27 +01:00
Kraig McConaghy
1f8e6e5c8b Added WinkLockAccessory 2015-10-26 22:49:36 -05:00
Kraig McConaghy
35ecb9e170 Switched to use the new HAP APIs, extracted base class WinkAccessory, and changed update system to properly notify changes on a timed basis. Functionality wise, left the LightBulb implementation the same. 2015-10-26 22:48:02 -05:00
fcarucci
b65acecb28 Merge remote-tracking branch 'upstream/master' 2015-10-26 13:01:29 -07:00
Nick Farina
e78a68352f Merge pull request #327 from tommasomarchionni/patch-1
Update Openhab.js with new structure.
2015-10-26 13:24:09 -04:00
Nick Farina
b21c37712d Merge pull request #325 from patricks/master
Fixed netatmo problem with duplicate module names
2015-10-26 13:23:45 -04:00
Tommaso Marchionni
65d732415e Update Openhab.js 2015-10-26 00:32:40 +01:00
Patrick
dc6faa9784 fixed netatmo problem with duplicate module names 2015-10-25 21:57:46 +01:00
Nick Farina
3da52388e8 Merge pull request #322 from mvanholstyn/master
adding support for usernames and password in http accessories
2015-10-25 10:03:31 -04:00
Mark Van Holstyn
ecdffbef23 adding support for usernames and password in http accessories 2015-10-24 23:22:23 -04:00
Francesco Carucci
76136589f4 LiftMaster: return current door state 2015-10-24 21:46:24 +00:00
Francesco Carucci
b4acdf3865 Change the garage door accessory name 2015-10-24 21:45:44 +00:00
Nick Farina
9a798855f5 Merge pull request #319 from planetk/netatmo
netatmo weatherstation first implementation
2015-10-24 14:52:30 -04:00
Stefan Kuper
6e156e48fb netatmo first imply 2015-10-24 20:06:30 +02:00
Nick Farina
d2fd510aa4 Merge pull request #317 from doob/master
Handle where no model is returned from Telldus Live
2015-10-24 09:53:18 -04:00
Nick Farina
45af486cfd Merge pull request #314 from KraigM/feature/PhilipsHue
Fixed initial color of Philips Hue
2015-10-24 09:52:57 -04:00
Nick Farina
d866f9abb6 Merge pull request #313 from fcarucci/master
Fix MyQ Garage Opener and multiple Nests configuration
2015-10-24 09:52:02 -04:00
Dennis Soderstrom
e72309fab0 Handle where no model is returned from Telldus Live 2015-10-24 12:39:43 +02:00
Kraig McConaghy
1ad323b0e8 Fixed initial color calc to range from 0-360 (as documented) instead of 0-100 (likely typo) 2015-10-23 19:53:02 -05:00
Francesco Carucci
1f7db5e661 MyQ: check MyQDeviceTypeName against 'VGDO' as well 2015-10-23 23:56:54 +00:00
Francesco Carucci
e2157aed9c Append id to nest accessory name 2015-10-23 13:09:24 -07:00
Nick Farina
48e0f8b812 Merge pull request #310 from snowdd1/master
Fix vindex variable scope for address registration
2015-10-23 06:50:22 -07:00
Snowdd1
b09835600d Variable out of scope 2015-10-23 12:04:27 +02:00
Snowdd1
afef06467d Merge remote-tracking branch 'refs/remotes/nfarina/master' 2015-10-23 12:01:12 +02:00
Nick Farina
386636975b Merge pull request #299 from KraigM/feature/Harmony
Enhanced and Fixed Logitech Harmony
2015-10-21 21:15:22 -07:00
Kraig McConaghy
bfc151e70a Updated to official version of harmonyhubjs-client package now that the fix has been merged/released 2015-10-21 18:01:43 -05:00
Nick Farina
8d7f986f84 Merge pull request #305 from mariodrengner/patch-1
Ignore Bridge
2015-10-21 06:23:19 -07:00
Mario Drengner
997906bab8 Ignore Bridge
No need to control the bridge
2015-10-21 14:35:35 +02:00
Pierre-Julien Cazaux
de06a2b12d Semi working HTTP garage door implementation
Currently, when use « Siri, open/close garage door » got the correct
answer on logs but my iPhone reply something like « The garage door in
now wrong » and the Insteon+ app icon is not updated.

I quite beginner I would like a hint :) THX
2015-10-21 10:59:13 +02:00
Nick Farina
1779ddd754 Fix "once" import 2015-10-20 12:00:27 -07:00
Nick Farina
d443286dcb Fix platform logger 2015-10-20 11:36:06 -07:00
Nick Farina
74f0f00321 Fix stray comma 2015-10-20 09:31:57 -07:00
Nick Farina
1a95978e0a Merge pull request #301 from SphtKr/zway-door-options
Z-Way Door sensor options
2015-10-20 06:34:41 -07:00
S'pht'Kr
13e8846138 Support Door, Window, and ContactSensor for sensorBinary.Door/Window 2015-10-20 07:04:18 +02:00
Kraig McConaghy
b5210f424f More fixes 2015-10-19 23:16:31 -05:00
Nick Farina
7c7ac5d828 Merge pull request #294 from rodtoll/master
Enhanced ISY-994i support
2015-10-19 19:48:21 -07:00
rodtoll
70ec9a8891 Fixed bug in alarm state. Was using alarm state instead of mode to translate current state. 2015-10-19 19:38:14 -07:00
rodtoll
00574f60b4 Brought in latest 2015-10-19 19:01:53 -07:00
Nick Farina
2c36f1745d Merge pull request #298 from justme-1968/master
this is the latest update to the FHEM platform shim
2015-10-19 13:26:29 -07:00
Andre Schröter
8ebd6ecdd2 hap-nodejs vs. HAP-NodeJS fix 2015-10-19 22:08:07 +02:00
Andre Schröter
e9e637c43d Merge remote-tracking branch 'upstream/master' 2015-10-19 21:16:35 +02:00
Andre Schröter
d7bca45e70 debug browser formating change 2015-10-19 21:13:44 +02:00
Kraig McConaghy
e8553c7203 Merge branch 'feature/Harmony'
Conflicts:
	package.json
2015-10-19 14:02:27 -05:00
Kraig McConaghy
626871680c Reorganized dependencies to help with merge 2015-10-19 13:46:06 -05:00
Andre Schröter
42f88864f9 check for hap-nodejs and HAP-NodeJS
allow longpoll to multiple fhem servers
increase reconnect timeout after subsequent longpoll disconnect and error
added target door state for HM-SEC-KEY -> allows open to be send
added current door state to contact sensor -> allows to ask for open windows with siri
some cleanups
2015-10-19 20:00:02 +02:00
rodtoll
0cb57d2a44 Improper constants for the fan speed. Fixing. 2015-10-19 08:03:42 -07:00
Nick Farina
92667b5666 Merge pull request #297 from tommasomarchionni/master
Integrate OpenHAB Platform
2015-10-19 07:03:14 -07:00
tommasomarchionni
7b24d142b4 Create Openhab.js 2015-10-19 14:52:08 +02:00
Nick Farina
27b39cbfa0 New Plugin API
- Homebridge calls single exported initializer function and passes an
API object
  - No more require() for HAP classes (doesn't play well with plugin
structure)
2015-10-18 22:20:06 -07:00
rodtoll
74e37cc488 Added more documentation. 2015-10-18 21:09:59 -07:00
rodtoll
8fcd5d39a6 Moved all debug logging to be controlled by the ISYJSDEBUG environment variable AND moved all log messages to go through the global logger offered by homebridge. 2015-10-18 19:47:30 -07:00
Kraig McConaghy
6604c2b69a Changed structure to handle numerous issues plus add enhancements. No activity off properly calls "Power Off" (and thus "Power Off" has been removed). HomeKit responses are properly set back when activity finishes switching. Some amount of handling has been added for timeouts while switching activities as well as request sync (you don't seem to be able to send multiple requests to the hub simultaneously, and the activity switching sometimes times out the connection, so a reconnect system was added). 2015-10-18 19:08:46 -05:00
Nick Farina
a3c0df1c7c Plugin support
- Homebridge is now designed to be `npm install`d globally and
executed via "homebridge" script
  - Remove all specific accessories/platforms except for an example
  - New internal structure and "cli"
2015-10-18 16:34:21 -07:00
Nick Farina
6e5c35ec88 Restore telldus-live
See #277 for discussion.
2015-10-18 14:23:00 -07:00
Kraig McConaghy
e30b504583 Added handling to keep track of activity state 2015-10-18 16:21:57 -05:00
Nick Farina
7b110191f9 Merge pull request #289 from MSchmidt/master
Change all HAP-NodeJS require calls to lowercase
2015-10-18 14:17:03 -07:00
Nick Farina
74a72c1096 Merge pull request #287 from webdeck/indigo
Graceful handling of JSON parse errors from Indigo responses.
2015-10-18 14:16:16 -07:00
Nick Farina
5066b108d2 Merge pull request #286 from dgarozzo/master
add async dependency for Indigo
2015-10-18 14:16:01 -07:00
Kraig McConaghy
6008374b9e Converted LogitechHarmony accessory to be based on new HAP api 2015-10-18 14:00:06 -05:00
Kraig McConaghy
3c53b319cb Updated to fixed harmonyhubjs-client 2015-10-18 13:58:10 -05:00
rodtoll
2d80e5f838 Updated to latest version of hap base library. Root project migrated to lower case version. Also added new uuid_base required field which is now needed. Refactored device constructors to use common code. 2015-10-18 11:19:08 -07:00
Kraig McConaghy
b2aa70d40a Merge commit '2652f33a0a2de8566df0d346ec19c2bc1ceaff9d' into feature/Harmony 2015-10-18 13:04:51 -05:00
rodtoll
a45779cf48 Merge branch 'master' of https://github.com/nfarina/homebridge
pulling down latest changes.
2015-10-18 10:19:14 -07:00
Andre Schröter
bf125078a1 added first version of HM-SEC-KEY-S
some bug fixes
2015-10-18 18:25:31 +02:00
Nick Farina
7ce190a7d0 Merge pull request #285 from masonjames/master
Update hap-nodejs requires
2015-10-18 08:16:51 -07:00
Matthias Schmidt
915e674583 Change all HAP-NodeJS require calls to lowercase 2015-10-18 13:29:50 +02:00
Mike Riccio
ad62bd4b15 Graceful handling of JSON parse errors from Indigo responses.
Make initial discover queries serialized to not overwhelm slow Indigo servers.
2015-10-17 20:27:04 -07:00
Kraig McConaghy
c897913005 Initial work. Still in progress 2015-10-17 20:51:57 -05:00
David Garozzo
15e9219c80 add async dependency for Indigo 2015-10-17 21:43:43 -04:00
Mason James
38cb94c012 Update hap-nodejs requires
Update remaining platforms and accessories
2015-10-17 18:41:57 -04:00
Nick Farina
ffc4edbc2a Merge pull request #284 from williambout/patch-1
Update Hyperion.js
2015-10-17 09:44:06 -07:00
William Bout
196cda8063 Update Hyperion.js
Fix require error
2015-10-17 18:12:11 +02:00
Nick Farina
3d03824e6a Merge pull request #282 from tommasomarchionni/patch-1
Update app.js
2015-10-17 05:49:21 -07:00
tommasomarchionni
385908fa89 Update app.js 2015-10-17 13:23:30 +02:00
tommasomarchionni
f2d22584fc Update app.js 2015-10-17 12:34:40 +02:00
Nick Farina
97cb18389f Merge pull request #279 from webdeck/indigo
Fix hap-nodejs import for Indigo platform
2015-10-16 22:21:26 -07:00
Mike Riccio
896d6b40d9 Fixed fahrenheit units, was giving error. 2015-10-16 20:36:32 -07:00
Mike Riccio
c5499c122e Add devices even if discovery errors.
Don't assign everything POWER_STATE_CTYPE.
2015-10-16 20:21:30 -07:00
Nick Farina
831480d035 Remove telldus for now 2015-10-16 10:40:04 -07:00
Nick Farina
286f700622 Merge pull request #273 from webdeck/indigo
Add platform support for Indigo server (http://www.indigodomo.com/)
2015-10-16 10:31:14 -07:00
Nick Farina
ecf9afee64 Merge pull request #275 from MSchmidt/master
Support for case-sensitive file systems
2015-10-16 10:21:11 -07:00
Matthias Schmidt
bb1c193bc0 Use npm release of hap-nodejs 2015-10-16 19:10:14 +02:00
Mike Riccio
c22c14584d Added more logging when there's an error getting device details. 2015-10-16 09:36:18 -07:00
stipus
c6b248e0ea Merge pull request #1 from nfarina/master
sync with nfarina/homebridge
2015-10-16 17:21:55 +02:00
Nick Farina
2df47b9893 Merge pull request #276 from stipus/master
GarageDoorOpener and Lock support
2015-10-16 06:11:18 -07:00
stipus
23f190e3d8 GarageDoorOpener and Lock support
- Smoke sensor battery fix
 - Added offEventGroup && offEventName to events (turn <event> on launches one HS event. turn <event> off can launch another HS event)
 - Added GarageDoorOpener support
 - Added Lock support
2015-10-16 13:00:18 +02:00
Matthias Schmidt
7233c5bf74 Support for case-sensitive file systems and current HAP-NodeJS with nodejs 4 support 2015-10-16 10:50:55 +02:00
Mike Riccio
aad811fe6e Fixed thermostats to report celsius. 2015-10-15 21:31:08 -07:00
Nick Farina
bab1eb730e Version bump to node-icontrol 2015-10-15 20:08:25 -07:00
Mike Riccio
6fa69c5c4b Removed tabs 2015-10-15 16:47:59 -07:00
Mike Riccio
9ad8a111c6 Added intro comment. 2015-10-15 15:07:14 -07:00
Mike Riccio
e40f10b82e Initial support for Indigo server (http://www.indigodomo.com) 2015-10-15 14:25:20 -07:00
S'pht'Kr
d5e4714551 Merge branch 'nfarina/master' 2015-10-15 05:52:26 +02:00
S'pht'Kr
81dbb285c7 Merge branch 'nfarina/master' 2015-10-15 05:50:52 +02:00
Nick Farina
99910960ae Merge pull request #268 from stipus/master
Added CarbonMonoxide and CarbonDioxide sensor support
2015-10-14 13:45:13 -07:00
stipus
a056b16c35 Added CarbonMonoxide and CarbonDioxide support
- Added CarbonMonoxide and CarbonDioxide sensor support
- Added onValues option to all binary sensors
- Added uuid_base parameter to all HomeSeer accessories
2015-10-14 22:06:11 +02:00
Nick Farina
a05ba696b6 Merge pull request #261 from iRaven/master
Extend/Add Homematic Accessories (get state for switch, thermostat & window support)
2015-10-13 10:01:44 -07:00
Nick Farina
60c48fc4cb Merge pull request #260 from SphtKr/zway-motionsensor-and-fixes
Z-Way Motion Sensor support and minor fixes/updates
2015-10-13 09:30:00 -07:00
iRaven
d8a3596326 Added HomematicThermo, HomematicWindow 2015-10-13 17:39:28 +02:00
iRaven
7c7ceb6453 Rename HomeMaticThermo to HomeMaticThermo.js 2015-10-13 13:06:11 +02:00
S'pht'Kr
fdbd33f29d Updated for new tweaked "setProps" API, removed cruft. 2015-10-13 06:50:23 +02:00
S'pht'Kr
c48c750917 MotionDetector added, with new needed configuration tags. 2015-10-13 05:37:44 +02:00
Nick Farina
d1e186ed99 Merge pull request #259 from stipus/master
Added support for humidity sensors, battery level sensors, low battery flag for all sensors, and ability to run HomeSeer events
2015-10-12 16:03:59 -07:00
stipus
1f8db79324 Added support for humidity sensors, battery level sensors, low battery flag for all sensors, and ability to run HomeSeer events
- Added Humidity sensor support
- Added Battery support
- Added low battery support for all sensors
- Added HomeSeer event support (using HomeKit switches...)
2015-10-13 00:53:19 +02:00
iRaven
d4279c5ef5 Added Support for Thermostat HM-CC-RT-DN 2015-10-12 18:12:14 +02:00
S'pht'Kr
e7ce658c3d Merge branch 'nfarina/master' into zway-motionsensor-and-fixes 2015-10-12 05:44:32 +02:00
Nick Farina
ff5762ad70 Merge pull request #258 from stipus/master
HomeSeer.js - Added thermostat support
2015-10-11 19:25:36 -07:00
stipus
c59b87d17d Update HomeSeer.js 2015-10-12 02:15:41 +02:00
stipus
41e6c20a61 Update HomeSeer.js 2015-10-12 02:11:24 +02:00
stipus
c506c44d60 Update HomeSeer.js 2015-10-12 02:02:37 +02:00
rodtoll
6030928c12 Fixed elk panel state transitions. 2015-10-11 13:34:58 -07:00
rodtoll
40a96808b2 Added docs to the isy-js file. 2015-10-11 08:45:18 -07:00
Nick Farina
59692df1a0 Merge pull request #248 from theodorton/telldus
Add support for local Telldus control
2015-10-11 08:21:44 -07:00
Nick Farina
ac1da10ea8 Merge pull request #251 from stipus/master
Fix for HomeSeer occupancy sensor
2015-10-11 08:21:15 -07:00
Nick Farina
55a17f7919 Merge pull request #253 from stevetrease/master
Added readonly thermometer and hygrometer accessories
2015-10-11 08:21:01 -07:00
Nick Farina
c6ed47cf38 Merge pull request #252 from SphtKr/zway-more-tags
Several additional tag-based configuration options
2015-10-11 08:20:47 -07:00
Nick Farina
a435cf2b00 Merge pull request #254 from SphtKr/yamaha-issue-247
YamahaAVR Issue #247
2015-10-11 08:19:23 -07:00
rodtoll
e4fa276de2 Adding isy-js based ISY platform support 2015-10-11 08:14:48 -07:00
S'pht'Kr
a274ae4eda Switches to the *new* new Characteristics API format for the two custom Characteristics.
Fixes #247
2015-10-11 05:44:44 +02:00
iRaven
05e811cdd6 Added HM-Sec-RHS Support as contact 2015-10-10 15:56:51 +02:00
S'pht'Kr
25299a7863 Last bits… 2015-10-10 15:45:27 +02:00
stevetrease@gmail.com
4a831422eb Added two new accessories for a readonly thermometer and hygrometer (humidity meter) based on HttpAccessory. 2015-10-10 14:41:00 +01:00
iRaven
b4f4f58519 Added Get-State Function 2015-10-10 14:05:16 +02:00
stipus
4fbd7eb775 Fix for HomeSeer occupancy sensor 2015-10-10 12:38:46 +02:00
Theodor Tonum
c0dfc9a8cd Add support for local Telldus control 2015-10-10 01:14:10 +02:00
S'pht'Kr
d5c4dfce1f Merge branch 'nfarina/master' into zway-more-tags 2015-10-09 06:05:44 +02:00
Nick Farina
fd420c7795 Merge pull request #223 from SphtKr/zway-rgb
RGB(+W+W) support
2015-10-08 13:37:58 -07:00
Nick Farina
8fe5f8b2aa Merge pull request #242 from ethansinjin/patch-1
Simple fix for undefined Nest device names
2015-10-08 10:57:24 -07:00
Ethan Gill
d8a21133e9 Simple fix for undefined Nest device names 2015-10-08 13:50:34 -04:00
Andre Schröter
490ed5db40 added EnOcean blinds 2015-10-08 18:55:30 +02:00
Nick Farina
9096e64891 Merge pull request #239 from stipus/master
Create HomeSeer.js
2015-10-08 09:40:11 -07:00
stipus
772f35efac Create HomeSeer.js 2015-10-08 09:07:33 +02:00
Snowdd1
d914b334a9 Merge remote-tracking branch 'nfarina/master' 2015-10-07 21:52:52 +02:00
Nick Farina
2b84fc1783 Merge pull request #238 from SphtKr/yamahaavr-manual-ip
YamahaAVR manual address specification
2015-10-07 12:51:20 -07:00
Nick Farina
64864bac85 Merge pull request #233 from snowdd1/UUID-creation
New UUID generation
2015-10-07 12:39:36 -07:00
Snowdd1
ed8a3c8062 uuid_base in constructor 2015-10-07 10:39:00 +02:00
S'pht'Kr
a15c026f2f Manual address specification working now 2015-10-07 06:59:04 +02:00
S'pht'Kr
d66ab79e8e Merge branch 'nfarina/master' 2015-10-07 05:38:46 +02:00
S'pht'Kr
af79ea4fbf Beginning tag support enhancements
Generalized tag recognition, tags are now `Homebridge.*` instead of
`Homebridge:*`, initial attempt at `IsPrimary` but probably not working
right yet.
2015-10-07 05:38:24 +02:00
Snowdd1
e6afda55d6 New UUID generation
addresses #203

# This is a breaking change for all configured homekit databases, as it
will remove room/area assignments and other things configured in iOS
apps
... unless your accessory type was called `Object` before

Changes:
- Without any changes to the configuration file all accessories UUIDs
will be generated by  `uuid.generate(accessoryType + ":"` instead of the
constructor name which turned out to be unreliable in that context. So
about all UUIDs will change.
- new parameter uuid_base can be used for accessories in config.json to
use a different base for UUID generation than the displayName, which
might not be unique
- platforms can add the same property to their accessories before
returned, just as they have a name property right now.
2015-10-06 20:47:07 +02:00
Snowdd1
b9fb57dd7a Merge remote-tracking branch 'nfarina/master' 2015-10-06 18:11:16 +02:00
Nick Farina
feca9fbf0d Support WeMo "Light" services 2015-10-05 17:29:53 -07:00
Nick Farina
b66610ce93 Update Lockitron state on successful change 2015-10-05 17:12:06 -07:00
Andre Schröter
c24a94c072 added ROOMMATE devices 2015-10-05 12:00:43 +02:00
Mike Enriquez
2652f33a0a send command every 20s to prevent timing out 2015-10-03 22:04:51 -04:00
Nick Farina
2eb82bcc98 Merge pull request #226 from snowdd1/master
Fix json in example in KNX.md
2015-10-03 07:37:35 -07:00
Raoul
ac62a44ce2 Updates to wrong json Examples in KNX.md doc file
Missing quotes around new key names.
2015-10-03 14:48:06 +02:00
Raoul
a55b166131 Merge pull request #6 from nfarina/master
Update to nfarina/master
2015-10-03 14:38:57 +02:00
Nick Farina
d3f0c73393 Merge pull request #225 from snowdd1/knx-dev
Update of handling of binary and percentage type KNX values
2015-10-02 09:22:36 -07:00
Snowdd1
c36ad9d631 Merge remote-tracking branch 'nfarina/master' into knx-dev 2015-10-02 18:06:23 +02:00
Snowdd1
3031ba7e0f Refactoring of reversely used group addresses
The use of 0/1 for binary objects depends on installation, a contact
sensor can have contact=1 or open=1
The same applies to percentages:  a window can be 100% closed or 100%
open.

For a more general usage of reversed values, a suffix ("R") can now be
attached to all DPT1 and DPT5 group addresses.
Please see updated KNX.md for examples and details.

Note: outdated characteristic-workaround throw an exception at boot-up:
-  ContactSensorStateContact1
-  LockCurrentStateSecured0
-  LockTargetStateSecured0
Please see error message and KNX.md for reference.
2015-10-02 18:05:40 +02:00
Snowdd1
fd72307384 Fix hint in KNX-sample-config.json 2015-10-02 08:17:59 +02:00
S'pht'Kr
d04d417344 Initial read/write support for RGB bulbs complete
Needs testing, but seems to work with my Aeon bulb…taking into account
the wonkiness of that bulb with Z-Way, at least.
2015-10-02 06:19:59 +02:00
Mike Enriquez
98519c84dd Fix crash when trying to get/set power state
- Call getPowerState on the object instead of passing the function to
  fix undefined isActivity error
- Remove callback param from setPowerState since it is not called with a
  callback param.
- startActivity expects the activity id as a parameter. We can't
  actually toggle activities off and on. There is a "PowerOff" activity
  that will turn everything off.
2015-10-01 23:08:36 -04:00
Mike Enriquez
3bca9f70f5 freeze node-xmpp-client version to working version 2015-10-01 20:39:00 -04:00
Nick Farina
1bb8d00884 Merge pull request #221 from Mikulas/config-lifx
add lifx to sample config
2015-10-01 14:25:50 -07:00
Mikulas
45f40874ef add lifx to sample config 2015-10-01 23:22:17 +02:00
Jon Maddox
fe5db08081 Merge pull request #219 from nfarina/ignore-hidden-ha-devices
Ignore Hidden Home Assistant Devices
2015-10-01 01:30:28 -04:00
Jon Maddox
1fce5c8754 ignore hiddent Home Assistant devices 2015-10-01 01:29:42 -04:00
Nick Farina
8e23024ef0 Merge pull request #217 from snowdd1/knx-dev
Characteristic.setProps() adaptation
2015-09-30 14:37:11 -07:00
Snowdd1
98ee9f8f95 Return value guarding; Cleaner logs 2015-09-30 22:07:33 +02:00
Snowdd1
bd979ea1f8 Merge remote-tracking branch 'nfarina/master' into knx-dev 2015-09-30 20:40:03 +02:00
Snowdd1
54f0c2f0cb Doc updates 2015-09-30 20:03:20 +02:00
Raoul
ba5722c517 props update minValue, maxValue
used in Temperature devices (Apple's default values too narrow for beer coolers I've heard)

adapted call syntax for "Simplify Characteristic properties"
1d84d128d1 (diff-cb84de3a1478a38b2cf8388d709f1c1cR108)
2015-09-30 09:21:59 +02:00
Andre Schröter
2d88dafa11 disabled thermostat mode
some cleanups
added timestamp to longpoll console output
2015-09-29 22:45:34 +02:00
Vincent Drevelle
84cac442e2 Read Philips Hue lights status from bridge.
Update the PhilipsHue.js platform to use HAP-NodeJS AccessoryInformation and LightBulb Service classes.
2015-09-29 11:59:58 +02:00
S'pht'Kr
99da61d30a Lost some subtype parameters in deconfliction 2015-09-29 07:06:05 +02:00
S'pht'Kr
0b3930e458 Erm...oops. 2015-09-29 06:59:40 +02:00
S'pht'Kr
ce35600b34 Merge remote-tracking branch 'nfarina/master' into zway-rgb
Conflicts:
	platforms/ZWayServer.js
2015-09-29 06:58:06 +02:00
S'pht'Kr
7aa758cb04 Working towards getting the extra dimmers in one accessory
Having trouble with service subtypes…hmm…
2015-09-29 06:53:22 +02:00
S'pht'Kr
bd6b8478a9 Merge branch 'nfarina/master' 2015-09-29 05:24:25 +02:00
Nick Farina
2e24e98227 Merge pull request #210 from SphtKr/yamaha-issue-153
YamahaAVR: Fix for multiple devices, refactor, and custom Service+Characteristic
2015-09-28 11:02:33 -07:00
S'pht'Kr
817f7ecf20 Merge branch 'nfarina/master' 2015-09-28 06:27:09 +02:00
S'pht'Kr
2a66eac058 Fix for multiple devices, refactor, and custom Service+Characteristic 2015-09-28 06:12:25 +02:00
Nick Farina
b54d37fb2d Merge pull request #174 from ilcato/ilcato-fibarohc2
Platform shim for Fibaro Home Center 2
2015-09-27 06:49:56 -07:00
ilcato
c6c45d9e3d Fixed incompatibility with new version of HAP-Node.js 2015-09-27 13:00:57 +02:00
Andre Schröter
78394bc95d added CurrentAmbientLightLevel characteristic 2015-09-25 22:43:57 +02:00
Andre Schröter
bfbaa74375 added firmware revision characteristic
fix for genericDeviceType ignore
some more fixes
set uuid_base to real device serial number if available
2015-09-25 19:20:54 +02:00
Nick Farina
bb3381e10a Update README.md 2015-09-24 22:15:58 -07:00
Nick Farina
5323b0afe1 Merge pull request #204 from derekforeman/master
Noted updated for requirement to successfully get HAP-NodeJS installed.
2015-09-24 22:10:30 -07:00
Derek Foreman
ca232f3111 Noted updated for requirement to successfully get HAP-NodeJS installed. 2015-09-25 00:26:07 -04:00
Andre Schröter
c9166f6ff0 Merge remote-tracking branch 'upstream/master' 2015-09-22 20:46:56 +02:00
Andre Schröter
afcb86ef0e changed harmony device to a single accessiory with mutliple switches
added hue colormode & xy to hsv conversion
some small cleanups
2015-09-22 18:53:19 +02:00
Andre Schröter
2a63a9ff2d adde MAX ShutterContact 2015-09-21 19:48:51 +02:00
Andre Schröter
1b69cffeb2 removed debug output 2015-09-21 18:50:40 +02:00
Andre Schröter
7695eced0d publish accessories only once
parse min/max/step values for thermostats
use this/bind instead of that
added genericType ignore
added batter state charactersitic
preliminary thermostat controll mode
use new characteristic.setProps() method
some minor cleanups
2015-09-21 18:29:01 +02:00
Nick Farina
625b9f47df Bump HAP-NodeJS
Closes #181
2015-09-21 08:17:48 -07:00
Nick Farina
c3ad5f26da Merge pull request #191 from luxus/patch-1
fixing thermostat.
2015-09-20 16:28:57 -07:00
Kai
67fb6e8b4d fixing thermostat.
switch the order of the characteristics
now my stellaZ thermostat is shown correct in apps like eve and insteon+
2015-09-21 00:59:26 +02:00
Nick Farina
b24a611fab Merge pull request #189 from snowdd1/knx-dev
KNX Platform: "Garage Door Opener" and "Motion Sensor" services
2015-09-19 13:00:48 -07:00
Snowdd1
80b2a047d4 Typo fixes 2015-09-19 21:56:20 +02:00
Snowdd1
48f613f8ae New Motion Sensor service 2015-09-19 21:47:07 +02:00
Snowdd1
84c51aa27a GarageDoorOpener fixed 2015-09-19 21:24:55 +02:00
Snowdd1
eec663a5c8 Routines for unsigned int values, such as enumeration like types 2015-09-19 18:37:19 +02:00
Snowdd1
387e7ec9ce KNX support for Garage Door Opener
The garage door opener device MUST adhere to HomeKit numeric
conventions, see KNX.md documentation
2015-09-19 15:19:11 +02:00
S'pht'Kr
6b0d701570 Merge branch 'nfarina/master' 2015-09-19 13:00:16 +02:00
S'pht'Kr
656a8057ac Initial work on reading, seems to work okay. 2015-09-19 13:00:01 +02:00
ilcato
24fbe4158d Added support for Fibaro Outlet 2015-09-19 10:12:35 +02:00
Nick Farina
1dcfe4a1fb Merge pull request #188 from snowdd1/knx-dev
KNX cleanup
2015-09-18 09:25:04 -07:00
ilcato
32877abc98 Complete refactoring and adoption of new API
Added support for LightSensor
2015-09-18 17:33:36 +02:00
Snowdd1
91f6ccb2d4 Added LightSensor Service 2015-09-18 13:18:27 +02:00
Snowdd1
93ea0deded Cleanup and new devices
Added services:
-  Switch
-  Outlet
2015-09-18 10:28:31 +02:00
Snowdd1
f802d84509 Even more markdown formatting 2015-09-18 09:47:52 +02:00
Snowdd1
24a8dcb8cc more markdown formatting 2015-09-18 09:45:08 +02:00
Snowdd1
76a5fd3b7f markdown \<BR\> test 2015-09-18 09:40:38 +02:00
Snowdd1
c7ab475dd4 Cleanup KNX file names and markdown
I found that the description was wrongly named and in the wrong folder.
It is now named after the platform shim to appear next to it in
listings, also the config-sample that comes along with it.

As long as we do not go for own repositories and npm packages, should we
consider folders for additional files if required?
2015-09-18 09:37:10 +02:00
Snowdd1
6f77e55c7b Merge remote-tracking branch 'nfarina/master' into knx-dev 2015-09-18 09:25:35 +02:00
Snowdd1
e7f35dfcbb Merge remote-tracking branch 'nfarina/master' 2015-09-18 08:52:55 +02:00
Andre Schröter
ddf9fa01cf fixed PossibleSets regex again... 2015-09-17 21:58:51 +02:00
Nick Farina
73148b060d Remove serial port dependency for now 2015-09-17 12:51:55 -07:00
Nick Farina
5720e82abd Remove test code from WeMo 2015-09-17 12:50:29 -07:00
Andre Schröter
6f14108905 fix for PossibleSets regex
first version of logitech harmony support (device and activity level)
2015-09-17 20:37:11 +02:00
Nick Farina
6fe4fe8249 Merge pull request #184 from Cosmo/master
Print scannable setup code to terminal on startup
2015-09-17 09:00:01 -07:00
Devran Ünal
a05a4b6f71 Print the setup code just before publishing the bridge 2015-09-17 17:36:29 +02:00
Devran Ünal
2135e7eccb Print scannable setup code to terminal on startup 2015-09-17 17:00:52 +02:00
Nick Farina
dbb130be02 Merge pull request #182 from Cosmo/master
Add shim for devices with serial connection
2015-09-17 06:23:12 -07:00
Devran Ünal
72b9175b78 Set siri name based on accessory name 2015-09-17 15:22:02 +02:00
Devran Ünal
65ec517fd8 Update to modern API and return error messages on failed connections 2015-09-17 11:37:20 +02:00
Devran Ünal
aa823baa8e Add shim for devices with serial connection (video projectors, screens, receivers, ..) 2015-09-17 02:03:53 +02:00
Nick Farina
118a4529b9 Merge pull request #179 from snowdd1/knx-dev
More iOS9 services
2015-09-16 13:01:09 -07:00
ilcato
9580d259db Fixed status update 2015-09-16 21:56:09 +02:00
Snowdd1
ace364644e MOve config-sample-knx.json to platforms/ 2015-09-16 21:22:49 +02:00
Snowdd1
0da4fe5d22 Welcome iOS9
More services, documentation, and cleanups.
2015-09-16 20:05:02 +02:00
Snowdd1
cd33f2e6c8 Merge remote-tracking branch 'nfarina/master' into knx-dev 2015-09-16 19:15:56 +02:00
Snowdd1
68f764864d Merge remote-tracking branch 'nfarina/master' 2015-09-16 19:12:00 +02:00
Nick Farina
2c118e9649 Pull in latest HAP fixes 2015-09-16 07:45:46 -07:00
Nick Farina
66439f6353 Merge pull request #177 from nfarina/ha-scenes
Home Assistant Scenes
2015-09-16 07:17:40 -07:00
Jon Maddox
773eb8fd0e some docs 2015-09-16 03:21:24 -04:00
Jon Maddox
651cdfa786 close it 2015-09-16 03:16:49 -04:00
Jon Maddox
0f89a6ae36 add new supported_types key for HA to sample config 2015-09-16 03:15:58 -04:00
Jon Maddox
ec8b556618 return informationServices for HA devices 2015-09-16 03:15:37 -04:00
Jon Maddox
eb6c881d28 support loading scenes from HA 2015-09-16 03:14:22 -04:00
Jon Maddox
7f753f79f6 HA switch takes a type argument
This lets you pass the domain to the switch to let it be other types of
objects, like say, a scene.
2015-09-16 03:14:09 -04:00
Jon Maddox
f3e08b0a15 add device types from Home Assistant based on the supported types declared in the config
Instead of matching with a regex, we use a config of supported device
types to attempt to load in. This allows the user to whitelist just the
ones they want to add.
2015-09-16 03:12:56 -04:00
S'pht'Kr
e06e982063 Merge branch 'nfarina/master' 2015-09-16 05:49:53 +02:00
Nick Farina
f9465ebc8e Merge pull request #175 from justme-1968/master
switched fhem platform shim to new api
2015-09-15 14:07:07 -07:00
Andre Schröter
2a430ad75c Merge remote-tracking branch 'upstream/master' 2015-09-15 22:58:01 +02:00
Andre Schröter
8ceaccfca4 cleanups, fixes and more new api stuff 2015-09-15 22:03:17 +02:00
Nick Farina
d6b3fc7667 Bump HAP-NodeJS with fixes for Node 4.0.0 2015-09-15 10:58:30 -07:00
Nick Farina
abd848fd31 Merge pull request #173 from SphtKr/filedate-sensor
File-based motion or contact sensor
2015-09-15 10:25:03 -07:00
ilcato
a041407104 Added automatic status update through event mechanism 2015-09-15 19:03:08 +02:00
ilcato
d36827f92d Fixed comments 2015-09-15 14:26:42 +02:00
ilcato
983f136271 Create FibaroHC2.js 2015-09-15 13:57:21 +02:00
S'pht'Kr
13d1ed75cf File-based motion or contact sensor
This module creates a motion sensor accessory based on watching a
directory or file.
2015-09-15 06:55:53 +02:00
Jon Maddox
030fec8cbf Merge pull request #170 from nfarina/ha-media-players
Home Assistant Media Players
2015-09-14 22:23:34 -04:00
Jon Maddox
be589d4fb5 you can do this too 2015-09-14 20:46:51 -04:00
Jon Maddox
1df32fca3d add some docs 2015-09-14 20:20:26 -04:00
Jon Maddox
e894b1a1a1 support media players to be addressable as lights 2015-09-14 20:14:27 -04:00
Andre Schröter
f4160c2d01 added identify 2015-09-14 22:43:37 +02:00
Andre Schröter
9f39b49dd5 complete switch to new api
basic device (light,hue,swap,thermometer,thermostat) are working
still problems with custom characteristic volume
2015-09-14 22:10:14 +02:00
Nick Farina
f658ca90c1 Merge pull request #169 from lukeredpath/fix-domoticz-dimmer-handling
Fix domoticz dimmer handling
2015-09-14 12:00:41 -07:00
Luke Redpath
7f14df0434 Honor the MaxDimLevel property of accessories.
LightwaveRF lights could not be dimmed properly as they require a dim level
of betwene 0-32. The Domoticz documentation incorrectly states this should be
0-16.

Other devices may use different values which is why the MaxDimLevel property
should be used rather than hardcoded value.

Also refactored the code slightly.
2015-09-14 19:40:04 +01:00
Luke Redpath
c88d01c9a9 Updated CHANGELOG with my previous changes 2015-09-14 19:29:02 +01:00
S'pht'Kr
b0e3829dfb Merge branch 'nfarina/master' into filedate-sensor 2015-09-14 19:39:38 +02:00
Nick Farina
adbe116a5a [Lockitron] Create an Error if necessary 2015-09-14 07:48:42 -07:00
Nick Farina
bb39f5f73e [Lockitron] err could be null 2015-09-14 07:47:21 -07:00
Nick Farina
db3f32c577 Fix name from config 2015-09-14 07:45:29 -07:00
Nick Farina
13347d1879 Upgrade Lockitron accessory to new API 2015-09-14 07:43:32 -07:00
Nick Farina
4feec29d5c Merge pull request #165 from dwery/master
add mpd client accessory
2015-09-14 07:25:34 -07:00
Nick Farina
0df809e395 Merge pull request #164 from jmtatsch/patch-1
Fix 406 error for lifxjs
2015-09-14 07:25:00 -07:00
Jon Maddox
293f56e826 Merge pull request #166 from nfarina/home-assistant
Home Assistant Shim
2015-09-14 01:45:31 -04:00
Jon Maddox
c1e3d45fa1 scan in switches 2015-09-14 01:40:09 -04:00
Jon Maddox
69d948e0fa add switch 2015-09-14 01:40:03 -04:00
Jon Maddox
488456c108 ohhhhhhh the callback signature 2015-09-14 01:30:55 -04:00
Jon Maddox
025bca7a43 factor things out of the accessory and make it a Light 2015-09-14 01:16:34 -04:00
Jon Maddox
544124fbab clean it up and get on that modern tip 2015-09-14 00:20:28 -04:00
Nick Farina
178430d25c Merge pull request #167 from SphtKr/zway-thermostat-mode-writable
Z-Way thermostat mode writable
2015-09-13 21:14:09 -07:00
Jon Maddox
167a983068 handle missing friendly name 2015-09-14 00:14:02 -04:00
S'pht'Kr
b39b33726d Make TargetHeatingCoolingState writable
Apparently, if TargetHeatingCoolingState is not writable, you can’t add
a thermostat to a scene. This fix makes it writable from HomeKit, but
it still always remains set to HEAT.
2015-09-14 05:43:11 +02:00
Jon Maddox
a6d61cc93a add link to HA in readme 2015-09-13 22:30:47 -04:00
Jon Maddox
ec550d1638 add sample config for home assistant 2015-09-13 22:24:54 -04:00
Jon Maddox
f5cc6cf6fb add home assistant shim 2015-09-13 22:24:39 -04:00
Alessandro Zummo
b1d0ef57ac add mpd client accessory 2015-09-14 01:15:56 +02:00
S'pht'Kr
80f73f8324 Merge branch 'nfarina/master' 2015-09-13 16:06:36 +02:00
jmtatsch
45ae56cf12 Fix 406 error for lifxjs
by git+https
2015-09-13 14:53:35 +02:00
Andre Schröter
1f4ae9e875 removed speaker stype 2015-09-13 13:30:04 +02:00
Andre Schröter
6c34301c13 sonos volume fix 2015-09-13 12:21:50 +02:00
Andre Schröter
428186eb86 fixed debug messages 2015-09-13 10:24:28 +02:00
Andre Schröter
5358ef94a8 catch undefined values in reading2homekit 2015-09-13 09:14:13 +02:00
Andre Schröter
ca941bd350 now for real... 2015-09-12 20:21:37 +02:00
Andre Schröter
b5c2ba2c14 Merge remote-tracking branch 'upstream/master' 2015-09-12 20:11:31 +02:00
Andre Schröter
197311534b started porting to new bridge api 2015-09-12 20:07:18 +02:00
Nick Farina
a246472401 Merge pull request #121 from SphtKr/z-way-server-2
Z-Way server support
2015-09-12 06:37:54 -07:00
S'pht'Kr
7e9793e20e Merge branch 'nfarina/master' into z-way-server-2
Conflicts:
	config-sample.json
2015-09-12 14:26:27 +02:00
S'pht'Kr
2b1aa5e296 Erm...missing modifications that should have been in the last commit. 2015-09-12 14:24:31 +02:00
S'pht'Kr
17573524ce Prep for initial release.
Cleaned up some more old cruft, added config-sample.json entry, and now
there’s a different default grouping of characteristics, which makes
for more optional characteristics on fewer services. The older behavior
(more services per accessory) can be switched on in config.json. The
new default works better in Eve, other clients not so much.
2015-09-12 14:23:17 +02:00
Nick Farina
be31cce972 Merge pull request #159 from devbobo/master
[LIFX] implement the LIFX LAN API
2015-09-11 06:32:58 -07:00
David Parry
7d5a992c98 minor cleanup 2015-09-11 17:30:25 +10:00
S'pht'Kr
3da6fcb510 FIX: Prevent light sensor values from going out of bounds 2015-09-11 05:43:33 +02:00
David Parry
17fc8f1829 implement the LiFX LAN API as a configurable option for higher lantency connections 2015-09-11 13:04:09 +10:00
Snowdd1
e15fc7d4e8 Merge branch 'master' of https://github.com/snowdd1/homebridge 2015-09-10 17:27:56 +02:00
Snowdd1
4ca372f9d9 Merge remote-tracking branch 'nfarina/master' 2015-09-10 17:27:07 +02:00
Nick Farina
fdadd068b8 Merge pull request #158 from devbobo/master
[LiFX] fix/enhance the LiFX platform
2015-09-10 07:26:08 -07:00
David Parry
7e6df6191e [LiFX] fix/enhance the LiFX platform 2015-09-10 22:19:41 +10:00
S'pht'Kr
3c35311c4a Added Luminiscence sensors
Though there doesn’t seem to be much app support for it yet…and my math
(% -> lux) is complete guesswork.
2015-09-10 06:55:07 +02:00
Nick Farina
b2ea770afe Merge pull request #157 from devbobo/master
[WeMo] add support for WeMo motion sensor
2015-09-09 20:32:06 -07:00
Nick Farina
a1343ed57e Merge pull request #156 from nmelo/master
LIFx Bulb Platform implementation
2015-09-09 20:16:51 -07:00
David Parry
09f5e2bed0 add support for WeMo motion sensor 2015-09-10 12:50:55 +10:00
Nelson Melo
4b16371522 Added LIFx on Readme 2015-09-09 15:31:11 -04:00
Nelson Melo
7866b582f9 Merge branch 'master' of https://github.com/nmelo/homebridge
* 'master' of https://github.com/nmelo/homebridge:
  [MiLight] Swap cooler/warmer direction for white bulbs, and add note about delay
  [MiLight] Update config-sample.json to replace MiLight accessory with MiLight platform
  [MiLight] Add missing callback from hue function
  [MiLight] Modify logging to show the zone name when used as a platform accessory
  [MiLight] Converted accessory to platform. Not fully tested yet
2015-09-09 15:26:47 -04:00
Nelson Melo
c73e22984d Implemented LIFx bulb platform 2015-09-09 15:26:21 -04:00
Nick Farina
e9cca92dee Merge pull request #155 from dotsam/milight-platform
[MiLight] Convert to platform
2015-09-09 08:55:30 -07:00
Sam Edwards
d6e31b4aa7 [MiLight] Swap cooler/warmer direction for white bulbs, and add note about delay 2015-09-09 08:13:22 -07:00
S'pht'Kr
e75726436e Merge branch 'nfarina/master' into z-way-server-2 2015-09-09 06:50:22 +02:00
S'pht'Kr
62cabc23f3 Added Door/Window sensors and ancillary temperature sensors.
Door/Window sensors are still implemented as garage door openers,
because that seems to make the most sense at the moment.
2015-09-09 06:48:33 +02:00
S'pht'Kr
4170b8a533 Polling!
Updates from ZWay are now reflected in HomeKit!
2015-09-09 06:28:17 +02:00
Sam Edwards
7dc168e9dc [MiLight] Update config-sample.json to replace MiLight accessory with MiLight platform 2015-09-08 11:33:17 -07:00
Sam Edwards
18333242ff [MiLight] Add missing callback from hue function 2015-09-08 11:20:36 -07:00
Sam Edwards
5cccd3f916 [MiLight] Modify logging to show the zone name when used as a platform accessory 2015-09-08 11:01:30 -07:00
Sam Edwards
abe88b7502 [MiLight] Converted accessory to platform. Not fully tested yet 2015-09-08 10:41:03 -07:00
Nick Farina
cd8ee6a7b9 Merge pull request #154 from snowdd1/patch-1
TESTING NEEDED: Sonos platform multiple devices (for #95, #153)
2015-09-08 06:27:29 -07:00
Raoul
1df411d916 TESTING NEEDED
Proposing idea to de-asynchronize Sonos device discovery with a timeout event for push.
I have no IDE at hand right now, so please check syntax before merging!
I have no Sonos devices, so please check somebody with the hardware at hand!
Thanks
Raoul
2015-09-08 09:25:44 +02:00
Nick Farina
b05ee83e9f Merge pull request #152 from dotsam/master
[MiLight] Fix scope issue that prevented config from working
2015-09-07 17:30:41 -07:00
Sam Edwards
2710412ca6 [MiLight] Correctly reference light object, and also fix bug with brightness=0 setting the lamp to night mode 2015-09-07 16:45:27 -07:00
Sam Edwards
a4c3f73eb5 [MiLight] Fix scope issue that prevented config from working, added documentation, and added correct pause for night mode commands 2015-09-07 13:29:51 -07:00
S'pht'Kr
78987a775f Might be stable now for Switches, Dimmers, & Thermostats
Basically, I needed to provide a default value instead of a
`getDefaultValue` function. Keeping with this for a while before trying
battery stats and door sensors again, but I think I figured out my
compliance problems…don’t know how it ever successfully added before,
actually.

Also removed some more cruft from the earlier old-API version and
started laying some groundwork for polling/updating from ZWay.
2015-09-07 19:16:51 +02:00
Nick Farina
08fa203027 Merge pull request #150 from snowdd1/patch-1
missing comma
2015-09-07 07:24:49 -07:00
Raoul
6614705d6c missing comma
should have used fsonlint.com after adding the comments and secriptions
2015-09-07 15:27:30 +02:00
Nick Farina
5ba7b46c26 Merge pull request #144 from luxus/patch-1
adding resolver chain workaround for raspberry pi
2015-09-06 15:40:24 -07:00
Raoul
7c6d6ba0ac Merge pull request #1 from nfarina/master
Merge new master from Nick
2015-09-06 22:22:08 +02:00
Nick Farina
1f44801f45 Merge pull request #145 from snowdd1/knx-dev
KNX platform and accessory shim
2015-09-06 07:50:59 -07:00
Snowdd1
9d8afc4bcb cleanup of sample.json and platform shim
removed non-existent old device types from platform shim.
2015-09-06 11:41:33 +02:00
Snowdd1
09516acaf3 package.json
update eibd dependency to include future versions (^0.3.1)
2015-09-06 11:11:58 +02:00
Snowdd1
86d548b8d9 Resolve package.json conflict
Merged WEMO update
2015-09-06 09:39:51 +02:00
Snowdd1
dd0aa6e2c8 Merge remote-tracking branch 'nfarina/master' into knx-dev
Conflicts:
	package.json
2015-09-06 09:38:34 +02:00
Snowdd1
6f5e6b6a0b More devices for KNX bus
Now supports
- Lightbulb
- Thermostat
- TemperatureSensor (as of iOS 8.4.1)
- LockMechanism
2015-09-06 09:37:03 +02:00
S'pht'Kr
2c1d9f6efb Merge branch 'nfarina/master' into z-way-server-2 2015-09-06 06:37:31 +02:00
Kai
70b5a9142a adding resolver chain workaround for raspberry pi
adding the workaround from https://github.com/agnat/node_mdns/issues/130 to fix #140
2015-09-06 04:31:05 +02:00
Nick Farina
94ef18c94d Bump HAP-NodeJS with fixes for WeMo 2015-09-05 15:43:11 -07:00
Nick Farina
3cc1f381a3 Fix for WeMo switch names 2015-09-05 14:04:31 -07:00
Nick Farina
6c6b5bf85f Pull in iControl fix 2015-09-05 12:47:56 -07:00
Nick Farina
816728c0cf Upgrade WeMo accessory
- Use new Service API
  - Experimental preliminary support for WeMo "Garage Door Openers"
(via WeMo Maker). No Sensor support yet.
2015-09-05 12:42:21 -07:00
Snowdd1
e2ef8fc0b6 package.json
- removed subtypes to be passed by default.
- package.json to inbclude "eibd"
2015-09-05 21:31:48 +02:00
Snowdd1
085ae0a22e Merge remote-tracking branch 'nfarina/master' into knx-dev 2015-09-05 20:13:29 +02:00
Snowdd1
64635833d6 Sample config file
config-sample-knx.jsnon
2015-09-05 20:11:06 +02:00
Snowdd1
ea1c1f6fce Working Version
Refactored version. KNX.js is used as platform, while knxdevice is
called for each accessory to add.
2015-09-05 19:08:28 +02:00
Nick Farina
fe4cd285d0 Use once() to guard multiple callbacks
Mentioned in #95
2015-09-04 10:05:37 -07:00
Nick Farina
03417e249a Merge pull request #138 from nfarina/new-xfinity-home-platform
New xfinity home platform
2015-09-04 10:05:07 -07:00
Snowdd1
116dd1b315 still WIP
with Thermostat stub. Somehow stops working after a few lines of
console.log
2015-09-04 17:39:46 +02:00
Snowdd1
1a98a6c9ac got it straight
does not use PR#15 for node-eibd any more.
2015-09-04 16:35:11 +02:00
Snowdd1
bad0ba0c3b initial test version 2015-09-04 15:45:19 +02:00
S'pht'Kr
a677edb2cf Trying for a stable baseline
Switched Door sensors back to GarageDoorOpener services for now, and
disabled battery service…lets see if we can get this stabilized.
2015-09-04 06:18:23 +02:00
Nick Farina
a3cbf5a380 Another node-icontrol bump 2015-09-02 18:51:23 -07:00
Nick Farina
86e17a3922 Bump node-icontrol with fixes 2015-09-02 07:20:36 -07:00
S'pht'Kr
2a5c4c76fe Merge branch 'nfarina/master' into z-way-server-2 2015-09-02 06:28:02 +02:00
S'pht'Kr
30a705e79f Fixes, cleanup
Got rid of old code I clearly don’t need anymore. Switched `this.log`s
to `debug`s. Fix for picking wrong vDev for thermostat current temp.
Added configuration for `Name` characteristic…which I guess became
required.
2015-09-02 06:27:13 +02:00
Nick Farina
01d2c21aac New iControl accessory, supports Xfinity Home.
- No more mysterious "dsig" param
2015-08-31 21:39:10 -07:00
Nick Farina
dcc224f898 Merge pull request #135 from dotsam/milight
[MiLight] Update to address correct bulbs and implement all features
2015-08-31 16:27:05 -07:00
Sam Edwards
47f000ecff [MiLight] Sent on command before brightness and hue to ensure we're targeting the right lamp, and implement night mode at brightness <= 2, and implement maximum brightness command for white lamps 2015-08-31 15:40:02 -07:00
S'pht'Kr
f287c9ee36 Merge branch 'nfarina/master' into z-way-server-2 2015-08-30 14:27:07 +02:00
S'pht'Kr
b56d9346c8 Refactored for new API.
Mostly working, but door sensors need further work, battery service still isn't right, and I'm losing the bridge periodically...merging from master to see if I need some bugfixes.
2015-08-30 14:24:43 +02:00
Nick Farina
d067711974 HAP-NodeJS bump 2015-08-29 13:14:55 -07:00
Nick Farina
2c50d76cb2 Bump HAP-NodeJS 2015-08-29 09:34:58 -07:00
Nick Farina
5bc1109c19 Merge pull request #132 from KhaosT/master
[Philips Hue] retry if command failed due to api rate limit.
2015-08-28 22:29:52 -07:00
Khaos Tian
bf2216209d [Philips Hue] retry if command failed due to api rate limit. 2015-08-28 21:49:16 -07:00
Nick Farina
2ca6410e0a Merge pull request #130 from EddyK69/HaveDimmer
[Domoticz] Fixed misc. dimmer issues (SwitchType instead of HaveDimmer etc)
2015-08-27 07:04:09 -07:00
EddyK69
76eaca8f78 Fixed misc. dimmer issues
Fixed issue that 'on/off'-type lights showed as dimmers in HomeKit.
Checking now on SwitchType instead of HaveDimmer
Fixed issue that 'on-off'-type lights would not react on Siri 'Switch
on/off light'; On/Off types are now handled as Lights instead of
Switches
2015-08-27 12:49:22 +02:00
EddyK69
2bab8bad8b Merge remote-tracking branch 'nfarina/master' into HaveDimmer 2015-08-27 12:46:08 +02:00
Nick Farina
5ecfb25314 Merge pull request #129 from EddyK69/master
Added parameter 'loadscenes' & fixed dimmer-bug
2015-08-26 21:39:20 -07:00
EddyK69
b170e81059 Implemented: config.json parameter loadscenes & fixed dimmer-bug
Added parameter in config.json: 'loadscenes' for enabling/disabling
loading scenes
Fixed issue with dimmer-range; was 0-100, should be 0-16
2015-08-26 23:20:42 +02:00
EddyK69
4c088d235c Merge remote-tracking branch 'nfarina/master' 2015-08-26 15:13:30 +02:00
Nick Farina
9c8a6ee90f Merge pull request #127 from dotsam/milight-new-api
Updated MiLight Shim
2015-08-25 20:44:03 -07:00
Sam Edwards
d7d80b8618 Don't actually add a name characteristic here 2015-08-25 17:46:28 -07:00
Sam Edwards
1050b4e550 Add a name to the accessory as well 2015-08-25 12:12:58 -07:00
S'pht'Kr
13d983aa28 Merge branch 'nfarina/master' into z-way-server-2 2015-08-25 20:23:33 +02:00
Sam Edwards
7d5caae96d Add the hue characteristics for all lamps, as we're trying to handle this in some way now 2015-08-25 10:46:57 -07:00
Sam Edwards
f81e594ff0 Update readme with reference to milight under lights section 2015-08-25 10:34:45 -07:00
Sam Edwards
11f71e076c Update MiLight module for new API and add improved (or some at all) handling of lamp types other than rgbw. Also remove invalid comments from json file, and support future versions of node-milight-promise 2015-08-25 10:28:08 -07:00
Nick Farina
b3675f4be3 Merge pull request #125 from nfarina/possible-domoticz-async-fix
Fix for Domoticz platform async callbacks
2015-08-24 22:12:24 -07:00
Nick Farina
7e84806846 Merge pull request #116 from nfarina/support-new-api
Support new API in getServices()
2015-08-24 19:28:49 -07:00
Nick Farina
77fbe5b53c Fix for Domoticz platform async callbacks 2015-08-24 19:23:43 -07:00
S'pht'Kr
f72cb43043 Numerous fixes and improvements.
Committing before merging back to master, since the upstream branches have merged into master!
2015-08-24 06:48:34 +02:00
S'pht'Kr
9d7a6768b8 Working at the moment.
Support for several device types. Gotta be careful to not throw the bridge out of compliance!
2015-08-24 06:48:34 +02:00
S'pht'Kr
c6877193cc Early, but lots done
This works (sometimes at least) but has lots of flaws, including ones
that make the whole bridge unreachable.
2015-08-24 06:48:34 +02:00
S'pht'Kr
5782ff997f Initial commit of ZWayServer platform. Not much working, having problems with authenticating. 2015-08-24 06:48:34 +02:00
S'pht'Kr
ea9801fa77 Merge branch 'nfarina/master' 2015-08-24 06:46:46 +02:00
S'pht'Kr
0dd76134ee Merge branch 'nfarina/master' 2015-08-24 06:45:19 +02:00
Nick Farina
76e7ef2677 Fix identify() in Http 2015-08-23 11:45:34 -07:00
Nick Farina
dfdbc865c8 Support AccessoryInformation service 2015-08-23 10:07:31 -07:00
Nick Farina
d8e27910cc Support new API in getServices()
See `getServices()` implementation in `accessories/Http.js` for an
example of how to use.

Fixes #114
Fixes #57
2015-08-22 21:37:42 -07:00
Nick Farina
d2547f7ae6 Fix HAP-NodeJS version 2015-08-22 08:51:20 -07:00
Nick Farina
9ebf41415c Version bump for npm 2015-08-22 08:13:11 -07:00
Nick Farina
7b873bdf48 Bump HAP-NodeJS 2015-08-21 07:56:44 -07:00
Nick Farina
64c665cebb Better error display for "npm run" and config.json 2015-08-20 21:38:29 -07:00
Nick Farina
5f1df2792f Better error handling for config.json problems 2015-08-20 21:25:29 -07:00
Nick Farina
b45318c3a3 Change default bridge username 2015-08-20 11:53:40 -07:00
Nick Farina
84fe474ccf Add startup note about migration 2015-08-20 11:52:24 -07:00
Nick Farina
5df7ffb40e README update; npm run start uses DEBUG 2015-08-19 22:05:08 -07:00
Nick Farina
9c63ce5017 Allow bridge to be configured in config.json
Fixes #104
2015-08-19 21:51:25 -07:00
Nick Farina
8a58328ad7 Merge pull request #94 from nfarina/use-hap-refactor
Use WIP version of HAP-NodeJS refactor
2015-08-19 21:50:31 -07:00
Nick Farina
bf3d746089 Use now-official refactored HAP-NodeJS 2015-08-19 21:45:16 -07:00
Nick Farina
84c08d360c Bump HAP with new types 2015-08-19 16:23:09 -07:00
Nick Farina
867aec36d8 One more bump 2015-08-19 14:08:53 -07:00
Nick Farina
9f0aaa53d6 HAP bump for socket timeouts 2015-08-19 13:48:27 -07:00
Andre Schröter
c98e7a1d54 added zwave model
more cleanups
2015-08-14 10:13:23 +02:00
Nick Farina
8bda79bcb5 Merge branch 'master' into use-hap-refactor 2015-08-13 20:25:17 -07:00
Nick Farina
4d9897ff26 Fix LiftMaster shim
For those of us without multiple garage doors...
2015-08-13 20:25:06 -07:00
Nick Farina
e1647d22ad Bump HAP-NodeJS with socket fix 2015-08-13 10:10:55 -07:00
Nick Farina
3a819e202d Bump HAP-NodeJS 2015-08-13 09:28:50 -07:00
Nick Farina
2c94d72afe Merge pull request #101 from Danimal4326/master
Add Hyperion Accessory
2015-08-13 08:47:12 -07:00
Danimal4326
0a2f2865fb Add accessory shim for controlling hyperion TV backlight.
(https://github.com/tvdzwan/hyperion)
2015-08-13 10:34:35 -05:00
Danimal4326
e9d43a7bdd clean up entry for MiLight 2015-08-13 10:28:36 -05:00
Danimal4326
a5aa5b6b90 Merge remote-tracking branch 'nfarina/master' 2015-08-13 10:05:55 -05:00
Nick Farina
50f8c6a3c9 Merge branch 'master' into use-hap-refactor
Conflicts:
	platforms/Sonos.js
2015-08-09 11:16:22 -07:00
Nick Farina
9d8c5f309f Merge branch 'master' of https://github.com/nfarina/homebridge 2015-08-09 08:24:07 -07:00
Nick Farina
667ad7a3aa [Sonos] don't add duplicate rooms
Fixes #98
2015-08-09 08:22:00 -07:00
Nick Farina
11560d3d42 Merge pull request #96 from justme-1968/master
update FHEM platform to the current version
2015-08-07 08:41:21 -07:00
Nick Farina
6fff108ccd Bump HAP-NodeJS 2015-08-06 09:58:03 -07:00
Andre Schröter
7cb835e4ea more cleanups
handle sonos volume caching
added garage door opener
started with homematic keymatic (HM-SEC-KEY)
even more cleanups :)
rgb hue/sat/bri fix
added identify for lightbulbs
2015-08-06 10:02:09 +02:00
Nick Farina
43c0346436 [Sonos] SUB isn't playable 2015-08-05 08:55:19 -07:00
Nick Farina
9653a3173d Bump HAP-NodeJS 2015-08-04 20:32:05 -07:00
Nick Farina
c9aea1b731 Initialize HAP storage 2015-08-04 16:44:34 -07:00
Nick Farina
f7f953b8d1 Fix name/log var corruption 2015-08-04 16:21:06 -07:00
Nick Farina
c756ea9456 HAP-NodeJS Bump for stable IDs 2015-08-04 16:00:52 -07:00
Nick Farina
b1e6536a95 Use latest refactor commit 2015-08-04 15:49:50 -07:00
Nick Farina
f71730cff6 Restore platform support 2015-08-04 15:48:52 -07:00
Nick Farina
2ad9643bee Update HAP-NodeJS version 2015-08-04 09:24:35 -07:00
Nick Farina
6e61287f59 Use WIP version of HAP-NodeJS refactor 2015-08-03 08:33:41 -07:00
Andre Schröter
86322c30c8 ignore state set_ in addition to set- 2015-08-02 18:52:03 +02:00
Andre Schröter
d9d55c34ad winmatic bug fix
addef FHTTK contact sensor
2015-08-02 17:40:10 +02:00
Andre Schröter
2e3a26c32f more cleanups (renamed endpoints to mappings)
added longpoll reconnect on close
added winmatic (HM-SEC-WIN)
2015-08-02 13:37:12 +02:00
Andre Schröter
40e264a79d started cleanup of has... use endpoints instead 2015-08-02 01:09:53 +02:00
Andre Schröter
e0de3f2a82 don't show on/off for blinds 2015-08-01 20:15:59 +02:00
Andre Schröter
939f79b2b0 some cleanups
try to detect closed longpoll connection
added last event timestamp
2015-08-01 17:47:42 +02:00
Nick Farina
ab71dc352f Merge pull request #93 from justme-1968/master
added FHEM.js platform shim for the FHEM home automation system
2015-07-31 14:50:21 -07:00
Andre Schröter
a29cabcd91 enhanced debug browser 2015-07-31 22:44:50 +02:00
Andre Schröter
4dcc26bf50 fix for rgb vs RGB issue 2015-07-30 17:10:29 +02:00
Andre Schröter
431199a388 added event map for state on/off 2015-07-30 12:37:46 +02:00
Andre Schröter
a268583803 fixed racecondition 2015-07-29 23:41:18 +02:00
Nick Farina
1099cb50e7 Merge pull request #88 from SphtKr/yamaha-avr
Initial attempt at a Yamaha AVR control module, like Sonos.
2015-07-29 14:17:54 -07:00
Andre Schröter
f050e51518 more on EnOcean switches 2015-07-29 21:40:29 +02:00
S'pht'Kr
b5f778051e First working version, probably could be merged if no one sees anything glaringly wrong.
Config can set the play volume (in decibels per the AVR display) and the default input. If "AirPlay" is the default input, then the play command will be sent to the AVR.
2015-07-29 20:34:38 +02:00
Andre Schröter
ed6d4baba2 internal cleanups 2015-07-28 21:34:05 +02:00
S'pht'Kr
76ad483a5f WIP - Initial attempt at a Yamaha AVR controll module, like Sonos. Not working quite yet, but looks close. 2015-07-28 07:05:35 +02:00
Andre Schröter
0fb6fc736e added FHEM.js platform shim for the FHEM home automation system.
supports switches, lightbulbs (dimmers and rgb/colored lights),
homematic window blinds, max and homematic thermostats, homematic
door&window sensors, PRESENCE and sonos (including volume)

the shim is bidirectional and uses longpoll to push fhem events
back to homekit. fhem events are cached so homekit querys for known
values will not cause a roundtrip to fhem

more:
http://forum.fhem.de/index.php/topic,32652.msg314166.html#msg314166
2015-07-27 09:38:53 +02:00
Nick Farina
d4c544e793 Recommend MyTouchHome, it's generally the best. 2015-07-25 12:45:34 -07:00
Nick Farina
18cc6cbdc0 Merge pull request #77 from bezzers/Sonos-platform
Make Sonos a platform rather than an accessory
2015-07-24 08:47:24 -07:00
Nick Farina
e8769d688a Merge pull request #83 from bezzers/tesla-accessory
Fix logic issue in error reporting
2015-07-24 08:46:55 -07:00
bezzers
275a88d358 Fix logic issue in error reporting 2015-07-24 09:45:52 -04:00
bezzers
443f77196a Add missing connection to config parameter 2015-07-24 08:22:24 -04:00
bezzers
2e689eb264 Merge branch 'master' into Sonos-platform 2015-07-23 20:50:39 -04:00
Nick Farina
da79d67106 Merge pull request #80 from bezzers/tesla-accessory
Add Tesla accessory
2015-07-23 11:50:53 -07:00
Paul Beswick
385b615b81 Creation of an accessory to control the climate control for a Tesla Model S with accompanying changes to package.json (to add the teslams package) and the sample config file. 2015-07-23 13:47:41 -04:00
Nick Farina
f7a2cd5f0c Merge pull request #75 from lukeredpath/feature/add-ssl-and-auth-support-to-domoticz
Add SSL and basic auth support to Domoticz shim
2015-07-23 09:30:29 -07:00
bezzers
8b81001647 Delete Sonos as an accessory
Replaced by new Sonos platform module
2015-07-23 09:41:29 -04:00
bezzers
9325b15437 Moving Sonos configuration example from accessories to platform 2015-07-23 09:39:10 -04:00
bezzers
0b457f9b7f Create a Sonos Platform handler - first version
Creates a platform for Sonos that scans for all Sonos boxes and adds any that are not bridges (type 11) as accessories.
Reads the names of each box from the Sonos configuration and creates device names based on the Sonos room names.
2015-07-23 09:36:37 -04:00
bezzers
0605c5ff86 Create Sonos.js 2015-07-23 09:27:58 -04:00
Luke Redpath
7b01e6c1d0 Won’t be able to do much unless accessories can use SSL/basic auth too. 2015-07-22 16:12:22 +01:00
Luke Redpath
34110039f5 Add SSL and basic auth support for Domoticz platform. 2015-07-22 15:27:52 +01:00
Nick Farina
38f3d9b82e Merge pull request #70 from hachidorii/master
Add support for Nest platform.
2015-07-18 14:42:56 -07:00
Chloe Stars
511b1873f7 I forked unofficial-nest-api and added support for 'off'.
For now the package location has been changed to reflect this. I've submitted a pull request but it doesn't look like it's been touched in a while so I'm not sure it's going to get merged.
2015-07-17 18:29:23 -07:00
Chloe Stars
e76269fcdc Merge https://github.com/nfarina/homebridge 2015-07-17 18:14:56 -07:00
Chloe Stars
c4f9e81828 Oops, made a rookie mistake. Make sure I'm using local variables instead of global variables. 2015-07-17 01:08:50 -07:00
Nick Farina
db9a1bd441 Merge pull request #66 from cdrx/patch-1
Logitech Harmony should be a platform
2015-07-14 11:01:45 -07:00
Chris R
42ba55e836 Update config-sample.json
Logitech Harmony shouldn't be an accessory
2015-07-14 18:57:33 +01:00
Chloe Stars
91602e4f99 Fix heating and cooling.
It works but the API doesn’t reflect the change soon enough for HomeKit
to be aware it worked. Not sure what to do here.
2015-07-13 14:58:39 -07:00
Nick Farina
ca311ae0a8 Merge pull request #43 from madmod/logitech-harmony
Logitech Harmony platform
2015-07-13 13:40:39 -07:00
madmod
1cd697fb8b Updated README.md with Logitech Harmony 2015-07-13 13:16:29 -06:00
madmod
6cb9008424 Add get and set power state for activity 2015-07-11 22:11:08 -06:00
madmod
681cd7d9eb Add Logitech Harmony platform 2015-07-11 19:18:14 -06:00
Chloe Stars
4aafdaea9b Update the sample config file with Nest platform. 2015-07-10 23:58:41 -07:00
Chloe Stars
81cc689b7e Add the required module to package.json. 2015-07-10 23:53:57 -07:00
Chloe Stars
a47c8dd4fd Added preliminary support for the Nest platform.
Some things are missing or may not be working correctly but the main
things like checking the current temperature, the target temperature,
the humidity, setting the target temperature and etc.. I don’t think
the current heating mode is correct.
2015-07-10 23:50:31 -07:00
Nick Farina
02c90cfc51 Merge pull request #64 from hachidorii/master
Add support for getting the state of the power to WeMo accessories.
2015-07-10 16:07:30 -07:00
Chloe Stars
03293409dd Add support for getting the state of the power to WeMo accessories. 2015-07-10 15:50:07 -07:00
Nick Farina
26e833013c Merge pull request #63 from johngson/master
Adding support for Telldus Live platform
2015-07-09 20:31:43 -07:00
John Gustafsson
f3b472b2d0 Added Telldus Live! to config-sample.json 2015-07-10 00:39:58 +02:00
John Gustafsson
54a24b4fdd Added Telldus Live! support (only lights) 2015-07-10 00:36:19 +02:00
Nick Farina
16829a9e4c Merge pull request #61 from dotsam/milight
Initial support for MiLight/LimitlessLED/EasyBulb/etc... bulbs using …
2015-07-07 18:19:45 -07:00
Sam Edwards
7b7b28571d Initial support for MiLight/LimitlessLED/EasyBulb/etc... bulbs using the node-milight-promise node module 2015-07-07 16:10:24 -07:00
33 changed files with 1064 additions and 3962 deletions

10
.gitignore vendored
View File

@@ -7,10 +7,6 @@ node_modules/
npm-debug.log npm-debug.log
.node-version .node-version
# Intellij # Ignore any extra plugins in the example directory that aren't in Git already
.idea/ # (this is a sandbox for the user)
*.iml example-plugins
# HomeBridge
config.json
persist/

0
.gitmodules vendored
View File

177
README.md
View File

@@ -1,114 +1,145 @@
# HomeBridge [![Slack Status](https://homebridge-slackin.herokuapp.com/badge.svg)](https://homebridge-slackin.herokuapp.com)
HomeBridge is a lightweight NodeJS server you can run on your home network that emulates the iOS HomeKit API. It includes a set of "shims" (found in the [accessories](accessories/) and [platforms](platforms/) folders) that provide a basic bridge from HomeKit to various 3rd-party APIs provided by manufacturers of "smart home" devices. # Homebridge
Since Siri supports devices added through HomeKit, this means that with HomeBridge you can ask Siri to control devices that don't have any support for HomeKit at all. For instance, using the included shims, you can say things like: ![](https://media.giphy.com/media/10l79ICohTu4iQ/giphy.gif)
* _Siri, unlock the front door._ ([Lockitron](https://lockitron.com)) Homebridge is a lightweight NodeJS server you can run on your home network that emulates the iOS HomeKit API. It supports Plugins, which are community-contributed modules that provide a basic bridge from HomeKit to various 3rd-party APIs provided by manufacturers of "smart home" devices.
* _Siri, open the garage door._ ([LiftMaster MyQ](https://www.myliftmaster.com))
* _Siri, turn on the Leaf._ ([Carwings](http://www.nissanusa.com/innovations/carwings.article.html))
* _Siri, turn off the Speakers._ ([Sonos](http://www.sonos.com))
* _Siri, turn on the Dehumidifier._ ([WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/))
* _Siri, turn on Away Mode._ ([Xfinity Home](http://www.comcast.com/home-security.html))
* _Siri, turn on the living room lights._ ([Wink](http://www.wink.com), [SmartThings](http://www.smartthings.com), [X10](http://github.com/edc1591/rest-mochad), [Philips Hue](http://meethue.com))
If you would like to support any other devices, please write a shim and create a pull request and I'd be happy to add it to this official list. Since Siri supports devices added through HomeKit, this means that with Homebridge you can ask Siri to control devices that don't have any support for HomeKit at all. For instance, using just some of the available plugins, you can say:
# Shim types * _Siri, unlock the back door._ [pictured above]
There are 2 types of shims supported in HomeBridge. * _Siri, open the garage door._
* _Siri, turn on the coffee maker._
* _Siri, turn on the living room lights._
* _Siri, good morning!_
* Accessory - Individual device You can explore all available plugins at the NPM website by [searching for the keyword `homebridge-plugin`](https://www.npmjs.com/search?q=homebridge-plugin).
* Platform - A full bridge to another system
## Accessories # Community
Accessories are individual devices you would like to bridge to HomeKit. You set them up by declaring them individually in your `config.json` file. Generally, you specify them by `name` or `id` and which system they use. If you're having an issue with a particular plugin, open an issue in that plugin's Github repository. If you're having an issue with Homebridge itself, feel free to open issues and PRs here.
## Platforms You can also chat with us in our nascent [Slack instance](http://homebridge-slackin.herokuapp.com).
Platforms bridge entire systems to HomeKit. Platforms can be things like Wink or SmartThings or Vera. By adding a platform to your `config.json`, HomeBridge will automatically detect all of your devices for you. # Installation
All you have to do is add the right config options so HomeBridge can authenticate and communicate with your other system, and voila, your devices will be available to HomeKit via HomeBridge. **Note:** If you're running on Linux, you'll need to make sure you have the `libavahi-compat-libdnssd-dev` package installed. If you're running on a Raspberry Pi, you should have a look at the [Wiki](https://github.com/nfarina/homebridge/wiki/Running-HomeBridge-on-a-Raspberry-Pi).
# Why? Homebridge is published through [NPM](https://www.npmjs.com/package/homebridge) and should be installed "globally" by typing:
Technically, the device manufacturers should be the ones implementing the HomeKit API. And I'm sure they will - eventually. When they do, these shims will be obsolete, and I hope that happens soon. In the meantime, this server is a fun way to get a taste of the future, for those who just can't bear to wait until "real" HomeKit devices are on the market. sudo npm install -g homebridge
# Credit Now you should be able to run Homebridge:
HomeBridge itself is basically just a set of shims and a README. The actual HomeKit API work was done by [KhaosT](http://twitter.com/khaost) in his [HAP-NodeJS](https://github.com/KhaosT/HAP-NodeJS) project. Additionally, many of the shims benefit from amazing NodeJS projects out there like `sonos` and `wemo` that implement all the interesting functionality. $ homebridge
No plugins found. See the README for information on installing plugins.
# Before you Begin Homebridge will complain if you don't have any Plugins installed, since it will essentially be useless, although you can still "pair" with it. See the next section "Installing Plugins" for more info.
I would call this project a "novelty" in its current form, and is for **intrepid hackers only**. To make any of this work, you'll need: Once you've installed a Plugin or two, you can run Homebridge again:
* An app on your iOS device that can manage your HomeKit database. $ homebridge
* An always-running server (like a Raspberry Pi) on which you can install NodeJS.
* Knowledge of Git submodules and npm.
You'll also need some patience, as Siri can be very strict about sentence structure, and occasionally she will forget about HomeKit altogether. But it's not surprising that HomeKit isn't rock solid, since almost no one can actually use it today besides developers who are creating hardware accessories for it. There are, to my knowledge, exactly zero licensed HomeKit devices on the market right now, so Apple can easily get away with this all being a work in progress.
Additionally, the shims I've created implement the bare minimum of HomeKit needed to provide basic functionality like turning things off and on. I haven't written any kind of good feedback or error handling, and although they support changing state, they don't support reading the current state, so if you ask questions like "Is my door unlocked?" Siri will respond with the default of "Nope!" no matter what.
# Getting Started
OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up. First, clone this repo:
$ git clone https://github.com/nfarina/homebridge.git
$ cd homebridge
$ npm install
**Node**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load.
Now you should be able to run the homebridge server:
$ cd homebridge
$ npm run start
Starting HomeBridge server...
Couldn't find a config.json file [snip] Couldn't find a config.json file [snip]
The server won't do anything until you've created a `config.json` file containing your home devices (or _accessories_ in HomeKit parlance) or platforms you wish to make available to iOS. You can start by copying and modifying the included `config-sample.json` file which includes declarations for all supported accessories and platforms. However, Homebridge won't do anything until you've created a `config.json` file containing your accessories and/or platforms. You can start by copying and modifying the included `config-sample.json` file which includes declarations for some example accessories and platforms. Each Plugin will have its own expected configuration; the documentation for Plugins should give you some real-world examples for that plugin.
Once you've added your devices and/or platforms, you should be able to run the server again and see them initialize: **NOTE**: Your `config.json` file MUST live in your home directory inside `.homebridge`. The full error message will contain the exact path where your config is expected to be found.
$ npm run start **REALLY IMPORTANT**: You must use a "plain text" editor to create or modify `config.json`. Do NOT use apps like TextEdit on Mac or Wordpad on Windows; these apps will corrupt the formatting of the file in hard-to-debug ways. I suggest using the free [Atom text editor](http://atom.io).
Starting HomeBridge server...
Loading 6 accessories...
[Speakers] Initializing 'Sonos' accessory...
[Coffee Maker] Initializing 'WeMo' accessory...
[Speakers] Initializing 'Sonos' accessory...
[Coffee Maker] Initializing 'WeMo' accessory...
[Wink] Initializing Wink platform...
[Wink] Fetching Wink devices.
[Wink] Initializing device with name Living Room Lamp...
Your server is now ready to receive commands from iOS. Once you've added your config file, you should be able to run Homebridge again:
# Adding your devices to iOS $ homebridge
Loaded plugin: homebridge-lockitron
Registering accessory 'Lockitron'
---
Loaded config.json with 1 accessories and 0 platforms.
---
Loading 0 platforms...
Loading 1 accessories...
[Back Door] Initializing Lockitron accessory...
Homebridge is now ready to receive commands from iOS.
# Installing Plugins
Plugins are NodeJS modules published through NPM and tagged with the keyword `homebridge-plugin`. They must have a name with the prefix `homebridge-`, like **homebridge-mysmartlock**.
Plugins can publish Accessories and/or Platforms. Accessories are individual devices, like a smart switch or a garage door. Platforms act like a single device but can expose a set of devices, like a house full of smart lightbulbs.
You install Plugins the same way you installed Homebridge - as a global NPM module. For example:
sudo npm install -g homebridge-lockitron
You can explore all available plugins at the NPM website by [searching for the keyword `homebridge-plugin`](https://www.npmjs.com/search?q=homebridge-plugin).
**IMPORTANT**: Many of the plugins that Homebridge used to include with its default installation have been moved to the single plugin [homebridge-legacy-plugins](https://www.npmjs.com/package/homebridge-legacy-plugins).
# Adding Homebridge to iOS
HomeKit is actually not an app; it's a "database" similar to HealthKit and PassKit. But where HealthKit has the companion _Health_ app and PassKit has _Passbook_, Apple has supplied no app for managing your HomeKit database (at least [not yet](http://9to5mac.com/2015/05/20/apples-planned-ios-9-home-app-uses-virtual-rooms-to-manage-homekit-accessories/)). However, the HomeKit API is open for developers to write their own apps for adding devices to HomeKit. HomeKit is actually not an app; it's a "database" similar to HealthKit and PassKit. But where HealthKit has the companion _Health_ app and PassKit has _Passbook_, Apple has supplied no app for managing your HomeKit database (at least [not yet](http://9to5mac.com/2015/05/20/apples-planned-ios-9-home-app-uses-virtual-rooms-to-manage-homekit-accessories/)). However, the HomeKit API is open for developers to write their own apps for adding devices to HomeKit.
Fortunately, there are now a few apps in the App Store that can manage your HomeKit devices. Try [Insteon+](https://itunes.apple.com/US/app/id919270334?mt=8) or [Lutron](https://itunes.apple.com/us/app/lutron-app-for-caseta-wireless/id886753021?mt=8) or a number of others. Fortunately, there are now a few apps in the App Store that can manage your HomeKit devices. The most comprehensive one I've used is [MyTouchHome](https://itunes.apple.com/us/app/mytouchhome/id965142360?mt=8&at=11lvmd&ct=mhweb) which costs $2.
## Adding HomeKit Accessories There are also some free apps that work OK. Try [Insteon+](https://itunes.apple.com/US/app/id919270334?mt=8) or [Lutron](https://itunes.apple.com/us/app/lutron-app-for-caseta-wireless/id886753021?mt=8) or a number of others.
Once you've gotten a HomeKit app running on your iOS device, you can begin adding accessories. The app should "discover" the accessories defined in your `config.json` file, assuming that you're still running the HomeBridge server and you're on the same Wifi network. If you are a member of the iOS developer program, I highly recommend Apple's [HomeKit Catalog](https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Introduction/Intro.html) app, as it is reliable and comprehensive and free (and open source).
When you attempt to add a device, it will ask for a "PIN code". The default code for _all_ HomeBridge accessories is `031-45-154`. Adding the device should create some files in the `persist` directory of the HomeBridge server, which stores the pairing relationship. Once you've gotten a HomeKit app running on your iOS device, it should "discover" the single accessory "Homebridge", assuming that you're still running Homebridge and you're on the same Wifi network. Adding this accessory will automatically add all accessories and platforms defined in `config.json`.
When you attempt to add Homebridge, it will ask for a "PIN code". The default code is `031-45-154` (but this can be changed, see `config-sample.json`).
# Interacting with your Devices # Interacting with your Devices
Once your device has been added to HomeKit, you should be able to tell Siri to control your devices. However, realize that Siri is a cloud service, and iOS may need some time to synchronize your device information with iCloud. Once your device has been added to HomeKit, you should be able to tell Siri to control your devices. However, realize that Siri is a cloud service, and iOS may need some time to synchronize your device information with iCloud.
Also, keep in mind HomeKit is not very robust yet, and it is common for it to fail intermittently ("Sorry, I wasn't able to control your devices" etc.) then start working again for no reason. Also I've noticed that it will get cranky and stop working altogether sometimes. The usual voodoo applies here: reboot your device, restart the homebridge server, run your HomeKit iOS app and poke around, etc. One final thing to remember is that Siri will almost always prefer its default phrase handling over HomeKit devices. For instance, if you name your Sonos device "Radio" and try saying "Siri, turn on the Radio" then Siri will probably start playing an iTunes Radio station on your phone. Even if you name it "Sonos" and say "Siri, turn on the Sonos", Siri will probably just launch the Sonos app instead. This is why, for instance, the suggested `name` for the Sonos accessory is "Speakers".
One final thing to remember is that Siri will almost always prefer its default phrase handling over HomeKit devices. For instance, if you name your Sonos device "Radio" and try saying "Siri, turn on the Radio" then Siri will probably start playing an iTunes Radio station on your phone. Even if you name it "Sonos" and say "Siri, turn on the Sonos", Siri will probably just launch the Sonos app instead. This is why, for instance, the suggested `name` for the Sonos shim in `config-samples.json` is "Speakers". # Writing Plugins
# Final Notes We don't have a lot of documentation right now for creating plugins, but there are many existing plugins you can study.
HomeKit is definitely amazing when it works. Speaking to Siri is often much quicker and easier than launching whatever app your device manufacturer provides. The best place to start is the included [Example Plugins](https://github.com/nfarina/homebridge/tree/master/example-plugins). Right now this contains a single plugin that registers a fake door lock Accessory. This will show you how to use the Homebridge Plugin API.
I welcome any suggestions or pull requests, but keep in mind that it's likely not possible to support all the things you might want to do with a device through HomeKit. For instance, you might want to hack the Sonos shim to play the specific kind of music you want and that's great, but it might not be appropriate to merge those specific changes into this repository. The shims here should be mostly simple "canonical examples" and easily hackable by others. For more example on how to construct HomeKit Services and Characteristics, see the many Accessories in the [Legacy Plugins](https://github.com/nfarina/homebridge-legacy-plugins/tree/master/accessories) repository.
Good luck! You can also view the [full list of supported HomeKit Services and Characteristics in the HAP-NodeJS protocol repository](https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js).
There isn't currently an example for how to publish a Platform (which allows the user to bridge many discovered devices at once, like a house full of smart lightbulbs), but the process is almost identical to registering an Accessory. Simply modify the example `index.js` in [homebridge-lockitron](https://github.com/nfarina/homebridge/tree/master/example-plugins/homebridge-lockitron) to say something like:
homebridge.registerPlatform("homebridge-myplugin", "MyPlatform", MyPlatform);
See more examples on how to create Platform classes in the [Legacy Plugins](https://github.com/nfarina/homebridge-legacy-plugins/tree/master/platforms) repository.
# Common Issues
### My iOS App Can't Find Homebridge
Two reasons why Homebridge may not be discoverable:
1. Homebridge server thinks it's been paired with, but iOS thinks otherwise. Fix: deleted `persist/` directory which is next to your `config.json`.
2. iOS device has gotten your Homebridge `username` (looks like a MAC address) "stuck" somehow, where it's in the database but inactive. Fix: change your `username` in the "bridge" section of `config.json` to be some new value.
### Errors on startup
The following errors are experienced when starting Homebridge and can be safely ignored. The cost of removing the issue at the core of the errors isn't worth the effort.
```
*** WARNING *** The program 'nodejs' uses the Apple Bonjour compatibility layer of Avahi
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see http://0pointerde/avahi-compat?s=libdns_sd&e=nodejs
*** WARNING *** The program 'nodejs' called 'DNSServiceRegister()' which is not supported (or only supported partially) in the Apple Bonjour compatibility layer of Avahi
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see http://0pointerde/avahi-compat?s=libdns_sd&e=nodejs&f=DNSServiceRegister
```
# Why Homebridge?
Technically, the device manufacturers should be the ones implementing the HomeKit API. And I'm sure they will - eventually. When they do, this project will be obsolete, and I hope that happens soon. In the meantime, Homebridge is a fun way to get a taste of the future, for those who just can't bear to wait until "real" HomeKit devices are on the market.
# Credit
The original HomeKit API work was done by [KhaosT](http://twitter.com/khaost) in his [HAP-NodeJS](https://github.com/KhaosT/HAP-NodeJS) project.

View File

@@ -1,245 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var AD2USB = require('ad2usb');
var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8";
function AD2USBAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.host = config["host"];
this.port = config["port"];
this.pin = config["pin"];
var that = this;
this.currentArmState = 2;
this.currentStateCharacteristic = undefined;
this.targetStateCharacteristic = undefined;
this.lcdCharacteristic = undefined;
var alarm = AD2USB.connect(this.host, this.port, function() {
// Send an initial empty character to get status
alarm.send('');
// Armed Away
alarm.on('armedAway', function() {
that.log("Armed to AWAY");
if (that.currentStateCharacteristic) {
that.currentStateCharacteristic.updateValue(0, null);
}
if (that.targetStateCharacteristic) {
that.targetStateCharacteristic.updateValue(1, null);
}
});
// Armed Stay
alarm.on('armedStay', function() {
that.log("Armed to STAY");
if (that.currentStateCharacteristic) {
that.currentStateCharacteristic.updateValue(0, null);
}
if (that.targetStateCharacteristic) {
that.targetStateCharacteristic.updateValue(0, null);
}
});
// Armed Night
alarm.on('armedNight', function() {
that.log("Armed to NIGHT");
if (that.currentStateCharacteristic) {
that.currentStateCharacteristic.updateValue(0, null);
}
if (that.targetStateCharacteristic) {
that.targetStateCharacteristic.updateValue(2, null);
}
});
// Disarmed
alarm.on('disarmed', function() {
that.log("Disarmed");
if (that.currentStateCharacteristic) {
that.currentStateCharacteristic.updateValue(1, null);
}
if (that.targetStateCharacteristic) {
that.targetStateCharacteristic.updateValue(3, null);
}
});
// Text Change
alarm.on('lcdtext', function(newText) {
that.log("LCD: " + newText);
if (that.lcdCharacteristic) {
that.lcdCharacteristic.updateValue(newText, null);
}
});
});
this.alarm = alarm;
}
AD2USBAccessory.prototype = {
setArmState: function(targetArmState) {
var that = this;
that.log("Desired target arm state: " + targetArmState);
// TARGET
// 0 - Stay
// 1 - Away
// 2 - Night
// 3 - Disarm
if (targetArmState == 0) {
that.alarm.armStay(that.pin);
}
else if (targetArmState == 1) {
that.alarm.armAway(that.pin);
}
else if (targetArmState == 2) {
that.alarm.armNight(that.pin);
}
else if (targetArmState == 3) {
that.alarm.disarm(that.pin);
}
// CURRENT
// 0 - Armed
// 1 - Disarmed
// 2 - Hold
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Nutech",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "AD2USB",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "AD2USBIF",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.ALARM_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.ALARM_CURRENT_STATE_CTYPE,
onUpdate: null,
onRegister: function(characteristic) {
that.currentStateCharacteristic = characteristic;
characteristic.eventEnabled = true;
},
perms: ["pr","ev"],
format: "int",
initialValue: 2,
supportEvents: true,
supportBonjour: false,
manfDescription: "Alarm current arm state",
designedMaxLength: 1
},{
cType: types.ALARM_TARGET_STATE_CTYPE,
onUpdate: function(value) { that.setArmState(value); },
onRegister: function(characteristic) {
that.targetStateCharacteristic = characteristic;
characteristic.eventEnabled = true;
},
perms: ["pw","pr","ev"],
format: "int",
initialValue: 1,
supportEvents: true,
supportBonjour: false,
manfDescription: "Alarm target arm state",
designedMaxLength: 1
},
{
cType: CUSTOM_PANEL_LCD_TEXT_CTYPE,
onUpdate: null,
onRegister: function(characteristic) {
that.lcdCharacteristic = characteristic;
characteristic.eventEnabled = true;
},
perms: ["pr","ev"],
format: "string",
initialValue: "Unknown",
supportEvents: false,
supportBonjour: false,
manfDescription: "Keypad Text",
designedMaxLength: 64
}]
}];
}
};
module.exports.accessory = AD2USBAccessory;

View File

@@ -1,126 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var carwings = require("carwingsjs");
function CarwingsAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.username = config["username"];
this.password = config["password"];
}
CarwingsAccessory.prototype = {
setPowerState: function(powerOn) {
var that = this;
carwings.login(this.username, this.password, function(err, result) {
if (!err) {
that.vin = result.vin;
that.log("Got VIN: " + that.vin);
if (powerOn) {
carwings.startClimateControl(that.vin, null, function(err, result) {
if (!err)
that.log("Started climate control.");
else
that.log("Error starting climate control: " + err);
});
}
else {
carwings.stopClimateControl(that.vin, function(err, result) {
if (!err)
that.log("Stopped climate control.");
else
that.log("Error stopping climate control: " + err);
});
}
}
else {
that.log("Error logging in: " + err);
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Nissan",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Change the power state of the car",
designedMaxLength: 1
}]
}];
}
};
module.exports.accessory = CarwingsAccessory;

View File

@@ -1,126 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var elkington = require("elkington");
function ElkM1Accessory(log, config) {
this.log = log;
this.name = config["name"];
this.zone = config["zone"];
this.host = config["host"];
this.port = config["port"];
this.pin = config["pin"];
this.arm = config["arm"];
}
ElkM1Accessory.prototype = {
setPowerState: function(alarmOn) {
var that = this;
if (!alarmOn)
{
return;
}
var elk = elkington.createConnection({
port: that.port,
host: that.host,
});
switch (that.arm)
{
case 'Away':
elk.armAway({area: that.zone, code: that.pin});
break;
case 'Stay':
elk.armStay({area: that.zone, code: that.pin});
break;
case 'Night':
elk.armNightInstant({area: that.zone, code: that.pin});
break;
default:
break;
}
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Elk",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "M1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Alarm the Zone",
designedMaxLength: 1
}]
}];
}
};
module.exports.accessory = ElkM1Accessory;

View File

@@ -1,116 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
function HomeMatic(log, config) {
this.log = log;
this.name = config["name"];
this.ccuID = config["ccu_id"];
this.ccuIP = config["ccu_ip"];
}
HomeMatic.prototype = {
setPowerState: function(powerOn) {
var binaryState = powerOn ? 1 : 0;
var that = this;
this.log("Setting power state of CCU to " + powerOn);
this.log(this.ccuID+ powerOn);
request.put({
url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuID+"&new_value="+ powerOn,
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
that.log("State change complete.");
}
else {
that.log("Error '"+err+"' setting lock state: " + body);
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "WeMo",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Change the power state of a Variable",
designedMaxLength: 1
}]
}];
}
};
module.exports.accessory = HomeMatic;

View File

@@ -1,160 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
function HttpAccessory(log, config) {
this.log = log;
// url info
this.on_url = config["on_url"];
this.off_url = config["off_url"];
this.brightness_url = config["brightness_url"];
this.http_method = config["http_method"];
// device info
this.name = config["name"];
}
HttpAccessory.prototype = {
httpRequest: function(url, method, callback) {
request({
url: url,
method: method
},
function (error, response, body) {
callback(error, response, body)
})
},
setPowerState: function(powerOn) {
var url;
if (powerOn) {
url = this.on_url
this.log("Setting power state on the '"+this.name+"' to on");
}else{
url = this.off_url
this.log("Setting power state on the '"+this.name+"' to off");
}
this.httpRequest(url, this.http_method, function(error, response, body){
if (error) {
return console.error('http power function failed:', error);
}else{
return console.log('http power function succeeded!');
}
});
},
setBrightness: function(level) {
var url = this.brightness_url.replace("%b", level)
this.log("Setting brightness on the '"+this.name+"' to " + level);
this.httpRequest(url, this.http_method, function(error, response, body){
if (error) {
return console.error('http brightness function failed:', error);
}else{
return console.log('http brightness function succeeded!');
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Http",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.LIGHTBULB_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: true,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1
},{
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) { that.setBrightness(value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
}]
}];
}
};
module.exports.accessory = HttpAccessory;

View File

@@ -1,305 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
// This seems to be the "id" of the official LiftMaster iOS app
var APP_ID = "JVM/G9Nwih5BwKgNCjLxiFUQxQijAebyyg8QUHr7JOrP+tuPb8iHfRHKwTmDzHOu"
function LiftMasterAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.username = config["username"];
this.password = config["password"];
this.requiredDeviceId = config["requiredDeviceId"];
}
LiftMasterAccessory.prototype = {
setState: function(state) {
this.targetState = state;
this.login();
},
login: function() {
var that = this;
// reset our logged-in state hint until we're logged in
this.deviceId = null;
// querystring params
var query = {
appId: APP_ID,
username: this.username,
password: this.password,
culture: "en"
};
// login to liftmaster
request.get({
url: "https://myqexternal.myqdevice.com/api/user/validatewithculture",
qs: query
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
// parse and interpret the response
var json = JSON.parse(body);
that.userId = json["UserId"];
that.securityToken = json["SecurityToken"];
that.log("Logged in with user ID " + that.userId);
that.getDevice();
}
else {
that.log("Error '"+err+"' logging in: " + body);
}
});
},
// find your garage door ID
getDevice: function() {
var that = this;
// querystring params
var query = {
appId: APP_ID,
SecurityToken: this.securityToken,
filterOn: "true"
};
// some necessary duplicated info in the headers
var headers = {
MyQApplicationId: APP_ID,
SecurityToken: this.securityToken
};
// request details of all your devices
request.get({
url: "https://myqexternal.myqdevice.com/api/v4/userdevicedetails/get",
qs: query,
headers: headers
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
// parse and interpret the response
var json = JSON.parse(body);
var devices = json["Devices"];
var foundDoors = [];
// look through the array of devices for an opener
for (var i=0; i<devices.length; i++) {
var device = devices[i];
if (device["MyQDeviceTypeName"] == "GarageDoorOpener") {
// If we haven't explicity specified a door ID, we'll loop to make sure we don't have multiple openers, which is confusing
if (that.requiredDeviceId == undefined) {
var thisDeviceId = device.MyQDeviceId;
var thisDoorName = "Unknown";
for (var j = 0; j < device.Attributes.length; j ++) {
var thisAttributeSet = device.Attributes[j];
if (thisAttributeSet.AttributeDisplayName == "desc") {
thisDoorName = thisAttributeSet.Value;
break;
}
}
foundDoors.push(thisDeviceId + " - " + thisDoorName);
}
// We specified a door ID, sanity check to make sure it's the one we expected
else if (that.requiredDeviceId == device.MyQDeviceId) {
that.deviceId = device.MyQDeviceId;
break;
}
}
}
// If we have multiple found doors, refuse to proceed
if (foundDoors.length > 1) {
that.log("WARNING: You have multiple doors on your MyQ account.");
that.log("WARNING: Specify the ID of the door you want to control using the 'requiredDeviceId' property in your config.json file.");
that.log("WARNING: You can have multiple liftmaster accessories to cover your multiple doors");
for (var j = 0; j < foundDoors.length; j++) {
that.log("Found Door: " + foundDoors[j]);
}
throw "FATAL: Please specify which specific door this Liftmaster accessory should control - you have multiples on your account";
}
// Did we get a device ID?
if (that.deviceId) {
that.log("Found an opener with ID " + that.deviceId +". Ready to send command...");
that.setTargetState();
}
else
{
that.log("Error: Couldn't find a door device, or the ID you specified isn't associated with your account");
}
}
else {
that.log("Error '"+err+"' getting devices: " + body);
}
});
},
setTargetState: function() {
var that = this;
var liftmasterState = (this.targetState + "") == "1" ? "0" : "1";
// querystring params
var query = {
appId: APP_ID,
SecurityToken: this.securityToken,
filterOn: "true"
};
// some necessary duplicated info in the headers
var headers = {
MyQApplicationId: APP_ID,
SecurityToken: this.securityToken
};
// PUT request body
var body = {
AttributeName: "desireddoorstate",
AttributeValue: liftmasterState,
ApplicationId: APP_ID,
SecurityToken: this.securityToken,
MyQDeviceId: this.deviceId
};
// send the state request to liftmaster
request.put({
url: "https://myqexternal.myqdevice.com/api/v4/DeviceAttribute/PutDeviceAttribute",
qs: query,
headers: headers,
body: body,
json: true
}, function(err, response, json) {
if (!err && response.statusCode == 200) {
if (json["ReturnCode"] == "0")
that.log("State was successfully set.");
else
that.log("Bad return code: " + json["ReturnCode"]);
that.log("Raw response " + JSON.stringify(json));
}
else {
that.log("Error '"+err+"' setting door state: " + JSON.stringify(json));
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "LiftMaster",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.GARAGE_DOOR_OPENER_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Garage Door Opener Control",
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.CURRENT_DOOR_STATE_CTYPE,
onUpdate: function(value) { that.log("Update current state to " + value); },
perms: ["pr","ev"],
format: "int",
initialValue: 0,
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla",
designedMinValue: 0,
designedMaxValue: 4,
designedMinStep: 1,
designedMaxLength: 1
},{
cType: types.TARGET_DOORSTATE_CTYPE,
onUpdate: function(value) { that.setState(value); },
perms: ["pr","pw","ev"],
format: "int",
initialValue: 0,
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla",
designedMinValue: 0,
designedMaxValue: 1,
designedMinStep: 1,
designedMaxLength: 1
},{
cType: types.OBSTRUCTION_DETECTED_CTYPE,
onUpdate: function(value) { that.log("Obstruction detected: " + value); },
perms: ["pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla"
}]
}];
}
};
module.exports.accessory = LiftMasterAccessory;

View File

@@ -1,196 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
function LockitronAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.lockID = config["lock_id"];
this.accessToken = config["api_token"];
}
LockitronAccessory.prototype = {
getState: function(callback) {
this.log("Getting current state...");
var that = this;
var query = {
access_token: this.accessToken
};
request.get({
url: "https://api.lockitron.com/v2/locks/"+this.lockID,
qs: query
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
var json = JSON.parse(body);
var state = json.state; // "lock" or "unlock"
var locked = state == "lock"
callback(locked);
}
else {
that.log("Error getting state (status code "+response.statusCode+"): " + err)
callback(undefined);
}
});
},
setState: function(state) {
this.log("Set state to " + state);
var lockitronState = (state == 1) ? "lock" : "unlock";
var that = this;
var query = {
access_token: this.accessToken,
state: lockitronState
};
request.put({
url: "https://api.lockitron.com/v2/locks/"+this.lockID,
qs: query
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
that.log("State change complete.");
}
else {
that.log("Error '"+err+"' setting lock state: " + body);
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Apigee",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-2",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.LOCK_MECHANISM_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Lock Mechanism",
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.CURRENT_LOCK_MECHANISM_STATE_CTYPE,
onRead: function(callback) { that.getState(callback); },
onUpdate: function(value) { that.log("Update current state to " + value); },
perms: ["pr","ev"],
format: "int",
initialValue: 0,
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla",
designedMinValue: 0,
designedMaxValue: 3,
designedMinStep: 1,
designedMaxLength: 1
},{
cType: types.TARGET_LOCK_MECHANISM_STATE_CTYPE,
onUpdate: function(value) { that.setState(value); },
perms: ["pr","pw","ev"],
format: "int",
initialValue: 0,
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla",
designedMinValue: 0,
designedMaxValue: 1,
designedMinStep: 1,
designedMaxLength: 1
}]
},{
sType: types.LOCK_MANAGEMENT_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Lock Management",
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.LOCK_MANAGEMENT_CONTROL_POINT_CTYPE,
onUpdate: function(value) { that.log("Update control point to " + value); },
perms: ["pw"],
format: "data",
initialValue: 0,
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla",
designedMaxLength: 255
},{
cType: types.VERSION_CTYPE,
onUpdate: function(value) { that.log("Update version to " + value); },
perms: ["pr"],
format: "string",
initialValue: "1.0",
supportEvents: false,
supportBonjour: false,
manfDescription: "BlaBla",
designedMaxLength: 255
}]
}];
}
};
module.exports.accessory = LockitronAccessory;

View File

@@ -1,146 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var sonos = require('sonos');
function SonosAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.playVolume = config["play_volume"];
this.device = null;
this.search();
}
SonosAccessory.prototype = {
search: function() {
var that = this;
sonos.search(function(device) {
that.log("Found device at " + device.host);
device.deviceDescription(function (err, description) {
if (description["zoneType"] == '3') {
that.log("Found playable device");
// device is an instance of sonos.Sonos
that.device = device;
}
});
});
},
setPlaying: function(playing) {
if (!this.device) {
this.log("No device found (yet?)");
return;
}
var that = this;
if (playing) {
this.device.play(function(err, success) {
that.log("Playback attempt with success: " + success);
});
if (this.playVolume) {
this.device.setVolume(this.playVolume, function(err, success) {
if (!err) {
that.log("Set volume to " + that.playVolume);
}
else {
that.log("Problem setting volume: " + err);
}
});
}
}
else {
this.device.stop(function(err, success) {
that.log("Stop attempt with success: " + success);
});
}
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Sonos",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Speakers",
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPlaying(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Change the playback state of the sonos",
designedMaxLength: 1
}]
}];
}
};
module.exports.accessory = SonosAccessory;

View File

@@ -1,139 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var wemo = require('wemo');
// extend our search timeout from 5 seconds to 60
wemo.SearchTimeout = 60000;
wemo.timeout = wemo.SearchTimeout // workaround for a bug in wemo.js v0.0.4
function WeMoAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.wemoName = config["wemo_name"];
this.device = null;
this.log("Searching for WeMo device with exact name '" + this.wemoName + "'...");
this.search();
}
WeMoAccessory.prototype = {
search: function() {
var that = this;
wemo.Search(this.wemoName, function(err, device) {
if (!err && device) {
that.log("Found '"+that.wemoName+"' device at " + device.ip);
that.device = new wemo(device.ip, device.port);
}
else {
that.log("Error finding device '" + that.wemoName + "': " + err);
that.log("Continuing search for WeMo device with exact name '" + that.wemoName + "'...");
that.search();
}
});
},
setPowerState: function(powerOn) {
if (!this.device) {
this.log("No '"+this.wemoName+"' device found (yet?)");
return;
}
var binaryState = powerOn ? 1 : 0;
var that = this;
this.log("Setting power state on the '"+this.wemoName+"' to " + binaryState);
this.device.setBinaryState(binaryState, function(err, result) {
if (!err) {
that.log("Successfully set power state on the '"+that.wemoName+"' to " + binaryState);
}
else {
that.log("Error setting power state on the '"+that.wemoName+"'")
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "WeMo",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Change the power state of the WeMo",
designedMaxLength: 1
}]
}];
}
};
module.exports.accessory = WeMoAccessory;

View File

@@ -1,151 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
function X10(log, config) {
this.log = log;
this.ip_address = config["ip_address"];
this.name = config["name"];
this.deviceID = config["device_id"];
this.protocol = config["protocol"];
this.canDim = config["can_dim"];
}
X10.prototype = {
setPowerState: function(powerOn) {
var binaryState = powerOn ? "on" : "off";
var that = this;
this.log("Setting power state of " + this.deviceID + " to " + powerOn);
request.put({
url: "http://"+this.ip_address+"/x10/"+this.deviceID+"/power/"+binaryState+"?protocol="+this.protocol,
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
that.log("State change complete.");
}
else {
that.log("Error '"+err+"' setting power state: " + body);
}
});
},
setBrightnessLevel: function(value) {
var that = this;
this.log("Setting brightness level of " + this.deviceID + " to " + value);
request.put({
url: "http://"+this.ip_address+"/x10/"+this.deviceID+"/brightness/?protocol="+this.protocol+"&value="+value,
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
that.log("State change complete.");
}
else {
that.log("Error '"+err+"' setting brightness level: " + body);
}
});
},
getServices: function() {
var that = this;
var services = [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "X10",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.LIGHTBULB_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Change the power state of a Variable",
designedMaxLength: 1
}]
}];
if (that.canDim) {
services[1].characteristics.push({
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) { that.setBrightnessLevel(value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: false,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
});
}
return services;
}
};
module.exports.accessory = X10;

View File

@@ -1,284 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
var xmldoc = require("xmldoc");
function XfinityHomeAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.email = config["email"];
this.password = config["password"];
this.dsig = config["dsig"];
this.pinCode = config["pin"];
}
XfinityHomeAccessory.prototype = {
armWithType: function(armed, type) {
this.log("Arming with type " + type + " = " + armed + "...");
this.targetArmed = armed;
this.targetArmType = type;
this.getLoginToken();
},
getLoginToken: function() {
this.log("Retrieving login token...");
var that = this;
request.post({
url: "https://login.comcast.net/api/login",
form: {
appkey:"iControl",
dsig: this.dsig,
u: this.email,
p: this.password
}
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
var doc = new xmldoc.XmlDocument(body);
that.loginToken = doc.valueWithPath("LoginToken");
that.refreshLoginCookie();
}
else {
that.log("Error '"+err+"' getting login token: " + body);
}
});
},
refreshLoginCookie: function() {
this.log("Refreshing login cookie...");
var that = this;
request.post({
url: "https://www.xfinityhomesecurity.com/rest/icontrol/login",
form: {
token: this.loginToken
}
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
// extract our "site" from the login response
var json = JSON.parse(body);
that.siteHref = json["login"]["site"]["href"];
// manual cookie handling
that.loginCookie = response.headers["set-cookie"];
that.getInstances();
}
else {
that.log("Error '"+err+"' refreshing login cookie: " + body);
}
});
},
getInstances: function() {
this.log("Getting instances for site " + this.siteHref + "...");
this.panelHref = null;
var that = this;
request.get({
url: "https://www.xfinityhomesecurity.com/"+that.siteHref+"/network/instances",
headers: { Cookie: this.loginCookie },
json: true
}, function(err, response, json) {
if (!err && response.statusCode == 200) {
// extract our "instance" from the response. look for the first "panel"
var instances = json["instances"]["instance"];
for (var i=0; i<instances.length; i++) {
var instance = instances[i];
if (instance["mediaType"] == "instance/panel") {
that.panelHref = instance.href;
}
}
if (that.panelHref) {
that.log("Found panel " + that.panelHref + ". Ready to arm.");
that.finishArm();
}
else {
that.log("Couldn't find a panel.");
}
}
else {
that.log("Error '"+err+"' getting instances: " + JSON.stringify(json));
}
});
},
finishArm: function() {
this.log("Finish arming with type " + this.targetArmType + " = " + this.targetArmed + "...");
var path, form;
var that = this;
if (!this.targetArmed) {
path = this.panelHref + "/functions/disarm";
form = {code: this.pinCode};
}
else {
path = this.panelHref + "/functions/arm";
form = {code: this.pinCode, armType: this.targetArmType };
}
request.post({
url: "https://www.xfinityhomesecurity.com"+path,
headers: { Cookie: this.loginCookie },
form: form
}, function(err, response, body) {
if (!err && response.statusCode >= 200 && response.statusCode < 300) {
that.log("Arm response: " + response);
}
else {
that.log("Error '"+err+"' performing arm request: " + body);
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Comcast",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Away Mode",
supportEvents: false,
supportBonjour: false,
manfDescription: "Away Mode service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.armWithType(value, "away"); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Turn on the Away alarm",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Night Mode",
supportEvents: false,
supportBonjour: false,
manfDescription: "Night Mode service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.armWithType(value, "night"); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Turn on the Night alarm",
designedMaxLength: 1
}]
},{
sType: types.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Stay Mode",
supportEvents: false,
supportBonjour: false,
manfDescription: "Stay Mode service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.armWithType(value, "stay"); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Turn on the Stay alarm",
designedMaxLength: 1
}]
}];
}
};
// Enable cookie handling and append our expected headers
request = request.defaults({
headers: {
"X-appkey": "comcastTokenKey",
"X-ClientInfo": "5.2.51",
"X-format": "json"
}
});
module.exports.accessory = XfinityHomeAccessory;

189
app.js
View File

@@ -1,189 +0,0 @@
var fs = require('fs');
var path = require('path');
var storage = require('node-persist');
var crypto = require('crypto');
console.log("Starting HomeBridge server...");
// Look for the configuration file
var configPath = path.join(__dirname, "config.json");
// Complain and exit if it doesn't exist yet
if (!fs.existsSync(configPath)) {
console.log("Couldn't find a config.json file in the same directory as app.js. Look at config-sample.json for examples of how to format your config.js and add your home accessories.");
process.exit(1);
}
// Initialize persistent storage
storage.initSync();
// Load up the configuration file
var config = JSON.parse(fs.readFileSync(configPath));
// Just to prevent them getting garbage collected
var accessories = [];
function startup() {
if (config.platforms) loadPlatforms();
if (config.accessories) loadAccessories();
}
function loadAccessories() {
// Instantiate all accessories in the config
console.log("Loading " + config.accessories.length + " accessories...");
for (var i=0; i<config.accessories.length; i++) {
var accessoryConfig = config.accessories[i];
// Load up the class for this accessory
var accessoryName = accessoryConfig["accessory"]; // like "WeMo"
var accessoryModule = require('./accessories/' + accessoryName + ".js"); // like "./accessories/WeMo.js"
var accessoryConstructor = accessoryModule.accessory; // like "WeMoAccessory", a JavaScript constructor
// Create a custom logging function that prepends the device display name for debugging
var name = accessoryConfig["name"];
var log = function(name) { return function(s) { console.log("[" + name + "] " + s); }; }(name);
log("Initializing " + accessoryName + " accessory...");
var accessory = new accessoryConstructor(log, accessoryConfig);
accessories.push(accessory);
// Extract the raw "services" for this accessory which is a big array of objects describing the various
// hooks in and out of HomeKit for the HAP-NodeJS server.
var services = accessory.getServices();
// Create the HAP server for this accessory
createHAPServer(name, services, accessory.transportCategory);
}
}
function loadPlatforms() {
console.log("Loading " + config.platforms.length + " platforms...");
for (var i=0; i<config.platforms.length; i++) {
var platformConfig = config.platforms[i];
// Load up the class for this accessory
var platformName = platformConfig["platform"]; // like "Wink"
var platformModule = require('./platforms/' + platformName + ".js"); // like "./platforms/Wink.js"
var platformConstructor = platformModule.platform; // like "WinkPlatform", a JavaScript constructor
// Create a custom logging function that prepends the platform display name for debugging
var name = platformConfig["name"];
var log = function(name) { return function(s) { console.log("[" + name + "] " + s); }; }(name);
log("Initializing " + platformName + " platform...");
var platform = new platformConstructor(log, platformConfig);
// query for devices
platform.accessories(function(foundAccessories){
// loop through accessories adding them to the list and registering them
for (var i = 0; i < foundAccessories.length; i++) {
accessory = foundAccessories[i]
accessories.push(accessory);
log("Initializing device with name " + accessory.name + "...")
// Extract the raw "services" for this accessory which is a big array of objects describing the various
// hooks in and out of HomeKit for the HAP-NodeJS server.
var services = accessory.getServices();
// Create the HAP server for this accessory
createHAPServer(accessory.name, services, accessory.transportCategory);
}
accessories.push.apply(accessories, foundAccessories);
})
}
}
//
// Creates the actual HAP servers which listen on different sockets
//
// Pull in required HAP-NodeJS stuff
var accessory_Factor = new require("HAP-NodeJS/Accessory.js");
var accessoryController_Factor = new require("HAP-NodeJS/AccessoryController.js");
var service_Factor = new require("HAP-NodeJS/Service.js");
var characteristic_Factor = new require("HAP-NodeJS/Characteristic.js");
// Each accessory has its own little server. We'll need to allocate some ports for these servers
var nextPort = 51826;
var nextServer = 0;
var accessoryServers = [];
var accessoryControllers = [];
var usernames = {};
function createHAPServer(name, services, transportCategory) {
var accessoryController = new accessoryController_Factor.AccessoryController();
//loop through services
for (var j = 0; j < services.length; j++) {
var service = new service_Factor.Service(services[j].sType);
//loop through characteristics
for (var k = 0; k < services[j].characteristics.length; k++) {
var options = {
onRead: services[j].characteristics[k].onRead,
onRegister: services[j].characteristics[k].onRegister,
type: services[j].characteristics[k].cType,
perms: services[j].characteristics[k].perms,
format: services[j].characteristics[k].format,
initialValue: services[j].characteristics[k].initialValue,
supportEvents: services[j].characteristics[k].supportEvents,
supportBonjour: services[j].characteristics[k].supportBonjour,
manfDescription: services[j].characteristics[k].manfDescription,
designedMaxLength: services[j].characteristics[k].designedMaxLength,
designedMinValue: services[j].characteristics[k].designedMinValue,
designedMaxValue: services[j].characteristics[k].designedMaxValue,
designedMinStep: services[j].characteristics[k].designedMinStep,
unit: services[j].characteristics[k].unit
};
var characteristic = new characteristic_Factor.Characteristic(options, services[j].characteristics[k].onUpdate);
service.addCharacteristic(characteristic);
}
accessoryController.addService(service);
}
// create a unique "username" for this accessory based on the default display name
var username = createUsername(name);
if (usernames[username]) {
console.log("Cannot create another accessory with the same name '" + name + "'. The 'name' property must be unique for each accessory.");
return;
}
// remember that we used this name already
usernames[username] = name;
// increment ports for each accessory
nextPort = nextPort + (nextServer*2);
// hardcode the PIN to something random (same PIN as HAP-NodeJS sample accessories)
var pincode = "031-45-154";
var accessory = new accessory_Factor.Accessory(name, username, storage, parseInt(nextPort), pincode, accessoryController, transportCategory);
accessoryServers[nextServer] = accessory;
accessoryControllers[nextServer] = accessoryController;
accessory.publishAccessory();
nextServer++;
}
// Creates a unique "username" for HomeKit from a hash of the given string
function createUsername(str) {
// Hash str into something like "098F6BCD4621D373CADE4E832627B4F6"
var hash = crypto.createHash('md5').update(str).digest("hex").toUpperCase();
// Turn it into a MAC-address-looking "username" for HomeKit
return hash[0] + hash[1] + ":" +
hash[2] + hash[3] + ":" +
hash[4] + hash[5] + ":" +
hash[6] + hash[7] + ":" +
hash[8] + hash[9] + ":" +
hash[10] + hash[11];
}
startup();

17
bin/homebridge Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env node
//
// This executable sets up the environment and runs the HomeBridge CLI.
//
'use strict';
process.title = 'homebridge';
// Find the HomeBridge lib
var path = require('path');
var fs = require('fs');
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
// Run HomeBridge
require(lib + '/cli')();

View File

@@ -1,125 +1,24 @@
{ {
"description": "This is an example configuration file with all supported devices. You can use this as a template for creating your own configuration file containing devices you actually own.", "bridge": {
"name": "Homebridge",
"platforms": [ "username": "CC:22:3D:E3:CE:30",
{ "port": 51826,
"platform": "Wink", "pin": "031-45-154"
"name": "Wink", },
"client_id": "YOUR_WINK_API_CLIENT_ID",
"client_secret": "YOUR_WINK_API_CLIENT_SECRET", "description": "This is an example configuration file with one fake accessory and one fake platform. You can use this as a template for creating your own configuration file containing devices you actually own.",
"username": "your@email.com",
"password": "WINK_PASSWORD"
},
{
"platform": "SmartThings",
"name": "SmartThings",
"app_id": "JSON SmartApp Id",
"access_token": "JSON SmartApp AccessToken"
},
{
"platform": "Domoticz",
"name": "Domoticz",
"server": "127.0.0.1",
"port": "8005"
},
{
"platform": "PhilipsHue",
"name": "Phillips Hue",
"username": ""
},
{
"platform": "ISY",
"name": "ISY",
"host": "192.168.1.20",
"port": "8000",
"username": "username",
"password": "password"
}
],
"accessories": [ "accessories": [
{ {
"accessory": "WeMo", "accessory": "WeMo",
"name": "Coffee Maker", "name": "Coffee Maker"
"description": "This shim supports Belkin WeMo devices on the same network as this server. You can create duplicate entries for this device and change the 'name' attribute to reflect what device is plugged into the WeMo, for instance 'Air Conditioner' or 'Coffee Maker'. This name will be used by Siri. Make sure to update the 'wemo_name' attribute with the EXACT name of the device in the WeMo app itself. This can be the same value as 'name' but it doesn't have to be.", }
"wemo_name": "CoffeeMaker" ],
},
"platforms": [
{ {
"accessory": "LiftMaster", "platform" : "PhilipsHue",
"name": "Garage Door", "name" : "Hue"
"description": "This shim supports LiftMaster garage door openers that are already internet-connected to the 'MyQ' service.",
// "requiredDeviceId", "<ID of door if you have multiple doors, prompted by shim during startup if needed>",
"username": "your-liftmaster-username",
"password" : "your-liftmaster-password"
},
{
"accessory": "Sonos",
"name": "Speakers",
"description": "This shim supports Sonos devices on the same network as this server. It acts as a simple switch that calls play() or pause() on the Sonos, so it's only useful for pausing and resuming tracks or radio stations that are already in the queue. When 'play_volume' is nonzero, the volume will be reset to that value when it turns the Sonos on.",
"play_volume": 25
},
{
"accessory": "Lockitron",
"name": "Front Door",
"description": "This shim supports Lockitron locks. It uses the Lockitron cloud API, so the Lockitron must be 'awake' for locking and unlocking to actually happen. You can wake up Lockitron after issuing an lock/unlock command by knocking on the door.",
"lock_id": "your-lock-id",
"api_token" : "your-lockitron-api-access-token"
},
{
"accessory": "Carwings",
"name": "Leaf",
"description": "This shim supports controlling climate control on Nissan cars with Carwings. Note that Carwings is super slow and it may take up to 5 minutes for your command to be processed by the Carwings system.",
"username": "your-carwings-username",
"password" : "your-carwings-password"
},
{
"accessory": "XfinityHome",
"name": "Xfinity Home",
"description": "This shim supports the 'Xfinity Home' security system. Unfortunately I don't know how to generate the 'dsig' property, so you'll need to figure yours out by running the Xfinity Home app on your iOS device while connected to a proxy server like Charles. If you didn't understand any of that, sorry! I welcome any suggestions for how to figure out dsig automatically.",
"email": "your-comcast-email@example.com",
"password": "your-comcast-password",
"dsig": "your-digital-signature",
"pin": "your-security-system-pin-code"
},
{
"accessory": "HomeMatic",
"name": "Light",
"description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)",
"ccu_id": "The XMP-API id of your HomeMatic device",
"ccu_ip": "The IP-Adress of your HomeMatic CCU device"
},
{
"accessory": "X10",
"name": "Lamp",
"ip_address": "localhost:3000",
"device_id": "E1",
"protocol": "pl",
"can_dim": true
},
{
"accessory": "Http",
"name": "Kitchen Lamp",
"on_url": "https://192.168.1.22:3030/devices/23222/on",
"off_url": "https://192.168.1.22:3030/devices/23222/off",
"brightness_url": "https://192.168.1.22:3030/devices/23222/brightness/%b",
"http_method": "POST"
},{
"accessory": "ELKM1",
"name": "Security System",
"description": "Allows basic control of Elk M1 security system. You can use 1 of 3 arm modes: Away, Stay, Night. If you need to access all 3, create 3 accessories with different names.",
"zone": "1",
"host": "192.168.1.10",
"port": "2101",
"pin": "1234",
"arm": "Away"
},
{
"accessory": "AD2USB",
"name": "Alarm",
"description": "Arm, disarm, and status monitoring of the default partition for Honeywell/Ademco alarm systems. Requires network configured AD2USB interface",
"host": "192.168.1.200", // IP address of the SER2SOCK service
"port" : 4999, // Port the SER2SOCK process is running on
"pin": "1234" // PIN used for arming / disarming
} }
] ]
} }

View File

@@ -0,0 +1,3 @@
This is an example plugin for homebridge. It is a fully-working implementation of a Lockitron door lock accessory.
Remember to run `npm install` in this directory in order to install the dependencies needed by this plugin. If a user is installing your plugin from npm, this will be done automatically for them.

View File

@@ -0,0 +1,82 @@
var request = require("request");
var Service, Characteristic;
module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerAccessory("homebridge-lockitron", "Lockitron", LockitronAccessory);
}
function LockitronAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.accessToken = config["api_token"];
this.lockID = config["lock_id"];
this.service = new Service.LockMechanism(this.name);
this.service
.getCharacteristic(Characteristic.LockCurrentState)
.on('get', this.getState.bind(this));
this.service
.getCharacteristic(Characteristic.LockTargetState)
.on('get', this.getState.bind(this))
.on('set', this.setState.bind(this));
}
LockitronAccessory.prototype.getState = function(callback) {
this.log("Getting current state...");
request.get({
url: "https://api.lockitron.com/v2/locks/"+this.lockID,
qs: { access_token: this.accessToken }
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
var json = JSON.parse(body);
var state = json.state; // "lock" or "unlock"
this.log("Lock state is %s", state);
var locked = state == "lock"
callback(null, locked); // success
}
else {
this.log("Error getting state (status code %s): %s", response.statusCode, err);
callback(err);
}
}.bind(this));
}
LockitronAccessory.prototype.setState = function(state, callback) {
var lockitronState = (state == Characteristic.LockTargetState.SECURED) ? "lock" : "unlock";
this.log("Set state to %s", lockitronState);
request.put({
url: "https://api.lockitron.com/v2/locks/"+this.lockID,
qs: { access_token: this.accessToken, state: lockitronState }
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
this.log("State change complete.");
// we succeeded, so update the "current" state as well
var currentState = (state == Characteristic.LockTargetState.SECURED) ?
Characteristic.LockCurrentState.SECURED : Characteristic.LockCurrentState.UNSECURED;
this.service
.setCharacteristic(Characteristic.LockCurrentState, currentState);
callback(null); // success
}
else {
this.log("Error '%s' setting lock state. Response: %s", err, body);
callback(err || new Error("Error setting lock state."));
}
}.bind(this));
}
LockitronAccessory.prototype.getServices = function() {
return [this.service];
}

View File

@@ -0,0 +1,23 @@
{
"name": "homebridge-lockitron",
"version": "0.0.1",
"description": "Lockitron plugin for homebridge: https://github.com/nfarina/homebridge",
"license": "ISC",
"keywords": [
"homebridge-plugin"
],
"repository": {
"type": "git",
"url": "git://github.com/example/homebridge-lockitron.git"
},
"bugs": {
"url": "http://github.com/example/homebridge-lockitron/issues"
},
"engines": {
"node": ">=0.12.0",
"homebridge": ">=0.2.0"
},
"dependencies": {
"request": "^2.65.0"
}
}

109
lib/api.js Normal file
View File

@@ -0,0 +1,109 @@
var hap = require("hap-nodejs");
var hapLegacyTypes = require("hap-nodejs/accessories/types.js");
var log = require("./logger")._system;
var User = require("./user").User;
// The official homebridge API is the object we feed the plugin's exported initializer function.
module.exports = {
API: API
}
function API() {
this._accessories = {}; // this._accessories[pluginName.accessoryName] = accessory constructor
this._platforms = {}; // this._platforms[pluginName.platformName] = platform constructor
// expose the User class methods to plugins to get paths. Example: homebridge.user.storagePath()
this.user = User;
// expose HAP-NodeJS in its entirely for plugins to use instead of making Plugins
// require() it as a dependency - it's a heavy dependency so we don't want it in
// every single plugin.
this.hap = hap;
// we also need to "bolt on" the legacy "types" constants for older accessories/platforms
// still using the "object literal" style JSON.
this.hapLegacyTypes = hapLegacyTypes;
}
API.prototype.accessory = function(name) {
// if you passed the "short form" name like "Lockitron" instead of "homebridge-lockitron.Lockitron",
// see if it matches exactly one accessory.
if (name.indexOf('.') == -1) {
var found = [];
for (var fullName in this._accessories) {
if (fullName.split(".")[1] == name)
found.push(fullName);
}
if (found.length == 1) {
return this._accessories[found[0]];
}
else if (found.length > 1) {
throw new Error("The requested accessory '" + name + "' has been registered multiple times. Please be more specific by writing one of: " + found.join(", "));
}
else {
throw new Error("The requested accessory '" + name + "' was not registered by any plugin.");
}
}
else {
if (!this._accessories[name])
throw new Error("The requested accessory '" + name + "' was not registered by any plugin.");
return this._accessories[name];
}
}
API.prototype.registerAccessory = function(pluginName, accessoryName, constructor) {
var fullName = pluginName + "." + accessoryName;
if (this._accessories[fullName])
throw new Error("Attempting to register an accessory '" + fullName + "' which has already been registered!");
log.info("Registering accessory '%s'", fullName);
this._accessories[fullName] = constructor;
}
API.prototype.platform = function(name) {
// if you passed the "short form" name like "Lockitron" instead of "homebridge-lockitron.Lockitron",
// see if it matches exactly one platform.
if (name.indexOf('.') == -1) {
var found = [];
for (var fullName in this._platforms) {
if (fullName.split(".")[1] == name)
found.push(fullName);
}
if (found.length == 1) {
return this._platforms[found[0]];
}
else if (found.length > 1) {
throw new Error("The requested platform '" + name + "' has been registered multiple times. Please be more specific by writing one of: " + found.join(", "));
}
else {
throw new Error("The requested platform '" + name + "' was not registered by any plugin.");
}
}
else {
if (!this._platforms[name])
throw new Error("The requested platform '" + name + "' was not registered by any plugin.");
return this._platforms[name];
}
}
API.prototype.registerPlatform = function(pluginName, platformName, constructor) {
var fullName = pluginName + "." + platformName;
if (this._platforms[fullName])
throw new Error("Attempting to register a platform '" + fullName + "' which has already been registered!");
log.info("Registering platform '%s'", fullName);
this._platforms[fullName] = constructor;
}

39
lib/cli.js Normal file
View File

@@ -0,0 +1,39 @@
var program = require('commander');
var hap = require("hap-nodejs");
var version = require('./version');
var Server = require('./server').Server;
var Plugin = require('./plugin').Plugin;
var User = require('./user').User;
var log = require("./logger")._system;
'use strict';
module.exports = function() {
var insecureAccess = false;
program
.version(version)
.option('-P, --plugin-path [path]', 'look for plugins installed at [path] as well as the default locations ([path] can also point to a single plugin)', function(p) { Plugin.addPluginPath(p); })
.option('-U, --user-storage-path [path]', 'look for homebridge user files at [path] instead of the default location (~/.homebridge)', function(p) { User.setStoragePath(p); })
.option('-D, --debug', 'turn on debug level logging', function() { require('./logger').setDebugEnabled(true) })
.option('-I, --insecure', 'allow unauthenticated requests (for easier hacking)', function() { insecureAccess = true })
.parse(process.argv);
// Initialize HAP-NodeJS with a custom persist directory
hap.init(User.persistPath());
var server = new Server(insecureAccess);
var signals = { 'SIGINT': 2, 'SIGTERM': 15 };
Object.keys(signals).forEach(function (signal) {
process.on(signal, function () {
log.info("Got %s, shutting down Homebridge...", signal);
// FIXME: Shut down server cleanly
process.exit(128 + signals[signal]);
});
});
server.run();
}

88
lib/logger.js Normal file
View File

@@ -0,0 +1,88 @@
var chalk = require('chalk');
var util = require('util');
'use strict';
module.exports = {
Logger: Logger,
setDebugEnabled: setDebugEnabled,
_system: new Logger() // system logger, for internal use only
}
var DEBUG_ENABLED = false;
// Turns on debug level logging
function setDebugEnabled(enabled) {
DEBUG_ENABLED = enabled;
}
// global cache of logger instances by plugin name
var loggerCache = {};
/**
* Logger class
*/
function Logger(prefix) {
this.prefix = prefix;
}
Logger.prototype.debug = function(msg) {
if (DEBUG_ENABLED)
this.log.apply(this, ['debug'].concat(Array.prototype.slice.call(arguments)));
}
Logger.prototype.info = function(msg) {
this.log.apply(this, ['info'].concat(Array.prototype.slice.call(arguments)));
}
Logger.prototype.warn = function(msg) {
this.log.apply(this, ['warn'].concat(Array.prototype.slice.call(arguments)));
}
Logger.prototype.error = function(msg) {
this.log.apply(this, ['error'].concat(Array.prototype.slice.call(arguments)));
}
Logger.prototype.log = function(level, msg) {
msg = util.format.apply(util, Array.prototype.slice.call(arguments, 1));
func = console.log;
if (level == 'debug') {
msg = chalk.gray(msg);
}
else if (level == 'warn') {
msg = chalk.yellow(msg);
func = console.error;
}
else if (level == 'error') {
msg = chalk.bold.red(msg);
func = console.error;
}
// prepend prefix if applicable
if (this.prefix)
msg = chalk.cyan("[" + this.prefix + "]") + " " + msg;
func(msg);
}
Logger.withPrefix = function(prefix) {
if (!loggerCache[prefix]) {
// create a class-like logger thing that acts as a function as well
// as an instance of Logger.
var logger = new Logger(prefix);
var log = logger.info.bind(logger);
log.debug = logger.debug;
log.info = logger.info;
log.warn = logger.warn;
log.error = logger.error;
log.log = logger.log;
log.prefix = logger.prefix;
loggerCache[prefix] = log;
}
return loggerCache[prefix];
}

203
lib/plugin.js Normal file
View File

@@ -0,0 +1,203 @@
var path = require('path');
var fs = require('fs');
var semver = require('semver');
var User = require('./user').User;
var version = require('./version');
var log = require("./logger")._system;
'use strict';
module.exports = {
Plugin: Plugin
}
/**
* Homebridge Plugin.
*
* Allows for discovering and loading installed Homebridge plugins.
*/
function Plugin(pluginPath) {
this.pluginPath = pluginPath; // like "/usr/local/lib/node_modules/homebridge-lockitron"
this.initializer; // exported function from the plugin that initializes it
}
Plugin.prototype.name = function() {
return path.basename(this.pluginPath);
}
Plugin.prototype.load = function(options) {
options = options || {};
// does this plugin exist at all?
if (!fs.existsSync(this.pluginPath)) {
throw new Error("Plugin " + this.pluginPath + " was not found. Make sure the module '" + this.pluginPath + "' is installed.");
}
// attempt to load package.json
var pjson = Plugin.loadPackageJSON(this.pluginPath);
// very temporary fix for first wave of plugins
if (pjson.peerDepdendencies && (!pjson.engines || !pjson.engines.homebridge)) {
var engines = pjson.engines || {}
engines.homebridge = pjson.peerDepdendencies.homebridge;
pjson.engines = engines;
}
// pluck out the HomeBridge version requirement
if (!pjson.engines || !pjson.engines.homebridge) {
throw new Error("Plugin " + this.pluginPath + " does not contain the 'homebridge' package in 'engines'.");
}
var versionRequired = pjson.engines.homebridge;
// make sure the version is satisfied by the currently running version of HomeBridge
if (!semver.satisfies(version, versionRequired)) {
throw new Error("Plugin " + this.pluginPath + " requires a HomeBridge version of " + versionRequired + " which does not satisfy the current HomeBridge version of " + version + ". You may need to upgrade your installation of HomeBridge.");
}
// figure out the main module - index.js unless otherwise specified
var main = pjson.main || "./index.js";
var mainPath = path.join(this.pluginPath, main);
// try to require() it and grab the exported initialization hook
this.initializer = require(mainPath);
}
Plugin.loadPackageJSON = function(pluginPath) {
// check for a package.json
var pjsonPath = path.join(pluginPath, "package.json");
var pjson = null;
if (!fs.existsSync(pjsonPath)) {
throw new Error("Plugin " + pluginPath + " does not contain a package.json.");
}
try {
// attempt to parse package.json
pjson = JSON.parse(fs.readFileSync(pjsonPath));
}
catch (err) {
throw new Error("Plugin " + pluginPath + " contains an invalid package.json. Error: " + err);
}
// make sure the name is prefixed with 'homebridge-'
if (!pjson.name || pjson.name.indexOf('homebridge-') != 0) {
throw new Error("Plugin " + pluginPath + " does not have a package name that begins with 'homebridge-'.");
}
// verify that it's tagged with the correct keyword
if (!pjson.keywords || pjson.keywords.indexOf("homebridge-plugin") == -1) {
throw new Error("Plugin " + pluginPath + " package.json does not contain the keyword 'homebridge-plugin'.");
}
return pjson;
}
Plugin.getDefaultPaths = function() {
var win32 = process.platform === 'win32';
var paths = [];
// add the paths used by require()
paths = paths.concat(require.main.paths);
// THIS SECTION FROM: https://github.com/yeoman/environment/blob/master/lib/resolver.js
// Adding global npm directories
// We tried using npm to get the global modules path, but it haven't work out
// because of bugs in the parseable implementation of `ls` command and mostly
// performance issues. So, we go with our best bet for now.
if (process.env.NODE_PATH) {
paths = process.env.NODE_PATH.split(path.delimiter)
.filter(function(p) { return !!p; }) // trim out empty values
.concat(paths);
} else {
// Default paths for each system
if (win32) {
paths.push(path.join(process.env.APPDATA, 'npm/node_modules'));
} else {
paths.push('/usr/local/lib/node_modules');
paths.push('/usr/lib/node_modules');
}
}
return paths;
}
// All search paths we will use to discover installed plugins
Plugin.paths = Plugin.getDefaultPaths();
Plugin.addPluginPath = function(pluginPath) {
Plugin.paths.unshift(path.resolve(process.cwd(), pluginPath));
}
// Gets all plugins installed on the local system
Plugin.installed = function() {
var plugins = [];
var pluginsByName = {}; // don't add duplicate plugins
var searchedPaths = {}; // don't search the same paths twice
// search for plugins among all known paths, in order
for (var index in Plugin.paths) {
var requirePath = Plugin.paths[index];
// did we already search this path?
if (searchedPaths[requirePath])
continue;
searchedPaths[requirePath] = true;
// just because this path is in require.main.paths doesn't mean it necessarily exists!
if (!fs.existsSync(requirePath))
continue;
var names = fs.readdirSync(requirePath);
// does this path point inside a single plugin and not a directory containing plugins?
if (fs.existsSync(path.join(requirePath, "package.json")))
names = [""];
// read through each directory in this node_modules folder
for (var index2 in names) {
var name = names[index2];
// reconstruct full path
var pluginPath = path.join(requirePath, name);
// we only care about directories
if (!fs.statSync(pluginPath).isDirectory()) continue;
// does this module contain a package.json?
var pjson;
try {
// throws an Error if this isn't a homebridge plugin
pjson = Plugin.loadPackageJSON(pluginPath);
}
catch (err) {
// is this "trying" to be a homebridge plugin? if so let you know what went wrong.
if (!name || name.indexOf('homebridge-') == 0) {
log.warn(err.message);
}
// skip this module
continue;
}
// get actual name if this path points inside a single plugin
if (!name) name = pjson.name;
// add it to the return list
if (!pluginsByName[name]) {
pluginsByName[name] = pluginPath;
plugins.push(new Plugin(pluginPath));
}
else {
log.warn("Warning: skipping plugin found at '" + pluginPath + "' since we already loaded the same plugin from '" + pluginsByName[name] + "'.");
}
}
}
return plugins;
}

298
lib/server.js Normal file
View File

@@ -0,0 +1,298 @@
var path = require('path');
var fs = require('fs');
var uuid = require("hap-nodejs").uuid;
var Bridge = require("hap-nodejs").Bridge;
var Accessory = require("hap-nodejs").Accessory;
var Service = require("hap-nodejs").Service;
var Characteristic = require("hap-nodejs").Characteristic;
var AccessoryLoader = require("hap-nodejs").AccessoryLoader;
var once = require("hap-nodejs/lib/util/once").once;
var Plugin = require('./plugin').Plugin;
var User = require('./user').User;
var API = require('./api').API;
var log = require("./logger")._system;
var Logger = require('./logger').Logger;
'use strict';
module.exports = {
Server: Server
}
function Server(insecureAccess) {
this._api = new API(); // object we feed to Plugins
this._plugins = this._loadPlugins(); // plugins[name] = Plugin instance
this._config = this._loadConfig();
this._bridge = this._createBridge();
// Server is "secure by default", meaning it creates a top-level Bridge accessory that
// will not allow unauthenticated requests. This matches the behavior of actual HomeKit
// accessories. However you can set this to true to allow all requests without authentication,
// which can be useful for easy hacking. Note that this will expose all functions of your
// bridged accessories, like changing charactersitics (i.e. flipping your lights on and off).
this._allowInsecureAccess = insecureAccess || false;
}
Server.prototype.run = function() {
// keep track of async calls we're waiting for callbacks on before we can start up
this._asyncCalls = 0;
this._asyncWait = true;
if (this._config.platforms) this._loadPlatforms();
if (this._config.accessories) this._loadAccessories();
this._asyncWait = false;
// publish now unless we're waiting on anyone
if (this._asyncCalls == 0)
this._publish();
}
Server.prototype._publish = function() {
// pull out our custom Bridge settings from config.json, if any
var bridgeConfig = this._config.bridge || {};
this._printPin(bridgeConfig.pin);
this._bridge.publish({
username: bridgeConfig.username || "CC:22:3D:E3:CE:30",
port: bridgeConfig.port || 51826,
pincode: bridgeConfig.pin || "031-45-154",
category: Accessory.Categories.BRIDGE
}, this._allowInsecureAccess);
log.info("Homebridge is running on port %s.", bridgeConfig.port || 51826);
}
Server.prototype._loadPlugins = function(accessories, platforms) {
var plugins = {};
var foundOnePlugin = false;
// load and validate plugins - check for valid package.json, etc.
Plugin.installed().forEach(function(plugin) {
// attempt to load it
try {
plugin.load();
}
catch (err) {
log.error("====================")
log.error("ERROR LOADING PLUGIN " + plugin.name() + ":")
log.error(err.stack);
log.error("====================")
plugin.loadError = err;
}
if (!plugin.loadError) {
// add it to our dict for easy lookup later
plugins[plugin.name()] = plugin;
log.info("Loaded plugin: " + plugin.name());
// call the plugin's initializer and pass it our API instance
plugin.initializer(this._api);
log.info("---");
foundOnePlugin = true;
}
}.bind(this));
// Complain if you don't have any plugins.
if (!foundOnePlugin) {
log.warn("No plugins found. See the README for information on installing plugins.")
}
return plugins;
}
Server.prototype._loadConfig = function() {
// Look for the configuration file
var configPath = User.configPath();
// Complain and exit if it doesn't exist yet
if (!fs.existsSync(configPath)) {
log.error("Couldn't find a config.json file at '"+configPath+"'. Look at config-sample.json for examples of how to format your config.js and add your home accessories.");
process.exit(1);
}
// Load up the configuration file
var config;
try {
config = JSON.parse(fs.readFileSync(configPath));
}
catch (err) {
log.error("There was a problem reading your config.json file.");
log.error("Please try pasting your config.json file here to validate it: http://jsonlint.com");
log.error("");
throw err;
}
var accessoryCount = (config.accessories && config.accessories.length) || 0;
var username = config.bridge.username;
var validMac = /^([0-9A-F]{2}:){5}([0-9A-F]{2})$/;
if (!validMac.test(username)){
throw new Error('Not a valid username: ' + username + '. Must be 6 pairs of colon-' +
'separated hexadecimal chars (A-F 0-9), like a MAC address.');
}
var accessoryCount = (config.accessories && config.accessories.length) || 0;
var platformCount = (config.platforms && config.platforms.length) || 0;
log.info("Loaded config.json with %s accessories and %s platforms.", accessoryCount, platformCount);
log.info("---");
return config;
}
Server.prototype._createBridge = function() {
// pull out our custom Bridge settings from config.json, if any
var bridgeConfig = this._config.bridge || {};
// Create our Bridge which will host all loaded Accessories
return new Bridge(bridgeConfig.name || 'Homebridge', uuid.generate("HomeBridge"));
}
Server.prototype._loadAccessories = function() {
// Instantiate all accessories in the config
log.info("Loading " + this._config.accessories.length + " accessories...");
for (var i=0; i<this._config.accessories.length; i++) {
var accessoryConfig = this._config.accessories[i];
// Load up the class for this accessory
var accessoryType = accessoryConfig["accessory"]; // like "Lockitron"
var accessoryConstructor = this._api.accessory(accessoryType); // like "LockitronAccessory", a JavaScript constructor
if (!accessoryConstructor)
throw new Error("Your config.json is requesting the accessory '" + accessoryType + "' which has not been published by any installed plugins.");
// Create a custom logging function that prepends the device display name for debugging
var accessoryName = accessoryConfig["name"];
var accessoryLogger = Logger.withPrefix(accessoryName);
accessoryLogger("Initializing %s accessory...", accessoryType);
var accessoryInstance = new accessoryConstructor(accessoryLogger, accessoryConfig);
var accessory = this._createAccessory(accessoryInstance, accessoryName, accessoryType, accessoryConfig.uuid_base); //pass accessoryType for UUID generation, and optional parameter uuid_base which can be used instead of displayName for UUID generation
// add it to the bridge
this._bridge.addBridgedAccessory(accessory);
}
}
Server.prototype._loadPlatforms = function() {
log.info("Loading " + this._config.platforms.length + " platforms...");
for (var i=0; i<this._config.platforms.length; i++) {
var platformConfig = this._config.platforms[i];
// Load up the class for this accessory
var platformType = platformConfig["platform"]; // like "Wink"
var platformName = platformConfig["name"];
var platformConstructor = this._api.platform(platformType); // like "WinkPlatform", a JavaScript constructor
if (!platformConstructor)
throw new Error("Your config.json is requesting the platform '" + platformType + "' which has not been published by any installed plugins.");
// Create a custom logging function that prepends the platform name for debugging
var platformLogger = Logger.withPrefix(platformName);
platformLogger("Initializing %s platform...", platformType);
var platformInstance = new platformConstructor(platformLogger, platformConfig);
this._loadPlatformAccessories(platformInstance, platformLogger, platformType);
}
}
Server.prototype._loadPlatformAccessories = function(platformInstance, log, platformType) {
this._asyncCalls++;
platformInstance.accessories(once(function(foundAccessories){
this._asyncCalls--;
// loop through accessories adding them to the list and registering them
for (var i = 0; i < foundAccessories.length; i++) {
var accessoryInstance = foundAccessories[i];
var accessoryName = accessoryInstance.name; // assume this property was set
log("Initializing platform accessory '%s'...", accessoryName);
var accessory = this._createAccessory(accessoryInstance, accessoryName, platformType, accessoryInstance.uuid_base);
// add it to the bridge
this._bridge.addBridgedAccessory(accessory);
}
// were we the last callback?
if (this._asyncCalls === 0 && !this._asyncWait)
this._publish();
}.bind(this)));
}
Server.prototype._createAccessory = function(accessoryInstance, displayName, accessoryType, uuid_base) {
var services = accessoryInstance.getServices();
if (!(services[0] instanceof Service)) {
// The returned "services" for this accessory is assumed to be the old style: a big array
// of JSON-style objects that will need to be parsed by HAP-NodeJS's AccessoryLoader.
// Create the actual HAP-NodeJS "Accessory" instance
return AccessoryLoader.parseAccessoryJSON({
displayName: displayName,
services: services
});
}
else {
// The returned "services" for this accessory are simply an array of new-API-style
// Service instances which we can add to a created HAP-NodeJS Accessory directly.
var accessoryUUID = uuid.generate(accessoryType + ":" + (uuid_base || displayName));
var accessory = new Accessory(displayName, accessoryUUID);
// listen for the identify event if the accessory instance has defined an identify() method
if (accessoryInstance.identify)
accessory.on('identify', function(paired, callback) { accessoryInstance.identify(callback); });
services.forEach(function(service) {
// if you returned an AccessoryInformation service, merge its values with ours
if (service instanceof Service.AccessoryInformation) {
var existingService = accessory.getService(Service.AccessoryInformation);
// pull out any values you may have defined
var manufacturer = service.getCharacteristic(Characteristic.Manufacturer).value;
var model = service.getCharacteristic(Characteristic.Model).value;
var serialNumber = service.getCharacteristic(Characteristic.SerialNumber).value;
if (manufacturer) existingService.setCharacteristic(Characteristic.Manufacturer, manufacturer);
if (model) existingService.setCharacteristic(Characteristic.Model, model);
if (serialNumber) existingService.setCharacteristic(Characteristic.SerialNumber, serialNumber);
}
else {
accessory.addService(service);
}
});
return accessory;
}
}
// Returns the setup code in a scannable format.
Server.prototype._printPin = function(pin) {
console.log("Scan this code with your HomeKit App on your iOS device to pair with Homebridge:");
console.log("\x1b[30;47m%s\x1b[0m", " ");
console.log("\x1b[30;47m%s\x1b[0m", " ┌────────────┐ ");
console.log("\x1b[30;47m%s\x1b[0m", " │ " + pin + " │ ");
console.log("\x1b[30;47m%s\x1b[0m", " └────────────┘ ");
console.log("\x1b[30;47m%s\x1b[0m", " ");
}

43
lib/user.js Normal file
View File

@@ -0,0 +1,43 @@
var path = require('path');
var fs = require('fs');
'use strict';
module.exports = {
User: User
}
/**
* Manages user settings and storage locations.
*/
// global cached config
var config;
// optional custom storage path
var customStoragePath;
function User() {
}
User.config = function() {
return config || (config = Config.load(User.configPath()));
}
User.storagePath = function() {
if (customStoragePath) return customStoragePath;
var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
return path.join(home, ".homebridge");
}
User.configPath = function() {
return path.join(User.storagePath(), "config.json");
}
User.persistPath = function() {
return path.join(User.storagePath(), "persist");
}
User.setStoragePath = function(path) {
customStoragePath = path;
}

12
lib/version.js Normal file
View File

@@ -0,0 +1,12 @@
var fs = require('fs');
var path = require('path');
'use strict';
module.exports = getVersion();
function getVersion() {
var packageJSONPath = path.join(__dirname, '../package.json');
var packageJSON = JSON.parse(fs.readFileSync(packageJSONPath));
return packageJSON.version;
}

View File

@@ -1,27 +1,37 @@
{ {
"name": "homebridge", "name": "homebridge",
"description": "HomeKit support for the impatient", "description": "HomeKit support for the impatient",
"version": "0.0.0", "version": "0.2.19",
"scripts": { "scripts": {
"start": "node app.js" "dev": "DEBUG=* ./bin/homebridge -D -P example-plugins/ || true"
},
"author": {
"name": "Nick Farina"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/nfarina/homebridge.git" "url": "git://github.com/nfarina/homebridge.git"
}, },
"license": "ISC", "bugs": {
"url": "http://github.com/nfarina/homebridge/issues"
},
"licenses": [
{
"type": "ISC",
"url": "http://github.com/nfarina/homebridge/blob/master/LICENSE"
}
],
"bin": {
"homebridge": "bin/homebridge"
},
"engines": {
"node": ">=0.12.0"
},
"preferGlobal": true,
"dependencies": { "dependencies": {
"hap-nodejs": "git+https://github.com/khaost/HAP-NodeJS#2a1bc8d99a2009317ab5da93faebea34c89f197c", "chalk": "^1.1.1",
"ad2usb": "git+https://github.com/alistairg/node-ad2usb.git#local", "commander": "2.8.1",
"request": "2.49.x", "hap-nodejs": "0.2.5",
"node-persist": "0.0.x", "semver": "5.0.3"
"xmldoc": "0.1.x",
"node-hue-api": "^1.0.5",
"xml2js": "0.4.x",
"carwingsjs": "0.0.x",
"sonos": "0.8.x",
"wemo": "0.2.x",
"wink-js": "0.0.5",
"elkington": "kevinohara80/elkington"
} }
} }

View File

@@ -1,320 +0,0 @@
// Domoticz Platform Shim for HomeBridge
// Written by Joep Verhaeg (http://www.joepverhaeg.nl)
//
// Revisions:
//
// 12 June 2015 [GizMoCuz]
// - Added support for RGB lights
// - Added support for Scenes
// - Sorting device names
//
// Domoticz JSON API required
// https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's#Lights_and_switches
//
// Remember to add platform to config.json. Example:
// "platforms": [
// {
// "platform": "Domoticz",
// "name": "Domoticz",
// "server": "127.0.0.1",
// "port": "8080",
// "roomid": 123 (0=no roomplan)
// }
// ],
//
// When you attempt to add a device, it will ask for a "PIN code".
// The default code for all HomeBridge accessories is 031-45-154.
//
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
function DomoticzPlatform(log, config){
this.log = log;
this.server = config["server"];
this.port = config["port"];
this.roomid = 0;
if (typeof config["roomid"] != 'undefined') {
this.roomid = config["roomid"];
}
}
function sortByKey(array, key) {
return array.sort(function(a, b) {
var x = a[key]; var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
DomoticzPlatform.prototype = {
accessories: function(callback) {
this.log("Fetching Domoticz lights and switches...");
var that = this;
var foundAccessories = [];
if (this.roomid == 0) {
//Get Lights
request.get({
url: "http://" + this.server + ":" + this.port + "/json.htm?type=devices&filter=light&used=true&order=Name",
json: true
}, function(err, response, json) {
if (!err && response.statusCode == 200) {
if (json['result'] != undefined) {
var sArray=sortByKey(json['result'],"Name");
sArray.map(function(s) {
accessory = new DomoticzAccessory(that.log, that.server, that.port, false, s.idx, s.Name, s.HaveDimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW"));
foundAccessories.push(accessory);
})
}
callback(foundAccessories);
} else {
that.log("There was a problem connecting to Domoticz.");
}
});
}
else {
//Get all devices specified in the room
request.get({
url: "http://" + this.server + ":" + this.port + "/json.htm?type=devices&plan=" + this.roomid,
json: true
}, function(err, response, json) {
if (!err && response.statusCode == 200) {
if (json['result'] != undefined) {
var sArray=sortByKey(json['result'],"Name");
sArray.map(function(s) {
//only accept switches for now
if (typeof s.SwitchType != 'undefined') {
accessory = new DomoticzAccessory(that.log, that.server, that.port, false, s.idx, s.Name, s.HaveDimmer, s.MaxDimLevel, (s.SubType=="RGB")||(s.SubType=="RGBW"));
foundAccessories.push(accessory);
}
})
}
callback(foundAccessories);
} else {
that.log("There was a problem connecting to Domoticz.");
}
});
}
//Get Scenes
foundAccessories = [];
request.get({
url: "http://" + this.server + ":" + this.port + "/json.htm?type=scenes",
json: true
}, function(err, response, json) {
if (!err && response.statusCode == 200) {
if (json['result'] != undefined) {
var sArray=sortByKey(json['result'],"Name");
sArray.map(function(s) {
accessory = new DomoticzAccessory(that.log, that.server, that.port, true, s.idx, s.Name, false, 0, false);
foundAccessories.push(accessory);
})
}
callback(foundAccessories);
} else {
that.log("There was a problem connecting to Domoticz.");
}
});
}
}
function DomoticzAccessory(log, server, port, IsScene, idx, name, HaveDimmer, MaxDimLevel, HaveRGB) {
// device info
this.IsScene = IsScene;
this.idx = idx;
this.name = name;
this.HaveDimmer = HaveDimmer;
this.MaxDimLevel = MaxDimLevel;
this.HaveRGB = HaveRGB;
this.log = log;
this.server = server;
this.port = port;
}
DomoticzAccessory.prototype = {
command: function(c,value) {
this.log(this.name + " sending command " + c + " with value " + value);
if (this.IsScene == false) {
//Lights
if (c == "On" || c == "Off") {
url = "http://" + this.server + ":" + this.port + "/json.htm?type=command&param=switchlight&idx=" + this.idx + "&switchcmd=" + c + "&level=0";
}
else if (c == "setHue") {
url = "http://" + this.server + ":" + this.port + "/json.htm?type=command&param=setcolbrightnessvalue&idx=" + this.idx + "&hue=" + value + "&brightness=100" + "&iswhite=false";
}
else if (c == "setLevel") {
url = "http://" + this.server + ":" + this.port + "/json.htm?type=command&param=switchlight&idx=" + this.idx + "&switchcmd=Set%20Level&level=" + value;
}
else if (value != undefined) {
this.log(this.name + " Unhandled Light command! cmd=" + c + ", value=" + value);
}
}
else {
//Scenes
if (c == "On" || c == "Off") {
url = "http://" + this.server + ":" + this.port + "/json.htm?type=command&param=switchscene&idx=" + this.idx + "&switchcmd=" + c;
}
else if (value != undefined) {
this.log(this.name + " Unhandled Scene command! cmd=" + c + ", value=" + value);
}
}
var that = this;
request.put({ url: url }, function(err, response) {
if (err) {
that.log("There was a problem sending command " + c + " to" + that.name);
that.log(url);
} else {
that.log(that.name + " sent command " + c);
}
})
},
informationCharacteristics: function() {
return [
{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Domoticz",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}
]
},
controlCharacteristics: function(that) {
cTypes = [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: true,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
}]
if (this.idx != undefined) {
cTypes.push({
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) {
if (value == 0) {
that.command("Off")
} else {
that.command("On")
}
},
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1
})
}
if (this.HaveDimmer == true) {
cTypes.push({
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) { that.command("setLevel", value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: this.MaxDimLevel,
designedMinStep: 1,
unit: "%"
})
}
if (this.HaveRGB == true) {
cTypes.push({
cType: types.HUE_CTYPE,
onUpdate: function(value) { that.command("setHue", value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Hue of Light",
designedMinValue: 0,
designedMaxValue: 360,
designedMinStep: 1,
unit: "arcdegrees"
})
}
return cTypes
},
sType: function() {
if (this.HaveDimmer == true) {
return types.LIGHTBULB_STYPE
} else {
return types.SWITCH_STYPE
}
},
getServices: function() {
var that = this;
var services = [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: this.informationCharacteristics(),
},
{
sType: this.sType(),
characteristics: this.controlCharacteristics(that)
}];
this.log("Loaded services for " + this.name)
return services;
}
};
module.exports.accessory = DomoticzAccessory;
module.exports.platform = DomoticzPlatform;

View File

@@ -1,385 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var xml2js = require('xml2js');
var request = require('request');
var util = require('util');
var parser = new xml2js.Parser();
var power_state_ctype = {
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { return; },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1
};
function ISYURL(user, pass, host, port, path) {
return util.format("http://%s:%s@%s:%d%s", user, pass, host, port, encodeURI(path));
}
function ISYPlatform(log, config) {
this.host = config["host"];
this.port = config["port"];
this.user = config["username"];
this.pass = config["password"];
this.log = log;
}
ISYPlatform.prototype = {
accessories: function(callback) {
this.log("Fetching ISY Devices.");
var that = this;
var url = ISYURL(this.user, this.pass, this.host, this.port, "/rest/nodes");
var options = {
url: url,
method: 'GET'
};
var foundAccessories = [];
request(options, function(error, response, body) {
if (error)
{
console.trace("Requesting ISY devices.");
that.log(error);
return error;
}
parser.parseString(body, function(err, result) {
result.nodes.node.forEach(function(obj) {
var enabled = obj.enabled[0] == 'true';
if (enabled)
{
var device = new ISYAccessory(
that.log,
that.host,
that.port,
that.user,
that.pass,
obj.name[0],
obj.address[0],
obj.property[0].$.uom
);
foundAccessories.push(device);
}
});
});
callback(foundAccessories.sort(function (a,b) {
return (a.name > b.name) - (a.name < b.name);
}));
});
}
}
function ISYAccessory(log, host, port, user, pass, name, address, uom) {
this.log = log;
this.host = host;
this.port = port;
this.user = user;
this.pass = pass;
this.name = name;
this.address = address;
this.uom = uom;
}
ISYAccessory.prototype = {
query: function() {
var path = util.format("/rest/status/%s", encodeURI(this.address));
var url = ISYURL(this.user, this.pass, this.host, this.port, path);
var options = { url: url, method: 'GET' };
request(options, function(error, response, body) {
if (error)
{
console.trace("Requesting Device Status.");
that.log(error);
return error;
}
parser.parseString(body, function(err, result) {
var value = result.properties.property[0].$.value;
return value;
});
});
},
command: function(c, value) {
this.log(this.name + " sending command " + c + " with value " + value);
switch (c)
{
case 'On':
path = "/rest/nodes/" + this.address + "/cmd/DFON";
break;
case 'Off':
path = "/rest/nodes/" + this.address + "/cmd/DFOF";
break;
case 'Low':
path = "/rest/nodes/" + this.address + "/cmd/DON/85";
break;
case 'Medium':
path = "/rest/nodes/" + this.address + "/cmd/DON/128";
break;
case 'High':
path = "/rest/nodes/" + this.address + "/cmd/DON/255";
break;
case 'setLevel':
if (value > 0)
{
path = "/rest/nodes/" + this.address + "/cmd/DON/" + Math.floor(255 * (value / 100));
}
break;
default:
this.log("Unimplemented command sent to " + this.name + " Command " + c);
break;
}
if (path)
{
var url = ISYURL(this.user, this.pass, this.host, this.port, path);
var options = {
url: url,
method: 'GET'
};
var that = this;
request(options, function(error, response, body) {
if (error)
{
console.trace("Sending Command.");
that.log(error);
return error;
}
that.log("Sent command " + path + " to " + that.name);
});
}
},
informationCharacteristics: function() {
return [
{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "SmartHome",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.address,
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}
]
},
controlCharacteristics: function(that) {
cTypes = [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: true,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
}]
if (this.uom == "%/on/off") {
cTypes.push({
cType: types.POWER_STATE_CTYPE,
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1,
onUpdate: function(value) {
if (value == 0) {
that.command("Off")
} else {
that.command("On")
}
},
onRead: function() {
return this.query();
}
});
cTypes.push({
cType: types.BRIGHTNESS_CTYPE,
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%",
onUpdate: function(value) {
that.command("setLevel", value);
},
onRead: function() {
var val = this.query();
that.log("Query: " + val);
return val;
}
});
}
else if (this.uom == "off/low/med/high")
{
cTypes.push({
cType: types.POWER_STATE_CTYPE,
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1,
onUpdate: function(value) {
if (value == 0) {
that.command("Off")
} else {
that.command("On")
}
},
onRead: function() {
return this.query();
}
});
cTypes.push({
cType: types.ROTATION_SPEED_CTYPE,
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the speed of the fan",
designedMaxLength: 1,
onUpdate: function(value) {
if (value == 0) {
that.command("Off");
} else if (value > 0 && value < 40) {
that.command("Low");
} else if (value > 40 && value < 75) {
that.command("Medium");
} else {
that.command("High");
}
},
onRead: function() {
return this.query();
}
});
}
else if (this.uom == "on/off")
{
cTypes.push({
cType: types.POWER_STATE_CTYPE,
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1,
onUpdate: function(value) {
if (value == 0) {
that.command("Off")
} else {
that.command("On")
}
},
onRead: function() {
return this.query();
}
});
}
return cTypes;
},
sType: function() {
if (this.uom == "%/on/off") {
return types.LIGHTBULB_STYPE;
} else if (this.uom == "on/off") {
return types.SWITCH_STYPE;
} else if (this.uom == "off/low/med/high") {
return types.FAN_STYPE;
}
return types.SWITCH_STYPE;
},
getServices: function() {
var that = this;
var services = [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: this.informationCharacteristics(),
},
{
sType: this.sType(),
characteristics: this.controlCharacteristics(that)
}];
//that.log("Loaded services for " + that.name);
return services;
}
};
module.exports.accessory = ISYAccessory;
module.exports.platform = ISYPlatform;

View File

@@ -1,369 +0,0 @@
// Philips Hue Platform Shim for HomeBridge
//
// Remember to add platform to config.json. Example:
// "platforms": [
// {
// "platform": "PhilipsHue",
// "name": "Philips Hue",
// "ip_address": "127.0.0.1",
// "username": "252deadbeef0bf3f34c7ecb810e832f"
// }
// ],
//
// If you do not know the IP address of your Hue Bridge, simply leave it blank and your Bridge
// will be discovered automatically.
//
// If you do not have a "username" for your Hue API already, simply leave the field blank and
// you will be prompted to press the link button on your Hue Bridge before running HomeBridge.
// A username will be created for you and printed out, then the server will exit so you may
// enter it in your config.json.
//
//
// When you attempt to add a device, it will ask for a "PIN code".
// The default code for all HomeBridge accessories is 031-45-154.
//
/* jslint node: true */
/* globals require: false */
/* globals config: false */
"use strict";
var hue = require("node-hue-api"),
HueApi = hue.HueApi,
lightState = hue.lightState;
var types = require("HAP-NodeJS/accessories/types.js");
function PhilipsHuePlatform(log, config) {
this.log = log;
this.ip_address = config["ip_address"];
this.username = config["username"];
}
function PhilipsHueAccessory(log, device, api) {
this.id = device.id;
this.name = device.name;
this.model = device.modelid;
this.device = device;
this.api = api;
this.log = log;
}
// Get the ip address of the first available bridge with meethue.com or a network scan.
var locateBridge = function (callback) {
var that = this;
// Report the results of the scan to the user
var getIp = function (err, bridges) {
if (!bridges || bridges.length === 0) {
that.log("No Philips Hue bridges found.");
callback(err || new Error("No bridges found"));
return;
}
if (bridges.length > 1) {
that.log("Warning: Multiple Philips Hue bridges detected. The first bridge will be used automatically. To use a different bridge, set the `ip_address` manually in the configuration.");
}
that.log(
"Philips Hue bridges found:\n" +
(bridges.map(function (bridge) {
// Bridge name is only returned from meethue.com so use id instead if it isn't there
return "\t" + bridge.ipaddress + ' - ' + (bridge.name || bridge.id);
})).join("\n")
);
callback(null, bridges[0].ipaddress);
};
// Try to discover the bridge ip using meethue.com
that.log("Attempting to discover Philips Hue bridge with meethue.com...");
hue.nupnpSearch(function (locateError, bridges) {
if (locateError) {
that.log("Philips Hue bridge discovery with meethue.com failed. Register your bridge with the meethue.com for more reliable discovery.");
that.log("Attempting to discover Philips Hue bridge with network scan...");
// Timeout after one minute
hue.upnpSearch(60000)
.then(function (bridges) {
that.log("Scan complete");
getIp(null, bridges);
})
.fail(function (scanError) {
that.log("Philips Hue bridge discovery with network scan failed. Check your network connection or set ip_address manually in configuration.");
getIp(new Error("Scan failed: " + scanError.message));
}).done();
} else {
getIp(null, bridges);
}
});
};
PhilipsHuePlatform.prototype = {
accessories: function(callback) {
this.log("Fetching Philips Hue lights...");
var that = this;
var getLights = function () {
var api = new HueApi(that.ip_address, that.username);
// Connect to the API
// Get a dump of all lights, so as not to hit rate limiting for installations with larger amounts of bulbs
api.fullState(function(err, response) {
if (err) throw err;
var foundAccessories = [];
for (var deviceId in response.lights) {
var device = response.lights[deviceId];
device.id = deviceId;
var accessory = new PhilipsHueAccessory(that.log, device, api);
foundAccessories.push(accessory);
}
callback(foundAccessories);
});
};
// Create a new user if needed
function checkUsername() {
if (!that.username) {
var api = new HueApi(that.ip_address);
api.createUser(that.ip_address, null, null, function(err, user) {
// try and help explain this particular error
if (err && err.message == "link button not pressed")
throw "Please press the link button on your Philips Hue bridge, then start the HomeBridge server within 30 seconds.";
if (err) throw err;
throw "Created a new username " + JSON.stringify(user) + " for your Philips Hue. Please add it to your config.json then start the HomeBridge server again: ";
});
}
else {
getLights();
}
}
// Discover the bridge if needed
if (!this.ip_address) {
locateBridge.call(this, function (err, ip_address) {
if (err) throw err;
// TODO: Find a way to persist this
that.ip_address = ip_address;
that.log("Save the Philips Hue bridge ip address "+ ip_address +" to your config to skip discovery.");
checkUsername();
});
} else {
checkUsername();
}
}
};
PhilipsHueAccessory.prototype = {
// Convert 0-65535 to 0-360
hueToArcDegrees: function(value) {
value = value/65535;
value = value*100;
value = Math.round(value);
return value;
},
// Convert 0-360 to 0-65535
arcDegreesToHue: function(value) {
value = value/360;
value = value*65535;
value = Math.round(value);
return value;
},
// Convert 0-255 to 0-100
bitsToPercentage: function(value) {
value = value/255;
value = value*100;
value = Math.round(value);
return value;
},
// Create and set a light state
executeChange: function(api, device, characteristic, value) {
var that = this;
var state = lightState.create();
switch(characteristic.toLowerCase()) {
case 'identify':
state.alert('select');
break;
case 'power':
if (value) {
state.on();
}
else {
state.off();
}
break;
case 'hue':
state.hue(this.arcDegreesToHue(value));
break;
case 'brightness':
state.brightness(value);
break;
case 'saturation':
state.saturation(value);
break;
}
api.setLightState(device.id, state, function(err, lights) {
if (!err) {
that.log(device.name + ", characteristic: " + characteristic + ", value: " + value + ".");
}
else {
that.log(err);
}
});
},
// Get Services
getServices: function() {
var that = this;
var bulb_characteristics = [
{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) {
that.executeChange(that.api, that.device, "power", value);
},
perms: ["pw","pr","ev"],
format: "bool",
initialValue: that.device.state.on,
supportEvents: false,
supportBonjour: false,
manfDescription: "Turn On the Light",
designedMaxLength: 1
},{
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) {
that.executeChange(that.api, that.device, "brightness", value);
},
perms: ["pw","pr","ev"],
format: "int",
initialValue: that.bitsToPercentage(that.device.state.bri),
supportEvents: false,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
}
];
// Handle the Hue/Hue Lux divergence
if (that.device.state.hasOwnProperty('hue') && that.device.state.hasOwnProperty('sat')) {
bulb_characteristics.push({
cType: types.HUE_CTYPE,
onUpdate: function(value) {
that.executeChange(that.api, that.device, "hue", value);
},
perms: ["pw","pr","ev"],
format: "int",
initialValue: that.hueToArcDegrees(that.device.state.hue),
supportEvents: false,
supportBonjour: false,
manfDescription: "Adjust Hue of Light",
designedMinValue: 0,
designedMaxValue: 360,
designedMinStep: 1,
unit: "arcdegrees"
});
bulb_characteristics.push({
cType: types.SATURATION_CTYPE,
onUpdate: function(value) {
that.executeChange(that.api, that.device, "saturation", value);
},
perms: ["pw","pr","ev"],
format: "int",
initialValue: that.bitsToPercentage(that.device.state.sat),
supportEvents: false,
supportBonjour: false,
manfDescription: "Adjust Saturation of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
});
}
var accessory_data = [
{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [
{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Philips",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: that.model,
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: that.device.uniqueid,
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: function(value) {
that.executeChange(that.api, that.device, "identify", value);
},
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}
]
},{
sType: types.LIGHTBULB_STYPE,
// `bulb_characteristics` defined based on bulb type
characteristics: bulb_characteristics
}
];
return accessory_data;
}
};
module.exports.platform = PhilipsHuePlatform;

View File

@@ -1,242 +0,0 @@
// SmartThings JSON API SmartApp required
// https://github.com/jnewland/SmartThings/blob/master/JSON.groovy
//
var types = require("HAP-NodeJS/accessories/types.js");
var request = require("request");
function SmartThingsPlatform(log, config){
this.log = log;
this.app_id = config["app_id"];
this.access_token = config["access_token"];
}
SmartThingsPlatform.prototype = {
accessories: function(callback) {
this.log("Fetching SmartThings devices...");
var that = this;
var foundAccessories = [];
request.get({
url: "https://graph.api.smartthings.com/api/smartapps/installations/"+this.app_id+"/devices?access_token="+this.access_token,
json: true
}, function(err, response, json) {
if (!err && response.statusCode == 200) {
if (json['switches'] != undefined) {
json['switches'].map(function(s) {
accessory = new SmartThingsAccessory(that.log, s.name, s.commands);
foundAccessories.push(accessory);
})
}
if (json['hues'] != undefined) {
json['hues'].map(function(s) {
accessory = new SmartThingsAccessory(that.log, s.name, s.commands);
foundAccessories.push(accessory);
})
}
callback(foundAccessories);
} else {
that.log("There was a problem authenticating with SmartThings.");
}
});
}
}
function SmartThingsAccessory(log, name, commands) {
// device info
this.name = name;
this.commands = commands;
this.log = log;
}
SmartThingsAccessory.prototype = {
command: function(c,value) {
this.log(this.name + " sending command " + c);
var url = this.commands[c];
if (value != undefined) {
url = this.commands[c] + "&value="+value
}
var that = this;
request.put({
url: url
}, function(err, response) {
if (err) {
that.log("There was a problem sending command " + c + " to" + that.name);
that.log(url);
} else {
that.log(that.name + " sent command " + c);
}
})
},
informationCharacteristics: function() {
return [
{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "SmartThings",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}
]
},
controlCharacteristics: function(that) {
cTypes = [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: true,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
}]
if (this.commands['on'] != undefined) {
cTypes.push({
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) {
if (value == 0) {
that.command("off")
} else {
that.command("on")
}
},
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1
})
}
if (this.commands['on'] != undefined) {
cTypes.push({
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) { that.command("setLevel", value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
})
}
if (this.commands['setHue'] != undefined) {
cTypes.push({
cType: types.HUE_CTYPE,
onUpdate: function(value) { that.command("setHue", value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Hue of Light",
designedMinValue: 0,
designedMaxValue: 360,
designedMinStep: 1,
unit: "arcdegrees"
})
}
if (this.commands['setSaturation'] != undefined) {
cTypes.push({
cType: types.SATURATION_CTYPE,
onUpdate: function(value) { that.command("setSaturation", value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
})
}
return cTypes
},
sType: function() {
if (this.commands['setLevel'] != undefined) {
return types.LIGHTBULB_STYPE
} else {
return types.SWITCH_STYPE
}
},
getServices: function() {
var that = this;
var services = [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: this.informationCharacteristics(),
},
{
sType: this.sType(),
characteristics: this.controlCharacteristics(that)
}];
this.log("Loaded services for " + this.name)
return services;
}
};
module.exports.accessory = SmartThingsAccessory;
module.exports.platform = SmartThingsPlatform;

View File

@@ -1,252 +0,0 @@
var types = require("HAP-NodeJS/accessories/types.js");
var wink = require('wink-js');
var model = {
light_bulbs: require('wink-js/lib/model/light')
};
function WinkPlatform(log, config){
// auth info
this.client_id = config["client_id"];
this.client_secret = config["client_secret"];
this.username = config["username"];
this.password = config["password"];
this.log = log;
}
WinkPlatform.prototype = {
accessories: function(callback) {
this.log("Fetching Wink devices.");
var that = this;
var foundAccessories = [];
wink.init({
"client_id": this.client_id,
"client_secret": this.client_secret,
"username": this.username,
"password": this.password
}, function(auth_return) {
if ( auth_return === undefined ) {
that.log("There was a problem authenticating with Wink.");
} else {
// success
wink.user().devices('light_bulbs', function(devices) {
for (var i=0; i<devices.data.length; i++){
device = model.light_bulbs(devices.data[i], wink)
accessory = new WinkAccessory(that.log, device);
foundAccessories.push(accessory);
}
callback(foundAccessories);
});
}
});
}
}
function WinkAccessory(log, device) {
// device info
this.name = device.name;
this.device = device;
this.log = log;
}
WinkAccessory.prototype = {
getPowerState: function(callback){
if (!this.device) {
this.log("No '"+this.name+"' device found (yet?)");
return;
}
var that = this;
this.log("checking power state for: " + this.name);
wink.user().device(this.name, function(light_obj){
powerState = light_obj.desired_state.powered
that.log("power state for " + that.name + " is: " + powerState)
callback(powerState);
});
},
getBrightness: function(callback){
if (!this.device) {
this.log("No '"+this.name+"' device found (yet?)");
return;
}
var that = this;
this.log("checking brightness level for: " + this.name);
wink.user().device(this.name, function(light_obj){
level = light_obj.desired_state.brightness * 100
that.log("brightness level for " + that.name + " is: " + level)
callback(level);
});
},
setPowerState: function(powerOn) {
if (!this.device) {
this.log("No '"+this.name+"' device found (yet?)");
return;
}
var that = this;
if (powerOn) {
this.log("Setting power state on the '"+this.name+"' to on");
this.device.power.on(function(response) {
if (response === undefined) {
that.log("Error setting power state on the '"+that.name+"'")
} else {
that.log("Successfully set power state on the '"+that.name+"' to on");
}
});
}else{
this.log("Setting power state on the '"+this.name+"' to off");
this.device.power.off(function(response) {
if (response === undefined) {
that.log("Error setting power state on the '"+that.name+"'")
} else {
that.log("Successfully set power state on the '"+that.name+"' to off");
}
});
}
},
setBrightness: function(level) {
if (!this.device) {
this.log("No '"+this.name+"' device found (yet?)");
return;
}
var that = this;
this.log("Setting brightness on the '"+this.name+"' to " + level);
this.device.brightness(level, function(response) {
if (response === undefined) {
that.log("Error setting brightness on the '"+that.name+"'")
} else {
that.log("Successfully set brightness on the '"+that.name+"' to " + level);
}
});
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Wink",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.LIGHTBULB_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: true,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) {
that.setPowerState(value);
},
onRead: function(callback) {
that.getPowerState(function(powerState){
callback(powerState);
});
},
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state of the Bulb",
designedMaxLength: 1
},{
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) {
that.setBrightness(value);
},
onRead: function(callback) {
that.getBrightness(function(level){
callback(level);
});
},
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness of Light",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
}]
}];
}
};
module.exports.accessory = WinkAccessory;
module.exports.platform = WinkPlatform;