node-red-contrib-knx-ultimate 7.1.4 → 8.0.2
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/CHANGELOG.md +20 -10
- package/MIGRATION.md +40 -0
- package/README.md +32 -6
- package/docs/README.md +34 -0
- package/docs/assets/brand/made-in-italy.svg +12 -0
- package/examples/Alerter - Device Alert Aggregation.json +5 -2
- package/examples/Auto Responder - Read Request Defaults.json +5 -2
- package/examples/Garage - Door Controller.json +5 -2
- package/examples/Global Context - Expose KNX Values.json +5 -2
- package/examples/KNX DateTime - Set Date and Time to Bus.json +111 -89
- package/examples/Load Control - Manual Shedding Mode.json +5 -2
- package/examples/Logger ETS File and Telegram Count.json +10 -2
- package/examples/Scene Controller Recall and Save.json +33 -6
- package/examples/Staircase - Timer, Override and Block.json +5 -2
- package/examples/WatchDog Connect Disconnect.json +98 -70
- package/nodes/commonFunctions.js +0 -587
- package/nodes/knxUltimate-config.html +0 -14
- package/nodes/knxUltimate-config.js +1 -18
- package/nodes/knxUltimate.html +1 -6
- package/nodes/knxUltimateIoTBridge.html +0 -14
- package/nodes/knxUltimateLegacyMigration.js +75 -0
- package/nodes/knxUltimateMultiRouting.html +1 -7
- package/nodes/knxUltimateRouterFilter.html +1 -7
- package/nodes/knxUltimateUtility.html +0 -6
- package/nodes/knxUltimateViewer.html +0 -6
- package/nodes/locales/de/knxUltimate-config.html +0 -8
- package/nodes/locales/de/knxUltimate.html +0 -8
- package/nodes/locales/de/knxUltimateIoTBridge.html +0 -8
- package/nodes/locales/de/knxUltimateMultiRouting.html +0 -8
- package/nodes/locales/de/knxUltimateRouterFilter.html +0 -8
- package/nodes/locales/de/knxUltimateUtility.html +0 -8
- package/nodes/locales/de/knxUltimateUtility.json +16 -1
- package/nodes/locales/de/knxUltimateViewer.html +0 -8
- package/nodes/locales/en/knxUltimate-config.html +0 -8
- package/nodes/locales/en/knxUltimate.html +0 -8
- package/nodes/locales/en/knxUltimateIoTBridge.html +0 -8
- package/nodes/locales/en/knxUltimateMultiRouting.html +0 -8
- package/nodes/locales/en/knxUltimateRouterFilter.html +0 -8
- package/nodes/locales/en/knxUltimateUtility.html +0 -8
- package/nodes/locales/en/knxUltimateUtility.json +16 -1
- package/nodes/locales/en/knxUltimateViewer.html +0 -8
- package/nodes/locales/es/knxUltimate-config.html +0 -8
- package/nodes/locales/es/knxUltimate.html +0 -8
- package/nodes/locales/es/knxUltimateIoTBridge.html +0 -8
- package/nodes/locales/es/knxUltimateMultiRouting.html +0 -8
- package/nodes/locales/es/knxUltimateRouterFilter.html +0 -8
- package/nodes/locales/es/knxUltimateUtility.html +0 -8
- package/nodes/locales/es/knxUltimateUtility.json +17 -2
- package/nodes/locales/es/knxUltimateViewer.html +0 -8
- package/nodes/locales/fr/knxUltimate-config.html +0 -8
- package/nodes/locales/fr/knxUltimate.html +0 -8
- package/nodes/locales/fr/knxUltimateIoTBridge.html +0 -8
- package/nodes/locales/fr/knxUltimateMultiRouting.html +0 -8
- package/nodes/locales/fr/knxUltimateRouterFilter.html +0 -8
- package/nodes/locales/fr/knxUltimateUtility.html +0 -8
- package/nodes/locales/fr/knxUltimateUtility.json +16 -1
- package/nodes/locales/fr/knxUltimateViewer.html +0 -8
- package/nodes/locales/it/knxUltimate-config.html +0 -8
- package/nodes/locales/it/knxUltimate.html +0 -8
- package/nodes/locales/it/knxUltimateIoTBridge.html +0 -8
- package/nodes/locales/it/knxUltimateMultiRouting.html +0 -8
- package/nodes/locales/it/knxUltimateRouterFilter.html +0 -8
- package/nodes/locales/it/knxUltimateUtility.html +0 -8
- package/nodes/locales/it/knxUltimateUtility.json +16 -1
- package/nodes/locales/it/knxUltimateViewer.html +0 -8
- package/nodes/locales/zh-CN/knxUltimate-config.html +0 -8
- package/nodes/locales/zh-CN/knxUltimate.html +0 -8
- package/nodes/locales/zh-CN/knxUltimateIoTBridge.html +0 -8
- package/nodes/locales/zh-CN/knxUltimateMultiRouting.html +0 -8
- package/nodes/locales/zh-CN/knxUltimateRouterFilter.html +0 -8
- package/nodes/locales/zh-CN/knxUltimateUtility.html +0 -8
- package/nodes/locales/zh-CN/knxUltimateUtility.json +16 -1
- package/nodes/locales/zh-CN/knxUltimateViewer.html +0 -8
- package/nodes/plugins/knxUltimate-v8-migration-recovery-plugin.html +140 -0
- package/package.json +22 -69
- package/resources/11f26b4500.js +2 -2
- package/resources/KNXAIChatAdapterMappings.js +2 -416
- package/resources/configNodeEditorSelection.js +2 -32
- package/resources/hueControllerEditorSelection.js +2 -32
- package/resources/hueControllerMigrationDialog.js +2 -254
- package/resources/hueControllerProfiles.js +2 -9319
- package/resources/knxUtilityProfiles.js +2041 -2082
- package/resources/legacyMigrationNotice.js +23 -111
- package/resources/matterQrScanner.js +2 -98
- package/resources/upgradeToV8.js +94 -26
- package/scripts/check-legacy-flows.js +142 -0
- package/examples/Hue Area Motion - Zone Presence.json +0 -77
- package/examples/Hue Battery - Level Status.json +0 -77
- package/examples/Hue Button - Short and Dim Commands.json +0 -83
- package/examples/Hue Camera Motion - Presence.json +0 -77
- package/examples/Hue Config - Bridge Template.json +0 -27
- package/examples/Hue Contact Sensor - Open Closed.json +0 -75
- package/examples/Hue Device Software Update - Availability.json +0 -77
- package/examples/Hue Humidity Sensor - Relative Humidity.json +0 -77
- package/examples/Hue Light - Switch and Brightness.json +0 -67
- package/examples/Hue Light Sensor - Lux to KNX.json +0 -77
- package/examples/Hue Motion - Presence Sensor.json +0 -76
- package/examples/Hue Plug - Switch and State.json +0 -63
- package/examples/Hue Scene - Recall from KNX.json +0 -63
- package/examples/Hue Tap Dial - Relative Dimming.json +0 -76
- package/examples/Hue Temperature Sensor - Celsius to KNX.json +0 -77
- package/examples/Hue Zigbee Connectivity - Online Status.json +0 -77
- package/examples/Matter Controller - Semantic Flow Input.json +0 -248
- package/nodes/hue-config.html +0 -378
- package/nodes/hue-config.js +0 -846
- package/nodes/icons/node-alerter-icon.png +0 -0
- package/nodes/icons/node-homekit-icon.png +0 -0
- package/nodes/icons/node-homekit-icon.svg +0 -10
- package/nodes/icons/node-hue-icon.svg +0 -10
- package/nodes/icons/node-knx-icon.png +0 -0
- package/nodes/icons/node-logger-icon.png +0 -0
- package/nodes/icons/node-logger2-icon.png +0 -0
- package/nodes/icons/node-logger2-icon.svg +0 -32
- package/nodes/icons/node-matter-bridge-icon.svg +0 -6
- package/nodes/icons/node-matter-controller-icon.svg +0 -6
- package/nodes/icons/node-matter-icon.svg +0 -12
- package/nodes/icons/node-scene-icon.png +0 -0
- package/nodes/icons/node-watchdog-icon.png +0 -0
- package/nodes/knxUltimateAI.html +0 -1826
- package/nodes/knxUltimateAI.js +0 -16797
- package/nodes/knxUltimateAIHomeAssistant.html +0 -54
- package/nodes/knxUltimateAIHomeAssistant.js +0 -152
- package/nodes/knxUltimateAlerter.html +0 -267
- package/nodes/knxUltimateAlerter.js +0 -300
- package/nodes/knxUltimateAutoResponder.html +0 -128
- package/nodes/knxUltimateAutoResponder.js +0 -264
- package/nodes/knxUltimateDateTime.html +0 -579
- package/nodes/knxUltimateDateTime.js +0 -282
- package/nodes/knxUltimateGarage.html +0 -337
- package/nodes/knxUltimateGarage.js +0 -504
- package/nodes/knxUltimateGlobalContext.html +0 -145
- package/nodes/knxUltimateGlobalContext.js +0 -208
- package/nodes/knxUltimateHATranslator.html +0 -85
- package/nodes/knxUltimateHATranslator.js +0 -83
- package/nodes/knxUltimateHueAreaMotion.html +0 -670
- package/nodes/knxUltimateHueAreaMotion.js +0 -218
- package/nodes/knxUltimateHueBattery.html +0 -646
- package/nodes/knxUltimateHueBattery.js +0 -182
- package/nodes/knxUltimateHueButton.html +0 -804
- package/nodes/knxUltimateHueButton.js +0 -292
- package/nodes/knxUltimateHueCameraMotion.html +0 -671
- package/nodes/knxUltimateHueCameraMotion.js +0 -218
- package/nodes/knxUltimateHueContactSensor.html +0 -609
- package/nodes/knxUltimateHueContactSensor.js +0 -188
- package/nodes/knxUltimateHueController.html +0 -1765
- package/nodes/knxUltimateHueController.js +0 -28
- package/nodes/knxUltimateHueHumiditySensor.html +0 -641
- package/nodes/knxUltimateHueHumiditySensor.js +0 -201
- package/nodes/knxUltimateHueLight.html +0 -2516
- package/nodes/knxUltimateHueLight.js +0 -1582
- package/nodes/knxUltimateHueLightSensor.html +0 -636
- package/nodes/knxUltimateHueLightSensor.js +0 -177
- package/nodes/knxUltimateHueMotion.html +0 -634
- package/nodes/knxUltimateHueMotion.js +0 -182
- package/nodes/knxUltimateHuePlug.html +0 -576
- package/nodes/knxUltimateHuePlug.js +0 -341
- package/nodes/knxUltimateHueScene.html +0 -967
- package/nodes/knxUltimateHueScene.js +0 -349
- package/nodes/knxUltimateHueTapDial.html +0 -640
- package/nodes/knxUltimateHueTapDial.js +0 -268
- package/nodes/knxUltimateHueTemperatureSensor.html +0 -639
- package/nodes/knxUltimateHueTemperatureSensor.js +0 -184
- package/nodes/knxUltimateHueZigbeeConnectivity.html +0 -648
- package/nodes/knxUltimateHueZigbeeConnectivity.js +0 -182
- package/nodes/knxUltimateHuedevice_software_update.html +0 -650
- package/nodes/knxUltimateHuedevice_software_update.js +0 -182
- package/nodes/knxUltimateLoadControl.html +0 -425
- package/nodes/knxUltimateLoadControl.js +0 -443
- package/nodes/knxUltimateLogger.html +0 -197
- package/nodes/knxUltimateLogger.js +0 -224
- package/nodes/knxUltimateMatterBridge.html +0 -689
- package/nodes/knxUltimateMatterBridge.js +0 -464
- package/nodes/knxUltimateMatterControllerDevice.html +0 -2851
- package/nodes/knxUltimateMatterControllerDevice.js +0 -1747
- package/nodes/knxUltimateSceneController.html +0 -529
- package/nodes/knxUltimateSceneController.js +0 -437
- package/nodes/knxUltimateStaircase.html +0 -346
- package/nodes/knxUltimateStaircase.js +0 -463
- package/nodes/knxUltimateWatchDog.html +0 -241
- package/nodes/knxUltimateWatchDog.js +0 -230
- package/nodes/locales/de/hue-config.html +0 -33
- package/nodes/locales/de/hue-config.json +0 -21
- package/nodes/locales/de/knxUltimateAI.html +0 -13
- package/nodes/locales/de/knxUltimateAI.json +0 -231
- package/nodes/locales/de/knxUltimateAIHomeAssistant.html +0 -11
- package/nodes/locales/de/knxUltimateAlerter.html +0 -159
- package/nodes/locales/de/knxUltimateAlerter.json +0 -27
- package/nodes/locales/de/knxUltimateAutoResponder.html +0 -96
- package/nodes/locales/de/knxUltimateAutoResponder.json +0 -8
- package/nodes/locales/de/knxUltimateDateTime.html +0 -45
- package/nodes/locales/de/knxUltimateDateTime.json +0 -32
- package/nodes/locales/de/knxUltimateGarage.html +0 -79
- package/nodes/locales/de/knxUltimateGarage.json +0 -51
- package/nodes/locales/de/knxUltimateGlobalContext.html +0 -76
- package/nodes/locales/de/knxUltimateGlobalContext.json +0 -24
- package/nodes/locales/de/knxUltimateHATranslator.html +0 -25
- package/nodes/locales/de/knxUltimateHATranslator.json +0 -8
- package/nodes/locales/de/knxUltimateHueAreaMotion.html +0 -44
- package/nodes/locales/de/knxUltimateHueAreaMotion.json +0 -31
- package/nodes/locales/de/knxUltimateHueBattery.html +0 -37
- package/nodes/locales/de/knxUltimateHueBattery.json +0 -30
- package/nodes/locales/de/knxUltimateHueButton.html +0 -52
- package/nodes/locales/de/knxUltimateHueButton.json +0 -34
- package/nodes/locales/de/knxUltimateHueCameraMotion.html +0 -38
- package/nodes/locales/de/knxUltimateHueCameraMotion.json +0 -30
- package/nodes/locales/de/knxUltimateHueContactSensor.html +0 -35
- package/nodes/locales/de/knxUltimateHueContactSensor.json +0 -26
- package/nodes/locales/de/knxUltimateHueController.html +0 -690
- package/nodes/locales/de/knxUltimateHueController.json +0 -64
- package/nodes/locales/de/knxUltimateHueHumiditySensor.html +0 -38
- package/nodes/locales/de/knxUltimateHueHumiditySensor.json +0 -30
- package/nodes/locales/de/knxUltimateHueLight.html +0 -121
- package/nodes/locales/de/knxUltimateHueLight.json +0 -94
- package/nodes/locales/de/knxUltimateHueLightSensor.html +0 -38
- package/nodes/locales/de/knxUltimateHueLightSensor.json +0 -19
- package/nodes/locales/de/knxUltimateHueMotion.html +0 -41
- package/nodes/locales/de/knxUltimateHueMotion.json +0 -27
- package/nodes/locales/de/knxUltimateHuePlug.html +0 -50
- package/nodes/locales/de/knxUltimateHuePlug.json +0 -33
- package/nodes/locales/de/knxUltimateHueScene.html +0 -45
- package/nodes/locales/de/knxUltimateHueScene.json +0 -41
- package/nodes/locales/de/knxUltimateHueTapDial.html +0 -40
- package/nodes/locales/de/knxUltimateHueTapDial.json +0 -28
- package/nodes/locales/de/knxUltimateHueTemperatureSensor.html +0 -38
- package/nodes/locales/de/knxUltimateHueTemperatureSensor.json +0 -30
- package/nodes/locales/de/knxUltimateHueZigbeeConnectivity.html +0 -39
- package/nodes/locales/de/knxUltimateHueZigbeeConnectivity.json +0 -30
- package/nodes/locales/de/knxUltimateHuedevice_software_update.html +0 -36
- package/nodes/locales/de/knxUltimateHuedevice_software_update.json +0 -30
- package/nodes/locales/de/knxUltimateLoadControl.html +0 -69
- package/nodes/locales/de/knxUltimateLoadControl.json +0 -28
- package/nodes/locales/de/knxUltimateLogger.html +0 -126
- package/nodes/locales/de/knxUltimateLogger.json +0 -31
- package/nodes/locales/de/knxUltimateMatterBridge.html +0 -65
- package/nodes/locales/de/knxUltimateMatterBridge.json +0 -123
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +0 -57
- package/nodes/locales/de/knxUltimateMatterControllerDevice.json +0 -146
- package/nodes/locales/de/knxUltimateSceneController.html +0 -98
- package/nodes/locales/de/knxUltimateSceneController.json +0 -32
- package/nodes/locales/de/knxUltimateStaircase.html +0 -77
- package/nodes/locales/de/knxUltimateStaircase.json +0 -62
- package/nodes/locales/de/knxUltimateWatchDog.html +0 -178
- package/nodes/locales/de/knxUltimateWatchDog.json +0 -27
- package/nodes/locales/de/matter-config.html +0 -47
- package/nodes/locales/de/matter-config.json +0 -47
- package/nodes/locales/de/matterbridge-config.html +0 -45
- package/nodes/locales/de/matterbridge-config.json +0 -25
- package/nodes/locales/en/hue-config.html +0 -31
- package/nodes/locales/en/hue-config.json +0 -21
- package/nodes/locales/en/knxUltimateAI.html +0 -13
- package/nodes/locales/en/knxUltimateAI.json +0 -234
- package/nodes/locales/en/knxUltimateAIHomeAssistant.html +0 -11
- package/nodes/locales/en/knxUltimateAlerter.html +0 -164
- package/nodes/locales/en/knxUltimateAlerter.json +0 -27
- package/nodes/locales/en/knxUltimateAutoResponder.html +0 -94
- package/nodes/locales/en/knxUltimateAutoResponder.json +0 -8
- package/nodes/locales/en/knxUltimateDateTime.html +0 -67
- package/nodes/locales/en/knxUltimateDateTime.json +0 -32
- package/nodes/locales/en/knxUltimateGarage.html +0 -79
- package/nodes/locales/en/knxUltimateGarage.json +0 -51
- package/nodes/locales/en/knxUltimateGlobalContext.html +0 -124
- package/nodes/locales/en/knxUltimateGlobalContext.json +0 -24
- package/nodes/locales/en/knxUltimateHATranslator.html +0 -25
- package/nodes/locales/en/knxUltimateHATranslator.json +0 -8
- package/nodes/locales/en/knxUltimateHueAreaMotion.html +0 -44
- package/nodes/locales/en/knxUltimateHueAreaMotion.json +0 -31
- package/nodes/locales/en/knxUltimateHueBattery.html +0 -37
- package/nodes/locales/en/knxUltimateHueBattery.json +0 -30
- package/nodes/locales/en/knxUltimateHueButton.html +0 -52
- package/nodes/locales/en/knxUltimateHueButton.json +0 -34
- package/nodes/locales/en/knxUltimateHueCameraMotion.html +0 -38
- package/nodes/locales/en/knxUltimateHueCameraMotion.json +0 -30
- package/nodes/locales/en/knxUltimateHueContactSensor.html +0 -35
- package/nodes/locales/en/knxUltimateHueContactSensor.json +0 -26
- package/nodes/locales/en/knxUltimateHueController.html +0 -691
- package/nodes/locales/en/knxUltimateHueController.json +0 -64
- package/nodes/locales/en/knxUltimateHueHumiditySensor.html +0 -38
- package/nodes/locales/en/knxUltimateHueHumiditySensor.json +0 -30
- package/nodes/locales/en/knxUltimateHueLight.html +0 -124
- package/nodes/locales/en/knxUltimateHueLight.json +0 -94
- package/nodes/locales/en/knxUltimateHueLightSensor.html +0 -38
- package/nodes/locales/en/knxUltimateHueLightSensor.json +0 -19
- package/nodes/locales/en/knxUltimateHueMotion.html +0 -41
- package/nodes/locales/en/knxUltimateHueMotion.json +0 -27
- package/nodes/locales/en/knxUltimateHuePlug.html +0 -50
- package/nodes/locales/en/knxUltimateHuePlug.json +0 -33
- package/nodes/locales/en/knxUltimateHueScene.html +0 -45
- package/nodes/locales/en/knxUltimateHueScene.json +0 -41
- package/nodes/locales/en/knxUltimateHueTapDial.html +0 -40
- package/nodes/locales/en/knxUltimateHueTapDial.json +0 -28
- package/nodes/locales/en/knxUltimateHueTemperatureSensor.html +0 -38
- package/nodes/locales/en/knxUltimateHueTemperatureSensor.json +0 -30
- package/nodes/locales/en/knxUltimateHueZigbeeConnectivity.html +0 -39
- package/nodes/locales/en/knxUltimateHueZigbeeConnectivity.json +0 -30
- package/nodes/locales/en/knxUltimateHuedevice_software_update.html +0 -36
- package/nodes/locales/en/knxUltimateHuedevice_software_update.json +0 -30
- package/nodes/locales/en/knxUltimateLoadControl.html +0 -76
- package/nodes/locales/en/knxUltimateLoadControl.json +0 -28
- package/nodes/locales/en/knxUltimateLogger.html +0 -161
- package/nodes/locales/en/knxUltimateLogger.json +0 -31
- package/nodes/locales/en/knxUltimateMatterBridge.html +0 -65
- package/nodes/locales/en/knxUltimateMatterBridge.json +0 -123
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +0 -57
- package/nodes/locales/en/knxUltimateMatterControllerDevice.json +0 -146
- package/nodes/locales/en/knxUltimateSceneController.html +0 -117
- package/nodes/locales/en/knxUltimateSceneController.json +0 -32
- package/nodes/locales/en/knxUltimateStaircase.html +0 -77
- package/nodes/locales/en/knxUltimateStaircase.json +0 -62
- package/nodes/locales/en/knxUltimateWatchDog.html +0 -244
- package/nodes/locales/en/knxUltimateWatchDog.json +0 -27
- package/nodes/locales/en/matter-config.html +0 -47
- package/nodes/locales/en/matter-config.json +0 -48
- package/nodes/locales/en/matterbridge-config.html +0 -45
- package/nodes/locales/en/matterbridge-config.json +0 -25
- package/nodes/locales/es/hue-config.html +0 -31
- package/nodes/locales/es/hue-config.json +0 -21
- package/nodes/locales/es/knxUltimateAI.html +0 -13
- package/nodes/locales/es/knxUltimateAI.json +0 -231
- package/nodes/locales/es/knxUltimateAIHomeAssistant.html +0 -11
- package/nodes/locales/es/knxUltimateAlerter.html +0 -164
- package/nodes/locales/es/knxUltimateAlerter.json +0 -27
- package/nodes/locales/es/knxUltimateAutoResponder.html +0 -81
- package/nodes/locales/es/knxUltimateAutoResponder.json +0 -8
- package/nodes/locales/es/knxUltimateDateTime.html +0 -45
- package/nodes/locales/es/knxUltimateDateTime.json +0 -32
- package/nodes/locales/es/knxUltimateGarage.html +0 -79
- package/nodes/locales/es/knxUltimateGarage.json +0 -51
- package/nodes/locales/es/knxUltimateGlobalContext.html +0 -128
- package/nodes/locales/es/knxUltimateGlobalContext.json +0 -24
- package/nodes/locales/es/knxUltimateHATranslator.html +0 -25
- package/nodes/locales/es/knxUltimateHATranslator.json +0 -8
- package/nodes/locales/es/knxUltimateHueAreaMotion.html +0 -44
- package/nodes/locales/es/knxUltimateHueAreaMotion.json +0 -31
- package/nodes/locales/es/knxUltimateHueBattery.html +0 -37
- package/nodes/locales/es/knxUltimateHueBattery.json +0 -30
- package/nodes/locales/es/knxUltimateHueButton.html +0 -52
- package/nodes/locales/es/knxUltimateHueButton.json +0 -34
- package/nodes/locales/es/knxUltimateHueCameraMotion.html +0 -38
- package/nodes/locales/es/knxUltimateHueCameraMotion.json +0 -30
- package/nodes/locales/es/knxUltimateHueContactSensor.html +0 -35
- package/nodes/locales/es/knxUltimateHueContactSensor.json +0 -26
- package/nodes/locales/es/knxUltimateHueController.html +0 -691
- package/nodes/locales/es/knxUltimateHueController.json +0 -64
- package/nodes/locales/es/knxUltimateHueHumiditySensor.html +0 -38
- package/nodes/locales/es/knxUltimateHueHumiditySensor.json +0 -30
- package/nodes/locales/es/knxUltimateHueLight.html +0 -124
- package/nodes/locales/es/knxUltimateHueLight.json +0 -94
- package/nodes/locales/es/knxUltimateHueLightSensor.html +0 -38
- package/nodes/locales/es/knxUltimateHueLightSensor.json +0 -19
- package/nodes/locales/es/knxUltimateHueMotion.html +0 -41
- package/nodes/locales/es/knxUltimateHueMotion.json +0 -27
- package/nodes/locales/es/knxUltimateHuePlug.html +0 -50
- package/nodes/locales/es/knxUltimateHuePlug.json +0 -33
- package/nodes/locales/es/knxUltimateHueScene.html +0 -45
- package/nodes/locales/es/knxUltimateHueScene.json +0 -41
- package/nodes/locales/es/knxUltimateHueTapDial.html +0 -40
- package/nodes/locales/es/knxUltimateHueTapDial.json +0 -28
- package/nodes/locales/es/knxUltimateHueTemperatureSensor.html +0 -38
- package/nodes/locales/es/knxUltimateHueTemperatureSensor.json +0 -30
- package/nodes/locales/es/knxUltimateHueZigbeeConnectivity.html +0 -39
- package/nodes/locales/es/knxUltimateHueZigbeeConnectivity.json +0 -30
- package/nodes/locales/es/knxUltimateHuedevice_software_update.html +0 -36
- package/nodes/locales/es/knxUltimateHuedevice_software_update.json +0 -30
- package/nodes/locales/es/knxUltimateLoadControl.html +0 -76
- package/nodes/locales/es/knxUltimateLoadControl.json +0 -28
- package/nodes/locales/es/knxUltimateLogger.html +0 -162
- package/nodes/locales/es/knxUltimateLogger.json +0 -31
- package/nodes/locales/es/knxUltimateMatterBridge.html +0 -65
- package/nodes/locales/es/knxUltimateMatterBridge.json +0 -123
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +0 -57
- package/nodes/locales/es/knxUltimateMatterControllerDevice.json +0 -146
- package/nodes/locales/es/knxUltimateSceneController.html +0 -115
- package/nodes/locales/es/knxUltimateSceneController.json +0 -32
- package/nodes/locales/es/knxUltimateStaircase.html +0 -77
- package/nodes/locales/es/knxUltimateStaircase.json +0 -62
- package/nodes/locales/es/knxUltimateWatchDog.html +0 -230
- package/nodes/locales/es/knxUltimateWatchDog.json +0 -27
- package/nodes/locales/es/matter-config.html +0 -47
- package/nodes/locales/es/matter-config.json +0 -47
- package/nodes/locales/es/matterbridge-config.html +0 -45
- package/nodes/locales/es/matterbridge-config.json +0 -25
- package/nodes/locales/fr/hue-config.html +0 -31
- package/nodes/locales/fr/hue-config.json +0 -21
- package/nodes/locales/fr/knxUltimateAI.html +0 -13
- package/nodes/locales/fr/knxUltimateAI.json +0 -231
- package/nodes/locales/fr/knxUltimateAIHomeAssistant.html +0 -11
- package/nodes/locales/fr/knxUltimateAlerter.html +0 -164
- package/nodes/locales/fr/knxUltimateAlerter.json +0 -27
- package/nodes/locales/fr/knxUltimateAutoResponder.html +0 -92
- package/nodes/locales/fr/knxUltimateAutoResponder.json +0 -8
- package/nodes/locales/fr/knxUltimateDateTime.html +0 -45
- package/nodes/locales/fr/knxUltimateDateTime.json +0 -32
- package/nodes/locales/fr/knxUltimateGarage.html +0 -79
- package/nodes/locales/fr/knxUltimateGarage.json +0 -51
- package/nodes/locales/fr/knxUltimateGlobalContext.html +0 -122
- package/nodes/locales/fr/knxUltimateGlobalContext.json +0 -24
- package/nodes/locales/fr/knxUltimateHATranslator.html +0 -25
- package/nodes/locales/fr/knxUltimateHATranslator.json +0 -8
- package/nodes/locales/fr/knxUltimateHueAreaMotion.html +0 -44
- package/nodes/locales/fr/knxUltimateHueAreaMotion.json +0 -31
- package/nodes/locales/fr/knxUltimateHueBattery.html +0 -37
- package/nodes/locales/fr/knxUltimateHueBattery.json +0 -30
- package/nodes/locales/fr/knxUltimateHueButton.html +0 -52
- package/nodes/locales/fr/knxUltimateHueButton.json +0 -34
- package/nodes/locales/fr/knxUltimateHueCameraMotion.html +0 -38
- package/nodes/locales/fr/knxUltimateHueCameraMotion.json +0 -30
- package/nodes/locales/fr/knxUltimateHueContactSensor.html +0 -35
- package/nodes/locales/fr/knxUltimateHueContactSensor.json +0 -26
- package/nodes/locales/fr/knxUltimateHueController.html +0 -691
- package/nodes/locales/fr/knxUltimateHueController.json +0 -64
- package/nodes/locales/fr/knxUltimateHueHumiditySensor.html +0 -38
- package/nodes/locales/fr/knxUltimateHueHumiditySensor.json +0 -30
- package/nodes/locales/fr/knxUltimateHueLight.html +0 -124
- package/nodes/locales/fr/knxUltimateHueLight.json +0 -86
- package/nodes/locales/fr/knxUltimateHueLightSensor.html +0 -38
- package/nodes/locales/fr/knxUltimateHueLightSensor.json +0 -19
- package/nodes/locales/fr/knxUltimateHueMotion.html +0 -41
- package/nodes/locales/fr/knxUltimateHueMotion.json +0 -27
- package/nodes/locales/fr/knxUltimateHuePlug.html +0 -50
- package/nodes/locales/fr/knxUltimateHuePlug.json +0 -33
- package/nodes/locales/fr/knxUltimateHueScene.html +0 -45
- package/nodes/locales/fr/knxUltimateHueScene.json +0 -41
- package/nodes/locales/fr/knxUltimateHueTapDial.html +0 -40
- package/nodes/locales/fr/knxUltimateHueTapDial.json +0 -28
- package/nodes/locales/fr/knxUltimateHueTemperatureSensor.html +0 -38
- package/nodes/locales/fr/knxUltimateHueTemperatureSensor.json +0 -30
- package/nodes/locales/fr/knxUltimateHueZigbeeConnectivity.html +0 -39
- package/nodes/locales/fr/knxUltimateHueZigbeeConnectivity.json +0 -30
- package/nodes/locales/fr/knxUltimateHuedevice_software_update.html +0 -36
- package/nodes/locales/fr/knxUltimateHuedevice_software_update.json +0 -30
- package/nodes/locales/fr/knxUltimateLoadControl.html +0 -76
- package/nodes/locales/fr/knxUltimateLoadControl.json +0 -28
- package/nodes/locales/fr/knxUltimateLogger.html +0 -160
- package/nodes/locales/fr/knxUltimateLogger.json +0 -31
- package/nodes/locales/fr/knxUltimateMatterBridge.html +0 -65
- package/nodes/locales/fr/knxUltimateMatterBridge.json +0 -123
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +0 -57
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +0 -146
- package/nodes/locales/fr/knxUltimateSceneController.html +0 -117
- package/nodes/locales/fr/knxUltimateSceneController.json +0 -32
- package/nodes/locales/fr/knxUltimateStaircase.html +0 -77
- package/nodes/locales/fr/knxUltimateStaircase.json +0 -62
- package/nodes/locales/fr/knxUltimateWatchDog.html +0 -228
- package/nodes/locales/fr/knxUltimateWatchDog.json +0 -27
- package/nodes/locales/fr/matter-config.html +0 -47
- package/nodes/locales/fr/matter-config.json +0 -47
- package/nodes/locales/fr/matterbridge-config.html +0 -45
- package/nodes/locales/fr/matterbridge-config.json +0 -25
- package/nodes/locales/it/hue-config.html +0 -33
- package/nodes/locales/it/hue-config.json +0 -21
- package/nodes/locales/it/knxUltimateAI.html +0 -13
- package/nodes/locales/it/knxUltimateAI.json +0 -234
- package/nodes/locales/it/knxUltimateAIHomeAssistant.html +0 -11
- package/nodes/locales/it/knxUltimateAlerter.html +0 -159
- package/nodes/locales/it/knxUltimateAlerter.json +0 -27
- package/nodes/locales/it/knxUltimateAutoResponder.html +0 -96
- package/nodes/locales/it/knxUltimateAutoResponder.json +0 -8
- package/nodes/locales/it/knxUltimateDateTime.html +0 -67
- package/nodes/locales/it/knxUltimateDateTime.json +0 -32
- package/nodes/locales/it/knxUltimateGarage.html +0 -79
- package/nodes/locales/it/knxUltimateGarage.json +0 -51
- package/nodes/locales/it/knxUltimateGlobalContext.html +0 -81
- package/nodes/locales/it/knxUltimateGlobalContext.json +0 -24
- package/nodes/locales/it/knxUltimateHATranslator.html +0 -24
- package/nodes/locales/it/knxUltimateHATranslator.json +0 -8
- package/nodes/locales/it/knxUltimateHueAreaMotion.html +0 -44
- package/nodes/locales/it/knxUltimateHueAreaMotion.json +0 -31
- package/nodes/locales/it/knxUltimateHueBattery.html +0 -37
- package/nodes/locales/it/knxUltimateHueBattery.json +0 -30
- package/nodes/locales/it/knxUltimateHueButton.html +0 -52
- package/nodes/locales/it/knxUltimateHueButton.json +0 -34
- package/nodes/locales/it/knxUltimateHueCameraMotion.html +0 -38
- package/nodes/locales/it/knxUltimateHueCameraMotion.json +0 -30
- package/nodes/locales/it/knxUltimateHueContactSensor.html +0 -35
- package/nodes/locales/it/knxUltimateHueContactSensor.json +0 -26
- package/nodes/locales/it/knxUltimateHueController.html +0 -690
- package/nodes/locales/it/knxUltimateHueController.json +0 -64
- package/nodes/locales/it/knxUltimateHueHumiditySensor 2.html +0 -11
- package/nodes/locales/it/knxUltimateHueHumiditySensor.html +0 -38
- package/nodes/locales/it/knxUltimateHueHumiditySensor.json +0 -30
- package/nodes/locales/it/knxUltimateHueLight.html +0 -124
- package/nodes/locales/it/knxUltimateHueLight.json +0 -94
- package/nodes/locales/it/knxUltimateHueLightSensor.html +0 -38
- package/nodes/locales/it/knxUltimateHueLightSensor.json +0 -19
- package/nodes/locales/it/knxUltimateHueMotion.html +0 -41
- package/nodes/locales/it/knxUltimateHueMotion.json +0 -27
- package/nodes/locales/it/knxUltimateHuePlug.html +0 -50
- package/nodes/locales/it/knxUltimateHuePlug.json +0 -33
- package/nodes/locales/it/knxUltimateHueScene.html +0 -45
- package/nodes/locales/it/knxUltimateHueScene.json +0 -41
- package/nodes/locales/it/knxUltimateHueTapDial.html +0 -40
- package/nodes/locales/it/knxUltimateHueTapDial.json +0 -28
- package/nodes/locales/it/knxUltimateHueTemperatureSensor.html +0 -38
- package/nodes/locales/it/knxUltimateHueTemperatureSensor.json +0 -30
- package/nodes/locales/it/knxUltimateHueZigbeeConnectivity.html +0 -39
- package/nodes/locales/it/knxUltimateHueZigbeeConnectivity.json +0 -30
- package/nodes/locales/it/knxUltimateHuedevice_software_update.html +0 -35
- package/nodes/locales/it/knxUltimateHuedevice_software_update.json +0 -30
- package/nodes/locales/it/knxUltimateLoadControl.html +0 -76
- package/nodes/locales/it/knxUltimateLoadControl.json +0 -28
- package/nodes/locales/it/knxUltimateLogger.html +0 -132
- package/nodes/locales/it/knxUltimateLogger.json +0 -31
- package/nodes/locales/it/knxUltimateMatterBridge.html +0 -65
- package/nodes/locales/it/knxUltimateMatterBridge.json +0 -123
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +0 -57
- package/nodes/locales/it/knxUltimateMatterControllerDevice.json +0 -146
- package/nodes/locales/it/knxUltimateSceneController.html +0 -101
- package/nodes/locales/it/knxUltimateSceneController.json +0 -32
- package/nodes/locales/it/knxUltimateStaircase.html +0 -77
- package/nodes/locales/it/knxUltimateStaircase.json +0 -62
- package/nodes/locales/it/knxUltimateWatchDog.html +0 -194
- package/nodes/locales/it/knxUltimateWatchDog.json +0 -27
- package/nodes/locales/it/matter-config.html +0 -47
- package/nodes/locales/it/matter-config.json +0 -48
- package/nodes/locales/it/matterbridge-config.html +0 -45
- package/nodes/locales/it/matterbridge-config.json +0 -25
- package/nodes/locales/zh-CN/hue-config.html +0 -33
- package/nodes/locales/zh-CN/hue-config.json +0 -21
- package/nodes/locales/zh-CN/knxUltimateAI.html +0 -13
- package/nodes/locales/zh-CN/knxUltimateAI.json +0 -231
- package/nodes/locales/zh-CN/knxUltimateAIHomeAssistant.html +0 -11
- package/nodes/locales/zh-CN/knxUltimateAlerter.html +0 -159
- package/nodes/locales/zh-CN/knxUltimateAlerter.json +0 -27
- package/nodes/locales/zh-CN/knxUltimateAutoResponder.html +0 -94
- package/nodes/locales/zh-CN/knxUltimateAutoResponder.json +0 -8
- package/nodes/locales/zh-CN/knxUltimateDateTime.html +0 -45
- package/nodes/locales/zh-CN/knxUltimateDateTime.json +0 -32
- package/nodes/locales/zh-CN/knxUltimateGarage.html +0 -79
- package/nodes/locales/zh-CN/knxUltimateGarage.json +0 -51
- package/nodes/locales/zh-CN/knxUltimateGlobalContext.html +0 -77
- package/nodes/locales/zh-CN/knxUltimateGlobalContext.json +0 -24
- package/nodes/locales/zh-CN/knxUltimateHATranslator.html +0 -25
- package/nodes/locales/zh-CN/knxUltimateHATranslator.json +0 -8
- package/nodes/locales/zh-CN/knxUltimateHueAreaMotion.html +0 -44
- package/nodes/locales/zh-CN/knxUltimateHueAreaMotion.json +0 -31
- package/nodes/locales/zh-CN/knxUltimateHueBattery.html +0 -37
- package/nodes/locales/zh-CN/knxUltimateHueBattery.json +0 -30
- package/nodes/locales/zh-CN/knxUltimateHueButton.html +0 -52
- package/nodes/locales/zh-CN/knxUltimateHueButton.json +0 -34
- package/nodes/locales/zh-CN/knxUltimateHueCameraMotion.html +0 -38
- package/nodes/locales/zh-CN/knxUltimateHueCameraMotion.json +0 -30
- package/nodes/locales/zh-CN/knxUltimateHueContactSensor.html +0 -35
- package/nodes/locales/zh-CN/knxUltimateHueContactSensor.json +0 -26
- package/nodes/locales/zh-CN/knxUltimateHueController.html +0 -693
- package/nodes/locales/zh-CN/knxUltimateHueController.json +0 -64
- package/nodes/locales/zh-CN/knxUltimateHueHumiditySensor.html +0 -38
- package/nodes/locales/zh-CN/knxUltimateHueHumiditySensor.json +0 -30
- package/nodes/locales/zh-CN/knxUltimateHueLight.html +0 -126
- package/nodes/locales/zh-CN/knxUltimateHueLight.json +0 -86
- package/nodes/locales/zh-CN/knxUltimateHueLightSensor.html +0 -38
- package/nodes/locales/zh-CN/knxUltimateHueLightSensor.json +0 -9
- package/nodes/locales/zh-CN/knxUltimateHueMotion.html +0 -41
- package/nodes/locales/zh-CN/knxUltimateHueMotion.json +0 -27
- package/nodes/locales/zh-CN/knxUltimateHuePlug.html +0 -50
- package/nodes/locales/zh-CN/knxUltimateHuePlug.json +0 -33
- package/nodes/locales/zh-CN/knxUltimateHueScene.html +0 -45
- package/nodes/locales/zh-CN/knxUltimateHueScene.json +0 -41
- package/nodes/locales/zh-CN/knxUltimateHueTapDial.html +0 -40
- package/nodes/locales/zh-CN/knxUltimateHueTapDial.json +0 -28
- package/nodes/locales/zh-CN/knxUltimateHueTemperatureSensor.html +0 -38
- package/nodes/locales/zh-CN/knxUltimateHueTemperatureSensor.json +0 -30
- package/nodes/locales/zh-CN/knxUltimateHueZigbeeConnectivity.html +0 -39
- package/nodes/locales/zh-CN/knxUltimateHueZigbeeConnectivity.json +0 -30
- package/nodes/locales/zh-CN/knxUltimateHuedevice_software_update.html +0 -36
- package/nodes/locales/zh-CN/knxUltimateHuedevice_software_update.json +0 -30
- package/nodes/locales/zh-CN/knxUltimateLoadControl.html +0 -76
- package/nodes/locales/zh-CN/knxUltimateLoadControl.json +0 -28
- package/nodes/locales/zh-CN/knxUltimateLogger.html +0 -126
- package/nodes/locales/zh-CN/knxUltimateLogger.json +0 -31
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +0 -65
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +0 -123
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +0 -57
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +0 -146
- package/nodes/locales/zh-CN/knxUltimateSceneController.html +0 -98
- package/nodes/locales/zh-CN/knxUltimateSceneController.json +0 -32
- package/nodes/locales/zh-CN/knxUltimateStaircase.html +0 -77
- package/nodes/locales/zh-CN/knxUltimateStaircase.json +0 -62
- package/nodes/locales/zh-CN/knxUltimateWatchDog.html +0 -178
- package/nodes/locales/zh-CN/knxUltimateWatchDog.json +0 -27
- package/nodes/locales/zh-CN/matter-config.html +0 -47
- package/nodes/locales/zh-CN/matter-config.json +0 -47
- package/nodes/locales/zh-CN/matterbridge-config.html +0 -45
- package/nodes/locales/zh-CN/matterbridge-config.json +0 -25
- package/nodes/matter-config.html +0 -578
- package/nodes/matter-config.js +0 -387
- package/nodes/matterbridge-config.html +0 -198
- package/nodes/matterbridge-config.js +0 -261
- package/nodes/plugins/knxUltimate-cerebrum-runtime-plugin.js +0 -79
- package/nodes/plugins/knxUltimate-migration-notice-plugin.html +0 -44
- package/nodes/plugins/knxUltimateAI-vue/assets/app.css +0 -1
- package/nodes/plugins/knxUltimateAI-vue/assets/app.js +0 -13
- package/nodes/plugins/knxUltimateAI-vue/assets/chunk-html2canvas.esm.js +0 -5
- package/nodes/plugins/knxUltimateAI-vue/assets/chunk-index.es.js +0 -5
- package/nodes/plugins/knxUltimateAI-vue/assets/chunk-jspdf.es.min.js +0 -79
- package/nodes/plugins/knxUltimateAI-vue/assets/chunk-purify.es.js +0 -3
- package/nodes/plugins/knxUltimateAI-vue/index.html +0 -13
- package/nodes/utils/HueEventStream.mjs +0 -176
- package/nodes/utils/colorManipulators/XYFromRGB_Supergiovane.js +0 -113
- package/nodes/utils/colorManipulators/hueColorConverter.js +0 -521
- package/nodes/utils/http.js +0 -291
- package/nodes/utils/hueControllerProfileAdapter.js +0 -104
- package/nodes/utils/hueControllerProfiles/runtime/area_motion.js +0 -221
- package/nodes/utils/hueControllerProfiles/runtime/button.js +0 -295
- package/nodes/utils/hueControllerProfiles/runtime/camera_motion.js +0 -221
- package/nodes/utils/hueControllerProfiles/runtime/contact.js +0 -191
- package/nodes/utils/hueControllerProfiles/runtime/device_power.js +0 -185
- package/nodes/utils/hueControllerProfiles/runtime/device_software_update.js +0 -185
- package/nodes/utils/hueControllerProfiles/runtime/humidity.js +0 -204
- package/nodes/utils/hueControllerProfiles/runtime/light.js +0 -1585
- package/nodes/utils/hueControllerProfiles/runtime/light_level.js +0 -180
- package/nodes/utils/hueControllerProfiles/runtime/motion.js +0 -185
- package/nodes/utils/hueControllerProfiles/runtime/plug.js +0 -344
- package/nodes/utils/hueControllerProfiles/runtime/relative_rotary.js +0 -271
- package/nodes/utils/hueControllerProfiles/runtime/scene.js +0 -352
- package/nodes/utils/hueControllerProfiles/runtime/temperature.js +0 -187
- package/nodes/utils/hueControllerProfiles/runtime/zigbee_connectivity.js +0 -185
- package/nodes/utils/hueControllerResourceCatalog.js +0 -71
- package/nodes/utils/hueEngine.mjs +0 -469
- package/nodes/utils/knxAiCamera.js +0 -302
- package/nodes/utils/knxAiCatalogRetrieval.js +0 -349
- package/nodes/utils/knxAiCerebrum.js +0 -406
- package/nodes/utils/knxAiChatContext.js +0 -496
- package/nodes/utils/knxAiEventHistory.js +0 -521
- package/nodes/utils/knxAiHomeMemory.js +0 -1021
- package/nodes/utils/knxAiScheduler.js +0 -445
- package/nodes/utils/knxAiSemanticContext.js +0 -662
- package/nodes/utils/knxAiTelegramVoice.js +0 -545
- package/nodes/utils/knxAiWebAccess.js +0 -631
- package/nodes/utils/lightEngines/canonicalLight.js +0 -75
- package/nodes/utils/lightEngines/hueLightEngine.js +0 -51
- package/nodes/utils/lightEngines/index.js +0 -25
- package/nodes/utils/lightEngines/knxLightCommand.js +0 -79
- package/nodes/utils/lightEngines/lightEngine.js +0 -46
- package/nodes/utils/lightEngines/matterHueShim.js +0 -59
- package/nodes/utils/lightEngines/matterLightEngine.js +0 -117
- package/nodes/utils/matterBridgeDeviceFactory.mjs +0 -780
- package/nodes/utils/matterBridgeEngine.mjs +0 -297
- package/nodes/utils/matterControllerCommandGate.js +0 -47
- package/nodes/utils/matterControllerProfiles/doorLock.js +0 -258
- package/nodes/utils/matterControllerProfiles/fan.js +0 -19
- package/nodes/utils/matterControllerProfiles/index.js +0 -34
- package/nodes/utils/matterControllerProfiles/mappedEndpoint.js +0 -217
- package/nodes/utils/matterControllerProfiles/switch.js +0 -13
- package/nodes/utils/matterControllerProfiles/thermostat.js +0 -19
- package/nodes/utils/matterControllerProfiles/universal.js +0 -462
- package/nodes/utils/matterControllerProfiles/windowCovering.js +0 -19
- package/nodes/utils/matterControllerSemanticInput.js +0 -212
- package/nodes/utils/matterEngine.mjs +0 -894
- package/nodes/utils/matterEnvironmentLock.mjs +0 -32
- package/nodes/utils/matterKnxConverter.js +0 -204
- package/nodes/utils/matterStorageBackup.js +0 -100
- package/resources/HA-icon.png +0 -0
- package/resources/dim.png +0 -0
- package/resources/rgb.png +0 -0
- package/resources/tunablewhite.png +0 -0
- package/resources/zxing-browser-0.2.0.min.js +0 -1
- package/resources/zxing-browser-LICENSE.txt +0 -21
|
@@ -1,631 +0,0 @@
|
|
|
1
|
-
const dns = require('dns')
|
|
2
|
-
const https = require('https')
|
|
3
|
-
const net = require('net')
|
|
4
|
-
|
|
5
|
-
const DEFAULT_TIMEOUT_MS = 10000
|
|
6
|
-
const DEFAULT_MAX_REDIRECTS = 3
|
|
7
|
-
const DEFAULT_MAX_ACTIONS = 2
|
|
8
|
-
const DEFAULT_MAX_RESULTS = 3
|
|
9
|
-
const DEFAULT_SEARCH_MAX_BYTES = 512 * 1024
|
|
10
|
-
const DEFAULT_OPEN_MAX_BYTES = 1024 * 1024
|
|
11
|
-
const DEFAULT_TEXT_MAX_CHARS = 16000
|
|
12
|
-
const HARD_MAX_BYTES = 2 * 1024 * 1024
|
|
13
|
-
const HARD_MAX_ACTIONS = 5
|
|
14
|
-
const HARD_MAX_REDIRECTS = 5
|
|
15
|
-
const HARD_MAX_RESULTS = 5
|
|
16
|
-
const MAX_QUERY_CHARS = 500
|
|
17
|
-
const MAX_URL_CHARS = 4096
|
|
18
|
-
const DEFAULT_SEARCH_ENDPOINTS = [
|
|
19
|
-
'https://html.duckduckgo.com/html/',
|
|
20
|
-
'https://lite.duckduckgo.com/lite/'
|
|
21
|
-
]
|
|
22
|
-
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (compatible; KNX-Ultimate-AI/1.0; +https://github.com/Supergiovane/node-red-contrib-knx-ultimate)'
|
|
23
|
-
|
|
24
|
-
class KnxAiWebAccessError extends Error {
|
|
25
|
-
constructor (code, message) {
|
|
26
|
-
super(message)
|
|
27
|
-
this.name = 'KnxAiWebAccessError'
|
|
28
|
-
this.code = code
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const boundedInteger = (value, fallback, minimum, maximum) => {
|
|
33
|
-
const numeric = Number(value)
|
|
34
|
-
if (!Number.isFinite(numeric)) return fallback
|
|
35
|
-
return Math.min(maximum, Math.max(minimum, Math.floor(numeric)))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const compactWhitespace = (value, maxChars = DEFAULT_TEXT_MAX_CHARS) => {
|
|
39
|
-
const limit = boundedInteger(maxChars, DEFAULT_TEXT_MAX_CHARS, 1, 100000)
|
|
40
|
-
const normalized = String(value || '')
|
|
41
|
-
.replace(/[\s\S]/g, character => {
|
|
42
|
-
const code = character.charCodeAt(0)
|
|
43
|
-
if (code === 0) return ''
|
|
44
|
-
if ((code >= 1 && code <= 8) || code === 11 || code === 12 || (code >= 14 && code <= 31) || (code >= 127 && code <= 159)) return ' '
|
|
45
|
-
if ((code >= 0x200b && code <= 0x200f) || (code >= 0x202a && code <= 0x202e) || (code >= 0x2066 && code <= 0x2069) || code === 0xfeff) return ''
|
|
46
|
-
return character
|
|
47
|
-
})
|
|
48
|
-
.replace(/\r\n?/g, '\n')
|
|
49
|
-
.replace(/[\t\f\v ]+/g, ' ')
|
|
50
|
-
.replace(/ +([,.;:!?])/g, '$1')
|
|
51
|
-
.replace(/ *\n */g, '\n')
|
|
52
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
53
|
-
.trim()
|
|
54
|
-
if (normalized.length <= limit) return normalized
|
|
55
|
-
return `${normalized.slice(0, Math.max(1, limit - 1)).trimEnd()}…`
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const decodeHtmlEntities = (value) => {
|
|
59
|
-
const named = {
|
|
60
|
-
amp: '&',
|
|
61
|
-
apos: "'",
|
|
62
|
-
gt: '>',
|
|
63
|
-
hellip: '…',
|
|
64
|
-
ldquo: '“',
|
|
65
|
-
lsquo: '‘',
|
|
66
|
-
lt: '<',
|
|
67
|
-
nbsp: ' ',
|
|
68
|
-
ndash: '–',
|
|
69
|
-
quot: '"',
|
|
70
|
-
rdquo: '”',
|
|
71
|
-
rsquo: '’',
|
|
72
|
-
trade: '™'
|
|
73
|
-
}
|
|
74
|
-
return String(value || '').replace(/&(#(?:x[0-9a-f]{1,6}|[0-9]{1,7})|[a-z][a-z0-9]{1,15});/gi, (match, entity) => {
|
|
75
|
-
if (entity[0] !== '#') return Object.prototype.hasOwnProperty.call(named, entity.toLowerCase()) ? named[entity.toLowerCase()] : ' '
|
|
76
|
-
const hexadecimal = entity[1].toLowerCase() === 'x'
|
|
77
|
-
const numeric = Number.parseInt(entity.slice(hexadecimal ? 2 : 1), hexadecimal ? 16 : 10)
|
|
78
|
-
if (!Number.isInteger(numeric) || numeric <= 0 || numeric > 0x10ffff || (numeric >= 0xd800 && numeric <= 0xdfff)) return ' '
|
|
79
|
-
try { return String.fromCodePoint(numeric) } catch (error) { return ' ' }
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const stripHtmlTags = (value, maxChars = DEFAULT_TEXT_MAX_CHARS) => {
|
|
84
|
-
let html = String(value || '')
|
|
85
|
-
html = html
|
|
86
|
-
.replace(/<!--[\s\S]*?-->/g, ' ')
|
|
87
|
-
.replace(/<!--[\s\S]*$/g, ' ')
|
|
88
|
-
.replace(/<(script|style|noscript|template|svg|canvas|iframe|object|embed)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, ' ')
|
|
89
|
-
.replace(/<(script|style|noscript|template|svg|canvas|iframe|object|embed)\b[^>]*>[\s\S]*$/gi, ' ')
|
|
90
|
-
.replace(/<(br|hr)\b[^>]*>/gi, '\n')
|
|
91
|
-
.replace(/<\/(address|article|aside|blockquote|div|dl|fieldset|figcaption|figure|footer|form|h[1-6]|header|li|main|nav|ol|p|pre|section|table|tr|ul)\s*>/gi, '\n')
|
|
92
|
-
.replace(/<[^>]+>/g, ' ')
|
|
93
|
-
return compactWhitespace(decodeHtmlEntities(html), maxChars)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const extractHtmlDocument = (html, maxChars = DEFAULT_TEXT_MAX_CHARS) => {
|
|
97
|
-
const source = String(html || '')
|
|
98
|
-
const titleMatch = source.match(/<title\b[^>]*>([\s\S]*?)<\/title\s*>/i)
|
|
99
|
-
const bodyMatch = source.match(/<body\b[^>]*>([\s\S]*?)<\/body\s*>/i)
|
|
100
|
-
return {
|
|
101
|
-
title: compactWhitespace(stripHtmlTags(titleMatch ? titleMatch[1] : '', 300), 300),
|
|
102
|
-
text: stripHtmlTags(bodyMatch ? bodyMatch[1] : source, maxChars)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const getHtmlAttribute = (tag, attributeName) => {
|
|
107
|
-
const escaped = String(attributeName || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
108
|
-
const expression = new RegExp(`(?:^|\\s)${escaped}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, 'i')
|
|
109
|
-
const match = String(tag || '').match(expression)
|
|
110
|
-
return match ? decodeHtmlEntities(match[1] ?? match[2] ?? match[3] ?? '') : ''
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const resolveDuckDuckGoResultUrl = (href) => {
|
|
114
|
-
const raw = decodeHtmlEntities(href).trim()
|
|
115
|
-
if (!raw || raw.length > MAX_URL_CHARS) return ''
|
|
116
|
-
let parsed
|
|
117
|
-
try { parsed = new URL(raw, 'https://duckduckgo.com/') } catch (error) { return '' }
|
|
118
|
-
const hostname = parsed.hostname.toLowerCase().replace(/\.$/, '')
|
|
119
|
-
if ((hostname === 'duckduckgo.com' || hostname.endsWith('.duckduckgo.com')) && parsed.pathname.startsWith('/l/')) {
|
|
120
|
-
const target = parsed.searchParams.get('uddg')
|
|
121
|
-
if (!target) return ''
|
|
122
|
-
try { parsed = new URL(target) } catch (error) { return '' }
|
|
123
|
-
}
|
|
124
|
-
if (parsed.protocol !== 'https:' || parsed.username || parsed.password || parsed.port) return ''
|
|
125
|
-
parsed.hash = ''
|
|
126
|
-
try { return assertSafeHttpsUrl(parsed.toString()).toString() } catch (error) { return '' }
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const parseDuckDuckGoResults = (html, { maxResults = DEFAULT_MAX_RESULTS, retrievedAt = new Date().toISOString() } = {}) => {
|
|
130
|
-
const source = String(html || '')
|
|
131
|
-
const limit = boundedInteger(maxResults, DEFAULT_MAX_RESULTS, 1, HARD_MAX_RESULTS)
|
|
132
|
-
const anchors = []
|
|
133
|
-
const anchorPattern = /<a\b[^>]*\bclass\s*=\s*(?:"[^"]*(?:result__a|result-link)[^"]*"|'[^']*(?:result__a|result-link)[^']*'|[^\s>]*(?:result__a|result-link)[^\s>]*)[^>]*>[\s\S]*?<\/a\s*>/gi
|
|
134
|
-
let match
|
|
135
|
-
while ((match = anchorPattern.exec(source)) && anchors.length < limit * 4) {
|
|
136
|
-
anchors.push({ index: match.index, end: anchorPattern.lastIndex, tag: match[0] })
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const results = []
|
|
140
|
-
const seen = new Set()
|
|
141
|
-
for (let index = 0; index < anchors.length && results.length < limit; index++) {
|
|
142
|
-
const anchor = anchors[index]
|
|
143
|
-
const url = resolveDuckDuckGoResultUrl(getHtmlAttribute(anchor.tag, 'href'))
|
|
144
|
-
if (!url || seen.has(url)) continue
|
|
145
|
-
const title = stripHtmlTags(anchor.tag.replace(/^<a\b[^>]*>/i, '').replace(/<\/a\s*>$/i, ''), 300)
|
|
146
|
-
if (!title) continue
|
|
147
|
-
const nextIndex = anchors[index + 1] ? anchors[index + 1].index : Math.min(source.length, anchor.end + 6000)
|
|
148
|
-
const vicinity = source.slice(anchor.end, nextIndex)
|
|
149
|
-
const snippetMatch = vicinity.match(/<(?:a|div|span|td)\b[^>]*\bclass\s*=\s*(?:"[^"]*(?:result__snippet|result-snippet)[^"]*"|'[^']*(?:result__snippet|result-snippet)[^']*'|[^\s>]*(?:result__snippet|result-snippet)[^\s>]*)[^>]*>([\s\S]*?)<\/(?:a|div|span|td)\s*>/i)
|
|
150
|
-
const text = stripHtmlTags(snippetMatch ? snippetMatch[1] : title, 1200) || title
|
|
151
|
-
seen.add(url)
|
|
152
|
-
results.push({ title, url, text, retrievedAt })
|
|
153
|
-
}
|
|
154
|
-
return results
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const ipv4ToInteger = (value) => {
|
|
158
|
-
if (net.isIP(value) !== 4) return null
|
|
159
|
-
return value.split('.').reduce((total, part) => ((total << 8) + Number(part)) >>> 0, 0) >>> 0
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const ipv4InRange = (value, base, prefix) => {
|
|
163
|
-
const candidate = ipv4ToInteger(value)
|
|
164
|
-
const network = ipv4ToInteger(base)
|
|
165
|
-
if (candidate === null || network === null) return false
|
|
166
|
-
if (prefix === 0) return true
|
|
167
|
-
const mask = (0xffffffff << (32 - prefix)) >>> 0
|
|
168
|
-
return (candidate & mask) === (network & mask)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const ipv6ToBigInt = (value) => {
|
|
172
|
-
let source = String(value || '').toLowerCase().replace(/^\[|\]$/g, '')
|
|
173
|
-
if (source.includes('%') || net.isIP(source) !== 6) return null
|
|
174
|
-
if (source.includes('.')) {
|
|
175
|
-
const separator = source.lastIndexOf(':')
|
|
176
|
-
const ipv4 = source.slice(separator + 1)
|
|
177
|
-
const numeric = ipv4ToInteger(ipv4)
|
|
178
|
-
if (numeric === null) return null
|
|
179
|
-
source = `${source.slice(0, separator)}:${(numeric >>> 16).toString(16)}:${(numeric & 0xffff).toString(16)}`
|
|
180
|
-
}
|
|
181
|
-
const halves = source.split('::')
|
|
182
|
-
if (halves.length > 2) return null
|
|
183
|
-
const left = halves[0] ? halves[0].split(':') : []
|
|
184
|
-
const right = halves.length === 2 && halves[1] ? halves[1].split(':') : []
|
|
185
|
-
if (halves.length === 1 && left.length !== 8) return null
|
|
186
|
-
const missing = 8 - left.length - right.length
|
|
187
|
-
if (missing < 0 || (halves.length === 2 && missing < 1)) return null
|
|
188
|
-
const parts = [...left, ...Array(missing).fill('0'), ...right]
|
|
189
|
-
if (parts.length !== 8 || parts.some(part => !/^[0-9a-f]{1,4}$/.test(part))) return null
|
|
190
|
-
return parts.reduce((total, part) => (total << 16n) + BigInt(Number.parseInt(part, 16)), 0n)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const ipv6InRange = (value, base, prefix) => {
|
|
194
|
-
const candidate = ipv6ToBigInt(value)
|
|
195
|
-
const network = ipv6ToBigInt(base)
|
|
196
|
-
if (candidate === null || network === null) return false
|
|
197
|
-
if (prefix === 0) return true
|
|
198
|
-
const shift = BigInt(128 - prefix)
|
|
199
|
-
return (candidate >> shift) === (network >> shift)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const BLOCKED_IPV4_RANGES = [
|
|
203
|
-
['0.0.0.0', 8], ['10.0.0.0', 8], ['100.64.0.0', 10], ['127.0.0.0', 8],
|
|
204
|
-
['169.254.0.0', 16], ['172.16.0.0', 12], ['192.0.0.0', 24], ['192.0.2.0', 24],
|
|
205
|
-
['192.88.99.0', 24], ['192.168.0.0', 16], ['198.18.0.0', 15], ['198.51.100.0', 24],
|
|
206
|
-
['203.0.113.0', 24], ['224.0.0.0', 4], ['240.0.0.0', 4]
|
|
207
|
-
]
|
|
208
|
-
|
|
209
|
-
const BLOCKED_IPV6_RANGES = [
|
|
210
|
-
['2001::', 23], ['2001:2::', 48], ['2001:db8::', 32], ['2002::', 16], ['3ffe::', 16], ['3fff::', 20]
|
|
211
|
-
]
|
|
212
|
-
|
|
213
|
-
const isPublicIpAddress = (value) => {
|
|
214
|
-
const address = String(value || '').trim().replace(/^\[|\]$/g, '')
|
|
215
|
-
const family = net.isIP(address)
|
|
216
|
-
if (family === 4) return !BLOCKED_IPV4_RANGES.some(([base, prefix]) => ipv4InRange(address, base, prefix))
|
|
217
|
-
if (family !== 6) return false
|
|
218
|
-
// Current globally routable unicast space is 2000::/3. Explicitly reject its special-use subnets.
|
|
219
|
-
if (!ipv6InRange(address, '2000::', 3)) return false
|
|
220
|
-
return !BLOCKED_IPV6_RANGES.some(([base, prefix]) => ipv6InRange(address, base, prefix))
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const normalizeHostname = (value) => String(value || '').trim().toLowerCase().replace(/^\[|\]$/g, '').replace(/\.$/, '')
|
|
224
|
-
|
|
225
|
-
const assertSafeHttpsUrl = (value) => {
|
|
226
|
-
const raw = String(value || '').trim()
|
|
227
|
-
if (!raw || raw.length > MAX_URL_CHARS) throw new KnxAiWebAccessError('INVALID_URL', 'A valid HTTPS URL is required')
|
|
228
|
-
let parsed
|
|
229
|
-
try { parsed = new URL(raw) } catch (error) { throw new KnxAiWebAccessError('INVALID_URL', 'A valid HTTPS URL is required') }
|
|
230
|
-
if (parsed.protocol !== 'https:') throw new KnxAiWebAccessError('UNSAFE_PROTOCOL', 'Only HTTPS web addresses are allowed')
|
|
231
|
-
if (parsed.username || parsed.password) throw new KnxAiWebAccessError('URL_CREDENTIALS', 'Web addresses containing credentials are not allowed')
|
|
232
|
-
if (parsed.port && parsed.port !== '443') throw new KnxAiWebAccessError('UNSAFE_PORT', 'Only the standard HTTPS port is allowed')
|
|
233
|
-
const hostname = normalizeHostname(parsed.hostname)
|
|
234
|
-
if (!hostname || hostname.length > 253 || hostname.includes('%')) throw new KnxAiWebAccessError('INVALID_HOST', 'The web address has an invalid host')
|
|
235
|
-
if (net.isIP(hostname)) {
|
|
236
|
-
if (!isPublicIpAddress(hostname)) throw new KnxAiWebAccessError('NON_PUBLIC_TARGET', 'Private, local, and reserved web addresses are not allowed')
|
|
237
|
-
} else {
|
|
238
|
-
const localSuffixes = ['localhost', 'local', 'lan', 'internal', 'home', 'home.arpa', 'test', 'invalid', 'example']
|
|
239
|
-
if (!hostname.includes('.') || localSuffixes.some(suffix => hostname === suffix || hostname.endsWith(`.${suffix}`))) {
|
|
240
|
-
throw new KnxAiWebAccessError('NON_PUBLIC_TARGET', 'Private, local, and reserved web addresses are not allowed')
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
parsed.hash = ''
|
|
244
|
-
return parsed
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const normalizeDnsRecords = (value) => {
|
|
248
|
-
const source = Array.isArray(value) ? value : [value]
|
|
249
|
-
const records = []
|
|
250
|
-
const seen = new Set()
|
|
251
|
-
for (const item of source) {
|
|
252
|
-
const address = typeof item === 'string' ? item : item && item.address
|
|
253
|
-
const normalized = String(address || '').trim().replace(/^\[|\]$/g, '')
|
|
254
|
-
const family = net.isIP(normalized)
|
|
255
|
-
if (!family) throw new KnxAiWebAccessError('DNS_INVALID', 'The web address returned an invalid DNS response')
|
|
256
|
-
const key = `${family}:${normalized}`
|
|
257
|
-
if (!seen.has(key)) records.push({ address: normalized, family })
|
|
258
|
-
seen.add(key)
|
|
259
|
-
}
|
|
260
|
-
if (!records.length || records.length > 32) throw new KnxAiWebAccessError('DNS_INVALID', 'The web address returned an invalid DNS response')
|
|
261
|
-
return records
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const defaultDnsLookup = async (hostname) => dns.promises.lookup(hostname, { all: true, verbatim: true })
|
|
265
|
-
|
|
266
|
-
const runWithTimeout = async (factory, timeoutMs, message) => {
|
|
267
|
-
const controller = new AbortController()
|
|
268
|
-
let timer
|
|
269
|
-
try {
|
|
270
|
-
return await new Promise((resolve, reject) => {
|
|
271
|
-
let settled = false
|
|
272
|
-
timer = setTimeout(() => {
|
|
273
|
-
if (settled) return
|
|
274
|
-
settled = true
|
|
275
|
-
controller.abort()
|
|
276
|
-
reject(new KnxAiWebAccessError('TIMEOUT', message))
|
|
277
|
-
}, timeoutMs)
|
|
278
|
-
Promise.resolve()
|
|
279
|
-
.then(() => factory(controller.signal))
|
|
280
|
-
.then(value => {
|
|
281
|
-
if (settled) return
|
|
282
|
-
settled = true
|
|
283
|
-
resolve(value)
|
|
284
|
-
}, error => {
|
|
285
|
-
if (settled) return
|
|
286
|
-
settled = true
|
|
287
|
-
reject(error)
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
} finally {
|
|
291
|
-
clearTimeout(timer)
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const resolvePublicTarget = async (parsedUrl, { dnsLookup, timeoutMs }) => {
|
|
296
|
-
const hostname = normalizeHostname(parsedUrl.hostname)
|
|
297
|
-
if (net.isIP(hostname)) return [{ address: hostname, family: net.isIP(hostname) }]
|
|
298
|
-
let rawRecords
|
|
299
|
-
try {
|
|
300
|
-
rawRecords = await runWithTimeout(
|
|
301
|
-
() => dnsLookup(hostname, { all: true, verbatim: true }),
|
|
302
|
-
timeoutMs,
|
|
303
|
-
'DNS lookup timed out'
|
|
304
|
-
)
|
|
305
|
-
} catch (error) {
|
|
306
|
-
if (error instanceof KnxAiWebAccessError) throw error
|
|
307
|
-
throw new KnxAiWebAccessError('DNS_FAILED', 'The web address could not be resolved')
|
|
308
|
-
}
|
|
309
|
-
const records = normalizeDnsRecords(rawRecords)
|
|
310
|
-
if (records.some(record => !isPublicIpAddress(record.address))) {
|
|
311
|
-
throw new KnxAiWebAccessError('NON_PUBLIC_TARGET', 'The web address resolves to a private, local, or reserved network')
|
|
312
|
-
}
|
|
313
|
-
return records.sort((left, right) => left.family - right.family)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const getHeader = (headers, name) => {
|
|
317
|
-
if (!headers) return ''
|
|
318
|
-
if (typeof headers.get === 'function') return String(headers.get(name) || '')
|
|
319
|
-
const normalized = String(name || '').toLowerCase()
|
|
320
|
-
const entry = Object.entries(headers).find(([key]) => String(key).toLowerCase() === normalized)
|
|
321
|
-
if (!entry) return ''
|
|
322
|
-
return Array.isArray(entry[1]) ? String(entry[1][0] || '') : String(entry[1] || '')
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const defaultPinnedHttpsTransport = ({ url, resolvedAddress, headers, timeoutMs, maxBytes, signal }) => new Promise((resolve, reject) => {
|
|
326
|
-
let settled = false
|
|
327
|
-
const finish = (error, response) => {
|
|
328
|
-
if (settled) return
|
|
329
|
-
settled = true
|
|
330
|
-
if (signal) signal.removeEventListener('abort', abort)
|
|
331
|
-
if (error) reject(error)
|
|
332
|
-
else resolve(response)
|
|
333
|
-
}
|
|
334
|
-
const abort = () => {
|
|
335
|
-
request.destroy(new KnxAiWebAccessError('TIMEOUT', 'Web request timed out'))
|
|
336
|
-
}
|
|
337
|
-
const lookup = (_hostname, options, callback) => {
|
|
338
|
-
if (typeof options === 'function') callback = options
|
|
339
|
-
const wantsAll = options && typeof options === 'object' && options.all
|
|
340
|
-
if (wantsAll) callback(null, [resolvedAddress])
|
|
341
|
-
else callback(null, resolvedAddress.address, resolvedAddress.family)
|
|
342
|
-
}
|
|
343
|
-
const request = https.request(url, {
|
|
344
|
-
method: 'GET',
|
|
345
|
-
agent: false,
|
|
346
|
-
headers,
|
|
347
|
-
lookup,
|
|
348
|
-
rejectUnauthorized: true
|
|
349
|
-
}, response => {
|
|
350
|
-
const statusCode = Number(response.statusCode) || 0
|
|
351
|
-
if (statusCode >= 300 && statusCode < 400) {
|
|
352
|
-
response.destroy()
|
|
353
|
-
return finish(null, { statusCode, headers: response.headers, body: Buffer.alloc(0) })
|
|
354
|
-
}
|
|
355
|
-
if (statusCode < 200 || statusCode >= 300) {
|
|
356
|
-
response.destroy()
|
|
357
|
-
return finish(null, { statusCode, headers: response.headers, body: Buffer.alloc(0) })
|
|
358
|
-
}
|
|
359
|
-
const declaredLength = Number(getHeader(response.headers, 'content-length'))
|
|
360
|
-
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
361
|
-
response.destroy()
|
|
362
|
-
return finish(new KnxAiWebAccessError('RESPONSE_TOO_LARGE', 'The web response exceeds the size limit'))
|
|
363
|
-
}
|
|
364
|
-
const chunks = []
|
|
365
|
-
let total = 0
|
|
366
|
-
response.on('data', chunk => {
|
|
367
|
-
if (settled) return
|
|
368
|
-
const buffer = Buffer.from(chunk)
|
|
369
|
-
total += buffer.length
|
|
370
|
-
if (total > maxBytes) {
|
|
371
|
-
response.destroy()
|
|
372
|
-
finish(new KnxAiWebAccessError('RESPONSE_TOO_LARGE', 'The web response exceeds the size limit'))
|
|
373
|
-
return
|
|
374
|
-
}
|
|
375
|
-
chunks.push(buffer)
|
|
376
|
-
})
|
|
377
|
-
response.on('end', () => finish(null, { statusCode, headers: response.headers, body: Buffer.concat(chunks) }))
|
|
378
|
-
response.on('error', error => finish(error))
|
|
379
|
-
})
|
|
380
|
-
request.setTimeout(timeoutMs, abort)
|
|
381
|
-
request.on('error', error => finish(error))
|
|
382
|
-
if (signal) {
|
|
383
|
-
if (signal.aborted) abort()
|
|
384
|
-
else signal.addEventListener('abort', abort, { once: true })
|
|
385
|
-
}
|
|
386
|
-
request.end()
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
const isTextualMimeType = (value) => {
|
|
390
|
-
const mimeType = String(value || '').split(';')[0].trim().toLowerCase()
|
|
391
|
-
if (mimeType.startsWith('text/')) return true
|
|
392
|
-
if (!mimeType.startsWith('application/')) return false
|
|
393
|
-
return mimeType === 'application/json' || mimeType.endsWith('+json') ||
|
|
394
|
-
mimeType === 'application/xml' || mimeType.endsWith('+xml')
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const responseBodyToBuffer = (body) => {
|
|
398
|
-
if (Buffer.isBuffer(body)) return body
|
|
399
|
-
if (body instanceof Uint8Array) return Buffer.from(body)
|
|
400
|
-
if (typeof body === 'string') return Buffer.from(body, 'utf8')
|
|
401
|
-
if (body === undefined || body === null) return Buffer.alloc(0)
|
|
402
|
-
throw new KnxAiWebAccessError('INVALID_RESPONSE', 'The web server returned an invalid response')
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const requestTextWithRedirects = async (initialUrl, options) => {
|
|
406
|
-
let current = assertSafeHttpsUrl(initialUrl)
|
|
407
|
-
const deadline = Date.now() + options.timeoutMs
|
|
408
|
-
const remainingTimeout = () => {
|
|
409
|
-
const remaining = deadline - Date.now()
|
|
410
|
-
if (remaining <= 0) throw new KnxAiWebAccessError('TIMEOUT', 'Web request timed out')
|
|
411
|
-
return remaining
|
|
412
|
-
}
|
|
413
|
-
for (let redirects = 0; redirects <= options.maxRedirects; redirects++) {
|
|
414
|
-
const resolvedAddresses = await resolvePublicTarget(current, {
|
|
415
|
-
...options,
|
|
416
|
-
timeoutMs: remainingTimeout()
|
|
417
|
-
})
|
|
418
|
-
let response
|
|
419
|
-
try {
|
|
420
|
-
const timeoutMs = remainingTimeout()
|
|
421
|
-
response = await runWithTimeout(
|
|
422
|
-
signal => options.transport({
|
|
423
|
-
url: current.toString(),
|
|
424
|
-
resolvedAddress: resolvedAddresses[0],
|
|
425
|
-
resolvedAddresses,
|
|
426
|
-
headers: options.headers,
|
|
427
|
-
timeoutMs,
|
|
428
|
-
maxBytes: options.maxBytes,
|
|
429
|
-
signal
|
|
430
|
-
}),
|
|
431
|
-
timeoutMs,
|
|
432
|
-
'Web request timed out'
|
|
433
|
-
)
|
|
434
|
-
} catch (error) {
|
|
435
|
-
if (error instanceof KnxAiWebAccessError) throw error
|
|
436
|
-
throw new KnxAiWebAccessError('REQUEST_FAILED', 'The web request failed')
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const statusCode = Number(response && (response.statusCode ?? response.status)) || 0
|
|
440
|
-
if (statusCode >= 300 && statusCode < 400) {
|
|
441
|
-
const location = getHeader(response.headers, 'location')
|
|
442
|
-
if (!location) throw new KnxAiWebAccessError('INVALID_REDIRECT', 'The web server returned an invalid redirect')
|
|
443
|
-
if (redirects >= options.maxRedirects) throw new KnxAiWebAccessError('TOO_MANY_REDIRECTS', 'The web request exceeded the redirect limit')
|
|
444
|
-
let redirected
|
|
445
|
-
try { redirected = new URL(location, current) } catch (error) { throw new KnxAiWebAccessError('INVALID_REDIRECT', 'The web server returned an invalid redirect') }
|
|
446
|
-
current = assertSafeHttpsUrl(redirected.toString())
|
|
447
|
-
continue
|
|
448
|
-
}
|
|
449
|
-
if (statusCode < 200 || statusCode >= 300) throw new KnxAiWebAccessError('HTTP_ERROR', `The web server returned HTTP ${statusCode || 'error'}`)
|
|
450
|
-
const contentEncoding = getHeader(response.headers, 'content-encoding').trim().toLowerCase()
|
|
451
|
-
if (contentEncoding && contentEncoding !== 'identity') throw new KnxAiWebAccessError('UNSUPPORTED_ENCODING', 'The web server returned an unsupported content encoding')
|
|
452
|
-
const contentType = getHeader(response.headers, 'content-type')
|
|
453
|
-
if (!isTextualMimeType(contentType)) throw new KnxAiWebAccessError('UNSUPPORTED_MIME', 'Only textual web content can be opened')
|
|
454
|
-
const body = responseBodyToBuffer(response.body)
|
|
455
|
-
const declaredLength = Number(getHeader(response.headers, 'content-length'))
|
|
456
|
-
if (body.length > options.maxBytes || (Number.isFinite(declaredLength) && declaredLength > options.maxBytes)) {
|
|
457
|
-
throw new KnxAiWebAccessError('RESPONSE_TOO_LARGE', 'The web response exceeds the size limit')
|
|
458
|
-
}
|
|
459
|
-
return {
|
|
460
|
-
body: body.toString('utf8'),
|
|
461
|
-
contentType: contentType.split(';')[0].trim().toLowerCase(),
|
|
462
|
-
statusCode,
|
|
463
|
-
url: current.toString()
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
throw new KnxAiWebAccessError('TOO_MANY_REDIRECTS', 'The web request exceeded the redirect limit')
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Normalize the explicit, provider-independent web action contract used by the AI runtime.
|
|
471
|
-
* No query classification or intent/keyword routing happens here.
|
|
472
|
-
*/
|
|
473
|
-
const normalizeKnxAiWebActions = (value, { maxActions = DEFAULT_MAX_ACTIONS } = {}) => {
|
|
474
|
-
let source = value
|
|
475
|
-
if (typeof source === 'string') {
|
|
476
|
-
try { source = JSON.parse(source) } catch (error) { return [] }
|
|
477
|
-
}
|
|
478
|
-
if (source && !Array.isArray(source) && Array.isArray(source.webActions)) source = source.webActions
|
|
479
|
-
if (!Array.isArray(source)) source = source && typeof source === 'object' ? [source] : []
|
|
480
|
-
const limit = boundedInteger(maxActions, DEFAULT_MAX_ACTIONS, 0, HARD_MAX_ACTIONS)
|
|
481
|
-
const normalized = []
|
|
482
|
-
for (const candidate of source) {
|
|
483
|
-
if (normalized.length >= limit) break
|
|
484
|
-
if (!candidate || typeof candidate !== 'object') continue
|
|
485
|
-
const rawOperation = String(candidate.operation || candidate.type || candidate.action || '').trim().toLowerCase().replace(/[ -]+/g, '_')
|
|
486
|
-
const operation = rawOperation === 'open_url' ? 'open' : rawOperation
|
|
487
|
-
if (operation === 'search') {
|
|
488
|
-
const query = compactWhitespace(candidate.query, MAX_QUERY_CHARS)
|
|
489
|
-
if (query) normalized.push({ operation, query, maxResults: boundedInteger(candidate.maxResults, DEFAULT_MAX_RESULTS, 1, HARD_MAX_RESULTS) })
|
|
490
|
-
} else if (operation === 'open') {
|
|
491
|
-
const url = String(candidate.url || '').trim().slice(0, MAX_URL_CHARS + 1)
|
|
492
|
-
if (url) normalized.push({ operation, url })
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
return normalized
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const normalizeSearchEndpoints = (value) => {
|
|
499
|
-
const source = Array.isArray(value) && value.length ? value : DEFAULT_SEARCH_ENDPOINTS
|
|
500
|
-
return source.slice(0, 3).map(endpoint => assertSafeHttpsUrl(endpoint).toString())
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Build an isolated web client. dnsLookup, transport and now are injectable for deterministic tests.
|
|
505
|
-
* A custom transport must connect to resolvedAddress while retaining url's host for TLS verification.
|
|
506
|
-
*/
|
|
507
|
-
const createKnxAiWebAccess = (options = {}) => {
|
|
508
|
-
const timeoutMs = boundedInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, 10, 60000)
|
|
509
|
-
const maxRedirects = boundedInteger(options.maxRedirects, DEFAULT_MAX_REDIRECTS, 0, HARD_MAX_REDIRECTS)
|
|
510
|
-
const dnsLookup = typeof options.dnsLookup === 'function' ? options.dnsLookup : defaultDnsLookup
|
|
511
|
-
const transport = typeof options.transport === 'function' ? options.transport : defaultPinnedHttpsTransport
|
|
512
|
-
const now = typeof options.now === 'function' ? options.now : () => new Date()
|
|
513
|
-
const maxTextChars = boundedInteger(options.maxTextChars, DEFAULT_TEXT_MAX_CHARS, 100, 100000)
|
|
514
|
-
const searchMaxBytes = boundedInteger(options.searchMaxBytes ?? options.maxBytes, DEFAULT_SEARCH_MAX_BYTES, 1, HARD_MAX_BYTES)
|
|
515
|
-
const openMaxBytes = boundedInteger(options.openMaxBytes ?? options.maxBytes, DEFAULT_OPEN_MAX_BYTES, 1, HARD_MAX_BYTES)
|
|
516
|
-
const searchEndpoints = normalizeSearchEndpoints(options.searchEndpoints)
|
|
517
|
-
const baseHeaders = {
|
|
518
|
-
accept: 'text/html,application/xhtml+xml,text/plain,application/json;q=0.8,application/xml;q=0.7',
|
|
519
|
-
'accept-encoding': 'identity',
|
|
520
|
-
'user-agent': compactWhitespace(options.userAgent || DEFAULT_USER_AGENT, 300)
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const open = async (url) => {
|
|
524
|
-
const response = await requestTextWithRedirects(url, {
|
|
525
|
-
dnsLookup,
|
|
526
|
-
headers: baseHeaders,
|
|
527
|
-
maxBytes: openMaxBytes,
|
|
528
|
-
maxRedirects,
|
|
529
|
-
timeoutMs,
|
|
530
|
-
transport
|
|
531
|
-
})
|
|
532
|
-
const retrievedAt = now().toISOString()
|
|
533
|
-
const html = response.contentType === 'text/html' || response.contentType === 'application/xhtml+xml'
|
|
534
|
-
const extracted = html
|
|
535
|
-
? extractHtmlDocument(response.body, maxTextChars)
|
|
536
|
-
: { title: '', text: compactWhitespace(response.body, maxTextChars) }
|
|
537
|
-
const finalUrl = assertSafeHttpsUrl(response.url)
|
|
538
|
-
return {
|
|
539
|
-
operation: 'open',
|
|
540
|
-
ok: true,
|
|
541
|
-
url: finalUrl.toString(),
|
|
542
|
-
title: extracted.title || normalizeHostname(finalUrl.hostname),
|
|
543
|
-
text: extracted.text,
|
|
544
|
-
retrievedAt
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const search = async (query, maxResults = DEFAULT_MAX_RESULTS) => {
|
|
549
|
-
const normalizedQuery = compactWhitespace(query, MAX_QUERY_CHARS)
|
|
550
|
-
if (!normalizedQuery) throw new KnxAiWebAccessError('INVALID_QUERY', 'A search query is required')
|
|
551
|
-
let lastError = null
|
|
552
|
-
for (const endpoint of searchEndpoints) {
|
|
553
|
-
const searchUrl = new URL(endpoint)
|
|
554
|
-
searchUrl.searchParams.set('q', normalizedQuery)
|
|
555
|
-
try {
|
|
556
|
-
const response = await requestTextWithRedirects(searchUrl.toString(), {
|
|
557
|
-
dnsLookup,
|
|
558
|
-
headers: baseHeaders,
|
|
559
|
-
maxBytes: searchMaxBytes,
|
|
560
|
-
maxRedirects,
|
|
561
|
-
timeoutMs,
|
|
562
|
-
transport
|
|
563
|
-
})
|
|
564
|
-
const retrievedAt = now().toISOString()
|
|
565
|
-
const results = parseDuckDuckGoResults(response.body, { maxResults, retrievedAt })
|
|
566
|
-
if (results.length) {
|
|
567
|
-
return { operation: 'search', ok: true, query: normalizedQuery, results, retrievedAt }
|
|
568
|
-
}
|
|
569
|
-
lastError = new KnxAiWebAccessError('NO_RESULTS', 'The web search returned no usable results')
|
|
570
|
-
} catch (error) {
|
|
571
|
-
lastError = error
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
throw lastError || new KnxAiWebAccessError('SEARCH_FAILED', 'The web search failed')
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const executeAction = async (action) => {
|
|
578
|
-
const retrievedAt = now().toISOString()
|
|
579
|
-
try {
|
|
580
|
-
if (action.operation === 'search') return await search(action.query, action.maxResults)
|
|
581
|
-
if (action.operation === 'open') return await open(action.url)
|
|
582
|
-
throw new KnxAiWebAccessError('UNSUPPORTED_OPERATION', 'Unsupported web operation')
|
|
583
|
-
} catch (error) {
|
|
584
|
-
const message = error instanceof KnxAiWebAccessError ? error.message : 'The web operation failed'
|
|
585
|
-
return {
|
|
586
|
-
operation: action.operation,
|
|
587
|
-
ok: false,
|
|
588
|
-
...(action.query ? { query: action.query } : {}),
|
|
589
|
-
...(action.url ? { url: action.url } : {}),
|
|
590
|
-
retrievedAt,
|
|
591
|
-
error: message
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
return { executeAction, open, search }
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Execute normalized actions sequentially and return one bounded result envelope per valid action.
|
|
601
|
-
*/
|
|
602
|
-
const executeKnxAiWebActions = async (actions, options = {}) => {
|
|
603
|
-
const normalized = normalizeKnxAiWebActions(actions, { maxActions: options.maxActions })
|
|
604
|
-
const access = createKnxAiWebAccess(options)
|
|
605
|
-
const results = []
|
|
606
|
-
for (const action of normalized) results.push(await access.executeAction(action))
|
|
607
|
-
return results
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
module.exports = {
|
|
611
|
-
createKnxAiWebAccess,
|
|
612
|
-
executeKnxAiWebActions,
|
|
613
|
-
normalizeKnxAiWebActions,
|
|
614
|
-
__test: {
|
|
615
|
-
KnxAiWebAccessError,
|
|
616
|
-
assertSafeHttpsUrl,
|
|
617
|
-
compactWhitespace,
|
|
618
|
-
decodeHtmlEntities,
|
|
619
|
-
extractHtmlDocument,
|
|
620
|
-
getHeader,
|
|
621
|
-
ipv4InRange,
|
|
622
|
-
ipv6InRange,
|
|
623
|
-
isPublicIpAddress,
|
|
624
|
-
isTextualMimeType,
|
|
625
|
-
normalizeDnsRecords,
|
|
626
|
-
parseDuckDuckGoResults,
|
|
627
|
-
requestTextWithRedirects,
|
|
628
|
-
resolveDuckDuckGoResultUrl,
|
|
629
|
-
stripHtmlTags
|
|
630
|
-
}
|
|
631
|
-
}
|