Commit 8abb7183499a28e916323d98529d501aea22454c

Authored by Andrii Shvaika
2 parents ba140644 86f21023

Merge with develop

Showing 80 changed files with 3988 additions and 244 deletions
  1 +{
  2 + "title": "Gateways",
  3 + "configuration": {
  4 + "widgets": {
  5 + "94715984-ae74-76e4-20b7-2f956b01ed80": {
  6 + "isSystemType": true,
  7 + "bundleAlias": "entity_admin_widgets",
  8 + "typeAlias": "device_admin_table2",
  9 + "type": "latest",
  10 + "title": "New widget",
  11 + "sizeX": 24,
  12 + "sizeY": 12,
  13 + "config": {
  14 + "timewindow": {
  15 + "realtime": {
  16 + "interval": 1000,
  17 + "timewindowMs": 86400000
  18 + },
  19 + "aggregation": {
  20 + "type": "NONE",
  21 + "limit": 200
  22 + }
  23 + },
  24 + "showTitle": true,
  25 + "backgroundColor": "rgb(255, 255, 255)",
  26 + "color": "rgba(0, 0, 0, 0.87)",
  27 + "padding": "4px",
  28 + "settings": {
  29 + "enableSearch": true,
  30 + "displayPagination": true,
  31 + "defaultPageSize": 10,
  32 + "defaultSortOrder": "entityName",
  33 + "displayEntityName": true,
  34 + "displayEntityType": false,
  35 + "entitiesTitle": "List of gateways",
  36 + "enableSelectColumnDisplay": true,
  37 + "displayEntityLabel": false,
  38 + "entityNameColumnTitle": "Gateway Name"
  39 + },
  40 + "title": "Devices gateway table",
  41 + "dropShadow": true,
  42 + "enableFullscreen": true,
  43 + "titleStyle": {
  44 + "fontSize": "16px",
  45 + "fontWeight": 400,
  46 + "padding": "5px 10px 5px 10px"
  47 + },
  48 + "useDashboardTimewindow": false,
  49 + "showLegend": false,
  50 + "datasources": [
  51 + {
  52 + "type": "entity",
  53 + "dataKeys": [
  54 + {
  55 + "name": "active",
  56 + "type": "attribute",
  57 + "label": "Active",
  58 + "color": "#2196f3",
  59 + "settings": {
  60 + "columnWidth": "0px",
  61 + "useCellStyleFunction": true,
  62 + "useCellContentFunction": true,
  63 + "cellContentFunction": "value = '⬤';\nreturn value;",
  64 + "cellStyleFunction": "var color;\nif (value == 'false') {\n color = '#EB5757';\n} else {\n color = '#27AE60';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};"
  65 + },
  66 + "_hash": 0.3646047595211721
  67 + },
  68 + {
  69 + "name": "eventsSent",
  70 + "type": "timeseries",
  71 + "label": "Sent",
  72 + "color": "#4caf50",
  73 + "settings": {
  74 + "columnWidth": "0px",
  75 + "useCellStyleFunction": false,
  76 + "useCellContentFunction": false
  77 + },
  78 + "_hash": 0.7235710720767985
  79 + },
  80 + {
  81 + "name": "eventsProduced",
  82 + "type": "timeseries",
  83 + "label": "Events",
  84 + "color": "#f44336",
  85 + "settings": {
  86 + "columnWidth": "0px",
  87 + "useCellStyleFunction": false,
  88 + "useCellContentFunction": false
  89 + },
  90 + "_hash": 0.5085933386303254
  91 + },
  92 + {
  93 + "name": "LOGS",
  94 + "type": "timeseries",
  95 + "label": "Latest log",
  96 + "color": "#ffc107",
  97 + "settings": {
  98 + "columnWidth": "0px",
  99 + "useCellStyleFunction": false,
  100 + "useCellContentFunction": false
  101 + },
  102 + "_hash": 0.3504240371585048,
  103 + "postFuncBody": "if(value) {\n return value.substring(0, 31) + \"...\";\n} else {\n return '';\n}"
  104 + },
  105 + {
  106 + "name": "RemoteLoggingLevel",
  107 + "type": "attribute",
  108 + "label": "Log level",
  109 + "color": "#607d8b",
  110 + "settings": {
  111 + "columnWidth": "0px",
  112 + "useCellStyleFunction": false,
  113 + "useCellContentFunction": false
  114 + },
  115 + "_hash": 0.9785994222542516
  116 + }
  117 + ],
  118 + "entityAliasId": "3e0f533a-0db1-3292-184f-06e73535061a"
  119 + }
  120 + ],
  121 + "showTitleIcon": true,
  122 + "titleIcon": "list",
  123 + "iconColor": "rgba(0, 0, 0, 0.87)",
  124 + "iconSize": "24px",
  125 + "titleTooltip": "List device",
  126 + "widgetStyle": {},
  127 + "displayTimewindow": true,
  128 + "actions": {
  129 + "headerButton": [
  130 + {
  131 + "id": "70837a9d-c3de-a9a7-03c5-dccd14998758",
  132 + "name": "Add device",
  133 + "icon": "add",
  134 + "type": "customPretty",
  135 + "customHtml": "<md-dialog aria-label=\"Add entity\" style=\"width: 480px\">\n <form name=\"addDeviceForm\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Add device</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-progress-linear class=\"md-warn\" md-mode=\"indeterminate\" ng-disabled=\"!$root.loading && !vm.loading\" ng-show=\"$root.loading || vm.loading\"></md-progress-linear>\n <span style=\"min-height: 5px;\" flex=\"\" ng-show=\"!$root.loading && !vm.loading\"></span>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <fieldset ng-disabled=\"$root.loading || vm.loading\">\n <md-input-container flex class=\"md-block\">\n <label>Device name</label>\n <input ng-model=\"vm.deviceName\" name=deviceName required>\n <div ng-messages=\"addDeviceForm.deviceName.$error\">\n <div ng-message=\"required\">Device name is required.</div>\n </div>\n </md-input-container>\n <div flex layout=\"row\">\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Latitude</label>\n <input type=\"number\" step=\"any\" name=\"latitude\" ng-model=\"vm.attributes.latitude\">\n </md-input-container>\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Longitude</label>\n <input type=\"number\" step=\"any\" name=\"longitude\" ng-model=\"vm.attributes.longitude\">\n </md-input-container>\n </div>\n <md-input-container class=\"md-block\">\n <label>Label</label>\n <input name=\"deviceLabel\" ng-model=\"vm.deviceLabel\">\n </md-input-container>\n </fieldset>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"vm.loading || addDeviceForm.$invalid || !addDeviceForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>\n",
  136 + "customCss": "",
  137 + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n $mdDialog.show({\n controller: ['$scope', '$mdDialog',\n AddDeviceDialogController\n ],\n controllerAs: 'vm',\n template: htmlTemplate,\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction AddDeviceDialogController($scope, $mdDialog) {\n let vm = this;\n vm.types = types;\n vm.attributes = {};\n vm.deviceType = \"gateway\";\n\n vm.cancel = () => {\n $mdDialog.hide();\n };\n\n vm.save = () => {\n vm.loading = true;\n $scope.addDeviceForm.$setPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.deviceName,\n type: vm.deviceType,\n label: vm.deviceLabel\n };\n deviceService.saveDevice(device).then(\n (device) => {\n saveAttributes(device.id).then(\n () => {\n vm.loading = false;\n updateAliasData();\n $mdDialog.hide();\n }\n );\n },\n () => {\n vm.loading = false;\n }\n );\n };\n\n function saveAttributes(entityId) {\n let attributesArray = [];\n for (let key in vm.attributes) {\n attributesArray.push({\n key: key,\n value: vm.attributes[key]\n });\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(\n entityId.entityType, entityId.id,\n \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when([]);\n }\n }\n\n function updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController\n .resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController\n .setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController\n .getAliasInfo(aliasId));\n });\n $q.all(tasks).then(() => {\n $rootScope.$broadcast(\n 'widgetForceReInit');\n });\n }\n}"
  138 + }
  139 + ],
  140 + "actionCellButton": [
  141 + {
  142 + "id": "78845501-234e-a452-6819-82b5b776e99f",
  143 + "name": "Configuration",
  144 + "icon": "settings",
  145 + "type": "openDashboardState",
  146 + "targetDashboardStateId": "__entityname__config",
  147 + "openRightLayout": false,
  148 + "setEntityId": true
  149 + },
  150 + {
  151 + "id": "f6ffdba8-e40f-2b8d-851b-f5ecaf18606b",
  152 + "name": "Graphs",
  153 + "icon": "show_chart",
  154 + "type": "openDashboardState",
  155 + "targetDashboardStateId": "__entityname_grafic",
  156 + "setEntityId": true
  157 + },
  158 + {
  159 + "id": "242671f3-76c6-6982-7acc-6f12addf0ccc",
  160 + "name": "Edit device",
  161 + "icon": "edit",
  162 + "type": "customPretty",
  163 + "customHtml": "<md-dialog aria-label=\"Edit entity\" style=\"width: 480px\">\n <form name=\"editDeviceForm\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Edit device</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-progress-linear class=\"md-warn\" md-mode=\"indeterminate\" ng-disabled=\"!$root.loading && !vm.loading\" ng-show=\"$root.loading || vm.loading\"></md-progress-linear>\n <span style=\"min-height: 5px;\" flex=\"\" ng-show=\"!$root.loading && !vm.loading\"></span>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <fieldset ng-disabled=\"$root.loading || vm.loading\">\n <md-input-container flex class=\"md-block\">\n <label>Device name</label>\n <input ng-model=\"vm.device.name\" name=deviceName required>\n <div ng-messages=\"editDeviceForm.deviceName.$error\">\n <div ng-message=\"required\">Device name is required.</div>\n </div>\n </md-input-container>\n <!--<div flex layout=\"row\">-->\n <!--<tb-entity-subtype-autocomplete flex=\"50\"-->\n <!-- ng-disabled=\"true\"-->\n <!-- tb-required=\"true\"-->\n <!-- the-form=\"editDeviceForm\"-->\n <!-- ng-model=\"vm.device.type\"-->\n <!-- entity-type=\"vm.types.entityType.device\">-->\n <!--</tb-entity-subtype-autocomplete>-->\n <!-- <md-input-container flex=\"50\" class=\"md-block\">-->\n <!-- <label>Label</label>-->\n <!-- <input name=\"deviceLabel\" ng-model=\"vm.device.label\">-->\n <!-- </md-input-container>-->\n <!--</div>-->\n <div flex layout=\"row\">\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Latitude</label>\n <input type=\"number\" step=\"any\" name=\"latitude\" ng-model=\"vm.attributes.latitude\">\n </md-input-container>\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Longitude</label>\n <input type=\"number\" step=\"any\" name=\"longitude\" ng-model=\"vm.attributes.longitude\">\n </md-input-container>\n </div>\n <md-input-container class=\"md-block\">\n <label>Label</label>\n <input name=\"deviceLabel\" ng-model=\"vm.device.label\">\n </md-input-container>\n </fieldset>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"vm.loading || editDeviceForm.$invalid || !editDeviceForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",
  164 + "customCss": "/*=======================================================================*/\n/*========== There are two examples: for edit and add entity ==========*/\n/*=======================================================================*/\n/*======================== Edit entity example ========================*/\n/*=======================================================================*/\n/*\n.edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n\n.relations-list.old-relations tb-entity-select tb-entity-autocomplete button {\n display: none;\n} \n*/\n/*========================================================================*/\n/*========================= Add entity example =========================*/\n/*========================================================================*/\n/*\n.add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n*/\n",
  165 + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n \nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', EditDeviceDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction EditDeviceDialogController($scope,$mdDialog) {\n let vm = this;\n vm.types = types;\n vm.loading = false;\n vm.attributes = {};\n \n getEntityInfo();\n \n function getEntityInfo() {\n vm.loading = true;\n deviceService.getDevice(entityId.id).then(\n (device) => {\n attributeService.getEntityAttributesValues(entityId.entityType, entityId.id, 'SERVER_SCOPE').then(\n (data) => {\n if (data.length) {\n getEntityAttributes(data);\n }\n vm.device = device;\n vm.loading = false;\n } \n );\n }\n )\n }\n \n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n vm.save = () => {\n vm.loading = true;\n $scope.editDeviceForm.$setPristine();\n deviceService.saveDevice(vm.device).then(\n () => {\n saveAttributes().then(\n () => {\n updateAliasData();\n vm.loading = false;\n $mdDialog.hide();\n }\n );\n },\n () => {\n vm.loading = false;\n }\n );\n }\n \n function getEntityAttributes(attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n }\n \n function saveAttributes() {\n let attributesArray = [];\n for (let key in vm.attributes) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when([]);\n }\n }\n \n function updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n console.log(widgetContext);\n $q.all(tasks).then(() => {\n $rootScope.$broadcast('widgetForceReInit');\n });\n }\n}\n"
  166 + },
  167 + {
  168 + "id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa",
  169 + "name": "Delete device",
  170 + "icon": "delete",
  171 + "type": "custom",
  172 + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n $rootScope = $injector.get('$rootScope'),\n $q = $injector.get('$q');\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n let confirm = $mdDialog.confirm()\n .targetEvent($event)\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .cancel('Cancel')\n .ok('Delete');\n $mdDialog.show(confirm).then(() => {\n deleteDevice();\n })\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).then(\n () => {\n updateAliasData();\n }\n );\n}\n\nfunction updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(() => {\n $rootScope.$broadcast('entityAliasesChanged', aliasIds);\n });\n}"
  173 + }
  174 + ],
  175 + "rowClick": [
  176 + {
  177 + "id": "ad5fc7e1-5e60-e056-6940-a75a383466a1",
  178 + "name": "to_entityname__config",
  179 + "icon": "more_horiz",
  180 + "type": "openDashboardState",
  181 + "targetDashboardStateId": "__entityname__config",
  182 + "setEntityId": true,
  183 + "stateEntityParamName": ""
  184 + }
  185 + ]
  186 + }
  187 + },
  188 + "id": "94715984-ae74-76e4-20b7-2f956b01ed80"
  189 + },
  190 + "eadabbc7-519e-76fc-ba10-b3fe8c18da10": {
  191 + "isSystemType": true,
  192 + "bundleAlias": "cards",
  193 + "typeAlias": "timeseries_table",
  194 + "type": "timeseries",
  195 + "title": "New widget",
  196 + "sizeX": 14,
  197 + "sizeY": 13,
  198 + "config": {
  199 + "datasources": [
  200 + {
  201 + "type": "entity",
  202 + "dataKeys": [
  203 + {
  204 + "name": "LOGS",
  205 + "type": "timeseries",
  206 + "label": "LOGS",
  207 + "color": "#2196f3",
  208 + "settings": {
  209 + "useCellStyleFunction": false,
  210 + "useCellContentFunction": false
  211 + },
  212 + "_hash": 0.3496649158709739,
  213 + "postFuncBody": "return value.replace(/ - (.*) - \\[/gi, ' - <b style=\"color:#0f0;\">$1</b> - [');"
  214 + }
  215 + ],
  216 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  217 + }
  218 + ],
  219 + "timewindow": {
  220 + "realtime": {
  221 + "interval": 1000,
  222 + "timewindowMs": 2592000000
  223 + },
  224 + "aggregation": {
  225 + "type": "NONE",
  226 + "limit": 200
  227 + }
  228 + },
  229 + "showTitle": true,
  230 + "backgroundColor": "rgb(255, 255, 255)",
  231 + "color": "rgba(0, 0, 0, 0.87)",
  232 + "padding": "8px",
  233 + "settings": {
  234 + "showTimestamp": true,
  235 + "displayPagination": true,
  236 + "defaultPageSize": 10
  237 + },
  238 + "title": "Debug events (logs)",
  239 + "dropShadow": true,
  240 + "enableFullscreen": true,
  241 + "titleStyle": {
  242 + "fontSize": "16px",
  243 + "fontWeight": 400
  244 + },
  245 + "useDashboardTimewindow": false,
  246 + "showLegend": false,
  247 + "widgetStyle": {},
  248 + "actions": {},
  249 + "showTitleIcon": false,
  250 + "titleIcon": null,
  251 + "iconColor": "rgba(0, 0, 0, 0.87)",
  252 + "iconSize": "24px",
  253 + "titleTooltip": "",
  254 + "displayTimewindow": true
  255 + },
  256 + "id": "eadabbc7-519e-76fc-ba10-b3fe8c18da10"
  257 + },
  258 + "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": {
  259 + "isSystemType": true,
  260 + "bundleAlias": "charts",
  261 + "typeAlias": "basic_timeseries",
  262 + "type": "timeseries",
  263 + "title": "New widget",
  264 + "sizeX": 17,
  265 + "sizeY": 4,
  266 + "config": {
  267 + "datasources": [
  268 + {
  269 + "type": "entity",
  270 + "dataKeys": [
  271 + {
  272 + "name": "opcuaEventsProduced",
  273 + "type": "timeseries",
  274 + "label": "opcuaEventsProduced",
  275 + "color": "#2196f3",
  276 + "settings": {
  277 + "excludeFromStacking": false,
  278 + "hideDataByDefault": false,
  279 + "disableDataHiding": false,
  280 + "removeFromLegend": false,
  281 + "showLines": true,
  282 + "fillLines": false,
  283 + "showPoints": false,
  284 + "showPointShape": "circle",
  285 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  286 + "showPointsLineWidth": 5,
  287 + "showPointsRadius": 3,
  288 + "tooltipValueFormatter": "",
  289 + "showSeparateAxis": false,
  290 + "axisTitle": "",
  291 + "axisPosition": "left",
  292 + "axisTicksFormatter": "",
  293 + "comparisonSettings": {
  294 + "showValuesForComparison": true,
  295 + "comparisonValuesLabel": "",
  296 + "color": ""
  297 + }
  298 + },
  299 + "_hash": 0.1477920581839779
  300 + },
  301 + {
  302 + "name": "opcuaEventsSent",
  303 + "type": "timeseries",
  304 + "label": "opcuaEventsSent",
  305 + "color": "#4caf50",
  306 + "settings": {
  307 + "excludeFromStacking": false,
  308 + "hideDataByDefault": false,
  309 + "disableDataHiding": false,
  310 + "removeFromLegend": false,
  311 + "showLines": true,
  312 + "fillLines": false,
  313 + "showPoints": false,
  314 + "showPointShape": "circle",
  315 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  316 + "showPointsLineWidth": 5,
  317 + "showPointsRadius": 3,
  318 + "tooltipValueFormatter": "",
  319 + "showSeparateAxis": false,
  320 + "axisTitle": "",
  321 + "axisPosition": "left",
  322 + "axisTicksFormatter": "",
  323 + "comparisonSettings": {
  324 + "showValuesForComparison": true,
  325 + "comparisonValuesLabel": "",
  326 + "color": ""
  327 + }
  328 + },
  329 + "_hash": 0.6500957113784758
  330 + }
  331 + ],
  332 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  333 + }
  334 + ],
  335 + "timewindow": {
  336 + "realtime": {
  337 + "interval": 1000,
  338 + "timewindowMs": 120000
  339 + },
  340 + "aggregation": {
  341 + "type": "NONE",
  342 + "limit": 25000
  343 + },
  344 + "hideInterval": false,
  345 + "hideAggregation": false
  346 + },
  347 + "showTitle": true,
  348 + "backgroundColor": "#fff",
  349 + "color": "rgba(0, 0, 0, 0.87)",
  350 + "padding": "8px",
  351 + "settings": {
  352 + "shadowSize": 4,
  353 + "fontColor": "#545454",
  354 + "fontSize": 10,
  355 + "xaxis": {
  356 + "showLabels": true,
  357 + "color": "#545454"
  358 + },
  359 + "yaxis": {
  360 + "showLabels": true,
  361 + "color": "#545454"
  362 + },
  363 + "grid": {
  364 + "color": "#545454",
  365 + "tickColor": "#DDDDDD",
  366 + "verticalLines": true,
  367 + "horizontalLines": true,
  368 + "outlineWidth": 1
  369 + },
  370 + "stack": false,
  371 + "tooltipIndividual": false,
  372 + "timeForComparison": "months",
  373 + "xaxisSecond": {
  374 + "axisPosition": "top",
  375 + "showLabels": true
  376 + }
  377 + },
  378 + "title": "Real time information",
  379 + "dropShadow": true,
  380 + "enableFullscreen": true,
  381 + "titleStyle": {
  382 + "fontSize": "16px",
  383 + "fontWeight": 400
  384 + },
  385 + "mobileHeight": null,
  386 + "showTitleIcon": false,
  387 + "titleIcon": null,
  388 + "iconColor": "rgba(0, 0, 0, 0.87)",
  389 + "iconSize": "24px",
  390 + "titleTooltip": "",
  391 + "widgetStyle": {},
  392 + "useDashboardTimewindow": false,
  393 + "displayTimewindow": true,
  394 + "showLegend": true,
  395 + "legendConfig": {
  396 + "direction": "column",
  397 + "position": "right",
  398 + "showMin": true,
  399 + "showMax": true,
  400 + "showAvg": true,
  401 + "showTotal": true
  402 + },
  403 + "actions": {}
  404 + },
  405 + "id": "f928afc4-30d1-8d0c-e3cf-777f9f9d1155"
  406 + },
  407 + "2a95b473-042d-59d0-2da2-40d0cccb6c8a": {
  408 + "isSystemType": true,
  409 + "bundleAlias": "cards",
  410 + "typeAlias": "timeseries_table",
  411 + "type": "timeseries",
  412 + "title": "New widget",
  413 + "sizeX": 7,
  414 + "sizeY": 7,
  415 + "config": {
  416 + "datasources": [
  417 + {
  418 + "type": "entity",
  419 + "dataKeys": [
  420 + {
  421 + "name": "eventsSent",
  422 + "type": "timeseries",
  423 + "label": "Events",
  424 + "color": "#2196f3",
  425 + "settings": {
  426 + "useCellStyleFunction": false,
  427 + "useCellContentFunction": false
  428 + },
  429 + "_hash": 0.8156044798125357
  430 + },
  431 + {
  432 + "name": "eventsProduced",
  433 + "type": "timeseries",
  434 + "label": "Produced",
  435 + "color": "#4caf50",
  436 + "settings": {
  437 + "useCellStyleFunction": false,
  438 + "useCellContentFunction": false
  439 + },
  440 + "_hash": 0.6538259344015449
  441 + }
  442 + ],
  443 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  444 + }
  445 + ],
  446 + "timewindow": {
  447 + "realtime": {
  448 + "interval": 1000,
  449 + "timewindowMs": 604800000
  450 + },
  451 + "aggregation": {
  452 + "type": "NONE",
  453 + "limit": 200
  454 + }
  455 + },
  456 + "showTitle": true,
  457 + "backgroundColor": "rgb(255, 255, 255)",
  458 + "color": "rgba(0, 0, 0, 0.87)",
  459 + "padding": "8px",
  460 + "settings": {
  461 + "showTimestamp": true,
  462 + "displayPagination": true,
  463 + "defaultPageSize": 6,
  464 + "hideEmptyLines": true
  465 + },
  466 + "title": "Total Messages",
  467 + "dropShadow": true,
  468 + "enableFullscreen": true,
  469 + "titleStyle": {
  470 + "fontSize": "16px",
  471 + "fontWeight": 400
  472 + },
  473 + "useDashboardTimewindow": false,
  474 + "showLegend": false,
  475 + "widgetStyle": {},
  476 + "actions": {},
  477 + "showTitleIcon": false,
  478 + "titleIcon": null,
  479 + "iconColor": "rgba(0, 0, 0, 0.87)",
  480 + "iconSize": "24px",
  481 + "titleTooltip": "",
  482 + "displayTimewindow": true,
  483 + "legendConfig": {
  484 + "direction": "column",
  485 + "position": "bottom",
  486 + "showMin": false,
  487 + "showMax": false,
  488 + "showAvg": true,
  489 + "showTotal": false
  490 + }
  491 + },
  492 + "id": "2a95b473-042d-59d0-2da2-40d0cccb6c8a"
  493 + },
  494 + "aaa69366-aacc-9028-65aa-645c0f8533ec": {
  495 + "isSystemType": true,
  496 + "bundleAlias": "charts",
  497 + "typeAlias": "basic_timeseries",
  498 + "type": "timeseries",
  499 + "title": "New widget",
  500 + "sizeX": 17,
  501 + "sizeY": 4,
  502 + "config": {
  503 + "datasources": [
  504 + {
  505 + "type": "entity",
  506 + "dataKeys": [
  507 + {
  508 + "name": "eventsSent",
  509 + "type": "timeseries",
  510 + "label": "eventsSent",
  511 + "color": "#2196f3",
  512 + "settings": {
  513 + "excludeFromStacking": false,
  514 + "hideDataByDefault": false,
  515 + "disableDataHiding": false,
  516 + "removeFromLegend": false,
  517 + "showLines": true,
  518 + "fillLines": false,
  519 + "showPoints": false,
  520 + "showPointShape": "circle",
  521 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  522 + "showPointsLineWidth": 5,
  523 + "showPointsRadius": 3,
  524 + "tooltipValueFormatter": "",
  525 + "showSeparateAxis": false,
  526 + "axisTitle": "",
  527 + "axisPosition": "left",
  528 + "axisTicksFormatter": "",
  529 + "comparisonSettings": {
  530 + "showValuesForComparison": true,
  531 + "comparisonValuesLabel": "",
  532 + "color": ""
  533 + }
  534 + },
  535 + "_hash": 0.41414001784591314
  536 + },
  537 + {
  538 + "name": "eventsProduced",
  539 + "type": "timeseries",
  540 + "label": "eventsProduced",
  541 + "color": "#4caf50",
  542 + "settings": {
  543 + "excludeFromStacking": false,
  544 + "hideDataByDefault": false,
  545 + "disableDataHiding": false,
  546 + "removeFromLegend": false,
  547 + "showLines": true,
  548 + "fillLines": false,
  549 + "showPoints": false,
  550 + "showPointShape": "circle",
  551 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  552 + "showPointsLineWidth": 5,
  553 + "showPointsRadius": 3,
  554 + "tooltipValueFormatter": "",
  555 + "showSeparateAxis": false,
  556 + "axisTitle": "",
  557 + "axisPosition": "left",
  558 + "axisTicksFormatter": "",
  559 + "comparisonSettings": {
  560 + "showValuesForComparison": true,
  561 + "comparisonValuesLabel": "",
  562 + "color": ""
  563 + }
  564 + },
  565 + "_hash": 0.7819101846284422
  566 + }
  567 + ],
  568 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  569 + }
  570 + ],
  571 + "timewindow": {
  572 + "realtime": {
  573 + "timewindowMs": 60000
  574 + }
  575 + },
  576 + "showTitle": true,
  577 + "backgroundColor": "#fff",
  578 + "color": "rgba(0, 0, 0, 0.87)",
  579 + "padding": "8px",
  580 + "settings": {
  581 + "shadowSize": 4,
  582 + "fontColor": "#545454",
  583 + "fontSize": 10,
  584 + "xaxis": {
  585 + "showLabels": true,
  586 + "color": "#545454"
  587 + },
  588 + "yaxis": {
  589 + "showLabels": true,
  590 + "color": "#545454"
  591 + },
  592 + "grid": {
  593 + "color": "#545454",
  594 + "tickColor": "#DDDDDD",
  595 + "verticalLines": true,
  596 + "horizontalLines": true,
  597 + "outlineWidth": 1
  598 + },
  599 + "stack": false,
  600 + "tooltipIndividual": false,
  601 + "timeForComparison": "months",
  602 + "xaxisSecond": {
  603 + "axisPosition": "top",
  604 + "showLabels": true
  605 + }
  606 + },
  607 + "title": "History information",
  608 + "dropShadow": true,
  609 + "enableFullscreen": true,
  610 + "titleStyle": {
  611 + "fontSize": "16px",
  612 + "fontWeight": 400
  613 + },
  614 + "mobileHeight": null,
  615 + "showTitleIcon": false,
  616 + "titleIcon": null,
  617 + "iconColor": "rgba(0, 0, 0, 0.87)",
  618 + "iconSize": "24px",
  619 + "titleTooltip": "",
  620 + "widgetStyle": {},
  621 + "useDashboardTimewindow": true,
  622 + "displayTimewindow": true,
  623 + "showLegend": true,
  624 + "legendConfig": {
  625 + "direction": "column",
  626 + "position": "right",
  627 + "showMin": true,
  628 + "showMax": true,
  629 + "showAvg": true,
  630 + "showTotal": true
  631 + },
  632 + "actions": {}
  633 + },
  634 + "id": "aaa69366-aacc-9028-65aa-645c0f8533ec"
  635 + },
  636 + "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": {
  637 + "isSystemType": true,
  638 + "bundleAlias": "charts",
  639 + "typeAlias": "basic_timeseries",
  640 + "type": "timeseries",
  641 + "title": "New widget",
  642 + "sizeX": 17,
  643 + "sizeY": 4,
  644 + "config": {
  645 + "datasources": [
  646 + {
  647 + "type": "entity",
  648 + "dataKeys": [
  649 + {
  650 + "name": "bleEventsProduced",
  651 + "type": "timeseries",
  652 + "label": "bleEventsProduced",
  653 + "color": "#2196f3",
  654 + "settings": {
  655 + "excludeFromStacking": false,
  656 + "hideDataByDefault": false,
  657 + "disableDataHiding": false,
  658 + "removeFromLegend": false,
  659 + "showLines": true,
  660 + "fillLines": false,
  661 + "showPoints": false,
  662 + "showPointShape": "circle",
  663 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  664 + "showPointsLineWidth": 5,
  665 + "showPointsRadius": 3,
  666 + "tooltipValueFormatter": "",
  667 + "showSeparateAxis": false,
  668 + "axisTitle": "",
  669 + "axisPosition": "left",
  670 + "axisTicksFormatter": "",
  671 + "comparisonSettings": {
  672 + "showValuesForComparison": true,
  673 + "comparisonValuesLabel": "",
  674 + "color": ""
  675 + }
  676 + },
  677 + "_hash": 0.5625165504526104
  678 + },
  679 + {
  680 + "name": "bleEventsSent",
  681 + "type": "timeseries",
  682 + "label": "bleEventsSent",
  683 + "color": "#4caf50",
  684 + "settings": {
  685 + "excludeFromStacking": false,
  686 + "hideDataByDefault": false,
  687 + "disableDataHiding": false,
  688 + "removeFromLegend": false,
  689 + "showLines": true,
  690 + "fillLines": false,
  691 + "showPoints": false,
  692 + "showPointShape": "circle",
  693 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  694 + "showPointsLineWidth": 5,
  695 + "showPointsRadius": 3,
  696 + "tooltipValueFormatter": "",
  697 + "showSeparateAxis": false,
  698 + "axisTitle": "",
  699 + "axisPosition": "left",
  700 + "axisTicksFormatter": "",
  701 + "comparisonSettings": {
  702 + "showValuesForComparison": true,
  703 + "comparisonValuesLabel": "",
  704 + "color": ""
  705 + }
  706 + },
  707 + "_hash": 0.6817950080745288
  708 + }
  709 + ],
  710 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  711 + }
  712 + ],
  713 + "timewindow": {
  714 + "realtime": {
  715 + "interval": 5000,
  716 + "timewindowMs": 120000
  717 + },
  718 + "aggregation": {
  719 + "type": "AVG",
  720 + "limit": 25000
  721 + }
  722 + },
  723 + "showTitle": true,
  724 + "backgroundColor": "#fff",
  725 + "color": "rgba(0, 0, 0, 0.87)",
  726 + "padding": "8px",
  727 + "settings": {
  728 + "shadowSize": 4,
  729 + "fontColor": "#545454",
  730 + "fontSize": 10,
  731 + "xaxis": {
  732 + "showLabels": true,
  733 + "color": "#545454"
  734 + },
  735 + "yaxis": {
  736 + "showLabels": true,
  737 + "color": "#545454"
  738 + },
  739 + "grid": {
  740 + "color": "#545454",
  741 + "tickColor": "#DDDDDD",
  742 + "verticalLines": true,
  743 + "horizontalLines": true,
  744 + "outlineWidth": 1
  745 + },
  746 + "stack": false,
  747 + "tooltipIndividual": false,
  748 + "timeForComparison": "months",
  749 + "xaxisSecond": {
  750 + "axisPosition": "top",
  751 + "showLabels": true
  752 + }
  753 + },
  754 + "title": "Real time information",
  755 + "dropShadow": true,
  756 + "enableFullscreen": true,
  757 + "titleStyle": {
  758 + "fontSize": "16px",
  759 + "fontWeight": 400
  760 + },
  761 + "mobileHeight": null,
  762 + "showTitleIcon": false,
  763 + "titleIcon": null,
  764 + "iconColor": "rgba(0, 0, 0, 0.87)",
  765 + "iconSize": "24px",
  766 + "titleTooltip": "",
  767 + "widgetStyle": {},
  768 + "useDashboardTimewindow": false,
  769 + "displayTimewindow": true,
  770 + "showLegend": true,
  771 + "legendConfig": {
  772 + "direction": "column",
  773 + "position": "right",
  774 + "showMin": true,
  775 + "showMax": true,
  776 + "showAvg": true,
  777 + "showTotal": true
  778 + },
  779 + "actions": {}
  780 + },
  781 + "id": "ce5c7d01-a3ef-5cf0-4578-8505135c23a0"
  782 + },
  783 + "466f046d-6005-a168-b107-60fcb2469cd5": {
  784 + "isSystemType": true,
  785 + "bundleAlias": "gateway_widgets",
  786 + "typeAlias": "attributes_card",
  787 + "type": "latest",
  788 + "title": "New widget",
  789 + "sizeX": 7,
  790 + "sizeY": 5,
  791 + "config": {
  792 + "datasources": [
  793 + {
  794 + "type": "entity",
  795 + "dataKeys": [],
  796 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  797 + }
  798 + ],
  799 + "timewindow": {
  800 + "realtime": {
  801 + "timewindowMs": 60000
  802 + }
  803 + },
  804 + "showTitle": true,
  805 + "backgroundColor": "#fff",
  806 + "color": "rgba(0, 0, 0, 0.87)",
  807 + "padding": "8px",
  808 + "settings": {
  809 + "eventsTitle": "Gateway Events Form",
  810 + "eventsReg": [
  811 + "EventsProduced",
  812 + "EventsSent"
  813 + ]
  814 + },
  815 + "title": "Gateway events",
  816 + "showTitleIcon": false,
  817 + "titleIcon": null,
  818 + "iconColor": "rgba(0, 0, 0, 0.87)",
  819 + "iconSize": "24px",
  820 + "titleTooltip": "",
  821 + "dropShadow": true,
  822 + "enableFullscreen": true,
  823 + "widgetStyle": {},
  824 + "titleStyle": {
  825 + "fontSize": "16px",
  826 + "fontWeight": 400
  827 + },
  828 + "useDashboardTimewindow": true,
  829 + "displayTimewindow": true,
  830 + "showLegend": false,
  831 + "actions": {}
  832 + },
  833 + "id": "466f046d-6005-a168-b107-60fcb2469cd5"
  834 + },
  835 + "8fc32225-164f-3258-73f7-e6b6d959cf0b": {
  836 + "isSystemType": true,
  837 + "bundleAlias": "gateway_widgets",
  838 + "typeAlias": "config_form_latest",
  839 + "type": "latest",
  840 + "title": "New widget",
  841 + "sizeX": 10,
  842 + "sizeY": 9,
  843 + "config": {
  844 + "datasources": [
  845 + {
  846 + "type": "entity",
  847 + "dataKeys": [],
  848 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  849 + }
  850 + ],
  851 + "timewindow": {
  852 + "realtime": {
  853 + "timewindowMs": 60000
  854 + }
  855 + },
  856 + "showTitle": true,
  857 + "backgroundColor": "#fff",
  858 + "color": "rgba(0, 0, 0, 0.87)",
  859 + "padding": "8px",
  860 + "settings": {
  861 + "gatewayTitle": "Gateway configuration (Single device)",
  862 + "readOnly": false
  863 + },
  864 + "title": "New Gateway configuration (Single device)",
  865 + "showTitleIcon": false,
  866 + "titleIcon": null,
  867 + "iconColor": "rgba(0, 0, 0, 0.87)",
  868 + "iconSize": "24px",
  869 + "titleTooltip": "",
  870 + "dropShadow": true,
  871 + "enableFullscreen": true,
  872 + "widgetStyle": {},
  873 + "titleStyle": {
  874 + "fontSize": "16px",
  875 + "fontWeight": 400
  876 + },
  877 + "useDashboardTimewindow": true,
  878 + "displayTimewindow": true,
  879 + "showLegend": false,
  880 + "actions": {}
  881 + },
  882 + "id": "8fc32225-164f-3258-73f7-e6b6d959cf0b"
  883 + },
  884 + "063fc179-c9fd-f952-e714-f24e9c43c05c": {
  885 + "isSystemType": true,
  886 + "bundleAlias": "control_widgets",
  887 + "typeAlias": "rpcbutton",
  888 + "type": "rpc",
  889 + "title": "New widget",
  890 + "sizeX": 4,
  891 + "sizeY": 2,
  892 + "config": {
  893 + "targetDeviceAliases": [],
  894 + "showTitle": false,
  895 + "backgroundColor": "#e6e7e8",
  896 + "color": "rgba(0, 0, 0, 0.87)",
  897 + "padding": "0px",
  898 + "settings": {
  899 + "requestTimeout": 5000,
  900 + "oneWayElseTwoWay": true,
  901 + "styleButton": {
  902 + "isRaised": true,
  903 + "isPrimary": false
  904 + },
  905 + "methodParams": "{}",
  906 + "methodName": "gateway_reboot",
  907 + "buttonText": "GATEWAY REBOOT"
  908 + },
  909 + "title": "New RPC Button",
  910 + "dropShadow": true,
  911 + "enableFullscreen": false,
  912 + "widgetStyle": {},
  913 + "titleStyle": {
  914 + "fontSize": "16px",
  915 + "fontWeight": 400
  916 + },
  917 + "useDashboardTimewindow": true,
  918 + "showLegend": false,
  919 + "actions": {},
  920 + "datasources": [],
  921 + "showTitleIcon": false,
  922 + "titleIcon": null,
  923 + "iconColor": "rgba(0, 0, 0, 0.87)",
  924 + "iconSize": "24px",
  925 + "titleTooltip": "",
  926 + "displayTimewindow": true,
  927 + "targetDeviceAliasIds": [
  928 + "b2487e75-2fa4-f211-142c-434dfd50c70c"
  929 + ]
  930 + },
  931 + "id": "063fc179-c9fd-f952-e714-f24e9c43c05c"
  932 + },
  933 + "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": {
  934 + "isSystemType": true,
  935 + "bundleAlias": "control_widgets",
  936 + "typeAlias": "rpcbutton",
  937 + "type": "rpc",
  938 + "title": "New widget",
  939 + "sizeX": 4,
  940 + "sizeY": 2,
  941 + "config": {
  942 + "targetDeviceAliases": [],
  943 + "showTitle": false,
  944 + "backgroundColor": "#e6e7e8",
  945 + "color": "rgba(0, 0, 0, 0.87)",
  946 + "padding": "0px",
  947 + "settings": {
  948 + "requestTimeout": 5000,
  949 + "oneWayElseTwoWay": true,
  950 + "styleButton": {
  951 + "isRaised": true,
  952 + "isPrimary": false
  953 + },
  954 + "methodName": "gateway_restart",
  955 + "methodParams": "{}",
  956 + "buttonText": "gateway restart"
  957 + },
  958 + "title": "New RPC Button",
  959 + "dropShadow": true,
  960 + "enableFullscreen": false,
  961 + "widgetStyle": {},
  962 + "titleStyle": {
  963 + "fontSize": "16px",
  964 + "fontWeight": 400
  965 + },
  966 + "useDashboardTimewindow": true,
  967 + "showLegend": false,
  968 + "actions": {},
  969 + "datasources": [],
  970 + "showTitleIcon": false,
  971 + "titleIcon": null,
  972 + "iconColor": "rgba(0, 0, 0, 0.87)",
  973 + "iconSize": "24px",
  974 + "titleTooltip": "",
  975 + "displayTimewindow": true,
  976 + "targetDeviceAliasIds": [
  977 + "b2487e75-2fa4-f211-142c-434dfd50c70c"
  978 + ]
  979 + },
  980 + "id": "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7"
  981 + },
  982 + "6770b6ba-eff8-df05-75f8-c1f9326d4842": {
  983 + "isSystemType": true,
  984 + "bundleAlias": "input_widgets",
  985 + "typeAlias": "markers_placement_openstreetmap",
  986 + "type": "latest",
  987 + "title": "New widget",
  988 + "sizeX": 6,
  989 + "sizeY": 4,
  990 + "config": {
  991 + "datasources": [
  992 + {
  993 + "type": "entity",
  994 + "dataKeys": [
  995 + {
  996 + "name": "latitude",
  997 + "type": "attribute",
  998 + "label": "latitude",
  999 + "color": "#2196f3",
  1000 + "settings": {},
  1001 + "_hash": 0.9743324774725604
  1002 + },
  1003 + {
  1004 + "name": "longitude",
  1005 + "type": "attribute",
  1006 + "label": "longitude",
  1007 + "color": "#4caf50",
  1008 + "settings": {},
  1009 + "_hash": 0.5530093635101525
  1010 + }
  1011 + ],
  1012 + "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"
  1013 + }
  1014 + ],
  1015 + "timewindow": {
  1016 + "realtime": {
  1017 + "timewindowMs": 60000
  1018 + }
  1019 + },
  1020 + "showTitle": false,
  1021 + "backgroundColor": "#fff",
  1022 + "color": "rgba(0, 0, 0, 0.87)",
  1023 + "padding": "8px",
  1024 + "settings": {
  1025 + "fitMapBounds": true,
  1026 + "latKeyName": "latitude",
  1027 + "lngKeyName": "longitude",
  1028 + "showLabel": true,
  1029 + "label": "${entityName}",
  1030 + "tooltipPattern": "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><br/><link-act name='delete'>Delete</link-act>",
  1031 + "markerImageSize": 34,
  1032 + "useColorFunction": false,
  1033 + "markerImages": [],
  1034 + "useMarkerImageFunction": false,
  1035 + "color": "#fe7569",
  1036 + "mapProvider": "OpenStreetMap.Mapnik",
  1037 + "showTooltip": true,
  1038 + "autocloseTooltip": true,
  1039 + "defaultCenterPosition": [
  1040 + 0,
  1041 + 0
  1042 + ],
  1043 + "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
  1044 + "showTooltipAction": "click",
  1045 + "polygonKeyName": "coordinates",
  1046 + "polygonOpacity": 0.5,
  1047 + "polygonStrokeOpacity": 1,
  1048 + "polygonStrokeWeight": 1,
  1049 + "zoomOnClick": true,
  1050 + "showCoverageOnHover": true,
  1051 + "animate": true,
  1052 + "maxClusterRadius": 80,
  1053 + "removeOutsideVisibleBounds": true,
  1054 + "defaultZoomLevel": 5
  1055 + },
  1056 + "title": "Gateway Location",
  1057 + "dropShadow": true,
  1058 + "enableFullscreen": false,
  1059 + "titleStyle": {
  1060 + "fontSize": "16px",
  1061 + "fontWeight": 400
  1062 + },
  1063 + "useDashboardTimewindow": true,
  1064 + "showLegend": false,
  1065 + "widgetStyle": {},
  1066 + "actions": {
  1067 + "tooltipAction": [
  1068 + {
  1069 + "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",
  1070 + "name": "delete",
  1071 + "icon": "more_horiz",
  1072 + "type": "custom",
  1073 + "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });"
  1074 + }
  1075 + ]
  1076 + },
  1077 + "showTitleIcon": false,
  1078 + "titleIcon": null,
  1079 + "iconColor": "rgba(0, 0, 0, 0.87)",
  1080 + "iconSize": "24px",
  1081 + "titleTooltip": "",
  1082 + "displayTimewindow": true
  1083 + },
  1084 + "id": "6770b6ba-eff8-df05-75f8-c1f9326d4842"
  1085 + }
  1086 + },
  1087 + "states": {
  1088 + "main_gateway": {
  1089 + "name": "Gateways",
  1090 + "root": true,
  1091 + "layouts": {
  1092 + "main": {
  1093 + "widgets": {
  1094 + "94715984-ae74-76e4-20b7-2f956b01ed80": {
  1095 + "sizeX": 24,
  1096 + "sizeY": 12,
  1097 + "row": 0,
  1098 + "col": 0
  1099 + }
  1100 + },
  1101 + "gridSettings": {
  1102 + "backgroundColor": "#eeeeee",
  1103 + "color": "rgba(0,0,0,0.870588)",
  1104 + "columns": 24,
  1105 + "margins": [
  1106 + 10,
  1107 + 10
  1108 + ],
  1109 + "backgroundSizeMode": "100%",
  1110 + "autoFillHeight": true,
  1111 + "mobileAutoFillHeight": false,
  1112 + "mobileRowHeight": 70
  1113 + }
  1114 + }
  1115 + }
  1116 + },
  1117 + "__entityname__config": {
  1118 + "name": "${entityName} Configuration",
  1119 + "root": false,
  1120 + "layouts": {
  1121 + "main": {
  1122 + "widgets": {
  1123 + "eadabbc7-519e-76fc-ba10-b3fe8c18da10": {
  1124 + "sizeX": 14,
  1125 + "sizeY": 13,
  1126 + "row": 0,
  1127 + "col": 10
  1128 + },
  1129 + "8fc32225-164f-3258-73f7-e6b6d959cf0b": {
  1130 + "sizeX": 10,
  1131 + "sizeY": 9,
  1132 + "row": 0,
  1133 + "col": 0
  1134 + },
  1135 + "063fc179-c9fd-f952-e714-f24e9c43c05c": {
  1136 + "sizeX": 4,
  1137 + "sizeY": 2,
  1138 + "row": 9,
  1139 + "col": 0
  1140 + },
  1141 + "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": {
  1142 + "sizeX": 4,
  1143 + "sizeY": 2,
  1144 + "row": 11,
  1145 + "col": 0
  1146 + },
  1147 + "6770b6ba-eff8-df05-75f8-c1f9326d4842": {
  1148 + "sizeX": 6,
  1149 + "sizeY": 4,
  1150 + "row": 9,
  1151 + "col": 4
  1152 + }
  1153 + },
  1154 + "gridSettings": {
  1155 + "backgroundColor": "#eeeeee",
  1156 + "color": "rgba(0,0,0,0.870588)",
  1157 + "columns": 24,
  1158 + "margins": [
  1159 + 10,
  1160 + 10
  1161 + ],
  1162 + "backgroundSizeMode": "100%",
  1163 + "autoFillHeight": true,
  1164 + "mobileAutoFillHeight": false,
  1165 + "mobileRowHeight": 70
  1166 + }
  1167 + }
  1168 + }
  1169 + },
  1170 + "__entityname_grafic": {
  1171 + "name": "${entityName} Details",
  1172 + "root": false,
  1173 + "layouts": {
  1174 + "main": {
  1175 + "widgets": {
  1176 + "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": {
  1177 + "sizeX": 17,
  1178 + "sizeY": 4,
  1179 + "mobileHeight": null,
  1180 + "row": 4,
  1181 + "col": 7
  1182 + },
  1183 + "2a95b473-042d-59d0-2da2-40d0cccb6c8a": {
  1184 + "sizeX": 7,
  1185 + "sizeY": 7,
  1186 + "row": 5,
  1187 + "col": 0
  1188 + },
  1189 + "aaa69366-aacc-9028-65aa-645c0f8533ec": {
  1190 + "sizeX": 17,
  1191 + "sizeY": 4,
  1192 + "mobileHeight": null,
  1193 + "row": 0,
  1194 + "col": 7
  1195 + },
  1196 + "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": {
  1197 + "sizeX": 17,
  1198 + "sizeY": 4,
  1199 + "mobileHeight": null,
  1200 + "row": 8,
  1201 + "col": 7
  1202 + },
  1203 + "466f046d-6005-a168-b107-60fcb2469cd5": {
  1204 + "sizeX": 7,
  1205 + "sizeY": 5,
  1206 + "row": 0,
  1207 + "col": 0
  1208 + }
  1209 + },
  1210 + "gridSettings": {
  1211 + "backgroundColor": "#eeeeee",
  1212 + "color": "rgba(0,0,0,0.870588)",
  1213 + "columns": 24,
  1214 + "margins": [
  1215 + 10,
  1216 + 10
  1217 + ],
  1218 + "backgroundSizeMode": "auto 100%",
  1219 + "autoFillHeight": true,
  1220 + "mobileAutoFillHeight": true,
  1221 + "mobileRowHeight": 70
  1222 + }
  1223 + }
  1224 + }
  1225 + }
  1226 + },
  1227 + "entityAliases": {
  1228 + "3e0f533a-0db1-3292-184f-06e73535061a": {
  1229 + "id": "3e0f533a-0db1-3292-184f-06e73535061a",
  1230 + "alias": "Gateways",
  1231 + "filter": {
  1232 + "type": "deviceType",
  1233 + "resolveMultiple": true,
  1234 + "deviceType": "gateway",
  1235 + "deviceNameFilter": ""
  1236 + }
  1237 + },
  1238 + "b2487e75-2fa4-f211-142c-434dfd50c70c": {
  1239 + "id": "b2487e75-2fa4-f211-142c-434dfd50c70c",
  1240 + "alias": "Current Gateway",
  1241 + "filter": {
  1242 + "type": "stateEntity",
  1243 + "resolveMultiple": false,
  1244 + "stateEntityParamName": "",
  1245 + "defaultStateEntity": null
  1246 + }
  1247 + }
  1248 + },
  1249 + "timewindow": {
  1250 + "realtime": {
  1251 + "interval": 1000,
  1252 + "timewindowMs": 86400000
  1253 + },
  1254 + "aggregation": {
  1255 + "type": "NONE",
  1256 + "limit": 25000
  1257 + },
  1258 + "hideInterval": false,
  1259 + "hideAggregation": false,
  1260 + "hideAggInterval": false
  1261 + },
  1262 + "settings": {
  1263 + "stateControllerId": "entity",
  1264 + "showTitle": true,
  1265 + "showDashboardsSelect": true,
  1266 + "showEntitiesSelect": true,
  1267 + "showDashboardTimewindow": true,
  1268 + "showDashboardExport": true,
  1269 + "toolbarAlwaysOpen": true,
  1270 + "titleColor": "rgba(0,0,0,0.870588)"
  1271 + }
  1272 + },
  1273 + "name": "Gateways"
  1274 +}
