Files
PlatformIO_Esp32C3_BedCooler/data/www/components/program-card.js
2026-06-28 16:25:55 -05:00

105 lines
4.0 KiB
JavaScript

class ProgramCard extends HTMLElement {
constructor() {
super();
this.source = null;
}
connectedCallback() {
this.innerHTML = `
<style>
:host{display:block}section{height:100%;box-sizing:border-box}.radioRow{display:flex;gap:10px;flex-wrap:wrap;margin:8px 0}.radioRow label{display:inline-flex;gap:5px;align-items:center;margin:0}.radioRow input{width:auto}.row{display:flex;gap:8px;align-items:end}.row label{flex:1}.status{display:flex;gap:6px;flex-wrap:wrap;margin:8px 0}.badge{display:inline-flex;align-items:center;background:#eef3f6;border-radius:6px;padding:4px 7px;color:#263238;font-size:12px}
</style>
<section>
<h3>Program</h3>
<div class="row"><label>Target Temperature<input id="target" type="number" min="-40" max="85" step="0.01" value="22.00"></label><button id="save">Set</button></div>
<label>Mode</label>
<div class="radioRow">
<label><input type="radio" name="programMode" value="off"> Off</label>
<label><input type="radio" name="programMode" value="cool"> Cool</label>
<label><input type="radio" name="programMode" value="warm"> Warm</label>
</div>
<div class="status"><span id="temp" class="badge">--</span><span id="pump" class="badge">Pump --</span></div>
<pre id="out"></pre>
</section>`;
this.querySelector('#save').addEventListener('click', () => this.saveProgram());
this.querySelectorAll('input[name="programMode"]').forEach(input => input.addEventListener('change', () => this.saveProgram()));
window.addEventListener('app:login', event => this.startEvents(event.detail.token));
if (window.App && window.App.token()) this.startEvents(window.App.token());
}
disconnectedCallback() {
this.stopEvents();
}
startEvents(token) {
this.stopEvents();
this.loadProgram();
if (!token || !window.EventSource) return;
this.source = new EventSource('/api/temperature/events?token=' + encodeURIComponent(token));
this.source.addEventListener('temperature', event => {
try {
this.renderLiveTemperature(JSON.parse(event.data));
} catch (error) {}
});
this.source.addEventListener('pump', event => {
try {
this.renderPump(JSON.parse(event.data));
} catch (error) {}
});
}
stopEvents() {
if (this.source) this.source.close();
this.source = null;
}
selectedMode() {
const input = this.querySelector('input[name="programMode"]:checked');
return input ? input.value : 'off';
}
renderSettings(json) {
const mode = json.mode || 'off';
const modeInput = this.querySelector('input[name="programMode"][value="' + mode + '"]');
if (modeInput) modeInput.checked = true;
if (json.targetTemperatureC !== undefined) this.querySelector('#target').value = Number(json.targetTemperatureC).toFixed(2);
}
renderLiveTemperature(json) {
this.querySelector('#temp').textContent = json.sensorConnected && json.temperatureC !== null ? Number(json.temperatureC).toFixed(2) + ' C' : 'Sensor --';
}
renderPump(json) {
const enabled = json.enabled !== undefined ? json.enabled : json.pumpEnabled;
if (enabled === undefined) return;
this.querySelector('#pump').textContent = enabled ? 'Pump on' : 'Pump off';
}
render(json) {
this.renderSettings(json);
this.renderLiveTemperature(json);
this.renderPump(json);
}
async loadProgram() {
const text = await window.App.req('GET', '/api/program');
this.querySelector('#out').textContent = text;
try {
const json = JSON.parse(text);
if (json.success) this.render(json);
} catch (error) {}
}
async saveProgram() {
const target = Number(this.querySelector('#target').value);
const text = await window.App.req('POST', '/api/program', {mode: this.selectedMode(), targetTemperatureC: Number(target.toFixed(2))});
this.querySelector('#out').textContent = text;
try {
const json = JSON.parse(text);
if (json.success) this.render(json);
} catch (error) {}
}
}
customElements.define('program-card', ProgramCard);