@deconz-community/ddf-validator 1.3.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,653 +1,416 @@
1
- import { z as t } from "zod";
2
- const s = [
3
- "attr/class",
4
- "attr/configid",
5
- "attr/extaddress",
6
- "attr/groupaddress",
7
- "attr/id",
8
- "attr/lastannounced",
9
- "attr/lastseen",
10
- "attr/levelmin",
11
- "attr/manufacturername",
12
- "attr/modelid",
13
- "attr/name",
14
- "attr/nwkaddress",
15
- "attr/poweronct",
16
- "attr/poweronlevel",
17
- "attr/powerup",
18
- "attr/productid",
19
- "attr/productname",
20
- "attr/swconfigid",
21
- "attr/swversion",
22
- "attr/type",
23
- "attr/uniqueid",
24
- "state/action",
25
- "state/airquality",
26
- "state/airqualityppb",
27
- "state/alarm",
28
- "state/alert",
29
- "state/all_on",
30
- "state/angle",
31
- "state/any_on",
32
- "state/armstate",
33
- "state/battery",
34
- "state/bri",
35
- "state/buttonevent",
36
- "state/carbonmonoxide",
37
- "state/charging",
38
- "state/colormode",
39
- "state/consumption",
40
- "state/consumption_2",
41
- "state/ct",
42
- "state/current",
43
- "state/current_P1",
44
- "state/current_P2",
45
- "state/current_P3",
46
- "state/dark",
47
- "state/daylight",
48
- "state/deviceruntime",
49
- "state/effect",
50
- "state/errorcode",
51
- "state/eventduration",
52
- "state/expectedeventduration",
53
- "state/expectedrotation",
54
- "state/filterruntime",
55
- "state/fire",
56
- "state/flag",
57
- "state/floortemperature",
58
- "state/gpd_frame_counter",
59
- "state/gpd_last_pair",
60
- "state/gesture",
61
- "state/gradient",
62
- "state/heating",
63
- "state/hue",
64
- "state/humidity",
65
- "state/lastcheckin",
66
- "state/lastset",
67
- "state/lastupdated",
68
- "state/lift",
69
- "state/lightlevel",
70
- "state/localtime",
71
- "state/lockstate",
72
- "state/lowbattery",
73
- "state/lux",
74
- "state/moisture",
75
- "state/mountingmodeactive",
76
- "state/on",
77
- "state/open",
78
- "state/orientation_x",
79
- "state/orientation_y",
80
- "state/orientation_z",
81
- "state/pm2_5",
82
- "state/panel",
83
- "state/power",
84
- "state/presence",
85
- "state/presenceevent",
86
- "state/pressure",
87
- "state/production",
88
- "state/reachable",
89
- "state/replacefilter",
90
- "state/rotaryevent",
91
- "state/sat",
92
- "state/seconds_remaining",
93
- "state/spectral_x",
94
- "state/spectral_y",
95
- "state/spectral_z",
96
- "state/speed",
97
- "state/status",
98
- "state/sunrise",
99
- "state/sunset",
100
- "state/tampered",
101
- "state/temperature",
102
- "state/test",
103
- "state/tilt",
104
- "state/tiltangle",
105
- "state/utc",
106
- "state/valve",
107
- "state/vibration",
108
- "state/vibrationstrength",
109
- "state/voltage",
110
- "state/water",
111
- "state/windowopen",
112
- "state/x",
113
- "state/y",
114
- "cap/alert/trigger_effect",
115
- "cap/bri/min_dim_level",
116
- "cap/bri/move_with_onoff",
117
- "cap/color/capabilities",
118
- "cap/color/ct/computes_xy",
119
- "cap/color/ct/max",
120
- "cap/color/ct/min",
121
- "cap/color/effects",
122
- "cap/color/gamut_type",
123
- "cap/color/gradient/max_segments",
124
- "cap/color/gradient/pixel_count",
125
- "cap/color/gradient/pixel_length",
126
- "cap/color/gradient/styles",
127
- "cap/color/xy/blue_x",
128
- "cap/color/xy/blue_y",
129
- "cap/color/xy/green_x",
130
- "cap/color/xy/green_y",
131
- "cap/color/xy/red_x",
132
- "cap/color/xy/red_y",
133
- "cap/groups/not_supported",
134
- "cap/on/off_with_effect",
135
- "cap/sleeper",
136
- "cap/transition_block",
137
- "config/alarmsystemid",
138
- "config/alert",
139
- "config/allowtouchlink",
140
- "config/armmode",
141
- "config/armed_away_entry_delay",
142
- "config/armed_away_exit_delay",
143
- "config/armed_away_trigger_duration",
144
- "config/armed_night_entry_delay",
145
- "config/armed_night_exit_delay",
146
- "config/armed_night_trigger_duration",
147
- "config/armed_stay_entry_delay",
148
- "config/armed_stay_exit_delay",
149
- "config/armed_stay_trigger_duration",
150
- "config/battery",
151
- "config/bri/execute_if_off",
152
- "config/bri/max",
153
- "config/bri/min",
154
- "config/bri/on_level",
155
- "config/bri/onoff_transitiontime",
156
- "config/bri/startup",
157
- "config/checkin",
158
- "config/clickmode",
159
- "config/colorcapabilities",
160
- "config/color/ct/startup",
161
- "config/color/execute_if_off",
162
- "config/color/gradient/reversed",
163
- "config/color/xy/startup_x",
164
- "config/color/xy/startup_y",
165
- "config/configured",
166
- "config/controlsequence",
167
- "config/coolsetpoint",
168
- "config/ctmax",
169
- "config/ctmin",
170
- "config/delay",
171
- "config/devicemode",
172
- "config/disarmed_entry_delay",
173
- "config/disarmed_exit_delay",
174
- "config/displayflipped",
175
- "config/duration",
176
- "config/enrolled",
177
- "config/externalsensortemp",
178
- "config/externalwindowopen",
179
- "config/fanmode",
180
- "config/filterlifetime",
181
- "config/gpd_device_id",
182
- "config/gpd_key",
183
- "config/group",
184
- "config/heatsetpoint",
185
- "config/hostflags",
186
- "config/humiditymaxthreshold",
187
- "config/humidityminthreshold",
188
- "config/interfacemode",
189
- "config/lastchange_amount",
190
- "config/lastchange_source",
191
- "config/lastchange_time",
192
- "config/lat",
193
- "config/ledindication",
194
- "config/localtime",
195
- "config/lock",
196
- "config/locked",
197
- "config/long",
198
- "config/melody",
199
- "config/mode",
200
- "config/mountingmode",
201
- "config/offset",
202
- "config/on",
203
- "config/on/startup",
204
- "config/pending",
205
- "config/preset",
206
- "config/pulseconfiguration",
207
- "config/reachable",
208
- "config/resetpresence",
209
- "config/schedule",
210
- "config/schedule_on",
211
- "config/selftest",
212
- "config/sensitivity",
213
- "config/sensitivitymax",
214
- "config/setvalve",
215
- "config/sunriseoffset",
216
- "config/sunsetoffset",
217
- "config/swingmode",
218
- "config/temperaturemaxthreshold",
219
- "config/temperatureminthreshold",
220
- "config/temperature",
221
- "config/temperaturemeasurement",
222
- "config/tholddark",
223
- "config/tholdoffset",
224
- "config/unoccupiedheatsetpoint",
225
- "config/triggerdistance",
226
- "config/ubisys_j1_additionalsteps",
227
- "config/ubisys_j1_configurationandstatus",
228
- "config/ubisys_j1_inactivepowerthreshold",
229
- "config/ubisys_j1_installedclosedlimitlift",
230
- "config/ubisys_j1_installedclosedlimittilt",
231
- "config/ubisys_j1_installedopenlimitlift",
232
- "config/ubisys_j1_installedopenlimittilt",
233
- "config/ubisys_j1_lifttotilttransitionsteps",
234
- "config/ubisys_j1_lifttotilttransitionsteps2",
235
- "config/ubisys_j1_mode",
236
- "config/ubisys_j1_startupsteps",
237
- "config/ubisys_j1_totalsteps",
238
- "config/ubisys_j1_totalsteps2",
239
- "config/ubisys_j1_turnaroundguardtime",
240
- "config/ubisys_j1_windowcoveringtype",
241
- "config/url",
242
- "config/usertest",
243
- "config/volume",
244
- "config/windowcoveringtype",
245
- "config/windowopen_set",
246
- // Missing from resource.cpp
247
- "attr/mode",
248
- "state/orientation",
249
- // Just present in at least one DDF
250
- "state/airqualityformaldehyde",
251
- "state/airqualityco2"
252
- ], c = [
253
- "$TYPE_AIR_PURIFIER",
254
- "$TYPE_AIR_QUALITY_SENSOR",
255
- "$TYPE_ALARM_SENSOR",
256
- "$TYPE_BATTERY_SENSOR",
257
- "$TYPE_COLOR_DIMMABLE_LIGHT",
258
- "$TYPE_COLOR_LIGHT",
259
- "$TYPE_COLOR_TEMPERATURE_LIGHT",
260
- "$TYPE_CONSUMPTION_SENSOR",
261
- "$TYPE_DIMMABLE_LIGHT",
262
- "$TYPE_DIMMABLE_PLUGIN_UNIT",
263
- "$TYPE_DIMMER_SWITCH",
264
- "$TYPE_DOOR_LOCK",
265
- "$TYPE_DOOR_LOCK_CONTROLLER",
266
- "$TYPE_EXTENDED_COLOR_LIGHT",
267
- "$TYPE_FIRE_SENSOR",
268
- "$TYPE_HUMIDITY_SENSOR",
269
- "$TYPE_LIGHT_LEVEL_SENSOR",
270
- "$TYPE_ON_OFF_LIGHT",
271
- "$TYPE_ON_OFF_OUTPUT",
272
- "$TYPE_ON_OFF_PLUGIN_UNIT",
273
- "$TYPE_OPEN_CLOSE_SENSOR",
274
- "$TYPE_POWER_SENSOR",
275
- "$TYPE_PRESENCE_SENSOR",
276
- "$TYPE_PRESSURE_SENSOR",
277
- "$TYPE_RANGE_EXTENDER",
278
- "$TYPE_RELATIVE_ROTARY",
279
- "$TYPE_SMART_PLUG",
280
- "$TYPE_SPECTRAL_SENSOR",
281
- "$TYPE_SWITCH",
282
- "$TYPE_TEMPERATURE_SENSOR",
283
- "$TYPE_THERMOSTAT",
284
- "$TYPE_VIBRATION_SENSOR",
285
- "$TYPE_WARNING_DEVICE",
286
- "$TYPE_WATER_LEAK_SENSOR",
287
- "$TYPE_WINDOW_COVERING_DEVICE",
288
- "$TYPE_ZGP_SWITCH",
289
- "Color dimmable light",
290
- "Color light",
291
- "Color temperature light",
292
- "Dimmable light",
293
- "Dimmable plug-in unit",
294
- "Dimmer switch",
295
- "Door lock controller",
296
- "Door Lock",
297
- "Extended color light",
298
- "On/Off light",
299
- "On/Off output",
300
- "On/Off plug-in unit",
301
- "Range extender",
302
- "Smart plug",
303
- "Warning device",
304
- "Window covering device",
305
- "ZGPSwitch",
306
- "ZHAAirPurifier",
307
- "ZHAAirQuality",
308
- "ZHAAlarm",
309
- "ZHAAncillaryControl",
310
- "ZHABattery",
311
- "ZHACarbonMonoxide",
312
- "ZHAConsumption",
313
- "ZHADoorLock",
314
- "ZHAFire",
315
- "ZHAHumidity",
316
- "ZHALightLevel",
317
- "ZHAOpenClose",
318
- "ZHAPower",
319
- "ZHAPresence",
320
- "ZHAPressure",
321
- "ZHARelativeRotary",
322
- "ZHASpectral",
323
- "ZHASwitch",
324
- "ZHATemperature",
325
- "ZHAThermostat",
326
- "ZHATime",
327
- "ZHAVibration",
328
- "ZHAWater"
1
+ import { z as e } from "zod";
2
+ const D = [
3
+ A,
4
+ O
329
5
  ];
330
- function e(n = void 0) {
331
- return n === void 0 ? t.string() : t.string().length(2 + n);
6
+ function A(t, r) {
7
+ const o = typeof t.manufacturername == "string" && typeof t.modelid == "string";
8
+ if (o)
9
+ return;
10
+ const i = Array.isArray(t.manufacturername) && Array.isArray(t.modelid);
11
+ if (i && t.manufacturername.length !== t.modelid.length) {
12
+ r.addIssue({
13
+ code: e.ZodIssueCode.invalid_intersection_types,
14
+ message: "When 'manufacturername' and 'modelid' are both arrays they should be the same length",
15
+ path: ["manufacturername", "modelid"]
16
+ });
17
+ return;
18
+ }
19
+ (o || i) === !1 && r.addIssue({
20
+ code: e.ZodIssueCode.invalid_intersection_types,
21
+ message: "Invalid properties 'manufacturername' and 'modelid' should have the same type",
22
+ path: ["manufacturername", "modelid"]
23
+ });
332
24
  }
333
- function o() {
334
- return t.string();
25
+ function O(t, r) {
26
+ if (!t.bindings)
27
+ return;
28
+ const o = {}, i = (s) => typeof s == "number" ? s : parseInt(s, 16);
29
+ t.bindings.forEach((s) => {
30
+ s.bind === "unicast" && s.report && s.report.forEach((u) => {
31
+ o[`${i(s["src.ep"])}.${i(s.cl)}.${i(u.at)}`] = u.max;
32
+ });
33
+ }), t.subdevices.forEach((s, u) => {
34
+ s.items.forEach((a, x) => {
35
+ var b;
36
+ if (a["refresh.interval"] && a.read && a.read.fn === "zcl") {
37
+ const w = i(a.read.ep ?? ((b = s.fingerprint) == null ? void 0 : b.endpoint) ?? s.uuid[1]), f = Array.isArray(a.read.at) ? a.read.at : [a.read.at];
38
+ for (let p = 0; p < f.length; p++) {
39
+ const m = `${w}.${i(a.read.cl)}.${i(f[p])}`;
40
+ o[m] !== void 0 && a["refresh.interval"] - 60 < o[m] && r.addIssue({
41
+ code: e.ZodIssueCode.custom,
42
+ message: `The refresh interval (${a["refresh.interval"]} - 60 = ${a["refresh.interval"] - 60}) should be greater than the binding max refresh value (${o[m]}) with a margin of 60 seconds`,
43
+ path: ["subdevices", u, "items", x, "refresh.interval"]
44
+ });
45
+ }
46
+ }
47
+ });
48
+ });
335
49
  }
336
- function l() {
337
- return t.string();
50
+ function $(t) {
51
+ return e.strictObject({
52
+ $schema: e.optional(e.string()),
53
+ schema: e.literal("constants1.schema.json"),
54
+ manufacturers: e.record(e.string().startsWith("$MF_"), e.string()),
55
+ "device-types": e.record(e.string().startsWith("$TYPE_"), e.string())
56
+ });
338
57
  }
339
- function i() {
340
- return t.union([
341
- e(2),
342
- t.number().min(0).max(255)
343
- ]);
58
+ function v() {
59
+ return e.string().regex(
60
+ // Regex for AAAA-MM-JJ
61
+ /^(\d{4})-(?:(?:0[1-9])|(?:1[0-2]))-(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01]))$/,
62
+ "Invalid date value"
63
+ );
344
64
  }
345
- function a() {
346
- return t.string();
65
+ function n(t = void 0) {
66
+ const r = "Invalid hexadecimal value";
67
+ return t === void 0 ? e.string().regex(/^0x[0-9a-fA-F]+$/, r) : e.string().regex(new RegExp(`^0x[0-9a-fA-F]{${t}}$`), r);
347
68
  }
348
- function _() {
349
- return t.custom((n) => {
350
- if (!Array.isArray(n) || n.length % 2 !== 0)
69
+ function c() {
70
+ return e.union([
71
+ n(2),
72
+ e.number().min(0).max(255)
73
+ ]);
74
+ }
75
+ function T() {
76
+ return e.custom((t) => {
77
+ if (!Array.isArray(t) || t.length % 2 !== 0)
351
78
  return !1;
352
- for (let r = 0; r < n.length; r += 2) {
353
- const m = n[r], d = n[r + 1];
354
- if (typeof m != "number" || typeof d != "string")
79
+ for (let r = 0; r < t.length; r += 2) {
80
+ const o = t[r], i = t[r + 1];
81
+ if (typeof o != "number" || typeof i != "string")
355
82
  return !1;
356
83
  }
357
84
  return !0;
358
85
  }, "The value must be an array with an even number of values and alternating between number and string.");
359
86
  }
360
- function p() {
361
- return t.union([
362
- t.tuple([
363
- t.literal("$address.ext"),
364
- e(2)
87
+ function g() {
88
+ return e.union([
89
+ e.tuple([
90
+ e.literal("$address.ext"),
91
+ n(2)
365
92
  ]),
366
- t.tuple([
367
- t.literal("$address.ext"),
368
- e(2),
369
- e(4)
93
+ e.tuple([
94
+ e.literal("$address.ext"),
95
+ n(2),
96
+ n(4)
370
97
  ])
371
98
  ]);
372
99
  }
373
- function A(n) {
374
- return b().parse(n);
100
+ function l() {
101
+ return e.string();
375
102
  }
376
- function b() {
377
- return t.discriminatedUnion("schema", [
378
- y(),
379
- v(),
380
- O(),
381
- P()
382
- ]);
103
+ function d() {
104
+ return e.string();
383
105
  }
384
106
  function y() {
385
- return t.strictObject({
386
- $schema: t.optional(t.string()),
387
- schema: t.literal("devcap1.schema.json"),
388
- "doc:path": t.optional(t.string()),
389
- "doc:hdr": t.optional(t.string()),
390
- "md:known_issues": t.optional(t.array(t.string())),
391
- manufacturername: t.string().or(t.array(t.string())),
392
- modelid: t.string().or(t.array(t.string())),
393
- vendor: t.optional(t.string()),
394
- comment: t.optional(t.string()),
395
- matchexpr: t.optional(o()),
396
- path: t.optional(a()),
397
- product: t.optional(t.string()),
398
- sleeper: t.optional(t.boolean()),
399
- supportsMgmtBind: t.optional(t.boolean()),
400
- status: t.enum(["Draft", "Bronze", "Silver", "Gold"]).describe("The code quality of the DDF file."),
401
- subdevices: t.array(h()),
402
- bindings: t.optional(t.array(T()))
403
- }).refine((n) => typeof n.manufacturername == "string" && typeof n.modelid == "string" || Array.isArray(n.manufacturername) && Array.isArray(n.modelid) && n.manufacturername.length === n.modelid.length, {
404
- message: "manufacturername and modelid should be both strings or arrays with the same length.",
405
- path: ["manufacturername", "modelid"]
406
- }).innerType();
407
- }
408
- function h() {
409
- return t.strictObject({
410
- type: t.enum(c),
411
- restapi: t.enum(["/lights", "/sensors"]),
412
- uuid: p(),
413
- fingerprint: t.optional(t.strictObject({
414
- profile: e(4),
415
- device: e(4),
416
- endpoint: i(),
417
- in: t.optional(t.array(e(4))),
418
- out: t.optional(t.array(e(4)))
419
- })),
420
- meta: t.optional(t.strictObject({
421
- // TODO validate this
422
- values: t.any(),
423
- "group.endpoints": t.optional(t.array(t.number()))
424
- })),
425
- // TODO validate this
426
- buttons: t.optional(t.any()),
427
- // TODO validate this
428
- buttonevents: t.optional(t.any()),
429
- items: t.array(E()),
430
- example: t.optional(t.unknown())
431
- });
432
- }
433
- function E() {
434
- return t.strictObject({
435
- name: t.enum(s),
436
- description: t.optional(t.string()),
437
- comment: t.optional(t.string()),
438
- public: t.optional(t.boolean()),
439
- static: t.optional(t.union([t.string(), t.number(), t.boolean()])),
440
- range: t.optional(t.tuple([t.number(), t.number()])),
441
- deprecated: t.optional(l()),
442
- access: t.optional(t.literal("R")),
443
- read: t.optional(u()),
444
- parse: t.optional(f()),
445
- write: t.optional(g()),
446
- awake: t.optional(t.boolean()),
447
- default: t.optional(t.unknown()),
448
- values: t.optional(t.unknown()),
449
- "refresh.interval": t.optional(t.number())
107
+ return e.discriminatedUnion("fn", [
108
+ e.strictObject({
109
+ fn: e.literal("none")
110
+ }),
111
+ e.strictObject({
112
+ fn: e.undefined().describe("Generic function to read ZCL attributes."),
113
+ at: n(4).or(e.array(n(4))).describe("Attribute ID."),
114
+ cl: n(4).describe("Cluster ID."),
115
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
116
+ mf: e.optional(n(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
117
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value.")
118
+ }),
119
+ e.strictObject({
120
+ fn: e.literal("zcl").describe("Generic function to read ZCL attributes."),
121
+ at: n(4).or(e.array(n(4))).describe("Attribute ID."),
122
+ cl: n(4).describe("Cluster ID."),
123
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
124
+ mf: e.optional(n(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
125
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value.")
126
+ }),
127
+ e.strictObject({
128
+ fn: e.literal("tuya").describe("Generic function to read all Tuya datapoints. It has no parameters.")
129
+ })
130
+ ]).refine((t) => !("eval" in t && "script" in t), {
131
+ message: "eval and script should not both be present"
450
132
  });
451
133
  }
452
- function u() {
453
- return t.discriminatedUnion("fn", [
454
- t.strictObject({
455
- fn: t.literal("none")
134
+ function j() {
135
+ return e.discriminatedUnion("fn", [
136
+ e.strictObject({
137
+ fn: e.undefined().describe("Generic function to parse ZCL attributes and commands."),
138
+ at: e.optional(n(4)).describe("Attribute ID."),
139
+ cl: n(4).describe("Cluster ID."),
140
+ cppsrc: e.optional(e.string()),
141
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
142
+ cmd: e.optional(n(2)).describe("Zigbee command."),
143
+ mf: e.optional(n(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
144
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
145
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
146
+ }),
147
+ e.strictObject({
148
+ fn: e.literal("zcl").describe("Generic function to parse ZCL attributes and commands."),
149
+ at: e.optional(n(4)).describe("Attribute ID."),
150
+ cl: n(4).describe("Cluster ID."),
151
+ cppsrc: e.optional(e.string()),
152
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
153
+ cmd: e.optional(n(2)).describe("Zigbee command."),
154
+ mf: e.optional(n(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
155
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
156
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
157
+ }),
158
+ e.strictObject({
159
+ fn: e.literal("ias:zonestatus").describe("Generic function to parse IAS ZONE status change notifications or zone status from read/report command."),
160
+ mask: e.optional(e.enum(["alarm1", "alarm2"]).or(e.literal("alarm1,alarm2"))).describe("Sets the bitmask for Alert1 and Alert2 item of the IAS Zone status.")
456
161
  }),
457
- t.strictObject({
458
- fn: t.undefined(),
459
- at: t.optional(e(4).or(t.array(e(4)))),
460
- cl: e(4),
461
- ep: t.optional(i()),
462
- mf: t.optional(e(4)),
463
- eval: t.optional(o())
162
+ e.strictObject({
163
+ fn: e.literal("numtostr").describe("Generic function to to convert number to string."),
164
+ // TODO use generic
165
+ srcitem: e.enum(["state/airqualityppb", "state/pm2_5"]).describe("The source item holding the number."),
166
+ op: e.enum(["lt", "le", "eq", "gt", "ge"]).describe("Comparison operator (lt | le | eq | gt | ge)"),
167
+ to: T().describe("Array of (num, string) mappings")
464
168
  }),
465
- t.strictObject({
466
- fn: t.literal("zcl"),
467
- at: t.optional(e(4).or(t.array(e(4)))),
468
- cl: e(4),
469
- ep: t.optional(i()),
470
- mf: t.optional(e(4)),
471
- eval: t.optional(o())
169
+ e.strictObject({
170
+ fn: e.literal("time").describe("Specialized function to parse time, local and last set time from read/report commands of the time cluster and auto-sync time if needed.")
472
171
  }),
473
- t.strictObject({
474
- fn: t.literal("tuya")
172
+ e.strictObject({
173
+ fn: e.literal("xiaomi:special").describe("Generic function to parse custom Xiaomi attributes and commands."),
174
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
175
+ at: e.optional(n(4)).describe("Attribute ID. The attribute to parse, shall be 0xff01, 0xff02 or 0x00f7"),
176
+ idx: n(2).describe("A 8-bit string hex value."),
177
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
178
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
179
+ }),
180
+ e.strictObject({
181
+ fn: e.literal("tuya").describe("Generic function to parse Tuya data."),
182
+ dpid: e.number().describe("Data point ID. 1-255 the datapoint ID."),
183
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
184
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
475
185
  })
476
- ]);
186
+ ]).refine((t) => !("eval" in t && "script" in t), {
187
+ message: "eval and script should not both be present"
188
+ });
477
189
  }
478
- function f() {
479
- return t.discriminatedUnion("fn", [
480
- t.strictObject({
481
- fn: t.undefined(),
482
- at: t.optional(e(4)),
483
- cl: e(4),
484
- cppsrc: t.optional(t.string()),
485
- ep: t.optional(i()),
486
- cmd: t.optional(e(2)),
487
- mf: t.optional(e(4)),
488
- eval: t.optional(o()),
489
- script: t.optional(a())
490
- }).refine((n) => !("eval" in n && "script" in n), {
491
- message: "eval and script should not both be present"
492
- }).innerType(),
493
- t.strictObject({
494
- fn: t.literal("zcl"),
495
- at: t.optional(e(4)),
496
- cl: e(4),
497
- cppsrc: t.optional(t.string()),
498
- ep: t.optional(i()),
499
- cmd: t.optional(e(2)),
500
- mf: t.optional(e(4)),
501
- eval: t.optional(o()),
502
- script: t.optional(a())
503
- }).refine((n) => !("eval" in n && "script" in n), {
504
- message: "eval and script should not both be present"
505
- }).innerType(),
506
- t.strictObject({
507
- fn: t.literal("ias:zonestatus"),
508
- mask: t.optional(t.enum(["alarm1", "alarm2"]).or(t.literal("alarm1,alarm2")))
190
+ function I() {
191
+ return e.discriminatedUnion("fn", [
192
+ e.strictObject({
193
+ fn: e.literal("none")
509
194
  }),
510
- t.strictObject({
511
- fn: t.literal("numtostr"),
512
- srcitem: t.enum(["state/airqualityppb", "state/pm2_5"]),
513
- op: t.literal("le"),
514
- to: _()
195
+ e.strictObject({
196
+ fn: e.undefined(),
197
+ at: e.optional(n(4).or(e.array(n(4)))).describe("Attribute ID."),
198
+ "state.timeout": e.optional(e.number()),
199
+ "change.timeout": e.optional(e.number()),
200
+ cl: n(4).describe("Cluster ID."),
201
+ dt: n(2).describe("Data type."),
202
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
203
+ mf: e.optional(n(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
204
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
205
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
515
206
  }),
516
- t.strictObject({
517
- fn: t.literal("time")
207
+ e.strictObject({
208
+ fn: e.literal("zcl"),
209
+ at: e.optional(n(4).or(e.array(n(4)))).describe("Attribute ID."),
210
+ "state.timeout": e.optional(e.number()),
211
+ "change.timeout": e.optional(e.number()),
212
+ cl: n(4).describe("Cluster ID."),
213
+ dt: n(2).describe("Data type."),
214
+ ep: e.optional(c()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
215
+ mf: e.optional(n(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
216
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
217
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
518
218
  }),
519
- t.strictObject({
520
- fn: t.literal("xiaomi:special"),
521
- ep: t.optional(i()),
522
- at: t.optional(e(4)),
523
- idx: e(2),
524
- eval: t.optional(o()),
525
- script: t.optional(a())
526
- }).refine((n) => !("eval" in n && "script" in n), {
527
- message: "eval and script should not both be present"
528
- }).innerType(),
529
- t.strictObject({
530
- fn: t.literal("tuya"),
531
- dpid: t.number(),
532
- eval: t.optional(o()),
533
- script: t.optional(a())
534
- }).refine((n) => !("eval" in n && "script" in n), {
535
- message: "eval and script should not both be present"
536
- }).innerType()
537
- ]);
219
+ e.strictObject({
220
+ fn: e.literal("tuya").describe("Generic function to write Tuya data."),
221
+ dpid: e.number().describe("Data point ID. 1-255 the datapoint ID."),
222
+ dt: n(2).describe("Data type."),
223
+ eval: e.optional(d()).describe("Javascript expression to transform the raw value."),
224
+ script: e.optional(l()).describe("Relative path of a Javascript .js file.")
225
+ })
226
+ ]).refine((t) => !("eval" in t && "script" in t), {
227
+ message: "eval and script should not both be present"
228
+ });
538
229
  }
539
- function g() {
540
- return t.discriminatedUnion("fn", [
541
- t.strictObject({
542
- fn: t.literal("none")
543
- }),
544
- t.strictObject({
545
- fn: t.undefined(),
546
- at: t.optional(e(4).or(t.array(e(4)))),
547
- "state.timeout": t.optional(t.number()),
548
- "change.timeout": t.optional(t.number()),
549
- cl: e(4),
550
- dt: e(2),
551
- ep: t.optional(i()),
552
- mf: t.optional(e(4)),
553
- eval: t.optional(o()),
554
- script: t.optional(a())
555
- }).refine((n) => !("eval" in n && "script" in n), {
556
- message: "eval and script should not both be present"
557
- }).innerType(),
558
- t.strictObject({
559
- fn: t.literal("zcl"),
560
- at: t.optional(e(4).or(t.array(e(4)))),
561
- "state.timeout": t.optional(t.number()),
562
- "change.timeout": t.optional(t.number()),
563
- cl: e(4),
564
- dt: e(2),
565
- ep: t.optional(i()),
566
- mf: t.optional(e(4)),
567
- eval: t.optional(o()),
568
- script: t.optional(a())
569
- }).refine((n) => !("eval" in n && "script" in n), {
570
- message: "eval and script should not both be present"
571
- }).innerType(),
572
- t.strictObject({
573
- fn: t.literal("tuya"),
574
- dpid: t.number(),
575
- dt: e(2),
576
- eval: t.optional(o()),
577
- script: t.optional(a())
578
- }).refine((n) => !("eval" in n && "script" in n), {
579
- message: "eval and script should not both be present"
580
- }).innerType()
581
- ]);
230
+ function S(t) {
231
+ return e.strictObject({
232
+ $schema: e.optional(e.string()),
233
+ schema: e.literal("devcap1.schema.json"),
234
+ "doc:path": e.optional(e.string()),
235
+ "doc:hdr": e.optional(e.string()),
236
+ "md:known_issues": e.optional(e.array(e.string())).describe("Know issues for this device, markdown file."),
237
+ manufacturername: e.union([
238
+ e.enum(Object.keys(t.manufacturers)),
239
+ e.string().regex(/^(?!\$MF_).*/g, "The manufacturer name start with $MF_ but is not present in constants.json"),
240
+ e.array(
241
+ e.union([
242
+ e.enum(Object.keys(t.manufacturers)),
243
+ e.string().regex(/^(?!\$MF_).*/g, "The manufacturer name start with $MF_ but is not present in constants.json")
244
+ ])
245
+ )
246
+ ]).describe("Manufacturer name from Basic Cluster."),
247
+ modelid: e.string().or(e.array(e.string())).describe("Model ID from Basic Cluster."),
248
+ vendor: e.optional(e.string()).describe("Friendly name of the manufacturer."),
249
+ comment: e.optional(e.string()),
250
+ matchexpr: e.optional(d()).describe("Need to return true for the DDF be used."),
251
+ path: e.optional(l()).describe("DDF path, useless, can be removed."),
252
+ product: e.optional(e.string()).describe("Complements the model id to be shown in the UI."),
253
+ sleeper: e.optional(e.boolean()).describe("Sleeping devices can only receive when awake."),
254
+ supportsMgmtBind: e.optional(e.boolean()),
255
+ status: e.enum(["Draft", "Bronze", "Silver", "Gold"]).describe("The code quality of the DDF file."),
256
+ subdevices: e.array(E(t)).describe("Devices section."),
257
+ bindings: e.optional(e.array(C())).describe("Bindings section.")
258
+ });
582
259
  }
583
- function T() {
584
- return t.discriminatedUnion("bind", [
585
- t.strictObject({
586
- bind: t.literal("unicast"),
587
- "src.ep": i(),
588
- "dst.ep": t.optional(i()),
589
- cl: e(4),
590
- report: t.optional(t.array(t.strictObject({
591
- at: e(4),
592
- dt: e(2),
593
- mf: t.optional(e(4)),
594
- min: t.number(),
595
- max: t.number(),
596
- change: t.optional(e().or(t.number()))
597
- })))
260
+ function E(t) {
261
+ return e.strictObject({
262
+ type: e.union([
263
+ e.enum(Object.keys(t.deviceTypes)),
264
+ e.string().regex(/^(?!\$TYPE_).*/g, "The type start with $TYPE_ but is not present in constants.json")
265
+ ]),
266
+ restapi: e.enum(["/lights", "/sensors"]),
267
+ uuid: g(),
268
+ fingerprint: e.optional(e.strictObject({
269
+ profile: n(4),
270
+ device: n(4),
271
+ endpoint: c(),
272
+ in: e.optional(e.array(n(4))),
273
+ out: e.optional(e.array(n(4)))
274
+ })),
275
+ meta: e.optional(e.strictObject({
276
+ // TODO validate this
277
+ values: e.any(),
278
+ "group.endpoints": e.optional(e.array(e.number()))
279
+ })),
280
+ // TODO validate this
281
+ buttons: e.optional(e.any()),
282
+ // TODO validate this
283
+ buttonevents: e.optional(e.any()),
284
+ items: e.array(k(t)),
285
+ example: e.optional(e.unknown())
286
+ });
287
+ }
288
+ function k(t) {
289
+ return e.strictObject({
290
+ name: e.enum(t.attributes).describe("Item name."),
291
+ description: e.optional(e.string()).describe("Item description, better to do not use it."),
292
+ comment: e.optional(e.string()),
293
+ public: e.optional(e.boolean()).describe("Item visible on the API."),
294
+ static: e.optional(e.union([e.string(), e.number(), e.boolean()])).describe("A static default value is fixed and can be not changed."),
295
+ range: e.optional(e.tuple([e.number(), e.number()])).describe("Values range limit."),
296
+ deprecated: e.optional(v()),
297
+ access: e.optional(e.literal("R")).describe("Access mode for this item, some of them are not editable."),
298
+ read: e.optional(y()).describe("Fonction used to read value."),
299
+ parse: e.optional(j()).describe("Fonction used to parse incoming values."),
300
+ write: e.optional(I()).describe("Fonction used to write value."),
301
+ awake: e.optional(e.boolean()).describe("The device is considered awake when this item is set due a incoming command."),
302
+ default: e.optional(e.unknown()).describe("Defaut value."),
303
+ values: e.optional(e.unknown()),
304
+ "refresh.interval": e.optional(e.number()).describe("Refresh interval used for read fonction, NEED to be superior at value used in binding part.")
305
+ });
306
+ }
307
+ function C(t) {
308
+ return e.discriminatedUnion("bind", [
309
+ e.strictObject({
310
+ bind: e.literal("unicast"),
311
+ "src.ep": c().describe("Source endpoint."),
312
+ "dst.ep": e.optional(c()).describe("Destination endpoint, generaly 0x01."),
313
+ cl: n(4).describe("Cluster."),
314
+ report: e.optional(e.array(
315
+ e.strictObject({
316
+ at: n(4),
317
+ dt: n(2),
318
+ mf: e.optional(n(4)),
319
+ min: e.number(),
320
+ max: e.number(),
321
+ change: e.optional(n().or(e.number()))
322
+ }).refine((r) => r.min <= r.max, { message: "invalid report time, min should be smaller than max" })
323
+ ))
598
324
  }),
599
- t.strictObject({
600
- bind: t.literal("groupcast"),
601
- "src.ep": i(),
602
- cl: e(4),
603
- "config.group": t.number()
325
+ e.strictObject({
326
+ bind: e.literal("groupcast"),
327
+ "src.ep": c().describe("Source endpoint."),
328
+ cl: n(4).describe("Cluster."),
329
+ "config.group": e.number().min(0).max(255)
604
330
  })
605
331
  ]);
606
332
  }
607
- function v() {
608
- return t.strictObject({
609
- $schema: t.optional(t.string()),
610
- schema: t.literal("constants1.schema.json"),
611
- manufacturers: t.record(t.string().startsWith("$MF_"), t.string()),
612
- "device-types": t.record(t.string().startsWith("$TYPE_"), t.string())
333
+ function _(t) {
334
+ return e.strictObject({
335
+ $schema: e.optional(e.string()),
336
+ schema: e.literal("resourceitem1.schema.json"),
337
+ id: e.string(),
338
+ description: e.string(),
339
+ deprecated: e.optional(v()),
340
+ datatype: e.enum(["String", "Bool", "Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Array", "Array[3]", "ISO 8601 timestamp"]),
341
+ access: e.enum(["R", "W", "RW"]),
342
+ public: e.boolean(),
343
+ implicit: e.optional(e.boolean()),
344
+ managed: e.optional(e.boolean()),
345
+ static: e.optional(e.boolean()),
346
+ virtual: e.optional(e.boolean()),
347
+ parse: e.optional(j()),
348
+ read: e.optional(y()),
349
+ write: e.optional(I()),
350
+ "refresh.interval": e.optional(e.number()),
351
+ // TODO Validate this
352
+ values: e.optional(e.unknown()),
353
+ range: e.optional(e.tuple([e.number(), e.number()])),
354
+ default: e.optional(e.unknown())
613
355
  });
614
356
  }
615
- function O() {
616
- return t.strictObject({
617
- $schema: t.optional(t.string()),
618
- schema: t.literal("resourceitem1.schema.json"),
619
- id: t.string(),
620
- description: t.string(),
621
- deprecated: t.optional(l()),
622
- datatype: t.enum(["String", "Bool", "Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Array", "Array[3]", "ISO 8601 timestamp"]),
623
- access: t.enum(["R", "W", "RW"]),
624
- public: t.boolean(),
625
- implicit: t.optional(t.boolean()),
626
- managed: t.optional(t.boolean()),
627
- static: t.optional(t.boolean()),
628
- virtual: t.optional(t.boolean()),
629
- parse: t.optional(f()),
630
- read: t.optional(u()),
631
- write: t.optional(g()),
632
- "refresh.interval": t.optional(t.number()),
633
- // TODO Validate this
634
- values: t.optional(t.unknown()),
635
- range: t.optional(t.tuple([t.number(), t.number()])),
636
- default: t.optional(t.unknown())
357
+ function F(t) {
358
+ return e.strictObject({
359
+ $schema: e.optional(e.string()),
360
+ schema: e.literal("subdevice1.schema.json"),
361
+ type: e.union([
362
+ e.enum(Object.keys(t.deviceTypes)),
363
+ e.string().regex(/^(?!\$TYPE_).*/g, "The type start with $TYPE_ but is not present in constants.json")
364
+ ]),
365
+ name: e.string(),
366
+ restapi: e.enum(["/lights", "/sensors"]),
367
+ order: e.number(),
368
+ uuid: g(),
369
+ items: e.array(e.enum(t.attributes))
637
370
  });
638
371
  }
639
- function P() {
640
- return t.strictObject({
641
- $schema: t.optional(t.string()),
642
- schema: t.literal("subdevice1.schema.json"),
643
- type: t.enum(c),
644
- name: t.string(),
645
- restapi: t.enum(["/lights", "/sensors"]),
646
- order: t.number(),
647
- uuid: p(),
648
- items: t.array(t.enum(s))
372
+ function h(t) {
373
+ return e.discriminatedUnion("schema", [
374
+ S(t),
375
+ $(),
376
+ _(),
377
+ F(t)
378
+ ]).superRefine((r, o) => {
379
+ switch (r.schema) {
380
+ case "devcap1.schema.json":
381
+ D.map((i) => i(r, o));
382
+ break;
383
+ }
649
384
  });
650
385
  }
386
+ function J(t = { attributes: [], manufacturers: {}, deviceTypes: {} }) {
387
+ let r = h(t);
388
+ const o = () => {
389
+ r = h(t);
390
+ };
391
+ return { generics: t, loadGeneric: (u) => {
392
+ const a = r.parse(u);
393
+ switch (a.schema) {
394
+ case "constants1.schema.json":
395
+ t.manufacturers = {
396
+ ...t.manufacturers,
397
+ ...a.manufacturers
398
+ }, t.deviceTypes = {
399
+ ...t.deviceTypes,
400
+ ...a["device-types"]
401
+ };
402
+ break;
403
+ case "resourceitem1.schema.json":
404
+ t.attributes.includes(a.id) || t.attributes.push(a.id);
405
+ break;
406
+ case "subdevice1.schema.json":
407
+ break;
408
+ case "devcap1.schema.json":
409
+ throw new Error("Got invalid generic file, got data with schema 'devcap1.schema.json'.");
410
+ }
411
+ return o(), a;
412
+ }, validate: (u) => r.parse(u), getSchema: () => r };
413
+ }
651
414
  export {
652
- A as validate
415
+ J as createValidator
653
416
  };