\ No newline at end of file
... ...
  1 +{
  2 + "title": "Rule Engine Statistics",
  3 + "configuration": {
  4 + "widgets": {
  5 + "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {
  6 + "isSystemType": true,
  7 + "bundleAlias": "charts",
  8 + "typeAlias": "basic_timeseries",
  9 + "type": "timeseries",
  10 + "title": "New widget",
  11 + "sizeX": 12,
  12 + "sizeY": 7,
  13 + "config": {
  14 + "datasources": [
  15 + {
  16 + "type": "entity",
  17 + "dataKeys": [
  18 + {
  19 + "name": "successfulMsgs",
  20 + "type": "timeseries",
  21 + "label": "${entityName} Successful",
  22 + "color": "#4caf50",
  23 + "settings": {
  24 + "excludeFromStacking": false,
  25 + "hideDataByDefault": false,
  26 + "disableDataHiding": false,
  27 + "removeFromLegend": false,
  28 + "showLines": true,
  29 + "fillLines": false,
  30 + "showPoints": false,
  31 + "showPointShape": "circle",
  32 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  33 + "showPointsLineWidth": 5,
  34 + "showPointsRadius": 3,
  35 + "showSeparateAxis": false,
  36 + "axisPosition": "left",
  37 + "thresholds": [
  38 + {
  39 + "thresholdValueSource": "predefinedValue"
  40 + }
  41 + ],
  42 + "comparisonSettings": {
  43 + "showValuesForComparison": true
  44 + }
  45 + },
  46 + "_hash": 0.15490750967648736
  47 + },
  48 + {
  49 + "name": "failedMsgs",
  50 + "type": "timeseries",
  51 + "label": "${entityName} Permanent Failures",
  52 + "color": "#ef5350",
  53 + "settings": {
  54 + "excludeFromStacking": false,
  55 + "hideDataByDefault": false,
  56 + "disableDataHiding": false,
  57 + "removeFromLegend": false,
  58 + "showLines": true,
  59 + "fillLines": false,
  60 + "showPoints": false,
  61 + "showPointShape": "circle",
  62 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  63 + "showPointsLineWidth": 5,
  64 + "showPointsRadius": 3,
  65 + "showSeparateAxis": false,
  66 + "axisPosition": "left",
  67 + "thresholds": [
  68 + {
  69 + "thresholdValueSource": "predefinedValue"
  70 + }
  71 + ],
  72 + "comparisonSettings": {
  73 + "showValuesForComparison": true
  74 + }
  75 + },
  76 + "_hash": 0.4186621166514697
  77 + },
  78 + {
  79 + "name": "tmpFailed",
  80 + "type": "timeseries",
  81 + "label": "${entityName} Processing Failures",
  82 + "color": "#ffc107",
  83 + "settings": {
  84 + "excludeFromStacking": false,
  85 + "hideDataByDefault": false,
  86 + "disableDataHiding": false,
  87 + "removeFromLegend": false,
  88 + "showLines": true,
  89 + "fillLines": false,
  90 + "showPoints": false,
  91 + "showPointShape": "circle",
  92 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  93 + "showPointsLineWidth": 5,
  94 + "showPointsRadius": 3,
  95 + "showSeparateAxis": false,
  96 + "axisPosition": "left",
  97 + "thresholds": [
  98 + {
  99 + "thresholdValueSource": "predefinedValue"
  100 + }
  101 + ],
  102 + "comparisonSettings": {
  103 + "showValuesForComparison": true
  104 + }
  105 + },
  106 + "_hash": 0.49891007198715376
  107 + }
  108 + ],
  109 + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
  110 + }
  111 + ],
  112 + "timewindow": {
  113 + "realtime": {
  114 + "interval": 1000,
  115 + "timewindowMs": 300000
  116 + },
  117 + "aggregation": {
  118 + "type": "NONE",
  119 + "limit": 8640
  120 + },
  121 + "hideInterval": false,
  122 + "hideAggregation": false,
  123 + "hideAggInterval": false
  124 + },
  125 + "showTitle": true,
  126 + "backgroundColor": "#fff",
  127 + "color": "rgba(0, 0, 0, 0.87)",
  128 + "padding": "8px",
  129 + "settings": {
  130 + "shadowSize": 4,
  131 + "fontColor": "#545454",
  132 + "fontSize": 10,
  133 + "xaxis": {
  134 + "showLabels": true,
  135 + "color": "#545454"
  136 + },
  137 + "yaxis": {
  138 + "showLabels": true,
  139 + "color": "#545454"
  140 + },
  141 + "grid": {
  142 + "color": "#545454",
  143 + "tickColor": "#DDDDDD",
  144 + "verticalLines": true,
  145 + "horizontalLines": true,
  146 + "outlineWidth": 1
  147 + },
  148 + "stack": false,
  149 + "tooltipIndividual": false,
  150 + "timeForComparison": "months",
  151 + "xaxisSecond": {
  152 + "axisPosition": "top",
  153 + "showLabels": true
  154 + }
  155 + },
  156 + "title": "Queue Stats",
  157 + "dropShadow": true,
  158 + "enableFullscreen": true,
  159 + "titleStyle": {
  160 + "fontSize": "16px",
  161 + "fontWeight": 400
  162 + },
  163 + "mobileHeight": null,
  164 + "showTitleIcon": false,
  165 + "titleIcon": null,
  166 + "iconColor": "rgba(0, 0, 0, 0.87)",
  167 + "iconSize": "24px",
  168 + "titleTooltip": "",
  169 + "widgetStyle": {},
  170 + "useDashboardTimewindow": false,
  171 + "displayTimewindow": true,
  172 + "showLegend": true,
  173 + "actions": {},
  174 + "legendConfig": {
  175 + "direction": "column",
  176 + "position": "bottom",
  177 + "showMin": true,
  178 + "showMax": true,
  179 + "showAvg": false,
  180 + "showTotal": true
  181 + }
  182 + },
  183 + "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0"
  184 + },
  185 + "5eb79712-5c24-3060-7e4f-6af36b8f842d": {
  186 + "isSystemType": true,
  187 + "bundleAlias": "cards",
  188 + "typeAlias": "timeseries_table",
  189 + "type": "timeseries",
  190 + "title": "New widget",
  191 + "sizeX": 24,
  192 + "sizeY": 5,
  193 + "config": {
  194 + "datasources": [
  195 + {
  196 + "type": "entity",
  197 + "dataKeys": [
  198 + {
  199 + "name": "ruleEngineException",
  200 + "type": "timeseries",
  201 + "label": "Rule Chain",
  202 + "color": "#2196f3",
  203 + "settings": {
  204 + "useCellStyleFunction": false,
  205 + "useCellContentFunction": true,
  206 + "cellContentFunction": "return JSON.parse(value).ruleChainName;"
  207 + },
  208 + "_hash": 0.9954481282345906
  209 + },
  210 + {
  211 + "name": "ruleEngineException",
  212 + "type": "timeseries",
  213 + "label": "Rule Node",
  214 + "color": "#4caf50",
  215 + "settings": {
  216 + "useCellStyleFunction": false,
  217 + "useCellContentFunction": true,
  218 + "cellContentFunction": "return JSON.parse(value).ruleNodeName;"
  219 + },
  220 + "_hash": 0.18580357036589978
  221 + },
  222 + {
  223 + "name": "ruleEngineException",
  224 + "type": "timeseries",
  225 + "label": "Latest Error",
  226 + "color": "#f44336",
  227 + "settings": {
  228 + "useCellStyleFunction": false,
  229 + "useCellContentFunction": true,
  230 + "cellContentFunction": "return JSON.parse(value).message;"
  231 + },
  232 + "_hash": 0.7255162989552142
  233 + }
  234 + ],
  235 + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
  236 + }
  237 + ],
  238 + "timewindow": {
  239 + "realtime": {
  240 + "interval": 1000,
  241 + "timewindowMs": 86400000
  242 + },
  243 + "aggregation": {
  244 + "type": "NONE",
  245 + "limit": 200
  246 + }
  247 + },
  248 + "showTitle": true,
  249 + "backgroundColor": "rgb(255, 255, 255)",
  250 + "color": "rgba(0, 0, 0, 0.87)",
  251 + "padding": "8px",
  252 + "settings": {
  253 + "showTimestamp": true,
  254 + "displayPagination": true,
  255 + "defaultPageSize": 10
  256 + },
  257 + "title": "Exceptions",
  258 + "dropShadow": true,
  259 + "enableFullscreen": true,
  260 + "titleStyle": {
  261 + "fontSize": "16px",
  262 + "fontWeight": 400
  263 + },
  264 + "useDashboardTimewindow": false,
  265 + "showLegend": false,
  266 + "widgetStyle": {},
  267 + "actions": {},
  268 + "showTitleIcon": false,
  269 + "titleIcon": null,
  270 + "iconColor": "rgba(0, 0, 0, 0.87)",
  271 + "iconSize": "24px",
  272 + "titleTooltip": "",
  273 + "displayTimewindow": true
  274 + },
  275 + "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d"
  276 + },
  277 + "ad3f1417-87a8-750e-fc67-49a2de1466d4": {
  278 + "isSystemType": true,
  279 + "bundleAlias": "charts",
  280 + "typeAlias": "basic_timeseries",
  281 + "type": "timeseries",
  282 + "title": "New widget",
  283 + "sizeX": 12,
  284 + "sizeY": 7,
  285 + "config": {
  286 + "datasources": [
  287 + {
  288 + "type": "entity",
  289 + "dataKeys": [
  290 + {
  291 + "name": "timeoutMsgs",
  292 + "type": "timeseries",
  293 + "label": "${entityName} Permanent Timeouts",
  294 + "color": "#4caf50",
  295 + "settings": {
  296 + "excludeFromStacking": false,
  297 + "hideDataByDefault": false,
  298 + "disableDataHiding": false,
  299 + "removeFromLegend": false,
  300 + "showLines": true,
  301 + "fillLines": false,
  302 + "showPoints": false,
  303 + "showPointShape": "circle",
  304 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  305 + "showPointsLineWidth": 5,
  306 + "showPointsRadius": 3,
  307 + "showSeparateAxis": false,
  308 + "axisPosition": "left",
  309 + "thresholds": [
  310 + {
  311 + "thresholdValueSource": "predefinedValue"
  312 + }
  313 + ],
  314 + "comparisonSettings": {
  315 + "showValuesForComparison": true
  316 + }
  317 + },
  318 + "_hash": 0.565222981550328
  319 + },
  320 + {
  321 + "name": "tmpTimeout",
  322 + "type": "timeseries",
  323 + "label": "${entityName} Processing Timeouts",
  324 + "color": "#9c27b0",
  325 + "settings": {
  326 + "excludeFromStacking": false,
  327 + "hideDataByDefault": false,
  328 + "disableDataHiding": false,
  329 + "removeFromLegend": false,
  330 + "showLines": true,
  331 + "fillLines": false,
  332 + "showPoints": false,
  333 + "showPointShape": "circle",
  334 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  335 + "showPointsLineWidth": 5,
  336 + "showPointsRadius": 3,
  337 + "showSeparateAxis": false,
  338 + "axisPosition": "left",
  339 + "thresholds": [
  340 + {
  341 + "thresholdValueSource": "predefinedValue"
  342 + }
  343 + ],
  344 + "comparisonSettings": {
  345 + "showValuesForComparison": true
  346 + }
  347 + },
  348 + "_hash": 0.2679547062508352
  349 + }
  350 + ],
  351 + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
  352 + }
  353 + ],
  354 + "timewindow": {
  355 + "realtime": {
  356 + "interval": 1000,
  357 + "timewindowMs": 300000
  358 + },
  359 + "aggregation": {
  360 + "type": "NONE",
  361 + "limit": 8640
  362 + },
  363 + "hideInterval": false,
  364 + "hideAggregation": false,
  365 + "hideAggInterval": false
  366 + },
  367 + "showTitle": true,
  368 + "backgroundColor": "#fff",
  369 + "color": "rgba(0, 0, 0, 0.87)",
  370 + "padding": "8px",
  371 + "settings": {
  372 + "shadowSize": 4,
  373 + "fontColor": "#545454",
  374 + "fontSize": 10,
  375 + "xaxis": {
  376 + "showLabels": true,
  377 + "color": "#545454"
  378 + },
  379 + "yaxis": {
  380 + "showLabels": true,
  381 + "color": "#545454"
  382 + },
  383 + "grid": {
  384 + "color": "#545454",
  385 + "tickColor": "#DDDDDD",
  386 + "verticalLines": true,
  387 + "horizontalLines": true,
  388 + "outlineWidth": 1
  389 + },
  390 + "stack": false,
  391 + "tooltipIndividual": false,
  392 + "timeForComparison": "months",
  393 + "xaxisSecond": {
  394 + "axisPosition": "top",
  395 + "showLabels": true
  396 + }
  397 + },
  398 + "title": "Processing Failures and Timeouts",
  399 + "dropShadow": true,
  400 + "enableFullscreen": true,
  401 + "titleStyle": {
  402 + "fontSize": "16px",
  403 + "fontWeight": 400
  404 + },
  405 + "mobileHeight": null,
  406 + "showTitleIcon": false,
  407 + "titleIcon": null,
  408 + "iconColor": "rgba(0, 0, 0, 0.87)",
  409 + "iconSize": "24px",
  410 + "titleTooltip": "",
  411 + "widgetStyle": {},
  412 + "useDashboardTimewindow": false,
  413 + "displayTimewindow": true,
  414 + "showLegend": true,
  415 + "actions": {},
  416 + "legendConfig": {
  417 + "direction": "column",
  418 + "position": "bottom",
  419 + "showMin": true,
  420 + "showMax": true,
  421 + "showAvg": false,
  422 + "showTotal": true
  423 + }
  424 + },
  425 + "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4"
  426 + }
  427 + },
  428 + "states": {
  429 + "default": {
  430 + "name": "Rule Engine Statistics",
  431 + "root": true,
  432 + "layouts": {
  433 + "main": {
  434 + "widgets": {
  435 + "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {
  436 + "sizeX": 12,
  437 + "sizeY": 7,
  438 + "mobileHeight": null,
  439 + "row": 0,
  440 + "col": 0
  441 + },
  442 + "5eb79712-5c24-3060-7e4f-6af36b8f842d": {
  443 + "sizeX": 24,
  444 + "sizeY": 5,
  445 + "row": 7,
  446 + "col": 0
  447 + },
  448 + "ad3f1417-87a8-750e-fc67-49a2de1466d4": {
  449 + "sizeX": 12,
  450 + "sizeY": 7,
  451 + "mobileHeight": null,
  452 + "row": 0,
  453 + "col": 12
  454 + }
  455 + },
  456 + "gridSettings": {
  457 + "backgroundColor": "#eeeeee",
  458 + "color": "rgba(0,0,0,0.870588)",
  459 + "columns": 24,
  460 + "margins": [
  461 + 10,
  462 + 10
  463 + ],
  464 + "backgroundSizeMode": "100%",
  465 + "autoFillHeight": true,
  466 + "mobileAutoFillHeight": false,
  467 + "mobileRowHeight": 70
  468 + }
  469 + }
  470 + }
  471 + }
  472 + },
  473 + "entityAliases": {
  474 + "140f23dd-e3a0-ed98-6189-03c49d2d8018": {
  475 + "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
  476 + "alias": "TbServiceQueues",
  477 + "filter": {
  478 + "type": "assetType",
  479 + "resolveMultiple": true,
  480 + "assetType": "TbServiceQueue",
  481 + "assetNameFilter": ""
  482 + }
  483 + }
  484 + },
  485 + "timewindow": {
  486 + "displayValue": "",
  487 + "selectedTab": 0,
  488 + "hideInterval": false,
  489 + "hideAggregation": false,
  490 + "hideAggInterval": false,
  491 + "realtime": {
  492 + "interval": 1000,
  493 + "timewindowMs": 60000
  494 + },
  495 + "history": {
  496 + "historyType": 0,
  497 + "interval": 1000,
  498 + "timewindowMs": 60000,
  499 + "fixedTimewindow": {
  500 + "startTimeMs": 1586176634823,
  501 + "endTimeMs": 1586263034823
  502 + }
  503 + },
  504 + "aggregation": {
  505 + "type": "AVG",
  506 + "limit": 25000
  507 + }
  508 + },
  509 + "settings": {
  510 + "stateControllerId": "entity",
  511 + "showTitle": false,
  512 + "showDashboardsSelect": true,
  513 + "showEntitiesSelect": true,
  514 + "showDashboardTimewindow": true,
  515 + "showDashboardExport": true,
  516 + "toolbarAlwaysOpen": true
  517 + }
  518 + },
  519 + "name": "Rule Engine Statistics"
  520 +}
