Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/204/head
Andrew Shvayka 9 years ago
parent
commit
d43995eadf
  1. 8
      application/src/main/data/json/system/widget_bundles/analogue_gauges.json
  2. 12
      application/src/main/data/json/system/widget_bundles/cards.json
  3. 20
      application/src/main/data/json/system/widget_bundles/charts.json
  4. 25
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  5. 38
      application/src/main/data/json/system/widget_bundles/digital_gauges.json
  6. 30
      application/src/main/data/json/system/widget_bundles/maps.json
  7. 73
      application/src/main/data/json/system/widget_bundles/maps__deprecated_.json
  8. 2
      ui/package.json
  9. 14
      ui/src/app/api/alias-controller.js
  10. 35
      ui/src/app/api/entity.service.js
  11. 9
      ui/src/app/api/subscription.js
  12. 14
      ui/src/app/api/widget.service.js
  13. 138
      ui/src/app/common/utf8-support.js
  14. 27
      ui/src/app/common/utils.service.js
  15. 3
      ui/src/app/components/dashboard.directive.js
  16. 13
      ui/src/app/components/datasource-entity.directive.js
  17. 13
      ui/src/app/components/datasource-func.directive.js
  18. 1
      ui/src/app/components/datasource.directive.js
  19. 2
      ui/src/app/components/datasource.tpl.html
  20. 2
      ui/src/app/components/widget/action/widget-action-dialog.controller.js
  21. 20
      ui/src/app/components/widget/action/widget-action-dialog.tpl.html
  22. 1
      ui/src/app/components/widget/widget-config.tpl.html
  23. 51
      ui/src/app/components/widget/widget.controller.js
  24. 8
      ui/src/app/dashboard/dashboard-settings.controller.js
  25. 3
      ui/src/app/dashboard/dashboard-settings.tpl.html
  26. 13
      ui/src/app/dashboard/states/default-state-controller.js
  27. 44
      ui/src/app/dashboard/states/entity-state-controller.js
  28. 9
      ui/src/app/dashboard/states/states-component.directive.js
  29. 4
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
  30. 2
      ui/src/app/entity/entity-filter.directive.js
  31. 70
      ui/src/app/entity/entity-filter.tpl.html
  32. 6
      ui/src/app/locale/locale.constant.js
  33. 5
      ui/src/app/widget/lib/entities-table-widget.js
  34. 19
      ui/src/app/widget/lib/google-map.js
  35. 3
      ui/src/app/widget/lib/map-widget.js
  36. 680
      ui/src/app/widget/lib/map-widget2.js
  37. 23
      ui/src/app/widget/lib/openstreet-map.js
  38. 209
      ui/src/app/widget/lib/widget-utils.js

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

@ -15,7 +15,7 @@
"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.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 };\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}}"
@ -31,7 +31,7 @@
"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.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 };\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}}"
@ -47,7 +47,7 @@
"resources": [],
"templateHtml": "<canvas id=\"linearGauge\"></canvas>\n",
"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.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 };\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() * 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,7 +63,7 @@
"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.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 };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}",
"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}}"

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

File diff suppressed because one or more lines are too long

20
application/src/main/data/json/system/widget_bundles/charts.json

File diff suppressed because one or more lines are too long

25
application/src/main/data/json/system/widget_bundles/control_widgets.json

@ -0,0 +1,25 @@
{
"widgetsBundle": {
"alias": "control_widgets",
"title": "Control widgets",
"image": null
},
"widgetTypes": [
{
"alias": "device_terminal",
"name": "Device terminal",
"descriptor": {
"type": "rpc",
"sizeX": 9.5,
"sizeY": 5.5,
"resources": [],
"templateHtml": "<div style=\"height: 100%;\" id=\"device-terminal\"></div>",
"templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n",
"controllerScript": "var requestTimeout = 500;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n var greetings = 'Welcome to ThingsBoard RPC commands terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = angular.copy(command).trim();\n if (localCommand == 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (cmdObj.args.length && cmdObj.args[0]) {\n try {\n params = JSON.parse(cmdObj.args[0]);\n } catch (e) {\n params = cmdObj.args[0];\n }\n }\n performRpc(this, cmdObj.name, params);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout).then(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n \nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"Device terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
}
]
}

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

@ -15,10 +15,10 @@
"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.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\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(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"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\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false},\"title\":\"Digital horizontal bar\",\"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 < 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\":{}}"
}
},
{
@ -31,10 +31,10 @@
"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.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 };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\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() * 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(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"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\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\"},\"title\":\"Digital speedometer\",\"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 < 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\":{}}"
}
},
{
@ -47,10 +47,10 @@
"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.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 };\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\":\"#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(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"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\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"decimals\":0,\"minValue\":-60,\"units\":\"°C\",\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\"},\"title\":\"Digital thermometer\",\"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 < -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\":{}}"
}
},
{
@ -63,10 +63,10 @@
"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.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 };\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\":\"#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(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"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\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"decimals\":0,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"units\":\"°C\",\"minValue\":-60,\"dashThickness\":1.2},\"title\":\"Digital vertical bar\",\"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\":{}}"
}
},
{
@ -79,7 +79,7 @@
"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.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 };\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\":\"#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}}"
@ -95,7 +95,7 @@
"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": "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.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 };\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\":\"#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}}"
@ -111,7 +111,7 @@
"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.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 };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}",
"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}}"
@ -127,7 +127,7 @@
"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.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 };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\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() * 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}}"
@ -143,7 +143,7 @@
"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.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 };\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\":\"#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}}"
@ -159,10 +159,10 @@
"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.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 };\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\":\"#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(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\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"decimals\":1,\"gaugeType\":\"arc\"},\"title\":\"Neon 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\":{}}"
}
},
{
@ -175,7 +175,7 @@
"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.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\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",
"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}}"
@ -191,10 +191,10 @@
"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.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 };\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\":\"#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(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"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\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"decimals\":0,\"gaugeType\":\"donut\"},\"title\":\"Simple neon 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\":\"#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\":{}}"
}
},
{
@ -207,7 +207,7 @@
"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.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 };\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\":\"#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}}"

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

File diff suppressed because one or more lines are too long

73
application/src/main/data/json/system/widget_bundles/maps__deprecated_.json

File diff suppressed because one or more lines are too long

2
ui/package.json

@ -45,6 +45,7 @@
"angular-ui-ace": "^0.2.3",
"angular-ui-router": "^0.3.1",
"angular-websocket": "^2.0.1",
"base64-js": "^1.2.1",
"brace": "^0.8.0",
"canvas-gauges": "^2.0.9",
"clipboard": "^1.5.15",
@ -54,6 +55,7 @@
"font-awesome": "^4.6.3",
"javascript-detect-element-resize": "^0.5.3",
"jquery": "^3.1.0",
"jquery.terminal": "^1.5.0",
"js-beautify": "^1.6.4",
"json-schema-defaults": "^0.2.0",
"leaflet": "^1.0.3",

14
ui/src/app/api/alias-controller.js

@ -53,10 +53,11 @@ export default class AliasController {
}
dashboardStateChanged() {
var newEntityId = this.stateController.getStateParams().entityId;
var changedAliasIds = [];
for (var aliasId in this.resolvedAliasesToStateEntities) {
var prevEntityId = this.resolvedAliasesToStateEntities[aliasId];
var stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId];
var newEntityId = this.stateController.getEntityId(stateEntityInfo.entityParamName);
var prevEntityId = stateEntityInfo.entityId;
if (!angular.equals(newEntityId, prevEntityId)) {
changedAliasIds.push(aliasId);
this.setAliasUnresolved(aliasId);
@ -93,19 +94,26 @@ export default class AliasController {
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
function success(aliasInfo) {
aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
delete aliasCtrl.resolvedAliasesPromise[aliasId];
if (aliasInfo.stateEntity) {
var stateEntityInfo = {
entityParamName: aliasInfo.entityParamName,
entityId: aliasCtrl.stateController.getEntityId(aliasInfo.entityParamName)
};
aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
aliasCtrl.stateController.getStateParams().entityId;
stateEntityInfo;
}
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId);
deferred.resolve(aliasInfo);
},
function fail() {
deferred.reject();
delete aliasCtrl.resolvedAliasesPromise[aliasId];
}
);
} else {
deferred.reject();
delete aliasCtrl.resolvedAliasesPromise[aliasId];
}
return this.resolvedAliasesPromise[aliasId];
}

