@iobroker/mcp-server 0.0.2

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.
@@ -0,0 +1,752 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isValidSmartName = isValidSmartName;
37
+ exports.controls = controls;
38
+ exports.getAiFriendlyStructure = getAiFriendlyStructure;
39
+ /**
40
+ * Device detection utilities, ported from the ioBroker n8n nodes
41
+ * (`IoBrokerNodes/DevicesUtils.ts`).
42
+ *
43
+ * Uses the ioBroker type detector (https://github.com/ioBroker/ioBroker.type-detector)
44
+ * to turn raw states/channels/devices into "controls" (functional devices) and groups
45
+ * them into an AI-friendly structure of rooms -> devices -> named controls.
46
+ *
47
+ * All object reads accept an optional `{ user }` so the detection respects the ACLs of
48
+ * the configured default user (see McpServer).
49
+ */
50
+ const type_detector_1 = __importStar(require("@iobroker/type-detector"));
51
+ /**
52
+ * Checks whether the provided value is a valid smart name.
53
+ *
54
+ * @param smartName The value to check
55
+ * @param lang Configured language
56
+ * @returns True if a valid smart name, false - otherwise.
57
+ */
58
+ function isValidSmartName(smartName, lang) {
59
+ let name = smartName;
60
+ if (smartName === false || smartName === 'ignore') {
61
+ return false;
62
+ }
63
+ if (smartName && typeof smartName === 'object') {
64
+ name = smartName[lang] || smartName.en || smartName.de;
65
+ }
66
+ return ![null, undefined, 'ignore', false].includes(name);
67
+ }
68
+ function isRoom(enumObject) {
69
+ return enumObject?._id?.startsWith('enum.rooms.');
70
+ }
71
+ function isFunctionality(enumObject) {
72
+ return enumObject?._id?.startsWith('enum.functions.');
73
+ }
74
+ async function allEnums(adapter, options) {
75
+ const result = await adapter.getObjectViewAsync('system', 'enum', {}, options);
76
+ return result.rows.map(row => row.value);
77
+ }
78
+ function parentOf(id) {
79
+ const parts = (id || '').split('.');
80
+ parts.pop();
81
+ return parts.join('.');
82
+ }
83
+ async function allObjects(adapter, options) {
84
+ const states = await adapter.getObjectViewAsync('system', 'state', {}, options);
85
+ const channels = await adapter.getObjectViewAsync('system', 'channel', {}, options);
86
+ const devices = await adapter.getObjectViewAsync('system', 'device', {}, options);
87
+ const enums = await adapter.getObjectViewAsync('system', 'enum', {}, options);
88
+ return states.rows
89
+ .concat(channels.rows)
90
+ .concat(devices.rows)
91
+ .concat(enums.rows)
92
+ .reduce((obj, item) => ((obj[item.id] = {
93
+ common: item.value?.common,
94
+ type: item.value?.type,
95
+ }),
96
+ obj), {});
97
+ }
98
+ function getSmartNameFromObj(obj, instanceId, noCommon) {
99
+ if (!obj) {
100
+ return undefined;
101
+ }
102
+ let result;
103
+ // If it is a common object
104
+ if (!obj.common) {
105
+ result = obj.smartName;
106
+ }
107
+ else if (!noCommon) {
108
+ result = obj.common.smartName;
109
+ }
110
+ else {
111
+ const custom = obj.common.custom;
112
+ if (!custom) {
113
+ return undefined;
114
+ }
115
+ result = custom[instanceId] ? custom[instanceId].smartName : undefined;
116
+ }
117
+ if (result && typeof result === 'string') {
118
+ if (result === 'ignore') {
119
+ return false;
120
+ }
121
+ return {
122
+ en: result,
123
+ };
124
+ }
125
+ return result;
126
+ }
127
+ async function functionalitiesAndRooms(adapter, options) {
128
+ const enumerations = await allEnums(adapter, options);
129
+ // skip empty enums (with no members, i.e. states, assigned)
130
+ const notEmptyRoomsAndFunctionalities = enumerations
131
+ .filter(item => {
132
+ const smartName = getSmartNameFromObj(item, adapter.namespace);
133
+ return smartName !== false;
134
+ })
135
+ .filter(item => item?.common?.members?.length);
136
+ // all enums that are of type 'function'
137
+ const functionalities = notEmptyRoomsAndFunctionalities.filter(item => isFunctionality(item));
138
+ // all enums, that are of type 'room'
139
+ const rooms = notEmptyRoomsAndFunctionalities.filter(item => isRoom(item));
140
+ return [functionalities, rooms];
141
+ }
142
+ function getChannelId(id, objects) {
143
+ if (objects[id] && objects[id].type === 'channel') {
144
+ return id;
145
+ }
146
+ if (objects[id] && objects[id].type === 'state') {
147
+ const channelId = parentOf(id);
148
+ if (objects[channelId] && objects[channelId].type === 'channel') {
149
+ return channelId;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ function getDeviceId(id, objects) {
155
+ const channelId = getChannelId(id, objects);
156
+ if (channelId) {
157
+ const deviceId = parentOf(channelId);
158
+ if (objects[deviceId] && (objects[deviceId].type === 'device' || objects[deviceId].type === 'channel')) {
159
+ return deviceId;
160
+ }
161
+ }
162
+ return null;
163
+ }
164
+ /**
165
+ * Inspects all objects (states, channels and devices) and tries to identify so-called 'controls'
166
+ *
167
+ * To identify the controls, the ioBroker type detector library is used (https://github.com/ioBroker/ioBroker.type-detector).
168
+ *
169
+ * @param adapter The adapter instance
170
+ * @param lang language
171
+ * @param options read options (ACL user)
172
+ * @returns An array containing the detected controls
173
+ */
174
+ async function controls(adapter, lang, options) {
175
+ // here we collect ids to inspect
176
+ const list = [];
177
+ // fetch all objects (states, channels and devices in terms of iobroker)
178
+ const devicesObject = await allObjects(adapter, options);
179
+ // fetch all defined rooms and functions (enumerations)
180
+ const [functionalities, rooms] = await functionalitiesAndRooms(adapter, options);
181
+ // every member of a function enumeration is added to the list of ids to inspect
182
+ functionalities.forEach(functionEnumItem => {
183
+ functionEnumItem.common.members?.forEach(id => {
184
+ const smartName = getSmartNameFromObj(devicesObject[id], adapter.namespace);
185
+ const objType = devicesObject[id]?.type;
186
+ if (devicesObject[id]?.common &&
187
+ (objType === 'state' || objType === 'channel' || objType === 'device') &&
188
+ !list.includes(id) &&
189
+ smartName !== false // if the device is not disabled
190
+ ) {
191
+ list.push(id);
192
+ }
193
+ });
194
+ });
195
+ // a member of a room enumeration is only added if neither its parent (channel) nor its grandparent (device) is in
196
+ rooms.forEach(roomEnumItem => {
197
+ roomEnumItem.common.members?.forEach(id => {
198
+ if (!devicesObject[id]) {
199
+ return;
200
+ }
201
+ const smartName = getSmartNameFromObj(devicesObject[id], adapter.namespace);
202
+ const objType = devicesObject[id].type;
203
+ if (devicesObject[id]?.common &&
204
+ (objType === 'state' || objType === 'channel' || objType === 'device') &&
205
+ !list.includes(id) &&
206
+ smartName !== false // if the device is not disabled
207
+ ) {
208
+ const channelId = getChannelId(id, devicesObject);
209
+ if (channelId) {
210
+ if (!list.includes(channelId)) {
211
+ const deviceId = getDeviceId(id, devicesObject);
212
+ if (deviceId) {
213
+ if (!list.includes(deviceId)) {
214
+ list.push(id);
215
+ }
216
+ }
217
+ else {
218
+ list.push(id);
219
+ }
220
+ }
221
+ }
222
+ else {
223
+ list.push(id);
224
+ }
225
+ }
226
+ });
227
+ });
228
+ // all ids, i.e. ids of all iobroker states/channels/devices
229
+ const keys = Object.keys(devicesObject).sort();
230
+ const idsWithSmartName = [];
231
+ // if a state has got a smart name directly assigned and neither itself nor its channel is in the list, add its id to the inspection list
232
+ // and process it first
233
+ keys.forEach(id => {
234
+ const smartName = devicesObject[id] && getSmartNameFromObj(devicesObject[id], adapter.namespace);
235
+ const objType = devicesObject[id].type;
236
+ if (isValidSmartName(smartName, lang) &&
237
+ devicesObject[id].common &&
238
+ (objType === 'state' || objType === 'channel' || objType === 'device')) {
239
+ idsWithSmartName.push(id);
240
+ }
241
+ });
242
+ // collect first all smart names and remove them from the auto-groups
243
+ const detectedControls = [];
244
+ const detector = new type_detector_1.default();
245
+ const patterns = type_detector_1.default.getPatterns();
246
+ // process states with defined smartName
247
+ for (let s = 0; s < idsWithSmartName.length; s++) {
248
+ const id = idsWithSmartName[s];
249
+ const common = devicesObject[id].common;
250
+ const smartName = getSmartNameFromObj(devicesObject[id], adapter.namespace);
251
+ // try to convert the state to typeDetector format
252
+ // "smartName": {
253
+ // "de": "Rote Lampe",
254
+ // "smartType": "LIGHT", // optional
255
+ // "byON": 80 // optional
256
+ // }
257
+ if (!smartName.smartType) {
258
+ // by default,
259
+ // all booleans are sockets
260
+ // all numbers are dimmer
261
+ // string is not possible to control
262
+ if (common.type === 'boolean' || common.type === 'mixed') {
263
+ // we will write boolean
264
+ smartName.smartType = 'socket';
265
+ }
266
+ else if (common.type === 'number') {
267
+ smartName.smartType = 'dimmer';
268
+ }
269
+ else {
270
+ smartName.smartType = 'socket';
271
+ }
272
+ }
273
+ // try to simulate typeDetector format
274
+ if (patterns[smartName.smartType]) {
275
+ const control = JSON.parse(JSON.stringify(patterns[smartName.smartType]));
276
+ // find first required
277
+ const state = control.states.find(state => state.required);
278
+ if (state) {
279
+ state.id = id;
280
+ // process control
281
+ // remove all unassigned control register
282
+ control.states = control.states.filter(s => s.id);
283
+ // take all smartNames if any
284
+ control.states.forEach(s => {
285
+ s.smartName = getSmartNameFromObj(devicesObject[s.id], adapter.namespace);
286
+ s.common = {
287
+ min: devicesObject[s.id]?.common?.min,
288
+ max: devicesObject[s.id]?.common?.max,
289
+ type: devicesObject[s.id]?.common?.type,
290
+ states: devicesObject[s.id]?.common?.states,
291
+ unit: devicesObject[s.id]?.common?.unit,
292
+ role: devicesObject[s.id]?.common?.role,
293
+ name: devicesObject[s.id]?.common?.name,
294
+ icon: devicesObject[s.id]?.common?.icon,
295
+ color: devicesObject[s.id]?.common?.color,
296
+ };
297
+ });
298
+ devicesObject[id].common.smartName = smartName;
299
+ control.object = {
300
+ id,
301
+ type: devicesObject[id].type,
302
+ common: devicesObject[id].common,
303
+ autoDetected: false,
304
+ toggle: smartName?.toggle,
305
+ };
306
+ // remove id from the groups
307
+ let pos = list.indexOf(id);
308
+ if (pos !== -1) {
309
+ list.splice(pos, 1);
310
+ }
311
+ const channelId = getChannelId(id, devicesObject);
312
+ if (channelId) {
313
+ pos = list.indexOf(channelId);
314
+ if (pos !== -1) {
315
+ list.splice(pos, 1);
316
+ }
317
+ }
318
+ const name = smartName[lang] || smartName.en || smartName.de;
319
+ control.groupNames = name?.split(',').map(n => n.trim()) || [];
320
+ detectedControls.push(control);
321
+ }
322
+ }
323
+ }
324
+ // initialize iobroker type detector
325
+ const usedIds = [];
326
+ const ignoreIndicators = ['UNREACH_STICKY']; // Ignore indicators by name
327
+ const excludedTypes = [type_detector_1.Types.info];
328
+ const detectOptions = {
329
+ objects: devicesObject,
330
+ _keysOptional: keys,
331
+ _usedIdsOptional: usedIds,
332
+ ignoreIndicators,
333
+ excludedTypes,
334
+ id: '', // this will be set for each id in the list
335
+ };
336
+ // go other the list of IDs to inspect and collect the detected controls
337
+ list.forEach(id => {
338
+ detectOptions.id = id;
339
+ const detected = detector.detect(detectOptions);
340
+ detected?.forEach(control => {
341
+ const iotControl = control;
342
+ // if any detected state has an ID, we can use this control
343
+ if (iotControl.states.find(state => state.id)) {
344
+ // remove all unassigned control register
345
+ iotControl.states = iotControl.states.filter(s => s.id);
346
+ // take all smartNames if any
347
+ iotControl.states.forEach(s => {
348
+ s.smartName = getSmartNameFromObj(devicesObject[s.id], adapter.namespace);
349
+ s.common = {
350
+ min: devicesObject[s.id]?.common?.min,
351
+ max: devicesObject[s.id]?.common?.max,
352
+ type: devicesObject[s.id]?.common?.type,
353
+ states: devicesObject[s.id]?.common?.states,
354
+ unit: devicesObject[s.id]?.common?.unit,
355
+ role: devicesObject[s.id]?.common?.role,
356
+ name: devicesObject[s.id]?.common?.name,
357
+ icon: devicesObject[s.id]?.common?.icon,
358
+ color: devicesObject[s.id]?.common?.color,
359
+ };
360
+ });
361
+ // find out the room the found control is in
362
+ const room = rooms.find(room => room?.common?.members?.includes(id));
363
+ // find out the functionality the found control assigned to
364
+ const functionality = functionalities.find(functionality => functionality?.common?.members?.includes(id));
365
+ const smartName = getSmartNameFromObj(devicesObject[id], adapter.namespace);
366
+ iotControl.object = {
367
+ id,
368
+ type: devicesObject[id].type,
369
+ common: {
370
+ min: devicesObject[id].common?.min,
371
+ max: devicesObject[id].common?.max,
372
+ type: devicesObject[id].common?.type,
373
+ states: devicesObject[id].common?.states,
374
+ role: devicesObject[id].common?.role,
375
+ name: devicesObject[id].common?.name,
376
+ icon: devicesObject[id].common?.icon,
377
+ color: devicesObject[id].common?.color,
378
+ smartName,
379
+ },
380
+ autoDetected: true,
381
+ toggle: smartName && typeof smartName === 'object' ? smartName.toggle : undefined,
382
+ };
383
+ iotControl.room = room
384
+ ? {
385
+ id: room._id,
386
+ common: room.common,
387
+ }
388
+ : undefined;
389
+ iotControl.functionality = functionality
390
+ ? {
391
+ id: functionality._id,
392
+ common: functionality.common,
393
+ }
394
+ : undefined;
395
+ detectedControls.push(iotControl);
396
+ }
397
+ });
398
+ });
399
+ return detectedControls;
400
+ }
401
+ function getName(name, lang, id) {
402
+ if (typeof name === 'string') {
403
+ return name;
404
+ }
405
+ if (name) {
406
+ return name[lang] || name.en || Object.values(name)[0] || id.split('.').pop() || 'Unnamed';
407
+ }
408
+ return id?.split('.').pop() || 'Unnamed';
409
+ }
410
+ function getControlType(device, state) {
411
+ let smartType;
412
+ // Try to guess from Device Type
413
+ if (device.type === type_detector_1.Types.airCondition) {
414
+ if (state.name === 'SET') {
415
+ // set temperature
416
+ smartType = 'targetTemperature';
417
+ }
418
+ else if (state.name === 'ACTUAL') {
419
+ smartType = 'actualTemperature';
420
+ }
421
+ else if (state.name === 'SPEED') {
422
+ smartType = 'fanSpeed';
423
+ }
424
+ else if (state.name === 'POWER') {
425
+ smartType = 'power';
426
+ }
427
+ else if (state.name === 'HUMIDITY') {
428
+ smartType = 'humidity';
429
+ }
430
+ else if (state.name === 'BOOST') {
431
+ smartType = 'boostMode';
432
+ }
433
+ else if (state.name === 'SWING') {
434
+ if (state.common.type === 'boolean') {
435
+ smartType = 'swingOnOff';
436
+ }
437
+ else if (state.common.type === 'number') {
438
+ smartType = 'swingPosition';
439
+ }
440
+ }
441
+ }
442
+ else if (device.type === type_detector_1.Types.blind) {
443
+ if (state.name === 'SET') {
444
+ smartType = 'blindPosition';
445
+ }
446
+ else if (state.name === 'STOP') {
447
+ smartType = 'stop';
448
+ }
449
+ else if (state.name === 'OPEN') {
450
+ smartType = 'open';
451
+ }
452
+ else if (state.name === 'CLOSE') {
453
+ smartType = 'close';
454
+ }
455
+ }
456
+ else if (device.type === type_detector_1.Types.cie) {
457
+ if (state.name === 'CIE') {
458
+ // set temperature
459
+ smartType = 'color';
460
+ }
461
+ else if (state.name === 'DIMMER' || state.name === 'BRIGHTNESS') {
462
+ smartType = 'dimmer';
463
+ }
464
+ else if (state.name === 'TEMPERATURE') {
465
+ smartType = 'colorTemperature';
466
+ }
467
+ else if (state.name === 'ON') {
468
+ smartType = 'power';
469
+ }
470
+ }
471
+ else if (device.type === type_detector_1.Types.ct) {
472
+ if (state.name === 'DIMMER' || state.name === 'BRIGHTNESS') {
473
+ smartType = 'dimmer';
474
+ }
475
+ else if (state.name === 'TEMPERATURE') {
476
+ smartType = 'colorTemperature';
477
+ }
478
+ else if (state.name === 'ON') {
479
+ smartType = 'power';
480
+ }
481
+ }
482
+ else if (device.type === type_detector_1.Types.dimmer) {
483
+ if (state.name === 'SET') {
484
+ smartType = 'dimmer';
485
+ }
486
+ else if (state.name === 'ON') {
487
+ smartType = 'power';
488
+ }
489
+ }
490
+ else if (device.type === type_detector_1.Types.door) {
491
+ if (state.name === 'ACTUAL') {
492
+ smartType = 'openedClosed';
493
+ }
494
+ }
495
+ else if (device.type === type_detector_1.Types.fireAlarm) {
496
+ if (state.name === 'ACTUAL') {
497
+ smartType = 'alarm';
498
+ }
499
+ }
500
+ else if (device.type === type_detector_1.Types.floodAlarm) {
501
+ if (state.name === 'ACTUAL') {
502
+ smartType = 'alarm';
503
+ }
504
+ }
505
+ else if (device.type === type_detector_1.Types.gate) {
506
+ if (state.name === 'SET') {
507
+ smartType = 'openClose';
508
+ }
509
+ else if (state.name === 'STOP') {
510
+ smartType = 'stop';
511
+ }
512
+ }
513
+ else if (device.type === type_detector_1.Types.hue) {
514
+ if (state.name === 'HUE') {
515
+ smartType = 'color';
516
+ }
517
+ else if (state.name === 'DIMMER' || state.name === 'BRIGHTNESS') {
518
+ smartType = 'dimmer';
519
+ }
520
+ else if (state.name === 'TEMPERATURE') {
521
+ smartType = 'colorTemperature';
522
+ }
523
+ else if (state.name === 'SATURATION') {
524
+ smartType = 'saturation';
525
+ }
526
+ else if (state.name === 'ON') {
527
+ smartType = 'power';
528
+ }
529
+ }
530
+ else if (device.type === type_detector_1.Types.humidity) {
531
+ if (state.name === 'ACTUAL') {
532
+ smartType = 'humidity';
533
+ }
534
+ }
535
+ else if (device.type === type_detector_1.Types.illuminance) {
536
+ if (state.name === 'ACTUAL') {
537
+ smartType = 'illuminance';
538
+ }
539
+ }
540
+ else if (device.type === type_detector_1.Types.slider) {
541
+ if (state.name === 'SET') {
542
+ smartType = 'level';
543
+ }
544
+ }
545
+ else if (device.type === type_detector_1.Types.light) {
546
+ if (state.name === 'SET') {
547
+ smartType = 'power';
548
+ }
549
+ }
550
+ else if (device.type === type_detector_1.Types.lock) {
551
+ if (state.name === 'SET') {
552
+ smartType = 'lock';
553
+ }
554
+ else if (state.name === 'OPEN') {
555
+ smartType = 'open';
556
+ }
557
+ }
558
+ else if (device.type === type_detector_1.Types.motion) {
559
+ if (state.name === 'ACTUAL') {
560
+ smartType = 'alarm';
561
+ }
562
+ }
563
+ else if (device.type === type_detector_1.Types.rgb) {
564
+ if (state.name === 'RED') {
565
+ smartType = 'colorRed';
566
+ }
567
+ else if (state.name === 'BLUE') {
568
+ smartType = 'colorBlue';
569
+ }
570
+ else if (state.name === 'GREEN') {
571
+ smartType = 'colorGreen';
572
+ }
573
+ else if (state.name === 'WHITE') {
574
+ smartType = 'colorWhite';
575
+ }
576
+ else if (state.name === 'DIMMER' || state.name === 'BRIGHTNESS') {
577
+ smartType = 'dimmer';
578
+ }
579
+ else if (state.name === 'TEMPERATURE') {
580
+ smartType = 'colorTemperature';
581
+ }
582
+ else if (state.name === 'ON') {
583
+ smartType = 'power';
584
+ }
585
+ }
586
+ else if (device.type === type_detector_1.Types.rgbSingle) {
587
+ if (state.name === 'RGB') {
588
+ smartType = 'color';
589
+ }
590
+ else if (state.name === 'DIMMER' || state.name === 'BRIGHTNESS') {
591
+ smartType = 'dimmer';
592
+ }
593
+ else if (state.name === 'TEMPERATURE') {
594
+ smartType = 'colorTemperature';
595
+ }
596
+ else if (state.name === 'ON') {
597
+ smartType = 'power';
598
+ }
599
+ }
600
+ else if (device.type === type_detector_1.Types.rgbwSingle) {
601
+ if (state.name === 'RGBW') {
602
+ smartType = 'color';
603
+ }
604
+ else if (state.name === 'DIMMER' || state.name === 'BRIGHTNESS') {
605
+ smartType = 'dimmer';
606
+ }
607
+ else if (state.name === 'TEMPERATURE') {
608
+ smartType = 'colorTemperature';
609
+ }
610
+ else if (state.name === 'ON') {
611
+ smartType = 'power';
612
+ }
613
+ }
614
+ else if (device.type === type_detector_1.Types.socket) {
615
+ if (state.name === 'SET') {
616
+ smartType = 'power';
617
+ }
618
+ }
619
+ else if (device.type === type_detector_1.Types.temperature) {
620
+ if (state.name === 'ACTUAL') {
621
+ smartType = 'actualTemperature';
622
+ }
623
+ }
624
+ else if (device.type === type_detector_1.Types.thermostat) {
625
+ if (state.name === 'ACTUAL') {
626
+ smartType = 'actualTemperature';
627
+ }
628
+ else if (state.name === 'SET') {
629
+ smartType = 'targetTemperature';
630
+ }
631
+ else if (state.name === 'HUMIDITY') {
632
+ smartType = 'humidity';
633
+ }
634
+ else if (state.name === 'BOOST') {
635
+ smartType = 'boostMode';
636
+ }
637
+ else if (state.name === 'POWER') {
638
+ smartType = 'power';
639
+ }
640
+ }
641
+ else if (device.type === type_detector_1.Types.vacuumCleaner) {
642
+ if (state.name === 'POWER') {
643
+ smartType = 'power';
644
+ }
645
+ }
646
+ else if (device.type === type_detector_1.Types.volume) {
647
+ if (state.name === 'SET') {
648
+ smartType = 'volume';
649
+ }
650
+ }
651
+ else if (device.type === type_detector_1.Types.volumeGroup) {
652
+ if (state.name === 'SET') {
653
+ smartType = 'volume';
654
+ }
655
+ }
656
+ else if (device.type === type_detector_1.Types.window) {
657
+ if (state.name === 'ACTUAL') {
658
+ smartType = 'openedClosed';
659
+ }
660
+ }
661
+ else if (device.type === type_detector_1.Types.windowTilt) {
662
+ if (state.name === 'ACTUAL') {
663
+ smartType = 'openedClosed';
664
+ }
665
+ }
666
+ return smartType || state.name;
667
+ }
668
+ /**
669
+ * Build an AI-friendly structure of rooms -> devices -> named controls.
670
+ *
671
+ * @param adapter The adapter instance
672
+ * @param lang language
673
+ * @param options read options (ACL user)
674
+ */
675
+ async function getAiFriendlyStructure(adapter, lang, options) {
676
+ const devices = await controls(adapter, lang, options);
677
+ // Reformat the data
678
+ const rooms = [];
679
+ devices.forEach(device => {
680
+ let roomName;
681
+ let roomObj;
682
+ if (!device.room) {
683
+ // Create "No room" object
684
+ const rObj = rooms.find(r => r.roomName === 'No room');
685
+ if (rObj) {
686
+ roomObj = rObj;
687
+ }
688
+ else {
689
+ roomObj = {
690
+ roomName: 'No room',
691
+ devicesInRoom: [],
692
+ };
693
+ rooms.push(roomObj);
694
+ }
695
+ roomName = 'No room';
696
+ }
697
+ else {
698
+ roomName = getName(device.room.common.name, lang, device.room.id);
699
+ const rObj = rooms.find(r => r.roomName === roomName);
700
+ if (rObj) {
701
+ roomObj = rObj;
702
+ }
703
+ else {
704
+ roomObj = {
705
+ roomName,
706
+ devicesInRoom: [],
707
+ };
708
+ rooms.push(roomObj);
709
+ }
710
+ }
711
+ const functionName = device.functionality
712
+ ? getName(device.functionality.common.name, lang, device.functionality.id)
713
+ : undefined;
714
+ const dev = {
715
+ deviceName: getName(device.object?.common?.name, lang, device.object?.id || ''),
716
+ deviceType: device.type,
717
+ friendlyDeviceNames: device.groupNames,
718
+ controls: {},
719
+ };
720
+ if (roomName) {
721
+ dev.room = roomName;
722
+ }
723
+ if (functionName) {
724
+ dev.functionality = functionName;
725
+ }
726
+ // Fill controls
727
+ device.states.forEach(state => {
728
+ const control = {
729
+ stateId: state.id,
730
+ controlType: state.id === device.object?.id ? 'power' : state.role || 'power',
731
+ role: state.defaultRole,
732
+ writable: state.write !== false,
733
+ readable: state.read !== false,
734
+ unit: state.common?.unit || state.defaultUnit,
735
+ min: typeof state.min === 'number' ? state.min : undefined,
736
+ max: typeof state.max === 'number' ? state.max : undefined,
737
+ ioBrokerValueType: state.common?.type || 'boolean',
738
+ };
739
+ let smartType = state.smartName?.smartType;
740
+ if (!smartType) {
741
+ // Try to guess from Device Type
742
+ smartType = getControlType(device, state);
743
+ }
744
+ if (smartType) {
745
+ dev.controls[smartType] = control;
746
+ }
747
+ });
748
+ roomObj.devicesInRoom.push(dev);
749
+ });
750
+ return rooms;
751
+ }
752
+ //# sourceMappingURL=devices.js.map