b635fdb981
change copilot to mqtt Signed-off-by: Peter Siegmund <developer@mars3142.org>
176 lines
4.7 KiB
JavaScript
176 lines
4.7 KiB
JavaScript
class CopilotAction extends ActionBase {
|
|
constructor($UD, context) {
|
|
super($UD, context);
|
|
this.config = { uri: '', username: '', password: '', topic: '' };
|
|
this.mqttClient = null;
|
|
this.value = null;
|
|
this.isError = false;
|
|
|
|
this.$UD.onDidReceiveSettings(jsn => {
|
|
if (jsn.context !== this.context) return;
|
|
const s = jsn.settings || {};
|
|
const prevKey = this.connectionKey();
|
|
this.config = Object.assign(this.config, s);
|
|
if (this.connectionKey() !== prevKey) {
|
|
this.connectMqtt();
|
|
}
|
|
});
|
|
}
|
|
|
|
connectionKey() {
|
|
return `${this.config.uri}|${this.config.username}|${this.config.topic}`;
|
|
}
|
|
|
|
setActive() {}
|
|
|
|
setParams(jsn) {
|
|
this.config = Object.assign(this.config, (jsn && jsn.param) || {});
|
|
this.$UD.getSettings(this.context);
|
|
this.connectMqtt();
|
|
}
|
|
|
|
onRun() {
|
|
if (!this.mqttClient || !this.mqttClient.connected) {
|
|
this.connectMqtt();
|
|
}
|
|
}
|
|
|
|
onClear() {
|
|
super.onClear();
|
|
this.disconnectMqtt();
|
|
}
|
|
|
|
disconnectMqtt() {
|
|
if (this.mqttClient) {
|
|
this.mqttClient.end(true);
|
|
this.mqttClient = null;
|
|
}
|
|
}
|
|
|
|
connectMqtt() {
|
|
this.disconnectMqtt();
|
|
if (!this.config.uri || !this.config.topic) {
|
|
this.renderGauge(null, false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const opts = {
|
|
reconnectPeriod: 3000,
|
|
rejectUnauthorized: false,
|
|
};
|
|
if (this.config.username) opts.username = this.config.username;
|
|
if (this.config.password) opts.password = this.config.password;
|
|
|
|
this.mqttClient = mqtt.connect(this.config.uri, opts);
|
|
|
|
this.mqttClient.on('connect', () => {
|
|
this.isError = false;
|
|
this.mqttClient.subscribe(this.config.topic);
|
|
this.renderGauge(this.value, false);
|
|
});
|
|
|
|
this.mqttClient.on('message', (topic, payload) => {
|
|
try {
|
|
const msg = JSON.parse(payload.toString());
|
|
if (msg.usage != null) {
|
|
this.value = parseFloat(msg.usage);
|
|
this.isError = false;
|
|
} else {
|
|
this.isError = true;
|
|
}
|
|
this.renderGauge(this.value, this.isError);
|
|
} catch (e) {
|
|
this.renderGauge(null, true);
|
|
}
|
|
});
|
|
|
|
this.mqttClient.on('reconnect', () => {
|
|
this.renderGauge(this.value, false);
|
|
});
|
|
|
|
this.mqttClient.on('error', () => {
|
|
this.isError = true;
|
|
this.renderGauge(null, true);
|
|
});
|
|
|
|
this.renderGauge(this.value, false);
|
|
} catch (e) {
|
|
this.mqttClient = null;
|
|
this.renderGauge(null, true);
|
|
}
|
|
}
|
|
|
|
renderGauge(value, isError) {
|
|
const { canvas, ctx } = this.createCanvas();
|
|
|
|
const cx = 98, cy = 100;
|
|
const r = 68;
|
|
const lineWidth = 14;
|
|
|
|
const startAngle = 135 * Math.PI / 180;
|
|
const totalSweep = 270 * Math.PI / 180;
|
|
const greenEnd = startAngle + 0.50 * totalSweep;
|
|
const yellowEnd = startAngle + 0.80 * totalSweep;
|
|
const endAngle = startAngle + totalSweep;
|
|
|
|
// Background track
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, r, startAngle, endAngle, false);
|
|
ctx.strokeStyle = '#333333';
|
|
ctx.lineWidth = lineWidth;
|
|
ctx.lineCap = 'round';
|
|
ctx.stroke();
|
|
|
|
const connected = this.mqttClient && this.mqttClient.connected;
|
|
|
|
if (value !== null && !isError && connected) {
|
|
const pct = Math.max(0, Math.min(100, value)) / 100;
|
|
const valueAngle = startAngle + pct * totalSweep;
|
|
|
|
if (pct > 0) {
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, r, startAngle, Math.min(valueAngle, greenEnd), false);
|
|
ctx.strokeStyle = '#4caf50';
|
|
ctx.lineWidth = lineWidth;
|
|
ctx.lineCap = 'round';
|
|
ctx.stroke();
|
|
}
|
|
if (pct > 0.5) {
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, r, greenEnd, Math.min(valueAngle, yellowEnd), false);
|
|
ctx.strokeStyle = '#ffeb3b';
|
|
ctx.lineWidth = lineWidth;
|
|
ctx.lineCap = 'butt';
|
|
ctx.stroke();
|
|
}
|
|
if (pct > 0.8) {
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, r, yellowEnd, valueAngle, false);
|
|
ctx.strokeStyle = '#f44336';
|
|
ctx.lineWidth = lineWidth;
|
|
ctx.lineCap = 'butt';
|
|
ctx.stroke();
|
|
}
|
|
|
|
const color = pct <= 0.5 ? '#4caf50' : pct <= 0.8 ? '#ffeb3b' : '#f44336';
|
|
const displayText = value.toFixed(1) + '%';
|
|
const fSize = displayText.length > 5 ? 28 : 34;
|
|
ctx.fillStyle = color;
|
|
ctx.font = `bold ${fSize}px "Source Han Sans SC"`;
|
|
ctx.fillText(displayText, cx, cy);
|
|
} else {
|
|
ctx.fillStyle = isError ? '#ff6b6b' : '#666666';
|
|
ctx.font = 'bold 28px "Source Han Sans SC"';
|
|
ctx.fillText(isError ? 'ERR' : 'N/A', cx, cy);
|
|
}
|
|
|
|
// Label — bottom
|
|
ctx.fillStyle = '#7ec8e3';
|
|
ctx.font = 'bold 20px "Source Han Sans SC"';
|
|
ctx.fillText('Copilot', cx, 165);
|
|
|
|
this.setIcon(canvas);
|
|
}
|
|
}
|