Commit 29f404acb1f654f6f47c7f191fbde5bd5d9db85f

Authored by Andrii Shvaika
2 parents 7edcc605 83e31f42

Adding Device Profile ID to the Firmware to make sure user assigns compatible firmware

@@ -63,6 +63,7 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -63,6 +63,7 @@ CREATE TABLE IF NOT EXISTS firmware (
63 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, 63 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
64 created_time bigint NOT NULL, 64 created_time bigint NOT NULL,
65 tenant_id uuid NOT NULL, 65 tenant_id uuid NOT NULL,
  66 + type varchar(32) NOT NULL,
66 title varchar(255) NOT NULL, 67 title varchar(255) NOT NULL,
67 version varchar(255) NOT NULL, 68 version varchar(255) NOT NULL,
68 file_name varchar(255), 69 file_name varchar(255),
@@ -16,16 +16,20 @@ @@ -16,16 +16,20 @@
16 package org.thingsboard.server.service.firmware; 16 package org.thingsboard.server.service.firmware;
17 17
18 import com.google.common.util.concurrent.FutureCallback; 18 import com.google.common.util.concurrent.FutureCallback;
  19 +import lombok.RequiredArgsConstructor;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
21 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; 22 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  23 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
22 import org.thingsboard.server.common.data.DataConstants; 24 import org.thingsboard.server.common.data.DataConstants;
23 import org.thingsboard.server.common.data.Device; 25 import org.thingsboard.server.common.data.Device;
24 import org.thingsboard.server.common.data.DeviceProfile; 26 import org.thingsboard.server.common.data.DeviceProfile;
25 import org.thingsboard.server.common.data.FirmwareInfo; 27 import org.thingsboard.server.common.data.FirmwareInfo;
  28 +import org.thingsboard.server.common.data.firmware.FirmwareKeyUtil;
26 import org.thingsboard.server.common.data.id.DeviceId; 29 import org.thingsboard.server.common.data.id.DeviceId;
27 import org.thingsboard.server.common.data.id.FirmwareId; 30 import org.thingsboard.server.common.data.id.FirmwareId;
28 import org.thingsboard.server.common.data.id.TenantId; 31 import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.kv.AttributeKey;
29 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 33 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
30 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 34 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
31 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@@ -41,46 +45,42 @@ import org.thingsboard.server.dao.firmware.FirmwareService; @@ -41,46 +45,42 @@ import org.thingsboard.server.dao.firmware.FirmwareService;
41 import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; 45 import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
42 import org.thingsboard.server.queue.TbQueueProducer; 46 import org.thingsboard.server.queue.TbQueueProducer;
43 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 47 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
44 -import org.thingsboard.server.queue.provider.TbCoreQueueFactory;  
45 import org.thingsboard.server.queue.util.TbCoreComponent; 48 import org.thingsboard.server.queue.util.TbCoreComponent;
  49 +import org.thingsboard.server.service.queue.TbClusterService;
46 50
47 import javax.annotation.Nullable; 51 import javax.annotation.Nullable;
48 import java.util.ArrayList; 52 import java.util.ArrayList;
49 -import java.util.Arrays;  
50 import java.util.Collections; 53 import java.util.Collections;
  54 +import java.util.HashSet;
51 import java.util.List; 55 import java.util.List;
  56 +import java.util.Set;
52 import java.util.UUID; 57 import java.util.UUID;
53 import java.util.function.Consumer; 58 import java.util.function.Consumer;
54 59
55 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;  
56 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM;  
57 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE;  
58 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE;  
59 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; 60 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM;
  61 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM;
  62 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE;
  63 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
  64 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
  65 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
  66 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
  67 +import static org.thingsboard.server.common.data.firmware.FirmwareKeyUtil.getAttributeKey;
  68 +import static org.thingsboard.server.common.data.firmware.FirmwareKeyUtil.getTargetTelemetryKey;
  69 +import static org.thingsboard.server.common.data.firmware.FirmwareKeyUtil.getTelemetryKey;
60 70
61 @Slf4j 71 @Slf4j
62 @Service 72 @Service
63 @TbCoreComponent 73 @TbCoreComponent
  74 +@RequiredArgsConstructor
64 public class DefaultFirmwareStateService implements FirmwareStateService { 75 public class DefaultFirmwareStateService implements FirmwareStateService {
65 76
  77 + private final TbClusterService tbClusterService;
66 private final FirmwareService firmwareService; 78 private final FirmwareService firmwareService;
67 private final DeviceService deviceService; 79 private final DeviceService deviceService;
68 private final DeviceProfileService deviceProfileService; 80 private final DeviceProfileService deviceProfileService;
69 private final RuleEngineTelemetryService telemetryService; 81 private final RuleEngineTelemetryService telemetryService;
70 private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer; 82 private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
71 83
72 - public DefaultFirmwareStateService(FirmwareService firmwareService,  
73 - DeviceService deviceService,  
74 - DeviceProfileService deviceProfileService,  
75 - RuleEngineTelemetryService telemetryService,  
76 - TbCoreQueueFactory coreQueueFactory) {  
77 - this.firmwareService = firmwareService;  
78 - this.deviceService = deviceService;  
79 - this.deviceProfileService = deviceProfileService;  
80 - this.telemetryService = telemetryService;  
81 - this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer();  
82 - }  
83 -  
84 @Override 84 @Override
85 public void update(Device device, Device oldDevice) { 85 public void update(Device device, Device oldDevice) {
86 FirmwareId newFirmwareId = device.getFirmwareId(); 86 FirmwareId newFirmwareId = device.getFirmwareId();
@@ -183,10 +183,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -183,10 +183,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
183 fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); 183 fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
184 184
185 List<TsKvEntry> telemetry = new ArrayList<>(); 185 List<TsKvEntry> telemetry = new ArrayList<>();
186 - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));  
187 - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion())));  
188 - telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts)));  
189 - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); 186 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
  187 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
  188 + telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
  189 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name())));