\ No newline at end of file
... ...
  1 +{
  2 + "title": "Thermostats",
  3 + "configuration": {
  4 + "widgets": {
  5 + "f33c746c-0dfc-c212-395b-b448c8a17209": {
  6 + "isSystemType": true,
  7 + "bundleAlias": "cards",
  8 + "typeAlias": "entities_table",
  9 + "type": "latest",
  10 + "title": "New widget",
  11 + "sizeX": 11,
  12 + "sizeY": 11,
  13 + "config": {
  14 + "timewindow": {
  15 + "realtime": {
  16 + "interval": 1000,
  17 + "timewindowMs": 86400000
  18 + },
  19 + "aggregation": {
  20 + "type": "NONE",
  21 + "limit": 200
  22 + }
  23 + },
  24 + "showTitle": true,
  25 + "backgroundColor": "rgb(255, 255, 255)",
  26 + "color": "rgba(0, 0, 0, 0.87)",
  27 + "padding": "4px",
  28 + "settings": {
  29 + "enableSearch": true,
  30 + "displayPagination": true,
  31 + "defaultPageSize": 10,
  32 + "defaultSortOrder": "entityName",
  33 + "displayEntityName": true,
  34 + "displayEntityType": false,
  35 + "enableSelectColumnDisplay": false,
  36 + "entitiesTitle": "Thermostats",
  37 + "displayEntityLabel": false,
  38 + "entityNameColumnTitle": "Thermostat name"
  39 + },
  40 + "title": "Thermostats",
  41 + "dropShadow": true,
  42 + "enableFullscreen": false,
  43 + "titleStyle": {
  44 + "fontSize": "16px",
  45 + "fontWeight": 400,
  46 + "padding": "5px 10px 5px 10px"
  47 + },
  48 + "useDashboardTimewindow": false,
  49 + "showLegend": false,
  50 + "datasources": [
  51 + {
  52 + "type": "entity",
  53 + "dataKeys": [
  54 + {
  55 + "name": "active",
  56 + "type": "attribute",
  57 + "label": "Active",
  58 + "color": "#2196f3",
  59 + "settings": {
  60 + "columnWidth": "0px",
  61 + "useCellStyleFunction": true,
  62 + "useCellContentFunction": true,
  63 + "cellContentFunction": "value = '&#11044;';\nreturn value;",
  64 + "cellStyleFunction": "var color;\nif (value === \"true\") {\n color = 'rgb(39, 134, 34)';\n} else {\n color = 'rgb(255, 0, 0)';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};"
  65 + },
  66 + "_hash": 0.9264526512320641
  67 + },
  68 + {
  69 + "name": "temperature",
  70 + "type": "timeseries",
  71 + "label": "Temperature",
  72 + "color": "#4caf50",
  73 + "settings": {
  74 + "columnWidth": "0px",
  75 + "useCellStyleFunction": false,
  76 + "useCellContentFunction": false
  77 + },
  78 + "_hash": 0.9801965063904188,
  79 + "units": "°C",
  80 + "decimals": 1
  81 + },
  82 + {
  83 + "name": "humidity",
  84 + "type": "timeseries",
  85 + "label": "Humidity",
  86 + "color": "#f44336",
  87 + "settings": {
  88 + "columnWidth": "0px",
  89 + "useCellStyleFunction": false,
  90 + "useCellContentFunction": false
  91 + },
  92 + "_hash": 0.5726727868178358,
  93 + "units": "%",
  94 + "decimals": 0
  95 + }
  96 + ],
  97 + "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e"
  98 + }
  99 + ],
  100 + "showTitleIcon": false,
  101 + "titleIcon": null,
  102 + "iconColor": "rgba(0, 0, 0, 0.87)",
  103 + "iconSize": "24px",
  104 + "titleTooltip": "",
  105 + "widgetStyle": {},
  106 + "displayTimewindow": true,
  107 + "actions": {
  108 + "headerButton": [
  109 + {
  110 + "id": "85b803db-90f2-5c63-1388-a378e0eb10d6",
  111 + "name": "Edit location",
  112 + "icon": "map",
  113 + "type": "openDashboardState",
  114 + "targetDashboardStateId": "map",
  115 + "setEntityId": false
  116 + },
  117 + {
  118 + "id": "8ab5a518-67d2-b6a2-956d-81fd512294b2",
  119 + "name": "Add",
  120 + "icon": "add",
  121 + "type": "customPretty",
  122 + "customHtml": "<md-dialog aria-label=\"Add entity\">\n <form name=\"addEntityForm\" class=\"add-entity-form\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Add thermostat</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <md-input-container flex class=\"md-block\">\n <label>Thermostat name</label>\n <input ng-model=\"vm.entityName\" name=entityName required>\n <div ng-messages=\"addEntityForm.entityName.$error\">\n <div ng-message=\"required\">Thermostat name is required.</div>\n </div>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmTemperature\">\n High temperature alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>High temperature threshold, °C</label>\n <input name=\"thresholdTemperature\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdTemperature\"\n ng-disabled=\"!vm.attributes.alarmTemperature\"\n ng-required=\"vm.attributes.alarmTemperature\">\n <div ng-messages=\"addEntityForm.thresholdTemperature.$error\">\n <div ng-message=\"required\">High temperature threshold is required.</div>\n </div>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmHumidity\">\n Low humidity alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>Low humidity threshold, %</label>\n <input name=\"thresholdHumidity\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdHumidity\"\n ng-disabled=\"!vm.attributes.alarmHumidity\"\n ng-required=\"vm.attributes.alarmHumidity\">\n <div ng-messages=\"addEntityForm.thresholdHumidity.$error\">\n <div ng-message=\"required\">Low humidity threshold is required.</div>\n </div>\n </md-input-container>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"addEntityForm.$invalid || !addEntityForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",
  123 + "customCss": ".add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n",
  124 + "customFunction": "var $injector = widgetContext.$scope.$injector;\nvar $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', AddEntityDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n locals: {\n entityId: entityId\n },\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction AddEntityDialogController($scope, $mdDialog) {\n var vm = this;\n vm.attributes = {};\n\n vm.save = function() {\n $scope.addEntityForm.$setPristine();\n saveEntityPromise().then(\n function (entity) {\n saveAttributes(entity.id);\n updateAliasData();\n $mdDialog.hide();\n }\n );\n };\n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n \n function saveEntityPromise() {\n var entity = {\n name: vm.entityName,\n type: \"thermostat\"\n };\n return deviceService.saveDevice(entity);\n }\n \n function saveAttributes(entityId) {\n var attributesArray = [];\n for (var key in vm.attributes) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n if (attributesArray.length > 0) {\n attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } \n }\n \n function updateAliasData() {\n var aliasIds = [];\n for (var id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n var tasks = [];\n aliasIds.forEach(function(aliasId) {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(function() {\n $rootScope.$broadcast('widgetForceReInit');\n });\n }\n}\n"
  125 + }
  126 + ],
  127 + "actionCellButton": [
  128 + {
  129 + "id": "ca241cd8-788d-5508-a9ce-74b03ef42a7f",
  130 + "name": "Chart",
  131 + "icon": "show_chart",
  132 + "type": "openDashboardState",
  133 + "targetDashboardStateId": "chart",
  134 + "setEntityId": true
  135 + },
  136 + {
  137 + "id": "7506576f-87ba-d3a0-88fb-e304d451776d",
  138 + "name": "Edit",
  139 + "icon": "edit",
  140 + "type": "customPretty",
  141 + "customHtml": "<md-dialog aria-label=\"Edit entity\">\n <form name=\"editEntityForm\" class=\"edit-entity-form\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Edit thermostat {{vm.entityName}}</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <md-input-container flex class=\"md-block\">\n <label>Thermostat name</label>\n <input ng-model=\"vm.entityName\" readonly>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmTemperature\">\n High temperature alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>High temperature threshold, °C</label>\n <input name=\"thresholdTemperature\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdTemperature\"\n ng-disabled=\"!vm.attributes.alarmTemperature\"\n ng-required=\"vm.attributes.alarmTemperature\">\n <div ng-messages=\"editEntityForm.thresholdTemperature.$error\">\n <div ng-message=\"required\">High temperature threshold is required.</div>\n </div>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmHumidity\">\n Low humidity alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>Low humidity threshold, %</label>\n <input name=\"thresholdHumidity\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdHumidity\"\n ng-disabled=\"!vm.attributes.alarmHumidity\"\n ng-required=\"vm.attributes.alarmHumidity\">\n <div ng-messages=\"editEntityForm.thresholdHumidity.$error\">\n <div ng-message=\"required\">Low humidity threshold is required.</div>\n </div>\n </md-input-container>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"editEntityForm.$invalid || !editEntityForm.$dirty\" class=\"md-raised md-primary\">Save</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",
  142 + "customCss": ".edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n",
  143 + "customFunction": "var $injector = widgetContext.$scope.$injector;\nvar $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n attributeService = $injector.get('attributeService');\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', EditEntityDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n locals: {\n entityId: entityId\n },\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction EditEntityDialogController($scope,$mdDialog) {\n var vm = this;\n vm.entityId = entityId;\n vm.entityName = entityName;\n vm.entityType = entityId.entityType;\n vm.attributes = {};\n vm.serverAttributes = {};\n getEntityInfo();\n \n vm.save = function() {\n saveAttributes();\n $mdDialog.hide();\n };\n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n function getEntityAttributes(attributes) {\n for (var i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n vm.serverAttributes = angular.copy(vm.attributes);\n }\n \n function getEntityInfo() {\n attributeService.getEntityAttributesValues(entityId.entityType, entityId.id, 'SERVER_SCOPE').then(\n function(data){\n if (data.length) {\n getEntityAttributes(data);\n }\n });\n }\n \n function saveAttributes() {\n var attributesArray = [];\n for (var key in vm.attributes) {\n if (vm.attributes[key] !== vm.serverAttributes[key]) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } \n }\n}"
  144 + },
  145 + {
  146 + "id": "3488848b-e47d-6af6-659f-5d78369ece5e",
  147 + "name": "Delete",
  148 + "icon": "delete",
  149 + "type": "custom",
  150 + "customFunction": "var $injector = widgetContext.$scope.$injector;\nvar $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n deviceService = $injector.get('deviceService')\n $rootScope = $injector.get('$rootScope'),\n $q = $injector.get('$q');\n\nopenDeleteEntityDialog();\n\nfunction openDeleteEntityDialog() {\n var title = 'Delete thermostat \"' + entityName + '\"';\n var content = 'Are you sure you want to delete the thermostat \"' +\n entityName + '\"?';\n var confirm = $mdDialog.confirm()\n .targetEvent($event)\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .cancel('Cancel')\n .ok('Delete');\n $mdDialog.show(confirm).then(function() {\n deleteEntity();\n })\n}\n\nfunction deleteEntity() {\n deviceService.deleteDevice(entityId.id).then(\n function success() {\n updateAliasData();\n },\n function fail() {\n showErrorDialog();\n }\n );\n}\n\nfunction updateAliasData() {\n var aliasIds = [];\n for (var id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n var tasks = [];\n aliasIds.forEach(function(aliasId) {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(function() {\n $rootScope.$broadcast('entityAliasesChanged', aliasIds);\n });\n}\n\nfunction showErrorDialog() {\n var title = 'Error';\n var content = 'An error occurred while deleting the thermostat. Please try again.';\n var alert = $mdDialog.alert()\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .parent(angular.element($document[0].body))\n .targetEvent($event)\n .multiple(true)\n .clickOutsideToClose(true)\n .ok('CLOSE');\n $mdDialog.show(alert);\n}"
  151 + }
  152 + ],
  153 + "rowClick": [
  154 + {
  155 + "id": "e3928f23-c135-0766-71d5-65ed61e0ce8d",
  156 + "name": "show alarm",
  157 + "icon": "more_horiz",
  158 + "type": "updateDashboardState",
  159 + "targetDashboardStateId": "default",
  160 + "setEntityId": true,
  161 + "stateEntityParamName": "alarm"
  162 + }
  163 + ]
  164 + }
  165 + },
  166 + "id": "f33c746c-0dfc-c212-395b-b448c8a17209"
  167 + },
  168 + "7943196b-eedb-d422-f9c3-b32d379ad172": {
  169 + "isSystemType": true,
  170 + "bundleAlias": "alarm_widgets",
  171 + "typeAlias": "alarms_table",
  172 + "type": "alarm",
  173 + "title": "New widget",
  174 + "sizeX": 13,
  175 + "sizeY": 5,
  176 + "config": {
  177 + "timewindow": {
  178 + "realtime": {
  179 + "interval": 1000,
  180 + "timewindowMs": 86400000
  181 + },
  182 + "aggregation": {
  183 + "type": "NONE",
  184 + "limit": 200
  185 + }
  186 + },
  187 + "showTitle": true,
  188 + "backgroundColor": "rgb(255, 255, 255)",
  189 + "color": "rgba(0, 0, 0, 0.87)",
  190 + "padding": "4px",
  191 + "settings": {
  192 + "enableSelection": true,
  193 + "enableSearch": true,
  194 + "displayDetails": true,
  195 + "allowAcknowledgment": true,
  196 + "allowClear": true,
  197 + "displayPagination": true,
  198 + "defaultPageSize": 10,
  199 + "defaultSortOrder": "-createdTime",
  200 + "enableSelectColumnDisplay": false,
  201 + "enableStatusFilter": true,
  202 + "alarmsTitle": "Alarms"
  203 + },
  204 + "title": "New Alarms table",
  205 + "dropShadow": true,
  206 + "enableFullscreen": false,
  207 + "titleStyle": {
  208 + "fontSize": "16px",
  209 + "fontWeight": 400,
  210 + "padding": "5px 10px 5px 10px"
  211 + },
  212 + "useDashboardTimewindow": false,
  213 + "showLegend": false,
  214 + "alarmSource": {
  215 + "type": "entity",
  216 + "dataKeys": [
  217 + {
  218 + "name": "createdTime",
  219 + "type": "alarm",
  220 + "label": "Created time",
  221 + "color": "#2196f3",
  222 + "settings": {},
  223 + "_hash": 0.7308410188824108
  224 + },
  225 + {
  226 + "name": "originator",
  227 + "type": "alarm",
  228 + "label": "Originator",
  229 + "color": "#4caf50",
  230 + "settings": {},
  231 + "_hash": 0.056085530105439485
  232 + },
  233 + {
  234 + "name": "type",
  235 + "type": "alarm",
  236 + "label": "Type",
  237 + "color": "#f44336",
  238 + "settings": {},
  239 + "_hash": 0.10212012352561795
  240 + },
  241 + {
  242 + "name": "severity",
  243 + "type": "alarm",
  244 + "label": "Severity",
  245 + "color": "#ffc107",
  246 + "settings": {},
  247 + "_hash": 0.1777349980531262
  248 + },
  249 + {
  250 + "name": "status",
  251 + "type": "alarm",
  252 + "label": "Status",
  253 + "color": "#607d8b",
  254 + "settings": {},
  255 + "_hash": 0.7977920750136249
  256 + }
  257 + ],
  258 + "entityAliasId": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
  259 + "name": "alarms"
  260 + },
  261 + "alarmSearchStatus": "ANY",
  262 + "alarmsPollingInterval": 5,
  263 + "showTitleIcon": false,
  264 + "titleIcon": null,
  265 + "iconColor": "rgba(0, 0, 0, 0.87)",
  266 + "iconSize": "24px",
  267 + "titleTooltip": "",
  268 + "widgetStyle": {},
  269 + "displayTimewindow": true,
  270 + "actions": {},
  271 + "datasources": [],
  272 + "alarmsMaxCountLoad": 0,
  273 + "alarmsFetchSize": 100
  274 + },
  275 + "id": "7943196b-eedb-d422-f9c3-b32d379ad172"
  276 + },
  277 + "14a19183-f0b2-d6be-0f62-9863f0a51111": {
  278 + "isSystemType": true,
  279 + "bundleAlias": "charts",
  280 + "typeAlias": "basic_timeseries",
  281 + "type": "timeseries",
  282 + "title": "New widget",
  283 + "sizeX": 18,
  284 + "sizeY": 6,
  285 + "config": {
  286 + "datasources": [
  287 + {
  288 + "type": "entity",
  289 + "dataKeys": [
  290 + {
  291 + "name": "temperature",
  292 + "type": "timeseries",
  293 + "label": "Temperature",
  294 + "color": "#ef5350",
  295 + "settings": {
  296 + "excludeFromStacking": false,
  297 + "hideDataByDefault": false,
  298 + "disableDataHiding": false,
  299 + "removeFromLegend": false,
  300 + "showLines": true,
  301 + "fillLines": true,
  302 + "showPoints": false,
  303 + "showPointShape": "circle",
  304 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  305 + "showPointsLineWidth": 5,
  306 + "showPointsRadius": 3,
  307 + "showSeparateAxis": false,
  308 + "axisPosition": "left",
  309 + "thresholds": [
  310 + {
  311 + "thresholdValueSource": "predefinedValue"
  312 + }
  313 + ],
  314 + "comparisonSettings": {
  315 + "showValuesForComparison": true
  316 + }
  317 + },
  318 + "_hash": 0.7852346160709658,
  319 + "units": "°C",
  320 + "decimals": 1
  321 + }
  322 + ],
  323 + "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
  324 + }
  325 + ],
  326 + "timewindow": {
  327 + "realtime": {
  328 + "interval": 30000,
  329 + "timewindowMs": 3600000
  330 + },
  331 + "aggregation": {
  332 + "type": "AVG",
  333 + "limit": 25000
  334 + }
  335 + },
  336 + "showTitle": true,
  337 + "backgroundColor": "#fff",
  338 + "color": "rgba(0, 0, 0, 0.87)",
  339 + "padding": "8px",
  340 + "settings": {
  341 + "shadowSize": 4,
  342 + "fontColor": "#545454",
  343 + "fontSize": 10,
  344 + "xaxis": {
  345 + "showLabels": true,
  346 + "color": "#545454"
  347 + },
  348 + "yaxis": {
  349 + "showLabels": true,
  350 + "color": "#545454"
  351 + },
  352 + "grid": {
  353 + "color": "#545454",
  354 + "tickColor": "#DDDDDD",
  355 + "verticalLines": true,
  356 + "horizontalLines": true,
  357 + "outlineWidth": 1
  358 + },
  359 + "stack": false,
  360 + "tooltipIndividual": false,
  361 + "timeForComparison": "months",
  362 + "xaxisSecond": {
  363 + "axisPosition": "top",
  364 + "showLabels": true
  365 + },
  366 + "smoothLines": true
  367 + },
  368 + "title": "Temperature",
  369 + "dropShadow": true,
  370 + "enableFullscreen": true,
  371 + "titleStyle": {
  372 + "fontSize": "16px",
  373 + "fontWeight": 400
  374 + },
  375 + "mobileHeight": null,
  376 + "showTitleIcon": false,
  377 + "titleIcon": null,
  378 + "iconColor": "rgba(0, 0, 0, 0.87)",
  379 + "iconSize": "24px",
  380 + "titleTooltip": "",
  381 + "widgetStyle": {},
  382 + "useDashboardTimewindow": false,
  383 + "displayTimewindow": true,
  384 + "showLegend": true,
  385 + "legendConfig": {
  386 + "direction": "column",
  387 + "position": "bottom",
  388 + "showMin": true,
  389 + "showMax": true,
  390 + "showAvg": true,
  391 + "showTotal": false
  392 + },
  393 + "actions": {}
  394 + },
  395 + "id": "14a19183-f0b2-d6be-0f62-9863f0a51111"
  396 + },
  397 + "07f49fd5-a73b-d74c-c220-362c20af81f4": {
  398 + "isSystemType": true,
  399 + "bundleAlias": "charts",
  400 + "typeAlias": "basic_timeseries",
  401 + "type": "timeseries",
  402 + "title": "New widget",
  403 + "sizeX": 18,
  404 + "sizeY": 6,
  405 + "config": {
  406 + "datasources": [
  407 + {
  408 + "type": "entity",
  409 + "dataKeys": [
  410 + {
  411 + "name": "humidity",
  412 + "type": "timeseries",
  413 + "label": "Humidity",
  414 + "color": "#2196f3",
  415 + "settings": {
  416 + "excludeFromStacking": false,
  417 + "hideDataByDefault": false,
  418 + "disableDataHiding": false,
  419 + "removeFromLegend": false,
  420 + "showLines": true,
  421 + "fillLines": true,
  422 + "showPoints": false,
  423 + "showPointShape": "circle",
  424 + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
  425 + "showPointsLineWidth": 5,
  426 + "showPointsRadius": 3,
  427 + "showSeparateAxis": false,
  428 + "axisPosition": "left",
  429 + "thresholds": [
  430 + {
  431 + "thresholdValueSource": "predefinedValue"
  432 + }
  433 + ],
  434 + "comparisonSettings": {
  435 + "showValuesForComparison": true
  436 + }
  437 + },
  438 + "_hash": 0.28640715926957183,
  439 + "units": "%",
  440 + "decimals": 0
  441 + }
  442 + ],
  443 + "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
  444 + }
  445 + ],
  446 + "timewindow": {
  447 + "realtime": {
  448 + "interval": 30000,
  449 + "timewindowMs": 3600000
  450 + },
  451 + "aggregation": {
  452 + "type": "AVG",
  453 + "limit": 25000
  454 + }
  455 + },
  456 + "showTitle": true,
  457 + "backgroundColor": "#fff",
  458 + "color": "rgba(0, 0, 0, 0.87)",
  459 + "padding": "8px",
  460 + "settings": {
  461 + "shadowSize": 4,
  462 + "fontColor": "#545454",
  463 + "fontSize": 10,
  464 + "xaxis": {
  465 + "showLabels": true,
  466 + "color": "#545454"
  467 + },
  468 + "yaxis": {
  469 + "showLabels": true,
  470 + "color": "#545454"
  471 + },
  472 + "grid": {
  473 + "color": "#545454",
  474 + "tickColor": "#DDDDDD",
  475 + "verticalLines": true,
  476 + "horizontalLines": true,
  477 + "outlineWidth": 1
  478 + },
  479 + "stack": false,
  480 + "tooltipIndividual": false,
  481 + "timeForComparison": "months",
  482 + "xaxisSecond": {
  483 + "axisPosition": "top",
  484 + "showLabels": true
  485 + },
  486 + "smoothLines": true
  487 + },
  488 + "title": "Humidity",
  489 + "dropShadow": true,
  490 + "enableFullscreen": true,
  491 + "titleStyle": {
  492 + "fontSize": "16px",
  493 + "fontWeight": 400
  494 + },
  495 + "mobileHeight": null,
  496 + "showTitleIcon": false,
  497 + "titleIcon": null,
  498 + "iconColor": "rgba(0, 0, 0, 0.87)",
  499 + "iconSize": "24px",
  500 + "titleTooltip": "",
  501 + "widgetStyle": {},
  502 + "useDashboardTimewindow": false,
  503 + "displayTimewindow": true,
  504 + "showLegend": true,
  505 + "legendConfig": {
  506 + "direction": "column",
  507 + "position": "bottom",
  508 + "showMin": true,
  509 + "showMax": true,
  510 + "showAvg": true,
  511 + "showTotal": false
  512 + },
  513 + "actions": {}
  514 + },
  515 + "id": "07f49fd5-a73b-d74c-c220-362c20af81f4"
  516 + },
  517 + "c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
  518 + "isSystemType": true,
  519 + "bundleAlias": "input_widgets",
  520 + "typeAlias": "update_multiple_attributes",
  521 + "type": "latest",
  522 + "title": "New widget",
  523 + "sizeX": 6,
  524 + "sizeY": 6,
  525 + "config": {
  526 + "datasources": [
  527 + {
  528 + "type": "entity",
  529 + "dataKeys": [
  530 + {
  531 + "name": "alarmTemperature",
  532 + "type": "attribute",
  533 + "label": "High temperature alarm",
  534 + "color": "#4caf50",
  535 + "settings": {
  536 + "dataKeyType": "server",
  537 + "dataKeyValueType": "booleanCheckbox",
  538 + "required": false,
  539 + "isEditable": "editable",
  540 + "dataKeyHidden": false,
  541 + "step": 1
  542 + },
  543 + "_hash": 0.8725278440159361
  544 + },
  545 + {
  546 + "name": "thresholdTemperature",
  547 + "type": "attribute",
  548 + "label": "High temperature threshold, °C",
  549 + "color": "#f44336",
  550 + "settings": {
  551 + "dataKeyType": "server",
  552 + "dataKeyValueType": "double",
  553 + "required": false,
  554 + "isEditable": "editable",
  555 + "dataKeyHidden": false,
  556 + "step": 1,
  557 + "disabledOnDataKey": "alarmTemperature"
  558 + },
  559 + "_hash": 0.7316078472857874
  560 + },
  561 + {
  562 + "name": "alarmHumidity",
  563 + "type": "attribute",
  564 + "label": "Low humidity alarm",
  565 + "color": "#ffc107",
  566 + "settings": {
  567 + "dataKeyType": "server",
  568 + "dataKeyValueType": "booleanCheckbox",
  569 + "required": false,
  570 + "isEditable": "editable",
  571 + "dataKeyHidden": false,
  572 + "step": 1
  573 + },
  574 + "_hash": 0.5339673667431057
  575 + },
  576 + {
  577 + "name": "thresholdHumidity",
  578 + "type": "attribute",
  579 + "label": "Low humidity threshold, %",
  580 + "color": "#607d8b",
  581 + "settings": {
  582 + "dataKeyType": "server",
  583 + "dataKeyValueType": "double",
  584 + "required": false,
  585 + "isEditable": "editable",
  586 + "dataKeyHidden": false,
  587 + "step": 1,
  588 + "disabledOnDataKey": "alarmHumidity"
  589 + },
  590 + "_hash": 0.2687091190358901
  591 + }
  592 + ],
  593 + "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
  594 + }
  595 + ],
  596 + "timewindow": {
  597 + "realtime": {
  598 + "timewindowMs": 60000
  599 + }
  600 + },
  601 + "showTitle": true,
  602 + "backgroundColor": "#fff",
  603 + "color": "rgba(0, 0, 0, 0.87)",
  604 + "padding": "8px",
  605 + "settings": {
  606 + "showActionButtons": false,
  607 + "showResultMessage": true,
  608 + "fieldsAlignment": "column",
  609 + "fieldsInRow": 2,
  610 + "groupTitle": "${entityName}",
  611 + "widgetTitle": "Termostat settings"
  612 + },
  613 + "title": "New Update Multiple Attributes",
  614 + "dropShadow": true,
  615 + "enableFullscreen": false,
  616 + "enableDataExport": false,
  617 + "widgetStyle": {},
  618 + "titleStyle": {
  619 + "fontSize": "16px",
  620 + "fontWeight": 400
  621 + },
  622 + "useDashboardTimewindow": true,
  623 + "showLegend": false,
  624 + "actions": {},
  625 + "showTitleIcon": false,
  626 + "titleIcon": null,
  627 + "iconColor": "rgba(0, 0, 0, 0.87)",
  628 + "iconSize": "24px",
  629 + "titleTooltip": "",
  630 + "displayTimewindow": true
  631 + },
  632 + "id": "c4631f94-2db3-523b-4d09-2a1a0a75d93f"
  633 + },
  634 + "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb": {
  635 + "isSystemType": true,
  636 + "bundleAlias": "maps_v2",
  637 + "typeAlias": "openstreetmap",
  638 + "type": "latest",
  639 + "title": "New widget",
  640 + "sizeX": 13,
  641 + "sizeY": 6,
  642 + "config": {
  643 + "datasources": [
  644 + {
  645 + "type": "entity",
  646 + "dataKeys": [
  647 + {
  648 + "name": "temperature",
  649 + "type": "timeseries",
  650 + "label": "temperature",
  651 + "color": "#2196f3",
  652 + "settings": {},
  653 + "_hash": 0.1371919646686739,
  654 + "decimals": 1,
  655 + "postFuncBody": "return value || \"\";"
  656 + },
  657 + {
  658 + "name": "humidity",
  659 + "type": "timeseries",
  660 + "label": "humidity",
  661 + "color": "#4caf50",
  662 + "settings": {},
  663 + "_hash": 0.043177186765847475,
  664 + "decimals": 0,
  665 + "postFuncBody": "return value || \"\";"
  666 + },
  667 + {
  668 + "name": "longitude",
  669 + "type": "attribute",
  670 + "label": "longitude",
  671 + "color": "#f44336",
  672 + "settings": {},
  673 + "_hash": 0.5548964320315584
  674 + },
  675 + {
  676 + "name": "latitude",
  677 + "type": "attribute",
  678 + "label": "latitude",
  679 + "color": "#ffc107",
  680 + "settings": {},
  681 + "_hash": 0.1803778014971602
  682 + },
  683 + {
  684 + "name": "active",
  685 + "type": "attribute",
  686 + "label": "active",
  687 + "color": "#607d8b",
  688 + "settings": {},
  689 + "_hash": 0.30926987994082844
  690 + }
  691 + ],
  692 + "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e"
  693 + }
  694 + ],
  695 + "timewindow": {
  696 + "realtime": {
  697 + "timewindowMs": 60000
  698 + }
  699 + },
  700 + "showTitle": false,
  701 + "backgroundColor": "#fff",
  702 + "color": "rgba(0, 0, 0, 0.87)",
  703 + "padding": "8px",
  704 + "settings": {
  705 + "fitMapBounds": true,
  706 + "latKeyName": "latitude",
  707 + "lngKeyName": "longitude",
  708 + "showLabel": true,
  709 + "label": "${entityName}",
  710 + "tooltipPattern": "<b>${entityName}</b><br/><br/><b>Temperature:</b> ${temperature:1} °C<br/><b>Humidity:</b> ${humidity:0} %<br/><br/><link-act name=\"navigate_to_details\">Thermostat details</link-act><br/>",
  711 + "markerImageSize": 48,
  712 + "useColorFunction": false,
  713 + "markerImages": [
  714 + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
  715 + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
  716 + ],
  717 + "useMarkerImageFunction": true,
  718 + "colorFunction": "\n",
  719 + "color": "#fe7569",
  720 + "mapProvider": "OpenStreetMap.HOT",
  721 + "showTooltip": true,
  722 + "autocloseTooltip": true,
  723 + "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
  724 + "defaultCenterPosition": [
  725 + 0,
  726 + 0
  727 + ],
  728 + "showTooltipAction": "click",
  729 + "polygonKeyName": "coordinates",
  730 + "polygonOpacity": 0.5,
  731 + "polygonStrokeOpacity": 1,
  732 + "polygonStrokeWeight": 1,
  733 + "zoomOnClick": true,
  734 + "showCoverageOnHover": true,
  735 + "animate": true,
  736 + "maxClusterRadius": 80,
  737 + "removeOutsideVisibleBounds": true,
  738 + "useLabelFunction": true,
  739 + "labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '<span style=\"border: solid ' + color + '; border-radius: 10px; color: ' + color + '; background-color: #fff; padding: 3px 5px; font-size: 14px\">' + \n '${entityLabel}' + \n '</span>'",
  740 + "defaultZoomLevel": 14,
  741 + "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;"
  742 + },
  743 + "title": "Thermostat maps",
  744 + "dropShadow": true,
  745 + "enableFullscreen": false,
  746 + "titleStyle": {
  747 + "fontSize": "16px",
  748 + "fontWeight": 400
  749 + },
  750 + "useDashboardTimewindow": true,
  751 + "showLegend": false,
  752 + "widgetStyle": {},
  753 + "actions": {
  754 + "headerButton": [],
  755 + "tooltipAction": [
  756 + {
  757 + "id": "bef25673-b37a-8821-bc0f-5d6dd3680f24",
  758 + "name": "navigate_to_details",
  759 + "icon": "more_horiz",
  760 + "type": "openDashboardState",
  761 + "targetDashboardStateId": "chart",
  762 + "setEntityId": true
  763 + }
  764 + ]
  765 + },
  766 + "showTitleIcon": false,
  767 + "titleIcon": null,
  768 + "iconColor": "rgba(0, 0, 0, 0.87)",
  769 + "iconSize": "24px",
  770 + "titleTooltip": "",
  771 + "displayTimewindow": true
  772 + },
  773 + "id": "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb"
  774 + },
  775 + "00fb2742-ba1f-7e43-673f-d6c08b72ed06": {
  776 + "isSystemType": true,
  777 + "bundleAlias": "input_widgets",
  778 + "typeAlias": "markers_placement_openstreetmap",
  779 + "type": "latest",
  780 + "title": "New widget",
  781 + "sizeX": 24,
  782 + "sizeY": 12,
  783 + "config": {
  784 + "datasources": [
  785 + {
  786 + "type": "entity",
  787 + "dataKeys": [
  788 + {
  789 + "name": "longitude",
  790 + "type": "attribute",
  791 + "label": "longitude",
  792 + "color": "#2196f3",
  793 + "settings": {},
  794 + "_hash": 0.3640193654284214
  795 + },
  796 + {
  797 + "name": "latitude",
  798 + "type": "attribute",
  799 + "label": "latitude",
  800 + "color": "#4caf50",
  801 + "settings": {},
  802 + "_hash": 0.49020393887695923
  803 + },
  804 + {
  805 + "name": "temperature",
  806 + "type": "timeseries",
  807 + "label": "temperature",
  808 + "color": "#f44336",
  809 + "settings": {},
  810 + "_hash": 0.5885892766009955,
  811 + "postFuncBody": "return value || \"\";"
  812 + },
  813 + {
  814 + "name": "humidity",
  815 + "type": "timeseries",
  816 + "label": "humidity",
  817 + "color": "#ffc107",
  818 + "settings": {},
  819 + "_hash": 0.21077893588180707,
  820 + "postFuncBody": "return value || \"\";"
  821 + },
  822 + {
  823 + "name": "active",
  824 + "type": "attribute",
  825 + "label": "active",
  826 + "color": "#607d8b",
  827 + "settings": {},
  828 + "_hash": 0.34722983638504346
  829 + }
  830 + ],
  831 + "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e"
  832 + }
  833 + ],
  834 + "timewindow": {
  835 + "realtime": {
  836 + "timewindowMs": 60000
  837 + }
  838 + },
  839 + "showTitle": false,
  840 + "backgroundColor": "#fff",
  841 + "color": "rgba(0, 0, 0, 0.87)",
  842 + "padding": "8px",
  843 + "settings": {
  844 + "fitMapBounds": true,
  845 + "latKeyName": "latitude",
  846 + "lngKeyName": "longitude",
  847 + "showLabel": true,
  848 + "label": "${entityName}",
  849 + "tooltipPattern": "<b>${entityName}</b><br/><br/><b>Temperature:</b> ${temperature:1} °C<br/><b>Humidity:</b> ${humidity:0} %<br/><br/><link-act name='delete'>Delete</link-act>",
  850 + "markerImageSize": 34,
  851 + "useColorFunction": false,
  852 + "markerImages": [
  853 + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
  854 + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
  855 + ],
  856 + "useMarkerImageFunction": true,
  857 + "color": "#fe7569",
  858 + "mapProvider": "OpenStreetMap.HOT",
  859 + "showTooltip": true,
  860 + "autocloseTooltip": true,
  861 + "defaultCenterPosition": [
  862 + 0,
  863 + 0
  864 + ],
  865 + "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
  866 + "showTooltipAction": "click",
  867 + "polygonKeyName": "coordinates",
  868 + "polygonOpacity": 0.5,
  869 + "polygonStrokeOpacity": 1,
  870 + "polygonStrokeWeight": 1,
  871 + "zoomOnClick": true,
  872 + "showCoverageOnHover": true,
  873 + "animate": true,
  874 + "maxClusterRadius": 80,
  875 + "removeOutsideVisibleBounds": true,
  876 + "defaultZoomLevel": 12,
  877 + "labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '<span style=\"border: solid ' + color + '; border-radius: 10px; color: ' + color + '; background-color: #fff; padding: 3px 5px; font-size: 14px\">' + \n '${entityLabel}' + \n '</span>'",
  878 + "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
  879 + "useLabelFunction": true
  880 + },
  881 + "title": "New Markers Placement - OpenStreetMap",
  882 + "dropShadow": true,
  883 + "enableFullscreen": false,
  884 + "titleStyle": {
  885 + "fontSize": "16px",
  886 + "fontWeight": 400
  887 + },
  888 + "useDashboardTimewindow": true,
  889 + "showLegend": false,
  890 + "widgetStyle": {},
  891 + "actions": {
  892 + "tooltipAction": [
  893 + {
  894 + "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",
  895 + "name": "delete",
  896 + "icon": "more_horiz",
  897 + "type": "custom",
  898 + "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });"
  899 + }
  900 + ]
  901 + },
  902 + "showTitleIcon": false,
  903 + "titleIcon": null,
  904 + "iconColor": "rgba(0, 0, 0, 0.87)",
  905 + "iconSize": "24px",
  906 + "titleTooltip": "",
  907 + "displayTimewindow": true
  908 + },
  909 + "id": "00fb2742-ba1f-7e43-673f-d6c08b72ed06"
  910 + },
  911 + "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": {
  912 + "isSystemType": true,
  913 + "bundleAlias": "input_widgets",
  914 + "typeAlias": "markers_placement_openstreetmap",
  915 + "type": "latest",
  916 + "title": "New widget",
  917 + "sizeX": 6,
  918 + "sizeY": 6,
  919 + "config": {
  920 + "datasources": [
  921 + {
  922 + "type": "entity",
  923 + "dataKeys": [
  924 + {
  925 + "name": "longitude",
  926 + "type": "attribute",
  927 + "label": "longitude",
  928 + "color": "#2196f3",
  929 + "settings": {},
  930 + "_hash": 0.3640193654284214
  931 + },
  932 + {
  933 + "name": "latitude",
  934 + "type": "attribute",
  935 + "label": "latitude",
  936 + "color": "#4caf50",
  937 + "settings": {},
  938 + "_hash": 0.49020393887695923
  939 + },
  940 + {
  941 + "name": "temperature",
  942 + "type": "timeseries",
  943 + "label": "temperature",
  944 + "color": "#f44336",
  945 + "settings": {},
  946 + "_hash": 0.5885892766009955,
  947 + "postFuncBody": "return value || \"\";"
  948 + },
  949 + {
  950 + "name": "humidity",
  951 + "type": "timeseries",
  952 + "label": "humidity",
  953 + "color": "#ffc107",
  954 + "settings": {},
  955 + "_hash": 0.21077893588180707,
  956 + "postFuncBody": "return value || \"\";"
  957 + },
  958 + {
  959 + "name": "active",
  960 + "type": "attribute",
  961 + "label": "active",
  962 + "color": "#607d8b",
  963 + "settings": {},
  964 + "_hash": 0.34722983638504346
  965 + }
  966 + ],
  967 + "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
  968 + }
  969 + ],
  970 + "timewindow": {
  971 + "realtime": {
  972 + "timewindowMs": 60000
  973 + }
  974 + },
  975 + "showTitle": false,
  976 + "backgroundColor": "#fff",
  977 + "color": "rgba(0, 0, 0, 0.87)",
  978 + "padding": "8px",
  979 + "settings": {
  980 + "fitMapBounds": true,
  981 + "latKeyName": "latitude",
  982 + "lngKeyName": "longitude",
  983 + "showLabel": true,
  984 + "label": "${entityName}",
  985 + "tooltipPattern": "<b>${entityName}</b><br/><br/><b>Temperature:</b> ${temperature:1} °C<br/><b>Humidity:</b> ${humidity:0} %<br/><br/><link-act name='delete'>Delete</link-act>",
  986 + "markerImageSize": 34,
  987 + "useColorFunction": false,
  988 + "markerImages": [
  989 + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
  990 + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
  991 + ],
  992 + "useMarkerImageFunction": true,
  993 + "color": "#fe7569",
  994 + "mapProvider": "OpenStreetMap.HOT",
  995 + "showTooltip": true,
  996 + "autocloseTooltip": true,
  997 + "defaultCenterPosition": [
  998 + 0,
  999 + 0
  1000 + ],
  1001 + "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
  1002 + "showTooltipAction": "click",
  1003 + "polygonKeyName": "coordinates",
  1004 + "polygonOpacity": 0.5,
  1005 + "polygonStrokeOpacity": 1,
  1006 + "polygonStrokeWeight": 1,
  1007 + "zoomOnClick": true,
  1008 + "showCoverageOnHover": true,
  1009 + "animate": true,
  1010 + "maxClusterRadius": 80,
  1011 + "removeOutsideVisibleBounds": true,
  1012 + "defaultZoomLevel": 12,
  1013 + "labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '<span style=\"border: solid ' + color + '; border-radius: 10px; color: ' + color + '; background-color: #fff; padding: 3px 5px; font-size: 14px\">' + \n '${entityLabel}' + \n '</span>'",
  1014 + "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
  1015 + "useLabelFunction": true
  1016 + },
  1017 + "title": "New Markers Placement - OpenStreetMap",
  1018 + "dropShadow": true,
  1019 + "enableFullscreen": false,
  1020 + "titleStyle": {
  1021 + "fontSize": "16px",
  1022 + "fontWeight": 400
  1023 + },
  1024 + "useDashboardTimewindow": true,
  1025 + "showLegend": false,
  1026 + "widgetStyle": {},
  1027 + "actions": {
  1028 + "tooltipAction": [
  1029 + {
  1030 + "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",
  1031 + "name": "delete",
  1032 + "icon": "more_horiz",
  1033 + "type": "custom",
  1034 + "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });"
  1035 + }
  1036 + ]
  1037 + },
  1038 + "showTitleIcon": false,
  1039 + "titleIcon": null,
  1040 + "iconColor": "rgba(0, 0, 0, 0.87)",
  1041 + "iconSize": "24px",
  1042 + "titleTooltip": "",
  1043 + "displayTimewindow": true
  1044 + },
  1045 + "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf"
  1046 + }
  1047 + },
  1048 + "states": {
  1049 + "default": {
  1050 + "name": "Thermostat",
  1051 + "root": true,
  1052 + "layouts": {
  1053 + "main": {
  1054 + "widgets": {
  1055 + "f33c746c-0dfc-c212-395b-b448c8a17209": {
  1056 + "sizeX": 11,
  1057 + "sizeY": 11,
  1058 + "row": 0,
  1059 + "col": 0
  1060 + },
  1061 + "7943196b-eedb-d422-f9c3-b32d379ad172": {
  1062 + "sizeX": 13,
  1063 + "sizeY": 5,
  1064 + "row": 0,
  1065 + "col": 11
  1066 + },
  1067 + "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb": {
  1068 + "sizeX": 13,
  1069 + "sizeY": 6,
  1070 + "row": 5,
  1071 + "col": 11
  1072 + }
  1073 + },
  1074 + "gridSettings": {
  1075 + "backgroundColor": "#eeeeee",
  1076 + "color": "rgba(0,0,0,0.870588)",
  1077 + "columns": 24,
  1078 + "margins": [
  1079 + 10,
  1080 + 10
  1081 + ],
  1082 + "backgroundSizeMode": "100%",
  1083 + "autoFillHeight": true,
  1084 + "mobileAutoFillHeight": false,
  1085 + "mobileRowHeight": 70
  1086 + }
  1087 + }
  1088 + }
  1089 + },
  1090 + "map": {
  1091 + "name": "Edit location",
  1092 + "root": false,
  1093 + "layouts": {
  1094 + "main": {
  1095 + "widgets": {
  1096 + "00fb2742-ba1f-7e43-673f-d6c08b72ed06": {
  1097 + "sizeX": 24,
  1098 + "sizeY": 12,
  1099 + "row": 0,
  1100 + "col": 0
  1101 + }
  1102 + },
  1103 + "gridSettings": {
  1104 + "backgroundColor": "#eeeeee",
  1105 + "color": "rgba(0,0,0,0.870588)",
  1106 + "columns": 24,
  1107 + "margins": [
  1108 + 10,
  1109 + 10
  1110 + ],
  1111 + "backgroundSizeMode": "100%",
  1112 + "autoFillHeight": true,
  1113 + "mobileAutoFillHeight": false,
  1114 + "mobileRowHeight": 70
  1115 + }
  1116 + }
  1117 + }
  1118 + },
  1119 + "chart": {
  1120 + "name": "${entityName}",
  1121 + "root": false,
  1122 + "layouts": {
  1123 + "main": {
  1124 + "widgets": {
  1125 + "14a19183-f0b2-d6be-0f62-9863f0a51111": {
  1126 + "sizeX": 18,
  1127 + "sizeY": 6,
  1128 + "mobileHeight": null,
  1129 + "row": 0,
  1130 + "col": 6
  1131 + },
  1132 + "07f49fd5-a73b-d74c-c220-362c20af81f4": {
  1133 + "sizeX": 18,
  1134 + "sizeY": 6,
  1135 + "mobileHeight": null,
  1136 + "row": 6,
  1137 + "col": 6
  1138 + },
  1139 + "c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
  1140 + "sizeX": 6,
  1141 + "sizeY": 6,
  1142 + "row": 0,
  1143 + "col": 0
  1144 + },
  1145 + "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": {
  1146 + "sizeX": 6,
  1147 + "sizeY": 6,
  1148 + "row": 6,
  1149 + "col": 0
  1150 + }
  1151 + },
  1152 + "gridSettings": {
  1153 + "backgroundColor": "#eeeeee",
  1154 + "color": "rgba(0,0,0,0.870588)",
  1155 + "columns": 24,
  1156 + "margins": [
  1157 + 10,
  1158 + 10
  1159 + ],
  1160 + "backgroundSizeMode": "100%",
  1161 + "autoFillHeight": true,
  1162 + "mobileAutoFillHeight": false,
  1163 + "mobileRowHeight": 70
  1164 + }
  1165 + }
  1166 + }
  1167 + }
  1168 + },
  1169 + "entityAliases": {
  1170 + "68a058e1-fdda-8482-715b-3ae4a488568e": {
  1171 + "id": "68a058e1-fdda-8482-715b-3ae4a488568e",
  1172 + "alias": "Thermostats",
  1173 + "filter": {
  1174 + "type": "deviceType",
  1175 + "resolveMultiple": true,
  1176 + "deviceType": "thermostat",
  1177 + "deviceNameFilter": ""
  1178 + }
  1179 + },
  1180 + "12ae98c7-1ea2-52cf-64d5-763e9d993547": {
  1181 + "id": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
  1182 + "alias": "Thermostat",
  1183 + "filter": {
  1184 + "type": "stateEntity",
  1185 + "resolveMultiple": false,
  1186 + "stateEntityParamName": null,
  1187 + "defaultStateEntity": null
  1188 + }
  1189 + },
  1190 + "ce27a9d0-93bf-b7a4-054d-d0369a8cf813": {
  1191 + "id": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
  1192 + "alias": "Thermostat-alarm",
  1193 + "filter": {
  1194 + "type": "stateEntity",
  1195 + "resolveMultiple": false,
  1196 + "stateEntityParamName": "alarm",
  1197 + "defaultStateEntity": null
  1198 + }
  1199 + }
  1200 + },
  1201 + "timewindow": {
  1202 + "displayValue": "",
  1203 + "selectedTab": 0,
  1204 + "hideInterval": false,
  1205 + "hideAggregation": false,
  1206 + "hideAggInterval": false,
  1207 + "realtime": {
  1208 + "interval": 1000,
  1209 + "timewindowMs": 60000
  1210 + },
  1211 + "history": {
  1212 + "historyType": 0,
  1213 + "interval": 1000,
  1214 + "timewindowMs": 60000,
  1215 + "fixedTimewindow": {
  1216 + "startTimeMs": 1587473857304,
  1217 + "endTimeMs": 1587560257304
  1218 + }
  1219 + },
  1220 + "aggregation": {
  1221 + "type": "AVG",
  1222 + "limit": 25000
  1223 + }
  1224 + },
  1225 + "settings": {
  1226 + "stateControllerId": "entity",
  1227 + "showTitle": false,
  1228 + "showDashboardsSelect": true,
  1229 + "showEntitiesSelect": true,
  1230 + "showDashboardTimewindow": true,
  1231 + "showDashboardExport": true,
  1232 + "toolbarAlwaysOpen": true
  1233 + }
  1234 + },
  1235 + "name": "Thermostats"
  1236 +}
