homematic-manager 3.0.0-beta.2 → 3.0.0-beta.21

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 (332) hide show
  1. package/README.md +2 -1
  2. package/data/easymode-time-selectors.json +285 -0
  3. package/data/icons/106_hm-lc-sw1-dr.webp +0 -0
  4. package/data/icons/108_hm-rc-dis-h-x-eu.webp +0 -0
  5. package/data/icons/109_hm-lc-sw1-pl-ct.webp +0 -0
  6. package/data/icons/110_hm-es-pmsw1-dr.webp +0 -0
  7. package/data/icons/112_hmip-wrc2.webp +0 -0
  8. package/data/icons/113_hmip-psm-ch.webp +0 -0
  9. package/data/icons/113_hmip-psm-it.webp +0 -0
  10. package/data/icons/113_hmip-psm-pe.webp +0 -0
  11. package/data/icons/113_hmip-psm-uk.webp +0 -0
  12. package/data/icons/113_hmip-psm.webp +0 -0
  13. package/data/icons/114_hm-lc-dim1t-fm-lf_3.webp +0 -0
  14. package/data/icons/116_hm-rc-4-3_brc-h3.webp +0 -0
  15. package/data/icons/118_hmip-swdo.webp +0 -0
  16. package/data/icons/119_hmip-rc8.webp +0 -0
  17. package/data/icons/120_hmip-etrv.webp +0 -0
  18. package/data/icons/121_hmip-wth.webp +0 -0
  19. package/data/icons/123_oligo.smart.ip.hm.webp +0 -0
  20. package/data/icons/125_hmip-smi.webp +0 -0
  21. package/data/icons/130_hmip-srh.webp +0 -0
  22. package/data/icons/131_hmip-wrc6.webp +0 -0
  23. package/data/icons/132_hmip-smo.webp +0 -0
  24. package/data/icons/133_hmip-asir.webp +0 -0
  25. package/data/icons/134_hmip-fsm.webp +0 -0
  26. package/data/icons/135_hmip-fsm16.webp +0 -0
  27. package/data/icons/136_hmip-miob.webp +0 -0
  28. package/data/icons/137_hmip-fal-c6.webp +0 -0
  29. package/data/icons/138_hmip-fal-c10.webp +0 -0
  30. package/data/icons/140_alpha-ip-rgb.webp +0 -0
  31. package/data/icons/141_alpha-ip-rgba.webp +0 -0
  32. package/data/icons/144_hmip-wgc.webp +0 -0
  33. package/data/icons/145_hmip-froll_hmip-fbl.webp +0 -0
  34. package/data/icons/146_hmip-sth.webp +0 -0
  35. package/data/icons/147_hmip-sthd.webp +0 -0
  36. package/data/icons/148_hmip-stho.webp +0 -0
  37. package/data/icons/149_hmip-sam.webp +0 -0
  38. package/data/icons/151_hmip-pcbs-bat.webp +0 -0
  39. package/data/icons/152_hmip-swdo-i.webp +0 -0
  40. package/data/icons/153_hmip-spi.webp +0 -0
  41. package/data/icons/154_hmip-spdr.webp +0 -0
  42. package/data/icons/155_hmip-whs2.webp +0 -0
  43. package/data/icons/156_hmip-mod-oc8.webp +0 -0
  44. package/data/icons/158_hmip-etrv-uk.webp +0 -0
  45. package/data/icons/159_hmip-mod-rc8.webp +0 -0
  46. package/data/icons/160_hmipw-drs4.webp +0 -0
  47. package/data/icons/161_hmipw-drs8.webp +0 -0
  48. package/data/icons/162_hmipw-drap.webp +0 -0
  49. package/data/icons/163_hmipw-drbl4.webp +0 -0
  50. package/data/icons/164_hmipw-dri16.webp +0 -0
  51. package/data/icons/165_hmipw-fio6.webp +0 -0
  52. package/data/icons/166_hmipw-drd3.webp +0 -0
  53. package/data/icons/167_hmipw-dri32.webp +0 -0
  54. package/data/icons/168_hmip-smi55.webp +0 -0
  55. package/data/icons/169_hmip-swo-pr.webp +0 -0
  56. package/data/icons/170_hmip-swo-pl.webp +0 -0
  57. package/data/icons/171_hmip-swo-b.webp +0 -0
  58. package/data/icons/172_hmip-swd.webp +0 -0
  59. package/data/icons/173_hmip-bsl.webp +0 -0
  60. package/data/icons/174_hmip-slo.webp +0 -0
  61. package/data/icons/175_hmip-group-heating.webp +0 -0
  62. package/data/icons/177_hmip-dbb.webp +0 -0
  63. package/data/icons/178_hmip-etrv-b1.webp +0 -0
  64. package/data/icons/179_hmip-swdm-b2.webp +0 -0
  65. package/data/icons/180_hmip-etrv-b.webp +0 -0
  66. package/data/icons/181_hmip-swdm.webp +0 -0
  67. package/data/icons/182_hmip-fci1.webp +0 -0
  68. package/data/icons/183_hmip-mod-tm.webp +0 -0
  69. package/data/icons/184_hmip-pcbs2.webp +0 -0
  70. package/data/icons/185_hmip-FCI6.webp +0 -0
  71. package/data/icons/186_hmip-mp3p.webp +0 -0
  72. package/data/icons/187_hmip-rcb1.webp +0 -0
  73. package/data/icons/188_hmip-etrv-c.webp +0 -0
  74. package/data/icons/190_hmip-sci.webp +0 -0
  75. package/data/icons/191_hmip-mod-ho.webp +0 -0
  76. package/data/icons/192_hmip-asir-o.webp +0 -0
  77. package/data/icons/193_hmip-wt.webp +0 -0
  78. package/data/icons/194_hmip-wrcd.webp +0 -0
  79. package/data/icons/195_hmipw-smi55.webp +0 -0
  80. package/data/icons/196_hmip-ASIR-2.webp +0 -0
  81. package/data/icons/197_hmip-swdo-pl.webp +0 -0
  82. package/data/icons/198_hmip-falmot-c12.webp +0 -0
  83. package/data/icons/199_hmip-mio16-pcb.webp +0 -0
  84. package/data/icons/200_hmip-wth-b.webp +0 -0
  85. package/data/icons/201_hmip-stv.webp +0 -0
  86. package/data/icons/202_hmip-wrcr.webp +0 -0
  87. package/data/icons/203_hmip-fsi16.webp +0 -0
  88. package/data/icons/204_hmip-drdi3.webp +0 -0
  89. package/data/icons/205_hmip-drsi4.webp +0 -0
  90. package/data/icons/206_hmip-drbli4.webp +0 -0
  91. package/data/icons/207_hmip-srdo.webp +0 -0
  92. package/data/icons/208_hmip-dsd-pcb.webp +0 -0
  93. package/data/icons/209_hmip-etrv-b-uk.webp +0 -0
  94. package/data/icons/210_hmip-ste2-pcb.webp +0 -0
  95. package/data/icons/211_hmip-drg-dali.webp +0 -0
  96. package/data/icons/211_hmip-drsi1.webp +0 -0
  97. package/data/icons/212_hmip-sfd.webp +0 -0
  98. package/data/icons/212_hmipw-scthd.webp +0 -0
  99. package/data/icons/213_hmip-smo230.webp +0 -0
  100. package/data/icons/213_hmip-wua.webp +0 -0
  101. package/data/icons/214_hmip-dld.webp +0 -0
  102. package/data/icons/215_hmip-psm-pe-2.webp +0 -0
  103. package/data/icons/215_hmip-scth230.webp +0 -0
  104. package/data/icons/216_hmip-etrv-e.webp +0 -0
  105. package/data/icons/216_hmip-psm-ch-2.webp +0 -0
  106. package/data/icons/217_hmip-usbsm.webp +0 -0
  107. package/data/icons/218_hmip-dls.webp +0 -0
  108. package/data/icons/219_hmip-fwi.webp +0 -0
  109. package/data/icons/220_hmip-wsc.webp +0 -0
  110. package/data/icons/221_hmip-sti.webp +0 -0
  111. package/data/icons/221_hmip-wkp.webp +0 -0
  112. package/data/icons/222_hmipw-wgd.webp +0 -0
  113. package/data/icons/223_hmip-mod-wd-vk.webp +0 -0
  114. package/data/icons/224_hmip-etrv-cl.webp +0 -0
  115. package/data/icons/225_hmip-rgbw.webp +0 -0
  116. package/data/icons/226_hmip-esi.webp +0 -0
  117. package/data/icons/227_hmip-lsc.webp +0 -0
  118. package/data/icons/228_elv-sh-sw1-bat.webp +0 -0
  119. package/data/icons/229_elv-sh-bm-s.webp +0 -0
  120. package/data/icons/230_hmip-etrv-f.webp +0 -0
  121. package/data/icons/231_elv-sh-cth.webp +0 -0
  122. package/data/icons/232_elv-sh-taco.webp +0 -0
  123. package/data/icons/233_hmip-flc.webp +0 -0
  124. package/data/icons/234_hmip-etrv-3.webp +0 -0
  125. package/data/icons/235_hmip-psmco.webp +0 -0
  126. package/data/icons/236_elv-sh-sb8.webp +0 -0
  127. package/data/icons/237_elv-sh-ctv.webp +0 -0
  128. package/data/icons/238_elv-sh-cap.webp +0 -0
  129. package/data/icons/239_hmip-wgs-f.webp +0 -0
  130. package/data/icons/240_hmip-wgtc-f.webp +0 -0
  131. package/data/icons/241_hmip-wgt-f.webp +0 -0
  132. package/data/icons/242_elv-sh-smsi.webp +0 -0
  133. package/data/icons/243_elv-sh-crc.webp +0 -0
  134. package/data/icons/244_elv-sh-krco.webp +0 -0
  135. package/data/icons/245_hmip-falmot-c8.webp +0 -0
  136. package/data/icons/246_elv-sh-wsm.webp +0 -0
  137. package/data/icons/247_hmip-hap-2.webp +0 -0
  138. package/data/icons/248_hmip-dlp.webp +0 -0
  139. package/data/icons/249_elv-sh-psmci.webp +0 -0
  140. package/data/icons/250_elv-sh-sps25.webp +0 -0
  141. package/data/icons/252_elv-sh-pti2.webp +0 -0
  142. package/data/icons/253_hmip-udi-smi55.webp +0 -0
  143. package/data/icons/254_hmip-udi-pb2.webp +0 -0
  144. package/data/icons/256_ELV-SH-SMS2.webp +0 -0
  145. package/data/icons/257_ELV-SH-FSI.webp +0 -0
  146. package/data/icons/258_hmip-m-td15.webp +0 -0
  147. package/data/icons/259_elv-sh-dusi.webp +0 -0
  148. package/data/icons/260_hmip-wss.webp +0 -0
  149. package/data/icons/54a_lc-ddc1-pcb.webp +0 -0
  150. package/data/icons/72_hm-rc-brc-h.webp +0 -0
  151. package/data/icons/73_hm-atent.webp +0 -0
  152. package/data/icons/79_hm-lc-dim1pwm-cv.webp +0 -0
  153. package/data/icons/84_hm-rc-4-3.webp +0 -0
  154. package/data/icons/85_hm-rc-sec4-3.webp +0 -0
  155. package/data/icons/86_hm-rc-key4-3.webp +0 -0
  156. package/data/icons/CCU3-1-50.webp +0 -0
  157. package/data/icons/CCU3.webp +0 -0
  158. package/data/icons/hm-coupling-dim.webp +0 -0
  159. package/data/icons/hm-coupling-group.webp +0 -0
  160. package/data/icons/hm-coupling-onoff.webp +0 -0
  161. package/data/icons/hm-coupling-rgb-dim.webp +0 -0
  162. package/data/icons/hm-coupling-rgb.webp +0 -0
  163. package/data/icons/hm-coupling-rgbw-dim.webp +0 -0
  164. package/data/icons/hm-coupling-rgbw.webp +0 -0
  165. package/data/icons/hm-coupling-white-dim.webp +0 -0
  166. package/data/icons/hm-coupling-white.webp +0 -0
  167. package/data/icons/hm-hue_gateway.webp +0 -0
  168. package/data/icons/hm-lightify_gateway.webp +0 -0
  169. package/data/icons/hm_resc-win-pcb-sc.webp +0 -0
  170. package/data/icons/unknown_device.webp +0 -0
  171. package/data/manifest.json +7 -1
  172. package/data/master-metadata.json +1997 -33
  173. package/data/profiles/ACCESS_RECEIVER.json +26 -13
  174. package/data/profiles/ACOUSTIC_SIGNAL_VIRTUAL_RECEIVER.json +3270 -76
  175. package/data/profiles/ACTOR_SECURITY.json +90 -10
  176. package/data/profiles/ACTOR_WINDOW.json +580 -43
  177. package/data/profiles/ALARMACTUATOR.json +564 -37
  178. package/data/profiles/ALARM_COND_SWITCH_RECEIVER.json +36 -4
  179. package/data/profiles/ALARM_SWITCH_VIRTUAL_RECEIVER.json +735 -33
  180. package/data/profiles/ARMING.json +115 -5
  181. package/data/profiles/BLIND.json +1038 -49
  182. package/data/profiles/BLIND_VIRTUAL_RECEIVER.json +4624 -124
  183. package/data/profiles/CLIMATECONTROL_FLOOR_PUMP_TRANSCEIVER.json +27 -1
  184. package/data/profiles/DIMMER.json +2233 -71
  185. package/data/profiles/DIMMER_VIRTUAL_RECEIVER.json +5773 -102
  186. package/data/profiles/DIMMER_woLongKeyPress.json +117 -4
  187. package/data/profiles/DOOR_RECEIVER.json +138 -10
  188. package/data/profiles/DUAL_WHITE_BRIGHTNESS.json +305 -11
  189. package/data/profiles/DUAL_WHITE_COLOR.json +730 -23
  190. package/data/profiles/HEATING_KEY_RECEIVER.json +127 -10
  191. package/data/profiles/HMW_BLIND.json +555 -20
  192. package/data/profiles/HMW_DIMMER.json +1337 -47
  193. package/data/profiles/HMW_INPUT_OUTPUT.json +375 -20
  194. package/data/profiles/HMW_SWITCH.json +375 -20
  195. package/data/profiles/JALOUSIE.json +1320 -52
  196. package/data/profiles/KEYMATIC.json +120 -12
  197. package/data/profiles/REMOTECONTROL_RECEIVER.json +10 -2
  198. package/data/profiles/RGBW_AUTOMATIC.json +46 -6
  199. package/data/profiles/RGBW_COLOR.json +14 -4
  200. package/data/profiles/SERVO_VIRTUAL_RECEIVER.json +117 -3
  201. package/data/profiles/SHUTTER_VIRTUAL_RECEIVER.json +4263 -128
  202. package/data/profiles/SIGNAL_CHIME.json +195 -14
  203. package/data/profiles/SIGNAL_CHIMEM.json +195 -14
  204. package/data/profiles/SIGNAL_LED.json +195 -14
  205. package/data/profiles/SIGNAL_LEDM.json +195 -14
  206. package/data/profiles/SIMPLE_SWITCH_RECEIVER.json +113 -9
  207. package/data/profiles/STATUS_INDICATOR.json +1073 -45
  208. package/data/profiles/SWITCH.json +1412 -60
  209. package/data/profiles/SWITCH_PANIC.json +315 -13
  210. package/data/profiles/SWITCH_SENSOR.json +243 -11
  211. package/data/profiles/SWITCH_VIRTUAL_RECEIVER.json +3963 -131
  212. package/data/profiles/UNIVERSAL_ACTOR.json +4319 -90
  213. package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_LSC.json +6094 -196
  214. package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_PWM.json +4920 -101
  215. package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_RGB(W).json +2353 -80
  216. package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_RGBW_DALI.json +6116 -176
  217. package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_TW.json +3844 -104
  218. package/data/profiles/VIRTUAL_DIMMER.json +305 -11
  219. package/data/profiles/VIRTUAL_DUAL_WHITE_COLOR.json +730 -23
  220. package/data/profiles/WATER_SWITCH_VIRTUAL_RECEIVER.json +3903 -128
  221. package/data/profiles/WINDOW_DRIVE_RECEIVER.json +220 -8
  222. package/data/profiles/WINMATIC.json +505 -23
  223. package/dist/cli.js +24 -0
  224. package/dist/occulite.d.ts +86 -0
  225. package/dist/occulite.js +140 -0
  226. package/dist/options.d.ts +37 -6
  227. package/dist/options.js +49 -6
  228. package/dist/server.d.ts +47 -4
  229. package/dist/server.js +237 -27
  230. package/dist/sessions.d.ts +9 -1
  231. package/dist/sessions.js +8 -2
  232. package/dist/testSupport.js +9 -2
  233. package/node_modules/@homematic-manager/backend/dist/api/backend.d.ts +33 -0
  234. package/node_modules/@homematic-manager/backend/dist/api/backend.js +558 -46
  235. package/node_modules/@homematic-manager/backend/dist/cache/names.d.ts +9 -0
  236. package/node_modules/@homematic-manager/backend/dist/cache/names.js +21 -0
  237. package/node_modules/@homematic-manager/backend/dist/config/defaults.js +10 -1
  238. package/node_modules/@homematic-manager/backend/dist/config/store.d.ts +28 -1
  239. package/node_modules/@homematic-manager/backend/dist/config/store.js +59 -3
  240. package/node_modules/@homematic-manager/backend/dist/devices/installMode.js +9 -0
  241. package/node_modules/@homematic-manager/backend/dist/errors.d.ts +18 -0
  242. package/node_modules/@homematic-manager/backend/dist/errors.js +51 -2
  243. package/node_modules/@homematic-manager/backend/dist/images/deviceImages.d.ts +6 -0
  244. package/node_modules/@homematic-manager/backend/dist/images/deviceImages.js +38 -2
  245. package/node_modules/@homematic-manager/backend/dist/index.d.ts +3 -1
  246. package/node_modules/@homematic-manager/backend/dist/index.js +4 -1
  247. package/node_modules/@homematic-manager/backend/dist/interfaces/manager.d.ts +70 -8
  248. package/node_modules/@homematic-manager/backend/dist/interfaces/manager.js +333 -44
  249. package/node_modules/@homematic-manager/backend/dist/meta/client.d.ts +120 -0
  250. package/node_modules/@homematic-manager/backend/dist/meta/client.js +307 -0
  251. package/node_modules/@homematic-manager/backend/dist/meta/credentials.d.ts +38 -0
  252. package/node_modules/@homematic-manager/backend/dist/meta/credentials.js +53 -0
  253. package/node_modules/@homematic-manager/backend/dist/meta/index.d.ts +12 -0
  254. package/node_modules/@homematic-manager/backend/dist/meta/index.js +12 -0
  255. package/node_modules/@homematic-manager/backend/dist/meta/localProvider.d.ts +47 -0
  256. package/node_modules/@homematic-manager/backend/dist/meta/localProvider.js +119 -0
  257. package/node_modules/@homematic-manager/backend/dist/meta/occuliteProvider.d.ts +79 -0
  258. package/node_modules/@homematic-manager/backend/dist/meta/occuliteProvider.js +305 -0
  259. package/node_modules/@homematic-manager/backend/dist/meta/provider.d.ts +68 -0
  260. package/node_modules/@homematic-manager/backend/dist/meta/provider.js +19 -0
  261. package/node_modules/@homematic-manager/backend/dist/meta/regaProvider.d.ts +84 -0
  262. package/node_modules/@homematic-manager/backend/dist/meta/regaProvider.js +403 -0
  263. package/node_modules/@homematic-manager/backend/dist/meta/service.d.ts +137 -0
  264. package/node_modules/@homematic-manager/backend/dist/meta/service.js +412 -0
  265. package/node_modules/@homematic-manager/backend/dist/rega/client.d.ts +11 -0
  266. package/node_modules/@homematic-manager/backend/dist/rega/client.js +13 -0
  267. package/node_modules/@homematic-manager/backend/dist/rega/scripts.d.ts +51 -0
  268. package/node_modules/@homematic-manager/backend/dist/rega/scripts.js +171 -0
  269. package/node_modules/@homematic-manager/backend/dist/rpc/client.d.ts +18 -5
  270. package/node_modules/@homematic-manager/backend/dist/rpc/client.js +11 -5
  271. package/node_modules/@homematic-manager/backend/dist/rpc/log.d.ts +86 -0
  272. package/node_modules/@homematic-manager/backend/dist/rpc/log.js +241 -0
  273. package/node_modules/@homematic-manager/backend/dist/rpc/origin.d.ts +30 -0
  274. package/node_modules/@homematic-manager/backend/dist/rpc/origin.js +35 -0
  275. package/node_modules/@homematic-manager/backend/dist/rpc/server.d.ts +12 -0
  276. package/node_modules/@homematic-manager/backend/dist/rpc/server.js +37 -5
  277. package/node_modules/@homematic-manager/backend/dist/util/net.d.ts +86 -1
  278. package/node_modules/@homematic-manager/backend/dist/util/net.js +223 -9
  279. package/node_modules/@homematic-manager/backend/package.json +1 -1
  280. package/node_modules/@homematic-manager/core/dist/api/types.d.ts +397 -10
  281. package/node_modules/@homematic-manager/core/dist/api/types.js +23 -2
  282. package/node_modules/@homematic-manager/core/dist/data/memory.d.ts +3 -1
  283. package/node_modules/@homematic-manager/core/dist/data/memory.js +3 -0
  284. package/node_modules/@homematic-manager/core/dist/data/types.d.ts +60 -0
  285. package/node_modules/@homematic-manager/core/dist/data/types.js +2 -0
  286. package/node_modules/@homematic-manager/core/dist/devices/index.d.ts +19 -0
  287. package/node_modules/@homematic-manager/core/dist/devices/index.js +53 -0
  288. package/node_modules/@homematic-manager/core/dist/easymodes/engine.d.ts +3 -1
  289. package/node_modules/@homematic-manager/core/dist/easymodes/engine.js +1 -0
  290. package/node_modules/@homematic-manager/core/dist/easymodes/form.d.ts +43 -0
  291. package/node_modules/@homematic-manager/core/dist/easymodes/form.js +84 -0
  292. package/node_modules/@homematic-manager/core/dist/i18n/messages.js +61 -0
  293. package/node_modules/@homematic-manager/core/dist/index.d.ts +3 -0
  294. package/node_modules/@homematic-manager/core/dist/index.js +4 -0
  295. package/node_modules/@homematic-manager/core/dist/interfaces/table.d.ts +23 -2
  296. package/node_modules/@homematic-manager/core/dist/interfaces/table.js +18 -1
  297. package/node_modules/@homematic-manager/core/dist/meta/document.d.ts +71 -0
  298. package/node_modules/@homematic-manager/core/dist/meta/document.js +260 -0
  299. package/node_modules/@homematic-manager/core/dist/meta/follow.d.ts +27 -0
  300. package/node_modules/@homematic-manager/core/dist/meta/follow.js +180 -0
  301. package/node_modules/@homematic-manager/core/dist/meta/index.d.ts +16 -0
  302. package/node_modules/@homematic-manager/core/dist/meta/index.js +16 -0
  303. package/node_modules/@homematic-manager/core/dist/meta/paths.d.ts +96 -0
  304. package/node_modules/@homematic-manager/core/dist/meta/paths.js +182 -0
  305. package/node_modules/@homematic-manager/core/dist/meta/slug.d.ts +22 -0
  306. package/node_modules/@homematic-manager/core/dist/meta/slug.js +59 -0
  307. package/node_modules/@homematic-manager/core/dist/meta/store.d.ts +147 -0
  308. package/node_modules/@homematic-manager/core/dist/meta/store.js +537 -0
  309. package/node_modules/@homematic-manager/core/dist/meta/tree.d.ts +19 -0
  310. package/node_modules/@homematic-manager/core/dist/meta/tree.js +53 -0
  311. package/node_modules/@homematic-manager/core/dist/meta/types.d.ts +143 -0
  312. package/node_modules/@homematic-manager/core/dist/meta/types.js +63 -0
  313. package/node_modules/@homematic-manager/core/dist/meta/view.d.ts +50 -0
  314. package/node_modules/@homematic-manager/core/dist/meta/view.js +103 -0
  315. package/node_modules/@homematic-manager/core/dist/rpc/methods.d.ts +10 -0
  316. package/node_modules/@homematic-manager/core/dist/rpc/methods.js +51 -1
  317. package/node_modules/@homematic-manager/core/dist/rpc/text.d.ts +22 -0
  318. package/node_modules/@homematic-manager/core/dist/rpc/text.js +46 -0
  319. package/node_modules/@homematic-manager/core/dist/rssi/index.d.ts +125 -13
  320. package/node_modules/@homematic-manager/core/dist/rssi/index.js +146 -29
  321. package/node_modules/@homematic-manager/core/dist/serviceMessages/index.d.ts +28 -2
  322. package/node_modules/@homematic-manager/core/dist/serviceMessages/index.js +44 -6
  323. package/node_modules/@homematic-manager/core/package.json +1 -1
  324. package/package.json +3 -3
  325. package/ui/assets/index-D-NsbBVS.css +1 -0
  326. package/ui/assets/index-DNUNMBbb.js +16 -0
  327. package/ui/favicon.ico +0 -0
  328. package/ui/index.html +3 -2
  329. package/node_modules/@homematic-manager/backend/dist/write/log.d.ts +0 -51
  330. package/node_modules/@homematic-manager/backend/dist/write/log.js +0 -151
  331. package/ui/assets/index-lB-HxsKR.js +0 -13
  332. package/ui/assets/index-qbhgN2pA.css +0 -1
