Commit afcadea9c2f6045cd4e8b5e21f0d7f4c32cf36b0
Merge branch 'master' into js-executors-kafka-batches
# Conflicts: # msa/js-executor/queue/kafkaTemplate.js
Showing
28 changed files
with
3336 additions
and
505 deletions
Too many changes to show.
To preserve performance only 28 of 333 files are displayed.
1 | 1 | { |
2 | 2 | "title": "Firmware", |
3 | + "image": null, | |
3 | 4 | "configuration": { |
4 | 5 | "description": "", |
5 | 6 | "widgets": { |
... | ... | @@ -222,6 +223,27 @@ |
222 | 223 | "funcBody": null, |
223 | 224 | "usePostProcessing": null, |
224 | 225 | "postFuncBody": null |
226 | + }, | |
227 | + { | |
228 | + "name": "fw_url", | |
229 | + "type": "attribute", | |
230 | + "label": "fw_url", | |
231 | + "color": "#e91e63", | |
232 | + "settings": { | |
233 | + "columnWidth": "0px", | |
234 | + "useCellStyleFunction": false, | |
235 | + "cellStyleFunction": "", | |
236 | + "useCellContentFunction": false, | |
237 | + "cellContentFunction": "", | |
238 | + "defaultColumnVisibility": "hidden", | |
239 | + "columnSelectionToDisplay": "disabled" | |
240 | + }, | |
241 | + "_hash": 0.4204673738685043, | |
242 | + "units": null, | |
243 | + "decimals": null, | |
244 | + "funcBody": null, | |
245 | + "usePostProcessing": null, | |
246 | + "postFuncBody": null | |
225 | 247 | } |
226 | 248 | ] |
227 | 249 | } |
... | ... | @@ -247,24 +269,24 @@ |
247 | 269 | "name": "Edit firmware", |
248 | 270 | "icon": "edit", |
249 | 271 | "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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\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": "", | |
272 | + "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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-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>", | |
273 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
252 | 274 | "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 | 275 | "customResources": [], |
254 | 276 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
255 | 277 | }, |
256 | 278 | { |
257 | - "name": "Download firware", | |
279 | + "name": "Download firmware", | |
258 | 280 | "icon": "file_download", |
259 | 281 | "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}", | |
282 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
261 | 283 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
262 | 284 | }, |
263 | 285 | { |
264 | - "name": "Copy checksum", | |
286 | + "name": "Copy checksum/URL", | |
265 | 287 | "icon": "content_copy", |
266 | 288 | "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}", | |
289 | + "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 data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
268 | 290 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
269 | 291 | } |
270 | 292 | ] |
... | ... | @@ -996,6 +1018,27 @@ |
996 | 1018 | "funcBody": null, |
997 | 1019 | "usePostProcessing": null, |
998 | 1020 | "postFuncBody": null |
1021 | + }, | |
1022 | + { | |
1023 | + "name": "fw_url", | |
1024 | + "type": "attribute", | |
1025 | + "label": "fw_url", | |
1026 | + "color": "#e91e63", | |
1027 | + "settings": { | |
1028 | + "columnWidth": "0px", | |
1029 | + "useCellStyleFunction": false, | |
1030 | + "cellStyleFunction": "", | |
1031 | + "useCellContentFunction": false, | |
1032 | + "cellContentFunction": "", | |
1033 | + "defaultColumnVisibility": "hidden", | |
1034 | + "columnSelectionToDisplay": "disabled" | |
1035 | + }, | |
1036 | + "_hash": 0.4204673738685043, | |
1037 | + "units": null, | |
1038 | + "decimals": null, | |
1039 | + "funcBody": null, | |
1040 | + "usePostProcessing": null, | |
1041 | + "postFuncBody": null | |
999 | 1042 | } |
1000 | 1043 | ] |
1001 | 1044 | } |
... | ... | @@ -1021,24 +1064,24 @@ |
1021 | 1064 | "name": "Edit firmware", |
1022 | 1065 | "icon": "edit", |
1023 | 1066 | "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": "", | |
1067 | + "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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-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>", | |
1068 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1026 | 1069 | "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 | 1070 | "customResources": [], |
1028 | 1071 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1029 | 1072 | }, |
1030 | 1073 | { |
1031 | - "name": "Download firware", | |
1074 | + "name": "Download firmware", | |
1032 | 1075 | "icon": "file_download", |
1033 | 1076 | "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}", | |
1077 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1035 | 1078 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1036 | 1079 | }, |
1037 | 1080 | { |
1038 | - "name": "Copy checksum", | |
1081 | + "name": "Copy checksum/URL", | |
1039 | 1082 | "icon": "content_copy", |
1040 | 1083 | "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}", | |
1084 | + "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 data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1042 | 1085 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1043 | 1086 | } |
1044 | 1087 | ] |
... | ... | @@ -1272,6 +1315,27 @@ |
1272 | 1315 | "funcBody": null, |
1273 | 1316 | "usePostProcessing": null, |
1274 | 1317 | "postFuncBody": null |
1318 | + }, | |
1319 | + { | |
1320 | + "name": "fw_url", | |
1321 | + "type": "attribute", | |
1322 | + "label": "fw_url", | |
1323 | + "color": "#e91e63", | |
1324 | + "settings": { | |
1325 | + "columnWidth": "0px", | |
1326 | + "useCellStyleFunction": false, | |
1327 | + "cellStyleFunction": "", | |
1328 | + "useCellContentFunction": false, | |
1329 | + "cellContentFunction": "", | |
1330 | + "defaultColumnVisibility": "hidden", | |
1331 | + "columnSelectionToDisplay": "disabled" | |
1332 | + }, | |
1333 | + "_hash": 0.4204673738685043, | |
1334 | + "units": null, | |
1335 | + "decimals": null, | |
1336 | + "funcBody": null, | |
1337 | + "usePostProcessing": null, | |
1338 | + "postFuncBody": null | |
1275 | 1339 | } |
1276 | 1340 | ] |
1277 | 1341 | } |
... | ... | @@ -1297,24 +1361,24 @@ |
1297 | 1361 | "name": "Edit firmware", |
1298 | 1362 | "icon": "edit", |
1299 | 1363 | "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": "", | |
1364 | + "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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-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>", | |
1365 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1302 | 1366 | "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 | 1367 | "customResources": [], |
1304 | 1368 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1305 | 1369 | }, |
1306 | 1370 | { |
1307 | - "name": "Download firware", | |
1371 | + "name": "Download firmware", | |
1308 | 1372 | "icon": "file_download", |
1309 | 1373 | "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}", | |
1374 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1311 | 1375 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1312 | 1376 | }, |
1313 | 1377 | { |
1314 | - "name": "Copy checksum", | |
1378 | + "name": "Copy checksum/URL", | |
1315 | 1379 | "icon": "content_copy", |
1316 | 1380 | "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}", | |
1381 | + "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 data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1318 | 1382 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1319 | 1383 | } |
1320 | 1384 | ] |
... | ... | @@ -1548,6 +1612,27 @@ |
1548 | 1612 | "funcBody": null, |
1549 | 1613 | "usePostProcessing": null, |
1550 | 1614 | "postFuncBody": null |
1615 | + }, | |
1616 | + { | |
1617 | + "name": "fw_url", | |
1618 | + "type": "attribute", | |
1619 | + "label": "fw_url", | |
1620 | + "color": "#e91e63", | |
1621 | + "settings": { | |
1622 | + "columnWidth": "0px", | |
1623 | + "useCellStyleFunction": false, | |
1624 | + "cellStyleFunction": "", | |
1625 | + "useCellContentFunction": false, | |
1626 | + "cellContentFunction": "", | |
1627 | + "defaultColumnVisibility": "hidden", | |
1628 | + "columnSelectionToDisplay": "disabled" | |
1629 | + }, | |
1630 | + "_hash": 0.4204673738685043, | |
1631 | + "units": null, | |
1632 | + "decimals": null, | |
1633 | + "funcBody": null, | |
1634 | + "usePostProcessing": null, | |
1635 | + "postFuncBody": null | |
1551 | 1636 | } |
1552 | 1637 | ] |
1553 | 1638 | } |
... | ... | @@ -1573,24 +1658,24 @@ |
1573 | 1658 | "name": "Edit firmware", |
1574 | 1659 | "icon": "edit", |
1575 | 1660 | "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": "", | |
1661 | + "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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-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>", | |
1662 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1578 | 1663 | "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 | 1664 | "customResources": [], |
1580 | 1665 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1581 | 1666 | }, |
1582 | 1667 | { |
1583 | - "name": "Download firware", | |
1668 | + "name": "Download firmware", | |
1584 | 1669 | "icon": "file_download", |
1585 | 1670 | "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}", | |
1671 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1587 | 1672 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1588 | 1673 | }, |
1589 | 1674 | { |
1590 | - "name": "Copy checksum", | |
1675 | + "name": "Copy checksum/URL", | |
1591 | 1676 | "icon": "content_copy", |
1592 | 1677 | "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}", | |
1678 | + "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 data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1594 | 1679 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1595 | 1680 | } |
1596 | 1681 | ] |
... | ... | @@ -1824,6 +1909,27 @@ |
1824 | 1909 | "funcBody": null, |
1825 | 1910 | "usePostProcessing": null, |
1826 | 1911 | "postFuncBody": null |
1912 | + }, | |
1913 | + { | |
1914 | + "name": "fw_url", | |
1915 | + "type": "attribute", | |
1916 | + "label": "fw_url", | |
1917 | + "color": "#e91e63", | |
1918 | + "settings": { | |
1919 | + "columnWidth": "0px", | |
1920 | + "useCellStyleFunction": false, | |
1921 | + "cellStyleFunction": "", | |
1922 | + "useCellContentFunction": false, | |
1923 | + "cellContentFunction": "", | |
1924 | + "defaultColumnVisibility": "hidden", | |
1925 | + "columnSelectionToDisplay": "disabled" | |
1926 | + }, | |
1927 | + "_hash": 0.4204673738685043, | |
1928 | + "units": null, | |
1929 | + "decimals": null, | |
1930 | + "funcBody": null, | |
1931 | + "usePostProcessing": null, | |
1932 | + "postFuncBody": null | |
1827 | 1933 | } |
1828 | 1934 | ] |
1829 | 1935 | } |
... | ... | @@ -1849,24 +1955,24 @@ |
1849 | 1955 | "name": "Edit firmware", |
1850 | 1956 | "icon": "edit", |
1851 | 1957 | "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": "", | |
1958 | + "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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-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>", | |
1959 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1854 | 1960 | "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 | 1961 | "customResources": [], |
1856 | 1962 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1857 | 1963 | }, |
1858 | 1964 | { |
1859 | - "name": "Download firware", | |
1965 | + "name": "Download firmware", | |
1860 | 1966 | "icon": "file_download", |
1861 | 1967 | "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}", | |
1968 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1863 | 1969 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1864 | 1970 | }, |
1865 | 1971 | { |
1866 | - "name": "Copy checksum", | |
1972 | + "name": "Copy checksum/URL", | |
1867 | 1973 | "icon": "content_copy", |
1868 | 1974 | "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}", | |
1975 | + "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 data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1870 | 1976 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1871 | 1977 | } |
1872 | 1978 | ] |
... | ... | @@ -1935,7 +2041,7 @@ |
1935 | 2041 | } |
1936 | 2042 | }, |
1937 | 2043 | "device_firmware_history": { |
1938 | - "name": "Device Firmware history", | |
2044 | + "name": "Firmware history: ${entityName}", | |
1939 | 2045 | "root": false, |
1940 | 2046 | "layouts": { |
1941 | 2047 | "main": { |
... | ... | @@ -2378,7 +2484,8 @@ |
2378 | 2484 | "titleColor": "rgba(0,0,0,0.870588)", |
2379 | 2485 | "showFilters": true, |
2380 | 2486 | "showDashboardLogo": false, |
2381 | - "dashboardLogoUrl": null | |
2487 | + "dashboardLogoUrl": null, | |
2488 | + "showUpdateDashboardImage": false | |
2382 | 2489 | } |
2383 | 2490 | }, |
2384 | 2491 | "name": "Firmware" | ... | ... |
1 | +{ | |
2 | + "title": "Software", | |
3 | + "image": null, | |
4 | + "configuration": { | |
5 | + "description": "", | |
6 | + "widgets": { | |
7 | + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { | |
8 | + "isSystemType": true, | |
9 | + "bundleAlias": "cards", | |
10 | + "typeAlias": "entities_table", | |
11 | + "type": "latest", | |
12 | + "title": "New widget", | |
13 | + "image": null, | |
14 | + "description": null, | |
15 | + "sizeX": 7.5, | |
16 | + "sizeY": 6.5, | |
17 | + "config": { | |
18 | + "timewindow": { | |
19 | + "realtime": { | |
20 | + "interval": 1000, | |
21 | + "timewindowMs": 86400000 | |
22 | + }, | |
23 | + "aggregation": { | |
24 | + "type": "NONE", | |
25 | + "limit": 200 | |
26 | + } | |
27 | + }, | |
28 | + "showTitle": true, | |
29 | + "backgroundColor": "rgb(255, 255, 255)", | |
30 | + "color": "rgba(0, 0, 0, 0.87)", | |
31 | + "padding": "4px", | |
32 | + "settings": { | |
33 | + "enableSearch": true, | |
34 | + "displayPagination": true, | |
35 | + "defaultPageSize": 10, | |
36 | + "defaultSortOrder": "entityLabel", | |
37 | + "displayEntityName": false, | |
38 | + "displayEntityType": false, | |
39 | + "enableSelectColumnDisplay": false, | |
40 | + "enableStickyHeader": true, | |
41 | + "enableStickyAction": false, | |
42 | + "entitiesTitle": "Devices", | |
43 | + "displayEntityLabel": true, | |
44 | + "entityLabelColumnTitle": "Device" | |
45 | + }, | |
46 | + "title": "New Entities table", | |
47 | + "dropShadow": true, | |
48 | + "enableFullscreen": true, | |
49 | + "titleStyle": { | |
50 | + "fontSize": "16px", | |
51 | + "fontWeight": 400, | |
52 | + "padding": "5px 10px 5px 10px" | |
53 | + }, | |
54 | + "useDashboardTimewindow": false, | |
55 | + "showLegend": false, | |
56 | + "datasources": [ | |
57 | + { | |
58 | + "type": "entity", | |
59 | + "name": null, | |
60 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
61 | + "filterId": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", | |
62 | + "dataKeys": [ | |
63 | + { | |
64 | + "name": "current_sw_title", | |
65 | + "type": "timeseries", | |
66 | + "label": "Current SW title", | |
67 | + "color": "#2196f3", | |
68 | + "settings": { | |
69 | + "columnWidth": "0px", | |
70 | + "useCellStyleFunction": false, | |
71 | + "cellStyleFunction": "", | |
72 | + "useCellContentFunction": false, | |
73 | + "defaultColumnVisibility": "visible", | |
74 | + "columnSelectionToDisplay": "enabled" | |
75 | + }, | |
76 | + "_hash": 0.09545533885166413, | |
77 | + "units": null, | |
78 | + "decimals": null, | |
79 | + "funcBody": null, | |
80 | + "usePostProcessing": null, | |
81 | + "postFuncBody": null | |
82 | + }, | |
83 | + { | |
84 | + "name": "current_sw_version", | |
85 | + "type": "timeseries", | |
86 | + "label": "Current SW version", | |
87 | + "color": "#4caf50", | |
88 | + "settings": { | |
89 | + "columnWidth": "0px", | |
90 | + "useCellStyleFunction": false, | |
91 | + "cellStyleFunction": "", | |
92 | + "useCellContentFunction": false, | |
93 | + "defaultColumnVisibility": "visible", | |
94 | + "columnSelectionToDisplay": "enabled" | |
95 | + }, | |
96 | + "_hash": 0.7206056602328659, | |
97 | + "units": null, | |
98 | + "decimals": null, | |
99 | + "funcBody": null, | |
100 | + "usePostProcessing": null, | |
101 | + "postFuncBody": null | |
102 | + }, | |
103 | + { | |
104 | + "name": "target_sw_title", | |
105 | + "type": "timeseries", | |
106 | + "label": "Target SW title", | |
107 | + "color": "#ffc107", | |
108 | + "settings": { | |
109 | + "columnWidth": "0px", | |
110 | + "useCellStyleFunction": false, | |
111 | + "cellStyleFunction": "", | |
112 | + "useCellContentFunction": false, | |
113 | + "defaultColumnVisibility": "visible", | |
114 | + "columnSelectionToDisplay": "enabled" | |
115 | + }, | |
116 | + "_hash": 0.9934225682766313, | |
117 | + "units": null, | |
118 | + "decimals": null, | |
119 | + "funcBody": null, | |
120 | + "usePostProcessing": null, | |
121 | + "postFuncBody": null | |
122 | + }, | |
123 | + { | |
124 | + "name": "target_sw_version", | |
125 | + "type": "timeseries", | |
126 | + "label": "Target SW version", | |
127 | + "color": "#607d8b", | |
128 | + "settings": { | |
129 | + "columnWidth": "0px", | |
130 | + "useCellStyleFunction": false, | |
131 | + "cellStyleFunction": "", | |
132 | + "useCellContentFunction": false, | |
133 | + "cellContentFunction": "", | |
134 | + "defaultColumnVisibility": "visible", | |
135 | + "columnSelectionToDisplay": "enabled" | |
136 | + }, | |
137 | + "_hash": 0.5251724416842531, | |
138 | + "units": null, | |
139 | + "decimals": null, | |
140 | + "funcBody": null, | |
141 | + "usePostProcessing": null, | |
142 | + "postFuncBody": null | |
143 | + }, | |
144 | + { | |
145 | + "name": "target_sw_ts", | |
146 | + "type": "timeseries", | |
147 | + "label": "Target SW set time", | |
148 | + "color": "#e91e63", | |
149 | + "settings": { | |
150 | + "columnWidth": "0px", | |
151 | + "useCellStyleFunction": false, | |
152 | + "cellStyleFunction": "", | |
153 | + "useCellContentFunction": true, | |
154 | + "defaultColumnVisibility": "visible", | |
155 | + "columnSelectionToDisplay": "enabled", | |
156 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
157 | + }, | |
158 | + "_hash": 0.31823244858578237, | |
159 | + "units": null, | |
160 | + "decimals": null, | |
161 | + "funcBody": null, | |
162 | + "usePostProcessing": null, | |
163 | + "postFuncBody": null | |
164 | + }, | |
165 | + { | |
166 | + "name": "sw_state", | |
167 | + "type": "timeseries", | |
168 | + "label": "Progress", | |
169 | + "color": "#9c27b0", | |
170 | + "settings": { | |
171 | + "columnWidth": "30%", | |
172 | + "useCellStyleFunction": true, | |
173 | + "useCellContentFunction": true, | |
174 | + "defaultColumnVisibility": "visible", | |
175 | + "columnSelectionToDisplay": "enabled", | |
176 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
177 | + "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}" | |
178 | + }, | |
179 | + "_hash": 0.8174211757846257, | |
180 | + "units": null, | |
181 | + "decimals": null, | |
182 | + "funcBody": null, | |
183 | + "usePostProcessing": null, | |
184 | + "postFuncBody": null | |
185 | + }, | |
186 | + { | |
187 | + "name": "sw_state", | |
188 | + "type": "timeseries", | |
189 | + "label": "Status", | |
190 | + "color": "#f44336", | |
191 | + "settings": { | |
192 | + "columnWidth": "130px", | |
193 | + "useCellStyleFunction": true, | |
194 | + "useCellContentFunction": true, | |
195 | + "defaultColumnVisibility": "visible", | |
196 | + "columnSelectionToDisplay": "enabled", | |
197 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
198 | + "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>';" | |
199 | + }, | |
200 | + "_hash": 0.7764426948615217, | |
201 | + "units": null, | |
202 | + "decimals": null, | |
203 | + "funcBody": null, | |
204 | + "usePostProcessing": null, | |
205 | + "postFuncBody": null | |
206 | + }, | |
207 | + { | |
208 | + "name": "sw_checksum", | |
209 | + "type": "attribute", | |
210 | + "label": "sw_checksum", | |
211 | + "color": "#3f51b5", | |
212 | + "settings": { | |
213 | + "columnWidth": "0px", | |
214 | + "useCellStyleFunction": false, | |
215 | + "cellStyleFunction": "", | |
216 | + "useCellContentFunction": false, | |
217 | + "defaultColumnVisibility": "hidden", | |
218 | + "columnSelectionToDisplay": "disabled" | |
219 | + }, | |
220 | + "_hash": 0.5594087842471693, | |
221 | + "units": null, | |
222 | + "decimals": null, | |
223 | + "funcBody": null, | |
224 | + "usePostProcessing": null, | |
225 | + "postFuncBody": null | |
226 | + }, | |
227 | + { | |
228 | + "name": "sw_url", | |
229 | + "type": "attribute", | |
230 | + "label": "sw_url", | |
231 | + "color": "#e91e63", | |
232 | + "settings": { | |
233 | + "columnWidth": "0px", | |
234 | + "useCellStyleFunction": false, | |
235 | + "cellStyleFunction": "", | |
236 | + "useCellContentFunction": false, | |
237 | + "cellContentFunction": "", | |
238 | + "defaultColumnVisibility": "hidden", | |
239 | + "columnSelectionToDisplay": "disabled" | |
240 | + }, | |
241 | + "_hash": 0.3355829384124256, | |
242 | + "units": null, | |
243 | + "decimals": null, | |
244 | + "funcBody": null, | |
245 | + "usePostProcessing": null, | |
246 | + "postFuncBody": null | |
247 | + } | |
248 | + ] | |
249 | + } | |
250 | + ], | |
251 | + "actions": { | |
252 | + "actionCellButton": [ | |
253 | + { | |
254 | + "name": "History software update", | |
255 | + "icon": "history", | |
256 | + "type": "openDashboardState", | |
257 | + "targetDashboardStateId": "device_software_history", | |
258 | + "setEntityId": true, | |
259 | + "stateEntityParamName": null, | |
260 | + "openInSeparateDialog": false, | |
261 | + "dialogTitle": "", | |
262 | + "dialogHideDashboardToolbar": true, | |
263 | + "dialogWidth": null, | |
264 | + "dialogHeight": null, | |
265 | + "openRightLayout": false, | |
266 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
267 | + }, | |
268 | + { | |
269 | + "name": "Edit software", | |
270 | + "icon": "edit", | |
271 | + "type": "customPretty", | |
272 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-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>", | |
273 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
274 | + "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 softwareId: [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 softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
275 | + "customResources": [], | |
276 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
277 | + }, | |
278 | + { | |
279 | + "name": "Download software", | |
280 | + "icon": "file_download", | |
281 | + "type": "custom", | |
282 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
283 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
284 | + }, | |
285 | + { | |
286 | + "name": "Copy checksum/URL", | |
287 | + "icon": "content_copy", | |
288 | + "type": "custom", | |
289 | + "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 === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
290 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
291 | + } | |
292 | + ] | |
293 | + }, | |
294 | + "showTitleIcon": false, | |
295 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
296 | + "iconSize": "24px", | |
297 | + "titleTooltip": "", | |
298 | + "widgetStyle": {} | |
299 | + }, | |
300 | + "row": 0, | |
301 | + "col": 0, | |
302 | + "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016" | |
303 | + }, | |
304 | + "100b756c-0082-6505-3ae1-3603e6deea48": { | |
305 | + "isSystemType": true, | |
306 | + "bundleAlias": "cards", | |
307 | + "typeAlias": "timeseries_table", | |
308 | + "type": "timeseries", | |
309 | + "title": "New widget", | |
310 | + "image": null, | |
311 | + "description": null, | |
312 | + "sizeX": 8, | |
313 | + "sizeY": 6.5, | |
314 | + "config": { | |
315 | + "datasources": [ | |
316 | + { | |
317 | + "type": "entity", | |
318 | + "name": null, | |
319 | + "entityAliasId": "19f41c21-d9af-e666-8f50-e1748778f955", | |
320 | + "filterId": null, | |
321 | + "dataKeys": [ | |
322 | + { | |
323 | + "name": "current_sw_title", | |
324 | + "type": "timeseries", | |
325 | + "label": "Current software title", | |
326 | + "color": "#2196f3", | |
327 | + "settings": { | |
328 | + "useCellStyleFunction": false, | |
329 | + "cellStyleFunction": "", | |
330 | + "useCellContentFunction": false, | |
331 | + "cellContentFunction": "" | |
332 | + }, | |
333 | + "_hash": 0.5978079905579401, | |
334 | + "units": null, | |
335 | + "decimals": null, | |
336 | + "funcBody": null, | |
337 | + "usePostProcessing": null, | |
338 | + "postFuncBody": null | |
339 | + }, | |
340 | + { | |
341 | + "name": "current_sw_version", | |
342 | + "type": "timeseries", | |
343 | + "label": "Current software version", | |
344 | + "color": "#4caf50", | |
345 | + "settings": { | |
346 | + "useCellStyleFunction": false, | |
347 | + "cellStyleFunction": "", | |
348 | + "useCellContentFunction": false, | |
349 | + "cellContentFunction": "" | |
350 | + }, | |
351 | + "_hash": 0.027392025058568192, | |
352 | + "units": null, | |
353 | + "decimals": null, | |
354 | + "funcBody": null, | |
355 | + "usePostProcessing": null, | |
356 | + "postFuncBody": null | |
357 | + }, | |
358 | + { | |
359 | + "name": "target_sw_title", | |
360 | + "type": "timeseries", | |
361 | + "label": "Target software title", | |
362 | + "color": "#f44336", | |
363 | + "settings": { | |
364 | + "useCellStyleFunction": false, | |
365 | + "cellStyleFunction": "", | |
366 | + "useCellContentFunction": false, | |
367 | + "cellContentFunction": "" | |
368 | + }, | |
369 | + "_hash": 0.9496350796287059, | |
370 | + "units": null, | |
371 | + "decimals": null, | |
372 | + "funcBody": null, | |
373 | + "usePostProcessing": null, | |
374 | + "postFuncBody": null | |
375 | + }, | |
376 | + { | |
377 | + "name": "target_sw_version", | |
378 | + "type": "timeseries", | |
379 | + "label": "Target software version", | |
380 | + "color": "#ffc107", | |
381 | + "settings": { | |
382 | + "useCellStyleFunction": false, | |
383 | + "cellStyleFunction": "", | |
384 | + "useCellContentFunction": false, | |
385 | + "cellContentFunction": "" | |
386 | + }, | |
387 | + "_hash": 0.6734152252264187, | |
388 | + "units": null, | |
389 | + "decimals": null, | |
390 | + "funcBody": null, | |
391 | + "usePostProcessing": null, | |
392 | + "postFuncBody": null | |
393 | + }, | |
394 | + { | |
395 | + "name": "sw_state", | |
396 | + "type": "timeseries", | |
397 | + "label": "Status", | |
398 | + "color": "#607d8b", | |
399 | + "settings": { | |
400 | + "useCellStyleFunction": false, | |
401 | + "cellStyleFunction": "", | |
402 | + "useCellContentFunction": false, | |
403 | + "cellContentFunction": "" | |
404 | + }, | |
405 | + "_hash": 0.2983399718643074, | |
406 | + "units": null, | |
407 | + "decimals": null, | |
408 | + "funcBody": null, | |
409 | + "usePostProcessing": true, | |
410 | + "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;" | |
411 | + } | |
412 | + ] | |
413 | + } | |
414 | + ], | |
415 | + "timewindow": { | |
416 | + "hideInterval": false, | |
417 | + "hideAggregation": false, | |
418 | + "hideAggInterval": false, | |
419 | + "hideTimezone": false, | |
420 | + "selectedTab": 0, | |
421 | + "realtime": { | |
422 | + "realtimeType": 0, | |
423 | + "timewindowMs": 2592000000, | |
424 | + "quickInterval": "CURRENT_DAY", | |
425 | + "interval": 1000 | |
426 | + }, | |
427 | + "aggregation": { | |
428 | + "type": "NONE", | |
429 | + "limit": 200 | |
430 | + } | |
431 | + }, | |
432 | + "showTitle": false, | |
433 | + "backgroundColor": "rgb(255, 255, 255)", | |
434 | + "color": "rgba(0, 0, 0, 0.87)", | |
435 | + "padding": "8px", | |
436 | + "settings": { | |
437 | + "showTimestamp": true, | |
438 | + "displayPagination": true, | |
439 | + "defaultPageSize": 10, | |
440 | + "enableSearch": true, | |
441 | + "enableStickyHeader": true, | |
442 | + "enableStickyAction": true | |
443 | + }, | |
444 | + "title": "Software history", | |
445 | + "dropShadow": false, | |
446 | + "enableFullscreen": false, | |
447 | + "titleStyle": { | |
448 | + "fontSize": "16px", | |
449 | + "fontWeight": 400, | |
450 | + "padding": "5px 10px 5px 10px" | |
451 | + }, | |
452 | + "useDashboardTimewindow": false, | |
453 | + "showLegend": false, | |
454 | + "widgetStyle": {}, | |
455 | + "actions": {}, | |
456 | + "showTitleIcon": false, | |
457 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
458 | + "iconSize": "24px", | |
459 | + "displayTimewindow": true, | |
460 | + "titleTooltip": "" | |
461 | + }, | |
462 | + "row": 0, | |
463 | + "col": 0, | |
464 | + "id": "100b756c-0082-6505-3ae1-3603e6deea48" | |
465 | + }, | |
466 | + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { | |
467 | + "isSystemType": true, | |
468 | + "bundleAlias": "cards", | |
469 | + "typeAlias": "html_value_card", | |
470 | + "type": "latest", | |
471 | + "title": "New widget", | |
472 | + "sizeX": 8, | |
473 | + "sizeY": 3, | |
474 | + "config": { | |
475 | + "datasources": [ | |
476 | + { | |
477 | + "type": "entityCount", | |
478 | + "name": "", | |
479 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
480 | + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
481 | + "dataKeys": [ | |
482 | + { | |
483 | + "name": "count", | |
484 | + "type": "count", | |
485 | + "label": "waitingDevicesNumber", | |
486 | + "color": "#4caf50", | |
487 | + "settings": {}, | |
488 | + "_hash": 0.7404827038869322, | |
489 | + "units": null, | |
490 | + "decimals": null, | |
491 | + "funcBody": null, | |
492 | + "usePostProcessing": null, | |
493 | + "postFuncBody": null | |
494 | + } | |
495 | + ] | |
496 | + } | |
497 | + ], | |
498 | + "timewindow": { | |
499 | + "realtime": { | |
500 | + "timewindowMs": 60000 | |
501 | + } | |
502 | + }, | |
503 | + "showTitle": false, | |
504 | + "backgroundColor": "#fff", | |
505 | + "color": "rgba(0, 0, 0, 0.87)", | |
506 | + "padding": "0px", | |
507 | + "settings": { | |
508 | + "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>", | |
509 | + "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}" | |
510 | + }, | |
511 | + "title": "New HTML Value Card", | |
512 | + "dropShadow": true, | |
513 | + "enableFullscreen": false, | |
514 | + "widgetStyle": {}, | |
515 | + "titleStyle": { | |
516 | + "fontSize": "16px", | |
517 | + "fontWeight": 400 | |
518 | + }, | |
519 | + "useDashboardTimewindow": true, | |
520 | + "showLegend": false, | |
521 | + "actions": { | |
522 | + "elementClick": [ | |
523 | + { | |
524 | + "name": "activeDevices", | |
525 | + "icon": "more_horiz", | |
526 | + "type": "openDashboardState", | |
527 | + "targetDashboardStateId": "device_waiting", | |
528 | + "setEntityId": false, | |
529 | + "stateEntityParamName": null, | |
530 | + "openInSeparateDialog": false, | |
531 | + "dialogTitle": "", | |
532 | + "dialogHideDashboardToolbar": true, | |
533 | + "dialogWidth": null, | |
534 | + "dialogHeight": null, | |
535 | + "openRightLayout": false, | |
536 | + "id": "4d9a77a2-f0a5-690c-a83b-b0e940be788c" | |
537 | + } | |
538 | + ] | |
539 | + }, | |
540 | + "showTitleIcon": false, | |
541 | + "titleIcon": null, | |
542 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
543 | + "iconSize": "24px", | |
544 | + "titleTooltip": "", | |
545 | + "enableDataExport": false, | |
546 | + "displayTimewindow": true | |
547 | + }, | |
548 | + "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6" | |
549 | + }, | |
550 | + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { | |
551 | + "isSystemType": true, | |
552 | + "bundleAlias": "cards", | |
553 | + "typeAlias": "html_value_card", | |
554 | + "type": "latest", | |
555 | + "title": "New widget", | |
556 | + "sizeX": 8, | |
557 | + "sizeY": 3, | |
558 | + "config": { | |
559 | + "datasources": [ | |
560 | + { | |
561 | + "type": "entityCount", | |
562 | + "name": "", | |
563 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
564 | + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
565 | + "dataKeys": [ | |
566 | + { | |
567 | + "name": "count", | |
568 | + "type": "count", | |
569 | + "label": "updatingDevicesNumber", | |
570 | + "color": "#4caf50", | |
571 | + "settings": {}, | |
572 | + "_hash": 0.7404827038869322, | |
573 | + "units": null, | |
574 | + "decimals": null, | |
575 | + "funcBody": null, | |
576 | + "usePostProcessing": null, | |
577 | + "postFuncBody": null | |
578 | + } | |
579 | + ] | |
580 | + } | |
581 | + ], | |
582 | + "timewindow": { | |
583 | + "realtime": { | |
584 | + "timewindowMs": 60000 | |
585 | + } | |
586 | + }, | |
587 | + "showTitle": false, | |
588 | + "backgroundColor": "#fff", | |
589 | + "color": "rgba(0, 0, 0, 0.87)", | |
590 | + "padding": "0px", | |
591 | + "settings": { | |
592 | + "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>", | |
593 | + "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}" | |
594 | + }, | |
595 | + "title": "New HTML Value Card", | |
596 | + "dropShadow": true, | |
597 | + "enableFullscreen": false, | |
598 | + "widgetStyle": {}, | |
599 | + "titleStyle": { | |
600 | + "fontSize": "16px", | |
601 | + "fontWeight": 400 | |
602 | + }, | |
603 | + "useDashboardTimewindow": true, | |
604 | + "showLegend": false, | |
605 | + "actions": { | |
606 | + "elementClick": [ | |
607 | + { | |
608 | + "name": "activeDevices", | |
609 | + "icon": "more_horiz", | |
610 | + "type": "openDashboardState", | |
611 | + "targetDashboardStateId": "device_updating", | |
612 | + "setEntityId": false, | |
613 | + "stateEntityParamName": null, | |
614 | + "openInSeparateDialog": false, | |
615 | + "dialogTitle": "", | |
616 | + "dialogHideDashboardToolbar": true, | |
617 | + "dialogWidth": null, | |
618 | + "dialogHeight": null, | |
619 | + "openRightLayout": false, | |
620 | + "id": "57d39904-2350-b29b-78ed-56b8268814cb" | |
621 | + } | |
622 | + ] | |
623 | + }, | |
624 | + "showTitleIcon": false, | |
625 | + "titleIcon": null, | |
626 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
627 | + "iconSize": "24px", | |
628 | + "titleTooltip": "", | |
629 | + "enableDataExport": false, | |
630 | + "displayTimewindow": true | |
631 | + }, | |
632 | + "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6" | |
633 | + }, | |
634 | + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { | |
635 | + "isSystemType": true, | |
636 | + "bundleAlias": "cards", | |
637 | + "typeAlias": "html_value_card", | |
638 | + "type": "latest", | |
639 | + "title": "New widget", | |
640 | + "sizeX": 8, | |
641 | + "sizeY": 3, | |
642 | + "config": { | |
643 | + "datasources": [ | |
644 | + { | |
645 | + "type": "entityCount", | |
646 | + "name": "", | |
647 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
648 | + "filterId": "6044e198-df64-cd76-f339-696f220c4943", | |
649 | + "dataKeys": [ | |
650 | + { | |
651 | + "name": "count", | |
652 | + "type": "count", | |
653 | + "label": "updatedDevicesNumber", | |
654 | + "color": "#4caf50", | |
655 | + "settings": {}, | |
656 | + "_hash": 0.7404827038869322, | |
657 | + "units": null, | |
658 | + "decimals": null, | |
659 | + "funcBody": null, | |
660 | + "usePostProcessing": null, | |
661 | + "postFuncBody": null | |
662 | + } | |
663 | + ] | |
664 | + } | |
665 | + ], | |
666 | + "timewindow": { | |
667 | + "realtime": { | |
668 | + "timewindowMs": 60000 | |
669 | + } | |
670 | + }, | |
671 | + "showTitle": false, | |
672 | + "backgroundColor": "#fff", | |
673 | + "color": "rgba(0, 0, 0, 0.87)", | |
674 | + "padding": "0px", | |
675 | + "settings": { | |
676 | + "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>", | |
677 | + "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}" | |
678 | + }, | |
679 | + "title": "New HTML Value Card", | |
680 | + "dropShadow": true, | |
681 | + "enableFullscreen": false, | |
682 | + "widgetStyle": {}, | |
683 | + "titleStyle": { | |
684 | + "fontSize": "16px", | |
685 | + "fontWeight": 400 | |
686 | + }, | |
687 | + "useDashboardTimewindow": true, | |
688 | + "showLegend": false, | |
689 | + "actions": { | |
690 | + "elementClick": [ | |
691 | + { | |
692 | + "name": "activeDevices", | |
693 | + "icon": "more_horiz", | |
694 | + "type": "openDashboardState", | |
695 | + "targetDashboardStateId": "device_updated", | |
696 | + "setEntityId": false, | |
697 | + "stateEntityParamName": null, | |
698 | + "openInSeparateDialog": false, | |
699 | + "dialogTitle": "", | |
700 | + "dialogHideDashboardToolbar": true, | |
701 | + "dialogWidth": null, | |
702 | + "dialogHeight": null, | |
703 | + "openRightLayout": false, | |
704 | + "id": "d787c212-8c56-34f0-349a-5aae2ffd1eae" | |
705 | + } | |
706 | + ] | |
707 | + }, | |
708 | + "showTitleIcon": false, | |
709 | + "titleIcon": null, | |
710 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
711 | + "iconSize": "24px", | |
712 | + "titleTooltip": "", | |
713 | + "enableDataExport": false, | |
714 | + "displayTimewindow": true | |
715 | + }, | |
716 | + "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81" | |
717 | + }, | |
718 | + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { | |
719 | + "isSystemType": true, | |
720 | + "bundleAlias": "cards", | |
721 | + "typeAlias": "html_value_card", | |
722 | + "type": "latest", | |
723 | + "title": "New widget", | |
724 | + "sizeX": 8, | |
725 | + "sizeY": 3, | |
726 | + "config": { | |
727 | + "datasources": [ | |
728 | + { | |
729 | + "type": "entityCount", | |
730 | + "name": "", | |
731 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
732 | + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
733 | + "dataKeys": [ | |
734 | + { | |
735 | + "name": "count", | |
736 | + "type": "count", | |
737 | + "label": "updatingDevicesNumber", | |
738 | + "color": "#4caf50", | |
739 | + "settings": {}, | |
740 | + "_hash": 0.7404827038869322, | |
741 | + "units": null, | |
742 | + "decimals": null, | |
743 | + "funcBody": null, | |
744 | + "usePostProcessing": null, | |
745 | + "postFuncBody": null | |
746 | + } | |
747 | + ] | |
748 | + } | |
749 | + ], | |
750 | + "timewindow": { | |
751 | + "realtime": { | |
752 | + "timewindowMs": 60000 | |
753 | + } | |
754 | + }, | |
755 | + "showTitle": false, | |
756 | + "backgroundColor": "#fff", | |
757 | + "color": "rgba(0, 0, 0, 0.87)", | |
758 | + "padding": "0px", | |
759 | + "settings": { | |
760 | + "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_software_failed_count' id=\"activeDevices\">\n ${updatingDevicesNumber:0}\n </div> \n <script type=\"text/javascript\">\n function init() {\n var counter = $('.error_software_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>", | |
761 | + "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}" | |
762 | + }, | |
763 | + "title": "New HTML Value Card", | |
764 | + "dropShadow": true, | |
765 | + "enableFullscreen": false, | |
766 | + "widgetStyle": {}, | |
767 | + "titleStyle": { | |
768 | + "fontSize": "16px", | |
769 | + "fontWeight": 400 | |
770 | + }, | |
771 | + "useDashboardTimewindow": true, | |
772 | + "showLegend": false, | |
773 | + "actions": { | |
774 | + "elementClick": [ | |
775 | + { | |
776 | + "name": "activeDevices", | |
777 | + "icon": "more_horiz", | |
778 | + "type": "openDashboardState", | |
779 | + "targetDashboardStateId": "device_error", | |
780 | + "setEntityId": false, | |
781 | + "stateEntityParamName": null, | |
782 | + "openInSeparateDialog": false, | |
783 | + "dialogTitle": "", | |
784 | + "dialogHideDashboardToolbar": true, | |
785 | + "dialogWidth": null, | |
786 | + "dialogHeight": null, | |
787 | + "openRightLayout": false, | |
788 | + "id": "0b3d2887-9929-84d5-3795-0763dca15cba" | |
789 | + } | |
790 | + ] | |
791 | + }, | |
792 | + "showTitleIcon": false, | |
793 | + "titleIcon": null, | |
794 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
795 | + "iconSize": "24px", | |
796 | + "titleTooltip": "", | |
797 | + "enableDataExport": false, | |
798 | + "displayTimewindow": true | |
799 | + }, | |
800 | + "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8" | |
801 | + }, | |
802 | + "21be08bb-ec90-f760-ad6f-e7678f12c401": { | |
803 | + "isSystemType": true, | |
804 | + "bundleAlias": "cards", | |
805 | + "typeAlias": "entities_table", | |
806 | + "type": "latest", | |
807 | + "title": "New widget", | |
808 | + "image": null, | |
809 | + "description": null, | |
810 | + "sizeX": 7.5, | |
811 | + "sizeY": 6.5, | |
812 | + "config": { | |
813 | + "timewindow": { | |
814 | + "realtime": { | |
815 | + "interval": 1000, | |
816 | + "timewindowMs": 86400000 | |
817 | + }, | |
818 | + "aggregation": { | |
819 | + "type": "NONE", | |
820 | + "limit": 200 | |
821 | + } | |
822 | + }, | |
823 | + "showTitle": true, | |
824 | + "backgroundColor": "rgb(255, 255, 255)", | |
825 | + "color": "rgba(0, 0, 0, 0.87)", | |
826 | + "padding": "4px", | |
827 | + "settings": { | |
828 | + "enableSearch": true, | |
829 | + "displayPagination": true, | |
830 | + "defaultPageSize": 10, | |
831 | + "defaultSortOrder": "entityLabel", | |
832 | + "displayEntityName": false, | |
833 | + "displayEntityType": false, | |
834 | + "enableSelectColumnDisplay": false, | |
835 | + "enableStickyHeader": true, | |
836 | + "enableStickyAction": true, | |
837 | + "entitiesTitle": "Devices", | |
838 | + "displayEntityLabel": true, | |
839 | + "entityLabelColumnTitle": "Device" | |
840 | + }, | |
841 | + "title": "New Entities table", | |
842 | + "dropShadow": true, | |
843 | + "enableFullscreen": true, | |
844 | + "titleStyle": { | |
845 | + "fontSize": "16px", | |
846 | + "fontWeight": 400, | |
847 | + "padding": "5px 10px 5px 10px" | |
848 | + }, | |
849 | + "useDashboardTimewindow": false, | |
850 | + "showLegend": false, | |
851 | + "datasources": [ | |
852 | + { | |
853 | + "type": "entity", | |
854 | + "name": null, | |
855 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
856 | + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
857 | + "dataKeys": [ | |
858 | + { | |
859 | + "name": "current_sw_title", | |
860 | + "type": "timeseries", | |
861 | + "label": "Current SW title", | |
862 | + "color": "#2196f3", | |
863 | + "settings": { | |
864 | + "columnWidth": "0px", | |
865 | + "useCellStyleFunction": false, | |
866 | + "cellStyleFunction": "", | |
867 | + "useCellContentFunction": false, | |
868 | + "defaultColumnVisibility": "visible", | |
869 | + "columnSelectionToDisplay": "enabled" | |
870 | + }, | |
871 | + "_hash": 0.09545533885166413, | |
872 | + "units": null, | |
873 | + "decimals": null, | |
874 | + "funcBody": null, | |
875 | + "usePostProcessing": null, | |
876 | + "postFuncBody": null | |
877 | + }, | |
878 | + { | |
879 | + "name": "current_sw_version", | |
880 | + "type": "timeseries", | |
881 | + "label": "Current SW version", | |
882 | + "color": "#4caf50", | |
883 | + "settings": { | |
884 | + "columnWidth": "0px", | |
885 | + "useCellStyleFunction": false, | |
886 | + "cellStyleFunction": "", | |
887 | + "useCellContentFunction": false, | |
888 | + "defaultColumnVisibility": "visible", | |
889 | + "columnSelectionToDisplay": "enabled" | |
890 | + }, | |
891 | + "_hash": 0.7206056602328659, | |
892 | + "units": null, | |
893 | + "decimals": null, | |
894 | + "funcBody": null, | |
895 | + "usePostProcessing": null, | |
896 | + "postFuncBody": null | |
897 | + }, | |
898 | + { | |
899 | + "name": "target_sw_title", | |
900 | + "type": "timeseries", | |
901 | + "label": "Target SW title", | |
902 | + "color": "#ffc107", | |
903 | + "settings": { | |
904 | + "columnWidth": "0px", | |
905 | + "useCellStyleFunction": false, | |
906 | + "cellStyleFunction": "", | |
907 | + "useCellContentFunction": false, | |
908 | + "defaultColumnVisibility": "visible", | |
909 | + "columnSelectionToDisplay": "enabled" | |
910 | + }, | |
911 | + "_hash": 0.9934225682766313, | |
912 | + "units": null, | |
913 | + "decimals": null, | |
914 | + "funcBody": null, | |
915 | + "usePostProcessing": null, | |
916 | + "postFuncBody": null | |
917 | + }, | |
918 | + { | |
919 | + "name": "target_sw_version", | |
920 | + "type": "timeseries", | |
921 | + "label": "Target SW version", | |
922 | + "color": "#607d8b", | |
923 | + "settings": { | |
924 | + "columnWidth": "0px", | |
925 | + "useCellStyleFunction": false, | |
926 | + "cellStyleFunction": "", | |
927 | + "useCellContentFunction": false, | |
928 | + "cellContentFunction": "", | |
929 | + "defaultColumnVisibility": "visible", | |
930 | + "columnSelectionToDisplay": "enabled" | |
931 | + }, | |
932 | + "_hash": 0.5251724416842531, | |
933 | + "units": null, | |
934 | + "decimals": null, | |
935 | + "funcBody": null, | |
936 | + "usePostProcessing": null, | |
937 | + "postFuncBody": null | |
938 | + }, | |
939 | + { | |
940 | + "name": "target_sw_ts", | |
941 | + "type": "timeseries", | |
942 | + "label": "Target SW set time", | |
943 | + "color": "#e91e63", | |
944 | + "settings": { | |
945 | + "columnWidth": "0px", | |
946 | + "useCellStyleFunction": false, | |
947 | + "cellStyleFunction": "", | |
948 | + "useCellContentFunction": true, | |
949 | + "defaultColumnVisibility": "visible", | |
950 | + "columnSelectionToDisplay": "enabled", | |
951 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
952 | + }, | |
953 | + "_hash": 0.31823244858578237, | |
954 | + "units": null, | |
955 | + "decimals": null, | |
956 | + "funcBody": null, | |
957 | + "usePostProcessing": null, | |
958 | + "postFuncBody": null | |
959 | + }, | |
960 | + { | |
961 | + "name": "sw_state", | |
962 | + "type": "timeseries", | |
963 | + "label": "Progress", | |
964 | + "color": "#9c27b0", | |
965 | + "settings": { | |
966 | + "columnWidth": "30%", | |
967 | + "useCellStyleFunction": true, | |
968 | + "useCellContentFunction": true, | |
969 | + "defaultColumnVisibility": "visible", | |
970 | + "columnSelectionToDisplay": "enabled", | |
971 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
972 | + "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}" | |
973 | + }, | |
974 | + "_hash": 0.8174211757846257, | |
975 | + "units": null, | |
976 | + "decimals": null, | |
977 | + "funcBody": null, | |
978 | + "usePostProcessing": null, | |
979 | + "postFuncBody": null | |
980 | + }, | |
981 | + { | |
982 | + "name": "sw_state", | |
983 | + "type": "timeseries", | |
984 | + "label": "Status", | |
985 | + "color": "#f44336", | |
986 | + "settings": { | |
987 | + "columnWidth": "130px", | |
988 | + "useCellStyleFunction": true, | |
989 | + "useCellContentFunction": true, | |
990 | + "defaultColumnVisibility": "visible", | |
991 | + "columnSelectionToDisplay": "enabled", | |
992 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
993 | + "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>';" | |
994 | + }, | |
995 | + "_hash": 0.7764426948615217, | |
996 | + "units": null, | |
997 | + "decimals": null, | |
998 | + "funcBody": null, | |
999 | + "usePostProcessing": null, | |
1000 | + "postFuncBody": null | |
1001 | + }, | |
1002 | + { | |
1003 | + "name": "sw_checksum", | |
1004 | + "type": "attribute", | |
1005 | + "label": "sw_checksum", | |
1006 | + "color": "#3f51b5", | |
1007 | + "settings": { | |
1008 | + "columnWidth": "0px", | |
1009 | + "useCellStyleFunction": false, | |
1010 | + "cellStyleFunction": "", | |
1011 | + "useCellContentFunction": false, | |
1012 | + "defaultColumnVisibility": "hidden", | |
1013 | + "columnSelectionToDisplay": "disabled" | |
1014 | + }, | |
1015 | + "_hash": 0.5594087842471693, | |
1016 | + "units": null, | |
1017 | + "decimals": null, | |
1018 | + "funcBody": null, | |
1019 | + "usePostProcessing": null, | |
1020 | + "postFuncBody": null | |
1021 | + }, | |
1022 | + { | |
1023 | + "name": "sw_url", | |
1024 | + "type": "attribute", | |
1025 | + "label": "sw_url", | |
1026 | + "color": "#e91e63", | |
1027 | + "settings": { | |
1028 | + "columnWidth": "0px", | |
1029 | + "useCellStyleFunction": false, | |
1030 | + "cellStyleFunction": "", | |
1031 | + "useCellContentFunction": false, | |
1032 | + "cellContentFunction": "", | |
1033 | + "defaultColumnVisibility": "hidden", | |
1034 | + "columnSelectionToDisplay": "disabled" | |
1035 | + }, | |
1036 | + "_hash": 0.3355829384124256, | |
1037 | + "units": null, | |
1038 | + "decimals": null, | |
1039 | + "funcBody": null, | |
1040 | + "usePostProcessing": null, | |
1041 | + "postFuncBody": null | |
1042 | + } | |
1043 | + ] | |
1044 | + } | |
1045 | + ], | |
1046 | + "actions": { | |
1047 | + "actionCellButton": [ | |
1048 | + { | |
1049 | + "name": "History software update", | |
1050 | + "icon": "history", | |
1051 | + "type": "openDashboardState", | |
1052 | + "targetDashboardStateId": "device_software_history", | |
1053 | + "setEntityId": true, | |
1054 | + "stateEntityParamName": null, | |
1055 | + "openInSeparateDialog": false, | |
1056 | + "dialogTitle": "", | |
1057 | + "dialogHideDashboardToolbar": true, | |
1058 | + "dialogWidth": null, | |
1059 | + "dialogHeight": null, | |
1060 | + "openRightLayout": false, | |
1061 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1062 | + }, | |
1063 | + { | |
1064 | + "name": "Edit software", | |
1065 | + "icon": "edit", | |
1066 | + "type": "customPretty", | |
1067 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-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>", | |
1068 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1069 | + "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 softwareId: [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 softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1070 | + "customResources": [], | |
1071 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1072 | + }, | |
1073 | + { | |
1074 | + "name": "Download software", | |
1075 | + "icon": "file_download", | |
1076 | + "type": "custom", | |
1077 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1078 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1079 | + }, | |
1080 | + { | |
1081 | + "name": "Copy checksum/URL", | |
1082 | + "icon": "content_copy", | |
1083 | + "type": "custom", | |
1084 | + "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 === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1085 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1086 | + } | |
1087 | + ] | |
1088 | + }, | |
1089 | + "showTitleIcon": false, | |
1090 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1091 | + "iconSize": "24px", | |
1092 | + "titleTooltip": "", | |
1093 | + "widgetStyle": {} | |
1094 | + }, | |
1095 | + "row": 0, | |
1096 | + "col": 0, | |
1097 | + "id": "21be08bb-ec90-f760-ad6f-e7678f12c401" | |
1098 | + }, | |
1099 | + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { | |
1100 | + "isSystemType": true, | |
1101 | + "bundleAlias": "cards", | |
1102 | + "typeAlias": "entities_table", | |
1103 | + "type": "latest", | |
1104 | + "title": "New widget", | |
1105 | + "image": null, | |
1106 | + "description": null, | |
1107 | + "sizeX": 7.5, | |
1108 | + "sizeY": 6.5, | |
1109 | + "config": { | |
1110 | + "timewindow": { | |
1111 | + "realtime": { | |
1112 | + "interval": 1000, | |
1113 | + "timewindowMs": 86400000 | |
1114 | + }, | |
1115 | + "aggregation": { | |
1116 | + "type": "NONE", | |
1117 | + "limit": 200 | |
1118 | + } | |
1119 | + }, | |
1120 | + "showTitle": true, | |
1121 | + "backgroundColor": "rgb(255, 255, 255)", | |
1122 | + "color": "rgba(0, 0, 0, 0.87)", | |
1123 | + "padding": "4px", | |
1124 | + "settings": { | |
1125 | + "enableSearch": true, | |
1126 | + "displayPagination": true, | |
1127 | + "defaultPageSize": 10, | |
1128 | + "defaultSortOrder": "entityLabel", | |
1129 | + "displayEntityName": false, | |
1130 | + "displayEntityType": false, | |
1131 | + "enableSelectColumnDisplay": false, | |
1132 | + "enableStickyHeader": true, | |
1133 | + "enableStickyAction": true, | |
1134 | + "entitiesTitle": "Devices", | |
1135 | + "displayEntityLabel": true, | |
1136 | + "entityLabelColumnTitle": "Device" | |
1137 | + }, | |
1138 | + "title": "New Entities table", | |
1139 | + "dropShadow": true, | |
1140 | + "enableFullscreen": true, | |
1141 | + "titleStyle": { | |
1142 | + "fontSize": "16px", | |
1143 | + "fontWeight": 400, | |
1144 | + "padding": "5px 10px 5px 10px" | |
1145 | + }, | |
1146 | + "useDashboardTimewindow": false, | |
1147 | + "showLegend": false, | |
1148 | + "datasources": [ | |
1149 | + { | |
1150 | + "type": "entity", | |
1151 | + "name": null, | |
1152 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1153 | + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
1154 | + "dataKeys": [ | |
1155 | + { | |
1156 | + "name": "current_sw_title", | |
1157 | + "type": "timeseries", | |
1158 | + "label": "Current SW title", | |
1159 | + "color": "#2196f3", | |
1160 | + "settings": { | |
1161 | + "columnWidth": "0px", | |
1162 | + "useCellStyleFunction": false, | |
1163 | + "cellStyleFunction": "", | |
1164 | + "useCellContentFunction": false, | |
1165 | + "defaultColumnVisibility": "visible", | |
1166 | + "columnSelectionToDisplay": "enabled" | |
1167 | + }, | |
1168 | + "_hash": 0.09545533885166413, | |
1169 | + "units": null, | |
1170 | + "decimals": null, | |
1171 | + "funcBody": null, | |
1172 | + "usePostProcessing": null, | |
1173 | + "postFuncBody": null | |
1174 | + }, | |
1175 | + { | |
1176 | + "name": "current_sw_version", | |
1177 | + "type": "timeseries", | |
1178 | + "label": "Current SW version", | |
1179 | + "color": "#4caf50", | |
1180 | + "settings": { | |
1181 | + "columnWidth": "0px", | |
1182 | + "useCellStyleFunction": false, | |
1183 | + "cellStyleFunction": "", | |
1184 | + "useCellContentFunction": false, | |
1185 | + "defaultColumnVisibility": "visible", | |
1186 | + "columnSelectionToDisplay": "enabled" | |
1187 | + }, | |
1188 | + "_hash": 0.7206056602328659, | |
1189 | + "units": null, | |
1190 | + "decimals": null, | |
1191 | + "funcBody": null, | |
1192 | + "usePostProcessing": null, | |
1193 | + "postFuncBody": null | |
1194 | + }, | |
1195 | + { | |
1196 | + "name": "target_sw_title", | |
1197 | + "type": "timeseries", | |
1198 | + "label": "Target SW title", | |
1199 | + "color": "#ffc107", | |
1200 | + "settings": { | |
1201 | + "columnWidth": "0px", | |
1202 | + "useCellStyleFunction": false, | |
1203 | + "cellStyleFunction": "", | |
1204 | + "useCellContentFunction": false, | |
1205 | + "defaultColumnVisibility": "visible", | |
1206 | + "columnSelectionToDisplay": "enabled" | |
1207 | + }, | |
1208 | + "_hash": 0.9934225682766313, | |
1209 | + "units": null, | |
1210 | + "decimals": null, | |
1211 | + "funcBody": null, | |
1212 | + "usePostProcessing": null, | |
1213 | + "postFuncBody": null | |
1214 | + }, | |
1215 | + { | |
1216 | + "name": "target_sw_version", | |
1217 | + "type": "timeseries", | |
1218 | + "label": "Target SW version", | |
1219 | + "color": "#607d8b", | |
1220 | + "settings": { | |
1221 | + "columnWidth": "0px", | |
1222 | + "useCellStyleFunction": false, | |
1223 | + "cellStyleFunction": "", | |
1224 | + "useCellContentFunction": false, | |
1225 | + "cellContentFunction": "", | |
1226 | + "defaultColumnVisibility": "visible", | |
1227 | + "columnSelectionToDisplay": "enabled" | |
1228 | + }, | |
1229 | + "_hash": 0.5251724416842531, | |
1230 | + "units": null, | |
1231 | + "decimals": null, | |
1232 | + "funcBody": null, | |
1233 | + "usePostProcessing": null, | |
1234 | + "postFuncBody": null | |
1235 | + }, | |
1236 | + { | |
1237 | + "name": "target_sw_ts", | |
1238 | + "type": "timeseries", | |
1239 | + "label": "Target SW set time", | |
1240 | + "color": "#e91e63", | |
1241 | + "settings": { | |
1242 | + "columnWidth": "0px", | |
1243 | + "useCellStyleFunction": false, | |
1244 | + "cellStyleFunction": "", | |
1245 | + "useCellContentFunction": true, | |
1246 | + "defaultColumnVisibility": "visible", | |
1247 | + "columnSelectionToDisplay": "enabled", | |
1248 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1249 | + }, | |
1250 | + "_hash": 0.31823244858578237, | |
1251 | + "units": null, | |
1252 | + "decimals": null, | |
1253 | + "funcBody": null, | |
1254 | + "usePostProcessing": null, | |
1255 | + "postFuncBody": null | |
1256 | + }, | |
1257 | + { | |
1258 | + "name": "sw_state", | |
1259 | + "type": "timeseries", | |
1260 | + "label": "Progress", | |
1261 | + "color": "#9c27b0", | |
1262 | + "settings": { | |
1263 | + "columnWidth": "30%", | |
1264 | + "useCellStyleFunction": true, | |
1265 | + "useCellContentFunction": true, | |
1266 | + "defaultColumnVisibility": "visible", | |
1267 | + "columnSelectionToDisplay": "enabled", | |
1268 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1269 | + "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}" | |
1270 | + }, | |
1271 | + "_hash": 0.8174211757846257, | |
1272 | + "units": null, | |
1273 | + "decimals": null, | |
1274 | + "funcBody": null, | |
1275 | + "usePostProcessing": null, | |
1276 | + "postFuncBody": null | |
1277 | + }, | |
1278 | + { | |
1279 | + "name": "sw_state", | |
1280 | + "type": "timeseries", | |
1281 | + "label": "Status", | |
1282 | + "color": "#f44336", | |
1283 | + "settings": { | |
1284 | + "columnWidth": "130px", | |
1285 | + "useCellStyleFunction": true, | |
1286 | + "useCellContentFunction": true, | |
1287 | + "defaultColumnVisibility": "visible", | |
1288 | + "columnSelectionToDisplay": "enabled", | |
1289 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1290 | + "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>';" | |
1291 | + }, | |
1292 | + "_hash": 0.7764426948615217, | |
1293 | + "units": null, | |
1294 | + "decimals": null, | |
1295 | + "funcBody": null, | |
1296 | + "usePostProcessing": null, | |
1297 | + "postFuncBody": null | |
1298 | + }, | |
1299 | + { | |
1300 | + "name": "sw_checksum", | |
1301 | + "type": "attribute", | |
1302 | + "label": "sw_checksum", | |
1303 | + "color": "#3f51b5", | |
1304 | + "settings": { | |
1305 | + "columnWidth": "0px", | |
1306 | + "useCellStyleFunction": false, | |
1307 | + "cellStyleFunction": "", | |
1308 | + "useCellContentFunction": false, | |
1309 | + "defaultColumnVisibility": "hidden", | |
1310 | + "columnSelectionToDisplay": "disabled" | |
1311 | + }, | |
1312 | + "_hash": 0.5594087842471693, | |
1313 | + "units": null, | |
1314 | + "decimals": null, | |
1315 | + "funcBody": null, | |
1316 | + "usePostProcessing": null, | |
1317 | + "postFuncBody": null | |
1318 | + }, | |
1319 | + { | |
1320 | + "name": "sw_url", | |
1321 | + "type": "attribute", | |
1322 | + "label": "sw_url", | |
1323 | + "color": "#e91e63", | |
1324 | + "settings": { | |
1325 | + "columnWidth": "0px", | |
1326 | + "useCellStyleFunction": false, | |
1327 | + "cellStyleFunction": "", | |
1328 | + "useCellContentFunction": false, | |
1329 | + "cellContentFunction": "", | |
1330 | + "defaultColumnVisibility": "hidden", | |
1331 | + "columnSelectionToDisplay": "disabled" | |
1332 | + }, | |
1333 | + "_hash": 0.3355829384124256, | |
1334 | + "units": null, | |
1335 | + "decimals": null, | |
1336 | + "funcBody": null, | |
1337 | + "usePostProcessing": null, | |
1338 | + "postFuncBody": null | |
1339 | + } | |
1340 | + ] | |
1341 | + } | |
1342 | + ], | |
1343 | + "actions": { | |
1344 | + "actionCellButton": [ | |
1345 | + { | |
1346 | + "name": "History software update", | |
1347 | + "icon": "history", | |
1348 | + "type": "openDashboardState", | |
1349 | + "targetDashboardStateId": "device_software_history", | |
1350 | + "setEntityId": true, | |
1351 | + "stateEntityParamName": null, | |
1352 | + "openInSeparateDialog": false, | |
1353 | + "dialogTitle": "", | |
1354 | + "dialogHideDashboardToolbar": true, | |
1355 | + "dialogWidth": null, | |
1356 | + "dialogHeight": null, | |
1357 | + "openRightLayout": false, | |
1358 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1359 | + }, | |
1360 | + { | |
1361 | + "name": "Edit software", | |
1362 | + "icon": "edit", | |
1363 | + "type": "customPretty", | |
1364 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-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>", | |
1365 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1366 | + "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 softwareId: [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 softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1367 | + "customResources": [], | |
1368 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1369 | + }, | |
1370 | + { | |
1371 | + "name": "Download software", | |
1372 | + "icon": "file_download", | |
1373 | + "type": "custom", | |
1374 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1375 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1376 | + }, | |
1377 | + { | |
1378 | + "name": "Copy checksum/URL", | |
1379 | + "icon": "content_copy", | |
1380 | + "type": "custom", | |
1381 | + "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 === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1382 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1383 | + } | |
1384 | + ] | |
1385 | + }, | |
1386 | + "showTitleIcon": false, | |
1387 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1388 | + "iconSize": "24px", | |
1389 | + "titleTooltip": "", | |
1390 | + "widgetStyle": {} | |
1391 | + }, | |
1392 | + "row": 0, | |
1393 | + "col": 0, | |
1394 | + "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91" | |
1395 | + }, | |
1396 | + "3624013b-378c-f110-5eba-ae95c25a4dcc": { | |
1397 | + "isSystemType": true, | |
1398 | + "bundleAlias": "cards", | |
1399 | + "typeAlias": "entities_table", | |
1400 | + "type": "latest", | |
1401 | + "title": "New widget", | |
1402 | + "image": null, | |
1403 | + "description": null, | |
1404 | + "sizeX": 7.5, | |
1405 | + "sizeY": 6.5, | |
1406 | + "config": { | |
1407 | + "timewindow": { | |
1408 | + "realtime": { | |
1409 | + "interval": 1000, | |
1410 | + "timewindowMs": 86400000 | |
1411 | + }, | |
1412 | + "aggregation": { | |
1413 | + "type": "NONE", | |
1414 | + "limit": 200 | |
1415 | + } | |
1416 | + }, | |
1417 | + "showTitle": true, | |
1418 | + "backgroundColor": "rgb(255, 255, 255)", | |
1419 | + "color": "rgba(0, 0, 0, 0.87)", | |
1420 | + "padding": "4px", | |
1421 | + "settings": { | |
1422 | + "enableSearch": true, | |
1423 | + "displayPagination": true, | |
1424 | + "defaultPageSize": 10, | |
1425 | + "defaultSortOrder": "entityLabel", | |
1426 | + "displayEntityName": false, | |
1427 | + "displayEntityType": false, | |
1428 | + "enableSelectColumnDisplay": false, | |
1429 | + "enableStickyHeader": true, | |
1430 | + "enableStickyAction": true, | |
1431 | + "entitiesTitle": "Devices", | |
1432 | + "displayEntityLabel": true, | |
1433 | + "entityLabelColumnTitle": "Device" | |
1434 | + }, | |
1435 | + "title": "New Entities table", | |
1436 | + "dropShadow": true, | |
1437 | + "enableFullscreen": true, | |
1438 | + "titleStyle": { | |
1439 | + "fontSize": "16px", | |
1440 | + "fontWeight": 400, | |
1441 | + "padding": "5px 10px 5px 10px" | |
1442 | + }, | |
1443 | + "useDashboardTimewindow": false, | |
1444 | + "showLegend": false, | |
1445 | + "datasources": [ | |
1446 | + { | |
1447 | + "type": "entity", | |
1448 | + "name": null, | |
1449 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1450 | + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
1451 | + "dataKeys": [ | |
1452 | + { | |
1453 | + "name": "current_sw_title", | |
1454 | + "type": "timeseries", | |
1455 | + "label": "Current SW title", | |
1456 | + "color": "#2196f3", | |
1457 | + "settings": { | |
1458 | + "columnWidth": "0px", | |
1459 | + "useCellStyleFunction": false, | |
1460 | + "cellStyleFunction": "", | |
1461 | + "useCellContentFunction": false, | |
1462 | + "defaultColumnVisibility": "visible", | |
1463 | + "columnSelectionToDisplay": "enabled" | |
1464 | + }, | |
1465 | + "_hash": 0.09545533885166413, | |
1466 | + "units": null, | |
1467 | + "decimals": null, | |
1468 | + "funcBody": null, | |
1469 | + "usePostProcessing": null, | |
1470 | + "postFuncBody": null | |
1471 | + }, | |
1472 | + { | |
1473 | + "name": "current_sw_version", | |
1474 | + "type": "timeseries", | |
1475 | + "label": "Current SW version", | |
1476 | + "color": "#4caf50", | |
1477 | + "settings": { | |
1478 | + "columnWidth": "0px", | |
1479 | + "useCellStyleFunction": false, | |
1480 | + "cellStyleFunction": "", | |
1481 | + "useCellContentFunction": false, | |
1482 | + "defaultColumnVisibility": "visible", | |
1483 | + "columnSelectionToDisplay": "enabled" | |
1484 | + }, | |
1485 | + "_hash": 0.7206056602328659, | |
1486 | + "units": null, | |
1487 | + "decimals": null, | |
1488 | + "funcBody": null, | |
1489 | + "usePostProcessing": null, | |
1490 | + "postFuncBody": null | |
1491 | + }, | |
1492 | + { | |
1493 | + "name": "target_sw_title", | |
1494 | + "type": "timeseries", | |
1495 | + "label": "Target SW title", | |
1496 | + "color": "#ffc107", | |
1497 | + "settings": { | |
1498 | + "columnWidth": "0px", | |
1499 | + "useCellStyleFunction": false, | |
1500 | + "cellStyleFunction": "", | |
1501 | + "useCellContentFunction": false, | |
1502 | + "defaultColumnVisibility": "visible", | |
1503 | + "columnSelectionToDisplay": "enabled" | |
1504 | + }, | |
1505 | + "_hash": 0.9934225682766313, | |
1506 | + "units": null, | |
1507 | + "decimals": null, | |
1508 | + "funcBody": null, | |
1509 | + "usePostProcessing": null, | |
1510 | + "postFuncBody": null | |
1511 | + }, | |
1512 | + { | |
1513 | + "name": "target_sw_version", | |
1514 | + "type": "timeseries", | |
1515 | + "label": "Target SW version", | |
1516 | + "color": "#607d8b", | |
1517 | + "settings": { | |
1518 | + "columnWidth": "0px", | |
1519 | + "useCellStyleFunction": false, | |
1520 | + "cellStyleFunction": "", | |
1521 | + "useCellContentFunction": false, | |
1522 | + "cellContentFunction": "", | |
1523 | + "defaultColumnVisibility": "visible", | |
1524 | + "columnSelectionToDisplay": "enabled" | |
1525 | + }, | |
1526 | + "_hash": 0.5251724416842531, | |
1527 | + "units": null, | |
1528 | + "decimals": null, | |
1529 | + "funcBody": null, | |
1530 | + "usePostProcessing": null, | |
1531 | + "postFuncBody": null | |
1532 | + }, | |
1533 | + { | |
1534 | + "name": "target_sw_ts", | |
1535 | + "type": "timeseries", | |
1536 | + "label": "Target SW set time", | |
1537 | + "color": "#e91e63", | |
1538 | + "settings": { | |
1539 | + "columnWidth": "0px", | |
1540 | + "useCellStyleFunction": false, | |
1541 | + "cellStyleFunction": "", | |
1542 | + "useCellContentFunction": true, | |
1543 | + "defaultColumnVisibility": "visible", | |
1544 | + "columnSelectionToDisplay": "enabled", | |
1545 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1546 | + }, | |
1547 | + "_hash": 0.31823244858578237, | |
1548 | + "units": null, | |
1549 | + "decimals": null, | |
1550 | + "funcBody": null, | |
1551 | + "usePostProcessing": null, | |
1552 | + "postFuncBody": null | |
1553 | + }, | |
1554 | + { | |
1555 | + "name": "sw_state", | |
1556 | + "type": "timeseries", | |
1557 | + "label": "Progress", | |
1558 | + "color": "#9c27b0", | |
1559 | + "settings": { | |
1560 | + "columnWidth": "30%", | |
1561 | + "useCellStyleFunction": true, | |
1562 | + "useCellContentFunction": true, | |
1563 | + "defaultColumnVisibility": "visible", | |
1564 | + "columnSelectionToDisplay": "enabled", | |
1565 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1566 | + "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}" | |
1567 | + }, | |
1568 | + "_hash": 0.8174211757846257, | |
1569 | + "units": null, | |
1570 | + "decimals": null, | |
1571 | + "funcBody": null, | |
1572 | + "usePostProcessing": null, | |
1573 | + "postFuncBody": null | |
1574 | + }, | |
1575 | + { | |
1576 | + "name": "sw_state", | |
1577 | + "type": "timeseries", | |
1578 | + "label": "Status", | |
1579 | + "color": "#f44336", | |
1580 | + "settings": { | |
1581 | + "columnWidth": "130px", | |
1582 | + "useCellStyleFunction": true, | |
1583 | + "useCellContentFunction": true, | |
1584 | + "defaultColumnVisibility": "visible", | |
1585 | + "columnSelectionToDisplay": "enabled", | |
1586 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1587 | + "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>';" | |
1588 | + }, | |
1589 | + "_hash": 0.7764426948615217, | |
1590 | + "units": null, | |
1591 | + "decimals": null, | |
1592 | + "funcBody": null, | |
1593 | + "usePostProcessing": null, | |
1594 | + "postFuncBody": null | |
1595 | + }, | |
1596 | + { | |
1597 | + "name": "sw_checksum", | |
1598 | + "type": "attribute", | |
1599 | + "label": "sw_checksum", | |
1600 | + "color": "#3f51b5", | |
1601 | + "settings": { | |
1602 | + "columnWidth": "0px", | |
1603 | + "useCellStyleFunction": false, | |
1604 | + "cellStyleFunction": "", | |
1605 | + "useCellContentFunction": false, | |
1606 | + "defaultColumnVisibility": "hidden", | |
1607 | + "columnSelectionToDisplay": "disabled" | |
1608 | + }, | |
1609 | + "_hash": 0.5594087842471693, | |
1610 | + "units": null, | |
1611 | + "decimals": null, | |
1612 | + "funcBody": null, | |
1613 | + "usePostProcessing": null, | |
1614 | + "postFuncBody": null | |
1615 | + }, | |
1616 | + { | |
1617 | + "name": "sw_url", | |
1618 | + "type": "attribute", | |
1619 | + "label": "sw_url", | |
1620 | + "color": "#e91e63", | |
1621 | + "settings": { | |
1622 | + "columnWidth": "0px", | |
1623 | + "useCellStyleFunction": false, | |
1624 | + "cellStyleFunction": "", | |
1625 | + "useCellContentFunction": false, | |
1626 | + "cellContentFunction": "", | |
1627 | + "defaultColumnVisibility": "hidden", | |
1628 | + "columnSelectionToDisplay": "disabled" | |
1629 | + }, | |
1630 | + "_hash": 0.3355829384124256, | |
1631 | + "units": null, | |
1632 | + "decimals": null, | |
1633 | + "funcBody": null, | |
1634 | + "usePostProcessing": null, | |
1635 | + "postFuncBody": null | |
1636 | + } | |
1637 | + ] | |
1638 | + } | |
1639 | + ], | |
1640 | + "actions": { | |
1641 | + "actionCellButton": [ | |
1642 | + { | |
1643 | + "name": "History software update", | |
1644 | + "icon": "history", | |
1645 | + "type": "openDashboardState", | |
1646 | + "targetDashboardStateId": "device_software_history", | |
1647 | + "setEntityId": true, | |
1648 | + "stateEntityParamName": null, | |
1649 | + "openInSeparateDialog": false, | |
1650 | + "dialogTitle": "", | |
1651 | + "dialogHideDashboardToolbar": true, | |
1652 | + "dialogWidth": null, | |
1653 | + "dialogHeight": null, | |
1654 | + "openRightLayout": false, | |
1655 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1656 | + }, | |
1657 | + { | |
1658 | + "name": "Edit software", | |
1659 | + "icon": "edit", | |
1660 | + "type": "customPretty", | |
1661 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-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>", | |
1662 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1663 | + "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 softwareId: [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 softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1664 | + "customResources": [], | |
1665 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1666 | + }, | |
1667 | + { | |
1668 | + "name": "Download software", | |
1669 | + "icon": "file_download", | |
1670 | + "type": "custom", | |
1671 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1672 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1673 | + }, | |
1674 | + { | |
1675 | + "name": "Copy checksum/URL", | |
1676 | + "icon": "content_copy", | |
1677 | + "type": "custom", | |
1678 | + "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 === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1679 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1680 | + } | |
1681 | + ] | |
1682 | + }, | |
1683 | + "showTitleIcon": false, | |
1684 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1685 | + "iconSize": "24px", | |
1686 | + "titleTooltip": "", | |
1687 | + "widgetStyle": {} | |
1688 | + }, | |
1689 | + "row": 0, | |
1690 | + "col": 0, | |
1691 | + "id": "3624013b-378c-f110-5eba-ae95c25a4dcc" | |
1692 | + }, | |
1693 | + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { | |
1694 | + "isSystemType": true, | |
1695 | + "bundleAlias": "cards", | |
1696 | + "typeAlias": "entities_table", | |
1697 | + "type": "latest", | |
1698 | + "title": "New widget", | |
1699 | + "image": null, | |
1700 | + "description": null, | |
1701 | + "sizeX": 7.5, | |
1702 | + "sizeY": 6.5, | |
1703 | + "config": { | |
1704 | + "timewindow": { | |
1705 | + "realtime": { | |
1706 | + "interval": 1000, | |
1707 | + "timewindowMs": 86400000 | |
1708 | + }, | |
1709 | + "aggregation": { | |
1710 | + "type": "NONE", | |
1711 | + "limit": 200 | |
1712 | + } | |
1713 | + }, | |
1714 | + "showTitle": true, | |
1715 | + "backgroundColor": "rgb(255, 255, 255)", | |
1716 | + "color": "rgba(0, 0, 0, 0.87)", | |
1717 | + "padding": "4px", | |
1718 | + "settings": { | |
1719 | + "enableSearch": true, | |
1720 | + "displayPagination": true, | |
1721 | + "defaultPageSize": 10, | |
1722 | + "defaultSortOrder": "entityLabel", | |
1723 | + "displayEntityName": false, | |
1724 | + "displayEntityType": false, | |
1725 | + "enableSelectColumnDisplay": false, | |
1726 | + "enableStickyHeader": true, | |
1727 | + "enableStickyAction": true, | |
1728 | + "entitiesTitle": "Devices", | |
1729 | + "displayEntityLabel": true, | |
1730 | + "entityLabelColumnTitle": "Device" | |
1731 | + }, | |
1732 | + "title": "New Entities table", | |
1733 | + "dropShadow": true, | |
1734 | + "enableFullscreen": true, | |
1735 | + "titleStyle": { | |
1736 | + "fontSize": "16px", | |
1737 | + "fontWeight": 400, | |
1738 | + "padding": "5px 10px 5px 10px" | |
1739 | + }, | |
1740 | + "useDashboardTimewindow": false, | |
1741 | + "showLegend": false, | |
1742 | + "datasources": [ | |
1743 | + { | |
1744 | + "type": "entity", | |
1745 | + "name": null, | |
1746 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1747 | + "filterId": "6044e198-df64-cd76-f339-696f220c4943", | |
1748 | + "dataKeys": [ | |
1749 | + { | |
1750 | + "name": "current_sw_title", | |
1751 | + "type": "timeseries", | |
1752 | + "label": "Current SW title", | |
1753 | + "color": "#2196f3", | |
1754 | + "settings": { | |
1755 | + "columnWidth": "0px", | |
1756 | + "useCellStyleFunction": false, | |
1757 | + "cellStyleFunction": "", | |
1758 | + "useCellContentFunction": false, | |
1759 | + "defaultColumnVisibility": "visible", | |
1760 | + "columnSelectionToDisplay": "enabled" | |
1761 | + }, | |
1762 | + "_hash": 0.09545533885166413, | |
1763 | + "units": null, | |
1764 | + "decimals": null, | |
1765 | + "funcBody": null, | |
1766 | + "usePostProcessing": null, | |
1767 | + "postFuncBody": null | |
1768 | + }, | |
1769 | + { | |
1770 | + "name": "current_sw_version", | |
1771 | + "type": "timeseries", | |
1772 | + "label": "Current SW version", | |
1773 | + "color": "#4caf50", | |
1774 | + "settings": { | |
1775 | + "columnWidth": "0px", | |
1776 | + "useCellStyleFunction": false, | |
1777 | + "cellStyleFunction": "", | |
1778 | + "useCellContentFunction": false, | |
1779 | + "defaultColumnVisibility": "visible", | |
1780 | + "columnSelectionToDisplay": "enabled" | |
1781 | + }, | |
1782 | + "_hash": 0.7206056602328659, | |
1783 | + "units": null, | |
1784 | + "decimals": null, | |
1785 | + "funcBody": null, | |
1786 | + "usePostProcessing": null, | |
1787 | + "postFuncBody": null | |
1788 | + }, | |
1789 | + { | |
1790 | + "name": "target_sw_title", | |
1791 | + "type": "timeseries", | |
1792 | + "label": "Target SW title", | |
1793 | + "color": "#ffc107", | |
1794 | + "settings": { | |
1795 | + "columnWidth": "0px", | |
1796 | + "useCellStyleFunction": false, | |
1797 | + "cellStyleFunction": "", | |
1798 | + "useCellContentFunction": false, | |
1799 | + "defaultColumnVisibility": "visible", | |
1800 | + "columnSelectionToDisplay": "enabled" | |
1801 | + }, | |
1802 | + "_hash": 0.9934225682766313, | |
1803 | + "units": null, | |
1804 | + "decimals": null, | |
1805 | + "funcBody": null, | |
1806 | + "usePostProcessing": null, | |
1807 | + "postFuncBody": null | |
1808 | + }, | |
1809 | + { | |
1810 | + "name": "target_sw_version", | |
1811 | + "type": "timeseries", | |
1812 | + "label": "Target SW version", | |
1813 | + "color": "#607d8b", | |
1814 | + "settings": { | |
1815 | + "columnWidth": "0px", | |
1816 | + "useCellStyleFunction": false, | |
1817 | + "cellStyleFunction": "", | |
1818 | + "useCellContentFunction": false, | |
1819 | + "cellContentFunction": "", | |
1820 | + "defaultColumnVisibility": "visible", | |
1821 | + "columnSelectionToDisplay": "enabled" | |
1822 | + }, | |
1823 | + "_hash": 0.5251724416842531, | |
1824 | + "units": null, | |
1825 | + "decimals": null, | |
1826 | + "funcBody": null, | |
1827 | + "usePostProcessing": null, | |
1828 | + "postFuncBody": null | |
1829 | + }, | |
1830 | + { | |
1831 | + "name": "target_sw_ts", | |
1832 | + "type": "timeseries", | |
1833 | + "label": "Target SW set time", | |
1834 | + "color": "#e91e63", | |
1835 | + "settings": { | |
1836 | + "columnWidth": "0px", | |
1837 | + "useCellStyleFunction": false, | |
1838 | + "cellStyleFunction": "", | |
1839 | + "useCellContentFunction": true, | |
1840 | + "defaultColumnVisibility": "visible", | |
1841 | + "columnSelectionToDisplay": "enabled", | |
1842 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1843 | + }, | |
1844 | + "_hash": 0.31823244858578237, | |
1845 | + "units": null, | |
1846 | + "decimals": null, | |
1847 | + "funcBody": null, | |
1848 | + "usePostProcessing": null, | |
1849 | + "postFuncBody": null | |
1850 | + }, | |
1851 | + { | |
1852 | + "name": "sw_state", | |
1853 | + "type": "timeseries", | |
1854 | + "label": "Progress", | |
1855 | + "color": "#9c27b0", | |
1856 | + "settings": { | |
1857 | + "columnWidth": "30%", | |
1858 | + "useCellStyleFunction": true, | |
1859 | + "useCellContentFunction": true, | |
1860 | + "defaultColumnVisibility": "visible", | |
1861 | + "columnSelectionToDisplay": "enabled", | |
1862 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1863 | + "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}" | |
1864 | + }, | |
1865 | + "_hash": 0.8174211757846257, | |
1866 | + "units": null, | |
1867 | + "decimals": null, | |
1868 | + "funcBody": null, | |
1869 | + "usePostProcessing": null, | |
1870 | + "postFuncBody": null | |
1871 | + }, | |
1872 | + { | |
1873 | + "name": "sw_state", | |
1874 | + "type": "timeseries", | |
1875 | + "label": "Status", | |
1876 | + "color": "#f44336", | |
1877 | + "settings": { | |
1878 | + "columnWidth": "130px", | |
1879 | + "useCellStyleFunction": true, | |
1880 | + "useCellContentFunction": true, | |
1881 | + "defaultColumnVisibility": "visible", | |
1882 | + "columnSelectionToDisplay": "enabled", | |
1883 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1884 | + "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>';" | |
1885 | + }, | |
1886 | + "_hash": 0.7764426948615217, | |
1887 | + "units": null, | |
1888 | + "decimals": null, | |
1889 | + "funcBody": null, | |
1890 | + "usePostProcessing": null, | |
1891 | + "postFuncBody": null | |
1892 | + }, | |
1893 | + { | |
1894 | + "name": "sw_checksum", | |
1895 | + "type": "attribute", | |
1896 | + "label": "sw_checksum", | |
1897 | + "color": "#3f51b5", | |
1898 | + "settings": { | |
1899 | + "columnWidth": "0px", | |
1900 | + "useCellStyleFunction": false, | |
1901 | + "cellStyleFunction": "", | |
1902 | + "useCellContentFunction": false, | |
1903 | + "defaultColumnVisibility": "hidden", | |
1904 | + "columnSelectionToDisplay": "disabled" | |
1905 | + }, | |
1906 | + "_hash": 0.5594087842471693, | |
1907 | + "units": null, | |
1908 | + "decimals": null, | |
1909 | + "funcBody": null, | |
1910 | + "usePostProcessing": null, | |
1911 | + "postFuncBody": null | |
1912 | + }, | |
1913 | + { | |
1914 | + "name": "sw_url", | |
1915 | + "type": "attribute", | |
1916 | + "label": "sw_url", | |
1917 | + "color": "#e91e63", | |
1918 | + "settings": { | |
1919 | + "columnWidth": "0px", | |
1920 | + "useCellStyleFunction": false, | |
1921 | + "cellStyleFunction": "", | |
1922 | + "useCellContentFunction": false, | |
1923 | + "cellContentFunction": "", | |
1924 | + "defaultColumnVisibility": "hidden", | |
1925 | + "columnSelectionToDisplay": "disabled" | |
1926 | + }, | |
1927 | + "_hash": 0.3355829384124256, | |
1928 | + "units": null, | |
1929 | + "decimals": null, | |
1930 | + "funcBody": null, | |
1931 | + "usePostProcessing": null, | |
1932 | + "postFuncBody": null | |
1933 | + } | |
1934 | + ] | |
1935 | + } | |
1936 | + ], | |
1937 | + "actions": { | |
1938 | + "actionCellButton": [ | |
1939 | + { | |
1940 | + "name": "History software update", | |
1941 | + "icon": "history", | |
1942 | + "type": "openDashboardState", | |
1943 | + "targetDashboardStateId": "device_software_history", | |
1944 | + "setEntityId": true, | |
1945 | + "stateEntityParamName": null, | |
1946 | + "openInSeparateDialog": false, | |
1947 | + "dialogTitle": "", | |
1948 | + "dialogHideDashboardToolbar": true, | |
1949 | + "dialogWidth": null, | |
1950 | + "dialogHeight": null, | |
1951 | + "openRightLayout": false, | |
1952 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1953 | + }, | |
1954 | + { | |
1955 | + "name": "Edit software", | |
1956 | + "icon": "edit", | |
1957 | + "type": "customPretty", | |
1958 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{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 *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-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>", | |
1959 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1960 | + "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 softwareId: [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 softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1961 | + "customResources": [], | |
1962 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1963 | + }, | |
1964 | + { | |
1965 | + "name": "Download software", | |
1966 | + "icon": "file_download", | |
1967 | + "type": "custom", | |
1968 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1969 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1970 | + }, | |
1971 | + { | |
1972 | + "name": "Copy checksum/URL", | |
1973 | + "icon": "content_copy", | |
1974 | + "type": "custom", | |
1975 | + "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 === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1976 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1977 | + } | |
1978 | + ] | |
1979 | + }, | |
1980 | + "showTitleIcon": false, | |
1981 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1982 | + "iconSize": "24px", | |
1983 | + "titleTooltip": "", | |
1984 | + "widgetStyle": {} | |
1985 | + }, | |
1986 | + "row": 0, | |
1987 | + "col": 0, | |
1988 | + "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176" | |
1989 | + } | |
1990 | + }, | |
1991 | + "states": { | |
1992 | + "default": { | |
1993 | + "name": "Device list", | |
1994 | + "root": true, | |
1995 | + "layouts": { | |
1996 | + "main": { | |
1997 | + "widgets": { | |
1998 | + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { | |
1999 | + "sizeX": 19, | |
2000 | + "sizeY": 12, | |
2001 | + "row": 0, | |
2002 | + "col": 0 | |
2003 | + }, | |
2004 | + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { | |
2005 | + "sizeX": 5, | |
2006 | + "sizeY": 3, | |
2007 | + "row": 0, | |
2008 | + "col": 19 | |
2009 | + }, | |
2010 | + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { | |
2011 | + "sizeX": 5, | |
2012 | + "sizeY": 3, | |
2013 | + "row": 3, | |
2014 | + "col": 19 | |
2015 | + }, | |
2016 | + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { | |
2017 | + "sizeX": 5, | |
2018 | + "sizeY": 3, | |
2019 | + "row": 9, | |
2020 | + "col": 19 | |
2021 | + }, | |
2022 | + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { | |
2023 | + "sizeX": 5, | |
2024 | + "sizeY": 3, | |
2025 | + "row": 6, | |
2026 | + "col": 19 | |
2027 | + } | |
2028 | + }, | |
2029 | + "gridSettings": { | |
2030 | + "backgroundColor": "#eeeeee", | |
2031 | + "color": "rgba(0,0,0,0.870588)", | |
2032 | + "columns": 24, | |
2033 | + "margin": 12, | |
2034 | + "backgroundSizeMode": "100%", | |
2035 | + "autoFillHeight": true, | |
2036 | + "backgroundImageUrl": null, | |
2037 | + "mobileAutoFillHeight": true, | |
2038 | + "mobileRowHeight": 70 | |
2039 | + } | |
2040 | + } | |
2041 | + } | |
2042 | + }, | |
2043 | + "device_software_history": { | |
2044 | + "name": "Software history: ${entityName}", | |
2045 | + "root": false, | |
2046 | + "layouts": { | |
2047 | + "main": { | |
2048 | + "widgets": { | |
2049 | + "100b756c-0082-6505-3ae1-3603e6deea48": { | |
2050 | + "sizeX": 24, | |
2051 | + "sizeY": 12, | |
2052 | + "row": 0, | |
2053 | + "col": 0 | |
2054 | + } | |
2055 | + }, | |
2056 | + "gridSettings": { | |
2057 | + "backgroundColor": "#eeeeee", | |
2058 | + "color": "rgba(0,0,0,0.870588)", | |
2059 | + "columns": 24, | |
2060 | + "margin": 10, | |
2061 | + "backgroundSizeMode": "100%", | |
2062 | + "autoFillHeight": true, | |
2063 | + "backgroundImageUrl": null, | |
2064 | + "mobileAutoFillHeight": false, | |
2065 | + "mobileRowHeight": 70 | |
2066 | + } | |
2067 | + } | |
2068 | + } | |
2069 | + }, | |
2070 | + "device_waiting": { | |
2071 | + "name": "Device waiting", | |
2072 | + "root": false, | |
2073 | + "layouts": { | |
2074 | + "main": { | |
2075 | + "widgets": { | |
2076 | + "21be08bb-ec90-f760-ad6f-e7678f12c401": { | |
2077 | + "sizeX": 24, | |
2078 | + "sizeY": 12, | |
2079 | + "row": 0, | |
2080 | + "col": 0 | |
2081 | + } | |
2082 | + }, | |
2083 | + "gridSettings": { | |
2084 | + "backgroundColor": "#eeeeee", | |
2085 | + "color": "rgba(0,0,0,0.870588)", | |
2086 | + "columns": 24, | |
2087 | + "margin": 10, | |
2088 | + "backgroundSizeMode": "100%", | |
2089 | + "autoFillHeight": true, | |
2090 | + "backgroundImageUrl": null, | |
2091 | + "mobileAutoFillHeight": false, | |
2092 | + "mobileRowHeight": 70 | |
2093 | + } | |
2094 | + } | |
2095 | + } | |
2096 | + }, | |
2097 | + "device_updating": { | |
2098 | + "name": "Device updating", | |
2099 | + "root": false, | |
2100 | + "layouts": { | |
2101 | + "main": { | |
2102 | + "widgets": { | |
2103 | + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { | |
2104 | + "sizeX": 24, | |
2105 | + "sizeY": 12, | |
2106 | + "row": 0, | |
2107 | + "col": 0 | |
2108 | + } | |
2109 | + }, | |
2110 | + "gridSettings": { | |
2111 | + "backgroundColor": "#eeeeee", | |
2112 | + "color": "rgba(0,0,0,0.870588)", | |
2113 | + "columns": 24, | |
2114 | + "margin": 10, | |
2115 | + "backgroundSizeMode": "100%", | |
2116 | + "autoFillHeight": true, | |
2117 | + "backgroundImageUrl": null, | |
2118 | + "mobileAutoFillHeight": false, | |
2119 | + "mobileRowHeight": 70 | |
2120 | + } | |
2121 | + } | |
2122 | + } | |
2123 | + }, | |
2124 | + "device_updated": { | |
2125 | + "name": "Device updated", | |
2126 | + "root": false, | |
2127 | + "layouts": { | |
2128 | + "main": { | |
2129 | + "widgets": { | |
2130 | + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { | |
2131 | + "sizeX": 27, | |
2132 | + "sizeY": 12, | |
2133 | + "row": 0, | |
2134 | + "col": 0 | |
2135 | + } | |
2136 | + }, | |
2137 | + "gridSettings": { | |
2138 | + "backgroundColor": "#eeeeee", | |
2139 | + "color": "rgba(0,0,0,0.870588)", | |
2140 | + "columns": 24, | |
2141 | + "margin": 10, | |
2142 | + "backgroundSizeMode": "100%", | |
2143 | + "autoFillHeight": true, | |
2144 | + "backgroundImageUrl": null, | |
2145 | + "mobileAutoFillHeight": false, | |
2146 | + "mobileRowHeight": 70 | |
2147 | + } | |
2148 | + } | |
2149 | + } | |
2150 | + }, | |
2151 | + "device_error": { | |
2152 | + "name": "Device failed", | |
2153 | + "root": false, | |
2154 | + "layouts": { | |
2155 | + "main": { | |
2156 | + "widgets": { | |
2157 | + "3624013b-378c-f110-5eba-ae95c25a4dcc": { | |
2158 | + "sizeX": 24, | |
2159 | + "sizeY": 12, | |
2160 | + "row": 0, | |
2161 | + "col": 0 | |
2162 | + } | |
2163 | + }, | |
2164 | + "gridSettings": { | |
2165 | + "backgroundColor": "#eeeeee", | |
2166 | + "color": "rgba(0,0,0,0.870588)", | |
2167 | + "columns": 24, | |
2168 | + "margin": 10, | |
2169 | + "backgroundSizeMode": "100%", | |
2170 | + "autoFillHeight": true, | |
2171 | + "backgroundImageUrl": null, | |
2172 | + "mobileAutoFillHeight": false, | |
2173 | + "mobileRowHeight": 70 | |
2174 | + } | |
2175 | + } | |
2176 | + } | |
2177 | + } | |
2178 | + }, | |
2179 | + "entityAliases": { | |
2180 | + "639da5b4-31f0-0151-6282-c37a3897b7e8": { | |
2181 | + "id": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
2182 | + "alias": "All devices", | |
2183 | + "filter": { | |
2184 | + "type": "entityType", | |
2185 | + "resolveMultiple": true, | |
2186 | + "entityType": "DEVICE" | |
2187 | + } | |
2188 | + }, | |
2189 | + "19f41c21-d9af-e666-8f50-e1748778f955": { | |
2190 | + "id": "19f41c21-d9af-e666-8f50-e1748778f955", | |
2191 | + "alias": "State entity", | |
2192 | + "filter": { | |
2193 | + "type": "stateEntity", | |
2194 | + "resolveMultiple": false, | |
2195 | + "stateEntityParamName": null, | |
2196 | + "defaultStateEntity": null | |
2197 | + } | |
2198 | + } | |
2199 | + }, | |
2200 | + "filters": { | |
2201 | + "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e": { | |
2202 | + "id": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
2203 | + "filter": "WaitingDevicesFilter", | |
2204 | + "keyFilters": [ | |
2205 | + { | |
2206 | + "key": { | |
2207 | + "type": "TIME_SERIES", | |
2208 | + "key": "sw_state" | |
2209 | + }, | |
2210 | + "valueType": "STRING", | |
2211 | + "predicates": [ | |
2212 | + { | |
2213 | + "keyFilterPredicate": { | |
2214 | + "operation": "EQUAL", | |
2215 | + "value": { | |
2216 | + "defaultValue": "QUEUED", | |
2217 | + "dynamicValue": null | |
2218 | + }, | |
2219 | + "ignoreCase": false, | |
2220 | + "type": "STRING" | |
2221 | + }, | |
2222 | + "userInfo": { | |
2223 | + "editable": true, | |
2224 | + "label": "", | |
2225 | + "autogeneratedLabel": true, | |
2226 | + "order": 0 | |
2227 | + } | |
2228 | + } | |
2229 | + ] | |
2230 | + } | |
2231 | + ], | |
2232 | + "editable": false | |
2233 | + }, | |
2234 | + "579f0468-9ce9-7e3e-b34c-88dd3de59897": { | |
2235 | + "id": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
2236 | + "filter": "UpdatingDevicesFilter", | |
2237 | + "keyFilters": [ | |
2238 | + { | |
2239 | + "key": { | |
2240 | + "type": "TIME_SERIES", | |
2241 | + "key": "sw_state" | |
2242 | + }, | |
2243 | + "valueType": "STRING", | |
2244 | + "predicates": [ | |
2245 | + { | |
2246 | + "keyFilterPredicate": { | |
2247 | + "operation": "OR", | |
2248 | + "predicates": [ | |
2249 | + { | |
2250 | + "keyFilterPredicate": { | |
2251 | + "operation": "EQUAL", | |
2252 | + "value": { | |
2253 | + "defaultValue": "INITIATED", | |
2254 | + "dynamicValue": null | |
2255 | + }, | |
2256 | + "ignoreCase": false, | |
2257 | + "type": "STRING" | |
2258 | + }, | |
2259 | + "userInfo": { | |
2260 | + "editable": false, | |
2261 | + "label": "sw_state equel", | |
2262 | + "autogeneratedLabel": true, | |
2263 | + "order": 0 | |
2264 | + } | |
2265 | + }, | |
2266 | + { | |
2267 | + "keyFilterPredicate": { | |
2268 | + "operation": "EQUAL", | |
2269 | + "value": { | |
2270 | + "defaultValue": "DOWNLOADING", | |
2271 | + "dynamicValue": null | |
2272 | + }, | |
2273 | + "ignoreCase": false, | |
2274 | + "type": "STRING" | |
2275 | + }, | |
2276 | + "userInfo": { | |
2277 | + "editable": false, | |
2278 | + "label": "sw_state equal", | |
2279 | + "autogeneratedLabel": true, | |
2280 | + "order": 0 | |
2281 | + } | |
2282 | + }, | |
2283 | + { | |
2284 | + "keyFilterPredicate": { | |
2285 | + "operation": "EQUAL", | |
2286 | + "value": { | |
2287 | + "defaultValue": "DOWNLOADED", | |
2288 | + "dynamicValue": null | |
2289 | + }, | |
2290 | + "ignoreCase": false, | |
2291 | + "type": "STRING" | |
2292 | + }, | |
2293 | + "userInfo": { | |
2294 | + "editable": false, | |
2295 | + "label": "sw_state equal", | |
2296 | + "autogeneratedLabel": true, | |
2297 | + "order": 0 | |
2298 | + } | |
2299 | + }, | |
2300 | + { | |
2301 | + "keyFilterPredicate": { | |
2302 | + "operation": "EQUAL", | |
2303 | + "value": { | |
2304 | + "defaultValue": "VERIFIED", | |
2305 | + "dynamicValue": null | |
2306 | + }, | |
2307 | + "ignoreCase": false, | |
2308 | + "type": "STRING" | |
2309 | + }, | |
2310 | + "userInfo": { | |
2311 | + "editable": false, | |
2312 | + "label": "sw_state equal", | |
2313 | + "autogeneratedLabel": true, | |
2314 | + "order": 0 | |
2315 | + } | |
2316 | + }, | |
2317 | + { | |
2318 | + "keyFilterPredicate": { | |
2319 | + "operation": "EQUAL", | |
2320 | + "value": { | |
2321 | + "defaultValue": "UPDATING", | |
2322 | + "dynamicValue": null | |
2323 | + }, | |
2324 | + "ignoreCase": false, | |
2325 | + "type": "STRING" | |
2326 | + }, | |
2327 | + "userInfo": { | |
2328 | + "editable": false, | |
2329 | + "label": "sw_state equal", | |
2330 | + "autogeneratedLabel": true, | |
2331 | + "order": 0 | |
2332 | + } | |
2333 | + } | |
2334 | + ], | |
2335 | + "type": "COMPLEX" | |
2336 | + }, | |
2337 | + "userInfo": { | |
2338 | + "editable": true, | |
2339 | + "label": "", | |
2340 | + "autogeneratedLabel": true, | |
2341 | + "order": 0 | |
2342 | + } | |
2343 | + } | |
2344 | + ] | |
2345 | + } | |
2346 | + ], | |
2347 | + "editable": false | |
2348 | + }, | |
2349 | + "6044e198-df64-cd76-f339-696f220c4943": { | |
2350 | + "id": "6044e198-df64-cd76-f339-696f220c4943", | |
2351 | + "filter": "UpdetedDevicesFilter", | |
2352 | + "keyFilters": [ | |
2353 | + { | |
2354 | + "key": { | |
2355 | + "type": "TIME_SERIES", | |
2356 | + "key": "sw_state" | |
2357 | + }, | |
2358 | + "valueType": "STRING", | |
2359 | + "predicates": [ | |
2360 | + { | |
2361 | + "keyFilterPredicate": { | |
2362 | + "operation": "EQUAL", | |
2363 | + "value": { | |
2364 | + "defaultValue": "UPDATED", | |
2365 | + "dynamicValue": null | |
2366 | + }, | |
2367 | + "ignoreCase": false, | |
2368 | + "type": "STRING" | |
2369 | + }, | |
2370 | + "userInfo": { | |
2371 | + "editable": true, | |
2372 | + "label": "", | |
2373 | + "autogeneratedLabel": true, | |
2374 | + "order": 0 | |
2375 | + } | |
2376 | + } | |
2377 | + ] | |
2378 | + } | |
2379 | + ], | |
2380 | + "editable": false | |
2381 | + }, | |
2382 | + "bdbc6ea1-95a7-3912-341a-58dc7704a00f": { | |
2383 | + "id": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
2384 | + "filter": "FailedDevicesFilter", | |
2385 | + "keyFilters": [ | |
2386 | + { | |
2387 | + "key": { | |
2388 | + "type": "TIME_SERIES", | |
2389 | + "key": "sw_state" | |
2390 | + }, | |
2391 | + "valueType": "STRING", | |
2392 | + "predicates": [ | |
2393 | + { | |
2394 | + "keyFilterPredicate": { | |
2395 | + "operation": "EQUAL", | |
2396 | + "value": { | |
2397 | + "defaultValue": "FAILED", | |
2398 | + "dynamicValue": null | |
2399 | + }, | |
2400 | + "ignoreCase": false, | |
2401 | + "type": "STRING" | |
2402 | + }, | |
2403 | + "userInfo": { | |
2404 | + "editable": true, | |
2405 | + "label": "", | |
2406 | + "autogeneratedLabel": true, | |
2407 | + "order": 0 | |
2408 | + } | |
2409 | + } | |
2410 | + ] | |
2411 | + } | |
2412 | + ], | |
2413 | + "editable": false | |
2414 | + }, | |
2415 | + "8fdb88d0-50ac-2232-fdb7-69c30c16544e": { | |
2416 | + "id": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", | |
2417 | + "filter": "DeviceSearch", | |
2418 | + "keyFilters": [ | |
2419 | + { | |
2420 | + "key": { | |
2421 | + "type": "ENTITY_FIELD", | |
2422 | + "key": "name" | |
2423 | + }, | |
2424 | + "valueType": "STRING", | |
2425 | + "predicates": [ | |
2426 | + { | |
2427 | + "keyFilterPredicate": { | |
2428 | + "operation": "CONTAINS", | |
2429 | + "value": { | |
2430 | + "defaultValue": "" | |
2431 | + }, | |
2432 | + "ignoreCase": true, | |
2433 | + "type": "STRING" | |
2434 | + }, | |
2435 | + "userInfo": { | |
2436 | + "editable": true, | |
2437 | + "label": "Device name", | |
2438 | + "autogeneratedLabel": false, | |
2439 | + "order": 0 | |
2440 | + } | |
2441 | + } | |
2442 | + ] | |
2443 | + } | |
2444 | + ], | |
2445 | + "editable": true | |
2446 | + } | |
2447 | + }, | |
2448 | + "timewindow": { | |
2449 | + "displayValue": "", | |
2450 | + "hideInterval": false, | |
2451 | + "hideAggregation": false, | |
2452 | + "hideAggInterval": false, | |
2453 | + "hideTimezone": false, | |
2454 | + "selectedTab": 0, | |
2455 | + "realtime": { | |
2456 | + "realtimeType": 0, | |
2457 | + "interval": 1000, | |
2458 | + "timewindowMs": 60000, | |
2459 | + "quickInterval": "CURRENT_DAY" | |
2460 | + }, | |
2461 | + "history": { | |
2462 | + "historyType": 0, | |
2463 | + "interval": 1000, | |
2464 | + "timewindowMs": 60000, | |
2465 | + "fixedTimewindow": { | |
2466 | + "startTimeMs": 1618998609030, | |
2467 | + "endTimeMs": 1619085009030 | |
2468 | + }, | |
2469 | + "quickInterval": "CURRENT_DAY" | |
2470 | + }, | |
2471 | + "aggregation": { | |
2472 | + "type": "AVG", | |
2473 | + "limit": 25000 | |
2474 | + } | |
2475 | + }, | |
2476 | + "settings": { | |
2477 | + "stateControllerId": "entity", | |
2478 | + "showTitle": false, | |
2479 | + "showDashboardsSelect": false, | |
2480 | + "showEntitiesSelect": false, | |
2481 | + "showDashboardTimewindow": true, | |
2482 | + "showDashboardExport": false, | |
2483 | + "toolbarAlwaysOpen": true, | |
2484 | + "titleColor": "rgba(0,0,0,0.870588)", | |
2485 | + "showFilters": true, | |
2486 | + "showDashboardLogo": false, | |
2487 | + "dashboardLogoUrl": null, | |
2488 | + "showUpdateDashboardImage": false | |
2489 | + } | |
2490 | + }, | |
2491 | + "name": "Software" | |
2492 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -28,7 +28,7 @@ |
28 | 28 | "alias": "html_card", |
29 | 29 | "name": "HTML Card", |
30 | 30 | "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAATFSURBVHja7d3tUxNXFMdx/u7vEkAJsKQK6bilKaBV1Eh5GGNaSyq0WodhRuqMtQJqW5UHCwKGMRJiQpL99cWGEGaaTjtjgaTnvNpz7mYnn8neu/duXtwWFbdWlxs8Vrf21VJcy5TU4FHKrBVbtjJqgshstayWmgFSWm1ZVlPEskEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAG+T9ACrlcLjjyc7lcWflcbfhBfnByOZfL5cpSKZfL+f/2G+WAJ/8dJAHh4GgFeK4BaiOjMYDtyskzAIvSQ+BtI0JmKif3NRBk3PM8Lwwhz/M8LxtAzgf30RoNBJEkjUOk0j4GwJok6XaDQ8LwtSSVXVrP1of4v1w978bu5YNh4fHlfnfwfpDo15Hevm/eHkCyP8Tc/rGV44d8FaLbl/QCRrrqQopXg251bkdSdjBIIpuSNAtAbwXyqgsA595Hh3Sm0+l0Or1QDzJxLWiYhCftdSFJoKMHiPnyvwTOhIHeD9KGA3SfIYBkOsGJtIPz/GNDauIvIfEFGJdKnZwtOPUgOw4kfT0E1vQMmPb12IEZaRJCz1RKBpBJCG+ocAVixw25XArTUdACTBbqdvY56MhLisK8xisDXRyiUi+MStoDnqjUCT9Keg3Oh48MCcXj8Xg8PlwPMqQE/Kzr8HK3LiQBFyozhaI8uClJ8+AUi8CD6qi1DYymUqkksH7Mnf0zLcNIvo2In64LGYXhauLCHUlaAnb3gIUq5FXN7//qmCH98s8RmoXv9KYuZBI+ryZ9cFsKzsvngUdVyAowcDGIjWOGRKRpaINNrdWF3IUeX9LS/PymLsMlSZqCLikM01XIe+CnE3ogRqRtAE9arQtZCVqynbCoWXBWpfddMCaNgLsnLQSj1gVwM5Je3/RPAKIBYPYIJOp5nud5dw8uFIPQxO1eCOf1oRs6Jm71QOtG0FMiydFQAHkKnJlIjTgkTwIyB87uEUglbh5c6G13UGhdkvSyPUicB5J0HYBQe/Bkv+tUPnr/JCDZVq7obyF6d6MNnC+COdTmSCs4A78Hs5dkG3zyYqAy13oec8CJ/XZ6l7qFzfW9apJ/80dNsr5V2yNyGxt7tmY3iEEMYhCDGOTEIJnD6YtBDGKQpocsDrvR5K4kyX96PeoOzu5Lysbj8b2Zvv7SkepphgwBcD4vqRAPVlXR91IauASUVAhWgny6e7ohtEdCwcpdCaDTBYYDCED5sHrxdEOuFZRx4Ya06cC3ZS05sKI04EzM3demA1NlLTqweuo7ewI86U7lHdYQzCgNzEnSHXB9SYPw/amHpKBPGoFzqVQqFYUxpYEX0pHqeINAav4wvXoI+eyweq1BIDHorry+nT6E1FRnGgQyCtFqUxVyo7baGJBHlVG4mFivgRxU9xMbjQIpR4HBqYRLz84hpBwFhqYSLu5Og0C07VZ6dXTvEHKk2iAQZZOdQHcqX3NrSdlb1WrjLHXL26/T/j+s2prdIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMY5PRAmmaD4ObYsvndVst+c2yiXWppjm3NS/oTe0OjFEeU1MMAAAAASUVORK5CYII=", |
31 | - "description": "Useful to inject custom HTML code. Designed to displays static information only.", | |
31 | + "description": "Useful to inject custom HTML code. Designed to display static information only.", | |
32 | 32 | "descriptor": { |
33 | 33 | "type": "static", |
34 | 34 | "sizeX": 7.5, | ... | ... |
... | ... | @@ -10,16 +10,16 @@ |
10 | 10 | "alias": "rpc_debug_terminal", |
11 | 11 | "name": "RPC debug terminal", |
12 | 12 | "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAWcklEQVR42u2dB5QWRZeGi6QgklFUDJhFMCJGRFHMomMAMStiBvUXRTFgRjGLERMoiIBZ1GVXdNcF4yIe5agYdvVnfkAwIAoiIs4+/72na3u+NN8wAzsD73vmzOnur7rCrbfuvV3VfSuUlZXNnDnzmGOOadKkSRCEKgAKlZSUzJgxA1IFWNWyZUsJRagutGjRAlIFdJVkIVQvevbsGWQBhWpH06ZNJQRBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEITlRK9evc5KwAdAnTt3rlevXvz1oIMOir+efvrp3bp1a9WqVUYOderUOfDAAy+77LJLL7308MMPX2ONNYop99577+ULyaOPPrpwsvfff59kG264Ya0W8uDBg6+55pqq5PDRRx8hh3XXXbfWtPnzzz8vK4/PPvtsjz328F9ffvnljF9///33q6++Ot6+/vrrv/fee+kEX3zxxeabb15biHXqqafecsstK5S4a6655pIlS/7444+11lprtSPWEUcc0alTp/333//hhx/mdNasWa54nFj9+vXj19122+2MM8747rvvuHLiiSe6rnrrrbc4feyxxzp27Aifbr/9dk6nTp1aW4j10ksvkf8uu+yyQoVM/piC1VFjbbbZZtGu+RUXhBMLgxjTQymuvPrqqxzvs88+HH/44Ydps/jJJ59wccstt8wua+ONN7722mvvuOOOQw89NJtYlAIv7777buwp+aSJtd122w0YMIBbUDDRUjMYsL8bbbSRn+63336ctm/f3k/XXnvtiy66iFsw4htssAE/MWzSlWncuDEXUc/kT7L4cXn9+vVxD+65557bbrttr732iunJ7dxzz91mm22o4QEHHECIA27v0aMHgkKFDxs2jIsk23PPPWng0KFD995773jv3wx+3Ldv34svvrh58+a06L777jv77LMbNGiQtgBcf+CBB8hz6623XnWIBd59990CxOrSpQtX3n77bY6HDBnCMfJNZ4gfRl9uuummGQWhz3788UfS//nnn/z/4Ycf0sR6/PHHOcVkLF26lAMkmybW3LlzMSVuaseOHes/jRkzhlPI7aeuLI8//niOGzVq5PxetmzZX3/9VVpayvFdd92Vrs86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", |
13 | - "description": "Allows to send any RPC command using it's name and parameters to device. Useful for debug.", | |
13 | + "description": "Allows to send any RPC command using its name and parameters to device. Useful for debug.", | |
14 | 14 | "descriptor": { |
15 | 15 | "type": "rpc", |
16 | 16 | "sizeX": 9.5, |
17 | 17 | "sizeY": 5.5, |
18 | 18 | "resources": [], |
19 | 19 | "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>", |
20 | - "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n", | |
21 | - "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = utils.guid();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n \n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' <method> [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n \nself.onDestroy = function() {\n}", | |
22 | - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\"\n ]\n}", | |
20 | + "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", | |
21 | + "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\nvar useRowStyleFunction = false;\nvar styleObj = {};\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n if (self.ctx.settings.useRowStyleFunction && self.ctx.settings.rowStyleFunction) {\n try {\n var style = self.ctx.settings.rowStyleFunction;\n styleObj = JSON.parse(style);\n if ((typeof styleObj !== \"object\")) {\n styleObj = null;\n throw new URIError(`${style === null ? 'null' : typeof style} instead of style object`);\n }\n else if (typeof styleObj === \"object\" && (typeof styleObj.length) === \"number\") {\n styleObj = null;\n throw new URIError('Array instead of style object');\n }\n }\n catch (e) {\n console.log(`Row style function in widget ` +\n `returns '${e}'. Please check your row style function.`); \n }\n useRowStyleFunction = self.ctx.settings.useRowStyleFunction;\n \n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n \n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (styleObj && styleObj !== null) {\n terminal.css(styleObj);\n }\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' <method> [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}", | |
22 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", | |
23 | 23 | "dataKeySettingsSchema": "{}\n", |
24 | 24 | "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
25 | 25 | } |
... | ... | @@ -28,7 +28,7 @@ |
28 | 28 | "alias": "rpc_remote_shell", |
29 | 29 | "name": "RPC remote shell", |
30 | 30 | "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAU6klEQVR42u2dCbSW0xrHtyZDOpVkyFCKIplCZpLMIVJIhsgYmSUakLniInOGyFRXaCn3ilws89Q1RrpRSq4k15Tx3N96Ht9e73m/4Xzn9EVH//8666z97u99997v3v/9PM8e3meH8vLyWbNmdevWrUGDBkEQFgNQqGvXrtOmTYNUAVatssoqqhShVGjcuDGkCsgq1YVQWnTv3j1IAwolR1lZmSpBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEAShmujRo8fxGfAB0NZbb127du3465577hl/7d2796677tqkSZNUCsstt9wee+zRv3//c845p0uXLvXq1ftLVtQ222zDJ50PP/xwlZ7abbfdeOruu+8mXKtWLcKzZ89eJoj1/vvvl1fEe++9t9122/mvEyZMSP26aNGiQYMGxcfXXHPNl156KXnDBx980KpVq6Xh1aD7FVdcsdVWW4lYfxqx9t9//y233JJauPXWW7mcM2eOCx4n1imnnMKv1Oyxxx47b948Yg4//HCXVc888wyXt99+e7t27eDT8OHDuXzttdeWhlcbOnQohTnmmGNErD+NWC1btox6zWPQiZFYKMR4P5QiZuLEiYR32WUXwq+//npSLb711ltEbrDBBqmMDjnkENTluuuue+mllx533HEe2axZs/POO++mm24666yzVl11VY9s27Ytd3bo0GHvvfe+/PLLhw0btummmxJ/0EEHXXfddRdddFGbNm2SKcPpCy+8kET69evXqFEjj6QzPPXUU5Rk3LhxJ554Yry5c+fOJHjttdfisIDS5qyTpk2bnn322eR1/vnnR+kbiUV2Q4YMufLKK7fffvvkU+uss86AAQMoxhlnnBEdIIhYLWPMiy++WIBYO+64IzHPP/884csuu4wwJEgmiB2GQbbeeuulMpo8eTI3z5w5k/8kS8xOO+20cOFCLr/77jv+z507t3nz5sT37NnTpSb/f/75Z/5///33Dz74IIGffvrJ74/pQ5pffvmFyB9++MGbbf311yceLxcew81Tp071m2l1T8TTueOOO7IrZOONN/7qq6/IlxTQ+9y57777RmKRPsl6qX799VdeNhLom2++ie+CH4S11lpLxPqd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", |
31 | - "description": "Allows to send emulate remote shell. Requires custom implementation on the target device to work properly.", | |
31 | + "description": "Allows to emulate remote shell. Requires custom implementation on the target device to work properly.", | |
32 | 32 | "descriptor": { |
33 | 33 | "type": "rpc", |
34 | 34 | "sizeX": 9.5, | ... | ... |
... | ... | @@ -100,7 +100,7 @@ |
100 | 100 | "alias": "lcd_bar_gauge", |
101 | 101 | "name": "LCD bar gauge", |
102 | 102 | "image": "data:image/png;base64,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", |
103 | - "description": "Preconfigured gauge to display any value reading as an bar. Allows to configure value range, gradient colors and other settings.", | |
103 | + "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors and other settings.", | |
104 | 104 | "descriptor": { |
105 | 105 | "type": "latest", |
106 | 106 | "sizeX": 2, | ... | ... |
... | ... | @@ -10,7 +10,7 @@ |
10 | 10 | "alias": "device_admin_table", |
11 | 11 | "name": "Device admin table", |
12 | 12 | "image": "data:image/png;base64,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", |
13 | - "description": "Customized entity table widget with preconfigured actions to create update and delete devices.", | |
13 | + "description": "Customized entity table widget with preconfigured actions to create, update and delete devices.", | |
14 | 14 | "descriptor": { |
15 | 15 | "type": "latest", |
16 | 16 | "sizeX": 7.5, |
... | ... | @@ -28,7 +28,7 @@ |
28 | 28 | "alias": "asset_admin_table", |
29 | 29 | "name": "Asset admin table", |
30 | 30 | "image": "data:image/png;base64,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", |
31 | - "description": "Customized entity table widget with preconfigured actions to create update and delete assets.", | |
31 | + "description": "Customized entity table widget with preconfigured actions to create, update and delete assets.", | |
32 | 32 | "descriptor": { |
33 | 33 | "type": "latest", |
34 | 34 | "sizeX": 7.5, | ... | ... |
... | ... | @@ -10,7 +10,7 @@ |
10 | 10 | "alias": "gateway_configuration", |
11 | 11 | "name": "Gateway Configuration", |
12 | 12 | "image": "data:image/png;base64,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", |
13 | - "description": "Allows to define widget gateway configuration for a single selected device.", | |
13 | + "description": "Allows to define gateway configuration for a single selected device.", | |
14 | 14 | "descriptor": { |
15 | 15 | "type": "static", |
16 | 16 | "sizeX": 8, | ... | ... |
... | ... | @@ -28,7 +28,7 @@ |
28 | 28 | "alias": "update_multiple_attributes", |
29 | 29 | "name": "Update Multiple Attributes", |
30 | 30 | "image": "data:image/png;base64,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", |
31 | - "description": "Allows to create an input form and set values for multiple attributes simultaniously.", | |
31 | + "description": "Allows to create an input form and set values for multiple attributes simultaneously.", | |
32 | 32 | "descriptor": { |
33 | 33 | "type": "latest", |
34 | 34 | "sizeX": 7.5, | ... | ... |
... | ... | @@ -46,7 +46,7 @@ |
46 | 46 | "alias": "route_map", |
47 | 47 | "name": "Route Map", |
48 | 48 | "image": "data:image/png;base64,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", |
49 | - "description": "TTrip animation on the Google maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", | |
49 | + "description": "Trip animation on the Google maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", | |
50 | 50 | "descriptor": { |
51 | 51 | "type": "timeseries", |
52 | 52 | "sizeX": 8.5, | ... | ... |
... | ... | @@ -59,14 +59,15 @@ CREATE TABLE IF NOT EXISTS resource ( |
59 | 59 | CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) |
60 | 60 | ); |
61 | 61 | |
62 | -CREATE TABLE IF NOT EXISTS firmware ( | |
63 | - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, | |
62 | +CREATE TABLE IF NOT EXISTS ota_package ( | |
63 | + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, | |
64 | 64 | created_time bigint NOT NULL, |
65 | 65 | tenant_id uuid NOT NULL, |
66 | 66 | device_profile_id uuid, |
67 | 67 | type varchar(32) NOT NULL, |
68 | 68 | title varchar(255) NOT NULL, |
69 | 69 | version varchar(255) NOT NULL, |
70 | + url varchar(255), | |
70 | 71 | file_name varchar(255), |
71 | 72 | content_type varchar(255), |
72 | 73 | checksum_algorithm varchar(32), |
... | ... | @@ -75,7 +76,68 @@ CREATE TABLE IF NOT EXISTS firmware ( |
75 | 76 | data_size bigint, |
76 | 77 | additional_info varchar, |
77 | 78 | search_text varchar(255), |
78 | - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) | |
79 | + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) | |
80 | +); | |
81 | + | |
82 | +CREATE TABLE IF NOT EXISTS oauth2_params ( | |
83 | + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, | |
84 | + enabled boolean, | |
85 | + tenant_id uuid, | |
86 | + created_time bigint NOT NULL | |
87 | +); | |
88 | + | |
89 | +CREATE TABLE IF NOT EXISTS oauth2_registration ( | |
90 | + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, | |
91 | + oauth2_params_id uuid NOT NULL, | |
92 | + created_time bigint NOT NULL, | |
93 | + additional_info varchar, | |
94 | + client_id varchar(255), | |
95 | + client_secret varchar(255), | |
96 | + authorization_uri varchar(255), | |
97 | + token_uri varchar(255), | |
98 | + scope varchar(255), | |
99 | + user_info_uri varchar(255), | |
100 | + user_name_attribute_name varchar(255), | |
101 | + jwk_set_uri varchar(255), | |
102 | + client_authentication_method varchar(255), | |
103 | + login_button_label varchar(255), | |
104 | + login_button_icon varchar(255), | |
105 | + allow_user_creation boolean, | |
106 | + activate_user boolean, | |
107 | + type varchar(31), | |
108 | + basic_email_attribute_key varchar(31), | |
109 | + basic_first_name_attribute_key varchar(31), | |
110 | + basic_last_name_attribute_key varchar(31), | |
111 | + basic_tenant_name_strategy varchar(31), | |
112 | + basic_tenant_name_pattern varchar(255), | |
113 | + basic_customer_name_pattern varchar(255), | |
114 | + basic_default_dashboard_name varchar(255), | |
115 | + basic_always_full_screen boolean, | |
116 | + custom_url varchar(255), | |
117 | + custom_username varchar(255), | |
118 | + custom_password varchar(255), | |
119 | + custom_send_token boolean, | |
120 | + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE | |
121 | +); | |
122 | + | |
123 | +CREATE TABLE IF NOT EXISTS oauth2_domain ( | |
124 | + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, | |
125 | + oauth2_params_id uuid NOT NULL, | |
126 | + created_time bigint NOT NULL, | |
127 | + domain_name varchar(255), | |
128 | + domain_scheme varchar(31), | |
129 | + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, | |
130 | + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) | |
131 | +); | |
132 | + | |
133 | +CREATE TABLE IF NOT EXISTS oauth2_mobile ( | |
134 | + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, | |
135 | + oauth2_params_id uuid NOT NULL, | |
136 | + created_time bigint NOT NULL, | |
137 | + pkg_name varchar(255), | |
138 | + callback_url_scheme varchar(255), | |
139 | + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, | |
140 | + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) | |
79 | 141 | ); |
80 | 142 | |
81 | 143 | ALTER TABLE dashboard |
... | ... | @@ -101,13 +163,13 @@ DO $$ |
101 | 163 | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN |
102 | 164 | ALTER TABLE device_profile |
103 | 165 | ADD CONSTRAINT fk_firmware_device_profile |
104 | - FOREIGN KEY (firmware_id) REFERENCES firmware(id); | |
166 | + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); | |
105 | 167 | END IF; |
106 | 168 | |
107 | 169 | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN |
108 | 170 | ALTER TABLE device_profile |
109 | 171 | ADD CONSTRAINT fk_software_device_profile |
110 | - FOREIGN KEY (firmware_id) REFERENCES firmware(id); | |
172 | + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); | |
111 | 173 | END IF; |
112 | 174 | |
113 | 175 | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN |
... | ... | @@ -119,13 +181,13 @@ DO $$ |
119 | 181 | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN |
120 | 182 | ALTER TABLE device |
121 | 183 | ADD CONSTRAINT fk_firmware_device |
122 | - FOREIGN KEY (firmware_id) REFERENCES firmware(id); | |
184 | + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); | |
123 | 185 | END IF; |
124 | 186 | |
125 | 187 | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN |
126 | 188 | ALTER TABLE device |
127 | 189 | ADD CONSTRAINT fk_software_device |
128 | - FOREIGN KEY (firmware_id) REFERENCES firmware(id); | |
190 | + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); | |
129 | 191 | END IF; |
130 | 192 | END; |
131 | 193 | $$; | ... | ... |
... | ... | @@ -60,7 +60,9 @@ import org.thingsboard.server.dao.edge.EdgeService; |
60 | 60 | import org.thingsboard.server.dao.entityview.EntityViewService; |
61 | 61 | import org.thingsboard.server.dao.event.EventService; |
62 | 62 | import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; |
63 | +import org.thingsboard.server.dao.ota.OtaPackageService; | |
63 | 64 | import org.thingsboard.server.dao.relation.RelationService; |
65 | +import org.thingsboard.server.dao.resource.ResourceService; | |
64 | 66 | import org.thingsboard.server.dao.rule.RuleChainService; |
65 | 67 | import org.thingsboard.server.dao.rule.RuleNodeStateService; |
66 | 68 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
... | ... | @@ -311,6 +313,14 @@ public class ActorSystemContext { |
311 | 313 | @Autowired(required = false) |
312 | 314 | @Getter private EdgeRpcService edgeRpcService; |
313 | 315 | |
316 | + @Lazy | |
317 | + @Autowired(required = false) | |
318 | + @Getter private ResourceService resourceService; | |
319 | + | |
320 | + @Lazy | |
321 | + @Autowired(required = false) | |
322 | + @Getter private OtaPackageService otaPackageService; | |
323 | + | |
314 | 324 | @Value("${actors.session.max_concurrent_sessions_per_device:1}") |
315 | 325 | @Getter |
316 | 326 | private long maxConcurrentSessionsPerDevice; | ... | ... |
... | ... | @@ -192,6 +192,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
192 | 192 | syncSessionSet.add(key); |
193 | 193 | } |
194 | 194 | }); |
195 | + log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions); | |
195 | 196 | syncSessionSet.forEach(rpcSubscriptions::remove); |
196 | 197 | } |
197 | 198 | |
... | ... | @@ -454,7 +455,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
454 | 455 | } else { |
455 | 456 | SessionInfoMetaData sessionMD = sessions.get(sessionId); |
456 | 457 | if (sessionMD == null) { |
457 | - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); | |
458 | + sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); | |
458 | 459 | } |
459 | 460 | sessionMD.setSubscribedToAttributes(true); |
460 | 461 | log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); |
... | ... | @@ -475,7 +476,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
475 | 476 | } else { |
476 | 477 | SessionInfoMetaData sessionMD = sessions.get(sessionId); |
477 | 478 | if (sessionMD == null) { |
478 | - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); | |
479 | + sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); | |
479 | 480 | } |
480 | 481 | sessionMD.setSubscribedToRPC(true); |
481 | 482 | log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); | ... | ... |
... | ... | @@ -69,7 +69,9 @@ import org.thingsboard.server.dao.edge.EdgeService; |
69 | 69 | import org.thingsboard.server.dao.entityview.EntityViewService; |
70 | 70 | import org.thingsboard.server.dao.nosql.CassandraStatementTask; |
71 | 71 | import org.thingsboard.server.dao.nosql.TbResultSetFuture; |
72 | +import org.thingsboard.server.dao.ota.OtaPackageService; | |
72 | 73 | import org.thingsboard.server.dao.relation.RelationService; |
74 | +import org.thingsboard.server.dao.resource.ResourceService; | |
73 | 75 | import org.thingsboard.server.dao.rule.RuleChainService; |
74 | 76 | import org.thingsboard.server.dao.tenant.TenantService; |
75 | 77 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
... | ... | @@ -487,6 +489,16 @@ class DefaultTbContext implements TbContext { |
487 | 489 | } |
488 | 490 | |
489 | 491 | @Override |
492 | + public ResourceService getResourceService() { | |
493 | + return mainCtx.getResourceService(); | |
494 | + } | |
495 | + | |
496 | + @Override | |
497 | + public OtaPackageService getOtaPackageService() { | |
498 | + return mainCtx.getOtaPackageService(); | |
499 | + } | |
500 | + | |
501 | + @Override | |
490 | 502 | public RuleEngineDeviceProfileCache getDeviceProfileCache() { |
491 | 503 | return mainCtx.getDeviceProfileCache(); |
492 | 504 | } | ... | ... |
... | ... | @@ -37,6 +37,8 @@ import org.springframework.util.StringUtils; |
37 | 37 | import org.springframework.web.util.UriComponents; |
38 | 38 | import org.springframework.web.util.UriComponentsBuilder; |
39 | 39 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
40 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
41 | +import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; | |
40 | 42 | import org.thingsboard.server.utils.MiscUtils; |
41 | 43 | |
42 | 44 | import javax.servlet.http.HttpServletRequest; |
... | ... | @@ -46,12 +48,13 @@ import java.security.NoSuchAlgorithmException; |
46 | 48 | import java.util.Base64; |
47 | 49 | import java.util.HashMap; |
48 | 50 | import java.util.Map; |
51 | +import java.util.UUID; | |
49 | 52 | |
50 | 53 | @Service |
51 | 54 | @Slf4j |
52 | 55 | public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { |
53 | - public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; | |
54 | - public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; | |
56 | + private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; | |
57 | + private static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; | |
55 | 58 | private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; |
56 | 59 | private static final char PATH_DELIMITER = '/'; |
57 | 60 | |
... | ... | @@ -63,6 +66,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
63 | 66 | @Autowired |
64 | 67 | private ClientRegistrationRepository clientRegistrationRepository; |
65 | 68 | |
69 | + @Autowired | |
70 | + private OAuth2Service oAuth2Service; | |
71 | + | |
66 | 72 | @Autowired(required = false) |
67 | 73 | private OAuth2Configuration oauth2Configuration; |
68 | 74 | |
... | ... | @@ -71,7 +77,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
71 | 77 | public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { |
72 | 78 | String registrationId = this.resolveRegistrationId(request); |
73 | 79 | String redirectUriAction = getAction(request, "login"); |
74 | - return resolve(request, registrationId, redirectUriAction); | |
80 | + String appPackage = getAppPackage(request); | |
81 | + return resolve(request, registrationId, redirectUriAction, appPackage); | |
75 | 82 | } |
76 | 83 | |
77 | 84 | @Override |
... | ... | @@ -80,7 +87,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
80 | 87 | return null; |
81 | 88 | } |
82 | 89 | String redirectUriAction = getAction(request, "authorize"); |
83 | - return resolve(request, registrationId, redirectUriAction); | |
90 | + String appPackage = getAppPackage(request); | |
91 | + return resolve(request, registrationId, redirectUriAction, appPackage); | |
84 | 92 | } |
85 | 93 | |
86 | 94 | private String getAction(HttpServletRequest request, String defaultAction) { |
... | ... | @@ -91,8 +99,12 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
91 | 99 | return action; |
92 | 100 | } |
93 | 101 | |
102 | + private String getAppPackage(HttpServletRequest request) { | |
103 | + return request.getParameter("pkg"); | |
104 | + } | |
105 | + | |
94 | 106 | @SuppressWarnings("deprecation") |
95 | - private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { | |
107 | + private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage) { | |
96 | 108 | if (registrationId == null) { |
97 | 109 | return null; |
98 | 110 | } |
... | ... | @@ -104,6 +116,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
104 | 116 | |
105 | 117 | Map<String, Object> attributes = new HashMap<>(); |
106 | 118 | attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); |
119 | + if (!StringUtils.isEmpty(appPackage)) { | |
120 | + String callbackUrlScheme = this.oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), appPackage); | |
121 | + if (StringUtils.isEmpty(callbackUrlScheme)) { | |
122 | + throw new IllegalArgumentException("Invalid package: " + appPackage + ". No package info found for Client Registration."); | |
123 | + } else { | |
124 | + attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme); | |
125 | + } | |
126 | + } | |
107 | 127 | |
108 | 128 | OAuth2AuthorizationRequest.Builder builder; |
109 | 129 | if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { | ... | ... |
... | ... | @@ -110,8 +110,11 @@ public class AlarmController extends BaseController { |
110 | 110 | checkParameter(ALARM_ID, strAlarmId); |
111 | 111 | try { |
112 | 112 | AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); |
113 | - checkAlarmId(alarmId, Operation.WRITE); | |
113 | + Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); | |
114 | 114 | |
115 | + logEntityAction(alarm.getOriginator(), alarm, | |
116 | + getCurrentUser().getCustomerId(), | |
117 | + ActionType.ALARM_DELETE, null); | |
115 | 118 | sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED); |
116 | 119 | |
117 | 120 | return alarmService.deleteAlarm(getTenantId(), alarmId); | ... | ... |
... | ... | @@ -17,7 +17,6 @@ package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
20 | -import com.fasterxml.jackson.databind.node.ArrayNode; | |
21 | 20 | import com.fasterxml.jackson.databind.node.ObjectNode; |
22 | 21 | import lombok.Getter; |
23 | 22 | import lombok.extern.slf4j.Slf4j; |
... | ... | @@ -31,7 +30,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; |
31 | 30 | import org.thingsboard.server.common.data.Customer; |
32 | 31 | import org.thingsboard.server.common.data.Dashboard; |
33 | 32 | import org.thingsboard.server.common.data.DashboardInfo; |
34 | -import org.thingsboard.server.common.data.DataConstants; | |
35 | 33 | import org.thingsboard.server.common.data.Device; |
36 | 34 | import org.thingsboard.server.common.data.DeviceInfo; |
37 | 35 | import org.thingsboard.server.common.data.DeviceProfile; |
... | ... | @@ -39,8 +37,8 @@ import org.thingsboard.server.common.data.EdgeUtils; |
39 | 37 | import org.thingsboard.server.common.data.EntityType; |
40 | 38 | import org.thingsboard.server.common.data.EntityView; |
41 | 39 | import org.thingsboard.server.common.data.EntityViewInfo; |
42 | -import org.thingsboard.server.common.data.Firmware; | |
43 | -import org.thingsboard.server.common.data.FirmwareInfo; | |
40 | +import org.thingsboard.server.common.data.OtaPackage; | |
41 | +import org.thingsboard.server.common.data.OtaPackageInfo; | |
44 | 42 | import org.thingsboard.server.common.data.HasName; |
45 | 43 | import org.thingsboard.server.common.data.HasTenantId; |
46 | 44 | import org.thingsboard.server.common.data.TbResourceInfo; |
... | ... | @@ -70,7 +68,7 @@ import org.thingsboard.server.common.data.id.EdgeId; |
70 | 68 | import org.thingsboard.server.common.data.id.EntityId; |
71 | 69 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
72 | 70 | import org.thingsboard.server.common.data.id.EntityViewId; |
73 | -import org.thingsboard.server.common.data.id.FirmwareId; | |
71 | +import org.thingsboard.server.common.data.id.OtaPackageId; | |
74 | 72 | import org.thingsboard.server.common.data.id.TbResourceId; |
75 | 73 | import org.thingsboard.server.common.data.id.RuleChainId; |
76 | 74 | import org.thingsboard.server.common.data.id.RuleNodeId; |
... | ... | @@ -79,10 +77,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId; |
79 | 77 | import org.thingsboard.server.common.data.id.UserId; |
80 | 78 | import org.thingsboard.server.common.data.id.WidgetTypeId; |
81 | 79 | import org.thingsboard.server.common.data.id.WidgetsBundleId; |
82 | -import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
83 | -import org.thingsboard.server.common.data.kv.DataType; | |
84 | -import org.thingsboard.server.common.data.kv.KvEntry; | |
85 | -import org.thingsboard.server.common.data.kv.TsKvEntry; | |
86 | 80 | import org.thingsboard.server.common.data.page.PageLink; |
87 | 81 | import org.thingsboard.server.common.data.page.SortOrder; |
88 | 82 | import org.thingsboard.server.common.data.page.TimePageLink; |
... | ... | @@ -94,9 +88,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType; |
94 | 88 | import org.thingsboard.server.common.data.rule.RuleNode; |
95 | 89 | import org.thingsboard.server.common.data.widget.WidgetTypeDetails; |
96 | 90 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
97 | -import org.thingsboard.server.common.msg.TbMsg; | |
98 | -import org.thingsboard.server.common.msg.TbMsgDataType; | |
99 | -import org.thingsboard.server.common.msg.TbMsgMetaData; | |
100 | 91 | import org.thingsboard.server.dao.asset.AssetService; |
101 | 92 | import org.thingsboard.server.dao.attributes.AttributesService; |
102 | 93 | import org.thingsboard.server.dao.audit.AuditLogService; |
... | ... | @@ -110,7 +101,7 @@ import org.thingsboard.server.dao.edge.EdgeService; |
110 | 101 | import org.thingsboard.server.dao.entityview.EntityViewService; |
111 | 102 | import org.thingsboard.server.dao.exception.DataValidationException; |
112 | 103 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
113 | -import org.thingsboard.server.dao.firmware.FirmwareService; | |
104 | +import org.thingsboard.server.dao.ota.OtaPackageService; | |
114 | 105 | import org.thingsboard.server.dao.model.ModelConstants; |
115 | 106 | import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; |
116 | 107 | import org.thingsboard.server.dao.oauth2.OAuth2Service; |
... | ... | @@ -127,8 +118,9 @@ import org.thingsboard.server.gen.transport.TransportProtos; |
127 | 118 | import org.thingsboard.server.queue.discovery.PartitionService; |
128 | 119 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
129 | 120 | import org.thingsboard.server.queue.util.TbCoreComponent; |
121 | +import org.thingsboard.server.service.action.RuleEngineEntityActionService; | |
130 | 122 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
131 | -import org.thingsboard.server.service.firmware.FirmwareStateService; | |
123 | +import org.thingsboard.server.service.ota.OtaPackageStateService; | |
132 | 124 | import org.thingsboard.server.service.edge.EdgeNotificationService; |
133 | 125 | import org.thingsboard.server.service.edge.rpc.EdgeGrpcService; |
134 | 126 | import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; |
... | ... | @@ -147,11 +139,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
147 | 139 | import javax.mail.MessagingException; |
148 | 140 | import javax.servlet.http.HttpServletResponse; |
149 | 141 | import java.util.List; |
150 | -import java.util.Map; | |
151 | 142 | import java.util.Optional; |
152 | 143 | import java.util.Set; |
153 | 144 | import java.util.UUID; |
154 | -import java.util.stream.Collectors; | |
155 | 145 | |
156 | 146 | import static org.thingsboard.server.dao.service.Validator.validateId; |
157 | 147 | |
... | ... | @@ -250,10 +240,10 @@ public abstract class BaseController { |
250 | 240 | protected TbResourceService resourceService; |
251 | 241 | |
252 | 242 | @Autowired |
253 | - protected FirmwareService firmwareService; | |
243 | + protected OtaPackageService otaPackageService; | |
254 | 244 | |
255 | 245 | @Autowired |
256 | - protected FirmwareStateService firmwareStateService; | |
246 | + protected OtaPackageStateService otaPackageStateService; | |
257 | 247 | |
258 | 248 | @Autowired |
259 | 249 | protected TbQueueProducerProvider producerProvider; |
... | ... | @@ -279,6 +269,9 @@ public abstract class BaseController { |
279 | 269 | @Autowired(required = false) |
280 | 270 | protected EdgeGrpcService edgeGrpcService; |
281 | 271 | |
272 | + @Autowired | |
273 | + protected RuleEngineEntityActionService ruleEngineEntityActionService; | |
274 | + | |
282 | 275 | @Value("${server.log_controller_error_stack_trace}") |
283 | 276 | @Getter |
284 | 277 | private boolean logControllerErrorStackTrace; |
... | ... | @@ -511,8 +504,8 @@ public abstract class BaseController { |
511 | 504 | case TB_RESOURCE: |
512 | 505 | checkResourceId(new TbResourceId(entityId.getId()), operation); |
513 | 506 | return; |
514 | - case FIRMWARE: | |
515 | - checkFirmwareId(new FirmwareId(entityId.getId()), operation); | |
507 | + case OTA_PACKAGE: | |
508 | + checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); | |
516 | 509 | return; |
517 | 510 | default: |
518 | 511 | throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); |
... | ... | @@ -769,25 +762,25 @@ public abstract class BaseController { |
769 | 762 | } |
770 | 763 | } |
771 | 764 | |
772 | - Firmware checkFirmwareId(FirmwareId firmwareId, Operation operation) throws ThingsboardException { | |
765 | + OtaPackage checkOtaPackageId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException { | |
773 | 766 | try { |
774 | - validateId(firmwareId, "Incorrect firmwareId " + firmwareId); | |
775 | - Firmware firmware = firmwareService.findFirmwareById(getCurrentUser().getTenantId(), firmwareId); | |
776 | - checkNotNull(firmware); | |
777 | - accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmware); | |
778 | - return firmware; | |
767 | + validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); | |
768 | + OtaPackage otaPackage = otaPackageService.findOtaPackageById(getCurrentUser().getTenantId(), otaPackageId); | |
769 | + checkNotNull(otaPackage); | |
770 | + accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackage); | |
771 | + return otaPackage; | |
779 | 772 | } catch (Exception e) { |
780 | 773 | throw handleException(e, false); |
781 | 774 | } |
782 | 775 | } |
783 | 776 | |
784 | - FirmwareInfo checkFirmwareInfoId(FirmwareId firmwareId, Operation operation) throws ThingsboardException { | |
777 | + OtaPackageInfo checkOtaPackageInfoId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException { | |
785 | 778 | try { |
786 | - validateId(firmwareId, "Incorrect firmwareId " + firmwareId); | |
787 | - FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(getCurrentUser().getTenantId(), firmwareId); | |
788 | - checkNotNull(firmwareInfo); | |
789 | - accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmwareInfo); | |
790 | - return firmwareInfo; | |
779 | + validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); | |
780 | + OtaPackageInfo otaPackageIn = otaPackageService.findOtaPackageInfoById(getCurrentUser().getTenantId(), otaPackageId); | |
781 | + checkNotNull(otaPackageIn); | |
782 | + accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackageIn); | |
783 | + return otaPackageIn; | |
791 | 784 | } catch (Exception e) { |
792 | 785 | throw handleException(e, false); |
793 | 786 | } |
... | ... | @@ -809,7 +802,7 @@ public abstract class BaseController { |
809 | 802 | customerId = user.getCustomerId(); |
810 | 803 | } |
811 | 804 | if (e == null) { |
812 | - pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo); | |
805 | + ruleEngineEntityActionService.pushEntityActionToRuleEngine(entityId, entity, user.getTenantId(), customerId, actionType, user, additionalInfo); | |
813 | 806 | } |
814 | 807 | auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); |
815 | 808 | } |
... | ... | @@ -819,184 +812,6 @@ public abstract class BaseController { |
819 | 812 | return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; |
820 | 813 | } |
821 | 814 | |
822 | - private <E extends HasName, I extends EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId, | |
823 | - ActionType actionType, Object... additionalInfo) { | |
824 | - String msgType = null; | |
825 | - switch (actionType) { | |
826 | - case ADDED: | |
827 | - msgType = DataConstants.ENTITY_CREATED; | |
828 | - break; | |
829 | - case DELETED: | |
830 | - msgType = DataConstants.ENTITY_DELETED; | |
831 | - break; | |
832 | - case UPDATED: | |
833 | - msgType = DataConstants.ENTITY_UPDATED; | |
834 | - break; | |
835 | - case ASSIGNED_TO_CUSTOMER: | |
836 | - msgType = DataConstants.ENTITY_ASSIGNED; | |
837 | - break; | |
838 | - case UNASSIGNED_FROM_CUSTOMER: | |
839 | - msgType = DataConstants.ENTITY_UNASSIGNED; | |
840 | - break; | |
841 | - case ATTRIBUTES_UPDATED: | |
842 | - msgType = DataConstants.ATTRIBUTES_UPDATED; | |
843 | - break; | |
844 | - case ATTRIBUTES_DELETED: | |
845 | - msgType = DataConstants.ATTRIBUTES_DELETED; | |
846 | - break; | |
847 | - case ALARM_ACK: | |
848 | - msgType = DataConstants.ALARM_ACK; | |
849 | - break; | |
850 | - case ALARM_CLEAR: | |
851 | - msgType = DataConstants.ALARM_CLEAR; | |
852 | - break; | |
853 | - case ASSIGNED_FROM_TENANT: | |
854 | - msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; | |
855 | - break; | |
856 | - case ASSIGNED_TO_TENANT: | |
857 | - msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; | |
858 | - break; | |
859 | - case PROVISION_SUCCESS: | |
860 | - msgType = DataConstants.PROVISION_SUCCESS; | |
861 | - break; | |
862 | - case PROVISION_FAILURE: | |
863 | - msgType = DataConstants.PROVISION_FAILURE; | |
864 | - break; | |
865 | - case TIMESERIES_UPDATED: | |
866 | - msgType = DataConstants.TIMESERIES_UPDATED; | |
867 | - break; | |
868 | - case TIMESERIES_DELETED: | |
869 | - msgType = DataConstants.TIMESERIES_DELETED; | |
870 | - break; | |
871 | - case ASSIGNED_TO_EDGE: | |
872 | - msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; | |
873 | - break; | |
874 | - case UNASSIGNED_FROM_EDGE: | |
875 | - msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; | |
876 | - break; | |
877 | - } | |
878 | - if (!StringUtils.isEmpty(msgType)) { | |
879 | - try { | |
880 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
881 | - metaData.putValue("userId", user.getId().toString()); | |
882 | - metaData.putValue("userName", user.getName()); | |
883 | - if (customerId != null && !customerId.isNullUid()) { | |
884 | - metaData.putValue("customerId", customerId.toString()); | |
885 | - } | |
886 | - if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { | |
887 | - String strCustomerId = extractParameter(String.class, 1, additionalInfo); | |
888 | - String strCustomerName = extractParameter(String.class, 2, additionalInfo); | |
889 | - metaData.putValue("assignedCustomerId", strCustomerId); | |
890 | - metaData.putValue("assignedCustomerName", strCustomerName); | |
891 | - } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { | |
892 | - String strCustomerId = extractParameter(String.class, 1, additionalInfo); | |
893 | - String strCustomerName = extractParameter(String.class, 2, additionalInfo); | |
894 | - metaData.putValue("unassignedCustomerId", strCustomerId); | |
895 | - metaData.putValue("unassignedCustomerName", strCustomerName); | |
896 | - } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) { | |
897 | - String strTenantId = extractParameter(String.class, 0, additionalInfo); | |
898 | - String strTenantName = extractParameter(String.class, 1, additionalInfo); | |
899 | - metaData.putValue("assignedFromTenantId", strTenantId); | |
900 | - metaData.putValue("assignedFromTenantName", strTenantName); | |
901 | - } else if (actionType == ActionType.ASSIGNED_TO_TENANT) { | |
902 | - String strTenantId = extractParameter(String.class, 0, additionalInfo); | |
903 | - String strTenantName = extractParameter(String.class, 1, additionalInfo); | |
904 | - metaData.putValue("assignedToTenantId", strTenantId); | |
905 | - metaData.putValue("assignedToTenantName", strTenantName); | |
906 | - } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { | |
907 | - String strEdgeId = extractParameter(String.class, 1, additionalInfo); | |
908 | - String strEdgeName = extractParameter(String.class, 2, additionalInfo); | |
909 | - metaData.putValue("assignedEdgeId", strEdgeId); | |
910 | - metaData.putValue("assignedEdgeName", strEdgeName); | |
911 | - } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { | |
912 | - String strEdgeId = extractParameter(String.class, 1, additionalInfo); | |
913 | - String strEdgeName = extractParameter(String.class, 2, additionalInfo); | |
914 | - metaData.putValue("unassignedEdgeId", strEdgeId); | |
915 | - metaData.putValue("unassignedEdgeName", strEdgeName); | |
916 | - } | |
917 | - ObjectNode entityNode; | |
918 | - if (entity != null) { | |
919 | - entityNode = json.valueToTree(entity); | |
920 | - if (entityId.getEntityType() == EntityType.DASHBOARD) { | |
921 | - entityNode.put("configuration", ""); | |
922 | - } | |
923 | - } else { | |
924 | - entityNode = json.createObjectNode(); | |
925 | - if (actionType == ActionType.ATTRIBUTES_UPDATED) { | |
926 | - String scope = extractParameter(String.class, 0, additionalInfo); | |
927 | - @SuppressWarnings("unchecked") | |
928 | - List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); | |
929 | - metaData.putValue(DataConstants.SCOPE, scope); | |
930 | - if (attributes != null) { | |
931 | - for (AttributeKvEntry attr : attributes) { | |
932 | - addKvEntry(entityNode, attr); | |
933 | - } | |
934 | - } | |
935 | - } else if (actionType == ActionType.ATTRIBUTES_DELETED) { | |
936 | - String scope = extractParameter(String.class, 0, additionalInfo); | |
937 | - @SuppressWarnings("unchecked") | |
938 | - List<String> keys = extractParameter(List.class, 1, additionalInfo); | |
939 | - metaData.putValue(DataConstants.SCOPE, scope); | |
940 | - ArrayNode attrsArrayNode = entityNode.putArray("attributes"); | |
941 | - if (keys != null) { | |
942 | - keys.forEach(attrsArrayNode::add); | |
943 | - } | |
944 | - } else if (actionType == ActionType.TIMESERIES_UPDATED) { | |
945 | - @SuppressWarnings("unchecked") | |
946 | - List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); | |
947 | - addTimeseries(entityNode, timeseries); | |
948 | - } else if (actionType == ActionType.TIMESERIES_DELETED) { | |
949 | - @SuppressWarnings("unchecked") | |
950 | - List<String> keys = extractParameter(List.class, 0, additionalInfo); | |
951 | - if (keys != null) { | |
952 | - ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); | |
953 | - keys.forEach(timeseriesArrayNode::add); | |
954 | - } | |
955 | - entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo)); | |
956 | - entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); | |
957 | - } | |
958 | - } | |
959 | - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); | |
960 | - TenantId tenantId = user.getTenantId(); | |
961 | - if (tenantId.isNullUid()) { | |
962 | - if (entity instanceof HasTenantId) { | |
963 | - tenantId = ((HasTenantId) entity).getTenantId(); | |
964 | - } | |
965 | - } | |
966 | - tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); | |
967 | - } catch (Exception e) { | |
968 | - log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); | |
969 | - } | |
970 | - } | |
971 | - } | |
972 | - | |
973 | - private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception { | |
974 | - if (kvEntry.getDataType() == DataType.BOOLEAN) { | |
975 | - kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | |
976 | - } else if (kvEntry.getDataType() == DataType.DOUBLE) { | |
977 | - kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | |
978 | - } else if (kvEntry.getDataType() == DataType.LONG) { | |
979 | - kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | |
980 | - } else if (kvEntry.getDataType() == DataType.JSON) { | |
981 | - if (kvEntry.getJsonValue().isPresent()) { | |
982 | - entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get())); | |
983 | - } | |
984 | - } else { | |
985 | - entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); | |
986 | - } | |
987 | - } | |
988 | - | |
989 | - private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) { | |
990 | - T result = null; | |
991 | - if (additionalInfo != null && additionalInfo.length > index) { | |
992 | - Object paramObject = additionalInfo[index]; | |
993 | - if (clazz.isInstance(paramObject)) { | |
994 | - result = clazz.cast(paramObject); | |
995 | - } | |
996 | - } | |
997 | - return result; | |
998 | - } | |
999 | - | |
1000 | 815 | protected <E extends HasName> String entityToStr(E entity) { |
1001 | 816 | try { |
1002 | 817 | return json.writeValueAsString(json.valueToTree(entity)); |
... | ... | @@ -1093,23 +908,6 @@ public abstract class BaseController { |
1093 | 908 | return result; |
1094 | 909 | } |
1095 | 910 | |
1096 | - private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception { | |
1097 | - if (timeseries != null && !timeseries.isEmpty()) { | |
1098 | - ArrayNode result = entityNode.putArray("timeseries"); | |
1099 | - Map<Long, List<TsKvEntry>> groupedTelemetry = timeseries.stream() | |
1100 | - .collect(Collectors.groupingBy(TsKvEntry::getTs)); | |
1101 | - for (Map.Entry<Long, List<TsKvEntry>> entry : groupedTelemetry.entrySet()) { | |
1102 | - ObjectNode element = json.createObjectNode(); | |
1103 | - element.put("ts", entry.getKey()); | |
1104 | - ObjectNode values = element.putObject("values"); | |
1105 | - for (TsKvEntry tsKvEntry : entry.getValue()) { | |
1106 | - addKvEntry(values, tsKvEntry); | |
1107 | - } | |
1108 | - result.add(element); | |
1109 | - } | |
1110 | - } | |
1111 | - } | |
1112 | - | |
1113 | 911 | protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { |
1114 | 912 | String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; |
1115 | 913 | if (dashboardId != null && !dashboardId.equals("null")) { | ... | ... |
... | ... | @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.id.DeviceId; |
53 | 53 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
54 | 54 | import org.thingsboard.server.common.data.id.EdgeId; |
55 | 55 | import org.thingsboard.server.common.data.id.TenantId; |
56 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
56 | 57 | import org.thingsboard.server.common.data.page.PageData; |
57 | 58 | import org.thingsboard.server.common.data.page.PageLink; |
58 | 59 | import org.thingsboard.server.common.data.page.TimePageLink; |
... | ... | @@ -75,6 +76,7 @@ import javax.annotation.Nullable; |
75 | 76 | import java.io.IOException; |
76 | 77 | import java.util.ArrayList; |
77 | 78 | import java.util.List; |
79 | +import java.util.UUID; | |
78 | 80 | import java.util.stream.Collectors; |
79 | 81 | |
80 | 82 | import static org.thingsboard.server.controller.EdgeController.EDGE_ID; |
... | ... | @@ -153,7 +155,7 @@ public class DeviceController extends BaseController { |
153 | 155 | deviceStateService.onDeviceUpdated(savedDevice); |
154 | 156 | } |
155 | 157 | |
156 | - firmwareStateService.update(savedDevice, oldDevice); | |
158 | + otaPackageStateService.update(savedDevice, oldDevice); | |
157 | 159 | |
158 | 160 | return savedDevice; |
159 | 161 | } catch (Exception e) { |
... | ... | @@ -778,4 +780,19 @@ public class DeviceController extends BaseController { |
778 | 780 | throw handleException(e); |
779 | 781 | } |
780 | 782 | } |
783 | + | |
784 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
785 | + @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET) | |
786 | + @ResponseBody | |
787 | + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType, | |
788 | + @RequestParam String deviceProfileId) throws ThingsboardException { | |
789 | + checkParameter("OtaPackageType", otaPackageType); | |
790 | + checkParameter("DeviceProfileId", deviceProfileId); | |
791 | + try { | |
792 | + return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( | |
793 | + getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType)); | |
794 | + } catch (Exception e) { | |
795 | + throw handleException(e); | |
796 | + } | |
797 | + } | |
781 | 798 | } | ... | ... |
... | ... | @@ -168,7 +168,7 @@ public class DeviceProfileController extends BaseController { |
168 | 168 | null, |
169 | 169 | created ? ActionType.ADDED : ActionType.UPDATED, null); |
170 | 170 | |
171 | - firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); | |
171 | + otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); | |
172 | 172 | |
173 | 173 | sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), |
174 | 174 | deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); | ... | ... |
... | ... | @@ -22,12 +22,13 @@ import org.springframework.security.access.prepost.PreAuthorize; |
22 | 22 | import org.springframework.web.bind.annotation.RequestBody; |
23 | 23 | import org.springframework.web.bind.annotation.RequestMapping; |
24 | 24 | import org.springframework.web.bind.annotation.RequestMethod; |
25 | +import org.springframework.web.bind.annotation.RequestParam; | |
25 | 26 | import org.springframework.web.bind.annotation.ResponseBody; |
26 | 27 | import org.springframework.web.bind.annotation.ResponseStatus; |
27 | 28 | import org.springframework.web.bind.annotation.RestController; |
28 | 29 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
29 | 30 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
30 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; | |
31 | +import org.thingsboard.server.common.data.oauth2.OAuth2Info; | |
31 | 32 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
32 | 33 | import org.thingsboard.server.queue.util.TbCoreComponent; |
33 | 34 | import org.thingsboard.server.service.security.permission.Operation; |
... | ... | @@ -49,7 +50,8 @@ public class OAuth2Controller extends BaseController { |
49 | 50 | |
50 | 51 | @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) |
51 | 52 | @ResponseBody |
52 | - public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { | |
53 | + public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request, | |
54 | + @RequestParam(required = false) String pkgName) throws ThingsboardException { | |
53 | 55 | try { |
54 | 56 | if (log.isDebugEnabled()) { |
55 | 57 | log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); |
... | ... | @@ -59,7 +61,7 @@ public class OAuth2Controller extends BaseController { |
59 | 61 | log.debug("Header: {} {}", header, request.getHeader(header)); |
60 | 62 | } |
61 | 63 | } |
62 | - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request)); | |
64 | + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName); | |
63 | 65 | } catch (Exception e) { |
64 | 66 | throw handleException(e); |
65 | 67 | } |
... | ... | @@ -68,10 +70,10 @@ public class OAuth2Controller extends BaseController { |
68 | 70 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
69 | 71 | @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") |
70 | 72 | @ResponseBody |
71 | - public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { | |
73 | + public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { | |
72 | 74 | try { |
73 | 75 | accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); |
74 | - return oAuth2Service.findOAuth2Params(); | |
76 | + return oAuth2Service.findOAuth2Info(); | |
75 | 77 | } catch (Exception e) { |
76 | 78 | throw handleException(e); |
77 | 79 | } |
... | ... | @@ -80,11 +82,11 @@ public class OAuth2Controller extends BaseController { |
80 | 82 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
81 | 83 | @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) |
82 | 84 | @ResponseStatus(value = HttpStatus.OK) |
83 | - public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { | |
85 | + public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { | |
84 | 86 | try { |
85 | 87 | accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); |
86 | - oAuth2Service.saveOAuth2Params(oauth2Params); | |
87 | - return oAuth2Service.findOAuth2Params(); | |
88 | + oAuth2Service.saveOAuth2Info(oauth2Info); | |
89 | + return oAuth2Service.findOAuth2Info(); | |
88 | 90 | } catch (Exception e) { |
89 | 91 | throw handleException(e); |
90 | 92 | } | ... | ... |
application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
renamed from
application/src/main/java/org/thingsboard/server/controller/FirmwareController.java
... | ... | @@ -30,14 +30,14 @@ 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 | 32 | import org.thingsboard.server.common.data.EntityType; |
33 | -import org.thingsboard.server.common.data.Firmware; | |
34 | -import org.thingsboard.server.common.data.FirmwareInfo; | |
33 | +import org.thingsboard.server.common.data.OtaPackage; | |
34 | +import org.thingsboard.server.common.data.OtaPackageInfo; | |
35 | 35 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | 36 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; | |
38 | -import org.thingsboard.server.common.data.firmware.FirmwareType; | |
37 | +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; | |
38 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
39 | 39 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
40 | -import org.thingsboard.server.common.data.id.FirmwareId; | |
40 | +import org.thingsboard.server.common.data.id.OtaPackageId; | |
41 | 41 | import org.thingsboard.server.common.data.page.PageData; |
42 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
43 | 43 | import org.thingsboard.server.queue.util.TbCoreComponent; |
... | ... | @@ -50,26 +50,30 @@ import java.nio.ByteBuffer; |
50 | 50 | @RestController |
51 | 51 | @TbCoreComponent |
52 | 52 | @RequestMapping("/api") |
53 | -public class FirmwareController extends BaseController { | |
53 | +public class OtaPackageController extends BaseController { | |
54 | 54 | |
55 | - public static final String FIRMWARE_ID = "firmwareId"; | |
55 | + public static final String OTA_PACKAGE_ID = "otaPackageId"; | |
56 | 56 | public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm"; |
57 | 57 | |
58 | 58 | @PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')") |
59 | - @RequestMapping(value = "/firmware/{firmwareId}/download", method = RequestMethod.GET) | |
59 | + @RequestMapping(value = "/otaPackage/{otaPackageId}/download", method = RequestMethod.GET) | |
60 | 60 | @ResponseBody |
61 | - public ResponseEntity<org.springframework.core.io.Resource> downloadFirmware(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { | |
62 | - checkParameter(FIRMWARE_ID, strFirmwareId); | |
61 | + public ResponseEntity<org.springframework.core.io.Resource> downloadOtaPackage(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { | |
62 | + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); | |
63 | 63 | try { |
64 | - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); | |
65 | - Firmware firmware = checkFirmwareId(firmwareId, Operation.READ); | |
64 | + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); | |
65 | + OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); | |
66 | 66 | |
67 | - ByteArrayResource resource = new ByteArrayResource(firmware.getData().array()); | |
67 | + if (otaPackage.hasUrl()) { | |
68 | + return ResponseEntity.badRequest().build(); | |
69 | + } | |
70 | + | |
71 | + ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); | |
68 | 72 | return ResponseEntity.ok() |
69 | - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmware.getFileName()) | |
70 | - .header("x-filename", firmware.getFileName()) | |
73 | + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) | |
74 | + .header("x-filename", otaPackage.getFileName()) | |
71 | 75 | .contentLength(resource.contentLength()) |
72 | - .contentType(parseMediaType(firmware.getContentType())) | |
76 | + .contentType(parseMediaType(otaPackage.getContentType())) | |
73 | 77 | .body(resource); |
74 | 78 | } catch (Exception e) { |
75 | 79 | throw handleException(e); |
... | ... | @@ -77,144 +81,143 @@ public class FirmwareController extends BaseController { |
77 | 81 | } |
78 | 82 | |
79 | 83 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
80 | - @RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET) | |
84 | + @RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET) | |
81 | 85 | @ResponseBody |
82 | - public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { | |
83 | - checkParameter(FIRMWARE_ID, strFirmwareId); | |
86 | + public OtaPackageInfo getOtaPackageInfoById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { | |
87 | + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); | |
84 | 88 | try { |
85 | - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); | |
86 | - return checkNotNull(firmwareService.findFirmwareInfoById(getTenantId(), firmwareId)); | |
89 | + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); | |
90 | + return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); | |
87 | 91 | } catch (Exception e) { |
88 | 92 | throw handleException(e); |
89 | 93 | } |
90 | 94 | } |
91 | 95 | |
92 | 96 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
93 | - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.GET) | |
97 | + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET) | |
94 | 98 | @ResponseBody |
95 | - public Firmware getFirmwareById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { | |
96 | - checkParameter(FIRMWARE_ID, strFirmwareId); | |
99 | + public OtaPackage getOtaPackageById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { | |
100 | + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); | |
97 | 101 | try { |
98 | - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); | |
99 | - return checkFirmwareId(firmwareId, Operation.READ); | |
102 | + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); | |
103 | + return checkOtaPackageId(otaPackageId, Operation.READ); | |
100 | 104 | } catch (Exception e) { |
101 | 105 | throw handleException(e); |
102 | 106 | } |
103 | 107 | } |
104 | 108 | |
105 | 109 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
106 | - @RequestMapping(value = "/firmware", method = RequestMethod.POST) | |
110 | + @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) | |
107 | 111 | @ResponseBody |
108 | - public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException { | |
109 | - boolean created = firmwareInfo.getId() == null; | |
112 | + public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException { | |
113 | + boolean created = otaPackageInfo.getId() == null; | |
110 | 114 | try { |
111 | - firmwareInfo.setTenantId(getTenantId()); | |
112 | - checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE); | |
113 | - FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); | |
114 | - logEntityAction(savedFirmwareInfo.getId(), savedFirmwareInfo, | |
115 | + otaPackageInfo.setTenantId(getTenantId()); | |
116 | + checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE); | |
117 | + OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(otaPackageInfo); | |
118 | + logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo, | |
115 | 119 | null, created ? ActionType.ADDED : ActionType.UPDATED, null); |
116 | - return savedFirmwareInfo; | |
120 | + return savedOtaPackageInfo; | |
117 | 121 | } catch (Exception e) { |
118 | - logEntityAction(emptyId(EntityType.FIRMWARE), firmwareInfo, | |
122 | + logEntityAction(emptyId(EntityType.OTA_PACKAGE), otaPackageInfo, | |
119 | 123 | null, created ? ActionType.ADDED : ActionType.UPDATED, e); |
120 | 124 | throw handleException(e); |
121 | 125 | } |
122 | 126 | } |
123 | 127 | |
124 | 128 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
125 | - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.POST) | |
129 | + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST) | |
126 | 130 | @ResponseBody |
127 | - public Firmware saveFirmwareData(@PathVariable(FIRMWARE_ID) String strFirmwareId, | |
128 | - @RequestParam(required = false) String checksum, | |
129 | - @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr, | |
130 | - @RequestBody MultipartFile file) throws ThingsboardException { | |
131 | - checkParameter(FIRMWARE_ID, strFirmwareId); | |
131 | + public OtaPackage saveOtaPackageData(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId, | |
132 | + @RequestParam(required = false) String checksum, | |
133 | + @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr, | |
134 | + @RequestBody MultipartFile file) throws ThingsboardException { | |
135 | + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); | |
132 | 136 | checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr); |
133 | 137 | try { |
134 | - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); | |
135 | - FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.READ); | |
136 | - | |
137 | - Firmware firmware = new Firmware(firmwareId); | |
138 | - firmware.setCreatedTime(info.getCreatedTime()); | |
139 | - firmware.setTenantId(getTenantId()); | |
140 | - firmware.setDeviceProfileId(info.getDeviceProfileId()); | |
141 | - firmware.setType(info.getType()); | |
142 | - firmware.setTitle(info.getTitle()); | |
143 | - firmware.setVersion(info.getVersion()); | |
144 | - firmware.setAdditionalInfo(info.getAdditionalInfo()); | |
138 | + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); | |
139 | + OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.READ); | |
140 | + | |
141 | + OtaPackage otaPackage = new OtaPackage(otaPackageId); | |
142 | + otaPackage.setCreatedTime(info.getCreatedTime()); | |
143 | + otaPackage.setTenantId(getTenantId()); | |
144 | + otaPackage.setDeviceProfileId(info.getDeviceProfileId()); | |
145 | + otaPackage.setType(info.getType()); | |
146 | + otaPackage.setTitle(info.getTitle()); | |
147 | + otaPackage.setVersion(info.getVersion()); | |
148 | + otaPackage.setAdditionalInfo(info.getAdditionalInfo()); | |
145 | 149 | |
146 | 150 | ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); |
147 | 151 | |
148 | 152 | byte[] bytes = file.getBytes(); |
149 | 153 | if (StringUtils.isEmpty(checksum)) { |
150 | - checksum = firmwareService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes)); | |
154 | + checksum = otaPackageService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes)); | |
151 | 155 | } |
152 | 156 | |
153 | - firmware.setChecksumAlgorithm(checksumAlgorithm); | |
154 | - firmware.setChecksum(checksum); | |
155 | - firmware.setFileName(file.getOriginalFilename()); | |
156 | - firmware.setContentType(file.getContentType()); | |
157 | - firmware.setData(ByteBuffer.wrap(bytes)); | |
158 | - firmware.setDataSize((long) bytes.length); | |
159 | - Firmware savedFirmware = firmwareService.saveFirmware(firmware); | |
160 | - logEntityAction(savedFirmware.getId(), savedFirmware, null, ActionType.UPDATED, null); | |
161 | - return savedFirmware; | |
157 | + otaPackage.setChecksumAlgorithm(checksumAlgorithm); | |
158 | + otaPackage.setChecksum(checksum); | |
159 | + otaPackage.setFileName(file.getOriginalFilename()); | |
160 | + otaPackage.setContentType(file.getContentType()); | |
161 | + otaPackage.setData(ByteBuffer.wrap(bytes)); | |
162 | + otaPackage.setDataSize((long) bytes.length); | |
163 | + OtaPackage savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); | |
164 | + logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null); | |
165 | + return savedOtaPackage; | |
162 | 166 | } catch (Exception e) { |
163 | - logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.UPDATED, e, strFirmwareId); | |
167 | + logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.UPDATED, e, strOtaPackageId); | |
164 | 168 | throw handleException(e); |
165 | 169 | } |
166 | 170 | } |
167 | 171 | |
168 | 172 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
169 | - @RequestMapping(value = "/firmwares", method = RequestMethod.GET) | |
173 | + @RequestMapping(value = "/otaPackages", method = RequestMethod.GET) | |
170 | 174 | @ResponseBody |
171 | - public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize, | |
172 | - @RequestParam int page, | |
173 | - @RequestParam(required = false) String textSearch, | |
174 | - @RequestParam(required = false) String sortProperty, | |
175 | - @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
175 | + public PageData<OtaPackageInfo> getOtaPackages(@RequestParam int pageSize, | |
176 | + @RequestParam int page, | |
177 | + @RequestParam(required = false) String textSearch, | |
178 | + @RequestParam(required = false) String sortProperty, | |
179 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
176 | 180 | try { |
177 | 181 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
178 | - return checkNotNull(firmwareService.findTenantFirmwaresByTenantId(getTenantId(), pageLink)); | |
182 | + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); | |
179 | 183 | } catch (Exception e) { |
180 | 184 | throw handleException(e); |
181 | 185 | } |
182 | 186 | } |
183 | 187 | |
184 | 188 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
185 | - @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) | |
189 | + @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET) | |
186 | 190 | @ResponseBody |
187 | - public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId, | |
188 | - @PathVariable("type") String strType, | |
189 | - @PathVariable("hasData") boolean hasData, | |
190 | - @RequestParam int pageSize, | |
191 | - @RequestParam int page, | |
192 | - @RequestParam(required = false) String textSearch, | |
193 | - @RequestParam(required = false) String sortProperty, | |
194 | - @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
191 | + public PageData<OtaPackageInfo> getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId, | |
192 | + @PathVariable("type") String strType, | |
193 | + @RequestParam int pageSize, | |
194 | + @RequestParam int page, | |
195 | + @RequestParam(required = false) String textSearch, | |
196 | + @RequestParam(required = false) String sortProperty, | |
197 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
195 | 198 | checkParameter("deviceProfileId", strDeviceProfileId); |
196 | 199 | checkParameter("type", strType); |
197 | 200 | try { |
198 | 201 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
199 | - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), | |
200 | - new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink)); | |
202 | + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), | |
203 | + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink)); | |
201 | 204 | } catch (Exception e) { |
202 | 205 | throw handleException(e); |
203 | 206 | } |
204 | 207 | } |
205 | 208 | |
206 | 209 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
207 | - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE) | |
210 | + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE) | |
208 | 211 | @ResponseBody |
209 | - public void deleteFirmware(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { | |
210 | - checkParameter(FIRMWARE_ID, strFirmwareId); | |
212 | + public void deleteOtaPackage(@PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException { | |
213 | + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); | |
211 | 214 | try { |
212 | - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); | |
213 | - FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.DELETE); | |
214 | - firmwareService.deleteFirmware(getTenantId(), firmwareId); | |
215 | - logEntityAction(firmwareId, info, null, ActionType.DELETED, null, strFirmwareId); | |
215 | + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); | |
216 | + OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.DELETE); | |
217 | + otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId); | |
218 | + logEntityAction(otaPackageId, info, null, ActionType.DELETED, null, strOtaPackageId); | |
216 | 219 | } catch (Exception e) { |
217 | - logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.DELETED, e, strFirmwareId); | |
220 | + logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId); | |
218 | 221 | throw handleException(e); |
219 | 222 | } |
220 | 223 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.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.service.action; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
19 | +import com.fasterxml.jackson.databind.node.ArrayNode; | |
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
21 | +import lombok.RequiredArgsConstructor; | |
22 | +import lombok.extern.slf4j.Slf4j; | |
23 | +import org.apache.commons.lang3.StringUtils; | |
24 | +import org.springframework.stereotype.Service; | |
25 | +import org.thingsboard.server.common.data.DataConstants; | |
26 | +import org.thingsboard.server.common.data.EntityType; | |
27 | +import org.thingsboard.server.common.data.HasName; | |
28 | +import org.thingsboard.server.common.data.HasTenantId; | |
29 | +import org.thingsboard.server.common.data.User; | |
30 | +import org.thingsboard.server.common.data.audit.ActionType; | |
31 | +import org.thingsboard.server.common.data.id.CustomerId; | |
32 | +import org.thingsboard.server.common.data.id.EntityId; | |
33 | +import org.thingsboard.server.common.data.id.TenantId; | |
34 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
35 | +import org.thingsboard.server.common.data.kv.DataType; | |
36 | +import org.thingsboard.server.common.data.kv.KvEntry; | |
37 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
38 | +import org.thingsboard.server.common.msg.TbMsg; | |
39 | +import org.thingsboard.server.common.msg.TbMsgDataType; | |
40 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | |
41 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
42 | +import org.thingsboard.server.service.queue.TbClusterService; | |
43 | + | |
44 | +import java.util.List; | |
45 | +import java.util.Map; | |
46 | +import java.util.stream.Collectors; | |
47 | + | |
48 | +@TbCoreComponent | |
49 | +@Service | |
50 | +@RequiredArgsConstructor | |
51 | +@Slf4j | |
52 | +public class RuleEngineEntityActionService { | |
53 | + private final TbClusterService tbClusterService; | |
54 | + | |
55 | + private static final ObjectMapper json = new ObjectMapper(); | |
56 | + | |
57 | + public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, | |
58 | + ActionType actionType, User user, Object... additionalInfo) { | |
59 | + String msgType = null; | |
60 | + switch (actionType) { | |
61 | + case ADDED: | |
62 | + msgType = DataConstants.ENTITY_CREATED; | |
63 | + break; | |
64 | + case DELETED: | |
65 | + msgType = DataConstants.ENTITY_DELETED; | |
66 | + break; | |
67 | + case UPDATED: | |
68 | + msgType = DataConstants.ENTITY_UPDATED; | |
69 | + break; | |
70 | + case ASSIGNED_TO_CUSTOMER: | |
71 | + msgType = DataConstants.ENTITY_ASSIGNED; | |
72 | + break; | |
73 | + case UNASSIGNED_FROM_CUSTOMER: | |
74 | + msgType = DataConstants.ENTITY_UNASSIGNED; | |
75 | + break; | |
76 | + case ATTRIBUTES_UPDATED: | |
77 | + msgType = DataConstants.ATTRIBUTES_UPDATED; | |
78 | + break; | |
79 | + case ATTRIBUTES_DELETED: | |
80 | + msgType = DataConstants.ATTRIBUTES_DELETED; | |
81 | + break; | |
82 | + case ALARM_ACK: | |
83 | + msgType = DataConstants.ALARM_ACK; | |
84 | + break; | |
85 | + case ALARM_CLEAR: | |
86 | + msgType = DataConstants.ALARM_CLEAR; | |
87 | + break; | |
88 | + case ALARM_DELETE: | |
89 | + msgType = DataConstants.ALARM_DELETE; | |
90 | + break; | |
91 | + case ASSIGNED_FROM_TENANT: | |
92 | + msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; | |
93 | + break; | |
94 | + case ASSIGNED_TO_TENANT: | |
95 | + msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; | |
96 | + break; | |
97 | + case PROVISION_SUCCESS: | |
98 | + msgType = DataConstants.PROVISION_SUCCESS; | |
99 | + break; | |
100 | + case PROVISION_FAILURE: | |
101 | + msgType = DataConstants.PROVISION_FAILURE; | |
102 | + break; | |
103 | + case TIMESERIES_UPDATED: | |
104 | + msgType = DataConstants.TIMESERIES_UPDATED; | |
105 | + break; | |
106 | + case TIMESERIES_DELETED: | |
107 | + msgType = DataConstants.TIMESERIES_DELETED; | |
108 | + break; | |
109 | + case ASSIGNED_TO_EDGE: | |
110 | + msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; | |
111 | + break; | |
112 | + case UNASSIGNED_FROM_EDGE: | |
113 | + msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; | |
114 | + break; | |
115 | + } | |
116 | + if (!StringUtils.isEmpty(msgType)) { | |
117 | + try { | |
118 | + TbMsgMetaData metaData = new TbMsgMetaData(); | |
119 | + if (user != null) { | |
120 | + metaData.putValue("userId", user.getId().toString()); | |
121 | + metaData.putValue("userName", user.getName()); | |
122 | + } | |
123 | + if (customerId != null && !customerId.isNullUid()) { | |
124 | + metaData.putValue("customerId", customerId.toString()); | |
125 | + } | |
126 | + if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { | |
127 | + String strCustomerId = extractParameter(String.class, 1, additionalInfo); | |
128 | + String strCustomerName = extractParameter(String.class, 2, additionalInfo); | |
129 | + metaData.putValue("assignedCustomerId", strCustomerId); | |
130 | + metaData.putValue("assignedCustomerName", strCustomerName); | |
131 | + } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { | |
132 | + String strCustomerId = extractParameter(String.class, 1, additionalInfo); | |
133 | + String strCustomerName = extractParameter(String.class, 2, additionalInfo); | |
134 | + metaData.putValue("unassignedCustomerId", strCustomerId); | |
135 | + metaData.putValue("unassignedCustomerName", strCustomerName); | |
136 | + } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) { | |
137 | + String strTenantId = extractParameter(String.class, 0, additionalInfo); | |
138 | + String strTenantName = extractParameter(String.class, 1, additionalInfo); | |
139 | + metaData.putValue("assignedFromTenantId", strTenantId); | |
140 | + metaData.putValue("assignedFromTenantName", strTenantName); | |
141 | + } else if (actionType == ActionType.ASSIGNED_TO_TENANT) { | |
142 | + String strTenantId = extractParameter(String.class, 0, additionalInfo); | |
143 | + String strTenantName = extractParameter(String.class, 1, additionalInfo); | |
144 | + metaData.putValue("assignedToTenantId", strTenantId); | |
145 | + metaData.putValue("assignedToTenantName", strTenantName); | |
146 | + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { | |
147 | + String strEdgeId = extractParameter(String.class, 1, additionalInfo); | |
148 | + String strEdgeName = extractParameter(String.class, 2, additionalInfo); | |
149 | + metaData.putValue("assignedEdgeId", strEdgeId); | |
150 | + metaData.putValue("assignedEdgeName", strEdgeName); | |
151 | + } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { | |
152 | + String strEdgeId = extractParameter(String.class, 1, additionalInfo); | |
153 | + String strEdgeName = extractParameter(String.class, 2, additionalInfo); | |
154 | + metaData.putValue("unassignedEdgeId", strEdgeId); | |
155 | + metaData.putValue("unassignedEdgeName", strEdgeName); | |
156 | + } | |
157 | + ObjectNode entityNode; | |
158 | + if (entity != null) { | |
159 | + entityNode = json.valueToTree(entity); | |
160 | + if (entityId.getEntityType() == EntityType.DASHBOARD) { | |
161 | + entityNode.put("configuration", ""); | |
162 | + } | |
163 | + } else { | |
164 | + entityNode = json.createObjectNode(); | |
165 | + if (actionType == ActionType.ATTRIBUTES_UPDATED) { | |
166 | + String scope = extractParameter(String.class, 0, additionalInfo); | |
167 | + @SuppressWarnings("unchecked") | |
168 | + List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); | |
169 | + metaData.putValue(DataConstants.SCOPE, scope); | |
170 | + if (attributes != null) { | |
171 | + for (AttributeKvEntry attr : attributes) { | |
172 | + addKvEntry(entityNode, attr); | |
173 | + } | |
174 | + } | |
175 | + } else if (actionType == ActionType.ATTRIBUTES_DELETED) { | |
176 | + String scope = extractParameter(String.class, 0, additionalInfo); | |
177 | + @SuppressWarnings("unchecked") | |
178 | + List<String> keys = extractParameter(List.class, 1, additionalInfo); | |
179 | + metaData.putValue(DataConstants.SCOPE, scope); | |
180 | + ArrayNode attrsArrayNode = entityNode.putArray("attributes"); | |
181 | + if (keys != null) { | |
182 | + keys.forEach(attrsArrayNode::add); | |
183 | + } | |
184 | + } else if (actionType == ActionType.TIMESERIES_UPDATED) { | |
185 | + @SuppressWarnings("unchecked") | |
186 | + List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); | |
187 | + addTimeseries(entityNode, timeseries); | |
188 | + } else if (actionType == ActionType.TIMESERIES_DELETED) { | |
189 | + @SuppressWarnings("unchecked") | |
190 | + List<String> keys = extractParameter(List.class, 0, additionalInfo); | |
191 | + if (keys != null) { | |
192 | + ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); | |
193 | + keys.forEach(timeseriesArrayNode::add); | |
194 | + } | |
195 | + entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo)); | |
196 | + entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); | |
197 | + } | |
198 | + } | |
199 | + TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); | |
200 | + if (tenantId.isNullUid()) { | |
201 | + if (entity instanceof HasTenantId) { | |
202 | + tenantId = ((HasTenantId) entity).getTenantId(); | |
203 | + } | |
204 | + } | |
205 | + tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); | |
206 | + } catch (Exception e) { | |
207 | + log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); | |
208 | + } | |
209 | + } | |
210 | + } | |
211 | + | |
212 | + | |
213 | + private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) { | |
214 | + T result = null; | |
215 | + if (additionalInfo != null && additionalInfo.length > index) { | |
216 | + Object paramObject = additionalInfo[index]; | |
217 | + if (clazz.isInstance(paramObject)) { | |
218 | + result = clazz.cast(paramObject); | |
219 | + } | |
220 | + } | |
221 | + return result; | |
222 | + } | |
223 | + | |
224 | + private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception { | |
225 | + if (timeseries != null && !timeseries.isEmpty()) { | |
226 | + ArrayNode result = entityNode.putArray("timeseries"); | |
227 | + Map<Long, List<TsKvEntry>> groupedTelemetry = timeseries.stream() | |
228 | + .collect(Collectors.groupingBy(TsKvEntry::getTs)); | |
229 | + for (Map.Entry<Long, List<TsKvEntry>> entry : groupedTelemetry.entrySet()) { | |
230 | + ObjectNode element = json.createObjectNode(); | |
231 | + element.put("ts", entry.getKey()); | |
232 | + ObjectNode values = element.putObject("values"); | |
233 | + for (TsKvEntry tsKvEntry : entry.getValue()) { | |
234 | + addKvEntry(values, tsKvEntry); | |
235 | + } | |
236 | + result.add(element); | |
237 | + } | |
238 | + } | |
239 | + } | |
240 | + | |
241 | + private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception { | |
242 | + if (kvEntry.getDataType() == DataType.BOOLEAN) { | |
243 | + kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | |
244 | + } else if (kvEntry.getDataType() == DataType.DOUBLE) { | |
245 | + kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | |
246 | + } else if (kvEntry.getDataType() == DataType.LONG) { | |
247 | + kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | |
248 | + } else if (kvEntry.getDataType() == DataType.JSON) { | |
249 | + if (kvEntry.getJsonValue().isPresent()) { | |
250 | + entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get())); | |
251 | + } | |
252 | + } else { | |
253 | + entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); | |
254 | + } | |
255 | + } | |
256 | +} | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
24 | 24 | import org.springframework.context.annotation.Profile; |
25 | 25 | import org.springframework.stereotype.Service; |
26 | +import org.thingsboard.common.util.JacksonUtil; | |
26 | 27 | import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; |
27 | 28 | import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; |
28 | 29 | import org.thingsboard.server.common.data.EntityView; |
... | ... | @@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.id.TenantId; |
35 | 36 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
36 | 37 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
37 | 38 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
39 | +import org.thingsboard.server.common.data.oauth2.OAuth2Info; | |
40 | +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; | |
38 | 41 | import org.thingsboard.server.common.data.page.PageData; |
39 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
40 | 43 | import org.thingsboard.server.common.data.page.TimePageLink; |
... | ... | @@ -45,10 +48,11 @@ import org.thingsboard.server.dao.alarm.AlarmDao; |
45 | 48 | import org.thingsboard.server.dao.alarm.AlarmService; |
46 | 49 | import org.thingsboard.server.dao.entity.EntityService; |
47 | 50 | import org.thingsboard.server.dao.entityview.EntityViewService; |
51 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
52 | +import org.thingsboard.server.dao.oauth2.OAuth2Utils; | |
48 | 53 | import org.thingsboard.server.dao.rule.RuleChainService; |
49 | 54 | import org.thingsboard.server.dao.tenant.TenantService; |
50 | 55 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
51 | -import org.thingsboard.common.util.JacksonUtil; | |
52 | 56 | import org.thingsboard.server.service.install.InstallScripts; |
53 | 57 | |
54 | 58 | import java.util.ArrayList; |
... | ... | @@ -88,6 +92,9 @@ public class DefaultDataUpdateService implements DataUpdateService { |
88 | 92 | @Autowired |
89 | 93 | private AlarmDao alarmDao; |
90 | 94 | |
95 | + @Autowired | |
96 | + private OAuth2Service oAuth2Service; | |
97 | + | |
91 | 98 | @Override |
92 | 99 | public void updateData(String fromVersion) throws Exception { |
93 | 100 | switch (fromVersion) { |
... | ... | @@ -107,6 +114,7 @@ public class DefaultDataUpdateService implements DataUpdateService { |
107 | 114 | log.info("Updating data from version 3.2.2 to 3.3.0 ..."); |
108 | 115 | tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); |
109 | 116 | tenantsAlarmsCustomerUpdater.updateEntities(null); |
117 | + updateOAuth2Params(); | |
110 | 118 | break; |
111 | 119 | default: |
112 | 120 | throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); |
... | ... | @@ -362,4 +370,20 @@ public class DefaultDataUpdateService implements DataUpdateService { |
362 | 370 | } |
363 | 371 | } |
364 | 372 | |
373 | + private void updateOAuth2Params() { | |
374 | + try { | |
375 | + OAuth2ClientsParams oauth2ClientsParams = oAuth2Service.findOAuth2Params(); | |
376 | + if (!oauth2ClientsParams.getDomainsParams().isEmpty()) { | |
377 | + log.info("Updating OAuth2 parameters ..."); | |
378 | + OAuth2Info oAuth2Info = OAuth2Utils.clientParamsToOAuth2Info(oauth2ClientsParams); | |
379 | + oAuth2Service.saveOAuth2Info(oAuth2Info); | |
380 | + oAuth2Service.saveOAuth2Params(new OAuth2ClientsParams(false, Collections.emptyList())); | |
381 | + log.info("Successfully updated OAuth2 parameters!"); | |
382 | + } | |
383 | + } | |
384 | + catch (Exception e) { | |
385 | + log.error("Failed to update OAuth2 parameters", e); | |
386 | + } | |
387 | + } | |
388 | + | |
365 | 389 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java
renamed from
application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.service.firmware; | |
16 | +package org.thingsboard.server.service.ota; | |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.FutureCallback; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
... | ... | @@ -23,12 +23,10 @@ import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; |
23 | 23 | import org.thingsboard.server.common.data.DataConstants; |
24 | 24 | import org.thingsboard.server.common.data.Device; |
25 | 25 | import org.thingsboard.server.common.data.DeviceProfile; |
26 | -import org.thingsboard.server.common.data.FirmwareInfo; | |
27 | -import org.thingsboard.server.common.data.firmware.FirmwareType; | |
28 | -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; | |
29 | -import org.thingsboard.server.common.data.firmware.FirmwareUtil; | |
26 | +import org.thingsboard.server.common.data.OtaPackageInfo; | |
27 | +import org.thingsboard.server.common.data.StringUtils; | |
30 | 28 | import org.thingsboard.server.common.data.id.DeviceId; |
31 | -import org.thingsboard.server.common.data.id.FirmwareId; | |
29 | +import org.thingsboard.server.common.data.id.OtaPackageId; | |
32 | 30 | import org.thingsboard.server.common.data.id.TenantId; |
33 | 31 | import org.thingsboard.server.common.data.kv.AttributeKey; |
34 | 32 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
... | ... | @@ -37,13 +35,16 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
37 | 35 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
38 | 36 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
39 | 37 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
38 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
39 | +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; | |
40 | +import org.thingsboard.server.common.data.ota.OtaPackageUtil; | |
40 | 41 | import org.thingsboard.server.common.data.page.PageData; |
41 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
42 | 43 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
43 | 44 | import org.thingsboard.server.dao.device.DeviceProfileService; |
44 | 45 | import org.thingsboard.server.dao.device.DeviceService; |
45 | -import org.thingsboard.server.dao.firmware.FirmwareService; | |
46 | -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; | |
46 | +import org.thingsboard.server.dao.ota.OtaPackageService; | |
47 | +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; | |
47 | 48 | import org.thingsboard.server.queue.TbQueueProducer; |
48 | 49 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
49 | 50 | import org.thingsboard.server.queue.provider.TbCoreQueueFactory; |
... | ... | @@ -58,44 +59,44 @@ import java.util.List; |
58 | 59 | import java.util.Set; |
59 | 60 | import java.util.UUID; |
60 | 61 | import java.util.function.Consumer; |
61 | -import java.util.function.Function; | |
62 | - | |
63 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM; | |
64 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM; | |
65 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE; | |
66 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; | |
67 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE; | |
68 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS; | |
69 | -import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION; | |
70 | -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
71 | -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; | |
72 | -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; | |
73 | -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey; | |
74 | -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey; | |
62 | + | |
63 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM; | |
64 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM; | |
65 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; | |
66 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; | |
67 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE; | |
68 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS; | |
69 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.URL; | |
70 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION; | |
71 | +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; | |
72 | +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; | |
73 | +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; | |
74 | +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTargetTelemetryKey; | |
75 | +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTelemetryKey; | |
75 | 76 | |
76 | 77 | @Slf4j |
77 | 78 | @Service |
78 | 79 | @TbCoreComponent |
79 | -public class DefaultFirmwareStateService implements FirmwareStateService { | |
80 | +public class DefaultOtaPackageStateService implements OtaPackageStateService { | |
80 | 81 | |
81 | 82 | private final TbClusterService tbClusterService; |
82 | - private final FirmwareService firmwareService; | |
83 | + private final OtaPackageService otaPackageService; | |
83 | 84 | private final DeviceService deviceService; |
84 | 85 | private final DeviceProfileService deviceProfileService; |
85 | 86 | private final RuleEngineTelemetryService telemetryService; |
86 | - private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer; | |
87 | + private final TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> otaPackageStateMsgProducer; | |
87 | 88 | |
88 | - public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService, | |
89 | - DeviceService deviceService, | |
90 | - DeviceProfileService deviceProfileService, | |
91 | - RuleEngineTelemetryService telemetryService, | |
92 | - TbCoreQueueFactory coreQueueFactory) { | |
89 | + public DefaultOtaPackageStateService(TbClusterService tbClusterService, OtaPackageService otaPackageService, | |
90 | + DeviceService deviceService, | |
91 | + DeviceProfileService deviceProfileService, | |
92 | + RuleEngineTelemetryService telemetryService, | |
93 | + TbCoreQueueFactory coreQueueFactory) { | |
93 | 94 | this.tbClusterService = tbClusterService; |
94 | - this.firmwareService = firmwareService; | |
95 | + this.otaPackageService = otaPackageService; | |
95 | 96 | this.deviceService = deviceService; |
96 | 97 | this.deviceProfileService = deviceProfileService; |
97 | 98 | this.telemetryService = telemetryService; |
98 | - this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer(); | |
99 | + this.otaPackageStateMsgProducer = coreQueueFactory.createToOtaPackageStateServiceMsgProducer(); | |
99 | 100 | } |
100 | 101 | |
101 | 102 | @Override |
... | ... | @@ -105,14 +106,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
105 | 106 | } |
106 | 107 | |
107 | 108 | private void updateFirmware(Device device, Device oldDevice) { |
108 | - FirmwareId newFirmwareId = device.getFirmwareId(); | |
109 | + OtaPackageId newFirmwareId = device.getFirmwareId(); | |
109 | 110 | if (newFirmwareId == null) { |
110 | 111 | DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); |
111 | 112 | newFirmwareId = newDeviceProfile.getFirmwareId(); |
112 | 113 | } |
113 | 114 | if (oldDevice != null) { |
114 | 115 | if (newFirmwareId != null) { |
115 | - FirmwareId oldFirmwareId = oldDevice.getFirmwareId(); | |
116 | + OtaPackageId oldFirmwareId = oldDevice.getFirmwareId(); | |
116 | 117 | if (oldFirmwareId == null) { |
117 | 118 | DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); |
118 | 119 | oldFirmwareId = oldDeviceProfile.getFirmwareId(); |
... | ... | @@ -132,14 +133,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
132 | 133 | } |
133 | 134 | |
134 | 135 | private void updateSoftware(Device device, Device oldDevice) { |
135 | - FirmwareId newSoftwareId = device.getSoftwareId(); | |
136 | + OtaPackageId newSoftwareId = device.getSoftwareId(); | |
136 | 137 | if (newSoftwareId == null) { |
137 | 138 | DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); |
138 | 139 | newSoftwareId = newDeviceProfile.getSoftwareId(); |
139 | 140 | } |
140 | 141 | if (oldDevice != null) { |
141 | 142 | if (newSoftwareId != null) { |
142 | - FirmwareId oldSoftwareId = oldDevice.getSoftwareId(); | |
143 | + OtaPackageId oldSoftwareId = oldDevice.getSoftwareId(); | |
143 | 144 | if (oldSoftwareId == null) { |
144 | 145 | DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); |
145 | 146 | oldSoftwareId = oldDeviceProfile.getSoftwareId(); |
... | ... | @@ -170,33 +171,20 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
170 | 171 | } |
171 | 172 | } |
172 | 173 | |
173 | - private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) { | |
174 | - Function<PageLink, PageData<Device>> getDevicesFunction; | |
174 | + private void update(TenantId tenantId, DeviceProfile deviceProfile, OtaPackageType otaPackageType) { | |
175 | 175 | Consumer<Device> updateConsumer; |
176 | 176 | |
177 | - switch (firmwareType) { | |
178 | - case FIRMWARE: | |
179 | - getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl); | |
180 | - break; | |
181 | - case SOFTWARE: | |
182 | - getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl); | |
183 | - break; | |
184 | - default: | |
185 | - log.warn("Unsupported firmware type: [{}]", firmwareType); | |
186 | - return; | |
187 | - } | |
188 | - | |
189 | 177 | if (deviceProfile.getFirmwareId() != null) { |
190 | 178 | long ts = System.currentTimeMillis(); |
191 | - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType); | |
179 | + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, otaPackageType); | |
192 | 180 | } else { |
193 | - updateConsumer = d -> remove(d, firmwareType); | |
181 | + updateConsumer = d -> remove(d, otaPackageType); | |
194 | 182 | } |
195 | 183 | |
196 | 184 | PageLink pageLink = new PageLink(100); |
197 | 185 | PageData<Device> pageData; |
198 | 186 | do { |
199 | - pageData = getDevicesFunction.apply(pageLink); | |
187 | + pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfile.getId(), otaPackageType, pageLink); | |
200 | 188 | pageData.getData().forEach(updateConsumer); |
201 | 189 | |
202 | 190 | if (pageData.hasNext()) { |
... | ... | @@ -206,60 +194,60 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
206 | 194 | } |
207 | 195 | |
208 | 196 | @Override |
209 | - public boolean process(ToFirmwareStateServiceMsg msg) { | |
197 | + public boolean process(ToOtaPackageStateServiceMsg msg) { | |
210 | 198 | boolean isSuccess = false; |
211 | - FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); | |
199 | + OtaPackageId targetOtaPackageId = new OtaPackageId(new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB())); | |
212 | 200 | DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); |
213 | 201 | TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); |
214 | - FirmwareType firmwareType = FirmwareType.valueOf(msg.getType()); | |
202 | + OtaPackageType firmwareType = OtaPackageType.valueOf(msg.getType()); | |
215 | 203 | long ts = msg.getTs(); |
216 | 204 | |
217 | 205 | Device device = deviceService.findDeviceById(tenantId, deviceId); |
218 | 206 | if (device == null) { |
219 | 207 | log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); |
220 | 208 | } else { |
221 | - FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); | |
222 | - if (currentFirmwareId == null) { | |
209 | + OtaPackageId currentOtaPackageId = OtaPackageUtil.getOtaPackageId(device, firmwareType); | |
210 | + if (currentOtaPackageId == null) { | |
223 | 211 | DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()); |
224 | - currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); | |
212 | + currentOtaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, firmwareType); | |
225 | 213 | } |
226 | 214 | |
227 | - if (targetFirmwareId.equals(currentFirmwareId)) { | |
228 | - update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts); | |
215 | + if (targetOtaPackageId.equals(currentOtaPackageId)) { | |
216 | + update(device, otaPackageService.findOtaPackageInfoById(device.getTenantId(), targetOtaPackageId), ts); | |
229 | 217 | isSuccess = true; |
230 | 218 | } else { |
231 | - log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); | |
219 | + log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetOtaPackageId, currentOtaPackageId); | |
232 | 220 | } |
233 | 221 | } |
234 | 222 | return isSuccess; |
235 | 223 | } |
236 | 224 | |
237 | - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) { | |
238 | - ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() | |
225 | + private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, long ts, OtaPackageType firmwareType) { | |
226 | + ToOtaPackageStateServiceMsg msg = ToOtaPackageStateServiceMsg.newBuilder() | |
239 | 227 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
240 | 228 | .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) |
241 | 229 | .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) |
242 | 230 | .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) |
243 | - .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) | |
244 | - .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) | |
231 | + .setOtaPackageIdMSB(firmwareId.getId().getMostSignificantBits()) | |
232 | + .setOtaPackageIdLSB(firmwareId.getId().getLeastSignificantBits()) | |
245 | 233 | .setType(firmwareType.name()) |
246 | 234 | .setTs(ts) |
247 | 235 | .build(); |
248 | 236 | |
249 | - FirmwareInfo firmware = firmwareService.findFirmwareInfoById(tenantId, firmwareId); | |
237 | + OtaPackageInfo firmware = otaPackageService.findOtaPackageInfoById(tenantId, firmwareId); | |
250 | 238 | if (firmware == null) { |
251 | 239 | log.warn("[{}] Failed to send firmware update because firmware was already deleted", firmwareId); |
252 | 240 | return; |
253 | 241 | } |
254 | 242 | |
255 | - TopicPartitionInfo tpi = new TopicPartitionInfo(fwStateMsgProducer.getDefaultTopic(), null, null, false); | |
256 | - fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); | |
243 | + TopicPartitionInfo tpi = new TopicPartitionInfo(otaPackageStateMsgProducer.getDefaultTopic(), null, null, false); | |
244 | + otaPackageStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); | |
257 | 245 | |
258 | 246 | List<TsKvEntry> telemetry = new ArrayList<>(); |
259 | 247 | telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle()))); |
260 | 248 | telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion()))); |
261 | 249 | telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); |
262 | - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name()))); | |
250 | + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); | |
263 | 251 | |
264 | 252 | telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { |
265 | 253 | @Override |
... | ... | @@ -275,11 +263,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
275 | 263 | } |
276 | 264 | |
277 | 265 | |
278 | - private void update(Device device, FirmwareInfo firmware, long ts) { | |
266 | + private void update(Device device, OtaPackageInfo otaPackage, long ts) { | |
279 | 267 | TenantId tenantId = device.getTenantId(); |
280 | 268 | DeviceId deviceId = device.getId(); |
269 | + OtaPackageType otaPackageType = otaPackage.getType(); | |
281 | 270 | |
282 | - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name())); | |
271 | + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name())); | |
283 | 272 | |
284 | 273 | telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { |
285 | 274 | @Override |
... | ... | @@ -294,11 +283,37 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
294 | 283 | }); |
295 | 284 | |
296 | 285 | List<AttributeKvEntry> attributes = new ArrayList<>(); |
297 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle()))); | |
298 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion()))); | |
299 | - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize()))); | |
300 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm().name()))); | |
301 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum()))); | |
286 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle()))); | |
287 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, VERSION), otaPackage.getVersion()))); | |
288 | + if (otaPackage.hasUrl()) { | |
289 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl()))); | |
290 | + List<String> attrToRemove = new ArrayList<>(); | |
291 | + | |
292 | + if (otaPackage.getDataSize() == null) { | |
293 | + attrToRemove.add(getAttributeKey(otaPackageType, SIZE)); | |
294 | + } else { | |
295 | + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); | |
296 | + } | |
297 | + | |
298 | + if (otaPackage.getChecksumAlgorithm() != null) { | |
299 | + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM)); | |
300 | + } else { | |
301 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); | |
302 | + } | |
303 | + | |
304 | + if (StringUtils.isEmpty(otaPackage.getChecksum())) { | |
305 | + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM)); | |
306 | + } else { | |
307 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); | |
308 | + } | |
309 | + | |
310 | + remove(device, otaPackageType, attrToRemove); | |
311 | + } else { | |
312 | + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); | |
313 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); | |
314 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); | |
315 | + remove(device, otaPackageType, Collections.singletonList(getAttributeKey(otaPackageType, URL))); | |
316 | + } | |
302 | 317 | |
303 | 318 | telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { |
304 | 319 | @Override |
... | ... | @@ -313,20 +328,24 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
313 | 328 | }); |
314 | 329 | } |
315 | 330 | |
316 | - private void remove(Device device, FirmwareType firmwareType) { | |
317 | - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType), | |
331 | + private void remove(Device device, OtaPackageType otaPackageType) { | |
332 | + remove(device, otaPackageType, OtaPackageUtil.getAttributeKeys(otaPackageType)); | |
333 | + } | |
334 | + | |
335 | + private void remove(Device device, OtaPackageType otaPackageType, List<String> attributesKeys) { | |
336 | + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, attributesKeys, | |
318 | 337 | new FutureCallback<>() { |
319 | 338 | @Override |
320 | 339 | public void onSuccess(@Nullable Void tmp) { |
321 | - log.trace("[{}] Success remove target firmware attributes!", device.getId()); | |
340 | + log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType); | |
322 | 341 | Set<AttributeKey> keysToNotify = new HashSet<>(); |
323 | - FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); | |
342 | + attributesKeys.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); | |
324 | 343 | tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); |
325 | 344 | } |
326 | 345 | |
327 | 346 | @Override |
328 | 347 | public void onFailure(Throwable t) { |
329 | - log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t); | |
348 | + log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t); | |
330 | 349 | } |
331 | 350 | }); |
332 | 351 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java
renamed from
application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java
... | ... | @@ -13,18 +13,18 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.service.firmware; | |
16 | +package org.thingsboard.server.service.ota; | |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.Device; |
19 | 19 | import org.thingsboard.server.common.data.DeviceProfile; |
20 | -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; | |
20 | +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; | |
21 | 21 | |
22 | -public interface FirmwareStateService { | |
22 | +public interface OtaPackageStateService { | |
23 | 23 | |
24 | 24 | void update(Device device, Device oldDevice); |
25 | 25 | |
26 | 26 | void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged); |
27 | 27 | |
28 | - boolean process(ToFirmwareStateServiceMsg msg); | |
28 | + boolean process(ToOtaPackageStateServiceMsg msg); | |
29 | 29 | |
30 | 30 | } | ... | ... |
... | ... | @@ -50,7 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP |
50 | 50 | import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; |
51 | 51 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
52 | 52 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; |
53 | -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; | |
53 | +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; | |
54 | 54 | import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; |
55 | 55 | import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; |
56 | 56 | import org.thingsboard.server.queue.TbQueueConsumer; |
... | ... | @@ -60,7 +60,7 @@ 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; |
62 | 62 | import org.thingsboard.server.service.edge.EdgeNotificationService; |
63 | -import org.thingsboard.server.service.firmware.FirmwareStateService; | |
63 | +import org.thingsboard.server.service.ota.OtaPackageStateService; | |
64 | 64 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
65 | 65 | import org.thingsboard.server.service.queue.processing.AbstractConsumerService; |
66 | 66 | import org.thingsboard.server.service.queue.processing.IdMsgPair; |
... | ... | @@ -75,7 +75,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra |
75 | 75 | |
76 | 76 | import javax.annotation.PostConstruct; |
77 | 77 | import javax.annotation.PreDestroy; |
78 | -import java.util.ArrayList; | |
79 | 78 | import java.util.List; |
80 | 79 | import java.util.Optional; |
81 | 80 | import java.util.UUID; |
... | ... | @@ -101,9 +100,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
101 | 100 | @Value("${queue.core.stats.enabled:false}") |
102 | 101 | private boolean statsEnabled; |
103 | 102 | |
104 | - @Value("${queue.core.firmware.pack-interval-ms:60000}") | |
103 | + @Value("${queue.core.ota.pack-interval-ms:60000}") | |
105 | 104 | private long firmwarePackInterval; |
106 | - @Value("${queue.core.firmware.pack-size:100}") | |
105 | + @Value("${queue.core.ota.pack-size:100}") | |
107 | 106 | private int firmwarePackSize; |
108 | 107 | |
109 | 108 | private final TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> mainConsumer; |
... | ... | @@ -113,10 +112,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
113 | 112 | private final SubscriptionManagerService subscriptionManagerService; |
114 | 113 | private final TbCoreDeviceRpcService tbCoreDeviceRpcService; |
115 | 114 | private final EdgeNotificationService edgeNotificationService; |
116 | - private final FirmwareStateService firmwareStateService; | |
115 | + private final OtaPackageStateService firmwareStateService; | |
117 | 116 | private final TbCoreConsumerStats stats; |
118 | 117 | protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer; |
119 | - private final TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> firmwareStatesConsumer; | |
118 | + private final TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> firmwareStatesConsumer; | |
120 | 119 | |
121 | 120 | protected volatile ExecutorService usageStatsExecutor; |
122 | 121 | |
... | ... | @@ -135,11 +134,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
135 | 134 | TbTenantProfileCache tenantProfileCache, |
136 | 135 | TbApiUsageStateService apiUsageStateService, |
137 | 136 | EdgeNotificationService edgeNotificationService, |
138 | - FirmwareStateService firmwareStateService) { | |
137 | + OtaPackageStateService firmwareStateService) { | |
139 | 138 | super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer()); |
140 | 139 | this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); |
141 | 140 | this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer(); |
142 | - this.firmwareStatesConsumer = tbCoreQueueFactory.createToFirmwareStateServiceMsgConsumer(); | |
141 | + this.firmwareStatesConsumer = tbCoreQueueFactory.createToOtaPackageStateServiceMsgConsumer(); | |
143 | 142 | this.stateService = stateService; |
144 | 143 | this.localSubscriptionService = localSubscriptionService; |
145 | 144 | this.subscriptionManagerService = subscriptionManagerService; |
... | ... | @@ -173,7 +172,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
173 | 172 | public void onApplicationEvent(ApplicationReadyEvent event) { |
174 | 173 | super.onApplicationEvent(event); |
175 | 174 | launchUsageStatsConsumer(); |
176 | - launchFirmwareUpdateNotificationConsumer(); | |
175 | + launchOtaPackageUpdateNotificationConsumer(); | |
177 | 176 | } |
178 | 177 | |
179 | 178 | @Override |
... | ... | @@ -361,20 +360,20 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
361 | 360 | }); |
362 | 361 | } |
363 | 362 | |
364 | - private void launchFirmwareUpdateNotificationConsumer() { | |
363 | + private void launchOtaPackageUpdateNotificationConsumer() { | |
365 | 364 | long maxProcessingTimeoutPerRecord = firmwarePackInterval / firmwarePackSize; |
366 | 365 | firmwareStatesExecutor.submit(() -> { |
367 | 366 | while (!stopped) { |
368 | 367 | try { |
369 | - List<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration()); | |
368 | + List<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration()); | |
370 | 369 | if (msgs.isEmpty()) { |
371 | 370 | continue; |
372 | 371 | } |
373 | 372 | long timeToSleep = maxProcessingTimeoutPerRecord; |
374 | - for (TbProtoQueueMsg<ToFirmwareStateServiceMsg> msg : msgs) { | |
373 | + for (TbProtoQueueMsg<ToOtaPackageStateServiceMsg> msg : msgs) { | |
375 | 374 | try { |
376 | 375 | long startTime = System.currentTimeMillis(); |
377 | - boolean isSuccessUpdate = handleFirmwareUpdates(msg); | |
376 | + boolean isSuccessUpdate = handleOtaPackageUpdates(msg); | |
378 | 377 | long endTime = System.currentTimeMillis(); |
379 | 378 | long spentTime = endTime - startTime; |
380 | 379 | timeToSleep = timeToSleep - spentTime; |
... | ... | @@ -402,7 +401,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
402 | 401 | } |
403 | 402 | } |
404 | 403 | } |
405 | - log.info("TB Firmware States Consumer stopped."); | |
404 | + log.info("TB Ota Package States Consumer stopped."); | |
406 | 405 | }); |
407 | 406 | } |
408 | 407 | |
... | ... | @@ -410,7 +409,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
410 | 409 | statsService.process(msg, callback); |
411 | 410 | } |
412 | 411 | |
413 | - private boolean handleFirmwareUpdates(TbProtoQueueMsg<ToFirmwareStateServiceMsg> msg) { | |
412 | + private boolean handleOtaPackageUpdates(TbProtoQueueMsg<ToOtaPackageStateServiceMsg> msg) { | |
414 | 413 | return firmwareStateService.process(msg.getValue()); |
415 | 414 | } |
416 | 415 | ... | ... |
... | ... | @@ -72,15 +72,15 @@ public class DefaultTbResourceService implements TbResourceService { |
72 | 72 | if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { |
73 | 73 | try { |
74 | 74 | List<ObjectModel> objectModels = |
75 | - ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
75 | + ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
76 | 76 | if (!objectModels.isEmpty()) { |
77 | 77 | ObjectModel objectModel = objectModels.get(0); |
78 | 78 | |
79 | - String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.getVersion(); | |
79 | + String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version; | |
80 | 80 | String name = objectModel.name; |
81 | 81 | resource.setResourceKey(resourceKey); |
82 | 82 | if (resource.getId() == null) { |
83 | - resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.getVersion()); | |
83 | + resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version); | |
84 | 84 | } |
85 | 85 | resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); |
86 | 86 | } else { |
... | ... | @@ -157,6 +157,11 @@ public class DefaultTbResourceService implements TbResourceService { |
157 | 157 | resourceService.deleteResourcesByTenantId(tenantId); |
158 | 158 | } |
159 | 159 | |
160 | + @Override | |
161 | + public long sumDataSizeByTenantId(TenantId tenantId) { | |
162 | + return resourceService.sumDataSizeByTenantId(tenantId); | |
163 | + } | |
164 | + | |
160 | 165 | private Comparator<? super LwM2mObject> getComparator(String sortProperty, String sortOrder) { |
161 | 166 | Comparator<LwM2mObject> comparator; |
162 | 167 | if ("name".equals(sortProperty)) { |
... | ... | @@ -171,7 +176,7 @@ public class DefaultTbResourceService implements TbResourceService { |
171 | 176 | try { |
172 | 177 | DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); |
173 | 178 | List<ObjectModel> objectModels = |
174 | - ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
179 | + ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
175 | 180 | if (objectModels.size() == 0) { |
176 | 181 | return null; |
177 | 182 | } else { | ... | ... |
... | ... | @@ -30,7 +30,7 @@ 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; | |
33 | +import org.thingsboard.server.common.data.OtaPackageInfo; | |
34 | 34 | import org.thingsboard.server.common.data.TbResourceInfo; |
35 | 35 | import org.thingsboard.server.common.data.Tenant; |
36 | 36 | import org.thingsboard.server.common.data.User; |
... | ... | @@ -46,7 +46,7 @@ import org.thingsboard.server.common.data.id.EdgeId; |
46 | 46 | import org.thingsboard.server.common.data.id.EntityId; |
47 | 47 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
48 | 48 | import org.thingsboard.server.common.data.id.EntityViewId; |
49 | -import org.thingsboard.server.common.data.id.FirmwareId; | |
49 | +import org.thingsboard.server.common.data.id.OtaPackageId; | |
50 | 50 | import org.thingsboard.server.common.data.id.RuleChainId; |
51 | 51 | import org.thingsboard.server.common.data.id.RuleNodeId; |
52 | 52 | import org.thingsboard.server.common.data.id.TbResourceId; |
... | ... | @@ -63,7 +63,7 @@ import org.thingsboard.server.dao.device.DeviceService; |
63 | 63 | import org.thingsboard.server.dao.edge.EdgeService; |
64 | 64 | import org.thingsboard.server.dao.entityview.EntityViewService; |
65 | 65 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
66 | -import org.thingsboard.server.dao.firmware.FirmwareService; | |
66 | +import org.thingsboard.server.dao.ota.OtaPackageService; | |
67 | 67 | import org.thingsboard.server.dao.resource.ResourceService; |
68 | 68 | import org.thingsboard.server.dao.rule.RuleChainService; |
69 | 69 | import org.thingsboard.server.dao.tenant.TenantService; |
... | ... | @@ -135,7 +135,7 @@ public class AccessValidator { |
135 | 135 | protected ResourceService resourceService; |
136 | 136 | |
137 | 137 | @Autowired |
138 | - protected FirmwareService firmwareService; | |
138 | + protected OtaPackageService otaPackageService; | |
139 | 139 | |
140 | 140 | private ExecutorService executor; |
141 | 141 | |
... | ... | @@ -232,8 +232,8 @@ public class AccessValidator { |
232 | 232 | case TB_RESOURCE: |
233 | 233 | validateResource(currentUser, operation, entityId, callback); |
234 | 234 | return; |
235 | - case FIRMWARE: | |
236 | - validateFirmware(currentUser, operation, entityId, callback); | |
235 | + case OTA_PACKAGE: | |
236 | + validateOtaPackage(currentUser, operation, entityId, callback); | |
237 | 237 | return; |
238 | 238 | default: |
239 | 239 | //TODO: add support of other entities |
... | ... | @@ -300,20 +300,20 @@ public class AccessValidator { |
300 | 300 | } |
301 | 301 | } |
302 | 302 | |
303 | - private void validateFirmware(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
303 | + private void validateOtaPackage(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
304 | 304 | if (currentUser.isSystemAdmin()) { |
305 | 305 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
306 | 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!")); | |
307 | + OtaPackageInfo otaPackage = otaPackageService.findOtaPackageInfoById(currentUser.getTenantId(), new OtaPackageId(entityId.getId())); | |
308 | + if (otaPackage == null) { | |
309 | + callback.onSuccess(ValidationResult.entityNotFound("OtaPackage with requested id wasn't found!")); | |
310 | 310 | } else { |
311 | 311 | try { |
312 | - accessControlService.checkPermission(currentUser, Resource.FIRMWARE, operation, entityId, firmware); | |
312 | + accessControlService.checkPermission(currentUser, Resource.OTA_PACKAGE, operation, entityId, otaPackage); | |
313 | 313 | } catch (ThingsboardException e) { |
314 | 314 | callback.onSuccess(ValidationResult.accessDenied(e.getMessage())); |
315 | 315 | } |
316 | - callback.onSuccess(ValidationResult.ok(firmware)); | |
316 | + callback.onSuccess(ValidationResult.ok(otaPackage)); | |
317 | 317 | } |
318 | 318 | } |
319 | 319 | } | ... | ... |