\ No newline at end of file
... ...
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Root Rule Chain",
  5 + "firstRuleNodeId": null,
  6 + "root": true,
  7 + "debugMode": false,
  8 + "configuration": null
  9 + },
  10 + "metadata": {
  11 + "firstNodeIndex": 2,
  12 + "nodes": [
  13 + {
  14 + "additionalInfo": {
  15 + "layoutX": 824,
  16 + "layoutY": 156
  17 + },
  18 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  19 + "name": "Save Timeseries",
  20 + "debugMode": false,
  21 + "configuration": {
  22 + "defaultTTL": 0
  23 + }
  24 + },
  25 + {
  26 + "additionalInfo": {
  27 + "layoutX": 825,
  28 + "layoutY": 52
  29 + },
  30 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  31 + "name": "Save Client Attributes",
  32 + "debugMode": false,
  33 + "configuration": {
  34 + "scope": "CLIENT_SCOPE"
  35 + }
  36 + },
  37 + {
  38 + "additionalInfo": {
  39 + "layoutX": 347,
  40 + "layoutY": 149
  41 + },
  42 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  43 + "name": "Message Type Switch",
  44 + "debugMode": false,
  45 + "configuration": {
  46 + "version": 0
  47 + }
  48 + },
  49 + {
  50 + "additionalInfo": {
  51 + "layoutX": 825,
  52 + "layoutY": 266
  53 + },
  54 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  55 + "name": "Log RPC from Device",
  56 + "debugMode": false,
  57 + "configuration": {
  58 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  59 + }
  60 + },
  61 + {
  62 + "additionalInfo": {
  63 + "layoutX": 825,
  64 + "layoutY": 379
  65 + },
  66 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  67 + "name": "Log Other",
  68 + "debugMode": false,
  69 + "configuration": {
  70 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  71 + }
  72 + },
  73 + {
  74 + "additionalInfo": {
  75 + "layoutX": 825,
  76 + "layoutY": 468
  77 + },
  78 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  79 + "name": "RPC Call Request",
  80 + "debugMode": false,
  81 + "configuration": {
  82 + "timeoutInSeconds": 60
  83 + }
  84 + },
  85 + {
  86 + "additionalInfo": {
  87 + "layoutX": 1069,
  88 + "layoutY": 90
  89 + },
  90 + "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
  91 + "name": "Is Thermostat?",
  92 + "debugMode": false,
  93 + "configuration": {
  94 + "jsScript": "return metadata[\"deviceType\"] === \"thermostat\";"
  95 + }
  96 + }
  97 + ],
  98 + "connections": [
  99 + {
  100 + "fromIndex": 0,
  101 + "toIndex": 6,
  102 + "type": "Success"
  103 + },
  104 + {
  105 + "fromIndex": 2,
  106 + "toIndex": 4,
  107 + "type": "Other"
  108 + },
  109 + {
  110 + "fromIndex": 2,
  111 + "toIndex": 1,
  112 + "type": "Post attributes"
  113 + },
  114 + {
  115 + "fromIndex": 2,
  116 + "toIndex": 0,
  117 + "type": "Post telemetry"
  118 + },
  119 + {
  120 + "fromIndex": 2,
  121 + "toIndex": 3,
  122 + "type": "RPC Request from Device"
  123 + },
  124 + {
  125 + "fromIndex": 2,
  126 + "toIndex": 5,
  127 + "type": "RPC Request to Device"
  128 + }
  129 + ],
  130 + "ruleChainConnections": [
  131 + {
  132 + "fromIndex": 6,
  133 + "targetRuleChainId": {
  134 + "entityType": "RULE_CHAIN",
  135 + "id": "83d42540-85fd-11ea-aee2-794850541ced"
  136 + },
  137 + "additionalInfo": {
  138 + "layoutX": 1088,
  139 + "layoutY": 203,
  140 + "ruleChainNodeId": "rule-chain-node-9"
  141 + },
  142 + "type": "True"
  143 + }
  144 + ]
  145 + }
  146 +}
