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 +8 -1
- package/io-package.json +14 -14
- package/lib/throw.js +149 -0
- package/lib/trafficLight.js +109 -0
- package/lib/visit.js +68 -0
- package/main.js +30 -191
- package/package.json +1 -1
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
|
|
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.
|
|
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/
|
|
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
|
|
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
|
-
|
|
93
|
-
|
|
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
|
|
321
|
-
de: "Zeit in ms, nach der isTriple und
|
|
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
|
-
|
|
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
|
-
//
|
|
518
|
-
|
|
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
|
|