@zwave-js/config 15.20.0 → 15.21.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.
Files changed (461) hide show
  1. package/build/cjs/_version.d.ts +1 -1
  2. package/build/cjs/_version.js +1 -1
  3. package/build/cjs/_version.js.map +1 -1
  4. package/build/cjs/configDir.node.js.map +1 -1
  5. package/build/cjs/devices/DeviceConfig.d.ts +15 -4
  6. package/build/cjs/devices/DeviceConfig.js +71 -14
  7. package/build/cjs/devices/DeviceConfig.js.map +2 -2
  8. package/build/cjs/devices/ParamInformation.d.ts +3 -2
  9. package/build/cjs/devices/ParamInformation.js +8 -1
  10. package/build/cjs/devices/ParamInformation.js.map +2 -2
  11. package/build/esm/_version.d.ts +1 -1
  12. package/build/esm/_version.js +1 -1
  13. package/build/esm/devices/DeviceConfig.d.ts +15 -4
  14. package/build/esm/devices/DeviceConfig.d.ts.map +1 -1
  15. package/build/esm/devices/DeviceConfig.js +94 -18
  16. package/build/esm/devices/DeviceConfig.js.map +1 -1
  17. package/build/esm/devices/ParamInformation.d.ts +3 -2
  18. package/build/esm/devices/ParamInformation.d.ts.map +1 -1
  19. package/build/esm/devices/ParamInformation.js +8 -0
  20. package/build/esm/devices/ParamInformation.js.map +1 -1
  21. package/config/devices/0x000c/hs-fls100-g2.json +2 -0
  22. package/config/devices/0x000c/hs-hsm200.json +5 -6
  23. package/config/devices/0x000c/hs-ls100.json +1 -0
  24. package/config/devices/0x000c/hs-ms100.json +2 -0
  25. package/config/devices/0x000c/templates/homeseer_template.json +3 -0
  26. package/config/devices/0x001a/rf9501.json +2 -5
  27. package/config/devices/0x001a/rf9536-n.json +3 -5
  28. package/config/devices/0x001a/rf9537.json +3 -5
  29. package/config/devices/0x001a/rf9540-n_0.0_1.1.json +3 -5
  30. package/config/devices/0x001a/rf9540-n_1.2.json +3 -5
  31. package/config/devices/0x001a/rf9542.json +3 -5
  32. package/config/devices/0x001a/rf9642.json +1 -0
  33. package/config/devices/0x001a/rfldm.json +1 -0
  34. package/config/devices/0x001a/rftr9505.json +1 -0
  35. package/config/devices/0x001a/rftr9605-t.json +1 -0
  36. package/config/devices/0x001d/templates/leviton_template.json +6 -0
  37. package/config/devices/0x001e/ezmultipli.json +2 -6
  38. package/config/devices/0x0039/39336_39443_zw3104.json +1 -0
  39. package/config/devices/0x0039/39339_39346_zw3107.json +3 -0
  40. package/config/devices/0x0039/39351_39458_zw3005.json +3 -0
  41. package/config/devices/0x0039/39351_39458_zw3010.json +3 -0
  42. package/config/devices/0x0039/39357_39464_zw3011.json +3 -0
  43. package/config/devices/0x003b/fe599nx.json +3 -6
  44. package/config/devices/0x0059/bbk001-z00.json +2 -0
  45. package/config/devices/0x0059/max10y-376.json +1 -0
  46. package/config/devices/0x0059/ses_302.json +2 -0
  47. package/config/devices/0x0059/ses_303.json +4 -0
  48. package/config/devices/0x0059/sir-321.json +1 -0
  49. package/config/devices/0x0059/swm301.json +1 -0
  50. package/config/devices/0x0060/ac301.json +1 -0
  51. package/config/devices/0x0060/ad146.json +1 -2
  52. package/config/devices/0x0060/an179.json +2 -10
  53. package/config/devices/0x0060/an180.json +2 -10
  54. package/config/devices/0x0060/an184.json +7 -10
  55. package/config/devices/0x0060/an188.json +7 -10
  56. package/config/devices/0x0060/hsp02.json +2 -0
  57. package/config/devices/0x0060/sp814.json +2 -0
  58. package/config/devices/0x0060/sp815.json +1 -0
  59. package/config/devices/0x0060/sp816.json +2 -0
  60. package/config/devices/0x0060/sp817.json +2 -0
  61. package/config/devices/0x0060/st814.json +25 -15
  62. package/config/devices/0x0063/38957_zw6305.json +1 -0
  63. package/config/devices/0x0063/38959_zw6306.json +3 -0
  64. package/config/devices/0x0063/43973_zw6305.json +1 -0
  65. package/config/devices/0x0063/43985_zw6306.json +3 -0
  66. package/config/devices/0x0063/52247_zw6309.json +3 -0
  67. package/config/devices/0x0063/52251_zw6307.json +2 -0
  68. package/config/devices/0x0063/templates/jasco_template.json +6 -2
  69. package/config/devices/0x0064/duwi_z-wave_plugin_switch.json +1 -0
  70. package/config/devices/0x0068/td13010.json +8 -6
  71. package/config/devices/0x0068/tr1b120z1.json +4 -3
  72. package/config/devices/0x0077/smartdimmer.json +1 -0
  73. package/config/devices/0x0084/fts05p.json +6 -10
  74. package/config/devices/0x0086/templates/aeotec_template.json +42 -21
  75. package/config/devices/0x0086/zw062.json +7 -2
  76. package/config/devices/0x0086/zw074.json +8 -0
  77. package/config/devices/0x0086/zw097.json +1 -1
  78. package/config/devices/0x0086/zw098.json +7 -5
  79. package/config/devices/0x0086/zw100.json +12 -1
  80. package/config/devices/0x0086/zw112.json +1 -1
  81. package/config/devices/0x0086/zw120.json +1 -1
  82. package/config/devices/0x0086/zw121.json +7 -5
  83. package/config/devices/0x0086/zw122.json +2 -1
  84. package/config/devices/0x0086/zw129.json +1 -1
  85. package/config/devices/0x0086/zw130.json +1 -1
  86. package/config/devices/0x008a/doorsensor.json +1 -0
  87. package/config/devices/0x008a/molite.json +2 -0
  88. package/config/devices/0x008b/zw111.json +3 -0
  89. package/config/devices/0x0097/multisensor.json +2 -0
  90. package/config/devices/0x0098/ct100.json +2 -0
  91. package/config/devices/0x0098/ct101.json +2 -0
  92. package/config/devices/0x0098/ct200.json +2 -0
  93. package/config/devices/0x0098/ct200x.json +3 -0
  94. package/config/devices/0x0098/ct32.json +2 -0
  95. package/config/devices/0x0107/fibefgs-213.json +6 -0
  96. package/config/devices/0x0108/dch-z110.json +5 -0
  97. package/config/devices/0x0108/dch-z120.json +5 -0
  98. package/config/devices/0x0109/zd2201.json +3 -0
  99. package/config/devices/0x0109/zd2301eu-5.json +7 -3
  100. package/config/devices/0x0109/zp3102.json +1 -0
  101. package/config/devices/0x0109/zp3111-5.json +5 -0
  102. package/config/devices/0x0109/zp3113-7.json +14 -6
  103. package/config/devices/0x010f/fgbs001.json +1 -0
  104. package/config/devices/0x010f/fgbs222.json +4 -0
  105. package/config/devices/0x010f/fgcd001.json +1 -0
  106. package/config/devices/0x010f/fgd212.json +11 -2
  107. package/config/devices/0x010f/fgdw002.json +3 -0
  108. package/config/devices/0x010f/fgfs101.json +1 -0
  109. package/config/devices/0x010f/fgk101.json +4 -0
  110. package/config/devices/0x010f/fgms001.json +15 -0
  111. package/config/devices/0x010f/fgpb101.json +26 -20
  112. package/config/devices/0x010f/fgr-224.json +8 -6
  113. package/config/devices/0x010f/fgs213.json +6 -0
  114. package/config/devices/0x010f/fgs223.json +24 -4
  115. package/config/devices/0x010f/fgsd002.json +1 -0
  116. package/config/devices/0x010f/fgss001.json +1 -0
  117. package/config/devices/0x010f/fgwdeu.json +24 -4
  118. package/config/devices/0x010f/fgwdseu-221.json +66 -17
  119. package/config/devices/0x010f/fgwp102.json +4 -0
  120. package/config/devices/0x010f/fgwpb-111.json +1 -0
  121. package/config/devices/0x010f/fgwpb-121.json +6 -0
  122. package/config/devices/0x010f/fgwpg-111.json +4 -0
  123. package/config/devices/0x010f/fgwpg-121.json +4 -0
  124. package/config/devices/0x010f/fgwreu-111.json +24 -4
  125. package/config/devices/0x010f/templates/fibaro_template.json +2 -12
  126. package/config/devices/0x0116/itemp.json +1 -0
  127. package/config/devices/0x0118/tsm02.json +6 -6
  128. package/config/devices/0x0118/tz04.json +1 -0
  129. package/config/devices/0x0118/tz88.json +3 -0
  130. package/config/devices/0x011a/plug-in_smart_meter_dimmer_switch.json +2 -0
  131. package/config/devices/0x011a/zw20rm.json +1 -0
  132. package/config/devices/0x011a/zw500dm.json +2 -0
  133. package/config/devices/0x012a/iq_outlet.json +1 -0
  134. package/config/devices/0x012a/qz2140-840.json +2 -2
  135. package/config/devices/0x012a/qz2142-840.json +2 -0
  136. package/config/devices/0x0131/ah-nas-pd07u1.json +7 -0
  137. package/config/devices/0x0131/ah-neo-ds07z.json +4 -0
  138. package/config/devices/0x0131/mh-p220.json +3 -0
  139. package/config/devices/0x0131/ne-nas-ds01z.json +2 -15
  140. package/config/devices/0x0131/ne-nas-pd01z.json +1 -0
  141. package/config/devices/0x0131/psp02.json +8 -15
  142. package/config/devices/0x0131/vs-zd2301.json +7 -3
  143. package/config/devices/0x013c/nsg-ab-02.json +1 -0
  144. package/config/devices/0x013c/pad02.json +1 -0
  145. package/config/devices/0x013c/pad15.json +2 -0
  146. package/config/devices/0x013c/pan03.json +1 -0
  147. package/config/devices/0x013c/pan04.json +1 -0
  148. package/config/devices/0x013c/pan05-1b.json +1 -0
  149. package/config/devices/0x013c/pan11.json +1 -0
  150. package/config/devices/0x013c/pan15-1-nes.json +1 -0
  151. package/config/devices/0x013c/pan16.json +1 -0
  152. package/config/devices/0x013c/pan30.json +35 -33
  153. package/config/devices/0x013c/pan34.json +1 -0
  154. package/config/devices/0x013c/pat02-a.json +4 -0
  155. package/config/devices/0x013c/pat02-b.json +6 -0
  156. package/config/devices/0x013c/pse04.json +4 -3
  157. package/config/devices/0x013c/psm02.json +4 -0
  158. package/config/devices/0x013c/psp05.json +2 -0
  159. package/config/devices/0x013c/psr03-a.json +3 -1
  160. package/config/devices/0x013c/psr03-b.json +3 -1
  161. package/config/devices/0x013c/pst02-a-br.json +2 -0
  162. package/config/devices/0x013c/pst02a.json +8 -0
  163. package/config/devices/0x013c/pst02b.json +9 -6
  164. package/config/devices/0x013c/pst02c.json +5 -0
  165. package/config/devices/0x013c/templates/philio_template.json +5 -22
  166. package/config/devices/0x0140/sc428zw.json +2 -0
  167. package/config/devices/0x0148/cometz_700.json +1 -0
  168. package/config/devices/0x0148/eur_airquality.json +2 -0
  169. package/config/devices/0x0148/spirit.json +2 -0
  170. package/config/devices/0x0148/temp_humidity_sensor.json +2 -0
  171. package/config/devices/0x0149/c7.json +3 -0
  172. package/config/devices/0x0149/dry.json +9 -18
  173. package/config/devices/0x0149/ums2.json +12 -36
  174. package/config/devices/0x0149/wps104.json +6 -12
  175. package/config/devices/0x0149/wsp1.json +8 -24
  176. package/config/devices/0x0149/wte.json +5 -6
  177. package/config/devices/0x014a/iszw7-eco.json +1 -0
  178. package/config/devices/0x014a/tstat-tbz500.json +5 -0
  179. package/config/devices/0x014f/pir200.json +2 -0
  180. package/config/devices/0x014f/zwn-bpc.json +1 -0
  181. package/config/devices/0x0154/005107_0.0-1.4.json +1 -0
  182. package/config/devices/0x0154/05438.json +1 -0
  183. package/config/devices/0x0154/123580.json +2 -0
  184. package/config/devices/0x0159/smart_plug_16a.json +4 -0
  185. package/config/devices/0x0159/zmnhaa.json +2 -0
  186. package/config/devices/0x0159/zmnhad.json +6 -1
  187. package/config/devices/0x0159/zmnhba.json +2 -0
  188. package/config/devices/0x0159/zmnhbd.json +8 -1
  189. package/config/devices/0x0159/zmnhca.json +2 -0
  190. package/config/devices/0x0159/zmnhcd.json +7 -2
  191. package/config/devices/0x0159/zmnhda.json +4 -0
  192. package/config/devices/0x0159/zmnhdd.json +11 -2
  193. package/config/devices/0x0159/zmnhhd.json +6 -0
  194. package/config/devices/0x0159/zmnhia.json +2 -0
  195. package/config/devices/0x0159/zmnhkd.json +4 -1
  196. package/config/devices/0x0159/zmnhla.json +3 -0
  197. package/config/devices/0x0159/zmnhld.json +6 -1
  198. package/config/devices/0x0159/zmnhnd.json +4 -1
  199. package/config/devices/0x0159/zmnhod.json +7 -1
  200. package/config/devices/0x0159/zmnhqd.json +2 -0
  201. package/config/devices/0x0159/zmnhsd.json +9 -2
  202. package/config/devices/0x0159/zmnhtd.json +6 -3
  203. package/config/devices/0x0159/zmnhvd.json +8 -1
  204. package/config/devices/0x0159/zmnkad1.json +14 -9
  205. package/config/devices/0x0159/zmnkid.json +6 -0
  206. package/config/devices/0x015d/nzw39.json +2 -0
  207. package/config/devices/0x015d/zen20.json +2 -0
  208. package/config/devices/0x015d/zw97.json +2 -0
  209. package/config/devices/0x015f/a8-9.json +45 -27
  210. package/config/devices/0x015f/mh-5900.json +16 -0
  211. package/config/devices/0x015f/mh-c221.json +2 -0
  212. package/config/devices/0x015f/mh-ds221.json +1 -0
  213. package/config/devices/0x015f/mh-dt311_411.json +3 -0
  214. package/config/devices/0x015f/mh-p220.json +3 -0
  215. package/config/devices/0x015f/mh-p311.json +1 -0
  216. package/config/devices/0x015f/mh-s220_3.2.json +1 -0
  217. package/config/devices/0x015f/mh10-pm2_5-wa_wd.json +15 -18
  218. package/config/devices/0x015f/mh9-co2.json +2 -0
  219. package/config/devices/0x015f/templates/mcohome_template.json +14 -3
  220. package/config/devices/0x016a/ft100.json +12 -1
  221. package/config/devices/0x016a/ft112.json +1 -1
  222. package/config/devices/0x016a/ft130-k.json +1 -0
  223. package/config/devices/0x016a/ft130.json +1 -0
  224. package/config/devices/0x016a/ufa01.json +2 -0
  225. package/config/devices/0x0175/mt02646.json +1 -0
  226. package/config/devices/0x0175/mt02647.json +6 -0
  227. package/config/devices/0x0175/mt02648.json +8 -6
  228. package/config/devices/0x0175/mt02755.json +6 -1
  229. package/config/devices/0x0175/mt02792.json +1 -0
  230. package/config/devices/0x0175/mt2759.json +3 -0
  231. package/config/devices/0x0175/mt2760.json +5 -0
  232. package/config/devices/0x0175/mt2761.json +4 -0
  233. package/config/devices/0x0175/pan11.json +1 -0
  234. package/config/devices/0x0175/pst02-1b.json +6 -6
  235. package/config/devices/0x0176/tzwp-102.json +22 -12
  236. package/config/devices/0x0178/ZSENS930.json +9 -0
  237. package/config/devices/0x0178/th100nx.json +11 -0
  238. package/config/devices/0x0179/ch-201.json +1 -0
  239. package/config/devices/0x017f/wink_d_w_sensor.json +2 -16
  240. package/config/devices/0x017f/wink_motion_sensor.json +5 -16
  241. package/config/devices/0x0184/ws-100.json +1 -0
  242. package/config/devices/0x0189/117001.json +7 -0
  243. package/config/devices/0x018c/pm-b400zw-n.json +1 -0
  244. package/config/devices/0x018c/ssd-105.json +2 -0
  245. package/config/devices/0x018c/ssd-205.json +2 -0
  246. package/config/devices/0x018c/ssd-305.json +2 -0
  247. package/config/devices/0x0190/adc-s2000-t-ra.json +7 -0
  248. package/config/devices/0x0190/adc-t_2000.json +45 -33
  249. package/config/devices/0x0190/adc-t_3000.json +63 -45
  250. package/config/devices/0x019a/11_02_022.json +19 -34
  251. package/config/devices/0x019a/11_04_21_22_28.json +3 -0
  252. package/config/devices/0x019b/heatit_z-trm_2_0.0_2.255.json +1 -0
  253. package/config/devices/0x019b/heatit_zdim.json +2 -0
  254. package/config/devices/0x019b/leakage-stopper.json +1 -0
  255. package/config/devices/0x019b/tf016_tf021.json +1 -0
  256. package/config/devices/0x019b/z-dim2.json +5 -0
  257. package/config/devices/0x019b/z-han2.json +1 -0
  258. package/config/devices/0x019b/z-temp2.json +12 -6
  259. package/config/devices/0x019b/z-temp3.json +6 -3
  260. package/config/devices/0x019b/z-trm2fx_3.0_255.255.json +1 -0
  261. package/config/devices/0x019b/z-trm3.json +1 -0
  262. package/config/devices/0x019b/z-trm6.json +1 -0
  263. package/config/devices/0x019b/z-trm6_dc.json +2 -0
  264. package/config/devices/0x019b/z-water2.json +1 -0
  265. package/config/devices/0x019b/zm_dimmer.json +6 -0
  266. package/config/devices/0x019b/zm_thermostat_16.json +1 -0
  267. package/config/devices/0x0208/hkzw-so01.json +2 -0
  268. package/config/devices/0x0208/hkzw-so05.json +2 -0
  269. package/config/devices/0x0208/hkzw-so08.json +6 -0
  270. package/config/devices/0x0208/hkzw_ms01.json +1 -0
  271. package/config/devices/0x0208/hkzw_ms02.json +1 -0
  272. package/config/devices/0x0208/hkzw_so03.json +6 -0
  273. package/config/devices/0x0208/pid15654.json +2 -0
  274. package/config/devices/0x0208/za-216001.json +6 -0
  275. package/config/devices/0x021d/ddl240x_15hzw.json +1 -0
  276. package/config/devices/0x021d/ddl240x_1hzw.json +1 -0
  277. package/config/devices/0x021d/ml2.json +1 -0
  278. package/config/devices/0x021f/dmdp1.json +1 -0
  279. package/config/devices/0x021f/dmms1.json +5 -1
  280. package/config/devices/0x021f/dmof1.json +1 -0
  281. package/config/devices/0x022c/osr-01.json +1 -0
  282. package/config/devices/0x022c/osr-02.json +1 -0
  283. package/config/devices/0x022c/sa08.json +1 -0
  284. package/config/devices/0x0233/sse-304.json +2 -0
  285. package/config/devices/0x0234/templates/logic_group_template.json +3 -0
  286. package/config/devices/0x024f/fms01.json +6 -0
  287. package/config/devices/0x024f/fwc01.json +6 -0
  288. package/config/devices/0x0258/nas-pd02z-2.json +5 -0
  289. package/config/devices/0x0258/nas-pd07u1.json +9 -0
  290. package/config/devices/0x0258/nas-pd07z.json +8 -0
  291. package/config/devices/0x0258/nas-rp01z1.json +3 -0
  292. package/config/devices/0x0258/nas-wr01z.json +1 -0
  293. package/config/devices/0x0258/nas-wr01ze.json +1 -0
  294. package/config/devices/0x0258/nas-wr02zu.json +3 -0
  295. package/config/devices/0x0258/neo-ds07z.json +4 -0
  296. package/config/devices/0x0258/templates/shenzhen_neo_template.json +5 -0
  297. package/config/devices/0x025d/da_vinci_switch_default.json +657 -130
  298. package/config/devices/0x0267/10002020-13x.json +3 -0
  299. package/config/devices/0x0267/10002041-130.json +2 -0
  300. package/config/devices/0x0267/10003041-13x.json +2 -0
  301. package/config/devices/0x0270/bvs-zwu.json +2 -0
  302. package/config/devices/0x0271/templates/steinel_template.json +16 -9
  303. package/config/devices/0x0273/osli-01.json +1 -0
  304. package/config/devices/0x027a/templates/zooz_template.json +131 -20
  305. package/config/devices/0x027a/zac36.json +1 -0
  306. package/config/devices/0x027a/zac38.json +1 -0
  307. package/config/devices/0x027a/zen04.json +10 -0
  308. package/config/devices/0x027a/zen06.json +6 -0
  309. package/config/devices/0x027a/zen07.json +2 -0
  310. package/config/devices/0x027a/zen14.json +29 -1
  311. package/config/devices/0x027a/zen15.json +15 -3
  312. package/config/devices/0x027a/zen20.json +15 -6
  313. package/config/devices/0x027a/zen22.json +1 -0
  314. package/config/devices/0x027a/zen24.json +1 -0
  315. package/config/devices/0x027a/zen25.json +5 -0
  316. package/config/devices/0x027a/zen26.json +1 -0
  317. package/config/devices/0x027a/zen31.json +4 -0
  318. package/config/devices/0x027a/zen32.json +21 -13
  319. package/config/devices/0x027a/zen35.json +18 -0
  320. package/config/devices/0x027a/zen37.json +1 -0
  321. package/config/devices/0x027a/zen54.json +2 -0
  322. package/config/devices/0x027a/zen56.json +238 -0
  323. package/config/devices/0x027a/zen57.json +96 -0
  324. package/config/devices/0x027a/zen71.json +13 -0
  325. package/config/devices/0x027a/zen72.json +18 -0
  326. package/config/devices/0x027a/zen77.json +34 -0
  327. package/config/devices/0x027a/zen78.json +9 -0
  328. package/config/devices/0x027a/zse09.json +4 -0
  329. package/config/devices/0x027a/zse11.json +11 -0
  330. package/config/devices/0x027a/zse29.json +3 -0
  331. package/config/devices/0x027a/zse40.json +10 -0
  332. package/config/devices/0x027a/zse42.json +32 -1
  333. package/config/devices/0x027a/zse44.json +8 -0
  334. package/config/devices/0x027a/zse50.json +1 -0
  335. package/config/devices/0x027a/zse70_800lr.json +10 -0
  336. package/config/devices/0x028c/pwly-7828-a1.json +1 -0
  337. package/config/devices/0x0299/1a90.json +1 -0
  338. package/config/devices/0x0299/1a91.json +1 -0
  339. package/config/devices/0x0299/1a92.json +9 -4
  340. package/config/devices/0x0299/1a93.json +1 -0
  341. package/config/devices/0x0300/7aa-ss-ve-a0.json +2 -0
  342. package/config/devices/0x0300/7ba-kp-v-b-a0.json +1 -0
  343. package/config/devices/0x0300/7ca-ss-ve-a0.json +3 -0
  344. package/config/devices/0x0307/86_102.json +1 -0
  345. package/config/devices/0x030f/ds100.json +1 -0
  346. package/config/devices/0x030f/mt-100.json +4 -12
  347. package/config/devices/0x0312/mse30z.json +4 -0
  348. package/config/devices/0x0312/templates/minoston_template.json +10 -0
  349. package/config/devices/0x0312/zw30.json +2 -0
  350. package/config/devices/0x0312/zw31.json +4 -0
  351. package/config/devices/0x0313/e0400z-ef.json +4 -30
  352. package/config/devices/0x0315/wd-100.json +1 -0
  353. package/config/devices/0x0315/ws-100.json +1 -0
  354. package/config/devices/0x0315/zl-ld-100.json +1 -0
  355. package/config/devices/0x0315/zl-pd-100.json +1 -0
  356. package/config/devices/0x031e/lzw30-sn.json +3 -0
  357. package/config/devices/0x031e/lzw30.json +1 -0
  358. package/config/devices/0x031e/lzw31-bsd.json +3 -0
  359. package/config/devices/0x031e/lzw31-sn.json +7 -0
  360. package/config/devices/0x031e/lzw40.json +1 -0
  361. package/config/devices/0x031e/lzw41.json +1 -0
  362. package/config/devices/0x031e/lzw42.json +1 -0
  363. package/config/devices/0x031e/lzw45.json +3 -0
  364. package/config/devices/0x031e/lzw60.json +11 -0
  365. package/config/devices/0x031e/vzw31-sn.json +8 -4
  366. package/config/devices/0x031e/vzw32-sn.json +11 -4
  367. package/config/devices/0x0329/lizy0005.json +1 -2
  368. package/config/devices/0x032e/dat-101.json +1 -0
  369. package/config/devices/0x0330/homemech-200X.json +8 -0
  370. package/config/devices/0x0330/sr-zv9021a.json +14 -6
  371. package/config/devices/0x0330/sr-zv9032a-eu.json +27 -35
  372. package/config/devices/0x0330/sr-zv9080a-a.json +9 -3
  373. package/config/devices/0x0330/sr-zv9092a.json +21 -12
  374. package/config/devices/0x0330/ves-zw-dim-001.json +8 -0
  375. package/config/devices/0x0330/ves-zw-hld-016.json +3 -0
  376. package/config/devices/0x0330/ves-zw-mot-018.json +3 -0
  377. package/config/devices/0x0330/ves-zw-swi-014.json +3 -0
  378. package/config/devices/0x0330/zv-9101.json +1 -0
  379. package/config/devices/0x0330/zv2400tac-sl-a.json +10 -0
  380. package/config/devices/0x0330/zv2835rac-nf.json +8 -0
  381. package/config/devices/0x0344/he-ft01.json +1 -0
  382. package/config/devices/0x0344/he-hls01.json +6 -0
  383. package/config/devices/0x0344/he-ht01.json +1 -0
  384. package/config/devices/0x0344/he-rs01.json +1 -0
  385. package/config/devices/0x0344/he-tps01.json +1 -0
  386. package/config/devices/0x0344/he-tps02.json +1 -0
  387. package/config/devices/0x0344/he-tps03.json +1 -0
  388. package/config/devices/0x0344/he-tps04.json +1 -0
  389. package/config/devices/0x0344/he-tps05.json +1 -0
  390. package/config/devices/0x0344/templates/heltun_template.json +1 -0
  391. package/config/devices/0x0345/templates/swidget_template.json +13 -0
  392. package/config/devices/0x0346/4aw1sz-0en0.json +1 -0
  393. package/config/devices/0x0346/alarm_keypad_gen1.json +1 -0
  394. package/config/devices/0x0346/contact_sensor_gen1.json +1 -0
  395. package/config/devices/0x0346/glass_break_sensor.json +1 -0
  396. package/config/devices/0x0346/keypad_v2.json +2 -1
  397. package/config/devices/0x0346/motion_sensor_gen1.json +1 -0
  398. package/config/devices/0x0346/motion_sensor_gen2.json +1 -0
  399. package/config/devices/0x0346/range_extender_gen1.json +1 -0
  400. package/config/devices/0x0346/ring_outdoor_siren.json +1 -0
  401. package/config/devices/0x0346/ring_smoke_co_listener.json +3 -0
  402. package/config/devices/0x0357/upowerswitch_1.json +2 -0
  403. package/config/devices/0x0357/upowerswitch_2.json +2 -0
  404. package/config/devices/0x0357/upowerswitch_3.json +2 -0
  405. package/config/devices/0x0371/zw175.json +5 -7
  406. package/config/devices/0x0371/zw187.json +1 -1
  407. package/config/devices/0x0371/zwa002.json +1 -0
  408. package/config/devices/0x0371/zwa003.json +1 -1
  409. package/config/devices/0x0371/zwa004.json +1 -1
  410. package/config/devices/0x0371/zwa005.json +7 -1
  411. package/config/devices/0x0371/zwa009_0.0_1.255.json +16 -30
  412. package/config/devices/0x0371/zwa009_11.0_255.255.json +16 -30
  413. package/config/devices/0x0371/zwa019.json +2 -0
  414. package/config/devices/0x0371/zwa021.json +6 -3
  415. package/config/devices/0x0371/zwa022.json +1 -0
  416. package/config/devices/0x0371/zwa023.json +9 -6
  417. package/config/devices/0x0371/zwa024.json +18 -5
  418. package/config/devices/0x0371/zwa039.json +16 -27
  419. package/config/devices/0x0371/zwa042.json +2 -0
  420. package/config/devices/0x0371/zwa045.json +13 -10
  421. package/config/devices/0x0371/zwa050.json +1 -1
  422. package/config/devices/0x0384/ha-zw-5sab.json +11 -0
  423. package/config/devices/0x0384/ha-zw-5sf.json +1 -0
  424. package/config/devices/0x039a/INT-SMDWD-01.json +1 -0
  425. package/config/devices/0x039a/INT-SMMD-N1.json +4 -0
  426. package/config/devices/0x039a/nas-wr01z.json +1 -0
  427. package/config/devices/0x0403/shbw10000.json +11 -1
  428. package/config/devices/0x0403/shha10000.json +3 -0
  429. package/config/devices/0x041b/39446_zw3107.json +2 -0
  430. package/config/devices/0x041b/39458_zw3010.json +2 -0
  431. package/config/devices/0x0431/ecodim.json +1 -0
  432. package/config/devices/0x0433/q-light_puck.json +3 -0
  433. package/config/devices/0x0433/q-light_zerodim.json +3 -0
  434. package/config/devices/0x0433/q-light_zerodim_2pol.json +3 -0
  435. package/config/devices/0x0438/4512725.json +26 -15
  436. package/config/devices/0x0438/4512744.json +17 -12
  437. package/config/devices/0x0438/4512745.json +17 -12
  438. package/config/devices/0x0438/4512746.json +4 -0
  439. package/config/devices/0x0438/4512757.json +35 -158
  440. package/config/devices/0x0438/dimmer-200w.json +2 -0
  441. package/config/devices/0x0438/dimmer-400w.json +1 -0
  442. package/config/devices/0x0438/dimmer2-400w.json +8 -0
  443. package/config/devices/0x0438/sr-zv9032a-eu.json +27 -35
  444. package/config/devices/0x045a/WB04V.json +3 -0
  445. package/config/devices/0x045a/WM25C.json +3 -0
  446. package/config/devices/0x045a/WM25L.json +3 -0
  447. package/config/devices/0x045a/Z-CM-V01.json +3 -0
  448. package/config/devices/0x045a/Z-DWS-V01.json +1 -0
  449. package/config/devices/0x045a/Z-TRV-V01.json +5 -0
  450. package/config/devices/0x0460/qnsh-001P10.json +9 -6
  451. package/config/devices/0x0460/templates/wave_template.json +21 -6
  452. package/config/devices/0x046a/WB04V.json +3 -0
  453. package/config/devices/0x046a/WM25L.json +3 -0
  454. package/config/devices/0x4118/tsm02.json +6 -6
  455. package/config/devices/0x5254/zdm-80.json +2 -0
  456. package/config/devices/0x5254/zds-210na.json +2 -0
  457. package/config/devices/0x5254/zts-110.json +1 -0
  458. package/config/devices/0x5254/zxt-800.json +3 -0
  459. package/config/devices/templates/honeywell_template.json +2 -0
  460. package/config/devices/templates/master_template.json +38 -0
  461. package/package.json +7 -7
