iobroker.autodarts 0.2.2 → 0.3.1

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
@@ -23,10 +23,13 @@ It also provides:
23
23
 
24
24
  - `visit.score`: Total score of the last complete visit (3 darts).
25
25
  - `throw.current`: Numeric score of the last thrown dart.
26
- - `throw.isTriple`: Boolean flag that only turns true for triple hits above a configurable score threshold.
26
+ - `throw.isTriple`: Boolean flag that turns true for triple hits within a configurable segment range (e.g. 1–20)
27
27
  - `throw.isBullseye`: Boolean flag that only turns true for bullseye hits.
28
28
  - `system.boardVersion`: Reported Board Manager version.
29
29
  - `system.cam0/1/2`: JSON with camera settings (width, height, fps).
30
+ - `status.trafficLightColor`: HEX color of the current board status.
31
+ - `status.trafficLightState`: `green` (player may throw), `yellow` (remove darts), `red` (board error).
32
+
30
33
 
31
34
  ## What this adapter does NOT do
32
35
 
@@ -56,6 +59,13 @@ In the adapter settings, enter:
56
59
  <!--
57
60
  ### **WORK IN PROGRESS**
58
61
  -->
62
+ ### 0.3.1 (2025-12-27)
63
+ - Changed: Object creation now uses extendObjectAsync with proper roles and types instead of setObjectNotExistsAsync.
64
+
65
+ ### 0.3.0 (2025-12-26)
66
+ - Added traffic light datapoints (`status.trafficLightColor`, `status.trafficLightState`) mapped from Board Manager status (`Throw` / `Takeout` / connection errors).
67
+ - Refactored code: visit handling, throw handling (triple / bull) and traffic light logic moved to separate modules.
68
+
59
69
  ### 0.2.2 (2025-12-25)
60
70
  - bugfix