\ No newline at end of file
... ...
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Thermostat Alarms",
  5 + "firstRuleNodeId": null,
  6 + "root": false,
  7 + "debugMode": false,
  8 + "configuration": null
  9 + },
  10 + "metadata": {
  11 + "firstNodeIndex": 5,
  12 + "nodes": [
  13 + {
  14 + "additionalInfo": {
  15 + "layoutX": 929,
  16 + "layoutY": 67
  17 + },
  18 + "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
  19 + "name": "Create Temp Alarm",
  20 + "debugMode": false,
  21 + "configuration": {
  22 + "alarmType": "High Temperature",
  23 + "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.temperature;\nreturn details;",
  24 + "severity": "MAJOR",
  25 + "propagate": true,
  26 + "useMessageAlarmData": false,
  27 + "relationTypes": []
  28 + }
  29 + },
  30 + {
  31 + "additionalInfo": {
  32 + "layoutX": 930,
  33 + "layoutY": 201
  34 + },
  35 + "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
  36 + "name": "Clear Temp Alarm",
  37 + "debugMode": false,
  38 + "configuration": {
  39 + "alarmType": "High Temperature",
  40 + "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
  41 + }
  42 + },
  43 + {
  44 + "additionalInfo": {
  45 + "layoutX": 930,
  46 + "layoutY": 131
  47 + },
  48 + "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
  49 + "name": "Create Humidity Alarm",
  50 + "debugMode": false,
  51 + "configuration": {
  52 + "alarmType": "Low Humidity",
  53 + "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.humidity;\nreturn details;",
  54 + "severity": "MINOR",
  55 + "propagate": true,
  56 + "useMessageAlarmData": false,
  57 + "relationTypes": []
  58 + }
  59 + },
  60 + {
  61 + "additionalInfo": {
  62 + "layoutX": 929,
  63 + "layoutY": 275
  64 + },
  65 + "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
  66 + "name": "Clear Humidity Alarm",
  67 + "debugMode": false,
  68 + "configuration": {
  69 + "alarmType": "Low Humidity",
  70 + "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
  71 + }
  72 + },
  73 + {
  74 + "additionalInfo": {
  75 + "layoutX": 586,
  76 + "layoutY": 148
  77 + },
  78 + "type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode",
  79 + "name": "Check Alarms",
  80 + "debugMode": true,
  81 + "configuration": {
  82 + "jsScript": "var relations = [];\nif(metadata[\"ss_alarmTemperature\"] === \"true\"){\n if(msg.temperature > metadata[\"ss_thresholdTemperature\"]){\n relations.push(\"NewTempAlarm\");\n } else {\n relations.push(\"ClearTempAlarm\");\n }\n}\nif(metadata[\"ss_alarmHumidity\"] === \"true\"){\n if(msg.humidity < metadata[\"ss_thresholdHumidity\"]){\n relations.push(\"NewHumidityAlarm\");\n } else {\n relations.push(\"ClearHumidityAlarm\");\n }\n}\n\nreturn relations;"
  83 + }
  84 + },
  85 + {
  86 + "additionalInfo": {
  87 + "layoutX": 321,
  88 + "layoutY": 149
  89 + },
  90 + "type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode",
  91 + "name": "Fetch Configuration",
  92 + "debugMode": true,
  93 + "configuration": {
  94 + "clientAttributeNames": [],
  95 + "sharedAttributeNames": [],
  96 + "serverAttributeNames": [
  97 + "alarmTemperature",
  98 + "thresholdTemperature",
  99 + "alarmHumidity",
  100 + "thresholdHumidity"
  101 + ],
  102 + "latestTsKeyNames": [],
  103 + "tellFailureIfAbsent": false,
  104 + "getLatestValueWithTs": false
  105 + }
  106 + }
  107 + ],
  108 + "connections": [
  109 + {
  110 + "fromIndex": 4,
  111 + "toIndex": 0,
  112 + "type": "NewTempAlarm"
  113 + },
  114 + {
  115 + "fromIndex": 4,
  116 + "toIndex": 1,
  117 + "type": "ClearTempAlarm"
  118 + },
  119 + {
  120 + "fromIndex": 4,
  121 + "toIndex": 2,
  122 + "type": "NewHumidityAlarm"
  123 + },
  124 + {
  125 + "fromIndex": 4,
  126 + "toIndex": 3,
  127 + "type": "ClearHumidityAlarm"
  128 + },
  129 + {
  130 + "fromIndex": 5,
  131 + "toIndex": 4,
  132 + "type": "Success"
  133 + }
  134 + ],
  135 + "ruleChainConnections": null
  136 + }
  137 +}
\ No newline at end of file
... ...
... ... @@ -123,3 +123,28 @@ BEGIN
123 123 END LOOP;
124 124 END
125 125 $$;
  126 +
  127 +CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)
  128 + LANGUAGE plpgsql AS
  129 +$$
  130 +DECLARE
  131 + ttl_ts bigint;
  132 + debug_ttl_ts bigint;
  133 + ttl_deleted_count bigint DEFAULT 0;
  134 + debug_ttl_deleted_count bigint DEFAULT 0;
  135 +BEGIN
  136 + IF ttl > 0 THEN
  137 + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
  138 + EXECUTE format(
  139 + 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type != %L::varchar AND event_type != %L::varchar) RETURNING *) SELECT count(*) FROM deleted', ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count;
  140 + END IF;
  141 + IF debug_ttl > 0 THEN
  142 + debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;
  143 + EXECUTE format(
  144 + 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type = %L::varchar OR event_type = %L::varchar) RETURNING *) SELECT count(*) FROM deleted', debug_ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count;
  145 + END IF;
  146 + RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
  147 + RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
  148 + deleted := ttl_deleted_count + debug_ttl_deleted_count;
  149 +END
  150 +$$;
... ...
... ... @@ -32,6 +32,7 @@ import lombok.Setter;
32 32 import lombok.extern.slf4j.Slf4j;
33 33 import org.springframework.beans.factory.annotation.Autowired;
34 34 import org.springframework.beans.factory.annotation.Value;
  35 +import org.springframework.context.annotation.Lazy;
35 36 import org.springframework.data.redis.core.RedisTemplate;
36 37 import org.springframework.scheduling.annotation.Scheduled;
37 38 import org.springframework.stereotype.Component;
... ... @@ -233,6 +234,7 @@ public class ActorSystemContext {
233 234 /**
234 235 * The following Service will be null if we operate in tb-core mode
235 236 */
  237 + @Lazy
236 238 @Autowired(required = false)
237 239 @Getter
238 240 private TbRuleEngineDeviceRpcService tbRuleEngineDeviceRpcService;
... ... @@ -240,6 +242,7 @@ public class ActorSystemContext {
240 242 /**
241 243 * The following Service will be null if we operate in tb-rule-engine mode
242 244 */
  245 + @Lazy
243 246 @Autowired(required = false)
244 247 @Getter
245 248 private TbCoreDeviceRpcService tbCoreDeviceRpcService;
... ...
... ... @@ -43,7 +43,7 @@ public class QueueController extends BaseController {
43 43 ServiceType type = ServiceType.valueOf(serviceType);
44 44 switch (type) {
45 45 case TB_RULE_ENGINE:
46   - return Arrays.asList("HighPriority", "Main");
  46 + return Arrays.asList("Main", "HighPriority", "SequentialByOriginator");
47 47 default:
48 48 return Collections.emptyList();
49 49 }
... ...
... ... @@ -161,15 +161,15 @@ public class RuleChainController extends BaseController {
161 161 TenantId tenantId = getCurrentUser().getTenantId();
162 162 RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
163 163 if (ruleChainService.setRootRuleChain(getTenantId(), ruleChainId)) {
  164 + if (previousRootRuleChain != null) {
  165 + previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId());
164 166
165   - previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId());
166   -
167   - tbClusterService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
168   - ComponentLifecycleEvent.UPDATED);
169   -
170   - logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain,
171   - null, ActionType.UPDATED, null);
  167 + tbClusterService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
  168 + ComponentLifecycleEvent.UPDATED);
172 169
  170 + logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain,
  171 + null, ActionType.UPDATED, null);
  172 + }
173 173 ruleChain = ruleChainService.findRuleChainById(getTenantId(), ruleChainId);
174 174
175 175 tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(),
... ...
... ... @@ -397,11 +397,6 @@ public class TelemetryController extends BaseController {
397 397 @Override
398 398 public void onSuccess(@Nullable Void tmp) {
399 399 logAttributesUpdated(user, entityId, scope, attributes, null);
400   - if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) {
401   - DeviceId deviceId = new DeviceId(entityId.getId());
402   - tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(
403   - user.getTenantId(), deviceId, scope, attributes), null);
404   - }
405 400 result.setResult(new ResponseEntity(HttpStatus.OK));
406 401 }
407 402
... ...
... ... @@ -23,8 +23,8 @@ import org.springframework.context.ApplicationContext;
23 23 import org.springframework.context.annotation.Profile;
24 24 import org.springframework.stereotype.Service;
25 25 import org.thingsboard.server.service.component.ComponentDiscoveryService;
26   -import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
27 26 import org.thingsboard.server.service.install.DatabaseEntitiesUpgradeService;
  27 +import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
28 28 import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
29 29 import org.thingsboard.server.service.install.SystemDataLoaderService;
30 30 import org.thingsboard.server.service.install.TsDatabaseSchemaService;
... ...
... ... @@ -25,24 +25,32 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
25 25 import org.springframework.stereotype.Service;
26 26 import org.thingsboard.server.common.data.AdminSettings;
27 27 import org.thingsboard.server.common.data.Customer;
  28 +import org.thingsboard.server.common.data.DataConstants;
28 29 import org.thingsboard.server.common.data.Device;
29 30 import org.thingsboard.server.common.data.Tenant;
30 31 import org.thingsboard.server.common.data.User;
31 32 import org.thingsboard.server.common.data.id.CustomerId;
  33 +import org.thingsboard.server.common.data.id.DeviceId;
32 34 import org.thingsboard.server.common.data.id.TenantId;
  35 +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  36 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  37 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  38 +import org.thingsboard.server.common.data.kv.LongDataEntry;
33 39 import org.thingsboard.server.common.data.security.Authority;
34 40 import org.thingsboard.server.common.data.security.DeviceCredentials;
35 41 import org.thingsboard.server.common.data.security.UserCredentials;
36 42 import org.thingsboard.server.common.data.widget.WidgetsBundle;
  43 +import org.thingsboard.server.dao.attributes.AttributesService;