35
ui/src/app/api/entity.service.js

@ -376,6 +376,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
var aliasInfo = {
alias: entityAlias.alias,
stateEntity: result.stateEntity,
entityParamName: result.entityParamName,
resolveMultiple: filter.resolveMultiple
};
aliasInfo.resolvedEntities = result.entities;
@ -392,12 +393,30 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise;
}
function getStateEntityId(filter, stateParams) {
var entityId = null;
if (stateParams) {
if (filter.stateEntityParamName && filter.stateEntityParamName.length) {
if (stateParams[filter.stateEntityParamName]) {
entityId = stateParams[filter.stateEntityParamName].entityId;
}
} else {
entityId = stateParams.entityId;
}
}
return entityId;
}
function resolveAliasFilter(filter, stateParams, maxItems) {
var deferred = $q.defer();
var result = {
entities: [],
stateEntity: false
};
if (filter.stateEntityParamName && filter.stateEntityParamName.length) {
result.entityParamName = filter.stateEntityParamName;
}
var stateEntityId = getStateEntityId(filter, stateParams);
switch (filter.type) {
case types.aliasFilterType.entityList.value:
getEntities(filter.entityType, filter.entityList).then(
@ -431,8 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
break;
case types.aliasFilterType.stateEntity.value:
result.stateEntity = true;
if (stateParams && stateParams.entityId) {
getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
if (stateEntityId) {
getEntity(stateEntityId.entityType, stateEntityId.id).then(
function success(entity) {
result.entities = entitiesToEntitiesInfo([entity]);
deferred.resolve(result);
@ -479,9 +498,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
result.stateEntity = filter.rootStateEntity;
var rootEntityType;
var rootEntityId;
if (result.stateEntity && stateParams && stateParams.entityId) {
rootEntityType = stateParams.entityId.entityType;
rootEntityId = stateParams.entityId.id;
if (result.stateEntity && stateEntityId) {
rootEntityType = stateEntityId.entityType;
rootEntityId = stateEntityId.id;
} else if (!result.stateEntity) {
rootEntityType = filter.rootEntity.entityType;
rootEntityId = filter.rootEntity.id;
@ -520,9 +539,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
case types.aliasFilterType.assetSearchQuery.value:
case types.aliasFilterType.deviceSearchQuery.value:
result.stateEntity = filter.rootStateEntity;
if (result.stateEntity && stateParams && stateParams.entityId) {
rootEntityType = stateParams.entityId.entityType;
rootEntityId = stateParams.entityId.id;
if (result.stateEntity && stateEntityId) {
rootEntityType = stateEntityId.entityType;
rootEntityId = stateEntityId.id;
} else if (!result.stateEntity) {
rootEntityType = filter.rootEntity.entityType;
rootEntityId = filter.rootEntity.id;

9
ui/src/app/api/subscription.js

@ -730,12 +730,15 @@ export default class Subscription {
index += datasource.dataKeys.length;
this.datasourceListeners.push(listener);
this.ctx.datasourceService.subscribeToDatasource(listener);
if (datasource.unresolvedStateEntity) {
if (datasource.dataKeys.length) {
this.ctx.datasourceService.subscribeToDatasource(listener);
}
if (datasource.unresolvedStateEntity || !datasource.dataKeys.length) {
this.notifyDataLoaded();
this.onDataUpdated();
}
}
}
}

14
ui/src/app/api/widget.service.js

@ -27,6 +27,10 @@ import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge';
import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge';
import TbCanvasDigitalGauge from '../widget/lib/canvas-digital-gauge';
import TbMapWidget from '../widget/lib/map-widget';
import TbMapWidgetV2 from '../widget/lib/map-widget2';
import 'jquery.terminal/js/jquery.terminal.min.js';
import 'jquery.terminal/css/jquery.terminal.min.css';
import 'oclazyload';
import cssjs from '../../vendor/css.js/css';
@ -53,6 +57,8 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
$window.TbAnalogueRadialGauge = TbAnalogueRadialGauge;
$window.TbCanvasDigitalGauge = TbCanvasDigitalGauge;
$window.TbMapWidget = TbMapWidget;
$window.TbMapWidgetV2 = TbMapWidgetV2;
$window.cssjs = cssjs;
var cssParser = new cssjs();
@ -550,8 +556,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
' self.typeParameters = function() {\n\n' +
return {
useCustomDatasources: false,
maxDatasources: -1 //unlimited
maxDataKeys: -1 //unlimited
maxDatasources: -1, //unlimited
maxDataKeys: -1, //unlimited
dataKeysOptional: false
};
' }\n\n' +
@ -619,6 +626,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
if (angular.isUndefined(result.typeParameters.maxDataKeys)) {
result.typeParameters.maxDataKeys = -1;
}
if (angular.isUndefined(result.typeParameters.dataKeysOptional)) {
result.typeParameters.dataKeysOptional = false;
}
if (angular.isFunction(widgetTypeInstance.actionSources)) {
result.actionSources = widgetTypeInstance.actionSources();
} else {

138
ui/src/app/common/utf8-support.js

@ -0,0 +1,138 @@
/*
* Copyright © 2016-2017 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.
*/
export function utf8Encode(str) {
var result;
if (angular.isUndefined(Uint8Array)) { // eslint-disable-line no-undef
result = utf8ToBytes(str);
} else {
result = new Uint8Array(utf8ToBytes(str)); // eslint-disable-line no-undef
}
return result;
}
export function utf8Decode(bytes) {
return utf8Slice(bytes, 0, bytes.length);
}
function utf8Slice (buf, start, end) {
var res = ''
var tmp = ''
end = Math.min(buf.length, end || Infinity)
start = start || 0;
for (var i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
}
}
return res + decodeUtf8Char(tmp)
}
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
function utf8ToBytes (string, units) {
units = units || Infinity
var codePoint
var length = string.length
var leadSurrogate = null
var bytes = []
var i = 0
for (; i < length; i++) {
codePoint = string.charCodeAt(i)
// is surrogate component
if (codePoint > 0xD7FF && codePoint < 0xE000) {
// last char was a lead
if (leadSurrogate) {
// 2 leads in a row
if (codePoint < 0xDC00) {
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = codePoint
continue
} else {
// valid surrogate pair
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
leadSurrogate = null
}
} else {
// no lead yet
if (codePoint > 0xDBFF) {
// unexpected trail
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else if (i + 1 === length) {
// unpaired lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else {
// valid lead
leadSurrogate = codePoint
continue
}
}
} else if (leadSurrogate) {
// valid bmp char, but last char was a lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = null
}
// encode utf8
if (codePoint < 0x80) {
if ((units -= 1) < 0) break
bytes.push(codePoint)
} else if (codePoint < 0x800) {
if ((units -= 2) < 0) break
bytes.push(
codePoint >> 0x6 | 0xC0,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x10000) {
if ((units -= 3) < 0) break
bytes.push(
codePoint >> 0xC | 0xE0,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x200000) {
if ((units -= 4) < 0) break
bytes.push(
codePoint >> 0x12 | 0xF0,
codePoint >> 0xC & 0x3F | 0x80,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else {
throw new Error('Invalid code point')
}
}
return bytes
}

27
ui/src/app/common/utils.service.js

@ -20,9 +20,12 @@ import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/c
/* eslint-enable import/no-unresolved, import/default */
import tinycolor from "tinycolor2";
import jsonSchemaDefaults from "json-schema-defaults";
import thingsboardTypes from "./types.constant";
import tinycolor from 'tinycolor2';
import jsonSchemaDefaults from 'json-schema-defaults';
import base64js from 'base64-js';
import {utf8Encode, utf8Decode} from './utf8-support';
import thingsboardTypes from './types.constant';
export default angular.module('thingsboard.utils', [thingsboardTypes])
.factory('utils', Utils)
@ -153,7 +156,9 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
createKey: createKey,
createLabelFromDatasource: createLabelFromDatasource,
insertVariable: insertVariable,
customTranslation: customTranslation
customTranslation: customTranslation,
objToBase64: objToBase64,
base64toObj: base64toObj
}
return service;
@ -486,4 +491,18 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
return result;
}
function objToBase64(obj) {
var json = angular.toJson(obj);
var encoded = utf8Encode(json);
var b64Encoded = base64js.fromByteArray(encoded);
return b64Encoded;
}
function base64toObj(b64Encoded) {
var encoded = base64js.toByteArray(b64Encoded);
var json = utf8Decode(encoded);
var obj = angular.fromJson(json);
return obj;
}
}

3
ui/src/app/components/dashboard.directive.js

@ -605,9 +605,6 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
}
function widgetClicked ($event, widget) {
if ($event) {
$event.stopPropagation();
}
if (vm.onWidgetClicked) {
vm.onWidgetClicked({event: $event, widget: widget});
}

13
ui/src/app/components/datasource-entity.directive.js

@ -68,10 +68,14 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
ngModelCtrl.$setValidity('entityAlias',
angular.isDefined(value.entityAliasId) &&
value.entityAliasId != null);
ngModelCtrl.$setValidity('entityKeys',
angular.isDefined(value.dataKeys) &&
value.dataKeys != null &&
value.dataKeys.length > 0);
if (scope.optDataKeys) {
ngModelCtrl.$setValidity('entityKeys', true);
} else {
ngModelCtrl.$setValidity('entityKeys',
angular.isDefined(value.dataKeys) &&
value.dataKeys != null &&
value.dataKeys.length > 0);
}
}
}
};
@ -281,6 +285,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
scope: {
widgetType: '=',
maxDataKeys: '=',
optDataKeys: '=',
aliasController: '=',
datakeySettingsSchema: '=',
generateDataKey: '&',

13
ui/src/app/components/datasource-func.directive.js

@ -63,10 +63,14 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
var dataValid = angular.isDefined(value) && value != null;
ngModelCtrl.$setValidity('deviceData', dataValid);
if (dataValid) {
ngModelCtrl.$setValidity('datasourceKeys',
angular.isDefined(value.dataKeys) &&
value.dataKeys != null &&
value.dataKeys.length > 0);
if (scope.optDataKeys) {
ngModelCtrl.$setValidity('datasourceKeys', true);
} else {
ngModelCtrl.$setValidity('datasourceKeys',
angular.isDefined(value.dataKeys) &&
value.dataKeys != null &&
value.dataKeys.length > 0);
}
}
}
};
@ -222,6 +226,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
scope: {
widgetType: '=',
maxDataKeys: '=',
optDataKeys: '=',
generateDataKey: '&',
datakeySettingsSchema: '='
},

1
ui/src/app/components/datasource.directive.js

@ -83,6 +83,7 @@ function Datasource($compile, $templateCache, utils, types) {
scope: {
aliasController: '=',
maxDataKeys: '=',
optDataKeys: '=',
widgetType: '=',
functionsOnly: '=',
datakeySettingsSchema: '=',

2
ui/src/app/components/datasource.tpl.html

@ -28,6 +28,7 @@
ng-switch-default
ng-model="model"
max-data-keys="maxDataKeys"
opt-data-keys="optDataKeys"
datakey-settings-schema="datakeySettingsSchema"
ng-required="model.type === types.datasourceType.function"
widget-type="widgetType"
@ -36,6 +37,7 @@
<tb-datasource-entity flex
ng-model="model"
max-data-keys="maxDataKeys"
opt-data-keys="optDataKeys"
datakey-settings-schema="datakeySettingsSchema"
ng-switch-when="entity"
ng-required="model.type === types.datasourceType.entity"

2
ui/src/app/components/widget/action/widget-action-dialog.controller.js

@ -145,11 +145,13 @@ export default function WidgetActionDialogController($scope, $mdDialog, $filter,
result.targetDashboardStateId = action.targetDashboardStateId;
result.openRightLayout = action.openRightLayout;
result.setEntityId = action.setEntityId;
result.stateEntityParamName = action.stateEntityParamName;
break;
case vm.types.widgetActionTypes.openDashboard.value:
result.targetDashboardId = action.targetDashboardId;
result.targetDashboardStateId = action.targetDashboardStateId;
result.setEntityId = action.setEntityId;
result.stateEntityParamName = action.stateEntityParamName;
break;
case vm.types.widgetActionTypes.custom.value:
result.customFunction = action.customFunction;

20
ui/src/app/components/widget/action/widget-action-dialog.tpl.html

@ -104,12 +104,20 @@
flex aria-label="{{ 'widget-action.open-right-layout' | translate }}"
ng-model="vm.action.openRightLayout">{{ 'widget-action.open-right-layout' | translate }}
</md-checkbox>
<md-checkbox ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value ||
vm.action.type == vm.types.widgetActionTypes.openDashboard.value"
flex aria-label="{{ 'widget-action.set-entity-from-widget' | translate }}"
ng-model="vm.action.setEntityId">{{ 'widget-action.set-entity-from-widget' | translate }}
</md-checkbox>
<div flex layout="column" ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value ||
vm.action.type == vm.types.widgetActionTypes.openDashboard.value">
<md-checkbox flex aria-label="{{ 'widget-action.set-entity-from-widget' | translate }}"
ng-model="vm.action.setEntityId">{{ 'widget-action.set-entity-from-widget' | translate }}
</md-checkbox>
<md-input-container ng-if="vm.action.setEntityId" class="md-block">
<label translate>alias.state-entity-parameter-name</label>
<input name="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
ng-model="vm.action.stateEntityParamName"
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
</md-input-container>
</div>
<tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value"
ng-model="vm.action.customFunction"
function-args="{{ ['$event', 'widgetContext', 'entityId'] }}"

1
ui/src/app/components/widget/widget-config.tpl.html

@ -96,6 +96,7 @@
<tb-datasource flex ng-model="datasource.value"
widget-type="widgetType"
max-data-keys="typeParameters.maxDataKeys"
opt-data-keys="typeParameters.dataKeysOptional"
alias-controller="aliasController"
functions-only="functionsOnly"
datakey-settings-schema="datakeySettingsSchema"

51
ui/src/app/components/widget/widget.controller.js

@ -421,23 +421,41 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
return result;
}
function updateEntityParams(params, targetEntityParamName, targetEntityId, entityName) {
if (targetEntityId) {
var targetEntityParams;
if (targetEntityParamName && targetEntityParamName.length) {
targetEntityParams = params[targetEntityParamName];
if (!targetEntityParams) {
targetEntityParams = {};
params[targetEntityParamName] = targetEntityParams;
}
} else {
targetEntityParams = params;
}
targetEntityParams.entityId = targetEntityId;
if (entityName) {
targetEntityParams.entityName = entityName;
}
}
}
function handleWidgetAction($event, descriptor, entityId, entityName) {
var type = descriptor.type;
var targetEntityParamName = descriptor.stateEntityParamName;
var targetEntityId;
if (descriptor.setEntityId) {
targetEntityId = entityId;
}
switch (type) {
case types.widgetActionTypes.openDashboardState.value:
case types.widgetActionTypes.updateDashboardState.value:
var targetDashboardStateId = descriptor.targetDashboardStateId;
var targetEntityId;
if (descriptor.setEntityId) {
targetEntityId = entityId;
}
var params = {};
if (targetEntityId) {
params.entityId = targetEntityId;
if (entityName) {
params.entityName = entityName;
}
var params = angular.copy(widgetContext.stateController.getStateParams());
if (!params) {
params = {};
}
updateEntityParams(params, targetEntityParamName, targetEntityId, entityName);
if (type == types.widgetActionTypes.openDashboardState.value) {
widgetContext.stateController.openState(targetDashboardStateId, params, descriptor.openRightLayout);
} else {
@ -447,24 +465,15 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
case types.widgetActionTypes.openDashboard.value:
var targetDashboardId = descriptor.targetDashboardId;
targetDashboardStateId = descriptor.targetDashboardStateId;
targetEntityId;
if (descriptor.setEntityId) {
targetEntityId = entityId;
}
var stateObject = {};
stateObject.params = {};
if (targetEntityId) {
stateObject.params.entityId = targetEntityId;
if (entityName) {
stateObject.params.entityName = entityName;
}
}
updateEntityParams(stateObject.params, targetEntityParamName, targetEntityId, entityName);
if (targetDashboardStateId) {
stateObject.id = targetDashboardStateId;
}
var stateParams = {
dashboardId: targetDashboardId,
state: angular.toJson([ stateObject ])
state: utils.objToBase64([ stateObject ])
}
$state.go('home.dashboards.dashboard', stateParams);
break;

8
ui/src/app/dashboard/dashboard-settings.controller.js

@ -25,6 +25,8 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon
vm.imageAdded = imageAdded;
vm.clearImage = clearImage;
vm.stateControllerIdChanged = stateControllerIdChanged;
vm.settings = settings;
vm.gridSettings = gridSettings;
vm.stateControllers = statesControllerService.getStateControllers();
@ -98,6 +100,12 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon
vm.gridSettings.backgroundImageUrl = null;
}
function stateControllerIdChanged() {
if (vm.settings.stateControllerId != 'default') {
vm.settings.toolbarAlwaysOpen = true;
}
}
function save() {
$scope.theForm.$setPristine();
if (vm.gridSettings) {

3
ui/src/app/dashboard/dashboard-settings.tpl.html

@ -34,7 +34,8 @@
<div ng-show="vm.settings">
<md-input-container class="md-block">
<label translate>dashboard.state-controller</label>
<md-select aria-label="{{ 'dashboard.state-controller' | translate }}" ng-model="vm.settings.stateControllerId">
<md-select aria-label="{{ 'dashboard.state-controller' | translate }}"
ng-model="vm.settings.stateControllerId" ng-change="vm.stateControllerIdChanged()">
<md-option ng-repeat="(stateControllerId, stateController) in vm.stateControllers" ng-value="stateControllerId">
{{stateControllerId}}
</md-option>

13
ui/src/app/dashboard/states/default-state-controller.js

@ -27,6 +27,7 @@ export default function DefaultStateController($scope, $location, $state, $state
vm.getStateId = getStateId;
vm.getStateParams = getStateParams;
vm.getStateParamsByStateId = getStateParamsByStateId;
vm.getEntityId = getEntityId;
vm.getStateName = getStateName;
@ -103,6 +104,10 @@ export default function DefaultStateController($scope, $location, $state, $state
}
}
function getEntityId() {
return null;
}
function getStateObjById(id) {
for (var i=0; i < vm.stateObject.length; i++) {
if (vm.stateObject[i].id === id) {
@ -116,11 +121,11 @@ export default function DefaultStateController($scope, $location, $state, $state
return utils.customTranslation(state.name, id);
}
function parseState(stateJson) {
function parseState(stateBase64) {
var result;
if (stateJson) {
if (stateBase64) {
try {
result = angular.fromJson(stateJson);
result = utils.base64toObj(stateBase64);
} catch (e) {
result = [ { id: null, params: {} } ];
}
@ -200,7 +205,7 @@ export default function DefaultStateController($scope, $location, $state, $state
function updateLocation() {
if (vm.stateObject[0].id) {
$location.search({state : angular.toJson(vm.stateObject)});
$location.search({state : utils.objToBase64(vm.stateObject)});
}
}
}

44
ui/src/app/dashboard/states/entity-state-controller.js

@ -29,6 +29,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
vm.getStateId = getStateId;
vm.getStateParams = getStateParams;
vm.getStateParamsByStateId = getStateParamsByStateId;
vm.getEntityId = getEntityId;
vm.getStateName = getStateName;
@ -111,6 +112,16 @@ export default function EntityStateController($scope, $location, $state, $stateP
}
}
function getEntityId(entityParamName) {
var stateParams = getStateParams();
if (!entityParamName || !entityParamName.length) {
return stateParams.entityId;
} else if (stateParams[entityParamName]) {
return stateParams[entityParamName].entityId;
}
return null;
}
function getStateObjById(id) {
for (var i=0; i < vm.stateObject.length; i++) {
if (vm.stateObject[i].id === id) {
@ -135,26 +146,33 @@ export default function EntityStateController($scope, $location, $state, $stateP
function resolveEntity(params) {
var deferred = $q.defer();
if (params && params.entityId && params.entityId.id && params.entityId.entityType) {
entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then(
function success(entity) {
var entityName = entity.name;
deferred.resolve(entityName);
},
function fail() {
deferred.reject();
}
);
if (params.entityName && params.entityName.length) {
deferred.resolve(params.entityName);
} else {
entityService.getEntity(params.entityId.entityType, params.entityId.id, {
ignoreLoading: true,
ignoreErrors: true
}).then(
function success(entity) {
var entityName = entity.name;
deferred.resolve(entityName);
},
function fail() {
deferred.reject();
}
);
}
} else {
deferred.reject();
}
return deferred.promise;
}
function parseState(stateJson) {
function parseState(stateBase64) {
var result;
if (stateJson) {
if (stateBase64) {
try {
result = angular.fromJson(stateJson);
result = utils.base64toObj(stateBase64);
} catch (e) {
result = [ { id: null, params: {} } ];
}
@ -260,7 +278,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
function updateLocation() {
if (vm.stateObject[vm.stateObject.length-1].id) {
$location.search({state : angular.toJson(vm.stateObject)});
$location.search({state : utils.objToBase64(vm.stateObject)});
}
}

9
ui/src/app/dashboard/states/states-component.directive.js

@ -70,6 +70,15 @@ export default function StatesComponent($compile, $templateCache, $controller, s
return null;
}
}
stateController.getEntityId = function(entityParamName) {
if (scope.statesController) {
return scope.statesController.getEntityId(entityParamName);
} else {
return null;
}
}
}
scope.$on('$destroy', function callOnDestroyHook() {

4
ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js

@ -23,7 +23,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la
/*@ngInject*/
export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils,
types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
utils, types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
var vm = this;
@ -143,7 +143,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
var stateIds = Object.keys(dashboard.configuration.states);
var stateIndex = stateIds.indexOf(targetState);
if (stateIndex > 0) {
stateParams.state = angular.toJson([ {id: targetState, params: {}} ]);
stateParams.state = utils.objToBase64([ {id: targetState, params: {}} ]);
}
$state.go('home.dashboards.dashboard', stateParams);
}

2
ui/src/app/entity/entity-filter.directive.js

@ -54,6 +54,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
filter.entityNameFilter = '';
break;
case types.aliasFilterType.stateEntity.value:
filter.stateEntityParamName = null;
break;
case types.aliasFilterType.assetType.value:
filter.assetType = null;
@ -67,6 +68,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
case types.aliasFilterType.assetSearchQuery.value:
case types.aliasFilterType.deviceSearchQuery.value:
filter.rootStateEntity = false;
filter.stateEntityParamName = null;
filter.rootEntity = null;
filter.direction = types.entitySearchDirection.from;
filter.maxLevel = 1;

70
ui/src/app/entity/entity-filter.tpl.html

@ -59,6 +59,13 @@
</md-input-container>
</section>
<section layout="column" ng-if="filter.type == types.aliasFilterType.stateEntity.value" id="stateEntityFilter">
<md-input-container class="md-block">
<label translate>alias.state-entity-parameter-name</label>
<input name="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
ng-model="filter.stateEntityParamName"
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
</md-input-container>
</section>
<section layout="column" ng-if="filter.type == types.aliasFilterType.assetType.value" id="assetTypeFilter">
<tb-entity-subtype-autocomplete
@ -97,12 +104,21 @@
ng-disabled="filter.rootStateEntity"
ng-model="filter.rootEntity">
</tb-entity-select>
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
aria-label="{{ 'alias.root-state-entity' | translate }}">
</md-switch>
</section>
<div layout="column">
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
aria-label="{{ 'alias.root-state-entity' | translate }}">
</md-switch>
</section>
<md-input-container ng-if="filter.rootStateEntity" class="md-block">
<label translate>alias.state-entity-parameter-name</label>
<input name="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
ng-model="filter.stateEntityParamName"
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
</md-input-container>
</div>
</div>
<div flex layout="row">
<md-input-container class="md-block" style="min-width: 100px;">
@ -139,12 +155,21 @@
ng-disabled="filter.rootStateEntity"
ng-model="filter.rootEntity">
</tb-entity-select>
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
aria-label="{{ 'alias.root-state-entity' | translate }}">
</md-switch>
</section>
<div layout="column">
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
aria-label="{{ 'alias.root-state-entity' | translate }}">
</md-switch>
</section>
<md-input-container ng-if="filter.rootStateEntity" class="md-block">
<label translate>alias.state-entity-parameter-name</label>
<input name="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
ng-model="filter.stateEntityParamName"
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
</md-input-container>
</div>
</div>
<div flex layout="row">
<md-input-container class="md-block" style="min-width: 100px;">
@ -189,12 +214,21 @@
ng-disabled="filter.rootStateEntity"
ng-model="filter.rootEntity">
</tb-entity-select>
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
aria-label="{{ 'alias.root-state-entity' | translate }}">
</md-switch>
</section>
<div layout="column">
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
aria-label="{{ 'alias.root-state-entity' | translate }}">
</md-switch>
</section>
<md-input-container ng-if="filter.rootStateEntity" class="md-block">
<label translate>alias.state-entity-parameter-name</label>
<input name="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
ng-model="filter.stateEntityParamName"
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
</md-input-container>
</div>
</div>
<div flex layout="row">
<md-input-container class="md-block" style="min-width: 100px;">

6
ui/src/app/locale/locale.constant.js

@ -187,6 +187,8 @@ export default angular.module('thingsboard.locale', [])
"no-entity-filter-specified": "No entity filter specified",
"root-state-entity": "Use dashboard state entity as root",
"root-entity": "Root entity",
"state-entity-parameter-name": "State entity parameter name",
"default-entity-parameter-name": "By default",
"max-relation-level": "Max relation level",
"unlimited-level": "Unlimited level",
"state-entity": "Dashboard state entity",
@ -1220,7 +1222,9 @@ export default angular.module('thingsboard.locale', [])
"custom": {
"widget-action": {
"action-cell-button": "Action cell button",
"row-click": "On row click"
"row-click": "On row click",
"marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action"
}
}
}

5
ui/src/app/widget/lib/entities-table-widget.js

@ -100,7 +100,7 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.cellContent = cellContent;
$scope.$watch('vm.ctx', function() {
if (vm.ctx) {
if (vm.ctx && vm.ctx.defaultSubscription) {
vm.settings = vm.ctx.settings;
vm.widgetConfig = vm.ctx.widgetConfig;
vm.subscription = vm.ctx.defaultSubscription;
@ -479,6 +479,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
for (var i=0;i<vm.datasources.length;i++) {
datasource = vm.datasources[i];
if (datasource.type == types.datasourceType.entity && !datasource.entityId) {
continue;
}
var entity = {
id: {}
};

19
ui/src/app/widget/lib/google-map.js

@ -143,6 +143,12 @@ export default class TbGoogleMap {
return angular.isDefined(this.map);
}
/* eslint-disable no-undef */
updateMarkerLabel(marker, settings) {
marker.set('labelContent', '<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>');
}
/* eslint-enable no-undef */
/* eslint-disable no-undef */
updateMarkerColor(marker, color) {
var pinColor = color.substr(1);
@ -174,7 +180,7 @@ export default class TbGoogleMap {
}
marker.setIcon(pinImage);
if (settings.showLabel) {
marker.set('labelAnchor', new google.maps.Point(50, height + 20));
marker.set('labelAnchor', new google.maps.Point(100, height + 20));
}
}
testImage.src = image;
@ -182,7 +188,7 @@ export default class TbGoogleMap {
/* eslint-enable no-undef */
/* eslint-disable no-undef */
createMarker(location, settings, onClickListener) {
createMarker(location, settings, onClickListener, markerArgs) {
var height = 34;
var pinColor = settings.color.substr(1);
var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
@ -200,9 +206,9 @@ export default class TbGoogleMap {
map: this.map,
icon: pinImage,
shadow: pinShadow,
labelContent: '<div style="color: '+ settings.labelColor +';"><b>'+settings.label+'</b></div>',
labelContent: '<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
labelClass: "tb-labels",
labelAnchor: new google.maps.Point(50, height + 20)
labelAnchor: new google.maps.Point(100, height + 20)
});
} else {
marker = new google.maps.Marker({
@ -218,7 +224,7 @@ export default class TbGoogleMap {
}
if (settings.displayTooltip) {
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo);
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, markerArgs);
}
if (onClickListener) {
@ -235,7 +241,7 @@ export default class TbGoogleMap {
/* eslint-enable no-undef */
/* eslint-disable no-undef */
createTooltip(marker, pattern, replaceInfo) {
createTooltip(marker, pattern, replaceInfo, markerArgs) {
var popup = new google.maps.InfoWindow({
content: ''
});
@ -243,6 +249,7 @@ export default class TbGoogleMap {
popup.open(this.map, marker);
});
this.tooltips.push( {
markerArgs: markerArgs,
popup: popup,
pattern: pattern,
replaceInfo: replaceInfo

3
ui/src/app/widget/lib/map-widget.js

@ -172,6 +172,7 @@ export default class TbMapWidget {
showLabel: subscriptionLocationSettings.showLabel !== false,
displayTooltip: subscriptionLocationSettings.displayTooltip !== false,
label: datasource.name,
labelText: datasource.name,
labelColor: subscriptionLocationSettings.labelColor || this.ctx.widgetConfig.color || '#000000',
color: "#FE7569",
useColorFunction: false,
@ -218,6 +219,7 @@ export default class TbMapWidget {
showLabel: true,
displayTooltip: true,
label: "",
labelText: "",
labelColor: this.ctx.widgetConfig.color || '#000000',
color: "#FE7569",
useColorFunction: false,
@ -246,6 +248,7 @@ export default class TbMapWidget {
this.locationsSettings[i].showLabel = configuredLocationsSettings[i].showLabel !== false;
this.locationsSettings[i].label = configuredLocationsSettings[i].label || this.locationsSettings[i].label;
this.locationsSettings[i].labelText = this.locationsSettings[i].label;
this.locationsSettings[i].color = configuredLocationsSettings[i].color ? tinycolor(configuredLocationsSettings[i].color).toHexString() : this.locationsSettings[i].color;
this.locationsSettings[i].useColorFunction = configuredLocationsSettings[i].useColorFunction === true;

680
ui/src/app/widget/lib/map-widget2.js

@ -0,0 +1,680 @@
/*
* Copyright © 2016-2017 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 tinycolor from 'tinycolor2';
import TbGoogleMap from './google-map';
import TbOpenStreetMap from './openstreet-map';
import {processPattern, arraysEqual, toLabelValueMap, fillPattern, fillPatternWithActions} from './widget-utils';
export default class TbMapWidgetV2 {
constructor(mapProvider, drawRoutes, ctx, useDynamicLocations, $element) {
var tbMap = this;
this.ctx = ctx;
if (!$element) {
$element = ctx.$container;
}
this.utils = ctx.$scope.$injector.get('utils');
this.drawRoutes = drawRoutes;
this.markers = [];
if (this.drawRoutes) {
this.polylines = [];
}
this.locationSettings = {};
var settings = ctx.settings;
this.callbacks = {};
this.callbacks.onLocationClick = function(){};
if (settings.defaultZoomLevel) {
if (settings.defaultZoomLevel > 0 && settings.defaultZoomLevel < 21) {
this.defaultZoomLevel = Math.floor(settings.defaultZoomLevel);
}
}
this.dontFitMapBounds = settings.fitMapBounds === false;
if (!useDynamicLocations) {
this.subscription = this.ctx.defaultSubscription;
}
this.configureLocationsSettings();
var minZoomLevel = this.drawRoutes ? 18 : 15;
var initCallback = function() {
tbMap.update();
tbMap.resize();
};
this.ctx.$scope.onTooltipAction = function(event, actionName, dsIndex) {
tbMap.onTooltipAction(event, actionName, dsIndex);
};
this.tooltipActionsMap = {};
var descriptors = this.ctx.actionsApi.getActionDescriptors('tooltipAction');
descriptors.forEach(function (descriptor) {
tbMap.tooltipActionsMap[descriptor.name] = descriptor;
});
if (mapProvider === 'google-map') {
this.map = new TbGoogleMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
} else if (mapProvider === 'openstreet-map') {
this.map = new TbOpenStreetMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel);
}
}
setCallbacks(callbacks) {
Object.assign(this.callbacks, callbacks);
}
clearLocations() {
if (this.locations) {
var tbMap = this;
this.locations.forEach(function(location) {
if (location.marker) {
tbMap.map.removeMarker(location.marker);
}
if (location.polyline) {
tbMap.map.removePolyline(location.polyline);
}
});
this.locations = null;
this.markers = [];
if (this.drawRoutes) {
this.polylines = [];
}
}
}
setSubscription(subscription) {
this.subscription = subscription;
this.clearLocations();
}
configureLocationsSettings() {
this.locationSettings.latKeyName = this.ctx.settings.latKeyName || 'latitude';
this.locationSettings.lngKeyName = this.ctx.settings.lngKeyName || 'longitude';
this.locationSettings.tooltipPattern = this.ctx.settings.tooltipPattern || "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${"+this.locationSettings.latKeyName+":7}<br/><b>Longitude:</b> ${"+this.locationSettings.lngKeyName+":7}";
this.locationSettings.showLabel = this.ctx.settings.showLabel !== false;
this.locationSettings.displayTooltip = true;
this.locationSettings.labelColor = this.ctx.widgetConfig.color || '#000000',
this.locationSettings.label = this.ctx.settings.label || "${entityName}";
this.locationSettings.color = this.ctx.settings.color ? tinycolor(this.ctx.settings.color).toHexString() : "#FE7569";
this.locationSettings.useColorFunction = this.ctx.settings.useColorFunction === true;
if (angular.isDefined(this.ctx.settings.colorFunction) && this.ctx.settings.colorFunction.length > 0) {
try {
this.locationSettings.colorFunction = new Function('data, dsData, dsIndex', this.ctx.settings.colorFunction);
} catch (e) {
this.locationSettings.colorFunction = null;
}
}
this.locationSettings.useMarkerImageFunction = this.ctx.settings.useMarkerImageFunction === true;
if (angular.isDefined(this.ctx.settings.markerImageFunction) && this.ctx.settings.markerImageFunction.length > 0) {
try {
this.locationSettings.markerImageFunction = new Function('data, images, dsData, dsIndex', this.ctx.settings.markerImageFunction);
} catch (e) {
this.locationSettings.markerImageFunction = null;
}
}
this.locationSettings.markerImages = this.ctx.settings.markerImages || [];
if (!this.locationSettings.useMarkerImageFunction &&
angular.isDefined(this.ctx.settings.markerImage) &&
this.ctx.settings.markerImage.length > 0) {
this.locationSettings.markerImage = this.ctx.settings.markerImage;
this.locationSettings.useMarkerImage = true;
this.locationSettings.markerImageSize = this.ctx.settings.markerImageSize || 34;
}
if (this.drawRoutes) {
this.locationSettings.strokeWeight = this.ctx.settings.strokeWeight || 2;
this.locationSettings.strokeOpacity = this.ctx.settings.strokeOpacity || 1.0;
}
}
onTooltipAction(event, actionName, dsIndex) {
var descriptor = this.tooltipActionsMap[actionName];
if (descriptor) {
var datasource = this.subscription.datasources[dsIndex];
var entityId = {};
entityId.id = datasource.entityId;
entityId.entityType = datasource.entityType;
var entityName = datasource.entityName;
this.ctx.actionsApi.handleWidgetAction(event, descriptor, entityId, entityName);
}
}
update() {
var tbMap = this;
function updateLocationLabel(location) {
if (location.settings.showLabel && location.settings.labelReplaceInfo.variables.length) {
location.settings.labelText = fillPattern(location.settings.label,
location.settings.labelReplaceInfo, tbMap.subscription.data);
tbMap.map.updateMarkerLabel(location.marker, location.settings);
}
}
function calculateLocationColor(location, dataMap) {
if (location.settings.useColorFunction && location.settings.colorFunction) {
var color;
try {
color = location.settings.colorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
} catch (e) {/**/}
if (!color) {
color = '#FE7569';
}
return tinycolor(color).toHexString();
} else {
return location.settings.color;
}
}
function updateLocationColor(location, color, image) {
if (!location.settings.calculatedColor || location.settings.calculatedColor !== color) {
if (!location.settings.useMarkerImage && !image) {
tbMap.map.updateMarkerColor(location.marker, color);
}
if (location.polyline) {
tbMap.map.updatePolylineColor(location.polyline, location.settings, color);
}
location.settings.calculatedColor = color;
}
}
function calculateLocationMarkerImage(location, dataMap) {
if (location.settings.useMarkerImageFunction && location.settings.markerImageFunction) {
var image = null;
try {
image = location.settings.markerImageFunction(dataMap.dataMap, location.settings.markerImages, dataMap.dsDataMap, location.dsIndex);
} catch (e) {
image = null;
}
return image;
} else {
return null;
}
}
function updateLocationMarkerImage(location, image) {
if (image && (!location.settings.calculatedImage || !angular.equals(location.settings.calculatedImage, image))) {
tbMap.map.updateMarkerImage(location.marker, location.settings, image.url, image.size);
location.settings.calculatedImage = image;
}
}
function updateLocationStyle(location, dataMap) {
updateLocationLabel(location);
var color = calculateLocationColor(location, dataMap);
var image = calculateLocationMarkerImage(location, dataMap);
updateLocationColor(location, color, image);
updateLocationMarkerImage(location, image);
}
function locationRowClick($event, location) {
var descriptors = tbMap.ctx.actionsApi.getActionDescriptors('markerClick');
if (descriptors.length) {
var datasource = tbMap.subscription.datasources[location.dsIndex];
var entityId = {};
entityId.id = datasource.entityId;
entityId.entityType = datasource.entityType;
var entityName = datasource.entityName;
tbMap.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
}
}
function updateLocation(location, data, dataMap) {
var locationChanged = false;
if (location.latIndex > -1 && location.lngIndex > -1) {
var latData = data[location.latIndex].data;
var lngData = data[location.lngIndex].data;
var lat, lng, latLng;
if (latData.length > 0 && lngData.length > 0) {
if (tbMap.drawRoutes) {
// Create or update route
var latLngs = [];
for (var i = 0; i < latData.length; i++) {
lat = latData[i][1];
lng = lngData[i][1];
latLng = tbMap.map.createLatLng(lat, lng);
if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
latLngs.push(latLng);
}
}
if (latLngs.length > 0) {
var markerLocation = latLngs[latLngs.length - 1];
if (!location.marker) {
location.marker = tbMap.map.createMarker(markerLocation, location.settings,
function (event) {
tbMap.callbacks.onLocationClick(location);
locationRowClick(event, location);
}, [location.dsIndex]
);
} else {
tbMap.map.setMarkerPosition(location.marker, markerLocation);
}
}
if (!location.polyline) {
location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
tbMap.polylines.push(location.polyline);
locationChanged = true;
} else {
var prevPath = tbMap.map.getPolylineLatLngs(location.polyline);
if (!prevPath || !arraysEqual(prevPath, latLngs)) {
tbMap.map.setPolylineLatLngs(location.polyline, latLngs);
locationChanged = true;
}
}
} else {
// Create or update marker
lat = latData[latData.length - 1][1];
lng = lngData[lngData.length - 1][1];
latLng = tbMap.map.createLatLng(lat, lng);
if (!location.marker) {
location.marker = tbMap.map.createMarker(latLng, location.settings,
function (event) {
tbMap.callbacks.onLocationClick(location);
locationRowClick(event, location);
}, [location.dsIndex]);
tbMap.markers.push(location.marker);
locationChanged = true;
} else {
var prevPosition = tbMap.map.getMarkerPosition(location.marker);
if (!prevPosition.equals(latLng)) {
tbMap.map.setMarkerPosition(location.marker, latLng);
locationChanged = true;
}
}
}
updateLocationStyle(location, dataMap);
}
}
return locationChanged;
}
function loadLocations(data, datasources) {
var bounds = tbMap.map.createBounds();
tbMap.locations = [];
var dataMap = toLabelValueMap(data, datasources);
var currentDatasource = null;
var currentDatasourceIndex = -1;
var latIndex = -1;
var lngIndex = -1;
for (var i=0;i<data.length;i++) {
var dataKeyData = data[i];
var dataKey = dataKeyData.dataKey;
if (dataKeyData.datasource != currentDatasource) {
currentDatasource = dataKeyData.datasource;
currentDatasourceIndex++;
latIndex = -1;
lngIndex = -1;
}
var nameToCheck;
if (dataKey.locationAttrName) {
nameToCheck = dataKey.locationAttrName;
} else {
nameToCheck = dataKey.label;
}
if (nameToCheck === tbMap.locationSettings.latKeyName) {
latIndex = i;
} else if (nameToCheck === tbMap.locationSettings.lngKeyName) {
lngIndex = i;
}
if (latIndex > -1 && lngIndex > -1) {
var location = {
latIndex: latIndex,
lngIndex: lngIndex,
dsIndex: currentDatasourceIndex,
settings: angular.copy(tbMap.locationSettings)
};
if (location.settings.showLabel) {
location.settings.label = tbMap.utils.createLabelFromDatasource(currentDatasource, location.settings.label);
location.settings.labelReplaceInfo = processPattern(location.settings.label, datasources, currentDatasourceIndex);
location.settings.labelText = location.settings.label;
}
if (location.settings.displayTooltip) {
location.settings.tooltipPattern = tbMap.utils.createLabelFromDatasource(currentDatasource, location.settings.tooltipPattern);
location.settings.tooltipReplaceInfo = processPattern(location.settings.tooltipPattern, datasources, currentDatasourceIndex);
}
tbMap.locations.push(location);
updateLocation(location, data, dataMap);
if (location.polyline) {
tbMap.map.extendBounds(bounds, location.polyline);
} else if (location.marker) {
tbMap.map.extendBoundsWithMarker(bounds, location.marker);
}
latIndex = -1;
lngIndex = -1;
}
}
tbMap.map.fitBounds(bounds);
}
function updateLocations(data, datasources) {
var locationsChanged = false;
var bounds = tbMap.map.createBounds();
var dataMap = toLabelValueMap(data, datasources);
for (var p = 0; p < tbMap.locations.length; p++) {
var location = tbMap.locations[p];
locationsChanged |= updateLocation(location, data, dataMap);
if (location.polyline) {
tbMap.map.extendBounds(bounds, location.polyline);
} else if (location.marker) {
tbMap.map.extendBoundsWithMarker(bounds, location.marker);
}
}
if (locationsChanged) {
tbMap.map.fitBounds(bounds);
}
}
if (this.map && this.map.inited() && this.subscription) {
if (this.subscription.data) {
if (!this.locations) {
loadLocations(this.subscription.data, this.subscription.datasources);
} else {
updateLocations(this.subscription.data, this.subscription.datasources);
}
var tooltips = this.map.getTooltips();
for (var t=0; t < tooltips.length; t++) {
var tooltip = tooltips[t];
var text = fillPattern(tooltip.pattern, tooltip.replaceInfo, this.subscription.data);
text = fillPatternWithActions(text, 'onTooltipAction', tooltip.markerArgs);
tooltip.popup.setContent(text);
}
}
}
}
resize() {
if (this.map && this.map.inited()) {
this.map.invalidateSize();
if (this.locations && this.locations.length > 0) {
var bounds = this.map.createBounds();
for (var m = 0; m < this.markers.length; m++) {
this.map.extendBoundsWithMarker(bounds, this.markers[m]);
}
if (this.polylines) {
for (var p = 0; p < this.polylines.length; p++) {
this.map.extendBounds(bounds, this.polylines[p]);
}
}
this.map.fitBounds(bounds);
}
}
}
static settingsSchema(mapProvider, drawRoutes) {
var schema;
if (mapProvider === 'google-map') {
schema = angular.copy(googleMapSettingsSchema);
} else if (mapProvider === 'openstreet-map') {
schema = angular.copy(openstreetMapSettingsSchema);
}
angular.merge(schema.schema.properties, commonMapSettingsSchema.schema.properties);
schema.schema.required = schema.schema.required.concat(commonMapSettingsSchema.schema.required);
schema.form = schema.form.concat(commonMapSettingsSchema.form);
if (drawRoutes) {
angular.merge(schema.schema.properties, routeMapSettingsSchema.schema.properties);
schema.schema.required = schema.schema.required.concat(routeMapSettingsSchema.schema.required);
schema.form = schema.form.concat(routeMapSettingsSchema.form);
}
return schema;
}
static dataKeySettingsSchema(/*mapProvider*/) {
return {};
}
static actionSources() {
return {
'markerClick': {
name: 'widget-action.marker-click',
multiple: false
},
'tooltipAction': {
name: 'widget-action.tooltip-tag-action',
multiple: true
}
};
}
}
const googleMapSettingsSchema =
{
"schema":{
"title":"Google Map Configuration",
"type":"object",
"properties":{
"gmApiKey":{
"title":"Google Maps API Key",
"type":"string"
},
"gmDefaultMapType":{
"title":"Default map type",
"type":"string",
"default":"roadmap"
}
},
"required":[
"gmApiKey"
]
},
"form":[
"gmApiKey",
{
"key":"gmDefaultMapType",
"type":"rc-select",
"multiple":false,
"items":[
{
"value":"roadmap",
"label":"Roadmap"
},
{
"value":"satellite",
"label":"Satellite"
},
{
"value":"hybrid",
"label":"Hybrid"
},
{
"value":"terrain",
"label":"Terrain"
}
]
}
]
};
const openstreetMapSettingsSchema =
{
"schema":{
"title":"Openstreet Map Configuration",
"type":"object",
"properties":{
},
"required":[
]
},
"form":[
]
};
const commonMapSettingsSchema =
{
"schema":{
"title":"Map Configuration",
"type":"object",
"properties":{
"defaultZoomLevel":{
"title":"Default map zoom level (1 - 20)",
"type":"number"
},
"fitMapBounds":{
"title":"Fit map bounds to cover all markers",
"type":"boolean",
"default":true
},
"latKeyName":{
"title":"Latitude key name",
"type":"string",
"default":"latitude"
},
"lngKeyName":{
"title":"Longitude key name",
"type":"string",
"default":"longitude"
},
"showLabel":{
"title":"Show label",
"type":"boolean",
"default":true
},
"label":{
"title":"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )",
"type":"string",
"default":"${entityName}"
},
"tooltipPattern":{
"title":"Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
"type":"string",
"default":"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}"
},
"color":{
"title":"Color",
"type":"string"
},
"useColorFunction":{
"title":"Use color function",
"type":"boolean",
"default":false
},
"colorFunction":{
"title":"Color function: f(data, dsData, dsIndex)",
"type":"string"
},
"markerImage":{
"title":"Custom marker image",
"type":"string"
},
"markerImageSize":{
"title":"Custom marker image size (px)",
"type":"number",
"default":34
},
"useMarkerImageFunction":{
"title":"Use marker image function",
"type":"boolean",
"default":false
},
"markerImageFunction":{
"title":"Marker image function: f(data, images, dsData, dsIndex)",
"type":"string"
},
"markerImages":{
"title":"Marker images",
"type":"array",
"items":{
"title":"Marker image",
"type":"string"
}
}
},
"required":[]
},
"form":[
"defaultZoomLevel",
"fitMapBounds",
"latKeyName",
"lngKeyName",
"showLabel",
"label",
{
"key": "tooltipPattern",
"type": "textarea"
},
{
"key":"color",
"type":"color"
},
"useColorFunction",
{
"key":"colorFunction",
"type":"javascript"
},
{
"key":"markerImage",
"type":"image"
},
"markerImageSize",
"useMarkerImageFunction",
{
"key":"markerImageFunction",
"type":"javascript"
},
{
"key":"markerImages",
"items":[
{
"key":"markerImages[]",
"type":"image"
}
]
}
]
};
const routeMapSettingsSchema =
{
"schema":{
"title":"Route Map Configuration",
"type":"object",
"properties":{
"strokeWeight": {
"title": "Stroke weight",
"type": "number",
"default": 2
},
"strokeOpacity": {
"title": "Stroke opacity",
"type": "number",
"default": 1.0
}
},
"required":[
]
},
"form":[
"strokeWeight",
"strokeOpacity"
]
};

23
ui/src/app/widget/lib/openstreet-map.js

@ -42,6 +42,12 @@ export default class TbOpenStreetMap {
return angular.isDefined(this.map);
}
updateMarkerLabel(marker, settings) {
marker.unbindTooltip();
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
updateMarkerColor(marker, color) {
var pinColor = color.substr(1);
var icon = L.icon({
@ -78,14 +84,15 @@ export default class TbOpenStreetMap {
marker.setIcon(icon);
if (settings.showLabel) {
marker.unbindTooltip();
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.label+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: [0, -height + 10] });
marker.tooltipOffset = [0, -height + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
}
testImage.src = image;
}
createMarker(location, settings, onClickListener) {
createMarker(location, settings, onClickListener, markerArgs) {
var height = 34;
var pinColor = settings.color.substr(1);
var icon = L.icon({
@ -101,8 +108,9 @@ export default class TbOpenStreetMap {
var marker = L.marker(location, {icon: icon}).addTo(this.map);
if (settings.showLabel) {
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.label+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: [0, -height + 10] });
marker.tooltipOffset = [0, -height + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
}
if (settings.useMarkerImage) {
@ -110,7 +118,7 @@ export default class TbOpenStreetMap {
}
if (settings.displayTooltip) {
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo);
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, markerArgs);
}
if (onClickListener) {
@ -124,11 +132,12 @@ export default class TbOpenStreetMap {
this.map.removeLayer(marker);
}
createTooltip(marker, pattern, replaceInfo) {
createTooltip(marker, pattern, replaceInfo, markerArgs) {
var popup = L.popup();
popup.setContent('');
marker.bindPopup(popup, {autoClose: false, closeOnClick: false});
this.tooltips.push( {
markerArgs: markerArgs,
popup: popup,
pattern: pattern,
replaceInfo: replaceInfo

209
ui/src/app/widget/lib/widget-utils.js

@ -0,0 +1,209 @@
/*
* Copyright © 2016-2017 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.
*/
const varsRegex = /\$\{([^\}]*)\}/g;
const linkActionRegex = /\<link-act name=['"]([^['"]*)['"]\>([^\<]*)\<\/link-act\>/g;
const buttonActionRegex = /\<button-act name=['"]([^['"]*)['"]\>([^\<]*)\<\/button-act\>/g;
export function processPattern(pattern, datasources, dsIndex) {
var match = varsRegex.exec(pattern);
var replaceInfo = {};
replaceInfo.variables = [];
while (match !== null) {
var variableInfo = {};
variableInfo.dataKeyIndex = -1;
var variable = match[0];
var label = match[1];
var valDec = 2;
var splitVals = label.split(':');
if (splitVals.length > 1) {
label = splitVals[0];
valDec = parseFloat(splitVals[1]);
}
variableInfo.variable = variable;
variableInfo.valDec = valDec;
var keyIndex = -1;
if (label.startsWith('#')) {
var keyIndexStr = label.substring(1);
var n = Math.floor(Number(keyIndexStr));
if (String(n) === keyIndexStr && n >= 0) {
keyIndex = n;
}
}
var offset = 0;
for (var i=0;i<datasources.length;i++) {
var datasource = datasources[i];
if (dsIndex == i) {
if (keyIndex > -1) {
if (keyIndex < datasource.dataKeys.length) {
variableInfo.dataKeyIndex = offset + keyIndex;
}
} else {
for (var k = 0; k < datasource.dataKeys.length; k++) {
var dataKey = datasource.dataKeys[k];
if (dataKey.label === label) {
variableInfo.dataKeyIndex = offset + k;
break;
}
}
}
}
offset += datasource.dataKeys.length;
}
replaceInfo.variables.push(variableInfo);
match = varsRegex.exec(pattern);
}
return replaceInfo;
}
export function fillPattern(pattern, replaceInfo, data) {
var text = angular.copy(pattern);
for (var v = 0; v < replaceInfo.variables.length; v++) {
var variableInfo = replaceInfo.variables[v];
var txtVal = '';
if (variableInfo.dataKeyIndex > -1 && data[variableInfo.dataKeyIndex]) {
var varData = data[variableInfo.dataKeyIndex].data;
if (varData.length > 0) {
var val = varData[varData.length - 1][1];
if (isNumber(val)) {
txtVal = padValue(val, variableInfo.valDec, 0);
} else {
txtVal = val;
}
}
}
text = text.split(variableInfo.variable).join(txtVal);
}
return text;
}
function createLink(actionName, actionText, actionCallbackName, additionalArgs) {
var args = 'event,\''+actionName+'\'';
if (additionalArgs && additionalArgs.length) {
args += ','+additionalArgs.join();
}
return '<a href="#" onclick="angular.element(this).scope().'+actionCallbackName+'('+args+'); return false;">'+actionText+'</a>';
}
function createButton(actionName, actionText, actionCallbackName, additionalArgs) {
var args = 'event,\''+actionName+'\'';
if (additionalArgs && additionalArgs.length) {
args += ','+additionalArgs.join();
}
return '<button onclick="angular.element(this).scope().'+actionCallbackName+'('+args+'); return false;">'+actionText+'</button>';
}
export function fillPatternWithActions(pattern, actionCallbackName, additionalArgs) {
var text = angular.copy(pattern);
var match = linkActionRegex.exec(pattern);
var actionTags;
var actionName;
var actionText;
var actionHtml;
while (match !== null) {
actionTags = match[0];
actionName = match[1];
actionText = match[2];
actionHtml = createLink(actionName, actionText, actionCallbackName, additionalArgs);
text = text.split(actionTags).join(actionHtml);
match = linkActionRegex.exec(pattern);
}
match = buttonActionRegex.exec(pattern);
while (match !== null) {
actionTags = match[0];
actionName = match[1];
actionText = match[2];
actionHtml = createButton(actionName, actionText, actionCallbackName, additionalArgs);
text = text.split(actionTags).join(actionHtml);
match = buttonActionRegex.exec(pattern);
}
return text;
}
export function toLabelValueMap(data, datasources) {
var dataMap = {};
var dsDataMap = [];
for (var d=0;d<datasources.length;d++) {
dsDataMap[d] = {};
}
for (var i = 0; i < data.length; i++) {
var dataKey = data[i].dataKey;
var label = dataKey.label;
var keyData = data[i].data;
var val = null;
if (keyData.length > 0) {
val = keyData[keyData.length-1][1];
}
dataMap[label] = val;
var dsIndex = datasources.indexOf(data[i].datasource);
dsDataMap[dsIndex][label] = val;
}
return {
dataMap: dataMap,
dsDataMap: dsDataMap
};
}
export function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
export function padValue(val, dec, int) {
var i = 0;
var s, strVal, n;
val = parseFloat(val);
n = (val < 0);
val = Math.abs(val);
if (dec > 0) {
strVal = val.toFixed(dec).toString().split('.');
s = int - strVal[0].length;
for (; i < s; ++i) {
strVal[0] = '0' + strVal[0];
}
strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];
}
else {
strVal = Math.round(val).toString();
s = int - strVal.length;
for (; i < s; ++i) {
strVal = '0' + strVal;
}
strVal = (n ? '-' : '') + strVal;
}
return strVal;
}
export function arraysEqual(a, b) {
if (a === b) return true;
if (a === null || b === null) return false;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (!a[i].equals(b[i])) return false;
}
return true;
}
Loading…
Cancel
Save