diff --git a/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json b/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json index deb82422c6..70e470e273 100644 --- a/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json +++ b/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 80) {\\n\\tvalue = 80;\\n} else if (value > 160) {\\n\\tvalue = 160;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 80) {\\n\\tvalue = 80;\\n} else if (value > 160) {\\n\\tvalue = 160;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/digital_speedometer.json b/application/src/main/data/json/system/widget_types/digital_speedometer.json index a3844fc560..4ac6ac7c1d 100644 --- a/application/src/main/data/json/system/widget_types/digital_speedometer.json +++ b/application/src/main/data/json/system/widget_types/digital_speedometer.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 45) {\\n\\tvalue = 45;\\n} else if (value > 130) {\\n\\tvalue = 130;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital speedometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 45) {\\n\\tvalue = 45;\\n} else if (value > 130) {\\n\\tvalue = 130;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital speedometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/digital_thermometer.json b/application/src/main/data/json/system/widget_types/digital_thermometer.json index ab75474f9c..df7d63ea1d 100644 --- a/application/src/main/data/json/system/widget_types/digital_thermometer.json +++ b/application/src/main/data/json/system/widget_types/digital_thermometer.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -60) {\\n\\tvalue = 60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"minValue\":-60,\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital thermometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -60) {\\n\\tvalue = 60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"minValue\":-60,\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital thermometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/digital_vertical_bar.json b/application/src/main/data/json/system/widget_types/digital_vertical_bar.json index aecd305300..7daf1f3b9c 100644 --- a/application/src/main/data/json/system/widget_types/digital_vertical_bar.json +++ b/application/src/main/data/json/system/widget_types/digital_vertical_bar.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"minValue\":-60,\"dashThickness\":1.2,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"minValue\":-60,\"dashThickness\":1.2,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json b/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json index b2c8caa4c3..bab8886b0b 100644 --- a/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json +++ b/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"400\",\"size\":16},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"verticalBar\",\"units\":\"%\"},\"title\":\"LCD bar gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"400\",\"size\":16},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"verticalBar\",\"units\":\"%\"},\"title\":\"LCD bar gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/lcd_gauge.json b/application/src/main/data/json/system/widget_types/lcd_gauge.json index bfe7a64a5f..175dc089f4 100644 --- a/application/src/main/data/json/system/widget_types/lcd_gauge.json +++ b/application/src/main/data/json/system/widget_types/lcd_gauge.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 180) {\\n\\tvalue = 180;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"arc\"},\"title\":\"LCD gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 180) {\\n\\tvalue = 180;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"arc\"},\"title\":\"LCD gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/neon_gauge.json b/application/src/main/data/json/system/widget_types/neon_gauge.json index 4f595392b6..a4f03169cf 100644 --- a/application/src/main/data/json/system/widget_types/neon_gauge.json +++ b/application/src/main/data/json/system/widget_types/neon_gauge.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_neon_gauge.json b/application/src/main/data/json/system/widget_types/simple_neon_gauge.json index fd84396430..ab46ed93e6 100644 --- a/application/src/main/data/json/system/widget_types/simple_neon_gauge.json +++ b/application/src/main/data/json/system/widget_types/simple_neon_gauge.json @@ -15,7 +15,9 @@ "settingsSchema": "{}", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#388e3c\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Simple neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#388e3c\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Simple neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.html index f848ad0186..84deb5ceaf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.html @@ -44,38 +44,36 @@ -
+
{{ 'widgets.gauge.donut-start-angle' | translate }}
- -
- - {{ 'widgets.gauge.min-and-max-value' | translate }} - -
-
widgets.gauge.min-value-short
- - - -
widgets.gauge.max-value-short
- - - - - - - -
+
+ + {{ 'widgets.gauge.min-and-max-value' | translate }} + +
+
widgets.gauge.min-value-short
+ + + +
widgets.gauge.max-value-short
+ + + + + + +
- +
@@ -139,7 +137,11 @@
{{ 'widgets.gauge.bar-color' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts index 6db5660765..b344e4b044 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts @@ -20,6 +20,7 @@ import { AppState } from '@core/core.state'; import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { + Datasource, datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig, @@ -29,18 +30,18 @@ import { getTimewindowConfig, setTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component'; -import { deepClone, formatValue, isUndefined } from '@core/utils'; +import { formatValue, isUndefined } from '@core/utils'; import { Component } from '@angular/core'; import { - colorLevel, + backwardCompatibilityFixedLevelColors, defaultDigitalSimpleGaugeOptions, digitalGaugeLayoutImages, digitalGaugeLayouts, digitalGaugeLayoutTranslations, DigitalGaugeSettings, - DigitalGaugeType, FixedLevelColors + DigitalGaugeType } from '@home/components/widget/lib/digital-gauge.models'; -import { AdvancedColorRange, ColorSettings, ColorType, constantColor } from '@shared/models/widget-settings.models'; +import { ColorSettings, ColorType, constantColor } from '@shared/models/widget-settings.models'; @Component({ selector: 'tb-digital-simple-gauge-basic-config', @@ -60,6 +61,15 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom return datasourcesHasOnlyComparisonAggregation(datasources); } + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + digitalGaugeType = DigitalGaugeType; digitalGaugeLayouts = digitalGaugeLayouts; @@ -89,12 +99,12 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom const settings: DigitalGaugeSettings = {...defaultDigitalSimpleGaugeOptions, ...(configData.config.settings || {})}; if (!settings.barColor) { - settings.barColor = constantColor(null); + settings.barColor = constantColor(settings.gaugeColor); if (settings.fixedLevelColors?.length) { settings.barColor.rangeList = { advancedMode: settings.useFixedLevelColor, range: null, - rangeAdvanced: deepClone(settings.fixedLevelColors) as AdvancedColorRange[] + rangeAdvanced: backwardCompatibilityFixedLevelColors(settings.fixedLevelColors) }; } if (settings.levelColors?.length) { @@ -205,8 +215,8 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom if (isDonut) { this.simpleGaugeWidgetConfigForm.get('donutStartAngle').enable(); this.simpleGaugeWidgetConfigForm.get('showMinMax').disable({emitEvent: false}); - this.simpleGaugeWidgetConfigForm.get('minValue').disable(); - this.simpleGaugeWidgetConfigForm.get('maxValue').disable(); + this.simpleGaugeWidgetConfigForm.get('minValue').enable(); + this.simpleGaugeWidgetConfigForm.get('maxValue').enable(); this.simpleGaugeWidgetConfigForm.get('minMaxFont').disable(); this.simpleGaugeWidgetConfigForm.get('minMaxColor').disable(); } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html index 71cf9faaf8..1c377029bb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html @@ -103,6 +103,8 @@ rangeAdvancedMode gradientAdvancedMode [aliasController]="aliasController" + [dataKeyCallbacks]="callbacks" + [datasource]="datasource" settingsKey="{{'widgets.battery-level.value' | translate }}">
@@ -115,6 +117,8 @@ minValue="0" maxValue="100" [aliasController]="aliasController" + [dataKeyCallbacks]="callbacks" + [datasource]="datasource" settingsKey="{{'widgets.battery-level.battery-level-color' | translate }}">
@@ -126,6 +130,8 @@ minValue="0" maxValue="100" [aliasController]="aliasController" + [dataKeyCallbacks]="callbacks" + [datasource]="datasource" settingsKey="{{'widgets.battery-level.battery-shape-color' | translate }}"> diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts index 8244fc85db..c9a5bf176a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts @@ -22,6 +22,7 @@ import { BasicWidgetConfigComponent } from '@home/components/widget/config/widge import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { DataKey, + Datasource, datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig, @@ -74,6 +75,15 @@ export class BatteryLevelBasicConfigComponent extends BasicWidgetConfigComponent return [BatteryLevelLayout.vertical_divided, BatteryLevelLayout.horizontal_divided].includes(layout); } + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts index cdd17e3739..c2c59046fe 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts @@ -17,7 +17,14 @@ import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; import { AnimationRule } from '@home/components/widget/lib/analogue-gauge.models'; import { FontSettings } from '@home/components/widget/lib/settings.models'; -import { ColorSettings } from '@shared/models/widget-settings.models'; +import { + AdvancedColorRange, + ColorSettings, + ValueSourceDataKeyType, + ValueSourceWithDataKey +} from '@shared/models/widget-settings.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { isDefinedAndNotNull } from '@core/utils'; export interface AttributeSourceProperty { valueSource: string; @@ -27,8 +34,8 @@ export interface AttributeSourceProperty { } export interface FixedLevelColors { - from?: AttributeSourceProperty | number; - to?: AttributeSourceProperty | number; + from?: ValueSourceWithDataKey | number; + to?: ValueSourceWithDataKey | number; color: string; } @@ -105,7 +112,7 @@ export interface DigitalGaugeSettings { hideValue?: boolean; hideMinMax?: boolean; showTicks?: boolean; - ticksValue?: AttributeSourceProperty[]; + ticksValue?: ValueSourceWithDataKey[]; ticks?: number[]; colorTicks?: string; tickWidth?: number; @@ -115,3 +122,41 @@ export const defaultDigitalSimpleGaugeOptions: DigitalGaugeSettings = { gaugeType: DigitalGaugeType.donut, timestampFormat: 'yyyy-MM-dd HH:mm:ss', }; + +export const backwardCompatibilityFixedLevelColors = (fixedLevelColors) => { + const valueSourceWithDataKey: AdvancedColorRange[] = []; + fixedLevelColors.forEach(fixedLevelColor => valueSourceWithDataKey.push({ + from: { + type: fixedLevelColor?.from?.valueSource === 'predefinedValue' ? ValueSourceDataKeyType.constant : ValueSourceDataKeyType.entity, + value: fixedLevelColor?.from?.value || null, + entityAlias: fixedLevelColor?.from?.entityAlias || '', + entityKey: fixedLevelColor?.from?.attribute || '', + entityKeyType: DataKeyType.attribute + }, + to: { + type: fixedLevelColor?.to?.valueSource === 'predefinedValue' ? ValueSourceDataKeyType.constant : ValueSourceDataKeyType.entity, + value: fixedLevelColor?.to?.value || null, + entityAlias: fixedLevelColor?.to?.entityAlias || '', + entityKey: fixedLevelColor?.to?.attribute || '', + entityKeyType: DataKeyType.attribute + }, + color: fixedLevelColor.color + }) ); + return valueSourceWithDataKey; +}; + +export const backwardCompatibilityTicks = (ticksValue) => { + const ticks: ValueSourceWithDataKey[] = []; + if (ticksValue?.length && isDefinedAndNotNull(ticksValue[0]?.valueSource)) { + ticksValue.forEach(tick => ticks.push({ + type: tick?.valueSource === 'predefinedValue' ? ValueSourceDataKeyType.constant : ValueSourceDataKeyType.entity, + value: tick?.value || null, + entityAlias: tick?.entityAlias || '', + entityKey: tick?.attribute || '', + entityKeyType: DataKeyType.attribute + }) ); + } else { + return ticksValue; + } + return ticks; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index ddd195a92d..1958a22cc4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -18,7 +18,6 @@ import * as CanvasGauges from 'canvas-gauges'; import { WidgetContext } from '@home/models/widget-component.models'; import { attributesGaugeType, - AttributeSourceProperty, ColorLevelSetting, DigitalGaugeSettings, FixedLevelColors @@ -32,7 +31,12 @@ import { DataKey, Datasource, DatasourceData, DatasourceType, widgetType } from import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { EMPTY, Observable } from 'rxjs'; -import { ColorType } from '@shared/models/widget-settings.models'; +import { + ColorProcessor, + ColorType, + ValueSourceDataKeyType, + ValueSourceWithDataKey +} from '@shared/models/widget-settings.models'; import GenericOptions = CanvasGauges.GenericOptions; // @dynamic @@ -212,9 +216,9 @@ export class TbCanvasDigitalGauge { throw new Error('Not valid entity aliase name ' + entityAlias); } - const datasource = datasources.find((datasourceIteration) => { - return datasourceIteration.entityAliasId === entityAliasId; - }); + const datasource = datasources.find((datasourceIteration) => + datasourceIteration.entityAliasId === entityAliasId + ); const dataKey: DataKey = { type: DataKeyType.attribute, @@ -225,9 +229,9 @@ export class TbCanvasDigitalGauge { }; if (datasource) { - const findDataKey = datasource.dataKeys.find((dataKeyIteration) => { - return dataKeyIteration.name === attribute; - }); + const findDataKey = datasource.dataKeys.find((dataKeyIteration) => + dataKeyIteration.name === attribute + ); if (findDataKey) { findDataKey.settings.push(settings); @@ -278,15 +282,16 @@ export class TbCanvasDigitalGauge { }); function setLevelColor(levelSetting, color: string) { - if (levelSetting.valueSource === 'predefinedValue' && isFinite(levelSetting.value)) { + if (levelSetting.type === ValueSourceDataKeyType.constant && isFinite(levelSetting.value)) { predefineLevelColors.push({ value: levelSetting.value, color }); - } else if (levelSetting.entityAlias && levelSetting.attribute) { + } else if (levelSetting.type === ValueSourceDataKeyType.latestKey || levelSetting.type === ValueSourceDataKeyType.entity) { try { - levelColorsDatasource = TbCanvasDigitalGauge.generateDatasource(this.ctx, levelColorsDatasource, - levelSetting.entityAlias, levelSetting.attribute, {color, index: predefineLevelColors.length}); + levelColorsDatasource = ColorProcessor.generateDatasource( + this.ctx, levelColorsDatasource, levelSetting, {color, index: predefineLevelColors.length} + ); } catch (e) { return; } @@ -315,17 +320,18 @@ export class TbCanvasDigitalGauge { return predefineLevelColors; } - settingTicksSubscribe(options: AttributeSourceProperty[]): number[] { + settingTicksSubscribe(options: ValueSourceWithDataKey[]): number[] { let ticksDatasource: Datasource[] = []; const predefineTicks: number[] = []; for (const tick of options) { - if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) { + if (tick.type === ValueSourceDataKeyType.constant && isFinite(tick.value)) { predefineTicks.push(tick.value); - } else if (tick.entityAlias && tick.attribute) { + } else { try { - ticksDatasource = TbCanvasDigitalGauge - .generateDatasource(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length); + ticksDatasource = ColorProcessor.generateDatasource( + this.ctx, ticksDatasource, tick, {index: predefineTicks.length} + ); } catch (e) { continue; } @@ -364,7 +370,7 @@ export class TbCanvasDigitalGauge { if (keyData && keyData.data && keyData.data[0]) { const attrValue = keyData.data[0][1]; if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings) { + for (const setting of keyData.dataKey.settings.dataKeySettings) { switch (typeAttributes) { case 'levelColors': this.localSettings.levelColors[setting.index] = { @@ -373,7 +379,7 @@ export class TbCanvasDigitalGauge { }; break; case 'ticks': - this.localSettings.ticks[setting] = attrValue; + this.localSettings.ticks[setting.index] = attrValue; break; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts index 9f0d69093a..429b59774b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts @@ -28,7 +28,7 @@ import { ViewEncapsulation } from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; -import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { formattedDataFormDatasourceData, formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; import { DatePipe } from '@angular/common'; import { backgroundStyle, @@ -46,9 +46,15 @@ import { BatteryLevelWidgetSettings } from '@home/components/widget/lib/indicator/battery-level-widget.models'; import { ResizeObserver } from '@juggle/resize-observer'; -import { Observable } from 'rxjs'; +import { forkJoin, Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { FormattedData } from '@shared/models/widget.models'; +import { + parseThresholdData, + TimeSeriesChartThresholdType +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { map } from 'rxjs/operators'; const verticalBatteryDimensions = { shapeAspectRatio: 64 / 113, @@ -201,6 +207,10 @@ export class BatteryLevelWidgetComponent implements OnInit, OnDestroy, AfterView this.overlayStyle = overlayStyle(this.settings.background.overlay); this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + + this.valueColor.colorUpdated.subscribe(() => this.cd.markForCheck()); + this.batteryLevelColor.colorUpdated.subscribe(() => this.cd.markForCheck()); + this.batteryShapeColor.colorUpdated.subscribe(() => this.cd.markForCheck()); } ngAfterViewInit() { @@ -218,6 +228,11 @@ export class BatteryLevelWidgetComponent implements OnInit, OnDestroy, AfterView if (this.batteryBoxResize$) { this.batteryBoxResize$.disconnect(); } + + this.valueColor.colorUpdated.unsubscribe(); + this.batteryLevelColor.colorUpdated.unsubscribe(); + this.batteryShapeColor.colorUpdated.unsubscribe(); + this.batteryLevelColor.destroy(); this.valueColor.destroy(); this.batteryShapeColor.destroy(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html index 34ac29be1d..4bd50a81c6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html @@ -20,21 +20,21 @@
widgets.color.from
- - + +
widgets.color.to
- - + +
-
- -
-
-
-
-
widgets.color.from
- - - -
widgets.color.to
- - - - - +
+ +
+
+
+
+
widgets.color.from
+ + + +
widgets.color.to
+ + + + + +
+ +
- +
+
+
+ + +
+
+ +
-
- - - -
-
- - - -
-
-
+ -
- +
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts index db5ac7c91b..e37186748d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts @@ -26,7 +26,12 @@ import { UntypedFormGroup, ValidationErrors } from '@angular/forms'; -import { AdvancedColorRange, ColorRange, ColorRangeSettings } from '@shared/models/widget-settings.models'; +import { + AdvancedColorRange, + ColorRange, + ColorRangeSettings, + ValueSourceDataKeyType +} from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -34,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isUndefined } from '@core/utils'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { IAliasController } from '@core/api/widget-api.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; export function advancedRangeValidator(control: AbstractControl): ValidationErrors | null { const range: AdvancedColorRange = control.value; @@ -71,6 +78,12 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On @Input() aliasController: IAliasController; + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + @Input() @coerceBoolean() advancedMode = false; @@ -184,10 +197,10 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On public addAdvancedRange() { const advancedRange: AdvancedColorRange = { from: { - valueSource: 'predefinedValue' + type: ValueSourceDataKeyType.constant }, to: { - valueSource: 'predefinedValue' + type: ValueSourceDataKeyType.constant }, color: null }; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html index 2b1db278bf..f7df367657 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html @@ -40,6 +40,8 @@ [maxValue]="maxValue" [advancedMode]="gradientAdvancedMode" [aliasController]="aliasController" + [dataKeyCallbacks]="dataKeyCallbacks" + [datasource]="datasource" [popover]="popover">
@@ -49,6 +51,8 @@ panelTitle="{{ 'widgets.color.value-range' | translate }}" [advancedMode]="rangeAdvancedMode" [aliasController]="aliasController" + [dataKeyCallbacks]="dataKeyCallbacks" + [datasource]="datasource" [popover]="popover">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts index d873b4ba7b..73b6cacf2e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts @@ -16,7 +16,12 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; -import { ColorSettings, ColorType, colorTypeTranslations } from '@shared/models/widget-settings.models'; +import { + ColorSettings, + ColorType, + colorTypeTranslations, + defaultGradient +} from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; @@ -26,6 +31,8 @@ import { WidgetService } from '@core/http/widget.service'; import { ColorSettingsComponent } from '@home/components/widget/lib/settings/common/color-settings.component'; import { IAliasController } from '@core/api/widget-api.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; @Component({ selector: 'tb-color-settings-panel', @@ -51,6 +58,12 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit @Input() aliasController: IAliasController; + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + @Input() @coerceBoolean() rangeAdvancedMode = false; @@ -86,7 +99,7 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit { type: [this.colorSettings?.type || ColorType.constant, []], color: [this.colorSettings?.color, []], - gradient: [this.colorSettings?.gradient, []], + gradient: [this.colorSettings?.gradient || defaultGradient(), []], rangeList: [this.colorSettings?.rangeList, []], colorFunction: [this.colorSettings?.colorFunction, []] } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts index 1e45481389..7594719cff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts @@ -34,6 +34,8 @@ import { import { IAliasController } from '@core/api/widget-api.models'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; @Injectable() export class ColorSettingsComponentService { @@ -85,6 +87,12 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD @Input() aliasController: IAliasController; + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + @Input() @coerceBoolean() rangeAdvancedMode = false; @@ -155,6 +163,8 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD colorSettings: this.modelValue, settingsComponents: this.colorSettingsComponentService.getOtherColorSettingsComponents(this), aliasController: this.aliasController, + dataKeyCallbacks: this.dataKeyCallbacks, + datasource: this.datasource, rangeAdvancedMode: this.rangeAdvancedMode, gradientAdvancedMode: this.gradientAdvancedMode, minValue: this.minValue, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss index f8564a46ac..43dbc042cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss @@ -28,8 +28,8 @@ padding-right: 0; .mat-mdc-form-field-infix { - padding-top: 0; - padding-bottom: 6px; + padding-top: 0 !important; + padding-bottom: 6px !important; .mdc-evolution-chip-set .mdc-evolution-chip { margin: 0; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html index 5fe3d9c025..2296aaf4e4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html @@ -40,7 +40,7 @@
widgets.color.gradient-start
-
+
widgets.color.start-value
@@ -98,7 +98,7 @@
widgets.color.gradient-end
-
+
widgets.color.end-value
@@ -118,13 +118,13 @@ widgets.color.gradient-start widgets.color.gradient-start-min
-
- - +
+ + @@ -146,12 +146,12 @@ widgets.color.gradient-color-min
- - + + @@ -190,13 +190,13 @@ widgets.color.gradient-end widgets.color.gradient-end-min
-
- - +
+ + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts index ea17f19637..35e51f6c98 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts @@ -24,7 +24,7 @@ import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { AdvancedGradient, ColorGradientSettings } from '@shared/models/widget-settings.models'; +import { AdvancedGradient, ColorGradientSettings, ValueSourceDataKeyType } from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -33,6 +33,8 @@ import { IAliasController } from '@core/api/widget-api.models'; import { DomSanitizer } from '@angular/platform-browser'; import { coerceBoolean } from '@shared/decorators/coercion'; import { isDefinedAndNotNull } from '@core/utils'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; @Component({ selector: 'tb-gradient', @@ -62,6 +64,12 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro @Input() aliasController: IAliasController; + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + @Input() minValue: string; @@ -84,6 +92,7 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro private sanitizer: DomSanitizer) {} ngOnInit(): void { + console.log(this.minValue, this.maxValue); this.gradientFormGroup = this.fb.group({ advancedMode: [false], gradient: this.fb.group({ @@ -93,12 +102,12 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro }), gradientAdvanced: this.fb.group({ start: this.fb.group({ - source: [], + source: [{type: ValueSourceDataKeyType.constant}], color: ['rgba(0, 255, 0, 1)'] }), gradientList: this.fb.array([]), end: this.fb.group({ - source: [], + source: [{type: ValueSourceDataKeyType.constant}], color: ['rgba(255, 0, 0, 1)'] }) }) @@ -147,26 +156,27 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro get generatePointers() { if (this.gradientFormGroup.get('advancedMode').value) { - const point = 100 / (this.advancedGradientListFormArray.value.length + 1); + const shift = 100 / (this.advancedGradientListFormArray.value.length + 1); return `
` + - this.advancedGradientListFormArray.value.map((v, i) => this.pointer(point * (i + 1), true)).join('') + + this.advancedGradientListFormArray.value.map((v, i) => this.pointer(shift * (i + 1), i+1, null, true)).join('') + `
`; } else { - const point = 100 / (this.gradientListFormArray.value.length + 1); + const point = (+this.maxValue - +this.minValue) / (this.gradientListFormArray.value.length + 1); + const shift = 100 / (this.gradientListFormArray.value.length + 1); const min = isDefinedAndNotNull(this.minValue) ? this.minValue : 0; const max = isDefinedAndNotNull(this.maxValue) ? this.maxValue : 100; return `
${min}
` + - this.gradientListFormArray.value.map((v, i) => this.pointer(point * (i + 1))).join('') + + this.gradientListFormArray.value.map((v, i) => this.pointer(shift * (i + 1), i+1, point)).join('') + `
${max}
`; } } - pointer(value?: number, advanced = false) { + pointer(shift: number, index?: number, value?: number, advanced = false) { if (advanced) { - return `
`; + return `
`; } else { - return `
` + - `
${Math.floor((+this.maxValue*value)/100)}
`; + return `
` + + `
${Math.floor(+this.minValue + (value * index))}
`; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.html new file mode 100644 index 0000000000..be6faaefaf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.html @@ -0,0 +1,77 @@ + +
+
+ + + + {{ valueSourceDataKeyTypeTranslation.get(type) | translate }} + + + + + +
+
+ + + + warning + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.scss new file mode 100644 index 0000000000..5d769d867a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.scss @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +:host { + .tb-value-source { + display: flex; + gap: 12px; + } + .tb-source-field { + display: flex; + flex: 1; + gap: 12px; + .tb-type-field, .tb-entity-alias-field { + flex: 1; + } + } + .tb-key-value-field, .tb-constant-field { + display: flex; + flex: 1; + } + ::ng-deep { + .tb-data-key-input { + flex: 1; + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts new file mode 100644 index 0000000000..ed5f8eadf5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts @@ -0,0 +1,191 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { + ValueSourceDataKeyType, + ValueSourceDataKeyTypes, + ValueSourceDataKeyTypeTranslation +} from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-value-source-data-key', + templateUrl: './value-source-data-key.component.html', + styleUrls: ['value-source-data-key.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ValueSourceDataKeyComponent), + multi: true + } + ] +}) +export class ValueSourceDataKeyComponent extends PageComponent implements OnInit, ControlValueAccessor { + + dataKeyType = DataKeyType; + datasourceType = DatasourceType; + + valueSourceDataKeyType = ValueSourceDataKeyType; + valueSourceDataKeyTypes = ValueSourceDataKeyTypes; + valueSourceDataKeyTypeTranslation = ValueSourceDataKeyTypeTranslation; + + @Input() + disabled: boolean; + + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + valueSourceFormGroup: UntypedFormGroup; + + modelValue; + + latestKeyFormControl: UntypedFormControl; + entityKeyFormControl: UntypedFormControl; + + private propagateChange = (_val: any) => {}; + + constructor(protected store: Store, + private fb: UntypedFormBuilder, + private cd: ChangeDetectorRef) { + super(store); + } + + ngOnInit(): void { + this.valueSourceFormGroup = this.fb.group({ + type: [ValueSourceDataKeyType.constant, []], + value: [null, [Validators.required]], + entityAlias: [null, [Validators.required]] + }); + this.latestKeyFormControl = this.fb.control(null, [Validators.required]); + this.entityKeyFormControl = this.fb.control(null, [Validators.required]); + this.valueSourceFormGroup.valueChanges.subscribe( + () => this.updateModel() + ); + this.latestKeyFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + this.entityKeyFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + this.valueSourceFormGroup.get('type').valueChanges.subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.valueSourceFormGroup.disable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else { + this.valueSourceFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value): void { + this.modelValue = value; + this.valueSourceFormGroup.patchValue( + { + type: value.type, + value: value.value, + entityAlias: value.entityAlias + }, {emitEvent: false} + ); + if (value.type === ValueSourceDataKeyType.latestKey) { + this.latestKeyFormControl.patchValue({ + type: value.latestKeyType, + name: value.latestKey + }, {emitEvent: false}); + } else if (value.type === ValueSourceDataKeyType.entity) { + this.entityKeyFormControl.patchValue({ + type: value.entityKeyType, + name: value.entityKey + }, {emitEvent: false}); + } + + this.updateValidators(); + this.cd.markForCheck(); + } + + private updateModel() { + const value = this.valueSourceFormGroup.value; + this.modelValue.type = value.type; + this.modelValue.value = value.value; + this.modelValue.entityAlias = value.entityAlias; + + if (value.type === ValueSourceDataKeyType.latestKey) { + const latestKey: DataKey = this.latestKeyFormControl.value; + this.modelValue.latestKey = latestKey?.name; + this.modelValue.latestKeyType = (latestKey?.type as any); + } else if (value.type === ValueSourceDataKeyType.entity) { + const entityKey: DataKey = this.entityKeyFormControl.value; + this.modelValue.entityKey = entityKey?.name; + this.modelValue.entityKeyType = (entityKey?.type as any); + } + this.propagateChange(this.modelValue); + } + + private updateValidators(): void { + const type: ValueSourceDataKeyType = this.valueSourceFormGroup.get('type').value; + if (type === ValueSourceDataKeyType.constant) { + this.valueSourceFormGroup.get('value').enable({emitEvent: false}); + this.valueSourceFormGroup.get('entityAlias').disable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else if (type === ValueSourceDataKeyType.latestKey) { + this.valueSourceFormGroup.get('value').disable({emitEvent: false}); + this.valueSourceFormGroup.get('entityAlias').disable({emitEvent: false}); + this.latestKeyFormControl.enable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else if (type === ValueSourceDataKeyType.entity) { + this.valueSourceFormGroup.get('value').disable({emitEvent: false}); + this.valueSourceFormGroup.get('entityAlias').enable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.enable({emitEvent: false}); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 75e4874b23..781042a4cf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -152,6 +152,9 @@ import { import { ChartBarSettingsComponent } from '@home/components/widget/lib/settings/common/chart/chart-bar-settings.component'; import { AdvancedRangeComponent } from '@home/components/widget/lib/settings/common/advanced-range.component'; import { GradientComponent } from '@home/components/widget/lib/settings/common/gradient.component'; +import { + ValueSourceDataKeyComponent +} from '@home/components/widget/lib/settings/common/value-source-data-key.component'; @NgModule({ declarations: [ @@ -170,6 +173,7 @@ import { GradientComponent } from '@home/components/widget/lib/settings/common/g BackgroundSettingsComponent, BackgroundSettingsPanelComponent, ValueSourceComponent, + ValueSourceDataKeyComponent, LegendConfigComponent, WidgetFontComponent, CountWidgetSettingsComponent, @@ -233,6 +237,7 @@ import { GradientComponent } from '@home/components/widget/lib/settings/common/g BackgroundSettingsComponent, BackgroundSettingsPanelComponent, ValueSourceComponent, + ValueSourceDataKeyComponent, LegendConfigComponent, WidgetFontComponent, CountWidgetSettingsComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html index 6c264d7a18..86cd0ff825 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html @@ -32,38 +32,36 @@ -
+
{{ 'widgets.gauge.donut-start-angle' | translate }}
- -
- - {{ 'widgets.gauge.min-and-max-value' | translate }} - -
-
widgets.gauge.min-value-short
- - - -
widgets.gauge.max-value-short
- - - - - - - -
+
+ + {{ 'widgets.gauge.min-and-max-value' | translate }} + +
+
widgets.gauge.min-value-short
+ + + +
widgets.gauge.max-value-short
+ + + + + + +
- +
@@ -185,6 +183,8 @@ [minValue]="digitalGaugeWidgetSettingsForm.get('minValue').value" [maxValue]="digitalGaugeWidgetSettingsForm.get('maxValue').value" [aliasController]="aliasController" + [dataKeyCallbacks]="dataKeyCallbacks" + [datasource]="datasource" settingsKey="{{'widgets.battery-level.value' | translate }}">
@@ -225,6 +225,8 @@ let $index = index; last as isLast;" fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts index ac299cee5c..579e01b830 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts @@ -14,22 +14,30 @@ /// limitations under the License. /// -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { Datasource, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { Component } from '@angular/core'; import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; -import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; import { + backwardCompatibilityFixedLevelColors, + backwardCompatibilityTicks, digitalGaugeLayoutImages, digitalGaugeLayouts, digitalGaugeLayoutTranslations, DigitalGaugeType } from '@home/components/widget/lib/digital-gauge.models'; -import { deepClone, formatValue } from '@core/utils'; -import { ColorSettings, ColorType, constantColor, simpleDateFormat } from '@shared/models/widget-settings.models'; +import { formatValue } from '@core/utils'; +import { + ColorSettings, + ColorType, + constantColor, + simpleDateFormat, + ValueSourceDataKeyType, + ValueSourceWithDataKey +} from '@shared/models/widget-settings.models'; @Component({ selector: 'tb-digital-gauge-widget-settings', @@ -49,6 +57,15 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent valuePreviewFn = this._valuePreviewFn.bind(this, true); previewFn = this._valuePreviewFn.bind(this, false); + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + constructor(protected store: Store, protected fb: UntypedFormBuilder) { super(store); @@ -120,16 +137,14 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } protected onSettingsSet(settings: WidgetSettings) { - - if (!settings.barColor) { - settings.barColor = constantColor(null); + settings.barColor = constantColor(settings.gaugeColor); if (settings.fixedLevelColors.length) { settings.barColor.rangeList = { advancedMode: settings.useFixedLevelColor, range: null, - rangeAdvanced: deepClone(settings.fixedLevelColors) + rangeAdvanced: backwardCompatibilityFixedLevelColors(settings.fixedLevelColors) }; } if (settings.levelColors.length) { @@ -184,7 +199,7 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent showTicks: [settings.showTicks, []], tickWidth: [settings.tickWidth, [Validators.min(0)]], colorTicks: [settings.colorTicks, []], - ticksValue: this.prepareTicksValueFormArray(settings.ticksValue), + ticksValue: this.prepareTicksValueFormArray(backwardCompatibilityTicks(settings.ticksValue)), animation: [settings.animation, []], animationDuration: [settings.animationDuration, [Validators.min(0)]], @@ -229,11 +244,32 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent const showTicks: boolean = this.digitalGaugeWidgetSettingsForm.get('showTicks').value; const animation: boolean = this.digitalGaugeWidgetSettingsForm.get('animation').value; + if (gaugeType === 'donut') { this.digitalGaugeWidgetSettingsForm.get('donutStartAngle').enable(); + + this.digitalGaugeWidgetSettingsForm.get('showMinMax').disable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('minValue').enable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('maxValue').enable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('minMaxFont').disable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').disable({emitEvent: false}); } else { this.digitalGaugeWidgetSettingsForm.get('donutStartAngle').disable(); + + this.digitalGaugeWidgetSettingsForm.get('showMinMax').enable({emitEvent: false}); + if (showMinMax) { + this.digitalGaugeWidgetSettingsForm.get('minValue').enable(); + this.digitalGaugeWidgetSettingsForm.get('maxValue').enable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxFont').enable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').enable(); + } else { + this.digitalGaugeWidgetSettingsForm.get('minValue').disable(); + this.digitalGaugeWidgetSettingsForm.get('maxValue').disable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxFont').disable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').disable(); + } } + if (showTitle) { this.digitalGaugeWidgetSettingsForm.get('title').enable(); this.digitalGaugeWidgetSettingsForm.get('titleFont').enable(); @@ -267,17 +303,6 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent this.digitalGaugeWidgetSettingsForm.get('valueFont').disable(); this.digitalGaugeWidgetSettingsForm.get('valueColor').disable(); } - if (showMinMax) { - this.digitalGaugeWidgetSettingsForm.get('minValue').enable(); - this.digitalGaugeWidgetSettingsForm.get('maxValue').enable(); - this.digitalGaugeWidgetSettingsForm.get('minMaxFont').enable(); - this.digitalGaugeWidgetSettingsForm.get('minMaxColor').enable(); - } else { - this.digitalGaugeWidgetSettingsForm.get('minValue').disable(); - this.digitalGaugeWidgetSettingsForm.get('maxValue').disable(); - this.digitalGaugeWidgetSettingsForm.get('minMaxFont').disable(); - this.digitalGaugeWidgetSettingsForm.get('minMaxColor').disable(); - } if (showTicks) { this.digitalGaugeWidgetSettingsForm.get('tickWidth').enable(); this.digitalGaugeWidgetSettingsForm.get('colorTicks').enable(); @@ -323,7 +348,7 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent settingsForm.setControl('ticksValue', this.prepareTicksValueFormArray(settings.ticksValue), {emitEvent: false}); } - private prepareTicksValueFormArray(ticksValue: ValueSourceProperty[] | undefined): UntypedFormArray { + private prepareTicksValueFormArray(ticksValue: ValueSourceWithDataKey[] | undefined): UntypedFormArray { const ticksValueControls: Array = []; if (ticksValue) { ticksValue.forEach((tickValue) => { @@ -346,8 +371,8 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } public addTickValue() { - const tickValue: ValueSourceProperty = { - valueSource: 'predefinedValue' + const tickValue: ValueSourceWithDataKey = { + type: ValueSourceDataKeyType.constant }; const tickValuesArray = this.digitalGaugeWidgetSettingsForm.get('ticksValue') as UntypedFormArray; const tickValueControl = this.fb.control(tickValue, []); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html index 1947ba701e..7155ad8d5e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html @@ -17,7 +17,12 @@ -->
- + +
@@ -64,6 +66,8 @@ minValue="0" maxValue="100" [aliasController]="aliasController" + [dataKeyCallbacks]="dataKeyCallbacks" + [datasource]="datasource" settingsKey="{{'widgets.battery-level.battery-level-color' | translate }}">
@@ -75,6 +79,8 @@ minValue="0" maxValue="100" [aliasController]="aliasController" + [dataKeyCallbacks]="dataKeyCallbacks" + [datasource]="datasource" settingsKey="{{'widgets.battery-level.battery-shape-color' | translate }}">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts index fb0e2b540c..2b27a40eff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component, Injector } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { Datasource, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -48,6 +48,15 @@ export class BatteryLevelWidgetSettingsComponent extends WidgetSettingsComponent return [BatteryLevelLayout.vertical_divided, BatteryLevelLayout.horizontal_divided].includes(layout); } + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + constructor(protected store: Store, private $injector: Injector, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 94c60465d4..917ed24feb 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -14,15 +14,7 @@ /// limitations under the License. /// -import { - isDefinedAndNotNull, - isNumber, - isNumeric, - isString, - isUndefinedOrNull, - mergeDeep, - parseFunction -} from '@core/utils'; +import { isDefinedAndNotNull, isNumber, isNumeric, isUndefinedOrNull, mergeDeep, parseFunction } from '@core/utils'; import { DataEntry, DataKey, @@ -30,9 +22,10 @@ import { DatasourceData, DatasourceType, TargetDevice, - TargetDeviceType, widgetType + TargetDeviceType, + widgetType } from '@shared/models/widget.models'; -import { ChangeDetectorRef, Injector } from '@angular/core'; +import { ChangeDetectorRef, EventEmitter, Injector } from '@angular/core'; import { DatePipe } from '@angular/common'; import { DateAgoPipe } from '@shared/pipe/date-ago.pipe'; import { TranslateService } from '@ngx-translate/core'; @@ -44,17 +37,10 @@ import { map } from 'rxjs/operators'; import { DomSanitizer } from '@angular/platform-browser'; import { AVG_MONTH, DAY, HOUR, Interval, IntervalMath, MINUTE, SECOND, YEAR } from '@shared/models/time/time.models'; import moment from 'moment'; -import { - attributesGaugeType, - ColorLevelSetting, - FixedLevelColors -} from '@home/components/widget/lib/digital-gauge.models'; import tinycolor from 'tinycolor2'; -import { DigitalGaugeColorRange } from '@home/components/widget/lib/canvas-digital-gauge'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; export type ComponentStyle = {[klass: string]: any}; @@ -120,9 +106,35 @@ export interface ColorRange { color: string; } +export enum ValueSourceDataKeyType { + constant = 'constant', + latestKey = 'latestKey', + entity = 'entity' +} + +export const ValueSourceDataKeyTypes = Object.keys(ValueSourceDataKeyType) as ValueSourceDataKeyType[]; + +export const ValueSourceDataKeyTypeTranslation = new Map( + [ + [ValueSourceDataKeyType.constant, 'widgets.value-source.type-constant'], + [ValueSourceDataKeyType.latestKey, 'widgets.value-source.type-latest-key'], + [ValueSourceDataKeyType.entity, 'widgets.value-source.type-entity'] + ] +); + +export interface ValueSourceWithDataKey { + type: ValueSourceDataKeyType; + value?: number; + latestKeyType?: DataKeyType.attribute | DataKeyType.timeseries; + latestKey?: string; + entityKeyType?: DataKeyType.attribute | DataKeyType.timeseries; + entityAlias?: string; + entityKey?: string; +} + export interface AdvancedColorRange { - from?: ValueSourceProperty; - to?: ValueSourceProperty; + from?: ValueSourceWithDataKey; + to?: ValueSourceWithDataKey; color: string; } @@ -214,7 +226,7 @@ export interface ColorRangeSettings { } export interface AdvancedGradient { - source: ValueSourceProperty; + source: ValueSourceWithDataKey; color: string; } @@ -224,6 +236,11 @@ export interface ColorGradientSettings { gradientAdvanced?: Array; } +export interface DataKeySettings { + color?: string; + index: number; +} + export interface TimewindowStyle { showIcon: boolean; icon: string; @@ -248,6 +265,21 @@ export const constantColor = (color: string): ColorSettings => ({ colorFunction: defaultColorFunction }); +export const defaultGradient = (): ColorGradientSettings => ({ + advancedMode: false, + gradient: ['rgba(0, 255, 0, 1)', 'rgba(255, 0, 0, 1)'], + gradientAdvanced: [ + { + source: {type: ValueSourceDataKeyType.constant}, + color: 'rgba(0, 255, 0, 1)' + }, + { + source: {type: ValueSourceDataKeyType.constant}, + color: 'rgba(255, 0, 0, 1)' + } + ] +}); + export const defaultColorFunction = 'var temperature = value;\n' + 'if (typeof temperature !== undefined) {\n' + ' var percent = (temperature + 60)/120 * 100;\n' + @@ -293,7 +325,7 @@ type ValueColorFunction = (value: any) => string; export abstract class ColorProcessor { - static fromSettings(color: ColorSettings, ctx?: WidgetContext, cd?: ChangeDetectorRef): ColorProcessor { + static fromSettings(color: ColorSettings, ctx?: WidgetContext, cd?: ChangeDetectorRef, maxValue = 100): ColorProcessor { const settings = color || constantColor(null); switch (settings.type) { case ColorType.constant: @@ -303,7 +335,7 @@ export abstract class ColorProcessor { case ColorType.function: return new FunctionColorProcessor(settings); case ColorType.gradient: - return new GradientColorProcessor(settings, ctx, cd); + return new GradientColorProcessor(settings, ctx, cd, maxValue); default: return new ConstantColorProcessor(settings); } @@ -311,6 +343,8 @@ export abstract class ColorProcessor { color: string; + colorUpdated: EventEmitter = new EventEmitter(); + protected constructor(protected settings: ColorSettings) { this.color = settings.color; } @@ -318,42 +352,70 @@ export abstract class ColorProcessor { abstract update(value: any): void; abstract destroy(): void; - generateDatasource(ctx: WidgetContext, datasources: Datasource[], entityAlias: string, - attribute: string, settings: any): Datasource[]{ - const entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); - if (!entityAliasId) { - throw new Error('Not valid entity aliase name ' + entityAlias); + static generateDatasource( + ctx: WidgetContext, + datasources: Datasource[], + valueSource: ValueSourceWithDataKey, + settings: DataKeySettings): Datasource[]{ + if (valueSource.type === ValueSourceDataKeyType.latestKey) { + if (ctx.datasources.length) { + if (isDefinedAndNotNull(ctx.datasources[0].aliasName)) { + datasources = this.parseDatasource(ctx, valueSource, ctx.datasources[0].aliasName, settings, datasources, true); + } else { + datasources = this.parseDatasource(ctx, valueSource, '', settings, datasources, true); + } + } + } else if (valueSource.type === ValueSourceDataKeyType.entity) { + datasources = this.parseDatasource(ctx, valueSource, valueSource.entityAlias, settings, datasources, false); } + return datasources; + } - const datasource = datasources.find((datasourceIteration) => datasourceIteration.entityAliasId === entityAliasId); - - const dataKey: DataKey = { - type: DataKeyType.attribute, - name: attribute, - label: attribute, - settings: [settings], - _hash: Math.random() + static parseDatasource(ctx: WidgetContext, + valueSource: ValueSourceWithDataKey, + entityAlias: string, settings: DataKeySettings, + datasources: Datasource[], + isLatest: boolean) { + let dataKey: DataKey = null; + const dataKeySettings = [settings]; + const entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); + let datasource = datasources.find(d => + entityAlias ? (d.entityAliasId === entityAliasId) : (d.deviceId === ctx.datasources[0].deviceId) + ); + dataKey = { + type: isLatest ? valueSource.latestKeyType : valueSource.entityKeyType, + name: isLatest ? valueSource.latestKey : valueSource.entityKey, + label: isLatest ? valueSource.latestKey : valueSource.entityKey, + settings: {dataKeySettings} }; - if (datasource) { - const findDataKey = datasource.dataKeys.find((dataKeyIteration) => dataKeyIteration.name === attribute); - + const findDataKey = datasource.dataKeys.find(dataKeyIteration => + dataKeyIteration.name === (isLatest ? valueSource.latestKey : valueSource.entityKey)); if (findDataKey) { - findDataKey.settings.push(settings); + findDataKey.settings.dataKeySettings.push(settings); } else { datasource.dataKeys.push(dataKey); } } else { - const datasourceAttribute: Datasource = { - type: DatasourceType.entity, - name: entityAlias, - aliasName: entityAlias, - entityAliasId, - dataKeys: [dataKey] - }; - datasources.push(datasourceAttribute); + if (entityAlias) { + datasource = { + type: DatasourceType.entity, + name: entityAlias, + aliasName: entityAlias, + entityAliasId, + dataKeys: [ dataKey ] + }; + } else { + datasource = { + type: DatasourceType.device, + name: ctx.datasources[0].name, + deviceName: ctx.datasources[0].name, + deviceId: ctx.datasources[0].deviceId, + dataKeys: [ dataKey ] + }; + } + datasources.push(datasource); } - return datasources; } @@ -384,13 +446,13 @@ class RangeColorProcessor extends ColorProcessor { let advancedRangeDatasource: Datasource[] = []; let index = 0; - function setRangeColor(rangeSetting, color: string) { - if (rangeSetting.valueSource === 'predefinedValue' && isFinite(rangeSetting.value)) { + function setRangeColor(rangeSetting: ValueSourceWithDataKey, color: string) { + if (rangeSetting.type === ValueSourceDataKeyType.constant && isFinite(rangeSetting.value)) { index++; - } else if (rangeSetting.entityAlias && rangeSetting.attribute) { + } else { try { advancedRangeDatasource = this.generateDatasource(this.ctx, advancedRangeDatasource, - rangeSetting.entityAlias, rangeSetting.attribute, {color, index}); + rangeSetting, {color, index}); } catch (e) { return; } @@ -413,7 +475,7 @@ class RangeColorProcessor extends ColorProcessor { }); } else { this.color = this.computeFromRange(this.progress); - this.cd.markForCheck(); + this.colorUpdated.emit(); } } @@ -430,7 +492,7 @@ class RangeColorProcessor extends ColorProcessor { onDataUpdated: (subscription) => { this.updateAttribute(subscription.data); this.color = this.computeFromRange(this.progress); - this.cd.markForCheck(); + this.colorUpdated.emit(); } } }; @@ -443,7 +505,7 @@ class RangeColorProcessor extends ColorProcessor { if (keyData && keyData.data && keyData.data[0]) { const attrValue = keyData.data[0][1]; if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings) { + for (const setting of keyData.dataKey.settings.dataKeySettings) { const index = Math.floor(setting.index/2); if (index === setting.index/2) { this.settings.rangeList.rangeAdvanced[index].from.value = attrValue; @@ -535,12 +597,13 @@ class GradientColorProcessor extends ColorProcessor { constructor(protected settings: ColorSettings, protected ctx: WidgetContext, - protected cd: ChangeDetectorRef) { + protected cd: ChangeDetectorRef, + protected maxValue: number) { super(settings); } update(value: any): void { - this.progress = value / 100; + this.progress = value / this.maxValue; if(isDefinedAndNotNull(this.settings.gradient.advancedMode) && this.settings.gradient.advancedMode) { this.advancedRangeSubscribe(this.settings.gradient.gradientAdvanced); } else { @@ -563,7 +626,7 @@ class GradientColorProcessor extends ColorProcessor { if (levelColor !== null) { const tColor = tinycolor(levelColor); colorsRange.push({ - pct: this.settings.gradient.advancedMode ? levelColors[i].source.value / 100 : inc * i, + pct: this.settings.gradient.advancedMode ? levelColors[i].source.value / this.maxValue : inc * i, color: tColor.toRgb(), alpha: tColor.getAlpha(), rgbString: tColor.toRgbString() @@ -599,13 +662,13 @@ class GradientColorProcessor extends ColorProcessor { let advancedGradientDatasource: Datasource[] = []; let index = 0; - function setGradientColor(gradientSetting: ValueSourceProperty) { - if (gradientSetting.valueSource === 'predefinedValue' && isFinite(gradientSetting.value)) { + function setGradientColor(gradientSetting: ValueSourceWithDataKey) { + if (gradientSetting.type === ValueSourceDataKeyType.constant && isFinite(gradientSetting.value)) { index++; - } else if (gradientSetting.entityAlias && gradientSetting.attribute) { + } else { try { advancedGradientDatasource = this.generateDatasource(this.ctx, advancedGradientDatasource, - gradientSetting.entityAlias, gradientSetting.attribute, {index}); + gradientSetting, {index}); } catch (e) { return; } @@ -623,7 +686,7 @@ class GradientColorProcessor extends ColorProcessor { }); } else { this.color = this.getProgressColor(this.progress, this.settings.gradient.gradientAdvanced); - this.cd.markForCheck(); + this.colorUpdated.emit(); } } @@ -640,7 +703,7 @@ class GradientColorProcessor extends ColorProcessor { onDataUpdated: (subscription) => { this.updateAttribute(subscription.data); this.color = this.getProgressColor(this.progress, this.settings.gradient.gradientAdvanced); - this.cd.markForCheck(); + this.colorUpdated.emit(); } } }; @@ -653,7 +716,7 @@ class GradientColorProcessor extends ColorProcessor { if (keyData && keyData.data && keyData.data[0]) { const attrValue = keyData.data[0][1]; if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings) { + for (const setting of keyData.dataKey.settings.dataKeySettings) { this.settings.gradient.gradientAdvanced[setting.index].source.value = attrValue; } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 431a3de86c..1816c0ef7c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -7019,8 +7019,14 @@ "predefined-value": "Constant", "entity-attribute": "Entity attribute", "value": "Value", + "value-required": "Value is required.", + "key-required": "Key is required.", + "entity-key-required": "Entity key is required.", "source-entity-alias": "Source entity alias", - "source-entity-attribute": "Source entity attribute" + "source-entity-attribute": "Source entity attribute", + "type-constant": "Constant", + "type-latest-key": "Key", + "type-entity": "Entity" }, "rpc-state": { "initial-state": "Initial state",