iobroker.metermaster 0.9.2 → 0.9.3

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![MeterMaster Banner](https://github.com/MPunktBPunkt/ioBroker.metermaster/raw/main/github-banner.svg)](https://github.com/MPunktBPunkt/ioBroker.metermaster)
4
4
 
5
- [![Version](https://img.shields.io/badge/version-0.9.1-blue.svg)](https://github.com/MPunktBPunkt/ioBroker.metermaster)
5
+ [![Version](https://img.shields.io/badge/version-0.9.3-blue.svg)](https://github.com/MPunktBPunkt/ioBroker.metermaster)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/MPunktBPunkt/ioBroker.metermaster/blob/main/LICENSE)
7
7
  [![Node.js](https://img.shields.io/badge/node-%3E%3D22-brightgreen.svg)](https://nodejs.org)
8
8
 
@@ -247,6 +247,14 @@ iobroker restart metermaster.0
247
247
 
248
248
  ## Changelog
249
249
 
250
+ ### 0.9.3
251
+ - Fix state roles for ioBroker object structure check (repochecker E1008/E1009/E1011)
252
+ - Migration of existing objects on adapter start
253
+
254
+ ### 0.9.2
255
+ - Adapter checker compliance: npm news cleanup, devDependencies, trusted publishing
256
+ - npm publish via GitHub Actions with provenance
257
+
250
258
  ### 0.9.1
251
259
  - Lowered admin dependency to >=7.6.20 (fixes startup when admin 7.7.x is installed)
252
260
 
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "metermaster",
4
- "version": "0.9.2",
4
+ "version": "0.9.3",
5
5
  "news": {
6
+ "0.9.3": {
7
+ "en": "Fix state roles for ioBroker object structure check (date, text, json, info.firmware)",
8
+ "de": "State-Rollen für ioBroker-Objektstruktur-Prüfung korrigiert (date, text, json, info.firmware)",
9
+ "ru": "Исправлены роли states для проверки структуры объектов ioBroker",
10
+ "pt": "Corrigidas roles de states para verificação da estrutura de objetos ioBroker",
11
+ "nl": "State-rollen gecorrigeerd voor ioBroker objectstructuurcontrole",
12
+ "fr": "Rôles des states corrigés pour la vérification de structure d'objets ioBroker",
13
+ "it": "Corrette le role degli state per il controllo struttura oggetti ioBroker",
14
+ "es": "Corregidos roles de states para verificación de estructura de objetos ioBroker",
15
+ "pl": "Poprawiono role states dla kontroli struktury obiektów ioBroker",
16
+ "uk": "Виправлено ролі states для перевірки структури об'єктів ioBroker",
17
+ "zh-cn": "修复 state 角色以通过 ioBroker 对象结构检查"
18
+ },
6
19
  "0.9.2": {
7
20
  "en": "Adapter checker compliance: npm news cleanup, devDependencies, trusted publishing",
8
21
  "de": "Adapter-Checker-Konformität: npm-News bereinigt, devDependencies, Trusted Publishing",
package/main.js CHANGED
@@ -6,7 +6,7 @@ const crypto = require('node:crypto');
6
6
  const https = require('node:https');
7
7
  const { exec } = require('node:child_process');
8
8
 
9
- const CURRENT_VERSION = '0.9.2';
9
+ const CURRENT_VERSION = '0.9.3';
10
10
  const GITHUB_REPO = 'MPunktBPunkt/ioBroker.metermaster';
11
11
  const GITHUB_URL = 'https://github.com/MPunktBPunkt/ioBroker.metermaster';
12
12
 
@@ -81,6 +81,7 @@ adapter.on('ready', async () => {
81
81
  // In-Memory-Cache aus gespeicherten ioBroker-States wiederherstellen
82
82
  await restoreCacheFromStates();
83
83
  await restoreNodesFromStates();
84
+ await migrateStateRoles();
84
85
 
85
86
  // ESP32-Node-States beobachten (Heartbeat-Erkennung via simple-api)
86
87
  adapter.subscribeStates('nodes.*');
@@ -215,11 +216,11 @@ async function ensureNodeStates(mac) {
215
216
  await ensureChannel(base, `ESP32 Node ${mac}`);
216
217
  await ensureState(`${base}.ip`, { name: 'IP-Adresse', type: 'string', role: 'info.ip', read: true, write: false });
217
218
  await ensureState(`${base}.name`, { name: 'Gerätename', type: 'string', role: 'info.name', read: true, write: false });
218
- await ensureState(`${base}.version`, { name: 'Firmware-Version', type: 'string', role: 'info.version', read: true, write: false });
219
- await ensureState(`${base}.lastSeen`, { name: 'Zuletzt gesehen (ms)', type: 'number', role: 'value.time', read: true, write: false });
220
- await ensureState(`${base}.config`, { name: 'Konfiguration (JSON)', type: 'string', role: 'value', read: true, write: true });
221
- await ensureState(`${base}.configAck`, { name: 'Config-Quittierung', type: 'string', role: 'value', read: true, write: false });
222
- await ensureState(`${base}.cmd`, { name: 'Befehl (JSON)', type: 'string', role: 'value', read: true, write: true });
219
+ await ensureState(`${base}.version`, { name: 'Firmware-Version', type: 'string', role: 'info.firmware', read: true, write: false });
220
+ await ensureState(`${base}.lastSeen`, { name: 'Zuletzt gesehen (ms)', type: 'number', role: 'value.time', read: true, write: false });
221
+ await ensureState(`${base}.config`, { name: 'Konfiguration (JSON)', type: 'string', role: 'json', read: true, write: true });
222
+ await ensureState(`${base}.configAck`, { name: 'Config-Quittierung', type: 'string', role: 'json', read: true, write: false });
223
+ await ensureState(`${base}.cmd`, { name: 'Befehl (JSON)', type: 'string', role: 'json', read: true, write: true });
223
224
  }
224
225
 
225
226
  // ─── HTTP-Server ──────────────────────────────────────────────────────────────
@@ -478,10 +479,10 @@ async function storeReading(data) {
478
479
  if (isNew) log(LVL.INFO, CAT.DATAPOINT, `Neuer Zähler`, `metermaster.0.${base}`);
479
480
 
480
481
  const dpNew = await ensureState(`${base}.readings.latest`, { name: `${data.meter} – Letzter Wert`, type: 'number', role: 'value', unit: data.unit||'', read: true, write: false });
481
- await ensureState(`${base}.readings.latestDate`, { name: 'Ablesedatum', type: 'string', role: 'value.datetime', read: true, write: false });
482
- await ensureState(`${base}.name`, { name: 'Zählername', type: 'string', role: 'info.name', read: true, write: false });
483
- await ensureState(`${base}.unit`, { name: 'Einheit', type: 'string', role: 'value.unit', read: true, write: false });
484
- await ensureState(`${base}.typeName`, { name: 'Zählertyp', type: 'string', role: 'info.type', read: true, write: false });
482
+ await ensureState(`${base}.readings.latestDate`, { name: 'Ablesedatum', type: 'string', role: 'date', read: true, write: false });
483
+ await ensureState(`${base}.name`, { name: 'Zählername', type: 'string', role: 'info.name', read: true, write: false });
484
+ await ensureState(`${base}.unit`, { name: 'Einheit', type: 'string', role: 'text', read: true, write: false });
485
+ await ensureState(`${base}.typeName`, { name: 'Zählertyp', type: 'string', role: 'text', read: true, write: false });
485
486
  await ensureState(`${base}.readings.history`, { name: 'Historische Ablesungen', type: 'array', role: 'list', read: true, write: false });
486
487
 
487
488
  if (dpNew) log(LVL.INFO, CAT.DATAPOINT, `Datenpunkte angelegt`, `${base}.readings.{latest,latestDate,history} + name/unit/typeName`);
@@ -532,20 +533,72 @@ async function ensureChannel(id, name) {
532
533
  return true;
533
534
  }
534
535
  async function ensureState(id, common) {
536
+ const fullCommon = {
537
+ ...common,
538
+ read: common.read !== undefined ? common.read : true,
539
+ write: common.write !== undefined ? common.write : false,
540
+ def: common.type === 'number' ? 0 : (common.type === 'array' ? '[]' : '')
541
+ };
535
542
  const ex = await adapter.getObjectAsync(id).catch(() => null);
536
- if (ex) return false;
543
+ if (ex) {
544
+ const patch = {};
545
+ for (const key of ['role', 'type', 'read', 'write', 'unit']) {
546
+ if (fullCommon[key] !== undefined && ex.common[key] !== fullCommon[key]) {
547
+ patch[key] = fullCommon[key];
548
+ }
549
+ }
550
+ if (Object.keys(patch).length) {
551
+ await adapter.extendObjectAsync(id, { common: patch });
552
+ }
553
+ return false;
554
+ }
537
555
  await adapter.setObjectNotExistsAsync(id, {
538
556
  type: 'state',
539
- common: { ...common,
540
- read: common.read !== undefined ? common.read : true,
541
- write: common.write !== undefined ? common.write : false,
542
- def: common.type === 'number' ? 0 : (common.type === 'array' ? '[]' : '')
543
- },
557
+ common: fullCommon,
544
558
  native: {}
545
559
  });
546
560
  return true;
547
561
  }
548
562
 
563
+ // ─── State-Rollen-Migration (deprecated → ioBroker-Rollenkatalog) ───────────────
564
+ const DEPRECATED_ROLE_MAP = {
565
+ 'value.datetime': 'date',
566
+ 'value.unit': 'text',
567
+ 'info.type': 'text',
568
+ 'info.version': 'info.firmware',
569
+ };
570
+
571
+ function resolveMigratedRole(id, common) {
572
+ if (DEPRECATED_ROLE_MAP[common.role]) {
573
+ return DEPRECATED_ROLE_MAP[common.role];
574
+ }
575
+ if (common.role === 'value' && common.type === 'string') {
576
+ const field = id.split('.').pop();
577
+ if (field === 'config' || field === 'configAck' || field === 'cmd') {
578
+ return 'json';
579
+ }
580
+ }
581
+ return null;
582
+ }
583
+
584
+ async function migrateStateRoles() {
585
+ try {
586
+ const objects = await adapter.getForeignObjectsAsync('*', { type: 'state' });
587
+ if (!objects) return;
588
+ let fixed = 0;
589
+ for (const [id, obj] of Object.entries(objects)) {
590
+ const newRole = resolveMigratedRole(id, obj.common || {});
591
+ if (newRole && obj.common.role !== newRole) {
592
+ await adapter.extendObjectAsync(id, { common: { role: newRole } });
593
+ fixed++;
594
+ }
595
+ }
596
+ if (fixed) log(LVL.INFO, CAT.SYSTEM, 'State-Rollen migriert', `${fixed} Objekte`);
597
+ } catch (e) {
598
+ log(LVL.WARN, CAT.SYSTEM, 'State-Rollen-Migration fehlgeschlagen', e.message);
599
+ }
600
+ }
601
+
549
602
  // ─── API Endpunkte ────────────────────────────────────────────────────────────
550
603
  function serveDataJson(res) {
551
604
  sendJson(res, 200, { data: receivedData, receivedTotal: readingsReceived });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.metermaster",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "ioBroker adapter for the MeterMaster Android app – receives meter readings and manages ESP32 display nodes",
5
5
  "author": {
6
6
  "name": "MPunktBPunkt",