Showing
13 changed files
with
3507 additions
and
700 deletions
Too many changes to show.
To preserve performance only 13 of 484 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 | +$$; | ... | ... |
... | ... | @@ -37,7 +37,6 @@ import org.springframework.data.redis.core.RedisTemplate; |
37 | 37 | import org.springframework.scheduling.annotation.Scheduled; |
38 | 38 | import org.springframework.stereotype.Component; |
39 | 39 | import org.thingsboard.rule.engine.api.MailService; |
40 | -import org.thingsboard.rule.engine.api.RuleChainTransactionService; | |
41 | 40 | import org.thingsboard.server.actors.service.ActorService; |
42 | 41 | import org.thingsboard.server.actors.tenant.DebugTbRateLimits; |
43 | 42 | import org.thingsboard.server.common.data.DataConstants; |
... | ... | @@ -45,10 +44,11 @@ import org.thingsboard.server.common.data.Event; |
45 | 44 | import org.thingsboard.server.common.data.id.EntityId; |
46 | 45 | import org.thingsboard.server.common.data.id.TenantId; |
47 | 46 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
47 | +import org.thingsboard.server.common.msg.TbActorMsg; | |
48 | 48 | import org.thingsboard.server.common.msg.TbMsg; |
49 | -import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
49 | +import org.thingsboard.server.common.msg.queue.ServiceType; | |
50 | +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | |
50 | 51 | import org.thingsboard.server.common.msg.tools.TbRateLimits; |
51 | -import org.thingsboard.server.common.transport.auth.DeviceAuthService; | |
52 | 52 | import org.thingsboard.server.dao.alarm.AlarmService; |
53 | 53 | import org.thingsboard.server.dao.asset.AssetService; |
54 | 54 | import org.thingsboard.server.dao.attributes.AttributesService; |
... | ... | @@ -65,24 +65,23 @@ import org.thingsboard.server.dao.rule.RuleChainService; |
65 | 65 | import org.thingsboard.server.dao.tenant.TenantService; |
66 | 66 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
67 | 67 | import org.thingsboard.server.dao.user.UserService; |
68 | -import org.thingsboard.server.kafka.TbNodeIdProvider; | |
69 | -import org.thingsboard.server.service.cluster.discovery.DiscoveryService; | |
70 | -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; | |
71 | -import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; | |
68 | +import org.thingsboard.server.queue.discovery.PartitionService; | |
69 | +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; | |
72 | 70 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
73 | 71 | import org.thingsboard.server.service.encoding.DataDecodingEncodingService; |
74 | -import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService; | |
75 | 72 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
76 | 73 | import org.thingsboard.server.service.executors.ExternalCallExecutorService; |
77 | 74 | import org.thingsboard.server.service.executors.SharedEventLoopGroupService; |
78 | 75 | import org.thingsboard.server.service.mail.MailExecutorService; |
79 | -import org.thingsboard.server.service.rpc.DeviceRpcService; | |
76 | +import org.thingsboard.server.service.queue.TbClusterService; | |
77 | +import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; | |
78 | +import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; | |
80 | 79 | import org.thingsboard.server.service.script.JsExecutorService; |
81 | 80 | import org.thingsboard.server.service.script.JsInvokeService; |
82 | 81 | import org.thingsboard.server.service.session.DeviceSessionCacheService; |
83 | 82 | import org.thingsboard.server.service.state.DeviceStateService; |
84 | 83 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
85 | -import org.thingsboard.server.service.transport.RuleEngineTransportService; | |
84 | +import org.thingsboard.server.service.transport.TbCoreToTransportService; | |
86 | 85 | |
87 | 86 | import javax.annotation.Nullable; |
88 | 87 | import java.io.IOException; |
... | ... | @@ -106,13 +105,14 @@ public class ActorSystemContext { |
106 | 105 | return debugPerTenantLimits; |
107 | 106 | } |
108 | 107 | |
108 | + @Autowired | |
109 | 109 | @Getter |
110 | 110 | @Setter |
111 | - private ActorService actorService; | |
111 | + private TbServiceInfoProvider serviceInfoProvider; | |
112 | 112 | |
113 | - @Autowired | |
114 | 113 | @Getter |
115 | - private DiscoveryService discoveryService; | |
114 | + @Setter | |
115 | + private ActorService actorService; | |
116 | 116 | |
117 | 117 | @Autowired |
118 | 118 | @Getter |
... | ... | @@ -121,22 +121,10 @@ public class ActorSystemContext { |
121 | 121 | |
122 | 122 | @Autowired |
123 | 123 | @Getter |
124 | - private ClusterRoutingService routingService; | |
125 | - | |
126 | - @Autowired | |
127 | - @Getter | |
128 | - private ClusterRpcService rpcService; | |
129 | - | |
130 | - @Autowired | |
131 | - @Getter | |
132 | 124 | private DataDecodingEncodingService encodingService; |
133 | 125 | |
134 | 126 | @Autowired |
135 | 127 | @Getter |
136 | - private DeviceAuthService deviceAuthService; | |
137 | - | |
138 | - @Autowired | |
139 | - @Getter | |
140 | 128 | private DeviceService deviceService; |
141 | 129 | |
142 | 130 | @Autowired |
... | ... | @@ -164,6 +152,13 @@ public class ActorSystemContext { |
164 | 152 | private RuleChainService ruleChainService; |
165 | 153 | |
166 | 154 | @Autowired |
155 | + private PartitionService partitionService; | |
156 | + | |
157 | + @Autowired | |
158 | + @Getter | |
159 | + private TbClusterService clusterService; | |
160 | + | |
161 | + @Autowired | |
167 | 162 | @Getter |
168 | 163 | private TimeseriesService tsService; |
169 | 164 | |
... | ... | @@ -197,10 +192,6 @@ public class ActorSystemContext { |
197 | 192 | |
198 | 193 | @Autowired |
199 | 194 | @Getter |
200 | - private DeviceRpcService deviceRpcService; | |
201 | - | |
202 | - @Autowired | |
203 | - @Getter | |
204 | 195 | private JsInvokeService jsSandbox; |
205 | 196 | |
206 | 197 | @Autowired |
... | ... | @@ -213,10 +204,6 @@ public class ActorSystemContext { |
213 | 204 | |
214 | 205 | @Autowired |
215 | 206 | @Getter |
216 | - private ClusterRpcCallbackExecutorService clusterRpcCallbackExecutor; | |
217 | - | |
218 | - @Autowired | |
219 | - @Getter | |
220 | 207 | private DbCallbackExecutorService dbCallbackExecutor; |
221 | 208 | |
222 | 209 | @Autowired |
... | ... | @@ -231,27 +218,34 @@ public class ActorSystemContext { |
231 | 218 | @Getter |
232 | 219 | private MailService mailService; |
233 | 220 | |
234 | - @Autowired | |
221 | + //TODO: separate context for TbCore and TbRuleEngine | |
222 | + @Autowired(required = false) | |
235 | 223 | @Getter |
236 | 224 | private DeviceStateService deviceStateService; |
237 | 225 | |
238 | - @Autowired | |
226 | + @Autowired(required = false) | |
239 | 227 | @Getter |
240 | 228 | private DeviceSessionCacheService deviceSessionCacheService; |
241 | 229 | |
242 | - @Lazy | |
243 | - @Autowired | |
230 | + @Autowired(required = false) | |
244 | 231 | @Getter |
245 | - private RuleEngineTransportService ruleEngineTransportService; | |
232 | + private TbCoreToTransportService tbCoreToTransportService; | |
246 | 233 | |
234 | + /** | |
235 | + * The following Service will be null if we operate in tb-core mode | |
236 | + */ | |
247 | 237 | @Lazy |
248 | - @Autowired | |
238 | + @Autowired(required = false) | |
249 | 239 | @Getter |
250 | - private RuleChainTransactionService ruleChainTransactionService; | |
240 | + private TbRuleEngineDeviceRpcService tbRuleEngineDeviceRpcService; | |
251 | 241 | |
252 | - @Value("${cluster.partition_id}") | |
242 | + /** | |
243 | + * The following Service will be null if we operate in tb-rule-engine mode | |
244 | + */ | |
245 | + @Lazy | |
246 | + @Autowired(required = false) | |
253 | 247 | @Getter |
254 | - private long queuePartitionId; | |
248 | + private TbCoreDeviceRpcService tbCoreDeviceRpcService; | |
255 | 249 | |
256 | 250 | @Value("${actors.session.max_concurrent_sessions_per_device:1}") |
257 | 251 | @Getter |
... | ... | @@ -269,10 +263,6 @@ public class ActorSystemContext { |
269 | 263 | @Getter |
270 | 264 | private long queuePersistenceTimeout; |
271 | 265 | |
272 | - @Value("${actors.client_side_rpc.timeout}") | |
273 | - @Getter | |
274 | - private long clientSideRpcTimeout; | |
275 | - | |
276 | 266 | @Value("${actors.rule.chain.error_persist_frequency}") |
277 | 267 | @Getter |
278 | 268 | private long ruleChainErrorPersistFrequency; |
... | ... | @@ -334,11 +324,6 @@ public class ActorSystemContext { |
334 | 324 | @Setter |
335 | 325 | private ActorSystem actorSystem; |
336 | 326 | |
337 | - @Autowired | |
338 | - @Getter | |
339 | - private TbNodeIdProvider nodeIdProvider; | |
340 | - | |
341 | - @Getter | |
342 | 327 | @Setter |
343 | 328 | private ActorRef appActor; |
344 | 329 | |
... | ... | @@ -365,6 +350,8 @@ public class ActorSystemContext { |
365 | 350 | config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); |
366 | 351 | } |
367 | 352 | |
353 | + | |
354 | + | |
368 | 355 | public Scheduler getScheduler() { |
369 | 356 | return actorSystem.scheduler(); |
370 | 357 | } |
... | ... | @@ -374,7 +361,7 @@ public class ActorSystemContext { |
374 | 361 | event.setTenantId(tenantId); |
375 | 362 | event.setEntityId(entityId); |
376 | 363 | event.setType(DataConstants.ERROR); |
377 | - event.setBody(toBodyJson(discoveryService.getCurrentServer().getServerAddress(), method, toString(e))); | |
364 | + event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), method, toString(e))); | |
378 | 365 | persistEvent(event); |
379 | 366 | } |
380 | 367 | |
... | ... | @@ -383,7 +370,7 @@ public class ActorSystemContext { |
383 | 370 | event.setTenantId(tenantId); |
384 | 371 | event.setEntityId(entityId); |
385 | 372 | event.setType(DataConstants.LC_EVENT); |
386 | - event.setBody(toBodyJson(discoveryService.getCurrentServer().getServerAddress(), lcEvent, Optional.ofNullable(e))); | |
373 | + event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), lcEvent, Optional.ofNullable(e))); | |
387 | 374 | persistEvent(event); |
388 | 375 | } |
389 | 376 | |
... | ... | @@ -397,8 +384,8 @@ public class ActorSystemContext { |
397 | 384 | return sw.toString(); |
398 | 385 | } |
399 | 386 | |
400 | - private JsonNode toBodyJson(ServerAddress server, ComponentLifecycleEvent event, Optional<Exception> e) { | |
401 | - ObjectNode node = mapper.createObjectNode().put("server", server.toString()).put("event", event.name()); | |
387 | + private JsonNode toBodyJson(String serviceId, ComponentLifecycleEvent event, Optional<Exception> e) { | |
388 | + ObjectNode node = mapper.createObjectNode().put("server", serviceId).put("event", event.name()); | |
402 | 389 | if (e.isPresent()) { |
403 | 390 | node = node.put("success", false); |
404 | 391 | node = node.put("error", toString(e.get())); |
... | ... | @@ -408,12 +395,21 @@ public class ActorSystemContext { |
408 | 395 | return node; |
409 | 396 | } |
410 | 397 | |
411 | - private JsonNode toBodyJson(ServerAddress server, String method, String body) { | |
412 | - return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body); | |
398 | + private JsonNode toBodyJson(String serviceId, String method, String body) { | |
399 | + return mapper.createObjectNode().put("server", serviceId).put("method", method).put("error", body); | |
413 | 400 | } |
414 | 401 | |
415 | - public String getServerAddress() { | |
416 | - return discoveryService.getCurrentServer().getServerAddress().toString(); | |
402 | + public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) { | |
403 | + return partitionService.resolve(serviceType, tenantId, entityId); | |
404 | + } | |
405 | + | |
406 | + public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) { | |
407 | + return partitionService.resolve(serviceType, queueName, tenantId, entityId); | |
408 | + } | |
409 | + | |
410 | + | |
411 | + public String getServiceId() { | |
412 | + return serviceInfoProvider.getServiceId(); | |
417 | 413 | } |
418 | 414 | |
419 | 415 | public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) { |
... | ... | @@ -444,7 +440,7 @@ public class ActorSystemContext { |
444 | 440 | |
445 | 441 | ObjectNode node = mapper.createObjectNode() |
446 | 442 | .put("type", type) |
447 | - .put("server", getServerAddress()) | |
443 | + .put("server", getServiceId()) | |
448 | 444 | .put("entityId", tbMsg.getOriginator().getId().toString()) |
449 | 445 | .put("entityName", tbMsg.getOriginator().getEntityType().name()) |
450 | 446 | .put("msgId", tbMsg.getId().toString()) |
... | ... | @@ -504,7 +500,7 @@ public class ActorSystemContext { |
504 | 500 | |
505 | 501 | ObjectNode node = mapper.createObjectNode() |
506 | 502 | //todo: what fields are needed here? |
507 | - .put("server", getServerAddress()) | |
503 | + .put("server", getServiceId()) | |
508 | 504 | .put("message", "Reached debug mode rate limit!"); |
509 | 505 | |
510 | 506 | if (error != null) { |
... | ... | @@ -530,4 +526,7 @@ public class ActorSystemContext { |
530 | 526 | return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); |
531 | 527 | } |
532 | 528 | |
529 | + public void tell(TbActorMsg tbActorMsg, ActorRef sender) { | |
530 | + appActor.tell(tbActorMsg, sender); | |
531 | + } | |
533 | 532 | } | ... | ... |
... | ... | @@ -20,19 +20,13 @@ import akka.actor.LocalActorRef; |
20 | 20 | import akka.actor.OneForOneStrategy; |
21 | 21 | import akka.actor.Props; |
22 | 22 | import akka.actor.SupervisorStrategy; |
23 | -import akka.actor.SupervisorStrategy.Directive; | |
24 | 23 | import akka.actor.Terminated; |
25 | -import akka.event.Logging; | |
26 | -import akka.event.LoggingAdapter; | |
27 | -import akka.japi.Function; | |
28 | 24 | import com.google.common.collect.BiMap; |
29 | 25 | import com.google.common.collect.HashBiMap; |
30 | -import lombok.extern.slf4j.Slf4j; | |
31 | 26 | import org.thingsboard.server.actors.ActorSystemContext; |
32 | -import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; | |
27 | +import org.thingsboard.server.actors.service.ContextAwareActor; | |
33 | 28 | import org.thingsboard.server.actors.service.ContextBasedCreator; |
34 | 29 | import org.thingsboard.server.actors.service.DefaultActorService; |
35 | -import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager; | |
36 | 30 | import org.thingsboard.server.actors.tenant.TenantActor; |
37 | 31 | import org.thingsboard.server.common.data.EntityType; |
38 | 32 | import org.thingsboard.server.common.data.Tenant; |
... | ... | @@ -42,29 +36,32 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
42 | 36 | import org.thingsboard.server.common.msg.MsgType; |
43 | 37 | import org.thingsboard.server.common.msg.TbActorMsg; |
44 | 38 | import org.thingsboard.server.common.msg.aware.TenantAwareMsg; |
45 | -import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | |
46 | -import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
47 | 39 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
48 | -import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | |
40 | +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; | |
41 | +import org.thingsboard.server.common.msg.queue.RuleEngineException; | |
42 | +import org.thingsboard.server.common.msg.queue.ServiceType; | |
49 | 43 | import org.thingsboard.server.dao.model.ModelConstants; |
50 | 44 | import org.thingsboard.server.dao.tenant.TenantService; |
45 | +import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; | |
51 | 46 | import scala.concurrent.duration.Duration; |
52 | 47 | |
53 | -import java.util.HashMap; | |
54 | -import java.util.Map; | |
48 | +import java.util.HashSet; | |
55 | 49 | import java.util.Optional; |
50 | +import java.util.Set; | |
56 | 51 | |
57 | -public class AppActor extends RuleChainManagerActor { | |
52 | +public class AppActor extends ContextAwareActor { | |
58 | 53 | |
59 | 54 | private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); |
60 | 55 | private final TenantService tenantService; |
61 | 56 | private final BiMap<TenantId, ActorRef> tenantActors; |
57 | + private final Set<TenantId> deletedTenants; | |
62 | 58 | private boolean ruleChainsInitialized; |
63 | 59 | |
64 | 60 | private AppActor(ActorSystemContext systemContext) { |
65 | - super(systemContext, new SystemRuleChainManager(systemContext)); | |
61 | + super(systemContext); | |
66 | 62 | this.tenantService = systemContext.getTenantService(); |
67 | 63 | this.tenantActors = HashBiMap.create(); |
64 | + this.deletedTenants = new HashSet<>(); | |
68 | 65 | } |
69 | 66 | |
70 | 67 | @Override |
... | ... | @@ -79,7 +76,7 @@ public class AppActor extends RuleChainManagerActor { |
79 | 76 | @Override |
80 | 77 | protected boolean process(TbActorMsg msg) { |
81 | 78 | if (!ruleChainsInitialized) { |
82 | - initRuleChainsAndTenantActors(); | |
79 | + initTenantActors(); | |
83 | 80 | ruleChainsInitialized = true; |
84 | 81 | if (msg.getMsgType() != MsgType.APP_INIT_MSG) { |
85 | 82 | log.warn("Rule Chains initialized by unexpected message: {}", msg); |
... | ... | @@ -88,17 +85,14 @@ public class AppActor extends RuleChainManagerActor { |
88 | 85 | switch (msg.getMsgType()) { |
89 | 86 | case APP_INIT_MSG: |
90 | 87 | break; |
91 | - case SEND_TO_CLUSTER_MSG: | |
92 | - onPossibleClusterMsg((SendToClusterMsg) msg); | |
93 | - break; | |
94 | - case CLUSTER_EVENT_MSG: | |
88 | + case PARTITION_CHANGE_MSG: | |
95 | 89 | broadcast(msg); |
96 | 90 | break; |
97 | 91 | case COMPONENT_LIFE_CYCLE_MSG: |
98 | 92 | onComponentLifecycleMsg((ComponentLifecycleMsg) msg); |
99 | 93 | break; |
100 | - case SERVICE_TO_RULE_ENGINE_MSG: | |
101 | - onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); | |
94 | + case QUEUE_TO_RULE_ENGINE_MSG: | |
95 | + onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); | |
102 | 96 | break; |
103 | 97 | case TRANSPORT_TO_DEVICE_ACTOR_MSG: |
104 | 98 | case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: |
... | ... | @@ -106,7 +100,6 @@ public class AppActor extends RuleChainManagerActor { |
106 | 100 | case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: |
107 | 101 | case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: |
108 | 102 | case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: |
109 | - case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG: | |
110 | 103 | onToDeviceActorMsg((TenantAwareMsg) msg); |
111 | 104 | break; |
112 | 105 | default: |
... | ... | @@ -115,16 +108,30 @@ public class AppActor extends RuleChainManagerActor { |
115 | 108 | return true; |
116 | 109 | } |
117 | 110 | |
118 | - private void initRuleChainsAndTenantActors() { | |
111 | + private void initTenantActors() { | |
119 | 112 | log.info("Starting main system actor."); |
120 | 113 | try { |
121 | - initRuleChains(); | |
122 | - if (systemContext.isTenantComponentsInitEnabled()) { | |
123 | - PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); | |
124 | - for (Tenant tenant : tenantIterator) { | |
114 | + // This Service may be started for specific tenant only. | |
115 | + Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant(); | |
116 | + if (isolatedTenantId.isPresent()) { | |
117 | + Tenant tenant = systemContext.getTenantService().findTenantById(isolatedTenantId.get()); | |
118 | + if (tenant != null) { | |
125 | 119 | log.debug("[{}] Creating tenant actor", tenant.getId()); |
126 | 120 | getOrCreateTenantActor(tenant.getId()); |
127 | 121 | log.debug("Tenant actor created."); |
122 | + } else { | |
123 | + log.error("[{}] Tenant with such ID does not exist", isolatedTenantId.get()); | |
124 | + } | |
125 | + } else if (systemContext.isTenantComponentsInitEnabled()) { | |
126 | + PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); | |
127 | + boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); | |
128 | + boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); | |
129 | + for (Tenant tenant : tenantIterator) { | |
130 | + if (isCore || (isRuleEngine && !tenant.isIsolatedTbRuleEngine())) { | |
131 | + log.debug("[{}] Creating tenant actor", tenant.getId()); | |
132 | + getOrCreateTenantActor(tenant.getId()); | |
133 | + log.debug("[{}] Tenant actor created.", tenant.getId()); | |
134 | + } | |
128 | 135 | } |
129 | 136 | } |
130 | 137 | log.info("Main system actor started."); |
... | ... | @@ -133,40 +140,33 @@ public class AppActor extends RuleChainManagerActor { |
133 | 140 | } |
134 | 141 | } |
135 | 142 | |
136 | - private void onPossibleClusterMsg(SendToClusterMsg msg) { | |
137 | - Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId()); | |
138 | - if (address.isPresent()) { | |
139 | - systemContext.getRpcService().tell( | |
140 | - systemContext.getEncodingService().convertToProtoDataMessage(address.get(), msg.getMsg())); | |
141 | - } else { | |
142 | - self().tell(msg.getMsg(), ActorRef.noSender()); | |
143 | - } | |
144 | - } | |
145 | - | |
146 | - private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { | |
143 | + private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) { | |
147 | 144 | if (SYSTEM_TENANT.equals(msg.getTenantId())) { |
148 | -// this may be a notification about system entities created. | |
149 | -// log.warn("[{}] Invalid service to rule engine msg called. System messages are not supported yet: {}", SYSTEM_TENANT, msg); | |
145 | + msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); | |
150 | 146 | } else { |
151 | - getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); | |
147 | + if (!deletedTenants.contains(msg.getTenantId())) { | |
148 | + getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); | |
149 | + } else { | |
150 | + msg.getTbMsg().getCallback().onSuccess(); | |
151 | + } | |
152 | 152 | } |
153 | 153 | } |
154 | 154 | |
155 | - @Override | |
156 | 155 | protected void broadcast(Object msg) { |
157 | - super.broadcast(msg); | |
158 | 156 | tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); |
159 | 157 | } |
160 | 158 | |
161 | 159 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { |
162 | 160 | ActorRef target = null; |
163 | 161 | if (SYSTEM_TENANT.equals(msg.getTenantId())) { |
164 | - target = getEntityActorRef(msg.getEntityId()); | |
162 | + log.warn("Message has system tenant id: {}", msg); | |
165 | 163 | } else { |
166 | 164 | if (msg.getEntityId().getEntityType() == EntityType.TENANT |
167 | 165 | && msg.getEvent() == ComponentLifecycleEvent.DELETED) { |
168 | - log.debug("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); | |
169 | - ActorRef tenantActor = tenantActors.remove(new TenantId(msg.getEntityId().getId())); | |
166 | + log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); | |
167 | + TenantId tenantId = new TenantId(msg.getEntityId().getId()); | |
168 | + deletedTenants.add(tenantId); | |
169 | + ActorRef tenantActor = tenantActors.get(tenantId); | |
170 | 170 | if (tenantActor != null) { |
171 | 171 | log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor); |
172 | 172 | context().stop(tenantActor); |
... | ... | @@ -183,16 +183,22 @@ public class AppActor extends RuleChainManagerActor { |
183 | 183 | } |
184 | 184 | |
185 | 185 | private void onToDeviceActorMsg(TenantAwareMsg msg) { |
186 | - getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); | |
186 | + if (!deletedTenants.contains(msg.getTenantId())) { | |
187 | + getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); | |
188 | + } else { | |
189 | + if (msg instanceof TransportToDeviceActorMsgWrapper) { | |
190 | + ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess(); | |
191 | + } | |
192 | + } | |
187 | 193 | } |
188 | 194 | |
189 | 195 | private ActorRef getOrCreateTenantActor(TenantId tenantId) { |
190 | 196 | return tenantActors.computeIfAbsent(tenantId, k -> { |
191 | - log.debug("[{}] Creating tenant actor.", tenantId); | |
197 | + log.info("[{}] Creating tenant actor.", tenantId); | |
192 | 198 | ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId)) |
193 | 199 | .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()); |
194 | 200 | context().watch(tenantActor); |
195 | - log.debug("[{}] Created tenant actor: {}.", tenantId, tenantActor); | |
201 | + log.info("[{}] Created tenant actor: {}.", tenantId, tenantActor); | |
196 | 202 | return tenantActor; |
197 | 203 | }); |
198 | 204 | } | ... | ... |
... | ... | @@ -22,10 +22,8 @@ import org.thingsboard.server.actors.service.ContextAwareActor; |
22 | 22 | import org.thingsboard.server.common.data.id.DeviceId; |
23 | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 24 | import org.thingsboard.server.common.msg.TbActorMsg; |
25 | -import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; | |
26 | 25 | import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; |
27 | 26 | import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; |
28 | -import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; | |
29 | 27 | import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; |
30 | 28 | |
31 | 29 | public class DeviceActor extends ContextAwareActor { |
... | ... | @@ -49,6 +47,11 @@ public class DeviceActor extends ContextAwareActor { |
49 | 47 | } |
50 | 48 | |
51 | 49 | @Override |
50 | + public void postStop() { | |
51 | + | |
52 | + } | |
53 | + | |
54 | + @Override | |
52 | 55 | protected boolean process(TbActorMsg msg) { |
53 | 56 | switch (msg.getMsgType()) { |
54 | 57 | case TRANSPORT_TO_DEVICE_ACTOR_MSG: |
... | ... | @@ -66,15 +69,9 @@ public class DeviceActor extends ContextAwareActor { |
66 | 69 | case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: |
67 | 70 | processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg); |
68 | 71 | break; |
69 | - case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: | |
70 | - processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg); | |
71 | - break; | |
72 | 72 | case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG: |
73 | 73 | processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg); |
74 | 74 | break; |
75 | - case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG: | |
76 | - processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg); | |
77 | - break; | |
78 | 75 | case SESSION_TIMEOUT_MSG: |
79 | 76 | processor.checkSessionsTimeout(); |
80 | 77 | break; | ... | ... |
... | ... | @@ -16,13 +16,10 @@ |
16 | 16 | package org.thingsboard.server.actors.device; |
17 | 17 | |
18 | 18 | import akka.actor.ActorContext; |
19 | -import com.datastax.driver.core.utils.UUIDs; | |
20 | 19 | import com.google.common.util.concurrent.FutureCallback; |
21 | 20 | import com.google.common.util.concurrent.Futures; |
22 | 21 | import com.google.common.util.concurrent.ListenableFuture; |
23 | 22 | import com.google.common.util.concurrent.MoreExecutors; |
24 | -import com.google.gson.Gson; | |
25 | -import com.google.gson.JsonObject; | |
26 | 23 | import com.google.protobuf.InvalidProtocolBufferException; |
27 | 24 | import lombok.extern.slf4j.Slf4j; |
28 | 25 | import org.apache.commons.collections.CollectionUtils; |
... | ... | @@ -38,38 +35,34 @@ import org.thingsboard.server.common.data.kv.AttributeKey; |
38 | 35 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
39 | 36 | import org.thingsboard.server.common.data.kv.KvEntry; |
40 | 37 | import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; |
41 | -import org.thingsboard.server.common.msg.TbMsg; | |
42 | -import org.thingsboard.server.common.msg.TbMsgDataType; | |
43 | 38 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
39 | +import org.thingsboard.server.common.msg.queue.TbCallback; | |
44 | 40 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; |
45 | -import org.thingsboard.server.common.msg.session.SessionMsgType; | |
46 | -import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; | |
47 | 41 | import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; |
48 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
49 | 42 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
50 | -import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg; | |
43 | +import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; | |
51 | 44 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; |
52 | 45 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
53 | 46 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; |
54 | 47 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; |
55 | -import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | |
56 | -import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | |
57 | 48 | import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; |
58 | 49 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; |
59 | 50 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; |
60 | 51 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
52 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionSubscriptionInfoProto; | |
53 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionType; | |
61 | 54 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; |
62 | 55 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; |
56 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; | |
63 | 57 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; |
64 | 58 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; |
59 | +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; | |
60 | +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; | |
65 | 61 | import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; |
66 | -import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto; | |
67 | 62 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; |
68 | 63 | import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; |
69 | 64 | import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; |
70 | -import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; | |
71 | 65 | import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; |
72 | -import org.thingsboard.server.utils.JsonUtils; | |
73 | 66 | |
74 | 67 | import javax.annotation.Nullable; |
75 | 68 | import java.util.ArrayList; |
... | ... | @@ -100,9 +93,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
100 | 93 | private final Map<UUID, SessionInfo> attributeSubscriptions; |
101 | 94 | private final Map<UUID, SessionInfo> rpcSubscriptions; |
102 | 95 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; |
103 | - private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap; | |
104 | - | |
105 | - private final Gson gson = new Gson(); | |
106 | 96 | |
107 | 97 | private int rpcSeq = 0; |
108 | 98 | private String deviceName; |
... | ... | @@ -117,7 +107,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
117 | 107 | this.attributeSubscriptions = new HashMap<>(); |
118 | 108 | this.rpcSubscriptions = new HashMap<>(); |
119 | 109 | this.toDeviceRpcPendingMap = new HashMap<>(); |
120 | - this.toServerRpcPendingMap = new HashMap<>(); | |
121 | 110 | if (initAttributes()) { |
122 | 111 | restoreSessions(); |
123 | 112 | } |
... | ... | @@ -153,7 +142,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
153 | 142 | Set<UUID> syncSessionSet = new HashSet<>(); |
154 | 143 | rpcSubscriptions.forEach((key, value) -> { |
155 | 144 | sendToTransport(rpcRequest, key, value.getNodeId()); |
156 | - if (TransportProtos.SessionType.SYNC == value.getType()) { | |
145 | + if (SessionType.SYNC == value.getType()) { | |
157 | 146 | syncSessionSet.add(key); |
158 | 147 | } |
159 | 148 | }); |
... | ... | @@ -161,7 +150,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
161 | 150 | |
162 | 151 | if (request.isOneway() && sent) { |
163 | 152 | log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); |
164 | - systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); | |
153 | + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); | |
165 | 154 | } else { |
166 | 155 | registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); |
167 | 156 | } |
... | ... | @@ -182,16 +171,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
182 | 171 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); |
183 | 172 | if (requestMd != null) { |
184 | 173 | log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); |
185 | - systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | |
174 | + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | |
186 | 175 | null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); |
187 | 176 | } |
188 | 177 | } |
189 | 178 | |
190 | 179 | private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) { |
191 | - TransportProtos.SessionType sessionType = getSessionType(sessionId); | |
180 | + SessionType sessionType = getSessionType(sessionId); | |
192 | 181 | if (!toDeviceRpcPendingMap.isEmpty()) { |
193 | 182 | log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); |
194 | - if (sessionType == TransportProtos.SessionType.SYNC) { | |
183 | + if (sessionType == SessionType.SYNC) { | |
195 | 184 | log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); |
196 | 185 | rpcSubscriptions.remove(sessionId); |
197 | 186 | } |
... | ... | @@ -199,7 +188,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
199 | 188 | log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); |
200 | 189 | } |
201 | 190 | Set<Integer> sentOneWayIds = new HashSet<>(); |
202 | - if (sessionType == TransportProtos.SessionType.ASYNC) { | |
191 | + if (sessionType == SessionType.ASYNC) { | |
203 | 192 | toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds)); |
204 | 193 | } else { |
205 | 194 | toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds)); |
... | ... | @@ -214,7 +203,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
214 | 203 | ToDeviceRpcRequestBody body = request.getBody(); |
215 | 204 | if (request.isOneway()) { |
216 | 205 | sentOneWayIds.add(entry.getKey()); |
217 | - systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null)); | |
206 | + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null)); | |
218 | 207 | } |
219 | 208 | ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId( |
220 | 209 | entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build(); |
... | ... | @@ -223,8 +212,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
223 | 212 | } |
224 | 213 | |
225 | 214 | void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) { |
226 | - boolean reportDeviceActivity = false; | |
227 | 215 | TransportToDeviceActorMsg msg = wrapper.getMsg(); |
216 | + TbCallback callback = wrapper.getCallback(); | |
228 | 217 | if (msg.hasSessionEvent()) { |
229 | 218 | processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); |
230 | 219 | } |
... | ... | @@ -234,34 +223,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
234 | 223 | if (msg.hasSubscribeToRPC()) { |
235 | 224 | processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC()); |
236 | 225 | } |
237 | - if (msg.hasPostAttributes()) { | |
238 | - handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes()); | |
239 | - reportDeviceActivity = true; | |
240 | - } | |
241 | - if (msg.hasPostTelemetry()) { | |
242 | - handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry()); | |
243 | - reportDeviceActivity = true; | |
244 | - } | |
245 | 226 | if (msg.hasGetAttributes()) { |
246 | 227 | handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); |
247 | 228 | } |
248 | 229 | if (msg.hasToDeviceRPCCallResponse()) { |
249 | 230 | processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); |
250 | 231 | } |
251 | - if (msg.hasToServerRPCCallRequest()) { | |
252 | - handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest()); | |
253 | - reportDeviceActivity = true; | |
254 | - } | |
255 | 232 | if (msg.hasSubscriptionInfo()) { |
256 | 233 | handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); |
257 | 234 | } |
258 | - if (reportDeviceActivity) { | |
259 | - reportLogicalDeviceActivity(); | |
260 | - } | |
261 | - } | |
262 | - | |
263 | - private void reportLogicalDeviceActivity() { | |
264 | - systemContext.getDeviceStateService().onDeviceActivity(deviceId); | |
235 | + callback.onSuccess(); | |
265 | 236 | } |
266 | 237 | |
267 | 238 | private void reportSessionOpen() { |
... | ... | @@ -326,67 +297,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
326 | 297 | return new HashSet<>(strings); |
327 | 298 | } |
328 | 299 | |
329 | - private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) { | |
330 | - JsonObject json = JsonUtils.getJsonObject(postAttributes.getKvList()); | |
331 | - TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), | |
332 | - TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); | |
333 | - pushToRuleEngine(context, tbMsg); | |
334 | - } | |
335 | - | |
336 | - private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) { | |
337 | - for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) { | |
338 | - JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); | |
339 | - TbMsgMetaData metaData = defaultMetaData.copy(); | |
340 | - metaData.putValue("ts", tsKv.getTs() + ""); | |
341 | - TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); | |
342 | - pushToRuleEngine(context, tbMsg); | |
343 | - } | |
344 | - } | |
345 | - | |
346 | - private void handleClientSideRPCRequest(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg request) { | |
347 | - UUID sessionId = getSessionId(sessionInfo); | |
348 | - JsonObject json = new JsonObject(); | |
349 | - json.addProperty("method", request.getMethodName()); | |
350 | - json.add("params", JsonUtils.parse(request.getParams())); | |
351 | - | |
352 | - TbMsgMetaData requestMetaData = defaultMetaData.copy(); | |
353 | - requestMetaData.putValue("requestId", Integer.toString(request.getRequestId())); | |
354 | - TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); | |
355 | - context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self()); | |
356 | - | |
357 | - scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout()); | |
358 | - toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(sessionId, getSessionType(sessionId), sessionInfo.getNodeId())); | |
359 | - } | |
360 | - | |
361 | - private TransportProtos.SessionType getSessionType(UUID sessionId) { | |
362 | - return sessions.containsKey(sessionId) ? TransportProtos.SessionType.ASYNC : TransportProtos.SessionType.SYNC; | |
363 | - } | |
364 | - | |
365 | - void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) { | |
366 | - ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId()); | |
367 | - if (data != null) { | |
368 | - log.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId()); | |
369 | - sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder() | |
370 | - .setRequestId(msg.getId()).setError("timeout").build() | |
371 | - , data.getSessionId(), data.getNodeId()); | |
372 | - } | |
373 | - } | |
374 | - | |
375 | - void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) { | |
376 | - int requestId = msg.getMsg().getRequestId(); | |
377 | - ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId); | |
378 | - if (data != null) { | |
379 | - log.debug("[{}] Pushing reply to [{}][{}]!", deviceId, data.getNodeId(), data.getSessionId()); | |
380 | - sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder() | |
381 | - .setRequestId(requestId).setPayload(msg.getMsg().getData()).build() | |
382 | - , data.getSessionId(), data.getNodeId()); | |
383 | - } else { | |
384 | - log.debug("[{}][{}] Pending RPC request to server not found!", deviceId, requestId); | |
385 | - } | |
386 | - } | |
387 | - | |
388 | - private void pushToRuleEngine(ActorContext context, TbMsg tbMsg) { | |
389 | - context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self()); | |
300 | + private SessionType getSessionType(UUID sessionId) { | |
301 | + return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC; | |
390 | 302 | } |
391 | 303 | |
392 | 304 | void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { |
... | ... | @@ -434,7 +346,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
434 | 346 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); |
435 | 347 | boolean success = requestMd != null; |
436 | 348 | if (success) { |
437 | - systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | |
349 | + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | |
438 | 350 | responseMsg.getPayload(), null)); |
439 | 351 | } else { |
440 | 352 | log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); |
... | ... | @@ -449,7 +361,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
449 | 361 | } else { |
450 | 362 | SessionInfoMetaData sessionMD = sessions.get(sessionId); |
451 | 363 | if (sessionMD == null) { |
452 | - sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); | |
364 | + sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); | |
453 | 365 | } |
454 | 366 | sessionMD.setSubscribedToAttributes(true); |
455 | 367 | log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); |
... | ... | @@ -470,7 +382,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
470 | 382 | } else { |
471 | 383 | SessionInfoMetaData sessionMD = sessions.get(sessionId); |
472 | 384 | if (sessionMD == null) { |
473 | - sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); | |
385 | + sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); | |
474 | 386 | } |
475 | 387 | sessionMD.setSubscribedToRPC(true); |
476 | 388 | log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); |
... | ... | @@ -494,10 +406,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
494 | 406 | notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); |
495 | 407 | } |
496 | 408 | } |
497 | - sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId()))); | |
409 | + sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId()))); | |
498 | 410 | if (sessions.size() == 1) { |
499 | 411 | reportSessionOpen(); |
500 | 412 | } |
413 | + systemContext.getDeviceStateService().onDeviceActivity(deviceId, System.currentTimeMillis()); | |
501 | 414 | dumpSessions(); |
502 | 415 | } else if (msg.getEvent() == SessionEvent.CLOSED) { |
503 | 416 | log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); |
... | ... | @@ -511,10 +424,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
511 | 424 | } |
512 | 425 | } |
513 | 426 | |
514 | - private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, TransportProtos.SubscriptionInfoProto subscriptionInfo) { | |
427 | + private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { | |
515 | 428 | UUID sessionId = getSessionId(sessionInfoProto); |
516 | 429 | SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId, |
517 | - id -> new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L)); | |
430 | + id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L)); | |
518 | 431 | |
519 | 432 | sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime()); |
520 | 433 | sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription()); |
... | ... | @@ -525,6 +438,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
525 | 438 | if (subscriptionInfo.getRpcSubscription()) { |
526 | 439 | rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo()); |
527 | 440 | } |
441 | + systemContext.getDeviceStateService().onDeviceActivity(deviceId, subscriptionInfo.getLastActivityTime()); | |
528 | 442 | dumpSessions(); |
529 | 443 | } |
530 | 444 | |
... | ... | @@ -536,11 +450,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
536 | 450 | } |
537 | 451 | |
538 | 452 | private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) { |
539 | - DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() | |
453 | + ToTransportMsg msg = ToTransportMsg.newBuilder() | |
540 | 454 | .setSessionIdMSB(sessionId.getMostSignificantBits()) |
541 | 455 | .setSessionIdLSB(sessionId.getLeastSignificantBits()) |
542 | 456 | .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build(); |
543 | - systemContext.getRuleEngineTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg); | |
457 | + systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg); | |
544 | 458 | } |
545 | 459 | |
546 | 460 | void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { |
... | ... | @@ -552,35 +466,35 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
552 | 466 | } |
553 | 467 | |
554 | 468 | private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) { |
555 | - DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() | |
469 | + ToTransportMsg msg = ToTransportMsg.newBuilder() | |
556 | 470 | .setSessionIdMSB(sessionInfo.getSessionIdMSB()) |
557 | 471 | .setSessionIdLSB(sessionInfo.getSessionIdLSB()) |
558 | 472 | .setGetAttributesResponse(responseMsg).build(); |
559 | - systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg); | |
473 | + systemContext.getTbCoreToTransportService().process(sessionInfo.getNodeId(), msg); | |
560 | 474 | } |
561 | 475 | |
562 | 476 | private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) { |
563 | - DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() | |
477 | + ToTransportMsg msg = ToTransportMsg.newBuilder() | |
564 | 478 | .setSessionIdMSB(sessionId.getMostSignificantBits()) |
565 | 479 | .setSessionIdLSB(sessionId.getLeastSignificantBits()) |
566 | 480 | .setAttributeUpdateNotification(notificationMsg).build(); |
567 | - systemContext.getRuleEngineTransportService().process(nodeId, msg); | |
481 | + systemContext.getTbCoreToTransportService().process(nodeId, msg); | |
568 | 482 | } |
569 | 483 | |
570 | 484 | private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) { |
571 | - DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() | |
485 | + ToTransportMsg msg = ToTransportMsg.newBuilder() | |
572 | 486 | .setSessionIdMSB(sessionId.getMostSignificantBits()) |
573 | 487 | .setSessionIdLSB(sessionId.getLeastSignificantBits()) |
574 | 488 | .setToDeviceRequest(rpcMsg).build(); |
575 | - systemContext.getRuleEngineTransportService().process(nodeId, msg); | |
489 | + systemContext.getTbCoreToTransportService().process(nodeId, msg); | |
576 | 490 | } |
577 | 491 | |
578 | - private void sendToTransport(TransportProtos.ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) { | |
579 | - DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() | |
492 | + private void sendToTransport(ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) { | |
493 | + ToTransportMsg msg = ToTransportMsg.newBuilder() | |
580 | 494 | .setSessionIdMSB(sessionId.getMostSignificantBits()) |
581 | 495 | .setSessionIdLSB(sessionId.getLeastSignificantBits()) |
582 | 496 | .setToServerResponse(rpcMsg).build(); |
583 | - systemContext.getRuleEngineTransportService().process(nodeId, msg); | |
497 | + systemContext.getTbCoreToTransportService().process(nodeId, msg); | |
584 | 498 | } |
585 | 499 | |
586 | 500 | |
... | ... | @@ -632,9 +546,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
632 | 546 | |
633 | 547 | private void restoreSessions() { |
634 | 548 | log.debug("[{}] Restoring sessions from cache", deviceId); |
635 | - TransportProtos.DeviceSessionsCacheEntry sessionsDump = null; | |
549 | + DeviceSessionsCacheEntry sessionsDump = null; | |
636 | 550 | try { |
637 | - sessionsDump = TransportProtos.DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId)); | |
551 | + sessionsDump = DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId)); | |
638 | 552 | } catch (InvalidProtocolBufferException e) { |
639 | 553 | log.warn("[{}] Failed to decode device sessions from cache", deviceId); |
640 | 554 | return; |
... | ... | @@ -643,11 +557,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
643 | 557 | log.debug("[{}] No session information found", deviceId); |
644 | 558 | return; |
645 | 559 | } |
646 | - for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { | |
560 | + for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { | |
647 | 561 | SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo(); |
648 | 562 | UUID sessionId = getSessionId(sessionInfoProto); |
649 | - SessionInfo sessionInfo = new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId()); | |
650 | - TransportProtos.SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo(); | |
563 | + SessionInfo sessionInfo = new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()); | |
564 | + SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo(); | |
651 | 565 | SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime()); |
652 | 566 | sessions.put(sessionId, sessionMD); |
653 | 567 | if (subInfo.getAttributeSubscription()) { |
... | ... | @@ -665,27 +579,27 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
665 | 579 | |
666 | 580 | private void dumpSessions() { |
667 | 581 | log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); |
668 | - List<TransportProtos.SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size()); | |
582 | + List<SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size()); | |
669 | 583 | sessions.forEach((uuid, sessionMD) -> { |
670 | - if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) { | |
584 | + if (sessionMD.getSessionInfo().getType() == SessionType.SYNC) { | |
671 | 585 | return; |
672 | 586 | } |
673 | 587 | SessionInfo sessionInfo = sessionMD.getSessionInfo(); |
674 | - TransportProtos.SubscriptionInfoProto subscriptionInfoProto = TransportProtos.SubscriptionInfoProto.newBuilder() | |
588 | + SubscriptionInfoProto subscriptionInfoProto = SubscriptionInfoProto.newBuilder() | |
675 | 589 | .setLastActivityTime(sessionMD.getLastActivityTime()) |
676 | 590 | .setAttributeSubscription(sessionMD.isSubscribedToAttributes()) |
677 | 591 | .setRpcSubscription(sessionMD.isSubscribedToRPC()).build(); |
678 | - TransportProtos.SessionInfoProto sessionInfoProto = TransportProtos.SessionInfoProto.newBuilder() | |
592 | + SessionInfoProto sessionInfoProto = SessionInfoProto.newBuilder() | |
679 | 593 | .setSessionIdMSB(uuid.getMostSignificantBits()) |
680 | 594 | .setSessionIdLSB(uuid.getLeastSignificantBits()) |
681 | 595 | .setNodeId(sessionInfo.getNodeId()).build(); |
682 | - sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder() | |
596 | + sessionsList.add(SessionSubscriptionInfoProto.newBuilder() | |
683 | 597 | .setSessionInfo(sessionInfoProto) |
684 | 598 | .setSubscriptionInfo(subscriptionInfoProto).build()); |
685 | 599 | log.debug("[{}] Dumping session: {}", deviceId, sessionMD); |
686 | 600 | }); |
687 | 601 | systemContext.getDeviceSessionCacheService() |
688 | - .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder() | |
602 | + .put(deviceId, DeviceSessionsCacheEntry.newBuilder() | |
689 | 603 | .addAllSessions(sessionsList).build().toByteArray()); |
690 | 604 | } |
691 | 605 | |
... | ... | @@ -706,4 +620,5 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
706 | 620 | dumpSessions(); |
707 | 621 | } |
708 | 622 | } |
623 | + | |
709 | 624 | } | ... | ... |
application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
deleted
100644 → 0
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.actors.rpc; | |
17 | - | |
18 | -import akka.actor.ActorRef; | |
19 | -import lombok.extern.slf4j.Slf4j; | |
20 | -import org.thingsboard.server.actors.ActorSystemContext; | |
21 | -import org.thingsboard.server.actors.service.ActorService; | |
22 | -import org.thingsboard.server.gen.cluster.ClusterAPIProtos; | |
23 | -import org.thingsboard.server.service.cluster.rpc.GrpcSession; | |
24 | -import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; | |
25 | -import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService; | |
26 | - | |
27 | -/** | |
28 | - * @author Andrew Shvayka | |
29 | - */ | |
30 | -@Slf4j | |
31 | -public class BasicRpcSessionListener implements GrpcSessionListener { | |
32 | - | |
33 | - private final ClusterRpcCallbackExecutorService callbackExecutorService; | |
34 | - private final ActorService service; | |
35 | - private final ActorRef manager; | |
36 | - private final ActorRef self; | |
37 | - | |
38 | - BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) { | |
39 | - this.service = context.getActorService(); | |
40 | - this.callbackExecutorService = context.getClusterRpcCallbackExecutor(); | |
41 | - this.manager = manager; | |
42 | - this.self = self; | |
43 | - } | |
44 | - | |
45 | - @Override | |
46 | - public void onConnected(GrpcSession session) { | |
47 | - log.info("[{}][{}] session started", session.getRemoteServer(), getType(session)); | |
48 | - if (!session.isClient()) { | |
49 | - manager.tell(new RpcSessionConnectedMsg(session.getRemoteServer(), session.getSessionId()), self); | |
50 | - } | |
51 | - } | |
52 | - | |
53 | - @Override | |
54 | - public void onDisconnected(GrpcSession session) { | |
55 | - log.info("[{}][{}] session closed", session.getRemoteServer(), getType(session)); | |
56 | - manager.tell(new RpcSessionDisconnectedMsg(session.isClient(), session.getRemoteServer()), self); | |
57 | - } | |
58 | - | |
59 | - @Override | |
60 | - public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) { | |
61 | - log.trace("Received session actor msg from [{}][{}]: {}", session.getRemoteServer(), getType(session), clusterMessage); | |
62 | - callbackExecutorService.execute(() -> { | |
63 | - try { | |
64 | - service.onReceivedMsg(session.getRemoteServer(), clusterMessage); | |
65 | - } catch (Exception e) { | |
66 | - log.debug("[{}][{}] Failed to process cluster message: {}", session.getRemoteServer(), getType(session), clusterMessage, e); | |
67 | - } | |
68 | - }); | |
69 | - } | |
70 | - | |
71 | - @Override | |
72 | - public void onError(GrpcSession session, Throwable t) { | |
73 | - log.warn("[{}][{}] session got error -> {}", session.getRemoteServer(), getType(session), t); | |
74 | - manager.tell(new RpcSessionClosedMsg(session.isClient(), session.getRemoteServer()), self); | |
75 | - session.close(); | |
76 | - } | |
77 | - | |
78 | - private static String getType(GrpcSession session) { | |
79 | - return session.isClient() ? "Client" : "Server"; | |
80 | - } | |
81 | - | |
82 | - | |
83 | -} |
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.actors.rpc; | |
17 | - | |
18 | -import akka.actor.ActorRef; | |
19 | -import akka.actor.OneForOneStrategy; | |
20 | -import akka.actor.Props; | |
21 | -import akka.actor.SupervisorStrategy; | |
22 | -import akka.event.Logging; | |
23 | -import akka.event.LoggingAdapter; | |
24 | -import lombok.extern.slf4j.Slf4j; | |
25 | -import org.thingsboard.server.actors.ActorSystemContext; | |
26 | -import org.thingsboard.server.actors.service.ContextAwareActor; | |
27 | -import org.thingsboard.server.actors.service.ContextBasedCreator; | |
28 | -import org.thingsboard.server.actors.service.DefaultActorService; | |
29 | -import org.thingsboard.server.common.msg.TbActorMsg; | |
30 | -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; | |
31 | -import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
32 | -import org.thingsboard.server.common.msg.cluster.ServerType; | |
33 | -import org.thingsboard.server.gen.cluster.ClusterAPIProtos; | |
34 | -import org.thingsboard.server.service.cluster.discovery.ServerInstance; | |
35 | -import scala.concurrent.duration.Duration; | |
36 | - | |
37 | -import java.util.*; | |
38 | - | |
39 | -/** | |
40 | - * @author Andrew Shvayka | |
41 | - */ | |
42 | -public class RpcManagerActor extends ContextAwareActor { | |
43 | - | |
44 | - private final Map<ServerAddress, SessionActorInfo> sessionActors; | |
45 | - private final Map<ServerAddress, Queue<ClusterAPIProtos.ClusterMessage>> pendingMsgs; | |
46 | - private final ServerAddress instance; | |
47 | - | |
48 | - private RpcManagerActor(ActorSystemContext systemContext) { | |
49 | - super(systemContext); | |
50 | - this.sessionActors = new HashMap<>(); | |
51 | - this.pendingMsgs = new HashMap<>(); | |
52 | - this.instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress(); | |
53 | - | |
54 | - systemContext.getDiscoveryService().getOtherServers().stream() | |
55 | - .filter(otherServer -> otherServer.getServerAddress().compareTo(instance) > 0) | |
56 | - .forEach(otherServer -> onCreateSessionRequest( | |
57 | - new RpcSessionCreateRequestMsg(UUID.randomUUID(), otherServer.getServerAddress(), null))); | |
58 | - } | |
59 | - | |
60 | - @Override | |
61 | - protected boolean process(TbActorMsg msg) { | |
62 | - //TODO Move everything here, to work with TbActorMsg | |
63 | - return false; | |
64 | - } | |
65 | - | |
66 | - @Override | |
67 | - public void onReceive(Object msg) { | |
68 | - if (msg instanceof ClusterAPIProtos.ClusterMessage) { | |
69 | - onMsg((ClusterAPIProtos.ClusterMessage) msg); | |
70 | - } else if (msg instanceof RpcBroadcastMsg) { | |
71 | - onMsg((RpcBroadcastMsg) msg); | |
72 | - } else if (msg instanceof RpcSessionCreateRequestMsg) { | |
73 | - onCreateSessionRequest((RpcSessionCreateRequestMsg) msg); | |
74 | - } else if (msg instanceof RpcSessionConnectedMsg) { | |
75 | - onSessionConnected((RpcSessionConnectedMsg) msg); | |
76 | - } else if (msg instanceof RpcSessionDisconnectedMsg) { | |
77 | - onSessionDisconnected((RpcSessionDisconnectedMsg) msg); | |
78 | - } else if (msg instanceof RpcSessionClosedMsg) { | |
79 | - onSessionClosed((RpcSessionClosedMsg) msg); | |
80 | - } else if (msg instanceof ClusterEventMsg) { | |
81 | - onClusterEvent((ClusterEventMsg) msg); | |
82 | - } | |
83 | - } | |
84 | - | |
85 | - private void onMsg(RpcBroadcastMsg msg) { | |
86 | - log.debug("Forwarding msg to session actors {}", msg); | |
87 | - sessionActors.keySet().forEach(address -> { | |
88 | - ClusterAPIProtos.ClusterMessage msgWithServerAddress = msg.getMsg() | |
89 | - .toBuilder() | |
90 | - .setServerAddress(ClusterAPIProtos.ServerAddress | |
91 | - .newBuilder() | |
92 | - .setHost(address.getHost()) | |
93 | - .setPort(address.getPort()) | |
94 | - .build()) | |
95 | - .build(); | |
96 | - onMsg(msgWithServerAddress); | |
97 | - }); | |
98 | - pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg())); | |
99 | - } | |
100 | - | |
101 | - private void onMsg(ClusterAPIProtos.ClusterMessage msg) { | |
102 | - if (msg.hasServerAddress()) { | |
103 | - ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE); | |
104 | - SessionActorInfo session = sessionActors.get(address); | |
105 | - if (session != null) { | |
106 | - log.debug("{} Forwarding msg to session actor: {}", address, msg); | |
107 | - session.getActor().tell(msg, ActorRef.noSender()); | |
108 | - } else { | |
109 | - log.debug("{} Storing msg to pending queue: {}", address, msg); | |
110 | - Queue<ClusterAPIProtos.ClusterMessage> queue = pendingMsgs.get(address); | |
111 | - if (queue == null) { | |
112 | - queue = new LinkedList<>(); | |
113 | - pendingMsgs.put(new ServerAddress( | |
114 | - msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE), queue); | |
115 | - } | |
116 | - queue.add(msg); | |
117 | - } | |
118 | - } else { | |
119 | - log.warn("Cluster msg doesn't have server address [{}]", msg); | |
120 | - } | |
121 | - } | |
122 | - | |
123 | - @Override | |
124 | - public void postStop() { | |
125 | - sessionActors.clear(); | |
126 | - pendingMsgs.clear(); | |
127 | - } | |
128 | - | |
129 | - private void onClusterEvent(ClusterEventMsg msg) { | |
130 | - ServerAddress server = msg.getServerAddress(); | |
131 | - if (server.compareTo(instance) > 0) { | |
132 | - if (msg.isAdded()) { | |
133 | - onCreateSessionRequest(new RpcSessionCreateRequestMsg(UUID.randomUUID(), server, null)); | |
134 | - } else { | |
135 | - onSessionClose(false, server); | |
136 | - } | |
137 | - } | |
138 | - } | |
139 | - | |
140 | - private void onSessionConnected(RpcSessionConnectedMsg msg) { | |
141 | - register(msg.getRemoteAddress(), msg.getId(), context().sender()); | |
142 | - } | |
143 | - | |
144 | - private void onSessionDisconnected(RpcSessionDisconnectedMsg msg) { | |
145 | - boolean reconnect = msg.isClient() && isRegistered(msg.getRemoteAddress()); | |
146 | - onSessionClose(reconnect, msg.getRemoteAddress()); | |
147 | - } | |
148 | - | |
149 | - private void onSessionClosed(RpcSessionClosedMsg msg) { | |
150 | - boolean reconnect = msg.isClient() && isRegistered(msg.getRemoteAddress()); | |
151 | - onSessionClose(reconnect, msg.getRemoteAddress()); | |
152 | - } | |
153 | - | |
154 | - private boolean isRegistered(ServerAddress address) { | |
155 | - for (ServerInstance server : systemContext.getDiscoveryService().getOtherServers()) { | |
156 | - if (server.getServerAddress().equals(address)) { | |
157 | - return true; | |
158 | - } | |
159 | - } | |
160 | - return false; | |
161 | - } | |
162 | - | |
163 | - private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) { | |
164 | - log.info("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect); | |
165 | - SessionActorInfo sessionRef = sessionActors.get(remoteAddress); | |
166 | - if (sessionRef != null && context().sender() != null && context().sender().equals(sessionRef.actor)) { | |
167 | - context().stop(sessionRef.actor); | |
168 | - sessionActors.remove(remoteAddress); | |
169 | - pendingMsgs.remove(remoteAddress); | |
170 | - if (reconnect) { | |
171 | - onCreateSessionRequest(new RpcSessionCreateRequestMsg(sessionRef.sessionId, remoteAddress, null)); | |
172 | - } | |
173 | - } | |
174 | - } | |
175 | - | |
176 | - private void onCreateSessionRequest(RpcSessionCreateRequestMsg msg) { | |
177 | - if (msg.getRemoteAddress() != null) { | |
178 | - if (!sessionActors.containsKey(msg.getRemoteAddress())) { | |
179 | - ActorRef actorRef = createSessionActor(msg); | |
180 | - register(msg.getRemoteAddress(), msg.getMsgUid(), actorRef); | |
181 | - } | |
182 | - } else { | |
183 | - createSessionActor(msg); | |
184 | - } | |
185 | - } | |
186 | - | |
187 | - private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) { | |
188 | - sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender)); | |
189 | - log.info("[{}][{}] Registering session actor.", remoteAddress, uuid); | |
190 | - Queue<ClusterAPIProtos.ClusterMessage> data = pendingMsgs.remove(remoteAddress); | |
191 | - if (data != null) { | |
192 | - log.info("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size()); | |
193 | - data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender())); | |
194 | - } else { | |
195 | - log.info("[{}][{}] No pending messages to forward.", remoteAddress, uuid); | |
196 | - } | |
197 | - } | |
198 | - | |
199 | - private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) { | |
200 | - log.info("[{}] Creating session actor.", msg.getMsgUid()); | |
201 | - ActorRef actor = context().actorOf( | |
202 | - Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid())) | |
203 | - .withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME)); | |
204 | - actor.tell(msg, context().self()); | |
205 | - return actor; | |
206 | - } | |
207 | - | |
208 | - public static class ActorCreator extends ContextBasedCreator<RpcManagerActor> { | |
209 | - private static final long serialVersionUID = 1L; | |
210 | - | |
211 | - public ActorCreator(ActorSystemContext context) { | |
212 | - super(context); | |
213 | - } | |
214 | - | |
215 | - @Override | |
216 | - public RpcManagerActor create() { | |
217 | - return new RpcManagerActor(context); | |
218 | - } | |
219 | - } | |
220 | - | |
221 | - @Override | |
222 | - public SupervisorStrategy supervisorStrategy() { | |
223 | - return strategy; | |
224 | - } | |
225 | - | |
226 | - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> { | |
227 | - log.warn("Unknown failure", t); | |
228 | - return SupervisorStrategy.resume(); | |
229 | - }); | |
230 | -} |
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.actors.rpc; | |
17 | - | |
18 | -import io.grpc.ManagedChannel; | |
19 | -import io.grpc.ManagedChannelBuilder; | |
20 | -import io.grpc.stub.StreamObserver; | |
21 | -import lombok.extern.slf4j.Slf4j; | |
22 | -import org.thingsboard.server.actors.ActorSystemContext; | |
23 | -import org.thingsboard.server.actors.service.ContextAwareActor; | |
24 | -import org.thingsboard.server.actors.service.ContextBasedCreator; | |
25 | -import org.thingsboard.server.common.msg.TbActorMsg; | |
26 | -import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
27 | -import org.thingsboard.server.gen.cluster.ClusterAPIProtos; | |
28 | -import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; | |
29 | -import org.thingsboard.server.service.cluster.rpc.GrpcSession; | |
30 | -import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; | |
31 | - | |
32 | -import java.util.UUID; | |
33 | - | |
34 | -import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE; | |
35 | - | |
36 | -/** | |
37 | - * @author Andrew Shvayka | |
38 | - */ | |
39 | -@Slf4j | |
40 | -public class RpcSessionActor extends ContextAwareActor { | |
41 | - | |
42 | - | |
43 | - private final UUID sessionId; | |
44 | - private GrpcSession session; | |
45 | - private GrpcSessionListener listener; | |
46 | - | |
47 | - private RpcSessionActor(ActorSystemContext systemContext, UUID sessionId) { | |
48 | - super(systemContext); | |
49 | - this.sessionId = sessionId; | |
50 | - } | |
51 | - | |
52 | - @Override | |
53 | - protected boolean process(TbActorMsg msg) { | |
54 | - //TODO Move everything here, to work with TbActorMsg | |
55 | - return false; | |
56 | - } | |
57 | - | |
58 | - @Override | |
59 | - public void onReceive(Object msg) { | |
60 | - if (msg instanceof ClusterAPIProtos.ClusterMessage) { | |
61 | - tell((ClusterAPIProtos.ClusterMessage) msg); | |
62 | - } else if (msg instanceof RpcSessionCreateRequestMsg) { | |
63 | - initSession((RpcSessionCreateRequestMsg) msg); | |
64 | - } | |
65 | - } | |
66 | - | |
67 | - private void tell(ClusterAPIProtos.ClusterMessage msg) { | |
68 | - if (session != null) { | |
69 | - session.sendMsg(msg); | |
70 | - } else { | |
71 | - log.trace("Failed to send message due to missing session!"); | |
72 | - } | |
73 | - } | |
74 | - | |
75 | - @Override | |
76 | - public void postStop() { | |
77 | - if (session != null) { | |
78 | - log.info("Closing session -> {}", session.getRemoteServer()); | |
79 | - try { | |
80 | - session.close(); | |
81 | - } catch (RuntimeException e) { | |
82 | - log.trace("Failed to close session!", e); | |
83 | - } | |
84 | - } | |
85 | - } | |
86 | - | |
87 | - private void initSession(RpcSessionCreateRequestMsg msg) { | |
88 | - log.info("[{}] Initializing session", context().self()); | |
89 | - ServerAddress remoteServer = msg.getRemoteAddress(); | |
90 | - listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self()); | |
91 | - if (msg.getRemoteAddress() == null) { | |
92 | - // Server session | |
93 | - session = new GrpcSession(listener); | |
94 | - session.setOutputStream(msg.getResponseObserver()); | |
95 | - session.initInputStream(); | |
96 | - session.initOutputStream(); | |
97 | - systemContext.getRpcService().onSessionCreated(msg.getMsgUid(), session.getInputStream()); | |
98 | - } else { | |
99 | - // Client session | |
100 | - ManagedChannel channel = ManagedChannelBuilder.forAddress(remoteServer.getHost(), remoteServer.getPort()).usePlaintext().build(); | |
101 | - session = new GrpcSession(remoteServer, listener, channel); | |
102 | - session.initInputStream(); | |
103 | - | |
104 | - ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel); | |
105 | - StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream = stub.handleMsgs(session.getInputStream()); | |
106 | - | |
107 | - session.setOutputStream(outputStream); | |
108 | - session.initOutputStream(); | |
109 | - outputStream.onNext(toConnectMsg()); | |
110 | - } | |
111 | - } | |
112 | - | |
113 | - public static class ActorCreator extends ContextBasedCreator<RpcSessionActor> { | |
114 | - private static final long serialVersionUID = 1L; | |
115 | - | |
116 | - private final UUID sessionId; | |
117 | - | |
118 | - public ActorCreator(ActorSystemContext context, UUID sessionId) { | |
119 | - super(context); | |
120 | - this.sessionId = sessionId; | |
121 | - } | |
122 | - | |
123 | - @Override | |
124 | - public RpcSessionActor create() { | |
125 | - return new RpcSessionActor(context, sessionId); | |
126 | - } | |
127 | - } | |
128 | - | |
129 | - private ClusterAPIProtos.ClusterMessage toConnectMsg() { | |
130 | - ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress(); | |
131 | - return ClusterAPIProtos.ClusterMessage.newBuilder().setMessageType(CONNECT_RPC_MESSAGE).setServerAddress( | |
132 | - ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost()) | |
133 | - .setPort(instance.getPort()).build()).build(); | |
134 | - } | |
135 | -} |