Browse Source

UI: data query widgets improvementas

pull/3053/head
Igor Kulikov 6 years ago
parent
commit
ad47902835
  1. 104
      application/src/main/data/json/demo/dashboards/thermostats.json
  2. 56
      application/src/main/data/json/system/widget_bundles/analogue_gauges.json
  3. 96
      application/src/main/data/json/system/widget_bundles/cards.json
  4. 146
      application/src/main/data/json/system/widget_bundles/digital_gauges.json
  5. 4
      application/src/main/data/json/system/widget_bundles/gateway_widgets.json
  6. 364
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  7. 112
      application/src/main/data/json/system/widget_bundles/maps.json
  8. 7
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java
  9. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java
  10. 286
      ui-ngx/src/app/core/api/alias-controller.ts
  11. 6
      ui-ngx/src/app/core/api/data-aggregator.ts
  12. 195
      ui-ngx/src/app/core/api/entity-data-subscription.ts
  13. 21
      ui-ngx/src/app/core/api/entity-data.service.ts
  14. 2
      ui-ngx/src/app/core/api/widget-api.models.ts
  15. 62
      ui-ngx/src/app/core/api/widget-subscription.ts
  16. 2
      ui-ngx/src/app/core/http/alarm.service.ts
  17. 238
      ui-ngx/src/app/core/http/entity.service.ts
  18. 2
      ui-ngx/src/app/core/ws/telemetry-websocket.service.ts
  19. 43
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.html
  20. 156
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts
  21. 11
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.html
  22. 7
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.ts
  23. 4
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.ts
  24. 3
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  25. 5
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html
  26. 69
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  27. 42
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
  28. 3
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
  29. 72
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  30. 17
      ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts
  31. 7
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
  32. 3
      ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
  33. 6
      ui-ngx/src/app/modules/home/components/widget/widget.component.html
  34. 12
      ui-ngx/src/app/modules/home/components/widget/widget.component.scss
  35. 38
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  36. 48
      ui-ngx/src/app/shared/models/query/query.models.ts
  37. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

104
application/src/main/data/json/demo/dashboards/thermostats.json

@ -50,6 +50,8 @@
"datasources": [ "datasources": [
{ {
"type": "entity", "type": "entity",
"name": null,
"entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e",
"dataKeys": [ "dataKeys": [
{ {
"name": "active", "name": "active",
@ -92,9 +94,36 @@
"_hash": 0.5726727868178358, "_hash": 0.5726727868178358,
"units": "%", "units": "%",
"decimals": 0 "decimals": 0
},
{
"name": "latitude",
"type": "attribute",
"label": "latitude",
"color": "#ffc107",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": ""
},
"_hash": 0.16055765877264894
},
{
"name": "longitude",
"type": "attribute",
"label": "longitude",
"color": "#607d8b",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": ""
},
"_hash": 0.10969512220289346
} }
], ]
"entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e"
} }
], ],
"showTitleIcon": false, "showTitleIcon": false,
@ -882,11 +911,11 @@
"actions": { "actions": {
"tooltipAction": [ "tooltipAction": [
{ {
"id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",
"name": "delete", "name": "delete",
"icon": "more_horiz", "icon": "more_horiz",
"type": "custom", "type": "custom",
"customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });" "customFunction": "var entityDatasource = widgetContext.mapInstance.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.mapInstance.saveMarkerLocation(entityDatasource[0], null, null).subscribe(function success() {\n widgetContext.updateAliases();\n});",
"id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66"
} }
] ]
}, },
@ -1017,11 +1046,11 @@
"actions": { "actions": {
"tooltipAction": [ "tooltipAction": [
{ {
"id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",
"name": "delete", "name": "delete",
"icon": "more_horiz", "icon": "more_horiz",
"type": "custom", "type": "custom",
"customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });" "customFunction": "var entityDatasource = widgetContext.mapInstance.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.mapInstance.saveMarkerLocation(entityDatasource[0], null, null).subscribe(function success() {\n widgetContext.updateAliases();\n});",
"id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66"
} }
] ]
}, },
@ -1033,6 +1062,61 @@
"displayTimewindow": true "displayTimewindow": true
}, },
"id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf" "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf"
},
"f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
"isSystemType": true,
"bundleAlias": "input_widgets",
"typeAlias": "update_double_timeseries",
"type": "latest",
"title": "New widget",
"sizeX": 7.5,
"sizeY": 3,
"config": {
"datasources": [
{
"type": "entity",
"name": null,
"entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
"dataKeys": [
{
"name": "temperature",
"type": "timeseries",
"label": "temperature",
"color": "#2196f3",
"settings": {},
"_hash": 0.4164505192982848
}
]
}
],
"timewindow": {
"realtime": {
"timewindowMs": 60000
}
},
"showTitle": true,
"backgroundColor": "#fff",
"color": "rgba(0, 0, 0, 0.87)",
"padding": "8px",
"settings": {
"showResultMessage": true,
"showLabel": true
},
"title": "New Update double timeseries",
"dropShadow": true,
"enableFullscreen": false,
"widgetStyle": {},
"titleStyle": {
"fontSize": "16px",
"fontWeight": 400
},
"useDashboardTimewindow": true,
"showLegend": false,
"actions": {}
},
"row": 0,
"col": 0,
"id": "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3"
} }
}, },
"states": { "states": {
@ -1131,6 +1215,12 @@
"sizeY": 6, "sizeY": 6,
"row": 6, "row": 6,
"col": 0 "col": 0
},
"f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
"sizeX": 7.5,
"sizeY": 3,
"row": 12,
"col": 0
} }
}, },
"gridSettings": { "gridSettings": {
@ -1214,4 +1304,4 @@
} }
}, },
"name": "Thermostats" "name": "Thermostats"
} }

56
application/src/main/data/json/system/widget_bundles/analogue_gauges.json

@ -6,35 +6,19 @@
}, },
"widgetTypes": [ "widgetTypes": [
{ {
"alias": "radial_gauge_canvas_gauges", "alias": "analogue_compass",
"name": "Radial gauge - Canvas Gauges", "name": "Analogue Compass",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 6, "sizeX": 6,
"sizeY": 5, "sizeY": 5,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n", "templateHtml": "<canvas id=\"compass\"></canvas>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"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() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":10,\"highlights\":[],\"showUnitTitle\":true,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":10,\"valueInt\":3,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":36,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"minValue\":-100,\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Radial gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
{
"alias": "speed_gauge_canvas_gauges",
"name": "Speed gauge - Canvas Gauges",
"descriptor": {
"type": "latest",
"sizeX": 7,
"sizeY": 5,
"resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueCompass.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":180,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":false,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"showUnitTitle\":false,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"minValue\":0,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"MPH\",\"majorTicksCount\":9,\"numbersFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Speed gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"minorTicks\":22,\"needleCircleSize\":15,\"showBorder\":true,\"borderOuterWidth\":10,\"colorPlate\":\"#222\",\"colorMajorTicks\":\"#f5f5f5\",\"colorMinorTicks\":\"#ddd\",\"colorNeedle\":\"#f08080\",\"colorNeedleCircle\":\"#e8e8e8\",\"colorBorder\":\"#ccc\",\"majorTickFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ccc\"},\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"animationTarget\":\"needle\",\"majorTicks\":[\"N\",\"NE\",\"E\",\"SE\",\"S\",\"SW\",\"W\",\"NW\"]},\"title\":\"Analogue Compass\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
} }
}, },
{ {
@ -47,7 +31,7 @@
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"linearGauge\"></canvas>\n", "templateHtml": "<canvas id=\"linearGauge\"></canvas>\n",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueLinearGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueLinearGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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() * 30 - 15;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"defaultColor\":\"#e64a19\",\"barStrokeWidth\":2.5,\"colorBar\":\"rgba(255, 255, 255, 0.4)\",\"colorBarEnd\":\"rgba(221, 221, 221, 0.38)\",\"showUnitTitle\":true,\"minorTicks\":2,\"valueBox\":true,\"valueInt\":3,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"colorNeedleShadowUp\":\"rgba(2,255,255,0.2)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"highlightsWidth\":10,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"showBorder\":false,\"majorTicksCount\":8,\"numbersFont\":{\"family\":\"Arial\",\"size\":18,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#78909c\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":26,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#37474f\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":40,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#444\",\"shadowColor\":\"rgba(0,0,0,0.3)\"},\"minValue\":-60,\"highlights\":[{\"from\":-60,\"to\":-40,\"color\":\"#90caf9\"},{\"from\":-40,\"to\":-20,\"color\":\"rgba(144, 202, 249, 0.66)\"},{\"from\":-20,\"to\":0,\"color\":\"rgba(144, 202, 249, 0.33)\"},{\"from\":0,\"to\":20,\"color\":\"rgba(244, 67, 54, 0.2)\"},{\"from\":20,\"to\":40,\"color\":\"rgba(244, 67, 54, 0.4)\"},{\"from\":40,\"to\":60,\"color\":\"rgba(244, 67, 54, 0.6)\"},{\"from\":60,\"to\":80,\"color\":\"rgba(244, 67, 54, 0.8)\"},{\"from\":80,\"to\":100,\"color\":\"#f44336\"}],\"unitTitle\":\"Temperature\",\"units\":\"°C\",\"colorBarProgress\":\"#90caf9\",\"colorBarProgressEnd\":\"#f44336\",\"colorBarStroke\":\"#b0bec5\",\"valueDec\":1},\"title\":\"Temperature gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "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() * 30 - 15;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"defaultColor\":\"#e64a19\",\"barStrokeWidth\":2.5,\"colorBar\":\"rgba(255, 255, 255, 0.4)\",\"colorBarEnd\":\"rgba(221, 221, 221, 0.38)\",\"showUnitTitle\":true,\"minorTicks\":2,\"valueBox\":true,\"valueInt\":3,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"colorNeedleShadowUp\":\"rgba(2,255,255,0.2)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"highlightsWidth\":10,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"showBorder\":false,\"majorTicksCount\":8,\"numbersFont\":{\"family\":\"Arial\",\"size\":18,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#78909c\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":26,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#37474f\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":40,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#444\",\"shadowColor\":\"rgba(0,0,0,0.3)\"},\"minValue\":-60,\"highlights\":[{\"from\":-60,\"to\":-40,\"color\":\"#90caf9\"},{\"from\":-40,\"to\":-20,\"color\":\"rgba(144, 202, 249, 0.66)\"},{\"from\":-20,\"to\":0,\"color\":\"rgba(144, 202, 249, 0.33)\"},{\"from\":0,\"to\":20,\"color\":\"rgba(244, 67, 54, 0.2)\"},{\"from\":20,\"to\":40,\"color\":\"rgba(244, 67, 54, 0.4)\"},{\"from\":40,\"to\":60,\"color\":\"rgba(244, 67, 54, 0.6)\"},{\"from\":60,\"to\":80,\"color\":\"rgba(244, 67, 54, 0.8)\"},{\"from\":80,\"to\":100,\"color\":\"#f44336\"}],\"unitTitle\":\"Temperature\",\"units\":\"°C\",\"colorBarProgress\":\"#90caf9\",\"colorBarProgressEnd\":\"#f44336\",\"colorBarStroke\":\"#b0bec5\",\"valueDec\":1},\"title\":\"Temperature gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
@ -63,26 +47,42 @@
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n", "templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"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\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":60,\"startAngle\":67.5,\"ticksAngle\":225,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":-60,\"to\":-50,\"color\":\"#42a5f5\"},{\"from\":-50,\"to\":-40,\"color\":\"rgba(66, 165, 245, 0.83)\"},{\"from\":-40,\"to\":-30,\"color\":\"rgba(66, 165, 245, 0.66)\"},{\"from\":-30,\"to\":-20,\"color\":\"rgba(66, 165, 245, 0.5)\"},{\"from\":-20,\"to\":-10,\"color\":\"rgba(66, 165, 245, 0.33)\"},{\"from\":-10,\"to\":0,\"color\":\"rgba(66, 165, 245, 0.16)\"},{\"from\":0,\"to\":10,\"color\":\"rgba(229, 115, 115, 0.16)\"},{\"from\":10,\"to\":20,\"color\":\"rgba(229, 115, 115, 0.33)\"},{\"from\":20,\"to\":30,\"color\":\"rgba(229, 115, 115, 0.5)\"},{\"from\":30,\"to\":40,\"color\":\"rgba(229, 115, 115, 0.66)\"},{\"from\":40,\"to\":50,\"color\":\"rgba(229, 115, 115, 0.83)\"},{\"from\":50,\"to\":60,\"color\":\"#e57373\"}],\"showUnitTitle\":true,\"colorPlate\":\"#cfd8dc\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"valueDec\":1,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1000,\"animationRule\":\"bounce\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"°C\",\"majorTicksCount\":12,\"numbersFont\":{\"family\":\"Roboto\",\"size\":20,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":30,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"unitTitle\":\"Temperature\",\"minValue\":-60},\"title\":\"Temperature radial gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"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\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":60,\"startAngle\":67.5,\"ticksAngle\":225,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":-60,\"to\":-50,\"color\":\"#42a5f5\"},{\"from\":-50,\"to\":-40,\"color\":\"rgba(66, 165, 245, 0.83)\"},{\"from\":-40,\"to\":-30,\"color\":\"rgba(66, 165, 245, 0.66)\"},{\"from\":-30,\"to\":-20,\"color\":\"rgba(66, 165, 245, 0.5)\"},{\"from\":-20,\"to\":-10,\"color\":\"rgba(66, 165, 245, 0.33)\"},{\"from\":-10,\"to\":0,\"color\":\"rgba(66, 165, 245, 0.16)\"},{\"from\":0,\"to\":10,\"color\":\"rgba(229, 115, 115, 0.16)\"},{\"from\":10,\"to\":20,\"color\":\"rgba(229, 115, 115, 0.33)\"},{\"from\":20,\"to\":30,\"color\":\"rgba(229, 115, 115, 0.5)\"},{\"from\":30,\"to\":40,\"color\":\"rgba(229, 115, 115, 0.66)\"},{\"from\":40,\"to\":50,\"color\":\"rgba(229, 115, 115, 0.83)\"},{\"from\":50,\"to\":60,\"color\":\"#e57373\"}],\"showUnitTitle\":true,\"colorPlate\":\"#cfd8dc\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"valueDec\":1,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1000,\"animationRule\":\"bounce\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"°C\",\"majorTicksCount\":12,\"numbersFont\":{\"family\":\"Roboto\",\"size\":20,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":30,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"unitTitle\":\"Temperature\",\"minValue\":-60},\"title\":\"Temperature radial gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
} }
}, },
{ {
"alias": "analogue_compass", "alias": "speed_gauge_canvas_gauges",
"name": "Analogue Compass", "name": "Speed gauge - Canvas Gauges",
"descriptor": {
"type": "latest",
"sizeX": 7,
"sizeY": 5,
"resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"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() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":180,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":false,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"showUnitTitle\":false,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"minValue\":0,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"MPH\",\"majorTicksCount\":9,\"numbersFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Speed gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
{
"alias": "radial_gauge_canvas_gauges",
"name": "Radial gauge - Canvas Gauges",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 6, "sizeX": 6,
"sizeY": 5, "sizeY": 5,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"compass\"></canvas>", "templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueCompass.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"minorTicks\":22,\"needleCircleSize\":15,\"showBorder\":true,\"borderOuterWidth\":10,\"colorPlate\":\"#222\",\"colorMajorTicks\":\"#f5f5f5\",\"colorMinorTicks\":\"#ddd\",\"colorNeedle\":\"#f08080\",\"colorNeedleCircle\":\"#e8e8e8\",\"colorBorder\":\"#ccc\",\"majorTickFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ccc\"},\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"animationTarget\":\"needle\",\"majorTicks\":[\"N\",\"NE\",\"E\",\"SE\",\"S\",\"SW\",\"W\",\"NW\"]},\"title\":\"Analogue Compass\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" "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() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":10,\"highlights\":[],\"showUnitTitle\":true,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":10,\"valueInt\":3,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":36,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"minValue\":-100,\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Radial gauge - Canvas Gauges\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
} }
} }
] ]

