mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +05:00
481 lines
21 KiB
HTML
481 lines
21 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title data-i18n>dashboard.title</title>
|
|
<link rel="stylesheet" href="/static/app.css" />
|
|
</head>
|
|
|
|
<body>
|
|
<header class="container">
|
|
<nav>
|
|
<ul>
|
|
<li><a href="/">
|
|
<div class="logo" data-i18n>logo</div>
|
|
</a></li>
|
|
</ul>
|
|
<ul>
|
|
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
|
<li>
|
|
<select id="lang" aria-label="Lang">
|
|
<option value="en" selected>EN</option>
|
|
<option value="ru">RU</option>
|
|
</select>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="container">
|
|
<article>
|
|
<hgroup>
|
|
<h2 data-i18n>dashboard.name</h2>
|
|
<p></p>
|
|
</hgroup>
|
|
|
|
<div id="dashboard-busy" aria-busy="true"></div>
|
|
<div id="dashboard-container" class="hidden">
|
|
<details open>
|
|
<summary><b data-i18n>dashboard.section.control</b></summary>
|
|
<div class="grid">
|
|
<div class="thermostat" id="thermostat-heating">
|
|
<div class="thermostat-header" data-i18n>dashboard.thermostat.heating</div>
|
|
<div class="thermostat-temp">
|
|
<div class="thermostat-temp-target"><span id="thermostat-heating-target"></span> <span class="temp-unit"></span></div>
|
|
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="thermostat-heating-current"></span> <span class="temp-unit"></span></div>
|
|
</div>
|
|
<div class="thermostat-minus"><button id="thermostat-heating-minus" class="outline"><i class="icons-down"></i></button></div>
|
|
<div class="thermostat-plus"><button id="thermostat-heating-plus" class="outline"><i class="icons-up"></i></button></div>
|
|
<div class="thermostat-control">
|
|
<input type="checkbox" role="switch" id="thermostat-heating-enabled" value="true">
|
|
<label htmlFor="thermostat-heating-enabled" data-i18n>dashboard.thermostat.enable</label>
|
|
|
|
<input type="checkbox" role="switch" id="thermostat-heating-turbo" value="true">
|
|
<label htmlFor="thermostat-heating-turbo" data-i18n>dashboard.thermostat.turbo</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="thermostat" id="thermostat-dhw">
|
|
<div class="thermostat-header" data-i18n>dashboard.thermostat.dhw</div>
|
|
<div class="thermostat-temp">
|
|
<div class="thermostat-temp-target"><span id="thermostat-dhw-target"></span> <span class="temp-unit"></span></div>
|
|
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="thermostat-dhw-current"></span> <span class="temp-unit"></span></div>
|
|
</div>
|
|
<div class="thermostat-minus"><button class="outline" id="thermostat-dhw-minus"><i class="icons-down"></i></button></div>
|
|
<div class="thermostat-plus"><button class="outline" id="thermostat-dhw-plus"><i class="icons-up"></i></button></div>
|
|
<div class="thermostat-control">
|
|
<input type="checkbox" role="switch" id="thermostat-dhw-enabled" value="true">
|
|
<label htmlFor="thermostat-dhw-enabled" data-i18n>dashboard.thermostat.enable</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<hr />
|
|
|
|
<details>
|
|
<summary><b data-i18n>dashboard.section.states</b></summary>
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.ot</th>
|
|
<td><input type="radio" id="ot-connected" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.mqtt</th>
|
|
<td><input type="radio" id="mqtt-connected" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.emergency</th>
|
|
<td><input type="radio" id="ot-emergency" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.heating</th>
|
|
<td><input type="radio" id="ot-heating" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.dhw</th>
|
|
<td><input type="radio" id="ot-dhw" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.flame</th>
|
|
<td><input type="radio" id="ot-flame" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.fault</th>
|
|
<td><input type="radio" id="ot-fault" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.diag</th>
|
|
<td><input type="radio" id="ot-diagnostic" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.extpump</th>
|
|
<td><input type="radio" id="ot-external-pump" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorConnected</th>
|
|
<td><input type="radio" id="outdoor-sensor-connected" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorRssi</th>
|
|
<td><b id="outdoor-sensor-rssi"></b> <span data-i18n>dbm</span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorHumidity</th>
|
|
<td><b id="outdoor-sensor-humidity"></b> %</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorBattery</th>
|
|
<td><b id="outdoor-sensor-battery"></b> %</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.indoorSensorConnected</th>
|
|
<td><input type="radio" id="indoor-sensor-connected" aria-invalid="false" checked disabled /></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.indoorSensorRssi</th>
|
|
<td><b id="indoor-sensor-rssi"></b> <span data-i18n>dbm</span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.indoorSensorHumidity</th>
|
|
<td><b id="indoor-sensor-humidity"></b> %</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.indoorSensorBattery</th>
|
|
<td><b id="indoor-sensor-battery"></b> %</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.modulation</th>
|
|
<td><b id="ot-modulation"></b> %</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.pressure</th>
|
|
<td><b id="ot-pressure"></b> <span class="pressure-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.dhwFlowRate</th>
|
|
<td><b id="ot-dhw-flow-rate"></b> <span class="volume-unit"></span>/min</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.faultCode</th>
|
|
<td><b id="ot-fault-code"></b></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.diagCode</th>
|
|
<td><b id="ot-diag-code"></b></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.indoorTemp</th>
|
|
<td><b id="indoor-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.outdoorTemp</th>
|
|
<td><b id="outdoor-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.heatingTemp</th>
|
|
<td><b id="heating-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.heatingSetpointTemp</th>
|
|
<td><b id="heating-setpoint-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.heatingReturnTemp</th>
|
|
<td><b id="heating-return-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.dhwTemp</th>
|
|
<td><b id="dhw-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row" data-i18n>dashboard.state.exhaustTemp</th>
|
|
<td><b id="exhaust-temp"></b> <span class="temp-unit"></span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</details>
|
|
|
|
<hr />
|
|
|
|
<details>
|
|
<summary><b data-i18n>dashboard.section.otDiag</b></summary>
|
|
<pre><b>Vendor:</b> <span id="slave-vendor"></span>
|
|
<b>Member ID:</b> <span id="slave-member-id"></span>
|
|
<b>Flags:</b> <span id="slave-flags"></span>
|
|
<b>Type:</b> <span id="slave-type"></span>
|
|
<b>Version:</b> <span id="slave-version"></span>
|
|
<b>OT version:</b> <span id="slave-ot-version"></span>
|
|
<b>Heating limits:</b> <span id="heating-min-temp"></span>...<span id="heating-max-temp"></span> <span class="temp-unit"></span>
|
|
<b>DHW limits:</b> <span id="dhw-min-temp"></span>...<span id="dhw-max-temp"></span> <span class="temp-unit"></span></pre>
|
|
</details>
|
|
</div>
|
|
</article>
|
|
</main>
|
|
|
|
<footer class="container">
|
|
<small>
|
|
<b>Made by Laxilef</b>
|
|
• <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary" data-i18n>nav.license</a>
|
|
• <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary" data-i18n>nav.source</a>
|
|
• <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary" data-i18n>nav.help</a>
|
|
• <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary" data-i18n>nav.issues</a>
|
|
• <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary" data-i18n>nav.releases</a>
|
|
</small>
|
|
</footer>
|
|
|
|
<script src="/static/app.js"></script>
|
|
<script>
|
|
let modifiedTime = null;
|
|
let noRegulators;
|
|
let prevSettings;
|
|
let newSettings = {
|
|
heating: {
|
|
enable: false,
|
|
turbo: false,
|
|
target: 0
|
|
},
|
|
dhw: {
|
|
enable: false,
|
|
target: 0
|
|
}
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
const lang = new Lang(document.getElementById('lang'));
|
|
lang.build();
|
|
|
|
document.querySelector('#thermostat-heating-minus').addEventListener('click', (event) => {
|
|
if (!prevSettings) {
|
|
return;
|
|
}
|
|
|
|
newSettings.heating.target -= 0.5;
|
|
modifiedTime = Date.now();
|
|
|
|
let minTemp;
|
|
if (noRegulators) {
|
|
minTemp = prevSettings.heating.minTemp;
|
|
} else {
|
|
minTemp = prevSettings.system.unitSystem == 0 ? 5 : 41;
|
|
}
|
|
|
|
if (prevSettings && newSettings.heating.target < minTemp) {
|
|
newSettings.heating.target = minTemp;
|
|
}
|
|
|
|
setValue('#thermostat-heating-target', newSettings.heating.target);
|
|
});
|
|
|
|
document.querySelector('#thermostat-heating-plus').addEventListener('click', (event) => {
|
|
if (!prevSettings) {
|
|
return;
|
|
}
|
|
|
|
newSettings.heating.target += 0.5;
|
|
modifiedTime = Date.now();
|
|
|
|
let maxTemp;
|
|
if (noRegulators) {
|
|
maxTemp = prevSettings.heating.maxTemp;
|
|
} else {
|
|
maxTemp = prevSettings.system.unitSystem == 0 ? 30 : 86;
|
|
}
|
|
|
|
if (prevSettings && newSettings.heating.target > maxTemp) {
|
|
newSettings.heating.target = maxTemp;
|
|
}
|
|
|
|
setValue('#thermostat-heating-target', newSettings.heating.target);
|
|
});
|
|
|
|
document.querySelector('#thermostat-dhw-minus').addEventListener('click', (event) => {
|
|
if (!prevSettings) {
|
|
return;
|
|
}
|
|
|
|
newSettings.dhw.target -= 1.0;
|
|
modifiedTime = Date.now();
|
|
|
|
if (newSettings.dhw.target < prevSettings.dhw.minTemp) {
|
|
newSettings.dhw.target = prevSettings.dhw.minTemp;
|
|
}
|
|
|
|
setValue('#thermostat-dhw-target', newSettings.dhw.target);
|
|
});
|
|
|
|
document.querySelector('#thermostat-dhw-plus').addEventListener('click', (event) => {
|
|
if (!prevSettings) {
|
|
return;
|
|
}
|
|
|
|
newSettings.dhw.target += 1.0;
|
|
modifiedTime = Date.now();
|
|
|
|
if (newSettings.dhw.target > prevSettings.dhw.maxTemp) {
|
|
newSettings.dhw.target = prevSettings.dhw.maxTemp;
|
|
}
|
|
|
|
setValue('#thermostat-dhw-target', newSettings.dhw.target);
|
|
});
|
|
|
|
document.querySelector('#thermostat-heating-enabled').addEventListener('change', (event) => {
|
|
modifiedTime = Date.now();
|
|
newSettings.heating.enable = event.currentTarget.checked;
|
|
});
|
|
|
|
document.querySelector('#thermostat-heating-turbo').addEventListener('change', (event) => {
|
|
modifiedTime = Date.now();
|
|
newSettings.heating.turbo = event.currentTarget.checked;
|
|
});
|
|
|
|
document.querySelector('#thermostat-dhw-enabled').addEventListener('change', (event) => {
|
|
modifiedTime = Date.now();
|
|
newSettings.dhw.enable = event.currentTarget.checked;
|
|
});
|
|
|
|
setTimeout(async function onLoadPage() {
|
|
if (modifiedTime) {
|
|
if ((Date.now() - modifiedTime) < 5000) {
|
|
setTimeout(onLoadPage, 1000);
|
|
return;
|
|
}
|
|
|
|
modifiedTime = null;
|
|
}
|
|
|
|
// settings
|
|
try {
|
|
let modified = prevSettings && (
|
|
(prevSettings.heating.enable != newSettings.heating.enable)
|
|
|| (prevSettings.heating.turbo != newSettings.heating.turbo)
|
|
|| (prevSettings.heating.target != newSettings.heating.target)
|
|
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.enable != newSettings.dhw.enable)
|
|
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.target != newSettings.dhw.target)
|
|
);
|
|
|
|
if (modified) {
|
|
console.log(newSettings);
|
|
}
|
|
|
|
let parameters = { cache: 'no-cache' };
|
|
if (modified) {
|
|
parameters.method = "POST";
|
|
parameters.body = JSON.stringify(newSettings);
|
|
}
|
|
|
|
const response = await fetch('/api/settings', parameters);
|
|
if (!response.ok) {
|
|
throw new Error('Response not valid');
|
|
}
|
|
|
|
const result = await response.json();
|
|
noRegulators = !result.opentherm.nativeHeatingControl && !result.equitherm.enable && !result.pid.enable;
|
|
prevSettings = result;
|
|
newSettings.heating.enable = result.heating.enable;
|
|
newSettings.heating.turbo = result.heating.turbo;
|
|
newSettings.heating.target = result.heating.target;
|
|
newSettings.dhw.enable = result.dhw.enable;
|
|
newSettings.dhw.target = result.dhw.target;
|
|
|
|
if (result.opentherm.dhwPresent) {
|
|
show('#thermostat-dhw');
|
|
} else {
|
|
hide('#thermostat-dhw');
|
|
}
|
|
|
|
setCheckboxValue('#thermostat-heating-enabled', result.heating.enable);
|
|
setCheckboxValue('#thermostat-heating-turbo', result.heating.turbo);
|
|
setValue('#thermostat-heating-target', result.heating.target);
|
|
|
|
setCheckboxValue('#thermostat-dhw-enabled', result.dhw.enable);
|
|
setValue('#thermostat-dhw-target', result.dhw.target);
|
|
|
|
setValue('.temp-unit', temperatureUnit(result.system.unitSystem));
|
|
setValue('.pressure-unit', pressureUnit(result.system.unitSystem));
|
|
setValue('.volume-unit', volumeUnit(result.system.unitSystem));
|
|
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
|
|
// vars
|
|
try {
|
|
const response = await fetch('/api/vars', { cache: 'no-cache' });
|
|
if (!response.ok) {
|
|
throw new Error('Response not valid');
|
|
}
|
|
|
|
const result = await response.json();
|
|
setValue('#thermostat-heating-current', noRegulators ? result.temperatures.heating : result.temperatures.indoor);
|
|
setValue('#thermostat-dhw-current', result.temperatures.dhw);
|
|
|
|
setState('#ot-connected', result.states.otStatus);
|
|
setState('#mqtt-connected', result.states.mqtt);
|
|
setState('#ot-emergency', result.states.emergency);
|
|
setState('#ot-heating', result.states.heating);
|
|
setState('#ot-dhw', result.states.dhw);
|
|
setState('#ot-flame', result.states.flame);
|
|
setState('#ot-fault', result.states.fault);
|
|
setState('#ot-diagnostic', result.states.diagnostic);
|
|
setState('#ot-external-pump', result.states.externalPump);
|
|
setState('#outdoor-sensor-connected', result.sensors.outdoor.connected);
|
|
setState('#indoor-sensor-connected', result.sensors.indoor.connected);
|
|
|
|
setValue('#outdoor-sensor-rssi', result.sensors.outdoor.rssi);
|
|
setValue('#outdoor-sensor-humidity', result.sensors.outdoor.humidity);
|
|
setValue('#outdoor-sensor-battery', result.sensors.outdoor.battery);
|
|
setValue('#indoor-sensor-rssi', result.sensors.indoor.rssi);
|
|
setValue('#indoor-sensor-humidity', result.sensors.indoor.humidity);
|
|
setValue('#indoor-sensor-battery', result.sensors.indoor.battery);
|
|
|
|
setValue('#ot-modulation', result.sensors.modulation);
|
|
setValue('#ot-pressure', result.sensors.pressure);
|
|
setValue('#ot-dhw-flow-rate', result.sensors.dhwFlowRate);
|
|
setValue(
|
|
'#ot-fault-code',
|
|
result.sensors.faultCode
|
|
? (result.sensors.faultCode + " (0x" + dec2hex(result.sensors.faultCode) + ")")
|
|
: "-"
|
|
);
|
|
setValue(
|
|
'#ot-diag-code',
|
|
result.sensors.diagnosticCode
|
|
? (result.sensors.diagnosticCode + " (0x" + dec2hex(result.sensors.diagnosticCode) + ")")
|
|
: "-"
|
|
);
|
|
|
|
setValue('#indoor-temp', result.temperatures.indoor);
|
|
setValue('#outdoor-temp', result.temperatures.outdoor);
|
|
setValue('#heating-temp', result.temperatures.heating);
|
|
setValue('#heating-return-temp', result.temperatures.heatingReturn);
|
|
setValue('#dhw-temp', result.temperatures.dhw);
|
|
setValue('#exhaust-temp', result.temperatures.exhaust);
|
|
|
|
setValue('#heating-min-temp', result.parameters.heatingMinTemp);
|
|
setValue('#heating-max-temp', result.parameters.heatingMaxTemp);
|
|
setValue('#heating-setpoint-temp', result.parameters.heatingSetpoint);
|
|
setValue('#dhw-min-temp', result.parameters.dhwMinTemp);
|
|
setValue('#dhw-max-temp', result.parameters.dhwMaxTemp);
|
|
|
|
setValue('#slave-member-id', result.parameters.slaveMemberId);
|
|
setValue('#slave-vendor', memberIdToVendor(result.parameters.slaveMemberId));
|
|
|
|
setValue('#slave-flags', result.parameters.slaveFlags);
|
|
setValue('#slave-type', result.parameters.slaveType);
|
|
setValue('#slave-version', result.parameters.slaveVersion);
|
|
setValue('#slave-ot-version', result.parameters.slaveOtVersion);
|
|
setBusy('#dashboard-busy', '#dashboard-container', false);
|
|
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
|
|
setTimeout(onLoadPage, 10000);
|
|
}, 1000);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |