iobroker.tint 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/LICENSE +21 -0
- package/README.md +201 -0
- package/admin/build/assets/__virtual_mf___mfe_internal__tintComponents__loadShare__react__loadShare__.js_commonjs-proxy-Cl6Kn7gP.js +9 -0
- package/admin/build/assets/_virtual_mf-localSharedImportMap___mfe_internal__tintComponents-B1A16Tgp.js +1 -0
- package/admin/build/assets/_virtual_mf___mfe_internal__tintComponents__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-C8Vyx7Bj.js +8 -0
- package/admin/build/assets/_virtual_mf___mfe_internal__tintComponents__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-ByxO1Xun.js +1 -0
- package/admin/build/assets/_virtual_mf___mfe_internal__tintComponents__loadShare___mf_0_mui_mf_1_material__loadShare__.js-Dg1UrPxy.js +248 -0
- package/admin/build/assets/_virtual_mf___mfe_internal__tintComponents__loadShare__react__loadShare__.js-CAwea2Mm.js +1 -0
- package/admin/build/assets/_virtual_mf___mfe_internal__tintComponents__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-B7t36uFG.js +9 -0
- package/admin/build/assets/_virtual_mf___mfe_internal__tintComponents__loadShare__react_mf_2_dom__loadShare__.js-rV2HHyiS.js +24 -0
- package/admin/build/assets/bootstrap-DdKMNh18.js +1 -0
- package/admin/build/assets/hostInit-C5jswnkw.js +1 -0
- package/admin/build/assets/index-C-tjmgJM.js +1 -0
- package/admin/build/assets/preload-helper-BlTxHScW.js +1 -0
- package/admin/build/assets/virtualExposes-Bu4cv-kd.js +1 -0
- package/admin/build/customComponents.js +7 -0
- package/admin/build/customComponents.ssr.js +48 -0
- package/admin/i18n/de.json +35 -0
- package/admin/i18n/en.json +35 -0
- package/admin/i18n/es.json +35 -0
- package/admin/i18n/fr.json +35 -0
- package/admin/i18n/it.json +35 -0
- package/admin/i18n/nl.json +35 -0
- package/admin/i18n/pl.json +35 -0
- package/admin/i18n/pt.json +35 -0
- package/admin/i18n/ru.json +35 -0
- package/admin/i18n/uk.json +35 -0
- package/admin/i18n/zh-cn.json +35 -0
- package/admin/jsonConfig.json +329 -0
- package/admin/tint.png +0 -0
- package/io-package.json +227 -0
- package/lib/adapter-config.d.ts +20 -0
- package/lib/admin-projections.js +61 -0
- package/lib/color-utils.js +230 -0
- package/lib/deconz-api.js +379 -0
- package/lib/deconz-ws.js +151 -0
- package/lib/device-category.js +42 -0
- package/lib/objects.js +1002 -0
- package/lib/remote-handler.js +218 -0
- package/main.js +1924 -0
- package/package.json +84 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { xyToHex, miredToKelvin, decodeRemoteZone } = require('./color-utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Button number → semantic name
|
|
7
|
+
*
|
|
8
|
+
* {Record<number, string>}
|
|
9
|
+
*/
|
|
10
|
+
const BUTTON_NAMES = {
|
|
11
|
+
1: 'onOff',
|
|
12
|
+
2: 'brightnessUp',
|
|
13
|
+
3: 'brightnessDown',
|
|
14
|
+
4: 'colorTempWarm',
|
|
15
|
+
5: 'colorTempCold',
|
|
16
|
+
6: 'colorWheel',
|
|
17
|
+
7: 'scene1',
|
|
18
|
+
8: 'scene2',
|
|
19
|
+
9: 'scene3',
|
|
20
|
+
10: 'scene4',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Last digit of buttonevent → press type
|
|
25
|
+
*
|
|
26
|
+
* {Record<number, string>}
|
|
27
|
+
*/
|
|
28
|
+
const PRESS_TYPES = {
|
|
29
|
+
1: 'hold',
|
|
30
|
+
2: 'short',
|
|
31
|
+
3: 'release',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Handles all Tint Remote (ZBT-Remote-ALL-RGBW) WebSocket events
|
|
36
|
+
* and maps them to ioBroker states.
|
|
37
|
+
*/
|
|
38
|
+
class RemoteHandler {
|
|
39
|
+
/**
|
|
40
|
+
* @param {object} adapter - ioBroker adapter instance
|
|
41
|
+
* @param {(sensorId: string, x: number, y: number, hex: string, angle: number) => Promise<void>} [onColorWheel] - Optional callback invoked on color wheel events
|
|
42
|
+
*/
|
|
43
|
+
constructor(adapter, onColorWheel) {
|
|
44
|
+
this.adapter = adapter;
|
|
45
|
+
this.onColorWheel = onColorWheel || null;
|
|
46
|
+
this._stopped = false;
|
|
47
|
+
this.adapter.log.debug('RemoteHandler initialised');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stop the handler — prevents further state writes after adapter unload.
|
|
52
|
+
*/
|
|
53
|
+
stop() {
|
|
54
|
+
this.adapter.log.debug('RemoteHandler: stopping (no further state writes)');
|
|
55
|
+
this._stopped = true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Main entry point ─────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Process a deCONZ WebSocket sensor-changed event.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} sensorId - deCONZ sensor id
|
|
64
|
+
* @param {object} state - event.state containing buttonevent and optional xy/angle/colortemp
|
|
65
|
+
* @param {object} [config] - event.config (may contain group and battery)
|
|
66
|
+
* @param {object} [attr] - event.attr (may contain lastseen)
|
|
67
|
+
*/
|
|
68
|
+
async handleEvent(sensorId, state, config, attr) {
|
|
69
|
+
if (this._stopped) {
|
|
70
|
+
this.adapter.log.debug(`RemoteHandler: ignoring event for sensor ${sensorId} — handler is stopped`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!state || state.buttonevent === undefined) {
|
|
74
|
+
this.adapter.log.debug(`RemoteHandler: sensor ${sensorId} — no buttonevent in state, ignoring`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const code = state.buttonevent;
|
|
79
|
+
const buttonNum = Math.floor(code / 1000);
|
|
80
|
+
const actionCode = code % 10;
|
|
81
|
+
const buttonName = BUTTON_NAMES[buttonNum] || `button${buttonNum}`;
|
|
82
|
+
const pressType = PRESS_TYPES[actionCode] || 'unknown';
|
|
83
|
+
|
|
84
|
+
this.adapter.log.info(
|
|
85
|
+
`Remote ${sensorId}: button "${buttonName}" ${pressType} ` +
|
|
86
|
+
`(code=${code}, buttonNum=${buttonNum}, actionCode=${actionCode})`,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const prefix = `remotes.${sensorId}`;
|
|
90
|
+
|
|
91
|
+
// ── Always update base button states ──────────────────────────────────
|
|
92
|
+
await this._setState(`${prefix}.button.lastEvent`, code);
|
|
93
|
+
await this._setState(`${prefix}.button.lastEventName`, buttonName);
|
|
94
|
+
await this._setState(`${prefix}.button.pressType`, pressType);
|
|
95
|
+
|
|
96
|
+
// ── Zone from config.group ────────────────────────────────────────────
|
|
97
|
+
const groupStr = config?.group || state.group || null;
|
|
98
|
+
if (groupStr !== null) {
|
|
99
|
+
const zone = decodeRemoteZone(String(groupStr));
|
|
100
|
+
this.adapter.log.debug(`Remote ${sensorId}: zone=${zone} (config.group="${groupStr}")`);
|
|
101
|
+
await this._setState(`${prefix}.button.activeZone`, zone);
|
|
102
|
+
} else {
|
|
103
|
+
this.adapter.log.debug(`Remote ${sensorId}: no group info in event — activeZone not updated`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── lastSeen / battery from attr/config ───────────────────────────────
|
|
107
|
+
if (attr?.lastseen) {
|
|
108
|
+
this.adapter.log.debug(`Remote ${sensorId}: lastSeen="${attr.lastseen}"`);
|
|
109
|
+
await this._setState(`${prefix}.info.lastSeen`, attr.lastseen);
|
|
110
|
+
}
|
|
111
|
+
if (config?.battery !== undefined) {
|
|
112
|
+
this.adapter.log.debug(`Remote ${sensorId}: battery=${config.battery}%`);
|
|
113
|
+
await this._setState(`${prefix}.info.battery`, config.battery);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Specialised handlers ──────────────────────────────────────────────
|
|
117
|
+
if (buttonNum === 6) {
|
|
118
|
+
this.adapter.log.debug(`Remote ${sensorId}: dispatching to color-wheel handler`);
|
|
119
|
+
await this._handleColorWheel(sensorId, prefix, state);
|
|
120
|
+
} else if (buttonNum === 4 || buttonNum === 5) {
|
|
121
|
+
this.adapter.log.debug(`Remote ${sensorId}: dispatching to color-temperature handler`);
|
|
122
|
+
await this._handleColorTemp(prefix, state, pressType);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Color wheel ──────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Handle a color wheel event (buttonevent 6002).
|
|
130
|
+
* Decodes xy and angle, writes color states, fires trigger pulse and optional callback.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} sensorId - deCONZ sensor id
|
|
133
|
+
* @param {string} prefix - ioBroker state path prefix e.g. "remotes.21"
|
|
134
|
+
* @param {object} state - deCONZ sensor state containing xy and angle
|
|
135
|
+
*/
|
|
136
|
+
async _handleColorWheel(sensorId, prefix, state) {
|
|
137
|
+
const xy = state.xy;
|
|
138
|
+
const angle = state.angle;
|
|
139
|
+
|
|
140
|
+
if (!Array.isArray(xy) || xy.length < 2) {
|
|
141
|
+
this.adapter.log.warn(
|
|
142
|
+
`Remote ${sensorId}: color-wheel event has no xy data — state=${JSON.stringify(state)}`,
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const [x, y] = xy;
|
|
148
|
+
const hex = xyToHex(x, y);
|
|
149
|
+
|
|
150
|
+
this.adapter.log.debug(
|
|
151
|
+
`Remote ${sensorId}: color wheel — angle=${angle}°, ` + `xy=[${x.toFixed(4)}, ${y.toFixed(4)}], hex=${hex}`,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
await this._setState(`${prefix}.colorWheel.angle`, angle ?? null);
|
|
155
|
+
await this._setState(`${prefix}.colorWheel.x`, x);
|
|
156
|
+
await this._setState(`${prefix}.colorWheel.y`, y);
|
|
157
|
+
await this._setState(`${prefix}.colorWheel.hex`, hex);
|
|
158
|
+
|
|
159
|
+
// Trigger-pulse: true for 250 ms → scripts/Blockly can react
|
|
160
|
+
this.adapter.log.debug(`Remote ${sensorId}: firing colorWheel.triggered pulse (250 ms)`);
|
|
161
|
+
await this._setState(`${prefix}.colorWheel.triggered`, true);
|
|
162
|
+
this.adapter.setTimeout(async () => {
|
|
163
|
+
if (!this._stopped) {
|
|
164
|
+
await this._setState(`${prefix}.colorWheel.triggered`, false);
|
|
165
|
+
}
|
|
166
|
+
}, 250);
|
|
167
|
+
|
|
168
|
+
// Optional callback → adapter can auto-apply color to bound lights
|
|
169
|
+
if (this.onColorWheel) {
|
|
170
|
+
this.adapter.log.debug(`Remote ${sensorId}: invoking onColorWheel callback`);
|
|
171
|
+
await this.onColorWheel(sensorId, x, y, hex, angle);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─── Color temperature ────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Handle a color temperature button event (buttons 4 and 5).
|
|
179
|
+
* Writes mired, Kelvin, and press type to the colorTemp channel.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} prefix - ioBroker state path prefix e.g. "remotes.21"
|
|
182
|
+
* @param {object} state - deCONZ sensor state containing colortemp in Mired
|
|
183
|
+
* @param {string} pressType - "short" | "hold" | "release"
|
|
184
|
+
*/
|
|
185
|
+
async _handleColorTemp(prefix, state, pressType) {
|
|
186
|
+
const mired = state.colortemp;
|
|
187
|
+
if (mired === undefined) {
|
|
188
|
+
this.adapter.log.debug(`${prefix}: color-temp event has no colortemp field — skipping`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const kelvin = miredToKelvin(mired);
|
|
193
|
+
this.adapter.log.debug(`${prefix}: color temperature — ${mired} mired = ${kelvin} K (${pressType})`);
|
|
194
|
+
|
|
195
|
+
await this._setState(`${prefix}.colorTemp.mired`, mired);
|
|
196
|
+
await this._setState(`${prefix}.colorTemp.value`, kelvin);
|
|
197
|
+
await this._setState(`${prefix}.colorTemp.pressType`, pressType);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ─── Helper ───────────────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Write a state value with ack:true, swallowing errors to prevent adapter crash.
|
|
204
|
+
*
|
|
205
|
+
* @param {string} id - Full ioBroker state id
|
|
206
|
+
* @param {unknown} val - Value to set
|
|
207
|
+
*/
|
|
208
|
+
async _setState(id, val) {
|
|
209
|
+
this.adapter.log.debug(`setState ${id} = ${JSON.stringify(val)}`);
|
|
210
|
+
try {
|
|
211
|
+
await this.adapter.setStateAsync(id, { val, ack: true });
|
|
212
|
+
} catch (err) {
|
|
213
|
+
this.adapter.log.warn(`setState ${id} failed: ${err.message}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = RemoteHandler;
|