61
71
 
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "autodarts",
4
- "version": "0.2.2",
4
+ "version": "0.3.1",
5
5
  "news": {
6
+ "0.3.1": {
7
+ "en": "Changed: Object creation now uses extendObjectAsync with proper roles and types instead of setObjectNotExistsAsync.",
8
+ "de": "Geändert: Objekt-Erstellung verwendet jetzt ExtendedObjectAsync mit richtigen Rollen und Typen anstelle von Set ObjectNotExistsAsync.",
9
+ "ru": "Изменилось: Создание объектов теперь использует extendObjectAsync с правильными ролями и типами вместо набора ObjectNotExistsAsync.",
10
+ "pt": "Alterado: Criação de objetos agora usa extendObjectAsync com papéis e tipos apropriados em vez de set ObjectNotExistsAsync.",
11
+ "nl": "Gewijzigd: Objectcreatie maakt nu gebruik van extendObjectAsync met juiste rollen en typen in plaats van set ObjectNietBestaatAsync.",
12
+ "fr": "Changement : La création d'objet utilise désormais extensionObjectAsync avec des rôles et types appropriés au lieu de set ObjetNotExistsAsync.",
13
+ "it": "Cambiato: Creazione oggetti ora utilizza extendObjectAsync con ruoli e tipi appropriati invece di set ObjectNotExistsAsync.",
14
+ "es": "Cambio: La creación de objetos ahora utiliza ExtendObjectAsync con roles y tipos adecuados en lugar de conjunto ObjectNoExistsAsync.",
15
+ "pl": "Zmieniono: Tworzenie obiektu wykorzystuje teraz extendObjectAsync z odpowiednimi rolami i typami zamiast zestawu ObjectNotExistsAsync.",
16
+ "uk": "Змінено: Створення об'єктів тепер використовує розширенняObjectAsync з відповідними ролями та типами замість набору Об'єктNotExistsAsync.",
17
+ "zh-cn": "已更改 : 对象创建现在使用扩展对象Async , 并配有合适的角色和类型而不是设定 Object NotExists Async. (原始内容存档于2018-09-31)."
18
+ },
19
+ "0.3.0": {
20
+ "en": "Added traffic light datapoints (`status.trafficLightColor`, `status.trafficLightState`) mapped from Board Manager status (`Throw` / `Takeout` / connection errors).\nRefactored code: visit handling, throw handling (triple / bull) and traffic light logic moved to separate modules.",
21
+ "de": "Es wurden Verkehrslichtdatenpunkte (`status.trafficLightColor`, `status.trafficLightState`) aus dem Status Board Manager (`Throw` / `Takeout` / Verbindungsfehler) abgebildet.\nRefactored Code: Besuchen Sie Handling, Wurf Handling (Triple / Bull) und Ampellogik in separate Module bewegt.",
22
+ "ru": "Добавлены точки данных светофора («status.trafficLightColor», «status.trafficLightState»), отображаемые из статуса менеджера совета директоров («Бросить» / «Выбрать» / ошибки подключения).\nРефакторированный код: обработка посещений, обработка бросков (тройной / бычий) и логика светофора перемещаются в отдельные модули.",
23
+ "pt": "Pontos de dados de semáforo adicionados (`status.trafficLightColor`, `status.trafficLightState`) mapeados a partir do status do Board Manager (`Throw` / `Takeout` / erros de conexão).\nCódigo refatorizado: manipulação de visita, manuseio de arremesso (triplo / touro) e lógica de semáforo movido para módulos separados.",
24
+ "nl": "Toegevoegd verkeerslicht datapoints (.\nRefactored code: bezoeken handling, gooien handling (triple / stier) en verkeerslicht logica verplaatst naar aparte modules.",
25
+ "fr": "Ajout de points de données de feux de circulation (`status.trafficLightColor`, `status.trafficLightState`) cartographié à partir de l'état du gestionnaire du conseil d'administration (`Throw` / `Takeout` / erreurs de connexion).\nCode refactoré: visite de la manipulation, la manipulation de lancer (triple / taureau) et la logique du feu de circulation déplacé vers des modules séparés.",
26
+ "it": "Aggiunti i datapoint del semaforo (`status.trafficLightColor`, `status.trafficLightState`) mappati dallo stato del Board Manager (`Throw` / `Takeout` / errori di connessione).\nCodice Refactored: visit handling, pitch handling (triple / bull) e logica del semaforo spostato a moduli separati.",
27
+ "es": "Los puntos de datos de la luz de tráfico ( \"status.trafficLightColor \" , \"status.trafficLightState \" ) se mapearon con el estado de administrador de la Junta ( \" THrow \" / \" Takeout \" / errores de conexión).\nCódigo refactorizado: manejo de visitas, manejo de tiro (triple / toro) y lógica de tráfico se movió a módulos separados.",
28
+ "pl": "Dodano punkty danych świateł drogowych (\"status.traffic LightColor\", \"status.traffic LightState\"), które zostały odwzorowane ze statusu zarządu (\"Throw\" / \"Takeout\" / błędy połączeń).\nZmieniony kod: obsługa odwiedzin, obsługa rzutów (potrójny / byk) i logika światła drogowego przeniesiona do oddzielnych modułów.",
29
+ "uk": "Додано точки світла трафіку (`status.trafficLightColor`, `status.trafficLightState`) на карті Board Manager (`Throw` / `Takeout` / помилки підключення).\nРефакторний код: візитна обробка, кидання (стриптиз / бик) і логіка трафіку переходить на окремі модулі.",
30
+ "zh-cn": "增加从董事会管理者身份绘制的交通灯数据点(`Status.trafficLightColor ' ,`Status.trafficLight Station ' ) (`扔'/`Takeout ' /连接错误).\n重构代码:访问处理,投掷处理(三重/公牛)和交通灯逻辑移动到单独的模块."
31
+ },
6
32
  "0.2.2": {
7
33
  "en": "bugfix",
8
34
  "de": "fehlerbehebung",
@@ -67,32 +93,6 @@
67
93
  "pl": "fix",
68
94
  "uk": "фіксація",
69
95
  "zh-cn": "固定"
70
- },
71
- "0.0.12": {
72
- "en": "fix",
73
- "de": "fixieren",
74
- "ru": "исправлять",
75
- "pt": "corrigir",
76
- "nl": "fix",
77
- "fr": "correction",
78
- "it": "correzione",
79
- "es": "arregle",
80
- "pl": "fix",
81
- "uk": "фіксація",
82
- "zh-cn": "固定"
83
- },
84
- "0.0.10": {
85
- "en": "fix",
86
- "de": "fixieren",
87
- "ru": "исправлять",
88
- "pt": "corrigir",
89
- "nl": "fix",
90
- "fr": "correction",
91
- "it": "correzione",
92
- "es": "arregle",
93
- "pl": "fix",
94
- "uk": "фіксація",
95
- "zh-cn": "固定"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/lib/throw.js ADDED
@@ -0,0 +1,149 @@
1
+ // lib/throw.js
2
+ "use strict";
3
+
4
+ /**
5
+ * Legt Throw-Channel und States an.
6
+ *
7
+ * @param {import("@iobroker/adapter-core").Adapter} adapter Adapter-Instanz dieses Autodarts-Adapters
8
+ */
9
+ async function init(adapter) {
10
+ await adapter.extendObjectAsync("throw", {
11
+ type: "channel",
12
+ common: {
13
+ name: {
14
+ en: "Current throw",
15
+ de: "Aktueller Wurf",
16
+ },
17
+ },
18
+ native: {},
19
+ });
20
+
21
+ await adapter.extendObjectAsync("throw.current", {
22
+ type: "state",
23
+ common: {
24
+ name: {
25
+ en: "Current dart score",
26
+ de: "Punkte aktueller Dart",
27
+ },
28
+ type: "number",
29
+ role: "value",
30
+ read: true,
31
+ write: false,
32
+ desc: {
33
+ en: "Score of the last dart",
34
+ de: "Punktzahl des letzten Dart",
35
+ },
36
+ },
37
+ native: {},
38
+ });
39
+
40
+ await adapter.extendObjectAsync("throw.isTriple", {
41
+ type: "state",
42
+ common: {
43
+ name: {
44
+ en: "Triple hit",
45
+ de: "Triple getroffen",
46
+ },
47
+ type: "boolean",
48
+ role: "indicator",
49
+ read: true,
50
+ write: false,
51
+ desc: {
52
+ en: "true if the dart hit a triple segment (and passes score range)",
53
+ de: "true, wenn ein Dart ein Triple-Segment getroffen hat (und innerhalb der Punkterange liegt)",
54
+ },
55
+ },
56
+ native: {},
57
+ });
58
+
59
+ await adapter.extendObjectAsync("throw.isBullseye", {
60
+ type: "state",
61
+ common: {
62
+ name: {
63
+ en: "Bullseye hit",
64
+ de: "Bullseye getroffen",
65
+ },
66
+ type: "boolean",
67
+ role: "indicator",
68
+ read: true,
69
+ write: false,
70
+ desc: {
71
+ en: "true if the dart hit the bull or bullseye",
72
+ de: "true, wenn ein Dart Bull oder Bullseye getroffen hat",
73
+ },
74
+ },
75
+ native: {},
76
+ });
77
+ }
78
+
79
+ /**
80
+ * Aktualisiert die Throw-States für den letzten Dart inkl. Triple/Bull-Flags.
81
+ *
82
+ * @param {import("@iobroker/adapter-core").Adapter} adapter Adapter-Instanz dieses Autodarts-Adapters
83
+ * @param {any} lastDart Letzter Dart aus state.throws
84
+ */
85
+ async function updateThrow(adapter, lastDart) {
86
+ const score = adapter.calcScore(lastDart);
87
+ const segment = lastDart?.segment?.number || 0;
88
+
89
+ // Score-Range aus Adapter übernehmen
90
+ let minScore = adapter.tripleMinScoreRuntime;
91
+ let maxScore = adapter.tripleMaxScoreRuntime;
92
+
93
+ if (!Number.isFinite(minScore)) {
94
+ // @ts-expect-error tripleMinScore is defined in io-package.json but not in AdapterConfig
95
+ minScore = Number(adapter.config.tripleMinScore) || 1;
96
+ }
97
+ if (!Number.isFinite(maxScore)) {
98
+ // @ts-expect-error tripleMaxScore is defined in io-package.json but not in AdapterConfig
99
+ maxScore = Number(adapter.config.tripleMaxScore) || 20;
100
+ }
101
+
102
+ if (minScore > maxScore) {
103
+ const tmp = minScore;
104
+ minScore = maxScore;
105
+ maxScore = tmp;
106
+ }
107
+
108
+ const isTriple =
109
+ !!lastDart?.segment && lastDart.segment.multiplier === 3 && segment >= minScore && segment <= maxScore;
110
+
111
+ const segName = (lastDart?.segment?.name || "").toUpperCase();
112
+ const isBullseye = segName.includes("BULL") || lastDart?.segment?.number === 25;
113
+
114
+ await adapter.setStateAsync("throw.current", { val: score, ack: true });
115
+
116
+ // Timer für Auto-Reset abbrechen
117
+ if (adapter.tripleResetTimer) {
118
+ clearTimeout(adapter.tripleResetTimer);
119
+ adapter.tripleResetTimer = null;
120
+ }
121
+ if (adapter.bullResetTimer) {
122
+ clearTimeout(adapter.bullResetTimer);
123
+ adapter.bullResetTimer = null;
124
+ }
125
+
126
+ await adapter.setStateAsync("throw.isTriple", { val: isTriple, ack: true });
127
+ await adapter.setStateAsync("throw.isBullseye", { val: isBullseye, ack: true });
128
+
129
+ const timeoutMs = Number(adapter.triggerResetMsRuntime) || 0;
130
+ if (timeoutMs > 0) {
131
+ if (isTriple) {
132
+ adapter.tripleResetTimer = setTimeout(() => {
133
+ adapter.setState("throw.isTriple", { val: false, ack: true });
134
+ adapter.tripleResetTimer = null;
135
+ }, timeoutMs);
136
+ }
137
+ if (isBullseye) {
138
+ adapter.bullResetTimer = setTimeout(() => {
139
+ adapter.setState("throw.isBullseye", { val: false, ack: true });
140
+ adapter.bullResetTimer = null;
141
+ }, timeoutMs);
142
+ }
143
+ }
144
+ }
145
+
146
+ module.exports = {
147
+ init,
148
+ updateThrow,
149
+ };
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Legt die beiden Ampel-Datenpunkte an.
5
+ * Channel: autodarts.x.status
6
+ * States:
7
+ * - trafficLightColor (HEX)
8
+ * - trafficLightState (String)
9
+ *
10
+ * @param {import("@iobroker/adapter-core").Adapter} adapter Adapter-Instanz dieses Autodarts-Adapters
11
+ */
12
+ async function init(adapter) {
13
+ const baseId = "status";
14
+
15
+ // Channel anlegen
16
+ await adapter.extendObjectAsync(baseId, {
17
+ type: "channel",
18
+ common: {
19
+ name: "Status",
20
+ },
21
+ native: {},
22
+ });
23
+
24
+ // HEX-Farbe
25
+ await adapter.extendObjectAsync(`${baseId}.trafficLightColor`, {
26
+ type: "state",
27
+ common: {
28
+ name: {
29
+ en: "Traffic light color (HEX)",
30
+ de: "Ampelfarbe (HEX)",
31
+ },
32
+ type: "string",
33
+ role: "level.color.rgb",
34
+ read: true,
35
+ write: false,
36
+ desc: {
37
+ en: "HEX color of the traffic light status",
38
+ de: "HEX-Farbe des Ampelstatus",
39
+ },
40
+ },
41
+ native: {},
42
+ });
43
+
44
+ // Text-Status
45
+ await adapter.extendObjectAsync(`${baseId}.trafficLightState`, {
46
+ type: "state",
47
+ common: {
48
+ name: {
49
+ en: "Traffic light state",
50
+ de: "Ampelstatus",
51
+ },
52
+ type: "string",
53
+ role: "text",
54
+ read: true,
55
+ write: false,
56
+ desc: {
57
+ en: "Green = player may throw, yellow = remove darts, red = board error",
58
+ de: "Grün = Spieler darf werfen, Gelb = Darts entfernen, Rot = Boardfehler",
59
+ },
60
+ states: {
61
+ green: "Player may throw / Spieler darf werfen",
62
+ yellow: "Remove darts / Darts entfernen",
63
+ red: "Board error / Boardfehler",
64
+ },
65
+ },
66
+ native: {},
67
+ });
68
+
69
+ // Optional: Initial auf "red" setzen
70
+ await setStatus(adapter, "red");
71
+ }
72
+
73
+ /**
74
+ * Setzt Ampel-Status.
75
+ *
76
+ * @param {import("@iobroker/adapter-core").Adapter} adapter Adapter-Instanz dieses Autodarts-Adapters
77
+ * @param {"green"|"yellow"|"red"} status Gewünschter Ampelstatus (green/yellow/red)
78
+ */
79
+ async function setStatus(adapter, status) {
80
+ let colorHex = "#FF0000"; // default rot
81
+
82
+ if (status === "green") {
83
+ colorHex = "#00FF00";
84
+ } else if (status === "yellow") {
85
+ colorHex = "#FFFF00";
86
+ } else if (status === "red") {
87
+ colorHex = "#FF0000";
88
+ } else {
89
+ adapter.log.warn(`Unknown traffic light status: ${status}, using red`);
90
+ status = "red";
91
+ }
92
+
93
+ const baseId = "status";
94
+
95
+ await adapter.setStateAsync(`${baseId}.trafficLightColor`, {
96
+ val: colorHex,
97
+ ack: true,
98
+ });
99
+
100
+ await adapter.setStateAsync(`${baseId}.trafficLightState`, {
101
+ val: status,
102
+ ack: true,
103
+ });
104
+ }
105
+
106
+ module.exports = {
107
+ init,
108
+ setStatus,
109
+ };
package/lib/visit.js ADDED
@@ -0,0 +1,68 @@
1
+ // lib/visit.js
2
+ "use strict";
3
+
4
+ /**
5
+ * Legt Visit-Channel und States an.
6
+ *
7
+ * @param {import("@iobroker/adapter-core").Adapter} adapter Adapter-Instanz dieses Autodarts-Adapters
8
+ */
9
+ async function init(adapter) {
10
+ await adapter.extendObjectAsync("visit", {
11
+ type: "channel",
12
+ common: {
13
+ name: {
14
+ en: "Current visit",
15
+ de: "Aktuelle Aufnahme",
16
+ },
17
+ },
18
+ native: {},
19
+ });
20
+
21
+ await adapter.extendObjectAsync("visit.score", {
22
+ type: "state",
23
+ common: {
24
+ name: {
25
+ en: "Visit score (Total of 3 darts)",
26
+ de: "Aufnahme (Summe dreier Darts)",
27
+ },
28
+ type: "number",
29
+ role: "value",
30
+ read: true,
31
+ write: false,
32
+ desc: {
33
+ en: "Total of the last complete visit",
34
+ de: "Summe der letzten vollständigen Aufnahme",
35
+ },
36
+ },
37
+ native: {},
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Berechnet und schreibt die Visit-Summe, wenn ein Visit abgeschlossen ist.
43
+ *
44
+ * @param {import("@iobroker/adapter-core").Adapter} adapter Adapter-Instanz dieses Autodarts-Adapters
45
+ * @param {any[]} currentThrows Array der aktuellen Würfe aus /api/state
46
+ * @param {number} lastThrowsCount Anzahl Würfe beim letzten Poll (adapter.lastThrowsCount)
47
+ * @returns {Promise<number>} Neue lastThrowsCount, damit du ihn im Adapter speichern kannst
48
+ */
49
+ async function updateVisit(adapter, currentThrows, lastThrowsCount) {
50
+ const currentCount = currentThrows.length;
51
+
52
+ // Nur schreiben, wenn:
53
+ // - genau 3 Darts geworfen wurden
54
+ // - vorher weniger als 3 waren (Visit gerade abgeschlossen)
55
+ if (currentCount === 3 && lastThrowsCount < 3) {
56
+ const lastThree = currentThrows.slice(-3);
57
+ const visitSum = lastThree.reduce((sum, dart) => sum + adapter.calcScore(dart), 0);
58
+
59
+ await adapter.setStateAsync("visit.score", { val: visitSum, ack: true });
60
+ }
61
+
62
+ return currentCount;
63
+ }
64
+
65
+ module.exports = {
66
+ init,
67
+ updateVisit,
68
+ };
package/main.js CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  const utils = require("@iobroker/adapter-core");
4
4
  const http = require("http");
5
+ const throwLogic = require("./lib/throw");
6
+ const visit = require("./lib/visit");
7
+ const trafficLight = require("./lib/trafficLight");
5
8
 
6
9
  class Autodarts extends utils.Adapter {
7
10
  constructor(options) {
@@ -23,7 +26,7 @@ class Autodarts extends utils.Adapter {
23
26
  this.tripleMinScoreRuntime = null; // Laufzeitwert für Triple-Minschwelle
24
27
  this.tripleMaxScoreRuntime = null; // Laufzeitwert für Triple-Maxschwelle
25
28
 
26
- // NEU: Reset-Timeout + Timer für isTriple/isBulleye
29
+ // NEU: Reset-Timeout + Timer für isTriple/isBullseye
27
30
  this.triggerResetMsRuntime = null;
28
31
  this.tripleResetTimer = null;
29
32
  this.bullResetTimer = null;
@@ -46,108 +49,14 @@ class Autodarts extends utils.Adapter {
46
49
  /** @ts-expect-error triggerResetMs is defined in io-package.json/jsonConfig but not in AdapterConfig */
47
50
  this.config.triggerResetMs ??= 0; // 0 = kein Auto-Reset
48
51
 
49
- // Visit-Struktur anlegen
50
- await this.setObjectNotExistsAsync("visit", {
51
- type: "channel",
52
- common: {
53
- name: {
54
- en: "Current visit",
55
- de: "Aktuelle Aufnahme",
56
- },
57
- },
58
- native: {},
59
- });
60
-
61
- await this.setObjectNotExistsAsync("visit.score", {
62
- type: "state",
63
- common: {
64
- name: {
65
- en: "Visit score (Total of 3 darts)",
66
- de: "Aufnahme (Summe dreier Darts)",
67
- },
68
- type: "number",
69
- role: "value",
70
- read: true,
71
- write: false,
72
- desc: {
73
- en: "Total of the last complete visit",
74
- de: "Summe der letzten vollständigen Aufnahme",
75
- },
76
- },
77
- native: {},
78
- });
79
-
80
- // Throw-Channel und States
81
- await this.setObjectNotExistsAsync("throw", {
82
- type: "channel",
83
- common: {
84
- name: {
85
- en: "Current throw",
86
- de: "Aktueller Wurf",
87
- },
88
- },
89
- native: {},
90
- });
52
+ // Visit-Struktur anlegen (ausgelagert)
53
+ await visit.init(this);
91
54
 
92
- await this.setObjectNotExistsAsync("throw.current", {
93
- type: "state",
94
- common: {
95
- name: {
96
- en: "Current dart score",
97
- de: "Punkte aktueller Dart",
98
- },
99
- type: "number",
100
- role: "value",
101
- read: true,
102
- write: false,
103
- desc: {
104
- en: "Score of the last dart",
105
- de: "Punktzahl des letzten Dart",
106
- },
107
- },
108
- native: {},
109
- });
110
-
111
- await this.setObjectNotExistsAsync("throw.isTriple", {
112
- type: "state",
113
- common: {
114
- name: {
115
- en: "Triple hit",
116
- de: "Triple getroffen",
117
- },
118
- type: "boolean",
119
- role: "indicator",
120
- read: true,
121
- write: false,
122
- desc: {
123
- en: "true if the dart hit a triple segment (and passes score range)",
124
- de: "true, wenn ein Dart ein Triple-Segment getroffen hat (und innerhalb der Punkterange liegt)",
125
- },
126
- },
127
- native: {},
128
- });
129
-
130
- await this.setObjectNotExistsAsync("throw.isBulleye", {
131
- type: "state",
132
- common: {
133
- name: {
134
- en: "Bulleye hit",
135
- de: "Bulleye getroffen",
136
- },
137
- type: "boolean",
138
- role: "indicator",
139
- read: true,
140
- write: false,
141
- desc: {
142
- en: "true if the dart hit the bull or bullseye",
143
- de: "true, wenn ein Dart Bull oder Bulleye getroffen hat",
144
- },
145
- },
146
- native: {},
147
- });
55
+ // Throw-Channel und States anlegen (ausgelagert)
56
+ await throwLogic.init(this);
148
57
 
149
58
  // Online-Datenpunkt
150
- await this.setObjectNotExistsAsync("online", {
59
+ await this.extendObjectAsync("online", {
151
60
  type: "state",
152
61
  common: {
153
62
  name: {
@@ -167,7 +76,7 @@ class Autodarts extends utils.Adapter {
167
76
  });
168
77
 
169
78
  // System-Channel und BoardVersion-Datenpunkt anlegen
170
- await this.setObjectNotExistsAsync("system", {
79
+ await this.extendObjectAsync("system", {
171
80
  type: "channel",
172
81
  common: {
173
82
  name: {
@@ -178,7 +87,7 @@ class Autodarts extends utils.Adapter {
178
87
  native: {},
179
88
  });
180
89
 
181
- await this.setObjectNotExistsAsync("system.boardVersion", {
90
+ await this.extendObjectAsync("system.boardVersion", {
182
91
  type: "state",
183
92
  common: {
184
93
  name: {
@@ -198,7 +107,7 @@ class Autodarts extends utils.Adapter {
198
107
  });
199
108
 
200
109
  // Kamera-Infos als JSON-States
201
- await this.setObjectNotExistsAsync("system.cam0", {
110
+ await this.extendObjectAsync("system.cam0", {
202
111
  type: "state",
203
112
  common: {
204
113
  name: {
@@ -217,7 +126,7 @@ class Autodarts extends utils.Adapter {
217
126
  native: {},
218
127
  });
219
128
 
220
- await this.setObjectNotExistsAsync("system.cam1", {
129
+ await this.extendObjectAsync("system.cam1", {
221
130
  type: "state",
222
131
  common: {
223
132
  name: {
@@ -236,7 +145,7 @@ class Autodarts extends utils.Adapter {
236
145
  native: {},
237
146
  });
238
147
 
239
- await this.setObjectNotExistsAsync("system.cam2", {
148
+ await this.extendObjectAsync("system.cam2", {
240
149
  type: "state",
241
150
  common: {
242
151
  name: {
@@ -256,7 +165,7 @@ class Autodarts extends utils.Adapter {
256
165
  });
257
166
 
258
167
  // Config-Channel und States für tripleMinScore / tripleMaxScore / triggerResetMs (zur Laufzeit änderbar)
259
- await this.setObjectNotExistsAsync("config", {
168
+ await this.extendObjectAsync("config", {
260
169
  type: "channel",
261
170
  common: {
262
171
  name: {
@@ -267,7 +176,7 @@ class Autodarts extends utils.Adapter {
267
176
  native: {},
268
177
  });
269
178
 
270
- await this.setObjectNotExistsAsync("config.tripleMinScore", {
179
+ await this.extendObjectAsync("config.tripleMinScore", {
271
180
  type: "state",
272
181
  common: {
273
182
  name: {
@@ -286,7 +195,7 @@ class Autodarts extends utils.Adapter {
286
195
  native: {},
287
196
  });
288
197
 
289
- await this.setObjectNotExistsAsync("config.tripleMaxScore", {
198
+ await this.extendObjectAsync("config.tripleMaxScore", {
290
199
  type: "state",
291
200
  common: {
292
201
  name: {
@@ -305,7 +214,7 @@ class Autodarts extends utils.Adapter {
305
214
  native: {},
306
215
  });
307
216
 
308
- await this.setObjectNotExistsAsync("config.triggerResetMs", {
217
+ await this.extendObjectAsync("config.triggerResetMs", {
309
218
  type: "state",
310
219
  common: {
311
220
  name: {
@@ -317,8 +226,8 @@ class Autodarts extends utils.Adapter {
317
226
  read: true,
318
227
  write: true,
319
228
  desc: {
320
- en: "Time in ms after which isTriple and isBulleye are reset to false",
321
- de: "Zeit in ms, nach der isTriple und isBulleye wieder auf false gesetzt werden",
229
+ en: "Time in ms after which isTriple and isBullseye are reset to false",
230
+ de: "Zeit in ms, nach der isTriple und isBullseye wieder auf false gesetzt werden",
322
231
  },
323
232
  },
324
233
  native: {},
@@ -343,11 +252,15 @@ class Autodarts extends utils.Adapter {
343
252
  val: this.tripleMaxScoreRuntime,
344
253
  ack: true,
345
254
  });
255
+
346
256
  await this.setStateAsync("config.triggerResetMs", {
347
257
  val: this.triggerResetMsRuntime,
348
258
  ack: true,
349
259
  });
350
260
 
261
+ // Ampel-States anlegen
262
+ await trafficLight.init(this);
263
+
351
264
  // Auf Änderungen am Config-State hören
352
265
  this.subscribeStates("config.tripleMinScore");
353
266
  this.subscribeStates("config.tripleMaxScore");
@@ -459,12 +372,19 @@ class Autodarts extends utils.Adapter {
459
372
  let data = "";
460
373
 
461
374
  res.on("data", chunk => (data += chunk));
462
- res.on("end", () => {
375
+ res.on("end", async () => {
463
376
  this.offline = false;
464
377
  this.setState("online", true, true); // Server erreichbar
465
378
 
466
379
  try {
467
380
  const state = JSON.parse(data);
381
+ const boardStatus = state.status || ""; // z.B. "Throw" oder "Takeout"
382
+
383
+ if (boardStatus === "Throw") {
384
+ await trafficLight.setStatus(this, "green");
385
+ } else if (boardStatus === "Takeout") {
386
+ await trafficLight.setStatus(this, "yellow");
387
+ }
468
388
 
469
389
  // Nur weiter, wenn throws existieren, Array ist und nicht leer
470
390
  if (!state.throws || !Array.isArray(state.throws) || state.throws.length === 0) {
@@ -472,7 +392,6 @@ class Autodarts extends utils.Adapter {
472
392
  }
473
393
 
474
394
  const currentThrows = state.throws;
475
- const currentCount = currentThrows.length;
476
395
 
477
396
  // Prüfen, ob sich die Würfe geändert haben
478
397
  const signature = JSON.stringify(
@@ -487,94 +406,12 @@ class Autodarts extends utils.Adapter {
487
406
  }
488
407
  this.lastSignature = signature;
489
408
 
490
- // letzten Dart in States schreiben
409
+ // letzten Dart in States schreiben (ausgelagert)
491
410
  const lastDart = currentThrows[currentThrows.length - 1];
492
- const score = this.calcScore(lastDart);
493
- const segment = lastDart?.segment?.number || 0;
494
-
495
- // Konfigurierte Score-Range für Triple-Flag:
496
- // zuerst Laufzeitwerte, Fallback auf Adapter-Config
497
- let minScore = this.tripleMinScoreRuntime;
498
- let maxScore = this.tripleMaxScoreRuntime;
499
-
500
- if (!Number.isFinite(minScore)) {
501
- // eslint-disable-next-line jsdoc/check-tag-names
502
- /** @ts-expect-error tripleMinScore is defined in io-package.json but not in AdapterConfig */
503
- minScore = Number(this.config.tripleMinScore) || 1;
504
- }
505
- if (!Number.isFinite(maxScore)) {
506
- // eslint-disable-next-line jsdoc/check-tag-names
507
- /** @ts-expect-error tripleMaxScore is defined in io-package.json but not in AdapterConfig */
508
- maxScore = Number(this.config.tripleMaxScore) || 20;
509
- }
510
-
511
- if (minScore > maxScore) {
512
- const tmp = minScore;
513
- minScore = maxScore;
514
- maxScore = tmp;
515
- }
411
+ await throwLogic.updateThrow(this, lastDart);
516
412
 
517
- // Triple nur, wenn:
518
- // - multiplier === 3
519
- // - score >= minScore
520
- // - score <= maxScore
521
- const isTriple =
522
- !!lastDart?.segment &&
523
- lastDart.segment.multiplier === 3 &&
524
- segment >= minScore &&
525
- segment <= maxScore;
526
-
527
- // Bulleye-Erkennung:
528
- // je nach Autodarts-API typischerweise über segment.name ("BULL", "DBULL", "SBULL")
529
- const segName = (lastDart?.segment?.name || "").toUpperCase();
530
- const isBulleye = segName.includes("BULL") || lastDart?.segment?.number === 25; // Fallback
531
-
532
- this.setState("throw.current", { val: score, ack: true });
533
-
534
- // vorhandene Timer abbrechen, damit der Reset immer ab letztem Treffer zählt
535
- if (this.tripleResetTimer) {
536
- clearTimeout(this.tripleResetTimer);
537
- this.tripleResetTimer = null;
538
- }
539
- if (this.bullResetTimer) {
540
- clearTimeout(this.bullResetTimer);
541
- this.bullResetTimer = null;
542
- }
543
-
544
- // Flags setzen
545
- this.setState("throw.isTriple", { val: isTriple, ack: true });
546
- this.setState("throw.isBulleye", { val: isBulleye, ack: true });
547
-
548
- // Auto-Reset nach triggerResetMs (0 = kein Auto-Reset)
549
- const timeoutMs = Number(this.triggerResetMsRuntime) || 0;
550
- if (timeoutMs > 0) {
551
- if (isTriple) {
552
- this.tripleResetTimer = setTimeout(() => {
553
- this.setState("throw.isTriple", { val: false, ack: true });
554
- this.tripleResetTimer = null;
555
- }, timeoutMs);
556
- }
557
- if (isBulleye) {
558
- this.bullResetTimer = setTimeout(() => {
559
- this.setState("throw.isBulleye", { val: false, ack: true });
560
- this.bullResetTimer = null;
561
- }, timeoutMs);
562
- }
563
- }
564
-
565
- // Nur schreiben, wenn:
566
- // - genau 3 Darts geworfen wurden
567
- // - vorher weniger als 3 waren (Visit gerade abgeschlossen)
568
- if (currentCount === 3 && this.lastThrowsCount < 3) {
569
- const lastThrows = currentThrows.slice(-3);
570
- const visitSum = lastThrows.reduce((sum, dart) => sum + this.calcScore(dart), 0);
571
-
572
- // WICHTIG: Immer schreiben, auch wenn Wert gleich bleibt
573
- this.setState("visit.score", { val: visitSum, ack: true });
574
- }
575
-
576
- // Zustand speichern
577
- this.lastThrowsCount = currentCount;
413
+ // Visit-Summe aktualisieren (ausgelagert)
414
+ this.lastThrowsCount = await visit.updateVisit(this, currentThrows, this.lastThrowsCount);
578
415
  } catch (e) {
579
416
  this.log.warn(`Autodarts API Fehler: ${e.message} | Daten: ${data.substring(0, 200)}...`);
580
417
  // Bei JSON-Fehler: Board war erreichbar, aber Antwort kaputt
@@ -583,20 +420,22 @@ class Autodarts extends utils.Adapter {
583
420
  });
584
421
  });
585
422
 
586
- req.on("error", () => {
423
+ req.on("error", async () => {
587
424
  if (!this.offline) {
588
425
  this.log.warn("Autodarts not reachable");
589
426
  this.offline = true;
590
427
  }
428
+ await trafficLight.setStatus(this, "red"); // Ampel = rot
591
429
  this.setState("online", false, true); // Server offline
592
430
  });
593
431
 
594
- req.on("timeout", () => {
432
+ req.on("timeout", async () => {
595
433
  req.destroy();
596
434
  if (!this.offline) {
597
435
  this.log.warn("Autodarts not reachable");
598
436
  this.offline = true;
599
437
  }
438
+ await trafficLight.setStatus(this, "red"); // Ampel = rot
600
439
  this.setState("online", false, true); // Server offline bei Timeout
601
440
  });
602
441
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.autodarts",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Autodarts Autoscoring",
5
5
  "author": "skvarel <skvarel@inventwo.com>",
6
6
  "contributors": [