This shows green until the bar is at 50%, yellow until 33%, and red below that. This also fixes the bar to show white when "colored" is disabled in the config.
312 lines
13 KiB
JavaScript
312 lines
13 KiB
JavaScript
let poolData = {};
|
|
let moduleObj;
|
|
|
|
Module.register('MMM-ScreenLogic',{
|
|
defaults: {
|
|
showPoolTemp: true,
|
|
showSpaTemp: true,
|
|
showPH: true,
|
|
showOrp: true,
|
|
showSaltLevel: true,
|
|
showSaturation: true,
|
|
showFreezeMode: true,
|
|
showControls: false,
|
|
controls: [],
|
|
colored: true,
|
|
coldTemp: 84,
|
|
hotTemp: 90,
|
|
columns: 3,
|
|
contentClass: 'light',
|
|
updateInterval: 30 * 60 * 1000,
|
|
showPHTankLevel: true,
|
|
pHTankLevelMax: 6
|
|
},
|
|
|
|
start: function() {
|
|
// this isn't a great solution...is there a better one? needed to do stuff with buttons
|
|
moduleObj = this;
|
|
if (this.config.showControls && (!this.config.controls || this.config.controls.length === 0)) {
|
|
Log.warn('Controls are enabled, but no controls are configured. See README for info on setting up controls.');
|
|
this.config.showControls = false;
|
|
}
|
|
|
|
this.sendSocketNotification('SCREENLOGIC_CONFIG', this.config);
|
|
this.sendSocketNotification('SCREENLOGIC_UPDATE');
|
|
},
|
|
|
|
getStyles: function() {
|
|
return ['screenlogic.css'];
|
|
},
|
|
|
|
getDom: function() {
|
|
if (!poolData.status) {
|
|
let wrapper = document.createElement('div');
|
|
wrapper.innerHTML = 'Loading...';
|
|
wrapper.className += 'dimmed light small';
|
|
|
|
return wrapper;
|
|
} else {
|
|
let table = document.createElement('table');
|
|
table.className = 'small';
|
|
if (this.config.colored) {
|
|
table.className += ' colored';
|
|
}
|
|
|
|
let contents = [];
|
|
|
|
if (this.config.showPoolTemp) {
|
|
let className = '';
|
|
if (poolData.status.currentTemp[0] <= this.config.coldTemp) {
|
|
className += ' cold-temp';
|
|
} else if (poolData.status.currentTemp[0] >= this.config.hotTemp) {
|
|
className += ' hot-temp';
|
|
}
|
|
|
|
contents.push({
|
|
header: 'Pool temp',
|
|
data: poolData.status.currentTemp[0] + '°' + (!isPoolActive(poolData.status) ? ' (last)' : ''),
|
|
class: this.config.contentClass + className
|
|
});
|
|
}
|
|
if (this.config.showSpaTemp) {
|
|
let className = '';
|
|
if (poolData.status.currentTemp[1] <= this.config.coldTemp) {
|
|
className = ' cold-temp';
|
|
} else if (poolData.status.currentTemp[1] >= this.config.hotTemp) {
|
|
className = ' hot-temp';
|
|
}
|
|
|
|
contents.push({
|
|
header: 'Spa temp',
|
|
data: poolData.status.currentTemp[1] + '°' + (!isSpaActive(poolData.status) ? ' (last)' : ''),
|
|
class: this.config.contentClass + className
|
|
});
|
|
}
|
|
if (this.config.showPH) {
|
|
let dataStr = poolData.status.pH
|
|
if (this.config.showPHTankLevel) {
|
|
let percent = Math.round(((poolData.status.pHTank - 1) / this.config.pHTankLevelMax) * 100)
|
|
let cls = ""
|
|
if (this.config.colored) {
|
|
if (percent <= 50 && percent > 33) {
|
|
cls = "progress-bar-warning"
|
|
} else if (percent <= 33) {
|
|
cls = "progress-bar-danger"
|
|
} else {
|
|
cls = "progress-bar-success"
|
|
}
|
|
}
|
|
let progBarDiv = `<div class="progress vertical">
|
|
<div class="progress-bar ${cls}" role="progressbar" aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100" style="width: ${percent}%;">
|
|
</div>
|
|
</div>`
|
|
|
|
dataStr = `${dataStr} ${progBarDiv}`
|
|
}
|
|
|
|
contents.push({
|
|
header: 'pH',
|
|
data: dataStr,
|
|
class: this.config.contentClass
|
|
});
|
|
}
|
|
if (this.config.showOrp) {
|
|
contents.push({
|
|
header: 'ORP',
|
|
data: poolData.status.orp,
|
|
class: this.config.contentClass
|
|
});
|
|
}
|
|
if (this.config.showSaltLevel) {
|
|
contents.push({
|
|
header: 'Salt PPM',
|
|
data: poolData.status.saltPPM,
|
|
class: this.config.contentClass
|
|
});
|
|
}
|
|
if (this.config.showSaturation) {
|
|
contents.push({
|
|
header: 'Saturation',
|
|
data: poolData.status.saturation,
|
|
class: this.config.contentClass
|
|
});
|
|
}
|
|
if (this.config.showControls) {
|
|
for (let control in this.config.controls) {
|
|
let controlObj = this.config.controls[control];
|
|
|
|
if (controlObj.type === 'circuit') {
|
|
let name = controlObj.name;
|
|
for (let circuit in poolData.controllerConfig.bodyArray) {
|
|
if (poolData.controllerConfig.bodyArray[circuit].circuitId === controlObj.id) {
|
|
if (!name) {
|
|
name = poolData.controllerConfig.bodyArray[circuit].name;
|
|
}
|
|
}
|
|
}
|
|
|
|
let on = false;
|
|
for (let circuit in poolData.status.circuitArray) {
|
|
if (poolData.status.circuitArray[circuit].id === controlObj.id) {
|
|
on = poolData.status.circuitArray[circuit].state !== 0;
|
|
}
|
|
}
|
|
|
|
let 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
|
|
});
|
|
} else if (controlObj.type === 'heatpoint') {
|
|
if (controlObj.body < 0 || controlObj.body > poolData.status.setPoint.length) {
|
|
Log.warn('Invalid body specified for heatpoint');
|
|
continue;
|
|
}
|
|
|
|
let temperature = poolData.status.setPoint[controlObj.body];
|
|
|
|
let dataHtml = '<div class="temperature-container">';
|
|
dataHtml += '<button id="sl-temp-up-'+controlObj.body+'" class="temperature control-off" onclick="setHeatpoint(this, 1)" data-body="'+controlObj.body+'" data-temperature="'+temperature+'"><div class="content">+</div></button>';
|
|
dataHtml += '<div class="temperature-label">'+controlObj.name+': '+temperature+'°</div>';
|
|
dataHtml += '<button id="sl-temp-down-'+controlObj.body+'" class="temperature control-off" onclick="setHeatpoint(this, -1)" data-body="'+controlObj.body+'" data-temperature="'+temperature+'"><div class="content">-</div></button>';
|
|
|
|
contents.push({
|
|
data: dataHtml,
|
|
class: this.config.contentClass
|
|
});
|
|
} else if (controlObj.type === 'heatmode') {
|
|
if (controlObj.body < 0 || controlObj.body > poolData.status.heatMode.length) {
|
|
Log.warn('Invalid body specified for heatmode');
|
|
continue;
|
|
}
|
|
|
|
let on = poolData.status.heatMode[controlObj.body] !== 0;
|
|
let mode = typeof controlObj.heatMode === 'number' ? controlObj.heatMode : 3;
|
|
|
|
let 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') + '" data-mode="' + mode.toString() + '"><div class="content">' +
|
|
controlObj.name + '</div></button>',
|
|
class: this.config.contentClass
|
|
});
|
|
} else {
|
|
Log.warn('circuit with unknown type, unable to display:');
|
|
Log.warn(controlObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
let headerRow = null;
|
|
let contentRow = null;
|
|
|
|
if (this.config.showFreezeMode && poolData.status.freezeMode !== 0) {
|
|
let row = document.createElement('tr');
|
|
table.appendChild(row);
|
|
row.className = 'cold-temp';
|
|
let cell = document.createElement('th');
|
|
row.appendChild(cell);
|
|
cell.colSpan = this.config.columns;
|
|
cell.innerHTML = '<center>FREEZE MODE</center>';
|
|
}
|
|
|
|
let cols = -1;
|
|
for (let item in contents) {
|
|
cols++;
|
|
if (cols % this.config.columns === 0) {
|
|
headerRow = document.createElement('tr');
|
|
contentRow = document.createElement('tr');
|
|
table.appendChild(headerRow);
|
|
table.appendChild(contentRow);
|
|
}
|
|
|
|
if (contents[item].header) {
|
|
let headerCell = document.createElement('th');
|
|
headerCell.innerHTML = contents[item].header;
|
|
headerRow.appendChild(headerCell);
|
|
}
|
|
|
|
let contentCell = document.createElement('td');
|
|
contentCell.innerHTML = contents[item].data;
|
|
contentCell.className = contents[item].class;
|
|
contentRow.appendChild(contentCell);
|
|
}
|
|
|
|
return table;
|
|
}
|
|
},
|
|
|
|
socketNotificationReceived: function(notification, payload) {
|
|
if (notification === 'SCREENLOGIC_RESULT') {
|
|
poolData = payload;
|
|
this.updateDom();
|
|
} else if (notification === 'SCREENLOGIC_CIRCUIT_DONE'
|
|
|| notification === 'SCREENLOGIC_HEATSTATE_DONE'
|
|
|| notification === 'SCREENLOGIC_HEATPOINT_DONE') {
|
|
poolData.status = payload.status;
|
|
this.updateDom();
|
|
}
|
|
},
|
|
});
|
|
|
|
const SPA_CIRCUIT_ID = 500;
|
|
const POOL_CIRCUIT_ID = 505;
|
|
|
|
function isPoolActive(status) {
|
|
for (let i = 0; i < status.circuitArray.length; i++) {
|
|
if (status.circuitArray[i].id === POOL_CIRCUIT_ID) {
|
|
return status.circuitArray[i].state === 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
function hasSpa(status) {
|
|
for (let i = 0; i < status.circuitArray.length; i++) {
|
|
if (status.circuitArray[i].id === SPA_CIRCUIT_ID) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isSpaActive(status) {
|
|
for (let i = 0; i < status.circuitArray.length; i++) {
|
|
if (status.circuitArray[i].id === SPA_CIRCUIT_ID) {
|
|
return status.circuitArray[i].state === 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
function setCircuit(e) {
|
|
let circuitId = parseInt(e.dataset.circuit);
|
|
let on = e.dataset.state !== '0';
|
|
moduleObj.sendSocketNotification('SCREENLOGIC_CIRCUIT', {id: circuitId, state: on ? 0 : 1});
|
|
e.classList.remove('control-on', 'control-off');
|
|
}
|
|
|
|
function setHeatmode(e) {
|
|
let bodyId = parseInt(e.dataset.body);
|
|
let on = e.dataset.state !== '0';
|
|
let mode = e.dataset.mode;
|
|
moduleObj.sendSocketNotification('SCREENLOGIC_HEATSTATE', {body: bodyId, state: on ? 0 : parseInt(mode)});
|
|
e.classList.remove('control-on', 'control-off');
|
|
}
|
|
|
|
function setHeatpoint(e, tempChange) {
|
|
let bodyId = parseInt(e.dataset.body);
|
|
let temp = parseInt(e.dataset.temperature) + tempChange;
|
|
moduleObj.sendSocketNotification('SCREENLOGIC_HEATPOINT', {body: bodyId, temperature: temp});
|
|
e.classList.remove('control-on', 'control-off');
|
|
}
|