mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-07-30 10:51:32 +08:00
471 lines
11 KiB
JavaScript
471 lines
11 KiB
JavaScript
'use strict';
|
|
'require baseclass';
|
|
'require rpc';
|
|
|
|
document.head.append(E('style', {'type': 'text/css'},
|
|
`
|
|
:root {
|
|
--app-temp-status-font-color: #2e2e2e;
|
|
--app-temp-status-border-color: var(--border-color-medium, #d4d4d4);
|
|
--app-temp-status-hot-color: #fff7e2;
|
|
--app-temp-status-overheat-color: #ffe9e8;
|
|
}
|
|
:root[data-darkmode="true"] {
|
|
--app-temp-status-font-color: #fff;
|
|
--app-temp-status-border-color: var(--border-color-medium, #444);
|
|
--app-temp-status-hot-color: #8d7000;
|
|
--app-temp-status-overheat-color: #a93734;
|
|
}
|
|
.temp-status-hot {
|
|
background-color: var(--app-temp-status-hot-color) !important;
|
|
color: var(--app-temp-status-font-color) !important;
|
|
}
|
|
.temp-status-hot .td {
|
|
color: var(--app-temp-status-font-color) !important;
|
|
}
|
|
.temp-status-hot td {
|
|
color: var(--app-temp-status-font-color) !important;
|
|
}
|
|
.temp-status-overheat {
|
|
background-color: var(--app-temp-status-overheat-color) !important;
|
|
color: var(--app-temp-status-font-color) !important;
|
|
}
|
|
.temp-status-overheat .td {
|
|
color: var(--app-temp-status-font-color) !important;
|
|
}
|
|
.temp-status-overheat td {
|
|
color: var(--app-temp-status-font-color) !important;
|
|
}
|
|
|
|
.temp-status-temp-area {
|
|
width: 100%;
|
|
padding: 0 0 1em 0;
|
|
display: flex;
|
|
-webkit-align-items: flex-start;
|
|
align-items: flex-start;
|
|
-webkit-justify-content: left;
|
|
justify-content: left;
|
|
-webkit-flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
-webkit-flex-direction: row;
|
|
flex-direction: row;
|
|
}
|
|
.temp-status-list-item {
|
|
display: inline-block;
|
|
flex-grow: 0;
|
|
-webkit-flex-grow 0;
|
|
flex-shrink: 1;
|
|
-webkit-flex-shrink 1;
|
|
width: 29em;
|
|
word-wrap: break-word;
|
|
margin: 0px 4px 4px 0 !important;
|
|
padding: 4px;
|
|
border: 1px solid;
|
|
border-color: transparent;
|
|
border-color: var(--app-temp-status-border-color);
|
|
-webkit-border-radius: 4px;
|
|
-moz-border-radius: 4px;
|
|
border-radius: 4px;
|
|
}
|
|
.temp-status-temp-value {
|
|
display: inline-block;
|
|
margin-left: 0.5em !important;
|
|
width: 4em;
|
|
text-align: left;
|
|
}
|
|
.temp-status-sensor-name {
|
|
display: inline-block;
|
|
padding-left: 1.5em;
|
|
font-size: 95%;
|
|
}
|
|
|
|
#temp-status-buttons-wrapper {
|
|
margin-bottom: 1em;
|
|
}
|
|
.temp-status-button {
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
margin: 2px 4px 2px 0 !important;
|
|
padding: 3px 5px;
|
|
border: 1px solid;
|
|
border-color: transparent;
|
|
-webkit-border-radius: 4px;
|
|
-moz-border-radius: 4px;
|
|
border-radius: 4px;
|
|
opacity: 0.7;
|
|
background-color: rgba(100 100 100 / 0.2);
|
|
}
|
|
.temp-status-button:hover {
|
|
opacity: 0.9;
|
|
}
|
|
.temp-status-button:active {
|
|
opacity: 1.0;
|
|
}
|
|
.temp-status-hide-item {
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
margin: 0 0.5em 0 0 !important;
|
|
padding: 1px 5px;
|
|
border: 1px dotted;
|
|
-webkit-border-radius: 4px;
|
|
-moz-border-radius: 4px;
|
|
border-radius: 4px;
|
|
opacity: 0.6;
|
|
background-color: rgba(100 100 100 / 0.2);
|
|
font-weight: bold;
|
|
}
|
|
.temp-status-hide-item:hover {
|
|
opacity: 0.9;
|
|
}
|
|
.temp-status-hide-item:active {
|
|
opacity: 1.0;
|
|
}
|
|
`));
|
|
|
|
return baseclass.extend({
|
|
title : _('Temperature'),
|
|
|
|
viewName : 'temp-status',
|
|
|
|
tempHot : 95,
|
|
|
|
tempOverheat : 105,
|
|
|
|
sensorsData : null,
|
|
|
|
tempData : null,
|
|
|
|
sensorsPath : [],
|
|
|
|
hiddenItems : new Set(),
|
|
|
|
hiddenNum : E('span', {}),
|
|
|
|
tempTable : E('table', { 'class': 'table' }),
|
|
|
|
tempArea : E('div', { 'class': 'temp-status-temp-area' }),
|
|
|
|
tempView : E('div', {}),
|
|
|
|
viewType : 'table',
|
|
|
|
callSensors : rpc.declare({
|
|
object: 'luci.temp-status',
|
|
method: 'getSensors',
|
|
expect: { '': {} },
|
|
}),
|
|
|
|
callTempData: rpc.declare({
|
|
object: 'luci.temp-status',
|
|
method: 'getTempData',
|
|
params: [ 'tpaths' ],
|
|
expect: { '': {} },
|
|
}),
|
|
|
|
formatTemp(mc) {
|
|
return Number((mc / 1000).toFixed(1));
|
|
},
|
|
|
|
sortFunc(a, b) {
|
|
return (a.number > b.number) ? 1 : (a.number < b.number) ? -1 : 0;
|
|
},
|
|
|
|
restoreSettingsFromLocalStorage() {
|
|
let hiddenItems = localStorage.getItem(`luci-app-${this.viewName}-hiddenItems`);
|
|
if(hiddenItems) {
|
|
this.hiddenItems = new Set(hiddenItems.split(','));
|
|
};
|
|
let view = localStorage.getItem(`luci-app-${this.viewName}-view`);
|
|
if(view) {
|
|
this.viewType = view;
|
|
};
|
|
},
|
|
|
|
saveSettingsToLocalStorage() {
|
|
localStorage.setItem(
|
|
`luci-app-${this.viewName}-hiddenItems`, Array.from(this.hiddenItems).join(','));
|
|
localStorage.setItem(
|
|
`luci-app-${this.viewName}-view`, this.viewType);
|
|
},
|
|
|
|
makeTempTableContent() {
|
|
this.tempTable.innerHTML = '';
|
|
this.tempTable.append(
|
|
E('tr', { 'class': 'tr table-titles' }, [
|
|
E('th', { 'class': 'th left', 'width': '33%' }, _('Sensor')),
|
|
E('th', { 'class': 'th left' }, _('Temperature')),
|
|
E('th', { 'class': 'th right', 'width': '1%' }, ' '),
|
|
])
|
|
);
|
|
|
|
if(this.sensorsData && this.tempData) {
|
|
for(let [k, v] of Object.entries(this.sensorsData)) {
|
|
v.sort(this.sortFunc);
|
|
|
|
for(let i of Object.values(v)) {
|
|
let sensor = i.title || i.item;
|
|
|
|
if(i.sources === undefined) {
|
|
continue;
|
|
};
|
|
|
|
i.sources.sort(this.sortFunc);
|
|
|
|
for(let j of i.sources) {
|
|
if(this.hiddenItems.has(j.path)) {
|
|
continue;
|
|
};
|
|
|
|
let temp = this.tempData[j.path];
|
|
let name = (j.label !== undefined) ? sensor + " / " + j.label :
|
|
(j.item !== undefined) ? sensor + " / " + j.item.replace(/_input$/, "") : sensor
|
|
|
|
if(temp !== undefined && temp !== null) {
|
|
temp = this.formatTemp(temp);
|
|
};
|
|
|
|
let tempHot = NaN;
|
|
let tempOverheat = NaN;
|
|
let tpoints = j.tpoints;
|
|
let tpointsString = '';
|
|
|
|
if(tpoints) {
|
|
for(let i of Object.values(tpoints)) {
|
|
let t = this.formatTemp(i.temp);
|
|
tpointsString += ` ${i.type}: ${t} °C`;
|
|
|
|
if(i.type == 'max' || i.type == 'critical' || i.type == 'emergency') {
|
|
if(!(tempOverheat <= t)) {
|
|
tempOverheat = t;
|
|
};
|
|
}
|
|
else if(i.type == 'hot') {
|
|
tempHot = t;
|
|
};
|
|
};
|
|
};
|
|
|
|
if(isNaN(tempHot) && isNaN(tempOverheat)) {
|
|
tempHot = this.tempHot;
|
|
tempOverheat = this.tempOverheat;
|
|
};
|
|
|
|
let rowStyle = (temp >= tempOverheat) ? ' temp-status-overheat':
|
|
(temp >= tempHot) ? ' temp-status-hot' : '';
|
|
|
|
this.tempTable.append(
|
|
E('tr', {
|
|
'class' : 'tr' + rowStyle,
|
|
'data-path': j.path ,
|
|
}, [
|
|
E('td', {
|
|
'class' : 'td left',
|
|
'data-title': _('Sensor')
|
|
},
|
|
(tpointsString.length > 0) ?
|
|
`<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${tpointsString}">${name}</span>` :
|
|
name
|
|
),
|
|
E('td', {
|
|
'class' : 'td left',
|
|
'data-title': _('Temperature')
|
|
},
|
|
(temp === undefined || temp === null) ? '-' : temp + ' °C'
|
|
),
|
|
E('td', {
|
|
'class' : 'td right',
|
|
'data-title': _('Hide'),
|
|
'title' : _('Hide'),
|
|
},
|
|
E('span', {
|
|
'class': 'temp-status-hide-item',
|
|
'title': _('Hide'),
|
|
'click': () => this.hideItem(j.path),
|
|
}, 'Χ'),
|
|
),
|
|
])
|
|
);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
if(this.tempTable.childNodes.length == 1) {
|
|
this.tempTable.append(
|
|
E('tr', { 'class': 'tr placeholder' },
|
|
E('td', { 'class': 'td' },
|
|
E('em', {}, _('No temperature sensors available'))
|
|
)
|
|
)
|
|
);
|
|
};
|
|
|
|
return this.tempTable;
|
|
},
|
|
|
|
makeTempAreaContent() {
|
|
this.tempArea.innerHTML = '';
|
|
|
|
for(let [k, v] of Object.entries(this.sensorsData)) {
|
|
v.sort(this.sortFunc);
|
|
|
|
for(let i of Object.values(v)) {
|
|
let sensor = i.title || i.item;
|
|
|
|
if(i.sources === undefined) {
|
|
continue;
|
|
};
|
|
|
|
i.sources.sort(this.sortFunc);
|
|
|
|
for(let j of i.sources) {
|
|
if(this.hiddenItems.has(j.path)) {
|
|
continue;
|
|
};
|
|
|
|
let temp = this.tempData[j.path];
|
|
let name = (j.label !== undefined) ? sensor + " / " + j.label :
|
|
(j.item !== undefined) ? sensor + " / " + j.item.replace(/_input$/, "") : sensor
|
|
|
|
if(temp !== undefined && temp !== null) {
|
|
temp = this.formatTemp(temp);
|
|
};
|
|
|
|
let tempHot = NaN;
|
|
let tempOverheat = NaN;
|
|
let tpoints = j.tpoints;
|
|
let tpointsString = '';
|
|
|
|
if(tpoints) {
|
|
for(let i of Object.values(tpoints)) {
|
|
let t = this.formatTemp(i.temp);
|
|
tpointsString += ` ${i.type}: ${t} °C`;
|
|
|
|
if(i.type == 'max' || i.type == 'critical' || i.type == 'emergency') {
|
|
if(!(tempOverheat <= t)) {
|
|
tempOverheat = t;
|
|
};
|
|
}
|
|
else if(i.type == 'hot') {
|
|
tempHot = t;
|
|
};
|
|
};
|
|
};
|
|
|
|
if(isNaN(tempHot) && isNaN(tempOverheat)) {
|
|
tempHot = this.tempHot;
|
|
tempOverheat = this.tempOverheat;
|
|
};
|
|
|
|
let itemStyle = (temp >= tempOverheat) ? ' temp-status-overheat':
|
|
(temp >= tempHot) ? ' temp-status-hot' : '';
|
|
|
|
this.tempArea.append(
|
|
E('div', { 'class': 'temp-status-list-item' + itemStyle }, [
|
|
E('span', {
|
|
'class': 'temp-status-hide-item',
|
|
'title': _('Hide'),
|
|
'click': () => this.hideItem(j.path),
|
|
}, 'Χ'),
|
|
E('span', {
|
|
'class': 'temp-status-temp-value',
|
|
}, (temp === undefined || temp === null) ? '-' : temp + ' °C'),
|
|
E('span', {
|
|
'class': 'temp-status-sensor-name'
|
|
}, (tpointsString.length > 0) ?
|
|
`<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${tpointsString}">${name}</span>` :
|
|
name
|
|
),
|
|
])
|
|
);
|
|
};
|
|
};
|
|
};
|
|
|
|
if(this.tempArea.childNodes.length == 0) {
|
|
this.tempArea.append(
|
|
E('em', {}, _('No temperature sensors available'))
|
|
);
|
|
};
|
|
|
|
return this.tempArea;
|
|
},
|
|
|
|
makeViewContent() {
|
|
this.tempView.innerHTML = '';
|
|
this.tempView.append((this.viewType == 'list') ? this.makeTempAreaContent() : this.makeTempTableContent());
|
|
this.hiddenNum.textContent = this.hiddenItems.size;
|
|
let unhide = document.getElementById('temp-status-unhide-all');
|
|
if(unhide) {
|
|
unhide.style.display = (this.hiddenItems.size > 0) ? 'inline-block' : 'none';
|
|
};
|
|
},
|
|
|
|
hideItem(path) {
|
|
this.hiddenItems.add(path);
|
|
this.saveSettingsToLocalStorage();
|
|
this.makeViewContent();
|
|
},
|
|
|
|
unhideAllItems() {
|
|
this.hiddenItems.clear();
|
|
this.saveSettingsToLocalStorage();
|
|
this.makeViewContent();
|
|
},
|
|
|
|
switchView() {
|
|
this.tempView.innerHTML = '';
|
|
this.viewType = (this.viewType == 'list') ? 'table' : 'list'
|
|
this.saveSettingsToLocalStorage();
|
|
this.makeViewContent();
|
|
},
|
|
|
|
load() {
|
|
this.restoreSettingsFromLocalStorage();
|
|
if(this.sensorsData) {
|
|
return (this.sensorsPath.length > 0) ?
|
|
L.resolveDefault(this.callTempData(this.sensorsPath), null) :
|
|
Promise.resolve(null);
|
|
} else {
|
|
return L.resolveDefault(this.callSensors(), null);
|
|
};
|
|
},
|
|
|
|
render(data) {
|
|
if(data) {
|
|
if(!this.sensorsData) {
|
|
this.sensorsData = data.sensors;
|
|
this.sensorsPath = data.temp && new Array(...Object.keys(data.temp));
|
|
};
|
|
this.tempData = data.temp;
|
|
};
|
|
|
|
if(!this.sensorsData || !this.tempData) {
|
|
return;
|
|
};
|
|
|
|
this.makeViewContent();
|
|
|
|
return E('div', { 'class': 'cbi-section' }, [
|
|
E('div', { 'id': 'temp-status-buttons-wrapper' }, [
|
|
E('span', {
|
|
'class': 'temp-status-button',
|
|
'click': () => this.switchView(),
|
|
}, _('Switch view')),
|
|
E('span', {
|
|
'id' : 'temp-status-unhide-all',
|
|
'class': 'temp-status-button',
|
|
'style': `display:${(this.hiddenItems.size > 0) ? 'inline-block' : 'none'}`,
|
|
'click': () => this.unhideAllItems(),
|
|
}, [
|
|
_('Show hidden sensors'),
|
|
' (',
|
|
this.hiddenNum,
|
|
')',
|
|
])
|
|
]),
|
|
this.tempView,
|
|
]);
|
|
},
|
|
});
|