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
@@ -117,6 +117,177 @@ export function acknowledgeAlarmScript(interfaceName, address, datapoint) {
117
117
  export function isPlainRegaName(value) {
118
118
  return /^[A-Za-z0-9_.:-]+$/.test(value);
119
119
  }
120
+ /*
121
+ * Task 27: rooms and functions through ReGa's own objects.
122
+ *
123
+ * ReGa keeps rooms and functions as enum objects under `ID_ROOMS` and `ID_FUNCTIONS`, each holding
124
+ * the ids of the channels in it (`EnumUsedIDs()`), and a channel is put in or taken out with
125
+ * `Add(id)` / `Remove(id)` on the enum - which is what the WebUI does when a room is edited. The
126
+ * read script is `homematic-rega`'s `channels.rega`, `rooms.rega` and `functions.rega` in one
127
+ * round trip, plus the interface name of every device, because the metadata store keys objects by
128
+ * `<interface>.<address>` and the address alone is not a ref.
129
+ */
130
+ /** Everything the ReGa metadata provider reads, in one script and one JSON document. */
131
+ export const META_READ_SCRIPT = `string sDevId;
132
+ string sChnId;
133
+ string sEnumId;
134
+ string sMember;
135
+ string sIface;
136
+ object oIface;
137
+ boolean bFirst = true;
138
+ boolean bFirstMember = true;
139
+ Write('{"objects":[');
140
+ foreach (sDevId, root.Devices().EnumUsedIDs()) {
141
+ object oDevice = dom.GetObject(sDevId);
142
+ if (oDevice.ReadyConfig()) {
143
+ sIface = "";
144
+ oIface = dom.GetObject(oDevice.Interface());
145
+ if (oIface) { sIface = oIface.Name(); }
146
+ if (bFirst) { bFirst = false; } else { Write(','); }
147
+ Write('{"id":' # sDevId # ',"address":"' # oDevice.Address() # '","interface":"' # sIface # '","name":"');
148
+ WriteURL(oDevice.Name());
149
+ Write('"}');
150
+ foreach (sChnId, oDevice.Channels()) {
151
+ object oChannel = dom.GetObject(sChnId);
152
+ Write(',{"id":' # sChnId # ',"address":"' # oChannel.Address() # '","interface":"' # sIface # '","name":"');
153
+ WriteURL(oChannel.Name());
154
+ Write('"}');
155
+ }
156
+ }
157
+ }
158
+ Write('],"rooms":[');
159
+ bFirst = true;
160
+ foreach (sEnumId, dom.GetObject(ID_ROOMS).EnumUsedIDs()) {
161
+ object oRoom = dom.GetObject(sEnumId);
162
+ if (bFirst) { bFirst = false; } else { Write(','); }
163
+ Write('{"id":' # sEnumId # ',"name":"');
164
+ WriteURL(oRoom.Name());
165
+ Write('","channels":[');
166
+ bFirstMember = true;
167
+ foreach (sMember, oRoom.EnumUsedIDs()) {
168
+ if (bFirstMember) { bFirstMember = false; } else { Write(','); }
169
+ Write(sMember);
170
+ }
171
+ Write(']}');
172
+ }
173
+ Write('],"functions":[');
174
+ bFirst = true;
175
+ foreach (sEnumId, dom.GetObject(ID_FUNCTIONS).EnumUsedIDs()) {
176
+ object oFunction = dom.GetObject(sEnumId);
177
+ if (bFirst) { bFirst = false; } else { Write(','); }
178
+ Write('{"id":' # sEnumId # ',"name":"');
179
+ WriteURL(oFunction.Name());
180
+ Write('","channels":[');
181
+ bFirstMember = true;
182
+ foreach (sMember, oFunction.EnumUsedIDs()) {
183
+ if (bFirstMember) { bFirstMember = false; } else { Write(','); }
184
+ Write(sMember);
185
+ }
186
+ Write(']}');
187
+ }
188
+ Write(']}');
189
+ `;
190
+ /**
191
+ * Decodes what ReGa's `WriteURL()` produces: `%XX` and `%uXXXX` escapes whose bytes are ISO-8859-1
192
+ * characters, not UTF-8 sequences - which is why `decodeURIComponent` would throw on `K%FCche`.
193
+ * The same rule `homematic-rega` applies to its own scripts' output.
194
+ */
195
+ export function unescapeRegaUrl(value) {
196
+ return value.replace(/%u([0-9a-f]{4})|%([0-9a-f]{2})/gi, (_match, long, short) => String.fromCharCode(Number.parseInt(long ?? short ?? '0', 16)));
197
+ }
198
+ /** Reads what {@link META_READ_SCRIPT} wrote. Throws on anything that is not the expected shape. */
199
+ export function parseMetaSnapshot(output) {
200
+ const parsed = JSON.parse(output);
201
+ if (typeof parsed !== 'object' || parsed === null) {
202
+ throw new Error('the ReGa metadata script answered with no document');
203
+ }
204
+ const raw = parsed;
205
+ return {
206
+ objects: parseObjects(raw.objects),
207
+ rooms: parseEnums(raw.rooms, 'rooms'),
208
+ functions: parseEnums(raw.functions, 'functions'),
209
+ };
210
+ }
211
+ function parseObjects(value) {
212
+ if (!Array.isArray(value)) {
213
+ throw new Error('the ReGa metadata script answered without an object list');
214
+ }
215
+ const objects = [];
216
+ for (const item of value) {
217
+ if (typeof item !== 'object' || item === null) {
218
+ continue;
219
+ }
220
+ const entry = item;
221
+ if (typeof entry.id !== 'number' || typeof entry.address !== 'string' || entry.address === '') {
222
+ continue;
223
+ }
224
+ objects.push({
225
+ id: entry.id,
226
+ address: entry.address,
227
+ interfaceName: typeof entry.interface === 'string' ? entry.interface : '',
228
+ name: typeof entry.name === 'string' ? unescapeRegaUrl(entry.name) : '',
229
+ });
230
+ }
231
+ return objects;
232
+ }
233
+ function parseEnums(value, what) {
234
+ if (!Array.isArray(value)) {
235
+ throw new Error(`the ReGa metadata script answered without the ${what}`);
236
+ }
237
+ const enums = [];
238
+ for (const item of value) {
239
+ if (typeof item !== 'object' || item === null) {
240
+ continue;
241
+ }
242
+ const entry = item;
243
+ if (typeof entry.id !== 'number') {
244
+ continue;
245
+ }
246
+ const channels = Array.isArray(entry.channels)
247
+ ? entry.channels.filter((id) => typeof id === 'number')
248
+ : [];
249
+ enums.push({ id: entry.id, name: typeof entry.name === 'string' ? unescapeRegaUrl(entry.name) : '', channels });
250
+ }
251
+ return enums;
252
+ }
253
+ const ENUM_ROOT = { room: 'ID_ROOMS', function: 'ID_FUNCTIONS' };
254
+ /**
255
+ * A new room or function: an enum object, named, and put into ReGa's list of them. The script
256
+ * writes the new id, which is the only thing the caller needs to address it from then on.
257
+ */
258
+ export function createEnumNodeScript(kind, name) {
259
+ return [
260
+ 'object oNew = dom.CreateObject(OT_ENUM);',
261
+ `oNew.Name("${escapeRegaString(name)}");`,
262
+ `dom.GetObject(${ENUM_ROOT[kind]}).Add(oNew.ID());`,
263
+ 'Write(oNew.ID());',
264
+ '',
265
+ ].join('\n');
266
+ }
267
+ /** Reads the id {@link createEnumNodeScript} wrote, or `undefined` when ReGa wrote something else. */
268
+ export function parseCreatedId(output) {
269
+ const trimmed = output.trim();
270
+ return /^\d+$/.test(trimmed) ? Number(trimmed) : undefined;
271
+ }
272
+ /**
273
+ * Removes a room or function: out of ReGa's list first, then the object itself. The memberships go
274
+ * with it, which is what the caller has already confirmed with the user (`has-members`).
275
+ */
276
+ export function deleteEnumNodeScript(kind, id) {
277
+ return [
278
+ `object oGone = dom.GetObject(${String(id)});`,
279
+ `if (oGone) { dom.GetObject(${ENUM_ROOT[kind]}).Remove(${String(id)}); dom.DeleteObject(${String(id)}); }`,
280
+ '',
281
+ ].join('\n');
282
+ }
283
+ /** `Add` / `Remove` on the enum, one statement per change; `undefined` when there is none. */
284
+ export function membershipScript(changes) {
285
+ if (changes.length === 0) {
286
+ return undefined;
287
+ }
288
+ const lines = changes.map((change) => `dom.GetObject(${String(change.enumId)}).${change.on ? 'Add' : 'Remove'}(${String(change.channelId)});`);
289
+ return `${lines.join('\n')}\n`;
290
+ }
120
291
  /**
121
292
  * D-32: does this user exist, and what is their level?
122
293
  *
@@ -25,7 +25,7 @@
25
25
  * `unitLabel()` in the core still repairs. See the README for what remains broken in the libraries
26
26
  * (BIN-RPC decodes strings as UTF-8, and both request paths write UTF-8).
27
27
  */
28
- import type { ExplicitDouble, RpcProtocol, RpcValue } from '@homematic-manager/core';
28
+ import type { ExplicitDouble, RpcOrigin, RpcProtocol, RpcValue } from '@homematic-manager/core';
29
29
  /**
30
30
  * What may be sent to an interface: everything an answer can carry, plus the explicit-double
31
31
  * wrapper both encoders understand. A paramset payload is full of them, and an interface that gets
@@ -34,7 +34,7 @@ import type { ExplicitDouble, RpcProtocol, RpcValue } from '@homematic-manager/c
34
34
  export type RpcOutValue = boolean | number | string | ExplicitDouble | RpcOutValue[] | {
35
35
  [key: string]: RpcOutValue;
36
36
  };
37
- /** What the write log and the RPC console record for one call. */
37
+ /** What the RPC log records for one call - every call, task 48. */
38
38
  export interface RpcCallRecord {
39
39
  readonly interfaceName: string;
40
40
  readonly method: string;
@@ -45,6 +45,13 @@ export interface RpcCallRecord {
45
45
  readonly durationMs: number;
46
46
  /** Milliseconds since epoch. */
47
47
  readonly timestamp: number;
48
+ /** Who asked: the console, a UI action, or the backend on its own. */
49
+ readonly origin: RpcOrigin;
50
+ }
51
+ /** Per-call options of {@link RpcClient.call}. */
52
+ export interface RpcCallOptions {
53
+ /** The origin of this call, when the caller knows it better than the client's resolver does. */
54
+ readonly origin?: RpcOrigin;
48
55
  }
49
56
  /** Anything that answers `methodCall`; the tests pass a fake instead of a socket. */
50
57
  export interface RpcTransport {
@@ -70,8 +77,13 @@ export interface RpcClientOptions {
70
77
  readonly encoding?: string;
71
78
  /** A CCU's TLS certificate is self-signed, so this is false by default. */
72
79
  readonly rejectUnauthorized?: boolean;
73
- /** Called for every finished call - the write log and the console history hang off it. */
80
+ /** Called for every finished call - the RPC log hangs off it. */
74
81
  readonly onCall?: (record: RpcCallRecord) => void;
82
+ /**
83
+ * The origin of a call that names none: the backend answers from its request context (task
84
+ * 48). Without a resolver every call is `background`, which is right for a client on its own.
85
+ */
86
+ readonly originOf?: () => RpcOrigin;
75
87
  /** Injected by the tests in place of the real libraries. */
76
88
  readonly createTransport?: (options: RpcClientOptions) => RpcTransport;
77
89
  }
@@ -90,9 +102,10 @@ export declare class RpcClient {
90
102
  get description(): string;
91
103
  /**
92
104
  * Calls a method. Rejects with a `BackendError`: `kind: 'rpc'` for a fault the interface
93
- * answered, `kind: 'connection'` for a timeout or a socket problem.
105
+ * answered, `kind: 'connection'` for a timeout or a socket problem. Every call, however it
106
+ * ends, is reported through `onCall` with its origin - the one given here, or the resolver's.
94
107
  */
95
- call(method: string, params?: readonly RpcOutValue[]): Promise<RpcValue>;
108
+ call(method: string, params?: readonly RpcOutValue[], options?: RpcCallOptions): Promise<RpcValue>;
96
109
  /** Closes the underlying socket, if the library has one. */
97
110
  close(): void;
98
111
  get closed(): boolean;
@@ -113,6 +113,7 @@ export class RpcClient {
113
113
  #transport;
114
114
  #timeoutMs;
115
115
  #onCall;
116
+ #originOf;
116
117
  #closed = false;
117
118
  constructor(options) {
118
119
  this.name = options.name;
@@ -121,6 +122,7 @@ export class RpcClient {
121
122
  this.protocol = options.protocol;
122
123
  this.#timeoutMs = options.timeoutMs ?? DEFAULT_RPC_TIMEOUT_MS;
123
124
  this.#onCall = options.onCall ?? (() => undefined);
125
+ this.#originOf = options.originOf ?? (() => 'background');
124
126
  this.#transport = (options.createTransport ?? createTransport)(options);
125
127
  }
126
128
  /** A short description for error messages: `HmIP-RF (ccu.lan:2010, xmlrpc)`. */
@@ -129,21 +131,24 @@ export class RpcClient {
129
131
  }
130
132
  /**
131
133
  * Calls a method. Rejects with a `BackendError`: `kind: 'rpc'` for a fault the interface
132
- * answered, `kind: 'connection'` for a timeout or a socket problem.
134
+ * answered, `kind: 'connection'` for a timeout or a socket problem. Every call, however it
135
+ * ends, is reported through `onCall` with its origin - the one given here, or the resolver's.
133
136
  */
134
- async call(method, params = []) {
137
+ async call(method, params = [], options = {}) {
135
138
  if (this.#closed) {
136
139
  throw connectionError(`${this.description}: the client is closed`);
137
140
  }
141
+ // resolved before the first await: the context that asked is the one that counts
142
+ const origin = options.origin ?? this.#originOf();
138
143
  const started = Date.now();
139
144
  try {
140
145
  const result = await withTimeout(this.#invoke(method, [...params]), this.#timeoutMs, () => connectionError(`${this.description}: ${method} timed out after ${String(this.#timeoutMs)} ms`));
141
- this.#record(method, params, started, { ok: true, result });
146
+ this.#record(method, params, started, origin, { ok: true, result });
142
147
  return result;
143
148
  }
144
149
  catch (error) {
145
150
  const failure = this.#asBackendError(method, error);
146
- this.#record(method, params, started, { ok: false, error: failure.message });
151
+ this.#record(method, params, started, origin, { ok: false, error: failure.message });
147
152
  throw failure;
148
153
  }
149
154
  }
@@ -186,7 +191,7 @@ export class RpcClient {
186
191
  const message = error instanceof Error ? error.message : String(error);
187
192
  return connectionError(`${this.description}: ${method} failed: ${message}`, error);
188
193
  }
189
- #record(method, params, started, outcome) {
194
+ #record(method, params, started, origin, outcome) {
190
195
  this.#onCall({
191
196
  interfaceName: this.name,
192
197
  method,
@@ -196,6 +201,7 @@ export class RpcClient {
196
201
  ...(outcome.error === undefined ? {} : { error: outcome.error }),
197
202
  durationMs: Date.now() - started,
198
203
  timestamp: started,
204
+ origin,
199
205
  });
200
206
  }
201
207
  }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The session RPC log: every outgoing call of the session, and the write list that decides what
3
+ * is paced.
4
+ *
5
+ * 2.x had no log at all except the debug option `rpcLogFolder` (`main.js:830-838`), which dumped
6
+ * every `putParamset` as a JSON file. That option is the right forensic tool for a device that
7
+ * ended up in `CONFIG_PENDING` and it is kept, verbatim in file name and content. On top of it
8
+ * every write was recorded in a ring the UI shows and exports (task 6, item 5) - and since task 48
9
+ * every call is: the maintainer wants to see *every* outgoing RPC call in the one global log, the
10
+ * reads, the keep-alive and the `init`s included, each with where it came from (`origin`) so the
11
+ * background traffic can be hidden rather than left out.
12
+ *
13
+ * What bounds it, because the keep-alive runs every few seconds on several interfaces and a
14
+ * `listDevices` answer is hundreds of kilobytes: a ring of {@link DEFAULT_CAPACITY} entries, a
15
+ * byte budget over the whole ring ({@link DEFAULT_BUDGET_BYTES}) that evicts the oldest entries
16
+ * first, and a cap per answer ({@link DEFAULT_RESULT_CAP_BYTES}) above which only a preview and
17
+ * the size are kept. Parameters are never cut - a write's parameters are what went on the wire -
18
+ * but the secrets among them are: a passphrase or a device key has no business in a log.
19
+ *
20
+ * Only the writes are persisted (`write-log.json`, as before task 48): they are the history that
21
+ * matters after a restart; the reads of the previous session are noise, and a CCU's flash does not
22
+ * want a `ping` written to it every few seconds.
23
+ *
24
+ * Which calls count as writes is a fixed list: it decides what is paced (`WriteQueue`), what is
25
+ * persisted, and what a `rpc.call` from the console is allowed to do without a warning.
26
+ */
27
+ import type { RpcLogEntry, RpcValue } from '@homematic-manager/core';
28
+ import type { RpcCallRecord } from './client.js';
29
+ /**
30
+ * The RPC methods that change something on the CCU or on a device.
31
+ *
32
+ * Everything not in here is a read: it bypasses the write queue and is not persisted. The list is
33
+ * the eQ-3 method catalogue of the core (`RPC_METHODS`) filtered by hand - `getLinks` and
34
+ * `rssiInfo` read, `addLink` and `setBidcosInterface` do not.
35
+ */
36
+ export declare const WRITE_METHODS: ReadonlySet<string>;
37
+ /** Does this method change something? */
38
+ export declare function isWriteMethod(method: string): boolean;
39
+ /** How many entries the ring keeps. */
40
+ export declare const DEFAULT_CAPACITY = 1000;
41
+ /** The byte budget of the whole ring, measured as the JSON length of its entries. */
42
+ export declare const DEFAULT_BUDGET_BYTES: number;
43
+ /** An answer bigger than this is kept as a preview with its size. */
44
+ export declare const DEFAULT_RESULT_CAP_BYTES: number;
45
+ /** What a secret is logged as. */
46
+ export declare const REDACTED = "***";
47
+ /**
48
+ * The parameters as the log keeps them. A struct with a `KEY` in it is a whitelist entry of
49
+ * `setInstallModeWithWhitelist` (the HmIP device key from the sticker); the key goes, the rest -
50
+ * `ADDRESS`, `KEY_MODE` - stays, so the log still says which device was admitted.
51
+ */
52
+ export declare function redactParams(method: string, params: readonly RpcValue[]): RpcValue[];
53
+ export interface RpcLogOptions {
54
+ /** How many entries the ring keeps. */
55
+ readonly capacity?: number;
56
+ /** The byte budget of the ring; the oldest entries go when it is exceeded. */
57
+ readonly budgetBytes?: number;
58
+ /** An answer over this many bytes of JSON is kept as a preview with its size. */
59
+ readonly resultCapBytes?: number;
60
+ /** `<cache>/write-log.json`; omit to keep the log in memory only. Only the writes go there. */
61
+ readonly file?: string;
62
+ /** The 2.x `rpcLogFolder`: every `putParamset` is dumped there as JSON. Empty = off. */
63
+ readonly rpcLogFolder?: string;
64
+ readonly onAppended?: (entry: RpcLogEntry) => void;
65
+ readonly onError?: (error: unknown) => void;
66
+ readonly writeDelayMs?: number;
67
+ }
68
+ /** Every outgoing RPC call of this session, newest last. */
69
+ export declare class RpcLog {
70
+ #private;
71
+ constructor(options?: RpcLogOptions);
72
+ /** The folder the `putParamset` dumps go to; empty switches them off. */
73
+ setRpcLogFolder(folder: string): void;
74
+ /** Records one finished call and returns its entry. */
75
+ append(record: RpcCallRecord): RpcLogEntry;
76
+ /** The newest `limit` entries, oldest first. */
77
+ list(limit?: number): RpcLogEntry[];
78
+ clear(): void;
79
+ get size(): number;
80
+ /** The JSON length of everything in the ring - what the budget is measured against. */
81
+ get bytes(): number;
82
+ /** Reads the writes of the previous session, if there is a file. */
83
+ load(): Promise<void>;
84
+ flush(): Promise<void>;
85
+ }
86
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1,241 @@
1
+ /**
2
+ * The session RPC log: every outgoing call of the session, and the write list that decides what
3
+ * is paced.
4
+ *
5
+ * 2.x had no log at all except the debug option `rpcLogFolder` (`main.js:830-838`), which dumped
6
+ * every `putParamset` as a JSON file. That option is the right forensic tool for a device that
7
+ * ended up in `CONFIG_PENDING` and it is kept, verbatim in file name and content. On top of it
8
+ * every write was recorded in a ring the UI shows and exports (task 6, item 5) - and since task 48
9
+ * every call is: the maintainer wants to see *every* outgoing RPC call in the one global log, the
10
+ * reads, the keep-alive and the `init`s included, each with where it came from (`origin`) so the
11
+ * background traffic can be hidden rather than left out.
12
+ *
13
+ * What bounds it, because the keep-alive runs every few seconds on several interfaces and a
14
+ * `listDevices` answer is hundreds of kilobytes: a ring of {@link DEFAULT_CAPACITY} entries, a
15
+ * byte budget over the whole ring ({@link DEFAULT_BUDGET_BYTES}) that evicts the oldest entries
16
+ * first, and a cap per answer ({@link DEFAULT_RESULT_CAP_BYTES}) above which only a preview and
17
+ * the size are kept. Parameters are never cut - a write's parameters are what went on the wire -
18
+ * but the secrets among them are: a passphrase or a device key has no business in a log.
19
+ *
20
+ * Only the writes are persisted (`write-log.json`, as before task 48): they are the history that
21
+ * matters after a restart; the reads of the previous session are noise, and a CCU's flash does not
22
+ * want a `ping` written to it every few seconds.
23
+ *
24
+ * Which calls count as writes is a fixed list: it decides what is paced (`WriteQueue`), what is
25
+ * persisted, and what a `rpc.call` from the console is allowed to do without a warning.
26
+ */
27
+ import fs from 'node:fs/promises';
28
+ import path from 'node:path';
29
+ import { DebouncedJsonFile } from '../util/jsonFile.js';
30
+ /**
31
+ * The RPC methods that change something on the CCU or on a device.
32
+ *
33
+ * Everything not in here is a read: it bypasses the write queue and is not persisted. The list is
34
+ * the eQ-3 method catalogue of the core (`RPC_METHODS`) filtered by hand - `getLinks` and
35
+ * `rssiInfo` read, `addLink` and `setBidcosInterface` do not.
36
+ */
37
+ export const WRITE_METHODS = new Set([
38
+ 'abortDeleteDevice',
39
+ 'activateLinkParamset',
40
+ 'addDevice',
41
+ 'addLink',
42
+ 'addVirtualDeviceInstance',
43
+ 'changeKey',
44
+ 'clearConfigCache',
45
+ 'deleteDevice',
46
+ 'determineParameter',
47
+ 'installFirmware',
48
+ 'putParamset',
49
+ 'removeLink',
50
+ 'replaceDevice',
51
+ 'reportValueUsage',
52
+ 'restoreConfigToDevice',
53
+ 'searchDevices',
54
+ 'setBidcosInterface',
55
+ 'setInstallMode',
56
+ 'setInstallModeWithWhitelist',
57
+ 'setInterfaceClock',
58
+ 'setLinkInfo',
59
+ 'setMetadata',
60
+ 'setRFLGWInfoLED',
61
+ 'setTeam',
62
+ 'setTempKey',
63
+ 'setValue',
64
+ 'updateFirmware',
65
+ ]);
66
+ /** Does this method change something? */
67
+ export function isWriteMethod(method) {
68
+ return WRITE_METHODS.has(method);
69
+ }
70
+ /** How many entries the ring keeps. */
71
+ export const DEFAULT_CAPACITY = 1000;
72
+ /** The byte budget of the whole ring, measured as the JSON length of its entries. */
73
+ export const DEFAULT_BUDGET_BYTES = 4 * 1024 * 1024;
74
+ /** An answer bigger than this is kept as a preview with its size. */
75
+ export const DEFAULT_RESULT_CAP_BYTES = 8 * 1024;
76
+ /** How much of a capped answer the preview shows. */
77
+ const PREVIEW_CHARS = 512;
78
+ /** What a secret is logged as. */
79
+ export const REDACTED = '***';
80
+ /**
81
+ * Methods whose every parameter is a secret: `changeKey(passphrase)` sets the BidCos security key,
82
+ * `setTempKey(passphrase)` the temporary one for pairing.
83
+ */
84
+ const SECRET_METHODS = new Set(['changeKey', 'setTempKey']);
85
+ /**
86
+ * The parameters as the log keeps them. A struct with a `KEY` in it is a whitelist entry of
87
+ * `setInstallModeWithWhitelist` (the HmIP device key from the sticker); the key goes, the rest -
88
+ * `ADDRESS`, `KEY_MODE` - stays, so the log still says which device was admitted.
89
+ */
90
+ export function redactParams(method, params) {
91
+ if (SECRET_METHODS.has(method)) {
92
+ return params.map(() => REDACTED);
93
+ }
94
+ if (method === 'setInstallModeWithWhitelist') {
95
+ return params.map((param) => redactKeys(param));
96
+ }
97
+ return [...params];
98
+ }
99
+ function redactKeys(value) {
100
+ if (Array.isArray(value)) {
101
+ return value.map((entry) => redactKeys(entry));
102
+ }
103
+ if (typeof value === 'object') {
104
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, key === 'KEY' ? REDACTED : entry]));
105
+ }
106
+ return value;
107
+ }
108
+ /** Every outgoing RPC call of this session, newest last. */
109
+ export class RpcLog {
110
+ #capacity;
111
+ #budgetBytes;
112
+ #resultCapBytes;
113
+ /** Oldest first; a plain array rather than the core's ring, because the byte budget evicts from the front too. */
114
+ #entries = [];
115
+ #bytes = 0;
116
+ #file;
117
+ #onAppended;
118
+ #onError;
119
+ #rpcLogFolder;
120
+ #nextId = 1;
121
+ constructor(options = {}) {
122
+ this.#capacity = options.capacity ?? DEFAULT_CAPACITY;
123
+ this.#budgetBytes = options.budgetBytes ?? DEFAULT_BUDGET_BYTES;
124
+ this.#resultCapBytes = options.resultCapBytes ?? DEFAULT_RESULT_CAP_BYTES;
125
+ this.#onAppended = options.onAppended ?? (() => undefined);
126
+ this.#onError = options.onError ?? (() => undefined);
127
+ this.#rpcLogFolder = options.rpcLogFolder ?? '';
128
+ this.#file =
129
+ options.file === undefined
130
+ ? undefined
131
+ : new DebouncedJsonFile(options.file, {
132
+ delayMs: options.writeDelayMs ?? 500,
133
+ ...(options.onError ? { onError: options.onError } : {}),
134
+ });
135
+ }
136
+ /** The folder the `putParamset` dumps go to; empty switches them off. */
137
+ setRpcLogFolder(folder) {
138
+ this.#rpcLogFolder = folder;
139
+ }
140
+ /** Records one finished call and returns its entry. */
141
+ append(record) {
142
+ const entry = {
143
+ id: this.#nextId,
144
+ timestamp: record.timestamp,
145
+ interfaceName: record.interfaceName,
146
+ method: record.method,
147
+ // an explicit-double wrapper is JSON like everything else; the log keeps it verbatim
148
+ // so that a CONFIG_PENDING can be traced back to exactly what went on the wire
149
+ params: redactParams(record.method, record.params),
150
+ ok: record.ok,
151
+ ...this.#resultOf(record.result),
152
+ ...(record.error === undefined ? {} : { error: record.error }),
153
+ durationMs: record.durationMs,
154
+ origin: record.origin,
155
+ };
156
+ this.#nextId += 1;
157
+ this.#push(entry);
158
+ if (isWriteMethod(entry.method)) {
159
+ this.#persist();
160
+ }
161
+ this.#onAppended(entry);
162
+ void this.#dump(record);
163
+ return entry;
164
+ }
165
+ /** The newest `limit` entries, oldest first. */
166
+ list(limit) {
167
+ const entries = this.#entries.map((item) => item.entry);
168
+ return limit === undefined || limit >= entries.length ? entries : entries.slice(entries.length - limit);
169
+ }
170
+ clear() {
171
+ this.#entries = [];
172
+ this.#bytes = 0;
173
+ this.#persist();
174
+ }
175
+ get size() {
176
+ return this.#entries.length;
177
+ }
178
+ /** The JSON length of everything in the ring - what the budget is measured against. */
179
+ get bytes() {
180
+ return this.#bytes;
181
+ }
182
+ /** Reads the writes of the previous session, if there is a file. */
183
+ async load() {
184
+ const stored = await this.#file?.read();
185
+ if (!Array.isArray(stored)) {
186
+ return;
187
+ }
188
+ for (const entry of stored) {
189
+ if (typeof entry === 'object' && entry !== null && typeof entry.id === 'number') {
190
+ // a file from before task 48 has no origin; a persisted entry is a write, and a
191
+ // write the user did not ask for does not exist
192
+ const stored = entry;
193
+ const loaded = { ...stored, origin: stored.origin ?? 'ui' };
194
+ this.#push(loaded);
195
+ this.#nextId = Math.max(this.#nextId, loaded.id + 1);
196
+ }
197
+ }
198
+ }
199
+ async flush() {
200
+ await this.#file?.flush();
201
+ }
202
+ #push(entry) {
203
+ const bytes = JSON.stringify(entry).length;
204
+ this.#entries.push({ entry, bytes });
205
+ this.#bytes += bytes;
206
+ while (this.#entries.length > this.#capacity || (this.#bytes > this.#budgetBytes && this.#entries.length > 1)) {
207
+ const evicted = this.#entries.shift();
208
+ if (evicted) {
209
+ this.#bytes -= evicted.bytes;
210
+ }
211
+ }
212
+ }
213
+ /** The answer as the entry keeps it: whole under the cap, a preview and its size above. */
214
+ #resultOf(result) {
215
+ if (result === undefined) {
216
+ return {};
217
+ }
218
+ const json = JSON.stringify(result);
219
+ if (json.length <= this.#resultCapBytes) {
220
+ return { result };
221
+ }
222
+ return { result: `${json.slice(0, PREVIEW_CHARS)}…`, resultBytes: json.length };
223
+ }
224
+ #persist() {
225
+ this.#file?.save(this.#entries.map((item) => item.entry).filter((entry) => isWriteMethod(entry.method)));
226
+ }
227
+ /** The 2.x dump: `<folder>/<epoch ms>_<interface>_<method>.json` with the parameters. */
228
+ async #dump(record) {
229
+ if (this.#rpcLogFolder === '' || record.method !== 'putParamset') {
230
+ return;
231
+ }
232
+ const file = path.join(this.#rpcLogFolder, `${String(record.timestamp)}_${record.interfaceName}_${record.method}.json`);
233
+ try {
234
+ await fs.writeFile(file, JSON.stringify(record.params, null, ' '), 'utf8');
235
+ }
236
+ catch (error) {
237
+ this.#onError(error);
238
+ }
239
+ }
240
+ }
241
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Who asked for an RPC call: the console, a UI action, or the backend on its own.
3
+ *
4
+ * Task 48: the RPC log shows every outgoing call, and the background ones - `init`, `ping`, the
5
+ * service-message poll, the sweeps after an event - can be frequent, so every entry carries where
6
+ * it came from and the drawer can hide them. The origin is a property of the call chain, not of a
7
+ * function: `#refreshDevices` runs for the Devices tab's refresh button and for a `newDevices`
8
+ * callback alike. Threading a parameter through the forty call sites of `#read`/`#write` and every
9
+ * helper between them would change signatures for nothing, so the origin travels in Node's
10
+ * `AsyncLocalStorage`: `Backend.request` opens a `ui` (or `console`) context, and whatever is
11
+ * awaited inside it inherits that.
12
+ *
13
+ * Two things must be explicit, and the tests hold them:
14
+ * - Anything long-lived that is *created* inside a request would inherit its context forever - the
15
+ * watchdog interval, the callback servers, the polling timers after a `config.set`. The places
16
+ * that create such things run under `background` on purpose (`Backend.#connect`, the timers, the
17
+ * manager's callbacks), and the manager's own calls name their origin outright.
18
+ * - A queued write runs from the timer that drained the queue, which is the *previous* task's
19
+ * context; `Backend.#write` therefore captures the origin at enqueue time.
20
+ *
21
+ * The RPC client itself does not read this module: it takes a resolver (`originOf`) and an
22
+ * explicit origin per call, so the logging layer stays free of Node (task 52 wants the same engine
23
+ * in a browser host one day, where an explicit parameter would replace the storage).
24
+ */
25
+ import type { RpcOrigin } from '@homematic-manager/core';
26
+ /** The origin of the current call chain; `background` outside of any request. */
27
+ export declare function currentOrigin(): RpcOrigin;
28
+ /** Runs `fn` with `origin` as the answer of {@link currentOrigin} for everything it awaits. */
29
+ export declare function runWithOrigin<T>(origin: RpcOrigin, fn: () => T): T;
30
+ //# sourceMappingURL=origin.d.ts.map