Skip to content

Instantly share code, notes, and snippets.

@enishoca
Last active August 26, 2017 01:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enishoca/4a1df399e62147b148105989c937a118 to your computer and use it in GitHub Desktop.
Save enishoca/4a1df399e62147b148105989c937a118 to your computer and use it in GitHub Desktop.
Mochad listener for sending X-10 commands to SmartThings
#!/usr/bin/env node
/* X10toSTsignals.js --
*
* Copyright (C) 2017 Enis Hoca
*
* This software may be modified and distributed under the terms
* of the MIT license.
*
* 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.
*/
var inject = require('reconnect-core');
var net = require('net');
var events = require("events");
const request = require('request');
var hostname = 'pi'; // host name or IP address of mochad server
var port = 1099; // mochad port - default 1099
// Modify the section below and map x10 commands to REST calls for Smartthing devices
var event = new events.EventEmitter();
event.on("data", function (eventData) {
var command = eventData.housecode + eventData.unitcode + "-" + eventData.state;
console.log("data: " + command);
var stUrl="";
switch (command) {
case "c1-on": //X-10 switch
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
break;
case "c1-off":
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/off?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
break;
case "0xfe*-arm_home_min_sh624": //X-10 Security Device
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
break;
case "0xfe*-disarm_sh624":
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/off?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
break;
case "c8-on": //multiple X-10 devices controlling multiple SmartThings device
case "b8-on":
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl); //Kitchen
break;
case "c8-off": //House
case "b8-off":
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/off?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/off?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999on?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl);
stUrl = "https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/88888888-9999-8888-9999-999999999999/switches/88888888-9999-8888-9999-999999999999/off?access_token=88888888-9999-8888-9999-999999999999";
callurl(stUrl); //Kitchen
break;
case "ss-on": //One X-10 device controlling another
stUrl = "RF A3 ON";
mochadConnection.write(stUrl + "\r\n");
break;
case "s-off":
stUrl = "RF A3 OFF";
mochadConnection.write(stUrl + "\r\n");
break;
}
});
// No need to modify anything below this
var mochadConnection = null;
var lastCodeSeen = {
housecode: "x",
unitcode: "0"
};
var reconnect = inject(function () {
return net.connect.apply(null, arguments);
});
var re = reconnect({
initialDelay: 1e3,
setKeepAlive: true,
maxDelay: 10e3,
strategy: 'fibonacci', // available: fibonacci, exponential
failAfter: Infinity,
randomisationFactor: 0,
immediate: false,
}, function (stream) {
stream.on('data', function (data) {
var lines;
lines = data.toString();
console.log(lines);
return parseReceivedCmd(lines);
});
})
.on('connect', function (con) {
console.log("(Re)Opened connection");
mochadConnection = con;
})
.on('disconnect', function (err) {
console.log(("Disconnected from " + hostname + ":" + port + ": ") + err);
mochadConnection = null;
})
.on('error', function (err) {
// never forget
console.log("Connection Error " + err);
})
.connect(port, hostname);
function callurl(url) {
console.log('stUrl:', url);
if (!url) {
return;
}
request( url, function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});
}
function parseReceivedCmd(lines) {
var eventData, m;
// Parsing all-units-on/off
// example: 05/22 00:34:04 Rx PL House: P Func: All units on
// example: 05/22 00:34:04 Rx PL House: P Func: All units off
if (m = /^\d{2}\/\d{2}\s+(?:\d{2}:){2}\d{2}\s(Rx|Tx)\s+(RF|PL)\s+House:\s+([a-pA-P])\s+Func:\s+All\s+(units|lights)\s+(on|off)$/m.exec(lines)) {
eventData = {
protocol: m[2].toLowerCase(),
direction: m[1].toLowerCase(),
housecode: m[3].toLowerCase(),
unitcode: "*" + m[4],
state: m[5].toLowerCase()
};
event.emit("data", eventData);
}
// Parsing security remotes / sensors
// example: 07/04 02:07:52 Rx RFSEC Addr: 0xFE Func: Lights_On_SH624
// example: 07/04 02:46:46 07/04 02:46:47 Rx RFSEC Addr: 0xFE Func: Arm_Home_min_SH624
// example: 07/04 02:46:46 07/04 02:46:48 Rx RFSEC Addr: 0xFE Func: Disarm_SH624
// example: 07/04 02:46:46 07/04 02:46:49 Rx RFSEC Addr: 0xFE Func: Arm_Away_min_SH624
// example: 07/04 02:46:46 07/04 02:46:50 Rx RFSEC Addr: 0xFE Func: Disarm_SH624
// example: 07/04 02:46:46 07/04 02:46:51 Rx RFSEC Addr: 0xFE Func: Lights_On_SH624
// example: 07/04 02:46:46 07/04 02:46:52 Rx RFSEC Addr: 0xFE Func: Lights_Off_SH624
if (m = /^\d{2}\/\d{2}\s+(?:\d{2}:){2}\d{2}\s(Rx|Tx)\s+(RFSEC)\s+Addr:\s+(0x[0-9a-fA-F]+)(\s+)Func:\s+(.+)$/m.exec(lines)) {
eventData = {
protocol: m[2].toLowerCase(),
direction: m[1].toLowerCase(),
housecode: m[3].toLowerCase(),
unitcode: "*" + m[4].trim(),
state: m[5].toLowerCase()
};
event.emit("data", eventData);
}
// Parsing simple on/off (RF-style)
// example: 11/30 17:57:12 Tx RF HouseUnit: A10 Func: On
// example: 11/30 17:57:24 Tx RF HouseUnit: A10 Func: Off
if (m = /^\d{2}\/\d{2}\s+(?:\d{2}:){2}\d{2}\s(Rx|Tx)\s+(RF|PL)\s+HouseUnit:\s+([a-pA-P])(\d{1,2})\s+Func:\s+(On|Off)/m.exec(lines)) {
eventData = {
protocol: m[2].toLowerCase(),
direction: m[1].toLowerCase(),
housecode: m[3].toLowerCase(),
unitcode: parseInt(m[4], 10),
state: m[5].toLowerCase()
};
event.emit("data", eventData);
}
//Parsing simple on/off (PL-style)
// example: 05/30 20:59:20 Tx PL HouseUnit: P1
//example: 05/30 20:59:20 Tx PL House: P Func: On
//example2: 09/01 23:42:03 Tx PL HouseUnit: P1
//example2: 09/01 23:42:03 Tx PL House: P Func: On
else if (m = /^\d{2}\/\d{2}\s+(?:\d{2}:){2}\d{2}\s(?:Rx|Tx)\s+(?:RF|PL)\s+HouseUnit:\s+([a-pA-P])(\d{1,2})/m.exec(lines)) {
lastCodeSeen.housecode = m[1].toLowerCase();
lastCodeSeen.unitcode = parseInt(m[2], 10);
}
if (lastCodeSeen.housecode && lastCodeSeen.unitcode && (m = /\d{2}\/\d{2}\s+(?:\d{2}:){2}\d{2}\s(Rx|Tx)\s+(RF|PL)\s+House:\s+([a-pA-P])\s+Func:\s+(On|Off)$/m.exec(lines))) {
Data = {
protocol: m[2].toLowerCase(),
direction: m[1].toLowerCase(),
housecode: m[3].toLowerCase(),
unitcode: null, // filled later
state: m[4].toLowerCase()
};
if (eventData.housecode === lastCodeSeen.housecode) {
eventData.unitcode = lastCodeSeen.unitcode;
event.emit("data", eventData);
}
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment