Add fixes for linters, add github workflows

This runs eslint --fix as well as manually fixing things that the automatic fixer couldn't handle.
This commit is contained in:
2020-08-21 14:58:11 -05:00
parent cca1e1bee9
commit 2a84b7f4fd
10 changed files with 1382 additions and 429 deletions

19
.eslintrc Normal file
View File

@ -0,0 +1,19 @@
{
"rules": {
"comma-dangle": 0,
"indent": [2, 4],
"max-len": "off",
"radix": [2, "as-needed"],
"no-console": 0,
"linebreak-style": "off",
"prettier/prettier": 0
},
"settings": {
"import/core-modules": [ "node_helper" ]
},
"env": {
"browser": true,
"node": true,
"es6": true
}
}

28
.github/workflows/nodejs.yml vendored Normal file
View File

@ -0,0 +1,28 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present

8
.markdownlint.json Normal file
View File

@ -0,0 +1,8 @@
{
"default": true,
"no-duplicate-header": {
"siblings_only": true
},
"line-length": false,
"no-inline-html": false
}

View File

@ -1,32 +1,46 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.2] - 2020-04-08 ## [1.1.2] - 2020-04-08
### Fixed ### Fixed
- Fixed which property was being read to determine whether heat mode was enabled or not. Previously the state of the heater itself was being used to display on/off status, now the requested mode is used instead (the heater toggles on/off as part of normal operation even while heating is enabled). - Fixed which property was being read to determine whether heat mode was enabled or not. Previously the state of the heater itself was being used to display on/off status, now the requested mode is used instead (the heater toggles on/off as part of normal operation even while heating is enabled).
## [1.1.1] - 2020-04-07 ## [1.1.1] - 2020-04-07
### Added ### Added
- Ability to specify which heat mode to use when enabling heating for a specific body. Previously mode 1 was always sent which means "solar" while most people probably want mode 3 which is "heat pump". - Ability to specify which heat mode to use when enabling heating for a specific body. Previously mode 1 was always sent which means "solar" while most people probably want mode 3 which is "heat pump".
## [1.1.0] - 2020-04-01 ## [1.1.0] - 2020-04-01
### Added ### Added
- Ability to show buttons for controlling pool equipment (with a touch screen, for example). NOTE: running `npm install` again is necessary after upgrading to this version if the heat controls are used. - Ability to show buttons for controlling pool equipment (with a touch screen, for example). NOTE: running `npm install` again is necessary after upgrading to this version if the heat controls are used.
## [1.0.3] - 2018-04-27 ## [1.0.3] - 2018-04-27
### Changed ### Changed
- Packaged a newer `node-screenlogic` dependency to fix server broadcast in certain environments. - Packaged a newer `node-screenlogic` dependency to fix server broadcast in certain environments.
## [1.0.2] - 2018-04-26 ## [1.0.2] - 2018-04-26
### Added ### Added
- New option `updateInterval` to control how often the pool data is updated (default 30 minutes). - New option `updateInterval` to control how often the pool data is updated (default 30 minutes).
## [1.0.1] - 2018-04-25 ## [1.0.1] - 2018-04-25
### Added ### Added
- New option `showFreezeMode` shows a banner acrosss the top if the pool is currently in freeze-protection mode. - New option `showFreezeMode` shows a banner acrosss the top if the pool is currently in freeze-protection mode.
## [1.0.0] - 2018-04-24 ## [1.0.0] - 2018-04-24
- Initial release. - Initial release.

View File

