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
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { realpathSync } from 'node:fs';
12
12
  import { pathToFileURL } from 'node:url';
13
+ import { isLoopbackHost } from './auth.js';
13
14
  import { createWebHost } from './server.js';
14
15
  import { installService, uninstallService } from './install.js';
15
16
  import { createLogger } from './log.js';
@@ -93,11 +94,26 @@ export async function runCli(options = {}) {
93
94
  log.debug(`token ${host.token}`);
94
95
  }
95
96
  log.debug(`a client without a cookie can use ${host.url}?token=${host.token}`);
97
+ if (host.token !== undefined && host.issueCookie && !isLoopbackHost(parsed.host)) {
98
+ // D-41: the Docker image binds 0.0.0.0 and issues the cookie so that the UI works out of
99
+ // the box - which means whoever reaches the port is in. Said once, at start, with the ways
100
+ // out; the host cannot see a proxy or TLS in front of it, so it says this whenever it is
101
+ // in that position
102
+ log.warn(`the token cookie is handed to every browser that loads the page on ${parsed.host}: whoever reaches this port is in;` +
103
+ ' put a proxy with authentication and TLS in front, publish the port on the loopback only,' +
104
+ ' or set HMM_ISSUE_COOKIE=false and open the UI once with ?token=');
105
+ }
96
106
  if (parsed.authMode === 'rega') {
97
107
  // D-32: the token still works and `settings.cgi` still hands it out - the login is a second
98
108
  // door, not a replacement, and saying so keeps the addon's two paths straight in the log
99
109
  log.info(`login: CCU credentials required (ReGa), sessions last ${String(Math.round(parsed.sessionTtlMs / 3_600_000))} h of inactivity; the token cookie of settings.cgi is still accepted`);
100
110
  }
111
+ if (parsed.authMode === 'occulite') {
112
+ // D-40: no form, no CCU users - the box decides who may in, and this host only checks that
113
+ // the session it was handed is one the box still knows
114
+ log.info(`login: the session openccu-lite hands over is checked against ${parsed.occuliteUrl ?? 'http://127.0.0.1'};` +
115
+ ' the token cookie of settings.cgi is still accepted');
116
+ }
101
117
  if (parsed.uiDevServer !== undefined) {
102
118
  log.info(`development mode: everything but the api is proxied to ${parsed.uiDevServer}`);
103
119
  }
@@ -147,11 +163,19 @@ function startHost(values, log, version) {
147
163
  ...(values.issueCookie === undefined ? {} : { issueCookie: values.issueCookie }),
148
164
  authMode: values.authMode,
149
165
  sessionTtlMs: values.sessionTtlMs,
166
+ ...(values.occuliteUrl === undefined ? {} : { occuliteUrl: values.occuliteUrl }),
150
167
  ...(values.ccu === undefined ? {} : { ccu: values.ccu }),
151
168
  ...(values.local === undefined ? {} : { local: values.local }),
152
169
  ...(values.callbackIp === undefined ? {} : { callbackIp: values.callbackIp }),
153
170
  ...(values.callbackXmlrpcPort === undefined ? {} : { callbackXmlrpcPort: values.callbackXmlrpcPort }),
154
171
  ...(values.callbackBinrpcPort === undefined ? {} : { callbackBinrpcPort: values.callbackBinrpcPort }),
172
+ ...(values.inContainer ? { inContainer: true } : {}),
173
+ ...(values.callbackXmlrpcDefaultPort === undefined
174
+ ? {}
175
+ : { callbackXmlrpcDefaultPort: values.callbackXmlrpcDefaultPort }),
176
+ ...(values.callbackBinrpcDefaultPort === undefined
177
+ ? {}
178
+ : { callbackBinrpcDefaultPort: values.callbackBinrpcDefaultPort }),
155
179
  idleUnsubscribeMs: values.idleUnsubscribeMs,
156
180
  });
157
181
  }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * D-40: the login of the addon when it runs on openccu-lite.
