From 08e814a8bba8ffbd3da5a760a5bf27461cfef9bd Mon Sep 17 00:00:00 2001 From: valkjsaaa Date: Wed, 4 Oct 2017 17:40:39 -0700 Subject: [PATCH 1/6] Make to work with node-switchmate3 --- .gitignore | 3 +- LICENSE | 4 +- LICENSE.original | 21 ++++++ README.md | 6 +- Switchmate3Manager.js | 137 +++++++++++++++++++++++++++++++++++++ SwitchmateManager.js | 141 --------------------------------------- config-sample-linux.json | 3 +- config-sample-macos.json | 5 +- index.js | 78 +++++++++++----------- package.json | 23 +++++-- 10 files changed, 222 insertions(+), 199 deletions(-) create mode 100644 LICENSE.original create mode 100644 Switchmate3Manager.js delete mode 100644 SwitchmateManager.js diff --git a/.gitignore b/.gitignore index 0974b57..902e473 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/ nbproject* ### Node ### # Logs @@ -49,4 +50,4 @@ jspm_packages .yarn-integrity -# End of https://www.gitignore.io/api/node \ No newline at end of file +# End of https://www.gitignore.io/api/node diff --git a/LICENSE b/LICENSE index de54fac..4dac2d3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Evan (EvMac) McClintock +Copyright (c) 2017 Jackie (Junrui) Yang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/LICENSE.original b/LICENSE.original new file mode 100644 index 0000000..de54fac --- /dev/null +++ b/LICENSE.original @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Evan (EvMac) McClintock + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8111ff4..eb25c7b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1 @@ -#homebridge-switchmate A [Homebridge](https://github.com/nfarina/homebridge) platform plugin for [Switchmate](http://www.myswitchmate.com). Switchmate is a Bluetooth LE lightswitch cover which allows for the automation of lightswitches without needing to rewire your home. This plugin was developed using the "Rocker" models of the Switchmate (RSM0001 & RSM0002), though the Toggle models (TSM0001 & TSM0002) will ***hopefully*** work too. This is still somewhat "experimental" and definitely will need further refinement. ##Prerequisites & Installation Be sure to install the following pre-requisites before installing homebridge-switchmate. The prerequisites and their detailed installation instructions are provided on their GitHub pages and websites: * **[Node.js](https://nodejs.org)** * **[Noble](https://github.com/sandeepmistry/noble#prerequisites)** ```npm install -g noble``` * **[Noble-Device](https://github.com/sandeepmistry/noble-device#prerequisites)** * **[Node-Switchmate](https://github.com/emmcc/node-switchmate#readme)** ```npm install -g node-switchmate``` ###Note for Raspberry Pi 3 Users: If you are using a Raspberry Pi 3, you may wish to use a third-party USB Bluetooth adapter. There have been issues with the Pi disconnecting, especially if its internal Wi-Fi is in use. The [IOGEAR GBU521](https://www.amazon.com/dp/B007GFX0PY/) is a decent and compact Bluetooth 4.0 adapter which works really well with the Pi 3. ###Installing homebridge-switchmate 1. After installing the pre-requisites, install homebridge-switchmate with the Node Package Manager: ```npm install -g homebridge-switchmate``` ##Adding Switchmate to Homebridge: 1. Follow the Switchmate pairing instructions at the [node-switchmate](https://github.com/emmcc/node-switchmate#readme) project page to receive and test your auth code. ##Sample Configuration file: ``` "platforms": [ { "platform": "Switchmate", "switchmates": [ { "displayName": "Porch Light", "id": "fe5c32ba4c95", "authCode": "VCcHQA==", "model": "RSM001W" } ] } ] - ``` - - -##Important Considerations: ###Reversed Light Switches: If your light switches are reversed, you can open the Switchmate App on your smartphone and set it to 'reversed mode'. There is nothing you need to do with this API, as the orientation will be correct in this API after the setting is applied in your Switchmate app. ###Avoiding an accidental Switchmate Reset: If you provide the **wrong** authCode in your config.json, there is a good chance your Switchmate will reset itself. This means you will need to relink it to your Smartphone and re-create any timers or schedules you have setup within the Switchmate app. It is imperative to run the toggle command using your authCode before adding a Switchmate to your config.json file. \ No newline at end of file +# A Homebridge plugin for Switchmate3 # Switchmate is a BLE controlled light switch add-on that provide smart home capability to conventional light switches. This plugin is developed for a specific model of Switchmate switch TSM003W. This is the newer toggle style Switchmate which have a smaller form factor for fitting on a multi-gang switch. This plugin is a fork of [homebridge-switchmate](https://github.com/emmcc/homebridge-switchmate) a Homebridge Plugin for the original Switchmate (RSM0001 & RSM0002). As the communication protocol of these two product is quite different. I have to use [node-switchmate3](https://github.com/valkjsaaa/node-switchmate3) as the communication library. Any attempt to merge them would be welcome :) This plugin is still very experimental. Please create an issue or a pull request for any problem you encountered. \ No newline at end of file diff --git a/Switchmate3Manager.js b/Switchmate3Manager.js new file mode 100644 index 0000000..9d08445 --- /dev/null +++ b/Switchmate3Manager.js @@ -0,0 +1,137 @@ +var Switchmate3Device = require('node-switchmate3').Switchmate3Device; +var util = require('util'); +var EventEmitter = require('events').EventEmitter; + +var Switchmate3Manager = function () +{ + var manager = this; + manager.initialized = false; + manager.SmIdList = []; + manager.event = new EventEmitter(); + manager.Switchmate3s = []; + if (!(this instanceof Switchmate3Manager)) + return new Switchmate3Manager(); + + + manager.FindAllSwitchmate3s = function () + { + Switchmate3Device.SCAN_DUPLICATES = true; + Switchmate3Device.discoverAll(onFound); + }; + + manager.StopFind = function () + { + Switchmate3Device.stopDiscoverAll(onFound); + }; + + manager.ResetSwitchmate3Scan = function() + { + manager.StopFind(); + manager.FindAllSwitchmate3s(); + }; + + function onFound(foundSm) { + var smid = foundSm.id; + + if (smid in manager.SmIdList) //if Switchmate3 is in the list of configured Switchmate3 ids. + { + if (!manager.SmIdList[smid] === true) //if Switchmate3 hasn't been found until now... + { + var foundSmToggleMode = foundSm.ToggleMode(); + foundSm.on('unreachable', function (smid) { + manager.event.emit('smUnreachable', smid); + }); + foundSm.on('reachable', function (smid) { + manager.event.emit('smReachable', smid); + }); + foundSmToggleMode.event.on('toggleDone',manager.ResetSwitchmate3Scan); + foundSm.foundMe(); + manager.Switchmate3s[smid] = foundSm; + manager.SmIdList[smid] = true; + manager.event.emit('smSetup', smid); + } else + { + if (manager.Switchmate3s[smid].ToggleState !== foundSm.ToggleState && foundSm.ToggleState !== null) + { + manager.Switchmate3s[smid].ToggleState = foundSm.ToggleState; + manager.event.emit('smToggleStateChange', smid, foundSm.ToggleState); + } + manager.Switchmate3s[smid].foundMe(); + } + } + } +}; + +Switchmate3Manager.prototype.Initialize = function (sm_config) +{ + var manager = this; + if (!manager.initialized) + { + manager.SmRaw = sm_config; + for (var i = 0, len = manager.SmRaw.length; i < len; i++) { + sminfo = manager.SmRaw[i]; + manager.SmIdList[sminfo.id] = false; + } + manager.FindAllSwitchmate3s(); + manager.initialized = true; + } +}; + +Switchmate3Manager.prototype.GetSwitchmate3State = function (smid) +{ + manager = this; + if (typeof manager.Switchmate3s[smid] !== 'undefined') + { + return manager.Switchmate3s[smid].ToggleState; //if Switchmate3 exists, return its last known Toggle State. + } else + { + return false; //otherwise, just assume it is turned off. + } +}; + +Switchmate3Manager.prototype.GetReachableState = function (smid) +{ + if (this.SmIdList[smid] === false || typeof this.SmIdList[smid] === 'undefined') + { + return false; + } else + { + return this.Switchmate3s[smid].Reachable; + } +}; + +Switchmate3Manager.prototype.On = function (smid) +{ + var manager = this; + if (manager.SmIdList[smid] === true && + (manager.Switchmate3s[smid].ToggleState === false || manager.Switchmate3s[smid].ToggleState === null)) + { + var ToggleMode = manager.Switchmate3s[smid].ToggleMode(); + //ToggleMode.event.on('toggleDone', restartScan); + ToggleMode.TurnOn(); + } +}; + +Switchmate3Manager.prototype.Off = function (smid) +{ + var manager = this; + if (manager.SmIdList[smid] === true && ( + manager.Switchmate3s[smid].ToggleState === true || manager.Switchmate3s[smid].ToggleState === null)) + { + var ToggleMode = manager.Switchmate3s[smid].ToggleMode(); + ToggleMode.TurnOff(); + } +}; + +Switchmate3Manager.prototype.Identify = function (smid) +{ + var manager = this; + if (manager.SmIdList[smid] === true) + { + var ToggleMode = manager.Switchmate3s[smid].ToggleMode(); + ToggleMode.IdentifySwitchmate3(); + } +}; + +util.inherits(Switchmate3Manager, EventEmitter); +module.exports = Switchmate3Manager; diff --git a/SwitchmateManager.js b/SwitchmateManager.js deleted file mode 100644 index 5b933d2..0000000 --- a/SwitchmateManager.js +++ /dev/null @@ -1,141 +0,0 @@ -var SwitchmateDevice = require('node-switchmate').SwitchmateDevice; -var util = require('util'); -var EventEmitter = require('events').EventEmitter; - -var SwitchmateManager = function () -{ - var manager = this; - manager.initialized = false; - manager.SmIdList = []; - manager.SmAuthList = []; - manager.event = new EventEmitter(); - manager.Switchmates = []; - if (!(this instanceof SwitchmateManager)) - return new SwitchmateManager(); - - - manager.FindAllSwitchmates = function () - { - SwitchmateDevice.SCAN_DUPLICATES = true; - SwitchmateDevice.discoverAll(onFound); - }; - - manager.StopFind = function () - { - SwitchmateDevice.stopDiscoverAll(onFound); - }; - - manager.ResetSwitchmateScan = function() - { - manager.StopFind(); - manager.FindAllSwitchmates(); - }; - - function onFound(foundSm) { - var smid = foundSm.id; - - if (smid in manager.SmIdList) //if switchmate is in the list of configured switchmate ids. - { - if (!manager.SmIdList[smid] === true) //if switchmate hasn't been found until now... - { - if (typeof manager.SmAuthList[smid] !== 'undefined') { //if an auth code exists, set it up! - foundSm.setAuthCode(manager.SmAuthList[smid]); - } - var foundSmToggleMode = foundSm.ToggleMode(); - foundSm.on('unreachable', function (smid) { - manager.event.emit('smUnreachable', smid); - }); - foundSm.on('reachable', function (smid) { - manager.event.emit('smReachable', smid); - }); - foundSmToggleMode.event.on('toggleDone',manager.ResetSwitchmateScan); - foundSm.foundMe(); - manager.Switchmates[smid] = foundSm; - manager.SmIdList[smid] = true; - manager.event.emit('smSetup', smid); - } else - { - if (manager.Switchmates[smid].ToggleState !== foundSm.ToggleState && foundSm.ToggleState !== null) - { - manager.Switchmates[smid].ToggleState = foundSm.ToggleState; - manager.event.emit('smToggleStateChange', smid, foundSm.ToggleState); - } - manager.Switchmates[smid].foundMe(); - } - } - } -}; - -SwitchmateManager.prototype.Initialize = function (sm_config) -{ - var manager = this; - if (!manager.initialized) - { - manager.SmRaw = sm_config; - for (var i = 0, len = manager.SmRaw.length; i < len; i++) { - sminfo = manager.SmRaw[i]; - manager.SmIdList[sminfo.id] = false; - manager.SmAuthList[sminfo.id] = sminfo.authCode; - } - manager.FindAllSwitchmates(); - manager.initialized = true; - } -}; - -SwitchmateManager.prototype.GetSwitchmateState = function (smid) -{ - manager = this; - if (typeof manager.Switchmates[smid] !== 'undefined') - { - return manager.Switchmates[smid].ToggleState; //if switchmate exists, return its last known Toggle State. - } else - { - return false; //otherwise, just assume it is turned off. - } -}; - -SwitchmateManager.prototype.GetReachableState = function (smid) -{ - if (this.SmIdList[smid] === false || typeof this.SmIdList[smid] === 'undefined') - { - return false; - } else - { - return this.Switchmates[smid].Reachable; - } -}; - -SwitchmateManager.prototype.On = function (smid) -{ - manager = this; - if (manager.SmIdList[smid] === true && manager.Switchmates[smid].ToggleState === false) - { - var ToggleMode = manager.Switchmates[smid].ToggleMode(); - //ToggleMode.event.on('toggleDone', restartScan); - - ToggleMode.TurnOn(); - } -}; - -SwitchmateManager.prototype.Off = function (smid) -{ - manager = this; - if (manager.SmIdList[smid] === true && manager.Switchmates[smid].ToggleState === true) - { - ToggleMode = manager.Switchmates[smid].ToggleMode(); - ToggleMode.TurnOff(); - } -}; - -SwitchmateManager.prototype.Identify = function (smid) -{ - manager = this; - if (manager.SmIdList[smid] === true) - { - ToggleMode = manager.Switchmates[smid].ToggleMode(); - ToggleMode.IdentifySwitchmate(); - } -}; - -util.inherits(SwitchmateManager, EventEmitter); -module.exports = SwitchmateManager; \ No newline at end of file diff --git a/config-sample-linux.json b/config-sample-linux.json index 5010222..5cb960e 100644 --- a/config-sample-linux.json +++ b/config-sample-linux.json @@ -6,8 +6,7 @@ { "displayName": "Porch Light", "id": "fe5c32ba4c95", - "authCode": "VCcHQA==", - "model": "RSM001W" + "model": "TSM003W" } ] } diff --git a/config-sample-macos.json b/config-sample-macos.json index ffdfbe8..30a9f7f 100644 --- a/config-sample-macos.json +++ b/config-sample-macos.json @@ -6,10 +6,9 @@ { "displayName": "Porch Light", "id": "754ffdf6021c4285b7240c0a778ebd96", - "authCode": "VCcHQA==", - "model": "RSM001W" + "model": "TSM003W" } ] } ] -} \ No newline at end of file +} diff --git a/index.js b/index.js index 6d57a77..11ae8f1 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ "use strict"; -var SwitchmateManager = require('./SwitchmateManager'); +var Switchmate3Manager = require('./Switchmate3Manager'); var Accessory, Service, Characteristic, UUIDGen; module.exports = function (homebridge) { @@ -9,72 +9,71 @@ module.exports = function (homebridge) { Characteristic = homebridge.hap.Characteristic; UUIDGen = homebridge.hap.uuid; - homebridge.registerPlatform("homebridge-switchmate", "Switchmate", SwitchmatePlatform); + homebridge.registerPlatform("homebridge-switchmate3", "Switchmate3", Switchmate3Platform); }; -function SwitchmatePlatform(log, config, api) { +function Switchmate3Platform(log, config, api) { this.log = log; this.config = config; - this.switchmates = this.config.switchmates || []; + this.switchmate3s = this.config.switchmate3s || []; this.accessories = []; - this.SmManager = new SwitchmateManager(); + this.SmManager = new Switchmate3Manager(); if (api) { this.api = api; this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); } } -; -SwitchmatePlatform.prototype.configureAccessory = function (accessory) { +Switchmate3Platform.prototype.configureAccessory = function (accessory) { var accessoryId = accessory.context.id; this.setService(accessory); this.accessories[accessoryId] = accessory; }; -SwitchmatePlatform.prototype.didFinishLaunching = function () { +Switchmate3Platform.prototype.didFinishLaunching = function () { var platform = this; - if (!this.switchmates.length) { - platform.log.error("No Switchmates configured. Please check your 'config.json' file!"); + if (!this.switchmate3s.length) { + platform.log.error("No Switchmate3s configured. Please check your 'config.json' file!"); } else { - for (var i in platform.switchmates) { - var data = platform.switchmates[i]; - data.name = "Switchmate " + data.id.substring(data.id.length - 6); + for (var i in platform.switchmate3s) { + var data = platform.switchmate3s[i]; + data.name = "Switchmate3 " + data.id.substring(data.id.length - 6); platform.log("Adding " + data.name + ": (" + data.displayName + ")"); platform.addAccessory(data); } for (var id in platform.accessories) { - var switchmate = platform.accessories[id]; - if (!switchmate.reachable) { - platform.removeAccessory(switchmate); + var switchmate3 = platform.accessories[id]; + if (!switchmate3.reachable) { + platform.removeAccessory(switchmate3); } } platform.SmManager.event.on('smSetup', function (smid) { - var mySwitchmate = platform.accessories[smid]; - mySwitchmate.getService(Service.Switch) + var mySwitchmate3 = platform.accessories[smid]; + mySwitchmate3.getService(Service.Switch) .getCharacteristic(Characteristic.On).getValue(); }); platform.SmManager.event.on('smToggleStateChange', function (smid, state) { - var mySwitchmate = platform.accessories[smid]; - mySwitchmate.getService(Service.Switch) + var mySwitchmate3 = platform.accessories[smid]; + mySwitchmate3.getService(Service.Switch) .getCharacteristic(Characteristic.On).getValue(); }); - platform.SmManager.Initialize(platform.switchmates); + platform.SmManager.Initialize(platform.switchmate3s); } }; -SwitchmatePlatform.prototype.addAccessory = function (data) { +Switchmate3Platform.prototype.addAccessory = function (data) { if (!this.accessories[data.id]) { var uuid = UUIDGen.generate(data.id); var newAccessory = new Accessory(data.id, uuid, 8); newAccessory.reachable = true; - newAccessory.context.name = "Switchmate " + data.id.substring(data.id.length - 6); + newAccessory.context.name = "Switchmate3 " + data.id.substring(data.id.length - 6); newAccessory.context.displayName = data.displayName; newAccessory.context.id = data.id; @@ -82,7 +81,7 @@ SwitchmatePlatform.prototype.addAccessory = function (data) { this.setService(newAccessory); - this.api.registerPlatformAccessories("homebridge-switchmate", "Switchmate", [newAccessory]); + this.api.registerPlatformAccessories("homebridge-switchmate3", "Switchmate3", [newAccessory]); } else { var newAccessory = this.accessories[data.id]; @@ -94,17 +93,17 @@ SwitchmatePlatform.prototype.addAccessory = function (data) { this.accessories[data.id] = newAccessory; }; -SwitchmatePlatform.prototype.removeAccessory = function (accessory) { +Switchmate3Platform.prototype.removeAccessory = function (accessory) { if (accessory) { var name = accessory.context.name; var id = accessory.context.id; - this.log.warn("Removing Switchmate: " + name + ". No longer configured."); - this.api.unregisterPlatformAccessories("homebridge-switchmate", "Switchmate", [accessory]); + this.log.warn("Removing Switchmate3: " + name + ". No longer configured."); + this.api.unregisterPlatformAccessories("homebridge-switchmate3", "Switchmate3", [accessory]); delete this.accessories[id]; } }; -SwitchmatePlatform.prototype.setService = function (accessory) { +Switchmate3Platform.prototype.setService = function (accessory) { accessory.getService(Service.Switch) .getCharacteristic(Characteristic.On) .on('set', this.setToggleState.bind(this, accessory.context)) @@ -113,10 +112,10 @@ SwitchmatePlatform.prototype.setService = function (accessory) { accessory.on('identify', this.identify.bind(this, accessory.context)); }; -SwitchmatePlatform.prototype.getInitState = function (accessory, data) { +Switchmate3Platform.prototype.getInitState = function (accessory, data) { var info = accessory.getService(Service.AccessoryInformation); - accessory.context.manufacturer = "Switchmate"; + accessory.context.manufacturer = "Switchmate3"; info.setCharacteristic(Characteristic.Manufacturer, accessory.context.manufacturer); accessory.context.model = data.model || ""; @@ -129,24 +128,23 @@ SwitchmatePlatform.prototype.getInitState = function (accessory, data) { .getValue(); }; -SwitchmatePlatform.prototype.setToggleState = function (mySwitchmate, powerState, callback) { - +Switchmate3Platform.prototype.setToggleState = function (mySwitchmate3, powerState, callback) { var platform = this; - platform.log("Setting %s (%s) to: %s", mySwitchmate.displayName, mySwitchmate.name, (powerState ? "ON" : "OFF")); - powerState ? platform.SmManager.On(mySwitchmate.id) : platform.SmManager.Off(mySwitchmate.id); + platform.log("Setting %s (%s) to: %s", mySwitchmate3.displayName, mySwitchmate3.name, (powerState ? "ON" : "OFF")); + powerState ? platform.SmManager.On(mySwitchmate3.id) : platform.SmManager.Off(mySwitchmate3.id); callback(); }; -SwitchmatePlatform.prototype.getToggleState = function (mySwitchmate, callback) { +Switchmate3Platform.prototype.getToggleState = function (mySwitchmate3, callback) { var platform = this; - var status = platform.SmManager.GetSwitchmateState(mySwitchmate.id); - platform.log("Status of %s (%s) is: %s", mySwitchmate.displayName, mySwitchmate.name, (status ? "ON" : "OFF")); + var status = platform.SmManager.GetSwitchmate3State(mySwitchmate3.id); + platform.log("Status of %s (%s) is: %s", mySwitchmate3.displayName, mySwitchmate3.name, (status ? "ON" : "OFF")); callback(null, status); }; -SwitchmatePlatform.prototype.identify = function (mySwitchmate, paired, callback) { +Switchmate3Platform.prototype.identify = function (mySwitchmate3, paired, callback) { var platform = this; - platform.log("Identify requested for " + mySwitchmate.name); + platform.log("Identify requested for " + mySwitchmate3.name); callback(); -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index a178f53..d7a6ca8 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,24 @@ { - "name": "homebridge-switchmate", "version": "0.0.1", "description": "Switchmate automation, via Homebridge for the impatient.", "license": "MIT", "keywords": [ + "name": "homebridge-switchmate3", + "version": "0.0.1", + "author": "Jackie Yang", + "description": "Switchmate3 automation, via Homebridge for the impatient.", + "license": "MIT", + "keywords": [ "homebridge-plugin", "switchmate" - ], "engines": { + ], + "repository": { + "type": "git", + "url": "git+https://github.com/valkjsaaa/homebridge-switchmate3.git" + }, + "bugs": { + "url": "https://github.com/valkjsaaa/homebridge-switchmate3/issues" + }, + "homepage": "https://github.com/valkjsaaa/homebridge-switchmate3", + "engines": { "homebridge": ">=0.4.0" }, "dependencies": { - "node-switchmate": ">=0.0.1" + "node-switchmate3": ">=0.0.2" } - -} \ No newline at end of file +} From 1c45fc2b1e5c2c6fedca9f32943d52a9a5683c23 Mon Sep 17 00:00:00 2001 From: valkjsaaa Date: Thu, 9 Nov 2017 00:10:21 -0800 Subject: [PATCH 2/6] Include node-switchmate3 0.0.3, which should improves stability --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d7a6ca8..6b98099 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-switchmate3", - "version": "0.0.1", + "version": "0.0.2", "author": "Jackie Yang", "description": "Switchmate3 automation, via Homebridge for the impatient.", "license": "MIT", @@ -19,6 +19,6 @@ "homebridge": ">=0.4.0" }, "dependencies": { - "node-switchmate3": ">=0.0.2" + "node-switchmate3": ">=0.0.3" } } From da9c66ea80978a08843cfaf07c9f3f1bafd76493 Mon Sep 17 00:00:00 2001 From: Ron Heft Date: Sat, 21 Apr 2018 12:40:43 -0400 Subject: [PATCH 3/6] Support for battery status --- Switchmate3Manager.js | 20 +++++++++++++++++++- index.js | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Switchmate3Manager.js b/Switchmate3Manager.js index 9d08445..bf3bff9 100644 --- a/Switchmate3Manager.js +++ b/Switchmate3Manager.js @@ -55,6 +55,7 @@ var Switchmate3Manager = function () { manager.Switchmate3s[smid].ToggleState = foundSm.ToggleState; manager.event.emit('smToggleStateChange', smid, foundSm.ToggleState); + manager.event.emit('smBatteryLevelChange', smid, foundSm.BatteryLevel); } manager.Switchmate3s[smid].foundMe(); } @@ -77,6 +78,18 @@ Switchmate3Manager.prototype.Initialize = function (sm_config) } }; +Switchmate3Manager.prototype.GetSwitchmate3BatteryLevel = function (smid) +{ + manager = this; + if (typeof manager.Switchmate3s[smid] !== 'undefined') + { + return manager.Switchmate3s[smid].BatteryLevel || 100; //if Switchmate3 exists, return its last known Battery Level. + } else + { + return 100; //otherwise, just assume it is 100%. + } +}; + Switchmate3Manager.prototype.GetSwitchmate3State = function (smid) { manager = this; @@ -107,7 +120,9 @@ Switchmate3Manager.prototype.On = function (smid) (manager.Switchmate3s[smid].ToggleState === false || manager.Switchmate3s[smid].ToggleState === null)) { var ToggleMode = manager.Switchmate3s[smid].ToggleMode(); - //ToggleMode.event.on('toggleDone', restartScan); + ToggleMode.event.once('toggleDone', function() { + manager.event.emit('smBatteryLevelChange', smid, manager.Switchmate3s[smid].BatteryLevel); + }); ToggleMode.TurnOn(); } }; @@ -119,6 +134,9 @@ Switchmate3Manager.prototype.Off = function (smid) manager.Switchmate3s[smid].ToggleState === true || manager.Switchmate3s[smid].ToggleState === null)) { var ToggleMode = manager.Switchmate3s[smid].ToggleMode(); + ToggleMode.event.once('toggleDone', function() { + manager.event.emit('smBatteryLevelChange', smid, manager.Switchmate3s[smid].BatteryLevel); + }); ToggleMode.TurnOff(); } }; diff --git a/index.js b/index.js index 11ae8f1..4671498 100644 --- a/index.js +++ b/index.js @@ -62,6 +62,14 @@ Switchmate3Platform.prototype.didFinishLaunching = function () { mySwitchmate3.getService(Service.Switch) .getCharacteristic(Characteristic.On).getValue(); }); + platform.SmManager.event.on('smBatteryLevelChange', function (smid, state) + { + var mySwitchmate3 = platform.accessories[smid]; + mySwitchmate3.getService(Service.BatteryService) + .getCharacteristic(Characteristic.BatteryLevel).getValue(); + mySwitchmate3.getService(Service.BatteryService) + .getCharacteristic(Characteristic.StatusLowBattery).getValue(); + }); platform.SmManager.Initialize(platform.switchmate3s); } }; @@ -110,6 +118,18 @@ Switchmate3Platform.prototype.setService = function (accessory) { .on('get', this.getToggleState.bind(this, accessory.context)); accessory.on('identify', this.identify.bind(this, accessory.context)); + + if ( !accessory.getService(Service.BatteryService) ) { + accessory.addService(Service.BatteryService); + } + + accessory.getService(Service.BatteryService) + .getCharacteristic(Characteristic.BatteryLevel) + .on('get', this.getBatteryLevel.bind(this, accessory.context)); + + accessory.getService(Service.BatteryService) + .getCharacteristic(Characteristic.StatusLowBattery) + .on('get', this.getBatteryIsLow.bind(this, accessory.context)); }; Switchmate3Platform.prototype.getInitState = function (accessory, data) { @@ -126,6 +146,14 @@ Switchmate3Platform.prototype.getInitState = function (accessory, data) { accessory.getService(Service.Switch) .getCharacteristic(Characteristic.On) .getValue(); + + accessory.getService(Service.BatteryService) + .getCharacteristic(Characteristic.BatteryLevel) + .getValue(); + + accessory.getService(Service.BatteryService) + .getCharacteristic(Characteristic.StatusLowBattery) + .getValue(); }; Switchmate3Platform.prototype.setToggleState = function (mySwitchmate3, powerState, callback) { @@ -148,3 +176,16 @@ Switchmate3Platform.prototype.identify = function (mySwitchmate3, paired, callba platform.log("Identify requested for " + mySwitchmate3.name); callback(); }; + +Switchmate3Platform.prototype.getBatteryLevel = function (mySwitchmate3, callback) { + var platform = this; + var battery = platform.SmManager.GetSwitchmate3BatteryLevel(mySwitchmate3.id); + platform.log("Battery Level of %s (%s) is: %s%", mySwitchmate3.displayName, mySwitchmate3.name, battery); + callback(null, battery); +}; + +Switchmate3Platform.prototype.getBatteryIsLow = function (mySwitchmate3, callback) { + var platform = this; + var isLowBattery = platform.SmManager.GetSwitchmate3BatteryLevel(mySwitchmate3.id) < 25; + callback(null, isLowBattery); +}; From 63ed3c4500016e9b0db702c09f0114bba465a6b1 Mon Sep 17 00:00:00 2001 From: Ron Heft Date: Sun, 1 Jul 2018 14:20:10 -0400 Subject: [PATCH 4/6] Configurable low battery warning --- config-sample-linux.json | 3 ++- config-sample-macos.json | 3 ++- index.js | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/config-sample-linux.json b/config-sample-linux.json index 5cb960e..f25a9f9 100644 --- a/config-sample-linux.json +++ b/config-sample-linux.json @@ -6,7 +6,8 @@ { "displayName": "Porch Light", "id": "fe5c32ba4c95", - "model": "TSM003W" + "model": "TSM003W", + "lowBatteryPercent": 25 } ] } diff --git a/config-sample-macos.json b/config-sample-macos.json index 30a9f7f..e5755f7 100644 --- a/config-sample-macos.json +++ b/config-sample-macos.json @@ -6,7 +6,8 @@ { "displayName": "Porch Light", "id": "754ffdf6021c4285b7240c0a778ebd96", - "model": "TSM003W" + "model": "TSM003W", + "lowBatteryPercent": 25 } ] } diff --git a/index.js b/index.js index 4671498..33424d8 100644 --- a/index.js +++ b/index.js @@ -141,6 +141,8 @@ Switchmate3Platform.prototype.getInitState = function (accessory, data) { accessory.context.model = data.model || ""; info.setCharacteristic(Characteristic.Model, accessory.context.model); + accessory.context.lowBatteryPercent = data.lowBatteryPercent || 25; + info.setCharacteristic(Characteristic.SerialNumber, accessory.context.id); accessory.getService(Service.Switch) @@ -186,6 +188,6 @@ Switchmate3Platform.prototype.getBatteryLevel = function (mySwitchmate3, callbac Switchmate3Platform.prototype.getBatteryIsLow = function (mySwitchmate3, callback) { var platform = this; - var isLowBattery = platform.SmManager.GetSwitchmate3BatteryLevel(mySwitchmate3.id) < 25; + var isLowBattery = platform.SmManager.GetSwitchmate3BatteryLevel(mySwitchmate3.id) <= parseInt(mySwitchmate3.lowBatteryPercent); callback(null, isLowBattery); }; From ca0f9361e239ae94f4ac23042b5470f2d114aeb5 Mon Sep 17 00:00:00 2001 From: Jackie Yang Date: Sun, 11 Nov 2018 00:14:01 -0800 Subject: [PATCH 5/6] Bump package and dependency version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6b98099..52cdb37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-switchmate3", - "version": "0.0.2", + "version": "0.0.3", "author": "Jackie Yang", "description": "Switchmate3 automation, via Homebridge for the impatient.", "license": "MIT", @@ -19,6 +19,6 @@ "homebridge": ">=0.4.0" }, "dependencies": { - "node-switchmate3": ">=0.0.3" + "node-switchmate3": ">=0.0.4" } } From e07e1451e8421027a7e532c78c5fb0309971b396 Mon Sep 17 00:00:00 2001 From: Jackie Yang Date: Tue, 8 Jun 2021 17:37:40 -0700 Subject: [PATCH 6/6] Update README.md --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb25c7b..0405fa0 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -# A Homebridge plugin for Switchmate3 # Switchmate is a BLE controlled light switch add-on that provide smart home capability to conventional light switches. This plugin is developed for a specific model of Switchmate switch TSM003W. This is the newer toggle style Switchmate which have a smaller form factor for fitting on a multi-gang switch. This plugin is a fork of [homebridge-switchmate](https://github.com/emmcc/homebridge-switchmate) a Homebridge Plugin for the original Switchmate (RSM0001 & RSM0002). As the communication protocol of these two product is quite different. I have to use [node-switchmate3](https://github.com/valkjsaaa/node-switchmate3) as the communication library. Any attempt to merge them would be welcome :) This plugin is still very experimental. Please create an issue or a pull request for any problem you encountered. \ No newline at end of file +# A Homebridge plugin for Switchmate3 # + +Notice: I haven't used this plugin for a long time. I now use Samsung Smartthings hub + Ecolink ZWAVE switch. +Similar to Swtichmate, it allows conventional light switches to be remotely controlled, but it's much more stable. +One tip: Ecolink ZWAVE switch can't operate with NiMH rechargeable battery due to their low operating voltage. +I recommend rechargeable lithium AA batteries, which can maintain a stable 5V supply. + +Switchmate is a BLE controlled light switch add-on that provide smart home capability to conventional light switches. + +This plugin is developed for a specific model of Switchmate switch TSM003W. This is the newer toggle style Switchmate +which have a smaller form factor for fitting on a multi-gang switch. + +This plugin is a fork of [homebridge-switchmate](https://github.com/emmcc/homebridge-switchmate) a Homebridge Plugin +for the original Switchmate (RSM0001 & RSM0002). As the communication protocol of these two product is quite different. +I have to use [node-switchmate3](https://github.com/valkjsaaa/node-switchmate3) as the communication library. Any +attempt to merge them would be welcome :) + +This plugin is still very experimental. Please create an issue or a pull request for any problem you encountered.