96
application/src/main/data/json/system/widget_bundles/cards.json

File diff suppressed because one or more lines are too long

146
application/src/main/data/json/system/widget_bundles/digital_gauges.json

@ -6,35 +6,19 @@
}, },
"widgetTypes": [ "widgetTypes": [
{ {
"alias": "digital_bar", "alias": "gauge_justgage",
"name": "Digital horizontal bar", "name": "Gauge - justGage",
"descriptor": {
"type": "latest",
"sizeX": 6,
"sizeY": 2.5,
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"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\":{}}"
}
},
{
"alias": "digital_speedometer",
"name": "Digital speedometer",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 5, "sizeX": 4,
"sizeY": 3, "sizeY": 3,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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\":{}}" "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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":36,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"arc\"},\"title\":\"Gauge - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
} }
}, },
{ {
@ -47,58 +31,58 @@
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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\":{}}" "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\":{}}"
} }
}, },
{ {
"alias": "digital_vertical_bar", "alias": "digital_speedometer",
"name": "Digital vertical bar", "name": "Digital speedometer",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 2.5, "sizeX": 5,
"sizeY": 4.5, "sizeY": 3,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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\":{}}" "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\":{}}"
} }
}, },
{ {
"alias": "gauge_justgage", "alias": "neon_gauge_justgage",
"name": "Gauge - justGage", "name": "Neon gauge - justGage",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 4, "sizeX": 5,
"sizeY": 3, "sizeY": 3,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":36,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"arc\"},\"title\":\"Gauge - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "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 - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
} }
}, },
{ {
"alias": "horizontal_bar_justgage", "alias": "lcd_gauge",
"name": "Horizontal bar - justGage", "name": "LCD gauge",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 7, "sizeX": 5,
"sizeY": 3, "sizeY": 3,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>\n", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f44336\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"horizontalBar\"},\"title\":\"Horizontal bar - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "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}}"
} }
}, },
{ {
@ -111,106 +95,122 @@
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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}}" "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}}"
} }
}, },
{ {
"alias": "lcd_gauge", "alias": "simple_neon_gauge_justgage",
"name": "LCD gauge", "name": "Simple neon gauge - justGage",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 5, "sizeX": 3,
"sizeY": 3, "sizeY": 3,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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}}" "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 - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
} }
}, },
{ {
"alias": "mini_gauge_justgage", "alias": "vertical_bar_justgage",
"name": "Mini gauge - justGage", "name": "Vertical bar - justGage",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 2, "sizeX": 2,
"sizeY": 2, "sizeY": 3.5,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#7cb342\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"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\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"roundedLineCap\":true,\"gaugeType\":\"donut\"},\"title\":\"Mini gauge - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f57c00\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":12,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":1.5,\"gaugeColor\":\"#eeeeee\",\"showTitle\":false,\"gaugeType\":\"verticalBar\"},\"title\":\"Vertical bar - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
} }
}, },
{ {
"alias": "neon_gauge_justgage", "alias": "simple_gauge_justgage",
"name": "Neon gauge - justGage", "name": "Simple gauge - justGage",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 5, "sizeX": 2,
"sizeY": 3, "sizeY": 2,
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>\n",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#ef6c00\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"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\":\"500\",\"size\":32,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"gaugeColor\":\"#eeeeee\",\"gaugeType\":\"donut\"},\"title\":\"Simple gauge - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
{
"alias": "digital_bar",
"name": "Digital horizontal bar",
"descriptor": {
"type": "latest",
"sizeX": 6,
"sizeY": 2.5,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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 - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" "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\":{}}"
} }
}, },
{ {
"alias": "simple_gauge_justgage", "alias": "mini_gauge_justgage",
"name": "Simple gauge - justGage", "name": "Mini gauge - justGage",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 2, "sizeX": 2,
"sizeY": 2, "sizeY": 2,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>\n", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#ef6c00\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"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\":\"500\",\"size\":32,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"gaugeColor\":\"#eeeeee\",\"gaugeType\":\"donut\"},\"title\":\"Simple gauge - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#7cb342\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"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\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"roundedLineCap\":true,\"gaugeType\":\"donut\"},\"title\":\"Mini gauge - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
} }
}, },
{ {
"alias": "simple_neon_gauge_justgage", "alias": "horizontal_bar_justgage",
"name": "Simple neon gauge - justGage", "name": "Horizontal bar - justGage",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 3, "sizeX": 7,
"sizeY": 3, "sizeY": 3,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>\n",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"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 - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f44336\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"horizontalBar\"},\"title\":\"Horizontal bar - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
} }
}, },
{ {
"alias": "vertical_bar_justgage", "alias": "digital_vertical_bar",
"name": "Vertical bar - justGage", "name": "Digital vertical bar",
"descriptor": { "descriptor": {
"type": "latest", "type": "latest",
"sizeX": 2, "sizeX": 2.5,
"sizeY": 3.5, "sizeY": 4.5,
"resources": [], "resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>", "templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n", "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f57c00\",\"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\":\"#ffffff\",\"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\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":12,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":1.5,\"gaugeColor\":\"#eeeeee\",\"showTitle\":false,\"gaugeType\":\"verticalBar\"},\"title\":\"Vertical bar - justGage\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "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\":{}}"
} }
} }
] ]

4
application/src/main/data/json/system/widget_bundles/gateway_widgets.json

File diff suppressed because one or more lines are too long

364
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

112
application/src/main/data/json/system/widget_bundles/maps.json

File diff suppressed because one or more lines are too long

7
dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -316,12 +316,13 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
EntityType entityType) { EntityType entityType) {
String permissionQuery = this.buildPermissionQuery(ctx, entityFilter, tenantId, customerId, entityType); String permissionQuery = this.buildPermissionQuery(ctx, entityFilter, tenantId, customerId, entityType);
String entityFilterQuery = this.buildEntityFilterQuery(ctx, entityFilter); String entityFilterQuery = this.buildEntityFilterQuery(ctx, entityFilter);
String entityFieldsQuery = EntityKeyMapping.buildQuery(ctx, entityFieldsFilters);
String result = permissionQuery; String result = permissionQuery;
if (!entityFilterQuery.isEmpty()) { if (!entityFilterQuery.isEmpty()) {
result += " and " + entityFilterQuery; result += " and " + entityFilterQuery;
} }
if (!entityFieldsFilters.isEmpty()) { if (!entityFieldsQuery.isEmpty()) {
result += " and " + entityFieldsFilters; result += " and " + entityFieldsQuery;
} }
return result; return result;
} }

2
dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,

286
ui-ngx/src/app/core/api/alias-controller.ts