3
+ *
4
+ * The CCU case (D-32, task 18) is unchanged and untouched: a user name and a password, checked
5
+ * against ReGa's user list and the CCU's authentication daemon on UDP 1998, both loopback-only.
6
+ * openccu-lite has neither - there is no ReGaHSS, and its own users live in `occulited` - so the
7
+ * question is a different one, and so is the answer.
8
+ *
9
+ * On openccu-lite nobody logs in *here*. The box's shell has already done it, lighttpd's gate has
10
+ * already refused everyone who has no session (their D-28), and the shell opens an addon page with
11
+ * the session on the URL: `?sid=@xxxxxxxxxx@`, the CCU convention. On an image from before their
12
+ * task 125 that session id **is** a valid credential for the box's own APIs, so the check is one
13
+ * request (on a newer image the shell sends the gate's `X-Occulite-Session` instead, see below):
14
+ *
15
+ * ```
16
+ * GET /api/meta/v1/enums Authorization: Bearer <sid> -> 200 valid, 401 not
17
+ * ```
18
+ *
19
+ * and nothing else is believed. The name of the user behind it comes from `/api/auth/v1/state`
20
+ * afterwards, best effort, only so that the UI can show who is logged in - a box that answers the
21
+ * first call and not the second still lets the session in.
22
+ *
23
+ * The metadata API and not the auth API for the decision, on purpose: it is the one openccu-lite
24
+ * declares normative (their D-16), it is the API this application depends on anyway, and a session
25
+ * that cannot read the metadata store is of no use to a device manager whatever else it can do.
26
+ */
27
+ /** What the check answers with: who it is, in the shape the rest of the host already speaks. */
28
+ export interface OcculiteSession {
29
+ readonly name: string;
30
+ /**
31
+ * ReGa's `UserLevel()`, so that `SessionInfo` keeps one meaning across the two boxes: 8 for an
32
+ * administrator, 2 for a user. openccu-lite has exactly those two roles.
33
+ */
34
+ readonly level: number;
35
+ /** The session id, which is also the credential every write to the box goes out with. */
36
+ readonly sid: string;
37
+ }
38
+ export interface OcculiteAuthOptions {
39
+ /** `http://127.0.0.1` on the box; a test points it at its own server. */
40
+ readonly baseUrl: string;
41
+ readonly timeoutMs?: number;
42
+ readonly fetch?: typeof globalThis.fetch;
43
+ readonly onNotice?: (level: 'info' | 'warn' | 'error', message: string) => void;
44
+ }
45
+ export declare const OCCULITE_TIMEOUT_MS = 5000;
46
+ /**
47
+ * B-94, D-65: the header openccu-lite's lighttpd gate sets on every request it lets through under
48
+ * `/addons/`, to the bare id of the session it found (from either session cookie or `?sid=`). The
49
+ * gate removes a copy the client sent first. A CCU has no gate and passes a client's header through,
50
+ * an older openccu-lite image does the same, and the backend's port is open to every process on the
51
+ * box - so the header is only a claim, and the box is asked about it like about any other session.
52
+ */
53
+ export declare const OCCULITE_SESSION_HEADER = "x-occulite-session";
54
+ export interface OcculiteCheckOptions {
55
+ /**
56
+ * The addon contract's validation (openccu-lite `docs/PORTING-PROMPT.md`): the session only
57
+ * counts when `GET /api/auth/v1/state` confirms it. The `?sid=` hand-over keeps the D-40 check,
58
+ * where that second answer only supplies the name.
59
+ */
60
+ readonly requireState?: boolean;
61
+ }
62
+ /** A session id as the shell or the gate hands it over, `@…@` and all, or `undefined` when it is not one. */
63
+ export declare function parseSid(value: string | null | undefined): string | undefined;
64
+ /**
65
+ * B-36: can the `?sid=` of a request be a session of the box whose gate named `gate`?
66
+ *
67
+ * A gate header of the 26-character shape says the box issues session ids of that shape (task 125),
68
+ * so a `?sid=` of any other shape there is the session's legacy alias, which the box's API refuses:
69
+ * the answer is known without asking. Without such a header - a CCU, an image from before the header,
70
+ * a ten-character header - nothing can be concluded, and the box decides as before. This only ever
71
+ * *refuses*: a `true` still has to be confirmed by the box.
72
+ */
73
+ export declare function sidCanBeSession(sid: string, gate: string | undefined): boolean;
74
+ /** Checks a session id against the box that issued it. */
75
+ export declare class OcculiteAuthenticator {
76
+ #private;
77
+ constructor(options: OcculiteAuthOptions);
78
+ get baseUrl(): string;
79
+ /**
80
+ * The session, or `undefined` - which is the answer for an unknown id, an expired one and a box
81
+ * that cannot be asked alike. A caller cannot tell them apart, and therefore cannot use this to
82
+ * find out whether a session exists.
83
+ */
84
+ check(rawSid: string | null | undefined, options?: OcculiteCheckOptions): Promise<OcculiteSession | undefined>;
85
+ }
86
+ //# sourceMappingURL=occulite.d.ts.map
@@ -0,0 +1,140 @@
1
+ /**
2
+ * D-40: the login of the addon when it runs on openccu-lite.
3
+ *
4
+ * The CCU case (D-32, task 18) is unchanged and untouched: a user name and a password, checked
5
+ * against ReGa's user list and the CCU's authentication daemon on UDP 1998, both loopback-only.
6
+ * openccu-lite has neither - there is no ReGaHSS, and its own users live in `occulited` - so the
7
+ * question is a different one, and so is the answer.
8
+ *
9
+ * On openccu-lite nobody logs in *here*. The box's shell has already done it, lighttpd's gate has
10
+ * already refused everyone who has no session (their D-28), and the shell opens an addon page with
11
+ * the session on the URL: `?sid=@xxxxxxxxxx@`, the CCU convention. On an image from before their
12
+ * task 125 that session id **is** a valid credential for the box's own APIs, so the check is one
13
+ * request (on a newer image the shell sends the gate's `X-Occulite-Session` instead, see below):
14
+ *
15
+ * ```
16
+ * GET /api/meta/v1/enums Authorization: Bearer <sid> -> 200 valid, 401 not
17
+ * ```
18
+ *
19
+ * and nothing else is believed. The name of the user behind it comes from `/api/auth/v1/state`
20
+ * afterwards, best effort, only so that the UI can show who is logged in - a box that answers the
21
+ * first call and not the second still lets the session in.
22
+ *
23
+ * The metadata API and not the auth API for the decision, on purpose: it is the one openccu-lite
24
+ * declares normative (their D-16), it is the API this application depends on anyway, and a session
25
+ * that cannot read the metadata store is of no use to a device manager whatever else it can do.
26
+ */
27
+ export const OCCULITE_TIMEOUT_MS = 5000;
28
+ /**
29
+ * B-94, D-65: the header openccu-lite's lighttpd gate sets on every request it lets through under
30
+ * `/addons/`, to the bare id of the session it found (from either session cookie or `?sid=`). The
31
+ * gate removes a copy the client sent first. A CCU has no gate and passes a client's header through,
32
+ * an older openccu-lite image does the same, and the backend's port is open to every process on the
33
+ * box - so the header is only a claim, and the box is asked about it like about any other session.
34
+ */
35
+ export const OCCULITE_SESSION_HEADER = 'x-occulite-session';
36
+ /**
37
+ * B-36: what an openccu-lite session id looks like, and nothing else is one.
38
+ *
39
+ * - **Since openccu-lite task 125** (occulited `1f71597`, their D-77) a session id is what Go's
40
+ * `crypto/rand.Text` hands out: 26 characters of base32, `A-Z` and `2-7`. The box's API and its gate
41
+ * match exactly this (`sidRe`, the gate's `SID_PATTERN`).
42
+ * - **Before task 125** it was the CCU's shape, ten of `[0-9a-zA-Z]` (auth-off's fixed id too). Since
43
+ * then that shape is only a session's *legacy alias*, which the addon CGIs parse and the box's API
44
+ * refuses as a credential from anywhere. The two cannot be told apart by their characters: a
45
+ * ten-character value is a session on an older box and never one on a newer box.
46
+ * - **An API token** (`olt_` and 32 hex digits) is neither and is never a session: the header path
47
+ * asks `/api/auth/v1/state`, which also answers for a token.
48
+ *
49
+ * A value of another shape or length cannot be an openccu-lite session on any image, so it is refused
50
+ * here, without asking the box.
51
+ */
52
+ const SESSION_ID = /^[A-Z2-7]{26}$/;
53
+ const LEGACY_ID = /^[0-9a-zA-Z]{10}$/;
54
+ /** A session id as the shell or the gate hands it over, `@…@` and all, or `undefined` when it is not one. */
55
+ export function parseSid(value) {
56
+ const bare = (value ?? '').replace(/^@|@$/g, '').trim();
57
+ return SESSION_ID.test(bare) || LEGACY_ID.test(bare) ? bare : undefined;
58
+ }
59
+ /**
60
+ * B-36: can the `?sid=` of a request be a session of the box whose gate named `gate`?
61
+ *
62
+ * A gate header of the 26-character shape says the box issues session ids of that shape (task 125),
63
+ * so a `?sid=` of any other shape there is the session's legacy alias, which the box's API refuses:
64
+ * the answer is known without asking. Without such a header - a CCU, an image from before the header,
65
+ * a ten-character header - nothing can be concluded, and the box decides as before. This only ever
66
+ * *refuses*: a `true` still has to be confirmed by the box.
67
+ */
68
+ export function sidCanBeSession(sid, gate) {
69
+ return gate === undefined || !SESSION_ID.test(gate) || SESSION_ID.test(sid);
70
+ }
71
+ /** Checks a session id against the box that issued it. */
72
+ export class OcculiteAuthenticator {
73
+ #options;
74
+ #fetch;
75
+ constructor(options) {
76
+ this.#options = options;
77
+ this.#fetch = options.fetch ?? globalThis.fetch;
78
+ }
79
+ get baseUrl() {
80
+ return this.#options.baseUrl.replace(/\/$/, '');
81
+ }
82
+ /**
83
+ * The session, or `undefined` - which is the answer for an unknown id, an expired one and a box
84
+ * that cannot be asked alike. A caller cannot tell them apart, and therefore cannot use this to
85
+ * find out whether a session exists.
86
+ */
87
+ async check(rawSid, options = {}) {
88
+ const sid = parseSid(rawSid);
89
+ if (sid === undefined) {
90
+ return undefined;
91
+ }
92
+ try {
93
+ const response = await this.#fetch(`${this.baseUrl}/api/meta/v1/enums`, {
94
+ signal: AbortSignal.timeout(this.#options.timeoutMs ?? OCCULITE_TIMEOUT_MS),
95
+ headers: { Authorization: `Bearer ${sid}`, Accept: 'application/json' },
96
+ });
97
+ if (response.status === 401 || response.status === 403) {
98
+ return undefined;
99
+ }
100
+ if (!response.ok) {
101
+ this.#options.onNotice?.('warn', `the box answered ${String(response.status)} for a session check`);
102
+ return undefined;
103
+ }
104
+ }
105
+ catch (error) {
106
+ this.#options.onNotice?.('warn', `the box could not be asked about a session: ${String(error)}`);
107
+ return undefined;
108
+ }
109
+ const who = await this.#who(sid);
110
+ if (who === undefined && options.requireState === true) {
111
+ this.#options.onNotice?.('warn', 'the box did not confirm the session of the X-Occulite-Session header');
112
+ return undefined;
113
+ }
114
+ return { sid, name: who?.name ?? 'openccu-lite', level: who?.level ?? 2 };
115
+ }
116
+ /**
117
+ * Who the session belongs to. Best effort: the session is already valid at this point, and a
118
+ * name that could not be read costs a label in the header and nothing else.
119
+ */
120
+ async #who(sid) {
121
+ try {
122
+ const response = await this.#fetch(`${this.baseUrl}/api/auth/v1/state`, {
123
+ signal: AbortSignal.timeout(this.#options.timeoutMs ?? OCCULITE_TIMEOUT_MS),
124
+ headers: { Authorization: `Bearer ${sid}`, Accept: 'application/json' },
125
+ });
126
+ if (!response.ok) {
127
+ return undefined;
128
+ }
129
+ const body = (await response.json());
130
+ if (body.authenticated !== true || typeof body.user !== 'string') {
131
+ return undefined;
132
+ }
133
+ return { name: body.user, level: body.role === 'admin' ? 8 : 2 };
134
+ }
135
+ catch {
136
+ return undefined;
137
+ }
138
+ }
139
+ }
140
+ //# sourceMappingURL=occulite.js.map
package/dist/options.d.ts CHANGED
@@ -19,11 +19,15 @@ export declare const ENV_PREFIX = "HMM";
19
19
  */