37 44 import org.thingsboard.server.dao.customer.CustomerService;
38 45 import org.thingsboard.server.dao.device.DeviceCredentialsService;
39 46 import org.thingsboard.server.dao.device.DeviceService;
40   -import org.thingsboard.server.dao.model.ModelConstants;
41 47 import org.thingsboard.server.dao.settings.AdminSettingsService;
42 48 import org.thingsboard.server.dao.tenant.TenantService;
43 49 import org.thingsboard.server.dao.user.UserService;
44 50 import org.thingsboard.server.dao.widget.WidgetsBundleService;
45 51
  52 +import java.util.Arrays;
  53 +
46 54 @Service
47 55 @Profile("install")
48 56 @Slf4j
... ... @@ -77,6 +85,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
77 85 private DeviceService deviceService;
78 86
79 87 @Autowired
  88 + private AttributesService attributesService;
  89 +
  90 + @Autowired
80 91 private DeviceCredentialsService deviceCredentialsService;
81 92
82 93 @Bean
... ... @@ -120,7 +131,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
120 131 demoTenant.setRegion("Global");
121 132 demoTenant.setTitle("Tenant");
122 133 demoTenant = tenantService.saveTenant(demoTenant);
123   - installScripts.createDefaultRuleChains(demoTenant.getId());
  134 + installScripts.loadDemoRuleChains(demoTenant.getId());
124 135 createUser(Authority.TENANT_ADMIN, demoTenant.getId(), null, "tenant@thingsboard.org", "tenant");
125 136
126 137 Customer customerA = new Customer();
... ... @@ -152,6 +163,25 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
152 163 createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
153 164 "Raspberry Pi GPIO control sample application");
154 165
  166 + DeviceId t1Id = createDevice(demoTenant.getId(), null, "thermostat", "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
  167 + DeviceId t2Id = createDevice(demoTenant.getId(), null, "thermostat", "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
  168 +
  169 + attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE,
  170 + Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)),
  171 + new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -122.1503)),
  172 + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmTemperature", true)),
  173 + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmHumidity", true)),
  174 + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdTemperature", (long) 20)),
  175 + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdHumidity", (long) 50))));
  176 +
  177 + attributesService.save(demoTenant.getId(), t2Id, DataConstants.SERVER_SCOPE,
  178 + Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.493801)),
  179 + new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -121.948769)),
  180 + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmTemperature", true)),
  181 + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmHumidity", true)),
  182 + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdTemperature", (long) 25)),
  183 + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdHumidity", (long) 30))));
  184 +
155 185 installScripts.loadDashboards(demoTenant.getId(), null);
156 186 }
157 187
... ...
... ... @@ -24,6 +24,7 @@ import org.springframework.util.StringUtils;
24 24 import org.thingsboard.server.common.data.Dashboard;
25 25 import org.thingsboard.server.common.data.id.CustomerId;
26 26 import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.id.RuleChainId;
27 28 import org.thingsboard.server.common.data.id.TenantId;
28 29 import org.thingsboard.server.common.data.rule.RuleChain;
29 30 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
... ... @@ -182,4 +183,30 @@ public class InstallScripts {
182 183 }
183 184
184 185
  186 + public void loadDemoRuleChains(TenantId tenantId) throws Exception {
  187 + Path ruleChainsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, RULE_CHAINS_DIR);
  188 + try {
  189 + JsonNode ruleChainJson = objectMapper.readTree(ruleChainsDir.resolve("thermostat_alarms.json").toFile());
  190 + RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
  191 + RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
  192 + ruleChain.setTenantId(tenantId);
  193 + ruleChain = ruleChainService.saveRuleChain(ruleChain);
  194 + ruleChainMetaData.setRuleChainId(ruleChain.getId());
  195 + ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData);
  196 +
  197 + JsonNode rootChainJson = objectMapper.readTree(ruleChainsDir.resolve("root_rule_chain.json").toFile());
  198 + RuleChain rootChain = objectMapper.treeToValue(rootChainJson.get("ruleChain"), RuleChain.class);
  199 + RuleChainMetaData rootChainMetaData = objectMapper.treeToValue(rootChainJson.get("metadata"), RuleChainMetaData.class);
  200 +
  201 + RuleChainId thermostatsRuleChainId = ruleChain.getId();
  202 + rootChainMetaData.getRuleChainConnections().forEach(connection -> connection.setTargetRuleChainId(thermostatsRuleChainId));
  203 + rootChain.setTenantId(tenantId);
  204 + rootChain = ruleChainService.saveRuleChain(rootChain);
  205 + rootChainMetaData.setRuleChainId(rootChain.getId());
  206 + ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), rootChainMetaData);
  207 + } catch (Exception e) {
  208 + log.error("Unable to load dashboard from json", e);
  209 + throw new RuntimeException("Unable to load dashboard from json", e);
  210 + }
  211 + }
185 212 }
... ...
... ... @@ -225,6 +225,11 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
225 225 conn.createStatement().execute("ALTER TABLE tenant ADD COLUMN isolated_tb_core boolean DEFAULT (false), ADD COLUMN isolated_tb_rule_engine boolean DEFAULT (false)");
226 226 } catch (Exception e) {
227 227 }
  228 + try {
  229 + long ts = System.currentTimeMillis();
  230 + conn.createStatement().execute("ALTER TABLE event ADD COLUMN ts bigint DEFAULT " + ts + ";"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
  231 + } catch (Exception e) {
  232 + }
228 233 log.info("Schema updated.");
229 234 }
230 235 break;
... ...
... ... @@ -52,6 +52,7 @@ import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
52 52 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
53 53
54 54 import javax.annotation.PostConstruct;
  55 +import javax.annotation.PreDestroy;
55 56 import java.util.List;
56 57 import java.util.Optional;
57 58 import java.util.UUID;
... ... @@ -98,6 +99,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
98 99 super.init("tb-core-consumer", "tb-core-notifications-consumer");
99 100 }
100 101
  102 + @PreDestroy
  103 + public void destroy(){
  104 + super.destroy();
  105 + }
  106 +
101 107 @Override
102 108 public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
103 109 if (partitionChangeEvent.getServiceType().equals(getServiceType())) {
... ... @@ -117,11 +123,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
117 123 }
118 124 ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> pendingMap = msgs.stream().collect(
119 125 Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity()));
120   - ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> failedMap = new ConcurrentHashMap<>();
121 126 CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
  127 + TbPackProcessingContext<TbProtoQueueMsg<ToCoreMsg>> ctx = new TbPackProcessingContext<>(
  128 + processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>());
122 129 pendingMap.forEach((id, msg) -> {
123 130 log.trace("[{}] Creating main callback for message: {}", id, msg.getValue());
124   - TbCallback callback = new TbPackCallback<>(id, processingTimeoutLatch, pendingMap, failedMap);
  131 + TbCallback callback = new TbPackCallback<>(id, ctx);
125 132 try {
126 133 ToCoreMsg toCoreMsg = msg.getValue();
127 134 if (toCoreMsg.hasToSubscriptionMgrMsg()) {
... ... @@ -147,8 +154,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
147 154 }
148 155 });
149 156 if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) {
150   - pendingMap.forEach((id, msg) -> log.warn("[{}] Timeout to process message: {}", id, msg.getValue()));
151   - failedMap.forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue()));
  157 + ctx.getAckMap().forEach((id, msg) -> log.warn("[{}] Timeout to process message: {}", id, msg.getValue()));
  158 + ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue()));
152 159 }
153 160 mainConsumer.commit();
154 161 } catch (Exception e) {
... ...
... ... @@ -31,7 +31,6 @@ import org.thingsboard.server.common.msg.queue.ServiceQueue;
31 31 import org.thingsboard.server.common.msg.queue.ServiceType;
32 32 import org.thingsboard.server.common.msg.queue.TbCallback;
33 33 import org.thingsboard.server.common.msg.queue.TbMsgCallback;
34   -import org.thingsboard.server.gen.transport.TransportProtos;
35 34 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
36 35 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
37 36 import org.thingsboard.server.queue.TbQueueConsumer;
... ... @@ -64,9 +63,6 @@ import java.util.concurrent.ConcurrentMap;
64 63 import java.util.concurrent.ExecutorService;
65 64 import java.util.concurrent.Executors;
66 65 import java.util.concurrent.TimeUnit;
67   -import java.util.function.BiConsumer;
68   -import java.util.function.Function;
69   -import java.util.stream.Collectors;
70 66
71 67 @Service
72 68 @TbRuleEngineComponent
... ... @@ -116,10 +112,10 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
116 112
117 113 @PreDestroy
118 114 public void stop() {
  115 + super.destroy();
119 116 if (submitExecutor != null) {
120 117 submitExecutor.shutdownNow();
121 118 }
122   -
123 119 ruleEngineSettings.getQueues().forEach(config -> consumerConfigurations.put(config.getName(), config));
124 120 }
125 121
... ... @@ -156,7 +152,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
156 152 submitStrategy.init(msgs);
157 153
158 154 while (!stopped) {
159   - ProcessingAttemptContext ctx = new ProcessingAttemptContext(submitStrategy);
  155 + TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(submitStrategy);
160 156 submitStrategy.submitAttempt((id, msg) -> submitExecutor.submit(() -> {
161 157 log.trace("[{}] Creating callback for message: {}", id, msg.getValue());
162 158 ToRuleEngineMsg toRuleEngineMsg = msg.getValue();
... ...
... ... @@ -79,7 +79,7 @@ public class TbCoreConsumerStats {
79 79 public void printStats() {
80 80 int total = totalCounter.getAndSet(0);
81 81 if (total > 0) {
82   - log.info("Transport total [{}] sessionEvents [{}] getAttr [{}] subToAttr [{}] subToRpc [{}] toDevRpc [{}] subInfo [{}] claimDevice [{}]" +
  82 + log.info("Total [{}] sessionEvents [{}] getAttr [{}] subToAttr [{}] subToRpc [{}] toDevRpc [{}] subInfo [{}] claimDevice [{}]" +
83 83 " deviceState [{}] subMgr [{}] coreNfs [{}]",
84 84 total, sessionEventCounter.getAndSet(0),
85 85 getAttributesCounter.getAndSet(0), subscribeToAttributesCounter.getAndSet(0),
... ...
... ... @@ -18,21 +18,17 @@ package org.thingsboard.server.service.queue;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.common.data.id.TenantId;
20 20 import org.thingsboard.server.common.msg.queue.RuleEngineException;
21   -import org.thingsboard.server.common.msg.queue.RuleNodeException;
22   -import org.thingsboard.server.common.msg.queue.TbCallback;
23 21 import org.thingsboard.server.common.msg.queue.TbMsgCallback;
24 22
25 23 import java.util.UUID;
26   -import java.util.concurrent.ConcurrentMap;
27   -import java.util.concurrent.CountDownLatch;
28 24
29 25 @Slf4j
30 26 public class TbMsgPackCallback implements TbMsgCallback {
31 27 private final UUID id;
32 28 private final TenantId tenantId;
33   - private final ProcessingAttemptContext ctx;
  29 + private final TbMsgPackProcessingContext ctx;
34 30
35   - public TbMsgPackCallback(UUID id, TenantId tenantId, ProcessingAttemptContext ctx) {
  31 + public TbMsgPackCallback(UUID id, TenantId tenantId, TbMsgPackProcessingContext ctx) {
36 32 this.id = id;
37 33 this.tenantId = tenantId;
38 34 this.ctx = ctx;
... ...
application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java renamed from application/src/main/java/org/thingsboard/server/service/queue/ProcessingAttemptContext.java
... ... @@ -29,7 +29,7 @@ import java.util.concurrent.CountDownLatch;
29 29 import java.util.concurrent.TimeUnit;
30 30 import java.util.concurrent.atomic.AtomicInteger;
31 31
32   -public class ProcessingAttemptContext {
  32 +public class TbMsgPackProcessingContext {
33 33
34 34 private final TbRuleEngineSubmitStrategy submitStrategy;
35 35
... ... @@ -44,7 +44,7 @@ public class ProcessingAttemptContext {
44 44 @Getter
45 45 private final ConcurrentMap<TenantId, RuleEngineException> exceptionsMap = new ConcurrentHashMap<>();
46 46
47   - public ProcessingAttemptContext(TbRuleEngineSubmitStrategy submitStrategy) {
  47 + public TbMsgPackProcessingContext(TbRuleEngineSubmitStrategy submitStrategy) {
48 48 this.submitStrategy = submitStrategy;
49 49 this.pendingMap = submitStrategy.getPendingMap();
50 50 this.pendingCount = new AtomicInteger(pendingMap.size());
... ...
... ... @@ -19,44 +19,26 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.common.msg.queue.TbCallback;
20 20
21 21 import java.util.UUID;
22   -import java.util.concurrent.ConcurrentMap;
23   -import java.util.concurrent.CountDownLatch;
24 22
25 23 @Slf4j
26 24 public class TbPackCallback<T> implements TbCallback {
27   - private final CountDownLatch processingTimeoutLatch;
28   - private final ConcurrentMap<UUID, T> ackMap;
29   - private final ConcurrentMap<UUID, T> failedMap;
  25 + private final TbPackProcessingContext<T> ctx;
30 26 private final UUID id;
31 27
32   - public TbPackCallback(UUID id,
33   - CountDownLatch processingTimeoutLatch,
34   - ConcurrentMap<UUID, T> ackMap,
35   - ConcurrentMap<UUID, T> failedMap) {
  28 + public TbPackCallback(UUID id, TbPackProcessingContext<T> ctx) {
36 29 this.id = id;
37   - this.processingTimeoutLatch = processingTimeoutLatch;
38   - this.ackMap = ackMap;
39   - this.failedMap = failedMap;
  30 + this.ctx = ctx;
40 31 }
41 32
42 33 @Override
43 34 public void onSuccess() {
44 35 log.trace("[{}] ON SUCCESS", id);
45   - T msg = ackMap.remove(id);
46   - if (msg != null && ackMap.isEmpty()) {
47   - processingTimeoutLatch.countDown();
48   - }
  36 + ctx.onSuccess(id);
49 37 }
50 38
51 39 @Override
52 40 public void onFailure(Throwable t) {
53 41 log.trace("[{}] ON FAILURE", id, t);
54   - T msg = ackMap.remove(id);
55   - if (msg != null) {
56   - failedMap.put(id, msg);
57   - }
58   - if (ackMap.isEmpty()) {
59   - processingTimeoutLatch.countDown();
60   - }
  42 + ctx.onFailure(id, t);
61 43 }
62 44 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.queue;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +
  20 +import java.util.UUID;
  21 +import java.util.concurrent.ConcurrentMap;
  22 +import java.util.concurrent.CountDownLatch;
  23 +import java.util.concurrent.TimeUnit;
  24 +import java.util.concurrent.atomic.AtomicInteger;
  25 +
  26 +@Slf4j
  27 +public class TbPackProcessingContext<T> {
  28 +
  29 + private final AtomicInteger pendingCount;
  30 + private final CountDownLatch processingTimeoutLatch;
  31 + private final ConcurrentMap<UUID, T> ackMap;
  32 + private final ConcurrentMap<UUID, T> failedMap;
  33 +
  34 + public TbPackProcessingContext(CountDownLatch processingTimeoutLatch,
  35 + ConcurrentMap<UUID, T> ackMap,
  36 + ConcurrentMap<UUID, T> failedMap) {
  37 + this.processingTimeoutLatch = processingTimeoutLatch;
  38 + this.pendingCount = new AtomicInteger(ackMap.size());
  39 + this.ackMap = ackMap;
  40 + this.failedMap = failedMap;
  41 + }
  42 +
  43 + public boolean await(long packProcessingTimeout, TimeUnit milliseconds) throws InterruptedException {
  44 + return processingTimeoutLatch.await(packProcessingTimeout, milliseconds);
  45 + }
  46 +
  47 + public void onSuccess(UUID id) {
  48 + boolean empty = false;
  49 + T msg = ackMap.remove(id);
  50 + if (msg != null) {
  51 + empty = pendingCount.decrementAndGet() == 0;
  52 + }
  53 + if (empty) {
  54 + processingTimeoutLatch.countDown();
  55 + } else {
  56 + if (log.isTraceEnabled()) {
  57 + log.trace("Items left: {}", ackMap.size());
  58 + for (T t : ackMap.values()) {
  59 + log.trace("left item: {}", t);
  60 + }
  61 + }
  62 + }
  63 + }
  64 +
  65 + public void onFailure(UUID id, Throwable t) {
  66 + boolean empty = false;
  67 + T msg = ackMap.remove(id);
  68 + if (msg != null) {
  69 + empty = pendingCount.decrementAndGet() == 0;
  70 + failedMap.put(id, msg);
  71 + if (log.isTraceEnabled()) {
  72 + log.trace("Items left: {}", ackMap.size());
  73 + for (T v : ackMap.values()) {
  74 + log.trace("left item: {}", v);
  75 + }
  76 + }
  77 + }
  78 + if (empty) {
  79 + processingTimeoutLatch.countDown();
  80 + }
  81 + }
  82 +
  83 + public ConcurrentMap<UUID, T> getAckMap() {
  84 + return ackMap;
  85 + }
  86 +
  87 + public ConcurrentMap<UUID, T> getFailedMap() {
  88 + return failedMap;
  89 + }
  90 +}
... ...
... ... @@ -23,11 +23,13 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 23 import org.thingsboard.server.actors.ActorSystemContext;
24 24 import org.thingsboard.server.common.msg.queue.ServiceType;
25 25 import org.thingsboard.server.common.msg.queue.TbCallback;
  26 +import org.thingsboard.server.gen.transport.TransportProtos;
26 27 import org.thingsboard.server.queue.TbQueueConsumer;
27 28 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
28 29 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
29 30 import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
30 31 import org.thingsboard.server.service.queue.TbPackCallback;
  32 +import org.thingsboard.server.service.queue.TbPackProcessingContext;
31 33
32 34 import javax.annotation.PreDestroy;
33 35 import java.util.List;
... ... @@ -92,11 +94,12 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
92 94 }
93 95 ConcurrentMap<UUID, TbProtoQueueMsg<N>> pendingMap = msgs.stream().collect(
94 96 Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity()));
95   - ConcurrentMap<UUID, TbProtoQueueMsg<N>> failedMap = new ConcurrentHashMap<>();
96 97 CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
  98 + TbPackProcessingContext<TbProtoQueueMsg<N>> ctx = new TbPackProcessingContext<>(
  99 + processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>());
97 100 pendingMap.forEach((id, msg) -> {
98 101 log.trace("[{}] Creating notification callback for message: {}", id, msg.getValue());
99   - TbCallback callback = new TbPackCallback<>(id, processingTimeoutLatch, pendingMap, failedMap);
  102 + TbCallback callback = new TbPackCallback<>(id, ctx);
100 103 try {
101 104 handleNotification(id, msg, callback);
102 105 } catch (Throwable e) {
... ... @@ -105,8 +108,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
105 108 }
106 109 });
107 110 if (!processingTimeoutLatch.await(getNotificationPackProcessingTimeout(), TimeUnit.MILLISECONDS)) {
108   - pendingMap.forEach((id, msg) -> log.warn("[{}] Timeout to process notification: {}", id, msg.getValue()));
109   - failedMap.forEach((id, msg) -> log.warn("[{}] Failed to process notification: {}", id, msg.getValue()));
  111 + ctx.getAckMap().forEach((id, msg) -> log.warn("[{}] Timeout to process notification: {}", id, msg.getValue()));
  112 + ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process notification: {}", id, msg.getValue()));
110 113 }
111 114 nfConsumer.commit();
112 115 } catch (Exception e) {
... ...
... ... @@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
20 20 import org.thingsboard.server.common.msg.queue.RuleEngineException;
21 21 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
22 22 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
23   -import org.thingsboard.server.service.queue.ProcessingAttemptContext;
  23 +import org.thingsboard.server.service.queue.TbMsgPackProcessingContext;
24 24
25 25 import java.util.UUID;
26 26 import java.util.concurrent.ConcurrentMap;
... ... @@ -32,9 +32,9 @@ public class TbRuleEngineProcessingResult {
32 32 @Getter
33 33 private final boolean timeout;
34 34 @Getter
35   - private final ProcessingAttemptContext ctx;
  35 + private final TbMsgPackProcessingContext ctx;
36 36
37   - public TbRuleEngineProcessingResult(boolean timeout, ProcessingAttemptContext ctx) {
  37 + public TbRuleEngineProcessingResult(boolean timeout, TbMsgPackProcessingContext ctx) {
38 38 this.timeout = timeout;
39 39 this.ctx = ctx;
40 40 this.success = !timeout && ctx.getPendingMap().isEmpty() && ctx.getFailedMap().isEmpty();
... ...
... ... @@ -29,9 +29,9 @@ public class TbRuleEngineSubmitStrategyFactory {
29 29 return new BurstTbRuleEngineSubmitStrategy(name);
30 30 case "BATCH":
31 31 return new BatchTbRuleEngineSubmitStrategy(name, configuration.getBatchSize());
32   - case "SEQUENTIAL_WITHIN_ORIGINATOR":
  32 + case "SEQUENTIAL_BY_ORIGINATOR":
33 33 return new SequentialByOriginatorIdTbRuleEngineSubmitStrategy(name);
34   - case "SEQUENTIAL_WITHIN_TENANT":
  34 + case "SEQUENTIAL_BY_TENANT":
35 35 return new SequentialByTenantIdTbRuleEngineSubmitStrategy(name);
36 36 case "SEQUENTIAL":
37 37 return new SequentialTbRuleEngineSubmitStrategy(name);
... ...
... ... @@ -46,7 +46,7 @@ import java.util.concurrent.atomic.AtomicInteger;
46 46 @Service
47 47 public class RemoteJsInvokeService extends AbstractJsInvokeService {
48 48
49   - @Value("${js.remote.max_requests_timeout}")
  49 + @Value("${queue.js.max_requests_timeout}")
50 50 private long maxRequestsTimeout;
51 51
52 52 @Getter
... ...
... ... @@ -345,6 +345,8 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
345 345 keys.forEach(key -> subState.put(key, 0L));
346 346 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
347 347
  348 + TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
  349 +
348 350 TbAttributeSubscription sub = TbAttributeSubscription.builder()
349 351 .serviceId(serviceId)
350 352 .sessionId(sessionId)
... ... @@ -353,7 +355,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
353 355 .entityId(entityId)
354 356 .allKeys(false)
355 357 .keyStates(subState)
356   - .scope(TbAttributeSubscriptionScope.valueOf(cmd.getScope())).build();
  358 + .scope(scope).build();
357 359 subService.addSubscription(sub);
358 360 }
359 361
... ... @@ -440,6 +442,8 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
440 442 Map<String, Long> subState = new HashMap<>(attributesData.size());
441 443 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
442 444
  445 + TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
  446 +
443 447 TbAttributeSubscription sub = TbAttributeSubscription.builder()
444 448 .serviceId(serviceId)
445 449 .sessionId(sessionId)
... ... @@ -448,7 +452,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
448 452 .entityId(entityId)
449 453 .allKeys(true)
450 454 .keyStates(subState)
451   - .scope(TbAttributeSubscriptionScope.valueOf(cmd.getScope())).build();
  455 + .scope(scope).build();
452 456 subService.addSubscription(sub);
453 457 }
454 458
... ...
application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java renamed from application/src/main/java/org/thingsboard/server/service/ttl/AbstractTimeseriesCleanUpService.java
... ... @@ -17,47 +17,27 @@ package org.thingsboard.server.service.ttl;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.beans.factory.annotation.Value;
20   -import org.springframework.scheduling.annotation.Scheduled;
21   -import org.thingsboard.server.dao.util.PsqlTsAnyDao;
  20 +import org.thingsboard.server.dao.util.PsqlDao;
22 21
23 22 import java.sql.Connection;
24   -import java.sql.DriverManager;
25 23 import java.sql.ResultSet;
26 24 import java.sql.SQLException;
27 25 import java.sql.SQLWarning;
28 26 import java.sql.Statement;
29 27
30   -@PsqlTsAnyDao
31   -@Slf4j
32   -public abstract class AbstractTimeseriesCleanUpService {
33   -
34   - @Value("${sql.ttl.ts_key_value_ttl}")
35   - protected long systemTtl;
36 28
37   - @Value("${sql.ttl.enabled}")
38   - private boolean ttlTaskExecutionEnabled;
  29 +@Slf4j
  30 +@PsqlDao
  31 +public abstract class AbstractCleanUpService {
39 32
40 33 @Value("${spring.datasource.url}")
41   - private String dbUrl;
  34 + protected String dbUrl;
42 35
43 36 @Value("${spring.datasource.username}")
44   - private String dbUserName;
  37 + protected String dbUserName;
45 38
46 39 @Value("${spring.datasource.password}")
47   - private String dbPassword;
48   -
49   - @Scheduled(initialDelayString = "${sql.ttl.execution_interval_ms}", fixedDelayString = "${sql.ttl.execution_interval_ms}")
50   - public void cleanUp() {
51   - if (ttlTaskExecutionEnabled) {
52   - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
53   - doCleanUp(conn);
54   - } catch (SQLException e) {
55   - log.error("SQLException occurred during TTL task execution ", e);
56   - }
57   - }
58   - }
59   -
60   - protected abstract void doCleanUp(Connection connection);
  40 + protected String dbPassword;
61 41
62 42 protected long executeQuery(Connection conn, String query) {
63 43 long removed = 0L;
... ... @@ -74,7 +54,7 @@ public abstract class AbstractTimeseriesCleanUpService {
74 54 return removed;
75 55 }
76 56
77   - private void getWarnings(Statement statement) throws SQLException {
  57 + protected void getWarnings(Statement statement) throws SQLException {
78 58 SQLWarning warnings = statement.getWarnings();
79 59 if (warnings != null) {
80 60 log.debug("{}", warnings.getMessage());
... ... @@ -86,4 +66,6 @@ public abstract class AbstractTimeseriesCleanUpService {
86 66 }
87 67 }
88 68
89   -}
\ No newline at end of file
  69 + protected abstract void doCleanUp(Connection connection);
  70 +
  71 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.ttl.events;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Value;
  20 +import org.springframework.scheduling.annotation.Scheduled;
  21 +import org.springframework.stereotype.Service;
  22 +import org.thingsboard.server.dao.util.PsqlDao;
  23 +import org.thingsboard.server.service.ttl.AbstractCleanUpService;
  24 +
  25 +import java.sql.Connection;
  26 +import java.sql.DriverManager;
  27 +import java.sql.SQLException;
  28 +
  29 +@PsqlDao
  30 +@Slf4j
  31 +@Service
  32 +public class EventsCleanUpService extends AbstractCleanUpService {
  33 +
  34 + @Value("${sql.ttl.events.events_ttl}")
  35 + private long ttl;
  36 +
  37 + @Value("${sql.ttl.events.debug_events_ttl}")
  38 + private long debugTtl;
  39 +
  40 + @Value("${sql.ttl.events.enabled}")
  41 + private boolean ttlTaskExecutionEnabled;
  42 +
  43 + @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}")
  44 + public void cleanUp() {
  45 + if (ttlTaskExecutionEnabled) {
  46 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  47 + doCleanUp(conn);
  48 + } catch (SQLException e) {
  49 + log.error("SQLException occurred during TTL task execution ", e);
  50 + }
  51 + }
  52 + }
  53 +
  54 + @Override
  55 + protected void doCleanUp(Connection connection) {
  56 + long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);");
  57 + log.info("Total events removed by TTL: [{}]", totalEventsRemoved);
  58 + }
  59 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.ttl.timeseries;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Value;
  20 +import org.springframework.scheduling.annotation.Scheduled;
  21 +import org.thingsboard.server.dao.util.PsqlTsAnyDao;
  22 +import org.thingsboard.server.service.ttl.AbstractCleanUpService;
  23 +
  24 +import java.sql.Connection;
  25 +import java.sql.DriverManager;
  26 +import java.sql.SQLException;
  27 +
  28 +@PsqlTsAnyDao
  29 +@Slf4j
  30 +public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpService {
  31 +
  32 + @Value("${sql.ttl.ts.ts_key_value_ttl}")
  33 + protected long systemTtl;
  34 +
  35 + @Value("${sql.ttl.ts.enabled}")
  36 + private boolean ttlTaskExecutionEnabled;
  37 +
  38 + @Scheduled(initialDelayString = "${sql.ttl.ts.execution_interval_ms}", fixedDelayString = "${sql.ttl.ts.execution_interval_ms}")
  39 + public void cleanUp() {
  40 + if (ttlTaskExecutionEnabled) {
  41 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  42 + doCleanUp(conn);
  43 + } catch (SQLException e) {
  44 + log.error("SQLException occurred during TTL task execution ", e);
  45 + }
  46 + }
  47 + }
  48 +
  49 +}
\ No newline at end of file
... ...
application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java renamed from application/src/main/java/org/thingsboard/server/service/ttl/PsqlTimeseriesCleanUpService.java
... ... @@ -13,7 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.service.ttl;
  16 +package org.thingsboard.server.service.ttl.timeseries;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.beans.factory.annotation.Value;
... ...
application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java renamed from application/src/main/java/org/thingsboard/server/service/ttl/TimescaleTimeseriesCleanUpService.java
... ... @@ -13,7 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.service.ttl;
  16 +package org.thingsboard.server.service.ttl.timeseries;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.stereotype.Service;
... ...
... ... @@ -181,31 +181,37 @@ cassandra:
181 181
182 182 # SQL configuration parameters
183 183 sql:
184   - # Specify batch size for persisting attribute updates
185   - attributes:
186   - batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}"
187   - batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}"
188   - stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}"
189   - ts:
190   - batch_size: "${SQL_TS_BATCH_SIZE:10000}"
191   - batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}"
192   - stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}"
193   - ts_latest:
194   - batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}"
195   - batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}"
196   - stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}"
197   - # Specify whether to remove null characters from strValue of attributes and timeseries before insert
198   - remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}"
199   - postgres:
200   - # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE.
201   - ts_key_value_partitioning: "${SQL_POSTGRES_TS_KV_PARTITIONING:MONTHS}"
202   - timescale:
203   - # Specify Interval size for new data chunks storage.
204   - chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}"
205   - ttl:
206   - enabled: "${SQL_TTL_ENABLED:true}"
207   - execution_interval_ms: "${SQL_TTL_EXECUTION_INTERVAL:86400000}" # Number of miliseconds
208   - ts_key_value_ttl: "${SQL_TTL_TS_KEY_VALUE_TTL:0}" # Number of seconds
  184 + # Specify batch size for persisting attribute updates
  185 + attributes:
  186 + batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}"
  187 + batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}"
  188 + stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}"
  189 + ts:
  190 + batch_size: "${SQL_TS_BATCH_SIZE:10000}"
  191 + batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}"
  192 + stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}"
  193 + ts_latest:
  194 + batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}"
  195 + batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}"
  196 + stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}"
  197 + # Specify whether to remove null characters from strValue of attributes and timeseries before insert
  198 + remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}"
  199 + postgres:
  200 + # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE.
  201 + ts_key_value_partitioning: "${SQL_POSTGRES_TS_KV_PARTITIONING:MONTHS}"
  202 + timescale:
  203 + # Specify Interval size for new data chunks storage.
  204 + chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}"
  205 + ttl:
  206 + ts:
  207 + enabled: "${SQL_TTL_TS_ENABLED:true}"
  208 + execution_interval_ms: "${SQL_TTL_TS_EXECUTION_INTERVAL:86400000}" # Number of miliseconds. The current value corresponds to one day
  209 + ts_key_value_ttl: "${SQL_TTL_TS_TS_KEY_VALUE_TTL:0}" # Number of seconds
  210 + events:
  211 + enabled: "${SQL_TTL_EVENTS_ENABLED:true}"
  212 + execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of miliseconds. The current value corresponds to one day
  213 + events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" # Number of seconds
  214 + debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" # Number of seconds. The current value corresponds to one week
209 215
210 216 # Actor system parameters
211 217 actors:
... ... @@ -410,8 +416,9 @@ audit-log:
410 416 password: "${AUDIT_LOG_SINK_PASSWORD:}"
411 417
412 418 state:
413   - defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:10}"
414   - defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:10}"
  419 + # Should be greater then transport.sessions.report_timeout
  420 + defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:600}"
  421 + defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:60}"
