iobroker.govee-local 0.2.9 → 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
@@ -24,6 +24,16 @@ For **Windows** Users: Please go to the settings and make sure you choose the co
24
24
  Placeholder for the next version (at the beginning of the line):
25
25
  ### **WORK IN PROGRESS**
26
26
  -->
27
+ ### 0.3.1 (2025-10-19)
28
+
29
+ - updated release script run configuration
30
+
31
+ ### 0.3.0 (2025-10-19)
32
+
33
+ - Refactored: Moved all device logic to a dedicated GoveeService class, modularized utilities
34
+ - Improved test coverage
35
+ - Dependencies updated
36
+
27
37
  ### 0.2.9 (2025-08-22)
28
38
 
29
39
  - update dependencies
@@ -32,77 +42,77 @@ For **Windows** Users: Please go to the settings and make sure you choose the co
32
42
 
33
43
  ### 0.2.8 (2025-03-28)
34
44
 
35
- - update dependencies
36
- - avoid repeated overriding of states
45
+ - update dependencies
46
+ - avoid repeated overriding of states
37
47
 
38
48
  ### 0.2.7 (2024-08-18)
39
49
 
40
- - update core dependencies and min required node version to 18
50
+ - update core dependencies and min required node version to 18
41
51
 
42
52
  ### 0.2.6 (2024-02-06)
43
53
 
44
- - fix brightness change also changing white temperature
54
+ - fix brightness change also changing white temperature
45
55
 
46
56
  ### 0.2.5 (2024-01-13)
47
57
 
48
- - create only one socket, as the second seems not to be necessary
49
- - refactoring the code for better structure
50
- - possibility to choose listen interface in settings
58
+ - create only one socket, as the second seems not to be necessary
59
+ - refactoring the code for better structure
60
+ - possibility to choose listen interface in settings
51
61
 
52
62
  ### 0.2.4 (2024-01-05)
53
63
 
54
- - fix access
64
+ - fix access
55
65
 
56
66
  ### 0.2.2 (2024-01-05)
57
67
 
58
- - fix color temperature message
68
+ - fix color temperature message
59
69
 
60
70
  ### 0.2.1 (2023-12-24)
61
71
 