20
20
  export declare const DEFAULT_IDLE_UNSUBSCRIBE = "5m";
21
21
  /**
22
- * D-32: how a browser is let in. `token` is what every install type but the CCU addon uses, and
22
+ * D-32/D-40: how a browser is let in. `token` is what every install type but the CCU addon uses, and
23
23
  * stays the default there too - the addon's `settings.cgi` hand-over is the primary path and the
24
24
  * login is switched on by hand in `etc/hmm.env` or from the addon's settings page.
25
+ *
26
+ * `occulite` is the openccu-lite form of the same idea: no form of our own, the box's shell hands
27
+ * the session over on the URL and the host checks it against the box. The addon's `update_script`
28
+ * sets it by itself when it is installed on a box (`VARIANT=lite` in `/VERSION`).
25
29
  */
26
- export declare const AUTH_MODES: readonly ["token", "rega"];
30
+ export declare const AUTH_MODES: readonly ["token", "rega", "occulite"];
27
31
  /** D-32: a login lasts a day of not being used. Sliding, so a tab in use never expires. */
28
32
  export declare const DEFAULT_SESSION_TTL = "24h";
29
33
  /**
@@ -34,7 +38,7 @@ export declare const DEFAULT_SESSION_TTL = "24h";
34
38
  * rather than a silently disabled feature.
35
39
  */
