Commit 5b1cc8ac3309b5a65ffe5835939dd528fcb7c512
Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature…
…/firmware-checksum-autogenerating
Showing
78 changed files
with
3586 additions
and
200 deletions
Too many changes to show.
To preserve performance only 78 of 160 files are displayed.
... | ... | @@ -90,6 +90,10 @@ |
90 | 90 | <artifactId>lwm2m</artifactId> |
91 | 91 | </dependency> |
92 | 92 | <dependency> |
93 | + <groupId>org.thingsboard.common.transport</groupId> | |
94 | + <artifactId>snmp</artifactId> | |
95 | + </dependency> | |
96 | + <dependency> | |
93 | 97 | <groupId>org.thingsboard</groupId> |
94 | 98 | <artifactId>dao</artifactId> |
95 | 99 | </dependency> | ... | ... |
1 | +{ | |
2 | + "title": "Firmware", | |
3 | + "configuration": { | |
4 | + "description": "", | |
5 | + "widgets": { | |
6 | + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { | |
7 | + "isSystemType": true, | |
8 | + "bundleAlias": "cards", | |
9 | + "typeAlias": "entities_table", | |
10 | + "type": "latest", | |
11 | + "title": "New widget", | |
12 | + "image": null, | |
13 | + "description": null, | |
14 | + "sizeX": 7.5, | |
15 | + "sizeY": 6.5, | |
16 | + "config": { | |
17 | + "timewindow": { | |
18 | + "realtime": { | |
19 | + "interval": 1000, | |
20 | + "timewindowMs": 86400000 | |
21 | + }, | |
22 | + "aggregation": { | |
23 | + "type": "NONE", | |
24 | + "limit": 200 | |
25 | + } | |
26 | + }, | |
27 | + "showTitle": true, | |
28 | + "backgroundColor": "rgb(255, 255, 255)", | |
29 | + "color": "rgba(0, 0, 0, 0.87)", | |
30 | + "padding": "4px", | |
31 | + "settings": { | |
32 | + "enableSearch": true, | |
33 | + "displayPagination": true, | |
34 | + "defaultPageSize": 10, | |
35 | + "defaultSortOrder": "entityLabel", | |
36 | + "displayEntityName": false, | |
37 | + "displayEntityType": false, | |
38 | + "enableSelectColumnDisplay": false, | |
39 | + "enableStickyHeader": true, | |
40 | + "enableStickyAction": false, | |
41 | + "entitiesTitle": "Devices", | |
42 | + "displayEntityLabel": true, | |
43 | + "entityLabelColumnTitle": "Device" | |
44 | + }, | |
45 | + "title": "New Entities table", | |
46 | + "dropShadow": true, | |
47 | + "enableFullscreen": true, | |
48 | + "titleStyle": { | |
49 | + "fontSize": "16px", | |
50 | + "fontWeight": 400, | |
51 | + "padding": "5px 10px 5px 10px" | |
52 | + }, | |
53 | + "useDashboardTimewindow": false, | |
54 | + "showLegend": false, | |
55 | + "datasources": [ | |
56 | + { | |
57 | + "type": "entity", | |
58 | + "name": null, | |
59 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
60 | + "filterId": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", | |
61 | + "dataKeys": [ | |
62 | + { | |
63 | + "name": "current_fw_title", | |
64 | + "type": "timeseries", | |
65 | + "label": "Current FW title", | |
66 | + "color": "#2196f3", | |
67 | + "settings": { | |
68 | + "columnWidth": "0px", | |
69 | + "useCellStyleFunction": false, | |
70 | + "cellStyleFunction": "", | |
71 | + "useCellContentFunction": false, | |
72 | + "defaultColumnVisibility": "visible", | |
73 | + "columnSelectionToDisplay": "enabled" | |
74 | + }, | |
75 | + "_hash": 0.09545533885166413, | |
76 | + "units": null, | |
77 | + "decimals": null, | |
78 | + "funcBody": null, | |
79 | + "usePostProcessing": null, | |
80 | + "postFuncBody": null | |
81 | + }, | |
82 | + { | |
83 | + "name": "current_fw_version", | |
84 | + "type": "timeseries", | |
85 | + "label": "Current FW version", | |
86 | + "color": "#4caf50", | |
87 | + "settings": { | |
88 | + "columnWidth": "0px", | |
89 | + "useCellStyleFunction": false, | |
90 | + "cellStyleFunction": "", | |
91 | + "useCellContentFunction": false, | |
92 | + "defaultColumnVisibility": "visible", | |
93 | + "columnSelectionToDisplay": "enabled" | |
94 | + }, | |
95 | + "_hash": 0.7206056602328659, | |
96 | + "units": null, | |
97 | + "decimals": null, | |
98 | + "funcBody": null, | |
99 | + "usePostProcessing": null, | |
100 | + "postFuncBody": null | |
101 | + }, | |
102 | + { | |
103 | + "name": "target_fw_title", | |
104 | + "type": "timeseries", | |
105 | + "label": "Target FW title", | |
106 | + "color": "#ffc107", | |
107 | + "settings": { | |
108 | + "columnWidth": "0px", | |
109 | + "useCellStyleFunction": false, | |
110 | + "cellStyleFunction": "", | |
111 | + "useCellContentFunction": false, | |
112 | + "defaultColumnVisibility": "visible", | |
113 | + "columnSelectionToDisplay": "enabled" | |
114 | + }, | |
115 | + "_hash": 0.9934225682766313, | |
116 | + "units": null, | |
117 | + "decimals": null, | |
118 | + "funcBody": null, | |
119 | + "usePostProcessing": null, | |
120 | + "postFuncBody": null | |
121 | + }, | |
122 | + { | |
123 | + "name": "target_fw_version", | |
124 | + "type": "timeseries", | |
125 | + "label": "Target FW version", | |
126 | + "color": "#607d8b", | |
127 | + "settings": { | |
128 | + "columnWidth": "0px", | |
129 | + "useCellStyleFunction": false, | |
130 | + "cellStyleFunction": "", | |
131 | + "useCellContentFunction": false, | |
132 | + "cellContentFunction": "", | |
133 | + "defaultColumnVisibility": "visible", | |
134 | + "columnSelectionToDisplay": "enabled" | |
135 | + }, | |
136 | + "_hash": 0.5251724416842531, | |
137 | + "units": null, | |
138 | + "decimals": null, | |
139 | + "funcBody": null, | |
140 | + "usePostProcessing": null, | |
141 | + "postFuncBody": null | |
142 | + }, | |
143 | + { | |
144 | + "name": "target_fw_ts", | |
145 | + "type": "timeseries", | |
146 | + "label": "Target FW set time", | |
147 | + "color": "#e91e63", | |
148 | + "settings": { | |
149 | + "columnWidth": "0px", | |
150 | + "useCellStyleFunction": false, | |
151 | + "cellStyleFunction": "", | |
152 | + "useCellContentFunction": true, | |
153 | + "defaultColumnVisibility": "visible", | |
154 | + "columnSelectionToDisplay": "enabled", | |
155 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
156 | + }, | |
157 | + "_hash": 0.31823244858578237, | |
158 | + "units": null, | |
159 | + "decimals": null, | |
160 | + "funcBody": null, | |
161 | + "usePostProcessing": null, | |
162 | + "postFuncBody": null | |
163 | + }, | |
164 | + { | |
165 | + "name": "fw_state", | |
166 | + "type": "timeseries", | |
167 | + "label": "Progress", | |
168 | + "color": "#9c27b0", | |
169 | + "settings": { | |
170 | + "columnWidth": "30%", | |
171 | + "useCellStyleFunction": true, | |
172 | + "useCellContentFunction": true, | |
173 | + "defaultColumnVisibility": "visible", | |
174 | + "columnSelectionToDisplay": "enabled", | |
175 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
176 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
177 | + }, | |
178 | + "_hash": 0.8174211757846257, | |
179 | + "units": null, | |
180 | + "decimals": null, | |
181 | + "funcBody": null, | |
182 | + "usePostProcessing": null, | |
183 | + "postFuncBody": null | |
184 | + }, | |
185 | + { | |
186 | + "name": "fw_state", | |
187 | + "type": "timeseries", | |
188 | + "label": "Status", | |
189 | + "color": "#f44336", | |
190 | + "settings": { | |
191 | + "columnWidth": "130px", | |
192 | + "useCellStyleFunction": true, | |
193 | + "useCellContentFunction": true, | |
194 | + "defaultColumnVisibility": "visible", | |
195 | + "columnSelectionToDisplay": "enabled", | |
196 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
197 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
198 | + }, | |
199 | + "_hash": 0.7764426948615217, | |
200 | + "units": null, | |
201 | + "decimals": null, | |
202 | + "funcBody": null, | |
203 | + "usePostProcessing": null, | |
204 | + "postFuncBody": null | |
205 | + }, | |
206 | + { | |
207 | + "name": "fw_checksum", | |
208 | + "type": "attribute", | |
209 | + "label": "fw_checksum", | |
210 | + "color": "#3f51b5", | |
211 | + "settings": { | |
212 | + "columnWidth": "0px", | |
213 | + "useCellStyleFunction": false, | |
214 | + "cellStyleFunction": "", | |
215 | + "useCellContentFunction": false, | |
216 | + "defaultColumnVisibility": "hidden", | |
217 | + "columnSelectionToDisplay": "disabled" | |
218 | + }, | |
219 | + "_hash": 0.5594087842471693, | |
220 | + "units": null, | |
221 | + "decimals": null, | |
222 | + "funcBody": null, | |
223 | + "usePostProcessing": null, | |
224 | + "postFuncBody": null | |
225 | + } | |
226 | + ] | |
227 | + } | |
228 | + ], | |
229 | + "actions": { | |
230 | + "actionCellButton": [ | |
231 | + { | |
232 | + "name": "History firmware update", | |
233 | + "icon": "history", | |
234 | + "type": "openDashboardState", | |
235 | + "targetDashboardStateId": "device_firmware_history", | |
236 | + "setEntityId": true, | |
237 | + "stateEntityParamName": null, | |
238 | + "openInSeparateDialog": false, | |
239 | + "dialogTitle": "", | |
240 | + "dialogHideDashboardToolbar": true, | |
241 | + "dialogWidth": null, | |
242 | + "dialogHeight": null, | |
243 | + "openRightLayout": false, | |
244 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
245 | + }, | |
246 | + { | |
247 | + "name": "Edit firmware", | |
248 | + "icon": "edit", | |
249 | + "type": "customPretty", | |
250 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
251 | + "customCss": "", | |
252 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
253 | + "customResources": [], | |
254 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
255 | + }, | |
256 | + { | |
257 | + "name": "Download firware", | |
258 | + "icon": "file_download", | |
259 | + "type": "custom", | |
260 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
261 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
262 | + }, | |
263 | + { | |
264 | + "name": "Copy checksum", | |
265 | + "icon": "content_copy", | |
266 | + "type": "custom", | |
267 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
268 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
269 | + } | |
270 | + ] | |
271 | + }, | |
272 | + "showTitleIcon": false, | |
273 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
274 | + "iconSize": "24px", | |
275 | + "titleTooltip": "", | |
276 | + "widgetStyle": {} | |
277 | + }, | |
278 | + "row": 0, | |
279 | + "col": 0, | |
280 | + "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016" | |
281 | + }, | |
282 | + "100b756c-0082-6505-3ae1-3603e6deea48": { | |
283 | + "isSystemType": true, | |
284 | + "bundleAlias": "cards", | |
285 | + "typeAlias": "timeseries_table", | |
286 | + "type": "timeseries", | |
287 | + "title": "New widget", | |
288 | + "image": null, | |
289 | + "description": null, | |
290 | + "sizeX": 8, | |
291 | + "sizeY": 6.5, | |
292 | + "config": { | |
293 | + "datasources": [ | |
294 | + { | |
295 | + "type": "entity", | |
296 | + "name": null, | |
297 | + "entityAliasId": "19f41c21-d9af-e666-8f50-e1748778f955", | |
298 | + "filterId": null, | |
299 | + "dataKeys": [ | |
300 | + { | |
301 | + "name": "current_fw_title", | |
302 | + "type": "timeseries", | |
303 | + "label": "Current firmware title", | |
304 | + "color": "#2196f3", | |
305 | + "settings": { | |
306 | + "useCellStyleFunction": false, | |
307 | + "cellStyleFunction": "", | |
308 | + "useCellContentFunction": false, | |
309 | + "cellContentFunction": "" | |
310 | + }, | |
311 | + "_hash": 0.5978079905579401, | |
312 | + "units": null, | |
313 | + "decimals": null, | |
314 | + "funcBody": null, | |
315 | + "usePostProcessing": null, | |
316 | + "postFuncBody": null | |
317 | + }, | |
318 | + { | |
319 | + "name": "current_fw_version", | |
320 | + "type": "timeseries", | |
321 | + "label": "Current firmware version", | |
322 | + "color": "#4caf50", | |
323 | + "settings": { | |
324 | + "useCellStyleFunction": false, | |
325 | + "cellStyleFunction": "", | |
326 | + "useCellContentFunction": false, | |
327 | + "cellContentFunction": "" | |
328 | + }, | |
329 | + "_hash": 0.027392025058568192, | |
330 | + "units": null, | |
331 | + "decimals": null, | |
332 | + "funcBody": null, | |
333 | + "usePostProcessing": null, | |
334 | + "postFuncBody": null | |
335 | + }, | |
336 | + { | |
337 | + "name": "target_fw_title", | |
338 | + "type": "timeseries", | |
339 | + "label": "Target firmware title", | |
340 | + "color": "#f44336", | |
341 | + "settings": { | |
342 | + "useCellStyleFunction": false, | |
343 | + "cellStyleFunction": "", | |
344 | + "useCellContentFunction": false, | |
345 | + "cellContentFunction": "" | |
346 | + }, | |
347 | + "_hash": 0.9496350796287059, | |
348 | + "units": null, | |
349 | + "decimals": null, | |
350 | + "funcBody": null, | |
351 | + "usePostProcessing": null, | |
352 | + "postFuncBody": null | |
353 | + }, | |
354 | + { | |
355 | + "name": "target_fw_version", | |
356 | + "type": "timeseries", | |
357 | + "label": "Target firmware version", | |
358 | + "color": "#ffc107", | |
359 | + "settings": { | |
360 | + "useCellStyleFunction": false, | |
361 | + "cellStyleFunction": "", | |
362 | + "useCellContentFunction": false, | |
363 | + "cellContentFunction": "" | |
364 | + }, | |
365 | + "_hash": 0.6734152252264187, | |
366 | + "units": null, | |
367 | + "decimals": null, | |
368 | + "funcBody": null, | |
369 | + "usePostProcessing": null, | |
370 | + "postFuncBody": null | |
371 | + }, | |
372 | + { | |
373 | + "name": "fw_state", | |
374 | + "type": "timeseries", | |
375 | + "label": "Status", | |
376 | + "color": "#607d8b", | |
377 | + "settings": { | |
378 | + "useCellStyleFunction": false, | |
379 | + "cellStyleFunction": "", | |
380 | + "useCellContentFunction": false, | |
381 | + "cellContentFunction": "" | |
382 | + }, | |
383 | + "_hash": 0.2983399718643074, | |
384 | + "units": null, | |
385 | + "decimals": null, | |
386 | + "funcBody": null, | |
387 | + "usePostProcessing": true, | |
388 | + "postFuncBody": "function capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\nif (value !== '') {\n return capitalize(value);\n}\nreturn value;" | |
389 | + } | |
390 | + ] | |
391 | + } | |
392 | + ], | |
393 | + "timewindow": { | |
394 | + "hideInterval": false, | |
395 | + "hideAggregation": false, | |
396 | + "hideAggInterval": false, | |
397 | + "hideTimezone": false, | |
398 | + "selectedTab": 0, | |
399 | + "realtime": { | |
400 | + "realtimeType": 0, | |
401 | + "timewindowMs": 2592000000, | |
402 | + "quickInterval": "CURRENT_DAY", | |
403 | + "interval": 1000 | |
404 | + }, | |
405 | + "aggregation": { | |
406 | + "type": "NONE", | |
407 | + "limit": 200 | |
408 | + } | |
409 | + }, | |
410 | + "showTitle": false, | |
411 | + "backgroundColor": "rgb(255, 255, 255)", | |
412 | + "color": "rgba(0, 0, 0, 0.87)", | |
413 | + "padding": "8px", | |
414 | + "settings": { | |
415 | + "showTimestamp": true, | |
416 | + "displayPagination": true, | |
417 | + "defaultPageSize": 10, | |
418 | + "enableSearch": true, | |
419 | + "enableStickyHeader": true, | |
420 | + "enableStickyAction": true | |
421 | + }, | |
422 | + "title": "Firmware history", | |
423 | + "dropShadow": false, | |
424 | + "enableFullscreen": false, | |
425 | + "titleStyle": { | |
426 | + "fontSize": "16px", | |
427 | + "fontWeight": 400, | |
428 | + "padding": "5px 10px 5px 10px" | |
429 | + }, | |
430 | + "useDashboardTimewindow": false, | |
431 | + "showLegend": false, | |
432 | + "widgetStyle": {}, | |
433 | + "actions": {}, | |
434 | + "showTitleIcon": false, | |
435 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
436 | + "iconSize": "24px", | |
437 | + "displayTimewindow": true, | |
438 | + "titleTooltip": "" | |
439 | + }, | |
440 | + "row": 0, | |
441 | + "col": 0, | |
442 | + "id": "100b756c-0082-6505-3ae1-3603e6deea48" | |
443 | + }, | |
444 | + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { | |
445 | + "isSystemType": true, | |
446 | + "bundleAlias": "cards", | |
447 | + "typeAlias": "html_value_card", | |
448 | + "type": "latest", | |
449 | + "title": "New widget", | |
450 | + "sizeX": 8, | |
451 | + "sizeY": 3, | |
452 | + "config": { | |
453 | + "datasources": [ | |
454 | + { | |
455 | + "type": "entityCount", | |
456 | + "name": "", | |
457 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
458 | + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
459 | + "dataKeys": [ | |
460 | + { | |
461 | + "name": "count", | |
462 | + "type": "count", | |
463 | + "label": "waitingDevicesNumber", | |
464 | + "color": "#4caf50", | |
465 | + "settings": {}, | |
466 | + "_hash": 0.7404827038869322, | |
467 | + "units": null, | |
468 | + "decimals": null, | |
469 | + "funcBody": null, | |
470 | + "usePostProcessing": null, | |
471 | + "postFuncBody": null | |
472 | + } | |
473 | + ] | |
474 | + } | |
475 | + ], | |
476 | + "timewindow": { | |
477 | + "realtime": { | |
478 | + "timewindowMs": 60000 | |
479 | + } | |
480 | + }, | |
481 | + "showTitle": false, | |
482 | + "backgroundColor": "#fff", | |
483 | + "color": "rgba(0, 0, 0, 0.87)", | |
484 | + "padding": "0px", | |
485 | + "settings": { | |
486 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <img id=\"activeDevices\" src='data:image/svg+xml;utf8,<svg width=\"24\" height=\"40\" viewBox=\"0 0 24 40\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M0 0V12H0.0200005L0 12.02L8 20L0 28L0.0200005 28.02H0V40H24V28.02H23.98L24 28L16 20L24 12.02L23.98 12H24V0H0ZM20 29V36H4V29L12 21L20 29ZM12 19L4 11V4H20V11L12 19Z\" fill=\"black\"/>\n</svg>\n'>\n <div class='value' id=\"activeDevices\">\n ${waitingDevicesNumber:0}\n </div> \n <div class='description' id=\"activeDevices\">\n Device Waiting\n </div>\n </div>\n</div>", | |
487 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
488 | + }, | |
489 | + "title": "New HTML Value Card", | |
490 | + "dropShadow": true, | |
491 | + "enableFullscreen": false, | |
492 | + "widgetStyle": {}, | |
493 | + "titleStyle": { | |
494 | + "fontSize": "16px", | |
495 | + "fontWeight": 400 | |
496 | + }, | |
497 | + "useDashboardTimewindow": true, | |
498 | + "showLegend": false, | |
499 | + "actions": { | |
500 | + "elementClick": [ | |
501 | + { | |
502 | + "name": "activeDevices", | |
503 | + "icon": "more_horiz", | |
504 | + "type": "openDashboardState", | |
505 | + "targetDashboardStateId": "device_waiting", | |
506 | + "setEntityId": false, | |
507 | + "stateEntityParamName": null, | |
508 | + "openInSeparateDialog": false, | |
509 | + "dialogTitle": "", | |
510 | + "dialogHideDashboardToolbar": true, | |
511 | + "dialogWidth": null, | |
512 | + "dialogHeight": null, | |
513 | + "openRightLayout": false, | |
514 | + "id": "4d9a77a2-f0a5-690c-a83b-b0e940be788c" | |
515 | + } | |
516 | + ] | |
517 | + }, | |
518 | + "showTitleIcon": false, | |
519 | + "titleIcon": null, | |
520 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
521 | + "iconSize": "24px", | |
522 | + "titleTooltip": "", | |
523 | + "enableDataExport": false, | |
524 | + "displayTimewindow": true | |
525 | + }, | |
526 | + "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6" | |
527 | + }, | |
528 | + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { | |
529 | + "isSystemType": true, | |
530 | + "bundleAlias": "cards", | |
531 | + "typeAlias": "html_value_card", | |
532 | + "type": "latest", | |
533 | + "title": "New widget", | |
534 | + "sizeX": 8, | |
535 | + "sizeY": 3, | |
536 | + "config": { | |
537 | + "datasources": [ | |
538 | + { | |
539 | + "type": "entityCount", | |
540 | + "name": "", | |
541 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
542 | + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
543 | + "dataKeys": [ | |
544 | + { | |
545 | + "name": "count", | |
546 | + "type": "count", | |
547 | + "label": "updatingDevicesNumber", | |
548 | + "color": "#4caf50", | |
549 | + "settings": {}, | |
550 | + "_hash": 0.7404827038869322, | |
551 | + "units": null, | |
552 | + "decimals": null, | |
553 | + "funcBody": null, | |
554 | + "usePostProcessing": null, | |
555 | + "postFuncBody": null | |
556 | + } | |
557 | + ] | |
558 | + } | |
559 | + ], | |
560 | + "timewindow": { | |
561 | + "realtime": { | |
562 | + "timewindowMs": 60000 | |
563 | + } | |
564 | + }, | |
565 | + "showTitle": false, | |
566 | + "backgroundColor": "#fff", | |
567 | + "color": "rgba(0, 0, 0, 0.87)", | |
568 | + "padding": "0px", | |
569 | + "settings": { | |
570 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <img id=\"activeDevices\" src='data:image/svg+xml;utf8,<svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M36 14.24H22.44L27.92 8.6C22.46 3.2 13.62 3 8.16001 8.4C2.70001 13.82 2.70001 22.56 8.16001 27.98C13.62 33.4 22.46 33.4 27.92 27.98C30.64 25.3 32 22.16 32 18.2H36C36 22.16 34.24 27.3 30.72 30.78C23.7 37.74 12.3 37.74 5.28001 30.78C-1.71999 23.84 -1.77999 12.56 5.24001 5.62C12.26 -1.32 23.52 -1.32 30.54 5.62L36 0V14.24ZM19 10V18.5L26 22.66L24.56 25.08L16 20V10H19Z\" fill=\"black\"/>\n</svg>'>\n <div class='value' id=\"activeDevices\">\n ${updatingDevicesNumber:0}\n </div> \n <div class='description' id=\"activeDevices\">\n Device Updating\n </div>\n </div>\n</div>", | |
571 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
572 | + }, | |
573 | + "title": "New HTML Value Card", | |
574 | + "dropShadow": true, | |
575 | + "enableFullscreen": false, | |
576 | + "widgetStyle": {}, | |
577 | + "titleStyle": { | |
578 | + "fontSize": "16px", | |
579 | + "fontWeight": 400 | |
580 | + }, | |
581 | + "useDashboardTimewindow": true, | |
582 | + "showLegend": false, | |
583 | + "actions": { | |
584 | + "elementClick": [ | |
585 | + { | |
586 | + "name": "activeDevices", | |
587 | + "icon": "more_horiz", | |
588 | + "type": "openDashboardState", | |
589 | + "targetDashboardStateId": "device_updating", | |
590 | + "setEntityId": false, | |
591 | + "stateEntityParamName": null, | |
592 | + "openInSeparateDialog": false, | |
593 | + "dialogTitle": "", | |
594 | + "dialogHideDashboardToolbar": true, | |
595 | + "dialogWidth": null, | |
596 | + "dialogHeight": null, | |
597 | + "openRightLayout": false, | |
598 | + "id": "57d39904-2350-b29b-78ed-56b8268814cb" | |
599 | + } | |
600 | + ] | |
601 | + }, | |
602 | + "showTitleIcon": false, | |
603 | + "titleIcon": null, | |
604 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
605 | + "iconSize": "24px", | |
606 | + "titleTooltip": "", | |
607 | + "enableDataExport": false, | |
608 | + "displayTimewindow": true | |
609 | + }, | |
610 | + "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6" | |
611 | + }, | |
612 | + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { | |
613 | + "isSystemType": true, | |
614 | + "bundleAlias": "cards", | |
615 | + "typeAlias": "html_value_card", | |
616 | + "type": "latest", | |
617 | + "title": "New widget", | |
618 | + "sizeX": 8, | |
619 | + "sizeY": 3, | |
620 | + "config": { | |
621 | + "datasources": [ | |
622 | + { | |
623 | + "type": "entityCount", | |
624 | + "name": "", | |
625 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
626 | + "filterId": "6044e198-df64-cd76-f339-696f220c4943", | |
627 | + "dataKeys": [ | |
628 | + { | |
629 | + "name": "count", | |
630 | + "type": "count", | |
631 | + "label": "updatedDevicesNumber", | |
632 | + "color": "#4caf50", | |
633 | + "settings": {}, | |
634 | + "_hash": 0.7404827038869322, | |
635 | + "units": null, | |
636 | + "decimals": null, | |
637 | + "funcBody": null, | |
638 | + "usePostProcessing": null, | |
639 | + "postFuncBody": null | |
640 | + } | |
641 | + ] | |
642 | + } | |
643 | + ], | |
644 | + "timewindow": { | |
645 | + "realtime": { | |
646 | + "timewindowMs": 60000 | |
647 | + } | |
648 | + }, | |
649 | + "showTitle": false, | |
650 | + "backgroundColor": "#fff", | |
651 | + "color": "rgba(0, 0, 0, 0.87)", | |
652 | + "padding": "0px", | |
653 | + "settings": { | |
654 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <img id=\"activeDevices\" src='data:image/svg+xml;utf8,<svg width=\"34\" height=\"32\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/>\n<path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/>\n</svg>'>\n <div class='value' id=\"activeDevices\">\n ${updatedDevicesNumber:0}\n </div> \n <div class='description' id=\"activeDevices\">\n Device Updated\n </div>\n </div>\n</div>", | |
655 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
656 | + }, | |
657 | + "title": "New HTML Value Card", | |
658 | + "dropShadow": true, | |
659 | + "enableFullscreen": false, | |
660 | + "widgetStyle": {}, | |
661 | + "titleStyle": { | |
662 | + "fontSize": "16px", | |
663 | + "fontWeight": 400 | |
664 | + }, | |
665 | + "useDashboardTimewindow": true, | |
666 | + "showLegend": false, | |
667 | + "actions": { | |
668 | + "elementClick": [ | |
669 | + { | |
670 | + "name": "activeDevices", | |
671 | + "icon": "more_horiz", | |
672 | + "type": "openDashboardState", | |
673 | + "targetDashboardStateId": "device_updated", | |
674 | + "setEntityId": false, | |
675 | + "stateEntityParamName": null, | |
676 | + "openInSeparateDialog": false, | |
677 | + "dialogTitle": "", | |
678 | + "dialogHideDashboardToolbar": true, | |
679 | + "dialogWidth": null, | |
680 | + "dialogHeight": null, | |
681 | + "openRightLayout": false, | |
682 | + "id": "d787c212-8c56-34f0-349a-5aae2ffd1eae" | |
683 | + } | |
684 | + ] | |
685 | + }, | |
686 | + "showTitleIcon": false, | |
687 | + "titleIcon": null, | |
688 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
689 | + "iconSize": "24px", | |
690 | + "titleTooltip": "", | |
691 | + "enableDataExport": false, | |
692 | + "displayTimewindow": true | |
693 | + }, | |
694 | + "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81" | |
695 | + }, | |
696 | + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { | |
697 | + "isSystemType": true, | |
698 | + "bundleAlias": "cards", | |
699 | + "typeAlias": "html_value_card", | |
700 | + "type": "latest", | |
701 | + "title": "New widget", | |
702 | + "sizeX": 8, | |
703 | + "sizeY": 3, | |
704 | + "config": { | |
705 | + "datasources": [ | |
706 | + { | |
707 | + "type": "entityCount", | |
708 | + "name": "", | |
709 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
710 | + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
711 | + "dataKeys": [ | |
712 | + { | |
713 | + "name": "count", | |
714 | + "type": "count", | |
715 | + "label": "updatingDevicesNumber", | |
716 | + "color": "#4caf50", | |
717 | + "settings": {}, | |
718 | + "_hash": 0.7404827038869322, | |
719 | + "units": null, | |
720 | + "decimals": null, | |
721 | + "funcBody": null, | |
722 | + "usePostProcessing": null, | |
723 | + "postFuncBody": null | |
724 | + } | |
725 | + ] | |
726 | + } | |
727 | + ], | |
728 | + "timewindow": { | |
729 | + "realtime": { | |
730 | + "timewindowMs": 60000 | |
731 | + } | |
732 | + }, | |
733 | + "showTitle": false, | |
734 | + "backgroundColor": "#fff", | |
735 | + "color": "rgba(0, 0, 0, 0.87)", | |
736 | + "padding": "0px", | |
737 | + "settings": { | |
738 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <div class=\"container-svg\" id=\"activeDevices\">\n <svg viewBox=\"0 0 24 24\" id=\"activeDevices\">\n <path id=\"activeDevices\" fill=\"currentColor\" d=\"M13 14H11V9H13M13 18H11V16H13M1 21H23L12 2L1 21Z\" />\n </svg>\n </div>\n <div class='value error_firmware_failed_count' id=\"activeDevices\">\n ${updatingDevicesNumber:0}\n </div> \n <script type=\"text/javascript\">\n function init() {\n var counter = $('.error_firmware_failed_count');\n var value = +counter.text();\n if(value) {\n counter.css('color', '#D93025');\n }\n };\n init();\n </script>\n <div class='description' id=\"activeDevices\">\n Device Failed\n </div>\n </div>\n</div>", | |
739 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .container-svg {\n height: 40px;\n width: 40px;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .container-svg {\n height: 28px;\n width: 28px;\n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
740 | + }, | |
741 | + "title": "New HTML Value Card", | |
742 | + "dropShadow": true, | |
743 | + "enableFullscreen": false, | |
744 | + "widgetStyle": {}, | |
745 | + "titleStyle": { | |
746 | + "fontSize": "16px", | |
747 | + "fontWeight": 400 | |
748 | + }, | |
749 | + "useDashboardTimewindow": true, | |
750 | + "showLegend": false, | |
751 | + "actions": { | |
752 | + "elementClick": [ | |
753 | + { | |
754 | + "name": "activeDevices", | |
755 | + "icon": "more_horiz", | |
756 | + "type": "openDashboardState", | |
757 | + "targetDashboardStateId": "device_error", | |
758 | + "setEntityId": false, | |
759 | + "stateEntityParamName": null, | |
760 | + "openInSeparateDialog": false, | |
761 | + "dialogTitle": "", | |
762 | + "dialogHideDashboardToolbar": true, | |
763 | + "dialogWidth": null, | |
764 | + "dialogHeight": null, | |
765 | + "openRightLayout": false, | |
766 | + "id": "0b3d2887-9929-84d5-3795-0763dca15cba" | |
767 | + } | |
768 | + ] | |
769 | + }, | |
770 | + "showTitleIcon": false, | |
771 | + "titleIcon": null, | |
772 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
773 | + "iconSize": "24px", | |
774 | + "titleTooltip": "", | |
775 | + "enableDataExport": false, | |
776 | + "displayTimewindow": true | |
777 | + }, | |
778 | + "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8" | |
779 | + }, | |
780 | + "21be08bb-ec90-f760-ad6f-e7678f12c401": { | |
781 | + "isSystemType": true, | |
782 | + "bundleAlias": "cards", | |
783 | + "typeAlias": "entities_table", | |
784 | + "type": "latest", | |
785 | + "title": "New widget", | |
786 | + "image": null, | |
787 | + "description": null, | |
788 | + "sizeX": 7.5, | |
789 | + "sizeY": 6.5, | |
790 | + "config": { | |
791 | + "timewindow": { | |
792 | + "realtime": { | |
793 | + "interval": 1000, | |
794 | + "timewindowMs": 86400000 | |
795 | + }, | |
796 | + "aggregation": { | |
797 | + "type": "NONE", | |
798 | + "limit": 200 | |
799 | + } | |
800 | + }, | |
801 | + "showTitle": true, | |
802 | + "backgroundColor": "rgb(255, 255, 255)", | |
803 | + "color": "rgba(0, 0, 0, 0.87)", | |
804 | + "padding": "4px", | |
805 | + "settings": { | |
806 | + "enableSearch": true, | |
807 | + "displayPagination": true, | |
808 | + "defaultPageSize": 10, | |
809 | + "defaultSortOrder": "entityLabel", | |
810 | + "displayEntityName": false, | |
811 | + "displayEntityType": false, | |
812 | + "enableSelectColumnDisplay": false, | |
813 | + "enableStickyHeader": true, | |
814 | + "enableStickyAction": true, | |
815 | + "entitiesTitle": "Devices", | |
816 | + "displayEntityLabel": true, | |
817 | + "entityLabelColumnTitle": "Device" | |
818 | + }, | |
819 | + "title": "New Entities table", | |
820 | + "dropShadow": true, | |
821 | + "enableFullscreen": true, | |
822 | + "titleStyle": { | |
823 | + "fontSize": "16px", | |
824 | + "fontWeight": 400, | |
825 | + "padding": "5px 10px 5px 10px" | |
826 | + }, | |
827 | + "useDashboardTimewindow": false, | |
828 | + "showLegend": false, | |
829 | + "datasources": [ | |
830 | + { | |
831 | + "type": "entity", | |
832 | + "name": null, | |
833 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
834 | + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
835 | + "dataKeys": [ | |
836 | + { | |
837 | + "name": "current_fw_title", | |
838 | + "type": "timeseries", | |
839 | + "label": "Current FW title", | |
840 | + "color": "#2196f3", | |
841 | + "settings": { | |
842 | + "columnWidth": "0px", | |
843 | + "useCellStyleFunction": false, | |
844 | + "cellStyleFunction": "", | |
845 | + "useCellContentFunction": false, | |
846 | + "defaultColumnVisibility": "visible", | |
847 | + "columnSelectionToDisplay": "enabled" | |
848 | + }, | |
849 | + "_hash": 0.09545533885166413, | |
850 | + "units": null, | |
851 | + "decimals": null, | |
852 | + "funcBody": null, | |
853 | + "usePostProcessing": null, | |
854 | + "postFuncBody": null | |
855 | + }, | |
856 | + { | |
857 | + "name": "current_fw_version", | |
858 | + "type": "timeseries", | |
859 | + "label": "Current FW version", | |
860 | + "color": "#4caf50", | |
861 | + "settings": { | |
862 | + "columnWidth": "0px", | |
863 | + "useCellStyleFunction": false, | |
864 | + "cellStyleFunction": "", | |
865 | + "useCellContentFunction": false, | |
866 | + "defaultColumnVisibility": "visible", | |
867 | + "columnSelectionToDisplay": "enabled" | |
868 | + }, | |
869 | + "_hash": 0.7206056602328659, | |
870 | + "units": null, | |
871 | + "decimals": null, | |
872 | + "funcBody": null, | |
873 | + "usePostProcessing": null, | |
874 | + "postFuncBody": null | |
875 | + }, | |
876 | + { | |
877 | + "name": "target_fw_title", | |
878 | + "type": "timeseries", | |
879 | + "label": "Target FW title", | |
880 | + "color": "#ffc107", | |
881 | + "settings": { | |
882 | + "columnWidth": "0px", | |
883 | + "useCellStyleFunction": false, | |
884 | + "cellStyleFunction": "", | |
885 | + "useCellContentFunction": false, | |
886 | + "defaultColumnVisibility": "visible", | |
887 | + "columnSelectionToDisplay": "enabled" | |
888 | + }, | |
889 | + "_hash": 0.9934225682766313, | |
890 | + "units": null, | |
891 | + "decimals": null, | |
892 | + "funcBody": null, | |
893 | + "usePostProcessing": null, | |
894 | + "postFuncBody": null | |
895 | + }, | |
896 | + { | |
897 | + "name": "target_fw_version", | |
898 | + "type": "timeseries", | |
899 | + "label": "Target FW version", | |
900 | + "color": "#607d8b", | |
901 | + "settings": { | |
902 | + "columnWidth": "0px", | |
903 | + "useCellStyleFunction": false, | |
904 | + "cellStyleFunction": "", | |
905 | + "useCellContentFunction": false, | |
906 | + "cellContentFunction": "", | |
907 | + "defaultColumnVisibility": "visible", | |
908 | + "columnSelectionToDisplay": "enabled" | |
909 | + }, | |
910 | + "_hash": 0.5251724416842531, | |
911 | + "units": null, | |
912 | + "decimals": null, | |
913 | + "funcBody": null, | |
914 | + "usePostProcessing": null, | |
915 | + "postFuncBody": null | |
916 | + }, | |
917 | + { | |
918 | + "name": "target_fw_ts", | |
919 | + "type": "timeseries", | |
920 | + "label": "Target FW set time", | |
921 | + "color": "#e91e63", | |
922 | + "settings": { | |
923 | + "columnWidth": "0px", | |
924 | + "useCellStyleFunction": false, | |
925 | + "cellStyleFunction": "", | |
926 | + "useCellContentFunction": true, | |
927 | + "defaultColumnVisibility": "visible", | |
928 | + "columnSelectionToDisplay": "enabled", | |
929 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
930 | + }, | |
931 | + "_hash": 0.31823244858578237, | |
932 | + "units": null, | |
933 | + "decimals": null, | |
934 | + "funcBody": null, | |
935 | + "usePostProcessing": null, | |
936 | + "postFuncBody": null | |
937 | + }, | |
938 | + { | |
939 | + "name": "fw_state", | |
940 | + "type": "timeseries", | |
941 | + "label": "Progress", | |
942 | + "color": "#9c27b0", | |
943 | + "settings": { | |
944 | + "columnWidth": "30%", | |
945 | + "useCellStyleFunction": true, | |
946 | + "useCellContentFunction": true, | |
947 | + "defaultColumnVisibility": "visible", | |
948 | + "columnSelectionToDisplay": "enabled", | |
949 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
950 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
951 | + }, | |
952 | + "_hash": 0.8174211757846257, | |
953 | + "units": null, | |
954 | + "decimals": null, | |
955 | + "funcBody": null, | |
956 | + "usePostProcessing": null, | |
957 | + "postFuncBody": null | |
958 | + }, | |
959 | + { | |
960 | + "name": "fw_state", | |
961 | + "type": "timeseries", | |
962 | + "label": "Status", | |
963 | + "color": "#f44336", | |
964 | + "settings": { | |
965 | + "columnWidth": "130px", | |
966 | + "useCellStyleFunction": true, | |
967 | + "useCellContentFunction": true, | |
968 | + "defaultColumnVisibility": "visible", | |
969 | + "columnSelectionToDisplay": "enabled", | |
970 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
971 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
972 | + }, | |
973 | + "_hash": 0.7764426948615217, | |
974 | + "units": null, | |
975 | + "decimals": null, | |
976 | + "funcBody": null, | |
977 | + "usePostProcessing": null, | |
978 | + "postFuncBody": null | |
979 | + }, | |
980 | + { | |
981 | + "name": "fw_checksum", | |
982 | + "type": "attribute", | |
983 | + "label": "fw_checksum", | |
984 | + "color": "#3f51b5", | |
985 | + "settings": { | |
986 | + "columnWidth": "0px", | |
987 | + "useCellStyleFunction": false, | |
988 | + "cellStyleFunction": "", | |
989 | + "useCellContentFunction": false, | |
990 | + "defaultColumnVisibility": "hidden", | |
991 | + "columnSelectionToDisplay": "disabled" | |
992 | + }, | |
993 | + "_hash": 0.5594087842471693, | |
994 | + "units": null, | |
995 | + "decimals": null, | |
996 | + "funcBody": null, | |
997 | + "usePostProcessing": null, | |
998 | + "postFuncBody": null | |
999 | + } | |
1000 | + ] | |
1001 | + } | |
1002 | + ], | |
1003 | + "actions": { | |
1004 | + "actionCellButton": [ | |
1005 | + { | |
1006 | + "name": "History firmware update", | |
1007 | + "icon": "history", | |
1008 | + "type": "openDashboardState", | |
1009 | + "targetDashboardStateId": "device_firmware_history", | |
1010 | + "setEntityId": true, | |
1011 | + "stateEntityParamName": null, | |
1012 | + "openInSeparateDialog": false, | |
1013 | + "dialogTitle": "", | |
1014 | + "dialogHideDashboardToolbar": true, | |
1015 | + "dialogWidth": null, | |
1016 | + "dialogHeight": null, | |
1017 | + "openRightLayout": false, | |
1018 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1019 | + }, | |
1020 | + { | |
1021 | + "name": "Edit firmware", | |
1022 | + "icon": "edit", | |
1023 | + "type": "customPretty", | |
1024 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1025 | + "customCss": "", | |
1026 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1027 | + "customResources": [], | |
1028 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1029 | + }, | |
1030 | + { | |
1031 | + "name": "Download firware", | |
1032 | + "icon": "file_download", | |
1033 | + "type": "custom", | |
1034 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1035 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1036 | + }, | |
1037 | + { | |
1038 | + "name": "Copy checksum", | |
1039 | + "icon": "content_copy", | |
1040 | + "type": "custom", | |
1041 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1042 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1043 | + } | |
1044 | + ] | |
1045 | + }, | |
1046 | + "showTitleIcon": false, | |
1047 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1048 | + "iconSize": "24px", | |
1049 | + "titleTooltip": "", | |
1050 | + "widgetStyle": {} | |
1051 | + }, | |
1052 | + "row": 0, | |
1053 | + "col": 0, | |
1054 | + "id": "21be08bb-ec90-f760-ad6f-e7678f12c401" | |
1055 | + }, | |
1056 | + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { | |
1057 | + "isSystemType": true, | |
1058 | + "bundleAlias": "cards", | |
1059 | + "typeAlias": "entities_table", | |
1060 | + "type": "latest", | |
1061 | + "title": "New widget", | |
1062 | + "image": null, | |
1063 | + "description": null, | |
1064 | + "sizeX": 7.5, | |
1065 | + "sizeY": 6.5, | |
1066 | + "config": { | |
1067 | + "timewindow": { | |
1068 | + "realtime": { | |
1069 | + "interval": 1000, | |
1070 | + "timewindowMs": 86400000 | |
1071 | + }, | |
1072 | + "aggregation": { | |
1073 | + "type": "NONE", | |
1074 | + "limit": 200 | |
1075 | + } | |
1076 | + }, | |
1077 | + "showTitle": true, | |
1078 | + "backgroundColor": "rgb(255, 255, 255)", | |
1079 | + "color": "rgba(0, 0, 0, 0.87)", | |
1080 | + "padding": "4px", | |
1081 | + "settings": { | |
1082 | + "enableSearch": true, | |
1083 | + "displayPagination": true, | |
1084 | + "defaultPageSize": 10, | |
1085 | + "defaultSortOrder": "entityLabel", | |
1086 | + "displayEntityName": false, | |
1087 | + "displayEntityType": false, | |
1088 | + "enableSelectColumnDisplay": false, | |
1089 | + "enableStickyHeader": true, | |
1090 | + "enableStickyAction": true, | |
1091 | + "entitiesTitle": "Devices", | |
1092 | + "displayEntityLabel": true, | |
1093 | + "entityLabelColumnTitle": "Device" | |
1094 | + }, | |
1095 | + "title": "New Entities table", | |
1096 | + "dropShadow": true, | |
1097 | + "enableFullscreen": true, | |
1098 | + "titleStyle": { | |
1099 | + "fontSize": "16px", | |
1100 | + "fontWeight": 400, | |
1101 | + "padding": "5px 10px 5px 10px" | |
1102 | + }, | |
1103 | + "useDashboardTimewindow": false, | |
1104 | + "showLegend": false, | |
1105 | + "datasources": [ | |
1106 | + { | |
1107 | + "type": "entity", | |
1108 | + "name": null, | |
1109 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1110 | + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
1111 | + "dataKeys": [ | |
1112 | + { | |
1113 | + "name": "current_fw_title", | |
1114 | + "type": "timeseries", | |
1115 | + "label": "Current FW title", | |
1116 | + "color": "#2196f3", | |
1117 | + "settings": { | |
1118 | + "columnWidth": "0px", | |
1119 | + "useCellStyleFunction": false, | |
1120 | + "cellStyleFunction": "", | |
1121 | + "useCellContentFunction": false, | |
1122 | + "defaultColumnVisibility": "visible", | |
1123 | + "columnSelectionToDisplay": "enabled" | |
1124 | + }, | |
1125 | + "_hash": 0.09545533885166413, | |
1126 | + "units": null, | |
1127 | + "decimals": null, | |
1128 | + "funcBody": null, | |
1129 | + "usePostProcessing": null, | |
1130 | + "postFuncBody": null | |
1131 | + }, | |
1132 | + { | |
1133 | + "name": "current_fw_version", | |
1134 | + "type": "timeseries", | |
1135 | + "label": "Current FW version", | |
1136 | + "color": "#4caf50", | |
1137 | + "settings": { | |
1138 | + "columnWidth": "0px", | |
1139 | + "useCellStyleFunction": false, | |
1140 | + "cellStyleFunction": "", | |
1141 | + "useCellContentFunction": false, | |
1142 | + "defaultColumnVisibility": "visible", | |
1143 | + "columnSelectionToDisplay": "enabled" | |
1144 | + }, | |
1145 | + "_hash": 0.7206056602328659, | |
1146 | + "units": null, | |
1147 | + "decimals": null, | |
1148 | + "funcBody": null, | |
1149 | + "usePostProcessing": null, | |
1150 | + "postFuncBody": null | |
1151 | + }, | |
1152 | + { | |
1153 | + "name": "target_fw_title", | |
1154 | + "type": "timeseries", | |
1155 | + "label": "Target FW title", | |
1156 | + "color": "#ffc107", | |
1157 | + "settings": { | |
1158 | + "columnWidth": "0px", | |
1159 | + "useCellStyleFunction": false, | |
1160 | + "cellStyleFunction": "", | |
1161 | + "useCellContentFunction": false, | |
1162 | + "defaultColumnVisibility": "visible", | |
1163 | + "columnSelectionToDisplay": "enabled" | |
1164 | + }, | |
1165 | + "_hash": 0.9934225682766313, | |
1166 | + "units": null, | |
1167 | + "decimals": null, | |
1168 | + "funcBody": null, | |
1169 | + "usePostProcessing": null, | |
1170 | + "postFuncBody": null | |
1171 | + }, | |
1172 | + { | |
1173 | + "name": "target_fw_version", | |
1174 | + "type": "timeseries", | |
1175 | + "label": "Target FW version", | |
1176 | + "color": "#607d8b", | |
1177 | + "settings": { | |
1178 | + "columnWidth": "0px", | |
1179 | + "useCellStyleFunction": false, | |
1180 | + "cellStyleFunction": "", | |
1181 | + "useCellContentFunction": false, | |
1182 | + "cellContentFunction": "", | |
1183 | + "defaultColumnVisibility": "visible", | |
1184 | + "columnSelectionToDisplay": "enabled" | |
1185 | + }, | |
1186 | + "_hash": 0.5251724416842531, | |
1187 | + "units": null, | |
1188 | + "decimals": null, | |
1189 | + "funcBody": null, | |
1190 | + "usePostProcessing": null, | |
1191 | + "postFuncBody": null | |
1192 | + }, | |
1193 | + { | |
1194 | + "name": "target_fw_ts", | |
1195 | + "type": "timeseries", | |
1196 | + "label": "Target FW set time", | |
1197 | + "color": "#e91e63", | |
1198 | + "settings": { | |
1199 | + "columnWidth": "0px", | |
1200 | + "useCellStyleFunction": false, | |
1201 | + "cellStyleFunction": "", | |
1202 | + "useCellContentFunction": true, | |
1203 | + "defaultColumnVisibility": "visible", | |
1204 | + "columnSelectionToDisplay": "enabled", | |
1205 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1206 | + }, | |
1207 | + "_hash": 0.31823244858578237, | |
1208 | + "units": null, | |
1209 | + "decimals": null, | |
1210 | + "funcBody": null, | |
1211 | + "usePostProcessing": null, | |
1212 | + "postFuncBody": null | |
1213 | + }, | |
1214 | + { | |
1215 | + "name": "fw_state", | |
1216 | + "type": "timeseries", | |
1217 | + "label": "Progress", | |
1218 | + "color": "#9c27b0", | |
1219 | + "settings": { | |
1220 | + "columnWidth": "30%", | |
1221 | + "useCellStyleFunction": true, | |
1222 | + "useCellContentFunction": true, | |
1223 | + "defaultColumnVisibility": "visible", | |
1224 | + "columnSelectionToDisplay": "enabled", | |
1225 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1226 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
1227 | + }, | |
1228 | + "_hash": 0.8174211757846257, | |
1229 | + "units": null, | |
1230 | + "decimals": null, | |
1231 | + "funcBody": null, | |
1232 | + "usePostProcessing": null, | |
1233 | + "postFuncBody": null | |
1234 | + }, | |
1235 | + { | |
1236 | + "name": "fw_state", | |
1237 | + "type": "timeseries", | |
1238 | + "label": "Status", | |
1239 | + "color": "#f44336", | |
1240 | + "settings": { | |
1241 | + "columnWidth": "130px", | |
1242 | + "useCellStyleFunction": true, | |
1243 | + "useCellContentFunction": true, | |
1244 | + "defaultColumnVisibility": "visible", | |
1245 | + "columnSelectionToDisplay": "enabled", | |
1246 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1247 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
1248 | + }, | |
1249 | + "_hash": 0.7764426948615217, | |
1250 | + "units": null, | |
1251 | + "decimals": null, | |
1252 | + "funcBody": null, | |
1253 | + "usePostProcessing": null, | |
1254 | + "postFuncBody": null | |
1255 | + }, | |
1256 | + { | |
1257 | + "name": "fw_checksum", | |
1258 | + "type": "attribute", | |
1259 | + "label": "fw_checksum", | |
1260 | + "color": "#3f51b5", | |
1261 | + "settings": { | |
1262 | + "columnWidth": "0px", | |
1263 | + "useCellStyleFunction": false, | |
1264 | + "cellStyleFunction": "", | |
1265 | + "useCellContentFunction": false, | |
1266 | + "defaultColumnVisibility": "hidden", | |
1267 | + "columnSelectionToDisplay": "disabled" | |
1268 | + }, | |
1269 | + "_hash": 0.5594087842471693, | |
1270 | + "units": null, | |
1271 | + "decimals": null, | |
1272 | + "funcBody": null, | |
1273 | + "usePostProcessing": null, | |
1274 | + "postFuncBody": null | |
1275 | + } | |
1276 | + ] | |
1277 | + } | |
1278 | + ], | |
1279 | + "actions": { | |
1280 | + "actionCellButton": [ | |
1281 | + { | |
1282 | + "name": "History firmware update", | |
1283 | + "icon": "history", | |
1284 | + "type": "openDashboardState", | |
1285 | + "targetDashboardStateId": "device_firmware_history", | |
1286 | + "setEntityId": true, | |
1287 | + "stateEntityParamName": null, | |
1288 | + "openInSeparateDialog": false, | |
1289 | + "dialogTitle": "", | |
1290 | + "dialogHideDashboardToolbar": true, | |
1291 | + "dialogWidth": null, | |
1292 | + "dialogHeight": null, | |
1293 | + "openRightLayout": false, | |
1294 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1295 | + }, | |
1296 | + { | |
1297 | + "name": "Edit firmware", | |
1298 | + "icon": "edit", | |
1299 | + "type": "customPretty", | |
1300 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1301 | + "customCss": "", | |
1302 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1303 | + "customResources": [], | |
1304 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1305 | + }, | |
1306 | + { | |
1307 | + "name": "Download firware", | |
1308 | + "icon": "file_download", | |
1309 | + "type": "custom", | |
1310 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1311 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1312 | + }, | |
1313 | + { | |
1314 | + "name": "Copy checksum", | |
1315 | + "icon": "content_copy", | |
1316 | + "type": "custom", | |
1317 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1318 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1319 | + } | |
1320 | + ] | |
1321 | + }, | |
1322 | + "showTitleIcon": false, | |
1323 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1324 | + "iconSize": "24px", | |
1325 | + "titleTooltip": "", | |
1326 | + "widgetStyle": {} | |
1327 | + }, | |
1328 | + "row": 0, | |
1329 | + "col": 0, | |
1330 | + "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91" | |
1331 | + }, | |
1332 | + "3624013b-378c-f110-5eba-ae95c25a4dcc": { | |
1333 | + "isSystemType": true, | |
1334 | + "bundleAlias": "cards", | |
1335 | + "typeAlias": "entities_table", | |
1336 | + "type": "latest", | |
1337 | + "title": "New widget", | |
1338 | + "image": null, | |
1339 | + "description": null, | |
1340 | + "sizeX": 7.5, | |
1341 | + "sizeY": 6.5, | |
1342 | + "config": { | |
1343 | + "timewindow": { | |
1344 | + "realtime": { | |
1345 | + "interval": 1000, | |
1346 | + "timewindowMs": 86400000 | |
1347 | + }, | |
1348 | + "aggregation": { | |
1349 | + "type": "NONE", | |
1350 | + "limit": 200 | |
1351 | + } | |
1352 | + }, | |
1353 | + "showTitle": true, | |
1354 | + "backgroundColor": "rgb(255, 255, 255)", | |
1355 | + "color": "rgba(0, 0, 0, 0.87)", | |
1356 | + "padding": "4px", | |
1357 | + "settings": { | |
1358 | + "enableSearch": true, | |
1359 | + "displayPagination": true, | |
1360 | + "defaultPageSize": 10, | |
1361 | + "defaultSortOrder": "entityLabel", | |
1362 | + "displayEntityName": false, | |
1363 | + "displayEntityType": false, | |
1364 | + "enableSelectColumnDisplay": false, | |
1365 | + "enableStickyHeader": true, | |
1366 | + "enableStickyAction": true, | |
1367 | + "entitiesTitle": "Devices", | |
1368 | + "displayEntityLabel": true, | |
1369 | + "entityLabelColumnTitle": "Device" | |
1370 | + }, | |
1371 | + "title": "New Entities table", | |
1372 | + "dropShadow": true, | |
1373 | + "enableFullscreen": true, | |
1374 | + "titleStyle": { | |
1375 | + "fontSize": "16px", | |
1376 | + "fontWeight": 400, | |
1377 | + "padding": "5px 10px 5px 10px" | |
1378 | + }, | |
1379 | + "useDashboardTimewindow": false, | |
1380 | + "showLegend": false, | |
1381 | + "datasources": [ | |
1382 | + { | |
1383 | + "type": "entity", | |
1384 | + "name": null, | |
1385 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1386 | + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
1387 | + "dataKeys": [ | |
1388 | + { | |
1389 | + "name": "current_fw_title", | |
1390 | + "type": "timeseries", | |
1391 | + "label": "Current FW title", | |
1392 | + "color": "#2196f3", | |
1393 | + "settings": { | |
1394 | + "columnWidth": "0px", | |
1395 | + "useCellStyleFunction": false, | |
1396 | + "cellStyleFunction": "", | |
1397 | + "useCellContentFunction": false, | |
1398 | + "defaultColumnVisibility": "visible", | |
1399 | + "columnSelectionToDisplay": "enabled" | |
1400 | + }, | |
1401 | + "_hash": 0.09545533885166413, | |
1402 | + "units": null, | |
1403 | + "decimals": null, | |
1404 | + "funcBody": null, | |
1405 | + "usePostProcessing": null, | |
1406 | + "postFuncBody": null | |
1407 | + }, | |
1408 | + { | |
1409 | + "name": "current_fw_version", | |
1410 | + "type": "timeseries", | |
1411 | + "label": "Current FW version", | |
1412 | + "color": "#4caf50", | |
1413 | + "settings": { | |
1414 | + "columnWidth": "0px", | |
1415 | + "useCellStyleFunction": false, | |
1416 | + "cellStyleFunction": "", | |
1417 | + "useCellContentFunction": false, | |
1418 | + "defaultColumnVisibility": "visible", | |
1419 | + "columnSelectionToDisplay": "enabled" | |
1420 | + }, | |
1421 | + "_hash": 0.7206056602328659, | |
1422 | + "units": null, | |
1423 | + "decimals": null, | |
1424 | + "funcBody": null, | |
1425 | + "usePostProcessing": null, | |
1426 | + "postFuncBody": null | |
1427 | + }, | |
1428 | + { | |
1429 | + "name": "target_fw_title", | |
1430 | + "type": "timeseries", | |
1431 | + "label": "Target FW title", | |
1432 | + "color": "#ffc107", | |
1433 | + "settings": { | |
1434 | + "columnWidth": "0px", | |
1435 | + "useCellStyleFunction": false, | |
1436 | + "cellStyleFunction": "", | |
1437 | + "useCellContentFunction": false, | |
1438 | + "defaultColumnVisibility": "visible", | |
1439 | + "columnSelectionToDisplay": "enabled" | |
1440 | + }, | |
1441 | + "_hash": 0.9934225682766313, | |
1442 | + "units": null, | |
1443 | + "decimals": null, | |
1444 | + "funcBody": null, | |
1445 | + "usePostProcessing": null, | |
1446 | + "postFuncBody": null | |
1447 | + }, | |
1448 | + { | |
1449 | + "name": "target_fw_version", | |
1450 | + "type": "timeseries", | |
1451 | + "label": "Target FW version", | |
1452 | + "color": "#607d8b", | |
1453 | + "settings": { | |
1454 | + "columnWidth": "0px", | |
1455 | + "useCellStyleFunction": false, | |
1456 | + "cellStyleFunction": "", | |
1457 | + "useCellContentFunction": false, | |
1458 | + "cellContentFunction": "", | |
1459 | + "defaultColumnVisibility": "visible", | |
1460 | + "columnSelectionToDisplay": "enabled" | |
1461 | + }, | |
1462 | + "_hash": 0.5251724416842531, | |
1463 | + "units": null, | |
1464 | + "decimals": null, | |
1465 | + "funcBody": null, | |
1466 | + "usePostProcessing": null, | |
1467 | + "postFuncBody": null | |
1468 | + }, | |
1469 | + { | |
1470 | + "name": "target_fw_ts", | |
1471 | + "type": "timeseries", | |
1472 | + "label": "Target FW set time", | |
1473 | + "color": "#e91e63", | |
1474 | + "settings": { | |
1475 | + "columnWidth": "0px", | |
1476 | + "useCellStyleFunction": false, | |
1477 | + "cellStyleFunction": "", | |
1478 | + "useCellContentFunction": true, | |
1479 | + "defaultColumnVisibility": "visible", | |
1480 | + "columnSelectionToDisplay": "enabled", | |
1481 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1482 | + }, | |
1483 | + "_hash": 0.31823244858578237, | |
1484 | + "units": null, | |
1485 | + "decimals": null, | |
1486 | + "funcBody": null, | |
1487 | + "usePostProcessing": null, | |
1488 | + "postFuncBody": null | |
1489 | + }, | |
1490 | + { | |
1491 | + "name": "fw_state", | |
1492 | + "type": "timeseries", | |
1493 | + "label": "Progress", | |
1494 | + "color": "#9c27b0", | |
1495 | + "settings": { | |
1496 | + "columnWidth": "30%", | |
1497 | + "useCellStyleFunction": true, | |
1498 | + "useCellContentFunction": true, | |
1499 | + "defaultColumnVisibility": "visible", | |
1500 | + "columnSelectionToDisplay": "enabled", | |
1501 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1502 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
1503 | + }, | |
1504 | + "_hash": 0.8174211757846257, | |
1505 | + "units": null, | |
1506 | + "decimals": null, | |
1507 | + "funcBody": null, | |
1508 | + "usePostProcessing": null, | |
1509 | + "postFuncBody": null | |
1510 | + }, | |
1511 | + { | |
1512 | + "name": "fw_state", | |
1513 | + "type": "timeseries", | |
1514 | + "label": "Status", | |
1515 | + "color": "#f44336", | |
1516 | + "settings": { | |
1517 | + "columnWidth": "130px", | |
1518 | + "useCellStyleFunction": true, | |
1519 | + "useCellContentFunction": true, | |
1520 | + "defaultColumnVisibility": "visible", | |
1521 | + "columnSelectionToDisplay": "enabled", | |
1522 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1523 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
1524 | + }, | |
1525 | + "_hash": 0.7764426948615217, | |
1526 | + "units": null, | |
1527 | + "decimals": null, | |
1528 | + "funcBody": null, | |
1529 | + "usePostProcessing": null, | |
1530 | + "postFuncBody": null | |
1531 | + }, | |
1532 | + { | |
1533 | + "name": "fw_checksum", | |
1534 | + "type": "attribute", | |
1535 | + "label": "fw_checksum", | |
1536 | + "color": "#3f51b5", | |
1537 | + "settings": { | |
1538 | + "columnWidth": "0px", | |
1539 | + "useCellStyleFunction": false, | |
1540 | + "cellStyleFunction": "", | |
1541 | + "useCellContentFunction": false, | |
1542 | + "defaultColumnVisibility": "hidden", | |
1543 | + "columnSelectionToDisplay": "disabled" | |
1544 | + }, | |
1545 | + "_hash": 0.5594087842471693, | |
1546 | + "units": null, | |
1547 | + "decimals": null, | |
1548 | + "funcBody": null, | |
1549 | + "usePostProcessing": null, | |
1550 | + "postFuncBody": null | |
1551 | + } | |
1552 | + ] | |
1553 | + } | |
1554 | + ], | |
1555 | + "actions": { | |
1556 | + "actionCellButton": [ | |
1557 | + { | |
1558 | + "name": "History firmware update", | |
1559 | + "icon": "history", | |
1560 | + "type": "openDashboardState", | |
1561 | + "targetDashboardStateId": "device_firmware_history", | |
1562 | + "setEntityId": true, | |
1563 | + "stateEntityParamName": null, | |
1564 | + "openInSeparateDialog": false, | |
1565 | + "dialogTitle": "", | |
1566 | + "dialogHideDashboardToolbar": true, | |
1567 | + "dialogWidth": null, | |
1568 | + "dialogHeight": null, | |
1569 | + "openRightLayout": false, | |
1570 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1571 | + }, | |
1572 | + { | |
1573 | + "name": "Edit firmware", | |
1574 | + "icon": "edit", | |
1575 | + "type": "customPretty", | |
1576 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1577 | + "customCss": "", | |
1578 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1579 | + "customResources": [], | |
1580 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1581 | + }, | |
1582 | + { | |
1583 | + "name": "Download firware", | |
1584 | + "icon": "file_download", | |
1585 | + "type": "custom", | |
1586 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1587 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1588 | + }, | |
1589 | + { | |
1590 | + "name": "Copy checksum", | |
1591 | + "icon": "content_copy", | |
1592 | + "type": "custom", | |
1593 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1594 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1595 | + } | |
1596 | + ] | |
1597 | + }, | |
1598 | + "showTitleIcon": false, | |
1599 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1600 | + "iconSize": "24px", | |
1601 | + "titleTooltip": "", | |
1602 | + "widgetStyle": {} | |
1603 | + }, | |
1604 | + "row": 0, | |
1605 | + "col": 0, | |
1606 | + "id": "3624013b-378c-f110-5eba-ae95c25a4dcc" | |
1607 | + }, | |
1608 | + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { | |
1609 | + "isSystemType": true, | |
1610 | + "bundleAlias": "cards", | |
1611 | + "typeAlias": "entities_table", | |
1612 | + "type": "latest", | |
1613 | + "title": "New widget", | |
1614 | + "image": null, | |
1615 | + "description": null, | |
1616 | + "sizeX": 7.5, | |
1617 | + "sizeY": 6.5, | |
1618 | + "config": { | |
1619 | + "timewindow": { | |
1620 | + "realtime": { | |
1621 | + "interval": 1000, | |
1622 | + "timewindowMs": 86400000 | |
1623 | + }, | |
1624 | + "aggregation": { | |
1625 | + "type": "NONE", | |
1626 | + "limit": 200 | |
1627 | + } | |
1628 | + }, | |
1629 | + "showTitle": true, | |
1630 | + "backgroundColor": "rgb(255, 255, 255)", | |
1631 | + "color": "rgba(0, 0, 0, 0.87)", | |
1632 | + "padding": "4px", | |
1633 | + "settings": { | |
1634 | + "enableSearch": true, | |
1635 | + "displayPagination": true, | |
1636 | + "defaultPageSize": 10, | |
1637 | + "defaultSortOrder": "entityLabel", | |
1638 | + "displayEntityName": false, | |
1639 | + "displayEntityType": false, | |
1640 | + "enableSelectColumnDisplay": false, | |
1641 | + "enableStickyHeader": true, | |
1642 | + "enableStickyAction": true, | |
1643 | + "entitiesTitle": "Devices", | |
1644 | + "displayEntityLabel": true, | |
1645 | + "entityLabelColumnTitle": "Device" | |
1646 | + }, | |
1647 | + "title": "New Entities table", | |
1648 | + "dropShadow": true, | |
1649 | + "enableFullscreen": true, | |
1650 | + "titleStyle": { | |
1651 | + "fontSize": "16px", | |
1652 | + "fontWeight": 400, | |
1653 | + "padding": "5px 10px 5px 10px" | |
1654 | + }, | |
1655 | + "useDashboardTimewindow": false, | |
1656 | + "showLegend": false, | |
1657 | + "datasources": [ | |
1658 | + { | |
1659 | + "type": "entity", | |
1660 | + "name": null, | |
1661 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1662 | + "filterId": "6044e198-df64-cd76-f339-696f220c4943", | |
1663 | + "dataKeys": [ | |
1664 | + { | |
1665 | + "name": "current_fw_title", | |
1666 | + "type": "timeseries", | |
1667 | + "label": "Current FW title", | |
1668 | + "color": "#2196f3", | |
1669 | + "settings": { | |
1670 | + "columnWidth": "0px", | |
1671 | + "useCellStyleFunction": false, | |
1672 | + "cellStyleFunction": "", | |
1673 | + "useCellContentFunction": false, | |
1674 | + "defaultColumnVisibility": "visible", | |
1675 | + "columnSelectionToDisplay": "enabled" | |
1676 | + }, | |
1677 | + "_hash": 0.09545533885166413, | |
1678 | + "units": null, | |
1679 | + "decimals": null, | |
1680 | + "funcBody": null, | |
1681 | + "usePostProcessing": null, | |
1682 | + "postFuncBody": null | |
1683 | + }, | |
1684 | + { | |
1685 | + "name": "current_fw_version", | |
1686 | + "type": "timeseries", | |
1687 | + "label": "Current FW version", | |
1688 | + "color": "#4caf50", | |
1689 | + "settings": { | |
1690 | + "columnWidth": "0px", | |
1691 | + "useCellStyleFunction": false, | |
1692 | + "cellStyleFunction": "", | |
1693 | + "useCellContentFunction": false, | |
1694 | + "defaultColumnVisibility": "visible", | |
1695 | + "columnSelectionToDisplay": "enabled" | |
1696 | + }, | |
1697 | + "_hash": 0.7206056602328659, | |
1698 | + "units": null, | |
1699 | + "decimals": null, | |
1700 | + "funcBody": null, | |
1701 | + "usePostProcessing": null, | |
1702 | + "postFuncBody": null | |
1703 | + }, | |
1704 | + { | |
1705 | + "name": "target_fw_title", | |
1706 | + "type": "timeseries", | |
1707 | + "label": "Target FW title", | |
1708 | + "color": "#ffc107", | |
1709 | + "settings": { | |
1710 | + "columnWidth": "0px", | |
1711 | + "useCellStyleFunction": false, | |
1712 | + "cellStyleFunction": "", | |
1713 | + "useCellContentFunction": false, | |
1714 | + "defaultColumnVisibility": "visible", | |
1715 | + "columnSelectionToDisplay": "enabled" | |
1716 | + }, | |
1717 | + "_hash": 0.9934225682766313, | |
1718 | + "units": null, | |
1719 | + "decimals": null, | |
1720 | + "funcBody": null, | |
1721 | + "usePostProcessing": null, | |
1722 | + "postFuncBody": null | |
1723 | + }, | |
1724 | + { | |
1725 | + "name": "target_fw_version", | |
1726 | + "type": "timeseries", | |
1727 | + "label": "Target FW version", | |
1728 | + "color": "#607d8b", | |
1729 | + "settings": { | |
1730 | + "columnWidth": "0px", | |
1731 | + "useCellStyleFunction": false, | |
1732 | + "cellStyleFunction": "", | |
1733 | + "useCellContentFunction": false, | |
1734 | + "cellContentFunction": "", | |
1735 | + "defaultColumnVisibility": "visible", | |
1736 | + "columnSelectionToDisplay": "enabled" | |
1737 | + }, | |
1738 | + "_hash": 0.5251724416842531, | |
1739 | + "units": null, | |
1740 | + "decimals": null, | |
1741 | + "funcBody": null, | |
1742 | + "usePostProcessing": null, | |
1743 | + "postFuncBody": null | |
1744 | + }, | |
1745 | + { | |
1746 | + "name": "target_fw_ts", | |
1747 | + "type": "timeseries", | |
1748 | + "label": "Target FW set time", | |
1749 | + "color": "#e91e63", | |
1750 | + "settings": { | |
1751 | + "columnWidth": "0px", | |
1752 | + "useCellStyleFunction": false, | |
1753 | + "cellStyleFunction": "", | |
1754 | + "useCellContentFunction": true, | |
1755 | + "defaultColumnVisibility": "visible", | |
1756 | + "columnSelectionToDisplay": "enabled", | |
1757 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1758 | + }, | |
1759 | + "_hash": 0.31823244858578237, | |
1760 | + "units": null, | |
1761 | + "decimals": null, | |
1762 | + "funcBody": null, | |
1763 | + "usePostProcessing": null, | |
1764 | + "postFuncBody": null | |
1765 | + }, | |
1766 | + { | |
1767 | + "name": "fw_state", | |
1768 | + "type": "timeseries", | |
1769 | + "label": "Progress", | |
1770 | + "color": "#9c27b0", | |
1771 | + "settings": { | |
1772 | + "columnWidth": "30%", | |
1773 | + "useCellStyleFunction": true, | |
1774 | + "useCellContentFunction": true, | |
1775 | + "defaultColumnVisibility": "visible", | |
1776 | + "columnSelectionToDisplay": "enabled", | |
1777 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1778 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
1779 | + }, | |
1780 | + "_hash": 0.8174211757846257, | |
1781 | + "units": null, | |
1782 | + "decimals": null, | |
1783 | + "funcBody": null, | |
1784 | + "usePostProcessing": null, | |
1785 | + "postFuncBody": null | |
1786 | + }, | |
1787 | + { | |
1788 | + "name": "fw_state", | |
1789 | + "type": "timeseries", | |
1790 | + "label": "Status", | |
1791 | + "color": "#f44336", | |
1792 | + "settings": { | |
1793 | + "columnWidth": "130px", | |
1794 | + "useCellStyleFunction": true, | |
1795 | + "useCellContentFunction": true, | |
1796 | + "defaultColumnVisibility": "visible", | |
1797 | + "columnSelectionToDisplay": "enabled", | |
1798 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1799 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
1800 | + }, | |
1801 | + "_hash": 0.7764426948615217, | |
1802 | + "units": null, | |
1803 | + "decimals": null, | |
1804 | + "funcBody": null, | |
1805 | + "usePostProcessing": null, | |
1806 | + "postFuncBody": null | |
1807 | + }, | |
1808 | + { | |
1809 | + "name": "fw_checksum", | |
1810 | + "type": "attribute", | |
1811 | + "label": "fw_checksum", | |
1812 | + "color": "#3f51b5", | |
1813 | + "settings": { | |
1814 | + "columnWidth": "0px", | |
1815 | + "useCellStyleFunction": false, | |
1816 | + "cellStyleFunction": "", | |
1817 | + "useCellContentFunction": false, | |
1818 | + "defaultColumnVisibility": "hidden", | |
1819 | + "columnSelectionToDisplay": "disabled" | |
1820 | + }, | |
1821 | + "_hash": 0.5594087842471693, | |
1822 | + "units": null, | |
1823 | + "decimals": null, | |
1824 | + "funcBody": null, | |
1825 | + "usePostProcessing": null, | |
1826 | + "postFuncBody": null | |
1827 | + } | |
1828 | + ] | |
1829 | + } | |
1830 | + ], | |
1831 | + "actions": { | |
1832 | + "actionCellButton": [ | |
1833 | + { | |
1834 | + "name": "History firmware update", | |
1835 | + "icon": "history", | |
1836 | + "type": "openDashboardState", | |
1837 | + "targetDashboardStateId": "device_firmware_history", | |
1838 | + "setEntityId": true, | |
1839 | + "stateEntityParamName": null, | |
1840 | + "openInSeparateDialog": false, | |
1841 | + "dialogTitle": "", | |
1842 | + "dialogHideDashboardToolbar": true, | |
1843 | + "dialogWidth": null, | |
1844 | + "dialogHeight": null, | |
1845 | + "openRightLayout": false, | |
1846 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1847 | + }, | |
1848 | + { | |
1849 | + "name": "Edit firmware", | |
1850 | + "icon": "edit", | |
1851 | + "type": "customPretty", | |
1852 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1853 | + "customCss": "", | |
1854 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1855 | + "customResources": [], | |
1856 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1857 | + }, | |
1858 | + { | |
1859 | + "name": "Download firware", | |
1860 | + "icon": "file_download", | |
1861 | + "type": "custom", | |
1862 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1863 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1864 | + }, | |
1865 | + { | |
1866 | + "name": "Copy checksum", | |
1867 | + "icon": "content_copy", | |
1868 | + "type": "custom", | |
1869 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1870 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1871 | + } | |
1872 | + ] | |
1873 | + }, | |
1874 | + "showTitleIcon": false, | |
1875 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1876 | + "iconSize": "24px", | |
1877 | + "titleTooltip": "", | |
1878 | + "widgetStyle": {} | |
1879 | + }, | |
1880 | + "row": 0, | |
1881 | + "col": 0, | |
1882 | + "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176" | |
1883 | + } | |
1884 | + }, | |
1885 | + "states": { | |
1886 | + "default": { | |
1887 | + "name": "Device list", | |
1888 | + "root": true, | |
1889 | + "layouts": { | |
1890 | + "main": { | |
1891 | + "widgets": { | |
1892 | + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { | |
1893 | + "sizeX": 19, | |
1894 | + "sizeY": 12, | |
1895 | + "row": 0, | |
1896 | + "col": 0 | |
1897 | + }, | |
1898 | + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { | |
1899 | + "sizeX": 5, | |
1900 | + "sizeY": 3, | |
1901 | + "row": 0, | |
1902 | + "col": 19 | |
1903 | + }, | |
1904 | + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { | |
1905 | + "sizeX": 5, | |
1906 | + "sizeY": 3, | |
1907 | + "row": 3, | |
1908 | + "col": 19 | |
1909 | + }, | |
1910 | + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { | |
1911 | + "sizeX": 5, | |
1912 | + "sizeY": 3, | |
1913 | + "row": 9, | |
1914 | + "col": 19 | |
1915 | + }, | |
1916 | + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { | |
1917 | + "sizeX": 5, | |
1918 | + "sizeY": 3, | |
1919 | + "row": 6, | |
1920 | + "col": 19 | |
1921 | + } | |
1922 | + }, | |
1923 | + "gridSettings": { | |
1924 | + "backgroundColor": "#eeeeee", | |
1925 | + "color": "rgba(0,0,0,0.870588)", | |
1926 | + "columns": 24, | |
1927 | + "margin": 12, | |
1928 | + "backgroundSizeMode": "100%", | |
1929 | + "autoFillHeight": true, | |
1930 | + "backgroundImageUrl": null, | |
1931 | + "mobileAutoFillHeight": true, | |
1932 | + "mobileRowHeight": 70 | |
1933 | + } | |
1934 | + } | |
1935 | + } | |
1936 | + }, | |
1937 | + "device_firmware_history": { | |
1938 | + "name": "Device Firmware history", | |
1939 | + "root": false, | |
1940 | + "layouts": { | |
1941 | + "main": { | |
1942 | + "widgets": { | |
1943 | + "100b756c-0082-6505-3ae1-3603e6deea48": { | |
1944 | + "sizeX": 24, | |
1945 | + "sizeY": 12, | |
1946 | + "row": 0, | |
1947 | + "col": 0 | |
1948 | + } | |
1949 | + }, | |
1950 | + "gridSettings": { | |
1951 | + "backgroundColor": "#eeeeee", | |
1952 | + "color": "rgba(0,0,0,0.870588)", | |
1953 | + "columns": 24, | |
1954 | + "margin": 10, | |
1955 | + "backgroundSizeMode": "100%", | |
1956 | + "autoFillHeight": true, | |
1957 | + "backgroundImageUrl": null, | |
1958 | + "mobileAutoFillHeight": false, | |
1959 | + "mobileRowHeight": 70 | |
1960 | + } | |
1961 | + } | |
1962 | + } | |
1963 | + }, | |
1964 | + "device_waiting": { | |
1965 | + "name": "Device waiting", | |
1966 | + "root": false, | |
1967 | + "layouts": { | |
1968 | + "main": { | |
1969 | + "widgets": { | |
1970 | + "21be08bb-ec90-f760-ad6f-e7678f12c401": { | |
1971 | + "sizeX": 24, | |
1972 | + "sizeY": 12, | |
1973 | + "row": 0, | |
1974 | + "col": 0 | |
1975 | + } | |
1976 | + }, | |
1977 | + "gridSettings": { | |
1978 | + "backgroundColor": "#eeeeee", | |
1979 | + "color": "rgba(0,0,0,0.870588)", | |
1980 | + "columns": 24, | |
1981 | + "margin": 10, | |
1982 | + "backgroundSizeMode": "100%", | |
1983 | + "autoFillHeight": true, | |
1984 | + "backgroundImageUrl": null, | |
1985 | + "mobileAutoFillHeight": false, | |
1986 | + "mobileRowHeight": 70 | |
1987 | + } | |
1988 | + } | |
1989 | + } | |
1990 | + }, | |
1991 | + "device_updating": { | |
1992 | + "name": "Device updating", | |
1993 | + "root": false, | |
1994 | + "layouts": { | |
1995 | + "main": { | |
1996 | + "widgets": { | |
1997 | + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { | |
1998 | + "sizeX": 24, | |
1999 | + "sizeY": 12, | |
2000 | + "row": 0, | |
2001 | + "col": 0 | |
2002 | + } | |
2003 | + }, | |
2004 | + "gridSettings": { | |
2005 | + "backgroundColor": "#eeeeee", | |
2006 | + "color": "rgba(0,0,0,0.870588)", | |
2007 | + "columns": 24, | |
2008 | + "margin": 10, | |
2009 | + "backgroundSizeMode": "100%", | |
2010 | + "autoFillHeight": true, | |
2011 | + "backgroundImageUrl": null, | |
2012 | + "mobileAutoFillHeight": false, | |
2013 | + "mobileRowHeight": 70 | |
2014 | + } | |
2015 | + } | |
2016 | + } | |
2017 | + }, | |
2018 | + "device_updated": { | |
2019 | + "name": "Device updated", | |
2020 | + "root": false, | |
2021 | + "layouts": { | |
2022 | + "main": { | |
2023 | + "widgets": { | |
2024 | + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { | |
2025 | + "sizeX": 27, | |
2026 | + "sizeY": 12, | |
2027 | + "row": 0, | |
2028 | + "col": 0 | |
2029 | + } | |
2030 | + }, | |
2031 | + "gridSettings": { | |
2032 | + "backgroundColor": "#eeeeee", | |
2033 | + "color": "rgba(0,0,0,0.870588)", | |
2034 | + "columns": 24, | |
2035 | + "margin": 10, | |
2036 | + "backgroundSizeMode": "100%", | |
2037 | + "autoFillHeight": true, | |
2038 | + "backgroundImageUrl": null, | |
2039 | + "mobileAutoFillHeight": false, | |
2040 | + "mobileRowHeight": 70 | |
2041 | + } | |
2042 | + } | |
2043 | + } | |
2044 | + }, | |
2045 | + "device_error": { | |
2046 | + "name": "Device failed", | |
2047 | + "root": false, | |
2048 | + "layouts": { | |
2049 | + "main": { | |
2050 | + "widgets": { | |
2051 | + "3624013b-378c-f110-5eba-ae95c25a4dcc": { | |
2052 | + "sizeX": 24, | |
2053 | + "sizeY": 12, | |
2054 | + "row": 0, | |
2055 | + "col": 0 | |
2056 | + } | |
2057 | + }, | |
2058 | + "gridSettings": { | |
2059 | + "backgroundColor": "#eeeeee", | |
2060 | + "color": "rgba(0,0,0,0.870588)", | |
2061 | + "columns": 24, | |
2062 | + "margin": 10, | |
2063 | + "backgroundSizeMode": "100%", | |
2064 | + "autoFillHeight": true, | |
2065 | + "backgroundImageUrl": null, | |
2066 | + "mobileAutoFillHeight": false, | |
2067 | + "mobileRowHeight": 70 | |
2068 | + } | |
2069 | + } | |
2070 | + } | |
2071 | + } | |
2072 | + }, | |
2073 | + "entityAliases": { | |
2074 | + "639da5b4-31f0-0151-6282-c37a3897b7e8": { | |
2075 | + "id": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
2076 | + "alias": "All devices", | |
2077 | + "filter": { | |
2078 | + "type": "entityType", | |
2079 | + "resolveMultiple": true, | |
2080 | + "entityType": "DEVICE" | |
2081 | + } | |
2082 | + }, | |
2083 | + "19f41c21-d9af-e666-8f50-e1748778f955": { | |
2084 | + "id": "19f41c21-d9af-e666-8f50-e1748778f955", | |
2085 | + "alias": "State entity", | |
2086 | + "filter": { | |
2087 | + "type": "stateEntity", | |
2088 | + "resolveMultiple": false, | |
2089 | + "stateEntityParamName": null, | |
2090 | + "defaultStateEntity": null | |
2091 | + } | |
2092 | + } | |
2093 | + }, | |
2094 | + "filters": { | |
2095 | + "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e": { | |
2096 | + "id": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
2097 | + "filter": "WaitingDevicesFilter", | |
2098 | + "keyFilters": [ | |
2099 | + { | |
2100 | + "key": { | |
2101 | + "type": "TIME_SERIES", | |
2102 | + "key": "fw_state" | |
2103 | + }, | |
2104 | + "valueType": "STRING", | |
2105 | + "predicates": [ | |
2106 | + { | |
2107 | + "keyFilterPredicate": { | |
2108 | + "operation": "EQUAL", | |
2109 | + "value": { | |
2110 | + "defaultValue": "QUEUED", | |
2111 | + "dynamicValue": null | |
2112 | + }, | |
2113 | + "ignoreCase": false, | |
2114 | + "type": "STRING" | |
2115 | + }, | |
2116 | + "userInfo": { | |
2117 | + "editable": true, | |
2118 | + "label": "", | |
2119 | + "autogeneratedLabel": true, | |
2120 | + "order": 0 | |
2121 | + } | |
2122 | + } | |
2123 | + ] | |
2124 | + } | |
2125 | + ], | |
2126 | + "editable": false | |
2127 | + }, | |
2128 | + "579f0468-9ce9-7e3e-b34c-88dd3de59897": { | |
2129 | + "id": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
2130 | + "filter": "UpdatingDevicesFilter", | |
2131 | + "keyFilters": [ | |
2132 | + { | |
2133 | + "key": { | |
2134 | + "type": "TIME_SERIES", | |
2135 | + "key": "fw_state" | |
2136 | + }, | |
2137 | + "valueType": "STRING", | |
2138 | + "predicates": [ | |
2139 | + { | |
2140 | + "keyFilterPredicate": { | |
2141 | + "operation": "OR", | |
2142 | + "predicates": [ | |
2143 | + { | |
2144 | + "keyFilterPredicate": { | |
2145 | + "operation": "EQUAL", | |
2146 | + "value": { | |
2147 | + "defaultValue": "INITIATED", | |
2148 | + "dynamicValue": null | |
2149 | + }, | |
2150 | + "ignoreCase": false, | |
2151 | + "type": "STRING" | |
2152 | + }, | |
2153 | + "userInfo": { | |
2154 | + "editable": false, | |
2155 | + "label": "fw_state equel", | |
2156 | + "autogeneratedLabel": true, | |
2157 | + "order": 0 | |
2158 | + } | |
2159 | + }, | |
2160 | + { | |
2161 | + "keyFilterPredicate": { | |
2162 | + "operation": "EQUAL", | |
2163 | + "value": { | |
2164 | + "defaultValue": "DOWNLOADING", | |
2165 | + "dynamicValue": null | |
2166 | + }, | |
2167 | + "ignoreCase": false, | |
2168 | + "type": "STRING" | |
2169 | + }, | |
2170 | + "userInfo": { | |
2171 | + "editable": false, | |
2172 | + "label": "fw_state equal", | |
2173 | + "autogeneratedLabel": true, | |
2174 | + "order": 0 | |
2175 | + } | |
2176 | + }, | |
2177 | + { | |
2178 | + "keyFilterPredicate": { | |
2179 | + "operation": "EQUAL", | |
2180 | + "value": { | |
2181 | + "defaultValue": "DOWNLOADED", | |
2182 | + "dynamicValue": null | |
2183 | + }, | |
2184 | + "ignoreCase": false, | |
2185 | + "type": "STRING" | |
2186 | + }, | |
2187 | + "userInfo": { | |
2188 | + "editable": false, | |
2189 | + "label": "fw_state equal", | |
2190 | + "autogeneratedLabel": true, | |
2191 | + "order": 0 | |
2192 | + } | |
2193 | + }, | |
2194 | + { | |
2195 | + "keyFilterPredicate": { | |
2196 | + "operation": "EQUAL", | |
2197 | + "value": { | |
2198 | + "defaultValue": "VERIFIED", | |
2199 | + "dynamicValue": null | |
2200 | + }, | |
2201 | + "ignoreCase": false, | |
2202 | + "type": "STRING" | |
2203 | + }, | |
2204 | + "userInfo": { | |
2205 | + "editable": false, | |
2206 | + "label": "fw_state equal", | |
2207 | + "autogeneratedLabel": true, | |
2208 | + "order": 0 | |
2209 | + } | |
2210 | + }, | |
2211 | + { | |
2212 | + "keyFilterPredicate": { | |
2213 | + "operation": "EQUAL", | |
2214 | + "value": { | |
2215 | + "defaultValue": "UPDATING", | |
2216 | + "dynamicValue": null | |
2217 | + }, | |
2218 | + "ignoreCase": false, | |
2219 | + "type": "STRING" | |
2220 | + }, | |
2221 | + "userInfo": { | |
2222 | + "editable": false, | |
2223 | + "label": "fw_state equal", | |
2224 | + "autogeneratedLabel": true, | |
2225 | + "order": 0 | |
2226 | + } | |
2227 | + } | |
2228 | + ], | |
2229 | + "type": "COMPLEX" | |
2230 | + }, | |
2231 | + "userInfo": { | |
2232 | + "editable": true, | |
2233 | + "label": "", | |
2234 | + "autogeneratedLabel": true, | |
2235 | + "order": 0 | |
2236 | + } | |
2237 | + } | |
2238 | + ] | |
2239 | + } | |
2240 | + ], | |
2241 | + "editable": false | |
2242 | + }, | |
2243 | + "6044e198-df64-cd76-f339-696f220c4943": { | |
2244 | + "id": "6044e198-df64-cd76-f339-696f220c4943", | |
2245 | + "filter": "UpdetedDevicesFilter", | |
2246 | + "keyFilters": [ | |
2247 | + { | |
2248 | + "key": { | |
2249 | + "type": "TIME_SERIES", | |
2250 | + "key": "fw_state" | |
2251 | + }, | |
2252 | + "valueType": "STRING", | |
2253 | + "predicates": [ | |
2254 | + { | |
2255 | + "keyFilterPredicate": { | |
2256 | + "operation": "EQUAL", | |
2257 | + "value": { | |
2258 | + "defaultValue": "UPDATED", | |
2259 | + "dynamicValue": null | |
2260 | + }, | |
2261 | + "ignoreCase": false, | |
2262 | + "type": "STRING" | |
2263 | + }, | |
2264 | + "userInfo": { | |
2265 | + "editable": true, | |
2266 | + "label": "", | |
2267 | + "autogeneratedLabel": true, | |
2268 | + "order": 0 | |
2269 | + } | |
2270 | + } | |
2271 | + ] | |
2272 | + } | |
2273 | + ], | |
2274 | + "editable": false | |
2275 | + }, | |
2276 | + "bdbc6ea1-95a7-3912-341a-58dc7704a00f": { | |
2277 | + "id": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
2278 | + "filter": "FailedDevicesFilter", | |
2279 | + "keyFilters": [ | |
2280 | + { | |
2281 | + "key": { | |
2282 | + "type": "TIME_SERIES", | |
2283 | + "key": "fw_state" | |
2284 | + }, | |
2285 | + "valueType": "STRING", | |
2286 | + "predicates": [ | |
2287 | + { | |
2288 | + "keyFilterPredicate": { | |
2289 | + "operation": "EQUAL", | |
2290 | + "value": { | |
2291 | + "defaultValue": "FAILED", | |
2292 | + "dynamicValue": null | |
2293 | + }, | |
2294 | + "ignoreCase": false, | |
2295 | + "type": "STRING" | |
2296 | + }, | |
2297 | + "userInfo": { | |
2298 | + "editable": true, | |
2299 | + "label": "", | |
2300 | + "autogeneratedLabel": true, | |
2301 | + "order": 0 | |
2302 | + } | |
2303 | + } | |
2304 | + ] | |
2305 | + } | |
2306 | + ], | |
2307 | + "editable": false | |
2308 | + }, | |
2309 | + "8fdb88d0-50ac-2232-fdb7-69c30c16544e": { | |
2310 | + "id": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", | |
2311 | + "filter": "DeviceSearch", | |
2312 | + "keyFilters": [ | |
2313 | + { | |
2314 | + "key": { | |
2315 | + "type": "ENTITY_FIELD", | |
2316 | + "key": "name" | |
2317 | + }, | |
2318 | + "valueType": "STRING", | |
2319 | + "predicates": [ | |
2320 | + { | |
2321 | + "keyFilterPredicate": { | |
2322 | + "operation": "CONTAINS", | |
2323 | + "value": { | |
2324 | + "defaultValue": "" | |
2325 | + }, | |
2326 | + "ignoreCase": true, | |
2327 | + "type": "STRING" | |
2328 | + }, | |
2329 | + "userInfo": { | |
2330 | + "editable": true, | |
2331 | + "label": "Device name", | |
2332 | + "autogeneratedLabel": false, | |
2333 | + "order": 0 | |
2334 | + } | |
2335 | + } | |
2336 | + ] | |
2337 | + } | |
2338 | + ], | |
2339 | + "editable": true | |
2340 | + } | |
2341 | + }, | |
2342 | + "timewindow": { | |
2343 | + "displayValue": "", | |
2344 | + "hideInterval": false, | |
2345 | + "hideAggregation": false, | |
2346 | + "hideAggInterval": false, | |
2347 | + "hideTimezone": false, | |
2348 | + "selectedTab": 0, | |
2349 | + "realtime": { | |
2350 | + "realtimeType": 0, | |
2351 | + "interval": 1000, | |
2352 | + "timewindowMs": 60000, | |
2353 | + "quickInterval": "CURRENT_DAY" | |
2354 | + }, | |
2355 | + "history": { | |
2356 | + "historyType": 0, | |
2357 | + "interval": 1000, | |
2358 | + "timewindowMs": 60000, | |
2359 | + "fixedTimewindow": { | |
2360 | + "startTimeMs": 1618998609030, | |
2361 | + "endTimeMs": 1619085009030 | |
2362 | + }, | |
2363 | + "quickInterval": "CURRENT_DAY" | |
2364 | + }, | |
2365 | + "aggregation": { | |
2366 | + "type": "AVG", | |
2367 | + "limit": 25000 | |
2368 | + } | |
2369 | + }, | |
2370 | + "settings": { | |
2371 | + "stateControllerId": "entity", | |
2372 | + "showTitle": false, | |
2373 | + "showDashboardsSelect": false, | |
2374 | + "showEntitiesSelect": false, | |
2375 | + "showDashboardTimewindow": true, | |
2376 | + "showDashboardExport": false, | |
2377 | + "toolbarAlwaysOpen": true, | |
2378 | + "titleColor": "rgba(0,0,0,0.870588)", | |
2379 | + "showFilters": true, | |
2380 | + "showDashboardLogo": false, | |
2381 | + "dashboardLogoUrl": null | |
2382 | + } | |
2383 | + }, | |
2384 | + "name": "Firmware" | |
2385 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -25,7 +25,6 @@ import org.springframework.stereotype.Service; |
25 | 25 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
26 | 26 | import org.thingsboard.server.actors.ActorSystemContext; |
27 | 27 | import org.thingsboard.server.actors.DefaultTbActorSystem; |
28 | -import org.thingsboard.server.actors.TbActorId; | |
29 | 28 | import org.thingsboard.server.actors.TbActorRef; |
30 | 29 | import org.thingsboard.server.actors.TbActorSystem; |
31 | 30 | import org.thingsboard.server.actors.TbActorSystemSettings; |
... | ... | @@ -33,14 +32,13 @@ import org.thingsboard.server.actors.app.AppActor; |
33 | 32 | import org.thingsboard.server.actors.app.AppInitMsg; |
34 | 33 | import org.thingsboard.server.actors.stats.StatsActor; |
35 | 34 | import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; |
36 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
37 | 35 | import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
36 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
38 | 37 | |
39 | 38 | import javax.annotation.PostConstruct; |
40 | 39 | import javax.annotation.PreDestroy; |
41 | 40 | import java.util.concurrent.ExecutorService; |
42 | 41 | import java.util.concurrent.Executors; |
43 | -import java.util.concurrent.ScheduledExecutorService; | |
44 | 42 | |
45 | 43 | @Service |
46 | 44 | @Slf4j | ... | ... |
... | ... | @@ -75,7 +75,6 @@ import javax.annotation.Nullable; |
75 | 75 | import java.io.IOException; |
76 | 76 | import java.util.ArrayList; |
77 | 77 | import java.util.List; |
78 | -import java.util.Objects; | |
79 | 78 | import java.util.stream.Collectors; |
80 | 79 | |
81 | 80 | import static org.thingsboard.server.controller.EdgeController.EDGE_ID; |
... | ... | @@ -120,12 +119,12 @@ public class DeviceController extends BaseController { |
120 | 119 | @ResponseBody |
121 | 120 | public Device saveDevice(@RequestBody Device device, |
122 | 121 | @RequestParam(name = "accessToken", required = false) String accessToken) throws ThingsboardException { |
122 | + boolean created = device.getId() == null; | |
123 | 123 | try { |
124 | 124 | device.setTenantId(getCurrentUser().getTenantId()); |
125 | 125 | |
126 | 126 | checkEntity(device.getId(), device, Resource.DEVICE); |
127 | 127 | |
128 | - boolean created = device.getId() == null; | |
129 | 128 | Device oldDevice; |
130 | 129 | if (!created) { |
131 | 130 | oldDevice = deviceService.findDeviceById(getTenantId(), device.getId()); |
... | ... | @@ -146,7 +145,7 @@ public class DeviceController extends BaseController { |
146 | 145 | |
147 | 146 | logEntityAction(savedDevice.getId(), savedDevice, |
148 | 147 | savedDevice.getCustomerId(), |
149 | - device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); | |
148 | + created ? ActionType.ADDED : ActionType.UPDATED, null); | |
150 | 149 | |
151 | 150 | if (device.getId() == null) { |
152 | 151 | deviceStateService.onDeviceAdded(savedDevice); |
... | ... | @@ -157,10 +156,9 @@ public class DeviceController extends BaseController { |
157 | 156 | firmwareStateService.update(savedDevice, oldDevice); |
158 | 157 | |
159 | 158 | return savedDevice; |
160 | - } catch ( | |
161 | - Exception e) { | |
159 | + } catch (Exception e) { | |
162 | 160 | logEntityAction(emptyId(EntityType.DEVICE), device, |
163 | - null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); | |
161 | + null, created ? ActionType.ADDED : ActionType.UPDATED, e); | |
164 | 162 | throw handleException(e); |
165 | 163 | } |
166 | 164 | ... | ... |
... | ... | @@ -29,8 +29,10 @@ import org.springframework.web.bind.annotation.RequestParam; |
29 | 29 | import org.springframework.web.bind.annotation.ResponseBody; |
30 | 30 | import org.springframework.web.bind.annotation.RestController; |
31 | 31 | import org.springframework.web.multipart.MultipartFile; |
32 | +import org.thingsboard.server.common.data.EntityType; | |
32 | 33 | import org.thingsboard.server.common.data.Firmware; |
33 | 34 | import org.thingsboard.server.common.data.FirmwareInfo; |
35 | +import org.thingsboard.server.common.data.audit.ActionType; | |
34 | 36 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
35 | 37 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
36 | 38 | import org.thingsboard.server.common.data.id.FirmwareId; |
... | ... | @@ -72,14 +74,14 @@ public class FirmwareController extends BaseController { |
72 | 74 | } |
73 | 75 | } |
74 | 76 | |
75 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
77 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
76 | 78 | @RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET) |
77 | 79 | @ResponseBody |
78 | 80 | public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { |
79 | 81 | checkParameter(FIRMWARE_ID, strFirmwareId); |
80 | 82 | try { |
81 | 83 | FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
82 | - return checkFirmwareInfoId(firmwareId, Operation.READ); | |
84 | + return checkNotNull(firmwareService.findFirmwareInfoById(getTenantId(), firmwareId)); | |
83 | 85 | } catch (Exception e) { |
84 | 86 | throw handleException(e); |
85 | 87 | } |
... | ... | @@ -102,11 +104,17 @@ public class FirmwareController extends BaseController { |
102 | 104 | @RequestMapping(value = "/firmware", method = RequestMethod.POST) |
103 | 105 | @ResponseBody |
104 | 106 | public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException { |
105 | - firmwareInfo.setTenantId(getTenantId()); | |
106 | - checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE); | |
107 | + boolean created = firmwareInfo.getId() == null; | |
107 | 108 | try { |
108 | - return firmwareService.saveFirmwareInfo(firmwareInfo); | |
109 | + firmwareInfo.setTenantId(getTenantId()); | |
110 | + checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE); | |
111 | + FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); | |
112 | + logEntityAction(savedFirmwareInfo.getId(), savedFirmwareInfo, | |
113 | + null, created ? ActionType.ADDED : ActionType.UPDATED, null); | |
114 | + return savedFirmwareInfo; | |
109 | 115 | } catch (Exception e) { |
116 | + logEntityAction(emptyId(EntityType.FIRMWARE), firmwareInfo, | |
117 | + null, created ? ActionType.ADDED : ActionType.UPDATED, e); | |
110 | 118 | throw handleException(e); |
111 | 119 | } |
112 | 120 | } |
... | ... | @@ -144,13 +152,16 @@ public class FirmwareController extends BaseController { |
144 | 152 | firmware.setContentType(file.getContentType()); |
145 | 153 | firmware.setData(data); |
146 | 154 | firmware.setDataSize((long) data.capacity()); |
147 | - return firmwareService.saveFirmware(firmware); | |
155 | + Firmware savedFirmware = firmwareService.saveFirmware(firmware); | |
156 | + logEntityAction(savedFirmware.getId(), savedFirmware, null, ActionType.UPDATED, null); | |
157 | + return savedFirmware; | |
148 | 158 | } catch (Exception e) { |
159 | + logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.UPDATED, e, strFirmwareId); | |
149 | 160 | throw handleException(e); |
150 | 161 | } |
151 | 162 | } |
152 | 163 | |
153 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
164 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
154 | 165 | @RequestMapping(value = "/firmwares", method = RequestMethod.GET) |
155 | 166 | @ResponseBody |
156 | 167 | public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize, |
... | ... | @@ -166,7 +177,7 @@ public class FirmwareController extends BaseController { |
166 | 177 | } |
167 | 178 | } |
168 | 179 | |
169 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
180 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
170 | 181 | @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) |
171 | 182 | @ResponseBody |
172 | 183 | public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData, |
... | ... | @@ -186,13 +197,15 @@ public class FirmwareController extends BaseController { |
186 | 197 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
187 | 198 | @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE) |
188 | 199 | @ResponseBody |
189 | - public void deleteResource(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { | |
200 | + public void deleteFirmware(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { | |
190 | 201 | checkParameter(FIRMWARE_ID, strFirmwareId); |
191 | 202 | try { |
192 | 203 | FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
193 | - checkFirmwareInfoId(firmwareId, Operation.DELETE); | |
204 | + FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.DELETE); | |
194 | 205 | firmwareService.deleteFirmware(getTenantId(), firmwareId); |
206 | + logEntityAction(firmwareId, info, null, ActionType.DELETED, null, strFirmwareId); | |
195 | 207 | } catch (Exception e) { |
208 | + logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.DELETED, e, strFirmwareId); | |
196 | 209 | throw handleException(e); |
197 | 210 | } |
198 | 211 | } | ... | ... |
... | ... | @@ -28,8 +28,10 @@ import org.springframework.web.bind.annotation.RequestMethod; |
28 | 28 | import org.springframework.web.bind.annotation.RequestParam; |
29 | 29 | import org.springframework.web.bind.annotation.ResponseBody; |
30 | 30 | import org.springframework.web.bind.annotation.RestController; |
31 | +import org.thingsboard.server.common.data.EntityType; | |
31 | 32 | import org.thingsboard.server.common.data.TbResource; |
32 | 33 | import org.thingsboard.server.common.data.TbResourceInfo; |
34 | +import org.thingsboard.server.common.data.audit.ActionType; | |
33 | 35 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
34 | 36 | import org.thingsboard.server.common.data.id.TbResourceId; |
35 | 37 | import org.thingsboard.server.common.data.lwm2m.LwM2mObject; |
... | ... | @@ -37,7 +39,6 @@ import org.thingsboard.server.common.data.page.PageData; |
37 | 39 | import org.thingsboard.server.common.data.page.PageLink; |
38 | 40 | import org.thingsboard.server.common.data.security.Authority; |
39 | 41 | import org.thingsboard.server.queue.util.TbCoreComponent; |
40 | -import org.thingsboard.server.service.resource.TbResourceService; | |
41 | 42 | import org.thingsboard.server.service.security.permission.Operation; |
42 | 43 | import org.thingsboard.server.service.security.permission.Resource; |
43 | 44 | |
... | ... | @@ -103,12 +104,18 @@ public class TbResourceController extends BaseController { |
103 | 104 | @RequestMapping(value = "/resource", method = RequestMethod.POST) |
104 | 105 | @ResponseBody |
105 | 106 | public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException { |
107 | + boolean created = resource.getId() == null; | |
106 | 108 | try { |
107 | - resource.setTenantId(getTenantId()); | |
108 | - checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); | |
109 | - return addResource(resource); | |
110 | - } | |
111 | - catch (Exception e) { | |
109 | + resource.setTenantId(getTenantId()); | |
110 | + checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); | |
111 | + TbResource savedResource = checkNotNull(resourceService.saveResource(resource)); | |
112 | + tbClusterService.onResourceChange(savedResource, null); | |
113 | + logEntityAction(savedResource.getId(), savedResource, | |
114 | + null, created ? ActionType.ADDED : ActionType.UPDATED, null); | |
115 | + return savedResource; | |
116 | + } catch (Exception e) { | |
117 | + logEntityAction(emptyId(EntityType.TB_RESOURCE), resource, | |
118 | + null, created ? ActionType.ADDED : ActionType.UPDATED, e); | |
112 | 119 | throw handleException(e); |
113 | 120 | } |
114 | 121 | } |
... | ... | @@ -172,15 +179,11 @@ public class TbResourceController extends BaseController { |
172 | 179 | TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); |
173 | 180 | resourceService.deleteResource(getTenantId(), resourceId); |
174 | 181 | tbClusterService.onResourceDeleted(tbResource, null); |
182 | + logEntityAction(resourceId, tbResource, null, ActionType.DELETED, null, strResourceId); | |
175 | 183 | } catch (Exception e) { |
184 | + logEntityAction(emptyId(EntityType.TB_RESOURCE), null, null, ActionType.DELETED, e, strResourceId); | |
176 | 185 | throw handleException(e); |
177 | 186 | } |
178 | 187 | } |
179 | 188 | |
180 | - private TbResource addResource(TbResource resource) throws Exception { | |
181 | - checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); | |
182 | - TbResource savedResource = checkNotNull(resourceService.saveResource(resource)); | |
183 | - tbClusterService.onResourceChange(savedResource, null); | |
184 | - return savedResource; | |
185 | - } | |
186 | 189 | } |
\ No newline at end of file | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
... | ... | @@ -56,7 +56,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
56 | 56 | import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; |
57 | 57 | import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; |
58 | 58 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
59 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
59 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
60 | 60 | import org.thingsboard.server.queue.discovery.PartitionService; |
61 | 61 | import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
62 | 62 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; | ... | ... |
... | ... | @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId; |
23 | 23 | import org.thingsboard.server.common.msg.queue.TbCallback; |
24 | 24 | import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; |
25 | 25 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
26 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
26 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
27 | 27 | |
28 | 28 | public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> { |
29 | 29 | ... | ... |
... | ... | @@ -22,7 +22,6 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; |
22 | 22 | import org.thingsboard.server.common.data.DataConstants; |
23 | 23 | import org.thingsboard.server.common.data.Device; |
24 | 24 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | -import org.thingsboard.server.common.data.Firmware; | |
26 | 25 | import org.thingsboard.server.common.data.FirmwareInfo; |
27 | 26 | import org.thingsboard.server.common.data.id.DeviceId; |
28 | 27 | import org.thingsboard.server.common.data.id.FirmwareId; |
... | ... | @@ -35,7 +34,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; |
35 | 34 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | 35 | import org.thingsboard.server.common.data.page.PageData; |
37 | 36 | import org.thingsboard.server.common.data.page.PageLink; |
38 | -import org.thingsboard.server.common.msg.queue.TbCallback; | |
39 | 37 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
40 | 38 | import org.thingsboard.server.dao.device.DeviceProfileService; |
41 | 39 | import org.thingsboard.server.dao.device.DeviceService; |
... | ... | @@ -155,7 +153,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
155 | 153 | } |
156 | 154 | |
157 | 155 | if (targetFirmwareId.equals(currentFirmwareId)) { |
158 | - update(device, firmwareService.findFirmwareById(device.getTenantId(), targetFirmwareId), ts); | |
156 | + update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts); | |
159 | 157 | isSuccess = true; |
160 | 158 | } else { |
161 | 159 | log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); |
... | ... | @@ -187,6 +185,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
187 | 185 | List<TsKvEntry> telemetry = new ArrayList<>(); |
188 | 186 | telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle()))); |
189 | 187 | telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); |
188 | + telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts))); | |
190 | 189 | telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); |
191 | 190 | |
192 | 191 | telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { | ... | ... |
... | ... | @@ -55,7 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM |
55 | 55 | import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; |
56 | 56 | import org.thingsboard.server.queue.TbQueueConsumer; |
57 | 57 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
58 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
58 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
59 | 59 | import org.thingsboard.server.queue.provider.TbCoreQueueFactory; |
60 | 60 | import org.thingsboard.server.queue.util.TbCoreComponent; |
61 | 61 | import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | ... | ... |
... | ... | @@ -38,7 +38,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
38 | 38 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
39 | 39 | import org.thingsboard.server.queue.TbQueueConsumer; |
40 | 40 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
41 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
41 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
42 | 42 | import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; |
43 | 43 | import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; |
44 | 44 | import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | 18 | import org.springframework.context.ApplicationListener; |
19 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
19 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
20 | 20 | |
21 | 21 | public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> { |
22 | 22 | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | 18 | import org.springframework.context.ApplicationListener; |
19 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
19 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
20 | 20 | |
21 | 21 | public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> { |
22 | 22 | ... | ... |
... | ... | @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; |
35 | 35 | import org.thingsboard.server.common.msg.queue.TbCallback; |
36 | 36 | import org.thingsboard.server.queue.TbQueueConsumer; |
37 | 37 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
38 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
38 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
39 | 39 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
40 | 40 | import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
41 | 41 | import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | ... | ... |
... | ... | @@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.Customer; |
30 | 30 | import org.thingsboard.server.common.data.Device; |
31 | 31 | import org.thingsboard.server.common.data.DeviceProfile; |
32 | 32 | import org.thingsboard.server.common.data.EntityView; |
33 | +import org.thingsboard.server.common.data.FirmwareInfo; | |
34 | +import org.thingsboard.server.common.data.TbResourceInfo; | |
33 | 35 | import org.thingsboard.server.common.data.Tenant; |
34 | 36 | import org.thingsboard.server.common.data.User; |
35 | 37 | import org.thingsboard.server.common.data.asset.Asset; |
... | ... | @@ -44,8 +46,10 @@ import org.thingsboard.server.common.data.id.EdgeId; |
44 | 46 | import org.thingsboard.server.common.data.id.EntityId; |
45 | 47 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
46 | 48 | import org.thingsboard.server.common.data.id.EntityViewId; |
49 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
47 | 50 | import org.thingsboard.server.common.data.id.RuleChainId; |
48 | 51 | import org.thingsboard.server.common.data.id.RuleNodeId; |
52 | +import org.thingsboard.server.common.data.id.TbResourceId; | |
49 | 53 | import org.thingsboard.server.common.data.id.TenantId; |
50 | 54 | import org.thingsboard.server.common.data.id.UserId; |
51 | 55 | import org.thingsboard.server.common.data.rule.RuleChain; |
... | ... | @@ -59,6 +63,8 @@ import org.thingsboard.server.dao.device.DeviceService; |
59 | 63 | import org.thingsboard.server.dao.edge.EdgeService; |
60 | 64 | import org.thingsboard.server.dao.entityview.EntityViewService; |
61 | 65 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
66 | +import org.thingsboard.server.dao.firmware.FirmwareService; | |
67 | +import org.thingsboard.server.dao.resource.ResourceService; | |
62 | 68 | import org.thingsboard.server.dao.rule.RuleChainService; |
63 | 69 | import org.thingsboard.server.dao.tenant.TenantService; |
64 | 70 | import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
... | ... | @@ -125,6 +131,12 @@ public class AccessValidator { |
125 | 131 | @Autowired |
126 | 132 | protected ApiUsageStateService apiUsageStateService; |
127 | 133 | |
134 | + @Autowired | |
135 | + protected ResourceService resourceService; | |
136 | + | |
137 | + @Autowired | |
138 | + protected FirmwareService firmwareService; | |
139 | + | |
128 | 140 | private ExecutorService executor; |
129 | 141 | |
130 | 142 | @PostConstruct |
... | ... | @@ -217,6 +229,12 @@ public class AccessValidator { |
217 | 229 | case API_USAGE_STATE: |
218 | 230 | validateApiUsageState(currentUser, operation, entityId, callback); |
219 | 231 | return; |
232 | + case TB_RESOURCE: | |
233 | + validateResource(currentUser, operation, entityId, callback); | |
234 | + return; | |
235 | + case FIRMWARE: | |
236 | + validateFirmware(currentUser, operation, entityId, callback); | |
237 | + return; | |
220 | 238 | default: |
221 | 239 | //TODO: add support of other entities |
222 | 240 | throw new IllegalStateException("Not Implemented!"); |
... | ... | @@ -282,6 +300,40 @@ public class AccessValidator { |
282 | 300 | } |
283 | 301 | } |
284 | 302 | |
303 | + private void validateFirmware(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
304 | + if (currentUser.isSystemAdmin()) { | |
305 | + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | |
306 | + } else { | |
307 | + FirmwareInfo firmware = firmwareService.findFirmwareInfoById(currentUser.getTenantId(), new FirmwareId(entityId.getId())); | |
308 | + if (firmware == null) { | |
309 | + callback.onSuccess(ValidationResult.entityNotFound("Firmware with requested id wasn't found!")); | |
310 | + } else { | |
311 | + try { | |
312 | + accessControlService.checkPermission(currentUser, Resource.FIRMWARE, operation, entityId, firmware); | |
313 | + } catch (ThingsboardException e) { | |
314 | + callback.onSuccess(ValidationResult.accessDenied(e.getMessage())); | |
315 | + } | |
316 | + callback.onSuccess(ValidationResult.ok(firmware)); | |
317 | + } | |
318 | + } | |
319 | + } | |
320 | + | |
321 | + private void validateResource(SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
322 | + ListenableFuture<TbResourceInfo> resourceFuture = resourceService.findResourceInfoByIdAsync(currentUser.getTenantId(), new TbResourceId(entityId.getId())); | |
323 | + Futures.addCallback(resourceFuture, getCallback(callback, resource -> { | |
324 | + if (resource == null) { | |
325 | + return ValidationResult.entityNotFound("Resource with requested id wasn't found!"); | |
326 | + } else { | |
327 | + try { | |
328 | + accessControlService.checkPermission(currentUser, Resource.TB_RESOURCE, operation, entityId, resource); | |
329 | + } catch (ThingsboardException e) { | |
330 | + return ValidationResult.accessDenied(e.getMessage()); | |
331 | + } | |
332 | + return ValidationResult.ok(resource); | |
333 | + } | |
334 | + }), executor); | |
335 | + } | |
336 | + | |
285 | 337 | private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { |
286 | 338 | if (currentUser.isSystemAdmin()) { |
287 | 339 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | ... | ... |
... | ... | @@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService; |
54 | 54 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
55 | 55 | import org.thingsboard.common.util.JacksonUtil; |
56 | 56 | import org.thingsboard.server.gen.transport.TransportProtos; |
57 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
57 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
58 | 58 | import org.thingsboard.server.queue.discovery.PartitionService; |
59 | 59 | import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
60 | 60 | import org.thingsboard.server.queue.util.TbCoreComponent; | ... | ... |
... | ... | @@ -18,7 +18,7 @@ package org.thingsboard.server.service.state; |
18 | 18 | import org.springframework.context.ApplicationListener; |
19 | 19 | import org.thingsboard.server.common.data.Device; |
20 | 20 | import org.thingsboard.server.common.data.id.DeviceId; |
21 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
21 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
22 | 22 | import org.thingsboard.server.gen.transport.TransportProtos; |
23 | 23 | import org.thingsboard.server.common.msg.queue.TbCallback; |
24 | 24 | ... | ... |
... | ... | @@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate |
46 | 46 | import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto; |
47 | 47 | import org.thingsboard.server.queue.TbQueueProducer; |
48 | 48 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
49 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
49 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
50 | 50 | import org.thingsboard.server.queue.discovery.PartitionService; |
51 | 51 | import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
52 | 52 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; | ... | ... |
... | ... | @@ -20,10 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.context.annotation.Lazy; |
21 | 21 | import org.springframework.context.event.EventListener; |
22 | 22 | import org.springframework.stereotype.Service; |
23 | -import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
24 | 23 | import org.thingsboard.server.gen.transport.TransportProtos; |
25 | -import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent; | |
26 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
24 | +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; | |
25 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
27 | 26 | import org.thingsboard.server.queue.discovery.PartitionService; |
28 | 27 | import org.thingsboard.server.common.msg.queue.ServiceType; |
29 | 28 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | ... | ... |
... | ... | @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
22 | 22 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
23 | 23 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
24 | 24 | import org.thingsboard.server.common.msg.queue.TbCallback; |
25 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
25 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
26 | 26 | |
27 | 27 | import java.util.List; |
28 | 28 | ... | ... |
... | ... | @@ -15,8 +15,8 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.subscription; |
17 | 17 | |
18 | -import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent; | |
19 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
18 | +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; | |
19 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
20 | 20 | import org.thingsboard.server.common.msg.queue.TbCallback; |
21 | 21 | import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; |
22 | 22 | import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; | ... | ... |
... | ... | @@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
23 | 23 | import org.springframework.context.ApplicationListener; |
24 | 24 | import org.springframework.context.event.EventListener; |
25 | -import org.springframework.stereotype.Service; | |
26 | 25 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
27 | -import org.thingsboard.server.common.data.id.EntityId; | |
28 | -import org.thingsboard.server.common.data.id.TenantId; | |
29 | -import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
30 | -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | |
31 | -import org.thingsboard.server.common.data.kv.BooleanDataEntry; | |
32 | -import org.thingsboard.server.common.data.kv.DoubleDataEntry; | |
33 | -import org.thingsboard.server.common.data.kv.LongDataEntry; | |
34 | -import org.thingsboard.server.common.data.kv.StringDataEntry; | |
35 | -import org.thingsboard.server.common.data.kv.TsKvEntry; | |
36 | 26 | import org.thingsboard.server.common.msg.queue.ServiceType; |
37 | -import org.thingsboard.server.common.msg.queue.TbCallback; | |
38 | 27 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
39 | -import org.thingsboard.server.dao.attributes.AttributesService; | |
40 | -import org.thingsboard.server.dao.timeseries.TimeseriesService; | |
41 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
42 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
28 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
43 | 29 | import org.thingsboard.server.queue.discovery.PartitionService; |
44 | 30 | import org.thingsboard.server.queue.discovery.TbApplicationEventListener; |
45 | 31 | import org.thingsboard.server.service.queue.TbClusterService; |
46 | 32 | import org.thingsboard.server.service.subscription.SubscriptionManagerService; |
47 | -import org.thingsboard.server.service.subscription.TbSubscriptionUtils; | |
48 | 33 | |
49 | 34 | import javax.annotation.Nullable; |
50 | 35 | import javax.annotation.PostConstruct; |
51 | 36 | import javax.annotation.PreDestroy; |
52 | -import java.util.Collections; | |
53 | -import java.util.List; | |
54 | 37 | import java.util.Optional; |
55 | 38 | import java.util.Set; |
56 | 39 | import java.util.concurrent.ConcurrentHashMap; | ... | ... |
... | ... | @@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry; |
17 | 17 | |
18 | 18 | import org.springframework.context.ApplicationListener; |
19 | 19 | import org.thingsboard.rule.engine.api.RuleEngineAlarmService; |
20 | -import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; | |
21 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
20 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
22 | 21 | |
23 | 22 | /** |
24 | 23 | * Created by ashvayka on 27.03.18. | ... | ... |
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
... | ... | @@ -16,8 +16,7 @@ |
16 | 16 | package org.thingsboard.server.service.telemetry; |
17 | 17 | |
18 | 18 | import org.springframework.context.ApplicationListener; |
19 | -import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; | |
20 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
19 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
21 | 20 | |
22 | 21 | /** |
23 | 22 | * Created by ashvayka on 27.03.18. | ... | ... |
... | ... | @@ -23,17 +23,18 @@ import com.google.common.util.concurrent.ListenableFuture; |
23 | 23 | import com.google.common.util.concurrent.MoreExecutors; |
24 | 24 | import com.google.protobuf.ByteString; |
25 | 25 | import lombok.extern.slf4j.Slf4j; |
26 | -import org.springframework.cache.CacheManager; | |
27 | 26 | import org.springframework.stereotype.Service; |
28 | 27 | import org.springframework.util.StringUtils; |
29 | 28 | import org.thingsboard.common.util.JacksonUtil; |
30 | -import org.thingsboard.server.cache.firmware.FirmwareCacheWriter; | |
29 | +import org.thingsboard.server.cache.firmware.FirmwareDataCache; | |
31 | 30 | import org.thingsboard.server.common.data.ApiUsageState; |
32 | 31 | import org.thingsboard.server.common.data.DataConstants; |
33 | 32 | import org.thingsboard.server.common.data.Device; |
34 | 33 | import org.thingsboard.server.common.data.DeviceProfile; |
34 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
35 | 35 | import org.thingsboard.server.common.data.EntityType; |
36 | 36 | import org.thingsboard.server.common.data.Firmware; |
37 | +import org.thingsboard.server.common.data.FirmwareInfo; | |
37 | 38 | import org.thingsboard.server.common.data.ResourceType; |
38 | 39 | import org.thingsboard.server.common.data.TbResource; |
39 | 40 | import org.thingsboard.server.common.data.TenantProfile; |
... | ... | @@ -45,6 +46,8 @@ import org.thingsboard.server.common.data.id.DeviceId; |
45 | 46 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
46 | 47 | import org.thingsboard.server.common.data.id.FirmwareId; |
47 | 48 | import org.thingsboard.server.common.data.id.TenantId; |
49 | +import org.thingsboard.server.common.data.page.PageData; | |
50 | +import org.thingsboard.server.common.data.page.PageLink; | |
48 | 51 | import org.thingsboard.server.common.data.relation.EntityRelation; |
49 | 52 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
50 | 53 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
... | ... | @@ -61,15 +64,18 @@ import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
61 | 64 | import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
62 | 65 | import org.thingsboard.server.dao.firmware.FirmwareService; |
63 | 66 | import org.thingsboard.server.dao.relation.RelationService; |
64 | -import org.thingsboard.server.dao.resource.ResourceService; | |
65 | 67 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
66 | 68 | import org.thingsboard.server.gen.transport.TransportProtos; |
67 | 69 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
70 | +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; | |
71 | +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg; | |
68 | 72 | import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; |
69 | 73 | import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; |
70 | 74 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; |
71 | 75 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; |
72 | 76 | import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; |
77 | +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg; | |
78 | +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg; | |
73 | 79 | import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; |
74 | 80 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
75 | 81 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
... | ... | @@ -92,6 +98,7 @@ import java.util.concurrent.ConcurrentHashMap; |
92 | 98 | import java.util.concurrent.ConcurrentMap; |
93 | 99 | import java.util.concurrent.locks.Lock; |
94 | 100 | import java.util.concurrent.locks.ReentrantLock; |
101 | +import java.util.stream.Collectors; | |
95 | 102 | |
96 | 103 | /** |
97 | 104 | * Created by ashvayka on 05.10.18. |
... | ... | @@ -116,7 +123,7 @@ public class DefaultTransportApiService implements TransportApiService { |
116 | 123 | private final DeviceProvisionService deviceProvisionService; |
117 | 124 | private final TbResourceService resourceService; |
118 | 125 | private final FirmwareService firmwareService; |
119 | - private final FirmwareCacheWriter firmwareCacheWriter; | |
126 | + private final FirmwareDataCache firmwareDataCache; | |
120 | 127 | |
121 | 128 | private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); |
122 | 129 | |
... | ... | @@ -125,7 +132,7 @@ public class DefaultTransportApiService implements TransportApiService { |
125 | 132 | RelationService relationService, DeviceCredentialsService deviceCredentialsService, |
126 | 133 | DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, |
127 | 134 | TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, |
128 | - DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareCacheWriter firmwareCacheWriter) { | |
135 | + DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) { | |
129 | 136 | this.deviceProfileCache = deviceProfileCache; |
130 | 137 | this.tenantProfileCache = tenantProfileCache; |
131 | 138 | this.apiUsageStateService = apiUsageStateService; |
... | ... | @@ -139,49 +146,49 @@ public class DefaultTransportApiService implements TransportApiService { |
139 | 146 | this.deviceProvisionService = deviceProvisionService; |
140 | 147 | this.resourceService = resourceService; |
141 | 148 | this.firmwareService = firmwareService; |
142 | - this.firmwareCacheWriter = firmwareCacheWriter; | |
149 | + this.firmwareDataCache = firmwareDataCache; | |
143 | 150 | } |
144 | 151 | |
145 | 152 | @Override |
146 | 153 | public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { |
147 | 154 | TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); |
155 | + ListenableFuture<TransportApiResponseMsg> result = null; | |
156 | + | |
148 | 157 | if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { |
149 | 158 | ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); |
150 | - return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN), | |
151 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
159 | + result = validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN); | |
152 | 160 | } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) { |
153 | 161 | TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg(); |
154 | - return Futures.transform(validateCredentials(msg), | |
155 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
162 | + result = validateCredentials(msg); | |
156 | 163 | } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { |
157 | 164 | ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); |
158 | - return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE), | |
159 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
165 | + result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE); | |
160 | 166 | } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { |
161 | - return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()), | |
162 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
167 | + result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()); | |
163 | 168 | } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) { |
164 | - return Futures.transform(handle(transportApiRequestMsg.getEntityProfileRequestMsg()), | |
165 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
169 | + result = handle(transportApiRequestMsg.getEntityProfileRequestMsg()); | |
166 | 170 | } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) { |
167 | - return Futures.transform(handle(transportApiRequestMsg.getLwM2MRequestMsg()), | |
168 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
171 | + result = handle(transportApiRequestMsg.getLwM2MRequestMsg()); | |
169 | 172 | } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) { |
170 | 173 | ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg(); |
171 | - return Futures.transform(validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS), | |
172 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
174 | + result = validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS); | |
173 | 175 | } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) { |
174 | - return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()), | |
175 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
176 | + result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()); | |
176 | 177 | } else if (transportApiRequestMsg.hasResourceRequestMsg()) { |
177 | - return Futures.transform(handle(transportApiRequestMsg.getResourceRequestMsg()), | |
178 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
178 | + result = handle(transportApiRequestMsg.getResourceRequestMsg()); | |
179 | + } else if (transportApiRequestMsg.hasSnmpDevicesRequestMsg()) { | |
180 | + result = handle(transportApiRequestMsg.getSnmpDevicesRequestMsg()); | |
181 | + } else if (transportApiRequestMsg.hasDeviceRequestMsg()) { | |
182 | + result = handle(transportApiRequestMsg.getDeviceRequestMsg()); | |
183 | + } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) { | |
184 | + result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg()); | |
179 | 185 | } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) { |
180 | - return Futures.transform(handle(transportApiRequestMsg.getFirmwareRequestMsg()), | |
181 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
186 | + result = handle(transportApiRequestMsg.getFirmwareRequestMsg()); | |
182 | 187 | } |
183 | - return Futures.transform(getEmptyTransportApiResponseFuture(), | |
184 | - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); | |
188 | + | |
189 | + return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), | |
190 | + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), | |
191 | + MoreExecutors.directExecutor()); | |
185 | 192 | } |
186 | 193 | |
187 | 194 | private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { |
... | ... | @@ -375,6 +382,39 @@ public class DefaultTransportApiService implements TransportApiService { |
375 | 382 | return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build()); |
376 | 383 | } |
377 | 384 | |
385 | + private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceRequestMsg requestMsg) { | |
386 | + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); | |
387 | + Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId); | |
388 | + | |
389 | + TransportApiResponseMsg responseMsg; | |
390 | + if (device != null) { | |
391 | + UUID deviceProfileId = device.getDeviceProfileId().getId(); | |
392 | + responseMsg = TransportApiResponseMsg.newBuilder() | |
393 | + .setDeviceResponseMsg(TransportProtos.GetDeviceResponseMsg.newBuilder() | |
394 | + .setDeviceProfileIdMSB(deviceProfileId.getMostSignificantBits()) | |
395 | + .setDeviceProfileIdLSB(deviceProfileId.getLeastSignificantBits()) | |
396 | + .setDeviceTransportConfiguration(ByteString.copyFrom( | |
397 | + dataDecodingEncodingService.encode(device.getDeviceData().getTransportConfiguration()) | |
398 | + ))) | |
399 | + .build(); | |
400 | + } else { | |
401 | + responseMsg = TransportApiResponseMsg.getDefaultInstance(); | |
402 | + } | |
403 | + | |
404 | + return Futures.immediateFuture(responseMsg); | |
405 | + } | |
406 | + | |
407 | + private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceCredentialsRequestMsg requestMsg) { | |
408 | + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); | |
409 | + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId); | |
410 | + | |
411 | + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder() | |
412 | + .setDeviceCredentialsResponseMsg(TransportProtos.GetDeviceCredentialsResponseMsg.newBuilder() | |
413 | + .setDeviceCredentialsData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceCredentials)))) | |
414 | + .build()); | |
415 | + } | |
416 | + | |
417 | + | |
378 | 418 | private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) { |
379 | 419 | TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); |
380 | 420 | ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); |
... | ... | @@ -393,6 +433,22 @@ public class DefaultTransportApiService implements TransportApiService { |
393 | 433 | return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build()); |
394 | 434 | } |
395 | 435 | |
436 | + private ListenableFuture<TransportApiResponseMsg> handle(GetSnmpDevicesRequestMsg requestMsg) { | |
437 | + PageLink pageLink = new PageLink(requestMsg.getPageSize(), requestMsg.getPage()); | |
438 | + PageData<UUID> result = deviceService.findDevicesIdsByDeviceProfileTransportType(DeviceTransportType.SNMP, pageLink); | |
439 | + | |
440 | + GetSnmpDevicesResponseMsg responseMsg = GetSnmpDevicesResponseMsg.newBuilder() | |
441 | + .addAllIds(result.getData().stream() | |
442 | + .map(UUID::toString) | |
443 | + .collect(Collectors.toList())) | |
444 | + .setHasNextPage(result.hasNext()) | |
445 | + .build(); | |
446 | + | |
447 | + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder() | |
448 | + .setSnmpDevicesResponseMsg(responseMsg) | |
449 | + .build()); | |
450 | + } | |
451 | + | |
396 | 452 | private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { |
397 | 453 | return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> { |
398 | 454 | if (device == null) { |
... | ... | @@ -473,19 +529,22 @@ public class DefaultTransportApiService implements TransportApiService { |
473 | 529 | if (firmwareId == null) { |
474 | 530 | builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); |
475 | 531 | } else { |
476 | - Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId); | |
532 | + FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(tenantId, firmwareId); | |
477 | 533 | |
478 | - if (firmware == null) { | |
534 | + if (firmwareInfo == null) { | |
479 | 535 | builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); |
480 | 536 | } else { |
481 | 537 | builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); |
482 | 538 | builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); |
483 | 539 | builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); |
484 | - builder.setTitle(firmware.getTitle()); | |
485 | - builder.setVersion(firmware.getVersion()); | |
486 | - builder.setFileName(firmware.getFileName()); | |
487 | - builder.setContentType(firmware.getContentType()); | |
488 | - firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array()); | |
540 | + builder.setTitle(firmwareInfo.getTitle()); | |
541 | + builder.setVersion(firmwareInfo.getVersion()); | |
542 | + builder.setFileName(firmwareInfo.getFileName()); | |
543 | + builder.setContentType(firmwareInfo.getContentType()); | |
544 | + if (!firmwareDataCache.has(firmwareId.toString())) { | |
545 | + Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId); | |
546 | + firmwareDataCache.put(firmwareId.toString(), firmware.getData().array()); | |
547 | + } | |
489 | 548 | } |
490 | 549 | } |
491 | 550 | ... | ... |
... | ... | @@ -26,6 +26,7 @@ |
26 | 26 | </appender> |
27 | 27 | |
28 | 28 | <logger name="org.thingsboard.server" level="INFO" /> |
29 | + <logger name="org.thingsboard.server.transport.snmp" level="TRACE" /> | |
29 | 30 | |
30 | 31 | <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> |
31 | 32 | <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> | ... | ... |
... | ... | @@ -372,8 +372,8 @@ caffeine: |
372 | 372 | timeToLiveInMinutes: 20000 |
373 | 373 | maxSize: 10000 |
374 | 374 | firmwares: |
375 | - timeToLiveInMinutes: 1440 | |
376 | - maxSize: 100 | |
375 | + timeToLiveInMinutes: 60 | |
376 | + maxSize: 10 | |
377 | 377 | edges: |
378 | 378 | timeToLiveInMinutes: 1440 |
379 | 379 | maxSize: 0 |
... | ... | @@ -496,6 +496,8 @@ audit-log: |
496 | 496 | "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}" |
497 | 497 | "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}" |
498 | 498 | "edge": "${AUDIT_LOG_MASK_EDGE:W}" |
499 | + "tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}" | |
500 | + "firmware": "${AUDIT_LOG_MASK_FIRMWARE:W}" | |
499 | 501 | sink: |
500 | 502 | # Type of external sink. possible options: none, elasticsearch |
501 | 503 | type: "${AUDIT_LOG_SINK_TYPE:none}" |
... | ... | @@ -619,9 +621,9 @@ transport: |
619 | 621 | key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}" |
620 | 622 | # Key alias |
621 | 623 | key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}" |
622 | - # Skip certificate validity check for client certificates. | |
623 | - skip_validity_check_for_client_cert: "${COAP_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" | |
624 | 624 | x509: |
625 | + # Skip certificate validity check for client certificates. | |
626 | + skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" | |
625 | 627 | dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}" |
626 | 628 | dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}" |
627 | 629 | # Local LwM2M transport parameters |
... | ... | @@ -684,6 +686,13 @@ transport: |
684 | 686 | alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}" |
685 | 687 | # Use redis for Security and Registration stores |
686 | 688 | redis.enabled: "${LWM2M_REDIS_ENABLED:false}" |
689 | + snmp: | |
690 | + enabled: "${SNMP_ENABLED:true}" | |
691 | + response_processing: | |
692 | + # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices | |
693 | + parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}" | |
694 | + # to configure SNMP to work over UDP or TCP | |
695 | + underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}" | |
687 | 696 | |
688 | 697 | # Edges parameters |
689 | 698 | edges: | ... | ... |
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java
renamed from
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheReader.java
... | ... | @@ -15,22 +15,20 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.cache.firmware; |
17 | 17 | |
18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
19 | 20 | import org.springframework.cache.CacheManager; |
20 | 21 | import org.springframework.stereotype.Service; |
21 | 22 | |
22 | 23 | import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; |
23 | 24 | |
24 | 25 | @Service |
25 | -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')") | |
26 | -public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { | |
26 | +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) | |
27 | +@RequiredArgsConstructor | |
28 | +public class CaffeineFirmwareCache implements FirmwareDataCache { | |
27 | 29 | |
28 | 30 | private final CacheManager cacheManager; |
29 | 31 | |
30 | - public CaffeineFirmwareCacheReader(CacheManager cacheManager) { | |
31 | - this.cacheManager = cacheManager; | |
32 | - } | |
33 | - | |
34 | 32 | @Override |
35 | 33 | public byte[] get(String key) { |
36 | 34 | return get(key, 0, 0); |
... | ... | @@ -57,4 +55,14 @@ public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { |
57 | 55 | } |
58 | 56 | return new byte[0]; |
59 | 57 | } |
58 | + | |
59 | + @Override | |
60 | + public void put(String key, byte[] value) { | |
61 | + cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); | |
62 | + } | |
63 | + | |
64 | + @Override | |
65 | + public void evict(String key) { | |
66 | + cacheManager.getCache(FIRMWARE_CACHE).evict(key); | |
67 | + } | |
60 | 68 | } | ... | ... |
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java
renamed from
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheReader.java
... | ... | @@ -15,8 +15,18 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.cache.firmware; |
17 | 17 | |
18 | -public interface FirmwareCacheReader { | |
18 | +public interface FirmwareDataCache { | |
19 | + | |
19 | 20 | byte[] get(String key); |
20 | 21 | |
21 | 22 | byte[] get(String key, int chunkSize, int chunk); |
23 | + | |
24 | + void put(String key, byte[] value); | |
25 | + | |
26 | + void evict(String key); | |
27 | + | |
28 | + default boolean has(String firmwareId) { | |
29 | + byte[] data = get(firmwareId, 1, 0); | |
30 | + return data != null && data.length > 0; | |
31 | + } | |
22 | 32 | } | ... | ... |
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java
renamed from
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheReader.java
... | ... | @@ -15,18 +15,20 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.cache.firmware; |
17 | 17 | |
18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
19 | 20 | import org.springframework.data.redis.connection.RedisConnection; |
20 | 21 | import org.springframework.data.redis.connection.RedisConnectionFactory; |
21 | 22 | import org.springframework.stereotype.Service; |
22 | 23 | |
24 | +import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; | |
25 | + | |
23 | 26 | @Service |
24 | -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && '${cache.type:null}'=='redis'") | |
25 | -public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache implements FirmwareCacheReader { | |
27 | +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") | |
28 | +@RequiredArgsConstructor | |
29 | +public class RedisFirmwareDataCache implements FirmwareDataCache { | |
26 | 30 | |
27 | - public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) { | |
28 | - super(redisConnectionFactory); | |
29 | - } | |
31 | + private final RedisConnectionFactory redisConnectionFactory; | |
30 | 32 | |
31 | 33 | @Override |
32 | 34 | public byte[] get(String key) { |
... | ... | @@ -46,4 +48,21 @@ public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache impleme |
46 | 48 | } |
47 | 49 | } |
48 | 50 | |
51 | + @Override | |
52 | + public void put(String key, byte[] value) { | |
53 | + try (RedisConnection connection = redisConnectionFactory.getConnection()) { | |
54 | + connection.set(toFirmwareCacheKey(key), value); | |
55 | + } | |
56 | + } | |
57 | + | |
58 | + @Override | |
59 | + public void evict(String key) { | |
60 | + try (RedisConnection connection = redisConnectionFactory.getConnection()) { | |
61 | + connection.del(toFirmwareCacheKey(key)); | |
62 | + } | |
63 | + } | |
64 | + | |
65 | + private byte[] toFirmwareCacheKey(String key) { | |
66 | + return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); | |
67 | + } | |
49 | 68 | } | ... | ... |
... | ... | @@ -19,11 +19,10 @@ import lombok.Getter; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 21 | import org.springframework.beans.factory.annotation.Value; |
22 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
23 | 22 | import org.springframework.stereotype.Component; |
24 | 23 | |
25 | 24 | @Slf4j |
26 | -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") | |
25 | +@TbCoapServerComponent | |
27 | 26 | @Component |
28 | 27 | public class CoapServerContext { |
29 | 28 | ... | ... |
... | ... | @@ -23,7 +23,6 @@ import org.eclipse.californium.core.server.resources.Resource; |
23 | 23 | import org.eclipse.californium.scandium.DTLSConnector; |
24 | 24 | import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
25 | 25 | import org.springframework.beans.factory.annotation.Autowired; |
26 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
27 | 26 | import org.springframework.stereotype.Component; |
28 | 27 | |
29 | 28 | import javax.annotation.PostConstruct; |
... | ... | @@ -39,7 +38,7 @@ import java.util.concurrent.TimeUnit; |
39 | 38 | |
40 | 39 | @Slf4j |
41 | 40 | @Component |
42 | -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") | |
41 | +@TbCoapServerComponent | |
43 | 42 | public class DefaultCoapServerService implements CoapServerService { |
44 | 43 | |
45 | 44 | @Autowired | ... | ... |
... | ... | @@ -39,7 +39,6 @@ import java.util.Collections; |
39 | 39 | import java.util.Optional; |
40 | 40 | |
41 | 41 | @Slf4j |
42 | -@ConditionalOnExpression("'${transport.coap.enabled}'=='true'") | |
43 | 42 | @ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false) |
44 | 43 | @Component |
45 | 44 | public class TbCoapDtlsSettings { |
... | ... | @@ -50,7 +49,7 @@ public class TbCoapDtlsSettings { |
50 | 49 | @Value("${transport.coap.dtls.bind_port}") |
51 | 50 | private Integer port; |
52 | 51 | |
53 | - @Value("${transport.coap.dtls.mode}") | |
52 | + @Value("${transport.coap.dtls.mode:NO_AUTH}") | |
54 | 53 | private String mode; |
55 | 54 | |
56 | 55 | @Value("${transport.coap.dtls.key_store}") |
... | ... | @@ -65,13 +64,13 @@ public class TbCoapDtlsSettings { |
65 | 64 | @Value("${transport.coap.dtls.key_alias}") |
66 | 65 | private String keyAlias; |
67 | 66 | |
68 | - @Value("${transport.coap.dtls.skip_validity_check_for_client_cert}") | |
67 | + @Value("${transport.coap.dtls.x509.skip_validity_check_for_client_cert:false}") | |
69 | 68 | private boolean skipValidityCheckForClientCert; |
70 | 69 | |
71 | - @Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout}") | |
70 | + @Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout:86400000}") | |
72 | 71 | private long dtlsSessionInactivityTimeout; |
73 | 72 | |
74 | - @Value("${transport.coap.dtls.x509.dtls_session_report_timeout}") | |
73 | + @Value("${transport.coap.dtls.x509.dtls_session_report_timeout:1800000}") | |
75 | 74 | private long dtlsSessionReportTimeout; |
76 | 75 | |
77 | 76 | @Autowired | ... | ... |
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapServerComponent.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.coapserver; | |
17 | + | |
18 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
19 | + | |
20 | +import java.lang.annotation.Retention; | |
21 | +import java.lang.annotation.RetentionPolicy; | |
22 | + | |
23 | +@Retention(RetentionPolicy.RUNTIME) | |
24 | +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") | |
25 | +public @interface TbCoapServerComponent { | |
26 | +} | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.Device; |
20 | 20 | import org.thingsboard.server.common.data.DeviceInfo; |
21 | 21 | import org.thingsboard.server.common.data.DeviceProfile; |
22 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
22 | 23 | import org.thingsboard.server.common.data.EntitySubtype; |
23 | 24 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
24 | 25 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; |
32 | 33 | import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
33 | 34 | |
34 | 35 | import java.util.List; |
36 | +import java.util.UUID; | |
35 | 37 | |
36 | 38 | public interface DeviceService { |
37 | 39 | |
... | ... | @@ -93,6 +95,8 @@ public interface DeviceService { |
93 | 95 | |
94 | 96 | Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile); |
95 | 97 | |
98 | + PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink); | |
99 | + | |
96 | 100 | Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); |
97 | 101 | |
98 | 102 | Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.firmware; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
18 | 19 | import org.thingsboard.server.common.data.Firmware; |
19 | 20 | import org.thingsboard.server.common.data.FirmwareInfo; |
20 | 21 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
... | ... | @@ -40,6 +41,8 @@ public interface FirmwareService { |
40 | 41 | |
41 | 42 | FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId); |
42 | 43 | |
44 | + ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId); | |
45 | + | |
43 | 46 | PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); |
44 | 47 | |
45 | 48 | PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.resource; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
18 | 19 | import org.thingsboard.server.common.data.ResourceType; |
19 | 20 | import org.thingsboard.server.common.data.TbResource; |
20 | 21 | import org.thingsboard.server.common.data.TbResourceInfo; |
... | ... | @@ -34,6 +35,8 @@ public interface ResourceService { |
34 | 35 | |
35 | 36 | TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); |
36 | 37 | |
38 | + ListenableFuture<TbResourceInfo> findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId); | |
39 | + | |
37 | 40 | PageData<TbResourceInfo> findAllTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); |
38 | 41 | |
39 | 42 | PageData<TbResourceInfo> findTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); | ... | ... |
... | ... | @@ -87,6 +87,10 @@ |
87 | 87 | <groupId>org.thingsboard</groupId> |
88 | 88 | <artifactId>protobuf-dynamic</artifactId> |
89 | 89 | </dependency> |
90 | + <dependency> | |
91 | + <groupId>org.apache.commons</groupId> | |
92 | + <artifactId>commons-lang3</artifactId> | |
93 | + </dependency> | |
90 | 94 | </dependencies> |
91 | 95 | |
92 | 96 | <build> | ... | ... |
... | ... | @@ -99,6 +99,7 @@ public class DataConstants { |
99 | 99 | public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version"; |
100 | 100 | public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; |
101 | 101 | public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; |
102 | + public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; | |
102 | 103 | public static final String FIRMWARE_STATE = "fw_state"; |
103 | 104 | |
104 | 105 | //attributes | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data; |
17 | 17 | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
18 | 19 | import lombok.Data; |
19 | 20 | import lombok.EqualsAndHashCode; |
20 | 21 | import lombok.extern.slf4j.Slf4j; |
... | ... | @@ -25,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
25 | 26 | @Slf4j |
26 | 27 | @Data |
27 | 28 | @EqualsAndHashCode(callSuper = true) |
28 | -public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasTenantId { | |
29 | +public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasName, HasTenantId { | |
29 | 30 | |
30 | 31 | private static final long serialVersionUID = 3168391583570815419L; |
31 | 32 | |
... | ... | @@ -65,4 +66,10 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> |
65 | 66 | public String getSearchText() { |
66 | 67 | return title; |
67 | 68 | } |
69 | + | |
70 | + @Override | |
71 | + @JsonIgnore | |
72 | + public String getName() { | |
73 | + return title; | |
74 | + } | |
68 | 75 | } | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data; |
17 | 17 | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
18 | 19 | import lombok.Data; |
19 | 20 | import lombok.EqualsAndHashCode; |
20 | 21 | import lombok.extern.slf4j.Slf4j; |
... | ... | @@ -25,7 +26,7 @@ import org.thingsboard.server.common.data.validation.NoXss; |
25 | 26 | @Slf4j |
26 | 27 | @Data |
27 | 28 | @EqualsAndHashCode(callSuper = true) |
28 | -public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasTenantId { | |
29 | +public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasName, HasTenantId { | |
29 | 30 | |
30 | 31 | private static final long serialVersionUID = 7282664529021651736L; |
31 | 32 | |
... | ... | @@ -54,6 +55,12 @@ public class TbResourceInfo extends SearchTextBased<TbResourceId> implements Has |
54 | 55 | } |
55 | 56 | |
56 | 57 | @Override |
58 | + @JsonIgnore | |
59 | + public String getName() { | |
60 | + return title; | |
61 | + } | |
62 | + | |
63 | + @Override | |
57 | 64 | public String getSearchText() { |
58 | 65 | return searchText != null ? searchText : title; |
59 | 66 | } | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java
renamed from
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java
... | ... | @@ -13,8 +13,8 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.cache.firmware; | |
16 | +package org.thingsboard.server.common.data; | |
17 | 17 | |
18 | -public interface FirmwareCacheWriter { | |
19 | - void put(String key, byte[] value); | |
18 | +public interface TbTransportService { | |
19 | + String getName(); | |
20 | 20 | } | ... | ... |
... | ... | @@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; |
21 | 21 | import com.fasterxml.jackson.annotation.JsonTypeInfo; |
22 | 22 | import org.thingsboard.server.common.data.DeviceTransportType; |
23 | 23 | |
24 | +import java.io.Serializable; | |
25 | + | |
24 | 26 | @JsonIgnoreProperties(ignoreUnknown = true) |
25 | 27 | @JsonTypeInfo( |
26 | 28 | use = JsonTypeInfo.Id.NAME, |
... | ... | @@ -29,11 +31,14 @@ import org.thingsboard.server.common.data.DeviceTransportType; |
29 | 31 | @JsonSubTypes({ |
30 | 32 | @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), |
31 | 33 | @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), |
34 | + @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"), | |
32 | 35 | @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), |
33 | - @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP")}) | |
34 | -public interface DeviceTransportConfiguration { | |
35 | - | |
36 | + @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")}) | |
37 | +public interface DeviceTransportConfiguration extends Serializable { | |
36 | 38 | @JsonIgnore |
37 | 39 | DeviceTransportType getType(); |
38 | 40 | |
41 | + default void validate() { | |
42 | + } | |
43 | + | |
39 | 44 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.device.data; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import lombok.Data; | |
20 | +import lombok.ToString; | |
21 | +import org.apache.commons.lang3.ObjectUtils; | |
22 | +import org.apache.commons.lang3.StringUtils; | |
23 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
24 | +import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol; | |
25 | +import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol; | |
26 | +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; | |
27 | + | |
28 | +import java.util.Objects; | |
29 | + | |
30 | +@Data | |
31 | +@ToString(of = {"host", "port", "protocolVersion"}) | |
32 | +public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration { | |
33 | + private String host; | |
34 | + private Integer port; | |
35 | + private SnmpProtocolVersion protocolVersion; | |
36 | + | |
37 | + /* | |
38 | + * For SNMP v1 and v2c | |
39 | + * */ | |
40 | + private String community; | |
41 | + | |
42 | + /* | |
43 | + * For SNMP v3 | |
44 | + * */ | |
45 | + private String username; | |
46 | + private String securityName; | |
47 | + private String contextName; | |
48 | + private AuthenticationProtocol authenticationProtocol; | |
49 | + private String authenticationPassphrase; | |
50 | + private PrivacyProtocol privacyProtocol; | |
51 | + private String privacyPassphrase; | |
52 | + private String engineId; | |
53 | + | |
54 | + @Override | |
55 | + public DeviceTransportType getType() { | |
56 | + return DeviceTransportType.SNMP; | |
57 | + } | |
58 | + | |
59 | + @Override | |
60 | + public void validate() { | |
61 | + if (!isValid()) { | |
62 | + throw new IllegalArgumentException("Transport configuration is not valid"); | |
63 | + } | |
64 | + } | |
65 | + | |
66 | + @JsonIgnore | |
67 | + private boolean isValid() { | |
68 | + boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null; | |
69 | + if (isValid) { | |
70 | + switch (protocolVersion) { | |
71 | + case V1: | |
72 | + case V2C: | |
73 | + isValid = StringUtils.isNotEmpty(community); | |
74 | + break; | |
75 | + case V3: | |
76 | + isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName) | |
77 | + && contextName != null && authenticationProtocol != null | |
78 | + && StringUtils.isNotBlank(authenticationPassphrase) | |
79 | + && privacyProtocol != null && privacyPassphrase != null && engineId != null; | |
80 | + break; | |
81 | + } | |
82 | + } | |
83 | + return isValid; | |
84 | + } | |
85 | +} | ... | ... |
... | ... | @@ -29,13 +29,18 @@ import java.io.Serializable; |
29 | 29 | include = JsonTypeInfo.As.PROPERTY, |
30 | 30 | property = "type") |
31 | 31 | @JsonSubTypes({ |
32 | - @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), | |
33 | - @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), | |
34 | - @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), | |
35 | - @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP")}) | |
32 | + @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), | |
33 | + @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), | |
34 | + @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), | |
35 | + @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"), | |
36 | + @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP") | |
37 | +}) | |
36 | 38 | public interface DeviceProfileTransportConfiguration extends Serializable { |
37 | 39 | |
38 | 40 | @JsonIgnore |
39 | 41 | DeviceTransportType getType(); |
40 | 42 | |
43 | + default void validate() { | |
44 | + } | |
45 | + | |
41 | 46 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.device.profile; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import lombok.Data; | |
20 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
21 | +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; | |
22 | +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; | |
23 | + | |
24 | +import java.util.List; | |
25 | + | |
26 | +@Data | |
27 | +public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { | |
28 | + private Integer timeoutMs; | |
29 | + private Integer retries; | |
30 | + private List<SnmpCommunicationConfig> communicationConfigs; | |
31 | + | |
32 | + @Override | |
33 | + public DeviceTransportType getType() { | |
34 | + return DeviceTransportType.SNMP; | |
35 | + } | |
36 | + | |
37 | + @Override | |
38 | + public void validate() { | |
39 | + if (!isValid()) { | |
40 | + throw new IllegalArgumentException("SNMP transport configuration is not valid"); | |
41 | + } | |
42 | + } | |
43 | + | |
44 | + @JsonIgnore | |
45 | + private boolean isValid() { | |
46 | + return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0 | |
47 | + && communicationConfigs != null | |
48 | + && communicationConfigs.stream().allMatch(config -> config != null && config.isValid()) | |
49 | + && communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid) | |
50 | + .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum(); | |
51 | + } | |
52 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp; | |
17 | + | |
18 | +import java.util.Arrays; | |
19 | +import java.util.Optional; | |
20 | + | |
21 | +public enum AuthenticationProtocol { | |
22 | + SHA_1("1.3.6.1.6.3.10.1.1.3"), | |
23 | + SHA_224("1.3.6.1.6.3.10.1.1.4"), | |
24 | + SHA_256("1.3.6.1.6.3.10.1.1.5"), | |
25 | + SHA_384("1.3.6.1.6.3.10.1.1.6"), | |
26 | + SHA_512("1.3.6.1.6.3.10.1.1.7"), | |
27 | + MD5("1.3.6.1.6.3.10.1.1.2"); | |
28 | + | |
29 | + // oids taken from org.snmp4j.security.SecurityProtocol implementations | |
30 | + private final String oid; | |
31 | + | |
32 | + AuthenticationProtocol(String oid) { | |
33 | + this.oid = oid; | |
34 | + } | |
35 | + | |
36 | + public String getOid() { | |
37 | + return oid; | |
38 | + } | |
39 | + | |
40 | + public static Optional<AuthenticationProtocol> forName(String name) { | |
41 | + return Arrays.stream(values()) | |
42 | + .filter(protocol -> protocol.name().equalsIgnoreCase(name)) | |
43 | + .findFirst(); | |
44 | + } | |
45 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp; | |
17 | + | |
18 | +import java.util.Arrays; | |
19 | +import java.util.Optional; | |
20 | + | |
21 | +public enum PrivacyProtocol { | |
22 | + DES("1.3.6.1.6.3.10.1.2.2"), | |
23 | + AES_128("1.3.6.1.6.3.10.1.2.4"), | |
24 | + AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"), | |
25 | + AES_256("1.3.6.1.4.1.4976.2.2.1.1.2"); | |
26 | + | |
27 | + // oids taken from org.snmp4j.security.SecurityProtocol implementations | |
28 | + private final String oid; | |
29 | + | |
30 | + PrivacyProtocol(String oid) { | |
31 | + this.oid = oid; | |
32 | + } | |
33 | + | |
34 | + public String getOid() { | |
35 | + return oid; | |
36 | + } | |
37 | + | |
38 | + public static Optional<PrivacyProtocol> forName(String name) { | |
39 | + return Arrays.stream(values()) | |
40 | + .filter(protocol -> protocol.name().equalsIgnoreCase(name)) | |
41 | + .findFirst(); | |
42 | + } | |
43 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp; | |
17 | + | |
18 | +public enum SnmpCommunicationSpec { | |
19 | + TELEMETRY_QUERYING, | |
20 | + | |
21 | + CLIENT_ATTRIBUTES_QUERYING, | |
22 | + SHARED_ATTRIBUTES_SETTING, | |
23 | + | |
24 | + TO_DEVICE_RPC_REQUEST, | |
25 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import lombok.AllArgsConstructor; | |
20 | +import lombok.Data; | |
21 | +import lombok.NoArgsConstructor; | |
22 | +import org.apache.commons.lang3.StringUtils; | |
23 | +import org.thingsboard.server.common.data.kv.DataType; | |
24 | + | |
25 | +import java.util.regex.Pattern; | |
26 | + | |
27 | +@Data | |
28 | +@AllArgsConstructor | |
29 | +@NoArgsConstructor | |
30 | +public class SnmpMapping { | |
31 | + private String oid; | |
32 | + private String key; | |
33 | + private DataType dataType; | |
34 | + | |
35 | + private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$"); | |
36 | + | |
37 | + @JsonIgnore | |
38 | + public boolean isValid() { | |
39 | + return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key); | |
40 | + } | |
41 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp; | |
17 | + | |
18 | +public enum SnmpMethod { | |
19 | + GET(-96), | |
20 | + SET(-93); | |
21 | + | |
22 | + // codes taken from org.snmp4j.PDU class | |
23 | + private final int code; | |
24 | + | |
25 | + SnmpMethod(int code) { | |
26 | + this.code = code; | |
27 | + } | |
28 | + | |
29 | + public int getCode() { | |
30 | + return code; | |
31 | + } | |
32 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp; | |
17 | + | |
18 | +public enum SnmpProtocolVersion { | |
19 | + V1(0), | |
20 | + V2C(1), | |
21 | + V3(3); | |
22 | + | |
23 | + private final int code; | |
24 | + | |
25 | + SnmpProtocolVersion(int code) { | |
26 | + this.code = code; | |
27 | + } | |
28 | + | |
29 | + public int getCode() { | |
30 | + return code; | |
31 | + } | |
32 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp.config; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; | |
20 | + | |
21 | +import java.util.List; | |
22 | + | |
23 | +@Data | |
24 | +public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig { | |
25 | + protected List<SnmpMapping> mappings; | |
26 | + | |
27 | + @Override | |
28 | + public boolean isValid() { | |
29 | + return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid()); | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public List<SnmpMapping> getAllMappings() { | |
34 | + return mappings; | |
35 | + } | |
36 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp.config; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; | |
21 | + | |
22 | +@EqualsAndHashCode(callSuper = true) | |
23 | +@Data | |
24 | +public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { | |
25 | + private Long queryingFrequencyMs; | |
26 | + | |
27 | + @Override | |
28 | + public SnmpMethod getMethod() { | |
29 | + return SnmpMethod.GET; | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public boolean isValid() { | |
34 | + return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid(); | |
35 | + } | |
36 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp.config; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |
20 | +import com.fasterxml.jackson.annotation.JsonSubTypes; | |
21 | +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; | |
22 | +import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
23 | +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; | |
24 | +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; | |
25 | +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; | |
26 | +import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig; | |
27 | +import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig; | |
28 | +import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig; | |
29 | +import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpcRequestSnmpCommunicationConfig; | |
30 | + | |
31 | +import java.util.List; | |
32 | + | |
33 | +@JsonIgnoreProperties(ignoreUnknown = true) | |
34 | +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec") | |
35 | +@JsonSubTypes({ | |
36 | + @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"), | |
37 | + @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"), | |
38 | + @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"), | |
39 | + @Type(value = ToDeviceRpcRequestSnmpCommunicationConfig.class, name = "TO_DEVICE_RPC_REQUEST") | |
40 | +}) | |
41 | +public interface SnmpCommunicationConfig { | |
42 | + | |
43 | + SnmpCommunicationSpec getSpec(); | |
44 | + | |
45 | + @JsonIgnore | |
46 | + default SnmpMethod getMethod() { | |
47 | + return null; | |
48 | + } | |
49 | + | |
50 | + @JsonIgnore | |
51 | + List<SnmpMapping> getAllMappings(); | |
52 | + | |
53 | + @JsonIgnore | |
54 | + boolean isValid(); | |
55 | + | |
56 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java
renamed from
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java
... | ... | @@ -13,26 +13,16 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.cache.firmware; | |
16 | +package org.thingsboard.server.common.data.transport.snmp.config.impl; | |
17 | 17 | |
18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
19 | -import org.springframework.data.redis.connection.RedisConnection; | |
20 | -import org.springframework.data.redis.connection.RedisConnectionFactory; | |
21 | -import org.springframework.stereotype.Service; | |
18 | +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; | |
19 | +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; | |
22 | 20 | |
23 | -@Service | |
24 | -@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${cache.type:null}'=='redis'") | |
25 | -public class RedisFirmwareCacheWriter extends AbstractRedisFirmwareCache implements FirmwareCacheWriter { | |
26 | - | |
27 | - public RedisFirmwareCacheWriter(RedisConnectionFactory redisConnectionFactory) { | |
28 | - super(redisConnectionFactory); | |
29 | - } | |
21 | +public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig { | |
30 | 22 | |
31 | 23 | @Override |
32 | - public void put(String key, byte[] value) { | |
33 | - try (RedisConnection connection = redisConnectionFactory.getConnection()) { | |
34 | - connection.set(toFirmwareCacheKey(key), value); | |
35 | - } | |
24 | + public SnmpCommunicationSpec getSpec() { | |
25 | + return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING; | |
36 | 26 | } |
37 | 27 | |
38 | 28 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp.config.impl; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; | |
19 | +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; | |
20 | +import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig; | |
21 | + | |
22 | +public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { | |
23 | + | |
24 | + @Override | |
25 | + public SnmpCommunicationSpec getSpec() { | |
26 | + return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING; | |
27 | + } | |
28 | + | |
29 | + @Override | |
30 | + public SnmpMethod getMethod() { | |
31 | + return SnmpMethod.SET; | |
32 | + } | |
33 | + | |
34 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.common.data.transport.snmp.config.impl; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; | |
21 | +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; | |
22 | + | |
23 | +@EqualsAndHashCode(callSuper = true) | |
24 | +@Data | |
25 | +public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig { | |
26 | + | |
27 | + @Override | |
28 | + public SnmpCommunicationSpec getSpec() { | |
29 | + return SnmpCommunicationSpec.TELEMETRY_QUERYING; | |
30 | + } | |
31 | + | |
32 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcRequestSnmpCommunicationConfig.java
renamed from
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java
... | ... | @@ -13,26 +13,14 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.cache.firmware; | |
16 | +package org.thingsboard.server.common.data.transport.snmp.config.impl; | |
17 | 17 | |
18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
19 | -import org.springframework.cache.CacheManager; | |
20 | -import org.springframework.stereotype.Service; | |
21 | - | |
22 | -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; | |
23 | - | |
24 | -@Service | |
25 | -@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')") | |
26 | -public class CaffeineFirmwareCacheWriter implements FirmwareCacheWriter { | |
27 | - | |
28 | - private final CacheManager cacheManager; | |
29 | - | |
30 | - public CaffeineFirmwareCacheWriter(CacheManager cacheManager) { | |
31 | - this.cacheManager = cacheManager; | |
32 | - } | |
18 | +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; | |
19 | +import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig; | |
33 | 20 | |
21 | +public class ToDeviceRpcRequestSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { | |
34 | 22 | @Override |
35 | - public void put(String key, byte[] value) { | |
36 | - cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); | |
23 | + public SnmpCommunicationSpec getSpec() { | |
24 | + return SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST; | |
37 | 25 | } |
38 | 26 | } | ... | ... |
... | ... | @@ -19,19 +19,23 @@ import lombok.Getter; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 21 | import org.springframework.beans.factory.annotation.Value; |
22 | +import org.springframework.context.ApplicationContext; | |
22 | 23 | import org.springframework.stereotype.Component; |
23 | 24 | import org.springframework.util.StringUtils; |
25 | +import org.thingsboard.server.common.data.TbTransportService; | |
24 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 27 | import org.thingsboard.server.common.msg.queue.ServiceType; |
26 | 28 | import org.thingsboard.server.gen.transport.TransportProtos; |
27 | 29 | import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; |
28 | 30 | import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; |
29 | 31 | import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; |
32 | +import org.thingsboard.server.queue.util.AfterContextReady; | |
30 | 33 | |
31 | 34 | import javax.annotation.PostConstruct; |
32 | 35 | import java.net.InetAddress; |
33 | 36 | import java.net.UnknownHostException; |
34 | 37 | import java.util.Arrays; |
38 | +import java.util.Collection; | |
35 | 39 | import java.util.Collections; |
36 | 40 | import java.util.List; |
37 | 41 | import java.util.Optional; |
... | ... | @@ -56,6 +60,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { |
56 | 60 | |
57 | 61 | @Autowired(required = false) |
58 | 62 | private TbQueueRuleEngineSettings ruleEngineSettings; |
63 | + @Autowired | |
64 | + private ApplicationContext applicationContext; | |
59 | 65 | |
60 | 66 | private List<ServiceType> serviceTypes; |
61 | 67 | private ServiceInfo serviceInfo; |
... | ... | @@ -102,6 +108,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { |
102 | 108 | serviceInfo = builder.build(); |
103 | 109 | } |
104 | 110 | |
111 | + @AfterContextReady | |
112 | + public void setTransports() { | |
113 | + serviceInfo = ServiceInfo.newBuilder(serviceInfo) | |
114 | + .addAllTransports(getTransportServices().stream() | |
115 | + .map(TbTransportService::getName) | |
116 | + .collect(Collectors.toSet())) | |
117 | + .build(); | |
118 | + } | |
119 | + | |
120 | + private Collection<TbTransportService> getTransportServices() { | |
121 | + return applicationContext.getBeansOfType(TbTransportService.class).values(); | |
122 | + } | |
123 | + | |
105 | 124 | @Override |
106 | 125 | public ServiceInfo getServiceInfo() { |
107 | 126 | return serviceInfo; | ... | ... |
... | ... | @@ -15,26 +15,27 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.queue.discovery; |
17 | 17 | |
18 | -import com.google.common.hash.HashCode; | |
19 | 18 | import com.google.common.hash.HashFunction; |
19 | +import com.google.common.hash.Hasher; | |
20 | 20 | import com.google.common.hash.Hashing; |
21 | -import lombok.Getter; | |
22 | 21 | import lombok.extern.slf4j.Slf4j; |
23 | 22 | import org.springframework.beans.factory.annotation.Value; |
24 | 23 | import org.springframework.context.ApplicationEventPublisher; |
25 | 24 | import org.springframework.stereotype.Service; |
26 | 25 | import org.thingsboard.server.common.data.id.EntityId; |
27 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
28 | -import org.thingsboard.server.common.msg.queue.ServiceQueueKey; | |
29 | 27 | import org.thingsboard.server.common.msg.queue.ServiceQueue; |
28 | +import org.thingsboard.server.common.msg.queue.ServiceQueueKey; | |
30 | 29 | import org.thingsboard.server.common.msg.queue.ServiceType; |
31 | 30 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
32 | 31 | import org.thingsboard.server.gen.transport.TransportProtos; |
33 | 32 | import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; |
33 | +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; | |
34 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
35 | +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; | |
34 | 36 | import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; |
35 | 37 | |
36 | 38 | import javax.annotation.PostConstruct; |
37 | -import java.nio.charset.StandardCharsets; | |
38 | 39 | import java.util.ArrayList; |
39 | 40 | import java.util.Collections; |
40 | 41 | import java.util.Comparator; |
... | ... | @@ -46,7 +47,6 @@ import java.util.Set; |
46 | 47 | import java.util.UUID; |
47 | 48 | import java.util.concurrent.ConcurrentHashMap; |
48 | 49 | import java.util.concurrent.ConcurrentMap; |
49 | -import java.util.concurrent.ConcurrentNavigableMap; | |
50 | 50 | import java.util.stream.Collectors; |
51 | 51 | |
52 | 52 | @Service |
... | ... | @@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService { |
186 | 186 | applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes)); |
187 | 187 | } |
188 | 188 | } |
189 | + | |
190 | + applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService)); | |
189 | 191 | } |
190 | 192 | |
191 | 193 | @Override |
... | ... | @@ -219,6 +221,14 @@ public class HashPartitionService implements PartitionService { |
219 | 221 | } |
220 | 222 | } |
221 | 223 | |
224 | + @Override | |
225 | + public int resolvePartitionIndex(UUID entityId, int partitions) { | |
226 | + int hash = hashFunction.newHasher() | |
227 | + .putLong(entityId.getMostSignificantBits()) | |
228 | + .putLong(entityId.getLeastSignificantBits()).hash().asInt(); | |
229 | + return Math.abs(hash % partitions); | |
230 | + } | |
231 | + | |
222 | 232 | private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) { |
223 | 233 | final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>(); |
224 | 234 | services.forEach(serviceInfo -> { | ... | ... |
... | ... | @@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId; |
20 | 20 | import org.thingsboard.server.common.msg.queue.ServiceType; |
21 | 21 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
22 | 22 | import org.thingsboard.server.gen.transport.TransportProtos; |
23 | +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; | |
23 | 24 | |
24 | 25 | import java.util.List; |
25 | 26 | import java.util.Set; |
27 | +import java.util.UUID; | |
26 | 28 | |
27 | 29 | /** |
28 | 30 | * Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent} |
... | ... | @@ -55,4 +57,6 @@ public interface PartitionService { |
55 | 57 | * @return |
56 | 58 | */ |
57 | 59 | TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId); |
60 | + | |
61 | + int resolvePartitionIndex(UUID entityId, int partitions); | |
58 | 62 | } | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.context.ApplicationListener; |
20 | +import org.thingsboard.server.queue.discovery.event.TbApplicationEvent; | |
20 | 21 | |
21 | 22 | import java.util.concurrent.locks.Lock; |
22 | 23 | import java.util.concurrent.locks.ReentrantLock; | ... | ... |
... | ... | @@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException; |
33 | 33 | import org.springframework.beans.factory.annotation.Value; |
34 | 34 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
35 | 35 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
36 | +import org.springframework.context.ApplicationEventPublisher; | |
36 | 37 | import org.springframework.context.event.EventListener; |
37 | 38 | import org.springframework.core.annotation.Order; |
38 | 39 | import org.springframework.stereotype.Service; |
39 | 40 | import org.springframework.util.Assert; |
40 | 41 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
41 | 42 | import org.thingsboard.server.gen.transport.TransportProtos; |
43 | +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; | |
42 | 44 | |
43 | 45 | import javax.annotation.PostConstruct; |
44 | 46 | import javax.annotation.PreDestroy; |
... | ... | @@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi |
77 | 79 | |
78 | 80 | private volatile boolean stopped = true; |
79 | 81 | |
80 | - public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) { | |
82 | + public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, | |
83 | + PartitionService partitionService) { | |
81 | 84 | this.serviceInfoProvider = serviceInfoProvider; |
82 | 85 | this.partitionService = partitionService; |
83 | 86 | } | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java
renamed from
common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java
... | ... | @@ -13,10 +13,9 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.queue.discovery; | |
16 | +package org.thingsboard.server.queue.discovery.event; | |
17 | 17 | |
18 | 18 | import lombok.Getter; |
19 | -import org.springframework.context.ApplicationEvent; | |
20 | 19 | import org.thingsboard.server.common.msg.queue.ServiceQueueKey; |
21 | 20 | |
22 | 21 | import java.util.Set; | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java
renamed from
common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java
... | ... | @@ -13,10 +13,9 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.queue.discovery; | |
16 | +package org.thingsboard.server.queue.discovery.event; | |
17 | 17 | |
18 | 18 | import lombok.Getter; |
19 | -import org.springframework.context.ApplicationEvent; | |
20 | 19 | import org.thingsboard.server.common.msg.queue.ServiceQueueKey; |
21 | 20 | import org.thingsboard.server.common.msg.queue.ServiceType; |
22 | 21 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.queue.discovery.event; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | +import lombok.ToString; | |
20 | +import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; | |
21 | + | |
22 | +import java.util.List; | |
23 | + | |
24 | +@Getter | |
25 | +@ToString | |
26 | +public class ServiceListChangedEvent extends TbApplicationEvent { | |
27 | + private final List<ServiceInfo> otherServices; | |
28 | + private final ServiceInfo currentService; | |
29 | + | |
30 | + public ServiceListChangedEvent(List<ServiceInfo> otherServices, ServiceInfo currentService) { | |
31 | + super(otherServices); | |
32 | + this.otherServices = otherServices; | |
33 | + this.currentService = currentService; | |
34 | + } | |
35 | +} | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java
renamed from
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.queue.discovery; | |
16 | +package org.thingsboard.server.queue.discovery.event; | |
17 | 17 | |
18 | 18 | import lombok.Getter; |
19 | 19 | import org.springframework.context.ApplicationEvent; | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.queue.util; | |
17 | + | |
18 | +import org.springframework.context.event.ContextRefreshedEvent; | |
19 | +import org.springframework.context.event.EventListener; | |
20 | +import org.springframework.core.annotation.AliasFor; | |
21 | +import org.springframework.core.annotation.Order; | |
22 | + | |
23 | +import java.lang.annotation.ElementType; | |
24 | +import java.lang.annotation.Retention; | |
25 | +import java.lang.annotation.RetentionPolicy; | |
26 | +import java.lang.annotation.Target; | |
27 | + | |
28 | +@Retention(RetentionPolicy.RUNTIME) | |
29 | +@Target(ElementType.METHOD) | |
30 | +@EventListener(ContextRefreshedEvent.class) | |
31 | +@Order | |
32 | +public @interface AfterContextReady { | |
33 | + @AliasFor(annotation = Order.class, attribute = "value") | |
34 | + int order() default Integer.MAX_VALUE; | |
35 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.queue.util; | |
17 | + | |
18 | +import org.springframework.boot.context.event.ApplicationReadyEvent; | |
19 | +import org.springframework.context.event.EventListener; | |
20 | +import org.springframework.core.annotation.AliasFor; | |
21 | +import org.springframework.core.annotation.Order; | |
22 | + | |
23 | +import java.lang.annotation.ElementType; | |
24 | +import java.lang.annotation.Retention; | |
25 | +import java.lang.annotation.RetentionPolicy; | |
26 | +import java.lang.annotation.Target; | |
27 | + | |
28 | +@Retention(RetentionPolicy.RUNTIME) | |
29 | +@Target(ElementType.METHOD) | |
30 | +@EventListener(ApplicationReadyEvent.class) | |
31 | +@Order | |
32 | +public @interface AfterStartUp { | |
33 | + @AliasFor(annotation = Order.class, attribute = "value") | |
34 | + int order() default Integer.MAX_VALUE; | |
35 | +} | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 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.queue.util; | |
17 | + | |
18 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
19 | + | |
20 | +import java.lang.annotation.ElementType; | |
21 | +import java.lang.annotation.Retention; | |
22 | +import java.lang.annotation.RetentionPolicy; | |
23 | +import java.lang.annotation.Target; | |
24 | + | |
25 | +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')") | |
26 | +@Retention(RetentionPolicy.RUNTIME) | |
27 | +@Target({ElementType.TYPE, ElementType.METHOD}) | |
28 | +public @interface TbSnmpTransportComponent { | |
29 | +} | ... | ... |
... | ... | @@ -34,6 +34,7 @@ message ServiceInfo { |
34 | 34 | int64 tenantIdMSB = 3; |
35 | 35 | int64 tenantIdLSB = 4; |
36 | 36 | repeated QueueInfo ruleEngineQueues = 5; |
37 | + repeated string transports = 6; | |
37 | 38 | } |
38 | 39 | |
39 | 40 | /** |
... | ... | @@ -246,6 +247,36 @@ message GetEntityProfileResponseMsg { |
246 | 247 | bytes apiState = 3; |
247 | 248 | } |
248 | 249 | |
250 | +message GetDeviceRequestMsg { | |
251 | + int64 deviceIdMSB = 1; | |
252 | + int64 deviceIdLSB = 2; | |
253 | +} | |
254 | + | |
255 | +message GetDeviceResponseMsg { | |
256 | + int64 deviceProfileIdMSB = 1; | |
257 | + int64 deviceProfileIdLSB = 2; | |
258 | + bytes deviceTransportConfiguration = 3; | |
259 | +} | |
260 | + | |
261 | +message GetDeviceCredentialsRequestMsg { | |
262 | + int64 deviceIdMSB = 1; | |
263 | + int64 deviceIdLSB = 2; | |
264 | +} | |
265 | + | |
266 | +message GetDeviceCredentialsResponseMsg { | |
267 | + bytes deviceCredentialsData = 1; | |
268 | +} | |
269 | + | |
270 | +message GetSnmpDevicesRequestMsg { | |
271 | + int32 page = 1; | |
272 | + int32 pageSize = 2; | |
273 | +} | |
274 | + | |
275 | +message GetSnmpDevicesResponseMsg { | |
276 | + repeated string ids = 1; | |
277 | + bool hasNextPage = 2; | |
278 | +} | |
279 | + | |
249 | 280 | message EntityUpdateMsg { |
250 | 281 | string entityType = 1; |
251 | 282 | bytes data = 2; |
... | ... | @@ -590,6 +621,9 @@ message TransportApiRequestMsg { |
590 | 621 | ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; |
591 | 622 | GetResourceRequestMsg resourceRequestMsg = 9; |
592 | 623 | GetFirmwareRequestMsg firmwareRequestMsg = 10; |
624 | + GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11; | |
625 | + GetDeviceRequestMsg deviceRequestMsg = 12; | |
626 | + GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13; | |
593 | 627 | } |
594 | 628 | |
595 | 629 | /* Response from ThingsBoard Core Service to Transport Service */ |
... | ... | @@ -598,9 +632,12 @@ message TransportApiResponseMsg { |
598 | 632 | GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; |
599 | 633 | GetEntityProfileResponseMsg entityProfileResponseMsg = 3; |
600 | 634 | ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; |
635 | + GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; | |
601 | 636 | LwM2MResponseMsg lwM2MResponseMsg = 6; |
602 | 637 | GetResourceResponseMsg resourceResponseMsg = 7; |
603 | 638 | GetFirmwareResponseMsg firmwareResponseMsg = 8; |
639 | + GetDeviceResponseMsg deviceResponseMsg = 9; | |
640 | + GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10; | |
604 | 641 | } |
605 | 642 | |
606 | 643 | /* Messages that are handled by ThingsBoard Core Service */ | ... | ... |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
... | ... | @@ -28,12 +28,12 @@ import org.eclipse.californium.core.observe.ObserveRelation; |
28 | 28 | import org.eclipse.californium.core.server.resources.CoapExchange; |
29 | 29 | import org.eclipse.californium.core.server.resources.Resource; |
30 | 30 | import org.eclipse.californium.core.server.resources.ResourceObserver; |
31 | -import org.springframework.util.StringUtils; | |
32 | 31 | import org.thingsboard.server.coapserver.CoapServerService; |
33 | 32 | import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; |
34 | 33 | import org.thingsboard.server.common.data.DataConstants; |
35 | 34 | import org.thingsboard.server.common.data.DeviceProfile; |
36 | 35 | import org.thingsboard.server.common.data.DeviceTransportType; |
36 | +import org.thingsboard.server.common.data.StringUtils; | |
37 | 37 | import org.thingsboard.server.common.data.TransportPayloadType; |
38 | 38 | import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; |
39 | 39 | import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; |
... | ... | @@ -120,6 +120,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
120 | 120 | processExchangeGetRequest(exchange, featureType.get()); |
121 | 121 | } else if (featureType.get() == FeatureType.ATTRIBUTES) { |
122 | 122 | processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); |
123 | + } else if (featureType.get() == FeatureType.FIRMWARE) { | |
124 | + processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); | |
123 | 125 | } else { |
124 | 126 | log.trace("Invalid feature type parameter"); |
125 | 127 | exchange.respond(CoAP.ResponseCode.BAD_REQUEST); |
... | ... | @@ -201,7 +203,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
201 | 203 | Request request = advanced.getRequest(); |
202 | 204 | |
203 | 205 | String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY); |
204 | - if (!StringUtils.isEmpty(dtlsSessionIdStr)) { | |
206 | + if (StringUtils.isNotEmpty(dtlsSessionIdStr)) { | |
205 | 207 | if (dtlsSessionIdMap != null) { |
206 | 208 | TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap |
207 | 209 | .computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> { |
... | ... | @@ -323,6 +325,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
323 | 325 | coapTransportAdaptor.convertToGetAttributes(sessionId, request), |
324 | 326 | new CoapNoOpCallback(exchange)); |
325 | 327 | break; |
328 | + case GET_FIRMWARE_REQUEST: | |
329 | + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() | |
330 | + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
331 | + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
332 | + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
333 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); | |
334 | + transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); | |
335 | + break; | |
326 | 336 | } |
327 | 337 | } catch (AdaptorException e) { |
328 | 338 | log.trace("[{}] Failed to decode message: ", sessionId, e); |
... | ... | @@ -424,6 +434,40 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
424 | 434 | } |
425 | 435 | } |
426 | 436 | |
437 | + private class FirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> { | |
438 | + private final CoapExchange exchange; | |
439 | + | |
440 | + FirmwareCallback(CoapExchange exchange) { | |
441 | + this.exchange = exchange; | |
442 | + } | |
443 | + | |
444 | + @Override | |
445 | + public void onSuccess(TransportProtos.GetFirmwareResponseMsg msg) { | |
446 | + String title = exchange.getQueryParameter("title"); | |
447 | + String version = exchange.getQueryParameter("version"); | |
448 | + if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { | |
449 | + if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { | |
450 | + String firmwareId = new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()).toString(); | |
451 | + String strChunkSize = exchange.getQueryParameter("size"); | |
452 | + String strChunk = exchange.getQueryParameter("chunk"); | |
453 | + int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); | |
454 | + int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); | |
455 | + exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getFirmwareDataCache().get(firmwareId, chunkSize, chunk)); | |
456 | + } else { | |
457 | + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); | |
458 | + } | |
459 | + } else { | |
460 | + exchange.respond(CoAP.ResponseCode.NOT_FOUND); | |
461 | + } | |
462 | + } | |
463 | + | |
464 | + @Override | |
465 | + public void onError(Throwable e) { | |
466 | + log.warn("Failed to process request", e); | |
467 | + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); | |
468 | + } | |
469 | + } | |
470 | + | |
427 | 471 | private static class CoapSessionListener implements SessionMsgListener { |
428 | 472 | |
429 | 473 | private final CoapExchange exchange; | ... | ... |
... | ... | @@ -18,11 +18,12 @@ package org.thingsboard.server.transport.coap; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.eclipse.californium.core.CoapResource; |
20 | 20 | import org.eclipse.californium.core.CoapServer; |
21 | - | |
22 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
23 | 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
24 | 23 | import org.springframework.stereotype.Service; |
24 | +import org.thingsboard.server.common.data.TbTransportService; | |
25 | 25 | import org.thingsboard.server.coapserver.CoapServerService; |
26 | +import org.thingsboard.server.coapserver.TbCoapServerComponent; | |
26 | 27 | import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; |
27 | 28 | |
28 | 29 | import javax.annotation.PostConstruct; |
... | ... | @@ -30,9 +31,9 @@ import javax.annotation.PreDestroy; |
30 | 31 | import java.net.UnknownHostException; |
31 | 32 | |
32 | 33 | @Service("CoapTransportService") |
33 | -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") | |
34 | +@TbCoapServerComponent | |
34 | 35 | @Slf4j |
35 | -public class CoapTransportService { | |
36 | +public class CoapTransportService implements TbTransportService { | |
36 | 37 | |
37 | 38 | private static final String V1 = "v1"; |
38 | 39 | private static final String API = "api"; |
... | ... | @@ -66,4 +67,9 @@ public class CoapTransportService { |
66 | 67 | public void shutdown() { |
67 | 68 | log.info("CoAP transport stopped!"); |
68 | 69 | } |
70 | + | |
71 | + @Override | |
72 | + public String getName() { | |
73 | + return "COAP"; | |
74 | + } | |
69 | 75 | } | ... | ... |