@ -16,14 +16,18 @@
import { AliasInfo, IAliasController, StateControllerHolder, StateEntityInfo } from '@core/api/widget-api.models'; import { AliasInfo, IAliasController, StateControllerHolder, StateEntityInfo } from '@core/api/widget-api.models';
import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs'; import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs';
import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models'; import { Datasource, DatasourceType } from '@app/shared/models/widget.models';
import { createLabelFromDatasource, deepClone, isEqual } from '@core/utils'; import { deepClone, isEqual } from '@core/utils';
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { AliasFilterType, EntityAliases } from '@shared/models/alias.models'; import { EntityAliases } from '@shared/models/alias.models';
import { EntityInfo } from '@shared/models/entity.models'; import { EntityInfo } from '@shared/models/entity.models';
import { map, mergeMap } from 'rxjs/operators'; import { map, mergeMap } from 'rxjs/operators';
import { createDefaultEntityDataPageLink, defaultEntityDataPageLink } from '@shared/models/query/query.models'; import {
createDefaultEntityDataPageLink,
defaultEntityDataPageLink, singleEntityDataPageLink,
updateDatasourceFromEntityInfo
} from '@shared/models/query/query.models';
export class AliasController implements IAliasController { export class AliasController implements IAliasController {
@ -139,11 +143,10 @@ export class AliasController implements IAliasController {
this.resolvedAliases[aliasId] = resolvedAliasInfo; this.resolvedAliases[aliasId] = resolvedAliasInfo;
delete this.resolvedAliasesObservable[aliasId]; delete this.resolvedAliasesObservable[aliasId];
if (resolvedAliasInfo.stateEntity) { if (resolvedAliasInfo.stateEntity) {
const stateEntityInfo: StateEntityInfo = { this.resolvedAliasesToStateEntities[aliasId] = {
entityParamName: resolvedAliasInfo.entityParamName, entityParamName: resolvedAliasInfo.entityParamName,
entityId: this.stateControllerHolder().getEntityId(resolvedAliasInfo.entityParamName) entityId: this.stateControllerHolder().getEntityId(resolvedAliasInfo.entityParamName)
}; };
this.resolvedAliasesToStateEntities[aliasId] = stateEntityInfo;
} }
this.entityAliasResolvedSubject.next(aliasId); this.entityAliasResolvedSubject.next(aliasId);
resolvedAliasSubject.next(resolvedAliasInfo); resolvedAliasSubject.next(resolvedAliasInfo);
@ -186,172 +189,59 @@ export class AliasController implements IAliasController {
); );
} }
private resolveDatasource(datasource: Datasource, isSingle?: boolean): Observable<Datasource> { private resolveDatasource(datasource: Datasource, forceFilter = false): Observable<Datasource> {
if (datasource.type === DatasourceType.entity) { const newDatasource = deepClone(datasource);
if (datasource.entityAliasId) { if (newDatasource.type === DatasourceType.entity) {
return this.getAliasInfo(datasource.entityAliasId).pipe( if (newDatasource.entityAliasId) {
map((aliasInfo) => { return this.getAliasInfo(newDatasource.entityAliasId).pipe(
datasource.aliasName = aliasInfo.alias; mergeMap((aliasInfo) => {
if (aliasInfo.resolveMultiple && !isSingle) { newDatasource.aliasName = aliasInfo.alias;
let newDatasource: Datasource; if (!aliasInfo.entityFilter) {
// const resolvedEntities = aliasInfo.resolvedEntities; newDatasource.unresolvedStateEntity = true;
if (aliasInfo.entityFilter) { newDatasource.name = 'Unresolved';
newDatasource = deepClone(datasource); newDatasource.entityName = 'Unresolved';
newDatasource.entityFilter = aliasInfo.entityFilter; return of(newDatasource);
/*const datasources: Array<Datasource> = [];
for (let i = 0; i < resolvedEntities.length; i++) {
const resolvedEntity = resolvedEntities[i];
newDatasource = deepClone(datasource);
if (resolvedEntity.origEntity) {
newDatasource.entity = deepClone(resolvedEntity.origEntity);
} else {
newDatasource.entity = {};
}
newDatasource.entityId = resolvedEntity.id;
newDatasource.entityType = resolvedEntity.entityType;
newDatasource.entityName = resolvedEntity.name;
newDatasource.entityLabel = resolvedEntity.label;
newDatasource.entityDescription = resolvedEntity.entityDescription;
newDatasource.name = resolvedEntity.name;
newDatasource.generated = i > 0 ? true : false;
datasources.push(newDatasource);
}
return datasources;*/
return newDatasource;
} else {
if (aliasInfo.stateEntity) {
newDatasource = deepClone(datasource);
newDatasource.unresolvedStateEntity = true;
return newDatasource;
} else {
return null;
// throw new Error('Unable to resolve datasource.');
}
}
} else {
const entity = aliasInfo.currentEntity;
if (entity) {
if (entity.origEntity) {
datasource.entity = deepClone(entity.origEntity);
} else {
datasource.entity = {};
}
datasource.entityId = entity.id;
datasource.entityType = entity.entityType;
datasource.entityName = entity.name;
datasource.entityLabel = entity.label;
datasource.name = entity.name;
datasource.entityDescription = entity.entityDescription;
datasource.entityFilter = {
type: AliasFilterType.singleEntity,
singleEntity: {
id: entity.id,
entityType: entity.entityType
}
};
return datasource;
} else {
if (aliasInfo.stateEntity) {
datasource.unresolvedStateEntity = true;
return datasource;
} else {
return null;
// throw new Error('Unable to resolve datasource.');
}
}
} }
}) if (aliasInfo.resolveMultiple) {
); newDatasource.entityFilter = aliasInfo.entityFilter;
} else { if (forceFilter) {
datasource.aliasName = datasource.entityName; return this.entityService.findSingleEntityInfoByEntityFilter(aliasInfo.entityFilter,
datasource.name = datasource.entityName; {ignoreLoading: true, ignoreErrors: true}).pipe(
return of(datasource); map((entity) => {
} if (entity) {
} else { updateDatasourceFromEntityInfo(newDatasource, entity, true);
return of(datasource); }
} return newDatasource;
} })
);
/* private resolveDatasourceOld(datasource: Datasource, isSingle?: boolean): Observable<Array<Datasource>> {
if (datasource.type === DatasourceType.entity) {
if (datasource.entityAliasId) {
return this.getAliasInfo(datasource.entityAliasId).pipe(
map((aliasInfo) => {
datasource.aliasName = aliasInfo.alias;
if (aliasInfo.resolveMultiple && !isSingle) {
let newDatasource: Datasource;
const resolvedEntities = aliasInfo.resolvedEntities;
if (resolvedEntities && resolvedEntities.length) {
const datasources: Array<Datasource> = [];
for (let i = 0; i < resolvedEntities.length; i++) {
const resolvedEntity = resolvedEntities[i];
newDatasource = deepClone(datasource);
if (resolvedEntity.origEntity) {
newDatasource.entity = deepClone(resolvedEntity.origEntity);
} else {
newDatasource.entity = {};
}
newDatasource.entityId = resolvedEntity.id;
newDatasource.entityType = resolvedEntity.entityType;
newDatasource.entityName = resolvedEntity.name;
newDatasource.entityLabel = resolvedEntity.label;
newDatasource.entityDescription = resolvedEntity.entityDescription;
newDatasource.name = resolvedEntity.name;
newDatasource.generated = i > 0 ? true : false;
datasources.push(newDatasource);
}
return datasources;
} else { } else {
if (aliasInfo.stateEntity) { return of(newDatasource);
newDatasource = deepClone(datasource);
newDatasource.unresolvedStateEntity = true;
return [newDatasource];
} else {
return [];
// throw new Error('Unable to resolve datasource.');
}
} }
} else { } else {
const entity = aliasInfo.currentEntity; if (aliasInfo.currentEntity) {
if (entity) { updateDatasourceFromEntityInfo(newDatasource, aliasInfo.currentEntity, true);
if (entity.origEntity) { } else if (aliasInfo.stateEntity) {
datasource.entity = deepClone(entity.origEntity); newDatasource.unresolvedStateEntity = true;
} else { newDatasource.name = 'Unresolved';
datasource.entity = {}; newDatasource.entityName = 'Unresolved';
}
datasource.entityId = entity.id;
datasource.entityType = entity.entityType;
datasource.entityName = entity.name;
datasource.entityLabel = entity.label;
datasource.name = entity.name;
datasource.entityDescription = entity.entityDescription;
return [datasource];
} else {
if (aliasInfo.stateEntity) {
datasource.unresolvedStateEntity = true;
return [datasource];
} else {
return [];
// throw new Error('Unable to resolve datasource.');
}
} }
return of(newDatasource);
} }
}) })
); );
} else { } else {
datasource.aliasName = datasource.entityName; newDatasource.aliasName = newDatasource.entityName;
datasource.name = datasource.entityName; newDatasource.name = newDatasource.entityName;
return of([datasource]); return of(newDatasource);
} }
} else { } else {
return of([datasource]); return of(newDatasource);
} }
} */ }
resolveAlarmSource(alarmSource: Datasource): Observable<Datasource> { resolveAlarmSource(alarmSource: Datasource): Observable<Datasource> {
return this.resolveDatasource(alarmSource, true).pipe( return this.resolveDatasource(alarmSource, true).pipe(
map((datasources) => { map((datasource) => {
const datasource = datasources && datasources.length ? datasources[0] : deepClone(alarmSource);
if (datasource.type === DatasourceType.function) { if (datasource.type === DatasourceType.function) {
let name: string; let name: string;
if (datasource.name && datasource.name.length) { if (datasource.name && datasource.name.length) {
@ -362,9 +252,6 @@ export class AliasController implements IAliasController {
datasource.name = name; datasource.name = name;
datasource.aliasName = name; datasource.aliasName = name;
datasource.entityName = name; datasource.entityName = name;
} else if (datasource.unresolvedStateEntity) {
datasource.name = 'Unresolved';
datasource.entityName = 'Unresolved';
} }
return datasource; return datasource;
}) })
@ -372,9 +259,9 @@ export class AliasController implements IAliasController {
} }
resolveDatasources(datasources: Array<Datasource>, singleEntity?: boolean): Observable<Array<Datasource>> { resolveDatasources(datasources: Array<Datasource>, singleEntity?: boolean): Observable<Array<Datasource>> {
const newDatasources = deepClone(singleEntity ? [datasources[0]] : datasources); const toResolve = singleEntity ? [datasources[0]] : datasources;
const observables = new Array<Observable<Datasource>>(); const observables = new Array<Observable<Datasource>>();
newDatasources.forEach((datasource) => { toResolve.forEach((datasource) => {
observables.push(this.resolveDatasource(datasource)); observables.push(this.resolveDatasource(datasource));
}); });
return forkJoin(observables).pipe( return forkJoin(observables).pipe(
@ -395,14 +282,10 @@ export class AliasController implements IAliasController {
datasource.name = name; datasource.name = name;
datasource.aliasName = name; datasource.aliasName = name;
datasource.entityName = name; datasource.entityName = name;
} else if (datasource.unresolvedStateEntity) { } else {
datasource.name = 'Unresolved';
datasource.entityName = 'Unresolved';
} else if (datasource.type === DatasourceType.entity) {
if (singleEntity) { if (singleEntity) {
datasource.pageLink = createDefaultEntityDataPageLink(1); datasource.pageLink = deepClone(singleEntityDataPageLink);
} } else if (!datasource.pageLink) {
if (!datasource.pageLink) {
datasource.pageLink = deepClone(defaultEntityDataPageLink); datasource.pageLink = deepClone(defaultEntityDataPageLink);
} }
} }
@ -412,71 +295,6 @@ export class AliasController implements IAliasController {
); );
} }
/*resolveDatasourcesOld(datasources: Array<Datasource>): Observable<Array<Datasource>> {
const newDatasources = deepClone(datasources);
const observables = new Array<Observable<Array<Datasource>>>();
newDatasources.forEach((datasource) => {
observables.push(this.resolveDatasource(datasource));
});
return forkJoin(observables).pipe(
map((arrayOfDatasources) => {
const result = new Array<Datasource>();
arrayOfDatasources.forEach((datasourcesArray) => {
result.push(...datasourcesArray);
});
result.sort((d1, d2) => {
const i1 = d1.generated ? 1 : 0;
const i2 = d2.generated ? 1 : 0;
return i1 - i2;
});
let index = 0;
let functionIndex = 0;
result.forEach((datasource) => {
if (datasource.type === DatasourceType.function) {
let name: string;
if (datasource.name && datasource.name.length) {
name = datasource.name;
} else {
functionIndex++;
name = DatasourceType.function;
if (functionIndex > 1) {
name += ' ' + functionIndex;
}
}
datasource.name = name;
datasource.aliasName = name;
datasource.entityName = name;
} else if (datasource.unresolvedStateEntity) {
datasource.name = 'Unresolved';
datasource.entityName = 'Unresolved';
}
datasource.dataKeys.forEach((dataKey) => {
if (datasource.generated) {
dataKey._hash = Math.random();
dataKey.color = this.utils.getMaterialColor(index);
}
index++;
});
this.updateDatasourceKeyLabels(datasource);
});
return result;
})
);
}*/
/* private updateDatasourceKeyLabels(datasource: Datasource) {
datasource.dataKeys.forEach((dataKey) => {
this.updateDataKeyLabel(dataKey, datasource);
});
}
private updateDataKeyLabel(dataKey: DataKey, datasource: Datasource) {
if (!dataKey.pattern) {
dataKey.pattern = deepClone(dataKey.label);
}
dataKey.label = createLabelFromDatasource(datasource, dataKey.pattern);
}*/
getInstantAliasInfo(aliasId: string): AliasInfo { getInstantAliasInfo(aliasId: string): AliasInfo {
return this.resolvedAliases[aliasId]; return this.resolvedAliases[aliasId];
} }

6
ui-ngx/src/app/core/api/data-aggregator.ts

@ -121,6 +121,12 @@ export class DataAggregator {
} }
} }
public updateOnDataCb(newOnDataCb: onAggregatedData): onAggregatedData {
const prevOnDataCb = this.onDataCb;
this.onDataCb = newOnDataCb;
return prevOnDataCb;
}
public reset(startTs: number, timeWindow: number, interval: number) { public reset(startTs: number, timeWindow: number, interval: number) {
if (this.intervalTimeoutHandle) { if (this.intervalTimeoutHandle) {
clearTimeout(this.intervalTimeoutHandle); clearTimeout(this.intervalTimeoutHandle);

195
ui-ngx/src/app/core/api/entity-data-subscription.ts

@ -15,7 +15,7 @@
/// ///
import { DataSet, DataSetHolder, DatasourceType, widgetType } from '@shared/models/widget.models'; import { DataSet, DataSetHolder, DatasourceType, widgetType } from '@shared/models/widget.models';
import { AggregationType, SubscriptionTimewindow, YEAR } from '@shared/models/time/time.models'; import { AggregationType, SubscriptionTimewindow } from '@shared/models/time/time.models';
import { SubscriptionDataKey } from '@core/api/datasource-subcription'; import { SubscriptionDataKey } from '@core/api/datasource-subcription';
import { import {
EntityData, EntityData,
@ -154,11 +154,15 @@ export class EntityDataSubscription {
} }
public subscribe(): Observable<EntityDataLoadResult> { public subscribe(): Observable<EntityDataLoadResult> {
if (!this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { this.entityDataResolveSubject = new ReplaySubject(1);
this.entityDataResolveSubject = new ReplaySubject(1); if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) {
} else {
this.started = true; this.started = true;
this.dataResolved = true; this.dataResolved = true;
this.subsTw = this.entityDataSubscriptionOptions.subscriptionTimewindow;
this.history = this.entityDataSubscriptionOptions.subscriptionTimewindow &&
isObject(this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow);
this.realtime = this.entityDataSubscriptionOptions.subscriptionTimewindow &&
isDefinedAndNotNull(this.entityDataSubscriptionOptions.subscriptionTimewindow.realtimeWindowMs);
} }
if (this.datasourceType === DatasourceType.entity) { if (this.datasourceType === DatasourceType.entity) {
const entityFields: Array<EntityKey> = const entityFields: Array<EntityKey> =
@ -200,13 +204,7 @@ export class EntityDataSubscription {
}; };
if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) {
if (this.entityDataSubscriptionOptions.type === widgetType.latest) { this.prepareSubscriptionCommands(this.dataCommand);
if (this.latestValues.length > 0) {
this.dataCommand.latestCmd = {
keys: this.latestValues
};
}
}
} }
this.subscriber.subscriptionCommands.push(this.dataCommand); this.subscriber.subscriptionCommands.push(this.dataCommand);
@ -222,23 +220,24 @@ export class EntityDataSubscription {
); );
this.subscriber.reconnect$.subscribe(() => { this.subscriber.reconnect$.subscribe(() => {
if (this.started && !this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { if (this.started) {
const targetCommand = this.entityDataSubscriptionOptions.isPaginatedDataSubscription ? this.dataCommand : this.subsCommand;
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && if (this.entityDataSubscriptionOptions.type === widgetType.timeseries &&
!this.history && this.tsFields.length) { !this.history && this.tsFields.length) {
const newSubsTw: SubscriptionTimewindow = this.listener.updateRealtimeSubscription(); const newSubsTw: SubscriptionTimewindow = this.listener.updateRealtimeSubscription();
this.subsTw = newSubsTw; this.subsTw = newSubsTw;
this.subsCommand.tsCmd.startTs = this.subsTw.startTs; targetCommand.tsCmd.startTs = this.subsTw.startTs;
this.subsCommand.tsCmd.timeWindow = this.subsTw.aggregation.timeWindow; targetCommand.tsCmd.timeWindow = this.subsTw.aggregation.timeWindow;
this.subsCommand.tsCmd.interval = this.subsTw.aggregation.interval; targetCommand.tsCmd.interval = this.subsTw.aggregation.interval;
this.subsCommand.tsCmd.limit = this.subsTw.aggregation.limit; targetCommand.tsCmd.limit = this.subsTw.aggregation.limit;
this.subsCommand.tsCmd.agg = this.subsTw.aggregation.type; targetCommand.tsCmd.agg = this.subsTw.aggregation.type;
this.subsCommand.tsCmd.fetchLatestPreviousPoint = this.subsTw.aggregation.stateData; targetCommand.tsCmd.fetchLatestPreviousPoint = this.subsTw.aggregation.stateData;
this.dataAggregators.forEach((dataAggregator) => { this.dataAggregators.forEach((dataAggregator) => {
dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval);
}); });
} }
this.subsCommand.query = this.dataCommand.query; targetCommand.query = this.dataCommand.query;
this.subscriber.subscriptionCommands = [this.subsCommand]; this.subscriber.subscriptionCommands = [targetCommand];
} else { } else {
this.subscriber.subscriptionCommands = [this.dataCommand]; this.subscriber.subscriptionCommands = [this.dataCommand];
} }
@ -265,12 +264,6 @@ export class EntityDataSubscription {
totalPages: 1 totalPages: 1
}; };
this.onPageData(pageData); this.onPageData(pageData);
if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) {
if (this.entityDataSubscriptionOptions.type === widgetType.latest) {
this.frequency = 1000;
this.timer = setTimeout(this.onTick.bind(this, true), 0);
}
}
} }
if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) {
return of(null); return of(null);
@ -289,6 +282,70 @@ export class EntityDataSubscription {
this.realtime = this.entityDataSubscriptionOptions.subscriptionTimewindow && this.realtime = this.entityDataSubscriptionOptions.subscriptionTimewindow &&
isDefinedAndNotNull(this.entityDataSubscriptionOptions.subscriptionTimewindow.realtimeWindowMs); isDefinedAndNotNull(this.entityDataSubscriptionOptions.subscriptionTimewindow.realtimeWindowMs);
this.prepareData();
if (this.datasourceType === DatasourceType.entity) {
this.subsCommand = new EntityDataCmd();
this.subsCommand.cmdId = this.dataCommand.cmdId;
this.prepareSubscriptionCommands(this.subsCommand);
if (!this.subsCommand.isEmpty()) {
this.subscriber.subscriptionCommands = [this.subsCommand];
this.subscriber.update();
}
} else if (this.datasourceType === DatasourceType.function) {
this.startFunction();
}
this.started = true;
}
private prepareSubscriptionCommands(cmd: EntityDataCmd) {
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) {
if (this.tsFields.length > 0) {
if (this.history) {
cmd.historyCmd = {
keys: this.tsFields.map(key => key.key),
startTs: this.subsTw.fixedWindow.startTimeMs,
endTs: this.subsTw.fixedWindow.endTimeMs,
interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type,
fetchLatestPreviousPoint: this.subsTw.aggregation.stateData
};
} else {
cmd.tsCmd = {
keys: this.tsFields.map(key => key.key),
startTs: this.subsTw.startTs,
timeWindow: this.subsTw.aggregation.timeWindow,
interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type,
fetchLatestPreviousPoint: this.subsTw.aggregation.stateData
}
}
}
} else if (this.entityDataSubscriptionOptions.type === widgetType.latest) {
if (this.latestValues.length > 0) {
cmd.latestCmd = {
keys: this.latestValues
};
}
}
}
private startFunction() {
this.frequency = 1000;
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) {
this.frequency = Math.min(this.entityDataSubscriptionOptions.subscriptionTimewindow.aggregation.interval, 5000);
}
this.tickScheduledTime = this.utils.currentPerfTime();
if (this.history) {
this.onTick(true);
} else {
this.timer = setTimeout(this.onTick.bind(this, true), 0);
}
}
private prepareData() {
if (this.timer) { if (this.timer) {
clearTimeout(this.timer); clearTimeout(this.timer);
this.timer = null; this.timer = null;
@ -324,57 +381,6 @@ export class EntityDataSubscription {
} }
} }
} }
if (this.datasourceType === DatasourceType.entity) {
this.subsCommand = new EntityDataCmd();
this.subsCommand.cmdId = this.dataCommand.cmdId;
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) {
if (this.tsFields.length > 0) {
if (this.history) {
this.subsCommand.historyCmd = {
keys: this.tsFields.map(key => key.key),
startTs: this.subsTw.fixedWindow.startTimeMs,
endTs: this.subsTw.fixedWindow.endTimeMs,
interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type,
fetchLatestPreviousPoint: this.subsTw.aggregation.stateData
};
} else {
this.subsCommand.tsCmd = {
keys: this.tsFields.map(key => key.key),
startTs: this.subsTw.startTs,
timeWindow: this.subsTw.aggregation.timeWindow,
interval: this.subsTw.aggregation.interval,
limit: this.subsTw.aggregation.limit,
agg: this.subsTw.aggregation.type,
fetchLatestPreviousPoint: this.subsTw.aggregation.stateData
}
}
}
} else if (this.entityDataSubscriptionOptions.type === widgetType.latest) {
if (this.latestValues.length > 0) {
this.subsCommand.latestCmd = {
keys: this.latestValues
};
}
}
if (!this.subsCommand.isEmpty()) {
this.subscriber.subscriptionCommands = [this.subsCommand];
this.subscriber.update();
}
} else if (this.datasourceType === DatasourceType.function) {
this.frequency = 1000;
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) {
this.frequency = Math.min(this.entityDataSubscriptionOptions.subscriptionTimewindow.aggregation.interval, 5000);
}
this.tickScheduledTime = this.utils.currentPerfTime();
if (this.history) {
this.onTick(true);
} else {
this.timer = setTimeout(this.onTick.bind(this, true), 0);
}
}
this.started = true;
} }
private resetData() { private resetData() {
@ -432,7 +438,10 @@ export class EntityDataSubscription {
} }
} }
this.pageData = pageData; this.pageData = pageData;
if (isInitialData || this.entityDataSubscriptionOptions.isPaginatedDataSubscription) {
if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) {
this.prepareData();
} else if (isInitialData) {
this.resetData(); this.resetData();
} }
const data: Array<Array<DataSetHolder>> = []; const data: Array<Array<DataSetHolder>> = [];
@ -462,6 +471,19 @@ export class EntityDataSubscription {
this.listener.dataLoaded(pageData, data, this.listener.dataLoaded(pageData, data,
this.listener.configDatasourceIndex); this.listener.configDatasourceIndex);
} }
if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription && isInitialData) {
if (this.datasourceType === DatasourceType.function) {
this.startFunction();
}
this.entityDataResolveSubject.next(
{
pageData,
data,
datasourceIndex: this.listener.configDatasourceIndex
}
);
this.entityDataResolveSubject.complete();
}
} }
} }
@ -478,7 +500,7 @@ export class EntityDataSubscription {
dataIndex, dataKeyIndex, detectChanges); dataIndex, dataKeyIndex, detectChanges);
} }
private processEntityData(entityData: EntityData, dataIndex: number, aggregate: boolean, private processEntityData(entityData: EntityData, dataIndex: number, isUpdate: boolean,
dataUpdatedCb: DataUpdatedCb) { dataUpdatedCb: DataUpdatedCb) {
if (this.entityDataSubscriptionOptions.type === widgetType.latest && entityData.latest) { if (this.entityDataSubscriptionOptions.type === widgetType.latest && entityData.latest) {
for (const type of Object.keys(entityData.latest)) { for (const type of Object.keys(entityData.latest)) {
@ -490,9 +512,20 @@ export class EntityDataSubscription {
if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && entityData.timeseries) { if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && entityData.timeseries) {
const subscriptionData = this.toSubscriptionData(entityData.timeseries, true); const subscriptionData = this.toSubscriptionData(entityData.timeseries, true);
if (this.dataAggregators && this.dataAggregators[dataIndex]) { if (this.dataAggregators && this.dataAggregators[dataIndex]) {
this.dataAggregators[dataIndex].onData({data: subscriptionData}, false, this.history, true); const dataAggregator = this.dataAggregators[dataIndex];
let prevDataCb;
if (!isUpdate) {
prevDataCb = dataAggregator.updateOnDataCb((data, detectChanges) => {
this.onData(data, this.datasourceType === DatasourceType.function ?
DataKeyType.function : DataKeyType.timeseries, dataIndex, detectChanges, dataUpdatedCb);
});
}
dataAggregator.onData({data: subscriptionData}, false, this.history, true);
if (prevDataCb) {
dataAggregator.updateOnDataCb(prevDataCb);
}
} }
if (!this.history && !aggregate) { if (!this.history && !isUpdate) {
this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb);
} }
} }

21
ui-ngx/src/app/core/api/entity-data.service.ts

@ -65,26 +65,33 @@ export class EntityDataService {
} }
public startSubscription(listener: EntityDataListener) { public startSubscription(listener: EntityDataListener) {
if (listener.subscriptionType === widgetType.timeseries) { if (listener.subscription) {
listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow); if (listener.subscriptionType === widgetType.timeseries) {
listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
}
listener.subscription.start();
} }
listener.subscription.start();
} }
public subscribeForPaginatedData(listener: EntityDataListener, public subscribeForPaginatedData(listener: EntityDataListener,
pageLink: EntityDataPageLink, pageLink: EntityDataPageLink,
keyFilters: KeyFilter[]) { keyFilters: KeyFilter[]): Observable<EntityDataLoadResult> {
const datasource = listener.configDatasource; const datasource = listener.configDatasource;
if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) { if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) {
return; return of(null);
} }
listener.subscription = this.createSubscription(listener, listener.subscription = this.createSubscription(listener,
pageLink, keyFilters, true); pageLink, keyFilters, true);
listener.subscription.subscribe(); if (listener.subscriptionType === widgetType.timeseries) {
listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
}
return listener.subscription.subscribe();
} }
public stopSubscription(listener: EntityDataListener) { public stopSubscription(listener: EntityDataListener) {
listener.subscription.unsubscribe(); if (listener.subscription) {
listener.subscription.unsubscribe();
}
} }
private createSubscription(listener: EntityDataListener, private createSubscription(listener: EntityDataListener,

2
ui-ngx/src/app/core/api/widget-api.models.ts

@ -287,7 +287,7 @@ export interface IWidgetSubscription {
subscribeForPaginatedData(datasourceIndex: number, subscribeForPaginatedData(datasourceIndex: number,
pageLink: EntityDataPageLink, pageLink: EntityDataPageLink,
keyFilters: KeyFilter[]): void; keyFilters: KeyFilter[]): Observable<any>;
isDataResolved(): boolean; isDataResolved(): boolean;

62
ui-ngx/src/app/core/api/widget-subscription.ts

@ -58,7 +58,7 @@ import {
EntityDataPageLink, EntityDataPageLink,
entityDataToEntityInfo, entityDataToEntityInfo,
EntityKeyType, EntityKeyType,
KeyFilter KeyFilter, updateDatasourceFromEntityInfo
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@ -450,7 +450,7 @@ export class WidgetSubscription implements IWidgetSubscription {
this.dataLoaded(resolveResult.pageData, resolveResult.data, resolveResult.datasourceIndex, false); this.dataLoaded(resolveResult.pageData, resolveResult.data, resolveResult.datasourceIndex, false);
}); });
this.configureLoadedData(); this.configureLoadedData();
this.hasResolvedData = true; this.hasResolvedData = this.datasources.length > 0;
this.notifyDataLoaded(); this.notifyDataLoaded();
this.onDataUpdated(true); this.onDataUpdated(true);
}) })
@ -577,11 +577,11 @@ export class WidgetSubscription implements IWidgetSubscription {
} */ } */
private resetData() { private resetData() {
this.data = []; this.data.length = 0;
this.hiddenData = []; this.hiddenData.length = 0;
if (this.displayLegend) { if (this.displayLegend) {
this.legendData.keys = []; this.legendData.keys.length = 0;
this.legendData.data = []; this.legendData.data.length = 0;
} }
this.onDataUpdated(); this.onDataUpdated();
} }
@ -885,32 +885,50 @@ export class WidgetSubscription implements IWidgetSubscription {
} }
subscribeAllForPaginatedData(pageLink: EntityDataPageLink, subscribeAllForPaginatedData(pageLink: EntityDataPageLink,
keyFilters: KeyFilter[]): void { keyFilters: KeyFilter[]): Observable<any> {
const observables: Observable<any>[] = [];
this.configuredDatasources.forEach((datasource, datasourceIndex) => { this.configuredDatasources.forEach((datasource, datasourceIndex) => {
this.subscribeForPaginatedData(datasourceIndex, pageLink, keyFilters); observables.push(this.subscribeForPaginatedData(datasourceIndex, pageLink, keyFilters));
}); });
if (observables.length) {
return forkJoin(observables);
} else {
return of(null);
}
} }
subscribeForPaginatedData(datasourceIndex: number, subscribeForPaginatedData(datasourceIndex: number,
pageLink: EntityDataPageLink, pageLink: EntityDataPageLink,
keyFilters: KeyFilter[]): void { keyFilters: KeyFilter[]): Observable<any> {
let entityDataListener = this.entityDataListeners[datasourceIndex]; let entityDataListener = this.entityDataListeners[datasourceIndex];
if (entityDataListener) { if (entityDataListener) {
this.ctx.entityDataService.stopSubscription(entityDataListener); this.ctx.entityDataService.stopSubscription(entityDataListener);
} }
const datasource = this.configuredDatasources[datasourceIndex]; const datasource = this.configuredDatasources[datasourceIndex];
if (datasource) { if (datasource) {
if (this.type === widgetType.timeseries && this.timeWindowConfig) {
this.updateRealtimeSubscription();
}
entityDataListener = { entityDataListener = {
subscriptionType: this.type, subscriptionType: this.type,
configDatasource: datasource, configDatasource: datasource,
configDatasourceIndex: datasourceIndex, configDatasourceIndex: datasourceIndex,
subscriptionTimewindow: this.subscriptionTimewindow,
dataLoaded: (pageData, data1, datasourceIndex1) => { dataLoaded: (pageData, data1, datasourceIndex1) => {
this.dataLoaded(pageData, data1, datasourceIndex1, true) this.dataLoaded(pageData, data1, datasourceIndex1, true)
}, },
dataUpdated: this.dataUpdated.bind(this) dataUpdated: this.dataUpdated.bind(this),
updateRealtimeSubscription: () => {
return this.updateRealtimeSubscription();
},
setRealtimeSubscription: (subscriptionTimewindow) => {
this.updateRealtimeSubscription(deepClone(subscriptionTimewindow));
}
}; };
this.entityDataListeners[datasourceIndex] = entityDataListener; this.entityDataListeners[datasourceIndex] = entityDataListener;
this.ctx.entityDataService.subscribeForPaginatedData(entityDataListener, pageLink, keyFilters); return this.ctx.entityDataService.subscribeForPaginatedData(entityDataListener, pageLink, keyFilters);
} else {
return of(null);
} }
} }
@ -1362,15 +1380,13 @@ export class WidgetSubscription implements IWidgetSubscription {
return datasource.dataKeys.map((dataKey, keyIndex) => { return datasource.dataKeys.map((dataKey, keyIndex) => {
dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false; dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false;
dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true; dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true;
if (this.comparisonEnabled && dataKey.isAdditional) { if (this.comparisonEnabled && dataKey.isAdditional && dataKey.settings.comparisonSettings.comparisonValuesLabel) {
if (dataKey.settings.comparisonSettings.comparisonValuesLabel) { dataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel);
dataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel); } else {
} else { if (this.comparisonEnabled && dataKey.isAdditional) {
dataKey.label = dataKey.label + ' ' + this.ctx.translate.instant('legend.comparison-time-ago.' + this.timeForComparison); dataKey.label = dataKey.label + ' ' + this.ctx.translate.instant('legend.comparison-time-ago.' + this.timeForComparison);
} }
dataKey.pattern = dataKey.label; dataKey.pattern = dataKey.label;
} else {
dataKey.pattern = dataKey.label;
dataKey.label = createLabelFromDatasource(datasource, dataKey.pattern); dataKey.label = createLabelFromDatasource(datasource, dataKey.pattern);
} }
const datasourceData: DatasourceData = { const datasourceData: DatasourceData = {
@ -1387,16 +1403,8 @@ export class WidgetSubscription implements IWidgetSubscription {
private entityDataToDatasource(configDatasource: Datasource, entityData: EntityData, index: number): Datasource { private entityDataToDatasource(configDatasource: Datasource, entityData: EntityData, index: number): Datasource {
const newDatasource = deepClone(configDatasource); const newDatasource = deepClone(configDatasource);
newDatasource.entity = {}; const entityInfo = entityDataToEntityInfo(entityData);
newDatasource.entityId = entityData.entityId.id; updateDatasourceFromEntityInfo(newDatasource, entityInfo);
newDatasource.entityType = entityData.entityId.entityType as EntityType;
if (configDatasource.type === DatasourceType.entity) {
const entityInfo = entityDataToEntityInfo(entityData);
newDatasource.name = entityInfo.name;
newDatasource.entityName = entityInfo.name;
newDatasource.entityLabel = entityInfo.label;
newDatasource.entityDescription = entityInfo.entityDescription;
}
newDatasource.generated = index > 0 ? true : false; newDatasource.generated = index > 0 ? true : false;
return newDatasource; return newDatasource;
} }

2
ui-ngx/src/app/core/http/alarm.service.ts

@ -222,7 +222,7 @@ export class AlarmService {
query.alarmStatus, query.alarmStatus,
query.fetchOriginator, query.fetchOriginator,
null); null);
return this.getAlarms(alarmQuery, {ignoreLoading: true}).pipe( return this.getAlarms(alarmQuery, {ignoreLoading: true, ignoreErrors: true}).pipe(
expand((data) => { expand((data) => {
let continueLoad = data.hasNext && !query.limit; let continueLoad = data.hasNext && !query.limit;
if (continueLoad && isDefined(leftToLoad)) { if (continueLoad && isDefined(leftToLoad)) {

238
ui-ngx/src/app/core/http/entity.service.ts

@ -47,16 +47,23 @@ import { AliasFilterType, EntityAlias, EntityAliasFilter, EntityAliasFilterResul
import { entityFields, EntityInfo, ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models'; import { entityFields, EntityInfo, ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models';
import { EntityRelationInfo, EntitySearchDirection } from '@shared/models/relation.models'; import { EntityRelationInfo, EntitySearchDirection } from '@shared/models/relation.models';
import { EntityRelationService } from '@core/http/entity-relation.service'; import { EntityRelationService } from '@core/http/entity-relation.service';
import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { deepClone, isDefined, isDefinedAndNotNull } from '@core/utils';
import { Asset } from '@shared/models/asset.models'; import { Asset } from '@shared/models/asset.models';
import { Device, DeviceCredentialsType } from '@shared/models/device.models'; import { Device, DeviceCredentialsType } from '@shared/models/device.models';
import { AttributeService } from '@core/http/attribute.service'; import { AttributeService } from '@core/http/attribute.service';
import { import {
createDefaultEntityDataPageLink, createDefaultEntityDataPageLink,
defaultEntityDataPageLink,
EntityData, EntityData,
EntityDataQuery, entityDataToEntityInfo, EntityDataQuery,
entityDataToEntityInfo,
EntityFilter, EntityFilter,
EntityKeyType entityInfoFields,
EntityKey,
EntityKeyType,
FilterPredicateType,
singleEntityDataPageLink, StringFilterPredicate,
StringOperation
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
@Injectable({ @Injectable({
@ -365,20 +372,53 @@ export class EntityService {
return this.http.post<PageData<EntityData>>('/api/entitiesQuery/find', query, defaultHttpOptionsFromConfig(config)); return this.http.post<PageData<EntityData>>('/api/entitiesQuery/find', query, defaultHttpOptionsFromConfig(config));
} }
public findSingleEntityInfoByEntityFilter(filter: EntityFilter, config?: RequestConfig): Observable<EntityInfo> { public findEntityInfosByFilterAndName(filter: EntityFilter,
searchText: string, config?: RequestConfig): Observable<PageData<EntityInfo>> {
const nameField: EntityKey = {
type: EntityKeyType.ENTITY_FIELD,
key: 'name'
};
const query: EntityDataQuery = { const query: EntityDataQuery = {
entityFilter: filter, entityFilter: filter,
pageLink: createDefaultEntityDataPageLink(1), pageLink: {
entityFields: [ pageSize: 10,
{ page: 0,
type: EntityKeyType.ENTITY_FIELD, sortOrder: {
key: 'name' key: nameField,
}, direction: Direction.ASC
}
},
entityFields: entityInfoFields,
keyFilters: searchText && searchText.length ? [
{ {
type: EntityKeyType.ENTITY_FIELD, key: nameField,
key: 'label' predicate: {
type: FilterPredicateType.STRING,
operation: StringOperation.STARTS_WITH,
ignoreCase: true,
value: searchText
}
} }
] ] : null
};
return this.findEntityDataByQuery(query, config).pipe(
map((data) => {
const entityInfos = data.data.map(entityData => entityDataToEntityInfo(entityData));
return {
data: entityInfos,
hasNext: data.hasNext,
totalElements: data.totalElements,
totalPages: data.totalPages
};
})
);
}
public findSingleEntityInfoByEntityFilter(filter: EntityFilter, config?: RequestConfig): Observable<EntityInfo> {
const query: EntityDataQuery = {
entityFilter: filter,
pageLink: createDefaultEntityDataPageLink(1),
entityFields: entityInfoFields
}; };
return this.findEntityDataByQuery(query, config).pipe( return this.findEntityDataByQuery(query, config).pipe(
map((data) => { map((data) => {
@ -602,35 +642,19 @@ export class EntityService {
); );
} }
public createDatasourcesFromSubscriptionsInfo(subscriptionsInfo: Array<SubscriptionInfo>): Observable<Array<Datasource>> { public createDatasourcesFromSubscriptionsInfo(subscriptionsInfo: Array<SubscriptionInfo>): Array<Datasource> {
const observables = new Array<Observable<Array<Datasource>>>(); const datasources = subscriptionsInfo.map(subscriptionInfo => this.createDatasourceFromSubscriptionInfo(subscriptionInfo));
subscriptionsInfo.forEach((subscriptionInfo) => { this.utils.generateColors(datasources);
observables.push(this.createDatasourcesFromSubscriptionInfo(subscriptionInfo)); return datasources;
});
return forkJoin(observables).pipe(
map((arrayOfDatasources) => {
const result = new Array<Datasource>();
arrayOfDatasources.forEach((datasources) => {
result.push(...datasources);
});
this.utils.generateColors(result);
return result;
})
);
} }
public createAlarmSourceFromSubscriptionInfo(subscriptionInfo: SubscriptionInfo): Observable<Datasource> { public createAlarmSourceFromSubscriptionInfo(subscriptionInfo: SubscriptionInfo): Datasource {
if (subscriptionInfo.entityId && subscriptionInfo.entityType) { if (subscriptionInfo.entityId && subscriptionInfo.entityType) {
return this.getEntity(subscriptionInfo.entityType, subscriptionInfo.entityId, const alarmSource = this.createDatasourceFromSubscriptionInfo(subscriptionInfo);
{ignoreLoading: true, ignoreErrors: true}).pipe( this.utils.generateColors([alarmSource]);
map((entity) => { return alarmSource;
const alarmSource = this.createDatasourceFromSubscription(subscriptionInfo, entity);
this.utils.generateColors([alarmSource]);
return alarmSource;
})
);
} else { } else {
return throwError(null); throw new Error('Can\'t crate alarm source without entityId information!');
} }
} }
@ -1156,58 +1180,41 @@ export class EntityService {
return entityId; return entityId;
} }
private createDatasourcesFromSubscriptionInfo(subscriptionInfo: SubscriptionInfo): Observable<Array<Datasource>> { private createDatasourceFromSubscriptionInfo(subscriptionInfo: SubscriptionInfo): Datasource {
subscriptionInfo = this.validateSubscriptionInfo(subscriptionInfo); subscriptionInfo = this.validateSubscriptionInfo(subscriptionInfo);
let datasource: Datasource = null;
if (subscriptionInfo.type === DatasourceType.entity) { if (subscriptionInfo.type === DatasourceType.entity) {
return this.resolveEntitiesFromSubscriptionInfo(subscriptionInfo).pipe( datasource = {
map((entities) => { type: subscriptionInfo.type,
const datasources = new Array<Datasource>(); entityName: subscriptionInfo.entityName,
entities.forEach((entity) => { name: subscriptionInfo.entityName,
datasources.push(this.createDatasourceFromSubscription(subscriptionInfo, entity)); entityType: subscriptionInfo.entityType,
}); entityId: subscriptionInfo.entityId,
return datasources; dataKeys: []
}) };
); this.prepareEntityFilterFromSubscriptionInfo(datasource, subscriptionInfo);
} else if (subscriptionInfo.type === DatasourceType.function) { } else if (subscriptionInfo.type === DatasourceType.function) {
return of([this.createDatasourceFromSubscription(subscriptionInfo)]); datasource = {
} else { type: subscriptionInfo.type,
return of([]); name: subscriptionInfo.name || DatasourceType.function,
dataKeys: []
};
} }
} if (datasource !== null) {
if (subscriptionInfo.timeseries) {
private resolveEntitiesFromSubscriptionInfo(subscriptionInfo: SubscriptionInfo): Observable<Array<BaseData<EntityId>>> { this.createDatasourceKeys(subscriptionInfo.timeseries, DataKeyType.timeseries, datasource);
if (subscriptionInfo.entityId) {
if (subscriptionInfo.entityName) {
const entity: BaseData<EntityId> = {
id: {id: subscriptionInfo.entityId, entityType: subscriptionInfo.entityType},
name: subscriptionInfo.entityName
};
return of([entity]);
} else {
return this.getEntity(subscriptionInfo.entityType, subscriptionInfo.entityId,
{ignoreLoading: true, ignoreErrors: true}).pipe(
map((entity) => [entity]),
catchError(e => of([]))
);
} }
} else if (subscriptionInfo.entityName || subscriptionInfo.entityNamePrefix || subscriptionInfo.entityIds) { if (subscriptionInfo.attributes) {
let entitiesObservable: Observable<Array<BaseData<EntityId>>>; this.createDatasourceKeys(subscriptionInfo.attributes, DataKeyType.attribute, datasource);
if (subscriptionInfo.entityName) { }
entitiesObservable = this.getEntitiesByNameFilter(subscriptionInfo.entityType, subscriptionInfo.entityName, if (subscriptionInfo.functions) {
1, null, {ignoreLoading: true, ignoreErrors: true}); this.createDatasourceKeys(subscriptionInfo.functions, DataKeyType.function, datasource);
} else if (subscriptionInfo.entityNamePrefix) { }
entitiesObservable = this.getEntitiesByNameFilter(subscriptionInfo.entityType, subscriptionInfo.entityNamePrefix, if (subscriptionInfo.alarmFields) {
100, null, {ignoreLoading: true, ignoreErrors: true}); this.createDatasourceKeys(subscriptionInfo.alarmFields, DataKeyType.alarm, datasource);
} else if (subscriptionInfo.entityIds) {
entitiesObservable = this.getEntities(subscriptionInfo.entityType, subscriptionInfo.entityIds,
{ignoreLoading: true, ignoreErrors: true});
} }
return entitiesObservable.pipe(
catchError(e => of([]))
);
} else {
return of([]);
} }
return datasource;
} }
private validateSubscriptionInfo(subscriptionInfo: SubscriptionInfo): SubscriptionInfo { private validateSubscriptionInfo(subscriptionInfo: SubscriptionInfo): SubscriptionInfo {
@ -1228,45 +1235,40 @@ export class EntityService {
return subscriptionInfo; return subscriptionInfo;
} }
private createDatasourceFromSubscription(subscriptionInfo: SubscriptionInfo, entity?: BaseData<EntityId>): Datasource { private prepareEntityFilterFromSubscriptionInfo(datasource: Datasource, subscriptionInfo: SubscriptionInfo) {
let datasource: Datasource; if (subscriptionInfo.entityId) {
if (subscriptionInfo.type === DatasourceType.entity) { datasource.entityFilter = {
datasource = { type: AliasFilterType.singleEntity,
type: subscriptionInfo.type, singleEntity: {
entityName: entity.name, entityType: subscriptionInfo.entityType,
name: entity.name, id: subscriptionInfo.entityId
}
};
datasource.pageLink = singleEntityDataPageLink;
} else if (subscriptionInfo.entityName || subscriptionInfo.entityNamePrefix) {
let nameFilter;
let pageLink;
if (isDefined(subscriptionInfo.entityName) && subscriptionInfo.entityName.length) {
nameFilter = subscriptionInfo.entityName;
pageLink = deepClone(singleEntityDataPageLink);
} else {
nameFilter = subscriptionInfo.entityNamePrefix;
pageLink = deepClone(defaultEntityDataPageLink);
}
datasource.entityFilter = {
type: AliasFilterType.entityName,
entityType: subscriptionInfo.entityType, entityType: subscriptionInfo.entityType,
entityId: entity.id.id, entityNameFilter: nameFilter
pageLink: {
pageSize: 1,
page: 0
},
entityFilter: {
type: AliasFilterType.singleEntity,
singleEntity: entity.id
},
dataKeys: []
}; };
} else if (subscriptionInfo.type === DatasourceType.function) { datasource.pageLink = pageLink;
datasource = { } else if (subscriptionInfo.entityIds) {
type: subscriptionInfo.type, datasource.entityFilter = {
name: subscriptionInfo.name || DatasourceType.function, type: AliasFilterType.entityList,
dataKeys: [] entityType: subscriptionInfo.entityType,
entityList: subscriptionInfo.entityIds
}; };
datasource.pageLink = deepClone(defaultEntityDataPageLink);
} }
if (subscriptionInfo.timeseries) {
this.createDatasourceKeys(subscriptionInfo.timeseries, DataKeyType.timeseries, datasource);
}
if (subscriptionInfo.attributes) {
this.createDatasourceKeys(subscriptionInfo.attributes, DataKeyType.attribute, datasource);
}
if (subscriptionInfo.functions) {
this.createDatasourceKeys(subscriptionInfo.functions, DataKeyType.function, datasource);
}
if (subscriptionInfo.alarmFields) {
this.createDatasourceKeys(subscriptionInfo.alarmFields, DataKeyType.alarm, datasource);
}
return datasource;
} }
private createDatasourceKeys(keyInfos: Array<KeyInfo>, type: DataKeyType, datasource: Datasource) { private createDatasourceKeys(keyInfos: Array<KeyInfo>, type: DataKeyType, datasource: Datasource) {

2
ui-ngx/src/app/core/ws/telemetry-websocket.service.ts

@ -299,7 +299,7 @@ export class TelemetryWebsocketService implements TelemetryService {
} }
private onClose(closeEvent: CloseEvent) { private onClose(closeEvent: CloseEvent) {
if (closeEvent && closeEvent.code > 1000 && closeEvent.code !== 1006) { if (closeEvent && closeEvent.code > 1001 && closeEvent.code !== 1006) {
this.showWsError(closeEvent.code, closeEvent.reason); this.showWsError(closeEvent.code, closeEvent.reason);
} }
this.isOpening = false; this.isOpening = false;

43
ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.html

@ -0,0 +1,43 @@
<!--
Copyright © 2016-2020 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.
-->
<mat-form-field [formGroup]="selectEntityInfoFormGroup" class="mat-block">
<input matInput type="text" placeholder="{{ alias }}"
#entityInfoInput
formControlName="entityInfo"
[required]="required"
[matAutocomplete]="entityInfoAutocomplete">
<button *ngIf="selectEntityInfoFormGroup.get('entityInfo').value && !disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Clear"
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<mat-autocomplete
class="tb-autocomplete"
#entityInfoAutocomplete="matAutocomplete"
[displayWith]="displayEntityInfoFn">
<mat-option *ngFor="let entityInfo of filteredEntityInfos | async" [value]="entityInfo">
<span [innerHTML]="entityInfo.name | highlight:searchText"></span>
</mat-option>
<mat-option *ngIf="!(filteredEntityInfos | async)?.length" [value]="null">
<span>
{{ translate.get('entity.no-entities-matching', {entity: searchText}) | async }}
</span>
</mat-option>
</mat-autocomplete>
</mat-form-field>

156
ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts

@ -0,0 +1,156 @@
///
/// Copyright © 2016-2020 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 { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, mergeMap, startWith, tap } from 'rxjs/operators';
import { PageData } from '@shared/models/page/page-data';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { EntityInfo } from '@shared/models/entity.models';
import { EntityFilter } from '@shared/models/query/query.models';
import { EntityService } from '@core/http/entity.service';
@Component({
selector: 'tb-aliases-entity-autocomplete',
templateUrl: './aliases-entity-autocomplete.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AliasesEntityAutocompleteComponent),
multi: true
}]
})
export class AliasesEntityAutocompleteComponent implements ControlValueAccessor, OnInit, AfterViewInit {
selectEntityInfoFormGroup: FormGroup;
modelValue: EntityInfo | null;
@Input()
alias: string;
@Input()
entityFilter: EntityFilter;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
@ViewChild('entityInfoInput', {static: true}) entityInfoInput: ElementRef;
filteredEntityInfos: Observable<Array<EntityInfo>>;
searchText = '';
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
public translate: TranslateService,
private entityService: EntityService,
private fb: FormBuilder) {
this.selectEntityInfoFormGroup = this.fb.group({
entityInfo: [null]
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.filteredEntityInfos = this.selectEntityInfoFormGroup.get('entityInfo').valueChanges
.pipe(
tap(value => {
let modelValue;
if (typeof value === 'string' || !value) {
modelValue = null;
} else {
modelValue = value;
}
this.updateView(modelValue);
}),
startWith<string | EntityInfo>(''),
map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
mergeMap(name => this.fetchEntityInfos(name) )
);
}
ngAfterViewInit(): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
writeValue(value: EntityInfo | null): void {
this.searchText = '';
if (value != null) {
this.modelValue = value;
this.selectEntityInfoFormGroup.get('entityInfo').patchValue(value, {emitEvent: true});
} else {
this.modelValue = null;
this.selectEntityInfoFormGroup.get('entityInfo').patchValue(null, {emitEvent: true});
}
}
updateView(value: EntityInfo | null) {
if (this.modelValue !== value) {
this.modelValue = value;
this.propagateChange(this.modelValue);
}
}
displayEntityInfoFn(entityInfo?: EntityInfo): string | undefined {
return entityInfo ? entityInfo.name : undefined;
}
fetchEntityInfos(searchText?: string): Observable<Array<EntityInfo>> {
this.searchText = searchText;
return this.getEntityInfos(this.searchText).pipe(
map(pageData => {
return pageData.data;
})
);
}
getEntityInfos(searchText: string): Observable<PageData<EntityInfo>> {
return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true});
}
clear() {
this.selectEntityInfoFormGroup.get('entityInfo').patchValue(null, {emitEvent: true});
setTimeout(() => {
this.entityInfoInput.nativeElement.blur();
this.entityInfoInput.nativeElement.focus();
}, 0);
}
}

11
ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.html

@ -17,7 +17,14 @@
--> -->
<div fxLayout="column" class="mat-content mat-padding"> <div fxLayout="column" class="mat-content mat-padding">
<div fxLayout="row" *ngFor="let alias of entityAliasesInfo | keyvalue"> <div fxLayout="row" *ngFor="let alias of entityAliasesInfo | keyvalue">
<mat-form-field fxFlex> <tb-aliases-entity-autocomplete fxFlex
required
[alias]="alias.value.alias"
[entityFilter]="alias.value.entityFilter"
[(ngModel)]="alias.value.currentEntity"
(ngModelChange)="currentAliasEntityChanged(alias.key, alias.value.currentEntity)">
</tb-aliases-entity-autocomplete>
<!--mat-form-field fxFlex>
<mat-label>{{alias.value.alias}}</mat-label> <mat-label>{{alias.value.alias}}</mat-label>
<mat-select [(ngModel)]="alias.value.selectedId" <mat-select [(ngModel)]="alias.value.selectedId"
(ngModelChange)="currentAliasEntityChanged(alias.key, alias.value.selectedId)"> (ngModelChange)="currentAliasEntityChanged(alias.key, alias.value.selectedId)">
@ -25,6 +32,6 @@
{{resolvedEntity.name}} {{resolvedEntity.name}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field-->
</div> </div>
</div> </div>

7
ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.ts

@ -17,6 +17,7 @@
import { Component, Inject, InjectionToken } from '@angular/core'; import { Component, Inject, InjectionToken } from '@angular/core';
import { AliasInfo, IAliasController } from '@core/api/widget-api.models'; import { AliasInfo, IAliasController } from '@core/api/widget-api.models';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { EntityInfo } from '@shared/models/entity.models';
export const ALIASES_ENTITY_SELECT_PANEL_DATA = new InjectionToken<any>('AliasesEntitySelectPanelData'); export const ALIASES_ENTITY_SELECT_PANEL_DATA = new InjectionToken<any>('AliasesEntitySelectPanelData');
@ -38,9 +39,9 @@ export class AliasesEntitySelectPanelComponent {
this.entityAliasesInfo = this.data.entityAliasesInfo; this.entityAliasesInfo = this.data.entityAliasesInfo;
} }
public currentAliasEntityChanged(aliasId: string, selectedId: string) { public currentAliasEntityChanged(aliasId: string, selected: EntityInfo | null) {
const resolvedEntities = this.entityAliasesInfo[aliasId].resolvedEntities; // const resolvedEntities = this.entityAliasesInfo[aliasId].resolvedEntities;
const selected = resolvedEntities.find((entity) => entity.id === selectedId); // const selected = resolvedEntities.find((entity) => entity.id === selectedId);
if (selected) { if (selected) {
this.data.aliasController.updateCurrentAliasEntity(aliasId, selected); this.data.aliasController.updateCurrentAliasEntity(aliasId, selected);
} }

4
ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.ts

@ -129,7 +129,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy {
overlayRef, overlayRef,
{ {
aliasController: this.aliasController, aliasController: this.aliasController,
entityAliasesInfo: this.entityAliasesInfo entityAliasesInfo: deepClone(this.entityAliasesInfo)
} }
); );
overlayRef.attach(new ComponentPortal(AliasesEntitySelectPanelComponent, this.viewContainerRef, injector)); overlayRef.attach(new ComponentPortal(AliasesEntitySelectPanelComponent, this.viewContainerRef, injector));
@ -178,7 +178,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy {
for (const aliasId of Object.keys(allEntityAliases)) { for (const aliasId of Object.keys(allEntityAliases)) {
const aliasInfo = this.aliasController.getInstantAliasInfo(aliasId); const aliasInfo = this.aliasController.getInstantAliasInfo(aliasId);
if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity
&& aliasInfo.resolvedEntities.length > 1) { && aliasInfo.entityFilter /*aliasInfo.resolvedEntities.length > 1*/) {
this.entityAliasesInfo[aliasId] = deepClone(aliasInfo); this.entityAliasesInfo[aliasId] = deepClone(aliasInfo);
this.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id; this.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id;
this.hasSelectableAliasEntities = true; this.hasSelectableAliasEntities = true;

3
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -65,6 +65,7 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten
import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
import { SelectTargetLayoutDialogComponent } from '@home/components/dashboard/select-target-layout-dialog.component'; import { SelectTargetLayoutDialogComponent } from '@home/components/dashboard/select-target-layout-dialog.component';
import { SelectTargetStateDialogComponent } from '@home/components/dashboard/select-target-state-dialog.component'; import { SelectTargetStateDialogComponent } from '@home/components/dashboard/select-target-state-dialog.component';
import { AliasesEntityAutocompleteComponent } from '@home/components/alias/aliases-entity-autocomplete.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -88,6 +89,7 @@ import { SelectTargetStateDialogComponent } from '@home/components/dashboard/sel
EditAttributeValuePanelComponent, EditAttributeValuePanelComponent,
AliasesEntitySelectPanelComponent, AliasesEntitySelectPanelComponent,
AliasesEntitySelectComponent, AliasesEntitySelectComponent,
AliasesEntityAutocompleteComponent,
EntityAliasesDialogComponent, EntityAliasesDialogComponent,
EntityAliasDialogComponent, EntityAliasDialogComponent,
DashboardComponent, DashboardComponent,
@ -131,6 +133,7 @@ import { SelectTargetStateDialogComponent } from '@home/components/dashboard/sel
AlarmTableComponent, AlarmTableComponent,
AttributeTableComponent, AttributeTableComponent,
AliasesEntitySelectComponent, AliasesEntitySelectComponent,
AliasesEntityAutocompleteComponent,
EntityAliasesDialogComponent, EntityAliasesDialogComponent,
EntityAliasDialogComponent, EntityAliasDialogComponent,
DashboardComponent, DashboardComponent,

5
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html

@ -86,9 +86,12 @@
*matRowDef="let entity; columns: displayedColumns;" *matRowDef="let entity; columns: displayedColumns;"
(click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
</table> </table>
<span [fxShow]="entityDatasource.isEmpty() | async" <span [fxShow]="(entityDatasource.isEmpty() | async) && !entityDatasource.dataLoading"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>entity.no-entities-prompt</span> class="no-data-found" translate>entity.no-entities-prompt</span>
<span [fxShow]="entityDatasource.dataLoading"
fxLayoutAlign="center center"
class="no-data-found">{{ 'common.loading' | translate }}</span>
</div> </div>
<mat-divider *ngIf="displayPagination"></mat-divider> <mat-divider *ngIf="displayPagination"></mat-divider>
<mat-paginator *ngIf="displayPagination" <mat-paginator *ngIf="displayPagination"

69
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -151,15 +151,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private translate: TranslateService, private translate: TranslateService,
private domSanitizer: DomSanitizer) { private domSanitizer: DomSanitizer) {
super(store); super(store);
// const sortOrder: EntityDataSortOrder = sortOrderFromString(this.defaultSortOrder);
this.pageLink = { this.pageLink = {
page: 0, page: 0,
pageSize: this.defaultPageSize, pageSize: this.defaultPageSize,
textSearch: null, textSearch: null,
dynamic: true dynamic: true
}; };
// new PageLink(this.defaultPageSize, 0, null, sortOrder);
} }
ngOnInit(): void { ngOnInit(): void {
@ -566,43 +563,15 @@ class EntityDatasource implements DataSource<EntityData> {
private entitiesSubject = new BehaviorSubject<EntityData[]>([]); private entitiesSubject = new BehaviorSubject<EntityData[]>([]);
private pageDataSubject = new BehaviorSubject<PageData<EntityData>>(emptyPageData<EntityData>()); private pageDataSubject = new BehaviorSubject<PageData<EntityData>>(emptyPageData<EntityData>());
// private allEntities: Array<EntityData> = [];
// private allEntitiesSubject = new BehaviorSubject<EntityData[]>([]);
// private allEntities$: Observable<Array<EntityData>> = this.allEntitiesSubject.asObservable();
private currentEntity: EntityData = null; private currentEntity: EntityData = null;
public dataLoading = true;
constructor( constructor(
private translate: TranslateService, private translate: TranslateService,
private dataKeys: Array<DataKey>, private dataKeys: Array<DataKey>,
private subscription: IWidgetSubscription private subscription: IWidgetSubscription
// datasources: Array<Datasource>
) { ) {
/*
for (const datasource of datasources) {
if (datasource.type === DatasourceType.entity && !datasource.entityId) {
continue;
}
const entity: EntityData = {
id: {} as EntityId,
entityName: datasource.entityName,
entityLabel: datasource.entityLabel ? datasource.entityLabel : datasource.entityName
};
if (datasource.entityId) {
entity.id.id = datasource.entityId;
}
if (datasource.entityType) {
entity.id.entityType = datasource.entityType;
entity.entityType = this.translate.instant(entityTypeTranslations.get(datasource.entityType).type);
} else {
entity.entityType = '';
}
this.dataKeys.forEach((dataKey) => {
entity[dataKey.label] = '';
});
this.allEntities.push(entity);
}
this.allEntitiesSubject.next(this.allEntities);*/
} }
connect(collectionViewer: CollectionViewer): Observable<EntityData[] | ReadonlyArray<EntityData>> { connect(collectionViewer: CollectionViewer): Observable<EntityData[] | ReadonlyArray<EntityData>> {
@ -615,15 +584,8 @@ class EntityDatasource implements DataSource<EntityData> {
} }
loadEntities(pageLink: EntityDataPageLink, keyFilters: KeyFilter[]) { loadEntities(pageLink: EntityDataPageLink, keyFilters: KeyFilter[]) {
this.dataLoading = true;
this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters);
/* this.fetchEntities(pageLink).pipe(
catchError(() => of(emptyPageData<EntityData>())),
).subscribe(
(pageData) => {
this.entitiesSubject.next(pageData.data);
this.pageDataSubject.next(pageData);
}
);*/
} }
dataUpdated() { dataUpdated() {
@ -641,6 +603,7 @@ class EntityDatasource implements DataSource<EntityData> {
}; };
this.entitiesSubject.next(entities); this.entitiesSubject.next(entities);
this.pageDataSubject.next(entitiesPageData); this.pageDataSubject.next(entitiesPageData);
this.dataLoading = false;
} }
private datasourceToEntityData(datasource: Datasource, data: DatasourceData[]): EntityData { private datasourceToEntityData(datasource: Datasource, data: DatasourceData[]): EntityData {
@ -670,24 +633,6 @@ class EntityDatasource implements DataSource<EntityData> {
return entity; return entity;
} }
/* updateEntitiesData(data: DatasourceData[]) {
for (let i = 0; i < this.allEntities.length; i++) {
const entity = this.allEntities[i];
for (let a = 0; a < this.dataKeys.length; a++) {
const dataKey = this.dataKeys[a];
const index = i * this.dataKeys.length + a;
const keyData = data[index].data;
if (keyData && keyData.length && keyData[0].length > 1) {
const value = keyData[0][1];
entity[dataKey.label] = value;
} else {
entity[dataKey.label] = '';
}
}
}
this.allEntitiesSubject.next(this.allEntities);
}*/
isEmpty(): Observable<boolean> { isEmpty(): Observable<boolean> {
return this.entitiesSubject.pipe( return this.entitiesSubject.pipe(
map((entities) => !entities.length) map((entities) => !entities.length)
@ -713,10 +658,4 @@ class EntityDatasource implements DataSource<EntityData> {
return (this.currentEntity && entity && this.currentEntity.id && entity.id) && return (this.currentEntity && entity && this.currentEntity.id && entity.id) &&
(this.currentEntity.id.id === entity.id.id); (this.currentEntity.id.id === entity.id.id);
} }
/* private fetchEntities(pageLink: PageLink): Observable<PageData<EntityData>> {
return this.allEntities$.pipe(
map((data) => pageLink.filterData(data))
);
}*/
} }

42
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts

@ -28,7 +28,7 @@ import {
UnitedMapSettings UnitedMapSettings
} from './map-models'; } from './map-models';
import { Marker } from './markers'; import { Marker } from './markers';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable, of } from 'rxjs';
import { filter } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { Polyline } from './polyline'; import { Polyline } from './polyline';
import { Polygon } from './polygon'; import { Polygon } from './polygon';
@ -113,10 +113,11 @@ export default abstract class LeafletMap {
dsItem.setAttribute('style', 'font-size: 14px'); dsItem.setAttribute('style', 'font-size: 14px');
dsItem.onclick = () => { dsItem.onclick = () => {
const updatedEnttity = { ...ds, ...customLatLng }; const updatedEnttity = { ...ds, ...customLatLng };
this.saveMarkerLocation(updatedEnttity); this.saveMarkerLocation(updatedEnttity).subscribe(() => {
this.map.removeLayer(newMarker); this.map.removeLayer(newMarker);
this.deleteMarker(ds.entityName); this.deleteMarker(ds.entityName);
this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options); this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options);
});
} }
datasourcesList.append(dsItem); datasourcesList.append(dsItem);
}); });
@ -171,12 +172,12 @@ export default abstract class LeafletMap {
this.map$.next(this.map); this.map$.next(this.map);
} }
public setDataSources(dataSources) { public setDataSources(dataSources: FormattedData[]) {
this.datasources = dataSources; this.datasources = dataSources;
} }
public saveMarkerLocation(_e) { public saveMarkerLocation(_e: FormattedData, lat?: number, lng?: number): Observable<any> {
return of(null);
} }
createLatLng(lat: number, lng: number): L.LatLng { createLatLng(lat: number, lng: number): L.LatLng {
@ -248,35 +249,40 @@ export default abstract class LeafletMap {
} }
} }
updateData(data: DatasourceData[], drawRoutes: boolean, showPolygon: boolean) { updateData(data: DatasourceData[], formattedData: FormattedData[], drawRoutes: boolean, showPolygon: boolean) {
this.ready$.subscribe(() => { this.ready$.subscribe(() => {
if (drawRoutes) { if (drawRoutes) {
this.updatePolylines(parseArray(data), false); this.updatePolylines(parseArray(data), false);
} }
if (showPolygon) { if (showPolygon) {
this.updatePolygons(parseData(data), false); this.updatePolygons(formattedData, false);
} }
this.updateMarkers(parseData(data), false); this.updateMarkers(formattedData, false);
this.updateBoundsInternal(drawRoutes, showPolygon); this.updateBoundsInternal(drawRoutes, showPolygon);
}); });
} }
private updateBoundsInternal(drawRoutes: boolean, showPolygon: boolean) { private updateBoundsInternal(drawRoutes: boolean, showPolygon: boolean) {
this.bounds = new L.LatLngBounds(null, null); const bounds = new L.LatLngBounds(null, null);
if (drawRoutes) { if (drawRoutes) {
this.polylines.forEach((polyline) => { this.polylines.forEach((polyline) => {
this.bounds.extend(polyline.leafletPoly.getBounds()); bounds.extend(polyline.leafletPoly.getBounds());
}); });
} }
if (showPolygon) { if (showPolygon) {
this.polygons.forEach((polygon) => { this.polygons.forEach((polygon) => {
this.bounds.extend(polygon.leafletPoly.getBounds()); bounds.extend(polygon.leafletPoly.getBounds());
}); });
} }
this.markers.forEach((marker) => { this.markers.forEach((marker) => {
this.bounds.extend(marker.leafletMarker.getLatLng()); bounds.extend(marker.leafletMarker.getLatLng());
}); });
this.fitBounds(this.bounds);
const mapBounds = this.map.getBounds();
if (bounds.isValid() && (!this.bounds || !mapBounds.contains(bounds))) {
this.bounds = bounds;
this.fitBounds(bounds);
}
} }
// Markers // Markers
@ -319,9 +325,9 @@ export default abstract class LeafletMap {
}); });
} }
dragMarker = (e, data = {}) => { dragMarker = (e, data = {} as FormattedData) => {
if (e.type !== 'dragend') return; if (e.type !== 'dragend') return;
this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }); this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }).subscribe();
} }
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings,

3
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts

@ -23,6 +23,7 @@ import {
openstreetMapSettingsSchema, tencentMapSettingsSchema, openstreetMapSettingsSchema, tencentMapSettingsSchema,
googleMapSettingsSchema, hereMapSettingsSchema, imageMapSettingsSchema googleMapSettingsSchema, hereMapSettingsSchema, imageMapSettingsSchema
} from './schemes'; } from './schemes';
import { EntityType } from '@shared/models/entity-type.models';
export const DEFAULT_MAP_PAGE_SIZE = 1024; export const DEFAULT_MAP_PAGE_SIZE = 1024;
@ -112,6 +113,8 @@ export type MarkerSettings = {
export interface FormattedData { export interface FormattedData {
$datasource: Datasource; $datasource: Datasource;
entityName: string; entityName: string;
entityId: string;
entityType: EntityType;
dsIndex: number; dsIndex: number;
deviceType: string; deviceType: string;
[key: string]: any [key: string]: any

72
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts

@ -16,7 +16,7 @@
import { import {
DEFAULT_MAP_PAGE_SIZE, DEFAULT_MAP_PAGE_SIZE,
defaultSettings, defaultSettings, FormattedData,
hereProviders, hereProviders,
MapProviders, MapProviders,
providerSets, providerSets,
@ -43,6 +43,8 @@ import { TranslateService } from '@ngx-translate/core';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import _ from 'lodash'; import _ from 'lodash';
import { EntityDataPageLink } from '@shared/models/query/query.models'; import { EntityDataPageLink } from '@shared/models/query/query.models';
import { isDefined } from '@core/utils';
import { forkJoin, Observable, of } from 'rxjs';
// @dynamic // @dynamic
export class MapWidgetController implements MapWidgetInterface { export class MapWidgetController implements MapWidgetInterface {
@ -77,10 +79,8 @@ export class MapWidgetController implements MapWidgetInterface {
} }
parseWithTranslation.setTranslate(this.translate); parseWithTranslation.setTranslate(this.translate);
this.map = new MapClass(this.ctx, $element, this.settings); this.map = new MapClass(this.ctx, $element, this.settings);
(this.ctx as any).mapInstance = this.map;
this.map.saveMarkerLocation = this.setMarkerLocation; this.map.saveMarkerLocation = this.setMarkerLocation;
if (this.settings.draggableMarker) {
this.map.setDataSources(parseData(this.data));
}
this.pageLink = { this.pageLink = {
page: 0, page: 0,
pageSize: DEFAULT_MAP_PAGE_SIZE, pageSize: DEFAULT_MAP_PAGE_SIZE,
@ -185,7 +185,7 @@ export class MapWidgetController implements MapWidgetInterface {
}, entityName, null, entityLabel); }, entityName, null, entityLabel);
} }
setMarkerLocation = (e) => { setMarkerLocation = (e: FormattedData, lat?: number, lng?: number) => {
const attributeService = this.ctx.$injector.get(AttributeService); const attributeService = this.ctx.$injector.get(AttributeService);
const entityId: EntityId = { const entityId: EntityId = {
@ -194,34 +194,50 @@ export class MapWidgetController implements MapWidgetInterface {
}; };
const attributes = []; const attributes = [];
const timeseries = []; const timeseries = [];
const latLngProperties = [this.settings.latKeyName, this.settings.lngKeyName, this.settings.xPosKeyName, this.settings.yPosKeyName];
const latProperties = [this.settings.latKeyName, this.settings.xPosKeyName];
const lngProperties = [this.settings.lngKeyName, this.settings.yPosKeyName];
e.$datasource.dataKeys.forEach(key => { e.$datasource.dataKeys.forEach(key => {
if (latLngProperties.includes(key.name)) { let value;
const value = { if (latProperties.includes(key.name)) {
value = {
key: key.name, key: key.name,
value: e[key.name] value: isDefined(lat) ? lat : e[key.name]
}; };
if (key.type === DataKeyType.attribute) { } else if (lngProperties.includes(key.name)) {
attributes.push(value) value = {
} key: key.name,
if (key.type === DataKeyType.timeseries) { value: isDefined(lng) ? lng : e[key.name]
timeseries.push(value) };
} }
if (value) {
if (key.type === DataKeyType.attribute) {
attributes.push(value)
}
if (key.type === DataKeyType.timeseries) {
timeseries.push(value)
}
} }
}); });
const observables: Observable<any>[] = [];
if (timeseries.length) { if (timeseries.length) {
attributeService.saveEntityTimeseries( observables.push(attributeService.saveEntityTimeseries(
entityId, entityId,
LatestTelemetry.LATEST_TELEMETRY, LatestTelemetry.LATEST_TELEMETRY,
timeseries timeseries
).subscribe(() => { }); ));
} }
if (attributes.length) { if (attributes.length) {
attributeService.saveEntityAttributes( observables.push(attributeService.saveEntityAttributes(
entityId, entityId,
AttributeScope.SERVER_SCOPE, AttributeScope.SERVER_SCOPE,
attributes attributes
).subscribe(() => { }); ));
}
if (observables.length) {
return forkJoin(observables);
} else {
return of(null);
} }
} }
@ -257,7 +273,11 @@ export class MapWidgetController implements MapWidgetInterface {
} }
update() { update() {
this.map.updateData(this.data, this.drawRoutes, this.settings.showPolygon); const formattedData = parseData(this.data);
this.map.updateData(this.data, formattedData, this.drawRoutes, this.settings.showPolygon);
if (this.settings.draggableMarker) {
this.map.setDataSources(formattedData);
}
} }
resize() { resize() {

17
ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts

@ -16,11 +16,12 @@
import L from 'leaflet'; import L from 'leaflet';
import { FormattedData, MarkerSettings, PolygonSettings, PolylineSettings } from './map-models'; import { FormattedData, MarkerSettings, PolygonSettings, PolylineSettings } from './map-models';
import { Datasource } from '@app/shared/models/widget.models'; import { Datasource, DatasourceData } from '@app/shared/models/widget.models';
import _ from 'lodash'; import _ from 'lodash';
import { Observable, Observer, of } from 'rxjs'; import { Observable, Observer, of } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { createLabelFromDatasource, hashCode, isNumber, isUndefined, padValue } from '@core/utils'; import { createLabelFromDatasource, hashCode, isNumber, isUndefined, padValue } from '@core/utils';
import { Form } from '@angular/forms';
export function createTooltip(target: L.Layer, export function createTooltip(target: L.Layer,
settings: MarkerSettings | PolylineSettings | PolygonSettings, settings: MarkerSettings | PolylineSettings | PolygonSettings,
@ -236,12 +237,14 @@ export const parseWithTranslation = {
} }
} }
export function parseData(input: any[]): FormattedData[] { export function parseData(input: DatasourceData[]): FormattedData[] {
return _(input).groupBy(el => el?.datasource?.entityName) return _(input).groupBy(el => el?.datasource?.entityName)
.values().value().map((entityArray, i) => { .values().value().map((entityArray, i) => {
const obj = { const obj: FormattedData = {
entityName: entityArray[0]?.datasource?.entityName, entityName: entityArray[0]?.datasource?.entityName,
$datasource: entityArray[0]?.datasource as Datasource, entityId: entityArray[0]?.datasource?.entityId,
entityType: entityArray[0]?.datasource?.entityType,
$datasource: entityArray[0]?.datasource,
dsIndex: i, dsIndex: i,
deviceType: null deviceType: null
}; };
@ -256,12 +259,14 @@ export function parseData(input: any[]): FormattedData[] {
}); });
} }
export function parseArray(input: any[]): any[] { export function parseArray(input: DatasourceData[]): FormattedData[][] {
return _(input).groupBy(el => el?.datasource?.entityName) return _(input).groupBy(el => el?.datasource?.entityName)
.values().value().map((entityArray, dsIndex) => .values().value().map((entityArray, dsIndex) =>
entityArray[0].data.map((el, i) => { entityArray[0].data.map((el, i) => {
const obj = { const obj: FormattedData = {
entityName: entityArray[0]?.datasource?.entityName, entityName: entityArray[0]?.datasource?.entityName,
entityId: entityArray[0]?.datasource?.entityId,
entityType: entityArray[0]?.datasource?.entityType,
$datasource: entityArray[0]?.datasource, $datasource: entityArray[0]?.datasource,
dsIndex: i, dsIndex: i,
time: el[0], time: el[0],

7
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts

@ -110,8 +110,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
this.settings.tooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']); this.settings.tooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']);
this.settings.labelFunction = parseFunction(this.settings.labelFunction, ['data', 'dsData', 'dsIndex']); this.settings.labelFunction = parseFunction(this.settings.labelFunction, ['data', 'dsData', 'dsIndex']);
this.normalizationStep = this.settings.normalizationStep; this.normalizationStep = this.settings.normalizationStep;
const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; const subscription = this.ctx.defaultSubscription;
if (subscription) subscription.callbacks.onDataUpdated = () => { subscription.callbacks.onDataUpdated = () => {
this.historicalData = parseArray(this.ctx.data).filter(arr => arr.length); this.historicalData = parseArray(this.ctx.data).filter(arr => arr.length);
if (this.historicalData.length) { if (this.historicalData.length) {
this.calculateIntervals(); this.calculateIntervals();
@ -123,8 +123,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
} }
ngAfterViewInit() { ngAfterViewInit() {
const ctxCopy: WidgetContext = _.cloneDeep(this.ctx); this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement);
this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement);
this.mapResize$ = new ResizeObserver(() => { this.mapResize$ = new ResizeObserver(() => {
this.mapWidget.resize(); this.mapWidget.resize();
}); });

3
ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts

@ -356,8 +356,7 @@ export class WidgetComponentService {
result.typeParameters.maxDataKeys = -1; result.typeParameters.maxDataKeys = -1;
} }
if (isUndefined(result.typeParameters.singleEntity)) { if (isUndefined(result.typeParameters.singleEntity)) {
result.typeParameters.singleEntity = result.typeParameters.maxDatasources === 1 && result.typeParameters.singleEntity = false;
result.typeParameters.maxDataKeys === 1;
} }
if (isUndefined(result.typeParameters.dataKeysOptional)) { if (isUndefined(result.typeParameters.dataKeysOptional)) {
result.typeParameters.dataKeysOptional = false; result.typeParameters.dataKeysOptional = false;

6
ui-ngx/src/app/modules/home/components/widget/widget.component.html

@ -35,6 +35,12 @@
<div class="tb-absolute-fill tb-widget-error" *ngIf="widgetErrorData"> <div class="tb-absolute-fill tb-widget-error" *ngIf="widgetErrorData">
<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span> <span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>
</div> </div>
<div class="tb-absolute-fill tb-widget-no-data" *ngIf="displayNoData">
<span fxLayoutAlign="center center"
style="text-transform: uppercase; display: flex;"
class="tb-absolute-fill"
translate>widget.no-data</span>
</div>
<div class="tb-absolute-fill tb-widget-loading" [fxShow]="loadingData" fxLayout="column" fxLayoutAlign="center center"> <div class="tb-absolute-fill tb-widget-loading" [fxShow]="loadingData" fxLayout="column" fxLayoutAlign="center center">
<mat-spinner color="accent" md-mode="indeterminate" diameter="40"></mat-spinner> <mat-spinner color="accent" md-mode="indeterminate" diameter="40"></mat-spinner>
</div> </div>

12
ui-ngx/src/app/modules/home/components/widget/widget.component.scss

@ -25,6 +25,18 @@
} }
} }
.tb-widget-no-data {
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, .75);
span {
color: #000;
text-align: center;
}
}
.tb-widget-loading { .tb-widget-loading {
z-index: 3; z-index: 3;
background: rgba(255, 255, 255, .15); background: rgba(255, 255, 255, .15);

38
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -123,6 +123,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
widgetTypeInstance: WidgetTypeInstance; widgetTypeInstance: WidgetTypeInstance;
widgetErrorData: ExceptionData; widgetErrorData: ExceptionData;
loadingData: boolean; loadingData: boolean;
displayNoData = false;
displayLegend: boolean; displayLegend: boolean;
legendConfig: LegendConfig; legendConfig: LegendConfig;
@ -493,6 +494,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
} }
} else { } else {
this.loadingData = false; this.loadingData = false;
this.displayNoData = true;
} }
this.detectChanges(); this.detectChanges();
} catch (e) { } catch (e) {
@ -625,6 +627,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds); subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds);
} }
if (subscriptionChanged && !this.typeParameters.useCustomDatasources) { if (subscriptionChanged && !this.typeParameters.useCustomDatasources) {
this.displayNoData = false;
this.reInit(); this.reInit();
} }
} }
@ -761,31 +764,24 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
options.useDashboardTimewindow = true; options.useDashboardTimewindow = true;
} }
} }
let createDatasourcesObservable: Observable<Array<Datasource> | Datasource>; let datasource: Datasource;
if (options.type === widgetType.alarm) { if (options.type === widgetType.alarm) {
createDatasourcesObservable = this.entityService.createAlarmSourceFromSubscriptionInfo(subscriptionsInfo[0]); datasource = this.entityService.createAlarmSourceFromSubscriptionInfo(subscriptionsInfo[0]);
} else { } else {
createDatasourcesObservable = this.entityService.createDatasourcesFromSubscriptionsInfo(subscriptionsInfo); datasource = this.entityService.createDatasourcesFromSubscriptionsInfo(subscriptionsInfo);
} }
createDatasourcesObservable.subscribe( if (options.type === widgetType.alarm) {
(result) => { options.alarmSource = datasource;
if (options.type === widgetType.alarm) { } else {
options.alarmSource = result as Datasource; options.datasources = [datasource];
} else { }
options.datasources = result as Array<Datasource>; this.createSubscription(options, subscribe).subscribe(
(subscription) => {
if (useDefaultComponents) {
this.defaultSubscriptionOptions(subscription, options);
} }
this.createSubscription(options, subscribe).subscribe( createSubscriptionSubject.next(subscription);
(subscription) => { createSubscriptionSubject.complete();
if (useDefaultComponents) {
this.defaultSubscriptionOptions(subscription, options);
}
createSubscriptionSubject.next(subscription);
createSubscriptionSubject.complete();
},
(err) => {
createSubscriptionSubject.error(err);
}
);
}, },
(err) => { (err) => {
createSubscriptionSubject.error(err); createSubscriptionSubject.error(err);

48
ui-ngx/src/app/shared/models/query/query.models.ts

@ -20,6 +20,7 @@ import { SortDirection } from '@angular/material/sort';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { EntityInfo } from '@shared/models/entity.models'; import { EntityInfo } from '@shared/models/entity.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { Datasource, DatasourceType } from '@shared/models/widget.models';
export enum EntityKeyType { export enum EntityKeyType {
ATTRIBUTE = 'ATTRIBUTE', ATTRIBUTE = 'ATTRIBUTE',
@ -85,35 +86,35 @@ export enum ComplexOperation {
} }
export interface StringFilterPredicate { export interface StringFilterPredicate {
type: FilterPredicateType.STRING,
operation: StringOperation; operation: StringOperation;
value: string; value: string;
ignoreCase: boolean; ignoreCase: boolean;
} }
export interface NumericFilterPredicate { export interface NumericFilterPredicate {
type: FilterPredicateType.NUMERIC,
operation: NumericOperation; operation: NumericOperation;
value: number; value: number;
} }
export interface BooleanFilterPredicate { export interface BooleanFilterPredicate {
type: FilterPredicateType.BOOLEAN,
operation: BooleanOperation; operation: BooleanOperation;
value: boolean; value: boolean;
} }
export interface ComplexFilterPredicate { export interface ComplexFilterPredicate {
type: FilterPredicateType.COMPLEX,
operation: ComplexOperation; operation: ComplexOperation;
predicates: Array<KeyFilterPredicate>; predicates: Array<KeyFilterPredicate>;
} }
export type KeyFilterPredicates = StringFilterPredicate & export type KeyFilterPredicate = StringFilterPredicate |
NumericFilterPredicate & NumericFilterPredicate |
BooleanFilterPredicate & BooleanFilterPredicate |
ComplexFilterPredicate; ComplexFilterPredicate;
export interface KeyFilterPredicate extends KeyFilterPredicates {
type?: FilterPredicateType;
}
export interface KeyFilter { export interface KeyFilter {
key: EntityKey; key: EntityKey;
predicate: KeyFilterPredicate; predicate: KeyFilterPredicate;
@ -163,6 +164,7 @@ export function createDefaultEntityDataPageLink(pageSize: number): EntityDataPag
} }
} }
export const singleEntityDataPageLink: EntityDataPageLink = createDefaultEntityDataPageLink(1);
export const defaultEntityDataPageLink: EntityDataPageLink = createDefaultEntityDataPageLink(1024); export const defaultEntityDataPageLink: EntityDataPageLink = createDefaultEntityDataPageLink(1024);
export interface EntityCountQuery { export interface EntityCountQuery {
@ -187,6 +189,17 @@ export interface EntityData {
timeseries: {[key: string]: Array<TsValue>}; timeseries: {[key: string]: Array<TsValue>};
} }
export const entityInfoFields: EntityKey[] = [
{
type: EntityKeyType.ENTITY_FIELD,
key: 'name'
},
{
type: EntityKeyType.ENTITY_FIELD,
key: 'label'
}
];
export function entityDataToEntityInfo(entityData: EntityData): EntityInfo { export function entityDataToEntityInfo(entityData: EntityData): EntityInfo {
const entityInfo: EntityInfo = { const entityInfo: EntityInfo = {
id: entityData.entityId.id, id: entityData.entityId.id,
@ -208,3 +221,24 @@ export function entityDataToEntityInfo(entityData: EntityData): EntityInfo {
} }
return entityInfo; return entityInfo;
} }
export function updateDatasourceFromEntityInfo(datasource: Datasource, entity: EntityInfo, createFilter = false) {
datasource.entity = {};
datasource.entityId = entity.id;
datasource.entityType = entity.entityType;
if (datasource.type === DatasourceType.entity) {
datasource.entityName = entity.name;
datasource.entityLabel = entity.label;
datasource.name = entity.name;
datasource.entityDescription = entity.entityDescription;
if (createFilter) {
datasource.entityFilter = {
type: AliasFilterType.singleEntity,
singleEntity: {
id: entity.id,
entityType: entity.entityType
}
};
}
}
}

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1711,7 +1711,8 @@
"widget-template-load-failed-error": "Failed to load widget template!", "widget-template-load-failed-error": "Failed to load widget template!",
"add": "Add Widget", "add": "Add Widget",
"undo": "Undo widget changes", "undo": "Undo widget changes",
"export": "Export widget" "export": "Export widget",
"no-data": "No data to display on widget"
}, },
"widget-action": { "widget-action": {
"header-button": "Widget header button", "header-button": "Widget header button",

Loading…
Cancel
Save