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.
- package/README.md +2 -1
- package/data/easymode-time-selectors.json +285 -0
- package/data/icons/106_hm-lc-sw1-dr.webp +0 -0
- package/data/icons/108_hm-rc-dis-h-x-eu.webp +0 -0
- package/data/icons/109_hm-lc-sw1-pl-ct.webp +0 -0
- package/data/icons/110_hm-es-pmsw1-dr.webp +0 -0
- package/data/icons/112_hmip-wrc2.webp +0 -0
- package/data/icons/113_hmip-psm-ch.webp +0 -0
- package/data/icons/113_hmip-psm-it.webp +0 -0
- package/data/icons/113_hmip-psm-pe.webp +0 -0
- package/data/icons/113_hmip-psm-uk.webp +0 -0
- package/data/icons/113_hmip-psm.webp +0 -0
- package/data/icons/114_hm-lc-dim1t-fm-lf_3.webp +0 -0
- package/data/icons/116_hm-rc-4-3_brc-h3.webp +0 -0
- package/data/icons/118_hmip-swdo.webp +0 -0
- package/data/icons/119_hmip-rc8.webp +0 -0
- package/data/icons/120_hmip-etrv.webp +0 -0
- package/data/icons/121_hmip-wth.webp +0 -0
- package/data/icons/123_oligo.smart.ip.hm.webp +0 -0
- package/data/icons/125_hmip-smi.webp +0 -0
- package/data/icons/130_hmip-srh.webp +0 -0
- package/data/icons/131_hmip-wrc6.webp +0 -0
- package/data/icons/132_hmip-smo.webp +0 -0
- package/data/icons/133_hmip-asir.webp +0 -0
- package/data/icons/134_hmip-fsm.webp +0 -0
- package/data/icons/135_hmip-fsm16.webp +0 -0
- package/data/icons/136_hmip-miob.webp +0 -0
- package/data/icons/137_hmip-fal-c6.webp +0 -0
- package/data/icons/138_hmip-fal-c10.webp +0 -0
- package/data/icons/140_alpha-ip-rgb.webp +0 -0
- package/data/icons/141_alpha-ip-rgba.webp +0 -0
- package/data/icons/144_hmip-wgc.webp +0 -0
- package/data/icons/145_hmip-froll_hmip-fbl.webp +0 -0
- package/data/icons/146_hmip-sth.webp +0 -0
- package/data/icons/147_hmip-sthd.webp +0 -0
- package/data/icons/148_hmip-stho.webp +0 -0
- package/data/icons/149_hmip-sam.webp +0 -0
- package/data/icons/151_hmip-pcbs-bat.webp +0 -0
- package/data/icons/152_hmip-swdo-i.webp +0 -0
- package/data/icons/153_hmip-spi.webp +0 -0
- package/data/icons/154_hmip-spdr.webp +0 -0
- package/data/icons/155_hmip-whs2.webp +0 -0
- package/data/icons/156_hmip-mod-oc8.webp +0 -0
- package/data/icons/158_hmip-etrv-uk.webp +0 -0
- package/data/icons/159_hmip-mod-rc8.webp +0 -0
- package/data/icons/160_hmipw-drs4.webp +0 -0
- package/data/icons/161_hmipw-drs8.webp +0 -0
- package/data/icons/162_hmipw-drap.webp +0 -0
- package/data/icons/163_hmipw-drbl4.webp +0 -0
- package/data/icons/164_hmipw-dri16.webp +0 -0
- package/data/icons/165_hmipw-fio6.webp +0 -0
- package/data/icons/166_hmipw-drd3.webp +0 -0
- package/data/icons/167_hmipw-dri32.webp +0 -0
- package/data/icons/168_hmip-smi55.webp +0 -0
- package/data/icons/169_hmip-swo-pr.webp +0 -0
- package/data/icons/170_hmip-swo-pl.webp +0 -0
- package/data/icons/171_hmip-swo-b.webp +0 -0
- package/data/icons/172_hmip-swd.webp +0 -0
- package/data/icons/173_hmip-bsl.webp +0 -0
- package/data/icons/174_hmip-slo.webp +0 -0
- package/data/icons/175_hmip-group-heating.webp +0 -0
- package/data/icons/177_hmip-dbb.webp +0 -0
- package/data/icons/178_hmip-etrv-b1.webp +0 -0
- package/data/icons/179_hmip-swdm-b2.webp +0 -0
- package/data/icons/180_hmip-etrv-b.webp +0 -0
- package/data/icons/181_hmip-swdm.webp +0 -0
- package/data/icons/182_hmip-fci1.webp +0 -0
- package/data/icons/183_hmip-mod-tm.webp +0 -0
- package/data/icons/184_hmip-pcbs2.webp +0 -0
- package/data/icons/185_hmip-FCI6.webp +0 -0
- package/data/icons/186_hmip-mp3p.webp +0 -0
- package/data/icons/187_hmip-rcb1.webp +0 -0
- package/data/icons/188_hmip-etrv-c.webp +0 -0
- package/data/icons/190_hmip-sci.webp +0 -0
- package/data/icons/191_hmip-mod-ho.webp +0 -0
- package/data/icons/192_hmip-asir-o.webp +0 -0
- package/data/icons/193_hmip-wt.webp +0 -0
- package/data/icons/194_hmip-wrcd.webp +0 -0
- package/data/icons/195_hmipw-smi55.webp +0 -0
- package/data/icons/196_hmip-ASIR-2.webp +0 -0
- package/data/icons/197_hmip-swdo-pl.webp +0 -0
- package/data/icons/198_hmip-falmot-c12.webp +0 -0
- package/data/icons/199_hmip-mio16-pcb.webp +0 -0
- package/data/icons/200_hmip-wth-b.webp +0 -0
- package/data/icons/201_hmip-stv.webp +0 -0
- package/data/icons/202_hmip-wrcr.webp +0 -0
- package/data/icons/203_hmip-fsi16.webp +0 -0
- package/data/icons/204_hmip-drdi3.webp +0 -0
- package/data/icons/205_hmip-drsi4.webp +0 -0
- package/data/icons/206_hmip-drbli4.webp +0 -0
- package/data/icons/207_hmip-srdo.webp +0 -0
- package/data/icons/208_hmip-dsd-pcb.webp +0 -0
- package/data/icons/209_hmip-etrv-b-uk.webp +0 -0
- package/data/icons/210_hmip-ste2-pcb.webp +0 -0
- package/data/icons/211_hmip-drg-dali.webp +0 -0
- package/data/icons/211_hmip-drsi1.webp +0 -0
- package/data/icons/212_hmip-sfd.webp +0 -0
- package/data/icons/212_hmipw-scthd.webp +0 -0
- package/data/icons/213_hmip-smo230.webp +0 -0
- package/data/icons/213_hmip-wua.webp +0 -0
- package/data/icons/214_hmip-dld.webp +0 -0
- package/data/icons/215_hmip-psm-pe-2.webp +0 -0
- package/data/icons/215_hmip-scth230.webp +0 -0
- package/data/icons/216_hmip-etrv-e.webp +0 -0
- package/data/icons/216_hmip-psm-ch-2.webp +0 -0
- package/data/icons/217_hmip-usbsm.webp +0 -0
- package/data/icons/218_hmip-dls.webp +0 -0
- package/data/icons/219_hmip-fwi.webp +0 -0
- package/data/icons/220_hmip-wsc.webp +0 -0
- package/data/icons/221_hmip-sti.webp +0 -0
- package/data/icons/221_hmip-wkp.webp +0 -0
- package/data/icons/222_hmipw-wgd.webp +0 -0
- package/data/icons/223_hmip-mod-wd-vk.webp +0 -0
- package/data/icons/224_hmip-etrv-cl.webp +0 -0
- package/data/icons/225_hmip-rgbw.webp +0 -0
- package/data/icons/226_hmip-esi.webp +0 -0
- package/data/icons/227_hmip-lsc.webp +0 -0
- package/data/icons/228_elv-sh-sw1-bat.webp +0 -0
- package/data/icons/229_elv-sh-bm-s.webp +0 -0
- package/data/icons/230_hmip-etrv-f.webp +0 -0
- package/data/icons/231_elv-sh-cth.webp +0 -0
- package/data/icons/232_elv-sh-taco.webp +0 -0
- package/data/icons/233_hmip-flc.webp +0 -0
- package/data/icons/234_hmip-etrv-3.webp +0 -0
- package/data/icons/235_hmip-psmco.webp +0 -0
- package/data/icons/236_elv-sh-sb8.webp +0 -0
- package/data/icons/237_elv-sh-ctv.webp +0 -0
- package/data/icons/238_elv-sh-cap.webp +0 -0
- package/data/icons/239_hmip-wgs-f.webp +0 -0
- package/data/icons/240_hmip-wgtc-f.webp +0 -0
- package/data/icons/241_hmip-wgt-f.webp +0 -0
- package/data/icons/242_elv-sh-smsi.webp +0 -0
- package/data/icons/243_elv-sh-crc.webp +0 -0
- package/data/icons/244_elv-sh-krco.webp +0 -0
- package/data/icons/245_hmip-falmot-c8.webp +0 -0
- package/data/icons/246_elv-sh-wsm.webp +0 -0
- package/data/icons/247_hmip-hap-2.webp +0 -0
- package/data/icons/248_hmip-dlp.webp +0 -0
- package/data/icons/249_elv-sh-psmci.webp +0 -0
- package/data/icons/250_elv-sh-sps25.webp +0 -0
- package/data/icons/252_elv-sh-pti2.webp +0 -0
- package/data/icons/253_hmip-udi-smi55.webp +0 -0
- package/data/icons/254_hmip-udi-pb2.webp +0 -0
- package/data/icons/256_ELV-SH-SMS2.webp +0 -0
- package/data/icons/257_ELV-SH-FSI.webp +0 -0
- package/data/icons/258_hmip-m-td15.webp +0 -0
- package/data/icons/259_elv-sh-dusi.webp +0 -0
- package/data/icons/260_hmip-wss.webp +0 -0
- package/data/icons/54a_lc-ddc1-pcb.webp +0 -0
- package/data/icons/72_hm-rc-brc-h.webp +0 -0
- package/data/icons/73_hm-atent.webp +0 -0
- package/data/icons/79_hm-lc-dim1pwm-cv.webp +0 -0
- package/data/icons/84_hm-rc-4-3.webp +0 -0
- package/data/icons/85_hm-rc-sec4-3.webp +0 -0
- package/data/icons/86_hm-rc-key4-3.webp +0 -0
- package/data/icons/CCU3-1-50.webp +0 -0
- package/data/icons/CCU3.webp +0 -0
- package/data/icons/hm-coupling-dim.webp +0 -0
- package/data/icons/hm-coupling-group.webp +0 -0
- package/data/icons/hm-coupling-onoff.webp +0 -0
- package/data/icons/hm-coupling-rgb-dim.webp +0 -0
- package/data/icons/hm-coupling-rgb.webp +0 -0
- package/data/icons/hm-coupling-rgbw-dim.webp +0 -0
- package/data/icons/hm-coupling-rgbw.webp +0 -0
- package/data/icons/hm-coupling-white-dim.webp +0 -0
- package/data/icons/hm-coupling-white.webp +0 -0
- package/data/icons/hm-hue_gateway.webp +0 -0
- package/data/icons/hm-lightify_gateway.webp +0 -0
- package/data/icons/hm_resc-win-pcb-sc.webp +0 -0
- package/data/icons/unknown_device.webp +0 -0
- package/data/manifest.json +7 -1
- package/data/master-metadata.json +1997 -33
- package/data/profiles/ACCESS_RECEIVER.json +26 -13
- package/data/profiles/ACOUSTIC_SIGNAL_VIRTUAL_RECEIVER.json +3270 -76
- package/data/profiles/ACTOR_SECURITY.json +90 -10
- package/data/profiles/ACTOR_WINDOW.json +580 -43
- package/data/profiles/ALARMACTUATOR.json +564 -37
- package/data/profiles/ALARM_COND_SWITCH_RECEIVER.json +36 -4
- package/data/profiles/ALARM_SWITCH_VIRTUAL_RECEIVER.json +735 -33
- package/data/profiles/ARMING.json +115 -5
- package/data/profiles/BLIND.json +1038 -49
- package/data/profiles/BLIND_VIRTUAL_RECEIVER.json +4624 -124
- package/data/profiles/CLIMATECONTROL_FLOOR_PUMP_TRANSCEIVER.json +27 -1
- package/data/profiles/DIMMER.json +2233 -71
- package/data/profiles/DIMMER_VIRTUAL_RECEIVER.json +5773 -102
- package/data/profiles/DIMMER_woLongKeyPress.json +117 -4
- package/data/profiles/DOOR_RECEIVER.json +138 -10
- package/data/profiles/DUAL_WHITE_BRIGHTNESS.json +305 -11
- package/data/profiles/DUAL_WHITE_COLOR.json +730 -23
- package/data/profiles/HEATING_KEY_RECEIVER.json +127 -10
- package/data/profiles/HMW_BLIND.json +555 -20
- package/data/profiles/HMW_DIMMER.json +1337 -47
- package/data/profiles/HMW_INPUT_OUTPUT.json +375 -20
- package/data/profiles/HMW_SWITCH.json +375 -20
- package/data/profiles/JALOUSIE.json +1320 -52
- package/data/profiles/KEYMATIC.json +120 -12
- package/data/profiles/REMOTECONTROL_RECEIVER.json +10 -2
- package/data/profiles/RGBW_AUTOMATIC.json +46 -6
- package/data/profiles/RGBW_COLOR.json +14 -4
- package/data/profiles/SERVO_VIRTUAL_RECEIVER.json +117 -3
- package/data/profiles/SHUTTER_VIRTUAL_RECEIVER.json +4263 -128
- package/data/profiles/SIGNAL_CHIME.json +195 -14
- package/data/profiles/SIGNAL_CHIMEM.json +195 -14
- package/data/profiles/SIGNAL_LED.json +195 -14
- package/data/profiles/SIGNAL_LEDM.json +195 -14
- package/data/profiles/SIMPLE_SWITCH_RECEIVER.json +113 -9
- package/data/profiles/STATUS_INDICATOR.json +1073 -45
- package/data/profiles/SWITCH.json +1412 -60
- package/data/profiles/SWITCH_PANIC.json +315 -13
- package/data/profiles/SWITCH_SENSOR.json +243 -11
- package/data/profiles/SWITCH_VIRTUAL_RECEIVER.json +3963 -131
- package/data/profiles/UNIVERSAL_ACTOR.json +4319 -90
- package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_LSC.json +6094 -196
- package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_PWM.json +4920 -101
- package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_RGB(W).json +2353 -80
- package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_RGBW_DALI.json +6116 -176
- package/data/profiles/UNIVERSAL_LIGHT_RECEIVER_TW.json +3844 -104
- package/data/profiles/VIRTUAL_DIMMER.json +305 -11
- package/data/profiles/VIRTUAL_DUAL_WHITE_COLOR.json +730 -23
- package/data/profiles/WATER_SWITCH_VIRTUAL_RECEIVER.json +3903 -128
- package/data/profiles/WINDOW_DRIVE_RECEIVER.json +220 -8
- package/data/profiles/WINMATIC.json +505 -23
- package/dist/cli.js +24 -0
- package/dist/occulite.d.ts +86 -0
- package/dist/occulite.js +140 -0
- package/dist/options.d.ts +37 -6
- package/dist/options.js +49 -6
- package/dist/server.d.ts +47 -4
- package/dist/server.js +237 -27
- package/dist/sessions.d.ts +9 -1
- package/dist/sessions.js +8 -2
- package/dist/testSupport.js +9 -2
- package/node_modules/@homematic-manager/backend/dist/api/backend.d.ts +33 -0
- package/node_modules/@homematic-manager/backend/dist/api/backend.js +558 -46
- package/node_modules/@homematic-manager/backend/dist/cache/names.d.ts +9 -0
- package/node_modules/@homematic-manager/backend/dist/cache/names.js +21 -0
- package/node_modules/@homematic-manager/backend/dist/config/defaults.js +10 -1
- package/node_modules/@homematic-manager/backend/dist/config/store.d.ts +28 -1
- package/node_modules/@homematic-manager/backend/dist/config/store.js +59 -3
- package/node_modules/@homematic-manager/backend/dist/devices/installMode.js +9 -0
- package/node_modules/@homematic-manager/backend/dist/errors.d.ts +18 -0
- package/node_modules/@homematic-manager/backend/dist/errors.js +51 -2
- package/node_modules/@homematic-manager/backend/dist/images/deviceImages.d.ts +6 -0
- package/node_modules/@homematic-manager/backend/dist/images/deviceImages.js +38 -2
- package/node_modules/@homematic-manager/backend/dist/index.d.ts +3 -1
- package/node_modules/@homematic-manager/backend/dist/index.js +4 -1
- package/node_modules/@homematic-manager/backend/dist/interfaces/manager.d.ts +70 -8
- package/node_modules/@homematic-manager/backend/dist/interfaces/manager.js +333 -44
- package/node_modules/@homematic-manager/backend/dist/meta/client.d.ts +120 -0
- package/node_modules/@homematic-manager/backend/dist/meta/client.js +307 -0
- package/node_modules/@homematic-manager/backend/dist/meta/credentials.d.ts +38 -0
- package/node_modules/@homematic-manager/backend/dist/meta/credentials.js +53 -0
- package/node_modules/@homematic-manager/backend/dist/meta/index.d.ts +12 -0
- package/node_modules/@homematic-manager/backend/dist/meta/index.js +12 -0
- package/node_modules/@homematic-manager/backend/dist/meta/localProvider.d.ts +47 -0
- package/node_modules/@homematic-manager/backend/dist/meta/localProvider.js +119 -0
- package/node_modules/@homematic-manager/backend/dist/meta/occuliteProvider.d.ts +79 -0
- package/node_modules/@homematic-manager/backend/dist/meta/occuliteProvider.js +305 -0
- package/node_modules/@homematic-manager/backend/dist/meta/provider.d.ts +68 -0
- package/node_modules/@homematic-manager/backend/dist/meta/provider.js +19 -0
- package/node_modules/@homematic-manager/backend/dist/meta/regaProvider.d.ts +84 -0
- package/node_modules/@homematic-manager/backend/dist/meta/regaProvider.js +403 -0
- package/node_modules/@homematic-manager/backend/dist/meta/service.d.ts +137 -0
- package/node_modules/@homematic-manager/backend/dist/meta/service.js +412 -0
- package/node_modules/@homematic-manager/backend/dist/rega/client.d.ts +11 -0
- package/node_modules/@homematic-manager/backend/dist/rega/client.js +13 -0
- package/node_modules/@homematic-manager/backend/dist/rega/scripts.d.ts +51 -0
- package/node_modules/@homematic-manager/backend/dist/rega/scripts.js +171 -0
- package/node_modules/@homematic-manager/backend/dist/rpc/client.d.ts +18 -5
- package/node_modules/@homematic-manager/backend/dist/rpc/client.js +11 -5
- package/node_modules/@homematic-manager/backend/dist/rpc/log.d.ts +86 -0
- package/node_modules/@homematic-manager/backend/dist/rpc/log.js +241 -0
- package/node_modules/@homematic-manager/backend/dist/rpc/origin.d.ts +30 -0
- package/node_modules/@homematic-manager/backend/dist/rpc/origin.js +35 -0
- package/node_modules/@homematic-manager/backend/dist/rpc/server.d.ts +12 -0
- package/node_modules/@homematic-manager/backend/dist/rpc/server.js +37 -5
- package/node_modules/@homematic-manager/backend/dist/util/net.d.ts +86 -1
- package/node_modules/@homematic-manager/backend/dist/util/net.js +223 -9
- package/node_modules/@homematic-manager/backend/package.json +1 -1
- package/node_modules/@homematic-manager/core/dist/api/types.d.ts +397 -10
- package/node_modules/@homematic-manager/core/dist/api/types.js +23 -2
- package/node_modules/@homematic-manager/core/dist/data/memory.d.ts +3 -1
- package/node_modules/@homematic-manager/core/dist/data/memory.js +3 -0
- package/node_modules/@homematic-manager/core/dist/data/types.d.ts +60 -0
- package/node_modules/@homematic-manager/core/dist/data/types.js +2 -0
- package/node_modules/@homematic-manager/core/dist/devices/index.d.ts +19 -0
- package/node_modules/@homematic-manager/core/dist/devices/index.js +53 -0
- package/node_modules/@homematic-manager/core/dist/easymodes/engine.d.ts +3 -1
- package/node_modules/@homematic-manager/core/dist/easymodes/engine.js +1 -0
- package/node_modules/@homematic-manager/core/dist/easymodes/form.d.ts +43 -0
- package/node_modules/@homematic-manager/core/dist/easymodes/form.js +84 -0
- package/node_modules/@homematic-manager/core/dist/i18n/messages.js +61 -0
- package/node_modules/@homematic-manager/core/dist/index.d.ts +3 -0
- package/node_modules/@homematic-manager/core/dist/index.js +4 -0
- package/node_modules/@homematic-manager/core/dist/interfaces/table.d.ts +23 -2
- package/node_modules/@homematic-manager/core/dist/interfaces/table.js +18 -1
- package/node_modules/@homematic-manager/core/dist/meta/document.d.ts +71 -0
- package/node_modules/@homematic-manager/core/dist/meta/document.js +260 -0
- package/node_modules/@homematic-manager/core/dist/meta/follow.d.ts +27 -0
- package/node_modules/@homematic-manager/core/dist/meta/follow.js +180 -0
- package/node_modules/@homematic-manager/core/dist/meta/index.d.ts +16 -0
- package/node_modules/@homematic-manager/core/dist/meta/index.js +16 -0
- package/node_modules/@homematic-manager/core/dist/meta/paths.d.ts +96 -0
- package/node_modules/@homematic-manager/core/dist/meta/paths.js +182 -0
- package/node_modules/@homematic-manager/core/dist/meta/slug.d.ts +22 -0
- package/node_modules/@homematic-manager/core/dist/meta/slug.js +59 -0
- package/node_modules/@homematic-manager/core/dist/meta/store.d.ts +147 -0
- package/node_modules/@homematic-manager/core/dist/meta/store.js +537 -0
- package/node_modules/@homematic-manager/core/dist/meta/tree.d.ts +19 -0
- package/node_modules/@homematic-manager/core/dist/meta/tree.js +53 -0
- package/node_modules/@homematic-manager/core/dist/meta/types.d.ts +143 -0
- package/node_modules/@homematic-manager/core/dist/meta/types.js +63 -0
- package/node_modules/@homematic-manager/core/dist/meta/view.d.ts +50 -0
- package/node_modules/@homematic-manager/core/dist/meta/view.js +103 -0
- package/node_modules/@homematic-manager/core/dist/rpc/methods.d.ts +10 -0
- package/node_modules/@homematic-manager/core/dist/rpc/methods.js +51 -1
- package/node_modules/@homematic-manager/core/dist/rpc/text.d.ts +22 -0
- package/node_modules/@homematic-manager/core/dist/rpc/text.js +46 -0
- package/node_modules/@homematic-manager/core/dist/rssi/index.d.ts +125 -13
- package/node_modules/@homematic-manager/core/dist/rssi/index.js +146 -29
- package/node_modules/@homematic-manager/core/dist/serviceMessages/index.d.ts +28 -2
- package/node_modules/@homematic-manager/core/dist/serviceMessages/index.js +44 -6
- package/node_modules/@homematic-manager/core/package.json +1 -1
- package/package.json +3 -3
- package/ui/assets/index-D-NsbBVS.css +1 -0
- package/ui/assets/index-DNUNMBbb.js +16 -0
- package/ui/favicon.ico +0 -0
- package/ui/index.html +3 -2
- package/node_modules/@homematic-manager/backend/dist/write/log.d.ts +0 -51
- package/node_modules/@homematic-manager/backend/dist/write/log.js +0 -151
- package/ui/assets/index-lB-HxsKR.js +0 -13
- package/ui/assets/index-qbhgN2pA.css +0 -1
|
@@ -0,0 +1,35 @@
|
|
|
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 { AsyncLocalStorage } from 'node:async_hooks';
|
|
26
|
+
const storage = new AsyncLocalStorage();
|
|
27
|
+
/** The origin of the current call chain; `background` outside of any request. */
|
|
28
|
+
export function currentOrigin() {
|
|
29
|
+
return storage.getStore() ?? 'background';
|
|
30
|
+
}
|
|
31
|
+
/** Runs `fn` with `origin` as the answer of {@link currentOrigin} for everything it awaits. */
|
|
32
|
+
export function runWithOrigin(origin, fn) {
|
|
33
|
+
return storage.run(origin, fn);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=origin.js.map
|
|
@@ -88,11 +88,23 @@ export declare class CallbackServers implements CallbackServerSet {
|
|
|
88
88
|
constructor(options: {
|
|
89
89
|
handler: CallbackHandler;
|
|
90
90
|
host?: string;
|
|
91
|
+
/** The configured ports; `0` means none is configured. */
|
|
91
92
|
ports: {
|
|
92
93
|
xmlrpc: number;
|
|
93
94
|
binrpc: number;
|
|
94
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* Task 35 (D-43): the port a protocol takes while its configured port is `0` - the CCU
|
|
98
|
+
* addon's fixed pair, so that a restart registers the very URL it registered before.
|
|
99
|
+
* Unlike a configured port, a default that is taken is no error: the server falls back to
|
|
100
|
+
* a free port for this start, and `onFallback` hears about it once.
|
|
101
|
+
*/
|
|
102
|
+
defaultPorts?: {
|
|
103
|
+
xmlrpc: number;
|
|
104
|
+
binrpc: number;
|
|
105
|
+
};
|
|
95
106
|
onError?: (error: unknown) => void;
|
|
107
|
+
onFallback?: (protocol: RpcProtocol, port: number, error: unknown) => void;
|
|
96
108
|
});
|
|
97
109
|
/** Starts the server for a protocol if it is not running yet, and returns its port. */
|
|
98
110
|
ensure(protocol: RpcProtocol): Promise<number>;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import binrpc from 'binrpc';
|
|
19
19
|
import xmlrpc from 'homematic-xmlrpc';
|
|
20
|
-
import { interfaceNameFromIdent } from '@homematic-manager/core';
|
|
20
|
+
import { interfaceNameFromIdent, normaliseDescription, } from '@homematic-manager/core';
|
|
21
21
|
import { connectionError, errorMessage } from '../errors.js';
|
|
22
22
|
/** The callback methods this server answers, in the order `system.listMethods` returns them. */
|
|
23
23
|
export const CALLBACK_METHODS = [
|
|
@@ -199,11 +199,18 @@ export class CallbackServer {
|
|
|
199
199
|
return new Promise((resolve, reject) => {
|
|
200
200
|
const server = xmlrpc.createServer({ host: this.host, port: this.#requestedPort });
|
|
201
201
|
this.#xmlrpcServer = server;
|
|
202
|
+
let listening = false;
|
|
202
203
|
server.on('error', (error) => {
|
|
203
|
-
|
|
204
|
+
// a bind that fails is the rejection's to report, once; `onError` is for a server
|
|
205
|
+
// that is running
|
|
206
|
+
if (!listening) {
|
|
207
|
+
reject(connectionError(`xmlrpc callback server on port ${String(this.#requestedPort)}: ${error.message}`, error));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
204
210
|
this.#onError(error);
|
|
205
211
|
});
|
|
206
212
|
server.on('listening', () => {
|
|
213
|
+
listening = true;
|
|
207
214
|
const address = server.httpServer.address();
|
|
208
215
|
resolve(typeof address === 'object' && address !== null ? address.port : this.#requestedPort);
|
|
209
216
|
});
|
|
@@ -221,11 +228,16 @@ export class CallbackServer {
|
|
|
221
228
|
return new Promise((resolve, reject) => {
|
|
222
229
|
const server = binrpc.createServer({ host: this.host, port: this.#requestedPort });
|
|
223
230
|
this.#binrpcServer = server;
|
|
231
|
+
let listening = false;
|
|
224
232
|
server.on('error', (error) => {
|
|
225
|
-
|
|
233
|
+
if (!listening) {
|
|
234
|
+
reject(connectionError(`binrpc callback server on port ${String(this.#requestedPort)}: ${error.message}`, error));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
226
237
|
this.#onError(error);
|
|
227
238
|
});
|
|
228
239
|
server.on('listening', () => {
|
|
240
|
+
listening = true;
|
|
229
241
|
const address = server.server.address();
|
|
230
242
|
resolve(typeof address === 'object' && address !== null ? address.port : this.#requestedPort);
|
|
231
243
|
});
|
|
@@ -266,7 +278,9 @@ function asDescriptions(value) {
|
|
|
266
278
|
entry !== null &&
|
|
267
279
|
!Array.isArray(entry) &&
|
|
268
280
|
typeof entry['ADDRESS'] === 'string') {
|
|
269
|
-
|
|
281
|
+
// #143: the list fields of a description are made into lists here, once, for every
|
|
282
|
+
// interface - the group process sends `PARAMSETS` as a string on some boxes.
|
|
283
|
+
descriptions.push(normaliseDescription(entry));
|
|
270
284
|
}
|
|
271
285
|
}
|
|
272
286
|
return descriptions;
|
|
@@ -282,12 +296,16 @@ export class CallbackServers {
|
|
|
282
296
|
#handler;
|
|
283
297
|
#host;
|
|
284
298
|
#ports;
|
|
299
|
+
#defaultPorts;
|
|
285
300
|
#onError;
|
|
301
|
+
#onFallback;
|
|
286
302
|
constructor(options) {
|
|
287
303
|
this.#handler = options.handler;
|
|
288
304
|
this.#host = options.host ?? '0.0.0.0';
|
|
289
305
|
this.#ports = options.ports;
|
|
306
|
+
this.#defaultPorts = options.defaultPorts ?? { xmlrpc: 0, binrpc: 0 };
|
|
290
307
|
this.#onError = options.onError ?? (() => undefined);
|
|
308
|
+
this.#onFallback = options.onFallback ?? (() => undefined);
|
|
291
309
|
}
|
|
292
310
|
/** Starts the server for a protocol if it is not running yet, and returns its port. */
|
|
293
311
|
async ensure(protocol) {
|
|
@@ -295,10 +313,24 @@ export class CallbackServers {
|
|
|
295
313
|
if (running) {
|
|
296
314
|
return running.port;
|
|
297
315
|
}
|
|
316
|
+
const configured = protocol === 'binrpc' ? this.#ports.binrpc : this.#ports.xmlrpc;
|
|
317
|
+
const preferred = protocol === 'binrpc' ? this.#defaultPorts.binrpc : this.#defaultPorts.xmlrpc;
|
|
318
|
+
if (configured !== 0 || preferred === 0) {
|
|
319
|
+
return this.#start(protocol, configured);
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
return await this.#start(protocol, preferred);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
this.#onFallback(protocol, preferred, error);
|
|
326
|
+
return this.#start(protocol, 0);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async #start(protocol, port) {
|
|
298
330
|
const server = new CallbackServer({
|
|
299
331
|
protocol,
|
|
300
332
|
host: this.#host,
|
|
301
|
-
port
|
|
333
|
+
port,
|
|
302
334
|
handler: this.#handler,
|
|
303
335
|
onError: this.#onError,
|
|
304
336
|
});
|
|
@@ -7,15 +7,100 @@
|
|
|
7
7
|
* 5 s timeout each before the window became usable) and it destroys the socket instead of leaving
|
|
8
8
|
* it to time out.
|
|
9
9
|
*/
|
|
10
|
+
import dgram from 'node:dgram';
|
|
11
|
+
import dns from 'node:dns';
|
|
10
12
|
import net from 'node:net';
|
|
11
13
|
import os from 'node:os';
|
|
12
|
-
|
|
14
|
+
import type { CallbackAddressInfo } from '@homematic-manager/core';
|
|
15
|
+
/**
|
|
16
|
+
* Every non-internal IPv4 address of this machine, then `127.0.0.1`; candidates for the callback
|
|
17
|
+
* address. The loopback comes last so nothing that picked "the first candidate" changes - but it
|
|
18
|
+
* has to be offered: on an openccu-lite box the interface processes bind the loopback only
|
|
19
|
+
* (their D-29), so for the Homematic Manager running *on* the box it is the one address they
|
|
20
|
+
* can call back (openccu-lite task 28.10, maintainer 2026-09-08).
|
|
21
|
+
*/
|
|
13
22
|
export declare function localIPv4Addresses(interfaces?: () => NodeJS.Dict<os.NetworkInterfaceInfo[]>): string[];
|
|
23
|
+
/** One IPv4 address of this machine; `netmask` is absent where it is not known (an injected list). */
|
|
24
|
+
export interface LocalIPv4 {
|
|
25
|
+
readonly address: string;
|
|
26
|
+
readonly netmask?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* B-53: every non-internal IPv4 address of this machine with its netmask, in the order
|
|
30
|
+
* `os.networkInterfaces()` gives them - the loopback is not in it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function localIPv4Interfaces(interfaces?: () => NodeJS.Dict<os.NetworkInterfaceInfo[]>): LocalIPv4[];
|
|
33
|
+
/** A dotted IPv4 address as a number; `undefined` for anything else. */
|
|
34
|
+
export declare function ipv4ToNumber(address: string): number | undefined;
|
|
35
|
+
/** True when `target` is in the network of `address/netmask`. False when any of them is no IPv4 address. */
|
|
36
|
+
export declare function inSubnet(address: string, netmask: string, target: string): boolean;
|
|
37
|
+
/** `169.254.0.0/16`, the address an interface gives itself when no DHCP server answered. */
|
|
38
|
+
export declare function isLinkLocal(address: string): boolean;
|
|
39
|
+
/** `127.0.0.0/8`. */
|
|
40
|
+
export declare function isLoopback(address: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* B-53: what the automatic callback address is worked out from. Injected by the tests, so that no
|
|
43
|
+
* unit test depends on the network of the machine it runs on.
|
|
44
|
+
*/
|
|
45
|
+
export interface CallbackNetwork {
|
|
46
|
+
/** This machine's non-internal IPv4 addresses, see {@link localIPv4Interfaces}. */
|
|
47
|
+
readonly interfaces: () => LocalIPv4[];
|
|
48
|
+
/** The CCU host's IPv4 address; `undefined` when it does not resolve. Never rejects. */
|
|
49
|
+
readonly resolve: (host: string) => Promise<string | undefined>;
|
|
50
|
+
/** The local address the operating system sends from towards `address`; `undefined` when there is no route. Never rejects. */
|
|
51
|
+
readonly route: (address: string) => Promise<string | undefined>;
|
|
52
|
+
}
|
|
53
|
+
/** How long a name lookup for the callback address may take before the first address is used. */
|
|
54
|
+
export declare const RESOLVE_TIMEOUT_MS = 3000;
|
|
55
|
+
/** The CCU host's IPv4 address, through the system resolver (`/etc/hosts`, mDNS where the OS has it). */
|
|
56
|
+
export declare function resolveIPv4(host: string, lookup?: typeof dns.promises.lookup): Promise<string | undefined>;
|
|
57
|
+
/**
|
|
58
|
+
* The local address a UDP socket gets when it is `connect()`ed to `address`: the source address of
|
|
59
|
+
* the route the operating system would take. `connect` on a datagram socket only sets the default
|
|
60
|
+
* peer - no packet is sent - so this works for a CCU behind a router or a VPN as well as for one in
|
|
61
|
+
* the same network.
|
|
62
|
+
*/
|
|
63
|
+
export declare function routeSource(address: string, createSocket?: typeof dgram.createSocket): Promise<string | undefined>;
|
|
64
|
+
/** The machine's own network, as the running host sees it. */
|
|
65
|
+
export declare function systemNetwork(): CallbackNetwork;
|
|
66
|
+
/**
|
|
67
|
+
* A network made of a fixed address list, for a caller that injects `localAddresses` (the tests and
|
|
68
|
+
* the e2e hosts): no netmasks, no name lookup beyond an address literal, no route.
|
|
69
|
+
*/
|
|
70
|
+
export declare function staticNetwork(addresses: () => string[]): CallbackNetwork;
|
|
71
|
+
/**
|
|
72
|
+
* B-53 (#162, #165): the automatic callback address, and every address with whether it is in the
|
|
73
|
+
* CCU's network. Synchronous: `hostAddress` and `routed` are what {@link describeCallbackAddresses}
|
|
74
|
+
* found out, and `undefined` when nothing could be.
|
|
75
|
+
*
|
|
76
|
+
* The order: the CCU on the loopback (or `local`) is called back on the loopback; then the
|
|
77
|
+
* address in the CCU's network; then the route's source address; then the first address that is
|
|
78
|
+
* not link-local. A link-local address wins only as the CCU's own network, and the loopback is
|
|
79
|
+
* never chosen for a CCU elsewhere unless this machine has no other address at all.
|
|
80
|
+
*/
|
|
81
|
+
export declare function pickCallbackAddress(host: string, hostAddress: string | undefined, interfaces: readonly LocalIPv4[], routed: string | undefined, local?: boolean): CallbackAddressInfo;
|
|
82
|
+
/**
|
|
83
|
+
* B-53: {@link pickCallbackAddress} with the facts looked up - the CCU's name resolved, and the route
|
|
84
|
+
* asked for only when no address shares the CCU's network. Never rejects.
|
|
85
|
+
*/
|
|
86
|
+
export declare function describeCallbackAddresses(host: string, network: CallbackNetwork, local?: boolean): Promise<CallbackAddressInfo>;
|
|
14
87
|
export interface ProbeOptions {
|
|
15
88
|
readonly timeoutMs?: number;
|
|
16
89
|
/** Injected for the tests; defaults to `net.connect`. */
|
|
17
90
|
readonly connect?: typeof net.connect;
|
|
18
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* What a port probe found.
|
|
94
|
+
*
|
|
95
|
+
* B-28: `refused` and `unreachable` are different answers. A refused connection means nothing listens
|
|
96
|
+
* there - the interface process is not installed, the normal state of BidCos-Wired on a CCU without a
|
|
97
|
+
* wired gateway. A connection that times out, or a host without a route, says nothing about the
|
|
98
|
+
* process: a slow CCU-Jack, a remote CUxD on a box that is rebooting. Only the first may be shown as
|
|
99
|
+
* "not present".
|
|
100
|
+
*/
|
|
101
|
+
export type PortProbe = 'open' | 'refused' | 'unreachable';
|
|
102
|
+
/** Whether a TCP connection to `host:port` is accepted, refused, or not answered in time. Never throws. */
|
|
103
|
+
export declare function probePortState(host: string, port: number, options?: ProbeOptions): Promise<PortProbe>;
|
|
19
104
|
/** True when a TCP connection to `host:port` is accepted within the timeout. Never throws. */
|
|
20
105
|
export declare function probePort(host: string, port: number, options?: ProbeOptions): Promise<boolean>;
|
|
21
106
|
/** Resolves after `ms`; the timer never keeps the process alive. */
|
|
@@ -7,9 +7,17 @@
|
|
|
7
7
|
* 5 s timeout each before the window became usable) and it destroys the socket instead of leaving
|
|
8
8
|
* it to time out.
|
|
9
9
|
*/
|
|
10
|
+
import dgram from 'node:dgram';
|
|
11
|
+
import dns from 'node:dns';
|
|
10
12
|
import net from 'node:net';
|
|
11
13
|
import os from 'node:os';
|
|
12
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Every non-internal IPv4 address of this machine, then `127.0.0.1`; candidates for the callback
|
|
16
|
+
* address. The loopback comes last so nothing that picked "the first candidate" changes - but it
|
|
17
|
+
* has to be offered: on an openccu-lite box the interface processes bind the loopback only
|
|
18
|
+
* (their D-29), so for the Homematic Manager running *on* the box it is the one address they
|
|
19
|
+
* can call back (openccu-lite task 28.10, maintainer 2026-09-08).
|
|
20
|
+
*/
|
|
13
21
|
export function localIPv4Addresses(interfaces = os.networkInterfaces) {
|
|
14
22
|
const addresses = [];
|
|
15
23
|
for (const entries of Object.values(interfaces())) {
|
|
@@ -19,33 +27,239 @@ export function localIPv4Addresses(interfaces = os.networkInterfaces) {
|
|
|
19
27
|
}
|
|
20
28
|
}
|
|
21
29
|
}
|
|
30
|
+
addresses.push('127.0.0.1');
|
|
22
31
|
return addresses;
|
|
23
32
|
}
|
|
24
|
-
/**
|
|
25
|
-
|
|
33
|
+
/** The address an interface process on this machine calls back on. */
|
|
34
|
+
const LOOPBACK = '127.0.0.1';
|
|
35
|
+
/**
|
|
36
|
+
* B-53: every non-internal IPv4 address of this machine with its netmask, in the order
|
|
37
|
+
* `os.networkInterfaces()` gives them - the loopback is not in it.
|
|
38
|
+
*/
|
|
39
|
+
export function localIPv4Interfaces(interfaces = os.networkInterfaces) {
|
|
40
|
+
const found = [];
|
|
41
|
+
for (const entries of Object.values(interfaces())) {
|
|
42
|
+
for (const entry of entries ?? []) {
|
|
43
|
+
if (entry.family === 'IPv4' && !entry.internal && !found.some((seen) => seen.address === entry.address)) {
|
|
44
|
+
found.push({ address: entry.address, netmask: entry.netmask });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return found;
|
|
49
|
+
}
|
|
50
|
+
/** A dotted IPv4 address as a number; `undefined` for anything else. */
|
|
51
|
+
export function ipv4ToNumber(address) {
|
|
52
|
+
const parts = address.split('.');
|
|
53
|
+
if (parts.length !== 4) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
let value = 0;
|
|
57
|
+
for (const part of parts) {
|
|
58
|
+
if (!/^\d{1,3}$/.test(part)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const byte = Number(part);
|
|
62
|
+
if (byte > 255) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
value = value * 256 + byte;
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
/** True when `target` is in the network of `address/netmask`. False when any of them is no IPv4 address. */
|
|
70
|
+
export function inSubnet(address, netmask, target) {
|
|
71
|
+
const a = ipv4ToNumber(address);
|
|
72
|
+
const m = ipv4ToNumber(netmask);
|
|
73
|
+
const t = ipv4ToNumber(target);
|
|
74
|
+
if (a === undefined || m === undefined || t === undefined) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// `>>> 0` keeps the results unsigned; `&` alone would make 192.168.x.x negative
|
|
78
|
+
return (a & m) >>> 0 === (t & m) >>> 0;
|
|
79
|
+
}
|
|
80
|
+
/** `169.254.0.0/16`, the address an interface gives itself when no DHCP server answered. */
|
|
81
|
+
export function isLinkLocal(address) {
|
|
82
|
+
return address.startsWith('169.254.');
|
|
83
|
+
}
|
|
84
|
+
/** `127.0.0.0/8`. */
|
|
85
|
+
export function isLoopback(address) {
|
|
86
|
+
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(address);
|
|
87
|
+
}
|
|
88
|
+
/** How long a name lookup for the callback address may take before the first address is used. */
|
|
89
|
+
export const RESOLVE_TIMEOUT_MS = 3000;
|
|
90
|
+
/** The CCU host's IPv4 address, through the system resolver (`/etc/hosts`, mDNS where the OS has it). */
|
|
91
|
+
export async function resolveIPv4(host, lookup = dns.promises.lookup) {
|
|
92
|
+
if (ipv4ToNumber(host) !== undefined) {
|
|
93
|
+
return host;
|
|
94
|
+
}
|
|
95
|
+
if (host === '' || net.isIPv6(host)) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const result = await withTimeout(lookup(host, { family: 4 }), RESOLVE_TIMEOUT_MS, () => new Error('timeout'));
|
|
100
|
+
return result.address;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The local address a UDP socket gets when it is `connect()`ed to `address`: the source address of
|
|
108
|
+
* the route the operating system would take. `connect` on a datagram socket only sets the default
|
|
109
|
+
* peer - no packet is sent - so this works for a CCU behind a router or a VPN as well as for one in
|
|
110
|
+
* the same network.
|
|
111
|
+
*/
|
|
112
|
+
export function routeSource(address, createSocket = dgram.createSocket) {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
let socket;
|
|
115
|
+
try {
|
|
116
|
+
socket = createSocket('udp4');
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
resolve(undefined);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let settled = false;
|
|
123
|
+
const done = (result) => {
|
|
124
|
+
if (settled) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
settled = true;
|
|
128
|
+
try {
|
|
129
|
+
socket.close();
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// already closed
|
|
133
|
+
}
|
|
134
|
+
resolve(result);
|
|
135
|
+
};
|
|
136
|
+
socket.on('error', () => {
|
|
137
|
+
done(undefined);
|
|
138
|
+
});
|
|
139
|
+
try {
|
|
140
|
+
// the port does not matter, nothing is sent; 9 is "discard"
|
|
141
|
+
socket.connect(9, address, () => {
|
|
142
|
+
try {
|
|
143
|
+
done(socket.address().address);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
done(undefined);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
done(undefined);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** The machine's own network, as the running host sees it. */
|
|
156
|
+
export function systemNetwork() {
|
|
157
|
+
return {
|
|
158
|
+
interfaces: () => localIPv4Interfaces(),
|
|
159
|
+
resolve: (host) => resolveIPv4(host),
|
|
160
|
+
route: (address) => routeSource(address),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A network made of a fixed address list, for a caller that injects `localAddresses` (the tests and
|
|
165
|
+
* the e2e hosts): no netmasks, no name lookup beyond an address literal, no route.
|
|
166
|
+
*/
|
|
167
|
+
export function staticNetwork(addresses) {
|
|
168
|
+
return {
|
|
169
|
+
interfaces: () => addresses()
|
|
170
|
+
.filter((address) => !isLoopback(address))
|
|
171
|
+
.map((address) => ({ address })),
|
|
172
|
+
resolve: (host) => Promise.resolve(ipv4ToNumber(host) === undefined ? undefined : host),
|
|
173
|
+
route: () => Promise.resolve(undefined),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* B-53 (#162, #165): the automatic callback address, and every address with whether it is in the
|
|
178
|
+
* CCU's network. Synchronous: `hostAddress` and `routed` are what {@link describeCallbackAddresses}
|
|
179
|
+
* found out, and `undefined` when nothing could be.
|
|
180
|
+
*
|
|
181
|
+
* The order: the CCU on the loopback (or `local`) is called back on the loopback; then the
|
|
182
|
+
* address in the CCU's network; then the route's source address; then the first address that is
|
|
183
|
+
* not link-local. A link-local address wins only as the CCU's own network, and the loopback is
|
|
184
|
+
* never chosen for a CCU elsewhere unless this machine has no other address at all.
|
|
185
|
+
*/
|
|
186
|
+
export function pickCallbackAddress(host, hostAddress, interfaces, routed, local = false) {
|
|
187
|
+
const matches = (entry) => hostAddress !== undefined && entry.netmask !== undefined && inSubnet(entry.address, entry.netmask, hostAddress);
|
|
188
|
+
const ccuOnLoopback = local || (hostAddress !== undefined && isLoopback(hostAddress));
|
|
189
|
+
const addresses = interfaces.map((entry) => ({
|
|
190
|
+
address: entry.address,
|
|
191
|
+
inSubnet: matches(entry),
|
|
192
|
+
...(isLinkLocal(entry.address) ? { linkLocal: true } : {}),
|
|
193
|
+
}));
|
|
194
|
+
addresses.push({ address: LOOPBACK, inSubnet: ccuOnLoopback });
|
|
195
|
+
const choose = () => {
|
|
196
|
+
if (ccuOnLoopback) {
|
|
197
|
+
return { address: LOOPBACK, reason: 'loopback' };
|
|
198
|
+
}
|
|
199
|
+
const subnet = interfaces.find((entry) => matches(entry));
|
|
200
|
+
if (subnet !== undefined) {
|
|
201
|
+
return { address: subnet.address, reason: 'subnet' };
|
|
202
|
+
}
|
|
203
|
+
if (routed !== undefined && !isLinkLocal(routed) && interfaces.some((entry) => entry.address === routed)) {
|
|
204
|
+
return { address: routed, reason: 'route' };
|
|
205
|
+
}
|
|
206
|
+
const first = interfaces.find((entry) => !isLinkLocal(entry.address));
|
|
207
|
+
return { address: first?.address ?? LOOPBACK, reason: 'first' };
|
|
208
|
+
};
|
|
209
|
+
return {
|
|
210
|
+
host,
|
|
211
|
+
...(hostAddress === undefined ? {} : { hostAddress }),
|
|
212
|
+
auto: choose(),
|
|
213
|
+
addresses,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* B-53: {@link pickCallbackAddress} with the facts looked up - the CCU's name resolved, and the route
|
|
218
|
+
* asked for only when no address shares the CCU's network. Never rejects.
|
|
219
|
+
*/
|
|
220
|
+
export async function describeCallbackAddresses(host, network, local = false) {
|
|
221
|
+
const interfaces = network.interfaces();
|
|
222
|
+
if (local) {
|
|
223
|
+
return pickCallbackAddress(host, undefined, interfaces, undefined, true);
|
|
224
|
+
}
|
|
225
|
+
const hostAddress = host === '' ? undefined : await network.resolve(host);
|
|
226
|
+
const inNetwork = hostAddress !== undefined &&
|
|
227
|
+
(isLoopback(hostAddress) ||
|
|
228
|
+
interfaces.some((entry) => entry.netmask !== undefined && inSubnet(entry.address, entry.netmask, hostAddress)));
|
|
229
|
+
const routed = hostAddress === undefined || inNetwork ? undefined : await network.route(hostAddress);
|
|
230
|
+
return pickCallbackAddress(host, hostAddress, interfaces, routed);
|
|
231
|
+
}
|
|
232
|
+
/** Whether a TCP connection to `host:port` is accepted, refused, or not answered in time. Never throws. */
|
|
233
|
+
export function probePortState(host, port, options = {}) {
|
|
26
234
|
const timeoutMs = options.timeoutMs ?? 2000;
|
|
27
235
|
const connect = options.connect ?? net.connect;
|
|
28
236
|
return new Promise((resolve) => {
|
|
29
237
|
let settled = false;
|
|
30
|
-
const done = (
|
|
238
|
+
const done = (result) => {
|
|
31
239
|
if (settled) {
|
|
32
240
|
return;
|
|
33
241
|
}
|
|
34
242
|
settled = true;
|
|
35
243
|
socket.destroy();
|
|
36
|
-
resolve(
|
|
244
|
+
resolve(result);
|
|
37
245
|
};
|
|
38
246
|
const socket = connect({ host, port, timeout: timeoutMs }, () => {
|
|
39
|
-
done(
|
|
247
|
+
done('open');
|
|
40
248
|
});
|
|
41
|
-
socket.on('error', () => {
|
|
42
|
-
|
|
249
|
+
socket.on('error', (error) => {
|
|
250
|
+
// node's happy-eyeballs connect aggregates the per-address failures in `errors`
|
|
251
|
+
const codes = [error.code, ...(error.errors ?? []).map((entry) => entry.code)];
|
|
252
|
+
done(codes.includes('ECONNREFUSED') ? 'refused' : 'unreachable');
|
|
43
253
|
});
|
|
44
254
|
socket.on('timeout', () => {
|
|
45
|
-
done(
|
|
255
|
+
done('unreachable');
|
|
46
256
|
});
|
|
47
257
|
});
|
|
48
258
|
}
|
|
259
|
+
/** True when a TCP connection to `host:port` is accepted within the timeout. Never throws. */
|
|
260
|
+
export async function probePort(host, port, options = {}) {
|
|
261
|
+
return (await probePortState(host, port, options)) === 'open';
|
|
262
|
+
}
|
|
49
263
|
/** Resolves after `ms`; the timer never keeps the process alive. */
|
|
50
264
|
export function delay(ms) {
|
|
51
265
|
return new Promise((resolve) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homematic-manager/backend",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Homematic Manager backend: XML-RPC/BIN-RPC clients and callback servers, caches, optional ReGa",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|