190 190
191 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { 191 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
192 @Override 192 @Override
@@ -206,7 +206,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -206,7 +206,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
206 TenantId tenantId = device.getTenantId(); 206 TenantId tenantId = device.getTenantId();
207 DeviceId deviceId = device.getId(); 207 DeviceId deviceId = device.getId();
208 208
209 - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name())); 209 + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name()));
210 210
211 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { 211 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
212 @Override 212 @Override
@@ -221,13 +221,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -221,13 +221,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
221 }); 221 });
222 222
223 List<AttributeKvEntry> attributes = new ArrayList<>(); 223 List<AttributeKvEntry> attributes = new ArrayList<>();
  224 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle())));
  225 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion())));
  226 + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize())));
  227 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm())));
  228 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum())));
224 229
225 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));  
226 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));  
227 -  
228 - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));  
229 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm())));  
230 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));  
231 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { 230 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
232 @Override 231 @Override
233 public void onSuccess(@Nullable Void tmp) { 232 public void onSuccess(@Nullable Void tmp) {
@@ -242,12 +241,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -242,12 +241,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
242 } 241 }
243 242
244 private void remove(Device device) { 243 private void remove(Device device) {
245 - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE,  
246 - Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM), 244 + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareKeyUtil.ALL_ATTRIBUTE_KEYS,
247 new FutureCallback<>() { 245 new FutureCallback<>() {
248 @Override 246 @Override
249 public void onSuccess(@Nullable Void tmp) { 247 public void onSuccess(@Nullable Void tmp) {
250 log.trace("[{}] Success remove target firmware attributes!", device.getId()); 248 log.trace("[{}] Success remove target firmware attributes!", device.getId());
  249 + Set<AttributeKey> keysToNotify = new HashSet<>();
  250 + FirmwareKeyUtil.ALL_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
  251 + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
251 } 252 }
252 253
253 @Override 254 @Override
@@ -93,22 +93,7 @@ public class DataConstants { @@ -93,22 +93,7 @@ public class DataConstants {
93 public static final String USERNAME = "username"; 93 public static final String USERNAME = "username";
94 public static final String PASSWORD = "password"; 94 public static final String PASSWORD = "password";
95 95
96 - //firmware  
97 - //telemetry  
98 - public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title";  
99 - public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version";  
100 - public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";  
101 - public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";  
102 - public static final String TARGET_FIRMWARE_TS = "target_fw_ts";  
103 - public static final String FIRMWARE_STATE = "fw_state";  
104 -  
105 - //attributes  
106 - //telemetry  
107 - public static final String FIRMWARE_TITLE = "fw_title";  
108 - public static final String FIRMWARE_VERSION = "fw_version";  
109 - public static final String FIRMWARE_SIZE = "fw_size";  
110 - public static final String FIRMWARE_CHECKSUM = "fw_checksum";  
111 - public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";  
112 public static final String EDGE_MSG_SOURCE = "edge"; 96 public static final String EDGE_MSG_SOURCE = "edge";
113 public static final String MSG_SOURCE_KEY = "source"; 97 public static final String MSG_SOURCE_KEY = "source";
  98 +
114 } 99 }
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
19 import lombok.Data; 19 import lombok.Data;
20 import lombok.EqualsAndHashCode; 20 import lombok.EqualsAndHashCode;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.server.common.data.firmware.FirmwareType;
22 import org.thingsboard.server.common.data.id.FirmwareId; 23 import org.thingsboard.server.common.data.id.FirmwareId;
23 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
24 25
@@ -30,6 +31,7 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> @@ -30,6 +31,7 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
30 private static final long serialVersionUID = 3168391583570815419L; 31 private static final long serialVersionUID = 3168391583570815419L;
31 32
32 private TenantId tenantId; 33 private TenantId tenantId;
  34 + private FirmwareType type;
33 private String title; 35 private String title;
34 private String version; 36 private String version;
35 private boolean hasData; 37 private boolean hasData;
@@ -51,6 +53,7 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> @@ -51,6 +53,7 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
51 public FirmwareInfo(FirmwareInfo firmwareInfo) { 53 public FirmwareInfo(FirmwareInfo firmwareInfo) {
52 super(firmwareInfo); 54 super(firmwareInfo);
53 this.tenantId = firmwareInfo.getTenantId(); 55 this.tenantId = firmwareInfo.getTenantId();
  56 + this.type = firmwareInfo.getType();
54 this.title = firmwareInfo.getTitle(); 57 this.title = firmwareInfo.getTitle();
55 this.version = firmwareInfo.getVersion(); 58 this.version = firmwareInfo.getVersion();
56 this.hasData = firmwareInfo.isHasData(); 59 this.hasData = firmwareInfo.isHasData();
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.firmware;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public enum FirmwareKey {
  21 +
  22 + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm");
  23 +
  24 + @Getter
  25 + private final String value;
  26 +
  27 + FirmwareKey(String value) {
  28 + this.value = value;
  29 + }
  30 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.firmware;
  17 +
  18 +import java.util.ArrayList;
  19 +import java.util.List;
  20 +
  21 +public class FirmwareKeyUtil {
  22 +
  23 + public static final List<String> ALL_ATTRIBUTE_KEYS;
  24 + static {
  25 + ALL_ATTRIBUTE_KEYS = new ArrayList<>();
  26 + for (FirmwareType type : FirmwareType.values()) {
  27 + for (FirmwareKey key : FirmwareKey.values()) {
  28 + ALL_ATTRIBUTE_KEYS.add(getAttributeKey(type, key));
  29 + }
  30 + }
  31 + }
  32 +
  33 + public static String getAttributeKey(FirmwareType type, FirmwareKey key) {
  34 + return type.getKeyPrefix() + "_" + key.getValue();
  35 + }
  36 +
  37 + public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) {
  38 + return getTelemetryKey("target_", type, key);
  39 + }
  40 +
  41 + public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) {
  42 + return getTelemetryKey("current_", type, key);
  43 + }
  44 +
  45 + private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) {
  46 + return prefix + type.getKeyPrefix() + "_" + key.getValue();
  47 + }
  48 +
  49 + public static String getTelemetryKey(FirmwareType type, FirmwareKey key) {
  50 + return type.getKeyPrefix() + "_" + key.getValue();
  51 + }
  52 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.firmware;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public enum FirmwareType {
  21 +
  22 + FIRMWARE("fw"), SOFTWARE("sw");
  23 +
  24 + @Getter
  25 + private final String keyPrefix;
  26 +
  27 + FirmwareType(String keyPrefix) {
  28 + this.keyPrefix = keyPrefix;
  29 + }
  30 +}
@@ -363,10 +363,11 @@ message GetFirmwareResponseMsg { @@ -363,10 +363,11 @@ message GetFirmwareResponseMsg {
363 ResponseStatus responseStatus = 1; 363 ResponseStatus responseStatus = 1;
364 int64 firmwareIdMSB = 2; 364 int64 firmwareIdMSB = 2;
365 int64 firmwareIdLSB = 3; 365 int64 firmwareIdLSB = 3;
366 - string title = 4;  
367 - string version = 5;  
368 - string contentType = 6;  
369 - string fileName = 7; 366 + string type = 4;
  367 + string title = 5;
  368 + string version = 6;
  369 + string contentType = 7;
  370 + string fileName = 8;
370 } 371 }
371 372
372 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. 373 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
@@ -476,6 +476,7 @@ public class ModelConstants { @@ -476,6 +476,7 @@ public class ModelConstants {
476 */ 476 */
477 public static final String FIRMWARE_TABLE_NAME = "firmware"; 477 public static final String FIRMWARE_TABLE_NAME = "firmware";
478 public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; 478 public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
  479 + public static final String FIRMWARE_TYPE_COLUMN = "type";
479 public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; 480 public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
480 public static final String FIRMWARE_VERSION_COLUMN = "version"; 481 public static final String FIRMWARE_VERSION_COLUMN = "version";
481 public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; 482 public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";
@@ -21,6 +21,7 @@ import lombok.EqualsAndHashCode; @@ -21,6 +21,7 @@ import lombok.EqualsAndHashCode;
21 import org.hibernate.annotations.Type; 21 import org.hibernate.annotations.Type;
22 import org.hibernate.annotations.TypeDef; 22 import org.hibernate.annotations.TypeDef;
23 import org.thingsboard.server.common.data.Firmware; 23 import org.thingsboard.server.common.data.Firmware;
  24 +import org.thingsboard.server.common.data.firmware.FirmwareType;
24 import org.thingsboard.server.common.data.id.FirmwareId; 25 import org.thingsboard.server.common.data.id.FirmwareId;
25 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.dao.model.BaseSqlEntity; 27 import org.thingsboard.server.dao.model.BaseSqlEntity;
@@ -31,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; @@ -31,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
31 import javax.persistence.Column; 32 import javax.persistence.Column;
32 import javax.persistence.Entity; 33 import javax.persistence.Entity;
33 import javax.persistence.Lob; 34 import javax.persistence.Lob;
  35 +import javax.persistence.EnumType;
  36 +import javax.persistence.Enumerated;
34 import javax.persistence.Table; 37 import javax.persistence.Table;
35 import java.nio.ByteBuffer; 38 import java.nio.ByteBuffer;
36 import java.util.UUID; 39 import java.util.UUID;
@@ -44,6 +47,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME @@ -44,6 +47,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME
44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; 47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; 48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  50 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; 51 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
48 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 52 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
49 53
@@ -57,6 +61,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex @@ -57,6 +61,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
57 @Column(name = FIRMWARE_TENANT_ID_COLUMN) 61 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
58 private UUID tenantId; 62 private UUID tenantId;
59 63
  64 + @Enumerated(EnumType.STRING)
  65 + @Column(name = FIRMWARE_TYPE_COLUMN)
  66 + private FirmwareType type;
  67 +
60 @Column(name = FIRMWARE_TITLE_COLUMN) 68 @Column(name = FIRMWARE_TITLE_COLUMN)
61 private String title; 69 private String title;
62 70
@@ -97,6 +105,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex @@ -97,6 +105,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
97 this.createdTime = firmware.getCreatedTime(); 105 this.createdTime = firmware.getCreatedTime();
98 this.setUuid(firmware.getUuidId()); 106 this.setUuid(firmware.getUuidId());
99 this.tenantId = firmware.getTenantId().getId(); 107 this.tenantId = firmware.getTenantId().getId();
  108 + this.type = firmware.getType();
100 this.title = firmware.getTitle(); 109 this.title = firmware.getTitle();
101 this.version = firmware.getVersion(); 110 this.version = firmware.getVersion();
102 this.fileName = firmware.getFileName(); 111 this.fileName = firmware.getFileName();
@@ -123,6 +132,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex @@ -123,6 +132,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
123 Firmware firmware = new Firmware(new FirmwareId(id)); 132 Firmware firmware = new Firmware(new FirmwareId(id));
124 firmware.setCreatedTime(createdTime); 133 firmware.setCreatedTime(createdTime);
125 firmware.setTenantId(new TenantId(tenantId)); 134 firmware.setTenantId(new TenantId(tenantId));
  135 + firmware.setType(type);
126 firmware.setTitle(title); 136 firmware.setTitle(title);
127 firmware.setVersion(version); 137 firmware.setVersion(version);
128 firmware.setFileName(fileName); 138 firmware.setFileName(fileName);
@@ -22,6 +22,7 @@ import org.hibernate.annotations.Type; @@ -22,6 +22,7 @@ import org.hibernate.annotations.Type;
22 import org.hibernate.annotations.TypeDef; 22 import org.hibernate.annotations.TypeDef;
23 import org.thingsboard.common.util.JacksonUtil; 23 import org.thingsboard.common.util.JacksonUtil;
24 import org.thingsboard.server.common.data.FirmwareInfo; 24 import org.thingsboard.server.common.data.FirmwareInfo;
  25 +import org.thingsboard.server.common.data.firmware.FirmwareType;
25 import org.thingsboard.server.common.data.id.FirmwareId; 26 import org.thingsboard.server.common.data.id.FirmwareId;
26 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.dao.model.BaseSqlEntity; 28 import org.thingsboard.server.dao.model.BaseSqlEntity;
@@ -31,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; @@ -31,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
31 32
32 import javax.persistence.Column; 33 import javax.persistence.Column;
33 import javax.persistence.Entity; 34 import javax.persistence.Entity;
  35 +import javax.persistence.EnumType;
  36 +import javax.persistence.Enumerated;
34 import javax.persistence.Table; 37 import javax.persistence.Table;
35 import javax.persistence.Transient; 38 import javax.persistence.Transient;
36 import java.util.UUID; 39 import java.util.UUID;
@@ -38,13 +41,12 @@ import java.util.UUID; @@ -38,13 +41,12 @@ import java.util.UUID;
38 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; 41 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
39 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; 42 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
40 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; 43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
41 -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;  
42 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
44 -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;  
45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; 46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; 47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; 48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  49 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
49 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 51 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
50 52
@@ -58,6 +60,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -58,6 +60,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
58 @Column(name = FIRMWARE_TENANT_ID_COLUMN) 60 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
59 private UUID tenantId; 61 private UUID tenantId;
60 62
  63 + @Enumerated(EnumType.STRING)
  64 + @Column(name = FIRMWARE_TYPE_COLUMN)
  65 + private FirmwareType type;
  66 +
61 @Column(name = FIRMWARE_TITLE_COLUMN) 67 @Column(name = FIRMWARE_TITLE_COLUMN)
62 private String title; 68 private String title;
63 69
@@ -107,12 +113,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -107,12 +113,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
107 this.additionalInfo = firmware.getAdditionalInfo(); 113 this.additionalInfo = firmware.getAdditionalInfo();
108 } 114 }
109 115
110 - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, 116 + public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String type, String title, String version,
111 String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize, 117 String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize,
112 Object additionalInfo, boolean hasData) { 118 Object additionalInfo, boolean hasData) {
113 this.id = id; 119 this.id = id;
114 this.createdTime = createdTime; 120 this.createdTime = createdTime;
115 this.tenantId = tenantId; 121 this.tenantId = tenantId;
  122 + this.type = FirmwareType.valueOf(type);
116 this.title = title; 123 this.title = title;
117 this.version = version; 124 this.version = version;
118 this.fileName = fileName; 125 this.fileName = fileName;
@@ -139,6 +146,7 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -139,6 +146,7 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
139 FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); 146 FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
140 firmware.setCreatedTime(createdTime); 147 firmware.setCreatedTime(createdTime);
141 firmware.setTenantId(new TenantId(tenantId)); 148 firmware.setTenantId(new TenantId(tenantId));
  149 + firmware.setType(type);
142 firmware.setTitle(title); 150 firmware.setTitle(title);
143 firmware.setVersion(version); 151 firmware.setVersion(version);
144 firmware.setFileName(fileName); 152 firmware.setFileName(fileName);
@@ -25,14 +25,14 @@ import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; @@ -25,14 +25,14 @@ import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
25 import java.util.UUID; 25 import java.util.UUID;
26 26
27 public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> { 27 public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
28 - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + 28 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
29 "f.tenantId = :tenantId " + 29 "f.tenantId = :tenantId " +
30 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 30 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
31 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, 31 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
32 @Param("searchText") String searchText, 32 @Param("searchText") String searchText,
33 Pageable pageable); 33 Pageable pageable);
34 34
35 - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + 35 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
36 "f.tenantId = :tenantId " + 36 "f.tenantId = :tenantId " +
37 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + 37 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
38 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 38 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
@@ -162,6 +162,7 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -162,6 +162,7 @@ CREATE TABLE IF NOT EXISTS firmware (
162 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, 162 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
163 created_time bigint NOT NULL, 163 created_time bigint NOT NULL,
164 tenant_id uuid NOT NULL, 164 tenant_id uuid NOT NULL,
  165 + type varchar(32) NOT NULL,
165 title varchar(255) NOT NULL, 166 title varchar(255) NOT NULL,
166 version varchar(255) NOT NULL, 167 version varchar(255) NOT NULL,
167 file_name varchar(255), 168 file_name varchar(255),
@@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); @@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
45 CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc); 45 CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
46 46
47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); 47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);
  48 +