415 422 persistToTelemetry: "${PERSIST_STATE_TO_TELEMETRY:false}"
416 423
417 424 js:
... ... @@ -595,7 +602,7 @@ queue:
595 602 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
596 603 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
597 604 stats:
598   - enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
  605 + enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"
599 606 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"
600 607 js:
601 608 # JS Eval request topic
... ... @@ -624,7 +631,7 @@ queue:
624 631 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
625 632 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}"
626 633 submit-strategy:
627   - type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL
  634 + type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
628 635 # For BATCH only
629 636 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
630 637 processing-strategy:
... ... @@ -636,10 +643,10 @@ queue:
636 643 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}"
637 644 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
638 645 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
639   - partitions: "${TB_QUEUE_RE_HP_PARTITIONS:3}"
  646 + partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
640 647 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}"
641 648 submit-strategy:
642   - type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_WITHIN_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL
  649 + type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
643 650 # For BATCH only
644 651 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
645 652 processing-strategy:
... ... @@ -648,6 +655,21 @@ queue:
648 655 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
649 656 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
650 657 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
  658 + - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}"
  659 + topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
  660 + poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
  661 + partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
  662 + pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:60000}"
  663 + submit-strategy:
  664 + type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
  665 + # For BATCH only
  666 + batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
  667 + processing-strategy:
  668 + type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  669 + # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  670 + retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
  671 + failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
  672 + pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
651 673 transport:
652 674 # For high priority notifications that require minimum latency and processing time
653 675 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
... ...
... ... @@ -97,7 +97,8 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
97 97 assertEquals("4", values.get("key4").get(0).get("value"));
98 98 }
99 99
100   - @Test
  100 +
  101 +// @Test - Unstable
101 102 public void testMqttQoSLevel() throws Exception {
102 103 String clientId = MqttAsyncClient.generateClientId();
103 104 MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);
... ... @@ -109,7 +110,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
109 110 client.setCallback(callback);
110 111 client.connect(options).waitForCompletion(5000);
111 112 client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value());
112   - String payload = "{\"key\":\"value\"}";
  113 + String payload = "{\"key\":\"uniqueValue\"}";
113 114 // TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue.
114 115 // MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
115 116 // MqttClient <- SUB_ACK <- Transport
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.service.cluster.routing;
17 17
18 18 import com.datastax.driver.core.utils.UUIDs;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.junit.Assert;
20 21 import org.junit.Before;
21 22 import org.junit.Test;
22 23 import org.junit.runner.RunWith;
... ... @@ -31,6 +32,8 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
31 32 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
32 33 import org.thingsboard.server.gen.transport.TransportProtos;
33 34 import org.thingsboard.server.queue.discovery.TenantRoutingInfoService;
  35 +import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
  36 +import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
34 37
35 38 import java.util.ArrayList;
36 39 import java.util.Collections;
... ... @@ -41,6 +44,7 @@ import java.util.Map;
41 44 import java.util.stream.Collectors;
42 45
43 46 import static org.mockito.Mockito.mock;
  47 +import static org.mockito.Mockito.when;