@@ -0,0 +1,307 @@
1
+ /**
2
+ * The HTTP client for openccu-lite's metadata API - `/api/meta/v1/`.
3
+ *
4
+ * Nothing but `fetch` and `AbortSignal`, both of which Node has had since 18 and Electron has
5
+ * always had: the porting rules of openccu-lite say in so many words not to add an HTTP or SSE
6
+ * dependency to an application that already has one, and a CCU addon pays for every kilobyte it
7
+ * ships. The change stream is Server-Sent Events read off `response.body`, parsed in
8
+ * {@link readEvents} - fifty lines against a `eventsource` package that would come with its own
9
+ * reconnect policy we would have to fight.
10
+ *
11
+ * Every call returns a typed value or throws a {@link MetaError}: `401` becomes `forbidden` with
12
+ * the credential named, which is what the provider degrades on rather than failing.
13
+ */
14
+ import { MetaError, parseDocument, } from '@homematic-manager/core';
15
+ /** How long a plain request may take. The box is on the LAN or on the loopback. */
16
+ export const DEFAULT_TIMEOUT_MS = 10_000;
17
+ /**
18
+ * How long the detection call may take.
19
+ *
20
+ * Short on purpose: it runs on every connect, a CCU answers 404 in milliseconds and an
21
+ * openccu-lite box answers just as fast. What this timeout is really for is the host that neither
22
+ * answers nor refuses - a firewall that drops the packet - and three seconds of that is already
23
+ * more patience than the answer is worth.
24
+ */
25
+ export const DETECT_TIMEOUT_MS = 3000;
26
+ /** Thin, typed access to one box's metadata API. */
27
+ export class MetaApiClient {
28
+ #options;
29
+ #fetch;
30
+ constructor(options) {
31
+ this.#options = options;
32
+ this.#fetch = options.fetch ?? globalThis.fetch;
33
+ }
34
+ get baseUrl() {
35
+ return this.#options.baseUrl;
36
+ }
37
+ /**
38
+ * `GET /version` - the runtime detection, and the only call that needs no credential.
39
+ *
40
+ * A CCU answers 404 or HTML here, which is exactly the point: no `/VERSION` sniffing, no host
41
+ * names, no ports. `undefined` means "this is not an openccu-lite box".
42
+ */
43
+ async version(timeoutMs = DETECT_TIMEOUT_MS) {
44
+ try {
45
+ const response = await this.#fetch(this.#url('/version'), {
46
+ signal: AbortSignal.timeout(timeoutMs),
47
+ headers: { Accept: 'application/json' },
48
+ });
49
+ if (!response.ok) {
50
+ return undefined;
51
+ }
52
+ const body = await response.json();
53
+ if (typeof body !== 'object' || body === null) {
54
+ return undefined;
55
+ }
56
+ const answer = body;
57
+ return answer.api === 'meta' && typeof answer.version === 'number' ? answer : undefined;
58
+ }
59
+ catch {
60
+ // a box that is off, a CCU that answers HTML, a DNS name that does not resolve: all of
61
+ // them mean the same thing here, and none of them is worth an exception
62
+ return undefined;
63
+ }
64
+ }
65
+ /** `GET /snapshot` - the whole document, validated as if it came off disk. */
66
+ async snapshot() {
67
+ return parseDocument(await this.#json('GET', '/snapshot'));
68
+ }
69
+ /** `PATCH /objects/{ref}` - the rename, and the room assignment. */
70
+ async patchObject(ref, patch) {
71
+ return this.#write('PATCH', `/objects/${encodeRef(ref)}`, patch);
72
+ }
73
+ /** `POST /objects:bulk` - many objects as one revision; what a multi-select assign sends. */
74
+ async bulk(sets, deletes = []) {
75
+ return this.#write('POST', '/objects:bulk', { set: sets, delete: deletes });
76
+ }
77
+ /** `POST /enums`. */
78
+ async createEnum(id, name) {
79
+ return this.#write('POST', '/enums', { id, name });
80
+ }
81
+ /** `PATCH /enums/{enum}`. */
82
+ async updateEnum(id, name) {
83
+ return this.#write('PATCH', `/enums/${encodeURIComponent(id)}`, { name });
84
+ }
85
+ /** `DELETE /enums/{enum}` - refused while it has members unless `detach` is asked for. */
86
+ async deleteEnum(id, members) {
87
+ const query = members === 'detach' ? '?members=detach' : '';
88
+ return this.#write('DELETE', `/enums/${encodeURIComponent(id)}${query}`, undefined);
89
+ }
90
+ /** `POST /enums/{enum}/nodes` - a new room, or a new floor above it. */
91
+ async createNode(enumId, body) {
92
+ return this.#write('POST', `/enums/${encodeURIComponent(enumId)}/nodes`, body);
93
+ }
94
+ /**
95
+ * `PATCH /enums/{enum}/nodes/{path…}` - rename, move, reorder.
96
+ *
97
+ * The path in the URL is **relative to the enum**: the node `room/eg/wohnzimmer` is
98
+ * `/enums/room/nodes/eg/wohnzimmer`. Writing the whole path there answers `unknown-path` for
99
+ * `room/room/eg/…`, which is a confusing five minutes the first time.
100
+ */
101
+ async updateNode(path, patch) {
102
+ return this.#write('PATCH', nodeUrl(path), patch);
103
+ }
104
+ /** `DELETE /enums/{enum}/nodes/{path…}`, with the same `detach` rule as an enum. */
105
+ async deleteNode(path, members) {
106
+ const query = members === 'detach' ? '?members=detach' : '';
107
+ return this.#write('DELETE', `${nodeUrl(path)}${query}`, undefined);
108
+ }
109
+ /** `PUT /import` - a whole document, validated and applied as one revision. */
110
+ async import(document, mode) {
111
+ return this.#write('PUT', `/import?mode=${mode}`, document);
112
+ }
113
+ /** `GET /export` - the document as the box would hand it to a user. */
114
+ async export() {
115
+ return parseDocument(await this.#json('GET', '/export?format=json'));
116
+ }
117
+ /** `GET /enums` - the trees alone, which is the cheapest authenticated read there is. */
118
+ async enums() {
119
+ const body = await this.#json('GET', '/enums');
120
+ const answer = body;
121
+ return (answer.enums ?? {});
122
+ }
123
+ /**
124
+ * The change stream as an async iterator over parsed events.
125
+ *
126
+ * `since` replays what the box still has; a `{"kind": "resync"}` answer means the history is
127
+ * gone and the consumer has to fetch the snapshot again. The iterator ends when the connection
128
+ * does - reconnecting is the provider's job, because only it knows the revision to resume from.
129
+ */
130
+ async *events(since, signal) {
131
+ const query = since === undefined ? '' : `?since=${String(since)}`;
132
+ const response = await this.#fetch(this.#url(`/events/sse${query}`), {
133
+ signal,
134
+ headers: { ...this.#headers(false), Accept: 'text/event-stream' },
135
+ });
136
+ if (!response.ok) {
137
+ throw await httpError(response);
138
+ }
139
+ if (!response.body) {
140
+ throw new MetaError('unknown-path', 'the event stream answered without a body');
141
+ }
142
+ yield* readEvents(response.body, signal);
143
+ }
144
+ #url(path) {
145
+ return `${this.#options.baseUrl.replace(/\/$/, '')}/api/meta/v1${path}`;
146
+ }
147
+ /**
148
+ * The credential, as a Bearer header.
149
+ *
150
+ * The API takes a session id or an API token in the same three places, and it accepts the CCU's
151
+ * `@…@` wrapping around a session id - but stripping it here keeps one shape on the wire and
152
+ * makes a log line readable.
153
+ */
154
+ #headers(write) {
155
+ const credential = write
156
+ ? (this.#options.writeCredential?.() ?? this.#options.credential?.())
157
+ : (this.#options.credential?.() ?? this.#options.writeCredential?.());
158
+ if (credential === undefined || credential === '') {
159
+ return {};
160
+ }
161
+ return { Authorization: `Bearer ${credential.replace(/^@|@$/g, '')}` };
162
+ }
163
+ async #json(method, path) {
164
+ const response = await this.#fetch(this.#url(path), {
165
+ method,
166
+ signal: AbortSignal.timeout(this.#options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
167
+ headers: { ...this.#headers(false), Accept: 'application/json' },
168
+ });
169
+ if (!response.ok) {
170
+ throw await httpError(response);
171
+ }
172
+ return response.json();
173
+ }
174
+ async #write(method, path, body) {
175
+ const response = await this.#fetch(this.#url(path), {
176
+ method,
177
+ signal: AbortSignal.timeout(this.#options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
178
+ headers: {
179
+ ...this.#headers(true),
180
+ Accept: 'application/json',
181
+ // lighttpd's mod_proxy answers 411 for a body-less PUT/POST/DELETE, which is why
182
+ // even a DELETE goes out with `{}` rather than nothing at all
183
+ 'Content-Type': 'application/json',
184
+ },
185
+ body: JSON.stringify(body ?? {}),
186
+ });
187
+ // 304: the write was valid and changed nothing, and the revision did not move
188
+ if (response.status === 304) {
189
+ return { revision: Number.NaN, changed: false };
190
+ }
191
+ if (!response.ok) {
192
+ throw await httpError(response);
193
+ }
194
+ const answer = (await response.json());
195
+ return {
196
+ revision: typeof answer.revision === 'number' ? answer.revision : Number.NaN,
197
+ changed: answer.changed !== false,
198
+ };
199
+ }
200
+ }
201
+ /** `BidCos-RF.JEQ0230153:1` as one path segment: the colon has to be percent-encoded. */
202
+ export function encodeRef(ref) {
203
+ return encodeURIComponent(ref);
204
+ }
205
+ /** `room/eg/wohnzimmer` → `/enums/room/nodes/eg/wohnzimmer`. */
206
+ export function nodeUrl(path) {
207
+ const [enumId, ...ids] = path.split('/');
208
+ return `/enums/${encodeURIComponent(enumId ?? '')}/nodes/${ids.map((id) => encodeURIComponent(id)).join('/')}`;
209
+ }
210
+ /** The error body of the API, or the status when there is none. */
211
+ async function httpError(response) {
212
+ let code = response.status === 403 || response.status === 401 ? 'forbidden' : 'unknown-path';
213
+ let message = `${String(response.status)} ${response.statusText}`;
214
+ try {
215
+ const body = (await response.json());
216
+ if (typeof body.error === 'string') {
217
+ code = body.error;
218
+ }
219
+ if (typeof body.message === 'string') {
220
+ message = body.message;
221
+ }
222
+ }
223
+ catch {
224
+ // not JSON: a proxy error page, or a CCU answering something else entirely
225
+ }
226
+ if (response.status === 401) {
227
+ // told apart from a 403 on purpose: no credential at all is what the provider degrades on,
228
+ // a credential without the role is what a write reports to the user
229
+ return new MetaError('forbidden', `the metadata API refused the credential (401): ${message}`, { status: 401 });
230
+ }
231
+ return new MetaError(code, message, { status: response.status });
232
+ }
233
+ /**
234
+ * Server-Sent Events off a byte stream: `data:` lines until a blank one, everything else ignored.
235
+ *
236
+ * The API sends one JSON object per message and a comment line every 30 s as a heartbeat; there are
237
+ * no event names and no ids, because the resume point is `?since=<revision>` and not
238
+ * `Last-Event-ID`. A message that is not JSON is skipped rather than thrown - a stream that carries
239
+ * one bad line still carries the next good one.
240
+ */
241
+ export async function* readEvents(stream, signal) {
242
+ const reader = stream.getReader();
243
+ const decoder = new TextDecoder();
244
+ let buffer = '';
245
+ let data = '';
246
+ // a pending `read()` does not notice a signal by itself; cancelling the reader is what ends it,
247
+ // and without this a `stop()` waits for a box that has nothing more to say
248
+ const cancel = () => {
249
+ void reader.cancel().catch(() => undefined);
250
+ };
251
+ signal?.addEventListener('abort', cancel, { once: true });
252
+ try {
253
+ for (;;) {
254
+ if (signal?.aborted === true) {
255
+ return;
256
+ }
257
+ const chunk = await reader.read();
258
+ if (chunk.done) {
259
+ return;
260
+ }
261
+ buffer += decoder.decode(chunk.value, { stream: true });
262
+ let newline = buffer.indexOf('\n');
263
+ while (newline !== -1) {
264
+ const line = buffer.slice(0, newline).replace(/\r$/, '');
265
+ buffer = buffer.slice(newline + 1);
266
+ newline = buffer.indexOf('\n');
267
+ if (line === '') {
268
+ const event = parseEvent(data);
269
+ data = '';
270
+ if (event) {
271
+ yield event;
272
+ }
273
+ continue;
274
+ }
275
+ if (line.startsWith(':')) {
276
+ // the heartbeat comment; it exists so that a dead connection is noticed
277
+ continue;
278
+ }
279
+ if (line.startsWith('data:')) {
280
+ data += line.slice('data:'.length).trimStart();
281
+ }
282
+ }
283
+ }
284
+ }
285
+ finally {
286
+ signal?.removeEventListener('abort', cancel);
287
+ // a reader that is not released keeps the socket, and the provider reconnects into it
288
+ reader.releaseLock();
289
+ }
290
+ }
291
+ function parseEvent(data) {
292
+ if (data === '') {
293
+ return undefined;
294
+ }
295
+ try {
296
+ const parsed = JSON.parse(data);
297
+ if (typeof parsed !== 'object' || parsed === null) {
298
+ return undefined;
299
+ }
300
+ const event = parsed;
301
+ return typeof event.kind === 'string' ? event : undefined;
302
+ }
303
+ catch {
304
+ return undefined;
305
+ }
306
+ }
307
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Where the credential for openccu-lite's metadata API comes from.
3
+ *
4
+ * Three cases, and the interesting one is the first:
5
+ *
6
+ * - **on the box**, as the addon: `/usr/local/etc/occulite/local-token` holds a token with the
7
+ * role `user` - it reads everything and writes nothing, on purpose. Writes use the session of
8
+ * whoever is looking at the page, which the shell hands the addon as `?sid=@xxxxxxxxxx@`
9
+ * (openccu-lite's embedding contract). So the addon reads as itself and writes as the user, which
10
+ * is exactly the split a device-management frontend wants: nothing renames a device unless a
11
+ * person asked for it.
12
+ * - **off the box** - a desktop, a server, Docker: an API token the administrator created on the
13
+ * box's Users page, stored in the connection settings like any other credential.
14
+ * - **neither**: reads are unauthenticated, the API answers 401, and the provider degrades to the
15
+ * local names with a notice. Never an exception (their invariant 5).
16
+ */
17
+ /** Where the box puts the read-only token for programs running on it. */
18
+ export declare const LOCAL_TOKEN_FILE = "/usr/local/etc/occulite/local-token";
19
+ /** What an API token looks like, so a typo in a settings field is caught before it is sent. */
20
+ export declare const TOKEN_PATTERN: RegExp;
21
+ /** Is this the shape of an openccu-lite API token? */
22
+ export declare function isApiToken(value: string): boolean;
23
+ /**
24
+ * A session id as the box hands it over: `@xxxxxxxxxx@`, the CCU convention, or bare.
25
+ *
26
+ * The API accepts both spellings; this normalises to the bare one so that one shape goes on the
27
+ * wire and a log line stays readable.
28
+ */
29
+ export declare function normaliseSid(value: string | undefined): string | undefined;
30
+ /**
31
+ * The box's local token, or `undefined` when there is no file - which is every installation that
32
+ * is not the addon on an openccu-lite box.
33
+ *
34
+ * Never throws: a token that cannot be read is the same as no token, and the answer to that is to
35
+ * run without names rather than to fail to start.
36
+ */
37
+ export declare function readLocalToken(file?: string): Promise<string | undefined>;
38
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Where the credential for openccu-lite's metadata API comes from.
3
+ *
4
+ * Three cases, and the interesting one is the first:
5
+ *
6
+ * - **on the box**, as the addon: `/usr/local/etc/occulite/local-token` holds a token with the
7
+ * role `user` - it reads everything and writes nothing, on purpose. Writes use the session of
8
+ * whoever is looking at the page, which the shell hands the addon as `?sid=@xxxxxxxxxx@`
9
+ * (openccu-lite's embedding contract). So the addon reads as itself and writes as the user, which
10
+ * is exactly the split a device-management frontend wants: nothing renames a device unless a
11
+ * person asked for it.
12
+ * - **off the box** - a desktop, a server, Docker: an API token the administrator created on the
13
+ * box's Users page, stored in the connection settings like any other credential.
14
+ * - **neither**: reads are unauthenticated, the API answers 401, and the provider degrades to the
15
+ * local names with a notice. Never an exception (their invariant 5).
16
+ */
17
+ import { readFile } from 'node:fs/promises';
18
+ /** Where the box puts the read-only token for programs running on it. */
19
+ export const LOCAL_TOKEN_FILE = '/usr/local/etc/occulite/local-token';
20
+ /** What an API token looks like, so a typo in a settings field is caught before it is sent. */
21
+ export const TOKEN_PATTERN = /^olt_[0-9a-f]{32}$/;
22
+ /** Is this the shape of an openccu-lite API token? */
23
+ export function isApiToken(value) {
24
+ return TOKEN_PATTERN.test(value.trim());
25
+ }
26
+ /**
27
+ * A session id as the box hands it over: `@xxxxxxxxxx@`, the CCU convention, or bare.
28
+ *
29
+ * The API accepts both spellings; this normalises to the bare one so that one shape goes on the
30
+ * wire and a log line stays readable.
31
+ */
32
+ export function normaliseSid(value) {
33
+ const bare = (value ?? '').replace(/^@|@$/g, '').trim();
34
+ return /^[0-9a-zA-Z]{6,64}$/.test(bare) ? bare : undefined;
35
+ }
36
+ /**
37
+ * The box's local token, or `undefined` when there is no file - which is every installation that
38
+ * is not the addon on an openccu-lite box.
39
+ *
40
+ * Never throws: a token that cannot be read is the same as no token, and the answer to that is to
41
+ * run without names rather than to fail to start.
42
+ */
43
+ export async function readLocalToken(file = LOCAL_TOKEN_FILE) {
44
+ try {
45
+ const content = await readFile(file, 'utf8');
46
+ const token = content.trim().split(/\r?\n/)[0]?.trim();
47
+ return token === undefined || token === '' ? undefined : token;
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * D-40: the metadata store - names, rooms, functions and any taxonomy a user adds - with the two
3
+ * providers behind it, the HTTP client for openccu-lite's API and where the credentials come from.
4
+ */
5
+ export * from './client.js';
6
+ export * from './credentials.js';
7
+ export * from './provider.js';
8
+ export * from './localProvider.js';
9
+ export * from './occuliteProvider.js';
10
+ export * from './regaProvider.js';
11
+ export * from './service.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * D-40: the metadata store - names, rooms, functions and any taxonomy a user adds - with the two
3
+ * providers behind it, the HTTP client for openccu-lite's API and where the credentials come from.
4
+ */
5
+ export * from './client.js';
6
+ export * from './credentials.js';
7
+ export * from './provider.js';
8
+ export * from './localProvider.js';
9
+ export * from './occuliteProvider.js';
10
+ export * from './regaProvider.js';
11
+ export * from './service.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The `local` metadata provider: names, rooms and functions in this profile.
3
+ *
4
+ * It is the same {@link MetaStore} the conformance corpus runs against, persisted to
5
+ * `<dataDir>/meta.json` - the **profile** directory and not the per-CCU cache, for the reason the
6
+ * link templates live there too (task 15): a taxonomy is the user's own work, it is not derived
7
+ * from any CCU, `config.clearCaches` may not throw it away and moving the profile to another
8
+ * installation has to take it along.
9
+ *
10
+ * On a system that has neither ReGa nor a box this is where a name lives; on a CCU it is where the
11
+ * taxonomy lives while the names come from ReGa. Nothing here talks to anything.
12
+ */
13
+ import { type MetaDocument, type MetaImportMode, type MetaNodePatch, type MetaState } from '@homematic-manager/core';
14
+ import type { MetadataProvider, MetaMembershipEntry, MetaNameEntry, MetaProviderEvents } from './provider.js';
15
+ export interface LocalMetaProviderOptions extends MetaProviderEvents {
16
+ /** `<dataDir>/meta.json`. */
17
+ readonly file: string;
18
+ }
19
+ export declare class LocalMetaProvider implements MetadataProvider {
20
+ #private;
21
+ readonly kind: "local";
22
+ constructor(options: LocalMetaProviderOptions);
23
+ state(): MetaState;
24
+ /**
25
+ * Reads the file. A broken one is reported and replaced by an empty store rather than left to
26
+ * fail every read afterwards - the names in the grid then come from the name cache alone, which
27
+ * is the degradation D-2 asks for everywhere else too.
28
+ */
29
+ start(): Promise<void>;
30
+ stop(): Promise<void>;
31
+ /** Nothing to read again: this store is in this process. */
32
+ refresh(): Promise<void>;
33
+ document(): MetaDocument;
34
+ setNames(entries: readonly MetaNameEntry[]): Promise<void>;
35
+ setMembership(entries: readonly MetaMembershipEntry[]): Promise<void>;
36
+ createEnum(id: string, name: Readonly<Record<string, string>>): Promise<void>;
37
+ updateEnum(id: string, name: Readonly<Record<string, string>>): Promise<void>;
38
+ deleteEnum(id: string, detach: boolean): Promise<void>;
39
+ createNode(enumId: string, parent: string | null, id: string, name: string, options: {
40
+ readonly icon?: string;
41
+ readonly position?: number;
42
+ }): Promise<string>;
43
+ updateNode(path: string, patch: MetaNodePatch): Promise<void>;
44
+ deleteNode(path: string, detach: boolean): Promise<void>;
45
+ import(document: unknown, mode: MetaImportMode): Promise<void>;
46
+ }
47
+ //# sourceMappingURL=localProvider.d.ts.map
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The `local` metadata provider: names, rooms and functions in this profile.
3
+ *
4
+ * It is the same {@link MetaStore} the conformance corpus runs against, persisted to
5
+ * `<dataDir>/meta.json` - the **profile** directory and not the per-CCU cache, for the reason the
6
+ * link templates live there too (task 15): a taxonomy is the user's own work, it is not derived
7
+ * from any CCU, `config.clearCaches` may not throw it away and moving the profile to another
8
+ * installation has to take it along.
9
+ *
10
+ * On a system that has neither ReGa nor a box this is where a name lives; on a CCU it is where the
11
+ * taxonomy lives while the names come from ReGa. Nothing here talks to anything.
12
+ */
13
+ import { MetaStore, emptyDocument, parseDocument, } from '@homematic-manager/core';
14
+ import { errorMessage } from '../errors.js';
15
+ import { readJsonFile, writeJsonFile } from '../util/jsonFile.js';
16
+ export class LocalMetaProvider {
17
+ kind = 'local';
18
+ #options;
19
+ #store = new MetaStore();
20
+ constructor(options) {
21
+ this.#options = options;
22
+ }
23
+ state() {
24
+ return {
25
+ provider: 'local',
26
+ reachable: true,
27
+ writable: true,
28
+ revision: this.#store.revision,
29
+ objects: this.#store.size,
30
+ };
31
+ }
32
+ /**
33
+ * Reads the file. A broken one is reported and replaced by an empty store rather than left to
34
+ * fail every read afterwards - the names in the grid then come from the name cache alone, which
35
+ * is the degradation D-2 asks for everywhere else too.
36
+ */
37
+ async start() {
38
+ const raw = await readJsonFile(this.#options.file);
39
+ if (raw === undefined) {
40
+ this.#store = new MetaStore();
41
+ this.#options.onStateChanged(this.state());
42
+ return;
43
+ }
44
+ try {
45
+ this.#store = new MetaStore({ document: parseDocument(raw) });
46
+ }
47
+ catch (error) {
48
+ this.#store = new MetaStore({ document: emptyDocument() });
49
+ this.#options.onNotice('warn', `${this.#options.file} could not be read: ${errorMessage(error)}`);
50
+ }
51
+ this.#options.onStateChanged(this.state());
52
+ this.#options.onChanged();
53
+ }
54
+ stop() {
55
+ return Promise.resolve();
56
+ }
57
+ /** Nothing to read again: this store is in this process. */
58
+ refresh() {
59
+ return Promise.resolve();
60
+ }
61
+ document() {
62
+ return this.#store.document();
63
+ }
64
+ async setNames(entries) {
65
+ const sets = {};
66
+ for (const entry of entries) {
67
+ sets[entry.ref] = { name: entry.name };
68
+ }
69
+ await this.#apply(() => this.#store.bulk(sets).changed);
70
+ }
71
+ async setMembership(entries) {
72
+ const sets = {};
73
+ for (const entry of entries) {
74
+ sets[entry.ref] = { enums: entry.paths };
75
+ }
76
+ await this.#apply(() => this.#store.bulk(sets).changed);
77
+ }
78
+ async createEnum(id, name) {
79
+ await this.#apply(() => this.#store.createEnum(id, name).changed);
80
+ }
81
+ async updateEnum(id, name) {
82
+ await this.#apply(() => this.#store.updateEnum(id, name).changed);
83
+ }
84
+ async deleteEnum(id, detach) {
85
+ await this.#apply(() => this.#store.deleteEnum(id, detach ? 'detach' : 'refuse').changed);
86
+ }
87
+ async createNode(enumId, parent, id, name, options) {
88
+ await this.#apply(() => this.#store.createNode(enumId, parent, id, name, options).changed);
89
+ return `${parent ?? enumId}/${id}`;
90
+ }
91
+ async updateNode(path, patch) {
92
+ await this.#apply(() => this.#store.updateNode(path, patch).changed);
93
+ }
94
+ async deleteNode(path, detach) {
95
+ await this.#apply(() => this.#store.deleteNode(path, detach ? 'detach' : 'refuse').changed);
96
+ }
97
+ async import(document, mode) {
98
+ await this.#apply(() => this.#store.import(document, mode).changed);
99
+ }
100
+ /** One write: apply, persist when it changed anything, tell the backend. */
101
+ async #apply(write) {
102
+ const changed = write();
103
+ if (!changed) {
104
+ return;
105
+ }
106
+ await this.#save();
107
+ this.#options.onStateChanged(this.state());
108
+ this.#options.onChanged();
109
+ }
110
+ async #save() {
111
+ try {
112
+ await writeJsonFile(this.#options.file, this.#store.document());
113
+ }
114
+ catch (error) {
115
+ this.#options.onNotice('warn', `${this.#options.file} could not be written: ${errorMessage(error)}`);
116
+ }
117
+ }
118
+ }
119
+ //# sourceMappingURL=localProvider.js.map
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The `occulite` provider: names, rooms and functions from an openccu-lite box, and written back
3
+ * to it.
4
+ *
5
+ * The shape of it is the one openccu-lite's porting kit prescribes and the one their own
6
+ * `occulited` expects: **snapshot once, then follow the change stream**, and re-snapshot when the
7
+ * stream says the history is gone (`resync`), when a whole store was imported, or when a revision
8
+ * arrives that is not the next one. Nothing polls. A rename made in another frontend, or by another
9
+ * person in another browser, is in this application's grid inside a second.
10
+ *
11
+ * Failure is degradation, never an exception (their invariant 5, our D-2):
12
+ *
13
+ * - the box is off → the last snapshot from the cache file, `reachable: false`, one notice, and a
14
+ * reconnect loop with a backoff;
15
+ * - the credential is refused (401) → no names rather than no application, one notice, and a retry
16
+ * on the next reconnect;
17
+ * - the credential may read but not write (403) → the names are shown and `writable` is false, so
18
+ * the UI can say why the rename button does nothing.
19
+ *
20
+ * The snapshot is persisted where the ReGa provider persists its names, for the same reason: a
21
+ * restart while the box is unreachable must not leave the grid nameless.
22
+ */
23
+ import { summariseAll, type MetaDocument, type MetaImportMode, type MetaNodePatch, type MetaState } from '@homematic-manager/core';
24
+ import { MetaApiClient } from './client.js';
25
+ import type { MetadataProvider, MetaMembershipEntry, MetaNameEntry, MetaProviderEvents } from './provider.js';
26
+ /** First wait before the event stream is opened again, in milliseconds. */
27
+ export declare const RECONNECT_MIN_MS = 2000;
28
+ /** Longest wait between two attempts. A box that is being updated is back well inside a minute. */
29
+ export declare const RECONNECT_MAX_MS = 60000;
30
+ export interface OcculiteProviderOptions extends MetaProviderEvents {
31
+ readonly client: MetaApiClient;
32
+ /** `<cacheDir>/occulite-meta.json`: the last snapshot, so a restart without the box has names. */
33
+ readonly cacheFile?: string;
34
+ /** `occulited 0.1.0`, from the detection call. */
35
+ readonly implementation?: string;
36
+ readonly reconnectMinMs?: number;
37
+ readonly reconnectMaxMs?: number;
38
+ /** Injected by the tests in place of `setTimeout`. */
39
+ readonly wait?: (ms: number, signal: AbortSignal) => Promise<void>;
40
+ }
41
+ export declare class OcculiteProvider implements MetadataProvider {
42
+ #private;
43
+ readonly kind: "occulite";
44
+ constructor(options: OcculiteProviderOptions);
45
+ state(): MetaState;
46
+ document(): MetaDocument;
47
+ /**
48
+ * The cached snapshot first, then the box.
49
+ *
50
+ * In that order on purpose: the cache is instant and the box may be a second away, and a grid
51
+ * that shows the names it had a moment ago and then corrects them is better than one that shows
52
+ * addresses while a request is in flight.
53
+ */
54
+ start(): Promise<void>;
55
+ stop(): Promise<void>;
56
+ /**
57
+ * A fresh snapshot, on demand - the credential changed, or a host asked for one.
58
+ *
59
+ * It also ends the wait before the next attempt at the event stream. On the box the first page
60
+ * load is what hands the addon a session at all, and without this the store would be read at
61
+ * once but only *followed* after the backoff had run out.
62
+ */
63
+ refresh(): Promise<void>;
64
+ setNames(entries: readonly MetaNameEntry[]): Promise<void>;
65
+ setMembership(entries: readonly MetaMembershipEntry[]): Promise<void>;
66
+ createEnum(id: string, name: Readonly<Record<string, string>>): Promise<void>;
67
+ updateEnum(id: string, name: Readonly<Record<string, string>>): Promise<void>;
68
+ deleteEnum(id: string, detach: boolean): Promise<void>;
69
+ createNode(enumId: string, parent: string | null, id: string, name: string, options: {
70
+ readonly icon?: string;
71
+ readonly position?: number;
72
+ }): Promise<string>;
73
+ updateNode(path: string, patch: MetaNodePatch): Promise<void>;
74
+ deleteNode(path: string, detach: boolean): Promise<void>;
75
+ import(document: unknown, mode: MetaImportMode): Promise<void>;
76
+ }
77
+ /** Every object of the current document as the UI shows it. Re-exported for the backend's reads. */
78
+ export { summariseAll };
79
+ //# sourceMappingURL=occuliteProvider.d.ts.map