iobroker.e3oncan 1.0.0 → 1.0.2
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.de.md +6 -47
- package/README.md +9 -2
- package/admin/tab.html +58 -5
- package/io-package.json +27 -27
- package/lib/protocols.md +527 -154
- package/main.js +24 -1
- package/package.json +1 -1
package/README.de.md
CHANGED
|
@@ -210,11 +210,11 @@ Die **e3oncan Datenpunkte**-Seite ist die zentrale Stelle zum Durchsuchen von Da
|
|
|
210
210
|
|
|
211
211
|
Alle Geräte und erkannte Energiezähler werden als aufklappbare Karten angezeigt, standardmäßig zugeklappt für einen schnellen Überblick über das gesamte System. Ein Klick auf den Karten-Header klappt die Karte auf. Das Suchfeld filtert nach Name oder ID, passende Karten werden automatisch ausgeklappt.
|
|
212
212
|
|
|
213
|
-
Wenn für ein Gerät noch kein Datenpunktscan durchgeführt wurde, erscheint oben auf der Seite ein Warnbanner als Erinnerung.
|
|
213
|
+
Wenn für ein Gerät noch kein Datenpunktscan durchgeführt wurde, erscheint oben auf der Seite ein Warnbanner als Erinnerung. Wenn ein Scan vorhanden ist, die in v1.x eingeführte Collect-Auto-Erkennung aber noch nicht ausgeführt wurde, empfiehlt ein Info-Banner einen erneuten Scan. Dieser Hinweis kann per Instanz dauerhaft ausgeblendet werden (Schaltfläche **Nicht mehr anzeigen**).
|
|
214
214
|
|
|
215
215
|
**Gerätekarten**
|
|
216
216
|
|
|
217
|
-
Jede Gerätekarte listet ihre Datenpunkte mit ID, Name, Codec und Zeitplaneinstellungen auf. Der Collect-Schalter und die minimale Aktualisierungszeit erscheinen im Karten-Header. Wenn während des Datenpunktscans Collect-Traffic vom Gerät erkannt wurde, wird im Karten-Header ein grünes Pin-Symbol als Bestätigung angezeigt.
|
|
217
|
+
Jede Gerätekarte listet ihre Datenpunkte mit ID, Name, Codec und Zeitplaneinstellungen auf. Der Collect-Schalter und die minimale Aktualisierungszeit erscheinen im Karten-Header. Wenn während des Datenpunktscans Collect-Traffic vom Gerät erkannt wurde, wird im Karten-Header ein grünes Pin-Symbol als Bestätigung angezeigt. Sind Datenpunkte eingeplant, erscheint ein grünes **N scheduled**-Badge — ein Klick darauf klappt die Karte auf und zeigt nur die eingeplanten Datenpunkte. Nochmaliger Klick auf das Badge hebt den Filter auf; ein Klick auf den Karten-Header hebt den Filter auf und klappt die Karte ein oder vollständig auf, je nachdem, ob das Badge sie geöffnet hatte.
|
|
218
218
|
|
|
219
219
|
**Energiezähler-Karten**
|
|
220
220
|
|
|
@@ -376,51 +376,10 @@ Ja. Den State `e3oncan.0.<GERÄT>.cmnd.udsReadByDid` bearbeiten und eine Liste v
|
|
|
376
376
|
Wenn Ihnen dieses Projekt gefällt — oder Sie einfach großzügig sind — würde ich mich über ein Bier freuen. Prost! :beers:
|
|
377
377
|
|
|
378
378
|
## Changelog
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
### **WORK IN PROGRESS**
|
|
384
|
-
* (MyHomeMyData) Behoben: Beim Speichern von Schedules in der Datenpunkte-Seite konnten unter bestimmten Umständen veraltete Einträge verbleiben
|
|
385
|
-
* (MyHomeMyData) Topology-Schaltfläche in der Datenpunkte-Seite ergänzt; öffnet das Bus-Topologie-Diagramm in einem modalen Dialog
|
|
386
|
-
* (MyHomeMyData) Scan-Status-Erkennung verbessert: nutzt `udsDidsWritable` statt `didsMetaDict`, um zuverlässig zu erkennen, ob ein Datenpunktscan durchgeführt wurde
|
|
387
|
-
* (MyHomeMyData) Hinweis auf erneuten Scan in der Datenpunkte-Seite ergänzt, wenn ein Scan vorhanden, aber die Collect-Erkennung noch nicht durchgeführt wurde
|
|
388
|
-
|
|
389
|
-
### 1.0.0-beta.1 (2026-04-30)
|
|
390
|
-
* (MyHomeMyData) Bus-Topologie-Analyse wird jetzt automatisch nach dem Datenpunktscan erstellt; Ergebnisse werden in `info.topology` (JSON) und `info.topologyHtml` (HTML) gespeichert; Details im Readme
|
|
391
|
-
* (MyHomeMyData) Eingabevalidierung für Intervall- und Verzögerungsfelder in der Datenpunkte-Seite ergänzt — nur positive ganze Zahlen werden akzeptiert
|
|
392
|
-
|
|
393
|
-
### 1.0.0-beta.0 (2026-04-26)
|
|
394
|
-
* (MyHomeMyData) Neue e3oncan Datenpunkte-Webseite, direkt in der Adapterinstanzzeile verankert
|
|
395
|
-
* (MyHomeMyData) Energiezähler (E380, E3100CB) werden jetzt während des Gerätescans durch passives CAN-Lauschen auf beiden Kanälen automatisch erkannt
|
|
396
|
-
* (MyHomeMyData) State-Namen für Energiezähler werden automatisch anhand von CAN-Adresse und Kanal vergeben; Details im Readme
|
|
397
|
-
* (MyHomeMyData) Collect-Schalter und Verzögerung für Energiezähler werden ausschließlich in der e3oncan Datenpunkte-Seite konfiguriert; Änderungen werden nach Adapter-Neustart wirksam
|
|
398
|
-
* (MyHomeMyData) Beim ersten Start nach einem Upgrade wird die Aktiv-Einstellung automatisch aus der bisherigen Adapterkonfiguration migriert
|
|
399
|
-
* (MyHomeMyData) Collect-fähige Geräte werden jetzt während des Datenpunktscans durch passives CAN-Lauschen automatisch erkannt; ein Pin-Symbol wird im Gerätekarten-Header für jedes erkannte Gerät angezeigt
|
|
400
|
-
* (MyHomeMyData) Option hinzugefügt, das Speichern von Datenpunktwerten während des Datenpunktscans zu unterdrücken
|
|
401
|
-
|
|
402
|
-
### 0.11.1 (2026-04-23)
|
|
403
|
-
* (MyHomeMyData) Robustheit verbessert: Ein Datenpunkt der Länge null wird als „negative response" behandelt
|
|
404
|
-
* (MyHomeMyData) Metadaten werden jetzt auch nach dem Löschen eines Datenpunkts wiederhergestellt
|
|
405
|
-
* (MyHomeMyData) Testfälle für deutsche Systemsprache angepasst
|
|
406
|
-
|
|
407
|
-
### 0.11.0 (2026-04-14)
|
|
408
|
-
* (MyHomeMyData) Für volle Unterstützung von Varianten-Datenpunkten und Metadaten bitte einen Gerätescan gefolgt von einem Datenpunktscan durchführen
|
|
409
|
-
* (MyHomeMyData) Unterstützung für Varianten-Datenpunkte und Geräte-Datenformatkonfiguration hinzugefügt; Details unter https://github.com/MyHomeMyData/ioBroker.e3oncan/lib/data-points.md
|
|
410
|
-
* (MyHomeMyData) Metadaten zu mehreren Datenpunkten hinzugefügt, z. B. Beschreibung, Einheit, Link zu weiteren Infos
|
|
411
|
-
* (MyHomeMyData) Beim Datenpunktscan werden jetzt Metadaten zu den Datenpunkt-Objekten hinzugefügt
|
|
412
|
-
* (MyHomeMyData) Handhabung schreibbarer Datenpunkte geändert; diese Information ist jetzt auch in der Datenpunktdefinition verfügbar; die Handhabung der Whitelist für Schreibzugriffe ist unverändert
|
|
413
|
-
* (MyHomeMyData) Beim Gerätescan werden die verwendeten Datenformate (Datenpunkt 382) ausgewertet
|
|
414
|
-
* (MyHomeMyData) Struktur vieler Datenpunkte aktualisiert; Details im [Changelog](lib/data-points.md#changelog-of-data-point-definitions)
|
|
415
|
-
|
|
416
|
-
### 0.10.14 (2025-11-03)
|
|
417
|
-
* (MyHomeMyData) Elemente zu enums.js basierend auf PR Nr. 182 von open3e hinzugefügt
|
|
418
|
-
* (MyHomeMyData) Konfiguration der Dids-Scan-Grenzen im Quellcode vereinfacht
|
|
419
|
-
* (MyHomeMyData) Scan bis Did 3338 erweitert
|
|
420
|
-
* (MyHomeMyData) Hinweis zum Scanbereich im Readme ergänzt
|
|
421
|
-
* (MyHomeMyData) Korrekturen für Issue #169 (Repository Checker)
|
|
422
|
-
* (MyHomeMyData) Bugfix: Manuelle Änderung gerätespezifischer Dids wurde für Collect-Worker nicht ausgewertet
|
|
423
|
-
* (MyHomeMyData) Liste der Datenpunkte für E3-Geräte auf Version 20251102 aktualisiert
|
|
379
|
+
|
|
380
|
+
### Historie der Änderungen
|
|
381
|
+
|
|
382
|
+
Die Changelog-Einträge sind in der englischen Verison der (README.MD)[README.MD#changelog] verfügbar.
|
|
424
383
|
|
|
425
384
|
### Ältere Versionen
|
|
426
385
|
|
package/README.md
CHANGED
|
@@ -229,11 +229,11 @@ The **e3oncan datapoints** page is the primary place for browsing data points an
|
|
|
229
229
|
|
|
230
230
|
All devices and any detected energy meters are shown as expandable cards, starting collapsed so you get an overview of your whole system at a glance. Click a card header to expand it. The search box filters by name or ID, and matching cards are expanded automatically.
|
|
231
231
|
|
|
232
|
-
If a data point scan has not been performed yet for a device, a warning banner is shown at the top of the page as a reminder.
|
|
232
|
+
If a data point scan has not been performed yet for a device, a warning banner is shown at the top of the page as a reminder. If a scan has been done but the Collect auto-detection introduced in v1.x has not yet run, an info banner recommends running a new data point scan. This hint can be permanently dismissed per instance with the **Don't show again** button.
|
|
233
233
|
|
|
234
234
|
**Device cards**
|
|
235
235
|
|
|
236
|
-
Each device card lists its data points with ID, name, codec, and schedule settings. The Collect toggle and min. update time appear in the card header. If Collect traffic from the device was detected during the data point scan, a green pin icon is shown in the card header as a confirmation.
|
|
236
|
+
Each device card lists its data points with ID, name, codec, and schedule settings. The Collect toggle and min. update time appear in the card header. If Collect traffic from the device was detected during the data point scan, a green pin icon is shown in the card header as a confirmation. If any data points are scheduled, a green **N scheduled** badge appears — click it to expand the card and show only the scheduled data points. Click the badge again to remove the filter; clicking the card header removes the filter and collapses or fully expands the card, depending on whether the badge had opened it.
|
|
237
237
|
|
|
238
238
|
**Energy meter cards**
|
|
239
239
|
|
|
@@ -399,6 +399,13 @@ If you enjoyed this project — or just feeling generous, consider buying me a b
|
|
|
399
399
|
Placeholder for the next version (at the beginning of the line):
|
|
400
400
|
### **WORK IN PROGRESS**
|
|
401
401
|
-->
|
|
402
|
+
### 1.0.2 (2026-05-17)
|
|
403
|
+
* (MyHomeMyData) Improved error message when native module socketcan fails to load after a Node.js version upgrade — adapter now logs a clear hint to run `iob rebuild`
|
|
404
|
+
|
|
405
|
+
### 1.0.1 (2026-05-11)
|
|
406
|
+
* (MyHomeMyData) Clicking the green scheduled badge on a device card filters the view to show only its scheduled data points; clicking the badge again or the card header restores the full view
|
|
407
|
+
* (MyHomeMyData) Fixed: saving from the datapoints tab now preserves inactive schedules (disabled in the old config UI) for full backward compatibility
|
|
408
|
+
|
|
402
409
|
### 1.0.0 (2026-05-06)
|
|
403
410
|
* (MyHomeMyData) Adapter requires node.js >= 22 now
|
|
404
411
|
* (MyHomeMyData) Improved scan status detection: uses `udsDidsWritable` instead of `didsMetaDict` to reliably detect whether a data point scan has been performed
|
package/admin/tab.html
CHANGED
|
@@ -125,6 +125,8 @@
|
|
|
125
125
|
let sock = null;
|
|
126
126
|
let loadedEnergyMeters = {};
|
|
127
127
|
let topologyHtml = '';
|
|
128
|
+
let scheduledFilter = null;
|
|
129
|
+
let filterWasCollapsed = false;
|
|
128
130
|
|
|
129
131
|
document.getElementById('btn-dismiss-rescan-hint').addEventListener('click', function() {
|
|
130
132
|
localStorage.setItem('rescanHintDismissed_' + namespace, '1');
|
|
@@ -317,7 +319,7 @@
|
|
|
317
319
|
'<div class="dev-name-col"><strong>' + dev.devStateName + '</strong>' +
|
|
318
320
|
'<code class="ms-2 small">' + dev.devAddr + '</code>' +
|
|
319
321
|
'<span class="badge bg-secondary ms-2">' + dev.dids.length + ' DIDs</span>' +
|
|
320
|
-
(activeCount > 0 ? '<span class="badge bg-success ms-1 scheduled-badge">' + activeCount + ' scheduled</span>' : '') +
|
|
322
|
+
(activeCount > 0 ? '<span class="badge ' + (scheduledFilter === dev.devStateName ? 'bg-primary' : 'bg-success') + ' ms-1 scheduled-badge" style="cursor:pointer" onclick="event.stopPropagation();toggleScheduledFilter(\'' + dev.devStateName.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + '\')">' + activeCount + ' scheduled</span>' : '') +
|
|
321
323
|
'</div>' +
|
|
322
324
|
collectHtml +
|
|
323
325
|
'</div>' +
|
|
@@ -338,7 +340,17 @@
|
|
|
338
340
|
const chevron = card.querySelector('.bi-chevron-down');
|
|
339
341
|
card.querySelector('.card-header').addEventListener('click', function(e) {
|
|
340
342
|
if (e.target.closest('.collect-area')) return;
|
|
341
|
-
|
|
343
|
+
if (scheduledFilter === dev.devStateName) {
|
|
344
|
+
scheduledFilter = null;
|
|
345
|
+
const badge = card.querySelector('.scheduled-badge');
|
|
346
|
+
if (badge) { badge.classList.remove('bg-primary'); badge.classList.add('bg-success'); }
|
|
347
|
+
if (!filterWasCollapsed) {
|
|
348
|
+
bootstrap.Collapse.getOrCreateInstance(collapseEl).hide();
|
|
349
|
+
}
|
|
350
|
+
filterRows();
|
|
351
|
+
} else {
|
|
352
|
+
bootstrap.Collapse.getOrCreateInstance(collapseEl).toggle();
|
|
353
|
+
}
|
|
342
354
|
});
|
|
343
355
|
chevron.style.transform = 'rotate(-90deg)';
|
|
344
356
|
collapseEl.addEventListener('hide.bs.collapse', function() { chevron.style.transform = 'rotate(-90deg)'; });
|
|
@@ -398,9 +410,13 @@
|
|
|
398
410
|
const schedMode = document.querySelector('input[name="schedFilter"]:checked').value;
|
|
399
411
|
const ivStr = document.getElementById('filter-interval-val').value.trim();
|
|
400
412
|
const ivFilter = (schedMode === 'interval' && ivStr) ? parseInt(ivStr, 10) : null;
|
|
401
|
-
const hasActiveFilter = search || schedMode !== 'all';
|
|
413
|
+
const hasActiveFilter = search || schedMode !== 'all' || scheduledFilter;
|
|
402
414
|
|
|
403
415
|
document.querySelectorAll('.ecu-group').forEach(function(group) {
|
|
416
|
+
if (scheduledFilter && group.dataset.devStateName !== scheduledFilter) {
|
|
417
|
+
group.style.display = 'none';
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
404
420
|
let visibleCount = 0;
|
|
405
421
|
group.querySelectorAll('tbody tr').forEach(function(row) {
|
|
406
422
|
const textOk = !search ||
|
|
@@ -408,7 +424,11 @@
|
|
|
408
424
|
(row.dataset.name || '').includes(search) ||
|
|
409
425
|
(row.dataset.desc || '').includes(search);
|
|
410
426
|
let schedOk = true;
|
|
411
|
-
if (
|
|
427
|
+
if (scheduledFilter) {
|
|
428
|
+
const onstart = row.querySelector('.onstart-check').checked;
|
|
429
|
+
const iv = row.querySelector('.interval-input').value.trim();
|
|
430
|
+
schedOk = onstart || iv !== '';
|
|
431
|
+
} else if (schedMode === 'onstart') {
|
|
412
432
|
schedOk = row.querySelector('.onstart-check').checked;
|
|
413
433
|
} else if (schedMode === 'interval') {
|
|
414
434
|
const iv = row.querySelector('.interval-input').value.trim();
|
|
@@ -439,6 +459,34 @@
|
|
|
439
459
|
});
|
|
440
460
|
}
|
|
441
461
|
|
|
462
|
+
function toggleScheduledFilter(devStateName) {
|
|
463
|
+
let group = null;
|
|
464
|
+
document.querySelectorAll('.ecu-group').forEach(function(g) {
|
|
465
|
+
if (g.dataset.devStateName === devStateName) group = g;
|
|
466
|
+
});
|
|
467
|
+
const collapseEl = group ? group.querySelector('.collapse') : null;
|
|
468
|
+
|
|
469
|
+
if (scheduledFilter === devStateName) {
|
|
470
|
+
scheduledFilter = null;
|
|
471
|
+
if (filterWasCollapsed && collapseEl) {
|
|
472
|
+
bootstrap.Collapse.getOrCreateInstance(collapseEl).hide();
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
filterWasCollapsed = collapseEl ? !collapseEl.classList.contains('show') : false;
|
|
476
|
+
scheduledFilter = devStateName;
|
|
477
|
+
if (collapseEl) bootstrap.Collapse.getOrCreateInstance(collapseEl).show();
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
document.querySelectorAll('.ecu-group').forEach(function(g) {
|
|
481
|
+
const badge = g.querySelector('.scheduled-badge');
|
|
482
|
+
if (!badge) return;
|
|
483
|
+
const isActive = scheduledFilter && g.dataset.devStateName === scheduledFilter;
|
|
484
|
+
badge.classList.toggle('bg-success', !isActive);
|
|
485
|
+
badge.classList.toggle('bg-primary', !!isActive);
|
|
486
|
+
});
|
|
487
|
+
filterRows();
|
|
488
|
+
}
|
|
489
|
+
|
|
442
490
|
function markChanged() {
|
|
443
491
|
hasChanges = true;
|
|
444
492
|
document.getElementById('btn-save').disabled = false;
|
|
@@ -462,6 +510,8 @@
|
|
|
462
510
|
if (!badge) {
|
|
463
511
|
badge = document.createElement('span');
|
|
464
512
|
badge.className = 'badge bg-success ms-1 scheduled-badge';
|
|
513
|
+
badge.style.cursor = 'pointer';
|
|
514
|
+
badge.onclick = (function(name) { return function(e) { e.stopPropagation(); toggleScheduledFilter(name); }; })(card.dataset.devStateName);
|
|
465
515
|
nameCol.appendChild(badge);
|
|
466
516
|
}
|
|
467
517
|
badge.textContent = count + ' scheduled';
|
|
@@ -509,7 +559,10 @@
|
|
|
509
559
|
|
|
510
560
|
const tabDevAddrs = new Set(allDevices.map(function(d) { return Number(d.devAddr); }));
|
|
511
561
|
const newSchedules = (adapterNative.tableUdsSchedules || []).filter(function(s) {
|
|
512
|
-
|
|
562
|
+
// Keep schedules for devices not managed by this tab, and keep
|
|
563
|
+
// inactive schedules for tab devices (backward compatibility with
|
|
564
|
+
// the old config UI that allows disabling a schedule without deleting it).
|
|
565
|
+
return !tabDevAddrs.has(Number(s.udsSelectDevAddr)) || !s.udsScheduleActive;
|
|
513
566
|
});
|
|
514
567
|
const newCollectExt = JSON.parse(JSON.stringify(adapterNative.tableCollectCanExt || []));
|
|
515
568
|
const newCollectInt = JSON.parse(JSON.stringify(adapterNative.tableCollectCanInt || []));
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "e3oncan",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.0.2": {
|
|
7
|
+
"en": "Improved error message when native module socketcan fails to load after a Node.js version upgrade — adapter now logs a clear hint to run `iob rebuild`",
|
|
8
|
+
"de": "Verbesserte Fehlermeldung, wenn das native Modul socketcan nach einem Node.js-Versions-Upgrade nicht geladen werden kann — Adapter protokolliert jetzt einen klaren Hinweis: `iob rebuild` ausführen",
|
|
9
|
+
"ru": "Улучшенное сообщение об ошибке, когда встроенный модуль socketcan не загружается после обновления версии Node.js — адаптер теперь регистрирует четкий намек на запуск «перестроения iob»",
|
|
10
|
+
"pt": "Mensagem de erro melhorada quando socketcan módulo nativo não consegue carregar após uma atualização de versão Node.js — adaptador agora registra uma dica clara para executar `iob reconstruction`",
|
|
11
|
+
"nl": "Verbeterde foutmelding wanneer native module socketcan niet kan worden geladen na een Node.js versie upgrade-adapter logt nu een duidelijke hint uit om te starten",
|
|
12
|
+
"fr": "Message d'erreur amélioré lorsque le module natif socketcan ne parvient pas à se charger après une mise à jour de la version Node.js — adaptateur enregistre maintenant un indice clair pour exécuter `iob rebuild`",
|
|
13
|
+
"it": "Messaggio di errore migliorato quando socket del modulo nativo può non caricare dopo un aggiornamento della versione Node.js — l'adattatore ora registra un chiaro suggerimento per eseguire `iob rebuild`",
|
|
14
|
+
"es": "Mensaje de error mejorado cuando el módulo nativo socketcan no se carga después de una actualización de la versión Node.js — el adaptador ahora registra una clara pista para ejecutar `iob reconstrucción`",
|
|
15
|
+
"pl": "Ulepszony komunikat błędu, gdy natywny moduł socketcan nie wczyta po aktualizacji wersji Node.js - adapter teraz loguje wyraźną wskazówkę do uruchomienia 'iob rebuild'",
|
|
16
|
+
"uk": "Удосконалено повідомлення про помилку, коли рідний модуль розетки може не навантажувати після оновлення версії Node.js — адаптер тепер записує чіткий підказок для запуску `iob rebuild`",
|
|
17
|
+
"zh-cn": "当本地模块套接字在节点.js版本升级后无法加载时改进错误信息——适配器现在登录一个运行“ iob 重建” 的明确提示"
|
|
18
|
+
},
|
|
19
|
+
"1.0.1": {
|
|
20
|
+
"en": "Clicking the green scheduled badge on a device card filters the view to show only its scheduled data points; clicking the badge again or the card header restores the full view\nFixed: saving from the datapoints tab now preserves inactive schedules (disabled in the old config UI) for full backward compatibility",
|
|
21
|
+
"de": "Ein Klick auf das grüne Badge auf einer Gerätekarte filtert die Ansicht für die aktivierten Datenpunkte; erneutes Klicken hebt den Filter wieder auf\nBehoben: Speichern von der Registerkarte Datenpunkte bewahrt nun inaktive Zeitpläne (in der alten config UI) für volle Rückwärtskompatibilität",
|
|
22
|
+
"ru": "Нажатие зеленого значка на карте устройства фильтрует вид, чтобы показать только его запланированные точки данных; нажатие снова на значок или заголовок карты восстанавливает полный вид\nИсправлено: сохранение со вкладки «Точки данных» теперь сохраняет неактивные графики (отключены в старом интерфейсе конфигурирования) для полной обратной совместимости",
|
|
23
|
+
"pt": "Clicar no crachá agendado verde num cartão de dispositivo filtra a vista para mostrar apenas os seus pontos de dados agendados; clicar novamente no crachá ou o cabeçalho do cartão restaura a visão completa\nCorrigido: salvar da aba pontos de dados agora preserva agendas inativas (desativadas na interface de configuração antiga) para compatibilidade com o backward completo",
|
|
24
|
+
"nl": "Klik op de groene geplande badge op een apparaatkaart filtert het uitzicht om alleen de geplande datapunten te tonen; opnieuw op de badge klikken of de kaartkop herstelt de volledige weergave\nFixed: opslaan vanaf het tabblad datapoints bewaart nu inactieve schema's (uitgeschakeld in de oude configuratie-UI) voor volledige compatibiliteit achterwaarts",
|
|
25
|
+
"fr": "Cliquer sur l'insigne programmé vert sur une carte de périphérique filtre la vue pour afficher seulement ses points de données programmés; cliquer de nouveau sur l'insigne ou l'en-tête de la carte restaure la vue complète\nCorrection : l'enregistrement depuis l'onglet datapoints préserve désormais les horaires inactifs (désactivés dans l'ancienne interface de configuration) pour une compatibilité arrière complète",
|
|
26
|
+
"it": "Fare clic sul badge verde programmato su una scheda di dispositivo filtra la vista per mostrare solo i suoi punti di dati programmati; fare clic sul badge di nuovo o l'intestazione della carta ripristina la vista completa\nRisolto: salvare dalla scheda datapoint ora conserva i programmi inattivi (disabilitati nella vecchia configurazione UI) per la piena compatibilità all'indietro",
|
|
27
|
+
"es": "Hacer clic en la placa verde programada en una tarjeta de dispositivo filtra la vista para mostrar sólo sus puntos de datos programados; hacer clic en la placa de nuevo o el encabezado de la tarjeta restaura la vista completa\nFijado: el ahorro de la ficha datapoints ahora preserva los horarios inactivos (desactivados en la antigua interfaz de usuario) para la compatibilidad atrasada completa",
|
|
28
|
+
"pl": "Kliknięcie zielonej planowej odznaki na karcie urządzenia filtruje widok, aby wyświetlić tylko swoje zaplanowane punkty danych; kliknięcie ponownie na odznakę lub nagłówek karty przywraca pełny widok\nNaprawiono: zapisywanie z zakładki datapoints teraz zachowuje nieaktywne harmonogramy (wyłączone w starym konfigu interfejsu) dla pełnej kompatybilności wstecznej",
|
|
29
|
+
"uk": "Натискання зеленої запланованої значки на картці пристрою фільтрує вигляд, щоб показати лише свої заплановані дані; натискання значка знову або заголовок картки відновлює повне подання\nВиправлено: збереження з вкладки даних тепер зберігає неактивні графіки (здатні у старій конфігурації UI) для повної сумісності з резервними сторонами",
|
|
30
|
+
"zh-cn": "在设备卡片上单击绿色的预定徽章过滤视图,只显示其预定的数据点;再次点击徽章或卡片头恢复全视图\n固定: 现在从数据点标签中保存不活动调度表( 在旧配置 UI 中已失效) , 完全向后兼容"
|
|
31
|
+
},
|
|
6
32
|
"1.0.0": {
|
|
7
33
|
"en": "Adapter requires node.js >= 22 now\nImproved scan status detection: uses `udsDidsWritable` instead of `didsMetaDict` to reliably detect whether a data point scan has been performed\nAdded re-scan recommendation hint in datapoints tab when a scan exists but Collect auto-detection has not yet been run",
|
|
8
34
|
"de": "Adapter benötigt node.js >= 22\nVerbesserte Scan-Status-Erkennung: verwendet `udsDidsWritable` anstelle von `didsMetaDict`, um sicher zu erkennen, ob ein Datenpunkt-Scan durchgeführt wurde\nZusätzlicher re-scan Hinweis in Datenpunkte Tab, wenn ein Scan existiert, aber Collect Auto-Detektion noch nicht ausgeführt wurde",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "Przypadkowo wymienione punkty danych 1415- 1418 zostały usunięte z changelog wersji 0.11.0",
|
|
68
94
|
"uk": "У випадково зазначених пунктах даних 1415-1418 було вилучено з зміни версії 0.11.0",
|
|
69
95
|
"zh-cn": "无意中提到的1415-1418数据点已经从0.110"
|
|
70
|
-
},
|
|
71
|
-
"0.11.2": {
|
|
72
|
-
"en": "Added \"What's new in v0.11.x\" section to Readme with upgrade notes for data point structure changes",
|
|
73
|
-
"de": "Hinzugefügt \"Was ist neu in v0.11.x\" Abschnitt zu Readme mit Upgrade-Notizen für Datenpunkt Strukturänderungen",
|
|
74
|
-
"ru": "Добавлен раздел «Что нового в v0.11.x» в Readme с примечаниями об обновлении для изменения структуры точек данных",
|
|
75
|
-
"pt": "Adicionado \"O que há de novo na seção v0.11.x\" para Readme com notas de atualização para mudanças na estrutura de pontos de dados",
|
|
76
|
-
"nl": "Toegevoegd \"Wat is nieuw in v0.11.x\" sectie aan Readme met upgrade notities voor gegevenspunt structuur wijzigingen",
|
|
77
|
-
"fr": "Ajout de la section « Quoi de neuf dans v0.11.x » à Readme avec des notes de mise à jour pour les changements de structure des points de données",
|
|
78
|
-
"it": "Aggiunto \"What's new in v0.11.x\" sezione a Readme con le note di aggiornamento per le modifiche della struttura del punto di dati",
|
|
79
|
-
"es": "Añadido \"Lo nuevo en v0.11.x\" sección a Readme con notas de actualización para cambios de estructura de puntos de datos",
|
|
80
|
-
"pl": "Dodano \"Co nowego w sekcji v0.11.x\" do Readme z notatkami aktualizacji zmian struktury punktów danych",
|
|
81
|
-
"uk": "Додано розділ \"Що нового в v0.11.x\" для читання з оновленнями нот для змін структури даних",
|
|
82
|
-
"zh-cn": "添加「 v0. 11. x. 中的新内容」 部分至 Readme, 并附有数据点结构变化的升级注释"
|
|
83
|
-
},
|
|
84
|
-
"0.11.1": {
|
|
85
|
-
"en": "Improved robustness: Receiving a data point of length zero is treated as a \"negative response\"\nThe metadata is now also restored after a data point is deleted \nAligned test cases for German system language",
|
|
86
|
-
"de": "Verbesserte Robustheit: Der Empfang eines Datenpunktes der Länge Null wird als \"negative Antwort\" behandelt\nDie Metadaten werden nun auch wiederhergestellt, nachdem ein Datenpunkt gelöscht wurde\nTestfälle für deutsche Systemsprache angepasst",
|
|
87
|
-
"ru": "Повышение надежности: получение нулевой точки данных рассматривается как «отрицательный ответ»\nМетаданные также восстанавливаются после удаления точки данных\nВыровненные тесты для немецкого системного языка",
|
|
88
|
-
"pt": "Maior robustez: Receber um ponto de dados de comprimento zero é tratado como uma \"resposta negativa\"\nOs metadados também são restaurados depois de um ponto de dados ser apagado\nCasos de teste alinhados para a língua do sistema alemão",
|
|
89
|
-
"nl": "Verbeterde robuustheid: Het ontvangen van een datapunt van lengte nul wordt behandeld als een \"negatieve respons\"\nDe metagegevens worden nu ook hersteld nadat een datapunt is verwijderd\nUitgelijnde testcases voor de Duitse systeemtaal",
|
|
90
|
-
"fr": "Amélioration de la robustesse : la réception d'un point de données de longueur zéro est traitée comme une \"réponse négative\"\nLes métadonnées sont maintenant également restaurées après la suppression d'un point de données\nCas d'essai alignés pour la langue du système allemand",
|
|
91
|
-
"it": "Maggiore robustezza: la ricezione di un data point di lunghezza zero viene trattata come una \"risposta negativa\"\nI metadati vengono ora ripristinati anche dopo la cancellazione di un data point\nCasi di prova allineati per la lingua del sistema tedesco",
|
|
92
|
-
"es": "Mayor robustez: Recibir un punto de datos de la longitud cero es tratado como una \"respuesta negativa\"\nLos metadatos ahora también se restauran después de que se elimina un punto de datos\nCasos de prueba de los Alineados para el lenguaje del sistema alemán",
|
|
93
|
-
"pl": "Lepsza odporność: Otrzymanie punktu danych o długości zero jest traktowane jako \"odpowiedź negatywna\"\nMetadane są teraz również przywrócone po usunięciu punktu danych\nDopasowanie przypadków testowych dla języka niemieckiego",
|
|
94
|
-
"uk": "Покращена надійність: Отримання точки даних довжини нульового обробляється як \"негативна відповідь\"\nМетадані також відновлюються після видалення даних\nВизначені тестові випадки для німецької мови системи",
|
|
95
|
-
"zh-cn": "增强强性:接收长度为0的数据点作为\"负反应\"处理\n在删除一个数据点后, 元数据现在也被恢复\n德语系统语言的对齐测试例"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
package/lib/protocols.md
CHANGED
|
@@ -1,165 +1,11 @@
|
|
|
1
1
|
# Description of used protocols
|
|
2
2
|
|
|
3
|
-
## Unsolicited sequences (mode "collect" of clients)
|
|
4
|
-
|
|
5
|
-
Viessmann E3 devices broadcast data point values on the CAN bus unsolicited,
|
|
6
|
-
whenever a value changes, without any prior request.
|
|
7
|
-
The format is similar to the response of a UDS ReadDataByIdentifier service,
|
|
8
|
-
but uses a different length encoding and does not require a flow-control
|
|
9
|
-
handshake.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
### CAN-ID
|
|
14
|
-
|
|
15
|
-
Each E3 device transmits on a fixed CAN-ID:
|
|
16
|
-
|
|
17
|
-
| Device | CAN-ID |
|
|
18
|
-
|---|---|
|
|
19
|
-
| Vitocharge VX3 | `0x451` |
|
|
20
|
-
| Vitocal 250 (internal bus and connected systems) | `0x693` |
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
### Frame types
|
|
25
|
-
|
|
26
|
-
Every CAN frame is exactly 8 bytes.
|
|
27
|
-
|
|
28
|
-
**First Frame (FF)** — marks the start of a new data point transmission:
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
Byte 0: 0x21 (always, identifies the start of a sequence)
|
|
32
|
-
Byte 1: DID_LOW (low byte of the Data Identifier)
|
|
33
|
-
Byte 2: DID_HIGH (high byte of the Data Identifier)
|
|
34
|
-
Byte 3: length code (see length encoding below)
|
|
35
|
-
Byte 4+: payload (start position depends on length code)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Continuation Frame (CF)** — carries the remaining payload bytes:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
Byte 0: sequence byte (starts at 0x22, increments with each frame,
|
|
42
|
-
wraps 0x2F → 0x20)
|
|
43
|
-
Byte 1–7: payload continuation
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
The last frame is padded to 8 bytes.
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
### Length encoding
|
|
51
|
-
|
|
52
|
-
The length code in byte 3 of the First Frame encodes both payload length and
|
|
53
|
-
frame type:
|
|
54
|
-
|
|
55
|
-
| Byte 3 (`v3`) | Byte 4 (`v4`) | Type | Payload length | Payload starts at |
|
|
56
|
-
|---|---|---|---|---|
|
|
57
|
-
| `0xB1`–`0xB4` | any | Single Frame | `v3 − 0xB0` (1–4 bytes) | Byte 4 |
|
|
58
|
-
| `0xB5`–`0xBF` | any | Multi Frame | `v3 − 0xB0` (5–15 bytes) | Byte 4 |
|
|
59
|
-
| `0xB0` | ≠ `0xC1` | Multi Frame | `v4` (16–255 bytes) | Byte 5 |
|
|
60
|
-
| `0xB0` | `0xC1` | Multi Frame | Byte 5 (`v5`) | Byte 6 |
|
|
61
|
-
|
|
62
|
-
The `0xC1` escape in the last row is used when the payload length value itself
|
|
63
|
-
would equal `0xB5` or `0xC1`, to avoid ambiguity with those special byte
|
|
64
|
-
values. In practice this has been observed for a payload length of `0xB5`
|
|
65
|
-
(181 bytes).
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
### Multi-frame sequence
|
|
70
|
-
|
|
71
|
-
No flow control is required. The device sends all frames back-to-back:
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
Device Listener
|
|
75
|
-
| |
|
|
76
|
-
|── First Frame ───────────────>|
|
|
77
|
-
|── Continuation Frame 1 ──────>|
|
|
78
|
-
|── Continuation Frame 2 ──────>|
|
|
79
|
-
| … |
|
|
80
|
-
|── Continuation Frame n ──────>| ← last frame, padded to 8 bytes
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
### Complete examples
|
|
86
|
-
|
|
87
|
-
**Single Frame** — DID `0x09BE`, payload length 4:
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
# seq DID len payload
|
|
91
|
-
can0 693 [8] 21 BE 09 B4 95 0E 00 00
|
|
92
|
-
```
|
|
93
|
-
`v3 = 0xB4` → length = `0xB4 − 0xB0` = 4. Payload: `95 0E 00 00`.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
**Multi Frame** — DID `0x011A`, payload length 9:
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
# seq DID len payload ...
|
|
101
|
-
can0 693 [8] 21 1A 01 B9 90 01 D4 00
|
|
102
|
-
can0 693 [8] 22 E5 01 82 01 00 55 55
|
|
103
|
-
```
|
|
104
|
-
`v3 = 0xB9` → length = 9. Payload bytes 1–4 start at byte 4 of frame 1,
|
|
105
|
-
bytes 5–9 follow in frame 2 (last 2 bytes are padding).
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
**Multi Frame** — DID `0x0224`, payload length 24 (`0x18`):
|
|
110
|
-
|
|
111
|
-
```
|
|
112
|
-
# seq DID len v4 payload ...
|
|
113
|
-
can0 693 [8] 21 24 02 B0 18 55 00 00
|
|
114
|
-
can0 693 [8] 22 00 1A 03 00 00 5F 0A
|
|
115
|
-
can0 693 [8] 23 00 00 38 0F 00 00 9B
|
|
116
|
-
can0 693 [8] 24 32 00 00 57 5E 00 00
|
|
117
|
-
```
|
|
118
|
-
`v3 = 0xB0`, `v4 = 0x18` (≠ `0xC1`) → length = 24. Payload starts at byte 5
|
|
119
|
-
of frame 1.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
**Multi Frame** — DID `0x0509`, payload length 181 (`0xB5`), using `0xC1` escape:
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
# seq DID len esc len2 payload ...
|
|
127
|
-
can0 693 [8] 21 09 05 B0 C1 B5 00 00
|
|
128
|
-
can0 693 [8] 22 00 00 00 00 00 00 00
|
|
129
|
-
can0 693 [8] 23 00 00 00 00 00 00 00
|
|
130
|
-
... (26 frames total) ...
|
|
131
|
-
can0 693 [8] 2B 00 00 00 00 55 55 55
|
|
132
|
-
```
|
|
133
|
-
`v3 = 0xB0`, `v4 = 0xC1` (escape) → length = `v5 = 0xB5` = 181. Payload
|
|
134
|
-
starts at byte 6 of frame 1. Last frame padded with `0x55`.
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
### Receiver implementation checklist
|
|
139
|
-
|
|
140
|
-
1. Listen on the device's CAN-ID for frames with byte 0 = `0x21` — this
|
|
141
|
-
marks the start of a new data point.
|
|
142
|
-
2. Extract the DID from bytes 1–2: `DID = byte1 + 256 × byte2` (little-endian).
|
|
143
|
-
3. Decode the length code in byte 3 using the table above to determine payload
|
|
144
|
-
length and start position.
|
|
145
|
-
4. **Single Frame:** extract the payload directly and decode the data point.
|
|
146
|
-
5. **Multi Frame:** record the expected next sequence byte (`0x22`), then
|
|
147
|
-
collect Continuation Frames until the full payload has been received.
|
|
148
|
-
- With each frame, verify byte 0 matches the expected sequence value.
|
|
149
|
-
If not, a frame was lost — discard the incomplete message.
|
|
150
|
-
- Increment the expected sequence byte after each frame; wrap `0x2F → 0x20`.
|
|
151
|
-
6. Discard padding bytes beyond the declared payload length in the last frame.
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
3
|
# UDS – ReadDataByIdentifier and WriteDataByIdentifier
|
|
156
4
|
|
|
157
5
|
UDS (Unified Diagnostic Services, ISO 14229) is a diagnostic protocol used in
|
|
158
6
|
automotive ECUs. This document covers the two services relevant for reading
|
|
159
7
|
and writing data points on Viessmann E3 devices.
|
|
160
8
|
|
|
161
|
-
UDS is **not** used by E3onCAN but by open3e.
|
|
162
|
-
|
|
163
9
|
---
|
|
164
10
|
|
|
165
11
|
## Transport layer – ISO-TP (ISO 15765-2)
|
|
@@ -390,6 +236,503 @@ The wrap is at 15 → 0, not 15 → 1.
|
|
|
390
236
|
**Timeout:** If no Flow Control arrives within ~1 s after a First Frame, abort
|
|
391
237
|
the transmission.
|
|
392
238
|
|
|
239
|
+
---
|
|
240
|
+
## Unsolicited sequences (mode "collect" of clients)
|
|
241
|
+
|
|
242
|
+
Viessmann E3 devices broadcast data point values on the CAN bus unsolicited,
|
|
243
|
+
whenever a value changes, without any prior request.
|
|
244
|
+
The format is similar to the response of a UDS ReadDataByIdentifier service,
|
|
245
|
+
but uses a different length encoding and does not require a flow-control
|
|
246
|
+
handshake.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
### CAN-ID
|
|
251
|
+
|
|
252
|
+
Each E3 device transmits on a fixed CAN-ID:
|
|
253
|
+
|
|
254
|
+
| Device | CAN-ID |
|
|
255
|
+
|---|---|
|
|
256
|
+
| Vitocharge VX3 | `0x451` |
|
|
257
|
+
| Vitocal 250 (internal bus and connected systems) | `0x693` |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### Frame types
|
|
262
|
+
|
|
263
|
+
Every CAN frame is exactly 8 bytes.
|
|
264
|
+
|
|
265
|
+
**First Frame (FF)** — marks the start of a new data point transmission:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
Byte 0: 0x21 (always, identifies the start of a sequence)
|
|
269
|
+
Byte 1: DID_LOW (low byte of the Data Identifier)
|
|
270
|
+
Byte 2: DID_HIGH (high byte of the Data Identifier)
|
|
271
|
+
Byte 3: length code (see length encoding below)
|
|
272
|
+
Byte 4+: payload (start position depends on length code)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Continuation Frame (CF)** — carries the remaining payload bytes:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
Byte 0: sequence byte (starts at 0x22, increments with each frame,
|
|
279
|
+
wraps 0x2F → 0x20)
|
|
280
|
+
Byte 1–7: payload continuation
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The last frame is padded to 8 bytes.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
### Length encoding
|
|
288
|
+
|
|
289
|
+
The length code in byte 3 of the First Frame encodes both payload length and
|
|
290
|
+
frame type:
|
|
291
|
+
|
|
292
|
+
| Byte 3 (`v3`) | Byte 4 (`v4`) | Type | Payload length | Payload starts at |
|
|
293
|
+
|---|---|---|---|---|
|
|
294
|
+
| low nibble 1–4 | any | Single Frame | low nibble of `v3` (1–4 bytes) | Byte 4 |
|
|
295
|
+
| low nibble 5–F | any | Multi Frame | low nibble of `v3` (5–15 bytes) | Byte 4 |
|
|
296
|
+
| low nibble 0 | ≠ `0xC1` | Multi Frame | `v4` (16–255 bytes) | Byte 5 |
|
|
297
|
+
| low nibble 0 | `0xC1` | Multi Frame | Byte 5 (`v5`) | Byte 6 |
|
|
298
|
+
|
|
299
|
+
**Length encoding rule:** the payload length is always the **low nibble** of
|
|
300
|
+
byte 3 (`v3 & 0x0F`). The high nibble is irrelevant for length purposes;
|
|
301
|
+
byte 3 at this position is always a length code.
|
|
302
|
+
Observed high-nibble values include `0x8` and `0xB`; both encode the same
|
|
303
|
+
lengths (e.g. `0x82` and `0xB2` both mean 2 bytes).
|
|
304
|
+
|
|
305
|
+
Low nibble = 0 signals a two-byte length field: the actual length is in
|
|
306
|
+
byte 4 (`v4`), except when `v4 = 0xC1`, which is an escape byte — the true
|
|
307
|
+
length is then in byte 5 (`v5`). The `0xC1` escape avoids ambiguity when the
|
|
308
|
+
payload length value itself would equal `0xB5` or `0xC1`. In practice this
|
|
309
|
+
has been observed for a payload length of `0xB5` (181 bytes).
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
### Multi-frame sequence
|
|
314
|
+
|
|
315
|
+
No flow control is required. The device sends all frames back-to-back:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
Device Listener
|
|
319
|
+
| |
|
|
320
|
+
|── First Frame ───────────────>|
|
|
321
|
+
|── Continuation Frame 1 ──────>|
|
|
322
|
+
|── Continuation Frame 2 ──────>|
|
|
323
|
+
| … |
|
|
324
|
+
|── Continuation Frame n ──────>| ← last frame, padded to 8 bytes
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### Complete examples
|
|
330
|
+
|
|
331
|
+
**Single Frame** — DID `0x09BE`, payload length 4:
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
# seq DID len payload
|
|
335
|
+
can0 693 [8] 21 BE 09 B4 95 0E 00 00
|
|
336
|
+
```
|
|
337
|
+
`v3 = 0xB4` → low nibble = 4 → length = 4. Payload: `95 0E 00 00`.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
**Multi Frame** — DID `0x011A`, payload length 9:
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
# seq DID len payload ...
|
|
345
|
+
can0 693 [8] 21 1A 01 B9 90 01 D4 00
|
|
346
|
+
can0 693 [8] 22 E5 01 82 01 00 55 55
|
|
347
|
+
```
|
|
348
|
+
`v3 = 0xB9` → low nibble = 9 → length = 9. Payload bytes 1–4 start at byte 4
|
|
349
|
+
of frame 1, bytes 5–9 follow in frame 2 (last 2 bytes are padding).
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
**Multi Frame** — DID `0x0224`, payload length 24 (`0x18`):
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
# seq DID len v4 payload ...
|
|
357
|
+
can0 693 [8] 21 24 02 B0 18 55 00 00
|
|
358
|
+
can0 693 [8] 22 00 1A 03 00 00 5F 0A
|
|
359
|
+
can0 693 [8] 23 00 00 38 0F 00 00 9B
|
|
360
|
+
can0 693 [8] 24 32 00 00 57 5E 00 00
|
|
361
|
+
```
|
|
362
|
+
`v3 = 0xB0`, `v4 = 0x18` (≠ `0xC1`) → length = 24. Payload starts at byte 5
|
|
363
|
+
of frame 1.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
**Multi Frame** — DID `0x0509`, payload length 181 (`0xB5`), using `0xC1` escape:
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
# seq DID len esc len2 payload ...
|
|
371
|
+
can0 693 [8] 21 09 05 B0 C1 B5 00 00
|
|
372
|
+
can0 693 [8] 22 00 00 00 00 00 00 00
|
|
373
|
+
can0 693 [8] 23 00 00 00 00 00 00 00
|
|
374
|
+
... (26 frames total) ...
|
|
375
|
+
can0 693 [8] 2B 00 00 00 00 55 55 55
|
|
376
|
+
```
|
|
377
|
+
`v3 = 0xB0`, `v4 = 0xC1` (escape) → length = `v5 = 0xB5` = 181. Payload
|
|
378
|
+
starts at byte 6 of frame 1. Last frame padded with `0x55`.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
### Receiver implementation checklist
|
|
383
|
+
|
|
384
|
+
1. Listen on the device's CAN-ID for frames with byte 0 = `0x21` — this
|
|
385
|
+
marks the start of a new data point.
|
|
386
|
+
2. Extract the DID from bytes 1–2: `DID = byte1 + 256 × byte2` (little-endian).
|
|
387
|
+
3. Decode the length code in byte 3 using the table above to determine payload
|
|
388
|
+
length and start position.
|
|
389
|
+
4. **Single Frame:** extract the payload directly and decode the data point.
|
|
390
|
+
5. **Multi Frame:** record the expected next sequence byte (`0x22`), then
|
|
391
|
+
collect Continuation Frames until the full payload has been received.
|
|
392
|
+
- With each frame, verify byte 0 matches the expected sequence value.
|
|
393
|
+
If not, a frame was lost — discard the incomplete message.
|
|
394
|
+
- Increment the expected sequence byte after each frame; wrap `0x2F → 0x20`.
|
|
395
|
+
6. Discard padding bytes beyond the declared payload length in the last frame.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Service 77 (proprietary write protocol)
|
|
400
|
+
|
|
401
|
+
### Background
|
|
402
|
+
|
|
403
|
+
Service 77 is a Viessmann-proprietary write protocol discovered via reverse engineering. It operates in parallel with UDS on a dedicated CAN-ID pair and allows writing of data points that are protected against normal `WriteDataByIdentifier` (UDS service 0x2E).
|
|
404
|
+
|
|
405
|
+
Viessmann uses this mechanism to protect certain data points from accidental or unauthorised modification. When a client receives NRC `0x22` (conditionsNotCorrect) in response to a normal UDS write, it can retry the same write using Service 77 on the dedicated CAN-ID.
|
|
406
|
+
|
|
407
|
+
Both protocols share the same data store: a value written via Service 77 is immediately readable via UDS `ReadDataByIdentifier`.
|
|
408
|
+
|
|
409
|
+
### CAN-ID mapping
|
|
410
|
+
|
|
411
|
+
The Service 77 CAN-IDs are derived from the device's UDS address:
|
|
412
|
+
|
|
413
|
+
| | CAN-ID |
|
|
414
|
+
|---|---|
|
|
415
|
+
| Service 77 request | `device_tx + 0x02` (e.g. `0x682` for main device at `0x680`) |
|
|
416
|
+
| Service 77 response | `device_tx + 0x12` (= request + `0x10`) |
|
|
417
|
+
|
|
418
|
+
### Transport layer
|
|
419
|
+
|
|
420
|
+
Service 77 uses the same ISO 15765-2 (ISO-TP) framing as UDS. The reassembled payload is described below.
|
|
421
|
+
|
|
422
|
+
### Request frame format
|
|
423
|
+
|
|
424
|
+
After ISO-TP reassembly the payload has this structure:
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
Byte 0: 0x77
|
|
428
|
+
Bytes 1–2: [CTR_L] [CTR_H]
|
|
429
|
+
Bytes 3–5: 0x43 0x01 0x82
|
|
430
|
+
Bytes 6–7: [DID_L] [DID_H]
|
|
431
|
+
Byte 8: 0xB0 + n
|
|
432
|
+
Bytes 9+: [DATA ...]
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
| Field | Bytes | Description |
|
|
436
|
+
|---|---|---|
|
|
437
|
+
| Service ID | 1 | Always `0x77` |
|
|
438
|
+
| Session counter | 2 | 16-bit little-endian counter; monotonically increasing across all writes in a session (~0.35 increments/s), wraps at 0xFFFF |
|
|
439
|
+
| Client ID | 3 | Fixed bytes `43 01 82`; constant across all observed frames |
|
|
440
|
+
| DID | 2 | Data identifier, **little-endian** (low byte first) |
|
|
441
|
+
| Length code | 1 | Present only when the high nibble is ≥ `0x8` (observed: `0x8x` and `0xBx`). Low nibble = data length in bytes (e.g. `0x82` and `0xB2` both mean 2 bytes). Low nibble 0 means the next byte carries the length (≥ 16 bytes). **If the byte at this position has high nibble < `0x8`, it is not a length code — the remaining payload bytes including that byte are raw data** (observed for small data points, e.g. a 1-byte value of `0x2B`). |
|
|
442
|
+
| Data | n | New value for the data point, little-endian |
|
|
443
|
+
|
|
444
|
+
### Response frame format
|
|
445
|
+
|
|
446
|
+
Positive response:
|
|
447
|
+
|
|
448
|
+
```
|
|
449
|
+
[0x77] [CTR_L] [CTR_H] [0x44]
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
| Field | Bytes | Description |
|
|
453
|
+
|---|---|---|
|
|
454
|
+
| Service ID | 1 | Always `0x77` |
|
|
455
|
+
| Session counter | 2 | 16-bit LE counter **echoed from the request** (not the DID) |
|
|
456
|
+
| Confirmation byte | 1 | Always `0x44` (Viessmann-specific, no UDS equivalent) |
|
|
457
|
+
|
|
458
|
+
Negative response (reuses UDS encoding):
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
[0x7F] [0x77] [NRC]
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
| NRC | Meaning |
|
|
465
|
+
|---|---|
|
|
466
|
+
| `0x12` | Payload too short (subFunctionNotSupported) |
|
|
467
|
+
| `0x31` | DID not present in data store (requestOutOfRange) |
|
|
468
|
+
|
|
469
|
+
### Interaction with UDS WriteDataByIdentifier
|
|
470
|
+
|
|
471
|
+
The `service77` key in `devices.json` specifies a list of DIDs that are protected against normal UDS writes:
|
|
472
|
+
|
|
473
|
+
* A `WriteDataByIdentifier` (0x2E) request targeting a protected DID returns NRC `0x22` (conditionsNotCorrect) without modifying the data store.
|
|
474
|
+
* A Service 77 request targeting the same DID is accepted and the value is written normally.
|
|
475
|
+
* Service 77 accepts writes to **all** known DIDs, including unprotected ones.
|
|
476
|
+
|
|
477
|
+
### Example exchange
|
|
478
|
+
|
|
479
|
+
Write DID `0x044C` (decimal 1100) with 2-byte value `0x012C` on the main device (`tx = 0x680`).
|
|
480
|
+
Session counter at this point: `0x0042`.
|
|
481
|
+
|
|
482
|
+
The reassembled UDS payload is 11 bytes, so ISO-TP multi-frame is used:
|
|
483
|
+
|
|
484
|
+
```
|
|
485
|
+
# Client request on 0x682 (= 0x680 + 0x02):
|
|
486
|
+
# ISO-TP FF (len=11)
|
|
487
|
+
682 [8] 10 0B 77 42 00 43 01 82
|
|
488
|
+
# ↑ ↑───↑ ↑──────↑
|
|
489
|
+
# SID CTR Client ID
|
|
490
|
+
|
|
491
|
+
# ISO-TP CF1
|
|
492
|
+
682 [8] 21 4C 04 B2 2C 01 CC CC
|
|
493
|
+
# ↑────↑ ↑ ↑────↑
|
|
494
|
+
# DID LE len data LE
|
|
495
|
+
|
|
496
|
+
# Device sends Flow Control first (standard ISO-TP):
|
|
497
|
+
692 [8] 30 00 00 00 00 00 00 00
|
|
498
|
+
|
|
499
|
+
# Server response on 0x692 (= 0x682 + 0x10):
|
|
500
|
+
692 [8] 04 77 42 00 44 CC CC CC
|
|
501
|
+
# ↑ ↑────↑ ↑
|
|
502
|
+
# SID CTR confirm=0x44
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Notes:
|
|
506
|
+
- DID `0x044C` is transmitted as `4C 04` (LE), not `04 4C` (BE).
|
|
507
|
+
- The response echoes the session counter `42 00`, not the DID.
|
|
508
|
+
- `0xB2` indicates 2 data bytes (low nibble = 2). `0x82` is equally valid and observed on real hardware.
|
|
509
|
+
|
|
510
|
+
### Service 77 read
|
|
511
|
+
|
|
512
|
+
In addition to writes, a **read** variant has been observed on the Vitocharge
|
|
513
|
+
VX3 external CAN bus. The client requests the current value of a specific DID;
|
|
514
|
+
the device responds with the full data payload. The framing is standard ISO-TP
|
|
515
|
+
with Flow Control, identical to a write exchange.
|
|
516
|
+
|
|
517
|
+
#### Read request
|
|
518
|
+
|
|
519
|
+
After ISO-TP reassembly (always 8 bytes):
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
Byte 0: 0x77
|
|
523
|
+
Bytes 1–2: [CTR_L] [CTR_H]
|
|
524
|
+
Bytes 3–5: 0x41 0x01 0x82 (read-request marker)
|
|
525
|
+
Bytes 6–7: [DID_L] [DID_H]
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
No length code and no data follow — the request carries only the DID.
|
|
529
|
+
|
|
530
|
+
#### Read response
|
|
531
|
+
|
|
532
|
+
After ISO-TP reassembly:
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
Byte 0: 0x77
|
|
536
|
+
Bytes 1–2: [CTR_L] [CTR_H] (echoed from request)
|
|
537
|
+
Bytes 3–5: 0x42 0x01 0x82 (read-response marker)
|
|
538
|
+
Bytes 6–7: [DID_L] [DID_H] (echoed from request)
|
|
539
|
+
Byte 8: length code (same encoding as write request, see above)
|
|
540
|
+
Bytes 9+: data
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### Example
|
|
544
|
+
|
|
545
|
+
Read DID `0x0509` (1289) from Vitocharge VX3 (`tx = 0x43F`, request channel
|
|
546
|
+
`0x441`, response channel `0x451`). The value is 181 bytes (0xB5), requiring
|
|
547
|
+
the `0xC1` length-code escape. CTR = `0x3634`.
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
# Client read request on 0x441 — ISO-TP total = 8 bytes
|
|
551
|
+
441 [8] 10 08 77 34 36 41 01 82
|
|
552
|
+
451 [8] 30 00 05 00 00 00 00 00 ← Flow Control from device
|
|
553
|
+
441 [8] 21 09 05 00 00 00 00 00
|
|
554
|
+
# ↑─────↑
|
|
555
|
+
# DID = 0x0509 LE
|
|
556
|
+
|
|
557
|
+
# Device read response on 0x451 — ISO-TP total = 192 bytes
|
|
558
|
+
451 [8] 10 C0 77 34 36 42 01 82
|
|
559
|
+
441 [8] 30 00 05 00 00 00 00 00 ← Flow Control from client
|
|
560
|
+
451 [8] 21 09 05 B0 C1 B5 00 00
|
|
561
|
+
# ↑─────↑ ↑────────↑
|
|
562
|
+
# DID echo len=181 (0xC1 escape)
|
|
563
|
+
451 [8] 22 ... (26 frames total, SN wraps 0x2F → 0x20, padded with 0x55)
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
### Device-initiated Service 77 (CTR = 0x0000)
|
|
569
|
+
|
|
570
|
+
The device can initiate Service 77 frames toward the client, always with
|
|
571
|
+
session counter `0x0000`. Two distinct patterns have been observed:
|
|
572
|
+
|
|
573
|
+
**Pattern A — Cross-device synchronization (echo)**
|
|
574
|
+
|
|
575
|
+
When a client writes a value, the device immediately propagates that write to
|
|
576
|
+
all other known devices using the same CAN-ID offset. The payload (DID + data)
|
|
577
|
+
is identical to the client's write; only the counter is reset to zero.
|
|
578
|
+
Confirmed across multiple traces and DIDs (e.g. `0x01F8`, `0x044D`).
|
|
579
|
+
|
|
580
|
+
Example: client `0x682` writes DID `0x01F8`. Immediately after confirming,
|
|
581
|
+
device `0x692` pushes the same DID/data back to `0x682`, and simultaneously
|
|
582
|
+
`0x693` pushes the identical frame to `0x683`:
|
|
583
|
+
|
|
584
|
+
```
|
|
585
|
+
# Client write
|
|
586
|
+
682 10 17 77 0D 00 43 01 82 ← FF, CTR=0x000D
|
|
587
|
+
...
|
|
588
|
+
692 04 77 0D 00 44 55 55 55 ← confirm, CTR=0x000D ✓
|
|
589
|
+
|
|
590
|
+
# Immediate cross-device sync (CTR=0x0000 in both)
|
|
591
|
+
692 10 17 77 00 00 43 01 82 ← device pushes same DID back to 682
|
|
592
|
+
693 10 17 77 00 00 43 01 82 ← sibling device pushes to 683 simultaneously
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Pattern B — Related-value notification**
|
|
596
|
+
|
|
597
|
+
After processing a write, the device pushes back a set of related DIDs whose
|
|
598
|
+
values were affected by (or are logically associated with) the write. These
|
|
599
|
+
pushed DIDs are different from the written DID.
|
|
600
|
+
|
|
601
|
+
Example: client `0x441` writes DID `0x08B2` (2226). Before sending the
|
|
602
|
+
confirmation, device `0x451` pushes back four related DIDs:
|
|
603
|
+
|
|
604
|
+
| Pushed DID | Decimal | Data bytes |
|
|
605
|
+
|---|---|---|
|
|
606
|
+
| `0x0643` | 1603 | 4 |
|
|
607
|
+
| `0x069A` | 1690 | 17 |
|
|
608
|
+
| `0x0720` | 1824 | 16 |
|
|
609
|
+
| `0x072C` | 1836 | 4 |
|
|
610
|
+
|
|
611
|
+
The confirmation (`0x44`) for the original write arrives *after* all the
|
|
612
|
+
device-initiated pushes have been sent. This pattern is reproducible: the same
|
|
613
|
+
four DIDs are pushed on every write cycle to DID `0x08B2`, with data values
|
|
614
|
+
that reflect the current device state at the time of the push.
|
|
615
|
+
|
|
616
|
+
For large pushed payloads the ISO-TP sequence number wraps normally
|
|
617
|
+
(`0x2F → 0x20`). Payloads up to 123 bytes (19 CFs) have been observed for
|
|
618
|
+
device-initiated pushes on some DID/channel combinations.
|
|
619
|
+
|
|
620
|
+
**Short pushes (1-byte data, no length code prefix):** For data points with a
|
|
621
|
+
1-byte value whose byte representation has high nibble < `0x8`, the reassembled
|
|
622
|
+
S77 payload is exactly 9 bytes (8-byte header + 1 data byte) and the data byte
|
|
623
|
+
sits at position 8 without a preceding length code. Example:
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
# ISO-TP FF (total = 9 bytes)
|
|
627
|
+
692 [8] 10 09 77 00 00 43 01 82
|
|
628
|
+
# ISO-TP CF1 (need 3 more bytes: EF 06 2B)
|
|
629
|
+
692 [8] 21 EF 06 2B 55 55 55 55
|
|
630
|
+
# ↑────↑ ↑
|
|
631
|
+
# DID LE data = 0x2B (43 decimal)
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Reassembled: `77 00 00 43 01 82 EF 06 2B` — DID `0x06EF`, 1-byte value `0x2B`.
|
|
635
|
+
There is no length code byte; `0x2B` (high nibble `2` < `0x8`) is the value itself.
|
|
636
|
+
|
|
637
|
+
**Summary: how to identify device-initiated frames**
|
|
638
|
+
|
|
639
|
+
| Field | Client write | Device push |
|
|
640
|
+
|---|---|---|
|
|
641
|
+
| CTR | Running counter (≠ 0) | Always `0x0000` |
|
|
642
|
+
| Direction | REQUEST_CH → RESPONSE_CH | RESPONSE_CH → REQUEST_CH |
|
|
643
|
+
| DID | What the client wants to write | Echo of client's DID (Pattern A) or related DID (Pattern B) |
|
|
644
|
+
|
|
645
|
+
### Anomaly: 4-byte Service 77 frames
|
|
646
|
+
|
|
647
|
+
On multiple CAN-ID pairs, 4-byte Service 77 frames have been observed that
|
|
648
|
+
are too short to carry a DID or data:
|
|
649
|
+
|
|
650
|
+
```
|
|
651
|
+
681 04 77 C9 11 21 00 00 00 ← client-side (00 padding)
|
|
652
|
+
691 04 77 C9 11 22 55 55 55 ← device-side (55 padding), ~3 ms later
|
|
653
|
+
|
|
654
|
+
682 04 77 41 75 21 55 55 55 ← client-side (55 padding)
|
|
655
|
+
692 04 77 41 75 22 55 55 55 ← device-side (55 padding), 1 ms later
|
|
656
|
+
|
|
657
|
+
683 04 77 D4 11 21 00 00 00 ← client-side (00 padding)
|
|
658
|
+
693 04 77 D4 11 22 55 55 55 ← device-side (55 padding), 14 ms later
|
|
659
|
+
|
|
660
|
+
686 04 77 08 6F 21 CC CC CC ← client-side (CC padding)
|
|
661
|
+
696 04 77 08 6F 22 55 55 55 ← device-side (55 padding), 1 ms later
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Payload: `[0x77] [CTR_L] [CTR_H] [0x21]` (client) / `[0x77] [CTR_L] [CTR_H] [0x22]` (device)
|
|
665
|
+
|
|
666
|
+
Observations:
|
|
667
|
+
|
|
668
|
+
- `0x21` / `0x22` are consistent across all four channel pairs.
|
|
669
|
+
- The CTR in each pair is always the global session counter incremented by
|
|
670
|
+
exactly 1 after the preceding write on that same channel (e.g. CTR=0x7540
|
|
671
|
+
for a write, CTR=0x7541 for the 4-byte frame immediately after).
|
|
672
|
+
- The CAN padding byte on the **client** side is inconsistent: `0x00` on
|
|
673
|
+
channels 681 and 683, `0x55` on 682, `0xCC` on 686. The device side always
|
|
674
|
+
pads with `0x55`. This suggests the frames originate from different software
|
|
675
|
+
components or firmware generations.
|
|
676
|
+
- These frames appear between write batches on all active channels, not only
|
|
677
|
+
after specific writes.
|
|
678
|
+
|
|
679
|
+
**Hypothesis:** byte 3 = `0x21` / `0x22` is a session-level keepalive or
|
|
680
|
+
commit signal, sent by the client after each write (or write batch) to confirm
|
|
681
|
+
that no further writes are pending in this slot, with the device acknowledging.
|
|
682
|
+
The CTR+1 spacing supports this: the client "spends" one CTR value on the
|
|
683
|
+
keepalive before moving to the next write cycle.
|
|
684
|
+
|
|
685
|
+
### CAN-ID sharing and protocol disambiguation
|
|
686
|
+
|
|
687
|
+
CAN-IDs `0x451` (Vitocharge VX3) and `0x693` (Vitocal 250) are shared between
|
|
688
|
+
the Collect protocol and the Service 77 response channel. Both protocols produce
|
|
689
|
+
frames with sequence byte `0x21` as byte 0, making raw inspection ambiguous.
|
|
690
|
+
|
|
691
|
+
**Disambiguation rule (ISO-TP state tracking):**
|
|
692
|
+
|
|
693
|
+
A frame with byte 0 = `0x21` on `0x451` or `0x693` is:
|
|
694
|
+
|
|
695
|
+
- **Service 77 CF1** — if a First Frame (`0x1x`) from the same CAN-ID has
|
|
696
|
+
been seen without a matching `0x21` CF1 consuming it yet.
|
|
697
|
+
- **Collect start frame (FF)** — if no ISO-TP FF is currently open for that
|
|
698
|
+
CAN-ID.
|
|
699
|
+
|
|
700
|
+
An implementation must maintain one boolean flag per CAN-ID: "FF open".
|
|
701
|
+
Set it on `0x1x`; clear it on `0x21` (CF1 consumed) or `0x2x` for x > 1.
|
|
702
|
+
|
|
703
|
+
**Operational modes:**
|
|
704
|
+
|
|
705
|
+
In practice the two protocols appear to be functionally exclusive by mode:
|
|
706
|
+
|
|
707
|
+
| Mode | Dominant protocol |
|
|
708
|
+
|---|---|
|
|
709
|
+
| Normal operation (passive/read-only) | Collect autonomous broadcasts |
|
|
710
|
+
| Active service session (writes in progress) | Service 77 device-initiated pushes |
|
|
711
|
+
|
|
712
|
+
Both distribute the same underlying data-point values. Service 77 device pushes
|
|
713
|
+
are addressed to specific client channels (with ISO-TP ACK); Collect broadcasts
|
|
714
|
+
are unaddressed and require no flow control. A receiver handling both protocols
|
|
715
|
+
on the same CAN-ID will typically observe one or the other depending on whether
|
|
716
|
+
a write session is active on the bus.
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
### Service 77 opcode summary
|
|
721
|
+
|
|
722
|
+
All Service 77 frames start with SID `0x77`. The byte or bytes immediately
|
|
723
|
+
following the session counter (bytes 3–5 of the reassembled payload) identify
|
|
724
|
+
the frame type:
|
|
725
|
+
|
|
726
|
+
| Bytes 3–5 | Total payload | Direction | Meaning |
|
|
727
|
+
|---|---|---|---|
|
|
728
|
+
| `43 01 82` | ≥ 10 bytes | Client → Device | **Write request** (data follows) |
|
|
729
|
+
| `0x44` (byte 3 only) | 4 bytes | Device → Client | **Write confirmation** (echoes CTR) |
|
|
730
|
+
| `43 01 82`, CTR = `0x0000` | ≥ 10 bytes | Device → Client | **Push / sync** (device-initiated) |
|
|
731
|
+
| `0x21` (byte 3 only) | 4 bytes | Client → Device | **Session keepalive** request |
|
|
732
|
+
| `0x22` (byte 3 only) | 4 bytes | Device → Client | **Session keepalive** response |
|
|
733
|
+
| `41 01 82` | 8 bytes | Client → Device | **Read request** (DID only, no data) |
|
|
734
|
+
| `42 01 82` | ≥ 10 bytes | Device → Client | **Read response** (echoes CTR + DID, data follows) |
|
|
735
|
+
|
|
393
736
|
---
|
|
394
737
|
|
|
395
738
|
# Energy Meters – E380 CA and E3100CB
|
|
@@ -546,3 +889,33 @@ can0 569 [8] XX XX XX 04 D0 07 00 00
|
|
|
546
889
|
```
|
|
547
890
|
|
|
548
891
|
Decoded (signed Int16, scale 1): `0x07D0` = 2000 W.
|
|
892
|
+
|
|
893
|
+
---
|
|
894
|
+
|
|
895
|
+
# External CAN bus — observed CAN-IDs
|
|
896
|
+
|
|
897
|
+
The external CAN bus connects the Vitocal 250 indoor unit to the Vitocharge VX3
|
|
898
|
+
and the E380 energy meter. The table below summarises all CAN-IDs observed in a
|
|
899
|
+
one-minute passive capture; it serves as a reference for tools that need to
|
|
900
|
+
filter or ignore non-Viessmann traffic.
|
|
901
|
+
|
|
902
|
+
| CAN-ID(s) | Protocol | Meaning |
|
|
903
|
+
|---|---|---|
|
|
904
|
+
| `0x441` ↔ `0x451` | Service 77 | S77 request/response for Vitocharge VX3 (`tx = 0x43F`); also carries Collect broadcasts from VX3 on `0x451` |
|
|
905
|
+
| `0x250`–`0x25D` | E380 CA | Energy meter broadcasts (see E380 section above) |
|
|
906
|
+
| `0x761`, `0x747`, `0x701` | CANopen Heartbeat | Node 97 (E380 CA), Node 71 (VX3), Node 1 |
|
|
907
|
+
| `0x271` | CANopen NMT/PDO | Node 0x71 (VX3) |
|
|
908
|
+
| `0x647` ↔ `0x5C7` | CANopen SDO | Client/Server, Node 71 (VX3) |
|
|
909
|
+
| `0x661` ↔ `0x5E1` | CANopen SDO | Client/Server, Node 97 (E380) |
|
|
910
|
+
| `0x6A1` ↔ `0x6B1` | UDS ReadDataByIdentifier | Periodic read of DID `0x2707` approximately every 15 s |
|
|
911
|
+
| `0x541` ↔ `0x531` | Proprietary (SDO-like) | Unknown device; exact protocol not identified |
|
|
912
|
+
| `0x1FF`, `0x190` | Periodic counter | Incrementing timestamp/counter frames |
|
|
913
|
+
|
|
914
|
+
**Notes:**
|
|
915
|
+
|
|
916
|
+
- Only `0x441`/`0x451` carries Service 77 traffic on the external bus. No
|
|
917
|
+
additional S77 pairs were found.
|
|
918
|
+
- S77-READ transactions (Client-ID `41 01 82`) have been observed on `0x441`
|
|
919
|
+
reading DID `0x0509` (181 bytes, `0xC1` escape) approximately every 5 s.
|
|
920
|
+
- CANopen node IDs follow the standard formula: heartbeat CAN-ID = `0x700 +
|
|
921
|
+
node`, SDO client = `0x600 + node`, SDO server = `0x580 + node`.
|
package/main.js
CHANGED
|
@@ -16,7 +16,13 @@
|
|
|
16
16
|
const utils = require('@iobroker/adapter-core');
|
|
17
17
|
|
|
18
18
|
// Loading modules:
|
|
19
|
-
|
|
19
|
+
let can = null;
|
|
20
|
+
let canLoadError = null;
|
|
21
|
+
try {
|
|
22
|
+
can = require('socketcan');
|
|
23
|
+
} catch (e) {
|
|
24
|
+
canLoadError = e;
|
|
25
|
+
}
|
|
20
26
|
const storage = require('./lib/storage');
|
|
21
27
|
const E3DidsDict = require('./lib/didsE3.json');
|
|
22
28
|
const E3DidsVarDict = require('./lib/didsE3var.json');
|
|
@@ -100,6 +106,23 @@ class E3oncan extends utils.Adapter {
|
|
|
100
106
|
async onReady() {
|
|
101
107
|
// Initialize your adapter here
|
|
102
108
|
|
|
109
|
+
if (!can) {
|
|
110
|
+
if (
|
|
111
|
+
canLoadError &&
|
|
112
|
+
/compiled against a different Node\.js version|did not self-register/i.test(canLoadError.message)
|
|
113
|
+
) {
|
|
114
|
+
this.log.error('Native module socketcan was compiled for a different Node.js version.');
|
|
115
|
+
this.log.error(
|
|
116
|
+
'Fix: stop the adapter, run iob rebuild on the command line, then restart the adapter.',
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
this.log.error(
|
|
120
|
+
`Failed to load native module socketcan: ${canLoadError ? canLoadError.message : 'unknown error'}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
await this.log.info(`Startup of instance ${this.namespace}: Starting.`);
|
|
104
127
|
//await this.log.debug('this.config:');
|
|
105
128
|
//await this.log.debug(JSON.stringify(this.config));
|
package/package.json
CHANGED