@@ -65,7 +65,7 @@
required
class="form-check-input"
[attr.disabled]="editmode ? '' : null"
- (change)="isChecked($event)"
+ (change)="isDeviceTypeChecked($event)"
[checked]="iotDevice.type.toString().includes('MQTT')"
/>
@@ -82,7 +82,7 @@
class="form-check-input"
required
[attr.disabled]="editmode ? '' : null"
- (change)="isChecked($event)"
+ (change)="isDeviceTypeChecked($event)"
[checked]="iotDevice.type.toString().includes('SIGFOX')"
/>
@@ -238,7 +238,7 @@
{{ "IOTDEVICE.LORAWANSETUP" | translate }}
[placeholder]="'QUESTION.DEVEUI-PLACEHOLDER' | translate"
class="form-control"
[(ngModel)]="iotDevice.lorawanSettings.devEUI"
- [disabled]="editmode"
+ [disabled]="editmode && !isDeviceCopy"
[ngClass]="{
'is-invalid': formFailedSubmit && errorFields.includes('devEUI'),
'is-valid': formFailedSubmit && !errorFields.includes('devEUI')
@@ -417,6 +417,20 @@
{{ "QUESTION.ABP" | translate }}
{{ "QUESTION.METADATA" | translate }}
+
diff --git a/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts b/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts
index 1e561111..a14437d9 100644
--- a/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts
+++ b/src/app/applications/iot-devices/iot-device-edit/iot-device-edit.component.ts
@@ -1,6 +1,6 @@
import { Location } from "@angular/common";
import { HttpErrorResponse } from "@angular/common/http";
-import { Component, OnDestroy, OnInit } from "@angular/core";
+import { Component, Input, OnDestroy, OnInit } from "@angular/core";
import { Title } from "@angular/platform-browser";
import { ActivatedRoute, Router } from "@angular/router";
import { Application } from "@app/applications/application.model";
@@ -17,11 +17,13 @@ import { jsonToList } from "@shared/helpers/json.helper";
import { ErrorMessage } from "@shared/models/error-message.model";
import { ScrollToTopService } from "@shared/services/scroll-to-top.service";
import { SharedVariableService } from "@shared/shared-variable/shared-variable.service";
-import { Subscription } from "rxjs";
+import { forkJoin, Subscription } from "rxjs";
import { IotDevice } from "../iot-device.model";
import { IoTDeviceService } from "../iot-device.service";
import { MeService } from "@shared/services/me.service";
import { OrganizationAccessScope } from "@shared/enums/access-scopes";
+import { PayloadDeviceDatatargetService } from "@payload-decoder/payload-device-datatarget.service";
+import { PayloadDeviceDatatargetGetManyResponse } from "@payload-decoder/payload-device-data.model";
@Component({
selector: "app-iot-device-edit",
@@ -29,6 +31,8 @@ import { OrganizationAccessScope } from "@shared/enums/access-scopes";
styleUrls: ["./iot-device-edit.component.scss"],
})
export class IotDeviceEditComponent implements OnInit, OnDestroy {
+ @Input() isDeviceCopy: boolean = false;
+ public copyPayloadAndDatatarget: boolean = false;
public errorMessages: any;
public errorFields: string[];
public formFailedSubmit = false;
@@ -57,6 +61,7 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
private deviceProfileService: DeviceProfileService,
private applicationService: ApplicationService,
private iotDeviceService: IoTDeviceService,
+ private datatargetPayloadService: PayloadDeviceDatatargetService,
private location: Location,
private shareVariable: SharedVariableService,
private deviceModelService: DeviceModelService,
@@ -113,7 +118,7 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
});
}
- isChecked(event) {
+ isDeviceTypeChecked(event) {
if (event.target.checked) {
this.iotDevice.type = event.target.name;
} else if (!event.target.checked && this.iotDevice.type.toString().includes(event.target.name)) {
@@ -121,6 +126,10 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
}
}
+ isCopyPayloadAndDatatargetChecked(event) {
+ this.copyPayloadAndDatatarget = event.target.checked;
+ }
+
getDevice(id: number): void {
this.deviceSubscription = this.iotDeviceService.getIoTDevice(id).subscribe((device: IotDevice) => {
this.iotDevice = device;
@@ -140,6 +149,44 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
if (device.metadata) {
this.metadataTags = jsonToList(device.metadata);
}
+
+ //If coming from copy, reset all these properties
+ if (this.isDeviceCopy) {
+ this.iotDevice.id = undefined;
+ this.iotDevice.name = undefined;
+ this.iotDevice.createdAt = undefined;
+ this.iotDevice.createdBy = undefined;
+ this.iotDevice.createdByName = undefined;
+ this.iotDevice.updatedAt = undefined;
+ this.iotDevice.updatedBy = undefined;
+ this.iotDevice.updatedByName = undefined;
+ this.copyPayloadAndDatatarget = true;
+
+ switch (this.iotDevice.type) {
+ case DeviceType.GENERIC_HTTP: {
+ this.iotDevice.apiKey = undefined;
+ break;
+ }
+ case DeviceType.LORAWAN: {
+ this.iotDevice.lorawanSettings.devEUI = undefined;
+ this.iotDevice.lorawanSettings.OTAAapplicationKey = undefined;
+ this.iotDevice.lorawanSettings.applicationSessionKey = undefined;
+ this.iotDevice.lorawanSettings.networkSessionKey = undefined;
+ this.iotDevice.lorawanSettings.devAddr = undefined;
+ this.iotDevice.lorawanSettings.fCntUp = undefined;
+ this.iotDevice.lorawanSettings.nFCntDown = undefined;
+ break;
+ }
+ case DeviceType.MQTT_INTERNAL_BROKER: {
+ this.iotDevice.mqttInternalBrokerSettings.caCertificate = undefined;
+ this.iotDevice.mqttInternalBrokerSettings.deviceCertificate = undefined;
+ this.iotDevice.mqttInternalBrokerSettings.deviceCertificateKey = undefined;
+ this.iotDevice.mqttInternalBrokerSettings.mqttpassword = undefined;
+ this.iotDevice.mqttInternalBrokerSettings.mqtttopicname = undefined;
+ this.iotDevice.mqttInternalBrokerSettings.mqttusername = undefined;
+ }
+ }
+ }
});
}
@@ -190,7 +237,7 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
}
}
- if (this.deviceId !== 0) {
+ if (this.deviceId !== 0 && !this.isDeviceCopy) {
this.updateIoTDevice(this.deviceId);
} else {
this.postIoTDevice();
@@ -275,23 +322,49 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
}
}
+ private navigateToDeviceDetails(device: IotDevice) {
+ this.router.navigate(["applications/" + this.iotDevice.applicationId + "/iot-device/" + device.id + "/details"]);
+ }
+
postIoTDevice() {
- // Sanitize devEUI for non-hex characters
+ // Sanitize devEUI
if (this.iotDevice.type === DeviceType.LORAWAN && this.iotDevice.lorawanSettings.devEUI) {
this.iotDevice.lorawanSettings.devEUI = this.iotDevice.lorawanSettings.devEUI.replace(/[^0-9A-Fa-f]/g, "");
}
- this.iotDeviceService.createIoTDevice(this.iotDevice).subscribe(
- (response: IotDevice) => {
- this.router.navigate([
- "applications/" + this.iotDevice.applicationId + "/iot-device/" + response.id + "/details",
- ]);
- },
- (error: HttpErrorResponse) => {
- this.handleError(error);
- this.formFailedSubmit = true;
+ //First create the device
+ this.iotDeviceService.createIoTDevice(this.iotDevice).subscribe((createdDevice: IotDevice) => {
+ if (!this.copyPayloadAndDatatarget) {
+ this.navigateToDeviceDetails(createdDevice);
+ return;
}
- );
+
+ //If it's the copy device flow, then get all datatargets from the device that we want to copy.
+ this.datatargetPayloadService
+ .getByIoTDevice(this.deviceId)
+ .subscribe((result: PayloadDeviceDatatargetGetManyResponse) => {
+ //For each of these datatargets, append the copied device to that datatarget. First we make the observables
+ const appendToDatatargetObservables = result.data.map(element =>
+ this.datatargetPayloadService.appendCopiedIoTDevice(element.id, { deviceId: createdDevice.id })
+ );
+
+ if (appendToDatatargetObservables.length === 0) {
+ this.navigateToDeviceDetails(createdDevice);
+ return;
+ }
+
+ //Forkjoin is running all observables in parallel and when all are done it returns.
+ forkJoin(appendToDatatargetObservables).subscribe(
+ () => this.navigateToDeviceDetails(createdDevice),
+ this.formFailedSubmitHandleError
+ );
+ }, this.formFailedSubmitHandleError);
+ }, this.formFailedSubmitHandleError);
+ }
+
+ formFailedSubmitHandleError(error: HttpErrorResponse) {
+ this.handleError(error);
+ this.formFailedSubmit = true;
}
updateIoTDevice(id: number) {
@@ -301,8 +374,7 @@ export class IotDeviceEditComponent implements OnInit, OnDestroy {
this.routeBack();
},
(error: HttpErrorResponse) => {
- this.handleError(error);
- this.formFailedSubmit = true;
+ this.formFailedSubmitHandleError(error);
}
);
}
diff --git a/src/app/applications/iot-devices/iot-devices-table/iot-devices-table.component.html b/src/app/applications/iot-devices/iot-devices-table/iot-devices-table.component.html
index c334c688..37d54ee8 100644
--- a/src/app/applications/iot-devices/iot-devices-table/iot-devices-table.component.html
+++ b/src/app/applications/iot-devices/iot-devices-table/iot-devices-table.component.html
@@ -16,7 +16,8 @@
matSort
matSortActive="name"
matSortDirection="asc"
- matSortDisableClear>
+ matSortDisableClear
+ >
{{ "IOTDEVICE-TABLE-ROW.NOT-SUPPORTED-SHORT" | translate }}
@@ -30,12 +31,12 @@
{{ "APPLICATION-TABLE.NAME" | translate }}
-
+
{{
- iotDevice.name
- }}
+ iotDevice.name
+ }}
|
@@ -45,7 +46,7 @@
{{ "IOT-TABLE.NETWORK-TECHNOLOGY" | translate }}
-
+
{{ "IOT-DEVICE-TYPES." + iotDevice.type | translate }} |
@@ -57,7 +58,7 @@
>
{{ "GATEWAY.PLACEMENT-LABEL" | translate }}
-
+
{{ iotDevice.commentOnLocation ? truncateText(iotDevice.commentOnLocation) : "-" }}
@@ -69,7 +70,7 @@
{{ "IOTDEVICE.DEVICEMODEL" | translate }}
-
+
| {{ iotDevice.deviceModel?.body?.name ?? "-" }} |
@@ -81,7 +82,7 @@
>
{{ "IOTDEVICE.LORA.DEVICEPROFILE" | translate }}
@@ -103,7 +104,7 @@
>
{{ "IOT-TABLE.APP-KEY" | translate }}