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.
Files changed (3) hide show
  1. package/io-package.json +14 -1
  2. package/main.js +90 -13
  3. 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.6",
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.6';
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.INFO, CAT.NODE, `Heartbeat`, `${mac} | IP: ${n.ip || '?'} | v${n.version || '?'} | ${n.name || 'unnamed'}`);
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.INFO, CAT.NODE, `Heartbeat`, `${mac} | IP: ${ip} | v${version} | ${name}`);
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.INFO, CAT.NODE, `Config acknowledged`, `${mac} | ack: ${ack}`);
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
- w.document.write(
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
- w.document.close();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.metermaster",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "description": "ioBroker adapter for the MeterMaster Android app \u2013 receives meter readings and manages ESP32 display nodes",
5
5
  "author": {
6
6
  "name": "MPunktBPunkt",