@ -1,288 +1,288 @@
poolData = {}; let poolData = {};
var moduleObj; let moduleObj;
Module.register("MMM-ScreenLogic",{ Module.register("MMM-ScreenLogic",{
defaults: { defaults: {
showPoolTemp: true, showPoolTemp: true,
showSpaTemp: true, showSpaTemp: true,
showPH: true, showPH: true,
showOrp: true, showOrp: true,
showSaltLevel: true, showSaltLevel: true,
showSaturation: true, showSaturation: true,
showFreezeMode: true, showFreezeMode: true,
showControls: false, showControls: false,
controls: [], controls: [],
colored: true, colored: true,
coldTemp: 84, coldTemp: 84,
hotTemp: 90, hotTemp: 90,
columns: 3, columns: 3,
contentClass: "light", contentClass: "light",
updateInterval: 30 * 60 * 1000 updateInterval: 30 * 60 * 1000
}, },
start: function() { start: function() {
// this isn't a great solution...is there a better one? needed to do stuff with buttons // this isn't a great solution...is there a better one? needed to do stuff with buttons
moduleObj = this; moduleObj = this;
if (this.config.showControls && (!this.config.controls || this.config.controls.length == 0)) { 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.'); Log.warn('Controls are enabled, but no controls are configured. See README for info on setting up controls.');
this.config.showControls = false; this.config.showControls = false;
} }
this.sendSocketNotification('SCREENLOGIC_CONFIG', this.config); this.sendSocketNotification('SCREENLOGIC_CONFIG', this.config);
this.sendSocketNotification('SCREENLOGIC_UPDATE'); this.sendSocketNotification('SCREENLOGIC_UPDATE');
}, },
getStyles: function() { getStyles: function() {
return ["screenlogic.css"]; return ["screenlogic.css"];
}, },
getDom: function() { getDom: function() {
if (!poolData.status) { if (!poolData.status) {
var wrapper = document.createElement("div"); let wrapper = document.createElement("div");
wrapper.innerHTML = 'Loading...'; wrapper.innerHTML = 'Loading...';
wrapper.className += "dimmed light small"; wrapper.className += "dimmed light small";
return wrapper; return wrapper;
} else { } else {
var table = document.createElement('table'); let table = document.createElement('table');
table.className = "small"; table.className = "small";
if (this.config.colored) { if (this.config.colored) {
table.className += " colored"; table.className += " colored";
} }
var contents = []; let contents = [];
if (this.config.showPoolTemp) { if (this.config.showPoolTemp) {
var className = ""; let className = "";
if (poolData.status.currentTemp[0] <= this.config.coldTemp) { if (poolData.status.currentTemp[0] <= this.config.coldTemp) {
className += " cold-temp"; className += " cold-temp";
} else if (poolData.status.currentTemp[0] >= this.config.hotTemp) { } else if (poolData.status.currentTemp[0] >= this.config.hotTemp) {
className += " hot-temp"; className += " hot-temp";
} }
contents.push({ contents.push({
header: "Pool temp", header: "Pool temp",
data: poolData.status.currentTemp[0] + "&deg;" + (!isPoolActive(poolData.status) ? " (last)" : ""), data: poolData.status.currentTemp[0] + "&deg;" + (!isPoolActive(poolData.status) ? " (last)" : ""),
class: this.config.contentClass + className class: this.config.contentClass + className
}); });
} }
if (this.config.showSpaTemp) { if (this.config.showSpaTemp) {
var className = ""; let className = "";
if (poolData.status.currentTemp[1] <= this.config.coldTemp) { if (poolData.status.currentTemp[1] <= this.config.coldTemp) {
className = " cold-temp"; className = " cold-temp";
} else if (poolData.status.currentTemp[1] >= this.config.hotTemp) { } else if (poolData.status.currentTemp[1] >= this.config.hotTemp) {
className = " hot-temp"; className = " hot-temp";
} }
contents.push({ contents.push({
header: "Spa temp", header: "Spa temp",
data: poolData.status.currentTemp[1] + "&deg;" + (!isSpaActive(poolData.status) ? " (last)" : ""), data: poolData.status.currentTemp[1] + "&deg;" + (!isSpaActive(poolData.status) ? " (last)" : ""),
class: this.config.contentClass + className class: this.config.contentClass + className
}); });
} }
if (this.config.showPH) { if (this.config.showPH) {
contents.push({ contents.push({
header: "pH", header: "pH",
data: poolData.status.pH, data: poolData.status.pH,
class: this.config.contentClass class: this.config.contentClass
}); });
} }
if (this.config.showOrp) { if (this.config.showOrp) {
contents.push({ contents.push({
header: "ORP", header: "ORP",
data: poolData.status.orp, data: poolData.status.orp,
class: this.config.contentClass class: this.config.contentClass
}); });
} }
if (this.config.showSaltLevel) { if (this.config.showSaltLevel) {
contents.push({ contents.push({
header: "Salt PPM", header: "Salt PPM",
data: poolData.status.saltPPM, data: poolData.status.saltPPM,
class: this.config.contentClass class: this.config.contentClass
}); });
} }
if (this.config.showSaturation) { if (this.config.showSaturation) {
contents.push({ contents.push({
header: "Saturation", header: "Saturation",
data: poolData.status.saturation, data: poolData.status.saturation,
class: this.config.contentClass class: this.config.contentClass
}); });
} }
if (this.config.showControls) { if (this.config.showControls) {
for (var control in this.config.controls) { for (let control in this.config.controls) {
var controlObj = this.config.controls[control]; let controlObj = this.config.controls[control];
if (controlObj.type === 'circuit') { if (controlObj.type === 'circuit') {
var name = controlObj.name; let name = controlObj.name;
for (var circuit in poolData.controllerConfig.bodyArray) { for (let circuit in poolData.controllerConfig.bodyArray) {
if (poolData.controllerConfig.bodyArray[circuit].circuitId == controlObj.id) { if (poolData.controllerConfig.bodyArray[circuit].circuitId === controlObj.id) {
if (!name) { if (!name) {
name = poolData.controllerConfig.bodyArray[circuit].name; name = poolData.controllerConfig.bodyArray[circuit].name;
} }
} }
} }
var on = false; let on = false;
for (var circuit in poolData.status.circuitArray) { for (let circuit in poolData.status.circuitArray) {
if (poolData.status.circuitArray[circuit].id == controlObj.id) { if (poolData.status.circuitArray[circuit].id === controlObj.id) {
on = poolData.status.circuitArray[circuit].state !== 0; on = poolData.status.circuitArray[circuit].state !== 0;
} }
} }
var cls = ''; let cls = '';
if (this.config.colored) { if (this.config.colored) {
cls = on ? 'control-on' : 'control-off'; cls = on ? 'control-on' : 'control-off';
} }
contents.push({ contents.push({
data: '<button id="sl-control-' + controlObj.id + '" class="control ' + cls + '" onclick="setCircuit(this)" data-circuit="' + 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">' + controlObj.id + '" data-state="' + (on ? '1' : '0') + '"><div class="content">' +
name + '</div></button>', name + '</div></button>',
class: this.config.contentClass class: this.config.contentClass
}); });
} else if (controlObj.type === 'heatpoint') { } else if (controlObj.type === 'heatpoint') {
if (controlObj.body < 0 || controlObj.body > poolData.status.setPoint.length) { if (controlObj.body < 0 || controlObj.body > poolData.status.setPoint.length) {
Log.warn('Invalid body specified for heatpoint'); Log.warn('Invalid body specified for heatpoint');
continue; continue;
} }
var temperature = poolData.status.setPoint[controlObj.body]; let temperature = poolData.status.setPoint[controlObj.body];
var dataHtml = '<div class="temperature-container">'; 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 += '<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+'&deg;</div>'; dataHtml += '<div class="temperature-label">'+controlObj.name+': '+temperature+'&deg;</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>'; 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({ contents.push({
data: dataHtml, data: dataHtml,
class: this.config.contentClass class: this.config.contentClass
}); });
} else if (controlObj.type === 'heatmode') { } else if (controlObj.type === 'heatmode') {
if (controlObj.body < 0 || controlObj.body > poolData.status.heatMode.length) { if (controlObj.body < 0 || controlObj.body > poolData.status.heatMode.length) {
Log.warn('Invalid body specified for heatmode'); Log.warn('Invalid body specified for heatmode');
continue; continue;
} }
var on = poolData.status.heatMode[controlObj.body] !== 0; let on = poolData.status.heatMode[controlObj.body] !== 0;
var mode = typeof controlObj.heatMode === 'number' ? controlObj.heatMode : 3; let mode = typeof controlObj.heatMode === 'number' ? controlObj.heatMode : 3;
var cls = ''; let cls = '';
if (this.config.colored) { if (this.config.colored) {
cls = on ? 'control-on' : 'control-off'; cls = on ? 'control-on' : 'control-off';
} }
contents.push({ contents.push({
data: '<button id="sl-heat-' + controlObj.body + '" class="control ' + cls + '" onclick="setHeatmode(this)" data-body="' + 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.body + '" data-state="' + (on ? '1' : '0') + '" data-mode="' + mode.toString() + '"><div class="content">' +
controlObj.name + '</div></button>', controlObj.name + '</div></button>',
class: this.config.contentClass class: this.config.contentClass
}); });
} else { } else {
Log.warn('circuit with unknown type, unable to display:'); Log.warn('circuit with unknown type, unable to display:');
Log.warn(controlObj); Log.warn(controlObj);
} }
} }
} }
var headerRow = null; let headerRow = null;
var contentRow = null; let contentRow = null;
if (this.config.showFreezeMode && poolData.status.freezeMode !== 0) { if (this.config.showFreezeMode && poolData.status.freezeMode !== 0) {
var row = document.createElement('tr'); let row = document.createElement('tr');
table.appendChild(row); table.appendChild(row);
row.className = 'cold-temp'; row.className = 'cold-temp';
var cell = document.createElement('th'); let cell = document.createElement('th');
row.appendChild(cell); row.appendChild(cell);
cell.colSpan = this.config.columns; cell.colSpan = this.config.columns;
cell.innerHTML = '<center>FREEZE MODE</center>'; cell.innerHTML = '<center>FREEZE MODE</center>';
} }
var cols = -1; let cols = -1;
for (var item in contents) { for (let item in contents) {
cols++; cols++;
if (cols % this.config.columns === 0) { if (cols % this.config.columns === 0) {
headerRow = document.createElement('tr'); headerRow = document.createElement('tr');
contentRow = document.createElement('tr'); contentRow = document.createElement('tr');
table.appendChild(headerRow); table.appendChild(headerRow);
table.appendChild(contentRow); table.appendChild(contentRow);
} }
if (contents[item].header) { if (contents[item].header) {
var headerCell = document.createElement('th'); let headerCell = document.createElement('th');
headerCell.innerHTML = contents[item].header; headerCell.innerHTML = contents[item].header;
headerRow.appendChild(headerCell); headerRow.appendChild(headerCell);
} }
var contentCell = document.createElement('td'); let contentCell = document.createElement('td');
contentCell.innerHTML = contents[item].data; contentCell.innerHTML = contents[item].data;
contentCell.className = contents[item].class; contentCell.className = contents[item].class;
contentRow.appendChild(contentCell); contentRow.appendChild(contentCell);
} }
return table; return table;
} }
}, },
socketNotificationReceived: function(notification, payload) { socketNotificationReceived: function(notification, payload) {
if (notification === 'SCREENLOGIC_RESULT') { if (notification === 'SCREENLOGIC_RESULT') {
poolData = payload; poolData = payload;
this.updateDom(); this.updateDom();
} else if (notification === 'SCREENLOGIC_CIRCUIT_DONE' } else if (notification === 'SCREENLOGIC_CIRCUIT_DONE'
|| notification === 'SCREENLOGIC_HEATSTATE_DONE' || notification === 'SCREENLOGIC_HEATSTATE_DONE'
|| notification === 'SCREENLOGIC_HEATPOINT_DONE') { || notification === 'SCREENLOGIC_HEATPOINT_DONE') {
poolData.status = payload.status; poolData.status = payload.status;
this.updateDom(); this.updateDom();
} }
}, },
}); });
const SPA_CIRCUIT_ID = 500; const SPA_CIRCUIT_ID = 500;
const POOL_CIRCUIT_ID = 505; const POOL_CIRCUIT_ID = 505;
function isPoolActive(status) { function isPoolActive(status) {
for (var i = 0; i < status.circuitArray.length; i++) { for (let i = 0; i < status.circuitArray.length; i++) {
if (status.circuitArray[i].id === POOL_CIRCUIT_ID) { if (status.circuitArray[i].id === POOL_CIRCUIT_ID) {
return status.circuitArray[i].state === 1; return status.circuitArray[i].state === 1;
} }
} }
} }
function hasSpa(status) { function hasSpa(status) {
for (var i = 0; i < status.circuitArray.length; i++) { for (let i = 0; i < status.circuitArray.length; i++) {
if (status.circuitArray[i].id === SPA_CIRCUIT_ID) { if (status.circuitArray[i].id === SPA_CIRCUIT_ID) {
return true; return true;
} }
} }
return false; return false;
} }
function isSpaActive(status) { function isSpaActive(status) {
for (var i = 0; i < status.circuitArray.length; i++) { for (let i = 0; i < status.circuitArray.length; i++) {
if (status.circuitArray[i].id === SPA_CIRCUIT_ID) { if (status.circuitArray[i].id === SPA_CIRCUIT_ID) {
return status.circuitArray[i].state === 1; return status.circuitArray[i].state === 1;
} }
} }
} }
function setCircuit(e) { function setCircuit(e) {
var circuitId = parseInt(e.dataset.circuit); let circuitId = parseInt(e.dataset.circuit);
var on = e.dataset.state !== '0'; let on = e.dataset.state !== '0';
moduleObj.sendSocketNotification('SCREENLOGIC_CIRCUIT', {id: circuitId, state: on ? 0 : 1}); moduleObj.sendSocketNotification('SCREENLOGIC_CIRCUIT', {id: circuitId, state: on ? 0 : 1});
e.classList.remove('control-on', 'control-off'); e.classList.remove('control-on', 'control-off');
} }
function setHeatmode(e) { function setHeatmode(e) {
var bodyId = parseInt(e.dataset.body); let bodyId = parseInt(e.dataset.body);
var on = e.dataset.state !== '0'; let on = e.dataset.state !== '0';
var mode = e.dataset.mode; let mode = e.dataset.mode;
moduleObj.sendSocketNotification('SCREENLOGIC_HEATSTATE', {body: bodyId, state: on ? 0 : parseInt(mode)}); moduleObj.sendSocketNotification('SCREENLOGIC_HEATSTATE', {body: bodyId, state: on ? 0 : parseInt(mode)});
e.classList.remove('control-on', 'control-off'); e.classList.remove('control-on', 'control-off');
} }
function setHeatpoint(e, tempChange) { function setHeatpoint(e, tempChange) {
var bodyId = parseInt(e.dataset.body); let bodyId = parseInt(e.dataset.body);
var temp = parseInt(e.dataset.temperature) + tempChange; let temp = parseInt(e.dataset.temperature) + tempChange;
moduleObj.sendSocketNotification('SCREENLOGIC_HEATPOINT', {body: bodyId, temperature: temp}); moduleObj.sendSocketNotification('SCREENLOGIC_HEATPOINT', {body: bodyId, temperature: temp});
e.classList.remove('control-on', 'control-off'); e.classList.remove('control-on', 'control-off');
} }