44 48
45 49 @Slf4j
46 50 @RunWith(MockitoJUnitRunner.class)
... ... @@ -52,6 +56,7 @@ public class ConsistentHashParitionServiceTest {
52 56 private TbServiceInfoProvider discoveryService;
53 57 private TenantRoutingInfoService routingInfoService;
54 58 private ApplicationEventPublisher applicationEventPublisher;
  59 + private TbQueueRuleEngineSettings ruleEngineSettings;
55 60
56 61 private String hashFunctionName = "murmur3_128";
57 62 private Integer virtualNodesSize = 16;
... ... @@ -62,12 +67,15 @@ public class ConsistentHashParitionServiceTest {
62 67 discoveryService = mock(TbServiceInfoProvider.class);
63 68 applicationEventPublisher = mock(ApplicationEventPublisher.class);
64 69 routingInfoService = mock(TenantRoutingInfoService.class);
65   - clusterRoutingService = new ConsistentHashPartitionService(discoveryService, routingInfoService, applicationEventPublisher);
  70 + ruleEngineSettings = mock(TbQueueRuleEngineSettings.class);
  71 + clusterRoutingService = new ConsistentHashPartitionService(discoveryService,
  72 + routingInfoService,
  73 + applicationEventPublisher,
  74 + ruleEngineSettings
  75 + );
  76 + when(ruleEngineSettings.getQueues()).thenReturn(Collections.emptyList());
66 77 ReflectionTestUtils.setField(clusterRoutingService, "coreTopic", "tb.core");
67 78 ReflectionTestUtils.setField(clusterRoutingService, "corePartitions", 3);
68   - ReflectionTestUtils.setField(clusterRoutingService, "ruleEngineTopic", "tb.rule-engine");
69   - ReflectionTestUtils.setField(clusterRoutingService, "ruleEnginePartitions", 100);
70   -
71 79 ReflectionTestUtils.setField(clusterRoutingService, "hashFunctionName", hashFunctionName);
72 80 ReflectionTestUtils.setField(clusterRoutingService, "virtualNodesSize", virtualNodesSize);
73 81 TransportProtos.ServiceInfo currentServer = TransportProtos.ServiceInfo.newBuilder()
... ... @@ -107,8 +115,9 @@ public class ConsistentHashParitionServiceTest {
107 115 List<Map.Entry<Integer, Integer>> data = map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).collect(Collectors.toList());
108 116 long end = System.currentTimeMillis();
109 117 double diff = (data.get(data.size() - 1).getValue() - data.get(0).getValue());
110   - System.out.println("Size: " + virtualNodesSize + " Time: " + (end - start) + " Diff: " + diff + "(" + String.format("%f", (diff / ITERATIONS) * 100.0) + "%)");
111   -
  118 + double diffPercent = (diff / ITERATIONS) * 100.0;
  119 + System.out.println("Size: " + virtualNodesSize + " Time: " + (end - start) + " Diff: " + diff + "(" + String.format("%f", diffPercent) + "%)");
  120 + Assert.assertTrue(diffPercent < 0.5);
112 121 for (Map.Entry<Integer, Integer> entry : data) {
113 122 System.out.println(entry.getKey() + ": " + entry.getValue());
114 123 }
... ...
application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContextTest.java renamed from application/src/test/java/org/thingsboard/server/service/queue/ProcessingAttemptContextTest.java
... ... @@ -37,7 +37,7 @@ import static org.mockito.Mockito.when;
37 37
38 38 @Slf4j
39 39 @RunWith(MockitoJUnitRunner.class)
40   -public class ProcessingAttemptContextTest {
  40 +public class TbMsgPackProcessingContextTest {
41 41
42 42 @Test
43 43 public void testHighConcurrencyCase() throws InterruptedException {
... ... @@ -51,7 +51,7 @@ public class ProcessingAttemptContextTest {
51 51 messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null));
52 52 }
53 53 when(strategyMock.getPendingMap()).thenReturn(messages);
54   - ProcessingAttemptContext context = new ProcessingAttemptContext(strategyMock);
  54 + TbMsgPackProcessingContext context = new TbMsgPackProcessingContext(strategyMock);
55 55 for (UUID uuid : messages.keySet()) {
56 56 for (int i = 0; i < parallelCount; i++) {
57 57 executorService.submit(() -> context.onSuccess(uuid));
... ...
... ... @@ -7,7 +7,7 @@
7 7 </encoder>
8 8 </appender>
9 9
10   - <logger name="org.thingsboard.server" level="TRACE"/>
  10 + <logger name="org.thingsboard.server" level="WARN"/>
11 11 <logger name="org.springframework" level="WARN"/>
12 12 <logger name="org.springframework.boot.test" level="WARN"/>
13 13 <logger name="org.apache.cassandra" level="WARN"/>
... ...
... ... @@ -30,6 +30,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
30 30 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
31 31 import org.thingsboard.server.gen.transport.TransportProtos;
32 32 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  33 +import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
33 34
34 35 import javax.annotation.PostConstruct;
35 36 import java.nio.charset.StandardCharsets;
... ... @@ -61,6 +62,7 @@ public class ConsistentHashPartitionService implements PartitionService {
61 62 private final ApplicationEventPublisher applicationEventPublisher;
62 63 private final TbServiceInfoProvider serviceInfoProvider;
63 64 private final TenantRoutingInfoService tenantRoutingInfoService;
  65 + private final TbQueueRuleEngineSettings tbQueueRuleEngineSettings;
64 66 private final ConcurrentMap<ServiceQueue, String> partitionTopics = new ConcurrentHashMap<>();
65 67 private final ConcurrentMap<ServiceQueue, Integer> partitionSizes = new ConcurrentHashMap<>();
66 68 private final ConcurrentMap<TenantId, TenantRoutingInfo> tenantRoutingInfoMap = new ConcurrentHashMap<>();
... ... @@ -74,10 +76,14 @@ public class ConsistentHashPartitionService implements PartitionService {
74 76
75 77 private HashFunction hashFunction;
76 78
77   - public ConsistentHashPartitionService(TbServiceInfoProvider serviceInfoProvider, TenantRoutingInfoService tenantRoutingInfoService, ApplicationEventPublisher applicationEventPublisher) {
  79 + public ConsistentHashPartitionService(TbServiceInfoProvider serviceInfoProvider,
  80 + TenantRoutingInfoService tenantRoutingInfoService,
  81 + ApplicationEventPublisher applicationEventPublisher,
  82 + TbQueueRuleEngineSettings tbQueueRuleEngineSettings) {
78 83 this.serviceInfoProvider = serviceInfoProvider;
79 84 this.tenantRoutingInfoService = tenantRoutingInfoService;
80 85 this.applicationEventPublisher = applicationEventPublisher;
  86 + this.tbQueueRuleEngineSettings = tbQueueRuleEngineSettings;
81 87 }
82 88
83 89 @PostConstruct
... ... @@ -85,6 +91,10 @@ public class ConsistentHashPartitionService implements PartitionService {
85 91 this.hashFunction = forName(hashFunctionName);
86 92 partitionSizes.put(new ServiceQueue(ServiceType.TB_CORE), corePartitions);
87 93 partitionTopics.put(new ServiceQueue(ServiceType.TB_CORE), coreTopic);
  94 + tbQueueRuleEngineSettings.getQueues().forEach(queueConfiguration -> {
  95 + partitionTopics.put(new ServiceQueue(ServiceType.TB_RULE_ENGINE, queueConfiguration.getName()), queueConfiguration.getTopic());
  96 + partitionSizes.put(new ServiceQueue(ServiceType.TB_RULE_ENGINE, queueConfiguration.getName()), queueConfiguration.getPartitions());
  97 + });
88 98 }
89 99
90 100 @Override
... ...
... ... @@ -544,6 +544,9 @@ public class DefaultTransportService implements TransportService {
544 544
545 545 protected void sendToDeviceActor(TransportProtos.SessionInfoProto sessionInfo, TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback<Void> callback) {
546 546 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, getTenantId(sessionInfo), getDeviceId(sessionInfo));
  547 + if (log.isTraceEnabled()) {
  548 + log.trace("[{}][{}] Pushing to topic {} message {}", getTenantId(sessionInfo), getDeviceId(sessionInfo), tpi.getFullTopicName(), toDeviceActorMsg);
  549 + }
547 550 tbCoreMsgProducer.send(tpi,
548 551 new TbProtoQueueMsg<>(getRoutingKey(sessionInfo),
549 552 ToCoreMsg.newBuilder().setToDeviceActorMsg(toDeviceActorMsg).build()), callback != null ?
... ... @@ -552,6 +555,9 @@ public class DefaultTransportService implements TransportService {
552 555
553 556 protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) {
554 557 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator());
  558 + if (log.isTraceEnabled()) {
  559 + log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, tbMsg.getOriginator(), tpi.getFullTopicName(), tbMsg);
  560 + }
555 561 ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder().setTbMsg(TbMsg.toByteString(tbMsg))
556 562 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
557 563 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build();
... ...
... ... @@ -32,6 +32,9 @@ public class ModelConstants {
32 32 public static final String NULL_UUID_STR = UUIDConverter.fromTimeUUID(NULL_UUID);
33 33 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
34 34
  35 + // this is the difference between midnight October 15, 1582 UTC and midnight January 1, 1970 UTC as 100 nanosecond units
  36 + public static final long EPOCH_DIFF = 122192928000000000L;
  37 +
35 38 /**
36 39 * Generic constants.
37 40 */
... ...
... ... @@ -37,6 +37,9 @@ import javax.persistence.EnumType;
37 37 import javax.persistence.Enumerated;
38 38 import javax.persistence.Table;
39 39
  40 +import java.util.UUID;
  41 +
  42 +import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF;
40 43 import static org.thingsboard.server.dao.model.ModelConstants.EVENT_BODY_PROPERTY;
41 44 import static org.thingsboard.server.dao.model.ModelConstants.EVENT_COLUMN_FAMILY_NAME;
42 45 import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_ID_PROPERTY;
... ... @@ -44,6 +47,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_TYPE_
44 47 import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TENANT_ID_PROPERTY;
45 48 import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TYPE_PROPERTY;
46 49 import static org.thingsboard.server.dao.model.ModelConstants.EVENT_UID_PROPERTY;
  50 +import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN;
47 51
48 52 @Data
49 53 @EqualsAndHashCode(callSuper = true)
... ... @@ -73,9 +77,15 @@ public class EventEntity extends BaseSqlEntity<Event> implements BaseEntity<Eve
73 77 @Column(name = EVENT_BODY_PROPERTY)
74 78 private JsonNode body;
75 79
  80 + @Column(name = TS_COLUMN)
  81 + private long ts;
  82 +
76 83 public EventEntity(Event event) {
77 84 if (event.getId() != null) {
78 85 this.setUuid(event.getId().getId());
  86 + this.ts = getTs(event.getId().getId());
  87 + } else {
  88 + this.ts = System.currentTimeMillis();
79 89 }
80 90 if (event.getTenantId() != null) {
81 91 this.tenantId = toString(event.getTenantId().getId());
... ... @@ -101,4 +111,8 @@ public class EventEntity extends BaseSqlEntity<Event> implements BaseEntity<Eve
101 111 event.setUid(eventUid);
102 112 return event;
103 113 }
  114 +
  115 + private long getTs(UUID uuid) {
  116 + return (uuid.timestamp() - EPOCH_DIFF) / 10000;
  117 + }
104 118 }
... ...
... ... @@ -88,26 +88,33 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
88 88 RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
89 89 if (!ruleChain.isRoot()) {
90 90 RuleChain previousRootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId());
91   - if (!previousRootRuleChain.getId().equals(ruleChain.getId())) {
92   - try {
  91 + try {
  92 + if (previousRootRuleChain == null) {
  93 + setRootAndSave(tenantId, ruleChain);
  94 + return true;
  95 + } else if (!previousRootRuleChain.getId().equals(ruleChain.getId())) {
93 96 deleteRelation(tenantId, new EntityRelation(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
94 97 EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
95 98 previousRootRuleChain.setRoot(false);
96 99 ruleChainDao.save(tenantId, previousRootRuleChain);
97   - createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
98   - EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
99   - ruleChain.setRoot(true);
100   - ruleChainDao.save(tenantId, ruleChain);
  100 + setRootAndSave(tenantId, ruleChain);
101 101 return true;
102   - } catch (ExecutionException | InterruptedException e) {
103   - log.warn("[{}] Failed to set root rule chain, ruleChainId: [{}]", ruleChainId);
104   - throw new RuntimeException(e);
105 102 }
  103 + } catch (ExecutionException | InterruptedException e) {
  104 + log.warn("[{}] Failed to set root rule chain, ruleChainId: [{}]", ruleChainId);
  105 + throw new RuntimeException(e);
106 106 }
107 107 }
108 108 return false;
109 109 }
110 110
  111 + private void setRootAndSave(TenantId tenantId, RuleChain ruleChain) throws ExecutionException, InterruptedException {
  112 + createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
  113 + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
  114 + ruleChain.setRoot(true);
  115 + ruleChainDao.save(tenantId, ruleChain);
  116 + }
  117 +
111 118 @Override
112 119 public RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData) {
113 120 Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id.");
... ...
... ... @@ -75,7 +75,8 @@ public abstract class AbstractEventInsertRepository implements EventInsertReposi
75 75 .setParameter("entity_type", entity.getEntityType().name())
76 76 .setParameter("event_type", entity.getEventType())
77 77 .setParameter("event_uid", entity.getEventUid())
78   - .setParameter("tenant_id", entity.getTenantId());
  78 + .setParameter("tenant_id", entity.getTenantId())
  79 + .setParameter("ts", entity.getTs());
79 80 }
80 81
81 82 private EventEntity processSaveOrUpdate(EventEntity entity, String query) {
... ...
... ... @@ -44,7 +44,7 @@ public class HsqlEventInsertRepository extends AbstractEventInsertRepository {
44 44 }
45 45
46 46 private static String getInsertString(String conflictStatement) {
47   - return "MERGE INTO event USING (VALUES :id, :body, :entity_id, :entity_type, :event_type, :event_uid, :tenant_id) I (id, body, entity_id, entity_type, event_type, event_uid, tenant_id) ON " + conflictStatement + " WHEN MATCHED THEN UPDATE SET event.id = I.id, event.body = I.body, event.entity_id = I.entity_id, event.entity_type = I.entity_type, event.event_type = I.event_type, event.event_uid = I.event_uid, event.tenant_id = I.tenant_id" +
48   - " WHEN NOT MATCHED THEN INSERT (id, body, entity_id, entity_type, event_type, event_uid, tenant_id) VALUES (I.id, I.body, I.entity_id, I.entity_type, I.event_type, I.event_uid, I.tenant_id)";
  47 + return "MERGE INTO event USING (VALUES :id, :body, :entity_id, :entity_type, :event_type, :event_uid, :tenant_id, :ts) I (id, body, entity_id, entity_type, event_type, event_uid, tenant_id, ts) ON " + conflictStatement + " WHEN MATCHED THEN UPDATE SET event.id = I.id, event.body = I.body, event.entity_id = I.entity_id, event.entity_type = I.entity_type, event.event_type = I.event_type, event.event_uid = I.event_uid, event.tenant_id = I.tenant_id, event.ts = I.ts" +
  48 + " WHEN NOT MATCHED THEN INSERT (id, body, entity_id, entity_type, event_type, event_uid, tenant_id, ts) VALUES (I.id, I.body, I.entity_id, I.entity_type, I.event_type, I.event_uid, I.tenant_id, I.ts)";
49 49 }
50 50 }
\ No newline at end of file
... ...
... ... @@ -48,6 +48,6 @@ public class PsqlEventInsertRepository extends AbstractEventInsertRepository {
48 48 }
49 49
50 50 private static String getInsertOrUpdateString(String eventKeyStatement, String updateKeyStatement) {
51   - return "INSERT INTO event (id, body, entity_id, entity_type, event_type, event_uid, tenant_id) VALUES (:id, :body, :entity_id, :entity_type, :event_type, :event_uid, :tenant_id) ON CONFLICT " + eventKeyStatement + " DO UPDATE SET body = :body, " + updateKeyStatement + " returning *";
  51 + return "INSERT INTO event (id, body, entity_id, entity_type, event_type, event_uid, tenant_id, ts) VALUES (:id, :body, :entity_id, :entity_type, :event_type, :event_uid, :tenant_id, :ts) ON CONFLICT " + eventKeyStatement + " DO UPDATE SET body = :body, ts = :ts," + updateKeyStatement + " returning *";
52 52 }
53 53 }
\ No newline at end of file
... ...
... ... @@ -144,6 +144,7 @@ CREATE TABLE IF NOT EXISTS event (
144 144 event_type varchar(255),
145 145 event_uid varchar(255),
146 146 tenant_id varchar(31),
  147 + ts bigint NOT NULL,
147 148 CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid)
148 149 );
149 150
... ... @@ -251,3 +252,4 @@ CREATE TABLE IF NOT EXISTS entity_view (
251 252 search_text varchar(255),
252 253 additional_info varchar
253 254 );
  255 +
... ...
... ... @@ -144,6 +144,7 @@ CREATE TABLE IF NOT EXISTS event (
144 144 event_type varchar(255),
145 145 event_uid varchar(255),
146 146 tenant_id varchar(31),
  147 + ts bigint NOT NULL,
147 148 CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid)
148 149 );
149 150
... ... @@ -251,3 +252,28 @@ CREATE TABLE IF NOT EXISTS entity_view (
251 252 search_text varchar(255),
252 253 additional_info varchar
253 254 );
  255 +
  256 +CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)
  257 + LANGUAGE plpgsql AS
  258 +$$
  259 +DECLARE
  260 + ttl_ts bigint;
  261 + debug_ttl_ts bigint;
  262 + ttl_deleted_count bigint DEFAULT 0;
  263 + debug_ttl_deleted_count bigint DEFAULT 0;
  264 +BEGIN
  265 + IF ttl > 0 THEN
  266 + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
  267 + EXECUTE format(
  268 + 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type != %L::varchar AND event_type != %L::varchar) RETURNING *) SELECT count(*) FROM deleted', ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count;
  269 + END IF;
  270 + IF debug_ttl > 0 THEN
  271 + debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;
  272 + EXECUTE format(
  273 + 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type = %L::varchar OR event_type = %L::varchar) RETURNING *) SELECT count(*) FROM deleted', debug_ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count;
  274 + END IF;
  275 + RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
  276 + RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
  277 + deleted := ttl_deleted_count + debug_ttl_deleted_count;
  278 +END
  279 +$$;
... ...
... ... @@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS tb_schema_settings
52 52 CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
53 53 );
54 54
55   -INSERT INTO tb_schema_settings (schema_version) VALUES (2005000);
  55 +INSERT INTO tb_schema_settings (schema_version) VALUES (2005000) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005000;
56 56
57 57 CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
58 58 $$
... ...
... ... @@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS tb_schema_settings
52 52 CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
53 53 );
54 54
55   -INSERT INTO tb_schema_settings (schema_version) VALUES (2005000);
  55 +INSERT INTO tb_schema_settings (schema_version) VALUES (2005000) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005000;
56 56
57 57 CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint)
58 58 LANGUAGE plpgsql AS
... ...
... ... @@ -37,6 +37,9 @@ service.type=monolith
37 37 #spring.datasource.driverClassName=org.postgresql.Driver
38 38 #spring.datasource.hikari.maximumPoolSize = 50
39 39
  40 +queue.core.pack-processing-timeout=3000
  41 +queue.rule-engine.pack-processing-timeout=3000
  42 +
40 43 queue.rule-engine.queues[0].name=Main
41 44 queue.rule-engine.queues[0].topic=tb_rule_engine.main
42 45 queue.rule-engine.queues[0].poll-interval=25
... ...
... ... @@ -58,7 +58,7 @@ In case of any issues you can examine service logs for errors.
58 58 For example to see ThingsBoard node logs execute the following command:
59 59
60 60 `
61   -$ docker-compose logs -f tb1
  61 +$ docker-compose logs -f tb-core1 tb-rule-engine1
62 62 `
63 63
64 64 Or use `docker-compose ps` to see the state of all the containers.
... ...
... ... @@ -24,14 +24,28 @@ services:
24 24 - "9042"
25 25 volumes:
26 26 - ./tb-node/cassandra:/var/lib/cassandra
27   - tb1:
  27 + tb-core1:
28 28 env_file:
29 29 - tb-node.cassandra.env
30 30 depends_on:
31 31 - kafka
32 32 - redis
33 33 - cassandra
34   - tb2:
  34 + tb-core2:
  35 + env_file:
  36 + - tb-node.cassandra.env
  37 + depends_on:
  38 + - kafka
  39 + - redis
  40 + - cassandra
  41 + tb-rule-engine1:
  42 + env_file:
  43 + - tb-node.cassandra.env
  44 + depends_on:
  45 + - kafka
  46 + - redis
  47 + - cassandra
  48 + tb-rule-engine2:
35 49 env_file:
36 50 - tb-node.cassandra.env
37 51 depends_on:
... ...
... ... @@ -20,10 +20,16 @@ services:
20 20 postgres:
21 21 volumes:
22 22 - postgres-db-volume:/var/lib/postgresql/data
23   - tb1:
  23 + tb-core1:
24 24 volumes:
25 25 - tb-log-volume:/var/log/thingsboard
26   - tb2:
  26 + tb-core2:
  27 + volumes:
  28 + - tb-log-volume:/var/log/thingsboard
  29 + tb-rule-engine1:
  30 + volumes:
  31 + - tb-log-volume:/var/log/thingsboard
  32 + tb-rule-engine2:
27 33 volumes:
28 34 - tb-log-volume:/var/log/thingsboard
29 35 tb-coap-transport:
... ...
... ... @@ -27,14 +27,28 @@ services:
27 27 POSTGRES_PASSWORD: postgres
28 28 volumes:
29 29 - ./tb-node/postgres:/var/lib/postgresql/data
30   - tb1:
  30 + tb-core1:
31 31 env_file:
32 32 - tb-node.postgres.env
33 33 depends_on:
34 34 - kafka
35 35 - redis
36 36 - postgres
37   - tb2:
  37 + tb-core2:
  38 + env_file:
  39 + - tb-node.postgres.env
  40 + depends_on:
  41 + - kafka
  42 + - redis
  43 + - postgres
  44 + tb-rule-engine1:
  45 + env_file:
  46 + - tb-node.postgres.env
  47 + depends_on:
  48 + - kafka
  49 + - redis
  50 + - postgres
  51 + tb-rule-engine2:
38 52 env_file:
39 53 - tb-node.postgres.env
40 54 depends_on:
... ...
... ... @@ -48,7 +48,7 @@ services:
48 48 - tb-js-executor.env
49 49 depends_on:
50 50 - kafka
51   - tb1:
  51 + tb-core1:
52 52 restart: always
53 53 image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
54 54 ports:
... ... @@ -59,8 +59,8 @@ services:
59 59 max-size: "200m"
60 60 max-file: "30"
61 61 environment:
62   - TB_HOST: tb1
63   - CLUSTER_NODE_ID: tb1
  62 + TB_SERVICE_ID: tb-core1
  63 + TB_SERVICE_TYPE: tb-core
64 64 env_file:
65 65 - tb-node.env
66 66 volumes:
... ... @@ -70,7 +70,9 @@ services:
70 70 - kafka
71 71 - redis
72 72 - tb-js-executor
73   - tb2:
  73 + - tb-rule-engine1
  74 + - tb-rule-engine2
  75 + tb-core2:
74 76 restart: always
75 77 image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
76 78 ports:
... ... @@ -81,8 +83,54 @@ services:
81 83 max-size: "200m"
82 84 max-file: "30"
83 85 environment:
84   - TB_HOST: tb2
85   - CLUSTER_NODE_ID: tb2
  86 + TB_SERVICE_ID: tb-core2
  87 + TB_SERVICE_TYPE: tb-core
  88 + env_file:
  89 + - tb-node.env
  90 + volumes:
  91 + - ./tb-node/conf:/config
  92 + - ./tb-node/log:/var/log/thingsboard
  93 + depends_on:
  94 + - kafka
  95 + - redis
  96 + - tb-js-executor
  97 + - tb-rule-engine1
  98 + - tb-rule-engine2
  99 + tb-rule-engine1:
  100 + restart: always
  101 + image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
  102 + ports:
  103 + - "8080"
  104 + logging:
  105 + driver: "json-file"
  106 + options:
  107 + max-size: "200m"
  108 + max-file: "30"
  109 + environment:
  110 + TB_SERVICE_ID: tb-rule-engine1
  111 + TB_SERVICE_TYPE: tb-rule-engine
  112 + env_file:
  113 + - tb-node.env
  114 + volumes:
  115 + - ./tb-node/conf:/config
  116 + - ./tb-node/log:/var/log/thingsboard
  117 + depends_on:
  118 + - kafka
  119 + - redis
  120 + - tb-js-executor
  121 + tb-rule-engine2:
  122 + restart: always
  123 + image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
  124 + ports:
  125 + - "8080"
  126 + logging:
  127 + driver: "json-file"
  128 + options:
  129 + max-size: "200m"
  130 + max-file: "30"
  131 + environment:
  132 + TB_SERVICE_ID: tb-rule-engine2
  133 + TB_SERVICE_TYPE: tb-rule-engine
86 134 env_file:
87 135 - tb-node.env
88 136 volumes:
... ... @@ -98,8 +146,7 @@ services:
98 146 ports:
99 147 - "1883"
100 148 environment:
101   - TB_HOST: tb-mqtt-transport1
102   - CLUSTER_NODE_ID: tb-mqtt-transport1
  149 + TB_SERVICE_ID: tb-mqtt-transport1
103 150 env_file:
104 151 - tb-mqtt-transport.env
105 152 volumes:
... ... @@ -113,8 +160,7 @@ services:
113 160 ports:
114 161 - "1883"
115 162 environment:
116   - TB_HOST: tb-mqtt-transport2
117   - CLUSTER_NODE_ID: tb-mqtt-transport2
  163 + TB_SERVICE_ID: tb-mqtt-transport2
118 164 env_file:
119 165 - tb-mqtt-transport.env
120 166 volumes:
... ... @@ -128,8 +174,7 @@ services:
128 174 ports:
129 175 - "8081"
130 176 environment:
131   - TB_HOST: tb-http-transport1
132   - CLUSTER_NODE_ID: tb-http-transport1
  177 + TB_SERVICE_ID: tb-http-transport1
133 178 env_file:
134 179 - tb-http-transport.env
135 180 volumes:
... ... @@ -143,8 +188,7 @@ services:
143 188 ports:
144 189 - "8081"
145 190 environment:
146   - TB_HOST: tb-http-transport2
147   - CLUSTER_NODE_ID: tb-http-transport2
  191 + TB_SERVICE_ID: tb-http-transport2
148 192 env_file:
149 193 - tb-http-transport.env
150 194 volumes:
... ... @@ -158,8 +202,7 @@ services:
158 202 ports:
159 203 - "5683:5683/udp"
160 204 environment:
161   - TB_HOST: tb-coap-transport
162   - CLUSTER_NODE_ID: tb-coap-transport
  205 + TB_SERVICE_ID: tb-coap-transport
163 206 env_file:
164 207 - tb-coap-transport.env
165 208 volumes:
... ... @@ -202,8 +245,8 @@ services:
202 245 MQTT_PORT: 1883
203 246 FORCE_HTTPS_REDIRECT: "false"
204 247 links:
205   - - tb1
206   - - tb2
  248 + - tb-core1
  249 + - tb-core2
207 250 - tb-web-ui1
208 251 - tb-web-ui2
209 252 - tb-mqtt-transport1
... ...
... ... @@ -49,6 +49,6 @@ if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then
49 49 docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
50 50 fi
51 51
52   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=${loadDemo} tb1
  52 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=${loadDemo} tb-core1
53 53
54 54
... ...
... ... @@ -44,8 +44,8 @@ ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
44 44
45 45 ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $?
46 46
47   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull tb1
  47 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull tb-core1
48 48
49 49 docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
50 50
51   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb1
  51 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb-core1
... ...
... ... @@ -111,6 +111,6 @@ backend tb-api-backend
111 111 balance leastconn
112 112 option tcp-check
113 113 option log-health-checks
114   - server tbApi1 tb1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4
115   - server tbApi2 tb2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4
  114 + server tbApi1 tb-core1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4
  115 + server tbApi2 tb-core2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4
116 116 http-request set-header X-Forwarded-Port %[dst_port]
... ...
... ... @@ -4,7 +4,7 @@ KAFKA_LISTENERS=INSIDE://:9093,OUTSIDE://:9092
4 4 KAFKA_ADVERTISED_LISTENERS=INSIDE://:9093,OUTSIDE://kafka:9092
5 5 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
6 6 KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
7   -KAFKA_CREATE_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.rule-engine:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600
  7 +KAFKA_CREATE_TOPICS=js_eval.requests:3:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb_transport.api.requests:3:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600
8 8 KAFKA_AUTO_CREATE_TOPICS_ENABLE=false
9 9 KAFKA_LOG_RETENTION_BYTES=1073741824
10 10 KAFKA_LOG_SEGMENT_BYTES=268435456
... ...
  1 +ZOOKEEPER_ENABLED=true
  2 +ZOOKEEPER_URL=zookeeper:2181
1 3
2 4 COAP_BIND_ADDRESS=0.0.0.0
3 5 COAP_BIND_PORT=5683
4 6 COAP_TIMEOUT=10000
5 7
  8 +TB_QUEUE_TYPE=kafka
6 9 TB_KAFKA_SERVERS=kafka:9092
\ No newline at end of file
... ...
  1 +ZOOKEEPER_ENABLED=true
  2 +ZOOKEEPER_URL=zookeeper:2181
1 3
2 4 HTTP_BIND_ADDRESS=0.0.0.0
3 5 HTTP_BIND_PORT=8081
4 6 HTTP_REQUEST_TIMEOUT=60000
5 7
  8 +TB_QUEUE_TYPE=kafka
6 9 TB_KAFKA_SERVERS=kafka:9092
\ No newline at end of file
... ...
1 1
2   -REMOTE_JS_EVAL_REQUEST_TOPIC=js.eval.requests
  2 +REMOTE_JS_EVAL_REQUEST_TOPIC=js_eval.requests
3 3 TB_KAFKA_SERVERS=kafka:9092
4 4 LOGGER_LEVEL=info
5 5 LOG_FOLDER=logs
... ...
  1 +ZOOKEEPER_ENABLED=true
  2 +ZOOKEEPER_URL=zookeeper:2181
1 3
2 4 MQTT_BIND_ADDRESS=0.0.0.0
3 5 MQTT_BIND_PORT=1883
4 6 MQTT_TIMEOUT=10000
5 7
  8 +TB_QUEUE_TYPE=kafka
6 9 TB_KAFKA_SERVERS=kafka:9092
\ No newline at end of file
... ...
... ... @@ -2,7 +2,7 @@
2 2
3 3 ZOOKEEPER_ENABLED=true
4 4 ZOOKEEPER_URL=zookeeper:2181
5   -RPC_HOST=${TB_HOST}
  5 +TB_QUEUE_TYPE=kafka
6 6 TB_KAFKA_SERVERS=kafka:9092
7 7 JS_EVALUATOR=remote
8 8 TRANSPORT_TYPE=remote
... ...
... ... @@ -21,10 +21,10 @@
21 21
22 22 <appender name="fileLogAppender"
23 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24   - <file>/var/log/thingsboard/${TB_HOST}/thingsboard.log</file>
  24 + <file>/var/log/thingsboard/${TB_SERVICE_ID}/thingsboard.log</file>
25 25 <rollingPolicy
26 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27   - <fileNamePattern>/var/log/thingsboard/${TB_HOST}/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  27 + <fileNamePattern>/var/log/thingsboard/${TB_SERVICE_ID}/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 28 <maxFileSize>100MB</maxFileSize>
29 29 <maxHistory>30</maxHistory>
30 30 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -15,7 +15,7 @@
15 15 #
16 16
17 17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=deb -Dinstall.data_dir=/usr/share/thingsboard/data"
18   -export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  18 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
19 19 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
20 20 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
21 21 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ...
... ... @@ -21,10 +21,10 @@
21 21
22 22 <appender name="fileLogAppender"
23 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24   - <file>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.log</file>
  24 + <file>/var/log/tb-coap-transport/${TB_SERVICE_ID}/tb-coap-transport.log</file>
25 25 <rollingPolicy
26 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27   - <fileNamePattern>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  27 + <fileNamePattern>/var/log/tb-coap-transport/${TB_SERVICE_ID}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 28 <maxFileSize>100MB</maxFileSize>
29 29 <maxHistory>30</maxHistory>
30 30 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -14,7 +14,7 @@
14 14 # limitations under the License.
15 15 #
16 16
17   -export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
18 18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
19 19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
20 20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ...
... ... @@ -21,10 +21,10 @@
21 21
22 22 <appender name="fileLogAppender"
23 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24   - <file>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.log</file>
  24 + <file>/var/log/tb-http-transport/${TB_SERVICE_ID}/tb-http-transport.log</file>
25 25 <rollingPolicy
26 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27   - <fileNamePattern>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  27 + <fileNamePattern>/var/log/tb-http-transport/${TB_SERVICE_ID}/tb-http-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 28 <maxFileSize>100MB</maxFileSize>
29 29 <maxHistory>30</maxHistory>
30 30 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -14,7 +14,7 @@
14 14 # limitations under the License.
15 15 #
16 16
17   -export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
18 18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
19 19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
20 20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ...
... ... @@ -21,10 +21,10 @@
21 21
22 22 <appender name="fileLogAppender"
23 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24   - <file>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.log</file>
  24 + <file>/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/tb-mqtt-transport.log</file>
25 25 <rollingPolicy
26 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27   - <fileNamePattern>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  27 + <fileNamePattern>/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 28 <maxFileSize>100MB</maxFileSize>
29 29 <maxHistory>30</maxHistory>
30 30 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -14,7 +14,7 @@
14 14 # limitations under the License.
15 15 #
16 16
17   -export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
18 18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
19 19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
20 20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ...
... ... @@ -23,7 +23,7 @@ metadata:
23 23 name: tb-coap-transport-config
24 24 data:
25 25 conf: |
26   - export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  26 + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
27 27 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
28 28 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
29 29 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ... @@ -36,10 +36,10 @@ data:
36 36
37 37 <appender name="fileLogAppender"
38 38 class="ch.qos.logback.core.rolling.RollingFileAppender">
39   - <file>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.log</file>
  39 + <file>/var/log/tb-coap-transport/${TB_SERVICE_ID}/tb-coap-transport.log</file>
40 40 <rollingPolicy
41 41 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
42   - <fileNamePattern>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  42 + <fileNamePattern>/var/log/tb-coap-transport/${TB_SERVICE_ID}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
43 43 <maxFileSize>100MB</maxFileSize>
44 44 <maxHistory>30</maxHistory>
45 45 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -23,7 +23,7 @@ metadata:
23 23 name: tb-http-transport-config
24 24 data:
25 25 conf: |
26   - export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  26 + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
27 27 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
28 28 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
29 29 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ... @@ -36,10 +36,10 @@ data:
36 36
37 37 <appender name="fileLogAppender"
38 38 class="ch.qos.logback.core.rolling.RollingFileAppender">
39   - <file>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.log</file>
  39 + <file>/var/log/tb-http-transport/${TB_SERVICE_ID}/tb-http-transport.log</file>
40 40 <rollingPolicy
41 41 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
42   - <fileNamePattern>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  42 + <fileNamePattern>/var/log/tb-http-transport/${TB_SERVICE_ID}/tb-http-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
43 43 <maxFileSize>100MB</maxFileSize>
44 44 <maxHistory>30</maxHistory>
45 45 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -23,7 +23,7 @@ metadata:
23 23 name: tb-mqtt-transport-config
24 24 data:
25 25 conf: |
26   - export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  26 + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
27 27 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
28 28 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
29 29 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ... @@ -36,10 +36,10 @@ data:
36 36
37 37 <appender name="fileLogAppender"
38 38 class="ch.qos.logback.core.rolling.RollingFileAppender">
39   - <file>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.log</file>
  39 + <file>/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/tb-mqtt-transport.log</file>
40 40 <rollingPolicy
41 41 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
42   - <fileNamePattern>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  42 + <fileNamePattern>/var/log/tb-mqtt-transport/${TB_SERVICE_ID}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
43 43 <maxFileSize>100MB</maxFileSize>
44 44 <maxHistory>30</maxHistory>
45 45 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -24,7 +24,7 @@ metadata:
24 24 data:
25 25 conf: |
26 26 export JAVA_OPTS="$JAVA_OPTS -Dplatform=deb -Dinstall.data_dir=/usr/share/thingsboard/data"
27   - export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
  27 + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_SERVICE_ID}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_SERVICE_ID}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
28 28 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
29 29 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
30 30 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
... ... @@ -37,10 +37,10 @@ data:
37 37
38 38 <appender name="fileLogAppender"
39 39 class="ch.qos.logback.core.rolling.RollingFileAppender">
40   - <file>/var/log/thingsboard/${TB_HOST}/thingsboard.log</file>
  40 + <file>/var/log/thingsboard/${TB_SERVICE_ID}/thingsboard.log</file>
41 41 <rollingPolicy
42 42 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
43   - <fileNamePattern>/var/log/thingsboard/${TB_HOST}/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  43 + <fileNamePattern>/var/log/thingsboard/${TB_SERVICE_ID}/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
44 44 <maxFileSize>100MB</maxFileSize>
45 45 <maxHistory>30</maxHistory>
46 46 <totalSizeCap>3GB</totalSizeCap>
... ...
... ... @@ -247,18 +247,12 @@ spec:
247 247 - containerPort: 9001
248 248 name: rpc
249 249 env:
250   - - name: RPC_HOST
251   - valueFrom:
252   - fieldRef:
253   - fieldPath: status.podIP
254   - - name: CLUSTER_NODE_ID
255   - valueFrom:
256   - fieldRef:
257   - fieldPath: metadata.name
258   - - name: TB_HOST
  250 + - name: TB_SERVICE_ID
259 251 valueFrom:
260 252 fieldRef:
261 253 fieldPath: metadata.name
  254 + - name: TB_SERVICE_TYPE
  255 + value: "monolith"
262 256 - name: ZOOKEEPER_ENABLED
263 257 value: "true"
264 258 - name: ZOOKEEPER_URL
... ... @@ -334,14 +328,12 @@ spec:
334 328 - containerPort: 1883
335 329 name: mqtt
336 330 env:
337   - - name: CLUSTER_NODE_ID
338   - valueFrom:
339   - fieldRef:
340   - fieldPath: metadata.name
341   - - name: TB_HOST
  331 + - name: TB_SERVICE_ID
342 332 valueFrom:
343 333 fieldRef:
344 334 fieldPath: metadata.name
  335 + - name: TB_SERVICE_TYPE
  336 + value: "monolith"
345 337 - name: MQTT_BIND_ADDRESS
346 338 value: "0.0.0.0"
347 339 - name: MQTT_BIND_PORT
... ... @@ -409,14 +401,12 @@ spec:
409 401 - containerPort: 8080
410 402 name: http
411 403 env:
412   - - name: CLUSTER_NODE_ID
413   - valueFrom:
414   - fieldRef:
415   - fieldPath: metadata.name
416   - - name: TB_HOST
  404 + - name: TB_SERVICE_ID
417 405 valueFrom:
418 406 fieldRef:
419 407 fieldPath: metadata.name
  408 + - name: TB_SERVICE_TYPE
  409 + value: "monolith"
420 410 - name: HTTP_BIND_ADDRESS
421 411 value: "0.0.0.0"
422 412 - name: HTTP_BIND_PORT
... ... @@ -484,14 +474,12 @@ spec:
484 474 name: coap
485 475 protocol: UDP
486 476 env:
487   - - name: CLUSTER_NODE_ID
488   - valueFrom:
489   - fieldRef:
490   - fieldPath: metadata.name
491   - - name: TB_HOST
  477 + - name: TB_SERVICE_ID
492 478 valueFrom:
493 479 fieldRef:
494 480 fieldPath: metadata.name
  481 + - name: TB_SERVICE_TYPE
  482 + value: "monolith"
495 483 - name: COAP_BIND_ADDRESS
496 484 value: "0.0.0.0"
497 485 - name: COAP_BIND_PORT
... ...
... ... @@ -92,7 +92,7 @@ public class ThingsBoardDbInstaller extends ExternalResource {
92 92 dockerCompose.withCommand("up -d redis postgres");
93 93 dockerCompose.invokeCompose();
94 94
95   - dockerCompose.withCommand("run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=true tb1");
  95 + dockerCompose.withCommand("run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=true tb-core1");
96 96 dockerCompose.invokeCompose();
97 97
98 98 } finally {
... ...
... ... @@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.TbMsg;
30 30
31 31 @Slf4j
32 32 @RuleNode(type = ComponentType.ENRICHMENT,
33   - name = "device attributes",
  33 + name = "related device attributes",
34 34 configClazz = TbGetDeviceAttrNodeConfiguration.class,
35 35 nodeDescription = "Add Originators Related Device Attributes and Latest Telemetry value into Message Metadata",
36 36 nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " +
... ...
... ... @@ -33,7 +33,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
33 33 type = ComponentType.ACTION,
34 34 name = "synchronization start",
35 35 configClazz = EmptyNodeConfiguration.class,
36   - nodeDescription = "Starts synchronization of message processing based on message originator",
  36 + nodeDescription = "This Node is now deprecated. Use \"Checkpoint\" instead.",
37 37 nodeDetails = "This node should be used together with \"synchronization end\" node. \n This node will put messages into queue based on message originator id. \n" +
38 38 "Subsequent messages will not be processed until the previous message processing is completed or timeout event occurs.\n" +
39 39 "Size of the queue per originator and timeout values are configurable on a system level",
... ...
... ... @@ -35,7 +35,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
35 35 type = ComponentType.ACTION,
36 36 name = "synchronization end",
37 37 configClazz = EmptyNodeConfiguration.class,
38   - nodeDescription = "Stops synchronization of message processing based on message originator",
  38 + nodeDescription = "This Node is now deprecated. Use \"Checkpoint\" instead.",
39 39 nodeDetails = "",
40 40 uiResources = {"static/rulenode/rulenode-core-config.js"},
41 41 configDirective = ("tbNodeEmptyConfig")
... ...