@@ -1,2 +1,2 @@
1
- export declare const PACKAGE_VERSION = "15.20.0";
1
+ export declare const PACKAGE_VERSION = "15.21.0";
2
2
  //# sourceMappingURL=_version.d.ts.map
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  PACKAGE_VERSION: () => PACKAGE_VERSION
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const PACKAGE_VERSION = "15.20.0";
24
+ const PACKAGE_VERSION = "15.21.0";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  PACKAGE_VERSION
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/_version.ts"],
4
- "sourcesContent": ["// This file is auto-generated by the codegen maintenance script\nexport const PACKAGE_VERSION = \"15.20.0\";\n"],
4
+ "sourcesContent": ["// This file is auto-generated by the codegen maintenance script\nexport const PACKAGE_VERSION = \"15.21.0\";\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;;;;;AACO,MAAM,kBAAkB;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/configDir.node.ts", "../../../../node_modules/.store/@alcalzone-esm2cjs-npm-1.4.2-64e8c7e670/package/shims/import.meta.url/shim.js"],
3
+ "sources": ["../../src/configDir.node.ts", "../../../../node_modules/@alcalzone/esm2cjs/shims/import.meta.url/shim.js"],
4
4
  "sourcesContent": ["import { fileURLToPath } from \"node:url\";\nimport path from \"pathe\";\n\n/** The absolute path of the embedded configuration directory */\nexport const configDir = import.meta.url.startsWith(\"file:\")\n\t? path.join(\n\t\tpath.dirname(fileURLToPath(import.meta.url)),\n\t\t/src\\/[^/\\\\]+\\.ts$/.test(import.meta.url)\n\t\t\t? \"..\"\n\t\t\t: \"../..\",\n\t\t\"config\",\n\t)\n\t: import.meta.resolve(\"/config\");\n", "export const __import_meta_url =\n typeof document === 'undefined' ? new (require('url'.replace('', '')).URL)('file:' + __filename).href :\n (document.currentScript && document.currentScript.src || new URL('main.js', document.baseURI).href)\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;ACAO,IAAM,oBACX,OAAO,aAAa,cAAc,KAAK,QAAQ,MAAM,QAAQ,IAAI,EAAE,CAAC,GAAE,IAAK,UAAU,UAAU,EAAE,OAC9F,SAAS,iBAAiB,SAAS,cAAc,OAAO,IAAI,IAAI,WAAW,SAAS,OAAO,EAAE;ADFlG,sBAA8B;AAC9B,mBAAiB;AADjB;AAIO,MAAM,YAAY,kBAAgB,WAAW,OAAO,IACxD,aAAAA,QAAK,KACN,aAAAA,QAAK,YAAQ,+BAAc,iBAAe,CAAC,GAC3C,oBAAoB,KAAK,iBAAe,IACrC,OACA,SACH,QAAQ,IAEP,YAAY,QAAQ,SAAS;",
6
6
  "names": ["path"]
@@ -73,7 +73,7 @@ export declare class ConditionalDeviceConfig {
73
73
  readonly isEmbedded: boolean;
74
74
  evaluate(deviceId?: DeviceID): DeviceConfig;
75
75
  }
76
- export type DeviceConfigHashVersion = 0 | 1 | 2 | 3;
76
+ export type DeviceConfigHashVersion = 0 | 1 | 2 | 3 | 4;
77
77
  export declare class DeviceConfig {
78
78
  static from(fs: ReadFileSystemInfo & ReadFile, filename: string, isEmbedded: boolean, options: {
79
79
  rootDir: string;
@@ -116,10 +116,21 @@ export declare class DeviceConfig {
116
116
  getAssociationConfigForEndpoint(endpointIndex: number, group: number): AssociationConfig | undefined;
117
117
  private getHashable;
118
118
  /**
119
- * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.
119
+ * Returns a hash code that can be used to check whether a device config
120
+ * has changed enough to require a re-interview.
120
121
  */
121
122
  getHash(version?: DeviceConfigHashVersion): Promise<BytesView>;
122
- static get maxHashVersion(): 3;
123
- static areHashesEqual(hash: BytesView, other: BytesView): boolean;
123
+ static get maxHashVersion(): 4;
124
+ /**
125
+ * Compares a cached device config hash against the current one.
126
+ * @param currentHash must be at the latest hash version
127
+ * @param cachedHash may be at any version and will be normalized for comparison
128
+ */
129
+ static areHashesEqual(currentHash: BytesView, cachedHash: BytesView): boolean;
124
130
  }
131
+ declare function parseHash(hash: BytesView): {
132
+ version: number;
133
+ hashData: BytesView;
134
+ } | undefined;
135
+ export { parseHash as parseDeviceConfigHash };
125
136
  //# sourceMappingURL=DeviceConfig.d.ts.map
@@ -35,7 +35,8 @@ __export(DeviceConfig_exports, {
35
35
  generatePriorityDeviceIndex: () => generatePriorityDeviceIndex,
36
36
  getDevicesPaths: () => getDevicesPaths,
37
37
  loadDeviceIndexInternal: () => loadDeviceIndexInternal,
38
- loadFulltextDeviceIndexInternal: () => loadFulltextDeviceIndexInternal
38
+ loadFulltextDeviceIndexInternal: () => loadFulltextDeviceIndexInternal,
39
+ parseDeviceConfigHash: () => parseHash
39
40
  });
40
41
  module.exports = __toCommonJS(DeviceConfig_exports);
41
42
  var import_config_dir = require("#config_dir");
@@ -557,6 +558,7 @@ class DeviceConfig {
557
558
  for (const param of ep.paramInformation ?? []) {
558
559
  delete param.label;
559
560
  delete param.description;
561
+ delete param.$purpose;
560
562
  for (const opt of param.options ?? []) {
561
563
  delete opt.label;
562
564
  }
@@ -577,11 +579,25 @@ class DeviceConfig {
577
579
  }
578
580
  }
579
581
  }
582
+ if (version >= 4) {
583
+ if (hashable.endpoints) {
584
+ for (const [key, ep] of Object.entries(hashable.endpoints)) {
585
+ delete ep.paramInformation;
586
+ if (Object.keys(ep).length === 0) {
587
+ delete hashable.endpoints[key];
588
+ }
589
+ }
590
+ if (Object.keys(hashable.endpoints).length === 0) {
591
+ delete hashable.endpoints;
592
+ }
593
+ }
594
+ }
580
595
  hashable = sortObject(hashable);
581
596
  return hashable;
582
597
  }
583
598
  /**
584
- * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.
599
+ * Returns a hash code that can be used to check whether a device config
600
+ * has changed enough to require a re-interview.
585
601
  */
586
602
  async getHash(version = DeviceConfig.maxHashVersion) {
587
603
  const hashable = this.getHashable(version);
@@ -603,23 +619,34 @@ class DeviceConfig {
603
619
  return import_shared.Bytes.concat([prefixBytes, hash]);
604
620
  }
605
621
  static get maxHashVersion() {
606
- return 3;
622
+ return 4;
607
623
  }
608
- static areHashesEqual(hash, other) {
609
- const parsedHash = parseHash(hash);
610
- const parsedOther = parseHash(other);
611
- if (!parsedHash || !parsedOther)
624
+ /**
625
+ * Compares a cached device config hash against the current one.
626
+ * @param currentHash must be at the latest hash version
627
+ * @param cachedHash may be at any version and will be normalized for comparison
628
+ */
629
+ static areHashesEqual(currentHash, cachedHash) {
630
+ const current = parseHash(currentHash);
631
+ const cached = parseHash(cachedHash);
632
+ if (!current || !cached)
612
633
  return false;
613
- if (parsedHash.version < 2 && parsedOther.version < 2) {
614
- return import_shared.Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);
634
+ if (current.version < 2 && cached.version < 2) {
635
+ return import_shared.Bytes.view(current.hashData).equals(cached.hashData);
615
636
  }
616
- if (parsedHash.version < 2 || parsedOther.version < 2) {
637
+ if (current.version < 2 || cached.version < 2) {
617
638
  return false;
618
639
  }
619
- if (parsedHash.version === parsedOther.version) {
620
- return import_shared.Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);
640
+ let hashable;
641
+ try {
642
+ hashable = JSON.parse(import_shared.Bytes.view((0, import_core.inflateSync)(import_shared.Bytes.view(cached.hashData), { dictionary: deflateDict })).toString("utf8"));
643
+ } catch {
644
+ return false;
621
645
  }
622
- return false;
646
+ fixBrokenV2Hashable(hashable, cached.version);
647
+ upgradeHashable(hashable, cached.version, current.version);
648
+ const normalizedData = (0, import_core.deflateSync)(import_shared.Bytes.from(JSON.stringify(hashable), "utf8"), { level: 9, dictionary: deflateDict });
649
+ return import_shared.Bytes.view(normalizedData).equals(current.hashData);
623
650
  }
624
651
  }
625
652
  function parseHash(hash) {
@@ -652,6 +679,35 @@ function parseHash(hash) {
652
679
  }
653
680
  }
654
681
  __name(parseHash, "parseHash");
682
+ function fixBrokenV2Hashable(hashable, version) {
683
+ if (version !== 2)
684
+ return;
685
+ for (const ep of Object.values(hashable.endpoints ?? {})) {
686
+ for (const param of ep.paramInformation ?? []) {
687
+ if (param.hidden === false) {
688
+ delete param.hidden;
689
+ }
690
+ }
691
+ }
692
+ }
693
+ __name(fixBrokenV2Hashable, "fixBrokenV2Hashable");
694
+ function upgradeHashable(hashable, fromVersion, targetVersion) {
695
+ if (fromVersion >= targetVersion)
696
+ return;
697
+ if (targetVersion >= 4 && fromVersion < 4 && hashable.endpoints) {
698
+ for (const [key, ep] of Object.entries(hashable.endpoints)) {
699
+ delete ep.paramInformation;
700
+ if (Object.keys(ep).length === 0) {
701
+ delete hashable.endpoints[key];
702
+ }
703
+ }
704
+ if (Object.keys(hashable.endpoints).length === 0) {
705
+ delete hashable.endpoints;
706
+ }
707
+ fromVersion = 4;
708
+ }
709
+ }
710
+ __name(upgradeHashable, "upgradeHashable");
655
711
  // Annotate the CommonJS export names for ESM import in node:
656
712
  0 && (module.exports = {
657
713
  ConditionalDeviceConfig,
@@ -660,6 +716,7 @@ __name(parseHash, "parseHash");
660
716
  generatePriorityDeviceIndex,
661
717
  getDevicesPaths,
662
718
  loadDeviceIndexInternal,
663
- loadFulltextDeviceIndexInternal
719
+ loadFulltextDeviceIndexInternal,
720
+ parseDeviceConfigHash
664
721
  });
665
722
  //# sourceMappingURL=DeviceConfig.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/devices/DeviceConfig.ts"],
4
- "sourcesContent": ["import { configDir } from \"#config_dir\";\nimport {\n\tZWaveError,\n\tZWaveErrorCodes,\n\tdeflateSync,\n\tdigest,\n} from \"@zwave-js/core\";\nimport {\n\tBytes,\n\ttype BytesView,\n\ttype JSONObject,\n\tcloneDeep,\n\tenumFilesRecursive,\n\tformatId,\n\tgetenv,\n\tnum2hex,\n\tpadVersion,\n\tpathExists,\n\tpick,\n\treadTextFile,\n\tstringify,\n\twriteTextFile,\n} from \"@zwave-js/shared\";\nimport type {\n\tReadFile,\n\tReadFileSystemInfo,\n\tWriteFile,\n} from \"@zwave-js/shared/bindings\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport path from \"pathe\";\nimport semverGt from \"semver/functions/gt.js\";\nimport { clearTemplateCache, readJsonWithTemplate } from \"../JsonTemplate.js\";\nimport type { ConfigLogger } from \"../Logger.js\";\nimport { hexKeyRegex4Digits, throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype AssociationConfig,\n\tConditionalAssociationConfig,\n} from \"./AssociationConfig.js\";\nimport { type CompatConfig, ConditionalCompatConfig } from \"./CompatConfig.js\";\nimport { evaluateDeep, validateCondition } from \"./ConditionalItem.js\";\nimport {\n\ttype ConditionalPrimitive,\n\tparseConditionalPrimitive,\n} from \"./ConditionalPrimitive.js\";\nimport {\n\tConditionalDeviceMetadata,\n\ttype DeviceMetadata,\n} from \"./DeviceMetadata.js\";\nimport {\n\tConditionalEndpointConfig,\n\ttype EndpointConfig,\n} from \"./EndpointConfig.js\";\nimport {\n\ttype ConditionalParamInfoMap,\n\ttype ParamInfoMap,\n\ttype ParamInformation,\n\tparseConditionalParamInformationMap,\n} from \"./ParamInformation.js\";\nimport { ConditionalSceneConfig, type SceneConfig } from \"./SceneConfig.js\";\nimport type { DeviceID, FirmwareVersionRange } from \"./shared.js\";\n\nexport interface DeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport interface FulltextDeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tmanufacturer: string;\n\tlabel: string;\n\tdescription: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport const embeddedDevicesDir = path.join(configDir, \"devices\");\nconst fulltextIndexPath = path.join(embeddedDevicesDir, \"fulltext_index.json\");\n\nexport function getDevicesPaths(configDir: string): {\n\tdevicesDir: string;\n\tindexPath: string;\n} {\n\tconst devicesDir = path.join(configDir, \"devices\");\n\tconst indexPath = path.join(devicesDir, \"index.json\");\n\treturn { devicesDir, indexPath };\n}\n\nexport type DeviceConfigIndex = DeviceConfigIndexEntry[];\nexport type FulltextDeviceConfigIndex = FulltextDeviceConfigIndexEntry[];\n\nasync function hasChangedDeviceFiles(\n\tfs: ReadFileSystemInfo,\n\tdevicesRoot: string,\n\tdir: string,\n\tlastChange: Date,\n): Promise<boolean> {\n\t// Check if there are any files BUT index.json that were changed\n\t// or directories that were modified\n\tconst filesAndDirs = await fs.readDir(dir);\n\tfor (const f of filesAndDirs) {\n\t\tconst fullPath = path.join(dir, f);\n\n\t\tconst stat = await fs.stat(fullPath);\n\t\tif (\n\t\t\t(dir !== devicesRoot || f !== \"index.json\")\n\t\t\t&& (stat.isFile() || stat.isDirectory())\n\t\t\t&& stat.mtime > lastChange\n\t\t) {\n\t\t\treturn true;\n\t\t} else if (stat.isDirectory()) {\n\t\t\t// we need to go deeper!\n\t\t\tif (\n\t\t\t\tawait hasChangedDeviceFiles(\n\t\t\t\t\tfs,\n\t\t\t\t\tdevicesRoot,\n\t\t\t\t\tfullPath,\n\t\t\t\t\tlastChange,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Read all device config files from a given directory and return them as index entries.\n * Does not update the index itself.\n */\nasync function generateIndex<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdevicesDir: string,\n\tisEmbedded: boolean,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string; rootDir?: string })[]> {\n\tconst index: (T & { filename: string; rootDir?: string })[] = [];\n\n\tclearTemplateCache();\n\tconst configFiles = await enumFilesRecursive(\n\t\tfs,\n\t\tdevicesDir,\n\t\t(file) =>\n\t\t\tfile.endsWith(\".json\")\n\t\t\t&& !file.endsWith(\"index.json\")\n\t\t\t&& !file.includes(\"/templates/\")\n\t\t\t&& !file.includes(\"\\\\templates\\\\\"),\n\t);\n\n\t// Add the embedded devices dir as a fallback if necessary\n\tconst fallbackDirs = devicesDir !== embeddedDevicesDir\n\t\t? [embeddedDevicesDir]\n\t\t: undefined;\n\n\tfor (const file of configFiles) {\n\t\tconst relativePath = path\n\t\t\t.relative(devicesDir, file)\n\t\t\t.replaceAll(\"\\\\\", \"/\");\n\t\t// Try parsing the file\n\t\ttry {\n\t\t\tconst config = await DeviceConfig.from(\n\t\t\t\tfs,\n\t\t\t\tfile,\n\t\t\t\tisEmbedded,\n\t\t\t\t{\n\t\t\t\t\trootDir: devicesDir,\n\t\t\t\t\tfallbackDirs,\n\t\t\t\t\trelative: true,\n\t\t\t\t},\n\t\t\t);\n\t\t\t// Add the file to the index\n\t\t\tindex.push(\n\t\t\t\t...extractIndexEntries(config).map((entry) => {\n\t\t\t\t\tconst ret: T & { filename: string; rootDir?: string } = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tfilename: relativePath,\n\t\t\t\t\t};\n\t\t\t\t\t// Only add the root dir to the index if necessary\n\t\t\t\t\tif (devicesDir !== embeddedDevicesDir) {\n\t\t\t\t\t\tret.rootDir = devicesDir;\n\t\t\t\t\t}\n\t\t\t\t\treturn ret;\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Error parsing config file ${relativePath}: ${\n\t\t\t\t(e as Error).message\n\t\t\t}`;\n\t\t\t// Crash hard during tests, just print an error when in production systems.\n\t\t\t// A user could have changed a config file\n\t\t\tif (process.env.NODE_ENV === \"test\" || !!getenv(\"CI\")) {\n\t\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Config_Invalid);\n\t\t\t} else {\n\t\t\t\tlogger?.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn index;\n}\n\nasync function loadDeviceIndexShared<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tdevicesDir: string,\n\tindexPath: string,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string })[]> {\n\t// The index file needs to be regenerated if it does not exist\n\tlet needsUpdate = !(await pathExists(fs, indexPath));\n\tlet index: (T & { filename: string })[] | undefined;\n\tlet mtimeIndex: Date | undefined;\n\t// ...or if cannot be parsed\n\tif (!needsUpdate) {\n\t\ttry {\n\t\t\tconst fileContents = await readTextFile(fs, indexPath, \"utf8\");\n\t\t\tindex = JSON5.parse(fileContents);\n\t\t\tmtimeIndex = (await fs.stat(indexPath)).mtime;\n\t\t} catch {\n\t\t\tlogger?.print(\n\t\t\t\t\"Error while parsing index file - regenerating...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tneedsUpdate = true;\n\t\t} finally {\n\t\t\tif (!index) {\n\t\t\t\tlogger?.print(\n\t\t\t\t\t\"Index file was malformed - regenerating...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tneedsUpdate = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ...or if there were any changes in the file system\n\tif (!needsUpdate) {\n\t\tneedsUpdate = await hasChangedDeviceFiles(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\tdevicesDir,\n\t\t\tmtimeIndex!,\n\t\t);\n\t\tif (needsUpdate) {\n\t\t\tlogger?.print(\n\t\t\t\t\"Device configuration files on disk changed - regenerating index...\",\n\t\t\t\t\"verbose\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (needsUpdate) {\n\t\t// Read all files from disk and generate an index\n\t\tindex = await generateIndex(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\ttrue,\n\t\t\textractIndexEntries,\n\t\t\tlogger,\n\t\t);\n\t\t// Save the index to disk\n\t\ttry {\n\t\t\tawait writeTextFile(\n\t\t\t\tfs,\n\t\t\t\tpath.join(indexPath),\n\t\t\t\t`// This file is auto-generated. DO NOT edit it by hand if you don't know what you're doing!\"\n${stringify(index, \"\\t\")}\n`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tlogger?.print(\"Device index regenerated\", \"verbose\");\n\t\t} catch (e) {\n\t\t\tlogger?.print(\n\t\t\t\t`Writing the device index to disk failed: ${\n\t\t\t\t\t(e as Error).message\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn index!;\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function generatePriorityDeviceIndex(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdeviceConfigPriorityDir: string,\n\tlogger?: ConfigLogger,\n): Promise<DeviceConfigIndex> {\n\treturn (\n\t\tawait generateIndex(\n\t\t\tfs,\n\t\t\tdeviceConfigPriorityDir,\n\t\t\tfalse,\n\t\t\t(config) =>\n\t\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\t\tmanufacturerId: formatId(\n\t\t\t\t\t\tconfig.manufacturerId.toString(16),\n\t\t\t\t\t),\n\t\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\t\tlabel: config.label,\n\t\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\t\trootDir: deviceConfigPriorityDir,\n\t\t\t\t})),\n\t\t\tlogger,\n\t\t)\n\t).map(({ filename, ...entry }) => ({\n\t\t...entry,\n\t\t// The generated index makes the filenames relative to the given directory\n\t\t// but we need them to be absolute\n\t\tfilename: path.join(deviceConfigPriorityDir, filename),\n\t}));\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n\texternalConfigDir?: string,\n): Promise<DeviceConfigIndex> {\n\tconst { devicesDir, indexPath } = getDevicesPaths(\n\t\texternalConfigDir || configDir,\n\t);\n\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tdevicesDir,\n\t\tindexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\n/**\n * @internal\n * Loads the full text index file to quickly search the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadFulltextDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n): Promise<FulltextDeviceConfigIndex> {\n\t// This method is not meant to operate with the external device index!\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tembeddedDevicesDir,\n\t\tfulltextIndexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tdescription: config.description,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\trootDir: embeddedDevicesDir,\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\nfunction isHexKeyWith4Digits(val: any): val is string {\n\treturn typeof val === \"string\" && hexKeyRegex4Digits.test(val);\n}\n\nconst firmwareVersionRegex = /^\\d{1,3}\\.\\d{1,3}(\\.\\d{1,3})?$/;\nfunction isFirmwareVersion(val: any): val is string {\n\treturn (\n\t\ttypeof val === \"string\"\n\t\t&& firmwareVersionRegex.test(val)\n\t\t&& val\n\t\t\t.split(\".\")\n\t\t\t.map((str) => parseInt(str, 10))\n\t\t\t.every((num) => num >= 0 && num <= 255)\n\t);\n}\n\nconst deflateDict = Bytes.from(\n\t// Substrings appearing in the device config files in descending order of frequency\n\t// except for very short ones like 0, 1, ...\n\t// WARNING: THIS MUST NOT BE CHANGED! Doing so breaks decompressing stored hashes.\n\t[\n\t\t`\"parameterNumber\":`,\n\t\t`255`,\n\t\t`\"value\":`,\n\t\t`\"defaultValue\":`,\n\t\t`\"valueSize\":`,\n\t\t`\"maxValue\":`,\n\t\t`\"minValue\":`,\n\t\t`\"options\":`,\n\t\t`true`,\n\t\t`false`,\n\t\t`\"allowManualEntry\":`,\n\t\t`\"maxNodes\":`,\n\t\t`100`,\n\t\t`\"unsigned\":`,\n\t\t`\"paramInformation\":`,\n\t\t`\"isLifeline\":`,\n\t\t`\"seconds\"`,\n\t\t`99`,\n\t\t`127`,\n\t\t`\"%\"`,\n\t\t`65535`,\n\t\t`32767`,\n\t\t`\"minutes\"`,\n\t\t`\"endpoints\":`,\n\t\t`\"hours\"`,\n\t\t`\"multiChannel\":`,\n\t]\n\t\t.join(\"\"),\n\t\"utf8\",\n);\n\n/** This class represents a device config entry whose conditional settings have not been evaluated yet */\nexport class ConditionalDeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t},\n\t): Promise<ConditionalDeviceConfig> {\n\t\tconst { relative, rootDir } = options;\n\n\t\tconst relativePath = relative\n\t\t\t? path.relative(rootDir, filename).replaceAll(\"\\\\\", \"/\")\n\t\t\t: filename;\n\t\tconst json = await readJsonWithTemplate(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\t[\n\t\t\t\toptions.rootDir,\n\t\t\t\t...(options.fallbackDirs ?? []),\n\t\t\t],\n\t\t);\n\t\treturn new ConditionalDeviceConfig(relativePath, isEmbedded, json);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\n\t\tif (!isHexKeyWith4Digits(definition.manufacturerId)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nmanufacturer id must be a lowercase hexadecimal number with 4 digits`,\n\t\t\t);\n\t\t}\n\t\tthis.manufacturerId = parseInt(definition.manufacturerId, 16);\n\n\t\tfor (const prop of [\"manufacturer\", \"label\", \"description\"] as const) {\n\t\t\tthis[prop] = parseConditionalPrimitive(\n\t\t\t\tfilename,\n\t\t\t\t\"string\",\n\t\t\t\tprop,\n\t\t\t\tdefinition[prop],\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\t!isArray(definition.devices)\n\t\t\t|| !(definition.devices as any[]).every(\n\t\t\t\t(dev: unknown) =>\n\t\t\t\t\tisObject(dev)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productType)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productId),\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\ndevices is malformed (not an object or type/id that is not a lowercase 4-digit hex key)`,\n\t\t\t);\n\t\t}\n\t\tthis.devices = (definition.devices as any[]).map(\n\t\t\t({ productType, productId }) => ({\n\t\t\t\tproductType: parseInt(productType, 16),\n\t\t\t\tproductId: parseInt(productId, 16),\n\t\t\t}),\n\t\t);\n\n\t\tif (\n\t\t\t!isObject(definition.firmwareVersion)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.min)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.max)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion is malformed or invalid. Must be x.y or x.y.z where x, y, and z are integers between 0 and 255`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst { min, max } = definition.firmwareVersion;\n\t\t\tif (semverGt(padVersion(min), padVersion(max))) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion.min ${min} must not be greater than firmwareVersion.max ${max}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.firmwareVersion = { min, max };\n\t\t}\n\n\t\tif (\n\t\t\tdefinition.preferred != undefined\n\t\t\t&& definition.preferred !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\npreferred must be true or omitted`,\n\t\t\t);\n\t\t}\n\t\tthis.preferred = !!definition.preferred;\n\n\t\tif (definition.endpoints != undefined) {\n\t\t\tconst endpoints = new Map<number, ConditionalEndpointConfig>();\n\t\t\tif (!isObject(definition.endpoints)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nendpoints is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const [key, ep] of Object.entries(definition.endpoints)) {\n\t\t\t\tif (!/^\\d+$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric endpoint index \"${key}\" in endpoints`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst epIndex = parseInt(key, 10);\n\t\t\t\tendpoints.set(\n\t\t\t\t\tepIndex,\n\t\t\t\t\tnew ConditionalEndpointConfig(this, epIndex, ep as any),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.endpoints = endpoints;\n\t\t}\n\n\t\tif (definition.associations != undefined) {\n\t\t\tconst associations = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalAssociationConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.associations)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nassociations is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, assocDefinition] of Object.entries(\n\t\t\t\t\tdefinition.associations,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric group id \"${key}\" in associations`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tassociations.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalAssociationConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tassocDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.associations = associations;\n\t\t}\n\n\t\tif (definition.paramInformation != undefined) {\n\t\t\tthis.paramInformation = parseConditionalParamInformationMap(\n\t\t\t\tdefinition,\n\t\t\t\tthis,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.proprietary != undefined) {\n\t\t\tif (!isObject(definition.proprietary)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nproprietary is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.proprietary = definition.proprietary;\n\t\t}\n\n\t\tif (definition.compat != undefined) {\n\t\t\tif (\n\t\t\t\tisArray(definition.compat)\n\t\t\t\t&& definition.compat.every((item: any) => isObject(item))\n\t\t\t) {\n\t\t\t\t// Make sure all conditions are valid\n\t\t\t\tfor (const entry of definition.compat) {\n\t\t\t\t\tvalidateCondition(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\t`At least one entry of compat contains an`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.compat = definition.compat.map(\n\t\t\t\t\t(item: any) => new ConditionalCompatConfig(filename, item),\n\t\t\t\t);\n\t\t\t} else if (isObject(definition.compat)) {\n\t\t\t\tthis.compat = new ConditionalCompatConfig(\n\t\t\t\t\tfilename,\n\t\t\t\t\tdefinition.compat,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\ncompat must be an object or any array of conditional objects`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (definition.metadata != undefined) {\n\t\t\tif (!isObject(definition.metadata)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nmetadata is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.metadata = new ConditionalDeviceMetadata(\n\t\t\t\tfilename,\n\t\t\t\tdefinition.metadata,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.scenes != undefined) {\n\t\t\tconst scenes = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalSceneConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.scenes)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nscenes is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, sceneDefinition] of Object.entries(\n\t\t\t\t\tdefinition.scenes,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\ninvalid scene id \"${key}\" in scenes - must be a positive integer (1-255)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tif (keyNum < 1 || keyNum > 255) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nscene number ${keyNum} must be between 1 and 255`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tscenes.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalSceneConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tsceneDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.scenes = scenes;\n\t\t}\n\t}\n\n\tpublic readonly filename: string;\n\n\tpublic readonly manufacturer!: ConditionalPrimitive<string>;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label!: ConditionalPrimitive<string>;\n\tpublic readonly description!: ConditionalPrimitive<string>;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, ConditionalEndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<\n\t\tnumber,\n\t\tConditionalAssociationConfig\n\t>;\n\tpublic readonly scenes?: ReadonlyMap<number, ConditionalSceneConfig>;\n\tpublic readonly paramInformation?: ConditionalParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?:\n\t\t| ConditionalCompatConfig\n\t\t| ConditionalCompatConfig[];\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: ConditionalDeviceMetadata;\n\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\n\tpublic evaluate(deviceId?: DeviceID): DeviceConfig {\n\t\treturn new DeviceConfig(\n\t\t\tthis.filename,\n\t\t\tthis.isEmbedded,\n\t\t\tevaluateDeep(this.manufacturer, deviceId),\n\t\t\tthis.manufacturerId,\n\t\t\tevaluateDeep(this.label, deviceId),\n\t\t\tevaluateDeep(this.description, deviceId),\n\t\t\tthis.devices,\n\t\t\tthis.firmwareVersion,\n\t\t\tthis.preferred,\n\t\t\tevaluateDeep(this.endpoints, deviceId),\n\t\t\tevaluateDeep(this.associations, deviceId),\n\t\t\tevaluateDeep(this.scenes, deviceId),\n\t\t\tevaluateDeep(this.paramInformation, deviceId),\n\t\t\tthis.proprietary,\n\t\t\tevaluateDeep(this.compat, deviceId),\n\t\t\tevaluateDeep(this.metadata, deviceId),\n\t\t);\n\t}\n}\n\nexport type DeviceConfigHashVersion = 0 | 1 | 2 | 3;\n\nexport class DeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t\tdeviceId?: DeviceID;\n\t\t},\n\t): Promise<DeviceConfig> {\n\t\tconst ret = await ConditionalDeviceConfig.from(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\tisEmbedded,\n\t\t\toptions,\n\t\t);\n\t\treturn ret.evaluate(options.deviceId);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tmanufacturer: string,\n\t\tmanufacturerId: number,\n\t\tlabel: string,\n\t\tdescription: string,\n\t\tdevices: readonly {\n\t\t\tproductType: number;\n\t\t\tproductId: number;\n\t\t}[],\n\t\tfirmwareVersion: FirmwareVersionRange,\n\t\tpreferred: boolean,\n\t\tendpoints?: ReadonlyMap<number, EndpointConfig>,\n\t\tassociations?: ReadonlyMap<number, AssociationConfig>,\n\t\tscenes?: ReadonlyMap<number, SceneConfig>,\n\t\tparamInformation?: ParamInfoMap,\n\t\tproprietary?: Record<string, unknown>,\n\t\tcompat?: CompatConfig,\n\t\tmetadata?: DeviceMetadata,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\t\tthis.manufacturer = manufacturer;\n\t\tthis.manufacturerId = manufacturerId;\n\t\tthis.label = label;\n\t\tthis.description = description;\n\t\tthis.devices = devices;\n\t\tthis.firmwareVersion = firmwareVersion;\n\t\tthis.preferred = preferred;\n\t\tthis.endpoints = endpoints;\n\t\tthis.associations = associations;\n\t\tthis.scenes = scenes;\n\t\tthis.paramInformation = paramInformation;\n\t\tthis.proprietary = proprietary;\n\t\tthis.compat = compat;\n\t\tthis.metadata = metadata;\n\t}\n\n\tpublic readonly filename: string;\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\tpublic readonly manufacturer: string;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label: string;\n\tpublic readonly description: string;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, EndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<number, AssociationConfig>;\n\tpublic readonly scenes?: ReadonlyMap<number, SceneConfig>;\n\tpublic readonly paramInformation?: ParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?: CompatConfig;\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: DeviceMetadata;\n\n\t/** Returns the association config for a given endpoint */\n\tpublic getAssociationConfigForEndpoint(\n\t\tendpointIndex: number,\n\t\tgroup: number,\n\t): AssociationConfig | undefined {\n\t\tif (endpointIndex === 0) {\n\t\t\t// The root endpoint's associations may be configured separately or as part of \"endpoints\"\n\t\t\treturn (\n\t\t\t\tthis.associations?.get(group)\n\t\t\t\t\t?? this.endpoints?.get(0)?.associations?.get(group)\n\t\t\t);\n\t\t} else {\n\t\t\t// The other endpoints can only have a configuration as part of \"endpoints\"\n\t\t\treturn this.endpoints?.get(endpointIndex)?.associations?.get(group);\n\t\t}\n\t}\n\n\tprivate getHashable(version: DeviceConfigHashVersion): Record<string, any> {\n\t\t// We only need to compare the information that is persisted elsewhere:\n\t\t// - config parameters\n\t\t// - functional association settings\n\t\t// - CC-related compat flags\n\n\t\tlet hashable: Record<string, any> = {\n\t\t\t// endpoints: {\n\t\t\t// \tassociations: {},\n\t\t\t// \tparamInformation: []\n\t\t\t// },\n\t\t\t// proprietary: {},\n\t\t\t// compat: {},\n\t\t};\n\n\t\tconst sortObject = (obj: Record<string, any>) => {\n\t\t\tconst ret: Record<string, any> = {};\n\t\t\tfor (const key of Object.keys(obj).toSorted()) {\n\t\t\t\tret[key] = obj[key];\n\t\t\t}\n\t\t\treturn ret;\n\t\t};\n\n\t\tconst cloneAssociationConfig = (a: AssociationConfig) => {\n\t\t\treturn sortObject(\n\t\t\t\tpick(a, [\"maxNodes\", \"multiChannel\", \"isLifeline\"]),\n\t\t\t);\n\t\t};\n\t\tconst cloneAssociationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ReadonlyMap<number, AssociationConfig> | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\ttarget.associations = {};\n\t\t\tfor (const [key, value] of map) {\n\t\t\t\ttarget.associations[key] = cloneAssociationConfig(value);\n\t\t\t}\n\t\t\ttarget.associations = sortObject(target.associations);\n\t\t};\n\n\t\tconst cloneParamInformationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ParamInfoMap | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\tconst getParamKey = (param: ParamInformation) =>\n\t\t\t\t`${param.parameterNumber}${\n\t\t\t\t\tparam.valueBitMask ? `[${num2hex(param.valueBitMask)}]` : \"\"\n\t\t\t\t}`;\n\t\t\ttarget.paramInformation = [...map.values()]\n\t\t\t\t.toSorted((a, b) =>\n\t\t\t\t\tgetParamKey(a).localeCompare(getParamKey(b))\n\t\t\t\t)\n\t\t\t\t.map((p) => cloneDeep(p));\n\t\t};\n\n\t\t// Clone associations and param information on the root (ep 0) and endpoints\n\t\t{\n\t\t\tlet ep0: Record<string, any> = {};\n\t\t\tcloneAssociationMap(ep0, this.associations);\n\t\t\tcloneParamInformationMap(ep0, this.paramInformation);\n\t\t\tep0 = sortObject(ep0);\n\n\t\t\tif (Object.keys(ep0).length > 0) {\n\t\t\t\thashable.endpoints ??= {};\n\t\t\t\thashable.endpoints[0] = ep0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.endpoints) {\n\t\t\tfor (const [index, endpoint] of this.endpoints) {\n\t\t\t\tlet ep: Record<string, any> = {};\n\n\t\t\t\tcloneAssociationMap(ep, endpoint.associations);\n\t\t\t\tcloneParamInformationMap(ep, endpoint.paramInformation);\n\n\t\t\t\tep = sortObject(ep);\n\n\t\t\t\tif (Object.keys(ep).length > 0) {\n\t\t\t\t\thashable.endpoints ??= {};\n\t\t\t\t\thashable.endpoints[index] = ep;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Clone proprietary config\n\t\tif (this.proprietary && Object.keys(this.proprietary).length > 0) {\n\t\t\thashable.proprietary = sortObject({ ...this.proprietary });\n\t\t}\n\n\t\t// Clone relevant compat flags\n\t\tif (this.compat) {\n\t\t\tlet c: Record<string, any> = {};\n\n\t\t\t// Copy some simple flags over\n\t\t\tfor (\n\t\t\t\tconst prop of [\n\t\t\t\t\t\"forceSceneControllerGroupCount\",\n\t\t\t\t\t\"mapRootReportsToEndpoint\",\n\t\t\t\t\t\"mapBasicSet\",\n\t\t\t\t\t\"preserveRootApplicationCCValueIDs\",\n\t\t\t\t\t\"preserveEndpoints\",\n\t\t\t\t\t\"removeEndpoints\",\n\t\t\t\t\t\"treatMultilevelSwitchSetAsEvent\",\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.compat[prop] != undefined) {\n\t\t\t\t\tc[prop] = this.compat[prop];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Copy other, more complex flags\n\t\t\tif (this.compat.overrideQueries) {\n\t\t\t\tc.overrideQueries = Object.fromEntries(\n\t\t\t\t\tthis.compat.overrideQueries[\"overrides\"],\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.addCCs) {\n\t\t\t\tc.addCCs = Object.fromEntries(\n\t\t\t\t\t[...this.compat.addCCs].map(([ccId, def]) => [\n\t\t\t\t\t\tccId,\n\t\t\t\t\t\tObject.fromEntries(def.endpoints),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.removeCCs) {\n\t\t\t\tc.removeCCs = Object.fromEntries(this.compat.removeCCs);\n\t\t\t}\n\t\t\tif (this.compat.treatSetAsReport) {\n\t\t\t\tc.treatSetAsReport = [...this.compat.treatSetAsReport]\n\t\t\t\t\t.toSorted();\n\t\t\t}\n\n\t\t\tc = sortObject(c);\n\t\t\tif (Object.keys(c).length > 0) {\n\t\t\t\thashable.compat = c;\n\t\t\t}\n\t\t}\n\n\t\tif (version >= 2) {\n\t\t\t// From version 2 and on, we ignore labels and descriptions, and load them dynamically\n\t\t\tfor (\n\t\t\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\t\t\thashable.endpoints ?? {},\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\t\t\tdelete param.label;\n\t\t\t\t\tdelete param.description;\n\t\t\t\t\tfor (const opt of param.options ?? []) {\n\t\t\t\t\t\tdelete opt.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (version < 3) {\n\t\t\t// Version 3 added the `allowed` field. When targeting older versions\n\t\t\t// and the allowed field only has a single range, replace it with\n\t\t\t// minValue/maxValue for compatibility\n\t\t\tfor (\n\t\t\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\t\t\thashable.endpoints ?? {},\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tisArray(param.allowed)\n\t\t\t\t\t\t&& param.allowed.length === 1\n\t\t\t\t\t\t&& isObject(param.allowed[0])\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst allowed = param.allowed[0] as Record<string, any>;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof allowed.from === \"number\"\n\t\t\t\t\t\t\t&& typeof allowed.to === \"number\"\n\t\t\t\t\t\t\t&& (allowed.step == undefined || allowed.step === 1)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tparam.minValue = allowed.from;\n\t\t\t\t\t\t\tparam.maxValue = allowed.to;\n\t\t\t\t\t\t\tdelete param.allowed;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\thashable = sortObject(hashable);\n\t\treturn hashable;\n\t}\n\n\t/**\n\t * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.\n\t */\n\tpublic async getHash(\n\t\tversion: DeviceConfigHashVersion = DeviceConfig.maxHashVersion,\n\t): Promise<BytesView> {\n\t\t// Figure out what to hash\n\t\tconst hashable = this.getHashable(version);\n\n\t\t// And create a \"hash\" from it. Older versions used a non-cryptographic hash,\n\t\t// newer versions compress a subset of the config file.\n\t\tlet hash: BytesView;\n\t\tif (version === 0) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"md5\", buffer);\n\t\t} else if (version === 1) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"sha-256\", buffer);\n\t\t} else {\n\t\t\thash = deflateSync(\n\t\t\t\tBytes.from(JSON.stringify(hashable), \"utf8\"),\n\t\t\t\t// Try to make the hash as small as possible\n\t\t\t\t{ level: 9, dictionary: deflateDict },\n\t\t\t);\n\t\t}\n\n\t\t// Version the hash from v2 onwards, so we can change the format in the future\n\t\tconst prefixBytes = Bytes.from(`$v${version}$`, \"utf8\");\n\t\treturn Bytes.concat([prefixBytes, hash]);\n\t}\n\n\tpublic static get maxHashVersion(): 3 {\n\t\treturn 3;\n\t}\n\n\tpublic static areHashesEqual(hash: BytesView, other: BytesView): boolean {\n\t\tconst parsedHash = parseHash(hash);\n\t\tconst parsedOther = parseHash(other);\n\t\t// If one of the hashes could not be parsed, they are not equal\n\t\tif (!parsedHash || !parsedOther) return false;\n\n\t\t// For legacy hashes, we only compare the hash data. We already make sure during\n\t\t// parsing of the cache files that we only need to compare hashes of the same version,\n\t\t// so simply comparing the contents is sufficient.\n\t\tif (parsedHash.version < 2 && parsedOther.version < 2) {\n\t\t\treturn Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);\n\t\t}\n\t\t// We take care during loading to downlevel the current config hash to legacy versions if needed.\n\t\t// If we end up with just one legacy hash here, something went wrong. Just bail in that case.\n\t\tif (parsedHash.version < 2 || parsedOther.version < 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This is a versioned hash. If both versions are equal, it's simple - just compare the hash data\n\t\tif (parsedHash.version === parsedOther.version) {\n\t\t\treturn Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);\n\t\t}\n\n\t\t// For different versions, we have to do some case by case checks. For example, a newer hash version\n\t\t// might remove or add data into the hashable, so we cannot simply convert between versions easily.\n\t\t// Implement when that is actually needed.\n\t\treturn false;\n\t}\n}\n\nfunction parseHash(hash: BytesView): {\n\tversion: number;\n\thashData: BytesView;\n} | undefined {\n\tconst hashString = Bytes.view(hash).toString(\"utf8\");\n\tconst versionMatch = hashString.match(/^\\$v(\\d+)\\$/);\n\tif (versionMatch) {\n\t\t// This is a versioned hash\n\t\tconst version = parseInt(versionMatch[1], 10);\n\t\tconst hashData = hash.subarray(\n\t\t\t// The prefix is ASCII, so this is safe to do even in the context of UTF-8\n\t\t\tversionMatch[0].length,\n\t\t);\n\t\treturn {\n\t\t\tversion,\n\t\t\thashData,\n\t\t};\n\t}\n\n\t// This is probably an unversioned legacy hash\n\tswitch (hash.length) {\n\t\tcase 16: // MD5\n\t\t\treturn {\n\t\t\t\tversion: 0,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tcase 32: // SHA-256\n\t\t\treturn {\n\t\t\t\tversion: 1,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tdefault:\n\t\t\t// This is not a valid hash\n\t\t\treturn undefined;\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { configDir } from \"#config_dir\";\nimport {\n\tZWaveError,\n\tZWaveErrorCodes,\n\tdeflateSync,\n\tdigest,\n\tinflateSync,\n} from \"@zwave-js/core\";\nimport {\n\tBytes,\n\ttype BytesView,\n\ttype JSONObject,\n\tcloneDeep,\n\tenumFilesRecursive,\n\tformatId,\n\tgetenv,\n\tnum2hex,\n\tpadVersion,\n\tpathExists,\n\tpick,\n\treadTextFile,\n\tstringify,\n\twriteTextFile,\n} from \"@zwave-js/shared\";\nimport type {\n\tReadFile,\n\tReadFileSystemInfo,\n\tWriteFile,\n} from \"@zwave-js/shared/bindings\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport path from \"pathe\";\nimport semverGt from \"semver/functions/gt.js\";\nimport { clearTemplateCache, readJsonWithTemplate } from \"../JsonTemplate.js\";\nimport type { ConfigLogger } from \"../Logger.js\";\nimport { hexKeyRegex4Digits, throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype AssociationConfig,\n\tConditionalAssociationConfig,\n} from \"./AssociationConfig.js\";\nimport { type CompatConfig, ConditionalCompatConfig } from \"./CompatConfig.js\";\nimport { evaluateDeep, validateCondition } from \"./ConditionalItem.js\";\nimport {\n\ttype ConditionalPrimitive,\n\tparseConditionalPrimitive,\n} from \"./ConditionalPrimitive.js\";\nimport {\n\tConditionalDeviceMetadata,\n\ttype DeviceMetadata,\n} from \"./DeviceMetadata.js\";\nimport {\n\tConditionalEndpointConfig,\n\ttype EndpointConfig,\n} from \"./EndpointConfig.js\";\nimport {\n\ttype ConditionalParamInfoMap,\n\ttype ParamInfoMap,\n\ttype ParamInformation,\n\tparseConditionalParamInformationMap,\n} from \"./ParamInformation.js\";\nimport { ConditionalSceneConfig, type SceneConfig } from \"./SceneConfig.js\";\nimport type { DeviceID, FirmwareVersionRange } from \"./shared.js\";\n\nexport interface DeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport interface FulltextDeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tmanufacturer: string;\n\tlabel: string;\n\tdescription: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport const embeddedDevicesDir = path.join(configDir, \"devices\");\nconst fulltextIndexPath = path.join(embeddedDevicesDir, \"fulltext_index.json\");\n\nexport function getDevicesPaths(configDir: string): {\n\tdevicesDir: string;\n\tindexPath: string;\n} {\n\tconst devicesDir = path.join(configDir, \"devices\");\n\tconst indexPath = path.join(devicesDir, \"index.json\");\n\treturn { devicesDir, indexPath };\n}\n\nexport type DeviceConfigIndex = DeviceConfigIndexEntry[];\nexport type FulltextDeviceConfigIndex = FulltextDeviceConfigIndexEntry[];\n\nasync function hasChangedDeviceFiles(\n\tfs: ReadFileSystemInfo,\n\tdevicesRoot: string,\n\tdir: string,\n\tlastChange: Date,\n): Promise<boolean> {\n\t// Check if there are any files BUT index.json that were changed\n\t// or directories that were modified\n\tconst filesAndDirs = await fs.readDir(dir);\n\tfor (const f of filesAndDirs) {\n\t\tconst fullPath = path.join(dir, f);\n\n\t\tconst stat = await fs.stat(fullPath);\n\t\tif (\n\t\t\t(dir !== devicesRoot || f !== \"index.json\")\n\t\t\t&& (stat.isFile() || stat.isDirectory())\n\t\t\t&& stat.mtime > lastChange\n\t\t) {\n\t\t\treturn true;\n\t\t} else if (stat.isDirectory()) {\n\t\t\t// we need to go deeper!\n\t\t\tif (\n\t\t\t\tawait hasChangedDeviceFiles(\n\t\t\t\t\tfs,\n\t\t\t\t\tdevicesRoot,\n\t\t\t\t\tfullPath,\n\t\t\t\t\tlastChange,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Read all device config files from a given directory and return them as index entries.\n * Does not update the index itself.\n */\nasync function generateIndex<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdevicesDir: string,\n\tisEmbedded: boolean,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string; rootDir?: string })[]> {\n\tconst index: (T & { filename: string; rootDir?: string })[] = [];\n\n\tclearTemplateCache();\n\tconst configFiles = await enumFilesRecursive(\n\t\tfs,\n\t\tdevicesDir,\n\t\t(file) =>\n\t\t\tfile.endsWith(\".json\")\n\t\t\t&& !file.endsWith(\"index.json\")\n\t\t\t&& !file.includes(\"/templates/\")\n\t\t\t&& !file.includes(\"\\\\templates\\\\\"),\n\t);\n\n\t// Add the embedded devices dir as a fallback if necessary\n\tconst fallbackDirs = devicesDir !== embeddedDevicesDir\n\t\t? [embeddedDevicesDir]\n\t\t: undefined;\n\n\tfor (const file of configFiles) {\n\t\tconst relativePath = path\n\t\t\t.relative(devicesDir, file)\n\t\t\t.replaceAll(\"\\\\\", \"/\");\n\t\t// Try parsing the file\n\t\ttry {\n\t\t\tconst config = await DeviceConfig.from(\n\t\t\t\tfs,\n\t\t\t\tfile,\n\t\t\t\tisEmbedded,\n\t\t\t\t{\n\t\t\t\t\trootDir: devicesDir,\n\t\t\t\t\tfallbackDirs,\n\t\t\t\t\trelative: true,\n\t\t\t\t},\n\t\t\t);\n\t\t\t// Add the file to the index\n\t\t\tindex.push(\n\t\t\t\t...extractIndexEntries(config).map((entry) => {\n\t\t\t\t\tconst ret: T & { filename: string; rootDir?: string } = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tfilename: relativePath,\n\t\t\t\t\t};\n\t\t\t\t\t// Only add the root dir to the index if necessary\n\t\t\t\t\tif (devicesDir !== embeddedDevicesDir) {\n\t\t\t\t\t\tret.rootDir = devicesDir;\n\t\t\t\t\t}\n\t\t\t\t\treturn ret;\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Error parsing config file ${relativePath}: ${\n\t\t\t\t(e as Error).message\n\t\t\t}`;\n\t\t\t// Crash hard during tests, just print an error when in production systems.\n\t\t\t// A user could have changed a config file\n\t\t\tif (process.env.NODE_ENV === \"test\" || !!getenv(\"CI\")) {\n\t\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Config_Invalid);\n\t\t\t} else {\n\t\t\t\tlogger?.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn index;\n}\n\nasync function loadDeviceIndexShared<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tdevicesDir: string,\n\tindexPath: string,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string })[]> {\n\t// The index file needs to be regenerated if it does not exist\n\tlet needsUpdate = !(await pathExists(fs, indexPath));\n\tlet index: (T & { filename: string })[] | undefined;\n\tlet mtimeIndex: Date | undefined;\n\t// ...or if cannot be parsed\n\tif (!needsUpdate) {\n\t\ttry {\n\t\t\tconst fileContents = await readTextFile(fs, indexPath, \"utf8\");\n\t\t\tindex = JSON5.parse(fileContents);\n\t\t\tmtimeIndex = (await fs.stat(indexPath)).mtime;\n\t\t} catch {\n\t\t\tlogger?.print(\n\t\t\t\t\"Error while parsing index file - regenerating...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tneedsUpdate = true;\n\t\t} finally {\n\t\t\tif (!index) {\n\t\t\t\tlogger?.print(\n\t\t\t\t\t\"Index file was malformed - regenerating...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tneedsUpdate = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ...or if there were any changes in the file system\n\tif (!needsUpdate) {\n\t\tneedsUpdate = await hasChangedDeviceFiles(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\tdevicesDir,\n\t\t\tmtimeIndex!,\n\t\t);\n\t\tif (needsUpdate) {\n\t\t\tlogger?.print(\n\t\t\t\t\"Device configuration files on disk changed - regenerating index...\",\n\t\t\t\t\"verbose\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (needsUpdate) {\n\t\t// Read all files from disk and generate an index\n\t\tindex = await generateIndex(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\ttrue,\n\t\t\textractIndexEntries,\n\t\t\tlogger,\n\t\t);\n\t\t// Save the index to disk\n\t\ttry {\n\t\t\tawait writeTextFile(\n\t\t\t\tfs,\n\t\t\t\tpath.join(indexPath),\n\t\t\t\t`// This file is auto-generated. DO NOT edit it by hand if you don't know what you're doing!\"\n${stringify(index, \"\\t\")}\n`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tlogger?.print(\"Device index regenerated\", \"verbose\");\n\t\t} catch (e) {\n\t\t\tlogger?.print(\n\t\t\t\t`Writing the device index to disk failed: ${\n\t\t\t\t\t(e as Error).message\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn index!;\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function generatePriorityDeviceIndex(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdeviceConfigPriorityDir: string,\n\tlogger?: ConfigLogger,\n): Promise<DeviceConfigIndex> {\n\treturn (\n\t\tawait generateIndex(\n\t\t\tfs,\n\t\t\tdeviceConfigPriorityDir,\n\t\t\tfalse,\n\t\t\t(config) =>\n\t\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\t\tmanufacturerId: formatId(\n\t\t\t\t\t\tconfig.manufacturerId.toString(16),\n\t\t\t\t\t),\n\t\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\t\tlabel: config.label,\n\t\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\t\trootDir: deviceConfigPriorityDir,\n\t\t\t\t})),\n\t\t\tlogger,\n\t\t)\n\t).map(({ filename, ...entry }) => ({\n\t\t...entry,\n\t\t// The generated index makes the filenames relative to the given directory\n\t\t// but we need them to be absolute\n\t\tfilename: path.join(deviceConfigPriorityDir, filename),\n\t}));\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n\texternalConfigDir?: string,\n): Promise<DeviceConfigIndex> {\n\tconst { devicesDir, indexPath } = getDevicesPaths(\n\t\texternalConfigDir || configDir,\n\t);\n\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tdevicesDir,\n\t\tindexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\n/**\n * @internal\n * Loads the full text index file to quickly search the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadFulltextDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n): Promise<FulltextDeviceConfigIndex> {\n\t// This method is not meant to operate with the external device index!\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tembeddedDevicesDir,\n\t\tfulltextIndexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tdescription: config.description,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\trootDir: embeddedDevicesDir,\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\nfunction isHexKeyWith4Digits(val: any): val is string {\n\treturn typeof val === \"string\" && hexKeyRegex4Digits.test(val);\n}\n\nconst firmwareVersionRegex = /^\\d{1,3}\\.\\d{1,3}(\\.\\d{1,3})?$/;\nfunction isFirmwareVersion(val: any): val is string {\n\treturn (\n\t\ttypeof val === \"string\"\n\t\t&& firmwareVersionRegex.test(val)\n\t\t&& val\n\t\t\t.split(\".\")\n\t\t\t.map((str) => parseInt(str, 10))\n\t\t\t.every((num) => num >= 0 && num <= 255)\n\t);\n}\n\nconst deflateDict = Bytes.from(\n\t// Substrings appearing in the device config files in descending order of frequency\n\t// except for very short ones like 0, 1, ...\n\t// WARNING: THIS MUST NOT BE CHANGED! Doing so breaks decompressing stored hashes.\n\t[\n\t\t`\"parameterNumber\":`,\n\t\t`255`,\n\t\t`\"value\":`,\n\t\t`\"defaultValue\":`,\n\t\t`\"valueSize\":`,\n\t\t`\"maxValue\":`,\n\t\t`\"minValue\":`,\n\t\t`\"options\":`,\n\t\t`true`,\n\t\t`false`,\n\t\t`\"allowManualEntry\":`,\n\t\t`\"maxNodes\":`,\n\t\t`100`,\n\t\t`\"unsigned\":`,\n\t\t`\"paramInformation\":`,\n\t\t`\"isLifeline\":`,\n\t\t`\"seconds\"`,\n\t\t`99`,\n\t\t`127`,\n\t\t`\"%\"`,\n\t\t`65535`,\n\t\t`32767`,\n\t\t`\"minutes\"`,\n\t\t`\"endpoints\":`,\n\t\t`\"hours\"`,\n\t\t`\"multiChannel\":`,\n\t]\n\t\t.join(\"\"),\n\t\"utf8\",\n);\n\n/** This class represents a device config entry whose conditional settings have not been evaluated yet */\nexport class ConditionalDeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t},\n\t): Promise<ConditionalDeviceConfig> {\n\t\tconst { relative, rootDir } = options;\n\n\t\tconst relativePath = relative\n\t\t\t? path.relative(rootDir, filename).replaceAll(\"\\\\\", \"/\")\n\t\t\t: filename;\n\t\tconst json = await readJsonWithTemplate(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\t[\n\t\t\t\toptions.rootDir,\n\t\t\t\t...(options.fallbackDirs ?? []),\n\t\t\t],\n\t\t);\n\t\treturn new ConditionalDeviceConfig(relativePath, isEmbedded, json);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\n\t\tif (!isHexKeyWith4Digits(definition.manufacturerId)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nmanufacturer id must be a lowercase hexadecimal number with 4 digits`,\n\t\t\t);\n\t\t}\n\t\tthis.manufacturerId = parseInt(definition.manufacturerId, 16);\n\n\t\tfor (const prop of [\"manufacturer\", \"label\", \"description\"] as const) {\n\t\t\tthis[prop] = parseConditionalPrimitive(\n\t\t\t\tfilename,\n\t\t\t\t\"string\",\n\t\t\t\tprop,\n\t\t\t\tdefinition[prop],\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\t!isArray(definition.devices)\n\t\t\t|| !(definition.devices as any[]).every(\n\t\t\t\t(dev: unknown) =>\n\t\t\t\t\tisObject(dev)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productType)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productId),\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\ndevices is malformed (not an object or type/id that is not a lowercase 4-digit hex key)`,\n\t\t\t);\n\t\t}\n\t\tthis.devices = (definition.devices as any[]).map(\n\t\t\t({ productType, productId }) => ({\n\t\t\t\tproductType: parseInt(productType, 16),\n\t\t\t\tproductId: parseInt(productId, 16),\n\t\t\t}),\n\t\t);\n\n\t\tif (\n\t\t\t!isObject(definition.firmwareVersion)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.min)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.max)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion is malformed or invalid. Must be x.y or x.y.z where x, y, and z are integers between 0 and 255`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst { min, max } = definition.firmwareVersion;\n\t\t\tif (semverGt(padVersion(min), padVersion(max))) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion.min ${min} must not be greater than firmwareVersion.max ${max}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.firmwareVersion = { min, max };\n\t\t}\n\n\t\tif (\n\t\t\tdefinition.preferred != undefined\n\t\t\t&& definition.preferred !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\npreferred must be true or omitted`,\n\t\t\t);\n\t\t}\n\t\tthis.preferred = !!definition.preferred;\n\n\t\tif (definition.endpoints != undefined) {\n\t\t\tconst endpoints = new Map<number, ConditionalEndpointConfig>();\n\t\t\tif (!isObject(definition.endpoints)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nendpoints is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const [key, ep] of Object.entries(definition.endpoints)) {\n\t\t\t\tif (!/^\\d+$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric endpoint index \"${key}\" in endpoints`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst epIndex = parseInt(key, 10);\n\t\t\t\tendpoints.set(\n\t\t\t\t\tepIndex,\n\t\t\t\t\tnew ConditionalEndpointConfig(this, epIndex, ep as any),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.endpoints = endpoints;\n\t\t}\n\n\t\tif (definition.associations != undefined) {\n\t\t\tconst associations = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalAssociationConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.associations)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nassociations is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, assocDefinition] of Object.entries(\n\t\t\t\t\tdefinition.associations,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric group id \"${key}\" in associations`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tassociations.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalAssociationConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tassocDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.associations = associations;\n\t\t}\n\n\t\tif (definition.paramInformation != undefined) {\n\t\t\tthis.paramInformation = parseConditionalParamInformationMap(\n\t\t\t\tdefinition,\n\t\t\t\tthis,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.proprietary != undefined) {\n\t\t\tif (!isObject(definition.proprietary)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nproprietary is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.proprietary = definition.proprietary;\n\t\t}\n\n\t\tif (definition.compat != undefined) {\n\t\t\tif (\n\t\t\t\tisArray(definition.compat)\n\t\t\t\t&& definition.compat.every((item: any) => isObject(item))\n\t\t\t) {\n\t\t\t\t// Make sure all conditions are valid\n\t\t\t\tfor (const entry of definition.compat) {\n\t\t\t\t\tvalidateCondition(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\t`At least one entry of compat contains an`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.compat = definition.compat.map(\n\t\t\t\t\t(item: any) => new ConditionalCompatConfig(filename, item),\n\t\t\t\t);\n\t\t\t} else if (isObject(definition.compat)) {\n\t\t\t\tthis.compat = new ConditionalCompatConfig(\n\t\t\t\t\tfilename,\n\t\t\t\t\tdefinition.compat,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\ncompat must be an object or any array of conditional objects`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (definition.metadata != undefined) {\n\t\t\tif (!isObject(definition.metadata)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nmetadata is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.metadata = new ConditionalDeviceMetadata(\n\t\t\t\tfilename,\n\t\t\t\tdefinition.metadata,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.scenes != undefined) {\n\t\t\tconst scenes = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalSceneConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.scenes)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nscenes is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, sceneDefinition] of Object.entries(\n\t\t\t\t\tdefinition.scenes,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\ninvalid scene id \"${key}\" in scenes - must be a positive integer (1-255)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tif (keyNum < 1 || keyNum > 255) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nscene number ${keyNum} must be between 1 and 255`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tscenes.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalSceneConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tsceneDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.scenes = scenes;\n\t\t}\n\t}\n\n\tpublic readonly filename: string;\n\n\tpublic readonly manufacturer!: ConditionalPrimitive<string>;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label!: ConditionalPrimitive<string>;\n\tpublic readonly description!: ConditionalPrimitive<string>;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, ConditionalEndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<\n\t\tnumber,\n\t\tConditionalAssociationConfig\n\t>;\n\tpublic readonly scenes?: ReadonlyMap<number, ConditionalSceneConfig>;\n\tpublic readonly paramInformation?: ConditionalParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?:\n\t\t| ConditionalCompatConfig\n\t\t| ConditionalCompatConfig[];\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: ConditionalDeviceMetadata;\n\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\n\tpublic evaluate(deviceId?: DeviceID): DeviceConfig {\n\t\treturn new DeviceConfig(\n\t\t\tthis.filename,\n\t\t\tthis.isEmbedded,\n\t\t\tevaluateDeep(this.manufacturer, deviceId),\n\t\t\tthis.manufacturerId,\n\t\t\tevaluateDeep(this.label, deviceId),\n\t\t\tevaluateDeep(this.description, deviceId),\n\t\t\tthis.devices,\n\t\t\tthis.firmwareVersion,\n\t\t\tthis.preferred,\n\t\t\tevaluateDeep(this.endpoints, deviceId),\n\t\t\tevaluateDeep(this.associations, deviceId),\n\t\t\tevaluateDeep(this.scenes, deviceId),\n\t\t\tevaluateDeep(this.paramInformation, deviceId),\n\t\t\tthis.proprietary,\n\t\t\tevaluateDeep(this.compat, deviceId),\n\t\t\tevaluateDeep(this.metadata, deviceId),\n\t\t);\n\t}\n}\n\nexport type DeviceConfigHashVersion = 0 | 1 | 2 | 3 | 4;\n\nexport class DeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t\tdeviceId?: DeviceID;\n\t\t},\n\t): Promise<DeviceConfig> {\n\t\tconst ret = await ConditionalDeviceConfig.from(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\tisEmbedded,\n\t\t\toptions,\n\t\t);\n\t\treturn ret.evaluate(options.deviceId);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tmanufacturer: string,\n\t\tmanufacturerId: number,\n\t\tlabel: string,\n\t\tdescription: string,\n\t\tdevices: readonly {\n\t\t\tproductType: number;\n\t\t\tproductId: number;\n\t\t}[],\n\t\tfirmwareVersion: FirmwareVersionRange,\n\t\tpreferred: boolean,\n\t\tendpoints?: ReadonlyMap<number, EndpointConfig>,\n\t\tassociations?: ReadonlyMap<number, AssociationConfig>,\n\t\tscenes?: ReadonlyMap<number, SceneConfig>,\n\t\tparamInformation?: ParamInfoMap,\n\t\tproprietary?: Record<string, unknown>,\n\t\tcompat?: CompatConfig,\n\t\tmetadata?: DeviceMetadata,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\t\tthis.manufacturer = manufacturer;\n\t\tthis.manufacturerId = manufacturerId;\n\t\tthis.label = label;\n\t\tthis.description = description;\n\t\tthis.devices = devices;\n\t\tthis.firmwareVersion = firmwareVersion;\n\t\tthis.preferred = preferred;\n\t\tthis.endpoints = endpoints;\n\t\tthis.associations = associations;\n\t\tthis.scenes = scenes;\n\t\tthis.paramInformation = paramInformation;\n\t\tthis.proprietary = proprietary;\n\t\tthis.compat = compat;\n\t\tthis.metadata = metadata;\n\t}\n\n\tpublic readonly filename: string;\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\tpublic readonly manufacturer: string;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label: string;\n\tpublic readonly description: string;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, EndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<number, AssociationConfig>;\n\tpublic readonly scenes?: ReadonlyMap<number, SceneConfig>;\n\tpublic readonly paramInformation?: ParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?: CompatConfig;\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: DeviceMetadata;\n\n\t/** Returns the association config for a given endpoint */\n\tpublic getAssociationConfigForEndpoint(\n\t\tendpointIndex: number,\n\t\tgroup: number,\n\t): AssociationConfig | undefined {\n\t\tif (endpointIndex === 0) {\n\t\t\t// The root endpoint's associations may be configured separately or as part of \"endpoints\"\n\t\t\treturn (\n\t\t\t\tthis.associations?.get(group)\n\t\t\t\t\t?? this.endpoints?.get(0)?.associations?.get(group)\n\t\t\t);\n\t\t} else {\n\t\t\t// The other endpoints can only have a configuration as part of \"endpoints\"\n\t\t\treturn this.endpoints?.get(endpointIndex)?.associations?.get(group);\n\t\t}\n\t}\n\n\tprivate getHashable(version: DeviceConfigHashVersion): Record<string, any> {\n\t\t// We only need to compare the information that is persisted elsewhere:\n\t\t// - config parameters\n\t\t// - functional association settings\n\t\t// - CC-related compat flags\n\n\t\tlet hashable: Record<string, any> = {\n\t\t\t// endpoints: {\n\t\t\t// \tassociations: {},\n\t\t\t// \tparamInformation: []\n\t\t\t// },\n\t\t\t// proprietary: {},\n\t\t\t// compat: {},\n\t\t};\n\n\t\tconst sortObject = (obj: Record<string, any>) => {\n\t\t\tconst ret: Record<string, any> = {};\n\t\t\tfor (const key of Object.keys(obj).toSorted()) {\n\t\t\t\tret[key] = obj[key];\n\t\t\t}\n\t\t\treturn ret;\n\t\t};\n\n\t\tconst cloneAssociationConfig = (a: AssociationConfig) => {\n\t\t\treturn sortObject(\n\t\t\t\tpick(a, [\"maxNodes\", \"multiChannel\", \"isLifeline\"]),\n\t\t\t);\n\t\t};\n\t\tconst cloneAssociationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ReadonlyMap<number, AssociationConfig> | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\ttarget.associations = {};\n\t\t\tfor (const [key, value] of map) {\n\t\t\t\ttarget.associations[key] = cloneAssociationConfig(value);\n\t\t\t}\n\t\t\ttarget.associations = sortObject(target.associations);\n\t\t};\n\n\t\tconst cloneParamInformationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ParamInfoMap | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\tconst getParamKey = (param: ParamInformation) =>\n\t\t\t\t`${param.parameterNumber}${\n\t\t\t\t\tparam.valueBitMask ? `[${num2hex(param.valueBitMask)}]` : \"\"\n\t\t\t\t}`;\n\t\t\ttarget.paramInformation = [...map.values()]\n\t\t\t\t.toSorted((a, b) =>\n\t\t\t\t\tgetParamKey(a).localeCompare(getParamKey(b))\n\t\t\t\t)\n\t\t\t\t.map((p) => cloneDeep(p));\n\t\t};\n\n\t\t// Clone associations and param information on the root (ep 0) and endpoints\n\t\t{\n\t\t\tlet ep0: Record<string, any> = {};\n\t\t\tcloneAssociationMap(ep0, this.associations);\n\t\t\tcloneParamInformationMap(ep0, this.paramInformation);\n\t\t\tep0 = sortObject(ep0);\n\n\t\t\tif (Object.keys(ep0).length > 0) {\n\t\t\t\thashable.endpoints ??= {};\n\t\t\t\thashable.endpoints[0] = ep0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.endpoints) {\n\t\t\tfor (const [index, endpoint] of this.endpoints) {\n\t\t\t\tlet ep: Record<string, any> = {};\n\n\t\t\t\tcloneAssociationMap(ep, endpoint.associations);\n\t\t\t\tcloneParamInformationMap(ep, endpoint.paramInformation);\n\n\t\t\t\tep = sortObject(ep);\n\n\t\t\t\tif (Object.keys(ep).length > 0) {\n\t\t\t\t\thashable.endpoints ??= {};\n\t\t\t\t\thashable.endpoints[index] = ep;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Clone proprietary config\n\t\tif (this.proprietary && Object.keys(this.proprietary).length > 0) {\n\t\t\thashable.proprietary = sortObject({ ...this.proprietary });\n\t\t}\n\n\t\t// Clone relevant compat flags\n\t\tif (this.compat) {\n\t\t\tlet c: Record<string, any> = {};\n\n\t\t\t// Copy some simple flags over\n\t\t\tfor (\n\t\t\t\tconst prop of [\n\t\t\t\t\t\"forceSceneControllerGroupCount\",\n\t\t\t\t\t\"mapRootReportsToEndpoint\",\n\t\t\t\t\t\"mapBasicSet\",\n\t\t\t\t\t\"preserveRootApplicationCCValueIDs\",\n\t\t\t\t\t\"preserveEndpoints\",\n\t\t\t\t\t\"removeEndpoints\",\n\t\t\t\t\t\"treatMultilevelSwitchSetAsEvent\",\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.compat[prop] != undefined) {\n\t\t\t\t\tc[prop] = this.compat[prop];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Copy other, more complex flags\n\t\t\tif (this.compat.overrideQueries) {\n\t\t\t\tc.overrideQueries = Object.fromEntries(\n\t\t\t\t\tthis.compat.overrideQueries[\"overrides\"],\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.addCCs) {\n\t\t\t\tc.addCCs = Object.fromEntries(\n\t\t\t\t\t[...this.compat.addCCs].map(([ccId, def]) => [\n\t\t\t\t\t\tccId,\n\t\t\t\t\t\tObject.fromEntries(def.endpoints),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.removeCCs) {\n\t\t\t\tc.removeCCs = Object.fromEntries(this.compat.removeCCs);\n\t\t\t}\n\t\t\tif (this.compat.treatSetAsReport) {\n\t\t\t\tc.treatSetAsReport = [...this.compat.treatSetAsReport]\n\t\t\t\t\t.toSorted();\n\t\t\t}\n\n\t\t\tc = sortObject(c);\n\t\t\tif (Object.keys(c).length > 0) {\n\t\t\t\thashable.compat = c;\n\t\t\t}\n\t\t}\n\n\t\tif (version >= 2) {\n\t\t\t// From version 2 and on, we ignore labels, descriptions and $purpose, and load them dynamically\n\t\t\tfor (\n\t\t\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\t\t\thashable.endpoints ?? {},\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\t\t\tdelete param.label;\n\t\t\t\t\tdelete param.description;\n\t\t\t\t\tdelete param.$purpose; // added later, but the point still stands\n\t\t\t\t\tfor (const opt of param.options ?? []) {\n\t\t\t\t\t\tdelete opt.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (version < 3) {\n\t\t\t// Version 3 added the `allowed` field. When targeting older versions\n\t\t\t// and the allowed field only has a single range, replace it with\n\t\t\t// minValue/maxValue for compatibility\n\t\t\tfor (\n\t\t\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\t\t\thashable.endpoints ?? {},\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tisArray(param.allowed)\n\t\t\t\t\t\t&& param.allowed.length === 1\n\t\t\t\t\t\t&& isObject(param.allowed[0])\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst allowed = param.allowed[0] as Record<string, any>;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof allowed.from === \"number\"\n\t\t\t\t\t\t\t&& typeof allowed.to === \"number\"\n\t\t\t\t\t\t\t&& (allowed.step == undefined || allowed.step === 1)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tparam.minValue = allowed.from;\n\t\t\t\t\t\t\tparam.maxValue = allowed.to;\n\t\t\t\t\t\t\tdelete param.allowed;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (version >= 4) {\n\t\t\t// From version 4 and on, all param information is applied dynamically\n\t\t\t// so it does not need to be part of the hash anymore.\n\t\t\tif (hashable.endpoints) {\n\t\t\t\tfor (\n\t\t\t\t\tconst [key, ep] of Object.entries<Record<string, any>>(\n\t\t\t\t\t\thashable.endpoints,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tdelete ep.paramInformation;\n\t\t\t\t\tif (Object.keys(ep).length === 0) {\n\t\t\t\t\t\tdelete hashable.endpoints[key];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Object.keys(hashable.endpoints).length === 0) {\n\t\t\t\t\tdelete hashable.endpoints;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\thashable = sortObject(hashable);\n\t\treturn hashable;\n\t}\n\n\t/**\n\t * Returns a hash code that can be used to check whether a device config\n\t * has changed enough to require a re-interview.\n\t */\n\tpublic async getHash(\n\t\tversion: DeviceConfigHashVersion = DeviceConfig.maxHashVersion,\n\t): Promise<BytesView> {\n\t\t// As of hash version 4, this includes:\n\t\t// - Association settings (maxNodes, isLifeline, multiChannel)\n\t\t// - Compat flags (addCCs, removeCCs, mapBasicSet, etc.)\n\t\t// - Proprietary config\n\t\t//\n\t\t// Configuration parameter metadata is NOT included, as changes\n\t\t// to it are applied dynamically without requiring re-interview.\n\n\t\t// Figure out what to hash\n\t\tconst hashable = this.getHashable(version);\n\n\t\t// And create a \"hash\" from it. Older versions used a non-cryptographic hash,\n\t\t// newer versions compress a subset of the config file.\n\t\tlet hash: BytesView;\n\t\tif (version === 0) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"md5\", buffer);\n\t\t} else if (version === 1) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"sha-256\", buffer);\n\t\t} else {\n\t\t\thash = deflateSync(\n\t\t\t\tBytes.from(JSON.stringify(hashable), \"utf8\"),\n\t\t\t\t// Try to make the hash as small as possible\n\t\t\t\t{ level: 9, dictionary: deflateDict },\n\t\t\t);\n\t\t}\n\n\t\t// Version the hash from v2 onwards, so we can change the format in the future\n\t\tconst prefixBytes = Bytes.from(`$v${version}$`, \"utf8\");\n\t\treturn Bytes.concat([prefixBytes, hash]);\n\t}\n\n\tpublic static get maxHashVersion(): 4 {\n\t\treturn 4;\n\t}\n\n\t/**\n\t * Compares a cached device config hash against the current one.\n\t * @param currentHash must be at the latest hash version\n\t * @param cachedHash may be at any version and will be normalized for comparison\n\t */\n\tpublic static areHashesEqual(\n\t\tcurrentHash: BytesView,\n\t\tcachedHash: BytesView,\n\t): boolean {\n\t\tconst current = parseHash(currentHash);\n\t\tconst cached = parseHash(cachedHash);\n\t\t// If one of the hashes could not be parsed, they are not equal\n\t\tif (!current || !cached) return false;\n\n\t\t// For legacy hashes, we only compare the hash data. We already make sure during\n\t\t// parsing of the cache files that we only need to compare hashes of the same version,\n\t\t// so simply comparing the contents is sufficient.\n\t\tif (current.version < 2 && cached.version < 2) {\n\t\t\treturn Bytes.view(current.hashData).equals(cached.hashData);\n\t\t}\n\t\t// We take care during loading to downlevel the current config hash to legacy versions if needed.\n\t\t// If we end up with just one legacy hash here, something went wrong. Just bail in that case.\n\t\tif (current.version < 2 || cached.version < 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// The cached hash may need normalization (v2 fix, version upgrade)\n\t\tlet hashable: Record<string, any>;\n\t\ttry {\n\t\t\thashable = JSON.parse(\n\t\t\t\tBytes.view(inflateSync(\n\t\t\t\t\tBytes.view(cached.hashData),\n\t\t\t\t\t{ dictionary: deflateDict },\n\t\t\t\t)).toString(\"utf8\"),\n\t\t\t);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Some Z-Wave JS versions had an issue where the optional \"hidden\"\n\t\t// property was included in the hashable, causing incorrect hashes.\n\t\tfixBrokenV2Hashable(hashable, cached.version);\n\t\t// If the cached hash has an older version, we need to upgrade it to\n\t\t// the current version. This is necessary because some of the version upgrades\n\t\t// remove the need for some information to be included in the hash, so\n\t\t// a newer version gets by with less information than an older version.\n\t\tupgradeHashable(hashable, cached.version, current.version);\n\n\t\t// Recompress and compare against the current hash\n\t\tconst normalizedData = deflateSync(\n\t\t\tBytes.from(JSON.stringify(hashable), \"utf8\"),\n\t\t\t{ level: 9, dictionary: deflateDict },\n\t\t);\n\t\treturn Bytes.view(normalizedData).equals(current.hashData);\n\t}\n}\n\nfunction parseHash(hash: BytesView): {\n\tversion: number;\n\thashData: BytesView;\n} | undefined {\n\tconst hashString = Bytes.view(hash).toString(\"utf8\");\n\tconst versionMatch = hashString.match(/^\\$v(\\d+)\\$/);\n\tif (versionMatch) {\n\t\t// This is a versioned hash\n\t\tconst version = parseInt(versionMatch[1], 10);\n\t\tconst hashData = hash.subarray(\n\t\t\t// The prefix is ASCII, so this is safe to do even in the context of UTF-8\n\t\t\tversionMatch[0].length,\n\t\t);\n\t\treturn {\n\t\t\tversion,\n\t\t\thashData,\n\t\t};\n\t}\n\n\t// This is probably an unversioned legacy hash\n\tswitch (hash.length) {\n\t\tcase 16: // MD5\n\t\t\treturn {\n\t\t\t\tversion: 0,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tcase 32: // SHA-256\n\t\t\treturn {\n\t\t\t\tversion: 1,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tdefault:\n\t\t\t// This is not a valid hash\n\t\t\treturn undefined;\n\t}\n}\n\nexport { parseHash as parseDeviceConfigHash };\n\n/**\n * Fixes broken v2 hashables that incorrectly included `hidden: false`.\n * Mutates the hashable in place.\n */\nfunction fixBrokenV2Hashable(\n\thashable: Record<string, any>,\n\tversion: number,\n): void {\n\tif (version !== 2) return;\n\n\tfor (\n\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\thashable.endpoints ?? {},\n\t\t)\n\t) {\n\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\tif (param.hidden === false) {\n\t\t\t\tdelete param.hidden;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Upgrades a hashable to a newer version by stripping fields that are\n * now applied dynamically. Mutates the hashable in place.\n */\nfunction upgradeHashable(\n\thashable: Record<string, any>,\n\tfromVersion: number,\n\ttargetVersion: number,\n): void {\n\tif (fromVersion >= targetVersion) return;\n\n\tif (targetVersion >= 4 && fromVersion < 4 && hashable.endpoints) {\n\t\tfor (\n\t\t\tconst [key, ep] of Object.entries<Record<string, any>>(\n\t\t\t\thashable.endpoints,\n\t\t\t)\n\t\t) {\n\t\t\tdelete ep.paramInformation;\n\t\t\tif (Object.keys(ep).length === 0) {\n\t\t\t\tdelete hashable.endpoints[key];\n\t\t\t}\n\t\t}\n\t\tif (Object.keys(hashable.endpoints).length === 0) {\n\t\t\tdelete hashable.endpoints;\n\t\t}\n\t\tfromVersion = 4;\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "configDir", "JSON5", "semverGt"]
7
7
  }
@@ -1,4 +1,4 @@
1
- import { type AllowedConfigValue } from "@zwave-js/core";
1
+ import { type AllowedValue } from "@zwave-js/core";
2
2
  import { type JSONObject, type ReadonlyObjectKeyMap } from "@zwave-js/shared";
3
3
  import { type ConditionalItem } from "./ConditionalItem.js";
4
4
  import type { ConditionalDeviceConfig } from "./DeviceConfig.js";
@@ -11,7 +11,7 @@ export declare class ConditionalParamInformation implements ConditionalItem<Para
11
11
  readonly label: string;
12
12
  readonly description?: string;
13
13
  readonly valueSize: number;
14
- readonly allowed?: readonly AllowedConfigValue[];
14
+ readonly allowed?: readonly AllowedValue[];
15
15
  readonly minValue?: number;
16
16
  readonly maxValue?: number;
17
17
  readonly unsigned?: boolean;
@@ -24,6 +24,7 @@ export declare class ConditionalParamInformation implements ConditionalItem<Para
24
24
  readonly destructive?: boolean;
25
25
  readonly options: readonly ConditionalConfigOption[];
26
26
  readonly hidden?: boolean;
27
+ readonly purpose?: string;
27
28
  readonly condition?: string;
28
29
  evaluateCondition(deviceId?: DeviceID): ParamInformation | undefined;
29
30
  }
@@ -118,6 +118,11 @@ Parameter #${parameterNumber}: options is malformed!`);
118
118
  Parameter #${parameterNumber} has a non-boolean property hidden`);
119
119
  }
120
120
  this.hidden = definition.hidden;
121
+ if (definition["$purpose"] != void 0 && typeof definition["$purpose"] !== "string") {
122
+ (0, import_utils_safe.throwInvalidConfig)("devices", `packages/config/config/devices/${parent.filename}:
123
+ Parameter #${parameterNumber} has a non-string property $purpose`);
124
+ }
125
+ this.purpose = definition["$purpose"];
121
126
  if (definition.allowed != void 0) {
122
127
  if (this.minValue != void 0 || this.maxValue != void 0) {
123
128
  (0, import_utils_safe.throwInvalidConfig)("devices", `packages/config/config/devices/${parent.filename}:
@@ -210,6 +215,7 @@ Parameter #${parameterNumber}: allowed[${i}] must have either "value" or "range"
210
215
  destructive;
211
216
  options;
212
217
  hidden;
218
+ purpose;
213
219
  condition;
214
220
  evaluateCondition(deviceId) {
215
221
  if (!(0, import_ConditionalItem.conditionApplies)(this, deviceId))
@@ -232,7 +238,8 @@ Parameter #${parameterNumber}: allowed[${i}] must have either "value" or "range"
232
238
  "writeOnly",
233
239
  "allowManualEntry",
234
240
  "destructive",
235
- "hidden"
241
+ "hidden",
242
+ "purpose"
236
243
  ]),
237
244
  options: (0, import_ConditionalItem.evaluateDeep)(this.options, deviceId, true)
238
245
  };