@@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware (
180 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, 180 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
181 created_time bigint NOT NULL, 181 created_time bigint NOT NULL,
182 tenant_id uuid NOT NULL, 182 tenant_id uuid NOT NULL,
  183 + device_profile_id uuid NOT NULL,
  184 + type varchar(32) NOT NULL,
183 title varchar(255) NOT NULL, 185 title varchar(255) NOT NULL,
184 version varchar(255) NOT NULL, 186 version varchar(255) NOT NULL,
185 file_name varchar(255), 187 file_name varchar(255),
@@ -190,7 +192,8 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -190,7 +192,8 @@ CREATE TABLE IF NOT EXISTS firmware (
190 data_size bigint, 192 data_size bigint,
191 additional_info varchar, 193 additional_info varchar,
192 search_text varchar(255), 194 search_text varchar(255),
193 - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) 195 + CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version),
  196 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE
194 ); 197 );
195 198
196 CREATE TABLE IF NOT EXISTS device_profile ( 199 CREATE TABLE IF NOT EXISTS device_profile (
@@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile (
206 is_default boolean, 209 is_default boolean,
207 tenant_id uuid, 210 tenant_id uuid,
208 firmware_id uuid, 211 firmware_id uuid,
  212 + software_id uuid,
209 default_rule_chain_id uuid, 213 default_rule_chain_id uuid,
210 default_queue_name varchar(255), 214 default_queue_name varchar(255),
211 provision_device_key varchar, 215 provision_device_key varchar,
212 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), 216 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
213 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), 217 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
214 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), 218 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
215 - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) 219 + CONSTRAINT fk_device_profile_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id)
  220 + CONSTRAINT fk_device_profile_software FOREIGN KEY (software_id) REFERENCES firmware(id)
216 ); 221 );
217 222
  223 +-- We will use one-to-many relation in the first release and extend this feature in case of user requests
  224 +-- CREATE TABLE IF NOT EXISTS device_profile_firmware (
  225 +-- device_profile_id uuid NOT NULL,
  226 +-- firmware_id uuid NOT NULL,
  227 +-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id),
  228 +-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE,
  229 +-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE,
  230 +-- );
  231 +
218 CREATE TABLE IF NOT EXISTS device ( 232 CREATE TABLE IF NOT EXISTS device (
219 id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, 233 id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
220 created_time bigint NOT NULL, 234 created_time bigint NOT NULL,
@@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device ( @@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device (
228 search_text varchar(255), 242 search_text varchar(255),
229 tenant_id uuid, 243 tenant_id uuid,
230 firmware_id uuid, 244 firmware_id uuid,
  245 + software_id uuid,
231 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), 246 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
232 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), 247 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
233 CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) 248 CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
  249 + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
234 ); 250 );
235 251
236 CREATE TABLE IF NOT EXISTS device_credentials ( 252 CREATE TABLE IF NOT EXISTS device_credentials (