homey-lib 2.45.3 → 2.45.4
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/.eslintignore +2 -1
- package/assets/app/schema.d.ts +507 -0
- package/assets/capability/schema.d.ts +53 -0
- package/helpers/index.js +11 -0
- package/index.js +47 -24
- package/lib/App/index.js +100 -5
- package/lib/Capability/index.js +42 -0
- package/lib/Device/index.js +10 -0
- package/lib/Energy/index.js +6 -0
- package/lib/Media/index.js +3 -0
- package/lib/Signal/index.js +25 -0
- package/lib/Signal/validators.js +21 -0
- package/lib/Util/index.js +10 -0
- package/package.json +11 -3
- package/tsconfig.types.json +15 -0
- package/types/assets/app/schema.d.ts +507 -0
- package/types/assets/capability/schema.d.ts +53 -0
- package/types/helpers/index.d.ts +11 -0
- package/types/helpers/index.d.ts.map +1 -0
- package/types/index.d.ts +23 -0
- package/types/index.d.ts.map +1 -0
- package/types/lib/App/index.d.ts +140 -0
- package/types/lib/App/index.d.ts.map +1 -0
- package/types/lib/Capability/index.d.ts +61 -0
- package/types/lib/Capability/index.d.ts.map +1 -0
- package/types/lib/Device/index.d.ts +18 -0
- package/types/lib/Device/index.d.ts.map +1 -0
- package/types/lib/Energy/index.d.ts +12 -0
- package/types/lib/Energy/index.d.ts.map +1 -0
- package/types/lib/Media/index.d.ts +8 -0
- package/types/lib/Media/index.d.ts.map +1 -0
- package/types/lib/Signal/index.d.ts +52 -0
- package/types/lib/Signal/index.d.ts.map +1 -0
- package/types/lib/Signal/validators.d.ts +37 -0
- package/types/lib/Signal/validators.d.ts.map +1 -0
- package/types/lib/Util/index.d.ts +23 -0
- package/types/lib/Util/index.d.ts.map +1 -0
- package/webpack/index.js +1 -1
package/lib/App/index.js
CHANGED
|
@@ -30,6 +30,8 @@ const {
|
|
|
30
30
|
dirname,
|
|
31
31
|
} = require('../../helpers');
|
|
32
32
|
|
|
33
|
+
/** @typedef {import('../../assets/app/schema').App} AppManifest */
|
|
34
|
+
|
|
33
35
|
const VALIDATION_LEVELS = [
|
|
34
36
|
'debug',
|
|
35
37
|
'publish',
|
|
@@ -76,6 +78,9 @@ class App {
|
|
|
76
78
|
static REQUIRED_PYTHON_PLATFORMS = ['arm64', 'amd64']
|
|
77
79
|
static SUPPORTED_PYTHON_VERSIONS = ['3.13', '3.14']
|
|
78
80
|
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} path
|
|
83
|
+
*/
|
|
79
84
|
constructor(path) {
|
|
80
85
|
this._path = path;
|
|
81
86
|
|
|
@@ -84,11 +89,19 @@ class App {
|
|
|
84
89
|
}
|
|
85
90
|
}
|
|
86
91
|
|
|
92
|
+
/**
|
|
93
|
+
* @param {...unknown} args
|
|
94
|
+
* @returns {void}
|
|
95
|
+
*/
|
|
87
96
|
debug(...args) {
|
|
88
97
|
if (!this._debug) return;
|
|
89
98
|
console.log('[dbg]', ...args);
|
|
90
99
|
}
|
|
91
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @param {{ level?: 'debug' | 'publish' | 'verified', debug?: boolean }} [options]
|
|
103
|
+
* @returns {Promise<void>}
|
|
104
|
+
*/
|
|
92
105
|
async validate({
|
|
93
106
|
level = 'debug',
|
|
94
107
|
debug = false,
|
|
@@ -104,8 +117,9 @@ class App {
|
|
|
104
117
|
const levelPublish = (level === 'publish' || level === 'verified');
|
|
105
118
|
const levelVerified = (level === 'verified');
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
const appJsonBuffer = await readFileAsync(join(this._path, 'app.json'));
|
|
121
|
+
/** @type {AppManifest} */
|
|
122
|
+
const appJson = JSON.parse(appJsonBuffer);
|
|
109
123
|
|
|
110
124
|
const schema = App.getJSONSchema();
|
|
111
125
|
|
|
@@ -216,9 +230,8 @@ class App {
|
|
|
216
230
|
// validate that Python apps target compatibility of at least >=13.0.0
|
|
217
231
|
if (appJson.runtime === 'python') {
|
|
218
232
|
const minVersion = semver.minVersion(appJson.compatibility);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
throw new Error(`Invalid compatibility (${appJson.compatibility}), Python apps must have a compatibility of at least >=12.11.1`);
|
|
233
|
+
if (!semver.gte(minVersion, '13.0.0')) {
|
|
234
|
+
throw new Error(`Invalid compatibility (${appJson.compatibility}), Python apps must have a compatibility of at least >=13.0.0`);
|
|
222
235
|
}
|
|
223
236
|
}
|
|
224
237
|
|
|
@@ -787,6 +800,10 @@ class App {
|
|
|
787
800
|
this.debug('Validated successfully');
|
|
788
801
|
}
|
|
789
802
|
|
|
803
|
+
/**
|
|
804
|
+
* @param {AppManifest} appJson
|
|
805
|
+
* @returns {void}
|
|
806
|
+
*/
|
|
790
807
|
static validatePythonVersion(appJson) {
|
|
791
808
|
// validate that Python apps specify a runtime version
|
|
792
809
|
if (appJson.runtime === 'python' && !App.SUPPORTED_PYTHON_VERSIONS.includes(appJson.pythonVersion)) {
|
|
@@ -794,6 +811,11 @@ class App {
|
|
|
794
811
|
}
|
|
795
812
|
}
|
|
796
813
|
|
|
814
|
+
/**
|
|
815
|
+
* @param {Record<string, unknown>} setting
|
|
816
|
+
* @param {{ id: string }} driver
|
|
817
|
+
* @returns {void}
|
|
818
|
+
*/
|
|
797
819
|
_checkPrivateSettingPrefixUse(setting, driver) {
|
|
798
820
|
if (
|
|
799
821
|
setting.type &&
|
|
@@ -818,6 +840,11 @@ class App {
|
|
|
818
840
|
}
|
|
819
841
|
}
|
|
820
842
|
|
|
843
|
+
/**
|
|
844
|
+
* @param {Record<string, unknown> | null | undefined} driver
|
|
845
|
+
* @param {Record<string, unknown> | null | undefined} setting
|
|
846
|
+
* @returns {void}
|
|
847
|
+
*/
|
|
821
848
|
_checkZwaveForSetting(driver, setting) {
|
|
822
849
|
if (!driver || !setting || !setting.zwave) return;
|
|
823
850
|
if (typeof setting.zwave.index !== 'number' || typeof setting.zwave.size !== 'number') throw new Error(`Missing property in "zwave" at ${driver.id}, ${setting.id}`);
|
|
@@ -841,6 +868,10 @@ class App {
|
|
|
841
868
|
}
|
|
842
869
|
}
|
|
843
870
|
|
|
871
|
+
/**
|
|
872
|
+
* @param {string} filepath
|
|
873
|
+
* @returns {Promise<string[]>}
|
|
874
|
+
*/
|
|
844
875
|
async _getDirectoryContents(filepath) {
|
|
845
876
|
await this._fileExistsCaseSensitive(filepath);
|
|
846
877
|
|
|
@@ -853,6 +884,10 @@ class App {
|
|
|
853
884
|
});
|
|
854
885
|
}
|
|
855
886
|
|
|
887
|
+
/**
|
|
888
|
+
* @param {string} filepath
|
|
889
|
+
* @returns {Promise<void>}
|
|
890
|
+
*/
|
|
856
891
|
async _ensureFileExistsCaseSensitive(filepath) {
|
|
857
892
|
const exists = await this._fileExistsCaseSensitive(filepath);
|
|
858
893
|
if (exists !== true) {
|
|
@@ -860,6 +895,10 @@ class App {
|
|
|
860
895
|
}
|
|
861
896
|
}
|
|
862
897
|
|
|
898
|
+
/**
|
|
899
|
+
* @param {string} filepath
|
|
900
|
+
* @returns {Promise<boolean>}
|
|
901
|
+
*/
|
|
863
902
|
async _fileExistsCaseSensitive(filepath) {
|
|
864
903
|
filepath = join(this._path, filepath);
|
|
865
904
|
const dir = dirname(filepath);
|
|
@@ -875,6 +914,12 @@ class App {
|
|
|
875
914
|
}
|
|
876
915
|
}
|
|
877
916
|
|
|
917
|
+
/**
|
|
918
|
+
* @param {{ small: string, large: string }} imagesObj
|
|
919
|
+
* @param {'app' | 'driver'} type
|
|
920
|
+
* @param {string} [errorPath]
|
|
921
|
+
* @returns {Promise<void>}
|
|
922
|
+
*/
|
|
878
923
|
async _validateImages(imagesObj, type, errorPath) {
|
|
879
924
|
const sizes = ['small', 'large'];
|
|
880
925
|
for (let i = 0; i < sizes.length; i++) {
|
|
@@ -904,6 +949,9 @@ class App {
|
|
|
904
949
|
}
|
|
905
950
|
}
|
|
906
951
|
|
|
952
|
+
/**
|
|
953
|
+
* @returns {Promise<void>}
|
|
954
|
+
*/
|
|
907
955
|
async _validateModules() {
|
|
908
956
|
const nodeModulesPath = join(this._path, 'node_modules');
|
|
909
957
|
|
|
@@ -927,6 +975,13 @@ class App {
|
|
|
927
975
|
}
|
|
928
976
|
}
|
|
929
977
|
|
|
978
|
+
/**
|
|
979
|
+
* @param {Record<string, unknown>} card
|
|
980
|
+
* @param {string} errorPath
|
|
981
|
+
* @param {AppManifest} appJson
|
|
982
|
+
* @param {{ levelPublish: boolean, levelVerified: boolean }} options
|
|
983
|
+
* @returns {void}
|
|
984
|
+
*/
|
|
930
985
|
_validateFlowCard(card, errorPath, appJson, { levelPublish, levelVerified }) {
|
|
931
986
|
if (Array.isArray(card.tokens)) {
|
|
932
987
|
for (const token of card.tokens) {
|
|
@@ -1006,6 +1061,12 @@ class App {
|
|
|
1006
1061
|
}
|
|
1007
1062
|
}
|
|
1008
1063
|
|
|
1064
|
+
/**
|
|
1065
|
+
* @param {string} titleFormatted
|
|
1066
|
+
* @param {{ name: string }[]} args
|
|
1067
|
+
* @param {string} errorPath
|
|
1068
|
+
* @returns {void}
|
|
1069
|
+
*/
|
|
1009
1070
|
static _checkTitleFormatted(titleFormatted, args, errorPath) {
|
|
1010
1071
|
const argsPresent = args.reduce((obj, arg) => {
|
|
1011
1072
|
obj[arg.name] = false;
|
|
@@ -1040,6 +1101,11 @@ class App {
|
|
|
1040
1101
|
}
|
|
1041
1102
|
}
|
|
1042
1103
|
|
|
1104
|
+
/**
|
|
1105
|
+
* @param {string} filepath
|
|
1106
|
+
* @param {number} numBytes
|
|
1107
|
+
* @returns {Promise<Buffer>}
|
|
1108
|
+
*/
|
|
1043
1109
|
async _readBytes(filepath, numBytes) {
|
|
1044
1110
|
filepath = join(this._path, filepath);
|
|
1045
1111
|
|
|
@@ -1050,6 +1116,10 @@ class App {
|
|
|
1050
1116
|
return buffer;
|
|
1051
1117
|
}
|
|
1052
1118
|
|
|
1119
|
+
/**
|
|
1120
|
+
* @param {string} appId
|
|
1121
|
+
* @returns {boolean}
|
|
1122
|
+
*/
|
|
1053
1123
|
static isValidId(appId) {
|
|
1054
1124
|
if (typeof appId !== 'string') return false;
|
|
1055
1125
|
if (appId.length < 1) return false;
|
|
@@ -1058,16 +1128,26 @@ class App {
|
|
|
1058
1128
|
return true;
|
|
1059
1129
|
}
|
|
1060
1130
|
|
|
1131
|
+
/**
|
|
1132
|
+
* @param {string} color
|
|
1133
|
+
* @returns {boolean}
|
|
1134
|
+
*/
|
|
1061
1135
|
static isValidBrandColor(color) {
|
|
1062
1136
|
return tinycolor(color).getBrightness() <= 184; // empirically determined by many colorpicker samples
|
|
1063
1137
|
}
|
|
1064
1138
|
|
|
1139
|
+
/**
|
|
1140
|
+
* @returns {Record<string, unknown>}
|
|
1141
|
+
*/
|
|
1065
1142
|
static getJSONSchema() {
|
|
1066
1143
|
// eslint-disable-next-line global-require
|
|
1067
1144
|
const schema = require('../../assets/app/schema.json');
|
|
1068
1145
|
return JSON.parse(JSON.stringify(schema));
|
|
1069
1146
|
}
|
|
1070
1147
|
|
|
1148
|
+
/**
|
|
1149
|
+
* @returns {Record<string, Record<string, unknown>>}
|
|
1150
|
+
*/
|
|
1071
1151
|
static getPermissions() {
|
|
1072
1152
|
// eslint-disable-next-line global-require
|
|
1073
1153
|
const permissions = require('../../assets/app/permissions.json');
|
|
@@ -1082,15 +1162,25 @@ class App {
|
|
|
1082
1162
|
return permissions;
|
|
1083
1163
|
}
|
|
1084
1164
|
|
|
1165
|
+
/**
|
|
1166
|
+
* @returns {string[]}
|
|
1167
|
+
*/
|
|
1085
1168
|
static getCategories() {
|
|
1086
1169
|
return ['lights', 'video', 'music', 'appliances', 'security', 'climate', 'tools', 'internet', 'localization', 'energy'];
|
|
1087
1170
|
}
|
|
1088
1171
|
|
|
1172
|
+
/**
|
|
1173
|
+
* @returns {string[]}
|
|
1174
|
+
*/
|
|
1089
1175
|
static getLocales() {
|
|
1090
1176
|
// eslint-disable-next-line max-len
|
|
1091
1177
|
return ['ab', 'aa', 'af', 'ak', 'sq', 'am', 'ar', 'an', 'hy', 'as', 'av', 'ae', 'ay', 'az', 'bm', 'ba', 'eu', 'be', 'bn', 'bh', 'bi', 'bs', 'br', 'bg', 'my', 'ca', 'ch', 'ce', 'ny', 'zh', 'cv', 'kw', 'co', 'cr', 'hr', 'cs', 'da', 'dv', 'nl', 'dz', 'en', 'eo', 'et', 'ee', 'fo', 'fj', 'fi', 'fr', 'ff', 'gl', 'ka', 'de', 'el', 'gn', 'gu', 'ht', 'ha', 'he', 'hz', 'hi', 'ho', 'hu', 'ia', 'id', 'ie', 'ga', 'ig', 'ik', 'io', 'is', 'it', 'iu', 'ja', 'jv', 'kl', 'kn', 'kr', 'ks', 'kk', 'km', 'ki', 'rw', 'ky', 'kv', 'kg', 'ko', 'ku', 'kj', 'la', 'lb', 'lg', 'li', 'ln', 'lo', 'lt', 'lu', 'lv', 'gv', 'mk', 'mg', 'ms', 'ml', 'mt', 'mi', 'mr', 'mh', 'mn', 'na', 'nv', 'nd', 'ne', 'ng', 'nb', 'nn', 'no', 'ii', 'nr', 'oc', 'oj', 'cu', 'om', 'or', 'os', 'pa', 'pi', 'fa', 'pl', 'ps', 'pt', 'qu', 'rm', 'rn', 'ro', 'ru', 'sa', 'sc', 'sd', 'se', 'sm', 'sg', 'sr', 'gd', 'sn', 'si', 'sk', 'sl', 'so', 'st', 'es', 'su', 'sw', 'ss', 'sv', 'ta', 'te', 'tg', 'th', 'ti', 'bo', 'tk', 'tl', 'tn', 'to', 'tr', 'ts', 'tt', 'tw', 'ty', 'ug', 'uk', 'ur', 'uz', 've', 'vi', 'vo', 'wa', 'cy', 'wo', 'fy', 'xh', 'yi', 'yo', 'za', 'zu'];
|
|
1092
1178
|
}
|
|
1093
1179
|
|
|
1180
|
+
/**
|
|
1181
|
+
* @param {string} appId
|
|
1182
|
+
* @returns {string}
|
|
1183
|
+
*/
|
|
1094
1184
|
static getBrandColor(appId) {
|
|
1095
1185
|
const appIdHex = Buffer.from(appId).toString('hex');
|
|
1096
1186
|
let brandColor;
|
|
@@ -1114,6 +1204,11 @@ class App {
|
|
|
1114
1204
|
return brandColor;
|
|
1115
1205
|
}
|
|
1116
1206
|
|
|
1207
|
+
/**
|
|
1208
|
+
* @param {Array<Record<string, unknown>> | undefined} errors
|
|
1209
|
+
* @param {AppManifest} appJson
|
|
1210
|
+
* @returns {string | null}
|
|
1211
|
+
*/
|
|
1117
1212
|
static errorsText(errors, appJson) {
|
|
1118
1213
|
if (Array.isArray(errors) === false) return null;
|
|
1119
1214
|
|
package/lib/Capability/index.js
CHANGED
|
@@ -4,19 +4,33 @@
|
|
|
4
4
|
|
|
5
5
|
const Ajv = require('ajv');
|
|
6
6
|
|
|
7
|
+
/** @typedef {import('../../assets/capability/schema').Capability} CapabilityDefinition */
|
|
8
|
+
|
|
9
|
+
/** @type {Record<string, CapabilityDefinition> | undefined} */
|
|
7
10
|
let capabilitiesCache;
|
|
8
11
|
|
|
9
12
|
class Capability {
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @param {CapabilityDefinition} capability
|
|
16
|
+
*/
|
|
11
17
|
constructor(capability) {
|
|
12
18
|
this._capability = capability;
|
|
13
19
|
}
|
|
14
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @param {...unknown} args
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*/
|
|
15
25
|
debug(...args) {
|
|
16
26
|
if (!this._debug) return;
|
|
17
27
|
console.log('[dbg]', ...args);
|
|
18
28
|
}
|
|
19
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @param {{ debug?: boolean }} [options]
|
|
32
|
+
* @returns {Promise<void>}
|
|
33
|
+
*/
|
|
20
34
|
async validate({
|
|
21
35
|
debug = false,
|
|
22
36
|
} = {}) {
|
|
@@ -33,11 +47,17 @@ class Capability {
|
|
|
33
47
|
this.debug('Validated successfully');
|
|
34
48
|
}
|
|
35
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @returns {Record<string, unknown>}
|
|
52
|
+
*/
|
|
36
53
|
static getJSONSchema() {
|
|
37
54
|
// eslint-disable-next-line global-require
|
|
38
55
|
return require('../../assets/capability/schema.json');
|
|
39
56
|
}
|
|
40
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @returns {Record<string, CapabilityDefinition>}
|
|
60
|
+
*/
|
|
41
61
|
static getCapabilities() {
|
|
42
62
|
if (capabilitiesCache) return capabilitiesCache;
|
|
43
63
|
|
|
@@ -52,6 +72,10 @@ class Capability {
|
|
|
52
72
|
return capabilitiesCache;
|
|
53
73
|
}
|
|
54
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} id
|
|
77
|
+
* @returns {CapabilityDefinition}
|
|
78
|
+
*/
|
|
55
79
|
static getCapability(id) {
|
|
56
80
|
const capabilities = Capability.getCapabilities();
|
|
57
81
|
const capability = capabilities[id];
|
|
@@ -61,11 +85,20 @@ class Capability {
|
|
|
61
85
|
return capability;
|
|
62
86
|
}
|
|
63
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} id
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
64
92
|
static hasCapability(id) {
|
|
65
93
|
const capabilities = this.getCapabilities();
|
|
66
94
|
return !!capabilities[id];
|
|
67
95
|
}
|
|
68
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @param {string} capabilityId
|
|
99
|
+
* @param {CapabilityDefinition} capability
|
|
100
|
+
* @returns {CapabilityDefinition}
|
|
101
|
+
*/
|
|
69
102
|
static _composeCapability(capabilityId, capability) {
|
|
70
103
|
if (capability.flow) console.warn(`Warning: using \`capability.flow\` (${capabilityId}), expected a \`capability.$flow\``);
|
|
71
104
|
if (capability.$flow) {
|
|
@@ -109,12 +142,21 @@ class Capability {
|
|
|
109
142
|
return capability;
|
|
110
143
|
}
|
|
111
144
|
|
|
145
|
+
/**
|
|
146
|
+
* @param {string} capabilityIdToCheck
|
|
147
|
+
* @param {string} capabilityId
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
112
150
|
static isInstanceOfId(capabilityIdToCheck, capabilityId) {
|
|
113
151
|
return (
|
|
114
152
|
capabilityId === capabilityIdToCheck || capabilityIdToCheck.startsWith(`${capabilityId}.`)
|
|
115
153
|
);
|
|
116
154
|
}
|
|
117
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @param {string} capabilityId
|
|
158
|
+
* @returns {string}
|
|
159
|
+
*/
|
|
118
160
|
static getBaseId(capabilityId) {
|
|
119
161
|
const parts = capabilityId.split('.');
|
|
120
162
|
return parts[0];
|
package/lib/Device/index.js
CHANGED
|
@@ -6,6 +6,9 @@ let classesCache;
|
|
|
6
6
|
|
|
7
7
|
class Device {
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @returns {Record<string, Record<string, unknown>>}
|
|
11
|
+
*/
|
|
9
12
|
static getClasses() {
|
|
10
13
|
if (classesCache) return classesCache;
|
|
11
14
|
|
|
@@ -19,6 +22,10 @@ class Device {
|
|
|
19
22
|
return classesCache;
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} id
|
|
27
|
+
* @returns {Record<string, unknown>}
|
|
28
|
+
*/
|
|
22
29
|
static getClass(id) {
|
|
23
30
|
const deviceClasses = Device.getClasses();
|
|
24
31
|
const deviceClass = deviceClasses[id];
|
|
@@ -29,6 +36,9 @@ class Device {
|
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
// legacy
|
|
39
|
+
/**
|
|
40
|
+
* @returns {ReturnType<typeof Capability.getCapabilities>}
|
|
41
|
+
*/
|
|
32
42
|
static getCapabilities() {
|
|
33
43
|
return Capability.getCapabilities();
|
|
34
44
|
}
|
package/lib/Energy/index.js
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
class Energy {
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @returns {Record<string, Record<string, unknown>>}
|
|
7
|
+
*/
|
|
5
8
|
static getCurrencies() {
|
|
6
9
|
// eslint-disable-next-line global-require
|
|
7
10
|
return require('../../assets/energy/currencies.json');
|
|
8
11
|
}
|
|
9
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @returns {string[]}
|
|
15
|
+
*/
|
|
10
16
|
static getBatteries() {
|
|
11
17
|
return [
|
|
12
18
|
'LS14250',
|
package/lib/Media/index.js
CHANGED
package/lib/Signal/index.js
CHANGED
|
@@ -11,8 +11,16 @@ const {
|
|
|
11
11
|
prontoValidator,
|
|
12
12
|
} = require('./validators');
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {'433' | '868' | 'ir'} SignalFrequency
|
|
16
|
+
*/
|
|
17
|
+
|
|
14
18
|
class Signal {
|
|
15
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @param {Record<string, unknown>} signal
|
|
22
|
+
* @param {{ frequency?: SignalFrequency }} [options]
|
|
23
|
+
*/
|
|
16
24
|
constructor(signal, { frequency = undefined } = {}) {
|
|
17
25
|
this._signal = signal;
|
|
18
26
|
this._frequency = frequency;
|
|
@@ -20,6 +28,10 @@ class Signal {
|
|
|
20
28
|
this._check = this._check.bind(this);
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @param {...unknown} args
|
|
33
|
+
* @returns {void}
|
|
34
|
+
*/
|
|
23
35
|
debug(...args) {
|
|
24
36
|
if (!this._debug) return;
|
|
25
37
|
|
|
@@ -27,12 +39,21 @@ class Signal {
|
|
|
27
39
|
console.log('[dbg]', ...args);
|
|
28
40
|
}
|
|
29
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} message
|
|
44
|
+
* @param {boolean} result
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
30
47
|
_check(message, result) {
|
|
31
48
|
if (result !== true) {
|
|
32
49
|
throw new Error(message);
|
|
33
50
|
}
|
|
34
51
|
}
|
|
35
52
|
|
|
53
|
+
/**
|
|
54
|
+
* @param {{ debug?: boolean }} [options]
|
|
55
|
+
* @returns {Promise<void>}
|
|
56
|
+
*/
|
|
36
57
|
async validate({
|
|
37
58
|
debug = false,
|
|
38
59
|
} = {}) {
|
|
@@ -65,6 +86,10 @@ class Signal {
|
|
|
65
86
|
this.debug('Validated successfully');
|
|
66
87
|
}
|
|
67
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @param {Record<string, (value: unknown, signal: Record<string, unknown>) => { result: boolean, msg: string }>} validatorEngine
|
|
91
|
+
* @returns {void}
|
|
92
|
+
*/
|
|
68
93
|
_validateWithEngine(validatorEngine) {
|
|
69
94
|
return validate(validatorEngine, this._check, this._signal);
|
|
70
95
|
}
|
package/lib/Signal/validators.js
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Record<string, unknown>} SignalDefinition
|
|
7
|
+
* @typedef {{ result: boolean, msg: string }} ValidationResult
|
|
8
|
+
* @typedef {(value: unknown, signal: SignalDefinition) => ValidationResult} Validator
|
|
9
|
+
* @typedef {Record<string, Validator>} ValidatorEngine
|
|
10
|
+
* @typedef {(message: string, result: boolean) => void} CheckFn
|
|
11
|
+
*/
|
|
12
|
+
|
|
5
13
|
/* static functions */
|
|
14
|
+
/**
|
|
15
|
+
* @param {ValidatorEngine} validator
|
|
16
|
+
* @param {CheckFn} check
|
|
17
|
+
* @param {SignalDefinition} signal
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
6
20
|
function validate(validator, check, signal) {
|
|
7
21
|
for (const propName in signal) {
|
|
8
22
|
const property = signal[propName];
|
|
@@ -70,6 +84,7 @@ function _validateGenericData(data, signal) {
|
|
|
70
84
|
return res;
|
|
71
85
|
}
|
|
72
86
|
|
|
87
|
+
/** @type {ValidatorEngine} */
|
|
73
88
|
const genericValidator = {
|
|
74
89
|
words(words, signal) {
|
|
75
90
|
const res = { result: true, msg: 'invalid_words' };
|
|
@@ -166,6 +181,7 @@ const rfBounds = {
|
|
|
166
181
|
repetitions: { min: 1, max: 255 },
|
|
167
182
|
};
|
|
168
183
|
|
|
184
|
+
/** @type {ValidatorEngine} */
|
|
169
185
|
const rfValidator = {
|
|
170
186
|
|
|
171
187
|
words(words, signal) {
|
|
@@ -222,6 +238,7 @@ const modulationBounds = {
|
|
|
222
238
|
channelDeviation: { min: 5000, max: 50000 },
|
|
223
239
|
};
|
|
224
240
|
|
|
241
|
+
/** @type {ValidatorEngine} */
|
|
225
242
|
const modulationValidator = {
|
|
226
243
|
modulation(modulation, signal) {
|
|
227
244
|
// replace by default modulation props in homey-microcontroller?
|
|
@@ -256,6 +273,7 @@ const modulationValidator = {
|
|
|
256
273
|
},
|
|
257
274
|
};
|
|
258
275
|
|
|
276
|
+
/** @type {ValidatorEngine} */
|
|
259
277
|
const prontoValidator = {
|
|
260
278
|
cmds(cmds, signal) {
|
|
261
279
|
const res = !Object.keys(cmds).some(cmd => {
|
|
@@ -279,6 +297,7 @@ const rf433Bounds = {
|
|
|
279
297
|
carrier: { min: 433000000, max: 433990000 },
|
|
280
298
|
};
|
|
281
299
|
|
|
300
|
+
/** @type {ValidatorEngine} */
|
|
282
301
|
const rf433Validator = {
|
|
283
302
|
carrier(carrier, signal) {
|
|
284
303
|
const res = _valid_bounds(carrier, 433000000, 433990000);
|
|
@@ -290,6 +309,7 @@ const rf868Bounds = {
|
|
|
290
309
|
carrier: { min: 868000000, max: 868900000 },
|
|
291
310
|
};
|
|
292
311
|
|
|
312
|
+
/** @type {ValidatorEngine} */
|
|
293
313
|
const rf868Validator = {
|
|
294
314
|
carrier(carrier, signal) {
|
|
295
315
|
const res = _valid_bounds(carrier, rf868Bounds.carrier.min, rf868Bounds.carrier.max);
|
|
@@ -302,6 +322,7 @@ const irBounds = {
|
|
|
302
322
|
dutyCycle: { min: 30, max: 70 },
|
|
303
323
|
};
|
|
304
324
|
|
|
325
|
+
/** @type {ValidatorEngine} */
|
|
305
326
|
const irValidator = {
|
|
306
327
|
carrier(carrier, signal) {
|
|
307
328
|
const res = _valid_bounds(carrier, irBounds.carrier.min, irBounds.carrier.max);
|
package/lib/Util/index.js
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
class Util {
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} modelId
|
|
7
|
+
* @returns {string[]}
|
|
8
|
+
*/
|
|
5
9
|
static getPlatformLocalFeatures(modelId) {
|
|
6
10
|
return Util._platformLocalFeatures[modelId] || [];
|
|
7
11
|
}
|
|
8
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} modelId
|
|
15
|
+
* @param {string[]} wantedFeatures
|
|
16
|
+
* @returns {string[]}
|
|
17
|
+
*/
|
|
9
18
|
static getMissingPlatformLocalFeatures(modelId, wantedFeatures) {
|
|
10
19
|
const features = Util.getPlatformLocalFeatures(modelId);
|
|
11
20
|
return wantedFeatures.filter(feature => !features.includes(feature));
|
|
@@ -18,6 +27,7 @@ Util.FEATURE_LED_RING = 'ledring';
|
|
|
18
27
|
Util.FEATURE_NFC = 'nfc';
|
|
19
28
|
Util.FEATURE_MATTER = 'matter';
|
|
20
29
|
Util.FEATURE_CAMERA_STREAMING = 'camera-streaming';
|
|
30
|
+
/** @type {Record<string, string[]>} */
|
|
21
31
|
Util._platformLocalFeatures = {
|
|
22
32
|
homey1s: [Util.FEATURE_SPEAKER, Util.FEATURE_LED_RING, Util.FEATURE_NFC],
|
|
23
33
|
homey1d: [Util.FEATURE_SPEAKER, Util.FEATURE_LED_RING, Util.FEATURE_NFC],
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homey-lib",
|
|
3
|
-
"version": "2.45.
|
|
3
|
+
"version": "2.45.4",
|
|
4
4
|
"description": "Shared Library for Homey",
|
|
5
|
+
"types": "types/index.d.ts",
|
|
5
6
|
"main": "index.js",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"build": "npm ci; npm run pack",
|
|
8
|
+
"build": "npm ci; npm run types:generate; npm run schema:generate; npm run schema:copy; npm run pack",
|
|
8
9
|
"pack": "webpack",
|
|
9
10
|
"lint": "eslint .",
|
|
10
11
|
"test": "mocha --parallel",
|
|
11
12
|
"locales:apply": "node scripts/apply-locale-files.js",
|
|
12
|
-
"locales:generate": "node scripts/generate-locale-files.js"
|
|
13
|
+
"locales:generate": "node scripts/generate-locale-files.js",
|
|
14
|
+
"types:generate": "tsc --project tsconfig.types.json",
|
|
15
|
+
"schema:generate": "npm run capability-schema:generate && npm run app-schema:generate",
|
|
16
|
+
"schema:copy": "mkdir -p types/assets/app types/assets/capability && cp assets/app/schema.d.ts types/assets/app/schema.d.ts && cp assets/capability/schema.d.ts types/assets/capability/schema.d.ts",
|
|
17
|
+
"capability-schema:generate": "json2ts -i assets/capability/schema.json -o assets/capability/schema.d.ts --unknownAny",
|
|
18
|
+
"app-schema:generate": "json2ts -i assets/app/schema.json -o assets/app/schema.d.ts --unknownAny"
|
|
13
19
|
},
|
|
14
20
|
"engines": {
|
|
15
21
|
"node": ">=12.13.0"
|
|
@@ -34,10 +40,12 @@
|
|
|
34
40
|
"devDependencies": {
|
|
35
41
|
"eslint": "^7.31.0",
|
|
36
42
|
"eslint-config-athom": "^2.1.1",
|
|
43
|
+
"json-schema-to-typescript": "^15.0.4",
|
|
37
44
|
"mocha": "^9.1.3",
|
|
38
45
|
"mock-fs": "^5.1.2",
|
|
39
46
|
"object-path": "^0.11.8",
|
|
40
47
|
"set-value": "^4.1.0",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
41
49
|
"webpack": "^5.98.0",
|
|
42
50
|
"webpack-cli": "^5.1.4"
|
|
43
51
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"checkJs": false,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"emitDeclarationOnly": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"outDir": "./types",
|
|
9
|
+
"module": "commonjs",
|
|
10
|
+
"target": "ES2020",
|
|
11
|
+
"strict": false
|
|
12
|
+
},
|
|
13
|
+
"include": ["index.js", "lib/**/*.js"],
|
|
14
|
+
"exclude": ["test/**", "node_modules/**"]
|
|
15
|
+
}
|