iobroker.metermaster 0.9.6 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/io-package.json +14 -1
- package/main.js +90 -13
- package/package.json +1 -1
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "metermaster",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.7",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.9.7": {
|
|
7
|
+
"en": "Print fix (Blob URL); ESP32 discover proxy (getStates/node-discover); node heartbeat/ack logs moved to debug",
|
|
8
|
+
"de": "Druck-Fix (Blob-URL); ESP32-Discover-Proxy (getStates/node-discover); Node-Heartbeat/Ack-Logs auf Debug-Level",
|
|
9
|
+
"ru": "Print fix; ESP32 discover proxy; node logs debug",
|
|
10
|
+
"pt": "Print fix; ESP32 discover proxy; node logs debug",
|
|
11
|
+
"nl": "Print fix; ESP32 discover proxy; node logs debug",
|
|
12
|
+
"fr": "Print fix; ESP32 discover proxy; node logs debug",
|
|
13
|
+
"it": "Print fix; ESP32 discover proxy; node logs debug",
|
|
14
|
+
"es": "Print fix; ESP32 discover proxy; node logs debug",
|
|
15
|
+
"pl": "Print fix; ESP32 discover proxy; node logs debug",
|
|
16
|
+
"uk": "Print fix; ESP32 discover proxy; node logs debug",
|
|
17
|
+
"zh-cn": "打印修复;ESP32 discover 代理;节点日志改为 debug"
|
|
18
|
+
},
|
|
6
19
|
"0.9.6": {
|
|
7
20
|
"en": "Assign display nodes via chips on meter cards in the Data tab",
|
|
8
21
|
"de": "Anzeige-Nodes per Chips auf den Zähler-Kacheln im Daten-Tab zuweisen",
|
package/main.js
CHANGED
|
@@ -4,7 +4,7 @@ const utils = require('@iobroker/adapter-core');
|
|
|
4
4
|
const http = require('node:http');
|
|
5
5
|
const crypto = require('node:crypto');
|
|
6
6
|
const https = require('node:https');
|
|
7
|
-
const CURRENT_VERSION = '0.9.
|
|
7
|
+
const CURRENT_VERSION = '0.9.7';
|
|
8
8
|
const GITHUB_REPO = 'MPunktBPunkt/ioBroker.metermaster';
|
|
9
9
|
const GITHUB_URL = 'https://github.com/MPunktBPunkt/ioBroker.metermaster';
|
|
10
10
|
|
|
@@ -142,7 +142,7 @@ adapter.on('stateChange', async (id, state) => {
|
|
|
142
142
|
|
|
143
143
|
if (field === 'lastSeen') {
|
|
144
144
|
const n = nodesCache[mac];
|
|
145
|
-
log(LVL.
|
|
145
|
+
log(LVL.DEBUG, CAT.NODE, `Heartbeat`, `${mac} | IP: ${n.ip || '?'} | v${n.version || '?'} | ${n.name || 'unnamed'}`);
|
|
146
146
|
await ensureNodeStates(mac);
|
|
147
147
|
}
|
|
148
148
|
});
|
|
@@ -271,6 +271,14 @@ function startHttpServer() {
|
|
|
271
271
|
if (req.method === 'GET' && url === '/api/version') { serveVersion(res); return; }
|
|
272
272
|
if (req.method === 'GET' && url === '/api/nodes') { serveNodesJson(res); return; }
|
|
273
273
|
if (req.method === 'GET' && url === '/api/discover'){ serveDiscoverJson(res); return; }
|
|
274
|
+
if (req.method === 'GET' && url === '/api/node-discover'){ serveNodeDiscoverJson(res); return; }
|
|
275
|
+
if (req.method === 'GET' && (url === '/getStates/metermaster.*' || url === '/getStates/metermaster.0.*.readings.latest')) {
|
|
276
|
+
serveGetStatesLatest(res); return;
|
|
277
|
+
}
|
|
278
|
+
const getStateMatch = req.method === 'GET' ? url.match(/^\/get\/(.+)$/) : null;
|
|
279
|
+
if (getStateMatch) {
|
|
280
|
+
serveGetState(decodeURIComponent(getStateMatch[1]), res); return;
|
|
281
|
+
}
|
|
274
282
|
|
|
275
283
|
// ESP32 node registration (no auth – ESP32 has no credentials)
|
|
276
284
|
if (req.method === 'POST' && url === '/api/register') {
|
|
@@ -713,6 +721,73 @@ function serveDiscoverJson(res) {
|
|
|
713
721
|
sendJson(res, 200, result);
|
|
714
722
|
}
|
|
715
723
|
|
|
724
|
+
// ─── simple-api-kompatibel: nur readings.latest (für ESP32-Nodes mit kleinem JSON-Puffer) ─
|
|
725
|
+
function serveGetStatesLatest(res) {
|
|
726
|
+
const result = {};
|
|
727
|
+
const ns = adapter.namespace;
|
|
728
|
+
const from = `system.adapter.${adapter.name}.${adapter.instance}`;
|
|
729
|
+
const now = Date.now();
|
|
730
|
+
for (const [house, apts] of Object.entries(receivedData)) {
|
|
731
|
+
for (const [apt, meters] of Object.entries(apts)) {
|
|
732
|
+
for (const [meter, data] of Object.entries(meters)) {
|
|
733
|
+
const ts = data.latestDate ? new Date(data.latestDate).getTime() : now;
|
|
734
|
+
result[`${ns}.${house}.${apt}.${meter}.readings.latest`] = {
|
|
735
|
+
val: data.latest,
|
|
736
|
+
ack: true,
|
|
737
|
+
ts,
|
|
738
|
+
q: 0,
|
|
739
|
+
from,
|
|
740
|
+
lc: ts,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
sendJson(res, 200, result);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
async function serveGetState(stateId, res) {
|
|
749
|
+
try {
|
|
750
|
+
const state = await adapter.getStateAsync(stateId);
|
|
751
|
+
if (!state || state.val === null || state.val === undefined) {
|
|
752
|
+
sendJson(res, 404, { error: 'State not found' });
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
sendJson(res, 200, {
|
|
756
|
+
val: state.val,
|
|
757
|
+
ack: !!state.ack,
|
|
758
|
+
ts: state.ts,
|
|
759
|
+
q: state.q || 0,
|
|
760
|
+
from: state.from || '',
|
|
761
|
+
lc: state.lc || state.ts,
|
|
762
|
+
});
|
|
763
|
+
} catch (e) {
|
|
764
|
+
sendJson(res, 500, { error: e.message });
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// ─── ESP32-WebUI: kompaktes Discover-Format {"ok":true,"states":[...]} ───────
|
|
769
|
+
function serveNodeDiscoverJson(res) {
|
|
770
|
+
const states = [];
|
|
771
|
+
const ns = adapter.namespace;
|
|
772
|
+
for (const [house, apts] of Object.entries(receivedData)) {
|
|
773
|
+
for (const [apt, meters] of Object.entries(apts)) {
|
|
774
|
+
for (const [meter, data] of Object.entries(meters)) {
|
|
775
|
+
const sid = `${ns}.${house}.${apt}.${meter}.readings.latest`;
|
|
776
|
+
let lbl = house;
|
|
777
|
+
if (apt) lbl += ' \u00B7 ' + apt;
|
|
778
|
+
if (meter) lbl += ' \u00B7 ' + meter;
|
|
779
|
+
states.push({
|
|
780
|
+
id: sid,
|
|
781
|
+
val: data.latest,
|
|
782
|
+
label: lbl,
|
|
783
|
+
unit: data.unit || '',
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
sendJson(res, 200, { ok: true, states });
|
|
789
|
+
}
|
|
790
|
+
|
|
716
791
|
// ─── Node Config schreiben ────────────────────────────────────────────────────
|
|
717
792
|
async function handleNodeConfig(mac, body, res, clientIp) {
|
|
718
793
|
let data;
|
|
@@ -801,7 +876,7 @@ async function handleNodeRegister(body, res, clientIp) {
|
|
|
801
876
|
await adapter.setStateAsync(`nodes.${mac}.lastSeen`, { val: ts, ack: true });
|
|
802
877
|
if (ack) await adapter.setStateAsync(`nodes.${mac}.configAck`, { val: ack, ack: true });
|
|
803
878
|
|
|
804
|
-
log(LVL.
|
|
879
|
+
log(LVL.DEBUG, CAT.NODE, `Heartbeat`, `${mac} | IP: ${ip} | v${version} | ${name}`);
|
|
805
880
|
|
|
806
881
|
// Aktuelle Config zurückgeben (null wenn noch keine gesetzt)
|
|
807
882
|
const config = nodesCache[mac].config || null;
|
|
@@ -825,7 +900,7 @@ async function handleNodeAck(mac, body, res) {
|
|
|
825
900
|
try {
|
|
826
901
|
await ensureNodeStates(mac);
|
|
827
902
|
await adapter.setStateAsync(`nodes.${mac}.configAck`, { val: ack, ack: true });
|
|
828
|
-
log(LVL.
|
|
903
|
+
log(LVL.DEBUG, CAT.NODE, `Config acknowledged`, `${mac} | ack: ${ack}`);
|
|
829
904
|
sendJson(res, 200, { ok: true });
|
|
830
905
|
} catch (e) {
|
|
831
906
|
sendJson(res, 500, { error: e.message });
|
|
@@ -2149,13 +2224,8 @@ function closeChartModal() {
|
|
|
2149
2224
|
}
|
|
2150
2225
|
|
|
2151
2226
|
function openPrintWindow(title, bodyHtml) {
|
|
2152
|
-
const w = window.open('', '_blank', 'noopener,noreferrer');
|
|
2153
|
-
if (!w) {
|
|
2154
|
-
window.alert(t('error'));
|
|
2155
|
-
return;
|
|
2156
|
-
}
|
|
2157
2227
|
const generated = new Date().toLocaleString(localeTag(), { hour12: false });
|
|
2158
|
-
|
|
2228
|
+
const html =
|
|
2159
2229
|
'<!DOCTYPE html><html><head><meta charset="utf-8"><title>'+esc(title)+'</title>'+
|
|
2160
2230
|
'<style>'+
|
|
2161
2231
|
'body{font-family:Segoe UI,Helvetica,Arial,sans-serif;color:#111;margin:24px;}'+
|
|
@@ -2172,9 +2242,16 @@ function openPrintWindow(title, bodyHtml) {
|
|
|
2172
2242
|
'<div class="meta">'+esc(t('print_generated'))+': '+esc(generated)+'</div>'+
|
|
2173
2243
|
bodyHtml+
|
|
2174
2244
|
'<script>window.onload=function(){window.focus();window.print();}<\\/script>'+
|
|
2175
|
-
'</body></html>'
|
|
2176
|
-
);
|
|
2177
|
-
|
|
2245
|
+
'</body></html>';
|
|
2246
|
+
const blob = new Blob([html], { type: 'text/html;charset=utf-8' });
|
|
2247
|
+
const url = URL.createObjectURL(blob);
|
|
2248
|
+
const w = window.open(url, '_blank');
|
|
2249
|
+
if (!w) {
|
|
2250
|
+
URL.revokeObjectURL(url);
|
|
2251
|
+
window.alert(t('error'));
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
|
2178
2255
|
}
|
|
2179
2256
|
|
|
2180
2257
|
window.printChartView = function printChartView() {
|
package/package.json
CHANGED