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,302 +0,0 @@
|
|
|
1
|
-
const KNX_AI_CAMERA_REGISTRY_KEY = Symbol.for('node-red.knx-ai.camera-adapters.v1')
|
|
2
|
-
const KNX_AI_CAMERA_IMAGE_MAX_BYTES = 6 * 1024 * 1024
|
|
3
|
-
const KNX_AI_CAMERA_MAX_ACTIONS = 8
|
|
4
|
-
|
|
5
|
-
const clampText = (value, maxChars = 240) => String(value === undefined || value === null ? '' : value)
|
|
6
|
-
.trim()
|
|
7
|
-
.slice(0, Math.max(0, Number(maxChars) || 0))
|
|
8
|
-
|
|
9
|
-
const normalizeSearchText = value => clampText(value, 300)
|
|
10
|
-
.normalize('NFKD')
|
|
11
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
12
|
-
.toLocaleLowerCase()
|
|
13
|
-
.replace(/[^a-z0-9]+/g, ' ')
|
|
14
|
-
.trim()
|
|
15
|
-
|
|
16
|
-
const uniqueTexts = values => Array.from(new Set((Array.isArray(values) ? values : [])
|
|
17
|
-
.map(value => clampText(value, 240))
|
|
18
|
-
.filter(Boolean)))
|
|
19
|
-
|
|
20
|
-
// Runtime contract for optional camera suites. A package registers one adapter
|
|
21
|
-
// manifest and one provider per configured controller. Providers expose:
|
|
22
|
-
// listCameras({ force }) -> camera[]
|
|
23
|
-
// takeSnapshot({ cameraId, cameraName, highQuality }) -> { data, mediaType, camera }
|
|
24
|
-
// subscribe(listener) -> unsubscribe(), with normalized camera event objects.
|
|
25
|
-
// The global Symbol lets separately installed Node-RED packages share the same
|
|
26
|
-
// in-process registry without either package importing the other one.
|
|
27
|
-
const getKnxAiCameraAdapterRegistry = () => {
|
|
28
|
-
const existing = globalThis[KNX_AI_CAMERA_REGISTRY_KEY]
|
|
29
|
-
if (existing && existing.version === 1 && existing.adapters instanceof Map && existing.providers instanceof Map) return existing
|
|
30
|
-
const registry = {
|
|
31
|
-
version: 1,
|
|
32
|
-
adapters: new Map(),
|
|
33
|
-
providers: new Map(),
|
|
34
|
-
listeners: new Set(),
|
|
35
|
-
registerAdapter (adapter) {
|
|
36
|
-
if (!adapter || !adapter.id) return
|
|
37
|
-
this.adapters.set(String(adapter.id), Object.freeze(Object.assign({}, adapter)))
|
|
38
|
-
this.listeners.forEach(listener => {
|
|
39
|
-
try { listener({ type: 'adapter_registered', adapter: this.adapters.get(String(adapter.id)) }) } catch (error) { /* ignore */ }
|
|
40
|
-
})
|
|
41
|
-
},
|
|
42
|
-
registerProvider (provider) {
|
|
43
|
-
if (!provider || !provider.id) return
|
|
44
|
-
this.providers.set(String(provider.id), provider)
|
|
45
|
-
this.listeners.forEach(listener => {
|
|
46
|
-
try { listener({ type: 'provider_registered', provider }) } catch (error) { /* ignore */ }
|
|
47
|
-
})
|
|
48
|
-
},
|
|
49
|
-
unregisterProvider (providerId) {
|
|
50
|
-
const id = String(providerId || '')
|
|
51
|
-
const provider = this.providers.get(id)
|
|
52
|
-
if (!provider) return
|
|
53
|
-
this.providers.delete(id)
|
|
54
|
-
this.listeners.forEach(listener => {
|
|
55
|
-
try { listener({ type: 'provider_unregistered', provider }) } catch (error) { /* ignore */ }
|
|
56
|
-
})
|
|
57
|
-
},
|
|
58
|
-
subscribe (listener) {
|
|
59
|
-
if (typeof listener !== 'function') return () => {}
|
|
60
|
-
this.listeners.add(listener)
|
|
61
|
-
return () => this.listeners.delete(listener)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
globalThis[KNX_AI_CAMERA_REGISTRY_KEY] = registry
|
|
65
|
-
return registry
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const normalizeKnxAiCameraRegistration = (value = {}) => {
|
|
69
|
-
const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {}
|
|
70
|
-
const id = clampText(source.cameraId || source.id, 160)
|
|
71
|
-
const name = clampText(source.cameraName || source.name || id, 240)
|
|
72
|
-
const state = clampText(source.state, 80).toUpperCase()
|
|
73
|
-
const online = typeof source.online === 'boolean'
|
|
74
|
-
? source.online
|
|
75
|
-
: state
|
|
76
|
-
? ['CONNECTED', 'ONLINE'].includes(state)
|
|
77
|
-
: null
|
|
78
|
-
if (!id && !name) return null
|
|
79
|
-
return {
|
|
80
|
-
id,
|
|
81
|
-
name,
|
|
82
|
-
aliases: uniqueTexts([name, id].concat(Array.isArray(source.aliases) ? source.aliases : [])),
|
|
83
|
-
source: clampText(source.source || 'camera-adapter', 80),
|
|
84
|
-
adapterId: clampText(source.adapterId, 120),
|
|
85
|
-
adapterTitle: clampText(source.adapterTitle, 240),
|
|
86
|
-
providerId: clampText(source.providerId, 200),
|
|
87
|
-
controllerId: clampText(source.controllerId, 160),
|
|
88
|
-
controllerName: clampText(source.controllerName, 240),
|
|
89
|
-
nativeCameraId: clampText(source.nativeCameraId, 160),
|
|
90
|
-
state,
|
|
91
|
-
online,
|
|
92
|
-
objectTypes: uniqueTexts(source.objectTypes).map(normalizeCameraObjectType).filter(Boolean).slice(0, 24),
|
|
93
|
-
lines: (Array.isArray(source.lines) ? source.lines : []).slice(0, 80),
|
|
94
|
-
zones: (Array.isArray(source.zones) ? source.zones : []).slice(0, 80),
|
|
95
|
-
lastSeenAt: clampText(source.lastSeenAt || new Date().toISOString(), 64)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const resolveKnxAiCamera = ({ target, cameras } = {}) => {
|
|
100
|
-
const requested = normalizeSearchText(target)
|
|
101
|
-
const list = (Array.isArray(cameras) ? cameras : [])
|
|
102
|
-
.map(normalizeKnxAiCameraRegistration)
|
|
103
|
-
.filter(Boolean)
|
|
104
|
-
if (!requested) return { camera: null, ambiguous: false }
|
|
105
|
-
|
|
106
|
-
const exact = list.filter(camera => [camera.id, camera.name].concat(camera.aliases)
|
|
107
|
-
.some(alias => normalizeSearchText(alias) === requested))
|
|
108
|
-
if (exact.length === 1) return { camera: exact[0], ambiguous: false }
|
|
109
|
-
if (exact.length > 1) return { camera: null, ambiguous: true }
|
|
110
|
-
|
|
111
|
-
const partial = list.filter(camera => [camera.id, camera.name].concat(camera.aliases)
|
|
112
|
-
.some(alias => {
|
|
113
|
-
const normalizedAlias = normalizeSearchText(alias)
|
|
114
|
-
return normalizedAlias && (normalizedAlias.includes(requested) || requested.includes(normalizedAlias))
|
|
115
|
-
}))
|
|
116
|
-
if (partial.length === 1) return { camera: partial[0], ambiguous: false }
|
|
117
|
-
return { camera: null, ambiguous: partial.length > 1 }
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const normalizeCameraEventType = value => {
|
|
121
|
-
const raw = normalizeSearchText(value).replace(/\s+/g, '')
|
|
122
|
-
if (['smartdetect', 'objectdetect', 'objectdetection', 'smartdetection'].includes(raw)) return 'smartDetect'
|
|
123
|
-
if (['smartdetectline', 'line', 'linecrossing', 'crossline'].includes(raw)) return 'smartDetectLine'
|
|
124
|
-
if (['smartdetectzone', 'zone', 'intrusionzone', 'intrusion'].includes(raw)) return 'smartDetectZone'
|
|
125
|
-
if (['smartdetectloiterzone', 'loiter', 'loiterzone'].includes(raw)) return 'smartDetectLoiterZone'
|
|
126
|
-
if (['motion', 'movement'].includes(raw)) return 'motion'
|
|
127
|
-
if (['ring', 'doorbell'].includes(raw)) return 'ring'
|
|
128
|
-
if (['smartaudiodetect', 'audio'].includes(raw)) return 'smartAudioDetect'
|
|
129
|
-
return clampText(value, 80)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const normalizeCameraObjectType = value => {
|
|
133
|
-
const raw = normalizeSearchText(value).replace(/\s+/g, '')
|
|
134
|
-
if (['person', 'people', 'persona', 'persone', 'human', 'someone', 'qualcuno'].includes(raw)) return 'person'
|
|
135
|
-
if (['animal', 'animale', 'animali', 'pet', 'pets', 'dog', 'cat', 'cane', 'gatto'].includes(raw)) return 'animal'
|
|
136
|
-
if (['vehicle', 'vehicles', 'veicolo', 'veicoli', 'car', 'auto', 'automobile'].includes(raw)) return 'vehicle'
|
|
137
|
-
if (['face', 'volto', 'faccia'].includes(raw)) return 'face'
|
|
138
|
-
if (['licenseplate', 'numberplate', 'targa'].includes(raw)) return 'licenseplate'
|
|
139
|
-
if (['package', 'parcel', 'pacco'].includes(raw)) return 'package'
|
|
140
|
-
return normalizeSearchText(value).replace(/\s+/g, '')
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const normalizeCameraActionType = value => {
|
|
144
|
-
const raw = normalizeSearchText(value).replace(/\s+/g, '_')
|
|
145
|
-
if (['snapshot', 'get_snapshot', 'send_snapshot', 'camera_snapshot'].includes(raw)) return 'snapshot'
|
|
146
|
-
if (['analyze', 'analyse', 'analyze_snapshot', 'describe_snapshot', 'vision'].includes(raw)) return 'analyze'
|
|
147
|
-
if (['watch', 'subscribe', 'notify', 'create_watch'].includes(raw)) return 'watch'
|
|
148
|
-
if (['unwatch', 'unsubscribe', 'stop', 'remove_watch', 'delete_watch'].includes(raw)) return 'unwatch'
|
|
149
|
-
if (['list', 'list_watches', 'show_watches'].includes(raw)) return 'list_watches'
|
|
150
|
-
return ''
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const normalizeKnxAiCameraAction = (value, cameras = []) => {
|
|
154
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
155
|
-
const type = normalizeCameraActionType(value.type || value.action || value.operation)
|
|
156
|
-
if (!type) return null
|
|
157
|
-
const target = clampText(value.cameraId || value.cameraName || value.camera || value.target, 240)
|
|
158
|
-
const resolved = resolveKnxAiCamera({ target, cameras })
|
|
159
|
-
const camera = resolved.camera
|
|
160
|
-
let eventType = normalizeCameraEventType(value.eventType || value.event)
|
|
161
|
-
const requestedScope = clampText(value.scopeId || value.scopeName || value.zoneId || value.lineId || value.zone || value.line, 240)
|
|
162
|
-
const availableScopes = camera
|
|
163
|
-
? eventType === 'smartDetectLine'
|
|
164
|
-
? camera.lines
|
|
165
|
-
: ['smartDetectZone', 'smartDetectLoiterZone'].includes(eventType)
|
|
166
|
-
? camera.zones
|
|
167
|
-
: []
|
|
168
|
-
: []
|
|
169
|
-
const normalizedScopeTarget = normalizeSearchText(requestedScope)
|
|
170
|
-
const matchingScopes = normalizedScopeTarget
|
|
171
|
-
? (Array.isArray(availableScopes) ? availableScopes : []).filter(scope => [scope && scope.id, scope && scope.name]
|
|
172
|
-
.some(candidate => normalizeSearchText(candidate) === normalizedScopeTarget))
|
|
173
|
-
: []
|
|
174
|
-
const resolvedScope = matchingScopes.length === 1 ? matchingScopes[0] : null
|
|
175
|
-
const objectTypes = uniqueTexts(Array.isArray(value.objectTypes)
|
|
176
|
-
? value.objectTypes
|
|
177
|
-
: [value.objectType || value.smartDetectType]).map(normalizeCameraObjectType).filter(Boolean).slice(0, 12)
|
|
178
|
-
// A plain motion event has no classified object metadata. Convert requests
|
|
179
|
-
// such as "motion caused by a person/animal" to the generic smart-detection
|
|
180
|
-
// family so they can match Protect zone, line, or loiter detections.
|
|
181
|
-
if (type === 'watch' && eventType === 'motion' && objectTypes.length > 0) eventType = 'smartDetect'
|
|
182
|
-
return {
|
|
183
|
-
type,
|
|
184
|
-
cameraId: camera ? camera.id : clampText(value.cameraId, 160),
|
|
185
|
-
cameraName: camera ? camera.name : clampText(value.cameraName || value.camera || value.target, 240),
|
|
186
|
-
unresolvedTarget: camera ? '' : target,
|
|
187
|
-
unresolved: Boolean(target && !camera),
|
|
188
|
-
ambiguous: resolved.ambiguous,
|
|
189
|
-
eventType,
|
|
190
|
-
scopeId: resolvedScope ? clampText(resolvedScope.id, 160) : clampText(value.scopeId || value.zoneId || value.lineId, 160),
|
|
191
|
-
scopeName: resolvedScope ? clampText(resolvedScope.name || resolvedScope.id, 240) : clampText(value.scopeName || value.zone || value.line, 240),
|
|
192
|
-
unresolvedScope: Boolean(requestedScope && camera && !resolvedScope),
|
|
193
|
-
ambiguousScope: matchingScopes.length > 1,
|
|
194
|
-
objectTypes,
|
|
195
|
-
cooldownSeconds: Math.max(10, Math.min(86400, Number(value.cooldownSeconds) || 60)),
|
|
196
|
-
sendSnapshot: value.sendSnapshot !== false,
|
|
197
|
-
reason: clampText(value.reason, 500)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const normalizeKnxAiCameraActions = ({ actions, cameras } = {}) => (Array.isArray(actions) ? actions : [])
|
|
202
|
-
.slice(0, KNX_AI_CAMERA_MAX_ACTIONS)
|
|
203
|
-
.map(action => normalizeKnxAiCameraAction(action, cameras))
|
|
204
|
-
.filter(Boolean)
|
|
205
|
-
|
|
206
|
-
const normalizeKnxAiCameraEvent = value => {
|
|
207
|
-
const meta = value && typeof value === 'object' && !Array.isArray(value) && value.eventType
|
|
208
|
-
? value
|
|
209
|
-
: null
|
|
210
|
-
if (!meta) return null
|
|
211
|
-
return {
|
|
212
|
-
cameraId: clampText(meta.cameraId, 160),
|
|
213
|
-
cameraName: clampText(meta.cameraName, 240),
|
|
214
|
-
eventType: normalizeCameraEventType(meta.eventType),
|
|
215
|
-
scopeId: clampText(meta.scopeId, 160),
|
|
216
|
-
scopeName: clampText(meta.scopeName, 240),
|
|
217
|
-
objectTypes: uniqueTexts(Array.isArray(meta.objectTypes) ? meta.objectTypes : []).map(normalizeSearchText).filter(Boolean),
|
|
218
|
-
eventId: clampText(meta.eventId, 160),
|
|
219
|
-
active: meta.active !== false,
|
|
220
|
-
at: clampText(meta.at || new Date().toISOString(), 64),
|
|
221
|
-
raw: meta.raw
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const cameraWatchMatchesEvent = (watch, event) => {
|
|
226
|
-
if (!watch || !event) return false
|
|
227
|
-
const watchCameraId = clampText(watch.cameraId, 160)
|
|
228
|
-
const eventCameraId = clampText(event.cameraId, 160)
|
|
229
|
-
if (watchCameraId && eventCameraId && watchCameraId !== eventCameraId) return false
|
|
230
|
-
if ((!watchCameraId || !eventCameraId) && normalizeSearchText(watch.cameraName) !== normalizeSearchText(event.cameraName)) return false
|
|
231
|
-
const wantedObjects = (Array.isArray(watch.objectTypes) ? watch.objectTypes : []).map(normalizeCameraObjectType).filter(Boolean)
|
|
232
|
-
const watchEventType = normalizeCameraEventType(watch.eventType)
|
|
233
|
-
const eventType = normalizeCameraEventType(event.eventType)
|
|
234
|
-
const genericSmartDetection = watchEventType === 'smartDetect' ||
|
|
235
|
-
(watchEventType === 'motion' && wantedObjects.length > 0)
|
|
236
|
-
if (genericSmartDetection) {
|
|
237
|
-
if (!['smartDetectZone', 'smartDetectLine', 'smartDetectLoiterZone'].includes(eventType)) return false
|
|
238
|
-
} else if (watchEventType && watchEventType !== eventType) return false
|
|
239
|
-
const watchScope = normalizeSearchText(watch.scopeId || watch.scopeName)
|
|
240
|
-
const eventScopeCandidates = [event.scopeId, event.scopeName].map(normalizeSearchText).filter(Boolean)
|
|
241
|
-
if (watchScope && !eventScopeCandidates.some(candidate => candidate === watchScope || candidate.includes(watchScope) || watchScope.includes(candidate))) return false
|
|
242
|
-
if (wantedObjects.length) {
|
|
243
|
-
const detectedObjects = (Array.isArray(event.objectTypes) ? event.objectTypes : []).map(normalizeCameraObjectType).filter(Boolean)
|
|
244
|
-
if (!detectedObjects.some(type => wantedObjects.includes(type))) return false
|
|
245
|
-
}
|
|
246
|
-
return true
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const normalizeKnxAiCameraImage = ({ data, mediaType } = {}) => {
|
|
250
|
-
if (!Buffer.isBuffer(data)) throw new Error('The camera snapshot is not binary image data')
|
|
251
|
-
if (data.length === 0) throw new Error('The camera snapshot is empty')
|
|
252
|
-
if (data.length > KNX_AI_CAMERA_IMAGE_MAX_BYTES) throw new Error(`The camera snapshot exceeds ${KNX_AI_CAMERA_IMAGE_MAX_BYTES} bytes`)
|
|
253
|
-
const type = clampText(mediaType || 'image/jpeg', 80).toLocaleLowerCase().split(';')[0]
|
|
254
|
-
if (!['image/jpeg', 'image/png', 'image/webp', 'image/gif'].includes(type)) throw new Error(`Unsupported camera image type: ${type}`)
|
|
255
|
-
return { data, mediaType: type, bytes: data.length }
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const buildKnxAiCameraNotificationText = ({ language, event } = {}) => {
|
|
259
|
-
const lang = clampText(language || 'en', 8).toLocaleLowerCase()
|
|
260
|
-
const camera = clampText(event && event.cameraName, 240) || clampText(event && event.cameraId, 160) || 'camera'
|
|
261
|
-
const eventType = normalizeCameraEventType(event && event.eventType)
|
|
262
|
-
const scope = clampText(event && (event.scopeName || event.scopeId), 240)
|
|
263
|
-
const objectTypes = uniqueTexts(event && event.objectTypes).join(', ')
|
|
264
|
-
const details = [scope, objectTypes].filter(Boolean).join(' — ')
|
|
265
|
-
const suffix = details ? ` (${details})` : ''
|
|
266
|
-
const labels = {
|
|
267
|
-
it: { smartDetectLine: 'un attraversamento di linea', smartDetectZone: 'un ingresso in zona', smartDetectLoiterZone: 'una permanenza in zona', motion: 'un movimento', ring: 'un suono del campanello', smartAudioDetect: 'un evento audio intelligente' },
|
|
268
|
-
de: { smartDetectLine: 'eine Linienüberquerung', smartDetectZone: 'einen Zoneneintritt', smartDetectLoiterZone: 'einen längeren Zonenaufenthalt', motion: 'eine Bewegung', ring: 'ein Klingeln', smartAudioDetect: 'ein intelligentes Audioereignis' },
|
|
269
|
-
fr: { smartDetectLine: 'un franchissement de ligne', smartDetectZone: 'une entrée dans une zone', smartDetectLoiterZone: 'une présence prolongée dans une zone', motion: 'un mouvement', ring: 'une sonnerie', smartAudioDetect: 'un événement audio intelligent' },
|
|
270
|
-
es: { smartDetectLine: 'un cruce de línea', smartDetectZone: 'una entrada en zona', smartDetectLoiterZone: 'una permanencia en zona', motion: 'un movimiento', ring: 'una llamada al timbre', smartAudioDetect: 'un evento de audio inteligente' },
|
|
271
|
-
zh: { smartDetectLine: '越线事件', smartDetectZone: '进入区域事件', smartDetectLoiterZone: '区域徘徊事件', motion: '移动事件', ring: '门铃事件', smartAudioDetect: '智能音频事件' },
|
|
272
|
-
en: { smartDetectLine: 'a line crossing', smartDetectZone: 'a zone entry', smartDetectLoiterZone: 'loitering in a zone', motion: 'motion', ring: 'a doorbell ring', smartAudioDetect: 'a smart audio event' }
|
|
273
|
-
}
|
|
274
|
-
const label = (labels[lang] || labels.en)[eventType] || (lang === 'zh' ? '摄像机事件' : lang === 'it' ? 'un evento' : lang === 'de' ? 'ein Ereignis' : lang === 'fr' ? 'un événement' : lang === 'es' ? 'un evento' : 'an event')
|
|
275
|
-
const copy = {
|
|
276
|
-
it: `La telecamera ${camera} ha rilevato ${label}${suffix}.`,
|
|
277
|
-
de: `Die Kamera ${camera} hat ${label} erkannt${suffix}.`,
|
|
278
|
-
fr: `La caméra ${camera} a détecté ${label}${suffix}.`,
|
|
279
|
-
es: `La cámara ${camera} ha detectado ${label}${suffix}.`,
|
|
280
|
-
zh: `摄像机 ${camera} 检测到${label}${suffix}。`,
|
|
281
|
-
en: `Camera ${camera} detected ${label}${suffix}.`
|
|
282
|
-
}
|
|
283
|
-
return copy[lang] || copy.en
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
module.exports = {
|
|
287
|
-
KNX_AI_CAMERA_IMAGE_MAX_BYTES,
|
|
288
|
-
KNX_AI_CAMERA_MAX_ACTIONS,
|
|
289
|
-
KNX_AI_CAMERA_REGISTRY_KEY,
|
|
290
|
-
buildKnxAiCameraNotificationText,
|
|
291
|
-
cameraWatchMatchesEvent,
|
|
292
|
-
getKnxAiCameraAdapterRegistry,
|
|
293
|
-
normalizeCameraEventType,
|
|
294
|
-
normalizeCameraObjectType,
|
|
295
|
-
normalizeKnxAiCameraAction,
|
|
296
|
-
normalizeKnxAiCameraActions,
|
|
297
|
-
normalizeKnxAiCameraEvent,
|
|
298
|
-
normalizeKnxAiCameraImage,
|
|
299
|
-
normalizeKnxAiCameraRegistration,
|
|
300
|
-
normalizeSearchText,
|
|
301
|
-
resolveKnxAiCamera
|
|
302
|
-
}
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
const KNX_AI_CATALOG_MAX_RESEARCH_ROUNDS = 1
|
|
2
|
-
const KNX_AI_CATALOG_MAX_ACTIONS_PER_ROUND = 4
|
|
3
|
-
const KNX_AI_CATALOG_MAX_RESULTS_PER_ACTION = 12
|
|
4
|
-
const KNX_AI_CATALOG_MAX_ACCUMULATED_OBJECTS = 24
|
|
5
|
-
|
|
6
|
-
const normalizeText = (value) => String(value || '')
|
|
7
|
-
.normalize('NFKD')
|
|
8
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
9
|
-
.toLowerCase()
|
|
10
|
-
.replace(/[^\p{L}\p{N}/]+/gu, ' ')
|
|
11
|
-
.replace(/\s+/g, ' ')
|
|
12
|
-
.trim()
|
|
13
|
-
|
|
14
|
-
const normalizeGa = (value) => String(value || '').trim()
|
|
15
|
-
|
|
16
|
-
const clampLimit = (value) => Math.max(1, Math.min(
|
|
17
|
-
KNX_AI_CATALOG_MAX_RESULTS_PER_ACTION,
|
|
18
|
-
Math.round(Number(value) || 8)
|
|
19
|
-
))
|
|
20
|
-
|
|
21
|
-
const normalizeStringList = (value, maxItems = 12) => Array.from(new Set(
|
|
22
|
-
(Array.isArray(value) ? value : [])
|
|
23
|
-
.map(item => String(item || '').trim())
|
|
24
|
-
.filter(Boolean)
|
|
25
|
-
)).slice(0, maxItems)
|
|
26
|
-
|
|
27
|
-
const normalizeKnxAiCatalogActions = (value, { maxActions = KNX_AI_CATALOG_MAX_ACTIONS_PER_ROUND } = {}) => {
|
|
28
|
-
const accepted = []
|
|
29
|
-
const allowedOperations = new Set(['search', 'get', 'list_areas', 'browse_area', 'related'])
|
|
30
|
-
const allowedAccess = new Set(['any', 'read-only', 'read-write'])
|
|
31
|
-
const allowedPurposes = new Set(['any', 'read', 'write', 'inspect'])
|
|
32
|
-
;(Array.isArray(value) ? value : []).slice(0, Math.max(1, Number(maxActions) || 1)).forEach(candidate => {
|
|
33
|
-
const source = candidate && typeof candidate === 'object' && !Array.isArray(candidate) ? candidate : {}
|
|
34
|
-
const operation = String(source.operation || '').trim().toLowerCase()
|
|
35
|
-
if (!allowedOperations.has(operation)) return
|
|
36
|
-
const query = String(source.query || '').trim().slice(0, 300)
|
|
37
|
-
const destinations = normalizeStringList(source.destinations, 20)
|
|
38
|
-
const area = String(source.area || '').trim().slice(0, 300)
|
|
39
|
-
if (operation === 'search' && !query) return
|
|
40
|
-
if ((operation === 'get' || operation === 'related') && !destinations.length) return
|
|
41
|
-
if (operation === 'browse_area' && !area) return
|
|
42
|
-
const semanticKinds = normalizeStringList(source.semanticKinds, 12)
|
|
43
|
-
.map(kind => normalizeText(kind))
|
|
44
|
-
.filter(Boolean)
|
|
45
|
-
const requestedAccess = String(source.access || '').trim().toLowerCase()
|
|
46
|
-
const requestedPurpose = String(source.purpose || '').trim().toLowerCase()
|
|
47
|
-
accepted.push({
|
|
48
|
-
operation,
|
|
49
|
-
query,
|
|
50
|
-
destinations,
|
|
51
|
-
area,
|
|
52
|
-
semanticKinds,
|
|
53
|
-
access: allowedAccess.has(requestedAccess) ? requestedAccess : 'any',
|
|
54
|
-
purpose: allowedPurposes.has(requestedPurpose) ? requestedPurpose : 'any',
|
|
55
|
-
offset: Math.max(0, Math.min(100000, Math.round(Number(source.offset) || 0))),
|
|
56
|
-
limit: clampLimit(source.limit),
|
|
57
|
-
reason: String(source.reason || '').trim().slice(0, 1000)
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
return accepted
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const editDistanceAtMostTwo = (left, right) => {
|
|
64
|
-
if (left === right) return 0
|
|
65
|
-
if (!left || !right || Math.abs(left.length - right.length) > 2) return 3
|
|
66
|
-
let previous = Array.from({ length: right.length + 1 }, (_, index) => index)
|
|
67
|
-
for (let i = 1; i <= left.length; i += 1) {
|
|
68
|
-
const current = [i]
|
|
69
|
-
let rowMinimum = current[0]
|
|
70
|
-
for (let j = 1; j <= right.length; j += 1) {
|
|
71
|
-
const substitution = previous[j - 1] + (left[i - 1] === right[j - 1] ? 0 : 1)
|
|
72
|
-
current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, substitution)
|
|
73
|
-
rowMinimum = Math.min(rowMinimum, current[j])
|
|
74
|
-
}
|
|
75
|
-
if (rowMinimum > 2) return 3
|
|
76
|
-
previous = current
|
|
77
|
-
}
|
|
78
|
-
return previous[right.length]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const tokenQuality = (queryToken, candidateToken) => {
|
|
82
|
-
if (queryToken === candidateToken) return 1
|
|
83
|
-
const substringMinimum = /[^\u0000-\u007f]/.test(queryToken) ? 2 : 3
|
|
84
|
-
if (queryToken.length >= substringMinimum && (candidateToken.includes(queryToken) || queryToken.includes(candidateToken))) return 0.82
|
|
85
|
-
if (queryToken.length < 4) return 0
|
|
86
|
-
const distance = editDistanceAtMostTwo(queryToken, candidateToken)
|
|
87
|
-
if (distance === 1) return 0.68
|
|
88
|
-
if (distance === 2 && queryToken.length >= 7) return 0.5
|
|
89
|
-
return 0
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const buildSearchDocument = (item) => {
|
|
93
|
-
const semantic = item && item.semantic && typeof item.semantic === 'object' ? item.semantic : {}
|
|
94
|
-
const aliases = Array.isArray(item && item.aliases) ? item.aliases : []
|
|
95
|
-
const options = Array.isArray(item && item.valueOptions) ? item.valueOptions : []
|
|
96
|
-
const fields = {
|
|
97
|
-
ga: normalizeText(item && item.ga),
|
|
98
|
-
label: normalizeText(item && item.label),
|
|
99
|
-
etsName: normalizeText(item && item.etsName),
|
|
100
|
-
hierarchy: normalizeText(item && item.hierarchyPath),
|
|
101
|
-
aliases: normalizeText(aliases.join(' ')),
|
|
102
|
-
semantic: normalizeText([semantic.kind, semantic.area, item && item.mainGroup, item && item.middleGroup, ...(Array.isArray(item && item.tags) ? item.tags : [])].join(' ')),
|
|
103
|
-
values: normalizeText(options.map(option => `${option && option.value} ${option && option.label}`).join(' ')),
|
|
104
|
-
dpt: normalizeText(item && item.dpt)
|
|
105
|
-
}
|
|
106
|
-
const combined = Object.values(fields).join(' ').trim()
|
|
107
|
-
return { fields, combined, tokens: Array.from(new Set(combined.split(' ').filter(Boolean))) }
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const itemPassesFilters = (item, action) => {
|
|
111
|
-
if (action.access === 'read-only' && item && item.readOnly !== true) return false
|
|
112
|
-
if (action.access === 'read-write' && item && item.readOnly === true) return false
|
|
113
|
-
if (action.purpose === 'write' && item && item.readOnly === true) return false
|
|
114
|
-
const semantic = item && item.semantic && typeof item.semantic === 'object' ? item.semantic : {}
|
|
115
|
-
if (action.semanticKinds.length && !action.semanticKinds.includes(normalizeText(semantic.kind))) return false
|
|
116
|
-
return true
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const purposeScore = (item, purpose) => {
|
|
120
|
-
const readOnly = item && item.readOnly === true
|
|
121
|
-
if (purpose === 'write') return readOnly ? -1000 : 130
|
|
122
|
-
if (purpose === 'read') return readOnly ? 90 : 50
|
|
123
|
-
if (purpose === 'inspect') return readOnly ? 70 : 55
|
|
124
|
-
return 0
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const scoreSearchMatch = (item, action) => {
|
|
128
|
-
if (!itemPassesFilters(item, action)) return 0
|
|
129
|
-
const query = normalizeText(action.query)
|
|
130
|
-
if (!query) return 0
|
|
131
|
-
const document = buildSearchDocument(item)
|
|
132
|
-
const exactGa = normalizeGa(item && item.ga) === normalizeGa(action.query)
|
|
133
|
-
if (exactGa) return 2000 + purposeScore(item, action.purpose)
|
|
134
|
-
let score = 0
|
|
135
|
-
if (document.fields.label === query) score += 900
|
|
136
|
-
if (document.fields.etsName === query) score += 850
|
|
137
|
-
if (document.fields.aliases.split(' ').includes(query)) score += 800
|
|
138
|
-
if (document.fields.hierarchy === query) score += 750
|
|
139
|
-
if (document.combined.includes(query)) score += 360
|
|
140
|
-
const queryTokens = query.split(' ').filter(Boolean)
|
|
141
|
-
let qualityTotal = 0
|
|
142
|
-
let matchedTokens = 0
|
|
143
|
-
let strongMatches = 0
|
|
144
|
-
for (const queryToken of queryTokens) {
|
|
145
|
-
let best = 0
|
|
146
|
-
for (const candidateToken of document.tokens) {
|
|
147
|
-
best = Math.max(best, tokenQuality(queryToken, candidateToken))
|
|
148
|
-
if (best === 1) break
|
|
149
|
-
}
|
|
150
|
-
if (best >= 0.5) {
|
|
151
|
-
matchedTokens += 1
|
|
152
|
-
qualityTotal += best
|
|
153
|
-
if (best >= 0.68) strongMatches += 1
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (strongMatches === 0) return 0
|
|
157
|
-
const missingTokens = Math.max(0, queryTokens.length - matchedTokens)
|
|
158
|
-
const coverage = queryTokens.length > 0 ? matchedTokens / queryTokens.length : 0
|
|
159
|
-
score += qualityTotal * 120
|
|
160
|
-
score += coverage * 200
|
|
161
|
-
score += Math.max(0, matchedTokens - 1) * 50
|
|
162
|
-
score -= missingTokens * 18
|
|
163
|
-
score += purposeScore(item, action.purpose)
|
|
164
|
-
if (action.area) {
|
|
165
|
-
const area = normalizeText(action.area)
|
|
166
|
-
const itemArea = normalizeText([
|
|
167
|
-
item && item.hierarchyPath,
|
|
168
|
-
item && item.mainGroup,
|
|
169
|
-
item && item.middleGroup,
|
|
170
|
-
item && item.semantic && item.semantic.area
|
|
171
|
-
].join(' '))
|
|
172
|
-
if (!itemArea.includes(area)) return 0
|
|
173
|
-
score += 120
|
|
174
|
-
}
|
|
175
|
-
return score
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const sortScoredItems = (scored) => scored
|
|
179
|
-
.sort((left, right) => right.score - left.score || String(left.item && left.item.hierarchyPath || left.item && left.item.label || left.item && left.item.ga || '').localeCompare(String(right.item && right.item.hierarchyPath || right.item && right.item.label || right.item && right.item.ga || '')))
|
|
180
|
-
.map(entry => entry.item)
|
|
181
|
-
|
|
182
|
-
const findSearchItems = (catalog, action) => sortScoredItems(
|
|
183
|
-
catalog.map(item => ({ item, score: scoreSearchMatch(item, action) })).filter(entry => entry.score > 0)
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
const buildAreaRows = (catalog) => {
|
|
187
|
-
const areas = new Map()
|
|
188
|
-
catalog.forEach(item => {
|
|
189
|
-
const semantic = item && item.semantic && typeof item.semantic === 'object' ? item.semantic : {}
|
|
190
|
-
const candidates = [semantic.area, item && item.hierarchyPath, item && item.middleGroup, item && item.mainGroup]
|
|
191
|
-
.map(value => String(value || '').trim())
|
|
192
|
-
.filter(Boolean)
|
|
193
|
-
const area = candidates[0] || 'Unclassified'
|
|
194
|
-
const key = normalizeText(area)
|
|
195
|
-
if (!key) return
|
|
196
|
-
if (!areas.has(key)) areas.set(key, { area, count: 0, sampleLabels: [] })
|
|
197
|
-
const row = areas.get(key)
|
|
198
|
-
row.count += 1
|
|
199
|
-
const label = String(item && item.label || item && item.ga || '').trim()
|
|
200
|
-
if (label && row.sampleLabels.length < 3 && !row.sampleLabels.includes(label)) row.sampleLabels.push(label)
|
|
201
|
-
})
|
|
202
|
-
return Array.from(areas.values()).sort((left, right) => right.count - left.count || left.area.localeCompare(right.area))
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const findAreaItems = (catalog, action) => {
|
|
206
|
-
const requestedArea = normalizeText(action.area)
|
|
207
|
-
return sortScoredItems(catalog
|
|
208
|
-
.filter(item => itemPassesFilters(item, action))
|
|
209
|
-
.map(item => {
|
|
210
|
-
const semantic = item && item.semantic && typeof item.semantic === 'object' ? item.semantic : {}
|
|
211
|
-
const areaText = normalizeText([semantic.area, item && item.hierarchyPath, item && item.middleGroup, item && item.mainGroup].join(' '))
|
|
212
|
-
if (!areaText.includes(requestedArea)) return { item, score: 0 }
|
|
213
|
-
return { item, score: 400 + purposeScore(item, action.purpose) }
|
|
214
|
-
})
|
|
215
|
-
.filter(entry => entry.score > 0))
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const findRelatedItems = (catalog, action) => {
|
|
219
|
-
const requested = new Set(action.destinations.map(normalizeGa))
|
|
220
|
-
const seeds = catalog.filter(item => requested.has(normalizeGa(item && item.ga)))
|
|
221
|
-
if (!seeds.length) return []
|
|
222
|
-
const seedDocuments = seeds.map(seed => ({ seed, tokens: new Set(buildSearchDocument(seed).tokens) }))
|
|
223
|
-
const scored = catalog.map(item => {
|
|
224
|
-
if (!itemPassesFilters(item, action)) return { item, score: 0 }
|
|
225
|
-
let best = requested.has(normalizeGa(item && item.ga)) ? 1000 : 0
|
|
226
|
-
const itemTokens = buildSearchDocument(item).tokens
|
|
227
|
-
seedDocuments.forEach(({ seed, tokens: seedTokens }) => {
|
|
228
|
-
if (seed === item) return
|
|
229
|
-
const seedSemantic = seed && seed.semantic && typeof seed.semantic === 'object' ? seed.semantic : {}
|
|
230
|
-
const itemSemantic = item && item.semantic && typeof item.semantic === 'object' ? item.semantic : {}
|
|
231
|
-
let relationScore = 0
|
|
232
|
-
if (normalizeText(seed.hierarchyPath) && normalizeText(seed.hierarchyPath) === normalizeText(item.hierarchyPath)) relationScore += 260
|
|
233
|
-
if (normalizeText(seedSemantic.area) && normalizeText(seedSemantic.area) === normalizeText(itemSemantic.area)) relationScore += 180
|
|
234
|
-
if (normalizeText(seed.middleGroup) && normalizeText(seed.middleGroup) === normalizeText(item.middleGroup)) relationScore += 100
|
|
235
|
-
if (normalizeText(seed.mainGroup) && normalizeText(seed.mainGroup) === normalizeText(item.mainGroup)) relationScore += 45
|
|
236
|
-
if (normalizeText(seedSemantic.kind) && normalizeText(seedSemantic.kind) === normalizeText(itemSemantic.kind)) relationScore += 65
|
|
237
|
-
const overlap = itemTokens.filter(token => token.length >= 4 && seedTokens.has(token)).length
|
|
238
|
-
relationScore += Math.min(100, overlap * 20)
|
|
239
|
-
if (relationScore <= 0) return
|
|
240
|
-
const score = relationScore + purposeScore(item, action.purpose)
|
|
241
|
-
best = Math.max(best, score)
|
|
242
|
-
})
|
|
243
|
-
return { item, score: best }
|
|
244
|
-
}).filter(entry => entry.score > 0)
|
|
245
|
-
return sortScoredItems(scored)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const actionFingerprint = (action) => JSON.stringify({
|
|
249
|
-
operation: action.operation,
|
|
250
|
-
query: normalizeText(action.query),
|
|
251
|
-
destinations: action.destinations.map(normalizeGa).sort(),
|
|
252
|
-
area: normalizeText(action.area),
|
|
253
|
-
semanticKinds: action.semanticKinds.slice().sort(),
|
|
254
|
-
access: action.access,
|
|
255
|
-
purpose: action.purpose,
|
|
256
|
-
offset: action.offset,
|
|
257
|
-
limit: action.limit
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
const executeKnxAiCatalogActions = ({ actions, catalog, priorResults = [] } = {}) => {
|
|
261
|
-
const safeCatalog = Array.isArray(catalog) ? catalog : []
|
|
262
|
-
const normalized = normalizeKnxAiCatalogActions(actions)
|
|
263
|
-
const seen = new Set((Array.isArray(priorResults) ? priorResults : []).map(result => String(result && result.fingerprint || '')).filter(Boolean))
|
|
264
|
-
const results = []
|
|
265
|
-
normalized.forEach(action => {
|
|
266
|
-
const fingerprint = actionFingerprint(action)
|
|
267
|
-
if (seen.has(fingerprint)) return
|
|
268
|
-
seen.add(fingerprint)
|
|
269
|
-
if (action.operation === 'list_areas') {
|
|
270
|
-
const allAreas = buildAreaRows(safeCatalog.filter(item => itemPassesFilters(item, action)))
|
|
271
|
-
results.push({
|
|
272
|
-
action,
|
|
273
|
-
fingerprint,
|
|
274
|
-
ok: true,
|
|
275
|
-
totalMatches: allAreas.length,
|
|
276
|
-
truncated: allAreas.length > action.offset + action.limit,
|
|
277
|
-
areas: allAreas.slice(action.offset, action.offset + action.limit),
|
|
278
|
-
items: []
|
|
279
|
-
})
|
|
280
|
-
return
|
|
281
|
-
}
|
|
282
|
-
let matched = []
|
|
283
|
-
if (action.operation === 'get') {
|
|
284
|
-
const requested = new Set(action.destinations.map(normalizeGa))
|
|
285
|
-
matched = safeCatalog.filter(item => requested.has(normalizeGa(item && item.ga)) && itemPassesFilters(item, action))
|
|
286
|
-
} else if (action.operation === 'search') {
|
|
287
|
-
matched = findSearchItems(safeCatalog, action)
|
|
288
|
-
} else if (action.operation === 'browse_area') {
|
|
289
|
-
matched = findAreaItems(safeCatalog, action)
|
|
290
|
-
} else if (action.operation === 'related') {
|
|
291
|
-
matched = findRelatedItems(safeCatalog, action)
|
|
292
|
-
}
|
|
293
|
-
results.push({
|
|
294
|
-
action,
|
|
295
|
-
fingerprint,
|
|
296
|
-
ok: true,
|
|
297
|
-
totalMatches: matched.length,
|
|
298
|
-
truncated: matched.length > action.offset + action.limit,
|
|
299
|
-
items: matched.slice(action.offset, action.offset + action.limit),
|
|
300
|
-
areas: []
|
|
301
|
-
})
|
|
302
|
-
})
|
|
303
|
-
return results
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const collectKnxAiCatalogObjects = (results, maxItems = KNX_AI_CATALOG_MAX_ACCUMULATED_OBJECTS) => {
|
|
307
|
-
const byGa = new Map()
|
|
308
|
-
;(Array.isArray(results) ? results.slice().reverse() : []).forEach(result => {
|
|
309
|
-
;(Array.isArray(result && result.items) ? result.items : []).forEach(item => {
|
|
310
|
-
const ga = normalizeGa(item && item.ga)
|
|
311
|
-
if (!ga || byGa.has(ga) || byGa.size >= maxItems) return
|
|
312
|
-
byGa.set(ga, item)
|
|
313
|
-
})
|
|
314
|
-
})
|
|
315
|
-
return Array.from(byGa.values())
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const buildKnxAiCatalogResearchContext = (results) => {
|
|
319
|
-
const source = Array.isArray(results) ? results : []
|
|
320
|
-
if (!source.length) return ''
|
|
321
|
-
const lines = ['LOCAL ETS CATALOG RETRIEVAL RESULTS:']
|
|
322
|
-
source.forEach((result, index) => {
|
|
323
|
-
const action = result && result.action ? result.action : {}
|
|
324
|
-
const target = action.operation === 'search'
|
|
325
|
-
? ` query=${JSON.stringify(action.query)}`
|
|
326
|
-
: action.operation === 'browse_area'
|
|
327
|
-
? ` area=${JSON.stringify(action.area)}`
|
|
328
|
-
: (action.operation === 'get' || action.operation === 'related')
|
|
329
|
-
? ` destinations=${JSON.stringify(action.destinations)}`
|
|
330
|
-
: ''
|
|
331
|
-
lines.push(`[C${index + 1}] ${action.operation || 'unknown'}${target}: ${Number(result && result.totalMatches) || 0} match(es); page offset=${Math.max(0, Number(action.offset) || 0)}, limit=${Math.max(1, Number(action.limit) || 1)}${result && result.truncated ? '; more matches are available with a larger offset' : ''}.`)
|
|
332
|
-
;(Array.isArray(result && result.areas) ? result.areas : []).forEach(area => {
|
|
333
|
-
lines.push(`- area ${JSON.stringify(area.area)} | ${area.count} objects | examples ${area.sampleLabels.join(', ') || '?'}`)
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
lines.push('END LOCAL ETS CATALOG RETRIEVAL RESULTS')
|
|
337
|
-
return lines.join('\n')
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
module.exports = {
|
|
341
|
-
KNX_AI_CATALOG_MAX_ACCUMULATED_OBJECTS,
|
|
342
|
-
KNX_AI_CATALOG_MAX_ACTIONS_PER_ROUND,
|
|
343
|
-
KNX_AI_CATALOG_MAX_RESEARCH_ROUNDS,
|
|
344
|
-
KNX_AI_CATALOG_MAX_RESULTS_PER_ACTION,
|
|
345
|
-
buildKnxAiCatalogResearchContext,
|
|
346
|
-
collectKnxAiCatalogObjects,
|
|
347
|
-
executeKnxAiCatalogActions,
|
|
348
|
-
normalizeKnxAiCatalogActions
|
|
349
|
-
}
|