62
- - repair onOff / all other actions Fixes: [#65](https://github.com/boergegrunicke/ioBroker.govee-local/issues/65)
63
- - fix log spamming because of wildcard
72
+ - repair onOff / all other actions Fixes: [#65](https://github.com/boergegrunicke/ioBroker.govee-local/issues/65)
73
+ - fix log spamming because of wildcard
64
74
 
65
75
  ### 0.2.0 (2023-12-17)
66
76
 
67
- - support controlling the color
68
- - extended logging mode
77
+ - support controlling the color
78
+ - extended logging mode
69
79
 
70
80
  ### 0.1.2 (2023-09-06)
71
81
 
72
- - change icon path and resolution
82
+ - change icon path and resolution
73
83
 
74
84
  ### 0.1.1 (2023-08-21)
75
85
 
76
- - fix image
86
+ - fix image
77
87
 
78
88
  ### 0.1.0 (2023-08-09)
79
89
 
80
- - make search intervals configurable
81
- - clear all timeouts, when adapter is stopped
82
- - replace forbidden characters in names
83
- - update translations
84
- - update dependencies
90
+ - make search intervals configurable
91
+ - clear all timeouts, when adapter is stopped
92
+ - replace forbidden characters in names
93
+ - update translations
94
+ - update dependencies
85
95
 
86
96
  ### 0.0.6 (2023-05-18)
87
97
 
88
- - update dependencies
98
+ - update dependencies
89
99
 
90
100
  ### 0.0.5 (2023-04-02)
91
101
 
92
- - make pipeline run
102
+ - make pipeline run
93
103
 
94
104
  ### 0.0.4 (2023-04-02)
95
105
 
96
- - make device status refresh independent from device search interval
106
+ - make device status refresh independent from device search interval
97
107
 
98
108
  ### 0.0.3 (2023-04-01)
99
109
 
100
- - update dependencies
110
+ - update dependencies
101
111
 
102
112
  ### 0.0.2
103
113
 
104
- - frequently searching for devices and requesting their specific state
105
- - on / off state, brightness and and color temperature can be controlled
114
+ - frequently searching for devices and requesting their specific state
115
+ - on / off state, brightness and and color temperature can be controlled
106
116
 
107
117
  ## License
108
118
 
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var goveeService_exports = {};
30
+ __export(goveeService_exports, {
31
+ GoveeService: () => GoveeService
32
+ });
33
+ module.exports = __toCommonJS(goveeService_exports);
34
+ var dgram = __toESM(require("node:dgram"));
35
+ var import_node_events = require("node:events");
36
+ var import_hexTool = require("./tools/hexTool");
37
+ class GoveeService extends import_node_events.EventEmitter {
38
+ socket;
39
+ options;
40
+ devices = {};
41
+ loggedDevices = [];
42
+ searchInterval;
43
+ refreshInterval;
44
+ static LOCAL_PORT = 4002;
45
+ static SEND_SCAN_PORT = 4001;
46
+ static CONTROL_PORT = 4003;
47
+ static M_CAST = "239.255.255.250";
48
+ static scanMessage = { msg: { cmd: "scan", data: { account_topic: "reserved" } } };
49
+ static requestStatusMessage = { msg: { cmd: "devStatus", data: {} } };
50
+ /**
51
+ * Create a new GoveeService instance.
52
+ *
53
+ * @param options Configuration options for the service.
54
+ */
55
+ constructor(options) {
56
+ super();
57
+ this.options = options;
58
+ this.socket = dgram.createSocket({ type: "udp4" });
59
+ }
60
+ /**
61
+ * Bind UDP socket and start device search/refresh intervals.
62
+ */
63
+ start() {
64
+ this.socket.on("message", this.onUdpMessage.bind(this));
65
+ this.socket.on("error", (error) => {
66
+ var _a;
67
+ (_a = this.options.logger) == null ? void 0 : _a.error(`server bind error : ${error.message}`);
68
+ });
69
+ this.socket.bind(
70
+ { address: this.options.interface, port: GoveeService.LOCAL_PORT },
71
+ this.serverBound.bind(this)
72
+ );
73
+ }
74
+ serverBound() {
75
+ var _a;
76
+ this.socket.setBroadcast(true);
77
+ this.socket.setMulticastTTL(128);
78
+ this.socket.setMulticastInterface(this.options.interface);
79
+ this.socket.addMembership(GoveeService.M_CAST);
80
+ (_a = this.options.logger) == null ? void 0 : _a.debug(
81
+ `UDP listening on ${this.socket.address().address}:${this.socket.address().port}`
82
+ );
83
+ this.searchInterval = setInterval(() => this.sendScan(), this.options.searchInterval * 1e3);
84
+ this.refreshInterval = setInterval(
85
+ () => this.refreshAllDevices(),
86
+ this.options.deviceStatusRefreshInterval * 1e3
87
+ );
88
+ }
89
+ /**
90
+ * Handle incoming UDP messages.
91
+ *
92
+ * @param message The message buffer.
93
+ * @param remote The sender info.
94
+ */
95
+ onUdpMessage(message, remote) {
96
+ var _a, _b, _c;
97
+ const messageObject = JSON.parse(message.toString());
98
+ switch (messageObject.msg.cmd) {
99
+ case "scan": {
100
+ if (messageObject.msg.data.device) {
101
+ const deviceName = messageObject.msg.data.device.replace(
102
+ (_a = this.options.forbiddenChars) != null ? _a : /[^a-zA-Z0-9_-]/g,
103
+ "_"
104
+ );
105
+ this.devices[remote.address] = deviceName;
106
+ this.emit("deviceDiscovered", {
107
+ ip: remote.address,
108
+ deviceName
109
+ });
110
+ }
111
+ break;
112
+ }
113
+ case "devStatus": {
114
+ const sendingDevice = this.devices[remote.address];
115
+ if (sendingDevice) {
116
+ if (this.options.extendedLogging && !this.loggedDevices.includes(remote.address.toString())) {
117
+ (_b = this.options.logger) == null ? void 0 : _b.info(`device status message data: ${JSON.stringify(messageObject)}`);
118
+ this.loggedDevices.push(remote.address.toString());
119
+ }
120
+ this.emitDeviceStatusUpdate(sendingDevice, remote.address, messageObject);
121
+ }
122
+ break;
123
+ }
124
+ default: {
125
+ (_c = this.options.logger) == null ? void 0 : _c.debug(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);
126
+ }
127
+ }
128
+ }
129
+ /**
130
+ * Send device status request to all known devices.
131
+ */
132
+ refreshAllDevices() {
133
+ for (const ip in this.devices) {
134
+ this.requestDeviceStatus(ip);
135
+ }
136
+ }
137
+ /**
138
+ * Send device status request to a specific device.
139
+ *
140
+ * @param receiver The IP address or hostname of the device.
141
+ */
142
+ requestDeviceStatus(receiver) {
143
+ const requestDeviceStatusBuffer = Buffer.from(JSON.stringify(GoveeService.requestStatusMessage));
144
+ this.socket.send(
145
+ requestDeviceStatusBuffer,
146
+ 0,
147
+ requestDeviceStatusBuffer.length,
148
+ GoveeService.CONTROL_PORT,
149
+ receiver
150
+ );
151
+ }
152
+ /**
153
+ * Send scan message to the UDP multicast address.
154
+ */
155
+ sendScan() {
156
+ var _a;
157
+ (_a = this.options.logger) == null ? void 0 : _a.debug("sending scan message");
158
+ const scanMessageBuffer = Buffer.from(JSON.stringify(GoveeService.scanMessage));
159
+ this.socket.send(
160
+ scanMessageBuffer,
161
+ 0,
162
+ scanMessageBuffer.length,
163
+ GoveeService.SEND_SCAN_PORT,
164
+ GoveeService.M_CAST
165
+ );
166
+ }
167
+ /**
168
+ * Stop all intervals and close the socket.
169
+ */
170
+ stop() {
171
+ if (this.searchInterval) {
172
+ clearInterval(this.searchInterval);
173
+ }
174
+ if (this.refreshInterval) {
175
+ clearInterval(this.refreshInterval);
176
+ }
177
+ this.socket.close();
178
+ }
179
+ /**
180
+ * Get all known devices.
181
+ */
182
+ getDevices() {
183
+ return this.devices;
184
+ }
185
+ /**
186
+ * Handles a state change and sends the appropriate command to the device.
187
+ *
188
+ * @param id The state ID
189
+ * @param state The new state object
190
+ * @param receiver The device IP as string
191
+ */
192
+ handleStateChange(id, state, receiver) {
193
+ var _a;
194
+ const stateKey = id.split(".")[4];
195
+ switch (stateKey) {
196
+ case "onOff":
197
+ this.sendTurnCommand(receiver, !!state.val);
198
+ break;
199
+ case "brightness":
200
+ this.sendBrightnessCommand(receiver, Number(state.val));
201
+ break;
202
+ case "colorTemInKelvin":
203
+ this.sendColorTempCommand(receiver, Number(state.val));
204
+ break;
205
+ case "color": {
206
+ const colorValue = (_a = state.val) == null ? void 0 : _a.toString();
207
+ if (colorValue) {
208
+ this.sendColorCommand(receiver, colorValue);
209
+ }
210
+ break;
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Send a turn on/off command to a device.
216
+ *
217
+ * @param receiver IP address or hostname
218
+ * @param value true for on, false for off
219
+ */
220
+ sendTurnCommand(receiver, value) {
221
+ const turnMessage = { msg: { cmd: "turn", data: { value: value ? 1 : 0 } } };
222
+ const turnMessageBuffer = Buffer.from(JSON.stringify(turnMessage));
223
+ this.socket.send(turnMessageBuffer, 0, turnMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);
224
+ }
225
+ /**
226
+ * Send a brightness command to a device.
227
+ *
228
+ * @param receiver IP address or hostname
229
+ * @param value Brightness value
230
+ */
231
+ sendBrightnessCommand(receiver, value) {
232
+ const brightnessMessage = { msg: { cmd: "brightness", data: { value } } };
233
+ const brightnessMessageBuffer = Buffer.from(JSON.stringify(brightnessMessage));
234
+ this.socket.send(
235
+ brightnessMessageBuffer,
236
+ 0,
237
+ brightnessMessageBuffer.length,
238
+ GoveeService.CONTROL_PORT,
239
+ receiver
240
+ );
241
+ }
242
+ /**
243
+ * Send a color temperature command to a device.
244
+ *
245
+ * @param receiver IP address or hostname
246
+ * @param kelvin Color temperature in Kelvin
247
+ */
248
+ sendColorTempCommand(receiver, kelvin) {
249
+ const colorTempMessageBuffer = Buffer.from(
250
+ JSON.stringify({
251
+ msg: {
252
+ cmd: "colorwc",
253
+ data: { color: { r: 0, g: 0, b: 0 }, colorTemInKelvin: kelvin }
254
+ }
255
+ })
256
+ );
257
+ this.socket.send(colorTempMessageBuffer, 0, colorTempMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);
258
+ }
259
+ /**
260
+ * Send a color command to a device.
261
+ *
262
+ * @param receiver IP address or hostname
263
+ * @param hexColor Color as hex string (e.g. #FFAABB)
264
+ */
265
+ sendColorCommand(receiver, hexColor) {
266
+ const rgb = (0, import_hexTool.hexToRgb)(hexColor);
267
+ const colorMessage = { msg: { cmd: "colorwc", data: { color: rgb } } };
268
+ const colorMessageBuffer = Buffer.from(JSON.stringify(colorMessage));
269
+ this.socket.send(colorMessageBuffer, 0, colorMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);
270
+ }
271
+ /**
272
+ * Emit device status update event with parsed status data.
273
+ *
274
+ * @param deviceName The device name.
275
+ * @param ip The device IP address.
276
+ * @param messageObject The parsed message object containing device status.
277
+ */
278
+ emitDeviceStatusUpdate(deviceName, ip, messageObject) {
279
+ const deviceData = messageObject.msg.data;
280
+ const colorString = `#${(0, import_hexTool.componentToHex)(deviceData.color.r)}${(0, import_hexTool.componentToHex)(deviceData.color.g)}${(0, import_hexTool.componentToHex)(deviceData.color.b)}`;
281
+ this.emit("deviceStatusUpdate", {
282
+ deviceName,
283
+ ip,
284
+ status: {
285
+ onOff: deviceData.onOff === 1,
286
+ brightness: deviceData.brightness,
287
+ color: colorString,
288
+ colorTemInKelvin: deviceData.colorTemInKelvin
289
+ }
290
+ });
291
+ }
292
+ }
293
+ // Annotate the CommonJS export names for ESM import in node:
294
+ 0 && (module.exports = {
295
+ GoveeService
296
+ });
297
+ //# sourceMappingURL=goveeService.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/goveeService.ts"],
4
+ "sourcesContent": ["/**\n * GoveeService handles all business logic for device discovery, status updates, and UDP communication.\n * This class is independent from ioBroker.Adapter and can be tested separately.\n */\n\nimport * as dgram from 'node:dgram';\nimport { EventEmitter } from 'node:events';\nimport type { GoveeServiceOptions } from './goveeServiceOptions';\nimport { componentToHex, hexToRgb } from './tools/hexTool';\n\n/**\n * Device discovery event data.\n */\nexport interface DeviceDiscoveryEvent {\n\t/** The IP address of the discovered device. */\n\tip: string;\n\t/** The sanitized device name. */\n\tdeviceName: string;\n}\n\n/**\n * Device status update event data.\n */\nexport interface DeviceStatusEvent {\n\t/** The sanitized device name. */\n\tdeviceName: string;\n\t/** The IP address of the device. */\n\tip: string;\n\t/** The current status of the device. */\n\tstatus: {\n\t\t/** Current on/off state. */\n\t\tonOff: boolean;\n\t\t/** Current brightness level. */\n\t\tbrightness: number;\n\t\t/** Current color as hex string. */\n\t\tcolor: string;\n\t\t/** Current color temperature in Kelvin. */\n\t\tcolorTemInKelvin: number;\n\t};\n}\n\n/**\n * GoveeService handles all business logic for device discovery, status updates, and UDP communication.\n * This class is independent from ioBroker.Adapter and can be tested separately.\n *\n * Events:\n * - 'deviceDiscovered': Emitted when a new device is found\n * - 'deviceStatusUpdate': Emitted when device status is updated\n *\n * Usage:\n * const service = new GoveeService(options);\n * service.on('deviceDiscovered', (data) => { ... });\n * service.on('deviceStatusUpdate', (data) => { ... });\n * service.start();\n * // ...\n * service.stop();\n */\nexport class GoveeService extends EventEmitter {\n\tprivate socket: dgram.Socket;\n\tprivate options: GoveeServiceOptions;\n\tprivate devices: { [ip: string]: string } = {};\n\tprivate loggedDevices: string[] = [];\n\tprivate searchInterval?: NodeJS.Timeout;\n\tprivate refreshInterval?: NodeJS.Timeout;\n\n\tstatic readonly LOCAL_PORT = 4002;\n\tstatic readonly SEND_SCAN_PORT = 4001;\n\tstatic readonly CONTROL_PORT = 4003;\n\tstatic readonly M_CAST = '239.255.255.250';\n\tstatic readonly scanMessage = { msg: { cmd: 'scan', data: { account_topic: 'reserved' } } };\n\tstatic readonly requestStatusMessage = { msg: { cmd: 'devStatus', data: {} } };\n\n\t/**\n\t * Create a new GoveeService instance.\n\t *\n\t * @param options Configuration options for the service.\n\t */\n\tconstructor(options: GoveeServiceOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.socket = dgram.createSocket({ type: 'udp4' });\n\t}\n\n\t/**\n\t * Bind UDP socket and start device search/refresh intervals.\n\t */\n\tpublic start(): void {\n\t\tthis.socket.on('message', this.onUdpMessage.bind(this));\n\t\tthis.socket.on('error', (error) => {\n\t\t\tthis.options.logger?.error(`server bind error : ${error.message}`);\n\t\t});\n\t\tthis.socket.bind(\n\t\t\t{ address: this.options.interface, port: GoveeService.LOCAL_PORT },\n\t\t\tthis.serverBound.bind(this),\n\t\t);\n\t}\n\n\tprivate serverBound(): void {\n\t\tthis.socket.setBroadcast(true);\n\t\tthis.socket.setMulticastTTL(128);\n\t\tthis.socket.setMulticastInterface(this.options.interface);\n\t\tthis.socket.addMembership(GoveeService.M_CAST);\n\t\tthis.options.logger?.debug(\n\t\t\t`UDP listening on ${(this.socket.address() as any).address}:${(this.socket.address() as any).port}`,\n\t\t);\n\t\tthis.searchInterval = setInterval(() => this.sendScan(), this.options.searchInterval * 1000);\n\t\tthis.refreshInterval = setInterval(\n\t\t\t() => this.refreshAllDevices(),\n\t\t\tthis.options.deviceStatusRefreshInterval * 1000,\n\t\t);\n\t}\n\n\t/**\n\t * Handle incoming UDP messages.\n\t *\n\t * @param message The message buffer.\n\t * @param remote The sender info.\n\t */\n\tprivate onUdpMessage(message: Buffer, remote: dgram.RemoteInfo): void {\n\t\tconst messageObject = JSON.parse(message.toString());\n\t\tswitch (messageObject.msg.cmd) {\n\t\t\tcase 'scan': {\n\t\t\t\tif (messageObject.msg.data.device) {\n\t\t\t\t\tconst deviceName = messageObject.msg.data.device.replace(\n\t\t\t\t\t\tthis.options.forbiddenChars ?? /[^a-zA-Z0-9_-]/g,\n\t\t\t\t\t\t'_',\n\t\t\t\t\t);\n\t\t\t\t\tthis.devices[remote.address] = deviceName;\n\t\t\t\t\tthis.emit('deviceDiscovered', {\n\t\t\t\t\t\tip: remote.address,\n\t\t\t\t\t\tdeviceName: deviceName,\n\t\t\t\t\t} as DeviceDiscoveryEvent);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'devStatus': {\n\t\t\t\tconst sendingDevice = this.devices[remote.address];\n\t\t\t\tif (sendingDevice) {\n\t\t\t\t\tif (this.options.extendedLogging && !this.loggedDevices.includes(remote.address.toString())) {\n\t\t\t\t\t\tthis.options.logger?.info(`device status message data: ${JSON.stringify(messageObject)}`);\n\t\t\t\t\t\tthis.loggedDevices.push(remote.address.toString());\n\t\t\t\t\t}\n\t\t\t\t\tthis.emitDeviceStatusUpdate(sendingDevice, remote.address, messageObject);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthis.options.logger?.debug(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Send device status request to all known devices.\n\t */\n\tpublic refreshAllDevices(): void {\n\t\tfor (const ip in this.devices) {\n\t\t\tthis.requestDeviceStatus(ip);\n\t\t}\n\t}\n\n\t/**\n\t * Send device status request to a specific device.\n\t *\n\t * @param receiver The IP address or hostname of the device.\n\t */\n\tpublic requestDeviceStatus(receiver: string): void {\n\t\tconst requestDeviceStatusBuffer = Buffer.from(JSON.stringify(GoveeService.requestStatusMessage));\n\t\tthis.socket.send(\n\t\t\trequestDeviceStatusBuffer,\n\t\t\t0,\n\t\t\trequestDeviceStatusBuffer.length,\n\t\t\tGoveeService.CONTROL_PORT,\n\t\t\treceiver,\n\t\t);\n\t}\n\n\t/**\n\t * Send scan message to the UDP multicast address.\n\t */\n\tpublic sendScan(): void {\n\t\tthis.options.logger?.debug('sending scan message');\n\t\tconst scanMessageBuffer = Buffer.from(JSON.stringify(GoveeService.scanMessage));\n\t\tthis.socket.send(\n\t\t\tscanMessageBuffer,\n\t\t\t0,\n\t\t\tscanMessageBuffer.length,\n\t\t\tGoveeService.SEND_SCAN_PORT,\n\t\t\tGoveeService.M_CAST,\n\t\t);\n\t}\n\n\t/**\n\t * Stop all intervals and close the socket.\n\t */\n\tpublic stop(): void {\n\t\tif (this.searchInterval) {\n\t\t\tclearInterval(this.searchInterval);\n\t\t}\n\t\tif (this.refreshInterval) {\n\t\t\tclearInterval(this.refreshInterval);\n\t\t}\n\t\tthis.socket.close();\n\t}\n\n\t/**\n\t * Get all known devices.\n\t */\n\tpublic getDevices(): { [ip: string]: string } {\n\t\treturn this.devices;\n\t}\n\n\t/**\n\t * Handles a state change and sends the appropriate command to the device.\n\t *\n\t * @param id The state ID\n\t * @param state The new state object\n\t * @param receiver The device IP as string\n\t */\n\tpublic handleStateChange(id: string, state: ioBroker.State, receiver: string): void {\n\t\tconst stateKey = id.split('.')[4];\n\t\tswitch (stateKey) {\n\t\t\tcase 'onOff':\n\t\t\t\tthis.sendTurnCommand(receiver, !!state.val);\n\t\t\t\tbreak;\n\t\t\tcase 'brightness':\n\t\t\t\tthis.sendBrightnessCommand(receiver, Number(state.val));\n\t\t\t\tbreak;\n\t\t\tcase 'colorTemInKelvin':\n\t\t\t\tthis.sendColorTempCommand(receiver, Number(state.val));\n\t\t\t\tbreak;\n\t\t\tcase 'color': {\n\t\t\t\tconst colorValue = state.val?.toString();\n\t\t\t\tif (colorValue) {\n\t\t\t\t\tthis.sendColorCommand(receiver, colorValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t * Send a turn on/off command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param value true for on, false for off\n\t */\n\tpublic sendTurnCommand(receiver: string, value: boolean): void {\n\t\tconst turnMessage = { msg: { cmd: 'turn', data: { value: value ? 1 : 0 } } };\n\t\tconst turnMessageBuffer = Buffer.from(JSON.stringify(turnMessage));\n\t\tthis.socket.send(turnMessageBuffer, 0, turnMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Send a brightness command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param value Brightness value\n\t */\n\tpublic sendBrightnessCommand(receiver: string, value: number): void {\n\t\tconst brightnessMessage = { msg: { cmd: 'brightness', data: { value } } };\n\t\tconst brightnessMessageBuffer = Buffer.from(JSON.stringify(brightnessMessage));\n\t\tthis.socket.send(\n\t\t\tbrightnessMessageBuffer,\n\t\t\t0,\n\t\t\tbrightnessMessageBuffer.length,\n\t\t\tGoveeService.CONTROL_PORT,\n\t\t\treceiver,\n\t\t);\n\t}\n\n\t/**\n\t * Send a color temperature command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param kelvin Color temperature in Kelvin\n\t */\n\tpublic sendColorTempCommand(receiver: string, kelvin: number): void {\n\t\tconst colorTempMessageBuffer = Buffer.from(\n\t\t\tJSON.stringify({\n\t\t\t\tmsg: {\n\t\t\t\t\tcmd: 'colorwc',\n\t\t\t\t\tdata: { color: { r: 0, g: 0, b: 0 }, colorTemInKelvin: kelvin },\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tthis.socket.send(colorTempMessageBuffer, 0, colorTempMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Send a color command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param hexColor Color as hex string (e.g. #FFAABB)\n\t */\n\tpublic sendColorCommand(receiver: string, hexColor: string): void {\n\t\tconst rgb = hexToRgb(hexColor);\n\t\tconst colorMessage = { msg: { cmd: 'colorwc', data: { color: rgb } } };\n\t\tconst colorMessageBuffer = Buffer.from(JSON.stringify(colorMessage));\n\t\tthis.socket.send(colorMessageBuffer, 0, colorMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Emit device status update event with parsed status data.\n\t *\n\t * @param deviceName The device name.\n\t * @param ip The device IP address.\n\t * @param messageObject The parsed message object containing device status.\n\t */\n\tprivate emitDeviceStatusUpdate(deviceName: string, ip: string, messageObject: any): void {\n\t\tconst deviceData = messageObject.msg.data;\n\t\tconst colorString = `#${componentToHex(deviceData.color.r)}${componentToHex(deviceData.color.g)}${componentToHex(deviceData.color.b)}`;\n\n\t\tthis.emit('deviceStatusUpdate', {\n\t\t\tdeviceName: deviceName,\n\t\t\tip: ip,\n\t\t\tstatus: {\n\t\t\t\tonOff: deviceData.onOff === 1,\n\t\t\t\tbrightness: deviceData.brightness,\n\t\t\t\tcolor: colorString,\n\t\t\t\tcolorTemInKelvin: deviceData.colorTemInKelvin,\n\t\t\t},\n\t\t} as DeviceStatusEvent);\n\t}\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var goveeServiceOptions_exports = {};
16
+ module.exports = __toCommonJS(goveeServiceOptions_exports);
17
+ //# sourceMappingURL=goveeServiceOptions.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/goveeServiceOptions.ts"],
4
+ "sourcesContent": ["/**\n * Configuration options for the GoveeService.\n */\nexport interface GoveeServiceOptions {\n\t/** Network interface to bind UDP socket to. */\n\tinterface: string;\n\t/** Interval in seconds for device search. */\n\tsearchInterval: number;\n\t/** Interval in seconds for device status refresh. */\n\tdeviceStatusRefreshInterval: number;\n\t/** Enable extended debug/info logging. */\n\textendedLogging?: boolean;\n\t/** RegExp for forbidden characters in device names. */\n\tforbiddenChars?: RegExp;\n\t/** Logger object with debug/info/error methods. */\n\tlogger?: { debug: (msg: string) => void; info: (msg: string) => void; error: (msg: string) => void };\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var datapointTools_exports = {};
20
+ __export(datapointTools_exports, {
21
+ getDatapointDescription: () => getDatapointDescription
22
+ });
23
+ module.exports = __toCommonJS(datapointTools_exports);
24
+ function getDatapointDescription(name) {
25
+ switch (name) {
26
+ case "model":
27
+ return "Specific model of the Lamp";
28
+ case "ip":
29
+ return "IP address of the Lamp";
30
+ case "bleVersionHard":
31
+ return "Bluetooth Low Energy Hardware Version";
32
+ case "bleVersionSoft":
33
+ return "Bluetooth Low Energy Software Version";
34
+ case "wifiVersionHard":
35
+ return "WiFi Hardware Version";
36
+ case "wifiVersionSoft":
37
+ return "WiFi Software Version";
38
+ default:
39
+ return "";
40
+ }
41
+ }
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ getDatapointDescription
45
+ });
46
+ //# sourceMappingURL=datapointTools.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/tools/datapointTools.ts"],
4
+ "sourcesContent": ["/**\n * Returns the description for device information datapoints.\n * This helps to keep the main adapter class clean and centralizes datapoint metadata.\n *\n * @param name The name of the parameter retrieved from the device\n * @returns The description to be set to the datapoint\n */\nexport function getDatapointDescription(name: string): string {\n\tswitch (name) {\n\t\tcase 'model':\n\t\t\treturn 'Specific model of the Lamp';\n\t\tcase 'ip':\n\t\t\treturn 'IP address of the Lamp';\n\t\tcase 'bleVersionHard':\n\t\t\treturn 'Bluetooth Low Energy Hardware Version';\n\t\tcase 'bleVersionSoft':\n\t\t\treturn 'Bluetooth Low Energy Software Version';\n\t\tcase 'wifiVersionHard':\n\t\t\treturn 'WiFi Hardware Version';\n\t\tcase 'wifiVersionSoft':\n\t\t\treturn 'WiFi Software Version';\n\t\tdefault:\n\t\t\treturn '';\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,wBAAwB,MAAsB;AAC7D,UAAQ,MAAM;AAAA,IACb,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR;AACC,aAAO;AAAA,EACT;AACD;",
6
+ "names": []
7
+ }
@@ -23,17 +23,19 @@ __export(hexTool_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(hexTool_exports);
25
25
  function componentToHex(c) {
26
- const hex = c.toString(16);
27
- return hex.length == 1 ? `0${hex}` : hex;
26
+ const hex = c.toString(16).toUpperCase();
27
+ return hex.padStart(2, "0");
28
28
  }
29
- function hexToRgb(hexString) {
30
- if (!/^#[0-9a-fA-F]{6}$/i.test(hexString)) {
31
- throw new Error("Invalid hex string");
29
+ function hexToRgb(hex) {
30
+ hex = hex.replace(/^#/, "");
31
+ if (hex.length === 3) {
32
+ hex = hex.split("").map((x) => x + x).join("");
32
33
  }
34
+ const num = parseInt(hex, 16);
33
35
  return {
34
- r: parseInt(hexString.slice(1, 3), 16),
35
- g: parseInt(hexString.slice(3, 5), 16),
36
- b: parseInt(hexString.slice(5, 7), 16)
36
+ r: num >> 16 & 255,
37
+ g: num >> 8 & 255,
38
+ b: num & 255
37
39
  };
38
40
  }
39
41
  // Annotate the CommonJS export names for ESM import in node:
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/tools/hexTool.ts"],
4
+ "sourcesContent": ["/**\n * Converts a number (0-255) to a two-digit hexadecimal string.\n *\n * @param c The number to convert.\n * @returns Hex string (e.g. '0A', 'FF').\n */\nexport function componentToHex(c: number): string {\n\tconst hex = c.toString(16).toUpperCase();\n\treturn hex.padStart(2, '0');\n}\n\n/**\n * Converts a hex color string (e.g. '#FFAABB') to an RGB object.\n *\n * @param hex The hex color string.\n * @returns An object with r, g, b properties.\n */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n\t// Remove leading # if present\n\thex = hex.replace(/^#/, '');\n\tif (hex.length === 3) {\n\t\thex = hex\n\t\t\t.split('')\n\t\t\t.map((x) => x + x)\n\t\t\t.join('');\n\t}\n\tconst num = parseInt(hex, 16);\n\treturn {\n\t\tr: (num >> 16) & 255,\n\t\tg: (num >> 8) & 255,\n\t\tb: num & 255,\n\t};\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,eAAe,GAAmB;AACjD,QAAM,MAAM,EAAE,SAAS,EAAE,EAAE,YAAY;AACvC,SAAO,IAAI,SAAS,GAAG,GAAG;AAC3B;AAQO,SAAS,SAAS,KAAkD;AAE1E,QAAM,IAAI,QAAQ,MAAM,EAAE;AAC1B,MAAI,IAAI,WAAW,GAAG;AACrB,UAAM,IACJ,MAAM,EAAE,EACR,IAAI,CAAC,MAAM,IAAI,CAAC,EAChB,KAAK,EAAE;AAAA,EACV;AACA,QAAM,MAAM,SAAS,KAAK,EAAE;AAC5B,SAAO;AAAA,IACN,GAAI,OAAO,KAAM;AAAA,IACjB,GAAI,OAAO,IAAK;AAAA,IAChB,GAAG,MAAM;AAAA,EACV;AACD;",
6
+ "names": []
7
+ }
package/build/main.js CHANGED
@@ -28,25 +28,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var main_exports = {};
30
30
  __export(main_exports, {
31
- GoveeLocal: () => GoveeLocal,
32
- getDatapointDescription: () => getDatapointDescription
31
+ GoveeLocal: () => GoveeLocal
33
32
  });
34
33
  module.exports = __toCommonJS(main_exports);
35
34
  var utils = __toESM(require("@iobroker/adapter-core"));
36
- var dgram = __toESM(require("node:dgram"));
37
- var import_hexTool = require("./tools/hexTool");
38
- const LOCAL_PORT = 4002;
39
- const SEND_SCAN_PORT = 4001;
40
- const CONTROL_PORT = 4003;
41
- const M_CAST = "239.255.255.250";
42
- const socket = dgram.createSocket({ type: "udp4" });
43
- const scanMessage = { msg: { cmd: "scan", data: { account_topic: "reserved" } } };
44
- const requestStatusMessage = { msg: { cmd: "devStatus", data: {} } };
45
- let searchInterval;
46
- let refreshInterval;
47
- const devices = {};
48
- const loggedDevices = [];
35
+ var import_goveeService = require("./lib/goveeService");
49
36
  class GoveeLocal extends utils.Adapter {
37
+ goveeService;
38
+ /**
39
+ * Constructor of the adapter class.
40
+ *
41
+ * @param options Optional adapter options to override defaults.
42
+ */
50
43
  constructor(options = {}) {
51
44
  super({
52
45
  ...options,
@@ -71,193 +64,61 @@ class GoveeLocal extends utils.Adapter {
71
64
  },
72
65
  native: {}
73
66
  });
74
- socket.on("message", this.onUdpMessage.bind(this));
75
- socket.on("error", (error) => {
76
- this.log.error(`server bind error : ${error.message}`);
77
- void this.setStateChanged("info.connection", { val: false, ack: true });
67
+ this.goveeService = new import_goveeService.GoveeService({
68
+ interface: this.config.interface,
69
+ searchInterval: this.config.searchInterval,
70
+ deviceStatusRefreshInterval: this.config.deviceStatusRefreshInterval,
71
+ extendedLogging: this.config.extendedLogging,
72
+ forbiddenChars: /[^a-zA-Z0-9_-]/g,
73
+ logger: {
74
+ debug: (msg) => this.log.debug(msg),
75
+ info: (msg) => this.log.info(msg),
76
+ error: (msg) => this.log.error(msg)
77
+ }
78
78
  });
79
+ this.goveeService.on("deviceDiscovered", (data) => {
80
+ void this.handleDeviceDiscovered(data);
81
+ });
82
+ this.goveeService.on("deviceStatusUpdate", (data) => {
83
+ void this.handleDeviceStatusUpdate(data);
84
+ });
85
+ this.goveeService.start();
79
86
  if (this.config.extendedLogging) {
80
87
  this.log.debug("running with extended logging");
81
88
  }
82
- void socket.bind({ address: this.config.interface, port: LOCAL_PORT }, this.serverBound.bind(this));
83
89
  void this.subscribeStates("*.devStatus.*");
84
90
  return Promise.resolve();
85
91
  }
86
92
  /**
87
- * handles when udp socket is up
88
- * configure multicast membership
89
- * start periodic scan for devices
90
- */
91
- async serverBound() {
92
- socket.setBroadcast(true);
93
- socket.setMulticastTTL(128);
94
- socket.setMulticastInterface(this.config.interface);
95
- socket.addMembership(M_CAST);
96
- void this.setStateChanged("info.connection", { val: true, ack: true });
97
- this.log.debug(`UDP listening on ${socket.address().address}:${socket.address().port}`);
98
- const deviceSearchInterval = this.setInterval(this.sendScan.bind(this), this.config.searchInterval * 1e3);
99
- if (deviceSearchInterval) {
100
- searchInterval = deviceSearchInterval;
101
- }
102
- const deviceRefreshInterval = this.setInterval(
103
- this.refreshAllDevices.bind(this),
104
- this.config.deviceStatusRefreshInterval * 1e3
105
- );
106
- if (deviceRefreshInterval) {
107
- refreshInterval = deviceRefreshInterval;
108
- }
109
- return Promise.resolve();
110
- }
111
- /**
112
- * handle icoming messages on the udp socket
93
+ * Is called if a subscribed state changes
113
94
  *
114
- * @param message the message itself
115
- * @param remote the sender of the message
95
+ * @param id The ID of the changed state.
96
+ * @param state The new state object or null/undefined.
116
97
  */
117
- async onUdpMessage(message, remote) {
118
- const messageObject = JSON.parse(message.toString());
119
- switch (messageObject.msg.cmd) {
120
- case "scan": {
121
- for (const key of Object.keys(messageObject.msg.data)) {
122
- if (key != "device") {
123
- const deviceName = messageObject.msg.data.device.replace(this.FORBIDDEN_CHARS, "_");
124
- devices[remote.address] = deviceName;
125
- void this.setObjectNotExists(deviceName, {
126
- type: "device",
127
- common: {
128
- name: messageObject.msg.data.sku,
129
- role: "group"
130
- },
131
- native: {}
132
- });
133
- void this.setObjectNotExists(`${deviceName}.deviceInfo.${key}`, {
134
- type: "state",
135
- common: {
136
- name: getDatapointDescription(key),
137
- type: "string",
138
- role: "state",
139
- read: true,
140
- write: false
141
- },
142
- native: {}
143
- });
144
- void this.updateStateAsync(`${deviceName}.deviceInfo.${key}`, messageObject.msg.data[key]);
145
- }
146
- }
147
- break;
148
- }
149
- case "devStatus": {
150
- const sendingDevice = devices[remote.address];
151
- if (sendingDevice) {
152
- if (this.config.extendedLogging && !loggedDevices.includes(remote.address.toString())) {
153
- this.log.info(`deivce status message data: ${JSON.stringify(messageObject)}`);
154
- loggedDevices.push(remote.address.toString());
155
- }
156
- const devStatusMessageObject = JSON.parse(message.toString());
157
- void this.setObjectNotExists(`${sendingDevice}.devStatus.onOff`, {
158
- type: "state",
159
- common: {
160
- name: "On / Off state of the lamp",
161
- type: "boolean",
162
- role: "switch",
163
- read: true,
164
- write: true
165
- },
166
- native: {}
167
- });
168
- void this.updateStateAsync(
169
- `${sendingDevice}.devStatus.onOff`,
170
- devStatusMessageObject.msg.data.onOff == 1
171
- );
172
- void this.setObjectNotExists(`${sendingDevice}.devStatus.brightness`, {
173
- type: "state",
174
- common: {
175
- name: "Brightness of the light",
176
- type: "number",
177
- role: "level.dimmer",
178
- read: true,
179
- write: true
180
- },
181
- native: {}
182
- });
183
- void this.updateStateAsync(
184
- `${sendingDevice}.devStatus.brightness`,
185
- devStatusMessageObject.msg.data.brightness
186
- );
187
- void this.setObjectNotExists(`${sendingDevice}.devStatus.color`, {
188
- type: "state",
189
- common: {
190
- name: "Current showing color of the lamp",
191
- type: "string",
192
- role: "level.color.rgb",
193
- read: true,
194
- write: true
195
- },
196
- native: {}
197
- });
198
- const colorString = `#${(0, import_hexTool.componentToHex)(devStatusMessageObject.msg.data.color.r)}${(0, import_hexTool.componentToHex)(devStatusMessageObject.msg.data.color.g)}${(0, import_hexTool.componentToHex)(devStatusMessageObject.msg.data.color.b)}`;
199
- void this.setState(`${sendingDevice}.devStatus.color`, {
200
- val: colorString,
201
- ack: true
202
- });
203
- void this.updateStateAsync(`${sendingDevice}.devStatus.color`, colorString);
204
- void this.setObjectNotExists(`${sendingDevice}.devStatus.colorTemInKelvin`, {
205
- type: "state",
206
- common: {
207
- name: "If staying in white light, the color temperature",
208
- type: "number",
209
- role: "level.color.temperature",
210
- read: true,
211
- write: true
212
- },
213
- native: {}
214
- });
215
- void this.updateStateAsync(
216
- `${sendingDevice}.devStatus.colorTemInKelvin`,
217
- devStatusMessageObject.msg.data.colorTemInKelvin
218
- );
219
- }
220
- break;
221
- }
222
- default: {
223
- this.log.error(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);
98
+ async onStateChange(id, state) {
99
+ var _a;
100
+ if (state && !state.ack) {
101
+ const ipOfDevice = await this.getStateAsync(`${id.split(".")[2]}.deviceInfo.ip`);
102
+ const receiver = (_a = ipOfDevice == null ? void 0 : ipOfDevice.val) == null ? void 0 : _a.toString();
103
+ if (typeof receiver === "string") {
104
+ this.goveeService.handleStateChange(id, state, receiver);
105
+ } else {
106
+ this.log.error("device not found or IP is not a string");
224
107
  }
225
108
  }
226
109
  return Promise.resolve();
227
110
  }
228
- async refreshAllDevices() {
229
- for (const ip in devices) {
230
- this.requestDeviceStatus(ip);
231
- }
232
- return Promise.resolve();
233
- }
234
111
  /**
235
- * sends the device status request to one specific device
112
+ * Called when adapter shuts down - callback must be called under any circumstances!
236
113
  *
237
- * @param receiver the ip ( / hsotname ) of the device that should be queried
238
- */
239
- requestDeviceStatus(receiver) {
240
- const requestDeviceStatusBuffer = Buffer.from(JSON.stringify(requestStatusMessage));
241
- void socket.send(requestDeviceStatusBuffer, 0, requestDeviceStatusBuffer.length, CONTROL_PORT, receiver);
242
- }
243
- /**
244
- * send the scan message to the udp multicast address
245
- */
246
- async sendScan() {
247
- const scanMessageBuffer = Buffer.from(JSON.stringify(scanMessage));
248
- void socket.send(scanMessageBuffer, 0, scanMessageBuffer.length, SEND_SCAN_PORT, M_CAST);
249
- return Promise.resolve();
250
- }
251
- /**
252
- * Is called when adapter shuts down - callback has to be called under any circumstances!
253
- *
254
- * @param callback Callback-Funktion, die nach Abschluss des Unload-Prozesses aufgerufen wird.
114
+ * @param callback Callback function after unload process.
255
115
  */
256
116
  onUnload(callback) {
257
117
  try {
258
- this.clearInterval(searchInterval);
259
- this.clearInterval(refreshInterval);
260
- socket.close();
118
+ if (this.goveeService) {
119
+ this.goveeService.removeAllListeners();
120
+ this.goveeService.stop();
121
+ }
261
122
  void this.updateStateAsync("info.connection", false);
262
123
  callback();
263
124
  } catch (e) {
@@ -265,72 +126,6 @@ class GoveeLocal extends utils.Adapter {
265
126
  callback();
266
127
  }
267
128
  }
268
- /**
269
- * Is called if a subscribed state changes
270
- *
271
- * @param id Die ID des geänderten States.
272
- * @param state Das neue State-Objekt oder null/undefined.
273
- */
274
- async onStateChange(id, state) {
275
- var _a, _b;
276
- if (state && !state.ack) {
277
- const ipOfDevice = await this.getStateAsync(`${id.split(".")[2]}.deviceInfo.ip`);
278
- if (ipOfDevice) {
279
- const receiver = (_a = ipOfDevice.val) == null ? void 0 : _a.toString();
280
- switch (id.split(".")[4]) {
281
- case "onOff": {
282
- const turnMessage = { msg: { cmd: "turn", data: { value: state.val ? 1 : 0 } } };
283
- const turnMessageBuffer = Buffer.from(JSON.stringify(turnMessage));
284
- void socket.send(turnMessageBuffer, 0, turnMessageBuffer.length, CONTROL_PORT, receiver);
285
- break;
286
- }
287
- case "brightness": {
288
- const brightnessMessage = { msg: { cmd: "brightness", data: { value: state.val } } };
289
- const brightnessMessageBuffer = Buffer.from(JSON.stringify(brightnessMessage));
290
- void socket.send(
291
- brightnessMessageBuffer,
292
- 0,
293
- brightnessMessageBuffer.length,
294
- CONTROL_PORT,
295
- receiver
296
- );
297
- break;
298
- }
299
- case "colorTemInKelvin": {
300
- const colorTempMessageBuffer = Buffer.from(
301
- JSON.stringify({
302
- msg: {
303
- cmd: "colorwc",
304
- data: { color: { r: "0", g: "0", b: "0" }, colorTemInKelvin: state.val }
305
- }
306
- })
307
- );
308
- void socket.send(
309
- colorTempMessageBuffer,
310
- 0,
311
- colorTempMessageBuffer.length,
312
- CONTROL_PORT,
313
- receiver
314
- );
315
- break;
316
- }
317
- case "color": {
318
- const colorValue = (_b = state.val) == null ? void 0 : _b.toString();
319
- if (colorValue) {
320
- const rgb = (0, import_hexTool.hexToRgb)(colorValue);
321
- const colorMessage = { msg: { cmd: "colorwc", data: { color: rgb } } };
322
- const colorMessageBuffer = Buffer.from(JSON.stringify(colorMessage));
323
- void socket.send(colorMessageBuffer, 0, colorMessageBuffer.length, CONTROL_PORT, receiver);
324
- }
325
- break;
326
- }
327
- }
328
- } else {
329
- this.log.error("device not found");
330
- }
331
- }
332
- return Promise.resolve();
333
- }
334
129
  async updateStateAsync(fullName, state, acknowledged = true) {
335
130
  const currentState = await this.getStateAsync(fullName);
336
131
  if (currentState != state) {
@@ -340,33 +135,112 @@ class GoveeLocal extends utils.Adapter {
340
135
  });
341
136
  }
342
137
  }
138
+ /**
139
+ * Handle device discovery event.
140
+ *
141
+ * @param event The device discovery event data.
142
+ */
143
+ async handleDeviceDiscovered(event) {
144
+ const { ip, deviceName } = event;
145
+ await this.setObjectNotExistsAsync(deviceName, {
146
+ type: "folder",
147
+ common: {
148
+ name: deviceName
149
+ },
150
+ native: {}
151
+ });
152
+ await this.setObjectNotExistsAsync(`${deviceName}.deviceInfo`, {
153
+ type: "folder",
154
+ common: {
155
+ name: "Device Info"
156
+ },
157
+ native: {}
158
+ });
159
+ await this.setObjectNotExistsAsync(`${deviceName}.deviceInfo.ip`, {
160
+ type: "state",
161
+ common: {
162
+ name: "IP address of the Lamp",
163
+ type: "string",
164
+ role: "info.ip",
165
+ read: true,
166
+ write: false
167
+ },
168
+ native: {}
169
+ });
170
+ await this.updateStateAsync(`${deviceName}.deviceInfo.ip`, ip);
171
+ await this.setObjectNotExistsAsync(`${deviceName}.devStatus`, {
172
+ type: "folder",
173
+ common: {
174
+ name: "Device Status"
175
+ },
176
+ native: {}
177
+ });
178
+ this.log.info(`Device discovered: ${deviceName} at ${ip}`);
179
+ }
180
+ /**
181
+ * Handle device status update event.
182
+ *
183
+ * @param event The device status update event data.
184
+ */
185
+ async handleDeviceStatusUpdate(event) {
186
+ const { deviceName, status } = event;
187
+ await this.setObjectNotExistsAsync(`${deviceName}.devStatus.onOff`, {
188
+ type: "state",
189
+ common: {
190
+ name: "On / Off state of the lamp",
191
+ type: "boolean",
192
+ role: "switch",
193
+ read: true,
194
+ write: true
195
+ },
196
+ native: {}
197
+ });
198
+ await this.updateStateAsync(`${deviceName}.devStatus.onOff`, status.onOff);
199
+ await this.setObjectNotExistsAsync(`${deviceName}.devStatus.brightness`, {
200
+ type: "state",
201
+ common: {
202
+ name: "Brightness of the light",
203
+ type: "number",
204
+ role: "level.dimmer",
205
+ read: true,
206
+ write: true
207
+ },
208
+ native: {}
209
+ });
210
+ await this.updateStateAsync(`${deviceName}.devStatus.brightness`, status.brightness);
211
+ await this.setObjectNotExistsAsync(`${deviceName}.devStatus.color`, {
212
+ type: "state",
213
+ common: {
214
+ name: "Current showing color of the lamp",
215
+ type: "string",
216
+ role: "level.color.rgb",
217
+ read: true,
218
+ write: true
219
+ },
220
+ native: {}
221
+ });
222
+ await this.updateStateAsync(`${deviceName}.devStatus.color`, status.color);
223
+ await this.setObjectNotExistsAsync(`${deviceName}.devStatus.colorTemInKelvin`, {
224
+ type: "state",
225
+ common: {
226
+ name: "If staying in white light, the color temperature",
227
+ type: "number",
228
+ role: "level.color.temperature",
229
+ read: true,
230
+ write: true
231
+ },
232
+ native: {}
233
+ });
234
+ await this.updateStateAsync(`${deviceName}.devStatus.colorTemInKelvin`, status.colorTemInKelvin);
235
+ }
343
236
  }
344
237
  if (require.main !== module) {
345
238
  module.exports = (options) => new GoveeLocal(options);
346
239
  } else {
347
240
  (() => new GoveeLocal())();
348
241
  }
349
- function getDatapointDescription(name) {
350
- switch (name) {
351
- case "model":
352
- return "Specific model of the Lamp";
353
- case "ip":
354
- return "IP address of the Lamp";
355
- case "bleVersionHard":
356
- return "Bluetooth Low Energy Hardware Version";
357
- case "bleVersionSoft":
358
- return "Bluetooth Low Energy Software Version";
359
- case "wifiVersionHard":
360
- return "WiFi Hardware Version";
361
- case "wifiVersionSoft":
362
- return "WiFi Software Version";
363
- default:
364
- return "";
365
- }
366
- }
367
242
  // Annotate the CommonJS export names for ESM import in node:
368
243
  0 && (module.exports = {
369
- GoveeLocal,
370
- getDatapointDescription
244
+ GoveeLocal
371
245
  });
372
246
  //# sourceMappingURL=main.js.map
package/build/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["/*\n * Created with @iobroker/create-adapter v2.3.0\n */\n\n// The adapter-core module gives you access to the core ioBroker functions\n// you need to create an adapter\nimport * as utils from '@iobroker/adapter-core';\nimport * as dgram from 'node:dgram';\nimport { componentToHex, hexToRgb } from './tools/hexTool';\n\n// Load your modules here, e.g.:\n\nconst LOCAL_PORT = 4002;\nconst SEND_SCAN_PORT = 4001;\nconst CONTROL_PORT = 4003;\nconst M_CAST = '239.255.255.250';\n\nconst socket = dgram.createSocket({ type: 'udp4' });\n\nconst scanMessage = { msg: { cmd: 'scan', data: { account_topic: 'reserved' } } };\nconst requestStatusMessage = { msg: { cmd: 'devStatus', data: {} } };\n\nlet searchInterval: ioBroker.Interval;\nlet refreshInterval: ioBroker.Interval;\n\nconst devices: { [ip: string]: string } = {};\n\nconst loggedDevices = [] as string[];\n\nexport class GoveeLocal extends utils.Adapter {\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: 'govee-local',\n\t\t});\n\t\tthis.on('ready', this.onReady.bind(this));\n\t\tthis.on('stateChange', this.onStateChange.bind(this));\n\t\t// this.on('objectChange', this.onObjectChange.bind(this));\n\t\t// this.on('message', this.onMessage.bind(this));\n\t\tthis.on('unload', this.onUnload.bind(this));\n\t}\n\n\t/**\n\t * Is called when databases are connected and adapter received configuration.\n\t */\n\tprivate async onReady(): Promise<void> {\n\t\tvoid this.setObjectNotExists('info.connection', {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device discovery running',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'indicator.connected',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tsocket.on('message', this.onUdpMessage.bind(this));\n\t\tsocket.on('error', (error) => {\n\t\t\tthis.log.error(`server bind error : ${error.message}`);\n\t\t\tvoid this.setStateChanged('info.connection', { val: false, ack: true });\n\t\t});\n\n\t\tif (this.config.extendedLogging) {\n\t\t\tthis.log.debug('running with extended logging');\n\t\t}\n\n\t\tvoid socket.bind({ address: this.config.interface, port: LOCAL_PORT }, this.serverBound.bind(this));\n\t\tvoid this.subscribeStates('*.devStatus.*');\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * handles when udp socket is up\n\t * configure multicast membership\n\t * start periodic scan for devices\n\t */\n\tprivate async serverBound(): Promise<void> {\n\t\tsocket.setBroadcast(true);\n\t\tsocket.setMulticastTTL(128);\n\t\tsocket.setMulticastInterface(this.config.interface);\n\t\tsocket.addMembership(M_CAST);\n\t\tvoid this.setStateChanged('info.connection', { val: true, ack: true });\n\t\tthis.log.debug(`UDP listening on ${(socket.address() as any).address}:${(socket.address() as any).port}`);\n\n\t\tconst deviceSearchInterval = this.setInterval(this.sendScan.bind(this), this.config.searchInterval * 1000);\n\t\tif (deviceSearchInterval) {\n\t\t\tsearchInterval = deviceSearchInterval;\n\t\t}\n\n\t\tconst deviceRefreshInterval = this.setInterval(\n\t\t\tthis.refreshAllDevices.bind(this),\n\t\t\tthis.config.deviceStatusRefreshInterval * 1000,\n\t\t);\n\t\tif (deviceRefreshInterval) {\n\t\t\trefreshInterval = deviceRefreshInterval;\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * handle icoming messages on the udp socket\n\t *\n\t * @param message the message itself\n\t * @param remote the sender of the message\n\t */\n\tprivate async onUdpMessage(message: Buffer, remote: dgram.RemoteInfo): Promise<void> {\n\t\tconst messageObject = JSON.parse(message.toString());\n\t\tswitch (messageObject.msg.cmd) {\n\t\t\tcase 'scan': {\n\t\t\t\tfor (const key of Object.keys(messageObject.msg.data)) {\n\t\t\t\t\tif (key != 'device') {\n\t\t\t\t\t\tconst deviceName = messageObject.msg.data.device.replace(this.FORBIDDEN_CHARS, '_');\n\t\t\t\t\t\tdevices[remote.address] = deviceName;\n\t\t\t\t\t\tvoid this.setObjectNotExists(deviceName, {\n\t\t\t\t\t\t\ttype: 'device',\n\t\t\t\t\t\t\tcommon: {\n\t\t\t\t\t\t\t\tname: messageObject.msg.data.sku,\n\t\t\t\t\t\t\t\trole: 'group',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnative: {},\n\t\t\t\t\t\t});\n\t\t\t\t\t\tvoid this.setObjectNotExists(`${deviceName}.deviceInfo.${key}`, {\n\t\t\t\t\t\t\ttype: 'state',\n\t\t\t\t\t\t\tcommon: {\n\t\t\t\t\t\t\t\tname: getDatapointDescription(key),\n\t\t\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\t\t\trole: 'state',\n\t\t\t\t\t\t\t\tread: true,\n\t\t\t\t\t\t\t\twrite: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnative: {},\n\t\t\t\t\t\t});\n\t\t\t\t\t\tvoid this.updateStateAsync(`${deviceName}.deviceInfo.${key}`, messageObject.msg.data[key]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'devStatus': {\n\t\t\t\tconst sendingDevice = devices[remote.address];\n\t\t\t\tif (sendingDevice) {\n\t\t\t\t\tif (this.config.extendedLogging && !loggedDevices.includes(remote.address.toString())) {\n\t\t\t\t\t\tthis.log.info(`deivce status message data: ${JSON.stringify(messageObject)}`);\n\t\t\t\t\t\tloggedDevices.push(remote.address.toString());\n\t\t\t\t\t}\n\t\t\t\t\tconst devStatusMessageObject = JSON.parse(message.toString());\n\t\t\t\t\tvoid this.setObjectNotExists(`${sendingDevice}.devStatus.onOff`, {\n\t\t\t\t\t\ttype: 'state',\n\t\t\t\t\t\tcommon: {\n\t\t\t\t\t\t\tname: 'On / Off state of the lamp',\n\t\t\t\t\t\t\ttype: 'boolean',\n\t\t\t\t\t\t\trole: 'switch',\n\t\t\t\t\t\t\tread: true,\n\t\t\t\t\t\t\twrite: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnative: {},\n\t\t\t\t\t});\n\t\t\t\t\tvoid this.updateStateAsync(\n\t\t\t\t\t\t`${sendingDevice}.devStatus.onOff`,\n\t\t\t\t\t\tdevStatusMessageObject.msg.data.onOff == 1,\n\t\t\t\t\t);\n\t\t\t\t\tvoid this.setObjectNotExists(`${sendingDevice}.devStatus.brightness`, {\n\t\t\t\t\t\ttype: 'state',\n\t\t\t\t\t\tcommon: {\n\t\t\t\t\t\t\tname: 'Brightness of the light',\n\t\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\t\trole: 'level.dimmer',\n\t\t\t\t\t\t\tread: true,\n\t\t\t\t\t\t\twrite: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnative: {},\n\t\t\t\t\t});\n\t\t\t\t\tvoid this.updateStateAsync(\n\t\t\t\t\t\t`${sendingDevice}.devStatus.brightness`,\n\t\t\t\t\t\tdevStatusMessageObject.msg.data.brightness,\n\t\t\t\t\t);\n\t\t\t\t\tvoid this.setObjectNotExists(`${sendingDevice}.devStatus.color`, {\n\t\t\t\t\t\ttype: 'state',\n\t\t\t\t\t\tcommon: {\n\t\t\t\t\t\t\tname: 'Current showing color of the lamp',\n\t\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\t\trole: 'level.color.rgb',\n\t\t\t\t\t\t\tread: true,\n\t\t\t\t\t\t\twrite: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnative: {},\n\t\t\t\t\t});\n\t\t\t\t\tconst colorString = `#${componentToHex(devStatusMessageObject.msg.data.color.r)}${componentToHex(devStatusMessageObject.msg.data.color.g)}${componentToHex(devStatusMessageObject.msg.data.color.b)}`;\n\t\t\t\t\tvoid this.setState(`${sendingDevice}.devStatus.color`, {\n\t\t\t\t\t\tval: colorString,\n\t\t\t\t\t\tack: true,\n\t\t\t\t\t});\n\t\t\t\t\tvoid this.updateStateAsync(`${sendingDevice}.devStatus.color`, colorString);\n\t\t\t\t\tvoid this.setObjectNotExists(`${sendingDevice}.devStatus.colorTemInKelvin`, {\n\t\t\t\t\t\ttype: 'state',\n\t\t\t\t\t\tcommon: {\n\t\t\t\t\t\t\tname: 'If staying in white light, the color temperature',\n\t\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\t\trole: 'level.color.temperature',\n\t\t\t\t\t\t\tread: true,\n\t\t\t\t\t\t\twrite: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnative: {},\n\t\t\t\t\t});\n\t\t\t\t\tvoid this.updateStateAsync(\n\t\t\t\t\t\t`${sendingDevice}.devStatus.colorTemInKelvin`,\n\t\t\t\t\t\tdevStatusMessageObject.msg.data.colorTemInKelvin,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthis.log.error(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);\n\t\t\t}\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\tprivate async refreshAllDevices(): Promise<void> {\n\t\tfor (const ip in devices) {\n\t\t\tthis.requestDeviceStatus(ip);\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * sends the device status request to one specific device\n\t *\n\t * @param receiver the ip ( / hsotname ) of the device that should be queried\n\t */\n\tprivate requestDeviceStatus(receiver: string): void {\n\t\tconst requestDeviceStatusBuffer = Buffer.from(JSON.stringify(requestStatusMessage));\n\t\tvoid socket.send(requestDeviceStatusBuffer, 0, requestDeviceStatusBuffer.length, CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * send the scan message to the udp multicast address\n\t */\n\tprivate async sendScan(): Promise<void> {\n\t\tconst scanMessageBuffer = Buffer.from(JSON.stringify(scanMessage));\n\t\tvoid socket.send(scanMessageBuffer, 0, scanMessageBuffer.length, SEND_SCAN_PORT, M_CAST);\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Is called when adapter shuts down - callback has to be called under any circumstances!\n\t *\n\t * @param callback Callback-Funktion, die nach Abschluss des Unload-Prozesses aufgerufen wird.\n\t */\n\tprivate onUnload(callback: () => void): void {\n\t\ttry {\n\t\t\tthis.clearInterval(searchInterval);\n\t\t\tthis.clearInterval(refreshInterval);\n\t\t\tsocket.close();\n\t\t\tvoid this.updateStateAsync('info.connection', false);\n\t\t\tcallback();\n\t\t} catch (e: any) {\n\t\t\tthis.log.error(e.message);\n\t\t\tcallback();\n\t\t}\n\t}\n\n\t/**\n\t * Is called if a subscribed state changes\n\t *\n\t * @param id Die ID des ge\u00E4nderten States.\n\t * @param state Das neue State-Objekt oder null/undefined.\n\t */\n\tprivate async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n\t\tif (state && !state.ack) {\n\t\t\tconst ipOfDevice = await this.getStateAsync(`${id.split('.')[2]}.deviceInfo.ip`);\n\t\t\tif (ipOfDevice) {\n\t\t\t\tconst receiver = ipOfDevice.val?.toString();\n\t\t\t\tswitch (id.split('.')[4]) {\n\t\t\t\t\tcase 'onOff': {\n\t\t\t\t\t\tconst turnMessage = { msg: { cmd: 'turn', data: { value: state.val ? 1 : 0 } } };\n\t\t\t\t\t\tconst turnMessageBuffer = Buffer.from(JSON.stringify(turnMessage));\n\t\t\t\t\t\tvoid socket.send(turnMessageBuffer, 0, turnMessageBuffer.length, CONTROL_PORT, receiver);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'brightness': {\n\t\t\t\t\t\tconst brightnessMessage = { msg: { cmd: 'brightness', data: { value: state.val } } };\n\t\t\t\t\t\tconst brightnessMessageBuffer = Buffer.from(JSON.stringify(brightnessMessage));\n\t\t\t\t\t\tvoid socket.send(\n\t\t\t\t\t\t\tbrightnessMessageBuffer,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tbrightnessMessageBuffer.length,\n\t\t\t\t\t\t\tCONTROL_PORT,\n\t\t\t\t\t\t\treceiver,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'colorTemInKelvin': {\n\t\t\t\t\t\tconst colorTempMessageBuffer = Buffer.from(\n\t\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\t\tmsg: {\n\t\t\t\t\t\t\t\t\tcmd: 'colorwc',\n\t\t\t\t\t\t\t\t\tdata: { color: { r: '0', g: '0', b: '0' }, colorTemInKelvin: state.val },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tvoid socket.send(\n\t\t\t\t\t\t\tcolorTempMessageBuffer,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tcolorTempMessageBuffer.length,\n\t\t\t\t\t\t\tCONTROL_PORT,\n\t\t\t\t\t\t\treceiver,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'color': {\n\t\t\t\t\t\tconst colorValue = state.val?.toString();\n\t\t\t\t\t\tif (colorValue) {\n\t\t\t\t\t\t\tconst rgb = hexToRgb(colorValue);\n\t\t\t\t\t\t\tconst colorMessage = { msg: { cmd: 'colorwc', data: { color: rgb } } };\n\t\t\t\t\t\t\tconst colorMessageBuffer = Buffer.from(JSON.stringify(colorMessage));\n\t\t\t\t\t\t\tvoid socket.send(colorMessageBuffer, 0, colorMessageBuffer.length, CONTROL_PORT, receiver);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.log.error('device not found');\n\t\t\t}\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\tprivate async updateStateAsync(fullName: string, state: any, acknowledged = true): Promise<void> {\n\t\tconst currentState = await this.getStateAsync(fullName);\n\t\tif (currentState != state) {\n\t\t\tvoid this.setState(fullName, {\n\t\t\t\tval: state,\n\t\t\t\tack: acknowledged,\n\t\t\t});\n\t\t}\n\t}\n}\n\nif (require.main !== module) {\n\t// Exportiere eine Factory-Funktion f\u00FCr ioBroker, aber die Klasse ist jetzt auch als ES6-Export verf\u00FCgbar\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new GoveeLocal(options);\n} else {\n\t// otherwise start the instance directly\n\t(() => new GoveeLocal())();\n}\n\n/**\n * This method returns the description for device information datapoints\n * to unbloat the upper methods.\n * tanslations would be great here\n *\n * @param name the name of the parameter retrieved from the device\n * @returns the description, that should be set to the datapoint\n */\nexport function getDatapointDescription(name: string): string {\n\tswitch (name) {\n\t\tcase 'model':\n\t\t\treturn 'Specific model of the Lamp';\n\t\tcase 'ip':\n\t\t\treturn 'IP address of the Lamp';\n\t\tcase 'bleVersionHard':\n\t\t\treturn 'Bluetooth Low Energy Hardware Version';\n\t\tcase 'bleVersionSoft':\n\t\t\treturn 'Bluetooth Low Energy Software Version';\n\t\tcase 'wifiVersionHard':\n\t\t\treturn 'WiFi Hardware Version';\n\t\tcase 'wifiVersionSoft':\n\t\t\treturn 'WiFi Software Version';\n\t\tdefault:\n\t\t\treturn '';\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\n * Created with @iobroker/create-adapter v2.3.0\n */\n\nimport * as utils from '@iobroker/adapter-core';\nimport { GoveeService, type DeviceDiscoveryEvent, type DeviceStatusEvent } from './lib/goveeService';\n\n/**\n * The adapter class.\n */\nexport class GoveeLocal extends utils.Adapter {\n\tprivate goveeService!: GoveeService;\n\t/**\n\t * Constructor of the adapter class.\n\t *\n\t * @param options Optional adapter options to override defaults.\n\t */\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: 'govee-local',\n\t\t});\n\t\tthis.on('ready', this.onReady.bind(this));\n\t\tthis.on('stateChange', this.onStateChange.bind(this));\n\t\t// this.on('objectChange', this.onObjectChange.bind(this));\n\t\t// this.on('message', this.onMessage.bind(this));\n\t\tthis.on('unload', this.onUnload.bind(this));\n\t}\n\n\t/**\n\t * Is called when databases are connected and adapter received configuration.\n\t */\n\tprivate async onReady(): Promise<void> {\n\t\tvoid this.setObjectNotExists('info.connection', {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device discovery running',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'indicator.connected',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\t// Initialize GoveeService with adapter config and logger\n\t\tthis.goveeService = new GoveeService({\n\t\t\tinterface: this.config.interface,\n\t\t\tsearchInterval: this.config.searchInterval,\n\t\t\tdeviceStatusRefreshInterval: this.config.deviceStatusRefreshInterval,\n\t\t\textendedLogging: this.config.extendedLogging,\n\t\t\tforbiddenChars: /[^a-zA-Z0-9_-]/g,\n\t\t\tlogger: {\n\t\t\t\tdebug: (msg) => this.log.debug(msg),\n\t\t\t\tinfo: (msg) => this.log.info(msg),\n\t\t\t\terror: (msg) => this.log.error(msg),\n\t\t\t},\n\t\t});\n\n\t\t// Set up event listeners\n\t\tthis.goveeService.on('deviceDiscovered', (data) => {\n\t\t\tvoid this.handleDeviceDiscovered(data);\n\t\t});\n\n\t\tthis.goveeService.on('deviceStatusUpdate', (data) => {\n\t\t\tvoid this.handleDeviceStatusUpdate(data);\n\t\t});\n\t\tthis.goveeService.start();\n\n\t\tif (this.config.extendedLogging) {\n\t\t\tthis.log.debug('running with extended logging');\n\t\t}\n\n\t\tvoid this.subscribeStates('*.devStatus.*');\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Is called if a subscribed state changes\n\t *\n\t * @param id The ID of the changed state.\n\t * @param state The new state object or null/undefined.\n\t */\n\tprivate async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n\t\tif (state && !state.ack) {\n\t\t\tconst ipOfDevice = await this.getStateAsync(`${id.split('.')[2]}.deviceInfo.ip`);\n\t\t\tconst receiver = ipOfDevice?.val?.toString();\n\t\t\tif (typeof receiver === 'string') {\n\t\t\t\tthis.goveeService.handleStateChange(id, state, receiver);\n\t\t\t} else {\n\t\t\t\tthis.log.error('device not found or IP is not a string');\n\t\t\t}\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\t/**\n\t * Called when adapter shuts down - callback must be called under any circumstances!\n\t *\n\t * @param callback Callback function after unload process.\n\t */\n\tprivate onUnload(callback: () => void): void {\n\t\ttry {\n\t\t\tif (this.goveeService) {\n\t\t\t\tthis.goveeService.removeAllListeners();\n\t\t\t\tthis.goveeService.stop();\n\t\t\t}\n\t\t\tvoid this.updateStateAsync('info.connection', false);\n\t\t\tcallback();\n\t\t} catch (e: any) {\n\t\t\tthis.log.error(e.message);\n\t\t\tcallback();\n\t\t}\n\t}\n\n\tprivate async updateStateAsync(fullName: string, state: any, acknowledged = true): Promise<void> {\n\t\tconst currentState = await this.getStateAsync(fullName);\n\t\tif (currentState != state) {\n\t\t\tvoid this.setState(fullName, {\n\t\t\t\tval: state,\n\t\t\t\tack: acknowledged,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Handle device discovery event.\n\t *\n\t * @param event The device discovery event data.\n\t */\n\tprivate async handleDeviceDiscovered(event: DeviceDiscoveryEvent): Promise<void> {\n\t\tconst { ip, deviceName } = event;\n\n\t\t// Create device folder\n\t\tawait this.setObjectNotExistsAsync(deviceName, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: deviceName,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create deviceInfo folder\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.deviceInfo`, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device Info',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create IP address state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.deviceInfo.ip`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'IP address of the Lamp',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'info.ip',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.deviceInfo.ip`, ip);\n\n\t\t// Create devStatus folder\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus`, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device Status',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\tthis.log.info(`Device discovered: ${deviceName} at ${ip}`);\n\t}\n\n\t/**\n\t * Handle device status update event.\n\t *\n\t * @param event The device status update event data.\n\t */\n\tprivate async handleDeviceStatusUpdate(event: DeviceStatusEvent): Promise<void> {\n\t\tconst { deviceName, status } = event;\n\n\t\t// Create and update onOff state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.onOff`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'On / Off state of the lamp',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'switch',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.onOff`, status.onOff);\n\n\t\t// Create and update brightness state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.brightness`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Brightness of the light',\n\t\t\t\ttype: 'number',\n\t\t\t\trole: 'level.dimmer',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.brightness`, status.brightness);\n\n\t\t// Create and update color state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.color`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Current showing color of the lamp',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'level.color.rgb',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.color`, status.color);\n\n\t\t// Create and update color temperature state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.colorTemInKelvin`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'If staying in white light, the color temperature',\n\t\t\t\ttype: 'number',\n\t\t\t\trole: 'level.color.temperature',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.colorTemInKelvin`, status.colorTemInKelvin);\n\t}\n}\n\nif (require.main !== module) {\n\t// Exportiere eine Factory-Funktion f\u00FCr ioBroker, aber die Klasse ist jetzt auch als ES6-Export verf\u00FCgbar\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new GoveeLocal(options);\n} else {\n\t// otherwise start the instance directly\n\t(() => new GoveeLocal())();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "govee-local",
4
- "version": "0.2.9",
4
+ "version": "0.3.1",
5
5
  "news": {
6
+ "0.3.1": {
7
+ "en": "updated release script run configuration",
8
+ "de": "aktualisierte versionsskriptlaufkonfiguration",
9
+ "ru": "обновленная конфигурация для запуска сценария",
10
+ "pt": "configuração de execução do script de versão atualizada",
11
+ "nl": "bijgewerkte versie script-run configuratie",
12
+ "fr": "mise à jour de la configuration du script de publication",
13
+ "it": "configurazione dell'esecuzione dello script di rilascio aggiornata",
14
+ "es": "configuración actualizada script script script run",
15
+ "pl": "zaktualizowana konfiguracja uruchamiania skryptu wydania",
16
+ "uk": "оновлена конфігурація запуску сценарію релізу",
17
+ "zh-cn": "更新发布脚本运行配置"
18
+ },
19
+ "0.3.0": {
20
+ "en": "Refactored: Moved all device logic to a dedicated GoveeService class, modularized utilities\nImproved test coverage\nDependencies updated",
21
+ "de": "Hergestellt: Alle Gerätelogik auf eine dedizierte GoveeService-Klasse verschoben, modularisierte Versorgungsprogramme\nVerbesserte Testabdeckung\nAusgaben aktualisiert",
22
+ "ru": "Рефакторированный: Перенес всю логику устройства в выделенный класс GoveeService, модульные утилиты\nУлучшенный охват испытаниями\nОбновленные зависимости",
23
+ "pt": "Refactorado: Moveu toda a lógica do dispositivo para uma classe dedicada do GoveeService, utilitários modularizados\nMelhor cobertura de teste\nDependências atualizadas",
24
+ "nl": "Refactored: Verplaatste alle apparaat logica naar een speciale GoveeService klasse, modulaire utilities\nVerbeterde testdekking\nAfhankelijkheden bijgewerkt",
25
+ "fr": "Reformulé : Déplace toute la logique de l'appareil vers une classe GoveeService dédiée, utilitaires modulaires\nAmélioration de la couverture des tests\nDépendances actualisées",
26
+ "it": "Refactored: Spostato tutta la logica del dispositivo a una classe GoveeService dedicata, utilità modularizzate\nCopertura di test migliorata\nDipendenze aggiornate",
27
+ "es": "Refactored: Movedo toda la lógica del dispositivo a una clase dedicada GoveeService, utilidades modulares\nMejora de la cobertura de pruebas\nActualización de las dependencias",
28
+ "pl": "Zmienione: Przesunął całą logikę urządzenia do specjalnej klasy GoveeService, modularyzowane narzędzia\nLepszy zakres badań\nAktualizacja zależności",
29
+ "uk": "Рефактор: Переміщено всі логіку пристрою для виділеного класу GoveeService, модульовані утиліти\nПокращене покриття тесту\nЗалежність оновлено",
30
+ "zh-cn": "已重构 : 将所有设备逻辑移至专用的 GoveeService 类,模块化的公用事业\n提高测试覆盖率\n更新的依赖关系"
31
+ },
6
32
  "0.2.9": {
7
33
  "en": "update dependencies\nadd support for node 24\nupdate to ESlint 9",
8
34
  "de": "aktualisierung der abhängigkeiten\nunterstützung für knoten 24 hinzufügen\nupdate auf ESlint 9",
@@ -67,32 +93,6 @@
67
93
  "pl": "utworzyć tylko jedno gniazdo, ponieważ drugie wydaje się nie być konieczne\nrefaktoring kodu dla lepszej struktury\nmożliwość wyboru interfejsu słuchania w ustawieniach",
68
94
  "uk": "створити тільки одну розетку, так як другий здається не потрібно\nрефакторинг коду для кращої структури\nможливість вибору інтерфейсу прослуховування в налаштуваннях",
69
95
  "zh-cn": "只创建一个套接字, 因为第二个似乎没有必要\n为更好的结构重构代码\n在设置中选择监听界面的可能性"
70
- },
71
- "0.2.4": {
72
- "en": "fix access",
73
- "de": "zugriff",
74
- "ru": "исправить доступ",
75
- "pt": "corrigir o acesso",
76
- "nl": "fix toegang",
77
- "fr": "fixer l'accès",
78
- "it": "fissare l'accesso",
79
- "es": "acceso fijo",
80
- "pl": "naprawić dostęp",
81
- "uk": "фіксувати доступ",
82
- "zh-cn": "固定访问"
83
- },
84
- "0.2.1": {
85
- "en": "repair onOff / all other actions Fixes: #65\nfix log spamming because of wildcard",
86
- "de": "reparatur onOff / alle anderen Aktionen Behebungen: #65\nprotokoll spamming wegen wildcard",
87
- "ru": "ремонт onOff / все другие действия Исправления: #65\nисправить спамирование лога из-за дикой карты",
88
- "pt": "reparação emOff / todas as outras ações Correções: #65 #\ncorrigir spam de log por causa do wildcard",
89
- "nl": "vertaling: Vertaling: 65\nvertaling:",
90
- "fr": "réparation onOff / toutes les autres actions Fixes: #65\ncorriger l'éblouissement du journal à cause de wildcard",
91
- "it": "riparazione onOff / tutte le altre azioni Fissi: #65\nsistemare il log spamming a causa di jolly",
92
- "es": "reparación onOff / todas las demás acciones Arreglos: #65\nfijar registro de spam debido a wildcard",
93
- "pl": "remontowanie Off / wszystkie inne działania Fiksy #65\nusunąć z powodu dzikiej karty",
94
- "uk": "ремонт наОфф / всі інші дії Виправлення: #65\nфіксація спамерування колод через дикийкарт",
95
- "zh-cn": "b. 修理效率/所有其他行动 固定: #65\nfix 由于野生素卡的投机"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -150,12 +150,12 @@
150
150
  },
151
151
  "dependencies": [
152
152
  {
153
- "js-controller": ">=5.0.19"
153
+ "js-controller": ">=6.0.11"
154
154
  }
155
155
  ],
156
156
  "globalDependencies": [
157
157
  {
158
- "admin": ">=7.4.10"
158
+ "admin": ">=7.6.17"
159
159
  }
160
160
  ]
161
161
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.govee-local",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "Control Govee devices via local access (no cloud)",
5
5
  "author": {
6
6
  "name": "Børge Grunicke",
@@ -22,7 +22,7 @@
22
22
  "node": ">= 20.0"
23
23
  },
24
24
  "dependencies": {
25
- "@iobroker/adapter-core": "^3.2.3"
25
+ "@iobroker/adapter-core": "^3.3.2"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@alcalzone/release-script": "^3.8.0",
@@ -30,27 +30,27 @@
30
30
  "@alcalzone/release-script-plugin-license": "^3.7.0",
31
31
  "@alcalzone/release-script-plugin-manual-review": "^3.7.0",
32
32
  "@iobroker/adapter-dev": "^1.4.0",
33
- "@iobroker/eslint-config": "^2.0.2",
34
- "@iobroker/testing": "^5.0.4",
35
- "@tsconfig/node14": "^14.1.3",
33
+ "@iobroker/eslint-config": "^2.1.0",
34
+ "@iobroker/testing": "^5.1.1",
35
+ "@tsconfig/node14": "^14.1.5",
36
36
  "@tsconfig/node18": "^18.2.4",
37
37
  "@tsconfig/node20": "^20.1.6",
38
38
  "@types/chai": "^4.1.1",
39
39
  "@types/chai-as-promised": "^8.0.2",
40
40
  "@types/mocha": "^10.0.10",
41
- "@types/node": "^24.0.8",
41
+ "@types/node": "^24.3.0",
42
42
  "@types/proxyquire": "^1.3.31",
43
43
  "@types/sinon": "^17.0.4",
44
44
  "@types/sinon-chai": "^3.2.12",
45
- "chai": "^4.5.0",
46
- "chai-as-promised": "^8.0.1",
47
- "eslint": "^9.32.0",
45
+ "chai": "^6.0.1",
46
+ "chai-as-promised": "^8.0.2",
47
+ "eslint": "^9.34.0",
48
48
  "mocha": "^11.7.1",
49
49
  "prettier": "^3.3.3",
50
50
  "proxyquire": "^2.1.3",
51
51
  "rimraf": "^6.0.1",
52
52
  "sinon": "^21.0.0",
53
- "sinon-chai": "^3.7.0",
53
+ "sinon-chai": "^4.0.1",
54
54
  "source-map-support": "^0.5.21",
55
55
  "ts-node": "^10.9.2",
56
56
  "typescript": "^5.6.3"
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/tools/hexTool.ts"],
4
- "sourcesContent": ["/**\n * Konvertiert eine Zahl (<255) in einen zweistelligen Hex-String f\u00FCr Farbwerte.\n *\n * @param c Die Zahl, die konvertiert werden soll (0-255)\n * @returns Der Hex-Wert als String\n */\nexport function componentToHex(c: number): string {\n\tconst hex = c.toString(16);\n\treturn hex.length == 1 ? `0${hex}` : hex;\n}\n\n/**\n * Wandelt einen Hex-Farbstring (#RRGGBB) in ein Color-Objekt um.\n *\n * @param hexString Hex-Farbwert als String (Format: #RRGGBB)\n * @returns Ein Objekt mit den Farbwerten r, g und b\n */\nexport function hexToRgb(hexString: string): Color {\n\tif (!/^#[0-9a-fA-F]{6}$/i.test(hexString)) {\n\t\tthrow new Error('Invalid hex string');\n\t}\n\treturn {\n\t\tr: parseInt(hexString.slice(1, 3), 16),\n\t\tg: parseInt(hexString.slice(3, 5), 16),\n\t\tb: parseInt(hexString.slice(5, 7), 16),\n\t};\n}\n\n/**\n * Farbobjekt mit Werten f\u00FCr Rot, Gr\u00FCn und Blau\n */\ntype Color = {\n\tr: number;\n\tg: number;\n\tb: number;\n};\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,eAAe,GAAmB;AACjD,QAAM,MAAM,EAAE,SAAS,EAAE;AACzB,SAAO,IAAI,UAAU,IAAI,IAAI,GAAG,KAAK;AACtC;AAQO,SAAS,SAAS,WAA0B;AAClD,MAAI,CAAC,qBAAqB,KAAK,SAAS,GAAG;AAC1C,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACrC;AACA,SAAO;AAAA,IACN,GAAG,SAAS,UAAU,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IACrC,GAAG,SAAS,UAAU,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IACrC,GAAG,SAAS,UAAU,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EACtC;AACD;",
6
- "names": []
7
- }