36
40
  export declare function parseDuration(value: string, option: string): number;
37
- /** Is this one of the two modes? `parseRaw` has already refused everything else. */
41
+ /** Is this one of the three modes? `parseRaw` has already refused everything else. */
38
42
  export declare function isAuthMode(value: string | undefined): value is AuthMode;
39
43
  export type OptionType = 'string' | 'number' | 'boolean';
40
44
  export interface OptionDefinition {
@@ -102,13 +106,18 @@ export declare const OPTIONS: {
102
106
  };
103
107
  readonly 'auth-mode': {
104
108
  readonly type: "string";
105
- readonly describe: "token: the token guards the api. rega: ask for a CCU login first (addon only, needs --local)";
106
- readonly choices: readonly ["token", "rega"];
109
+ readonly describe: string;
110
+ readonly choices: readonly ["token", "rega", "occulite"];
107
111
  readonly default: "token";
108
112
  };
113
+ readonly 'occulite-url': {
114
+ readonly type: "string";
115
+ readonly describe: "with --auth-mode occulite: the box that issued the session";
116
+ readonly defaultDescription: "http://127.0.0.1";
117
+ };
109
118
  readonly 'session-ttl': {
110
119
  readonly type: "string";
111
- readonly describe: "with --auth-mode rega: how long a login lasts without being used (24h, 90m, ...)";
120
+ readonly describe: "with --auth-mode rega or occulite: how long a login lasts without being used (24h, 90m, ...)";
112
121
  readonly default: "24h";
113
122
  };
114
123
  readonly ccu: {
@@ -135,6 +144,21 @@ export declare const OPTIONS: {
135
144
  readonly describe: "fixed port for the binrpc callback server; must differ from the xmlrpc one";
136
145
  readonly defaultDescription: "0, a free port";
137
146
  };
147
+ readonly 'callback-xmlrpc-default-port': {
148
+ readonly type: "number";
149
+ readonly describe: "xmlrpc callback port while the configured one is 0; a free port when it is taken (the CCU addon sets 2031)";
150
+ readonly defaultDescription: "none, a free port";
151
+ };
152
+ readonly 'callback-binrpc-default-port': {
153
+ readonly type: "number";
154
+ readonly describe: "binrpc callback port while the configured one is 0; a free port when it is taken (the CCU addon sets 2032)";
155
+ readonly defaultDescription: "none, a free port";
156
+ };
157
+ readonly 'in-container': {
158
+ readonly type: "boolean";
159
+ readonly describe: "we run in a container (the image sets it): the interface popup says to publish the callback ports unchanged";
160
+ readonly default: false;
161
+ };
138
162
  readonly 'idle-unsubscribe': {
139
163
  readonly type: "string";
140
164
  readonly describe: "drop the event subscriptions after this long with no page open (5m, 300s, 0 to disable)";
@@ -203,6 +227,8 @@ export interface WebOptions {
203
227
  readonly issueCookie: boolean | undefined;
204
228
  /** D-32: `token` or `rega`. */
205
229
  readonly authMode: AuthMode;
230
+ /** D-40: the box `--auth-mode occulite` checks a handed-over session against. */
231
+ readonly occuliteUrl: string | undefined;
206
232
  /** D-32, in milliseconds. */
207
233
  readonly sessionTtlMs: number;
208
234
  readonly ccu: string | undefined;
@@ -210,6 +236,11 @@ export interface WebOptions {
210
236
  readonly callbackIp: string | undefined;
211
237
  readonly callbackXmlrpcPort: number | undefined;
212
238
  readonly callbackBinrpcPort: number | undefined;
239
+ /** Task 35 (D-43): the CCU addon's fixed callback ports, taken while the configured ones are `0`. */
240
+ readonly callbackXmlrpcDefaultPort: number | undefined;
241
+ readonly callbackBinrpcDefaultPort: number | undefined;
242
+ /** Task 38: set by the image's `HMM_IN_CONTAINER`. */
243
+ readonly inContainer: boolean;
213
244
  readonly demo: boolean;
214
245
  /** D-31, in milliseconds; `0` disables the idle unsubscribe. */
215
246
  readonly idleUnsubscribeMs: number;
package/dist/options.js CHANGED
@@ -20,11 +20,15 @@ export const ENV_PREFIX = 'HMM';
20
20
  */
21
21
  export const DEFAULT_IDLE_UNSUBSCRIBE = '5m';
22
22
  /**
23
- * D-32: how a browser is let in. `token` is what every install type but the CCU addon uses, and
23
+ * D-32/D-40: how a browser is let in. `token` is what every install type but the CCU addon uses, and
24
24
  * stays the default there too - the addon's `settings.cgi` hand-over is the primary path and the
25
25
  * login is switched on by hand in `etc/hmm.env` or from the addon's settings page.
26
+ *
27
+ * `occulite` is the openccu-lite form of the same idea: no form of our own, the box's shell hands
28
+ * the session over on the URL and the host checks it against the box. The addon's `update_script`
29
+ * sets it by itself when it is installed on a box (`VARIANT=lite` in `/VERSION`).
26
30
  */
27
- export const AUTH_MODES = ['token', 'rega'];
31
+ export const AUTH_MODES = ['token', 'rega', 'occulite'];
28
32
  /** D-32: a login lasts a day of not being used. Sliding, so a tab in use never expires. */
29
33
  export const DEFAULT_SESSION_TTL = '24h';
30
34
  /**
@@ -43,9 +47,9 @@ export function parseDuration(value, option) {
43
47
  const factor = { ms: 1, s: 1000, m: 60_000, h: 3_600_000 }[match[2] ?? 's'] ?? 1000;
44
48
  return amount * factor;
45
49
  }
46
- /** Is this one of the two modes? `parseRaw` has already refused everything else. */
50
+ /** Is this one of the three modes? `parseRaw` has already refused everything else. */
47
51
  export function isAuthMode(value) {
48
- return value === 'token' || value === 'rega';
52
+ return value === 'token' || value === 'rega' || value === 'occulite';
49
53
  }
50
54
  export const OPTIONS = {
51
55
  port: {
@@ -101,13 +105,19 @@ export const OPTIONS = {
101
105
  },
102
106
  'auth-mode': {
103
107
  type: 'string',
104
- describe: 'token: the token guards the api. rega: ask for a CCU login first (addon only, needs --local)',
108
+ describe: 'token: the token guards the api. rega: ask for a CCU login first (addon only, needs --local). ' +
109
+ 'occulite: take the session openccu-lite hands over',
105
110
  choices: AUTH_MODES,
106
111
  default: 'token',
107
112
  },
113
+ 'occulite-url': {
114
+ type: 'string',
115
+ describe: 'with --auth-mode occulite: the box that issued the session',
116
+ defaultDescription: 'http://127.0.0.1',
117
+ },
108
118
  'session-ttl': {
109
119
  type: 'string',
110
- describe: 'with --auth-mode rega: how long a login lasts without being used (24h, 90m, ...)',
120
+ describe: 'with --auth-mode rega or occulite: how long a login lasts without being used (24h, 90m, ...)',
111
121
  default: DEFAULT_SESSION_TTL,
112
122
  },
113
123
  ccu: {
@@ -134,6 +144,21 @@ export const OPTIONS = {
134
144
  describe: 'fixed port for the binrpc callback server; must differ from the xmlrpc one',
135
145
  defaultDescription: '0, a free port',
136
146
  },
147
+ 'callback-xmlrpc-default-port': {
148
+ type: 'number',
149
+ describe: 'xmlrpc callback port while the configured one is 0; a free port when it is taken (the CCU addon sets 2031)',
150
+ defaultDescription: 'none, a free port',
151
+ },
152
+ 'callback-binrpc-default-port': {
153
+ type: 'number',
154
+ describe: 'binrpc callback port while the configured one is 0; a free port when it is taken (the CCU addon sets 2032)',
155
+ defaultDescription: 'none, a free port',
156
+ },
157
+ 'in-container': {
158
+ type: 'boolean',
159
+ describe: 'we run in a container (the image sets it): the interface popup says to publish the callback ports unchanged',
160
+ default: false,
161
+ },
137
162
  'idle-unsubscribe': {
138
163
  type: 'string',
139
164
  describe: 'drop the event subscriptions after this long with no page open (5m, 300s, 0 to disable)',
@@ -282,6 +307,20 @@ export function parseOptions(argv, env = process.env) {
282
307
  const value = raw[name] ?? OPTIONS[name].default;
283
308
  return value === undefined ? undefined : Number(value);
284
309
  };
310
+ const port = (name) => {
311
+ const value = number(name);
312
+ if (value !== undefined && !(Number.isInteger(value) && value >= 0 && value <= 65535)) {
313
+ throw new CliError(`--${name}: "${String(value)}" is not a port`);
314
+ }
315
+ return value;
316
+ };
317
+ const callbackXmlrpcDefaultPort = port('callback-xmlrpc-default-port');
318
+ const callbackBinrpcDefaultPort = port('callback-binrpc-default-port');
319
+ if (callbackXmlrpcDefaultPort !== undefined && callbackXmlrpcDefaultPort !== 0) {
320
+ if (callbackXmlrpcDefaultPort === callbackBinrpcDefaultPort) {
321
+ throw new CliError('--callback-xmlrpc-default-port and --callback-binrpc-default-port must differ');
322
+ }
323
+ }
285
324
  const logLevel = string('log-level');
286
325
  return {
287
326
  port: Number(raw['port'] ?? OPTIONS.port.default),
@@ -295,12 +334,16 @@ export function parseOptions(argv, env = process.env) {
295
334
  auth: boolean('auth'),
296
335
  issueCookie: boolean('issue-cookie'),
297
336
  authMode: isAuthMode(string('auth-mode')) ? string('auth-mode') : 'token',
337
+ occuliteUrl: string('occulite-url'),
298
338
  sessionTtlMs: parseDuration(string('session-ttl'), '--session-ttl'),
299
339
  ccu: string('ccu'),
300
340
  local: boolean('local'),
301
341
  callbackIp: string('callback-ip'),
302
342
  callbackXmlrpcPort: number('callback-xmlrpc-port'),
303
343
  callbackBinrpcPort: number('callback-binrpc-port'),
344
+ callbackXmlrpcDefaultPort,
345
+ callbackBinrpcDefaultPort,
346
+ inContainer: boolean('in-container') ?? false,
304
347
  demo: boolean('demo'),
305
348
  idleUnsubscribeMs: parseDuration(string('idle-unsubscribe'), '--idle-unsubscribe'),
306
349
  logLevel: isLogLevel(logLevel) ? logLevel : 'info',
package/dist/server.d.ts CHANGED
@@ -26,6 +26,7 @@ import type { SessionInfo } from '@homematic-manager/core';
26
26
  import { ApiWebSocketServer, Backend, type BackendOptions } from '@homematic-manager/backend';
27
27
  import { DeviceImageService } from './images.js';
28
28
  import { type Logger, type LogLevel } from './log.js';
29
+ import { type OcculiteCheckOptions } from './occulite.js';
29
30
  import { SessionStore } from './sessions.js';
30
31
  /** The port the CLI binds by default - 8090, next to nothing else a Homematic user runs. */
31
32
  export declare const DEFAULT_PORT = 8090;
@@ -33,6 +34,12 @@ export declare const DEFAULT_PORT = 8090;
33
34
  export declare const DEFAULT_HOST = "127.0.0.1";
34
35
  /** How long `close()` waits for `backend.stop()` before it gives up and exits anyway. */
35
36
  export declare const SHUTDOWN_TIMEOUT_MS = 5000;
37
+ /**
38
+ * B-94, D-65: how long the box's answer about an `X-Occulite-Session` is believed without asking
39
+ * again. The addon contract says to cache it briefly, if at all; a minute covers the burst of
40
+ * assets and the socket of one page load.
41
+ */
42
+ export declare const GATE_RECHECK_MS = 60000;
36
43
  /**
37
44
  * How often an idle API socket is pinged. Below lighttpd's `server.max-read-idle` /
38
45
  * `max-write-idle` default of 60 s, which is what would otherwise cut the proxied socket of the
@@ -46,8 +53,14 @@ export declare const KEEPALIVE_INTERVAL_MS = 25000;
46
53
  * command line, the environment or the addon's session-checked `settings.cgi`. `rega` puts a login
47
54
  * page in front of the UI and checks the credentials against the CCU itself - which only works
48
55
  * *on* the CCU, because both services it asks are loopback-only.
56
+ *
57
+ * D-40 adds `occulite`, for the addon on an openccu-lite box. There is no login page in that mode:
58
+ * the box's shell has already authenticated the user and opens the addon with the session on the
59
+ * URL (`?sid=@xxxxxxxxxx@`), and the host checks that session against the box's metadata API. A
60
+ * request without one is sent to the box's own login rather than shown a form this application
61
+ * could not answer - openccu-lite's users are the box's, not the CCU's.
49
62
  */
50
- export type AuthMode = 'token' | 'rega';
63
+ export type AuthMode = 'token' | 'rega' | 'occulite';
51
64
  /** What the login endpoint asks. `RegaAuthenticator` is the real one; a test passes a fake. */
52
65
  export interface CredentialChecker {
53
66
  authenticate(user: string, password: string): Promise<{
@@ -55,6 +68,14 @@ export interface CredentialChecker {
55
68
  level: number;
56
69
  } | undefined>;
57
70
  }
71
+ /** D-40: what the `occulite` hand-over asks. `OcculiteAuthenticator` is the real one. */
72
+ export interface SessionChecker {
73
+ check(sid: string | null | undefined, options?: OcculiteCheckOptions): Promise<{
74
+ name: string;
75
+ level: number;
76
+ sid: string;
77
+ } | undefined>;
78
+ }
58
79
  export interface WebHostOptions {
59
80
  readonly port?: number;
60
81
  readonly host?: string;
@@ -85,6 +106,10 @@ export interface WebHostOptions {
85
106
  readonly sessionTtlMs?: number | undefined;
86
107
  /** D-32: injected by the tests in place of the real ReGa and UDP check. */
87
108
  readonly authenticator?: CredentialChecker | undefined;
109
+ /** D-40: the box the `occulite` mode checks sessions against. `http://127.0.0.1` by default. */
110
+ readonly occuliteUrl?: string | undefined;
111
+ /** D-40: injected by the tests in place of the real box. */
112
+ readonly sessionChecker?: SessionChecker | undefined;
88
113
  /** Serve the UI in demo mode and start no backend at all. */
89
114
  readonly demo?: boolean;
90
115
  /** Written to `ConnectionConfig.host` when it differs from what is configured. */
@@ -92,14 +117,30 @@ export interface WebHostOptions {
92
117
  /** Written to `ConnectionConfig.local` - the addon's "we run on the CCU" mode. */
93
118
  readonly local?: boolean | undefined;
94
119
  /**
95
- * Written to `ConnectionConfig.callback`. A host that cannot see the address the CCU reaches it
120
+ * Laid over `ConnectionConfig.callback`. A host that cannot see the address the CCU reaches it
96
121
  * on has to be told: a container on a bridge network sees `172.17.0.x`, and an `init` that
97
122
  * announces that address gets no events at all. With `--network host` none of this is needed.
123
+ *
124
+ * Task 38: these three are pinned, not written. They win over `config.json` for as long as the
125
+ * host runs with them, the settings dialog shows them read-only, and the file keeps what the
126
+ * user saved. An empty address and a port of `0` pin nothing.
98
127
  */
99
128
  readonly callbackIp?: string | undefined;
100
- /** Fixed callback ports, so a container can publish them. `0` picks a free one. */
129
+ /** Fixed callback ports, so a container can publish them. `0` pins nothing. */
101
130
  readonly callbackXmlrpcPort?: number | undefined;
102
131
  readonly callbackBinrpcPort?: number | undefined;
132
+ /**
133
+ * Task 38: the host runs in a container (`HMM_IN_CONTAINER`, set by the image). The interface
134
+ * popup then says beside each callback URL that its port must be published unchanged.
135
+ */
136
+ readonly inContainer?: boolean | undefined;
137
+ /**
138
+ * Task 35 (D-43): the callback ports taken while the configuration says `0` - the CCU addon's
139
+ * fixed pair. Handed to the backend and never written to `config.json`, so a port the user set
140
+ * in the settings dialog always wins and no update of the addon can overwrite it.
141
+ */
142
+ readonly callbackXmlrpcDefaultPort?: number | undefined;
143
+ readonly callbackBinrpcDefaultPort?: number | undefined;
103
144
  /** How long `close()` waits for `backend.stop()`. */
104
145
  readonly shutdownTimeoutMs?: number;
105
146
  /** How often an idle api socket is pinged; `0` turns the heartbeat off. */
@@ -124,9 +165,11 @@ export interface WebHost {
124
165
  readonly base: string;
125
166
  /** `undefined` when auth is off. */
126
167
  readonly token: string | undefined;
168
+ /** Whether the page load hands the token to the browser as a cookie (D-41 says when to warn). */
169
+ readonly issueCookie: boolean;
127
170
  /** D-32: how a browser is let in. */
128
171
  readonly authMode: AuthMode;
129
- /** D-32: the login sessions; `undefined` in `token` mode, where there are none. */
172
+ /** D-32/D-40: the login sessions; `undefined` in `token` mode, where there are none. */
130
173
  readonly sessions: SessionStore | undefined;
131
174
  close(): Promise<void>;
132
175
  }