iobroker.autodarts 0.2.2 → 0.3.0

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,10 @@ In the adapter settings, enter:
56
59
  <!--
57
60
  ### **WORK IN PROGRESS**
58
61
  -->
62
+ ### 0.3.0 (2025-12-26)
63
+ - Added traffic light datapoints (`status.trafficLightColor`, `status.trafficLightState`) mapped from Board Manager status (`Throw` / `Takeout` / connection errors).
64
+ - Refactored code: visit handling, throw handling (triple / bull) and traffic light logic moved to separate modules.
65
+
59
66
  ### 0.2.2 (2025-12-25)
60
67
  - bugfix
61
68
 
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "autodarts",
4
- "version": "0.2.2",
4
+ "version": "0.3.0",
5
5
  "news": {
6
+ "0.3.0": {
7
+ "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.",
8
+ "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.",
9
+ "ru": "Добавлены точки данных светофора («status.trafficLightColor», «status.trafficLightState»), отображаемые из статуса менеджера совета директоров («Бросить» / «Выбрать» / ошибки подключения).\nРефакторированный код: обработка посещений, обработка бросков (тройной / бычий) и логика светофора перемещаются в отдельные модули.",
10
+ "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.",
11
+ "nl": "Toegevoegd verkeerslicht datapoints (.\nRefactored code: bezoeken handling, gooien handling (triple / stier) en verkeerslicht logica verplaatst naar aparte modules.",
12
+ "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.",
13
+ "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.",
14
+ "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.",
15
+ "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.",
16
+ "uk": "Додано точки світла трафіку (`status.trafficLightColor`, `status.trafficLightState`) на карті Board Manager (`Throw` / `Takeout` / помилки підключення).\nРефакторний код: візитна обробка, кидання (стриптиз / бик) і логіка трафіку переходить на окремі модулі.",
17
+ "zh-cn": "增加从董事会管理者身份绘制的交通灯数据点(`Status.trafficLightColor ' ,`Status.trafficLight Station ' ) (`扔'/`Takeout ' /连接错误).\n重构代码:访问处理,投掷处理(三重/公牛)和交通灯逻辑移动到单独的模块."
18
+ },
6
19
  "0.2.2": {
7
20
  "en": "bugfix",
8
21
  "de": "fehlerbehebung",
@@ -80,19 +93,6 @@
80
93
  "pl": "fix",
81
94
  "uk": "фіксація",
82
95
  "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.setObjectNotExistsAsync("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.setObjectNotExistsAsync("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.setObjectNotExistsAsync("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.setObjectNotExistsAsync("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.setObjectNotExistsAsync(baseId, {
17
+ type: "channel",
18
+ common: {
19
+ name: "Status",
20
+ },
21
+ native: {},
22
+ });
23
+
24
+ // HEX-Farbe
25
+ await adapter.setObjectNotExistsAsync(`${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: "value",
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.setObjectNotExistsAsync(`${baseId}.trafficLightState`, {
46
+ type: "state",
47
+ common: {
48
+ name: {
49
+ en: "Traffic light state",
50
+ de: "Ampelstatus",
51
+ },
52
+ type: "string",
53
+ role: "value",
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.setObjectNotExistsAsync("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.setObjectNotExistsAsync("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,105 +49,11 @@ 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
59
  await this.setObjectNotExistsAsync("online", {
@@ -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.0",
4
4
  "description": "Autodarts Autoscoring",
5
5
  "author": "skvarel <skvarel@inventwo.com>",
6
6
  "contributors": [