View File

@ -1,13 +1,16 @@
# MMM-ScreenLogic # MMM-ScreenLogic
A <a href="https://github.com/MichMich/MagicMirror">MagicMirror²</a> module used to connect to a local Pentair ScreenLogic pool controller system.
[MagicMirror²](https://github.com/MichMich/MagicMirror) module used to connect to a local Pentair ScreenLogic pool controller system.
## Installation ## Installation
1. Navigate into your MagicMirror's `modules` folder and execute `git clone https://github.com/parnic/MMM-ScreenLogic.git`. 1. Navigate into your MagicMirror's `modules` folder and execute `git clone https://github.com/parnic/MMM-ScreenLogic.git`.
2. `cd MMM-ScreenLogic` 2. `cd MMM-ScreenLogic`
3. Execute `npm install` to install the node dependencies. 3. Execute `npm install --production` to install the node dependencies.
4. Add the module inside `config.js` placing it where you prefer. 4. Add the module inside `config.js` placing it where you prefer.
## Config ## Config
|Option|Type|Description|Default| |Option|Type|Description|Default|
|---|---|---|---| |---|---|---|---|
|`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`| |`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`|
@ -29,32 +32,36 @@ A <a href="https://github.com/MichMich/MagicMirror">MagicMirror²</a> module use
|`updateInterval`|Integer|How frequently, in milliseconds, to update pool data.|`30 * 60 * 1000` (every 30 minutes)| |`updateInterval`|Integer|How frequently, in milliseconds, to update pool data.|`30 * 60 * 1000` (every 30 minutes)|
Here is an example of an entry in config.js Here is an example of an entry in config.js
```
```js
{ {
module: 'MMM-ScreenLogic', module: 'MMM-ScreenLogic',
header: 'Pool info', header: 'Pool info',
position: 'top_left', position: 'top_left',
config: { config: {
showSpaTemp: false, showSpaTemp: false,
columns: 2, columns: 2,
contentClass: 'thin', contentClass: 'thin',
showControls: true, showControls: true,
controls: [ controls: [
{type: 'circuit', id: 500}, {type: 'circuit', id: 500},
{type: 'circuit', id: 505, name: 'Pool'}, {type: 'circuit', id: 505, name: 'Pool'},
{type: 'heatmode', body: 0, name: 'Pool heater'}, {type: 'heatmode', body: 0, name: 'Pool heater'},
{type: 'heatpoint', body: 0, name: 'Pool'}, {type: 'heatpoint', body: 0, name: 'Pool'},
{type: 'heatmode', body: 1, heatMode: 2, name: 'Spa heater'}, {type: 'heatmode', body: 1, heatMode: 2, name: 'Spa heater'},
] ]
} }
}, },
``` ```
## Screenshot ## Screenshot
#### With color
### With color
![Screenshot with color](/screenshot.png?raw=true "colored: true") ![Screenshot with color](/screenshot.png?raw=true "colored: true")
## Notes ## Notes
Pull requests are very welcome! If you'd like to see any additional functionality, don't hesitate to let me know. Pull requests are very welcome! If you'd like to see any additional functionality, don't hesitate to let me know.
This module only works with ScreenLogic controllers on the local network via either a UDP broadcast on 255.255.255.255 or a direct connection if you've specified an address and port in the configuration. This module only works with ScreenLogic controllers on the local network via either a UDP broadcast on 255.255.255.255 or a direct connection if you've specified an address and port in the configuration.
@ -64,4 +71,5 @@ The data is updated every 30 minutes by default (configurable with `updateInterv
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! 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 ## Libraries
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. 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

@ -1,210 +1,210 @@
var NodeHelper = require('node_helper'); var NodeHelper = require('node_helper');
module.exports = NodeHelper.create({ module.exports = NodeHelper.create({
start: function() { start: function() {
this.setTimer(30 * 60 * 1000); this.setTimer(30 * 60 * 1000);
}, },
doUpdate: function() { doUpdate: function() {
var self = this; var self = this;
getPoolData(this.config, function(poolData) { getPoolData(this.config, function(poolData) {
self.sendSocketNotification('SCREENLOGIC_RESULT', poolData); self.sendSocketNotification('SCREENLOGIC_RESULT', poolData);
}); });
}, },
setCircuit: function(circuitState) { setCircuit: function(circuitState) {
var self = this; var self = this;
setCircuitState(circuitState, function(poolStatus) { setCircuitState(circuitState, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_CIRCUIT_DONE', {circuitState: circuitState, status: poolStatus}); self.sendSocketNotification('SCREENLOGIC_CIRCUIT_DONE', {circuitState: circuitState, status: poolStatus});
}); });
}, },
setHeatpoint: function(heatpoint) { setHeatpoint: function(heatpoint) {
var self = this; var self = this;
setHeatpointState(heatpoint, function(poolStatus) { setHeatpointState(heatpoint, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_HEATPOINT_DONE', {heatpoint: heatpoint, status: poolStatus}); self.sendSocketNotification('SCREENLOGIC_HEATPOINT_DONE', {heatpoint: heatpoint, status: poolStatus});
}); });
}, },
setHeatstate: function(heatstate) { setHeatstate: function(heatstate) {
var self = this; var self = this;
setHeatstateState(heatstate, function(poolStatus) { setHeatstateState(heatstate, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_HEATSTATE_DONE', {heatstate: heatstate, status: poolStatus}); self.sendSocketNotification('SCREENLOGIC_HEATSTATE_DONE', {heatstate: heatstate, status: poolStatus});
}); });
}, },
setLightcmd: function(lightCmd) { setLightcmd: function(lightCmd) {
var self = this; var self = this;
setLights(lightCmd, function(poolStatus) { setLights(lightCmd, function(poolStatus) {
self.sendSocketNotification('SCREENLOGIC_LIGHTCMD_DONE', {lightCmd: lightCmd, status: poolStatus}); self.sendSocketNotification('SCREENLOGIC_LIGHTCMD_DONE', {lightCmd: lightCmd, status: poolStatus});
}); });
}, },
restartTimer: function() { restartTimer: function() {
var interval = this.updateInterval; var interval = this.updateInterval;
this.updateInterval = undefined; this.updateInterval = undefined;
this.setTimer(interval); this.setTimer(interval);
}, },
setTimer: function(updateInterval) { setTimer: function(updateInterval) {
var update = true; var update = true;
update = typeof this.updateInterval === 'undefined' || this.updateInterval != updateInterval; update = typeof this.updateInterval === 'undefined' || this.updateInterval !== updateInterval;
this.updateInterval = updateInterval; this.updateInterval = updateInterval;
if (update) { if (update) {
if (typeof this.timer !== 'undefined') { if (typeof this.timer !== 'undefined') {
clearInterval(this.timer); clearInterval(this.timer);
} }
var self = this; var self = this;
self.timer = setInterval(function() { self.timer = setInterval(function() {
self.doUpdate() self.doUpdate()
}, self.updateInterval); }, self.updateInterval);
} }
}, },
socketNotificationReceived: function(notification, payload) { socketNotificationReceived: function(notification, payload) {
if (notification === 'SCREENLOGIC_CONFIG') { if (notification === 'SCREENLOGIC_CONFIG') {
this.config = payload; this.config = payload;
this.setTimer(this.config.updateInterval); this.setTimer(this.config.updateInterval);
} }
if (notification === 'SCREENLOGIC_UPDATE') { if (notification === 'SCREENLOGIC_UPDATE') {
this.doUpdate(); this.doUpdate();
} }
if (notification === 'SCREENLOGIC_CIRCUIT') { if (notification === 'SCREENLOGIC_CIRCUIT') {
this.setCircuit(payload); this.setCircuit(payload);
} }
if (notification === 'SCREENLOGIC_HEATPOINT') { if (notification === 'SCREENLOGIC_HEATPOINT') {
this.setHeatpoint(payload); this.setHeatpoint(payload);
} }
if (notification === 'SCREENLOGIC_HEATSTATE') { if (notification === 'SCREENLOGIC_HEATSTATE') {
this.setHeatstate(payload); this.setHeatstate(payload);
} }
if (notification === 'SCREENLOGIC_LIGHTCMD') { if (notification === 'SCREENLOGIC_LIGHTCMD') {
this.setLightcmd(payload); this.setLightcmd(payload);
} }
} }
}); });
const ScreenLogic = require('node-screenlogic'); const ScreenLogic = require('node-screenlogic');
var foundUnit; var foundUnit;
function getPoolData(config, cb) { function getPoolData(config, cb) {
if (!foundUnit && typeof config !== 'undefined' && config.serverAddress && config.serverPort) { if (!foundUnit && typeof config !== 'undefined' && config.serverAddress && config.serverPort) {
foundUnit = new ScreenLogic.UnitConnection(config.serverPort, config.serverAddress); foundUnit = new ScreenLogic.UnitConnection(config.serverPort, config.serverAddress);
} }
if (foundUnit) { if (foundUnit) {
populateSystemData(cb); populateSystemData(cb);
} else { } else {
findServer(cb); findServer(cb);
} }
} }
function findServer(cb) { function findServer(cb) {
var finder = new ScreenLogic.FindUnits(); var finder = new ScreenLogic.FindUnits();
finder.on('serverFound', function(server) { finder.on('serverFound', function(server) {
finder.close(); finder.close();
foundUnit = new ScreenLogic.UnitConnection(server); foundUnit = new ScreenLogic.UnitConnection(server);
populateSystemData(cb); populateSystemData(cb);
}); });
finder.search(); finder.search();
} }
function populateSystemData(cb) { function populateSystemData(cb) {
var poolData = {}; var poolData = {};
if (!foundUnit) { if (!foundUnit) {
cb(poolData); cb(poolData);
return; return;
} }
foundUnit.once('loggedIn', function() { foundUnit.once('loggedIn', function() {
foundUnit.getControllerConfig(); foundUnit.getControllerConfig();
}).once('controllerConfig', function(config) { }).once('controllerConfig', function(config) {
poolData.controllerConfig = config; poolData.controllerConfig = config;
poolData.degStr = config.degC ? 'C' : 'F'; poolData.degStr = config.degC ? 'C' : 'F';
foundUnit.getPoolStatus(); foundUnit.getPoolStatus();
}).once('poolStatus', function(status) { }).once('poolStatus', function(status) {
poolData.status = status; poolData.status = status;
foundUnit.close(); foundUnit.close();
cb(poolData); cb(poolData);
}); });
foundUnit.connect(); foundUnit.connect();
} }
function setCircuitState(circuitState, cb) { function setCircuitState(circuitState, cb) {
if (!foundUnit) { if (!foundUnit) {
cb(); cb();
return; return;
} }
foundUnit.once('loggedIn', function() { foundUnit.once('loggedIn', function() {
foundUnit.setCircuitState(0, circuitState.id, circuitState.state); foundUnit.setCircuitState(0, circuitState.id, circuitState.state);
}).once('circuitStateChanged', function() { }).once('circuitStateChanged', function() {
foundUnit.getPoolStatus(); foundUnit.getPoolStatus();
}).once('poolStatus', function(status) { }).once('poolStatus', function(status) {
foundUnit.close(); foundUnit.close();
cb(status); cb(status);
}); });
foundUnit.connect(); foundUnit.connect();
} }
function setHeatpointState(heatpoint, cb) { function setHeatpointState(heatpoint, cb) {
if (!foundUnit) { if (!foundUnit) {
cb(); cb();
return; return;
} }
foundUnit.once('loggedIn', function() { foundUnit.once('loggedIn', function() {
foundUnit.setSetPoint(0, heatpoint.body, heatpoint.temperature); foundUnit.setSetPoint(0, heatpoint.body, heatpoint.temperature);
}).once('setPointChanged', function() { }).once('setPointChanged', function() {
foundUnit.getPoolStatus(); foundUnit.getPoolStatus();
}).once('poolStatus', function(status) { }).once('poolStatus', function(status) {
foundUnit.close(); foundUnit.close();
cb(status); cb(status);
}); });
foundUnit.connect(); foundUnit.connect();
} }
function setHeatstateState(heatstate, cb) { function setHeatstateState(heatstate, cb) {
if (!foundUnit) { if (!foundUnit) {
cb(); cb();
return; return;
} }
foundUnit.once('loggedIn', function() { foundUnit.once('loggedIn', function() {
foundUnit.setHeatMode(0, heatstate.body, heatstate.state); foundUnit.setHeatMode(0, heatstate.body, heatstate.state);
}).once('heatModeChanged', function() { }).once('heatModeChanged', function() {
foundUnit.getPoolStatus(); foundUnit.getPoolStatus();
}).once('poolStatus', function(status) { }).once('poolStatus', function(status) {
foundUnit.close(); foundUnit.close();
cb(status); cb(status);
}); });
foundUnit.connect(); foundUnit.connect();
} }
function setLights(lightCmd, cb) { function setLights(lightCmd, cb) {
if (!foundUnit) { if (!foundUnit) {
cb(); cb();
return; return;
} }
foundUnit.once('loggedIn', function() { foundUnit.once('loggedIn', function() {
foundUnit.sendLightCommand(0, lightCmd); foundUnit.sendLightCommand(0, lightCmd);
}).once('sentLightCommand', function() { }).once('sentLightCommand', function() {
foundUnit.getPoolStatus(); foundUnit.getPoolStatus();
}).once('poolStatus', function(status) { }).once('poolStatus', function(status) {
foundUnit.close(); foundUnit.close();
cb(status); cb(status);
}); });
foundUnit.connect(); foundUnit.connect();
} }

872
package-lock.json generated
View File

@ -1,9 +1,628 @@
{ {
"name": "magic-mirror-module-screenlogic", "name": "magic-mirror-module-screenlogic",
"version": "1.0.3", "version": "1.1.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@babel/code-frame": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
},
"@babel/highlight": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
}
}
},
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"acorn": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
"dev": true
},
"acorn-jsx": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
"integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
"dev": true
},
"ajv": {
"version": "6.12.4",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
"dev": true
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"astral-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
"dev": true
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"requires": {
"esutils": "^2.0.2"
}
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
"integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
"dev": true,
"requires": {
"ansi-colors": "^4.1.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"eslint": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz",
"integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"enquirer": "^2.3.5",
"eslint-scope": "^5.1.0",
"eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^1.3.0",
"espree": "^7.2.0",
"esquery": "^1.2.0",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash": "^4.17.19",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"progress": "^2.0.0",
"regexpp": "^3.1.0",
"semver": "^7.2.1",
"strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0",
"table": "^5.2.3",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
}
},
"eslint-scope": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz",
"integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
},
"eslint-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
}
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
},
"espree": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz",
"integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==",
"dev": true,
"requires": {
"acorn": "^7.3.1",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.3.0"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"esquery": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
"integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
"dev": true,
"requires": {
"estraverse": "^5.1.0"
},
"dependencies": {
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
}
}
},
"esrecurse": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
"dev": true,
"requires": {
"estraverse": "^4.1.0"
}
},
"estraverse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
"file-entry-cache": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
"integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
"dev": true,
"requires": {
"flat-cache": "^2.0.1"
}
},
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
"integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
"dev": true,
"requires": {
"flatted": "^2.0.0",
"rimraf": "2.6.3",
"write": "1.0.3"
}
},
"flatted": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"globals": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
"dev": true,
"requires": {
"type-fest": "^0.8.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
}
},
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"js-yaml": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
"levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"requires": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"node-screenlogic": { "node-screenlogic": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-screenlogic/-/node-screenlogic-1.3.1.tgz", "resolved": "https://registry.npmjs.org/node-screenlogic/-/node-screenlogic-1.3.1.tgz",
@ -12,10 +631,261 @@
"smart-buffer": "~4.0.1" "smart-buffer": "~4.0.1"
} }
}, },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
"dev": true,
"requires": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
"type-check": "^0.4.0",
"word-wrap": "^1.2.3"
}
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"regexpp": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
"integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
"dev": true
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"slice-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
"integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"astral-regex": "^1.0.0",
"is-fullwidth-code-point": "^2.0.0"
}
},
"smart-buffer": { "smart-buffer": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz",
"integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==" "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw=="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
},
"strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"table": {
"version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
"integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
"dev": true,
"requires": {
"ajv": "^6.10.2",
"lodash": "^4.17.14",
"slice-ansi": "^2.1.0",
"string-width": "^3.0.0"
}
},
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
"type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"requires": {
"prelude-ls": "^1.2.1"
}
},
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
},
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"write": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
"integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
"dev": true,
"requires": {
"mkdirp": "^0.5.1"
}
} }
} }
} }

View File

@ -8,5 +8,11 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"node-screenlogic": "1.x" "node-screenlogic": "1.x"
},
"devDependencies": {
"eslint": "^7.7.0"
},
"scripts": {
"lint": "./node_modules/.bin/eslint ."
} }
} }

View File

@ -1,13 +1,13 @@
.MMM-ScreenLogic table.colored .cold-temp { .MMM-ScreenLogic table.colored .cold-temp {
color: #BCDDFF; color: #BCDDFF;
} }
.MMM-ScreenLogic table.colored .hot-temp { .MMM-ScreenLogic table.colored .hot-temp {
color: #FF8E99; color: #FF8E99;
} }
.MMM-ScreenLogic td { .MMM-ScreenLogic td {
width: 33%; width: 33%;
} }
.MMM-ScreenLogic .control { .MMM-ScreenLogic .control {
@ -19,11 +19,11 @@
text-align: center; text-align: center;
line-height: 1.5; line-height: 1.5;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
width: 80%; width: 80%;
height: 5rem; height: 5rem;
margin: auto; margin: auto;
font-weight: bold; font-weight: bold;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
@ -34,13 +34,13 @@
} }
.MMM-ScreenLogic .control:after { .MMM-ScreenLogic .control:after {
padding-bottom: 100%; padding-bottom: 100%;
content: ""; content: "";
display: block; display: block;
} }
.MMM-ScreenLogic .content { .MMM-ScreenLogic .content {
position: absolute; position: absolute;
width: 90%; width: 90%;
height: 90%; height: 90%;
left: 0px; left: 0px;
@ -52,13 +52,13 @@
} }
.MMM-ScreenLogic .control-on { .MMM-ScreenLogic .control-on {
background: #28a745 !important; background: #28a745 !important;
border-color: #28a745 !important; border-color: #28a745 !important;
} }
.MMM-ScreenLogic .control-off { .MMM-ScreenLogic .control-off {
background: transparent !important; background: transparent !important;
border-color: #6c757d !important; border-color: #6c757d !important;
} }
.MMM-ScreenLogic .temperature-container { .MMM-ScreenLogic .temperature-container {
@ -81,10 +81,10 @@
background: #000; background: #000;
border: 0px solid #6c757d; border: 0px solid #6c757d;
color: #fff; color: #fff;
position: relative; position: relative;
width: 80%; width: 80%;
height: 2.45rem; height: 2.45rem;
margin: auto; margin: auto;
font-weight: bold; font-weight: bold;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;