Showing
53 changed files
with
3870 additions
and
166 deletions
Too many changes to show.
To preserve performance only 53 of 80 files are displayed.
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 = '⬤';\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; | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -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 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/queue/TbPackProcessingContext.java
0 → 100644
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 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java
0 → 100644
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: | ... | ... |