node-red-contrib-smartthings
Version:
Querying/Controlling Smartthings Devices
376 lines (331 loc) • 14.5 kB
JavaScript
var Promise = require('promise');
module.exports = function(RED) {
function SmartthingsThermostatNode(config) {
RED.nodes.createNode(this, config);
let node = this;
console.debug("SmartthingsThermostatNode")
console.debug(config);
this.conf = RED.nodes.getNode(config.conf);
this.name = config.name;
this.function = config.function;
this.device = config.device;
this.state = {
temperature: null,
coolingSetpoint: null,
heatingSetpoint: null,
thermostatSetpoint: null,
thermostatFanMode: null,
thermostatMode: null,
thermostatOperatingState: null
};
this.reportState = function(send, done, original) {
send = send || function() { node.send.apply(node,arguments) };
done = done || function() { };
let msg = [null,null,null,null,null,null,null];
if(this.state.temperature != null){
msg[0] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "temperature",
name: this.name,
value: this.state.temperature.value,
unit: this.state.temperature.unit,
timestamp: new Date().toISOString()
}
};
}
if(this.state.coolingSetpoint != null){
msg[1] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "coolingSetpoint",
name: this.name,
value: this.state.coolingSetpoint.value,
unit: this.state.coolingSetpoint.unit,
timestamp: new Date().toISOString()
}
};
}
if(this.state.heatingSetpoint != null){
msg[2] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "heatingSetpoint",
name: this.name,
value: this.state.heatingSetpoint.value,
unit: this.state.heatingSetpoint.unit,
timestamp: new Date().toISOString()
}
};
}
if(this.state.thermostatSetpoint != null){
msg[3] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "thermostatSetpoint",
name: this.name,
value: this.state.thermostatSetpoint.value,
timestamp: new Date().toISOString()
}
};
}
if(this.state.thermostatFanMode != null){
msg[4] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "thermostatFanMode",
name: this.name,
value: this.state.thermostatFanMode,
timestamp: new Date().toISOString()
}
};
}
if(this.state.thermostatMode != null){
msg[5] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "thermostatMode",
name: this.name,
value: this.state.thermostatMode,
timestamp: new Date().toISOString()
}
};
}
if(this.state.thermostatOperatingState != null){
msg[6] = {
topic: "device",
payload: {
deviceId: this.device,
deviceType: "thermostatOperatingState",
name: this.name,
value: this.state.thermostatOperatingState,
timestamp: new Date().toISOString()
}
};
}
if(original !== undefined){
msg.forEach( (m) => {
if(m){
original.payload = m.payload;
Object.assign(m,original);
}
});
}
send(msg);
done();
};
this.setState = function(value, send, done){
Object.assign(this.state, value);
this.reportState(send, done);
};
this.pullState = function(){
this.conf.getDeviceStatus(this.device,"main").then( (status) => {
console.debug("ThermostatDevice("+this.name+") Status Refreshed");
let state = {};
if(status["thermostat"] !== undefined && status["thermostat"]["temperature"] !== undefined){
state.temperature = {
value: parseFloat(status["thermostat"]["temperature"]["value"]),
unit: status["thermostat"]["temperature"]["unit"]
};
}
if(status["thermostat"] !== undefined && status["thermostat"]["thermostatFanMode"] !== undefined){
state.thermostatFanMode = status["thermostat"]["thermostatFanMode"]["value"];
}
if(status["thermostat"] !== undefined && status["thermostat"]["thermostatMode"] !== undefined){
state.thermostatMode = status["thermostat"]["thermostatMode"]["value"];
}
if(status["thermostat"] !== undefined && status["thermostat"]["coolingSetpoint"] !== undefined){
state.coolingSetpoint = {
value: status["thermostat"]["coolingSetpoint"]["value"],
unit: status["thermostat"]["coolingSetpoint"]["unit"]
};
}
if(status["thermostat"] !== undefined && status["thermostat"]["heatingSetpoint"] !== undefined){
state.heatingSetpoint = {
value: status["thermostat"]["heatingSetpoint"]["value"],
unit: status["thermostat"]["heatingSetpoint"]["unit"]
}
}
if(status["thermostat"] !== undefined && status["thermostat"]["thermostatSetpoint"] !== undefined){
state.thermostatSetpoint = {
value: status["thermostat"]["thermostatSetpoint"]["value"]
}
}
if(status["thermostat"] !== undefined && status["thermostat"]["thermostatOperatingState"] !== undefined){
state.thermostatOperatingState = status["thermostat"]["thermostatOperatingState"]["value"];
}
this.setState(state);
}).catch( err => {
this.error("Ops... error getting device state (ThermostatDevice)");
this.error(err);
});
};
if(this.conf && this.device){
const callback = (evt) => {
console.debug("ThermostatDevice("+this.name+") Callback called");
console.debug(evt);
let state = {};
switch(evt["attribute"].toLowerCase()){
case "temperature":
this.setState({
temperature: {
value: evt["value"]
}
});
break;
case "coolingsetpoint":
this.setState({
coolingSetpoint: {
value: evt["value"]
}
});
break;
case "thermostatfanmode":
this.setState({
thermostatFanMode: evt["value"]
});
break;
case "heatingsetpoint":
this.setState({
heatingSetpoint: {
value: evt["value"]
}
});
break;
case "thermostatsetpoint":
this.setState({
thermostatSetpoint: {
value: evt["value"]
}
});
break;
case "thermostatmode":
this.setState({
thermostatMode: evt["value"]
});
break;
case "thermostatoperatingstate":
this.setState({
thermostatOperatingState: evt["value"]
});
break;
}
this.setState(state);
}
this.conf.registerCallback(this, this.device, callback);
this.pullState();
this.on('input', (msg, send, done) => {
send = send || function() { node.send.apply(node,arguments) };
done = done || function() { };
console.debug("Input Message Received");
console.log(msg);
if(msg && msg.topic !== undefined){
switch(msg.topic.toLowerCase()){
case "pull":
this.pullState();
break;
case "update":
this.reportState(send, done, msg);
break;
case "coolingsetpoint":
this.conf.executeDeviceCommand(this.device,[{
component: "main",
capability: "thermostat",
command: "setCoolingSetpoint",
arguments: [
msg.payload.value
]
}]).then( (ret) => {
const state = {
coolingSetpoint: {
value: msg.payload.value
}
};
this.setState(state, send, done);
}).catch( (ret) => {
this.error("Error updating device");
this.error(ret);
done("Erro updating device");
});
break;
case "thermostatfanmode":
this.conf.executeDeviceCommand(this.device,[{
component: "main",
capability: "thermostat",
command: "setThermostatFanMode",
arguments: [
msg.payload.value
]
}]).then( (ret) => {
const state = {
thermostatFanMode: msg.payload.value
};
this.setState(state, send, done);
}).catch( (ret) => {
this.error("Error updating device");
this.error(ret);
done("Erro updating device");
});
break;
case "heatingsetpoint":
this.conf.executeDeviceCommand(this.device,[{
component: "main",
capability: "thermostat",
command: "setHeatingSetpoint",
arguments: [
msg.payload.value
]
}]).then( (ret) => {
const state = {
heatingSetpoint: {
value: msg.payload.value
}
};
this.setState(state, send, done);
}).catch( (ret) => {
this.error("Error updating device");
this.error(ret);
done("Erro updating device");
});
break;
case "thermostatmode":
this.conf.executeDeviceCommand(this.device,[{
component: "main",
capability: "thermostat",
command: "setThermostatMode",
arguments: [
msg.payload.value
]
}]).then( (ret) => {
const state = {
thermostatMode: msg.payload.value
};
this.setState(state, send, done);
}).catch( (ret) => {
this.error("Error updating device");
this.error(ret);
done("Erro updating device");
});
break;
default:
done("Invalid topic");
break;
}
} else {
done("Invalid message");
}
});
this.on('close', () => {
console.debug("Closed");
this.conf.unregisterCallback(this, this.device, callback);
});
}
}
RED.nodes.registerType("smartthings-node-thermostat", SmartthingsThermostatNode);
};