@deconz-community/ddf-validator 2.1.0 → 2.2.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/dist/ddf-schema.json +1 -1
- package/dist/ddf-validator.cjs +1 -1
- package/dist/ddf-validator.d.ts +275 -48
- package/dist/ddf-validator.mjs +375 -198
- package/package.json +15 -15
package/dist/ddf-validator.mjs
CHANGED
|
@@ -1,53 +1,169 @@
|
|
|
1
1
|
import { z as e } from "zod";
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const I = [
|
|
3
|
+
j,
|
|
4
|
+
D,
|
|
5
|
+
O,
|
|
6
|
+
w
|
|
5
7
|
];
|
|
6
|
-
function
|
|
7
|
-
const
|
|
8
|
-
if (
|
|
8
|
+
function j(a, i) {
|
|
9
|
+
const n = typeof a.manufacturername == "string" && typeof a.modelid == "string";
|
|
10
|
+
if (n)
|
|
9
11
|
return;
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
|
|
12
|
+
const c = Array.isArray(a.manufacturername) && Array.isArray(a.modelid);
|
|
13
|
+
if (c && a.manufacturername.length !== a.modelid.length) {
|
|
14
|
+
i.addIssue({
|
|
13
15
|
code: e.ZodIssueCode.invalid_intersection_types,
|
|
14
16
|
message: "When 'manufacturername' and 'modelid' are both arrays they should be the same length",
|
|
15
17
|
path: ["manufacturername", "modelid"]
|
|
16
18
|
});
|
|
17
19
|
return;
|
|
18
20
|
}
|
|
19
|
-
(
|
|
21
|
+
(n || c) === !1 && i.addIssue({
|
|
20
22
|
code: e.ZodIssueCode.invalid_intersection_types,
|
|
21
23
|
message: "Invalid properties 'manufacturername' and 'modelid' should have the same type",
|
|
22
24
|
path: ["manufacturername", "modelid"]
|
|
23
25
|
});
|
|
24
26
|
}
|
|
25
|
-
function
|
|
26
|
-
if (!
|
|
27
|
+
function D(a, i) {
|
|
28
|
+
if (!a.bindings)
|
|
27
29
|
return;
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
s.bind === "unicast" && s.report && s.report.forEach((
|
|
31
|
-
|
|
30
|
+
const n = (s) => `0x${(typeof s == "number" ? s : parseInt(s, 16)).toString(16)}`, c = {};
|
|
31
|
+
a.bindings.forEach((s) => {
|
|
32
|
+
s.bind === "unicast" && s.report && s.report.forEach((m) => {
|
|
33
|
+
c[`${n(s["src.ep"])}.${n(s.cl)}.${n(m.at)}`] = m.max;
|
|
32
34
|
});
|
|
33
|
-
}),
|
|
34
|
-
s.items.forEach((
|
|
35
|
-
var
|
|
36
|
-
if (
|
|
37
|
-
const
|
|
38
|
-
for (let
|
|
39
|
-
const
|
|
40
|
-
|
|
35
|
+
}), a.subdevices.forEach((s, m) => {
|
|
36
|
+
s.items.forEach((r, o) => {
|
|
37
|
+
var l;
|
|
38
|
+
if (r["refresh.interval"] && r.read && r.read.fn === "zcl") {
|
|
39
|
+
const f = n(r.read.ep ?? ((l = s.fingerprint) == null ? void 0 : l.endpoint) ?? s.uuid[1]), b = Array.isArray(r.read.at) ? r.read.at : [r.read.at];
|
|
40
|
+
for (let h = 0; h < b.length; h++) {
|
|
41
|
+
const v = `${f}.${n(r.read.cl)}.${n(b[h])}`;
|
|
42
|
+
c[v] !== void 0 && r["refresh.interval"] - 60 < c[v] && i.addIssue({
|
|
41
43
|
code: e.ZodIssueCode.custom,
|
|
42
|
-
message: `The refresh interval (${
|
|
43
|
-
path: ["subdevices",
|
|
44
|
+
message: `The refresh interval (${r["refresh.interval"]} - 60 = ${r["refresh.interval"] - 60}) should be greater than the binding max refresh value (${c[v]}) with a margin of 60 seconds`,
|
|
45
|
+
path: ["subdevices", m, "items", o, "refresh.interval"]
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
});
|
|
48
50
|
});
|
|
49
51
|
}
|
|
50
|
-
function
|
|
52
|
+
function O(a, i) {
|
|
53
|
+
if (!a.bindings)
|
|
54
|
+
return;
|
|
55
|
+
const n = [
|
|
56
|
+
/*
|
|
57
|
+
{
|
|
58
|
+
description: 'a device should always have basic attributes.',
|
|
59
|
+
if: {},
|
|
60
|
+
need: {
|
|
61
|
+
item: [
|
|
62
|
+
'attr/id',
|
|
63
|
+
'attr/name',
|
|
64
|
+
'attr/uniqueid',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
*/
|
|
69
|
+
{
|
|
70
|
+
description: 'a color light should always have "state/ct" item.',
|
|
71
|
+
if: {
|
|
72
|
+
type: [
|
|
73
|
+
"$TYPE_COLOR_TEMPERATURE_LIGHT",
|
|
74
|
+
"Color Temperature Light",
|
|
75
|
+
"$TYPE_EXTENDED_COLOR_LIGHT",
|
|
76
|
+
"Extended Color Light"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
need: {
|
|
80
|
+
item: [
|
|
81
|
+
"state/ct"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
description: 'a device with "state/ct" need the "min" and "max" values for capability.',
|
|
87
|
+
if: {
|
|
88
|
+
item: ["state/ct"]
|
|
89
|
+
},
|
|
90
|
+
need: {
|
|
91
|
+
item: [
|
|
92
|
+
"cap/color/ct/min",
|
|
93
|
+
"cap/color/ct/max"
|
|
94
|
+
// 'config/color/ct/startup',
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
description: 'a device with "state/x" or "state/y" need the corresponding red, green and blue x and y values.',
|
|
100
|
+
if: {
|
|
101
|
+
item: [
|
|
102
|
+
"state/x",
|
|
103
|
+
"state/y"
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
need: {
|
|
107
|
+
item: [
|
|
108
|
+
"state/x",
|
|
109
|
+
"state/y",
|
|
110
|
+
"cap/color/xy/red_x",
|
|
111
|
+
"cap/color/xy/green_x",
|
|
112
|
+
"cap/color/xy/blue_x",
|
|
113
|
+
"cap/color/xy/red_y",
|
|
114
|
+
"cap/color/xy/green_y",
|
|
115
|
+
"cap/color/xy/blue_y"
|
|
116
|
+
// 'cap/color/ct/computes_xy',
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
];
|
|
121
|
+
a.subdevices.forEach((c, s) => {
|
|
122
|
+
const m = c.items.map((r) => r.name);
|
|
123
|
+
n.forEach((r) => {
|
|
124
|
+
(Object.keys(r.if).length === 0 || Object.keys(r.if).some((o) => {
|
|
125
|
+
var l;
|
|
126
|
+
switch (o) {
|
|
127
|
+
case "type":
|
|
128
|
+
return (l = r.if[o]) == null ? void 0 : l.includes(c.type);
|
|
129
|
+
case "item":
|
|
130
|
+
return m.some((f) => {
|
|
131
|
+
var b;
|
|
132
|
+
return (b = r.if[o]) == null ? void 0 : b.includes(f);
|
|
133
|
+
});
|
|
134
|
+
default:
|
|
135
|
+
return !1;
|
|
136
|
+
}
|
|
137
|
+
})) && r.need.item.forEach((o) => {
|
|
138
|
+
m.includes(o) || i.addIssue({
|
|
139
|
+
code: e.ZodIssueCode.custom,
|
|
140
|
+
message: `The device should have the item "${o}" because ${r.description}`,
|
|
141
|
+
path: ["subdevices", s, "items"]
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function w(a, i) {
|
|
148
|
+
const n = ["parse", "write"];
|
|
149
|
+
a.subdevices.forEach((c, s) => {
|
|
150
|
+
c.items.forEach((m, r) => {
|
|
151
|
+
n.forEach((o) => {
|
|
152
|
+
const l = m[o];
|
|
153
|
+
l !== void 0 && (l.fn === void 0 || l.fn === "zcl" || l.fn === "zcl:attr" || l.fn === "zcl:cmd") && (l.eval === void 0 && l.script === void 0 && i.addIssue({
|
|
154
|
+
code: e.ZodIssueCode.custom,
|
|
155
|
+
message: `The '${o}' function is missing 'eval' or 'script' option.`,
|
|
156
|
+
path: ["subdevices", s, "items", r, o]
|
|
157
|
+
}), l.eval !== void 0 && l.script !== void 0 && i.addIssue({
|
|
158
|
+
code: e.ZodIssueCode.custom,
|
|
159
|
+
message: `The '${o}' function is having both 'eval' and 'script' option.`,
|
|
160
|
+
path: ["subdevices", s, "items", r, o]
|
|
161
|
+
}));
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function E(a) {
|
|
51
167
|
return e.strictObject({
|
|
52
168
|
$schema: e.optional(e.string()),
|
|
53
169
|
schema: e.literal("constants1.schema.json"),
|
|
@@ -55,105 +171,142 @@ function $(t) {
|
|
|
55
171
|
"device-types": e.record(e.string().startsWith("$TYPE_"), e.string())
|
|
56
172
|
});
|
|
57
173
|
}
|
|
58
|
-
function
|
|
174
|
+
function T() {
|
|
59
175
|
return e.string().regex(
|
|
60
176
|
// Regex for AAAA-MM-JJ
|
|
61
177
|
/^(\d{4})-(?:(?:0[1-9])|(?:1[0-2]))-(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01]))$/,
|
|
62
178
|
"Invalid date value"
|
|
63
179
|
);
|
|
64
180
|
}
|
|
65
|
-
function
|
|
66
|
-
const
|
|
67
|
-
return
|
|
181
|
+
function t(a = void 0) {
|
|
182
|
+
const i = "Invalid hexadecimal value";
|
|
183
|
+
return a === void 0 ? e.string().regex(/^0x[0-9a-fA-F]+$/, i) : e.string().regex(new RegExp(`^0x[0-9a-fA-F]{${a}}$`), i);
|
|
68
184
|
}
|
|
69
|
-
function
|
|
185
|
+
function d() {
|
|
70
186
|
return e.union([
|
|
71
|
-
|
|
187
|
+
t(2),
|
|
72
188
|
e.number().min(0).max(255)
|
|
73
189
|
]);
|
|
74
190
|
}
|
|
75
|
-
function
|
|
76
|
-
return e.custom((
|
|
77
|
-
if (!Array.isArray(
|
|
191
|
+
function C() {
|
|
192
|
+
return e.custom((a) => {
|
|
193
|
+
if (!Array.isArray(a) || a.length % 2 !== 0)
|
|
78
194
|
return !1;
|
|
79
|
-
for (let
|
|
80
|
-
const
|
|
81
|
-
if (typeof
|
|
195
|
+
for (let i = 0; i < a.length; i += 2) {
|
|
196
|
+
const n = a[i], c = a[i + 1];
|
|
197
|
+
if (typeof n != "number" || typeof c != "string")
|
|
82
198
|
return !1;
|
|
83
199
|
}
|
|
84
200
|
return !0;
|
|
85
201
|
}, "The value must be an array with an even number of values and alternating between number and string.");
|
|
86
202
|
}
|
|
87
|
-
function
|
|
203
|
+
function y() {
|
|
88
204
|
return e.union([
|
|
89
205
|
e.tuple([
|
|
90
206
|
e.literal("$address.ext"),
|
|
91
|
-
|
|
207
|
+
t(2)
|
|
92
208
|
]),
|
|
93
209
|
e.tuple([
|
|
94
210
|
e.literal("$address.ext"),
|
|
95
|
-
|
|
96
|
-
|
|
211
|
+
t(2),
|
|
212
|
+
t(4)
|
|
97
213
|
])
|
|
98
214
|
]);
|
|
99
215
|
}
|
|
100
|
-
function
|
|
216
|
+
function p() {
|
|
101
217
|
return e.string();
|
|
102
218
|
}
|
|
103
|
-
function
|
|
219
|
+
function u() {
|
|
104
220
|
return e.string();
|
|
105
221
|
}
|
|
106
|
-
function
|
|
222
|
+
function $() {
|
|
107
223
|
return e.discriminatedUnion("fn", [
|
|
108
224
|
e.strictObject({
|
|
109
225
|
fn: e.literal("none")
|
|
110
226
|
}),
|
|
111
227
|
e.strictObject({
|
|
112
228
|
fn: e.undefined().describe("Generic function to read ZCL attributes."),
|
|
113
|
-
at:
|
|
114
|
-
cl:
|
|
115
|
-
ep: e.optional(
|
|
116
|
-
mf: e.optional(
|
|
117
|
-
eval: e.optional(
|
|
229
|
+
at: t(4).or(e.array(t(4))).describe("Attribute ID."),
|
|
230
|
+
cl: t(4).describe("Cluster ID."),
|
|
231
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
232
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
233
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value.")
|
|
118
234
|
}),
|
|
119
235
|
e.strictObject({
|
|
120
236
|
fn: e.literal("zcl").describe("Generic function to read ZCL attributes."),
|
|
121
|
-
at:
|
|
122
|
-
cl:
|
|
123
|
-
ep: e.optional(
|
|
124
|
-
mf: e.optional(
|
|
125
|
-
eval: e.optional(
|
|
237
|
+
at: t(4).or(e.array(t(4))).describe("Attribute ID."),
|
|
238
|
+
cl: t(4).describe("Cluster ID."),
|
|
239
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
240
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
241
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
242
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
243
|
+
}).describe("Deprecated"),
|
|
244
|
+
e.strictObject({
|
|
245
|
+
fn: e.literal("zcl:attr").describe("Generic function to parse ZCL values from read/report commands."),
|
|
246
|
+
at: t(4).or(e.array(t(4))).describe("String hex value or array of string hex values."),
|
|
247
|
+
cl: t(4).describe("Cluster ID."),
|
|
248
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
249
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
250
|
+
eval: e.optional(u()).describe("Javascript expression to transform the attribute value to the Item value."),
|
|
251
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
252
|
+
}),
|
|
253
|
+
e.strictObject({
|
|
254
|
+
fn: e.literal("zcl:cmd").describe("Generic function to parse ZCL values from read/report commands."),
|
|
255
|
+
cl: t(4).describe("Cluster ID."),
|
|
256
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
257
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
258
|
+
cmd: e.optional(t(2)).describe("Zigbee command."),
|
|
259
|
+
eval: e.optional(u()).describe("Javascript expression to transform the attribute value to the Item value."),
|
|
260
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
126
261
|
}),
|
|
127
262
|
e.strictObject({
|
|
128
263
|
fn: e.literal("tuya").describe("Generic function to read all Tuya datapoints. It has no parameters.")
|
|
129
264
|
})
|
|
130
|
-
]).refine((
|
|
265
|
+
]).refine((a) => !("eval" in a && "script" in a), {
|
|
131
266
|
message: "eval and script should not both be present"
|
|
132
267
|
});
|
|
133
268
|
}
|
|
134
|
-
function
|
|
269
|
+
function A() {
|
|
135
270
|
return e.discriminatedUnion("fn", [
|
|
136
271
|
e.strictObject({
|
|
137
272
|
fn: e.undefined().describe("Generic function to parse ZCL attributes and commands."),
|
|
138
|
-
at: e.optional(
|
|
139
|
-
cl:
|
|
273
|
+
at: e.optional(t(4).or(e.array(t(4)))).describe("Attribute ID."),
|
|
274
|
+
cl: t(4).describe("Cluster ID."),
|
|
140
275
|
cppsrc: e.optional(e.string()),
|
|
141
|
-
ep: e.optional(
|
|
142
|
-
cmd: e.optional(
|
|
143
|
-
mf: e.optional(
|
|
144
|
-
eval: e.optional(
|
|
145
|
-
script: e.optional(
|
|
276
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
277
|
+
cmd: e.optional(t(2)).describe("Zigbee command."),
|
|
278
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
279
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
280
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
146
281
|
}),
|
|
147
282
|
e.strictObject({
|
|
148
283
|
fn: e.literal("zcl").describe("Generic function to parse ZCL attributes and commands."),
|
|
149
|
-
at: e.optional(
|
|
150
|
-
cl:
|
|
284
|
+
at: e.optional(t(4).or(e.array(t(4)))).describe("Attribute ID."),
|
|
285
|
+
cl: t(4).describe("Cluster ID."),
|
|
151
286
|
cppsrc: e.optional(e.string()),
|
|
152
|
-
ep: e.optional(
|
|
153
|
-
cmd: e.optional(
|
|
154
|
-
mf: e.optional(
|
|
155
|
-
eval: e.optional(
|
|
156
|
-
script: e.optional(
|
|
287
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
288
|
+
cmd: e.optional(t(2)).describe("Zigbee command."),
|
|
289
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
290
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
291
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
292
|
+
}).describe("Deprecated"),
|
|
293
|
+
e.strictObject({
|
|
294
|
+
fn: e.literal("zcl:attr").describe("Generic function to parse ZCL values from read/report commands."),
|
|
295
|
+
at: t(4).or(e.array(t(4))).describe("String hex value or array of string hex values."),
|
|
296
|
+
cl: t(4).describe("Cluster ID."),
|
|
297
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
298
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
299
|
+
eval: e.optional(u()).describe("Javascript expression to transform the attribute value to the Item value."),
|
|
300
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
301
|
+
}),
|
|
302
|
+
e.strictObject({
|
|
303
|
+
fn: e.literal("zcl:cmd").describe("Generic function to parse ZCL values from read/report commands."),
|
|
304
|
+
cl: t(4).describe("Cluster ID."),
|
|
305
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
306
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
307
|
+
cmd: e.optional(t(2)).describe("Zigbee command."),
|
|
308
|
+
eval: e.optional(u()).describe("Javascript expression to transform the attribute value to the Item value."),
|
|
309
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
157
310
|
}),
|
|
158
311
|
e.strictObject({
|
|
159
312
|
fn: e.literal("ias:zonestatus").describe("Generic function to parse IAS ZONE status change notifications or zone status from read/report command."),
|
|
@@ -164,70 +317,119 @@ function j() {
|
|
|
164
317
|
// TODO use generic
|
|
165
318
|
srcitem: e.enum(["state/airqualityppb", "state/pm2_5"]).describe("The source item holding the number."),
|
|
166
319
|
op: e.enum(["lt", "le", "eq", "gt", "ge"]).describe("Comparison operator (lt | le | eq | gt | ge)"),
|
|
167
|
-
to:
|
|
320
|
+
to: C().describe("Array of (num, string) mappings")
|
|
168
321
|
}),
|
|
169
322
|
e.strictObject({
|
|
170
323
|
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.")
|
|
171
324
|
}),
|
|
172
325
|
e.strictObject({
|
|
173
326
|
fn: e.literal("xiaomi:special").describe("Generic function to parse custom Xiaomi attributes and commands."),
|
|
174
|
-
ep: e.optional(
|
|
175
|
-
at: e.optional(
|
|
176
|
-
idx:
|
|
177
|
-
eval: e.optional(
|
|
178
|
-
script: e.optional(
|
|
327
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
328
|
+
at: e.optional(t(4)).describe("Attribute ID. The attribute to parse, shall be 0xff01, 0xff02 or 0x00f7"),
|
|
329
|
+
idx: t(2).describe("A 8-bit string hex value."),
|
|
330
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
331
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
179
332
|
}),
|
|
180
333
|
e.strictObject({
|
|
181
334
|
fn: e.literal("tuya").describe("Generic function to parse Tuya data."),
|
|
182
335
|
dpid: e.number().describe("Data point ID. 1-255 the datapoint ID."),
|
|
183
|
-
eval: e.optional(
|
|
184
|
-
script: e.optional(
|
|
336
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
337
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
185
338
|
})
|
|
186
|
-
]).refine((
|
|
339
|
+
]).refine((a) => !("eval" in a && "script" in a), {
|
|
187
340
|
message: "eval and script should not both be present"
|
|
188
341
|
});
|
|
189
342
|
}
|
|
190
|
-
function
|
|
343
|
+
function J() {
|
|
191
344
|
return e.discriminatedUnion("fn", [
|
|
192
345
|
e.strictObject({
|
|
193
346
|
fn: e.literal("none")
|
|
194
347
|
}),
|
|
195
348
|
e.strictObject({
|
|
196
349
|
fn: e.undefined(),
|
|
197
|
-
at: e.optional(
|
|
350
|
+
at: e.optional(t(4).or(e.array(t(4)))).describe("Attribute ID."),
|
|
198
351
|
"state.timeout": e.optional(e.number()),
|
|
199
352
|
"change.timeout": e.optional(e.number()),
|
|
200
|
-
cl:
|
|
201
|
-
dt:
|
|
202
|
-
ep: e.optional(
|
|
203
|
-
mf: e.optional(
|
|
204
|
-
eval: e.optional(
|
|
205
|
-
script: e.optional(
|
|
353
|
+
cl: t(4).describe("Cluster ID."),
|
|
354
|
+
dt: t(2).describe("Data type."),
|
|
355
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
356
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
357
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
358
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
206
359
|
}),
|
|
207
360
|
e.strictObject({
|
|
208
361
|
fn: e.literal("zcl"),
|
|
209
|
-
at: e.optional(
|
|
362
|
+
at: e.optional(t(4).or(e.array(t(4)))).describe("Attribute ID."),
|
|
363
|
+
"state.timeout": e.optional(e.number()),
|
|
364
|
+
"change.timeout": e.optional(e.number()),
|
|
365
|
+
cl: t(4).describe("Cluster ID."),
|
|
366
|
+
dt: t(2).describe("Data type."),
|
|
367
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
368
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
369
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
370
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
371
|
+
}).describe("Deprecated"),
|
|
372
|
+
e.strictObject({
|
|
373
|
+
fn: e.literal("zcl:attr").describe("Generic function to parse ZCL values from read/report commands."),
|
|
374
|
+
at: t(4).or(e.array(t(4))).describe("String hex value or array of string hex values."),
|
|
210
375
|
"state.timeout": e.optional(e.number()),
|
|
211
376
|
"change.timeout": e.optional(e.number()),
|
|
212
|
-
cl:
|
|
213
|
-
dt:
|
|
214
|
-
ep: e.optional(
|
|
215
|
-
mf: e.optional(
|
|
216
|
-
eval: e.optional(
|
|
217
|
-
script: e.optional(
|
|
377
|
+
cl: t(4).describe("Cluster ID."),
|
|
378
|
+
dt: t(2).describe("Data type."),
|
|
379
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
380
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
381
|
+
eval: e.optional(u()).describe("Javascript expression to transform the attribute value to the Item value."),
|
|
382
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
383
|
+
}),
|
|
384
|
+
e.strictObject({
|
|
385
|
+
fn: e.literal("zcl:cmd").describe("Generic function to parse ZCL values from read/report commands."),
|
|
386
|
+
cl: t(4).describe("Cluster ID."),
|
|
387
|
+
dt: t(2).describe("Data type."),
|
|
388
|
+
ep: e.optional(d()).describe("Endpoint, 255 means any endpoint, 0 means auto selected from subdevice."),
|
|
389
|
+
mf: e.optional(t(4)).describe("Manufacturer code, must be set to 0x0000 for non manufacturer specific commands."),
|
|
390
|
+
cmd: e.optional(t(2)).describe("Zigbee command."),
|
|
391
|
+
eval: e.optional(u()).describe("Javascript expression to transform the attribute value to the Item value."),
|
|
392
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
218
393
|
}),
|
|
219
394
|
e.strictObject({
|
|
220
395
|
fn: e.literal("tuya").describe("Generic function to write Tuya data."),
|
|
221
396
|
dpid: e.number().describe("Data point ID. 1-255 the datapoint ID."),
|
|
222
|
-
dt:
|
|
223
|
-
eval: e.optional(
|
|
224
|
-
script: e.optional(
|
|
397
|
+
dt: t(2).describe("Data type."),
|
|
398
|
+
eval: e.optional(u()).describe("Javascript expression to transform the raw value."),
|
|
399
|
+
script: e.optional(p()).describe("Relative path of a Javascript .js file.")
|
|
225
400
|
})
|
|
226
|
-
]).refine((
|
|
401
|
+
]).refine((a) => !("eval" in a && "script" in a), {
|
|
227
402
|
message: "eval and script should not both be present"
|
|
228
403
|
});
|
|
229
404
|
}
|
|
230
|
-
function
|
|
405
|
+
function x(a) {
|
|
406
|
+
return e.strictObject({
|
|
407
|
+
$schema: e.optional(e.string()),
|
|
408
|
+
schema: e.literal("resourceitem1.schema.json"),
|
|
409
|
+
id: e.string(),
|
|
410
|
+
description: e.optional(e.string()).describe("Item description, better to do not use it."),
|
|
411
|
+
comment: e.optional(e.string()).describe("TODO: What is this ? What the difference with description ?"),
|
|
412
|
+
deprecated: e.optional(T()).describe("Date of deprecation, if the item is deprecated, it's better to use the new one."),
|
|
413
|
+
datatype: e.optional(e.enum(["String", "Bool", "Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Array", "Array[3]", "ISO 8601 timestamp"])).describe("Data type of the item."),
|
|
414
|
+
access: e.optional(e.enum(["R", "W", "RW"])).describe("Access mode for this item, some of them are not editable."),
|
|
415
|
+
public: e.optional(e.boolean()).describe("Item visible on the API."),
|
|
416
|
+
implicit: e.optional(e.boolean()).describe("TODO: What is this ?"),
|
|
417
|
+
managed: e.optional(e.boolean()).describe("TODO: What is this ?"),
|
|
418
|
+
awake: e.optional(e.boolean()).describe("The device is considered awake when this item is set due a incoming command."),
|
|
419
|
+
static: e.optional(e.union([e.string(), e.number(), e.boolean()])).describe("A static default value is fixed and can be not changed."),
|
|
420
|
+
range: e.optional(e.tuple([e.number(), e.number()])).describe("Values range limit."),
|
|
421
|
+
virtual: e.optional(e.boolean()).describe("TODO: What is this ?"),
|
|
422
|
+
read: e.optional($()).describe("Fonction used to read value."),
|
|
423
|
+
parse: e.optional(A()).describe("Fonction used to parse incoming values."),
|
|
424
|
+
write: e.optional(J()).describe("Fonction used to write value."),
|
|
425
|
+
"refresh.interval": e.optional(e.number()).describe("Refresh interval used for read fonction, NEED to be superior at value used in binding part."),
|
|
426
|
+
// TODO Validate this
|
|
427
|
+
values: e.optional(e.unknown()).describe("TODO: What is this ?"),
|
|
428
|
+
// TODO Validate this
|
|
429
|
+
default: e.optional(e.unknown()).describe("Defaut value.")
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function _(a) {
|
|
231
433
|
return e.strictObject({
|
|
232
434
|
$schema: e.optional(e.string()),
|
|
233
435
|
schema: e.literal("devcap1.schema.json"),
|
|
@@ -235,11 +437,11 @@ function S(t) {
|
|
|
235
437
|
"doc:hdr": e.optional(e.string()),
|
|
236
438
|
"md:known_issues": e.optional(e.array(e.string())).describe("Know issues for this device, markdown file."),
|
|
237
439
|
manufacturername: e.union([
|
|
238
|
-
e.enum(Object.keys(
|
|
440
|
+
e.enum(Object.keys(a.manufacturers)),
|
|
239
441
|
e.string().regex(/^(?!\$MF_).*/g, "The manufacturer name start with $MF_ but is not present in constants.json"),
|
|
240
442
|
e.array(
|
|
241
443
|
e.union([
|
|
242
|
-
e.enum(Object.keys(
|
|
444
|
+
e.enum(Object.keys(a.manufacturers)),
|
|
243
445
|
e.string().regex(/^(?!\$MF_).*/g, "The manufacturer name start with $MF_ but is not present in constants.json")
|
|
244
446
|
])
|
|
245
447
|
)
|
|
@@ -247,30 +449,30 @@ function S(t) {
|
|
|
247
449
|
modelid: e.string().or(e.array(e.string())).describe("Model ID from Basic Cluster."),
|
|
248
450
|
vendor: e.optional(e.string()).describe("Friendly name of the manufacturer."),
|
|
249
451
|
comment: e.optional(e.string()),
|
|
250
|
-
matchexpr: e.optional(
|
|
251
|
-
path: e.optional(
|
|
452
|
+
matchexpr: e.optional(u()).describe("Need to return true for the DDF be used."),
|
|
453
|
+
path: e.optional(p()).describe("DDF path, useless, can be removed."),
|
|
252
454
|
product: e.optional(e.string()).describe("Complements the model id to be shown in the UI."),
|
|
253
455
|
sleeper: e.optional(e.boolean()).describe("Sleeping devices can only receive when awake."),
|
|
254
456
|
supportsMgmtBind: e.optional(e.boolean()),
|
|
255
457
|
status: e.enum(["Draft", "Bronze", "Silver", "Gold"]).describe("The code quality of the DDF file."),
|
|
256
|
-
subdevices: e.array(
|
|
257
|
-
bindings: e.optional(e.array(
|
|
458
|
+
subdevices: e.array(S(a)).describe("Devices section."),
|
|
459
|
+
bindings: e.optional(e.array(M())).describe("Bindings section.")
|
|
258
460
|
});
|
|
259
461
|
}
|
|
260
|
-
function
|
|
462
|
+
function S(a) {
|
|
261
463
|
return e.strictObject({
|
|
262
464
|
type: e.union([
|
|
263
|
-
e.enum(Object.keys(
|
|
465
|
+
e.enum(Object.keys(a.deviceTypes)),
|
|
264
466
|
e.string().regex(/^(?!\$TYPE_).*/g, "The type start with $TYPE_ but is not present in constants.json")
|
|
265
467
|
]),
|
|
266
468
|
restapi: e.enum(["/lights", "/sensors"]),
|
|
267
|
-
uuid:
|
|
469
|
+
uuid: y(),
|
|
268
470
|
fingerprint: e.optional(e.strictObject({
|
|
269
|
-
profile:
|
|
270
|
-
device:
|
|
271
|
-
endpoint:
|
|
272
|
-
in: e.optional(e.array(
|
|
273
|
-
out: e.optional(e.array(
|
|
471
|
+
profile: t(4),
|
|
472
|
+
device: t(4),
|
|
473
|
+
endpoint: d(),
|
|
474
|
+
in: e.optional(e.array(t(4))),
|
|
475
|
+
out: e.optional(e.array(t(4)))
|
|
274
476
|
})),
|
|
275
477
|
meta: e.optional(e.strictObject({
|
|
276
478
|
// TODO validate this
|
|
@@ -281,136 +483,111 @@ function E(t) {
|
|
|
281
483
|
buttons: e.optional(e.any()),
|
|
282
484
|
// TODO validate this
|
|
283
485
|
buttonevents: e.optional(e.any()),
|
|
284
|
-
items: e.array(
|
|
486
|
+
items: e.array(R(a)),
|
|
285
487
|
example: e.optional(e.unknown())
|
|
286
488
|
});
|
|
287
489
|
}
|
|
288
|
-
function
|
|
289
|
-
return
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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.")
|
|
490
|
+
function R(a) {
|
|
491
|
+
return x().omit({
|
|
492
|
+
$schema: !0,
|
|
493
|
+
schema: !0,
|
|
494
|
+
id: !0
|
|
495
|
+
}).extend({
|
|
496
|
+
name: e.enum(a.attributes).describe("Item name.")
|
|
305
497
|
});
|
|
306
498
|
}
|
|
307
|
-
function
|
|
499
|
+
function M(a) {
|
|
308
500
|
return e.discriminatedUnion("bind", [
|
|
309
501
|
e.strictObject({
|
|
310
502
|
bind: e.literal("unicast"),
|
|
311
|
-
"src.ep":
|
|
312
|
-
"dst.ep": e.optional(
|
|
313
|
-
cl:
|
|
503
|
+
"src.ep": d().describe("Source endpoint."),
|
|
504
|
+
"dst.ep": e.optional(d()).describe("Destination endpoint, generaly 0x01."),
|
|
505
|
+
cl: t(4).describe("Cluster."),
|
|
314
506
|
report: e.optional(e.array(
|
|
315
507
|
e.strictObject({
|
|
316
|
-
at:
|
|
317
|
-
dt:
|
|
318
|
-
mf: e.optional(
|
|
508
|
+
at: t(4),
|
|
509
|
+
dt: t(2),
|
|
510
|
+
mf: e.optional(t(4)),
|
|
319
511
|
min: e.number(),
|
|
320
512
|
max: e.number(),
|
|
321
|
-
change: e.optional(
|
|
322
|
-
}).refine((
|
|
513
|
+
change: e.optional(t().or(e.number()))
|
|
514
|
+
}).refine((i) => i.min <= i.max, { message: "invalid report time, min should be smaller than max" })
|
|
323
515
|
))
|
|
324
516
|
}),
|
|
325
517
|
e.strictObject({
|
|
326
518
|
bind: e.literal("groupcast"),
|
|
327
|
-
"src.ep":
|
|
328
|
-
cl:
|
|
519
|
+
"src.ep": d().describe("Source endpoint."),
|
|
520
|
+
cl: t(4).describe("Cluster."),
|
|
329
521
|
"config.group": e.number().min(0).max(255)
|
|
330
522
|
})
|
|
331
523
|
]);
|
|
332
524
|
}
|
|
333
|
-
function
|
|
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())
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
function F(t) {
|
|
525
|
+
function G(a) {
|
|
358
526
|
return e.strictObject({
|
|
359
527
|
$schema: e.optional(e.string()),
|
|
360
528
|
schema: e.literal("subdevice1.schema.json"),
|
|
361
529
|
type: e.union([
|
|
362
|
-
e.enum(Object.keys(
|
|
530
|
+
e.enum(Object.keys(a.deviceTypes)),
|
|
363
531
|
e.string().regex(/^(?!\$TYPE_).*/g, "The type start with $TYPE_ but is not present in constants.json")
|
|
364
532
|
]),
|
|
365
533
|
name: e.string(),
|
|
366
534
|
restapi: e.enum(["/lights", "/sensors"]),
|
|
367
535
|
order: e.number(),
|
|
368
|
-
uuid:
|
|
369
|
-
items: e.array(e.enum(
|
|
536
|
+
uuid: y(),
|
|
537
|
+
items: e.array(e.enum(a.attributes))
|
|
370
538
|
});
|
|
371
539
|
}
|
|
372
|
-
function
|
|
540
|
+
function g(a) {
|
|
373
541
|
return e.discriminatedUnion("schema", [
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
]).superRefine((
|
|
379
|
-
switch (
|
|
542
|
+
_(a),
|
|
543
|
+
E(),
|
|
544
|
+
x(),
|
|
545
|
+
G(a)
|
|
546
|
+
]).superRefine((i, n) => {
|
|
547
|
+
switch (i.schema) {
|
|
380
548
|
case "devcap1.schema.json":
|
|
381
|
-
|
|
549
|
+
I.map((c) => c(i, n));
|
|
382
550
|
break;
|
|
383
551
|
}
|
|
384
552
|
});
|
|
385
553
|
}
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
554
|
+
function k(a = {
|
|
555
|
+
attributes: [],
|
|
556
|
+
manufacturers: {},
|
|
557
|
+
deviceTypes: {},
|
|
558
|
+
resources: {}
|
|
559
|
+
}) {
|
|
560
|
+
let i = g(a);
|
|
561
|
+
const n = () => {
|
|
562
|
+
i = g(a);
|
|
390
563
|
};
|
|
391
|
-
return { generics:
|
|
392
|
-
const
|
|
393
|
-
switch (
|
|
564
|
+
return { generics: a, loadGeneric: (m) => {
|
|
565
|
+
const r = i.parse(m);
|
|
566
|
+
switch (r.schema) {
|
|
394
567
|
case "constants1.schema.json":
|
|
395
|
-
|
|
396
|
-
...
|
|
397
|
-
...
|
|
398
|
-
},
|
|
399
|
-
...
|
|
400
|
-
...
|
|
568
|
+
a.manufacturers = {
|
|
569
|
+
...a.manufacturers,
|
|
570
|
+
...r.manufacturers
|
|
571
|
+
}, a.deviceTypes = {
|
|
572
|
+
...a.deviceTypes,
|
|
573
|
+
...r["device-types"]
|
|
401
574
|
};
|
|
402
575
|
break;
|
|
403
|
-
case "resourceitem1.schema.json":
|
|
404
|
-
|
|
576
|
+
case "resourceitem1.schema.json": {
|
|
577
|
+
if (a.attributes.includes(r.id))
|
|
578
|
+
throw new Error(`Got duplicate resource item with attribute id '${r.id}'.`);
|
|
579
|
+
const o = structuredClone(r);
|
|
580
|
+
delete o.$schema, delete o.schema, delete o.id, a.resources[r.id] = o, a.attributes.push(r.id);
|
|
405
581
|
break;
|
|
582
|
+
}
|
|
406
583
|
case "subdevice1.schema.json":
|
|
407
584
|
break;
|
|
408
585
|
case "devcap1.schema.json":
|
|
409
586
|
throw new Error("Got invalid generic file, got data with schema 'devcap1.schema.json'.");
|
|
410
587
|
}
|
|
411
|
-
return
|
|
412
|
-
}, validate: (
|
|
588
|
+
return n(), r;
|
|
589
|
+
}, validate: (m) => i.parse(m), getSchema: () => i };
|
|
413
590
|
}
|
|
414
591
|
export {
|
|
415
|
-
|
|
592
|
+
k as createValidator
|
|
416
593
|
};
|