Hooked up heat mode/state

Hooked up most of the server-side support for heat setpoint

I decided to continue using the `controls` array and differentiate with a new `type` property. This allows much simpler sorting of controls than trying to figure out a good layout for a mix of circuits, heat modes, and heat points.

Heat point UI is going to require a bit more thinking than the simple toggles of circuit and heat modes, but I wanted to get the current progress committed. I'm also not terribly happy with the terminology I've gone with here, so I expect to iterate on that.
This commit is contained in:
2020-03-30 21:48:12 -05:00
parent 994fc68251
commit b07a1c67b6
3 changed files with 125 additions and 35 deletions

View File

@ -112,33 +112,59 @@ Module.register("MMM-ScreenLogic",{
for (var control in this.config.controls) {
var controlObj = this.config.controls[control];
var name = controlObj.name;
for (var circuit in poolData.controllerConfig.bodyArray) {
if (poolData.controllerConfig.bodyArray[circuit].circuitId == controlObj.id) {
if (!name) {
name = poolData.controllerConfig.bodyArray[circuit].name;
if (controlObj.type === 'circuit') {
var name = controlObj.name;
for (var circuit in poolData.controllerConfig.bodyArray) {
if (poolData.controllerConfig.bodyArray[circuit].circuitId == controlObj.id) {
if (!name) {
name = poolData.controllerConfig.bodyArray[circuit].name;
}
}
}
}
var on = false;
for (var circuit in poolData.status.circuitArray) {
if (poolData.status.circuitArray[circuit].id == controlObj.id) {
on = poolData.status.circuitArray[circuit].state !== 0;
var on = false;
for (var circuit in poolData.status.circuitArray) {
if (poolData.status.circuitArray[circuit].id == controlObj.id) {
on = poolData.status.circuitArray[circuit].state !== 0;
}
}
}
var cls = '';
if (this.config.colored) {
cls = on ? 'control-on' : 'control-off';
}
var cls = '';
if (this.config.colored) {
cls = on ? 'control-on' : 'control-off';
}
contents.push({
data: '<button id="sl-control-' + controlObj.id + '" class="control ' + cls + '" onclick="setCircuit(this)" data-circuit="' +
controlObj.id + '" data-state="' + (on ? '1' : '0') + '"><div class="content">' +
name + '</div></button>',
class: this.config.contentClass
});
contents.push({
data: '<button id="sl-control-' + controlObj.id + '" class="control ' + cls + '" onclick="setCircuit(this)" data-circuit="' +
controlObj.id + '" data-state="' + (on ? '1' : '0') + '"><div class="content">' +
name + '</div></button>',
class: this.config.contentClass
});
} else if (controlObj.type === 'heatpoint') {
} else if (controlObj.type === 'heatmode') {
if (controlObj.body < 0 || controlObj.body > poolData.status.heatStatus.length) {
Log.warn('Invalid body specified for heatmode');
continue;
}
var on = poolData.status.heatStatus[controlObj.body] !== 0;
var cls = '';
if (this.config.colored) {
cls = on ? 'control-on' : 'control-off';
}
contents.push({
data: '<button id="sl-heat-' + controlObj.body + '" class="control ' + cls + '" onclick="setHeatmode(this)" data-body="' +
controlObj.body + '" data-state="' + (on ? '1' : '0') + '"><div class="content">' +
controlObj.name + '</div></button>',
class: this.config.contentClass
});
} else {
Log.warn('circuit with unknown type, unable to display:');
Log.warn(controlObj);
}
}
}
@ -185,13 +211,9 @@ Module.register("MMM-ScreenLogic",{
if (notification === 'SCREENLOGIC_RESULT') {
poolData = payload;
this.updateDom();
} else if (notification === 'SCREENLOGIC_CIRCUIT_DONE') {
var obj = document.getElementById('sl-control-' + payload.id);
if (this.config.colored) {
var on = payload.state !== 0;
obj.classList.add(on ? 'control-on' : 'control-off');
}
obj.dataset.state = payload.state;
} else if (notification === 'SCREENLOGIC_CIRCUIT_DONE' || notification === 'SCREENLOGIC_HEATSTATE_DONE') {
poolData.status = payload.status;
this.updateDom();
}
},
});
@ -231,3 +253,10 @@ function setCircuit(e) {
moduleObj.sendSocketNotification('SCREENLOGIC_CIRCUIT', {id: circuitId, state: on ? 0 : 1});
e.classList.remove('control-on', 'control-off');
}
function setHeatmode(e) {
var bodyId = parseInt(e.dataset.body);
var on = e.dataset.state !== '0';
moduleObj.sendSocketNotification('SCREENLOGIC_HEATSTATE', {body: bodyId, state: on ? 0 : 1});
e.classList.remove('control-on', 'control-off');
}

View File

@ -22,7 +22,7 @@ A <a href="https://github.com/MichMich/MagicMirror">MagicMirror²</a> module use
|`showFreezeMode`|Boolean|Whether you'd like to show a banner when the pool is in freeze mode or not. [added in v1.0.1]|`true`|
|`colored`|Boolean|Whether you'd like colored output or not.|`true`|
|`coldTemp`|Integer|Show the temperature colored blue if it's at or below this level for pool/spa (requires option `colored`). This is in whatever scale your system is set to (Fahrenheit/Celsius).|`84`|
|`controls`|Array|List of controls to show buttons for. Must also set `showControls` to `true`.<br><br>Each entry in this list is an object with an `id` property and optionally a `name` override. If no `name` is specified, the name of the equipment in the ScreenLogic system will be used.|`[]`|
|`controls`|Array|List of controls to show buttons for. Must also set `showControls` to `true`.<br><br>Each entry in this list is an object with a `type` property and a `name` to display.<br><br>Valid `type`s:<br>`circuit` - toggle a circuit on or off. Must also have an `id` property defining the circuit ID to set (see [node-screenlogic](https://github.com/parnic/node-screenlogic) documentation for circuit IDs). `name` is optional; if not specified, the name of the equipment in the ScreenLogic system will be used.<br>`heatmode` - enable or disable the heater for the pool or spa. Must also have a `body` property that defines which body to toggle the heater for (`0` is the pool, `1` is the spa)|`[]`|
|`hotTemp`|Integer|Show the temperature colored red if it's at or above this level for pool/spa (requires option `colored`). This is in whatever scale your system is set to (Fahrenheit/Celsius).|`90`|
|`columns`|Integer|How many columns to use to display the data before starting a new row.|`3`|
|`contentClass`|String|The CSS class used to display content values (beneath the header).|`"light"`|
@ -40,8 +40,9 @@ Here is an example of an entry in config.js
contentClass: 'thin',
showControls: true,
controls: [
{id: 500},
{id: 505, name: 'Pool'}
{type: 'circuit', id: 500},
{type: 'circuit', id: 505, name: 'Pool'},
{type: 'heatmode', body: 0, name: 'Pool heater'}
]
}
},
@ -58,5 +59,7 @@ This module only works with ScreenLogic controllers on the local network via eit
The data is updated every 30 minutes by default (configurable with `updateInterval`).
When toggling a circuit or changing heat mode, sometimes other circuits are affected. For example, some pools share the same pump for the pool and spa, so when the pool is toggled on the spa must be toggled off. Unfortunately the ScreenLogic system doesn't update its internal status at any predictable rate, so the data on the screen can be wrong immediately after toggling a circuit until the next periodic update runs. If you know of a reliable way around this, please open a pull request!
## Libraries
This uses a Node.JS library I created for interfacing with ScreenLogic controllers over the network: <a href="https://github.com/parnic/node-screenlogic">node-screenlogic</a>, so feel free to check that out for more information.
This uses a Node.JS library I created for interfacing with ScreenLogic controllers over the network: [node-screenlogic](https://github.com/parnic/node-screenlogic), so feel free to check that out for more information.

View File

@ -14,11 +14,31 @@ module.exports = NodeHelper.create({
setCircuit: function(circuitState) {
var self = this;
setCircuitState(circuitState, function(done) {
self.sendSocketNotification('SCREENLOGIC_CIRCUIT_DONE', circuitState);
setCircuitState(circuitState, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_CIRCUIT_DONE', {circuitState: circuitState, status: poolStatus});
});
},
setHeatpoint: function(heatpoint) {
var self = this;
setHeatpointState(heatpoint, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_HEATPOINT_DONE', {heatpoint: heatpoint, status: poolStatus});
});
},
setHeatstate: function(heatstate) {
var self = this;
setHeatstateState(heatstate, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_HEATSTATE_DONE', {heatstate: heatstate, status: poolStatus});
});
},
restartTimer: function() {
var interval = this.updateInterval;
this.updateInterval = undefined;
this.setTimer(interval);
},
setTimer: function(updateInterval) {
var update = true;
update = typeof this.updateInterval === 'undefined' || this.updateInterval != updateInterval;
@ -119,8 +139,46 @@ function setCircuitState(circuitState, cb) {
foundUnit.once('loggedIn', function() {
foundUnit.setCircuitState(0, circuitState.id, circuitState.state);
}).once('circuitStateChanged', function() {
foundUnit.getPoolStatus();
}).once('poolStatus', function(status) {
foundUnit.close();
cb(true);
cb(status);
});
foundUnit.connect();
}
function setHeatpointState(heatpoint, cb) {
if (!foundUnit) {
cb();
return;
}
foundUnit.once('loggedIn', function() {
foundUnit.setSetPoint(0, heatpoint.body, heatpoint.temperature);
}).once('setPointChanged', function() {
foundUnit.getPoolStatus();
}).once('poolStatus', function(status) {
foundUnit.close();
cb(status);
});
foundUnit.connect();
}
function setHeatstateState(heatstate, cb) {
if (!foundUnit) {
cb();
return;
}
foundUnit.once('loggedIn', function() {
foundUnit.setHeatMode(0, heatstate.body, heatstate.state);
}).once('heatModeChanged', function() {
foundUnit.getPoolStatus();
}).once('poolStatus', function(status) {
foundUnit.close();
cb(status);
});
foundUnit.connect();